From 560042b45f170039f33096d2a948445643d6ddfb Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 9 Jul 2021 13:50:42 +0200 Subject: [PATCH] ipc: let secondary cores acknowledge IPC messages Currently when an IPC message arrives, the primary core receives the interrupt, schedules a task and begins processing the message. If it identifies, that the message is for another core, it sends an IDC message to it and waits in a busy loop until the other core completes processing the message and writes a response into the mailbox. After that the primary core notifies the host about completing the processing. This patch lets the processing secondary core notify the host directly, which also frees the primary core to handle other tasks instead of waiting in a busy loop. Signed-off-by: Guennadi Liakhovetski --- src/drivers/intel/cavs/ipc.c | 5 +++-- src/idc/idc.c | 7 +++++-- src/include/sof/ipc/common.h | 4 +++- src/ipc/ipc-common.c | 16 ++++++++++++++-- src/ipc/ipc3/dai.c | 18 ++++++++++++++---- src/ipc/ipc3/handler.c | 21 +++++++++++++-------- src/ipc/ipc3/helper.c | 12 ++++++------ 7 files changed, 58 insertions(+), 25 deletions(-) diff --git a/src/drivers/intel/cavs/ipc.c b/src/drivers/intel/cavs/ipc.c index 0435dc02152f..67528d6748b4 100644 --- a/src/drivers/intel/cavs/ipc.c +++ b/src/drivers/intel/cavs/ipc.c @@ -200,9 +200,10 @@ enum task_state ipc_platform_do_cmd(void *data) void ipc_platform_complete_cmd(void *data) { -#if CONFIG_SUECREEK struct ipc *ipc = data; -#endif + + if (!cpu_is_me(ipc->core)) + return; /* write 1 to clear busy, and trigger interrupt to host*/ #if CAVS_VERSION == CAVS_VERSION_1_5 diff --git a/src/idc/idc.c b/src/idc/idc.c index 3ec1274f4405..582e6813bc06 100644 --- a/src/idc/idc.c +++ b/src/idc/idc.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -120,9 +121,11 @@ int idc_wait_in_blocking_mode(uint32_t target_core, bool (*cond)(int)) static void idc_ipc(void) { struct ipc *ipc = ipc_get(); - ipc_cmd_hdr *hdr = ipc->comp_data; - ipc_cmd(hdr); + ipc_cmd(ipc->comp_data); + + /* Signal the host */ + ipc_platform_complete_cmd(ipc); } /** diff --git a/src/include/sof/ipc/common.h b/src/include/sof/ipc/common.h index 33a7dcdcb518..a53010a02f69 100644 --- a/src/include/sof/ipc/common.h +++ b/src/include/sof/ipc/common.h @@ -68,6 +68,7 @@ struct ipc { struct list_item msg_list; /* queue of messages to be sent */ bool is_notification_pending; /* notification is being sent to host */ + unsigned int core; /* core, processing the IPC */ struct list_item comp_list; /* list of component devices */ @@ -190,8 +191,9 @@ void ipc_cmd(ipc_cmd_hdr *hdr); /** * \brief IPC message to be processed on other core. * @param[in] core Core id for IPC to be processed on. + * @param[in] blocking Process in blocking mode: wait for completion. * @return 1 if successful (reply sent by other core), error code otherwise. */ -int ipc_process_on_core(uint32_t core); +int ipc_process_on_core(uint32_t core, bool blocking); #endif /* __SOF_DRIVERS_IPC_H__ */ diff --git a/src/ipc/ipc-common.c b/src/ipc/ipc-common.c index 4e7386c78a30..f88412558a37 100644 --- a/src/ipc/ipc-common.c +++ b/src/ipc/ipc-common.c @@ -38,8 +38,9 @@ DECLARE_SOF_UUID("ipc", ipc_uuid, 0xbe60f97d, 0x78df, 0x4796, DECLARE_TR_CTX(ipc_tr, SOF_UUID(ipc_uuid), LOG_LEVEL_INFO); -int ipc_process_on_core(uint32_t core) +int ipc_process_on_core(uint32_t core, bool blocking) { + struct ipc *ipc = ipc_get(); struct idc_msg msg = { .header = IDC_MSG_IPC, .core = core, }; int ret; @@ -49,8 +50,19 @@ int ipc_process_on_core(uint32_t core) return -EACCES; } + /* The other core will write there its response */ + dcache_invalidate_region((void *)MAILBOX_HOSTBOX_BASE, + ((struct sof_ipc_cmd_hdr *)ipc->comp_data)->size); + + /* + * If the primary core is waiting for secondary cores to complete, it + * will also reply to the host + */ + if (!blocking) + ipc->core = core; + /* send IDC message */ - ret = idc_send_msg(&msg, IDC_BLOCKING); + ret = idc_send_msg(&msg, blocking ? IDC_BLOCKING : IDC_NON_BLOCKING); if (ret < 0) return ret; diff --git a/src/ipc/ipc3/dai.c b/src/ipc/ipc3/dai.c index c4403c465760..8dfa2929182e 100644 --- a/src/ipc/ipc3/dai.c +++ b/src/ipc/ipc3/dai.c @@ -191,17 +191,27 @@ int ipc_comp_dai_config(struct ipc *ipc, struct ipc_config_dai *common_config, if (!comp_on_core[i]) continue; - ret = ipc_process_on_core(i); + /* + * TODO: can secondary cores execute dai_config() in + * parallel? Then we could just wait for the last of + * them. For now execute them sequentially. + */ + ret = ipc_process_on_core(i, true); if (ret < 0) - return ret; + break; /* check whether IPC failed on secondary core */ mailbox_hostbox_read(&reply, sizeof(reply), 0, sizeof(reply)); - if (reply.error < 0) + if (reply.error < 0) { /* error reply already written */ - return 1; + ret = 1; + break; + } } + + /* We have waited until all secondary cores configured their DAIs */ + ipc->core = PLATFORM_PRIMARY_CORE_ID; } return ret; diff --git a/src/ipc/ipc3/handler.c b/src/ipc/ipc3/handler.c index c5d1c034e7cd..a2fbabb4c828 100644 --- a/src/ipc/ipc3/handler.c +++ b/src/ipc/ipc3/handler.c @@ -207,7 +207,7 @@ static int ipc_stream_pcm_params(uint32_t stream) /* check core */ if (!cpu_is_me(pcm_dev->core)) - return ipc_process_on_core(pcm_dev->core); + return ipc_process_on_core(pcm_dev->core, false); tr_dbg(&ipc_tr, "ipc: comp %d -> params", pcm_params.comp_id); @@ -319,7 +319,7 @@ static int ipc_stream_pcm_free(uint32_t header) /* check core */ if (!cpu_is_me(pcm_dev->core)) - return ipc_process_on_core(pcm_dev->core); + return ipc_process_on_core(pcm_dev->core, false); tr_dbg(&ipc_tr, "ipc: comp %d -> free", free_req.comp_id); @@ -356,7 +356,7 @@ static int ipc_stream_position(uint32_t header) /* check core */ if (!cpu_is_me(pcm_dev->core)) - return ipc_process_on_core(pcm_dev->core); + return ipc_process_on_core(pcm_dev->core, false); tr_info(&ipc_tr, "ipc: comp %d -> position", stream.comp_id); @@ -405,7 +405,7 @@ static int ipc_stream_trigger(uint32_t header) /* check core */ if (!cpu_is_me(pcm_dev->core)) - return ipc_process_on_core(pcm_dev->core); + return ipc_process_on_core(pcm_dev->core, false); tr_dbg(&ipc_tr, "ipc: comp %d -> trigger cmd 0x%x", stream.comp_id, ipc_command); @@ -1109,7 +1109,7 @@ static int ipc_comp_value(uint32_t header, uint32_t cmd) /* check core */ if (!cpu_is_me(comp_dev->core)) - return ipc_process_on_core(comp_dev->core); + return ipc_process_on_core(comp_dev->core, false); tr_dbg(&ipc_tr, "ipc: comp %d -> cmd %d", data->comp_id, data->cmd); @@ -1169,7 +1169,7 @@ static int ipc_glb_tplg_comp_new(uint32_t header) /* check core */ if (!cpu_is_me(comp->core)) - return ipc_process_on_core(comp->core); + return ipc_process_on_core(comp->core, false); tr_dbg(&ipc_tr, "ipc: pipe %d comp %d -> new (type %d)", comp->pipeline_id, comp->id, comp->type); @@ -1205,7 +1205,7 @@ static int ipc_glb_tplg_buffer_new(uint32_t header) /* check core */ if (!cpu_is_me(ipc_buffer.comp.core)) - return ipc_process_on_core(ipc_buffer.comp.core); + return ipc_process_on_core(ipc_buffer.comp.core, false); tr_dbg(&ipc_tr, "ipc: pipe %d buffer %d -> new (0x%x bytes)", ipc_buffer.comp.pipeline_id, ipc_buffer.comp.id, @@ -1242,7 +1242,7 @@ static int ipc_glb_tplg_pipe_new(uint32_t header) /* check core */ if (!cpu_is_me(ipc_pipeline.core)) - return ipc_process_on_core(ipc_pipeline.core); + return ipc_process_on_core(ipc_pipeline.core, false); tr_dbg(&ipc_tr, "ipc: pipe %d -> new", ipc_pipeline.pipeline_id); @@ -1495,6 +1495,7 @@ void ipc_boot_complete_msg(ipc_cmd_hdr *header, uint32_t *data) void ipc_cmd(ipc_cmd_hdr *_hdr) { struct sof_ipc_cmd_hdr *hdr = ipc_from_hdr(_hdr); + struct ipc *ipc = ipc_get(); struct sof_ipc_reply reply; uint32_t type = 0; int ret; @@ -1505,6 +1506,10 @@ void ipc_cmd(ipc_cmd_hdr *_hdr) goto out; } + if (!cpu_is_secondary(cpu_get_id())) + /* A new IPC from the host, delivered to the primary core */ + ipc->core = PLATFORM_PRIMARY_CORE_ID; + type = iGS(hdr->cmd); switch (type) { diff --git a/src/ipc/ipc3/helper.c b/src/ipc/ipc3/helper.c index 0e2008129e84..a32c1402f14d 100644 --- a/src/ipc/ipc3/helper.c +++ b/src/ipc/ipc3/helper.c @@ -513,7 +513,7 @@ int ipc_pipeline_free(struct ipc *ipc, uint32_t comp_id) /* check core */ if (!cpu_is_me(ipc_pipe->core)) - return ipc_process_on_core(ipc_pipe->core); + return ipc_process_on_core(ipc_pipe->core, false); /* free buffer and remove from list */ ret = pipeline_free(ipc_pipe->pipeline); @@ -548,7 +548,7 @@ int ipc_pipeline_complete(struct ipc *ipc, uint32_t comp_id) /* check core */ if (!cpu_is_me(ipc_pipe->core)) - return ipc_process_on_core(ipc_pipe->core); + return ipc_process_on_core(ipc_pipe->core, false); p = ipc_pipe->pipeline; @@ -653,7 +653,7 @@ int ipc_buffer_free(struct ipc *ipc, uint32_t buffer_id) /* check core */ if (!cpu_is_me(ibd->core)) - return ipc_process_on_core(ibd->core); + return ipc_process_on_core(ibd->core, false); /* try to find sink/source components to check if they still exists */ list_for_item(clist, &ipc->comp_list) { @@ -707,7 +707,7 @@ static int ipc_comp_to_buffer_connect(struct ipc_comp_dev *comp, int ret; if (!cpu_is_me(comp->core)) - return ipc_process_on_core(comp->core); + return ipc_process_on_core(comp->core, false); tr_dbg(&ipc_tr, "ipc: comp sink %d, source %d -> connect", buffer->id, comp->id); @@ -739,7 +739,7 @@ static int ipc_buffer_to_comp_connect(struct ipc_comp_dev *buffer, int ret; if (!cpu_is_me(comp->core)) - return ipc_process_on_core(comp->core); + return ipc_process_on_core(comp->core, false); tr_dbg(&ipc_tr, "ipc: comp sink %d, source %d -> connect", comp->id, buffer->id); @@ -856,7 +856,7 @@ int ipc_comp_free(struct ipc *ipc, uint32_t comp_id) /* check core */ if (!cpu_is_me(icd->core)) - return ipc_process_on_core(icd->core); + return ipc_process_on_core(icd->core, false); /* check state */ if (icd->cd->state != COMP_STATE_READY)