diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-06-02 13:17:23 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-06-02 13:17:23 -0700 |
commit | 355ba37d756c38962fe9bb616f5f48eb12a7e11e (patch) | |
tree | 74bb0fc617a9a9fb5660454c205bf3d08d95896a /drivers | |
parent | a5a82e0a5918fb2ae27b80d3b9c954b16ae6a7b7 (diff) | |
parent | a34024d98eeaa78e2cd22017180df778800b83fc (diff) | |
download | linux-355ba37d756c38962fe9bb616f5f48eb12a7e11e.tar.bz2 |
Merge tag 'pm-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management updates from Rafael Wysocki:
"These rework the system-wide PM driver flags, make runtime switching
of cpuidle governors easier, improve the user space hibernation
interface code, add intel-speed-select interface documentation, add
more debug messages to the ACPI code handling suspend to idle, update
the cpufreq core and drivers, fix a minor issue in the cpuidle core
and update two cpuidle drivers, improve the PM-runtime framework,
update the Intel RAPL power capping driver, update devfreq core and
drivers, and clean up the cpupower utility.
Specifics:
- Rework the system-wide PM driver flags to make them easier to
understand and use and update their documentation (Rafael Wysocki,
Alan Stern).
- Allow cpuidle governors to be switched at run time regardless of
the kernel configuration and update the related documentation
accordingly (Hanjun Guo).
- Improve the resume device handling in the user space hibernarion
interface code (Domenico Andreoli).
- Document the intel-speed-select sysfs interface (Srinivas
Pandruvada).
- Make the ACPI code handing suspend to idle print more debug
messages to help diagnose issues with it (Rafael Wysocki).
- Fix a helper routine in the cpufreq core and correct a typo in the
struct cpufreq_driver kerneldoc comment (Rafael Wysocki, Wang
Wenhu).
- Update cpufreq drivers:
- Make the intel_pstate driver start in the passive mode by
default on systems without HWP (Rafael Wysocki).
- Add i.MX7ULP support to the imx-cpufreq-dt driver and add
i.MX7ULP to the cpufreq-dt-platdev blacklist (Peng Fan).
- Convert the qoriq cpufreq driver to a platform one, make the
platform code create a suitable device object for it and add
platform dependencies to it (Mian Yousaf Kaukab, Geert
Uytterhoeven).
- Fix wrong compatible binding in the qcom driver (Ansuel Smith).
- Build the omap driver by default for ARCH_OMAP2PLUS (Anders
Roxell).
- Add r8a7742 SoC support to the dt cpufreq driver (Lad
Prabhakar).
- Update cpuidle core and drivers:
- Fix three reference count leaks in error code paths in the
cpuidle core (Qiushi Wu).
- Convert Qualcomm SPM to a generic cpuidle driver (Stephan
Gerhold).
- Fix up the execution order when entering a domain idle state in
the PSCI driver (Ulf Hansson).
- Fix a reference counting issue related to clock management and
clean up two oddities in the PM-runtime framework (Rafael Wysocki,
Andy Shevchenko).
- Add ElkhartLake support to the Intel RAPL power capping driver and
remove an unused local MSR definition from it (Jacob Pan, Sumeet
Pawnikar).
- Update devfreq core and drivers:
- Replace strncpy() with strscpy() in the devfreq core and use
lockdep asserts instead of manual checks for a locked mutex in
it (Dmitry Osipenko, Krzysztof Kozlowski).
- Add a generic imx bus scaling driver and make it register an
interconnect device (Leonard Crestez, Gustavo A. R. Silva).
- Make the cpufreq notifier in the tegra30 driver take boosting
into account and delete an unuseful error message from that
driver (Dmitry Osipenko, Markus Elfring).
- Remove unneeded semicolon from the cpupower code (Zou Wei)"
* tag 'pm-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (51 commits)
cpuidle: Fix three reference count leaks
PM: runtime: Replace pm_runtime_callbacks_present()
PM / devfreq: Use lockdep asserts instead of manual checks for locked mutex
PM / devfreq: imx-bus: Fix inconsistent IS_ERR and PTR_ERR
PM / devfreq: Replace strncpy with strscpy
PM / devfreq: imx: Register interconnect device
PM / devfreq: Add generic imx bus scaling driver
PM / devfreq: tegra30: Delete an error message in tegra_devfreq_probe()
PM / devfreq: tegra30: Make CPUFreq notifier to take into account boosting
PM: hibernate: Restrict writes to the resume device
PM: runtime: clk: Fix clk_pm_runtime_get() error path
cpuidle: Convert Qualcomm SPM driver to a generic CPUidle driver
ACPI: EC: PM: s2idle: Extend GPE dispatching debug message
ACPI: PM: s2idle: Print type of wakeup debug messages
powercap: RAPL: remove unused local MSR define
PM: runtime: Make clear what we do when conditions are wrong in rpm_suspend()
Documentation: admin-guide: pm: Document intel-speed-select
PM: hibernate: Split off snapshot dev option
PM: hibernate: Incorporate concurrency handling
Documentation: ABI: make current_governer_ro as a candidate for removal
...
Diffstat (limited to 'drivers')
43 files changed, 632 insertions, 533 deletions
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index dee999938213..5e2bfbcf526f 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -1041,7 +1041,7 @@ static int acpi_lpss_do_suspend_late(struct device *dev) { int ret; - if (dev_pm_smart_suspend_and_suspended(dev)) + if (dev_pm_skip_suspend(dev)) return 0; ret = pm_generic_suspend_late(dev); @@ -1093,6 +1093,9 @@ static int acpi_lpss_resume_early(struct device *dev) if (pdata->dev_desc->resume_from_noirq) return 0; + if (dev_pm_skip_resume(dev)) + return 0; + return acpi_lpss_do_resume_early(dev); } @@ -1102,12 +1105,9 @@ static int acpi_lpss_resume_noirq(struct device *dev) int ret; /* Follow acpi_subsys_resume_noirq(). */ - if (dev_pm_may_skip_resume(dev)) + if (dev_pm_skip_resume(dev)) return 0; - if (dev_pm_smart_suspend_and_suspended(dev)) - pm_runtime_set_active(dev); - ret = pm_generic_resume_noirq(dev); if (ret) return ret; @@ -1169,7 +1169,7 @@ static int acpi_lpss_poweroff_late(struct device *dev) { struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - if (dev_pm_smart_suspend_and_suspended(dev)) + if (dev_pm_skip_suspend(dev)) return 0; if (pdata->dev_desc->resume_from_noirq) @@ -1182,7 +1182,7 @@ static int acpi_lpss_poweroff_noirq(struct device *dev) { struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - if (dev_pm_smart_suspend_and_suspended(dev)) + if (dev_pm_skip_suspend(dev)) return 0; if (pdata->dev_desc->resume_from_noirq) { diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index 33a4bcdaa4d7..7d45cce0c3c1 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -624,7 +624,7 @@ static int acpi_tad_probe(struct platform_device *pdev) */ device_init_wakeup(dev, true); dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND | - DPM_FLAG_LEAVE_SUSPENDED); + DPM_FLAG_MAY_SKIP_RESUME); /* * The platform bus type layer tells the ACPI PM domain powers up the * device, so set the runtime PM status of it to "active". diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 5832bc10aca8..b44b12a931e7 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -1084,7 +1084,7 @@ int acpi_subsys_suspend_late(struct device *dev) { int ret; - if (dev_pm_smart_suspend_and_suspended(dev)) + if (dev_pm_skip_suspend(dev)) return 0; ret = pm_generic_suspend_late(dev); @@ -1100,10 +1100,8 @@ int acpi_subsys_suspend_noirq(struct device *dev) { int ret; - if (dev_pm_smart_suspend_and_suspended(dev)) { - dev->power.may_skip_resume = true; + if (dev_pm_skip_suspend(dev)) return 0; - } ret = pm_generic_suspend_noirq(dev); if (ret) @@ -1116,8 +1114,8 @@ int acpi_subsys_suspend_noirq(struct device *dev) * acpi_subsys_complete() to take care of fixing up the device's state * anyway, if need be. */ - dev->power.may_skip_resume = device_may_wakeup(dev) || - !device_can_wakeup(dev); + if (device_can_wakeup(dev) && !device_may_wakeup(dev)) + dev->power.may_skip_resume = false; return 0; } @@ -1129,17 +1127,9 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_noirq); */ static int acpi_subsys_resume_noirq(struct device *dev) { - if (dev_pm_may_skip_resume(dev)) + if (dev_pm_skip_resume(dev)) return 0; - /* - * Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend - * during system suspend, so update their runtime PM status to "active" - * as they will be put into D0 going forward. - */ - if (dev_pm_smart_suspend_and_suspended(dev)) - pm_runtime_set_active(dev); - return pm_generic_resume_noirq(dev); } @@ -1153,7 +1143,12 @@ static int acpi_subsys_resume_noirq(struct device *dev) */ static int acpi_subsys_resume_early(struct device *dev) { - int ret = acpi_dev_resume(dev); + int ret; + + if (dev_pm_skip_resume(dev)) + return 0; + + ret = acpi_dev_resume(dev); return ret ? ret : pm_generic_resume_early(dev); } @@ -1218,7 +1213,7 @@ static int acpi_subsys_poweroff_late(struct device *dev) { int ret; - if (dev_pm_smart_suspend_and_suspended(dev)) + if (dev_pm_skip_suspend(dev)) return 0; ret = pm_generic_poweroff_late(dev); @@ -1234,7 +1229,7 @@ static int acpi_subsys_poweroff_late(struct device *dev) */ static int acpi_subsys_poweroff_noirq(struct device *dev) { - if (dev_pm_smart_suspend_and_suspended(dev)) + if (dev_pm_skip_suspend(dev)) return 0; return pm_generic_poweroff_noirq(dev); diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 1af2125e17d5..c44448ab19ef 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -2017,7 +2017,7 @@ bool acpi_ec_dispatch_gpe(void) */ ret = acpi_dispatch_gpe(NULL, first_ec->gpe); if (ret == ACPI_INTERRUPT_HANDLED) { - pm_pr_dbg("EC GPE dispatched\n"); + pm_pr_dbg("ACPI EC GPE dispatched\n"); /* Flush the event and query workqueues. */ acpi_ec_flush_work(); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index fd9d4e8318e9..31c9d0c8ae11 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -992,23 +992,31 @@ static bool acpi_s2idle_wake(void) * wakeup is pending anyway and the SCI is not the source of * it). */ - if (irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) + if (irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) { + pm_pr_dbg("Wakeup unrelated to ACPI SCI\n"); return true; + } /* * If the status bit of any enabled fixed event is set, the * wakeup is regarded as valid. */ - if (acpi_any_fixed_event_status_set()) + if (acpi_any_fixed_event_status_set()) { + pm_pr_dbg("ACPI fixed event wakeup\n"); return true; + } /* Check wakeups from drivers sharing the SCI. */ - if (acpi_check_wakeup_handlers()) + if (acpi_check_wakeup_handlers()) { + pm_pr_dbg("ACPI custom handler wakeup\n"); return true; + } /* Check non-EC GPE wakeups and dispatch the EC GPE. */ - if (acpi_ec_dispatch_gpe()) + if (acpi_ec_dispatch_gpe()) { + pm_pr_dbg("ACPI non-EC GPE wakeup\n"); return true; + } /* * Cancel the SCI wakeup and process all pending events in case @@ -1027,8 +1035,10 @@ static bool acpi_s2idle_wake(void) * are pending here, they must be resulting from the processing * of EC events above or coming from somewhere else. */ - if (pm_wakeup_pending()) + if (pm_wakeup_pending()) { + pm_pr_dbg("Wakeup after ACPI Notify sync\n"); return true; + } rearm_wake_irq(acpi_sci_irq); } diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 0e07e17c2def..bb98b813554f 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -562,72 +562,26 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd) /*------------------------- Resume routines -------------------------*/ /** - * suspend_event - Return a "suspend" message for given "resume" one. - * @resume_msg: PM message representing a system-wide resume transition. - */ -static pm_message_t suspend_event(pm_message_t resume_msg) -{ - switch (resume_msg.event) { - case PM_EVENT_RESUME: - return PMSG_SUSPEND; - case PM_EVENT_THAW: - case PM_EVENT_RESTORE: - return PMSG_FREEZE; - case PM_EVENT_RECOVER: - return PMSG_HIBERNATE; - } - return PMSG_ON; -} - -/** - * dev_pm_may_skip_resume - System-wide device resume optimization check. + * dev_pm_skip_resume - System-wide device resume optimization check. * @dev: Target device. * - * Checks whether or not the device may be left in suspend after a system-wide - * transition to the working state. + * Return: + * - %false if the transition under way is RESTORE. + * - Return value of dev_pm_skip_suspend() if the transition under way is THAW. + * - The logical negation of %power.must_resume otherwise (that is, when the + * transition under way is RESUME). */ -bool dev_pm_may_skip_resume(struct device *dev) +bool dev_pm_skip_resume(struct device *dev) { - return !dev->power.must_resume && pm_transition.event != PM_EVENT_RESTORE; -} - -static pm_callback_t dpm_subsys_resume_noirq_cb(struct device *dev, - pm_message_t state, - const char **info_p) -{ - pm_callback_t callback; - const char *info; - - if (dev->pm_domain) { - info = "noirq power domain "; - callback = pm_noirq_op(&dev->pm_domain->ops, state); - } else if (dev->type && dev->type->pm) { - info = "noirq type "; - callback = pm_noirq_op(dev->type->pm, state); - } else if (dev->class && dev->class->pm) { - info = "noirq class "; - callback = pm_noirq_op(dev->class->pm, state); - } else if (dev->bus && dev->bus->pm) { - info = "noirq bus "; - callback = pm_noirq_op(dev->bus->pm, state); - } else { - return NULL; - } + if (pm_transition.event == PM_EVENT_RESTORE) + return false; - if (info_p) - *info_p = info; + if (pm_transition.event == PM_EVENT_THAW) + return dev_pm_skip_suspend(dev); - return callback; + return !dev->power.must_resume; } -static pm_callback_t dpm_subsys_suspend_noirq_cb(struct device *dev, - pm_message_t state, - const char **info_p); - -static pm_callback_t dpm_subsys_suspend_late_cb(struct device *dev, - pm_message_t state, - const char **info_p); - /** * device_resume_noirq - Execute a "noirq resume" callback for given device. * @dev: Device to handle. @@ -639,8 +593,8 @@ static pm_callback_t dpm_subsys_suspend_late_cb(struct device *dev, */ static int device_resume_noirq(struct device *dev, pm_message_t state, bool async) { - pm_callback_t callback; - const char *info; + pm_callback_t callback = NULL; + const char *info = NULL; bool skip_resume; int error = 0; @@ -656,37 +610,41 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn if (!dpm_wait_for_superior(dev, async)) goto Out; - skip_resume = dev_pm_may_skip_resume(dev); + skip_resume = dev_pm_skip_resume(dev); + /* + * If the driver callback is skipped below or by the middle layer + * callback and device_resume_early() also skips the driver callback for + * this device later, it needs to appear as "suspended" to PM-runtime, + * so change its status accordingly. + * + * Otherwise, the device is going to be resumed, so set its PM-runtime + * status to "active", but do that only if DPM_FLAG_SMART_SUSPEND is set + * to avoid confusing drivers that don't use it. + */ + if (skip_resume) + pm_runtime_set_suspended(dev); + else if (dev_pm_skip_suspend(dev)) + pm_runtime_set_active(dev); - callback = dpm_subsys_resume_noirq_cb(dev, state, &info); + if (dev->pm_domain) { + info = "noirq power domain "; + callback = pm_noirq_op(&dev->pm_domain->ops, state); + } else if (dev->type && dev->type->pm) { + info = "noirq type "; + callback = pm_noirq_op(dev->type->pm, state); + } else if (dev->class && dev->class->pm) { + info = "noirq class "; + callback = pm_noirq_op(dev->class->pm, state); + } else if (dev->bus && dev->bus->pm) { + info = "noirq bus "; + callback = pm_noirq_op(dev->bus->pm, state); + } if (callback) goto Run; if (skip_resume) goto Skip; - if (dev_pm_smart_suspend_and_suspended(dev)) { - pm_message_t suspend_msg = suspend_event(state); - - /* - * If "freeze" callbacks have been skipped during a transition - * related to hibernation, the subsequent "thaw" callbacks must - * be skipped too or bad things may happen. Otherwise, resume - * callbacks are going to be run for the device, so its runtime - * PM status must be changed to reflect the new state after the - * transition under way. - */ - if (!dpm_subsys_suspend_late_cb(dev, suspend_msg, NULL) && - !dpm_subsys_suspend_noirq_cb(dev, suspend_msg, NULL)) { - if (state.event == PM_EVENT_THAW) { - skip_resume = true; - goto Skip; - } else { - pm_runtime_set_active(dev); - } - } - } - if (dev->driver && dev->driver->pm) { info = "noirq driver "; callback = pm_noirq_op(dev->driver->pm, state); @@ -698,20 +656,6 @@ Run: Skip: dev->power.is_noirq_suspended = false; - if (skip_resume) { - /* Make the next phases of resume skip the device. */ - dev->power.is_late_suspended = false; - dev->power.is_suspended = false; - /* - * The device is going to be left in suspend, but it might not - * have been in runtime suspend before the system suspended, so - * its runtime PM status needs to be updated to avoid confusing - * the runtime PM framework when runtime PM is enabled for the - * device again. - */ - pm_runtime_set_suspended(dev); - } - Out: complete_all(&dev->power.completion); TRACE_RESUME(error); @@ -810,35 +754,6 @@ void dpm_resume_noirq(pm_message_t state) cpuidle_resume(); } -static pm_callback_t dpm_subsys_resume_early_cb(struct device *dev, - pm_message_t state, - const char **info_p) -{ - pm_callback_t callback; - const char *info; - - if (dev->pm_domain) { - info = "early power domain "; - callback = pm_late_early_op(&dev->pm_domain->ops, state); - } else if (dev->type && dev->type->pm) { - info = "early type "; - callback = pm_late_early_op(dev->type->pm, state); - } else if (dev->class && dev->class->pm) { - info = "early class "; - callback = pm_late_early_op(dev->class->pm, state); - } else if (dev->bus && dev->bus->pm) { - info = "early bus "; - callback = pm_late_early_op(dev->bus->pm, state); - } else { - return NULL; - } - - if (info_p) - *info_p = info; - - return callback; -} - /** * device_resume_early - Execute an "early resume" callback for given device. * @dev: Device to handle. @@ -849,8 +764,8 @@ static pm_callback_t dpm_subsys_resume_early_cb(struct device *dev, */ static int device_resume_early(struct device *dev, pm_message_t state, bool async) { - pm_callback_t callback; - const char *info; + pm_callback_t callback = NULL; + const char *info = NULL; int error = 0; TRACE_DEVICE(dev); @@ -865,17 +780,37 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn if (!dpm_wait_for_superior(dev, async)) goto Out; - callback = dpm_subsys_resume_early_cb(dev, state, &info); + if (dev->pm_domain) { + info = "early power domain "; + callback = pm_late_early_op(&dev->pm_domain->ops, state); + } else if (dev->type && dev->type->pm) { + info = "early type "; + callback = pm_late_early_op(dev->type->pm, state); + } else if (dev->class && dev->class->pm) { + info = "early class "; + callback = pm_late_early_op(dev->class->pm, state); + } else if (dev->bus && dev->bus->pm) { + info = "early bus "; + callback = pm_late_early_op(dev->bus->pm, state); + } + if (callback) + goto Run; + + if (dev_pm_skip_resume(dev)) + goto Skip; - if (!callback && dev->driver && dev->driver->pm) { + if (dev->driver && dev->driver->pm) { info = "early driver "; callback = pm_late_early_op(dev->driver->pm, state); } +Run: error = dpm_run_callback(callback, dev, state, info); + +Skip: dev->power.is_late_suspended = false; - Out: +Out: TRACE_RESUME(error); pm_runtime_enable(dev); @@ -1245,61 +1180,6 @@ static void dpm_superior_set_must_resume(struct device *dev) device_links_read_unlock(idx); } -static pm_callback_t dpm_subsys_suspend_noirq_cb(struct device *dev, - pm_message_t state, - const char **info_p) -{ - pm_callback_t callback; - const char *info; - - if (dev->pm_domain) { - info = "noirq power domain "; - callback = pm_noirq_op(&dev->pm_domain->ops, state); - } else if (dev->type && dev->type->pm) { - info = "noirq type "; - callback = pm_noirq_op(dev->type->pm, state); - } else if (dev->class && dev->class->pm) { - info = "noirq class "; - callback = pm_noirq_op(dev->class->pm, state); - } else if (dev->bus && dev->bus->pm) { - info = "noirq bus "; - callback = pm_noirq_op(dev->bus->pm, state); - } else { - return NULL; - } - - if (info_p) - *info_p = info; - - return callback; -} - -static bool device_must_resume(struct device *dev, pm_message_t state, - bool no_subsys_suspend_noirq) -{ - pm_message_t resume_msg = resume_event(state); - - /* - * If all of the device driver's "noirq", "late" and "early" callbacks - * are invoked directly by the core, the decision to allow the device to - * stay in suspend can be based on its current runtime PM status and its - * wakeup settings. - */ - if (no_subsys_suspend_noirq && - !dpm_subsys_suspend_late_cb(dev, state, NULL) && - !dpm_subsys_resume_early_cb(dev, resume_msg, NULL) && - !dpm_subsys_resume_noirq_cb(dev, resume_msg, NULL)) - return !pm_runtime_status_suspended(dev) && - (resume_msg.event != PM_EVENT_RESUME || - (device_can_wakeup(dev) && !device_may_wakeup(dev))); - - /* - * The only safe strategy here is to require that if the device may not - * be left in suspend, resume callbacks must be invoked for it. - */ - return !dev->power.may_skip_resume; -} - /** * __device_suspend_noirq - Execute a "noirq suspend" callback for given device. * @dev: Device to handle. @@ -1311,9 +1191,8 @@ static bool device_must_resume(struct device *dev, pm_message_t state, */ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool async) { - pm_callback_t callback; - const char *info; - bool no_subsys_cb = false; + pm_callback_t callback = NULL; + const char *info = NULL; int error = 0; TRACE_DEVICE(dev); @@ -1327,13 +1206,23 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a if (dev->power.syscore || dev->power.direct_complete) goto Complete; - callback = dpm_subsys_suspend_noirq_cb(dev, state, &info); + if (dev->pm_domain) { + info = "noirq power domain "; + callback = pm_noirq_op(&dev->pm_domain->ops, state); + } else if (dev->type && dev->type->pm) { + info = "noirq type "; + callback = pm_noirq_op(dev->type->pm, state); + } else if (dev->class && dev->class->pm) { + info = "noirq class "; + callback = pm_noirq_op(dev->class->pm, state); + } else if (dev->bus && dev->bus->pm) { + info = "noirq bus "; + callback = pm_noirq_op(dev->bus->pm, state); + } if (callback) goto Run; - no_subsys_cb = !dpm_subsys_suspend_late_cb(dev, state, NULL); - - if (dev_pm_smart_suspend_and_suspended(dev) && no_subsys_cb) + if (dev_pm_skip_suspend(dev)) goto Skip; if (dev->driver && dev->driver->pm) { @@ -1351,13 +1240,16 @@ Run: Skip: dev->power.is_noirq_suspended = true; - if (dev_pm_test_driver_flags(dev, DPM_FLAG_LEAVE_SUSPENDED)) { - dev->power.must_resume = dev->power.must_resume || - atomic_read(&dev->power.usage_count) > 1 || - device_must_resume(dev, state, no_subsys_cb); - } else { + /* + * Skipping the resume of devices that were in use right before the + * system suspend (as indicated by their PM-runtime usage counters) + * would be suboptimal. Also resume them if doing that is not allowed + * to be skipped. + */ + if (atomic_read(&dev->power.usage_count) > 1 || + !(dev_pm_test_driver_flags(dev, DPM_FLAG_MAY_SKIP_RESUME) && + dev->power.may_skip_resume)) dev->power.must_resume = true; - } if (dev->power.must_resume) dpm_superior_set_must_resume(dev); @@ -1474,35 +1366,6 @@ static void dpm_propagate_wakeup_to_parent(struct device *dev) spin_unlock_irq(&parent->power.lock); } -static pm_callback_t dpm_subsys_suspend_late_cb(struct device *dev, - pm_message_t state, - const char **info_p) -{ - pm_callback_t callback; - const char *info; - - if (dev->pm_domain) { - info = "late power domain "; - callback = pm_late_early_op(&dev->pm_domain->ops, state); - } else if (dev->type && dev->type->pm) { - info = "late type "; - callback = pm_late_early_op(dev->type->pm, state); - } else if (dev->class && dev->class->pm) { - info = "late class "; - callback = pm_late_early_op(dev->class->pm, state); - } else if (dev->bus && dev->bus->pm) { - info = "late bus "; - callback = pm_late_early_op(dev->bus->pm, state); - } else { - return NULL; - } - - if (info_p) - *info_p = info; - - return callback; -} - /** * __device_suspend_late - Execute a "late suspend" callback for given device. * @dev: Device to handle. @@ -1513,8 +1376,8 @@ static pm_callback_t dpm_subsys_suspend_late_cb(struct device *dev, */ static int __device_suspend_late(struct device *dev, pm_message_t state, bool async) { - pm_callback_t callback; - const char *info; + pm_callback_t callback = NULL; + const char *info = NULL; int error = 0; TRACE_DEVICE(dev); @@ -1535,12 +1398,23 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as if (dev->power.syscore || dev->power.direct_complete) goto Complete; - callback = dpm_subsys_suspend_late_cb(dev, state, &info); + if (dev->pm_domain) { + info = "late power domain "; + callback = pm_late_early_op(&dev->pm_domain->ops, state); + } else if (dev->type && dev->type->pm) { + info = "late type "; + callback = pm_late_early_op(dev->type->pm, state); + } else if (dev->class && dev->class->pm) { + info = "late class "; + callback = pm_late_early_op(dev->class->pm, state); + } else if (dev->bus && dev->bus->pm) { + info = "late bus "; + callback = pm_late_early_op(dev->bus->pm, state); + } if (callback) goto Run; - if (dev_pm_smart_suspend_and_suspended(dev) && - !dpm_subsys_suspend_noirq_cb(dev, state, NULL)) + if (dev_pm_skip_suspend(dev)) goto Skip; if (dev->driver && dev->driver->pm) { @@ -1766,7 +1640,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) dev->power.direct_complete = false; } - dev->power.may_skip_resume = false; + dev->power.may_skip_resume = true; dev->power.must_resume = false; dpm_watchdog_set(&wd, dev); @@ -1970,7 +1844,7 @@ unlock: spin_lock_irq(&dev->power.lock); dev->power.direct_complete = state.event == PM_EVENT_SUSPEND && (ret > 0 || dev->power.no_pm_callbacks) && - !dev_pm_test_driver_flags(dev, DPM_FLAG_NEVER_SKIP); + !dev_pm_test_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE); spin_unlock_irq(&dev->power.lock); return 0; } @@ -2128,7 +2002,7 @@ void device_pm_check_callbacks(struct device *dev) spin_unlock_irq(&dev->power.lock); } -bool dev_pm_smart_suspend_and_suspended(struct device *dev) +bool dev_pm_skip_suspend(struct device *dev) { return dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) && pm_runtime_status_suspended(dev); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 99c7da112c95..9f62790f644c 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -523,13 +523,11 @@ static int rpm_suspend(struct device *dev, int rpmflags) repeat: retval = rpm_check_suspend_allowed(dev); - if (retval < 0) - ; /* Conditions are wrong. */ + goto out; /* Conditions are wrong. */ /* Synchronous suspends are not allowed in the RPM_RESUMING state. */ - else if (dev->power.runtime_status == RPM_RESUMING && - !(rpmflags & RPM_ASYNC)) + if (dev->power.runtime_status == RPM_RESUMING && !(rpmflags & RPM_ASYNC)) retval = -EAGAIN; if (retval) goto out; diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 2b99fe1eb207..24d25cf8ab14 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -666,7 +666,7 @@ int dpm_sysfs_add(struct device *dev) if (rc) return rc; - if (pm_runtime_callbacks_present(dev)) { + if (!pm_runtime_has_no_callbacks(dev)) { rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group); if (rc) goto err_out; @@ -709,7 +709,7 @@ int dpm_sysfs_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid) if (rc) return rc; - if (pm_runtime_callbacks_present(dev)) { + if (!pm_runtime_has_no_callbacks(dev)) { rc = sysfs_group_change_owner( &dev->kobj, &pm_runtime_attr_group, kuid, kgid); if (rc) diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index d5946f7486d6..374afcab89af 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -95,6 +95,7 @@ struct clockgen { }; static struct clockgen clockgen; +static bool add_cpufreq_dev __initdata; static void cg_out(struct clockgen *cg, u32 val, u32 __iomem *reg) { @@ -1019,7 +1020,7 @@ static void __init create_muxes(struct clockgen *cg) } } -static void __init clockgen_init(struct device_node *np); +static void __init _clockgen_init(struct device_node *np, bool legacy); /* * Legacy nodes may get probed before the parent clockgen node. @@ -1030,7 +1031,7 @@ static void __init clockgen_init(struct device_node *np); static void __init legacy_init_clockgen(struct device_node *np) { if (!clockgen.node) - clockgen_init(of_get_parent(np)); + _clockgen_init(of_get_parent(np), true); } /* Legacy node */ @@ -1447,7 +1448,7 @@ static bool __init has_erratum_a4510(void) } #endif -static void __init clockgen_init(struct device_node *np) +static void __init _clockgen_init(struct device_node *np, bool legacy) { int i, ret; bool is_old_ls1021a = false; @@ -1516,12 +1517,35 @@ static void __init clockgen_init(struct device_node *np) __func__, np, ret); } + /* Don't create cpufreq device for legacy clockgen blocks */ + add_cpufreq_dev = !legacy; + return; err: iounmap(clockgen.regs); clockgen.regs = NULL; } +static void __init clockgen_init(struct device_node *np) +{ + _clockgen_init(np, false); +} + +static int __init clockgen_cpufreq_init(void) +{ + struct platform_device *pdev; + + if (add_cpufreq_dev) { + pdev = platform_device_register_simple("qoriq-cpufreq", -1, + NULL, 0); + if (IS_ERR(pdev)) + pr_err("Couldn't register qoriq-cpufreq err=%ld\n", + PTR_ERR(pdev)); + } + return 0; +} +device_initcall(clockgen_cpufreq_init); + CLK_OF_DECLARE(qoriq_clockgen_1, "fsl,qoriq-clockgen-1.0", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_2, "fsl,qoriq-clockgen-2.0", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_b4420, "fsl,b4420-clockgen", clockgen_init); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 2dfb30b963c4..407f6919604c 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -114,7 +114,11 @@ static int clk_pm_runtime_get(struct clk_core *core) return 0; ret = pm_runtime_get_sync(core->dev); - return ret < 0 ? ret : 0; + if (ret < 0) { + pm_runtime_put_noidle(core->dev); + return ret; + } + return 0; } static void clk_pm_runtime_put(struct clk_core *core) diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index c3e6bd59e920..e91750132552 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -323,7 +323,8 @@ endif config QORIQ_CPUFREQ tristate "CPU frequency scaling driver for Freescale QorIQ SoCs" - depends on OF && COMMON_CLK && (PPC_E500MC || ARM || ARM64) + depends on OF && COMMON_CLK + depends on PPC_E500MC || SOC_LS1021A || ARCH_LAYERSCAPE || COMPILE_TEST select CLK_QORIQ help This adds the CPUFreq driver support for Freescale QorIQ SoCs diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 15c1a1231516..9481292981f0 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -317,6 +317,7 @@ config ARM_TEGRA186_CPUFREQ config ARM_TI_CPUFREQ bool "Texas Instruments CPUFreq support" depends on ARCH_OMAP2PLUS + default ARCH_OMAP2PLUS help This driver enables valid OPPs on the running platform based on values contained within the SoC in use. Enable this in order to diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index cb9db16bea61..e8e20fef400b 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -53,6 +53,7 @@ static const struct of_device_id whitelist[] __initconst = { { .compatible = "renesas,r7s72100", }, { .compatible = "renesas,r8a73a4", }, { .compatible = "renesas,r8a7740", }, + { .compatible = "renesas,r8a7742", }, { .compatible = "renesas,r8a7743", }, { .compatible = "renesas,r8a7744", }, { .compatible = "renesas,r8a7745", }, @@ -105,6 +106,7 @@ static const struct of_device_id blacklist[] __initconst = { { .compatible = "calxeda,highbank", }, { .compatible = "calxeda,ecx-2000", }, + { .compatible = "fsl,imx7ulp", }, { .compatible = "fsl,imx7d", }, { .compatible = "fsl,imx8mq", }, { .compatible = "fsl,imx8mm", }, diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 045f9fe157ce..d03f250f68e4 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2535,26 +2535,27 @@ EXPORT_SYMBOL_GPL(cpufreq_update_limits); static int cpufreq_boost_set_sw(int state) { struct cpufreq_policy *policy; - int ret = -EINVAL; for_each_active_policy(policy) { + int ret; + if (!policy->freq_table) - continue; + return -ENXIO; ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table); if (ret) { pr_err("%s: Policy frequency update failed\n", __func__); - break; + return ret; } ret = freq_qos_update_request(policy->max_freq_req, policy->max); if (ret < 0) - break; + return ret; } - return ret; + return 0; } int cpufreq_boost_trigger_state(int state) diff --git a/drivers/cpufreq/imx-cpufreq-dt.c b/drivers/cpufreq/imx-cpufreq-dt.c index de206d2745fe..3fe9125156b4 100644 --- a/drivers/cpufreq/imx-cpufreq-dt.c +++ b/drivers/cpufreq/imx-cpufreq-dt.c @@ -3,7 +3,9 @@ * Copyright 2019 NXP */ +#include <linux/clk.h> #include <linux/cpu.h> +#include <linux/cpufreq.h> #include <linux/err.h> #include <linux/init.h> #include <linux/kernel.h> @@ -12,8 +14,11 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_opp.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> +#include "cpufreq-dt.h" + #define OCOTP_CFG3_SPEED_GRADE_SHIFT 8 #define OCOTP_CFG3_SPEED_GRADE_MASK (0x3 << 8) #define IMX8MN_OCOTP_CFG3_SPEED_GRADE_MASK (0xf << 8) @@ -22,20 +27,92 @@ #define IMX8MP_OCOTP_CFG3_MKT_SEGMENT_SHIFT 5 #define IMX8MP_OCOTP_CFG3_MKT_SEGMENT_MASK (0x3 << 5) +#define IMX7ULP_MAX_RUN_FREQ 528000 + /* cpufreq-dt device registered by imx-cpufreq-dt */ static struct platform_device *cpufreq_dt_pdev; static struct opp_table *cpufreq_opp_table; +static struct device *cpu_dev; + +enum IMX7ULP_CPUFREQ_CLKS { + ARM, + CORE, + SCS_SEL, + HSRUN_CORE, + HSRUN_SCS_SEL, + FIRC, +}; + +static struct clk_bulk_data imx7ulp_clks[] = { + { .id = "arm" }, + { .id = "core" }, + { .id = "scs_sel" }, + { .id = "hsrun_core" }, + { .id = "hsrun_scs_sel" }, + { .id = "firc" }, +}; + +static unsigned int imx7ulp_get_intermediate(struct cpufreq_policy *policy, + unsigned int index) +{ + return clk_get_rate(imx7ulp_clks[FIRC].clk); +} + +static int imx7ulp_target_intermediate(struct cpufreq_policy *policy, + unsigned int index) +{ + unsigned int newfreq = policy->freq_table[index].frequency; + + clk_set_parent(imx7ulp_clks[SCS_SEL].clk, imx7ulp_clks[FIRC].clk); + clk_set_parent(imx7ulp_clks[HSRUN_SCS_SEL].clk, imx7ulp_clks[FIRC].clk); + + if (newfreq > IMX7ULP_MAX_RUN_FREQ) + clk_set_parent(imx7ulp_clks[ARM].clk, + imx7ulp_clks[HSRUN_CORE].clk); + else + clk_set_parent(imx7ulp_clks[ARM].clk, imx7ulp_clks[CORE].clk); + + return 0; +} + +static struct cpufreq_dt_platform_data imx7ulp_data = { + .target_intermediate = imx7ulp_target_intermediate, + .get_intermediate = imx7ulp_get_intermediate, +}; static int imx_cpufreq_dt_probe(struct platform_device *pdev) { - struct device *cpu_dev = get_cpu_device(0); + struct platform_device *dt_pdev; u32 cell_value, supported_hw[2]; int speed_grade, mkt_segment; int ret; + cpu_dev = get_cpu_device(0); + if (!of_find_property(cpu_dev->of_node, "cpu-supply", NULL)) return -ENODEV; + if (of_machine_is_compatible("fsl,imx7ulp")) { + ret = clk_bulk_get(cpu_dev, ARRAY_SIZE(imx7ulp_clks), + imx7ulp_clks); + if (ret) + return ret; + + dt_pdev = platform_device_register_data(NULL, "cpufreq-dt", + -1, &imx7ulp_data, + sizeof(imx7ulp_data)); + if (IS_ERR(dt_pdev)) { + clk_bulk_put(ARRAY_SIZE(imx7ulp_clks), imx7ulp_clks); + ret = PTR_ERR(dt_pdev); + dev_err(&pdev->dev, "Failed to register cpufreq-dt: %d\n", ret); + return ret; + } + + cpufreq_dt_pdev = dt_pdev; + + return 0; + } + ret = nvmem_cell_read_u32(cpu_dev, "speed_grade", &cell_value); if (ret) return ret; @@ -98,7 +175,10 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev) static int imx_cpufreq_dt_remove(struct platform_device *pdev) { platform_device_unregister(cpufreq_dt_pdev); - dev_pm_opp_put_supported_hw(cpufreq_opp_table); + if (!of_machine_is_compatible("fsl,imx7ulp")) + dev_pm_opp_put_supported_hw(cpufreq_opp_table); + else + clk_bulk_put(ARRAY_SIZE(imx7ulp_clks), imx7ulp_clks); return 0; } diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 4d3429b2058f..8e23a698ce04 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2771,6 +2771,8 @@ static int __init intel_pstate_init(void) pr_info("Invalid MSRs\n"); return -ENODEV; } + /* Without HWP start in the passive mode. */ + default_driver = &intel_cpufreq; hwp_cpu_matched: /* @@ -2816,7 +2818,6 @@ static int __init intel_pstate_setup(char *str) if (!strcmp(str, "disable")) { no_load = 1; } else if (!strcmp(str, "passive")) { - pr_info("Passive mode enabled\n"); default_driver = &intel_cpufreq; no_hwp = 1; } diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index a1b8238872a2..d06b37822c3d 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -277,7 +277,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) if (!np) return -ENOENT; - ret = of_device_is_compatible(np, "operating-points-v2-qcom-cpu"); + ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu"); if (!ret) { of_node_put(np); return -ENOENT; diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c index 8e436dc75c8b..6b6b20da2bcf 100644 --- a/drivers/cpufreq/qoriq-cpufreq.c +++ b/drivers/cpufreq/qoriq-cpufreq.c @@ -18,6 +18,7 @@ #include <linux/of.h> #include <linux/slab.h> #include <linux/smp.h> +#include <linux/platform_device.h> /** * struct cpu_data @@ -29,12 +30,6 @@ struct cpu_data { struct cpufreq_frequency_table *table; }; -/* - * Don't use cpufreq on this SoC -- used when the SoC would have otherwise - * matched a more generic compatible. - */ -#define SOC_BLACKLIST 1 - /** * struct soc_data - SoC specific data * @flags: SOC_xxx @@ -264,64 +259,51 @@ static struct cpufreq_driver qoriq_cpufreq_driver = { .attr = cpufreq_generic_attr, }; -static const struct soc_data blacklist = { - .flags = SOC_BLACKLIST, -}; - -static const struct of_device_id node_matches[] __initconst = { +static const struct of_device_id qoriq_cpufreq_blacklist[] = { /* e6500 cannot use cpufreq due to erratum A-008083 */ - { .compatible = "fsl,b4420-clockgen", &blacklist }, - { .compatible = "fsl,b4860-clockgen", &blacklist }, - { .compatible = "fsl,t2080-clockgen", &blacklist }, - { .compatible = "fsl,t4240-clockgen", &blacklist }, - - { .compatible = "fsl,ls1012a-clockgen", }, - { .compatible = "fsl,ls1021a-clockgen", }, - { .compatible = "fsl,ls1028a-clockgen", }, - { .compatible = "fsl,ls1043a-clockgen", }, - { .compatible = "fsl,ls1046a-clockgen", }, - { .compatible = "fsl,ls1088a-clockgen", }, - { .compatible = "fsl,ls2080a-clockgen", }, - { .compatible = "fsl,lx2160a-clockgen", }, - { .compatible = "fsl,p4080-clockgen", }, - { .compatible = "fsl,qoriq-clockgen-1.0", }, - { .compatible = "fsl,qoriq-clockgen-2.0", }, + { .compatible = "fsl,b4420-clockgen", }, + { .compatible = "fsl,b4860-clockgen", }, + { .compatible = "fsl,t2080-clockgen", }, + { .compatible = "fsl,t4240-clockgen", }, {} }; -static int __init qoriq_cpufreq_init(void) +static int qoriq_cpufreq_probe(struct platform_device *pdev) { int ret; - struct device_node *np; - const struct of_device_id *match; - const struct soc_data *data; - - np = of_find_matching_node(NULL, node_matches); - if (!np) - return -ENODEV; - - match = of_match_node(node_matches, np); - data = match->data; - - of_node_put(np); + struct device_node *np; - if (data && data->flags & SOC_BLACKLIST) + np = of_find_matching_node(NULL, qoriq_cpufreq_blacklist); + if (np) { + dev_info(&pdev->dev, "Disabling due to erratum A-008083"); return -ENODEV; + } ret = cpufreq_register_driver(&qoriq_cpufreq_driver); - if (!ret) - pr_info("Freescale QorIQ CPU frequency scaling driver\n"); + if (ret) + return ret; - return ret; + dev_info(&pdev->dev, "Freescale QorIQ CPU frequency scaling driver\n"); + return 0; } -module_init(qoriq_cpufreq_init); -static void __exit qoriq_cpufreq_exit(void) +static int qoriq_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&qoriq_cpufreq_driver); + + return 0; } -module_exit(qoriq_cpufreq_exit); +static struct platform_driver qoriq_cpufreq_platform_driver = { + .driver = { + .name = "qoriq-cpufreq", + }, + .probe = qoriq_cpufreq_probe, + .remove = qoriq_cpufreq_remove, +}; +module_platform_driver(qoriq_cpufreq_platform_driver); + +MODULE_ALIAS("platform:qoriq-cpufreq"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>"); MODULE_DESCRIPTION("cpufreq driver for Freescale QorIQ series SoCs"); diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 99a2d72ac02b..51a7e89085c0 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -94,3 +94,16 @@ config ARM_TEGRA_CPUIDLE select ARM_CPU_SUSPEND help Select this to enable cpuidle for NVIDIA Tegra20/30/114/124 SoCs. + +config ARM_QCOM_SPM_CPUIDLE + bool "CPU Idle Driver for Qualcomm Subsystem Power Manager (SPM)" + depends on (ARCH_QCOM || COMPILE_TEST) && !ARM64 + select ARM_CPU_SUSPEND + select CPU_IDLE_MULTIPLE_DRIVERS + select DT_IDLE_STATES + select QCOM_SCM + help + Select this to enable cpuidle for Qualcomm processors. + The Subsystem Power Manager (SPM) controls low power modes for the + CPU and L2 cores. It interface with various system drivers to put + the cores in low power modes. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 55a464f6a78b..f07800cbb43f 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_ARM_PSCI_CPUIDLE) += cpuidle_psci.o cpuidle_psci-y := cpuidle-psci.o cpuidle_psci-$(CONFIG_PM_GENERIC_DOMAINS_OF) += cpuidle-psci-domain.o obj-$(CONFIG_ARM_TEGRA_CPUIDLE) += cpuidle-tegra.o +obj-$(CONFIG_ARM_QCOM_SPM_CPUIDLE) += cpuidle-qcom-spm.o ############################################################################### # MIPS drivers diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c index bae9140a65a5..d0fb585073c6 100644 --- a/drivers/cpuidle/cpuidle-psci.c +++ b/drivers/cpuidle/cpuidle-psci.c @@ -58,6 +58,10 @@ static int psci_enter_domain_idle_state(struct cpuidle_device *dev, u32 state; int ret; + ret = cpu_pm_enter(); + if (ret) + return -1; + /* Do runtime PM to manage a hierarchical CPU toplogy. */ pm_runtime_put_sync_suspend(pd_dev); @@ -65,10 +69,12 @@ static int psci_enter_domain_idle_state(struct cpuidle_device *dev, if (!state) state = states[idx]; - ret = psci_enter_state(idx, state); + ret = psci_cpu_suspend_enter(state) ? -1 : idx; pm_runtime_get_sync(pd_dev); + cpu_pm_exit(); + /* Clear the domain state to start fresh when back from idle. */ psci_set_domain_state(0); return ret; diff --git a/drivers/soc/qcom/spm.c b/drivers/cpuidle/cpuidle-qcom-spm.c index 8e10e02c6aa5..adf91a6e4d7d 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/cpuidle/cpuidle-qcom-spm.c @@ -19,10 +19,11 @@ #include <linux/cpu_pm.h> #include <linux/qcom_scm.h> -#include <asm/cpuidle.h> #include <asm/proc-fns.h> #include <asm/suspend.h> +#include "dt_idle_states.h" + #define MAX_PMIC_DATA 2 #define MAX_SEQ_DATA 64 #define SPM_CTL_INDEX 0x7f @@ -62,6 +63,7 @@ struct spm_reg_data { }; struct spm_driver_data { + struct cpuidle_driver cpuidle_driver; void __iomem *reg_base; const struct spm_reg_data *reg_data; }; @@ -107,11 +109,6 @@ static const struct spm_reg_data spm_reg_8064_cpu = { .start_index[PM_SLEEP_MODE_SPC] = 2, }; -static DEFINE_PER_CPU(struct spm_driver_data *, cpu_spm_drv); - -typedef int (*idle_fn)(void); -static DEFINE_PER_CPU(idle_fn*, qcom_idle_ops); - static inline void spm_register_write(struct spm_driver_data *drv, enum spm_reg reg, u32 val) { @@ -172,10 +169,9 @@ static int qcom_pm_collapse(unsigned long int unused) return -1; } -static int qcom_cpu_spc(void) +static int qcom_cpu_spc(struct spm_driver_data *drv) { int ret; - struct spm_driver_data *drv = __this_cpu_read(cpu_spm_drv); spm_set_low_power_mode(drv, PM_SLEEP_MODE_SPC); ret = cpu_suspend(0, qcom_pm_collapse); @@ -190,94 +186,49 @@ static int qcom_cpu_spc(void) return ret; } -static int qcom_idle_enter(unsigned long index) +static int spm_enter_idle_state(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int idx) { - return __this_cpu_read(qcom_idle_ops)[index](); + struct spm_driver_data *data = container_of(drv, struct spm_driver_data, + cpuidle_driver); + + return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data); } -static const struct of_device_id qcom_idle_state_match[] __initconst = { - { .compatible = "qcom,idle-state-spc", .data = qcom_cpu_spc }, +static struct cpuidle_driver qcom_spm_idle_driver = { + .name = "qcom_spm", + .owner = THIS_MODULE, + .states[0] = { + .enter = spm_enter_idle_state, + .exit_latency = 1, + .target_residency = 1, + .power_usage = UINT_MAX, + .name = "WFI", + .desc = "ARM WFI", + } +}; + +static const struct of_device_id qcom_idle_state_match[] = { + { .compatible = "qcom,idle-state-spc", .data = spm_enter_idle_state }, { }, }; -static int __init qcom_cpuidle_init(struct device_node *cpu_node, int cpu) +static int spm_cpuidle_init(struct cpuidle_driver *drv, int cpu) { - const struct of_device_id *match_id; - struct device_node *state_node; - int i; - int state_count = 1; - idle_fn idle_fns[CPUIDLE_STATE_MAX]; - idle_fn *fns; - cpumask_t mask; - bool use_scm_power_down = false; - - if (!qcom_scm_is_available()) - return -EPROBE_DEFER; - - for (i = 0; ; i++) { - state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); - if (!state_node) - break; - - if (!of_device_is_available(state_node)) - continue; - - if (i == CPUIDLE_STATE_MAX) { - pr_warn("%s: cpuidle states reached max possible\n", - __func__); - break; - } - - match_id = of_match_node(qcom_idle_state_match, state_node); - if (!match_id) - return -ENODEV; - - idle_fns[state_count] = match_id->data; - - /* Check if any of the states allow power down */ - if (match_id->data == qcom_cpu_spc) - use_scm_power_down = true; - - state_count++; - } - - if (state_count == 1) - goto check_spm; - - fns = devm_kcalloc(get_cpu_device(cpu), state_count, sizeof(*fns), - GFP_KERNEL); - if (!fns) - return -ENOMEM; - - for (i = 1; i < state_count; i++) - fns[i] = idle_fns[i]; + int ret; - if (use_scm_power_down) { - /* We have atleast one power down mode */ - cpumask_clear(&mask); - cpumask_set_cpu(cpu, &mask); - qcom_scm_set_warm_boot_addr(cpu_resume_arm, &mask); - } + memcpy(drv, &qcom_spm_idle_driver, sizeof(*drv)); + drv->cpumask = (struct cpumask *)cpumask_of(cpu); - per_cpu(qcom_idle_ops, cpu) = fns; + /* Parse idle states from device tree */ + ret = dt_init_idle_driver(drv, qcom_idle_state_match, 1); + if (ret <= 0) + return ret ? : -ENODEV; - /* - * SPM probe for the cpu should have happened by now, if the - * SPM device does not exist, return -ENXIO to indicate that the - * cpu does not support idle states. - */ -check_spm: - return per_cpu(cpu_spm_drv, cpu) ? 0 : -ENXIO; + /* We have atleast one power down mode */ + return qcom_scm_set_warm_boot_addr(cpu_resume_arm, drv->cpumask); } -static const struct cpuidle_ops qcom_cpuidle_ops __initconst = { - .suspend = qcom_idle_enter, - .init = qcom_cpuidle_init, -}; - -CPUIDLE_METHOD_OF_DECLARE(qcom_idle_v1, "qcom,kpss-acc-v1", &qcom_cpuidle_ops); -CPUIDLE_METHOD_OF_DECLARE(qcom_idle_v2, "qcom,kpss-acc-v2", &qcom_cpuidle_ops); - static struct spm_driver_data *spm_get_drv(struct platform_device *pdev, int *spm_cpu) { @@ -323,11 +274,15 @@ static int spm_dev_probe(struct platform_device *pdev) struct resource *res; const struct of_device_id *match_id; void __iomem *addr; - int cpu; + int cpu, ret; + + if (!qcom_scm_is_available()) + return -EPROBE_DEFER; drv = spm_get_drv(pdev, &cpu); if (!drv) return -EINVAL; + platform_set_drvdata(pdev, drv); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); drv->reg_base = devm_ioremap_resource(&pdev->dev, res); @@ -340,6 +295,10 @@ static int spm_dev_probe(struct platform_device *pdev) drv->reg_data = match_id->data; + ret = spm_cpuidle_init(&drv->cpuidle_driver, cpu); + if (ret) + return ret; + /* Write the SPM sequences first.. */ addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY]; __iowrite32_copy(addr, drv->reg_data->seq, @@ -362,13 +321,20 @@ static int spm_dev_probe(struct platform_device *pdev) /* Set up Standby as the default low power mode */ spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); - per_cpu(cpu_spm_drv, cpu) = drv; + return cpuidle_register(&drv->cpuidle_driver, NULL); +} + +static int spm_dev_remove(struct platform_device *pdev) +{ + struct spm_driver_data *drv = platform_get_drvdata(pdev); + cpuidle_unregister(&drv->cpuidle_driver); return 0; } static struct platform_driver spm_driver = { .probe = spm_dev_probe, + .remove = spm_dev_remove, .driver = { .name = "saw", .of_match_table = spm_match_table, diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index cdeedbf02646..091d1caceb41 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -18,14 +18,6 @@ #include "cpuidle.h" -static unsigned int sysfs_switch; -static int __init cpuidle_sysfs_setup(char *unused) -{ - sysfs_switch = 1; - return 1; -} -__setup("cpuidle_sysfs_switch", cpuidle_sysfs_setup); - static ssize_t show_available_governors(struct device *dev, struct device_attribute *attr, char *buf) @@ -35,10 +27,10 @@ static ssize_t show_available_governors(struct device *dev, mutex_lock(&cpuidle_lock); list_for_each_entry(tmp, &cpuidle_governors, governor_list) { - if (i >= (ssize_t) ((PAGE_SIZE/sizeof(char)) - - CPUIDLE_NAME_LEN - 2)) + if (i >= (ssize_t) (PAGE_SIZE - (CPUIDLE_NAME_LEN + 2))) goto out; - i += scnprintf(&buf[i], CPUIDLE_NAME_LEN, "%s ", tmp->name); + + i += scnprintf(&buf[i], CPUIDLE_NAME_LEN + 1, "%s ", tmp->name); } out: @@ -85,58 +77,43 @@ static ssize_t store_current_governor(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - char gov_name[CPUIDLE_NAME_LEN]; - int ret = -EINVAL; - size_t len = count; + char gov_name[CPUIDLE_NAME_LEN + 1]; + int ret; struct cpuidle_governor *gov; - if (!len || len >= sizeof(gov_name)) + ret = sscanf(buf, "%" __stringify(CPUIDLE_NAME_LEN) "s", gov_name); + if (ret != 1) return -EINVAL; - memcpy(gov_name, buf, len); - gov_name[len] = '\0'; - if (gov_name[len - 1] == '\n') - gov_name[--len] = '\0'; - mutex_lock(&cpuidle_lock); - + ret = -EINVAL; list_for_each_entry(gov, &cpuidle_governors, governor_list) { - if (strlen(gov->name) == len && !strcmp(gov->name, gov_name)) { + if (!strncmp(gov->name, gov_name, CPUIDLE_NAME_LEN)) { ret = cpuidle_switch_governor(gov); break; } } - mutex_unlock(&cpuidle_lock); - if (ret) - return ret; - else - return count; + return ret ? ret : count; } -static DEVICE_ATTR(current_driver, 0444, show_current_driver, NULL); -static DEVICE_ATTR(current_governor_ro, 0444, show_current_governor, NULL); - -static struct attribute *cpuidle_default_attrs[] = { - &dev_attr_current_driver.attr, - &dev_attr_current_governor_ro.attr, - NULL -}; - static DEVICE_ATTR(available_governors, 0444, show_available_governors, NULL); +static DEVICE_ATTR(current_driver, 0444, show_current_driver, NULL); static DEVICE_ATTR(current_governor, 0644, show_current_governor, - store_current_governor); + store_current_governor); +static DEVICE_ATTR(current_governor_ro, 0444, show_current_governor, NULL); -static struct attribute *cpuidle_switch_attrs[] = { +static struct attribute *cpuidle_attrs[] = { &dev_attr_available_governors.attr, &dev_attr_current_driver.attr, &dev_attr_current_governor.attr, + &dev_attr_current_governor_ro.attr, NULL }; static struct attribute_group cpuidle_attr_group = { - .attrs = cpuidle_default_attrs, + .attrs = cpuidle_attrs, .name = "cpuidle", }; @@ -146,9 +123,6 @@ static struct attribute_group cpuidle_attr_group = { */ int cpuidle_add_interface(struct device *dev) { - if (sysfs_switch) - cpuidle_attr_group.attrs = cpuidle_switch_attrs; - return sysfs_create_group(&dev->kobj, &cpuidle_attr_group); } @@ -167,11 +141,6 @@ struct cpuidle_attr { ssize_t (*store)(struct cpuidle_device *, const char *, size_t count); }; -#define define_one_ro(_name, show) \ - static struct cpuidle_attr attr_##_name = __ATTR(_name, 0444, show, NULL) -#define define_one_rw(_name, show, store) \ - static struct cpuidle_attr attr_##_name = __ATTR(_name, 0644, show, store) - #define attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr) struct cpuidle_device_kobj { @@ -431,12 +400,12 @@ static inline void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *k #define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr) static ssize_t cpuidle_state_show(struct kobject *kobj, struct attribute *attr, - char * buf) + char *buf) { int ret = -EIO; struct cpuidle_state *state = kobj_to_state(kobj); struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj); - struct cpuidle_state_attr * cattr = attr_to_stateattr(attr); + struct cpuidle_state_attr *cattr = attr_to_stateattr(attr); if (cattr->show) ret = cattr->show(state, state_usage, buf); @@ -515,7 +484,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device) ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, &kdev->kobj, "state%d", i); if (ret) { - kfree(kobj); + kobject_put(&kobj->kobj); goto error_state; } cpuidle_add_s2idle_attr_group(kobj); @@ -646,7 +615,7 @@ static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) ret = kobject_init_and_add(&kdrv->kobj, &ktype_driver_cpuidle, &kdev->kobj, "driver"); if (ret) { - kfree(kdrv); + kobject_put(&kdrv->kobj); return ret; } @@ -740,7 +709,7 @@ int cpuidle_add_sysfs(struct cpuidle_device *dev) error = kobject_init_and_add(&kdev->kobj, &ktype_cpuidle, &cpu_dev->kobj, "cpuidle"); if (error) { - kfree(kdev); + kobject_put(&kdev->kobj); return error; } diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 0b1df12e0f21..37dc40d1fcfb 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -91,6 +91,14 @@ config ARM_EXYNOS_BUS_DEVFREQ and adjusts the operating frequencies and voltages with OPP support. This does not yet operate with optimal voltages. +config ARM_IMX_BUS_DEVFREQ + tristate "i.MX Generic Bus DEVFREQ Driver" + depends on ARCH_MXC || COMPILE_TEST + select DEVFREQ_GOV_USERSPACE + help + This adds the generic DEVFREQ driver for i.MX interconnects. It + allows adjusting NIC/NOC frequency. + config ARM_IMX8M_DDRC_DEVFREQ tristate "i.MX8M DDRC DEVFREQ Driver" depends on (ARCH_MXC && HAVE_ARM_SMCCC) || \ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 3eb4d5e6635c..3ca1ad0ecb97 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o +obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 6fecd11dafdd..52b9c3e141f3 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -60,12 +60,12 @@ static struct devfreq *find_device_devfreq(struct device *dev) { struct devfreq *tmp_devfreq; + lockdep_assert_held(&devfreq_list_lock); + if (IS_ERR_OR_NULL(dev)) { pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); return ERR_PTR(-EINVAL); } - WARN(!mutex_is_locked(&devfreq_list_lock), - "devfreq_list_lock must be locked."); list_for_each_entry(tmp_devfreq, &devfreq_list, node) { if (tmp_devfreq->dev.parent == dev) @@ -258,12 +258,12 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) { struct devfreq_governor *tmp_governor; + lockdep_assert_held(&devfreq_list_lock); + if (IS_ERR_OR_NULL(name)) { pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); return ERR_PTR(-EINVAL); } - WARN(!mutex_is_locked(&devfreq_list_lock), - "devfreq_list_lock must be locked."); list_for_each_entry(tmp_governor, &devfreq_governor_list, node) { if (!strncmp(tmp_governor->name, name, DEVFREQ_NAME_LEN)) @@ -289,12 +289,12 @@ static struct devfreq_governor *try_then_request_governor(const char *name) struct devfreq_governor *governor; int err = 0; + lockdep_assert_held(&devfreq_list_lock); + if (IS_ERR_OR_NULL(name)) { pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); return ERR_PTR(-EINVAL); } - WARN(!mutex_is_locked(&devfreq_list_lock), - "devfreq_list_lock must be locked."); governor = find_devfreq_governor(name); if (IS_ERR(governor)) { @@ -392,10 +392,7 @@ int update_devfreq(struct devfreq *devfreq) int err = 0; u32 flags = 0; - if (!mutex_is_locked(&devfreq->lock)) { - WARN(true, "devfreq->lock must be locked by the caller.\n"); - return -EINVAL; - } + lockdep_assert_held(&devfreq->lock); if (!devfreq->governor) return -EINVAL; @@ -768,7 +765,7 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->dev.release = devfreq_dev_release; INIT_LIST_HEAD(&devfreq->node); devfreq->profile = profile; - strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN); + strscpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN); devfreq->previous_freq = profile->initial_freq; devfreq->last_status.current_frequency = profile->initial_freq; devfreq->data = data; diff --git a/drivers/devfreq/imx-bus.c b/drivers/devfreq/imx-bus.c new file mode 100644 index 000000000000..4f38455ad742 --- /dev/null +++ b/drivers/devfreq/imx-bus.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP + */ + +#include <linux/clk.h> +#include <linux/devfreq.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pm_opp.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +struct imx_bus { + struct devfreq_dev_profile profile; + struct devfreq *devfreq; + struct clk *clk; + struct platform_device *icc_pdev; +}; + +static int imx_bus_target(struct device *dev, + unsigned long *freq, u32 flags) +{ + struct dev_pm_opp *new_opp; + int ret; + + new_opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(new_opp)) { + ret = PTR_ERR(new_opp); + dev_err(dev, "failed to get recommended opp: %d\n", ret); + return ret; + } + dev_pm_opp_put(new_opp); + + return dev_pm_opp_set_rate(dev, *freq); +} + +static int imx_bus_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct imx_bus *priv = dev_get_drvdata(dev); + + *freq = clk_get_rate(priv->clk); + + return 0; +} + +static int imx_bus_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct imx_bus *priv = dev_get_drvdata(dev); + + stat->busy_time = 0; + stat->total_time = 0; + stat->current_frequency = clk_get_rate(priv->clk); + + return 0; +} + +static void imx_bus_exit(struct device *dev) +{ + struct imx_bus *priv = dev_get_drvdata(dev); + + dev_pm_opp_of_remove_table(dev); + platform_device_unregister(priv->icc_pdev); +} + +/* imx_bus_init_icc() - register matching icc provider if required */ +static int imx_bus_init_icc(struct device *dev) +{ + struct imx_bus *priv = dev_get_drvdata(dev); + const char *icc_driver_name; + + if (!of_get_property(dev->of_node, "#interconnect-cells", 0)) + return 0; + if (!IS_ENABLED(CONFIG_INTERCONNECT_IMX)) { + dev_warn(dev, "imx interconnect drivers disabled\n"); + return 0; + } + + icc_driver_name = of_device_get_match_data(dev); + if (!icc_driver_name) { + dev_err(dev, "unknown interconnect driver\n"); + return 0; + } + + priv->icc_pdev = platform_device_register_data( + dev, icc_driver_name, -1, NULL, 0); + if (IS_ERR(priv->icc_pdev)) { + dev_err(dev, "failed to register icc provider %s: %ld\n", + icc_driver_name, PTR_ERR(priv->icc_pdev)); + return PTR_ERR(priv->icc_pdev); + } + + return 0; +} + +static int imx_bus_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imx_bus *priv; + const char *gov = DEVFREQ_GOV_USERSPACE; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* + * Fetch the clock to adjust but don't explicitly enable. + * + * For imx bus clock clk_set_rate is safe no matter if the clock is on + * or off and some peripheral side-buses might be off unless enabled by + * drivers for devices on those specific buses. + * + * Rate adjustment on a disabled bus clock just takes effect later. + */ + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + dev_err(dev, "failed to fetch clk: %d\n", ret); + return ret; + } + platform_set_drvdata(pdev, priv); + + ret = dev_pm_opp_of_add_table(dev); + if (ret < 0) { + dev_err(dev, "failed to get OPP table\n"); + return ret; + } + + priv->profile.polling_ms = 1000; + priv->profile.target = imx_bus_target; + priv->profile.get_dev_status = imx_bus_get_dev_status; + priv->profile.exit = imx_bus_exit; + priv->profile.get_cur_freq = imx_bus_get_cur_freq; + priv->profile.initial_freq = clk_get_rate(priv->clk); + + priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, + gov, NULL); + if (IS_ERR(priv->devfreq)) { + ret = PTR_ERR(priv->devfreq); + dev_err(dev, "failed to add devfreq device: %d\n", ret); + goto err; + } + + ret = imx_bus_init_icc(dev); + if (ret) + goto err; + + return 0; + +err: + dev_pm_opp_of_remove_table(dev); + return ret; +} + +static const struct of_device_id imx_bus_of_match[] = { + { .compatible = "fsl,imx8mq-noc", .data = "imx8mq-interconnect", }, + { .compatible = "fsl,imx8mm-noc", .data = "imx8mm-interconnect", }, + { .compatible = "fsl,imx8mn-noc", .data = "imx8mn-interconnect", }, + { .compatible = "fsl,imx8m-noc", }, + { .compatible = "fsl,imx8m-nic", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, imx_bus_of_match); + +static struct platform_driver imx_bus_platdrv = { + .probe = imx_bus_probe, + .driver = { + .name = "imx-bus-devfreq", + .of_match_table = of_match_ptr(imx_bus_of_match), + }, +}; +module_platform_driver(imx_bus_platdrv); + +MODULE_DESCRIPTION("Generic i.MX bus frequency scaling driver"); +MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c index 28b2c7ca416e..e94a27804c20 100644 --- a/drivers/devfreq/tegra30-devfreq.c +++ b/drivers/devfreq/tegra30-devfreq.c @@ -420,7 +420,7 @@ tegra_actmon_cpufreq_contribution(struct tegra_devfreq *tegra, static_cpu_emc_freq = actmon_cpu_to_emc_rate(tegra, cpu_freq); - if (dev_freq >= static_cpu_emc_freq) + if (dev_freq + actmon_dev->boost_freq >= static_cpu_emc_freq) return 0; return static_cpu_emc_freq; @@ -807,10 +807,9 @@ static int tegra_devfreq_probe(struct platform_device *pdev) } err = platform_get_irq(pdev, 0); - if (err < 0) { - dev_err(&pdev->dev, "Failed to get IRQ: %d\n", err); + if (err < 0) return err; - } + tegra->irq = err; irq_set_status_flags(tegra->irq, IRQ_NOAUTOEN); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index fd1dc3236eca..a9086ea1ab60 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -191,7 +191,7 @@ int amdgpu_driver_load_kms(struct drm_device *dev, unsigned long flags) } if (adev->runpm) { - dev_pm_set_driver_flags(dev->dev, DPM_FLAG_NEVER_SKIP); + dev_pm_set_driver_flags(dev->dev, DPM_FLAG_NO_DIRECT_COMPLETE); pm_runtime_use_autosuspend(dev->dev); pm_runtime_set_autosuspend_delay(dev->dev, 5000); pm_runtime_set_active(dev->dev); diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index ad719c9602af..9cb2d7548daa 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -549,7 +549,7 @@ void intel_runtime_pm_enable(struct intel_runtime_pm *rpm) * becaue the HDA driver may require us to enable the audio power * domain during system suspend. */ - dev_pm_set_driver_flags(kdev, DPM_FLAG_NEVER_SKIP); + dev_pm_set_driver_flags(kdev, DPM_FLAG_NO_DIRECT_COMPLETE); pm_runtime_set_autosuspend_delay(kdev, 10000); /* 10s */ pm_runtime_mark_last_busy(kdev); diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 58176db85952..372962358a18 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -158,7 +158,7 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags) } if (radeon_is_px(dev)) { - dev_pm_set_driver_flags(dev->dev, DPM_FLAG_NEVER_SKIP); + dev_pm_set_driver_flags(dev->dev, DPM_FLAG_NO_DIRECT_COMPLETE); pm_runtime_use_autosuspend(dev->dev); pm_runtime_set_autosuspend_delay(dev->dev, 5000); pm_runtime_set_active(dev->dev); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 5536673060cc..c429d664f655 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -357,12 +357,12 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) if (dev->flags & ACCESS_NO_IRQ_SUSPEND) { dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_SMART_PREPARE | - DPM_FLAG_LEAVE_SUSPENDED); + DPM_FLAG_MAY_SKIP_RESUME); } else { dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_SMART_PREPARE | DPM_FLAG_SMART_SUSPEND | - DPM_FLAG_LEAVE_SUSPENDED); + DPM_FLAG_MAY_SKIP_RESUME); } /* The code below assumes runtime PM to be disabled. */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index a1ed375fed37..71f795b510ce 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -241,7 +241,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * MEI requires to resume from runtime suspend mode * in order to perform link reset flow upon system suspend. */ - dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP); + dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE); /* * ME maps runtime suspend/resume to D0i states, diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index beacf2a2f2b5..4bf26ce61044 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -128,7 +128,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * MEI requires to resume from runtime suspend mode * in order to perform link reset flow upon system suspend. */ - dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP); + dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE); /* * TXE maps runtime suspend/resume to own power gating states, diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 177c6da80c57..2730b1c7dddb 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -7549,7 +7549,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) e1000_print_device_info(adapter); - dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP); + dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE); if (pci_dev_run_wake(pdev) && hw->mac.type < e1000_pch_cnp) pm_runtime_put_noidle(&pdev->dev); diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index b46bff8fe056..8bb3db2cbd41 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3445,7 +3445,7 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } - dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP); + dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE); pm_runtime_put_noidle(&pdev->dev); return 0; diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 69fa1ce1f927..59fc0097438f 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -4825,7 +4825,7 @@ static int igc_probe(struct pci_dev *pdev, pcie_print_link_status(pdev); netdev_info(netdev, "MAC: %pM\n", netdev->dev_addr); - dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP); + dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE); pm_runtime_put_noidle(&pdev->dev); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 312cc45c44c7..bf779f291f15 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -275,7 +275,7 @@ static int pciehp_suspend(struct pcie_device *dev) * If the port is already runtime suspended we can keep it that * way. */ - if (dev_pm_smart_suspend_and_suspended(&dev->port->dev)) + if (dev_pm_skip_suspend(&dev->port->dev)) return 0; pciehp_disable_interrupt(dev); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 0454ca0e4e3f..da6510af1221 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -776,7 +776,7 @@ static int pci_pm_suspend(struct device *dev) static int pci_pm_suspend_late(struct device *dev) { - if (dev_pm_smart_suspend_and_suspended(dev)) + if (dev_pm_skip_suspend(dev)) return 0; pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev)); @@ -789,10 +789,8 @@ static int pci_pm_suspend_noirq(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - if (dev_pm_smart_suspend_and_suspended(dev)) { - dev->power.may_skip_resume = true; + if (dev_pm_skip_suspend(dev)) return 0; - } if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend_late(dev, PMSG_SUSPEND); @@ -880,8 +878,8 @@ Fixup: * pci_pm_complete() to take care of fixing up the device's state * anyway, if need be. */ - dev->power.may_skip_resume = device_may_wakeup(dev) || - !device_can_wakeup(dev); + if (device_can_wakeup(dev) && !device_may_wakeup(dev)) + dev->power.may_skip_resume = false; return 0; } @@ -893,18 +891,10 @@ static int pci_pm_resume_noirq(struct device *dev) pci_power_t prev_state = pci_dev->current_state; bool skip_bus_pm = pci_dev->skip_bus_pm; - if (dev_pm_may_skip_resume(dev)) + if (dev_pm_skip_resume(dev)) return 0; /* - * Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend - * during system suspend, so update their runtime PM status to "active" - * as they are going to be put into D0 shortly. - */ - if (dev_pm_smart_suspend_and_suspended(dev)) - pm_runtime_set_active(dev); - - /* * In the suspend-to-idle case, devices left in D0 during suspend will * stay in D0, so it is not necessary to restore or update their * configuration here and attempting to put them into D0 again is @@ -928,6 +918,14 @@ static int pci_pm_resume_noirq(struct device *dev) return 0; } +static int pci_pm_resume_early(struct device *dev) +{ + if (dev_pm_skip_resume(dev)) + return 0; + + return pm_generic_resume_early(dev); +} + static int pci_pm_resume(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -961,6 +959,7 @@ static int pci_pm_resume(struct device *dev) #define pci_pm_suspend_late NULL #define pci_pm_suspend_noirq NULL #define pci_pm_resume NULL +#define pci_pm_resume_early NULL #define pci_pm_resume_noirq NULL #endif /* !CONFIG_SUSPEND */ @@ -1127,7 +1126,7 @@ static int pci_pm_poweroff(struct device *dev) static int pci_pm_poweroff_late(struct device *dev) { - if (dev_pm_smart_suspend_and_suspended(dev)) + if (dev_pm_skip_suspend(dev)) return 0; pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev)); @@ -1140,7 +1139,7 @@ static int pci_pm_poweroff_noirq(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - if (dev_pm_smart_suspend_and_suspended(dev)) + if (dev_pm_skip_suspend(dev)) return 0; if (pci_has_legacy_pm_support(pci_dev)) @@ -1358,6 +1357,7 @@ static const struct dev_pm_ops pci_dev_pm_ops = { .suspend = pci_pm_suspend, .suspend_late = pci_pm_suspend_late, .resume = pci_pm_resume, + .resume_early = pci_pm_resume_early, .freeze = pci_pm_freeze, .thaw = pci_pm_thaw, .poweroff = pci_pm_poweroff, diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 160d67c59310..3acf151ae015 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -115,7 +115,7 @@ static int pcie_portdrv_probe(struct pci_dev *dev, pci_save_state(dev); - dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_NEVER_SKIP | + dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_NO_DIRECT_COMPLETE | DPM_FLAG_SMART_SUSPEND); if (pci_bridge_d3_possible(dev)) { diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index eb328655bc01..61a63a16b5e7 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -26,9 +26,6 @@ #include <asm/cpu_device_id.h> #include <asm/intel-family.h> -/* Local defines */ -#define MSR_PLATFORM_POWER_LIMIT 0x0000065C - /* bitmasks for RAPL MSRs, used by primitive access functions */ #define ENERGY_STATUS_MASK 0xffffffff @@ -989,6 +986,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = { X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, &rapl_defaults_core), X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_PLUS, &rapl_defaults_core), X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_D, &rapl_defaults_core), + X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT, &rapl_defaults_core), X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_D, &rapl_defaults_core), X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L, &rapl_defaults_core), diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index bf42a17a45de..285baa7e474e 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -80,16 +80,6 @@ config QCOM_PDR_HELPERS tristate select QCOM_QMI_HELPERS -config QCOM_PM - bool "Qualcomm Power Management" - depends on ARCH_QCOM && !ARM64 - select ARM_CPU_SUSPEND - select QCOM_SCM - help - QCOM Platform specific power driver to manage cores and L2 low power - modes. It interface with various system drivers to put the cores in - low power modes. - config QCOM_QMI_HELPERS tristate depends on NET diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 5d6b83dc58e8..92cc4232d72c 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o obj-$(CONFIG_QCOM_PDR_HELPERS) += pdr_interface.o -obj-$(CONFIG_QCOM_PM) += spm.o obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o qmi_helpers-y += qmi_encdec.o qmi_interface.o obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o |