/** * ipa_mhi_init() - Initialize IPA MHI driver * @params: initialization params * * This function is called by MHI client driver on boot to initialize IPA MHI * Driver. When this function returns device can move to READY state. * This function is doing the following: * - Initialize MHI IPA internal data structures * - Create IPA RM resources * - Initialize debugfs * * Return codes: 0 : success * negative : error */ int ipa_mhi_init(struct ipa_mhi_init_params *params) { int res; struct ipa_rm_create_params mhi_prod_params; struct ipa_rm_create_params mhi_cons_params; IPA_MHI_FUNC_ENTRY(); if (!params) { IPA_MHI_ERR("null args\n"); return -EINVAL; } if (!params->notify) { IPA_MHI_ERR("null notify function\n"); return -EINVAL; } if (ipa_mhi_ctx) { IPA_MHI_ERR("already initialized\n"); return -EPERM; } IPA_MHI_DBG("msi: addr_lo = 0x%x addr_hi = 0x%x\n", params->msi.addr_low, params->msi.addr_hi); IPA_MHI_DBG("msi: data = 0x%x mask = 0x%x\n", params->msi.data, params->msi.mask); IPA_MHI_DBG("mmio_addr = 0x%x\n", params->mmio_addr); IPA_MHI_DBG("first_ch_idx = 0x%x\n", params->first_ch_idx); IPA_MHI_DBG("first_er_idx = 0x%x\n", params->first_er_idx); IPA_MHI_DBG("notify = %pF priv = %p\n", params->notify, params->priv); /* Initialize context */ ipa_mhi_ctx = kzalloc(sizeof(*ipa_mhi_ctx), GFP_KERNEL); if (!ipa_mhi_ctx) { IPA_MHI_ERR("no memory\n"); res = -EFAULT; goto fail_alloc_ctx; } ipa_mhi_ctx->state = IPA_MHI_STATE_INITIALIZED; ipa_mhi_ctx->msi = params->msi; ipa_mhi_ctx->mmio_addr = params->mmio_addr; ipa_mhi_ctx->first_ch_idx = params->first_ch_idx; ipa_mhi_ctx->first_er_idx = params->first_er_idx; ipa_mhi_ctx->cb_notify = params->notify; ipa_mhi_ctx->cb_priv = params->priv; ipa_mhi_ctx->rm_cons_state = IPA_MHI_RM_STATE_RELEASED; ipa_mhi_ctx->qmi_req_id = 0; init_completion(&ipa_mhi_ctx->rm_prod_granted_comp); spin_lock_init(&ipa_mhi_ctx->state_lock); init_completion(&ipa_mhi_ctx->rm_cons_comp); ipa_mhi_ctx->wq = create_singlethread_workqueue("ipa_mhi_wq"); if (!ipa_mhi_ctx->wq) { IPA_MHI_ERR("failed to create workqueue\n"); res = -EFAULT; goto fail_create_wq; } /* Initialize debugfs */ ipa_mhi_debugfs_init(); /* Create PROD in IPA RM */ memset(&mhi_prod_params, 0, sizeof(mhi_prod_params)); mhi_prod_params.name = IPA_RM_RESOURCE_MHI_PROD; mhi_prod_params.floor_voltage = IPA_VOLTAGE_SVS; mhi_prod_params.reg_params.notify_cb = ipa_mhi_rm_prod_notify; res = ipa_rm_create_resource(&mhi_prod_params); if (res) { IPA_MHI_ERR("fail to create IPA_RM_RESOURCE_MHI_PROD\n"); goto fail_create_rm_prod; } /* Create CONS in IPA RM */ memset(&mhi_cons_params, 0, sizeof(mhi_cons_params)); mhi_cons_params.name = IPA_RM_RESOURCE_MHI_CONS; mhi_cons_params.floor_voltage = IPA_VOLTAGE_SVS; mhi_cons_params.request_resource = ipa_mhi_rm_cons_request; mhi_cons_params.release_resource = ipa_mhi_rm_cons_release; res = ipa_rm_create_resource(&mhi_cons_params); if (res) { IPA_MHI_ERR("fail to create IPA_RM_RESOURCE_MHI_CONS\n"); goto fail_create_rm_cons; } /* Initialize uC interface */ ipa_uc_mhi_init(ipa_mhi_uc_ready_cb, ipa_mhi_uc_wakeup_request_cb); if (ipa_uc_state_check() == 0) ipa_mhi_set_state(IPA_MHI_STATE_READY); IPA_MHI_FUNC_EXIT(); return 0; fail_create_rm_cons: ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_PROD); fail_create_rm_prod: destroy_workqueue(ipa_mhi_ctx->wq); fail_create_wq: kfree(ipa_mhi_ctx); ipa_mhi_ctx = NULL; fail_alloc_ctx: return res; }
} else { IPADBG("unsupported uC evt opcode=%u\n", ipa_ctx->uc_ctx.uc_sram_mmio->eventOp); } ipa_dec_client_disable_clks(); } static int ipa_uc_panic_notifier(struct notifier_block *this, unsigned long event, void *ptr) { int result = 0; IPADBG("this=%p evt=%lu ptr=%p\n", this, event, ptr); result = ipa_uc_state_check(); if (result) goto fail; if (ipa_inc_client_enable_clks_no_block()) goto fail; ipa_ctx->uc_ctx.uc_sram_mmio->cmdOp = IPA_CPU_2_HW_CMD_ERR_FATAL; ipa_ctx->uc_ctx.pending_cmd = ipa_ctx->uc_ctx.uc_sram_mmio->cmdOp; /* ensure write to shared memory is done before triggering uc */ wmb(); ipa_write_reg(ipa_ctx->mmio, IPA_IRQ_EE_UC_n_OFFS(0), 0x1); /* give uc enough time to save state */ udelay(IPA_PKT_FLUSH_TO_US);
/** * 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; }