cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pm.c (9545B)


      1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
      2//
      3// This file is provided under a dual BSD/GPLv2 license.  When using or
      4// redistributing this file, you may do so under either license.
      5//
      6// Copyright(c) 2018 Intel Corporation. All rights reserved.
      7//
      8// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
      9//
     10
     11#include "ops.h"
     12#include "sof-priv.h"
     13#include "sof-audio.h"
     14
     15/*
     16 * Helper function to determine the target DSP state during
     17 * system suspend. This function only cares about the device
     18 * D-states. Platform-specific substates, if any, should be
     19 * handled by the platform-specific parts.
     20 */
     21static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev)
     22{
     23	u32 target_dsp_state;
     24
     25	switch (sdev->system_suspend_target) {
     26	case SOF_SUSPEND_S5:
     27	case SOF_SUSPEND_S4:
     28		/* DSP should be in D3 if the system is suspending to S3+ */
     29	case SOF_SUSPEND_S3:
     30		/* DSP should be in D3 if the system is suspending to S3 */
     31		target_dsp_state = SOF_DSP_PM_D3;
     32		break;
     33	case SOF_SUSPEND_S0IX:
     34		/*
     35		 * Currently, the only criterion for retaining the DSP in D0
     36		 * is that there are streams that ignored the suspend trigger.
     37		 * Additional criteria such Soundwire clock-stop mode and
     38		 * device suspend latency considerations will be added later.
     39		 */
     40		if (snd_sof_stream_suspend_ignored(sdev))
     41			target_dsp_state = SOF_DSP_PM_D0;
     42		else
     43			target_dsp_state = SOF_DSP_PM_D3;
     44		break;
     45	default:
     46		/* This case would be during runtime suspend */
     47		target_dsp_state = SOF_DSP_PM_D3;
     48		break;
     49	}
     50
     51	return target_dsp_state;
     52}
     53
     54#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
     55static void sof_cache_debugfs(struct snd_sof_dev *sdev)
     56{
     57	struct snd_sof_dfsentry *dfse;
     58
     59	list_for_each_entry(dfse, &sdev->dfsentry_list, list) {
     60
     61		/* nothing to do if debugfs buffer is not IO mem */
     62		if (dfse->type == SOF_DFSENTRY_TYPE_BUF)
     63			continue;
     64
     65		/* cache memory that is only accessible in D0 */
     66		if (dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY)
     67			memcpy_fromio(dfse->cache_buf, dfse->io_mem,
     68				      dfse->size);
     69	}
     70}
     71#endif
     72
     73static int sof_resume(struct device *dev, bool runtime_resume)
     74{
     75	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
     76	const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
     77	const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
     78	u32 old_state = sdev->dsp_power_state.state;
     79	int ret;
     80
     81	/* do nothing if dsp resume callbacks are not set */
     82	if (!runtime_resume && !sof_ops(sdev)->resume)
     83		return 0;
     84
     85	if (runtime_resume && !sof_ops(sdev)->runtime_resume)
     86		return 0;
     87
     88	/* DSP was never successfully started, nothing to resume */
     89	if (sdev->first_boot)
     90		return 0;
     91
     92	/*
     93	 * if the runtime_resume flag is set, call the runtime_resume routine
     94	 * or else call the system resume routine
     95	 */
     96	if (runtime_resume)
     97		ret = snd_sof_dsp_runtime_resume(sdev);
     98	else
     99		ret = snd_sof_dsp_resume(sdev);
    100	if (ret < 0) {
    101		dev_err(sdev->dev,
    102			"error: failed to power up DSP after resume\n");
    103		return ret;
    104	}
    105
    106	/*
    107	 * Nothing further to be done for platforms that support the low power
    108	 * D0 substate. Resume trace and return when resuming from
    109	 * low-power D0 substate
    110	 */
    111	if (!runtime_resume && sof_ops(sdev)->set_power_state &&
    112	    old_state == SOF_DSP_PM_D0) {
    113		ret = sof_fw_trace_resume(sdev);
    114		if (ret < 0)
    115			/* non fatal */
    116			dev_warn(sdev->dev,
    117				 "failed to enable trace after resume %d\n", ret);
    118		return 0;
    119	}
    120
    121	sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
    122
    123	/* load the firmware */
    124	ret = snd_sof_load_firmware(sdev);
    125	if (ret < 0) {
    126		dev_err(sdev->dev,
    127			"error: failed to load DSP firmware after resume %d\n",
    128			ret);
    129		sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
    130		return ret;
    131	}
    132
    133	sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS);
    134
    135	/*
    136	 * Boot the firmware. The FW boot status will be modified
    137	 * in snd_sof_run_firmware() depending on the outcome.
    138	 */
    139	ret = snd_sof_run_firmware(sdev);
    140	if (ret < 0) {
    141		dev_err(sdev->dev,
    142			"error: failed to boot DSP firmware after resume %d\n",
    143			ret);
    144		sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
    145		return ret;
    146	}
    147
    148	/* resume DMA trace */
    149	ret = sof_fw_trace_resume(sdev);
    150	if (ret < 0) {
    151		/* non fatal */
    152		dev_warn(sdev->dev,
    153			 "warning: failed to init trace after resume %d\n",
    154			 ret);
    155	}
    156
    157	/* restore pipelines */
    158	if (tplg_ops->set_up_all_pipelines) {
    159		ret = tplg_ops->set_up_all_pipelines(sdev, false);
    160		if (ret < 0) {
    161			dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret);
    162			return ret;
    163		}
    164	}
    165
    166	/* Notify clients not managed by pm framework about core resume */
    167	sof_resume_clients(sdev);
    168
    169	/* notify DSP of system resume */
    170	if (pm_ops && pm_ops->ctx_restore) {
    171		ret = pm_ops->ctx_restore(sdev);
    172		if (ret < 0)
    173			dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret);
    174	}
    175
    176	return ret;
    177}
    178
    179static int sof_suspend(struct device *dev, bool runtime_suspend)
    180{
    181	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
    182	const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
    183	const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
    184	pm_message_t pm_state;
    185	u32 target_state = 0;
    186	int ret;
    187
    188	/* do nothing if dsp suspend callback is not set */
    189	if (!runtime_suspend && !sof_ops(sdev)->suspend)
    190		return 0;
    191
    192	if (runtime_suspend && !sof_ops(sdev)->runtime_suspend)
    193		return 0;
    194
    195	if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
    196		goto suspend;
    197
    198	/* prepare for streams to be resumed properly upon resume */
    199	if (!runtime_suspend) {
    200		ret = snd_sof_dsp_hw_params_upon_resume(sdev);
    201		if (ret < 0) {
    202			dev_err(sdev->dev,
    203				"error: setting hw_params flag during suspend %d\n",
    204				ret);
    205			return ret;
    206		}
    207	}
    208
    209	target_state = snd_sof_dsp_power_target(sdev);
    210	pm_state.event = target_state;
    211
    212	/* Skip to platform-specific suspend if DSP is entering D0 */
    213	if (target_state == SOF_DSP_PM_D0) {
    214		sof_fw_trace_suspend(sdev, pm_state);
    215		/* Notify clients not managed by pm framework about core suspend */
    216		sof_suspend_clients(sdev, pm_state);
    217		goto suspend;
    218	}
    219
    220	if (tplg_ops->tear_down_all_pipelines)
    221		tplg_ops->tear_down_all_pipelines(sdev, false);
    222
    223	/* suspend DMA trace */
    224	sof_fw_trace_suspend(sdev, pm_state);
    225
    226	/* Notify clients not managed by pm framework about core suspend */
    227	sof_suspend_clients(sdev, pm_state);
    228
    229#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
    230	/* cache debugfs contents during runtime suspend */
    231	if (runtime_suspend)
    232		sof_cache_debugfs(sdev);
    233#endif
    234	/* notify DSP of upcoming power down */
    235	if (pm_ops && pm_ops->ctx_save) {
    236		ret = pm_ops->ctx_save(sdev);
    237		if (ret == -EBUSY || ret == -EAGAIN) {
    238			/*
    239			 * runtime PM has logic to handle -EBUSY/-EAGAIN so
    240			 * pass these errors up
    241			 */
    242			dev_err(sdev->dev, "ctx_save IPC error during suspend: %d\n", ret);
    243			return ret;
    244		} else if (ret < 0) {
    245			/* FW in unexpected state, continue to power down */
    246			dev_warn(sdev->dev, "ctx_save IPC error: %d, proceeding with suspend\n",
    247				 ret);
    248		}
    249	}
    250
    251suspend:
    252
    253	/* return if the DSP was not probed successfully */
    254	if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED)
    255		return 0;
    256
    257	/* platform-specific suspend */
    258	if (runtime_suspend)
    259		ret = snd_sof_dsp_runtime_suspend(sdev);
    260	else
    261		ret = snd_sof_dsp_suspend(sdev, target_state);
    262	if (ret < 0)
    263		dev_err(sdev->dev,
    264			"error: failed to power down DSP during suspend %d\n",
    265			ret);
    266
    267	/* Do not reset FW state if DSP is in D0 */
    268	if (target_state == SOF_DSP_PM_D0)
    269		return ret;
    270
    271	/* reset FW state */
    272	sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
    273	sdev->enabled_cores_mask = 0;
    274
    275	return ret;
    276}
    277
    278int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev)
    279{
    280	const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
    281
    282	/* Notify DSP of upcoming power down */
    283	if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save)
    284		return pm_ops->ctx_save(sdev);
    285
    286	return 0;
    287}
    288
    289int snd_sof_runtime_suspend(struct device *dev)
    290{
    291	return sof_suspend(dev, true);
    292}
    293EXPORT_SYMBOL(snd_sof_runtime_suspend);
    294
    295int snd_sof_runtime_idle(struct device *dev)
    296{
    297	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
    298
    299	return snd_sof_dsp_runtime_idle(sdev);
    300}
    301EXPORT_SYMBOL(snd_sof_runtime_idle);
    302
    303int snd_sof_runtime_resume(struct device *dev)
    304{
    305	return sof_resume(dev, true);
    306}
    307EXPORT_SYMBOL(snd_sof_runtime_resume);
    308
    309int snd_sof_resume(struct device *dev)
    310{
    311	return sof_resume(dev, false);
    312}
    313EXPORT_SYMBOL(snd_sof_resume);
    314
    315int snd_sof_suspend(struct device *dev)
    316{
    317	return sof_suspend(dev, false);
    318}
    319EXPORT_SYMBOL(snd_sof_suspend);
    320
    321int snd_sof_prepare(struct device *dev)
    322{
    323	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
    324	const struct sof_dev_desc *desc = sdev->pdata->desc;
    325
    326	/* will suspend to S3 by default */
    327	sdev->system_suspend_target = SOF_SUSPEND_S3;
    328
    329	/*
    330	 * if the firmware is crashed or boot failed then we try to aim for S3
    331	 * to reboot the firmware
    332	 */
    333	if (sdev->fw_state == SOF_FW_CRASHED ||
    334	    sdev->fw_state == SOF_FW_BOOT_FAILED)
    335		return 0;
    336
    337	if (!desc->use_acpi_target_states)
    338		return 0;
    339
    340#if defined(CONFIG_ACPI)
    341	switch (acpi_target_system_state()) {
    342	case ACPI_STATE_S0:
    343		sdev->system_suspend_target = SOF_SUSPEND_S0IX;
    344		break;
    345	case ACPI_STATE_S1:
    346	case ACPI_STATE_S2:
    347	case ACPI_STATE_S3:
    348		sdev->system_suspend_target = SOF_SUSPEND_S3;
    349		break;
    350	case ACPI_STATE_S4:
    351		sdev->system_suspend_target = SOF_SUSPEND_S4;
    352		break;
    353	case ACPI_STATE_S5:
    354		sdev->system_suspend_target = SOF_SUSPEND_S5;
    355		break;
    356	default:
    357		break;
    358	}
    359#endif
    360
    361	return 0;
    362}
    363EXPORT_SYMBOL(snd_sof_prepare);
    364
    365void snd_sof_complete(struct device *dev)
    366{
    367	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
    368
    369	sdev->system_suspend_target = SOF_SUSPEND_NONE;
    370}
    371EXPORT_SYMBOL(snd_sof_complete);