Unverified Commit 6ca5cecb authored by Ranjani Sridharan's avatar Ranjani Sridharan Committed by Mark Brown
Browse files

ASoC: SOF: Introduce state machine for FW boot



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: default avatarCurtis Malainey <cujomalainey@chromium.org>
Reviewed-by: default avatarDaniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: default avatarPierre-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: default avatarMark Brown <broonie@kernel.org>
parent cf5629e4
Loading
Loading
Loading
Loading
+49 −1
Original line number Diff line number Diff line
@@ -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 */
+0 −1
Original line number Diff line number Diff line
@@ -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,
+2 −2
Original line number Diff line number Diff line
@@ -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,
+5 −12
Original line number Diff line number Diff line
@@ -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);
+13 −6
Original line number Diff line number Diff line
@@ -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;
	}

	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);
Loading