From 604d895857c2fc6748feb805c2290a60491eed43 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Mon, 12 Jun 2017 22:51:55 +0200 Subject: PM / sleep: Print timing information if debug is enabled Avoid printing the device suspend/resume timing information if CONFIG_PM_DEBUG is not set to reduce the log noise level. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9faee1c893e5..253f860e8981 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -417,6 +417,7 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info, dev_name(dev), pm_verb(state.event), info, error); } +#ifdef CONFIG_PM_DEBUG static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) { ktime_t calltime; @@ -433,6 +434,9 @@ static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) info ?: "", info ? " " : "", pm_verb(state.event), usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC); } +#else +static inline void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) {} +#endif /* CONFIG_PM_DEBUG */ static int dpm_run_callback(pm_callback_t cb, struct device *dev, pm_message_t state, char *info) -- cgit v1.2.3 From 33e4f80ee69b5168badf37edbfed796eb48434b9 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Mon, 12 Jun 2017 22:56:34 +0200 Subject: ACPI / PM: Ignore spurious SCI wakeups from suspend-to-idle The ACPI SCI (System Control Interrupt) is set up as a wakeup IRQ during suspend-to-idle transitions and, consequently, any events signaled through it wake up the system from that state. However, on some systems some of the events signaled via the ACPI SCI while suspended to idle should not cause the system to wake up. In fact, quite often they should just be discarded. Arguably, systems should not resume entirely on such events, but in order to decide which events really should cause the system to resume and which are spurious, it is necessary to resume up to the point when ACPI SCIs are actually handled and processed, which is after executing dpm_resume_noirq() in the system resume path. For this reasons, add a loop around freeze_enter() in which the platforms can process events signaled via multiplexed IRQ lines like the ACPI SCI and add suspend-to-idle hooks that can be used for this purpose to struct platform_freeze_ops. In the ACPI case, the ->wake hook is used for checking if the SCI has triggered while suspended and deferring the interrupt-induced system wakeup until the events signaled through it are actually processed sufficiently to decide whether or not the system should resume. In turn, the ->sync hook allows all of the relevant event queues to be flushed so as to prevent events from being missed due to race conditions. In addition to that, some ACPI code processing wakeup events needs to be modified to use the "hard" version of wakeup triggers, so that it will cause a system resume to happen on device-induced wakeup events even if the "soft" mechanism to prevent the system from suspending is not enabled. However, to preserve the existing behavior with respect to suspend-to-RAM, this only is done in the suspend-to-idle case and only if an SCI has occurred while suspended. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/battery.c | 2 +- drivers/acpi/button.c | 5 +++-- drivers/acpi/device_pm.c | 9 ++++++++- drivers/acpi/internal.h | 2 ++ drivers/acpi/sleep.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/base/power/main.c | 5 ----- drivers/base/power/wakeup.c | 18 ++++++++++++------ include/acpi/acpi_bus.h | 6 +++++- include/linux/suspend.h | 7 +++++-- kernel/power/process.c | 2 +- kernel/power/suspend.c | 35 +++++++++++++++++++++++++++++------ 11 files changed, 103 insertions(+), 25 deletions(-) (limited to 'drivers/base') diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index d42eeef9d928..1cbb88d938e5 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -782,7 +782,7 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume) if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) || (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) && (battery->capacity_now <= battery->alarm))) - pm_wakeup_event(&battery->device->dev, 0); + acpi_pm_wakeup_event(&battery->device->dev); return result; } diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index e19f530f1083..91cfdf377df7 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -217,7 +217,7 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state) } if (state) - pm_wakeup_event(&device->dev, 0); + acpi_pm_wakeup_event(&device->dev); ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); if (ret == NOTIFY_DONE) @@ -402,7 +402,7 @@ static void acpi_button_notify(struct acpi_device *device, u32 event) } else { int keycode; - pm_wakeup_event(&device->dev, 0); + acpi_pm_wakeup_event(&device->dev); if (button->suspended) break; @@ -534,6 +534,7 @@ static int acpi_button_add(struct acpi_device *device) lid_device = device; } + device_init_wakeup(&device->dev, true); printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device)); return 0; diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index f13c62c4b117..ca0210213773 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "internal.h" @@ -385,6 +386,12 @@ EXPORT_SYMBOL(acpi_bus_power_manageable); #ifdef CONFIG_PM static DEFINE_MUTEX(acpi_pm_notifier_lock); +void acpi_pm_wakeup_event(struct device *dev) +{ + pm_wakeup_dev_event(dev, 0, acpi_s2idle_wakeup()); +} +EXPORT_SYMBOL_GPL(acpi_pm_wakeup_event); + static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used) { struct acpi_device *adev; @@ -399,7 +406,7 @@ static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used) mutex_lock(&acpi_pm_notifier_lock); if (adev->wakeup.flags.notifier_present) { - __pm_wakeup_event(adev->wakeup.ws, 0); + pm_wakeup_ws_event(adev->wakeup.ws, 0, acpi_s2idle_wakeup()); if (adev->wakeup.context.func) adev->wakeup.context.func(&adev->wakeup.context); } diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 66229ffa909b..75924ea69071 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -198,8 +198,10 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit); Suspend/Resume -------------------------------------------------------------------------- */ #ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT +extern bool acpi_s2idle_wakeup(void); extern int acpi_sleep_init(void); #else +static inline bool acpi_s2idle_wakeup(void) { return false; } static inline int acpi_sleep_init(void) { return -ENXIO; } #endif diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index a4782c75ebdd..555de11a56b6 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -650,6 +650,8 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = { .recover = acpi_pm_finish, }; +static bool s2idle_wakeup; + static int acpi_freeze_begin(void) { acpi_scan_lock_acquire(); @@ -666,6 +668,33 @@ static int acpi_freeze_prepare(void) return 0; } +static void acpi_freeze_wake(void) +{ + /* + * If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means + * that the SCI has triggered while suspended, so cancel the wakeup in + * case it has not been a wakeup event (the GPEs will be checked later). + */ + if (acpi_sci_irq_valid() && + !irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) { + pm_system_cancel_wakeup(); + s2idle_wakeup = true; + } +} + +static void acpi_freeze_sync(void) +{ + /* + * Process all pending events in case there are any wakeup ones. + * + * The EC driver uses the system workqueue, so that one needs to be + * flushed too. + */ + acpi_os_wait_events_complete(); + flush_scheduled_work(); + s2idle_wakeup = false; +} + static void acpi_freeze_restore(void) { if (acpi_sci_irq_valid()) @@ -682,6 +711,8 @@ static void acpi_freeze_end(void) static const struct platform_freeze_ops acpi_freeze_ops = { .begin = acpi_freeze_begin, .prepare = acpi_freeze_prepare, + .wake = acpi_freeze_wake, + .sync = acpi_freeze_sync, .restore = acpi_freeze_restore, .end = acpi_freeze_end, }; @@ -700,9 +731,15 @@ static void acpi_sleep_suspend_setup(void) } #else /* !CONFIG_SUSPEND */ +#define s2idle_wakeup (false) static inline void acpi_sleep_suspend_setup(void) {} #endif /* !CONFIG_SUSPEND */ +bool acpi_s2idle_wakeup(void) +{ + return s2idle_wakeup; +} + #ifdef CONFIG_PM_SLEEP static u32 saved_bm_rld; diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 253f860e8981..ef5b6a6e5045 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1095,11 +1095,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a if (async_error) goto Complete; - if (pm_wakeup_pending()) { - async_error = -EBUSY; - goto Complete; - } - if (dev->power.syscore || dev->power.direct_complete) goto Complete; diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index c313b600d356..9c36b27996fc 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -28,8 +28,8 @@ bool events_check_enabled __read_mostly; /* First wakeup IRQ seen by the kernel in the last cycle. */ unsigned int pm_wakeup_irq __read_mostly; -/* If set and the system is suspending, terminate the suspend. */ -static bool pm_abort_suspend __read_mostly; +/* If greater than 0 and the system is suspending, terminate the suspend. */ +static atomic_t pm_abort_suspend __read_mostly; /* * Combined counters of registered wakeup events and wakeup events in progress. @@ -855,20 +855,26 @@ bool pm_wakeup_pending(void) pm_print_active_wakeup_sources(); } - return ret || pm_abort_suspend; + return ret || atomic_read(&pm_abort_suspend) > 0; } void pm_system_wakeup(void) { - pm_abort_suspend = true; + atomic_inc(&pm_abort_suspend); freeze_wake(); } EXPORT_SYMBOL_GPL(pm_system_wakeup); -void pm_wakeup_clear(void) +void pm_system_cancel_wakeup(void) +{ + atomic_dec(&pm_abort_suspend); +} + +void pm_wakeup_clear(bool reset) { - pm_abort_suspend = false; pm_wakeup_irq = 0; + if (reset) + atomic_set(&pm_abort_suspend, 0); } void pm_system_irq_wakeup(unsigned int irq_number) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 79c0af419300..63a90a624a0f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -598,15 +598,19 @@ static inline bool acpi_device_always_present(struct acpi_device *adev) #endif #ifdef CONFIG_PM +void acpi_pm_wakeup_event(struct device *dev); acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, void (*func)(struct acpi_device_wakeup_context *context)); acpi_status acpi_remove_pm_notifier(struct acpi_device *adev); int acpi_pm_device_sleep_state(struct device *, int *, int); int acpi_pm_device_run_wake(struct device *, bool); #else +static inline void acpi_pm_wakeup_event(struct device *dev) +{ +} static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, - void (*work_func)(struct work_struct *work)) + void (*func)(struct acpi_device_wakeup_context *context)) { return AE_SUPPORT; } diff --git a/include/linux/suspend.h b/include/linux/suspend.h index d9718378a8be..0b1cf32edfd7 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -189,6 +189,8 @@ struct platform_suspend_ops { struct platform_freeze_ops { int (*begin)(void); int (*prepare)(void); + void (*wake)(void); + void (*sync)(void); void (*restore)(void); void (*end)(void); }; @@ -428,7 +430,8 @@ extern unsigned int pm_wakeup_irq; extern bool pm_wakeup_pending(void); extern void pm_system_wakeup(void); -extern void pm_wakeup_clear(void); +extern void pm_system_cancel_wakeup(void); +extern void pm_wakeup_clear(bool reset); extern void pm_system_irq_wakeup(unsigned int irq_number); extern bool pm_get_wakeup_count(unsigned int *count, bool block); extern bool pm_save_wakeup_count(unsigned int count); @@ -478,7 +481,7 @@ static inline int unregister_pm_notifier(struct notifier_block *nb) static inline bool pm_wakeup_pending(void) { return false; } static inline void pm_system_wakeup(void) {} -static inline void pm_wakeup_clear(void) {} +static inline void pm_wakeup_clear(bool reset) {} static inline void pm_system_irq_wakeup(unsigned int irq_number) {} static inline void lock_system_sleep(void) {} diff --git a/kernel/power/process.c b/kernel/power/process.c index c7209f060eeb..78672d324a6e 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -132,7 +132,7 @@ int freeze_processes(void) if (!pm_freezing) atomic_inc(&system_freezing_cnt); - pm_wakeup_clear(); + pm_wakeup_clear(true); pr_info("Freezing user space processes ... "); pm_freezing = true; error = try_to_freeze_tasks(true); diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 15e6baef5c73..3ecf275d7e44 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -72,6 +72,8 @@ static void freeze_begin(void) static void freeze_enter(void) { + trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, true); + spin_lock_irq(&suspend_freeze_lock); if (pm_wakeup_pending()) goto out; @@ -84,11 +86,9 @@ static void freeze_enter(void) /* Push all the CPUs into the idle loop. */ wake_up_all_idle_cpus(); - pr_debug("PM: suspend-to-idle\n"); /* Make the current CPU wait so it can enter the idle loop too. */ wait_event(suspend_freeze_wait_head, suspend_freeze_state == FREEZE_STATE_WAKE); - pr_debug("PM: resume from suspend-to-idle\n"); cpuidle_pause(); put_online_cpus(); @@ -98,6 +98,31 @@ static void freeze_enter(void) out: suspend_freeze_state = FREEZE_STATE_NONE; spin_unlock_irq(&suspend_freeze_lock); + + trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, false); +} + +static void s2idle_loop(void) +{ + pr_debug("PM: suspend-to-idle\n"); + + do { + freeze_enter(); + + if (freeze_ops && freeze_ops->wake) + freeze_ops->wake(); + + dpm_resume_noirq(PMSG_RESUME); + if (freeze_ops && freeze_ops->sync) + freeze_ops->sync(); + + if (pm_wakeup_pending()) + break; + + pm_wakeup_clear(false); + } while (!dpm_suspend_noirq(PMSG_SUSPEND)); + + pr_debug("PM: resume from suspend-to-idle\n"); } void freeze_wake(void) @@ -371,10 +396,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) * all the devices are suspended. */ if (state == PM_SUSPEND_FREEZE) { - trace_suspend_resume(TPS("machine_suspend"), state, true); - freeze_enter(); - trace_suspend_resume(TPS("machine_suspend"), state, false); - goto Platform_wake; + s2idle_loop(); + goto Platform_early_resume; } error = disable_nonboot_cpus(); -- cgit v1.2.3 From b4883ca449473e8879a062c1f55f9d062c168ae5 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 16 May 2017 10:52:43 +0530 Subject: PM / Domains: pdd->dev can't be NULL in genpd_dev_pm_qos_notifier() The pm_domain_data (pdd) pointer is set from genpd_alloc_dev_data() and pdd->dev is guaranteed to be valid. There is no need to check pdd and pdd->dev in rest of the code as pdd->dev will always be valid for a non NULL pdd pointer. Signed-off-by: Viresh Kumar Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index ad196427b4f2..bf3945a58cce 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -443,7 +443,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, pdd = dev->power.subsys_data ? dev->power.subsys_data->domain_data : NULL; - if (pdd && pdd->dev) { + if (pdd) { to_gpd_data(pdd)->td.constraint_changed = true; genpd = dev_to_genpd(dev); } else { -- cgit v1.2.3 From c74b32fadc00f2412d86a19295eb867b1a772948 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 23 May 2017 09:32:10 +0530 Subject: PM / OPP: Reorganize _generic_set_opp_regulator() The code was overly complicated here because of the limitations that we had with RCUs (Couldn't use opp-table and OPPs outside RCU protected section and can't call sleep-able routines from within that). But that is long gone now. Reorganize _generic_set_opp_regulator() in order to avoid using "struct dev_pm_set_opp_data" and copying data into it for the case where opp_table->set_opp is not set. Signed-off-by: Viresh Kumar Reviewed-by: Stephen Boyd Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp/core.c | 73 ++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 39 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index dae61720b314..c4590fc017ba 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -543,17 +543,18 @@ _generic_set_opp_clk_only(struct device *dev, struct clk *clk, return ret; } -static int _generic_set_opp(struct dev_pm_set_opp_data *data) +static int _generic_set_opp_regulator(const struct opp_table *opp_table, + struct device *dev, + unsigned long old_freq, + unsigned long freq, + struct dev_pm_opp_supply *old_supply, + struct dev_pm_opp_supply *new_supply) { - struct dev_pm_opp_supply *old_supply = data->old_opp.supplies; - struct dev_pm_opp_supply *new_supply = data->new_opp.supplies; - unsigned long old_freq = data->old_opp.rate, freq = data->new_opp.rate; - struct regulator *reg = data->regulators[0]; - struct device *dev= data->dev; + struct regulator *reg = opp_table->regulators[0]; int ret; /* This function only supports single regulator per device */ - if (WARN_ON(data->regulator_count > 1)) { + if (WARN_ON(opp_table->regulator_count > 1)) { dev_err(dev, "multiple regulators are not supported\n"); return -EINVAL; } @@ -566,7 +567,7 @@ static int _generic_set_opp(struct dev_pm_set_opp_data *data) } /* Change frequency */ - ret = _generic_set_opp_clk_only(dev, data->clk, old_freq, freq); + ret = _generic_set_opp_clk_only(dev, opp_table->clk, old_freq, freq); if (ret) goto restore_voltage; @@ -580,12 +581,12 @@ static int _generic_set_opp(struct dev_pm_set_opp_data *data) return 0; restore_freq: - if (_generic_set_opp_clk_only(dev, data->clk, freq, old_freq)) + if (_generic_set_opp_clk_only(dev, opp_table->clk, freq, old_freq)) dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n", __func__, old_freq); restore_voltage: /* This shouldn't harm even if the voltages weren't updated earlier */ - if (old_supply->u_volt) + if (old_supply) _set_opp_voltage(dev, reg, old_supply); return ret; @@ -603,10 +604,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) { struct opp_table *opp_table; unsigned long freq, old_freq; - int (*set_opp)(struct dev_pm_set_opp_data *data); struct dev_pm_opp *old_opp, *opp; - struct regulator **regulators; - struct dev_pm_set_opp_data *data; struct clk *clk; int ret, size; @@ -661,38 +659,35 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", __func__, old_freq, freq); - regulators = opp_table->regulators; - /* Only frequency scaling */ - if (!regulators) { + if (!opp_table->regulators) { ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq); - goto put_opps; - } + } else if (!opp_table->set_opp) { + ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq, + IS_ERR(old_opp) ? NULL : old_opp->supplies, + opp->supplies); + } else { + struct dev_pm_set_opp_data *data; - if (opp_table->set_opp) - set_opp = opp_table->set_opp; - else - set_opp = _generic_set_opp; - - data = opp_table->set_opp_data; - data->regulators = regulators; - data->regulator_count = opp_table->regulator_count; - data->clk = clk; - data->dev = dev; - - data->old_opp.rate = old_freq; - size = sizeof(*opp->supplies) * opp_table->regulator_count; - if (IS_ERR(old_opp)) - memset(data->old_opp.supplies, 0, size); - else - memcpy(data->old_opp.supplies, old_opp->supplies, size); + data = opp_table->set_opp_data; + data->regulators = opp_table->regulators; + data->regulator_count = opp_table->regulator_count; + data->clk = clk; + data->dev = dev; - data->new_opp.rate = freq; - memcpy(data->new_opp.supplies, opp->supplies, size); + data->old_opp.rate = old_freq; + size = sizeof(*opp->supplies) * opp_table->regulator_count; + if (IS_ERR(old_opp)) + memset(data->old_opp.supplies, 0, size); + else + memcpy(data->old_opp.supplies, old_opp->supplies, size); - ret = set_opp(data); + data->new_opp.rate = freq; + memcpy(data->new_opp.supplies, opp->supplies, size); + + ret = opp_table->set_opp(data); + } -put_opps: dev_pm_opp_put(opp); put_old_opp: if (!IS_ERR(old_opp)) -- cgit v1.2.3 From 478256bddb0323898694bd22acdf42a70d4ff024 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 23 May 2017 09:32:11 +0530 Subject: PM / OPP: Don't create copy of regulators unnecessarily This code was required while the OPP core was managed with help of RCUs, but not anymore. Get rid of unnecessary alloc/memcpy operations. Signed-off-by: Viresh Kumar Reviewed-by: Stephen Boyd Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp/core.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index c4590fc017ba..5ee7aadf0abf 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -180,7 +180,7 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) { struct opp_table *opp_table; struct dev_pm_opp *opp; - struct regulator *reg, **regulators; + struct regulator *reg; unsigned long latency_ns = 0; int ret, i, count; struct { @@ -198,15 +198,9 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) if (!count) goto put_opp_table; - regulators = kmalloc_array(count, sizeof(*regulators), GFP_KERNEL); - if (!regulators) - goto put_opp_table; - uV = kmalloc_array(count, sizeof(*uV), GFP_KERNEL); if (!uV) - goto free_regulators; - - memcpy(regulators, opp_table->regulators, count * sizeof(*regulators)); + goto put_opp_table; mutex_lock(&opp_table->lock); @@ -232,15 +226,13 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) * isn't freed, while we are executing this routine. */ for (i = 0; i < count; i++) { - reg = regulators[i]; + reg = opp_table->regulators[i]; ret = regulator_set_voltage_time(reg, uV[i].min, uV[i].max); if (ret > 0) latency_ns += ret * 1000; } kfree(uV); -free_regulators: - kfree(regulators); put_opp_table: dev_pm_opp_put_opp_table(opp_table); -- cgit v1.2.3 From 688a48b0d27000167e0450bc1243e29a560395ca Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 23 May 2017 09:32:12 +0530 Subject: PM / OPP: opp-microvolt is not optional if regulators are set If dev_pm_opp_set_regulators() is called for a device and its regulators are set in the OPP core, the OPP nodes for the device must contain the "opp-microvolt" property, otherwise there is something wrong and we better error out. Signed-off-by: Viresh Kumar Reviewed-by: Stephen Boyd Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp/of.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp/of.c b/drivers/base/power/opp/of.c index 779428676f63..57eec1ca0569 100644 --- a/drivers/base/power/opp/of.c +++ b/drivers/base/power/opp/of.c @@ -131,8 +131,14 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, prop = of_find_property(opp->np, name, NULL); /* Missing property isn't a problem, but an invalid entry is */ - if (!prop) - return 0; + if (!prop) { + if (!opp_table->regulator_count) + return 0; + + dev_err(dev, "%s: opp-microvolt missing although OPP managing regulators\n", + __func__); + return -EINVAL; + } } vcount = of_property_count_u32_elems(opp->np, name); -- cgit v1.2.3 From 1fae788ed640e2a961c2edab54bfd56c73e2506a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 23 May 2017 09:32:13 +0530 Subject: PM / OPP: Don't create debugfs "supply-0" directory unnecessarily We create "supply-0" debugfs directory even if the device doesn't do voltage scaling. That looks confusing, as if the regulator is found but we never managed to get voltage levels for it. Avoid creating such a directory unnecessarily. Signed-off-by: Viresh Kumar Reviewed-by: Stephen Boyd Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp/debugfs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp/debugfs.c b/drivers/base/power/opp/debugfs.c index 95f433db4ac7..81cf120fcf43 100644 --- a/drivers/base/power/opp/debugfs.c +++ b/drivers/base/power/opp/debugfs.c @@ -40,11 +40,10 @@ static bool opp_debug_create_supplies(struct dev_pm_opp *opp, struct dentry *pdentry) { struct dentry *d; - int i = 0; + int i; char *name; - /* Always create at least supply-0 directory */ - do { + for (i = 0; i < opp_table->regulator_count; i++) { name = kasprintf(GFP_KERNEL, "supply-%d", i); /* Create per-opp directory */ @@ -70,7 +69,7 @@ static bool opp_debug_create_supplies(struct dev_pm_opp *opp, if (!debugfs_create_ulong("u_amp", S_IRUGO, d, &opp->supplies[i].u_amp)) return false; - } while (++i < opp_table->regulator_count); + } return true; } -- cgit v1.2.3 From 829a4e8c0e9aab17bcfe2ddb070388b8ada26292 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 21 Jun 2017 10:29:13 +0530 Subject: PM / OPP: Add dev_pm_opp_{set|put}_clkname() In order to support OPP switching, OPP layer needs to get pointer to the clock for the device. Simple cases work fine without using the routines added by this patch (i.e. by passing connection-id as NULL), but for a device with multiple clocks available, the OPP core needs to know the exact name of the clk to use. Add a new set of APIs to get that done. Tested-by: Rajendra Nayak Signed-off-by: Viresh Kumar Reviewed-by: Stephen Boyd Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp/core.c | 67 +++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 9 ++++++ 2 files changed, 76 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index 5ee7aadf0abf..a8cc14fd8ae4 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -1362,6 +1362,73 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table) } EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators); +/** + * dev_pm_opp_set_clkname() - Set clk name for the device + * @dev: Device for which clk name is being set. + * @name: Clk name. + * + * In order to support OPP switching, OPP layer needs to get pointer to the + * clock for the device. Simple cases work fine without using this routine (i.e. + * by passing connection-id as NULL), but for a device with multiple clocks + * available, the OPP core needs to know the exact name of the clk to use. + * + * This must be called before any OPPs are initialized for the device. + */ +struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name) +{ + struct opp_table *opp_table; + int ret; + + opp_table = dev_pm_opp_get_opp_table(dev); + if (!opp_table) + return ERR_PTR(-ENOMEM); + + /* This should be called before OPPs are initialized */ + if (WARN_ON(!list_empty(&opp_table->opp_list))) { + ret = -EBUSY; + goto err; + } + + /* Already have default clk set, free it */ + if (!IS_ERR(opp_table->clk)) + clk_put(opp_table->clk); + + /* Find clk for the device */ + opp_table->clk = clk_get(dev, name); + if (IS_ERR(opp_table->clk)) { + ret = PTR_ERR(opp_table->clk); + if (ret != -EPROBE_DEFER) { + dev_err(dev, "%s: Couldn't find clock: %d\n", __func__, + ret); + } + goto err; + } + + return opp_table; + +err: + dev_pm_opp_put_opp_table(opp_table); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_set_clkname); + +/** + * dev_pm_opp_put_clkname() - Releases resources blocked for clk. + * @opp_table: OPP table returned from dev_pm_opp_set_clkname(). + */ +void dev_pm_opp_put_clkname(struct opp_table *opp_table) +{ + /* Make sure there are no concurrent readers while updating opp_table */ + WARN_ON(!list_empty(&opp_table->opp_list)); + + clk_put(opp_table->clk); + opp_table->clk = ERR_PTR(-EINVAL); + + dev_pm_opp_put_opp_table(opp_table); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_put_clkname); + /** * dev_pm_opp_register_set_opp_helper() - Register custom set OPP helper * @dev: Device for which the helper is getting registered. diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index a6685b3dde26..51ec727b4824 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -121,6 +121,8 @@ struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) void dev_pm_opp_put_prop_name(struct opp_table *opp_table); struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count); void dev_pm_opp_put_regulators(struct opp_table *opp_table); +struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name); +void dev_pm_opp_put_clkname(struct opp_table *opp_table); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); @@ -257,6 +259,13 @@ static inline struct opp_table *dev_pm_opp_set_regulators(struct device *dev, co static inline void dev_pm_opp_put_regulators(struct opp_table *opp_table) {} +static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name) +{ + return ERR_PTR(-ENOTSUPP); +} + +static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {} + static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) { return -ENOTSUPP; -- cgit v1.2.3 From ea0212f40c6bc0594c8eff79266759e3ecd4bacc Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 25 Jun 2017 19:31:13 +0200 Subject: PM / wakeirq: Convert to SRCU The wakeirq infrastructure uses RCU to protect the list of wakeirqs. That breaks the irq bus locking infrastructure, which is allows sleeping functions to be called so interrupt controllers behind slow busses, e.g. i2c, can be handled. The wakeirq functions hold rcu_read_lock and call into irq functions, which in case of interrupts using the irq bus locking will trigger a might_sleep() splat. Convert the wakeirq infrastructure to Sleepable RCU and unbreak it. Fixes: 4990d4fe327b (PM / Wakeirq: Add automated device wake IRQ handling) Reported-by: Brian Norris Suggested-by: Paul E. McKenney Signed-off-by: Thomas Gleixner Reviewed-by: Paul E. McKenney Tested-by: Tony Lindgren Tested-by: Brian Norris Cc: 4.2+ # 4.2+ Signed-off-by: Rafael J. Wysocki --- drivers/base/power/wakeup.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index c313b600d356..994bbf8b1476 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -60,6 +60,8 @@ static LIST_HEAD(wakeup_sources); static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue); +DEFINE_STATIC_SRCU(wakeup_srcu); + static struct wakeup_source deleted_ws = { .name = "deleted", .lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock), @@ -198,7 +200,7 @@ void wakeup_source_remove(struct wakeup_source *ws) spin_lock_irqsave(&events_lock, flags); list_del_rcu(&ws->entry); spin_unlock_irqrestore(&events_lock, flags); - synchronize_rcu(); + synchronize_srcu(&wakeup_srcu); } EXPORT_SYMBOL_GPL(wakeup_source_remove); @@ -332,12 +334,12 @@ void device_wakeup_detach_irq(struct device *dev) void device_wakeup_arm_wake_irqs(void) { struct wakeup_source *ws; + int srcuidx; - rcu_read_lock(); + srcuidx = srcu_read_lock(&wakeup_srcu); list_for_each_entry_rcu(ws, &wakeup_sources, entry) dev_pm_arm_wake_irq(ws->wakeirq); - - rcu_read_unlock(); + srcu_read_unlock(&wakeup_srcu, srcuidx); } /** @@ -348,12 +350,12 @@ void device_wakeup_arm_wake_irqs(void) void device_wakeup_disarm_wake_irqs(void) { struct wakeup_source *ws; + int srcuidx; - rcu_read_lock(); + srcuidx = srcu_read_lock(&wakeup_srcu); list_for_each_entry_rcu(ws, &wakeup_sources, entry) dev_pm_disarm_wake_irq(ws->wakeirq); - - rcu_read_unlock(); + srcu_read_unlock(&wakeup_srcu, srcuidx); } /** @@ -804,10 +806,10 @@ EXPORT_SYMBOL_GPL(pm_wakeup_dev_event); void pm_print_active_wakeup_sources(void) { struct wakeup_source *ws; - int active = 0; + int srcuidx, active = 0; struct wakeup_source *last_activity_ws = NULL; - rcu_read_lock(); + srcuidx = srcu_read_lock(&wakeup_srcu); list_for_each_entry_rcu(ws, &wakeup_sources, entry) { if (ws->active) { pr_debug("active wakeup source: %s\n", ws->name); @@ -823,7 +825,7 @@ void pm_print_active_wakeup_sources(void) if (!active && last_activity_ws) pr_debug("last active wakeup source: %s\n", last_activity_ws->name); - rcu_read_unlock(); + srcu_read_unlock(&wakeup_srcu, srcuidx); } EXPORT_SYMBOL_GPL(pm_print_active_wakeup_sources); @@ -950,8 +952,9 @@ void pm_wakep_autosleep_enabled(bool set) { struct wakeup_source *ws; ktime_t now = ktime_get(); + int srcuidx; - rcu_read_lock(); + srcuidx = srcu_read_lock(&wakeup_srcu); list_for_each_entry_rcu(ws, &wakeup_sources, entry) { spin_lock_irq(&ws->lock); if (ws->autosleep_enabled != set) { @@ -965,7 +968,7 @@ void pm_wakep_autosleep_enabled(bool set) } spin_unlock_irq(&ws->lock); } - rcu_read_unlock(); + srcu_read_unlock(&wakeup_srcu, srcuidx); } #endif /* CONFIG_PM_AUTOSLEEP */ @@ -1026,15 +1029,16 @@ static int print_wakeup_source_stats(struct seq_file *m, static int wakeup_sources_stats_show(struct seq_file *m, void *unused) { struct wakeup_source *ws; + int srcuidx; seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t" "expire_count\tactive_since\ttotal_time\tmax_time\t" "last_change\tprevent_suspend_time\n"); - rcu_read_lock(); + srcuidx = srcu_read_lock(&wakeup_srcu); list_for_each_entry_rcu(ws, &wakeup_sources, entry) print_wakeup_source_stats(m, ws); - rcu_read_unlock(); + srcu_read_unlock(&wakeup_srcu, srcuidx); print_wakeup_source_stats(m, &deleted_ws); -- cgit v1.2.3 From d8600c8b0cd11d2249e14bf8b2eccbf4fa0db770 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 12 Jun 2017 17:17:41 +0200 Subject: PM / Domains: Constify genpd pointer Mark pointer to struct generic_pm_domain const (either passed in argument or used localy in a function), whenever it is not modifed by the function itself. Signed-off-by: Krzysztof Kozlowski Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index bf3945a58cce..0f7b1bd3680e 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -126,7 +126,7 @@ static const struct genpd_lock_ops genpd_spin_ops = { #define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON) static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, - struct generic_pm_domain *genpd) + const struct generic_pm_domain *genpd) { bool ret; @@ -181,12 +181,14 @@ static struct generic_pm_domain *dev_to_genpd(struct device *dev) return pd_to_genpd(dev->pm_domain); } -static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) +static int genpd_stop_dev(const struct generic_pm_domain *genpd, + struct device *dev) { return GENPD_DEV_CALLBACK(genpd, int, stop, dev); } -static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) +static int genpd_start_dev(const struct generic_pm_domain *genpd, + struct device *dev) { return GENPD_DEV_CALLBACK(genpd, int, start, dev); } @@ -738,7 +740,7 @@ static bool pm_genpd_present(const struct generic_pm_domain *genpd) #ifdef CONFIG_PM_SLEEP -static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, +static bool genpd_dev_active_wakeup(const struct generic_pm_domain *genpd, struct device *dev) { return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); @@ -840,7 +842,8 @@ static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock, * signal remote wakeup from the system's working state as needed by runtime PM. * Return 'true' in either of the above cases. */ -static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) +static bool resume_needed(struct device *dev, + const struct generic_pm_domain *genpd) { bool active_wakeup; @@ -975,7 +978,7 @@ static int pm_genpd_resume_noirq(struct device *dev) */ static int pm_genpd_freeze_noirq(struct device *dev) { - struct generic_pm_domain *genpd; + const struct generic_pm_domain *genpd; int ret = 0; dev_dbg(dev, "%s()\n", __func__); @@ -999,7 +1002,7 @@ static int pm_genpd_freeze_noirq(struct device *dev) */ static int pm_genpd_thaw_noirq(struct device *dev) { - struct generic_pm_domain *genpd; + const struct generic_pm_domain *genpd; int ret = 0; dev_dbg(dev, "%s()\n", __func__); -- cgit v1.2.3 From 952856db9019f914227f5fbe24f7810e20fec0fd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 12 Jun 2017 17:19:31 +0200 Subject: PM: Constify returned PM event name The pm_verb() returns a pointer to string from .rodata so it should be marked as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9faee1c893e5..924a5ba7aa27 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -62,7 +62,7 @@ static pm_message_t pm_transition; static int async_error; -static char *pm_verb(int event) +static const char *pm_verb(int event) { switch (event) { case PM_EVENT_SUSPEND: -- cgit v1.2.3 From e3771fa98e266aba29211c624ecf01200be497ad Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 12 Jun 2017 17:19:32 +0200 Subject: PM: Constify info string used in messages The 'info' string appearing in many places points to a .rodata string so it should be passes as pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 924a5ba7aa27..6add28799f6d 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -208,7 +208,8 @@ static ktime_t initcall_debug_start(struct device *dev) } static void initcall_debug_report(struct device *dev, ktime_t calltime, - int error, pm_message_t state, char *info) + int error, pm_message_t state, + const char *info) { ktime_t rettime; s64 nsecs; @@ -403,21 +404,22 @@ static pm_callback_t pm_noirq_op(const struct dev_pm_ops *ops, pm_message_t stat return NULL; } -static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info) +static void pm_dev_dbg(struct device *dev, pm_message_t state, const char *info) { dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event), ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ? ", may wakeup" : ""); } -static void pm_dev_err(struct device *dev, pm_message_t state, char *info, +static void pm_dev_err(struct device *dev, pm_message_t state, const char *info, int error) { printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n", dev_name(dev), pm_verb(state.event), info, error); } -static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) +static void dpm_show_time(ktime_t starttime, pm_message_t state, + const char *info) { ktime_t calltime; u64 usecs64; @@ -435,7 +437,7 @@ static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) } static int dpm_run_callback(pm_callback_t cb, struct device *dev, - pm_message_t state, char *info) + pm_message_t state, const char *info) { ktime_t calltime; int error; @@ -535,7 +537,7 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd) static int device_resume_noirq(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; - char *info = NULL; + const char *info = NULL; int error = 0; TRACE_DEVICE(dev); @@ -665,7 +667,7 @@ void dpm_resume_noirq(pm_message_t state) static int device_resume_early(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; - char *info = NULL; + const char *info = NULL; int error = 0; TRACE_DEVICE(dev); @@ -793,7 +795,7 @@ EXPORT_SYMBOL_GPL(dpm_resume_start); static int device_resume(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; - char *info = NULL; + const char *info = NULL; int error = 0; DECLARE_DPM_WATCHDOG_ON_STACK(wd); @@ -955,7 +957,7 @@ void dpm_resume(pm_message_t state) static void device_complete(struct device *dev, pm_message_t state) { void (*callback)(struct device *) = NULL; - char *info = NULL; + const char *info = NULL; if (dev->power.syscore) return; @@ -1080,7 +1082,7 @@ static pm_message_t resume_event(pm_message_t sleep_state) static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; - char *info = NULL; + const char *info = NULL; int error = 0; TRACE_DEVICE(dev); @@ -1225,7 +1227,7 @@ int dpm_suspend_noirq(pm_message_t state) static int __device_suspend_late(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; - char *info = NULL; + const char *info = NULL; int error = 0; TRACE_DEVICE(dev); @@ -1384,7 +1386,7 @@ EXPORT_SYMBOL_GPL(dpm_suspend_end); */ static int legacy_suspend(struct device *dev, pm_message_t state, int (*cb)(struct device *dev, pm_message_t state), - char *info) + const char *info) { int error; ktime_t calltime; @@ -1426,7 +1428,7 @@ static void dpm_clear_suppliers_direct_complete(struct device *dev) static int __device_suspend(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; - char *info = NULL; + const char *info = NULL; int error = 0; DECLARE_DPM_WATCHDOG_ON_STACK(wd); -- cgit v1.2.3 From cbcba35dba56f046bbd3437bd275ad3a7156cbc9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 12 Jun 2017 17:19:33 +0200 Subject: PM / sysfs: Constify attribute groups Local instances of struct attribute_group are not modified so they can be made const to increase code safeness. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/base/power/sysfs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 33b4b902741a..185a52581cfa 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -607,7 +607,7 @@ static struct attribute *power_attrs[] = { #endif /* CONFIG_PM_ADVANCED_DEBUG */ NULL, }; -static struct attribute_group pm_attr_group = { +static const struct attribute_group pm_attr_group = { .name = power_group_name, .attrs = power_attrs, }; @@ -629,7 +629,7 @@ static struct attribute *wakeup_attrs[] = { #endif NULL, }; -static struct attribute_group pm_wakeup_attr_group = { +static const struct attribute_group pm_wakeup_attr_group = { .name = power_group_name, .attrs = wakeup_attrs, }; @@ -644,7 +644,7 @@ static struct attribute *runtime_attrs[] = { &dev_attr_autosuspend_delay_ms.attr, NULL, }; -static struct attribute_group pm_runtime_attr_group = { +static const struct attribute_group pm_runtime_attr_group = { .name = power_group_name, .attrs = runtime_attrs, }; @@ -653,7 +653,7 @@ static struct attribute *pm_qos_resume_latency_attrs[] = { &dev_attr_pm_qos_resume_latency_us.attr, NULL, }; -static struct attribute_group pm_qos_resume_latency_attr_group = { +static const struct attribute_group pm_qos_resume_latency_attr_group = { .name = power_group_name, .attrs = pm_qos_resume_latency_attrs, }; @@ -662,7 +662,7 @@ static struct attribute *pm_qos_latency_tolerance_attrs[] = { &dev_attr_pm_qos_latency_tolerance_us.attr, NULL, }; -static struct attribute_group pm_qos_latency_tolerance_attr_group = { +static const struct attribute_group pm_qos_latency_tolerance_attr_group = { .name = power_group_name, .attrs = pm_qos_latency_tolerance_attrs, }; @@ -672,7 +672,7 @@ static struct attribute *pm_qos_flags_attrs[] = { &dev_attr_pm_qos_remote_wakeup.attr, NULL, }; -static struct attribute_group pm_qos_flags_attr_group = { +static const struct attribute_group pm_qos_flags_attr_group = { .name = power_group_name, .attrs = pm_qos_flags_attrs, }; -- cgit v1.2.3 From dbb1d8b70c222544e9b54fdb3e22c15745be0155 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Thu, 22 Jun 2017 16:23:32 +0530 Subject: PM / QoS: constify *_attribute_group. File size before: text data bss dec hex filename 3890 1152 8 5050 13ba drivers/base/power/sysfs.o File size After adding 'const': text data bss dec hex filename 4250 800 8 5058 13c2 drivers/base/power/sysfs.o Signed-off-by: Arvind Yadav Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki --- drivers/base/power/sysfs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 33b4b902741a..185a52581cfa 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -607,7 +607,7 @@ static struct attribute *power_attrs[] = { #endif /* CONFIG_PM_ADVANCED_DEBUG */ NULL, }; -static struct attribute_group pm_attr_group = { +static const struct attribute_group pm_attr_group = { .name = power_group_name, .attrs = power_attrs, }; @@ -629,7 +629,7 @@ static struct attribute *wakeup_attrs[] = { #endif NULL, }; -static struct attribute_group pm_wakeup_attr_group = { +static const struct attribute_group pm_wakeup_attr_group = { .name = power_group_name, .attrs = wakeup_attrs, }; @@ -644,7 +644,7 @@ static struct attribute *runtime_attrs[] = { &dev_attr_autosuspend_delay_ms.attr, NULL, }; -static struct attribute_group pm_runtime_attr_group = { +static const struct attribute_group pm_runtime_attr_group = { .name = power_group_name, .attrs = runtime_attrs, }; @@ -653,7 +653,7 @@ static struct attribute *pm_qos_resume_latency_attrs[] = { &dev_attr_pm_qos_resume_latency_us.attr, NULL, }; -static struct attribute_group pm_qos_resume_latency_attr_group = { +static const struct attribute_group pm_qos_resume_latency_attr_group = { .name = power_group_name, .attrs = pm_qos_resume_latency_attrs, }; @@ -662,7 +662,7 @@ static struct attribute *pm_qos_latency_tolerance_attrs[] = { &dev_attr_pm_qos_latency_tolerance_us.attr, NULL, }; -static struct attribute_group pm_qos_latency_tolerance_attr_group = { +static const struct attribute_group pm_qos_latency_tolerance_attr_group = { .name = power_group_name, .attrs = pm_qos_latency_tolerance_attrs, }; @@ -672,7 +672,7 @@ static struct attribute *pm_qos_flags_attrs[] = { &dev_attr_pm_qos_remote_wakeup.attr, NULL, }; -static struct attribute_group pm_qos_flags_attr_group = { +static const struct attribute_group pm_qos_flags_attr_group = { .name = power_group_name, .attrs = pm_qos_flags_attrs, }; -- cgit v1.2.3 From 10da65423fdbee185da5bb65f829a9d9312c1198 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Thu, 22 Jun 2017 10:18:33 +0300 Subject: PM / Domains: Call driver's noirq callbacks Currently genpd installs its own noirq callbacks, but never calls down to the driver's corresponding callbacks. Add these calls. Signed-off-by: Mikko Perttunen Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 68 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 9 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 0f7b1bd3680e..bbbb1d72395b 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -902,19 +902,19 @@ static int pm_genpd_prepare(struct device *dev) } /** - * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. + * genpd_finish_suspend - Completion of suspend or hibernation of device in an + * I/O pm domain. * @dev: Device to suspend. + * @poweroff: Specifies if this is a poweroff_noirq or suspend_noirq callback. * * Stop the device and remove power from the domain if all devices in it have * been stopped. */ -static int pm_genpd_suspend_noirq(struct device *dev) +static int genpd_finish_suspend(struct device *dev, bool poweroff) { struct generic_pm_domain *genpd; int ret; - dev_dbg(dev, "%s()\n", __func__); - genpd = dev_to_genpd(dev); if (IS_ERR(genpd)) return -EINVAL; @@ -922,6 +922,13 @@ static int pm_genpd_suspend_noirq(struct device *dev) if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) return 0; + if (poweroff) + ret = pm_generic_poweroff_noirq(dev); + else + ret = pm_generic_suspend_noirq(dev); + if (ret) + return ret; + if (genpd->dev_ops.stop && genpd->dev_ops.start) { ret = pm_runtime_force_suspend(dev); if (ret) @@ -936,6 +943,20 @@ static int pm_genpd_suspend_noirq(struct device *dev) return 0; } +/** + * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. + * @dev: Device to suspend. + * + * Stop the device and remove power from the domain if all devices in it have + * been stopped. + */ +static int pm_genpd_suspend_noirq(struct device *dev) +{ + dev_dbg(dev, "%s()\n", __func__); + + return genpd_finish_suspend(dev, false); +} + /** * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. * @dev: Device to resume. @@ -964,6 +985,10 @@ static int pm_genpd_resume_noirq(struct device *dev) if (genpd->dev_ops.stop && genpd->dev_ops.start) ret = pm_runtime_force_resume(dev); + ret = pm_generic_resume_noirq(dev); + if (ret) + return ret; + return ret; } @@ -987,6 +1012,10 @@ static int pm_genpd_freeze_noirq(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; + ret = pm_generic_freeze_noirq(dev); + if (ret) + return ret; + if (genpd->dev_ops.stop && genpd->dev_ops.start) ret = pm_runtime_force_suspend(dev); @@ -1011,10 +1040,28 @@ static int pm_genpd_thaw_noirq(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - if (genpd->dev_ops.stop && genpd->dev_ops.start) + if (genpd->dev_ops.stop && genpd->dev_ops.start) { ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + } - return ret; + return pm_generic_thaw_noirq(dev); +} + +/** + * pm_genpd_poweroff_noirq - Completion of hibernation of device in an + * I/O PM domain. + * @dev: Device to poweroff. + * + * Stop the device and remove power from the domain if all devices in it have + * been stopped. + */ +static int pm_genpd_poweroff_noirq(struct device *dev) +{ + dev_dbg(dev, "%s()\n", __func__); + + return genpd_finish_suspend(dev, true); } /** @@ -1051,10 +1098,13 @@ static int pm_genpd_restore_noirq(struct device *dev) genpd_sync_power_on(genpd, true, 0); genpd_unlock(genpd); - if (genpd->dev_ops.stop && genpd->dev_ops.start) + if (genpd->dev_ops.stop && genpd->dev_ops.start) { ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + } - return ret; + return pm_generic_restore_noirq(dev); } /** @@ -1496,7 +1546,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd, genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; - genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; + genpd->domain.ops.poweroff_noirq = pm_genpd_poweroff_noirq; genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; genpd->domain.ops.complete = pm_genpd_complete; -- cgit v1.2.3 From 8b55e55ee44356d68f4a7ee4b11f9cbb1f5958cc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 Jun 2017 16:56:17 +0200 Subject: PM / Domains: Handle safely genpd_syscore_switch() call on non-genpd device genpd_syscore_switch() had two problems: 1. It silently assumed that device, it is being called for, belongs to generic power domain and used container_of() on its power domain pointer. Such assumption might not be true always. 2. It iterated over list of generic power domains without holding gpd_list_lock mutex thus list could have been modified at the same time. Usage of genpd_lookup_dev() solves both problems as it is safe a call for non-generic power domains and uses mutex when iterating. Reported-by: Ulf Hansson Signed-off-by: Krzysztof Kozlowski Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index bbbb1d72395b..b8d7907ee101 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1148,8 +1148,8 @@ static void genpd_syscore_switch(struct device *dev, bool suspend) { struct generic_pm_domain *genpd; - genpd = dev_to_genpd(dev); - if (!pm_genpd_present(genpd)) + genpd = genpd_lookup_dev(dev); + if (!genpd) return; if (suspend) { -- cgit v1.2.3 From c6e83cac3eda5f7dd32ee1453df2f7abb5c6cd46 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 Jun 2017 16:56:18 +0200 Subject: PM / Domains: Fix unsafe iteration over modified list of device links pm_genpd_remove_subdomain() iterates over domain's master_links list and removes matching element thus it has to use safe version of list iteration. Fixes: f721889ff65a ("PM / Domains: Support for generic I/O PM domains (v8)") Cc: 3.1+ # 3.1+ Signed-off-by: Krzysztof Kozlowski Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index b8d7907ee101..048dc74e3d72 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1446,7 +1446,7 @@ EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *subdomain) { - struct gpd_link *link; + struct gpd_link *l, *link; int ret = -EINVAL; if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) @@ -1462,7 +1462,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, goto out; } - list_for_each_entry(link, &genpd->master_links, master_node) { + list_for_each_entry_safe(link, l, &genpd->master_links, master_node) { if (link->slave != subdomain) continue; -- cgit v1.2.3 From b556b15dc04e9b9b98790f04c21acf5e24f994b2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 Jun 2017 16:56:19 +0200 Subject: PM / Domains: Fix unsafe iteration over modified list of domain providers of_genpd_del_provider() iterates over list of domain provides and removes matching element thus it has to use safe version of list iteration. Fixes: aa42240ab254 (PM / Domains: Add generic OF-based PM domain look-up) Cc: 3.19+ # 3.19+ Signed-off-by: Krzysztof Kozlowski Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 048dc74e3d72..0b836fdc99ad 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1835,12 +1835,12 @@ EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell); */ void of_genpd_del_provider(struct device_node *np) { - struct of_genpd_provider *cp; + struct of_genpd_provider *cp, *tmp; struct generic_pm_domain *gpd; mutex_lock(&gpd_list_lock); mutex_lock(&of_genpd_mutex); - list_for_each_entry(cp, &of_genpd_providers, link) { + list_for_each_entry_safe(cp, tmp, &of_genpd_providers, link) { if (cp->node == np) { /* * For each PM domain associated with the -- cgit v1.2.3 From a7e2d1bce4c1db471f1cbc0c4666a3112bbf0994 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 Jun 2017 16:56:20 +0200 Subject: PM / Domains: Fix unsafe iteration over modified list of domains of_genpd_remove_last() iterates over list of domains and removes matching element thus it has to use safe version of list iteration. Fixes: 17926551c98a (PM / Domains: Add support for removing nested PM domains by provider) Cc: 4.9+ # 4.9+ Signed-off-by: Krzysztof Kozlowski Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 0b836fdc99ad..e342408cfb8d 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1980,14 +1980,14 @@ EXPORT_SYMBOL_GPL(of_genpd_add_subdomain); */ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np) { - struct generic_pm_domain *gpd, *genpd = ERR_PTR(-ENOENT); + struct generic_pm_domain *gpd, *tmp, *genpd = ERR_PTR(-ENOENT); int ret; if (IS_ERR_OR_NULL(np)) return ERR_PTR(-EINVAL); mutex_lock(&gpd_list_lock); - list_for_each_entry(gpd, &gpd_list, gpd_list_node) { + list_for_each_entry_safe(gpd, tmp, &gpd_list, gpd_list_node) { if (gpd->provider == &np->fwnode) { ret = genpd_remove(gpd); genpd = ret ? ERR_PTR(ret) : gpd; -- cgit v1.2.3 From 268cd2ed3d4413a963d765eb6c0b87f06932b971 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 Jun 2017 16:56:21 +0200 Subject: PM / Domains: Fix missing default_power_down_ok comment Commit fc5cbf0c94b6 (PM / Domains: Support for multiple states) split out some code out of default_power_down_ok() function so the documentation has to be moved to appropriate place. Signed-off-by: Krzysztof Kozlowski Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain_governor.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c index 2e0fce711135..281f949c5ffe 100644 --- a/drivers/base/power/domain_governor.c +++ b/drivers/base/power/domain_governor.c @@ -92,12 +92,6 @@ static bool default_suspend_ok(struct device *dev) return td->cached_suspend_ok; } -/** - * default_power_down_ok - Default generic PM domain power off governor routine. - * @pd: PM domain to check. - * - * This routine must be executed under the PM domain's lock. - */ static bool __default_power_down_ok(struct dev_pm_domain *pd, unsigned int state) { @@ -187,6 +181,12 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd, return true; } +/** + * default_power_down_ok - Default generic PM domain power off governor routine. + * @pd: PM domain to check. + * + * This routine must be executed under the PM domain's lock. + */ static bool default_power_down_ok(struct dev_pm_domain *pd) { struct generic_pm_domain *genpd = pd_to_genpd(pd); -- cgit v1.2.3