From c8ed7ce242db83ca2c4e9eab557a88adbae5ef6a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 18 Oct 2022 15:40:06 +0300 Subject: ASoC: SOF: Intel: ipc4: Read the interrupt reason registers at the same time Read both registers as the first step in the interrupt handler to make sure that we are handling the event which triggered the interrupt. The delayed reading of the target request register might reflect incorrect information about the reason why the interrupt was risen. Note also that the IPC3 interrupt handler is implemented in this way also. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Rander Wang Link: https://lore.kernel.org/r/20221018124008.6846-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-ipc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/soc/sof/intel/hda-ipc.c') diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 9b3667c705e4..4118532faf3f 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -126,6 +126,8 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) u32 hipcie, hipct; hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE); + hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); + if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { /* DSP received the message */ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL, @@ -135,7 +137,6 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) ipc_irq = true; } - hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); if (hipct & HDA_DSP_REG_HIPCT_BUSY) { /* Message from DSP (reply or notification) */ u32 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, -- cgit v1.2.3 From 483e4cdfb502e6bea6b0a226a3ff7c22e60153de Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 18 Oct 2022 15:40:07 +0300 Subject: ASoC: SOF: Intel: ipc4: Wait for channel to be free before sending a message Before attempting to send a message to the DSP we need to check if the downstream BUSY flag has been cleared by the firmware to avoid lost IPC messages by the firmware. This is required by a firmware which only acks the received message after it has sent a reply to the host. With a bad luck, the host would send a message before the firmware gets to the clearing the flag and thus losing a message. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Rander Wang Link: https://lore.kernel.org/r/20221018124008.6846-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/cnl.c | 17 +++++++++++++++++ sound/soc/sof/intel/hda-ipc.c | 17 +++++++++++++++++ sound/soc/sof/intel/hda.c | 11 +++++++++++ sound/soc/sof/intel/hda.h | 9 +++++++++ sound/soc/sof/intel/mtl.c | 17 +++++++++++++++++ 5 files changed, 71 insertions(+) (limited to 'sound/soc/sof/intel/hda-ipc.c') diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 2f2bcde42759..4bf233787757 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -37,6 +37,7 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) { struct sof_ipc4_msg notification_data = {{ 0 }}; struct snd_sof_dev *sdev = context; + bool ack_received = false; bool ipc_irq = false; u32 hipcida, hipctdr; @@ -50,6 +51,7 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) cnl_ipc_dsp_done(sdev); ipc_irq = true; + ack_received = true; } if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) { @@ -98,6 +100,13 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) /* This interrupt is not shared so no need to return IRQ_NONE. */ dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); + if (ack_received) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + + if (hdev->delayed_ipc_tx_msg) + cnl_ipc4_send_msg(sdev, hdev->delayed_ipc_tx_msg); + } + return IRQ_HANDLED; } @@ -251,8 +260,16 @@ static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg, int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct sof_ipc4_msg *msg_data = msg->msg_data; + if (hda_ipc4_tx_is_busy(sdev)) { + hdev->delayed_ipc_tx_msg = msg; + return 0; + } + + hdev->delayed_ipc_tx_msg = NULL; + /* send the message via mailbox */ if (msg_data->data_size) sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr, diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 4118532faf3f..b4668c969a29 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -69,8 +69,16 @@ int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct sof_ipc4_msg *msg_data = msg->msg_data; + if (hda_ipc4_tx_is_busy(sdev)) { + hdev->delayed_ipc_tx_msg = msg; + return 0; + } + + hdev->delayed_ipc_tx_msg = NULL; + /* send the message via mailbox */ if (msg_data->data_size) sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr, @@ -122,6 +130,7 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) { struct sof_ipc4_msg notification_data = {{ 0 }}; struct snd_sof_dev *sdev = context; + bool ack_received = false; bool ipc_irq = false; u32 hipcie, hipct; @@ -135,6 +144,7 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) hda_dsp_ipc_dsp_done(sdev); ipc_irq = true; + ack_received = true; } if (hipct & HDA_DSP_REG_HIPCT_BUSY) { @@ -187,6 +197,13 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) /* This interrupt is not shared so no need to return IRQ_NONE. */ dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); + if (ack_received) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + + if (hdev->delayed_ipc_tx_msg) + hda_dsp_ipc4_send_msg(sdev, hdev->delayed_ipc_tx_msg); + } + return IRQ_HANDLED; } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 1188ec51816b..eddd20beea0d 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -681,6 +681,17 @@ void hda_ipc4_dump(struct snd_sof_dev *sdev) hipci, hipcie, hipct, hipcte, hipcctl); } +bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; + u32 val; + + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->ipc_req); + + return !!(val & chip->ipc_req_mask); +} + static int hda_init(struct snd_sof_dev *sdev) { struct hda_bus *hbus; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 2ab3c3840b92..65657d145dc2 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -521,6 +521,14 @@ struct sof_intel_hda_dev { /* Intel NHLT information */ struct nhlt_acpi_table *nhlt; + + /* + * Pointing to the IPC message if immediate sending was not possible + * because the downlink communication channel was BUSY at the time. + * The message will be re-tried when the channel becomes free (the ACK + * is received from the DSP for the previous message) + */ + struct snd_sof_ipc_msg *delayed_ipc_tx_msg; }; static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s) @@ -852,6 +860,7 @@ int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask); irqreturn_t cnl_ipc4_irq_thread(int irq, void *context); int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context); +bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev); int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); void hda_ipc4_dump(struct snd_sof_dev *sdev); extern struct sdw_intel_ops sdw_callback; diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index a9b31b31a4e4..9d1bc74395e7 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -90,8 +90,16 @@ static bool mtl_dsp_check_sdw_irq(struct snd_sof_dev *sdev) static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct sof_ipc4_msg *msg_data = msg->msg_data; + if (hda_ipc4_tx_is_busy(sdev)) { + hdev->delayed_ipc_tx_msg = msg; + return 0; + } + + hdev->delayed_ipc_tx_msg = NULL; + /* send the message via mailbox */ if (msg_data->data_size) sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr, @@ -492,6 +500,7 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) { struct sof_ipc4_msg notification_data = {{ 0 }}; struct snd_sof_dev *sdev = context; + bool ack_received = false; bool ipc_irq = false; u32 hipcida; u32 hipctdr; @@ -508,6 +517,7 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) mtl_ipc_dsp_done(sdev); ipc_irq = true; + ack_received = true; } if (hipctdr & MTL_DSP_REG_HFIPCXTDR_BUSY) { @@ -558,6 +568,13 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); } + if (ack_received) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + + if (hdev->delayed_ipc_tx_msg) + mtl_ipc_send_msg(sdev, hdev->delayed_ipc_tx_msg); + } + return IRQ_HANDLED; } -- cgit v1.2.3 From 010c050fe9ea263e3fc17493822117610a23f662 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 18 Oct 2022 15:40:08 +0300 Subject: ASoC: SOF: Intel: ipc4: Ack a received reply or notification separately By acking a received message we tell the DSP that we have processed the message (reply or notification) and we are open to receive a new one. The original implementation did this in a common code after the received message got handled as reply or notification. With right timing this opens up a small window when we have processed the reply and let the other thread proceed to send a new message to the DSP, which is allowed as the DSP is free to receive message. But when the message is received and processed by the DSP and it wants to send a reply it will still see that the previous message has not been acked, so it fails to send a reply. Later the first reply got acked by the kernel, but it is too late and the in-flight message got a timeout due to firmware not responding (which it tried, but could not). Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Rander Wang Link: https://lore.kernel.org/r/20221018124008.6846-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/cnl.c | 7 ++++--- sound/soc/sof/intel/hda-ipc.c | 7 ++++--- sound/soc/sof/intel/mtl.c | 5 +++-- 3 files changed, 11 insertions(+), 8 deletions(-) (limited to 'sound/soc/sof/intel/hda-ipc.c') diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 4bf233787757..da26f0ce9abc 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -72,6 +72,7 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) spin_lock_irq(&sdev->ipc_lock); snd_sof_ipc_get_reply(sdev); + cnl_ipc_host_done(sdev); snd_sof_ipc_reply(sdev, data->primary); spin_unlock_irq(&sdev->ipc_lock); @@ -88,10 +89,10 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) sdev->ipc->msg.rx_data = ¬ification_data; snd_sof_ipc_msgs_rx(sdev); sdev->ipc->msg.rx_data = NULL; - } - /* Let DSP know that we have finished processing the message */ - cnl_ipc_host_done(sdev); + /* Let DSP know that we have finished processing the message */ + cnl_ipc_host_done(sdev); + } ipc_irq = true; } diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index b4668c969a29..a7c454e03952 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -169,6 +169,7 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) spin_lock_irq(&sdev->ipc_lock); snd_sof_ipc_get_reply(sdev); + hda_dsp_ipc_host_done(sdev); snd_sof_ipc_reply(sdev, data->primary); spin_unlock_irq(&sdev->ipc_lock); @@ -185,10 +186,10 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) sdev->ipc->msg.rx_data = ¬ification_data; snd_sof_ipc_msgs_rx(sdev); sdev->ipc->msg.rx_data = NULL; - } - /* Let DSP know that we have finished processing the message */ - hda_dsp_ipc_host_done(sdev); + /* Let DSP know that we have finished processing the message */ + hda_dsp_ipc_host_done(sdev); + } ipc_irq = true; } diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 9d1bc74395e7..054b9ab721ff 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -540,6 +540,7 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) spin_lock_irq(&sdev->ipc_lock); snd_sof_ipc_get_reply(sdev); + mtl_ipc_host_done(sdev); snd_sof_ipc_reply(sdev, data->primary); spin_unlock_irq(&sdev->ipc_lock); @@ -556,9 +557,9 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) sdev->ipc->msg.rx_data = ¬ification_data; snd_sof_ipc_msgs_rx(sdev); sdev->ipc->msg.rx_data = NULL; - } - mtl_ipc_host_done(sdev); + mtl_ipc_host_done(sdev); + } ipc_irq = true; } -- cgit v1.2.3