ASoC: SOF: Introduce state machine for FW boot
authorRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Wed, 18 Dec 2019 00:26:09 +0000 (18:26 -0600)
committerMark Brown <broonie@kernel.org>
Wed, 18 Dec 2019 19:52:20 +0000 (19:52 +0000)
Add a state machine for FW boot to track the
different stages of FW boot and replace the boot_complete
field with fw_state field in struct snd_sof_dev.
This will be used to determine the actions to be performed
during system suspend.

One of the main motivations for adding this change is the
fact that errors during the top-level SOF device probe cannot
be propagated and therefore suspending the SOF device normally
during system suspend could potentially run into errors.
For example, with the current flow, if the FW boot failed
for some reason and the system suspends, the SOF device
suspend could fail because the CTX_SAVE IPC would be attempted
even though the FW never really booted successfully causing it
to time out. Another scenario that the state machine fixes
is when the runtime suspend for the SOF device fails and
the DSP is powered down nevertheless, the CTX_SAVE IPC during
system suspend would timeout because the DSP is already
powered down.

Reviewed-by: Curtis Malainey <cujomalainey@chromium.org>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20191218002616.7652-2-pierre-louis.bossart@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sof/core.c
sound/soc/sof/intel/hda-loader.c
sound/soc/sof/intel/hda.c
sound/soc/sof/ipc.c
sound/soc/sof/loader.c
sound/soc/sof/pm.c
sound/soc/sof/sof-priv.h

index e258f6a8e7a5c5c115f3d5f4408ba58d31cdcae3..44f9c04d54aa027cc8c27fc2162cc606a5daf271 100644 (file)
@@ -92,6 +92,46 @@ out:
 }
 EXPORT_SYMBOL(snd_sof_get_status);
 
+/*
+ *                     FW Boot State Transition Diagram
+ *
+ *    +-----------------------------------------------------------------------+
+ *    |                                                                              |
+ * ------------------       ------------------                               |
+ * |               |        |                |                               |
+ * |   BOOT_FAILED  |       |  READY_FAILED  |-------------------------+     |
+ * |               |        |                |                         |     |
+ * ------------------       ------------------                         |     |
+ *     ^                           ^                                   |     |
+ *     |                           |                                   |     |
+ * (FW Boot Timeout)           (FW_READY FAIL)                         |     |
+ *     |                           |                                   |     |
+ *     |                           |                                   |     |
+ * ------------------              |              ------------------   |     |
+ * |               |               |              |                |   |     |
+ * |   IN_PROGRESS  |---------------+------------->|    COMPLETE    |  |     |
+ * |               | (FW Boot OK)   (FW_READY OK) |                |   |     |
+ * ------------------                             ------------------   |     |
+ *     ^                                               |               |     |
+ *     |                                               |               |     |
+ * (FW Loading OK)                            (System Suspend/Runtime Suspend)
+ *     |                                               |               |     |
+ *     |                                               |               |     |
+ * ------------------          ------------------      |               |     |
+ * |               |           |                |<-----+               |     |
+ * |   PREPARE     |           |   NOT_STARTED  |<---------------------+     |
+ * |               |           |                |<---------------------------+
+ * ------------------          ------------------
+ *    |            ^                       |      ^
+ *    |            |                       |      |
+ *    |            +-----------------------+      |
+ *    |                (DSP Probe OK)             |
+ *    |                                           |
+ *    |                                           |
+ *    +------------------------------------+
+ *     (System Suspend/Runtime Suspend)
+ */
+
 static int sof_probe_continue(struct snd_sof_dev *sdev)
 {
        struct snd_sof_pdata *plat_data = sdev->pdata;
@@ -104,6 +144,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
                return ret;
        }
 
+       sdev->fw_state = SOF_FW_BOOT_PREPARE;
+
        /* check machine info */
        ret = sof_machine_check(sdev);
        if (ret < 0) {
@@ -143,7 +185,12 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
                goto fw_load_err;
        }
 
-       /* boot the firmware */
+       sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS;
+
+       /*
+        * Boot the firmware. The FW boot status will be modified
+        * in snd_sof_run_firmware() depending on the outcome.
+        */
        ret = snd_sof_run_firmware(sdev);
        if (ret < 0) {
                dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n",
@@ -254,6 +301,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
 
        sdev->pdata = plat_data;
        sdev->first_boot = true;
+       sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
        dev_set_drvdata(dev, sdev);
 
        /* check all mandatory ops */
index b1783360fe106bdbf33acffc4443d142bfd81240..1782f50926391cf19dec7e026186053abb9f9856 100644 (file)
@@ -295,7 +295,6 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
 
        /* init for booting wait */
        init_waitqueue_head(&sdev->boot_wait);
-       sdev->boot_complete = false;
 
        /* prepare DMA for code loader stream */
        tag = cl_stream_prepare(sdev, 0x40, stripped_firmware.size,
index 8d27846d90486c658680f2d9c5f7fd507aacfe0b..3335e0076180feee32ff5d7a4c0178f8afd7e421 100644 (file)
@@ -168,7 +168,7 @@ void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags)
        panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
                                 HDA_ADSP_ERROR_CODE_SKL + 0x4);
 
-       if (sdev->boot_complete) {
+       if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
                hda_dsp_get_registers(sdev, &xoops, &panic_info, stack,
                                      HDA_DSP_STACK_DUMP_SIZE);
                snd_sof_get_status(sdev, status, panic, &xoops, &panic_info,
@@ -195,7 +195,7 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
                                  HDA_DSP_SRAM_REG_FW_STATUS);
        panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP);
 
-       if (sdev->boot_complete) {
+       if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
                hda_dsp_get_registers(sdev, &xoops, &panic_info, stack,
                                      HDA_DSP_STACK_DUMP_SIZE);
                snd_sof_get_status(sdev, status, panic, &xoops, &panic_info,
index 293c5ae8e88274d5708eaf208e829c137e14300e..6186c7ff044781e005779af3d391d3a667a0d1fc 100644 (file)
@@ -347,19 +347,12 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
                break;
        case SOF_IPC_FW_READY:
                /* check for FW boot completion */
-               if (!sdev->boot_complete) {
+               if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
                        err = sof_ops(sdev)->fw_ready(sdev, cmd);
-                       if (err < 0) {
-                               /*
-                                * this indicates a mismatch in ABI
-                                * between the driver and fw
-                                */
-                               dev_err(sdev->dev, "error: ABI mismatch %d\n",
-                                       err);
-                       } else {
-                               /* firmware boot completed OK */
-                               sdev->boot_complete = true;
-                       }
+                       if (err < 0)
+                               sdev->fw_state = SOF_FW_BOOT_READY_FAILED;
+                       else
+                               sdev->fw_state = SOF_FW_BOOT_COMPLETE;
 
                        /* wake up firmware loader */
                        wake_up(&sdev->boot_wait);
index 432d12bd493791837ff38f8afe0e71c35d858915..31847aa3975dc53fef971f1197d2fe5b07227f37 100644 (file)
@@ -512,7 +512,6 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
        int init_core_mask;
 
        init_waitqueue_head(&sdev->boot_wait);
-       sdev->boot_complete = false;
 
        /* create read-only fw_version debugfs to store boot version info */
        if (sdev->first_boot) {
@@ -544,19 +543,27 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
 
        init_core_mask = ret;
 
-       /* now wait for the DSP to boot */
-       ret = wait_event_timeout(sdev->boot_wait, sdev->boot_complete,
+       /*
+        * now wait for the DSP to boot. There are 3 possible outcomes:
+        * 1. Boot wait times out indicating FW boot failure.
+        * 2. FW boots successfully and fw_ready op succeeds.
+        * 3. FW boots but fw_ready op fails.
+        */
+       ret = wait_event_timeout(sdev->boot_wait,
+                                sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS,
                                 msecs_to_jiffies(sdev->boot_timeout));
        if (ret == 0) {
                dev_err(sdev->dev, "error: firmware boot failure\n");
                snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX |
                        SOF_DBG_TEXT | SOF_DBG_PCI);
-               /* after this point FW_READY msg should be ignored */
-               sdev->boot_complete = true;
+               sdev->fw_state = SOF_FW_BOOT_FAILED;
                return -EIO;
        }
 
-       dev_info(sdev->dev, "firmware boot complete\n");
+       if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
+               dev_info(sdev->dev, "firmware boot complete\n");
+       else
+               return -EIO; /* FW boots but fw_ready op failed */
 
        /* perform post fw run operations */
        ret = snd_sof_dsp_post_fw_run(sdev);
index d1a7b98886d13bc8c17efc5b8ee7722147dd354c..84290bbeebddb04315938c1e27103003575efa38 100644 (file)
@@ -70,6 +70,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)
                return ret;
        }
 
+       sdev->fw_state = SOF_FW_BOOT_PREPARE;
+
        /* load the firmware */
        ret = snd_sof_load_firmware(sdev);
        if (ret < 0) {
@@ -79,7 +81,12 @@ static int sof_resume(struct device *dev, bool runtime_resume)
                return ret;
        }
 
-       /* boot the firmware */
+       sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS;
+
+       /*
+        * Boot the firmware. The FW boot status will be modified
+        * in snd_sof_run_firmware() depending on the outcome.
+        */
        ret = snd_sof_run_firmware(sdev);
        if (ret < 0) {
                dev_err(sdev->dev,
@@ -128,6 +135,9 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
        if (!sof_ops(sdev)->suspend)
                return 0;
 
+       if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
+               goto power_down;
+
        /* release trace */
        snd_sof_release_trace(sdev);
 
@@ -165,6 +175,12 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
                         ret);
        }
 
+power_down:
+
+       /* return if the DSP was not probed successfully */
+       if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED)
+               return 0;
+
        /* power down all DSP cores */
        if (runtime_suspend)
                ret = snd_sof_dsp_runtime_suspend(sdev);
@@ -175,6 +191,9 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
                        "error: failed to power down DSP during suspend %d\n",
                        ret);
 
+       /* reset FW state */
+       sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
+
        return ret;
 }
 
index 54dd6d4b4c128eb8ebb74e1e5fc860593a5e6d91..220b35141c34a57bf8d0394e828d2c85609ae778 100644 (file)
@@ -298,6 +298,15 @@ struct snd_sof_ipc_msg {
        bool ipc_complete;
 };
 
+enum snd_sof_fw_state {
+       SOF_FW_BOOT_NOT_STARTED = 0,
+       SOF_FW_BOOT_PREPARE,
+       SOF_FW_BOOT_IN_PROGRESS,
+       SOF_FW_BOOT_FAILED,
+       SOF_FW_BOOT_READY_FAILED, /* firmware booted but fw_ready op failed */
+       SOF_FW_BOOT_COMPLETE,
+};
+
 /*
  * SOF Device Level.
  */
@@ -319,7 +328,7 @@ struct snd_sof_dev {
 
        /* DSP firmware boot */
        wait_queue_head_t boot_wait;
-       u32 boot_complete;
+       enum snd_sof_fw_state fw_state;
        u32 first_boot;
 
        /* work queue in case the probe is implemented in two steps */