/**
 * ipa_mhi_start() - Start IPA MHI engine
 * @params: pcie addresses for MHI
 *
 * This function is called by MHI client driver on MHI engine start for
 * handling MHI accelerated channels. This function is called after
 * ipa_mhi_init() was called and can be called after MHI reset to restart MHI
 * engine. When this function returns device can move to M0 state.
 * This function is doing the following:
 *	- Send command to uC for initialization of MHI engine
 *	- Add dependencies to IPA RM
 *	- Request MHI_PROD in IPA RM
 *
 * Return codes: 0	  : success
 *		 negative : error
 */
int ipa_mhi_start(struct ipa_mhi_start_params *params)
{
	int res;

	IPA_MHI_FUNC_ENTRY();

	if (!params) {
		IPA_MHI_ERR("null args\n");
		return -EINVAL;
	}

	if (unlikely(!ipa_mhi_ctx)) {
		IPA_MHI_ERR("IPA MHI was not initialized\n");
		return -EINVAL;
	}

	if (ipa_uc_state_check()) {
		IPA_MHI_ERR("IPA uc is not loaded\n");
		return -EAGAIN;
	}

	res = ipa_mhi_set_state(IPA_MHI_STATE_STARTED);
	if (res) {
		IPA_MHI_ERR("ipa_mhi_set_state %d\n", res);
		return res;
	}

	ipa_mhi_ctx->host_ctrl_addr = params->host_ctrl_addr;
	ipa_mhi_ctx->host_data_addr = params->host_data_addr;

	/* Add MHI <-> Q6 dependencies to IPA RM */
	res = ipa_rm_add_dependency(IPA_RM_RESOURCE_MHI_PROD,
		IPA_RM_RESOURCE_Q6_CONS);
	if (res && res != -EINPROGRESS) {
		IPA_MHI_ERR("failed to add dependency %d\n", res);
		goto fail_add_mhi_q6_dep;
	}

	res = ipa_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD,
		IPA_RM_RESOURCE_MHI_CONS);
	if (res && res != -EINPROGRESS) {
		IPA_MHI_ERR("failed to add dependency %d\n", res);
		goto fail_add_q6_mhi_dep;
	}

	res = ipa_mhi_request_prod();
	if (res) {
		IPA_MHI_ERR("failed request prod %d\n", res);
		goto fail_request_prod;
	}

	/* Initialize IPA MHI engine */
	res = ipa_uc_mhi_init_engine(&ipa_mhi_ctx->msi, ipa_mhi_ctx->mmio_addr,
		ipa_mhi_ctx->host_ctrl_addr, ipa_mhi_ctx->host_data_addr,
		ipa_mhi_ctx->first_ch_idx, ipa_mhi_ctx->first_er_idx);
	if (res) {
		IPA_MHI_ERR("failed to start MHI engine %d\n", res);
		goto fail_init_engine;
	}

	/* Update UL/DL sync if valid */
	res = ipa_uc_mhi_send_dl_ul_sync_info(cached_dl_ul_sync_info);
	if (res) {
		IPA_MHI_ERR("failed to update ul/dl sync %d\n", res);
		goto fail_init_engine;
	}

	IPA_MHI_FUNC_EXIT();
	return 0;

fail_init_engine:
	ipa_mhi_release_prod();
fail_request_prod:
	ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
		IPA_RM_RESOURCE_MHI_CONS);
fail_add_q6_mhi_dep:
	ipa_rm_delete_dependency(IPA_RM_RESOURCE_MHI_PROD,
		IPA_RM_RESOURCE_Q6_CONS);
fail_add_mhi_q6_dep:
	ipa_mhi_set_state(IPA_MHI_STATE_INITIALIZED);
	return res;
}
static int __init wwan_init(void)
{
	int ret;
	struct net_device *dev;
	struct wwan_private *wwan_ptr;
	unsigned n;
	struct ipa_rm_create_params ipa_rm_params;

	pr_info("%s: WWAN devices[%d]\n", __func__, WWAN_DEVICE_COUNT);
	for (n = 0; n < WWAN_DEVICE_COUNT; n++) {
		dev = alloc_netdev(sizeof(struct wwan_private),
				   WWAN_DEV_NAME, wwan_setup);
		if (!dev) {
			pr_err("%s: no memory for netdev %d\n", __func__, n);
			ret = -ENOMEM;
			goto fail;
		}
		netdevs[n] = dev;
		wwan_ptr = netdev_priv(dev);
		wwan_ptr->ch_id = n;
		spin_lock_init(&wwan_ptr->lock);
		init_completion(&wwan_ptr->resource_granted_completion);
		memset(&ipa_rm_params, 0, sizeof(struct ipa_rm_create_params));
		ipa_rm_params.name = ipa_rm_resource_by_ch_id[n];
		ipa_rm_params.reg_params.user_data = dev;
		ipa_rm_params.reg_params.notify_cb = ipa_rm_notify;
		ret = ipa_rm_create_resource(&ipa_rm_params);
		if (ret) {
			pr_err("%s: unable to create resourse %d in IPA RM\n",
			       __func__, ipa_rm_resource_by_ch_id[n]);
			goto fail;
		}
		ret = ipa_rm_inactivity_timer_init(ipa_rm_resource_by_ch_id[n],
						   IPA_RM_INACTIVITY_TIMER);
		if (ret) {
			pr_err("%s: ipa rm timer init failed %d on ins %d\n",
			       __func__, ret, n);
			goto fail;
		}
		ret = ipa_rm_add_dependency(ipa_rm_resource_by_ch_id[n],
					    IPA_RM_RESOURCE_A2_CONS);
		if (ret) {
			pr_err("%s: unable to add dependency %d rc=%d\n",
			       __func__, n, ret);
			goto fail;
		}
		ret = register_netdev(dev);
		if (ret) {
			pr_err("%s: unable to register netdev %d rc=%d\n",
			       __func__, n, ret);
			goto fail;
		}
	}
	return 0;
fail:
	for (n = 0; n < WWAN_DEVICE_COUNT; n++) {
		if (!netdevs[n])
			break;
		unregister_netdev(netdevs[n]);
		ipa_rm_inactivity_timer_destroy(ipa_rm_resource_by_ch_id[n]);
		free_netdev(netdevs[n]);
		netdevs[n] = NULL;
	}
	return ret;
}