diff options
Diffstat (limited to 'sound/soc/sof/intel/hda-ipc.c')
-rw-r--r-- | sound/soc/sof/intel/hda-ipc.c | 60 |
1 files changed, 35 insertions, 25 deletions
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 73ead7070cde..50244b82600c 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -56,13 +56,11 @@ static void hda_dsp_ipc_dsp_done(struct snd_sof_dev *sdev) int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { - u32 cmd = msg->header; - /* send IPC message to DSP */ sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, msg->msg_size); snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, - cmd | HDA_DSP_REG_HIPCI_BUSY); + HDA_DSP_REG_HIPCI_BUSY); return 0; } @@ -72,7 +70,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) struct snd_sof_ipc_msg *msg = sdev->msg; struct sof_ipc_reply reply; struct sof_ipc_cmd_hdr *hdr; - unsigned long flags; int ret = 0; /* @@ -84,7 +81,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); return; } - spin_lock_irqsave(&sdev->ipc_lock, flags); hdr = msg->msg_data; if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE)) { @@ -123,7 +119,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) out: msg->reply_error = ret; - spin_unlock_irqrestore(&sdev->ipc_lock, flags); } static bool hda_dsp_ipc_is_sof(uint32_t msg) @@ -136,30 +131,23 @@ static bool hda_dsp_ipc_is_sof(uint32_t msg) irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) { struct snd_sof_dev *sdev = context; - irqreturn_t ret = IRQ_NONE; u32 hipci; u32 hipcie; u32 hipct; u32 hipcte; - u32 hipcctl; u32 msg; u32 msg_ext; + bool ipc_irq = false; /* read IPC status */ 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); - hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL); - - /* reenable IPC interrupt */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, - HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI); + hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCTE); /* is this a reply message from the DSP */ - if (hipcie & HDA_DSP_REG_HIPCIE_DONE && - hipcctl & HDA_DSP_REG_HIPCCTL_DONE) { - hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_REG_HIPCI); + if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK; msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK; @@ -172,6 +160,18 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) HDA_DSP_REG_HIPCCTL, HDA_DSP_REG_HIPCCTL_DONE, 0); + /* + * Make sure the interrupt thread cannot be preempted between + * waking up the sender and re-enabling the interrupt. Also + * protect against a theoretical race with sof_ipc_tx_message(): + * if the DSP is fast enough to receive an IPC message, reply to + * it, and the host interrupt processing calls this function on + * a different core from the one, where the sending is taking + * place, the message might not yet be marked as expecting a + * reply. + */ + spin_lock_irq(&sdev->ipc_lock); + /* handle immediate reply from DSP core - ignore ROM messages */ if (hda_dsp_ipc_is_sof(msg)) { hda_dsp_ipc_get_reply(sdev); @@ -187,15 +187,13 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) /* set the done bit */ hda_dsp_ipc_dsp_done(sdev); - ret = IRQ_HANDLED; + spin_unlock_irq(&sdev->ipc_lock); + + ipc_irq = true; } /* is this a new message from DSP */ - if (hipct & HDA_DSP_REG_HIPCT_BUSY && - hipcctl & HDA_DSP_REG_HIPCCTL_BUSY) { - - hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_REG_HIPCTE); + if (hipct & HDA_DSP_REG_HIPCT_BUSY) { msg = hipct & HDA_DSP_REG_HIPCT_MSG_MASK; msg_ext = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK; @@ -219,10 +217,22 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) hda_dsp_ipc_host_done(sdev); - ret = IRQ_HANDLED; + ipc_irq = true; } - return ret; + if (!ipc_irq) { + /* + * This interrupt is not shared so no need to return IRQ_NONE. + */ + dev_err_ratelimited(sdev->dev, + "error: nothing to do in IRQ thread\n"); + } + + /* re-enable IPC interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + + return IRQ_HANDLED; } /* is this IRQ for ADSP ? - we only care about IPC here */ |