From 17621e11fda095459e2f986c019f52686c7a4ffb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:25:38 +0200 Subject: ACPI / PM: Drop pm_message_t argument from device suspend callback None of the drivers implementing the ACPI device suspend callback uses the pm_message_t argument of it, so this argument may be dropped entirely from that callback. This will simplify switching the ACPI bus type to PM handling based on struct dev_pm_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 0f0356c..ed1e58d 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -46,7 +46,7 @@ MODULE_LICENSE("GPL"); static int acpi_fan_add(struct acpi_device *device); static int acpi_fan_remove(struct acpi_device *device, int type); -static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state); +static int acpi_fan_suspend(struct acpi_device *device); static int acpi_fan_resume(struct acpi_device *device); static const struct acpi_device_id fan_device_ids[] = { @@ -183,7 +183,7 @@ static int acpi_fan_remove(struct acpi_device *device, int type) return 0; } -static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state) +static int acpi_fan_suspend(struct acpi_device *device) { if (!device) return -EINVAL; diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 47a8caa..e28af8d 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -241,7 +241,7 @@ static void acpi_idle_bm_rld_restore(void) acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, saved_bm_rld); } -int acpi_processor_suspend(struct acpi_device * device, pm_message_t state) +int acpi_processor_suspend(struct acpi_device * device) { if (acpi_idle_suspend == 1) return 0; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c8a1f3b..ec65ec9 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -296,7 +296,7 @@ static int acpi_device_suspend(struct device *dev, pm_message_t state) struct acpi_driver *acpi_drv = acpi_dev->driver; if (acpi_drv && acpi_drv->ops.suspend) - return acpi_drv->ops.suspend(acpi_dev, state); + return acpi_drv->ops.suspend(acpi_dev); return 0; } diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 22b2dfa..c9e0520 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -353,7 +353,7 @@ static int lis3lv02d_remove(struct acpi_device *device, int type) #ifdef CONFIG_PM -static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) +static int lis3lv02d_suspend(struct acpi_device *device) { /* make sure the device is off when we suspend */ lis3lv02d_poweroff(&lis3_dev); diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 210d4ae..2b604f3 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -4243,7 +4243,7 @@ err_free_resources: return result; } -static int sony_pic_suspend(struct acpi_device *device, pm_message_t state) +static int sony_pic_suspend(struct acpi_device *device) { if (sony_pic_disable(device)) return -ENXIO; diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index dab10f6..fd90b6d 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -1296,8 +1296,7 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) } } -static int toshiba_acpi_suspend(struct acpi_device *acpi_dev, - pm_message_t state) +static int toshiba_acpi_suspend(struct acpi_device *acpi_dev) { struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); u32 result; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 9e6e1c6..c2bbec7 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -117,8 +117,7 @@ struct acpi_device; typedef int (*acpi_op_add) (struct acpi_device * device); typedef int (*acpi_op_remove) (struct acpi_device * device, int type); typedef int (*acpi_op_start) (struct acpi_device * device); -typedef int (*acpi_op_suspend) (struct acpi_device * device, - pm_message_t state); +typedef int (*acpi_op_suspend) (struct acpi_device * device); typedef int (*acpi_op_resume) (struct acpi_device * device); typedef int (*acpi_op_bind) (struct acpi_device * device); typedef int (*acpi_op_unbind) (struct acpi_device * device); diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 9d65047..8a1894a 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -334,7 +334,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr); int acpi_processor_hotplug(struct acpi_processor *pr); int acpi_processor_power_exit(struct acpi_processor *pr, struct acpi_device *device); -int acpi_processor_suspend(struct acpi_device * device, pm_message_t state); +int acpi_processor_suspend(struct acpi_device * device); int acpi_processor_resume(struct acpi_device * device); extern struct cpuidle_driver acpi_idle_driver; -- cgit v0.10.2 From 707156e600dbfd89e129239ee1d1d934cbe2119c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:25:49 +0200 Subject: ACPI / PM: Make acpi_bus_type use struct dev_pm_ops for PM handling Make the acpi_bus_type bus type define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct bus_type. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ec65ec9..7c37be5 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -290,7 +290,7 @@ static void acpi_device_release(struct device *dev) kfree(acpi_dev); } -static int acpi_device_suspend(struct device *dev, pm_message_t state) +static int acpi_device_suspend(struct device *dev) { struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_driver *acpi_drv = acpi_dev->driver; @@ -310,6 +310,8 @@ static int acpi_device_resume(struct device *dev) return 0; } +static SIMPLE_DEV_PM_OPS(acpi_bus_pm, acpi_device_suspend, acpi_device_resume); + static int acpi_bus_match(struct device *dev, struct device_driver *drv) { struct acpi_device *acpi_dev = to_acpi_device(dev); @@ -441,12 +443,11 @@ static int acpi_device_remove(struct device * dev) struct bus_type acpi_bus_type = { .name = "acpi", - .suspend = acpi_device_suspend, - .resume = acpi_device_resume, .match = acpi_bus_match, .probe = acpi_device_probe, .remove = acpi_device_remove, .uevent = acpi_device_uevent, + .pm = &acpi_bus_pm, }; static int acpi_device_register(struct acpi_device *device) -- cgit v0.10.2 From 67699c5f0c68f5dc49e92b172d372c99cdd2bf09 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:25:59 +0200 Subject: ACPI / PM: Make acpi_bus_type use driver struct dev_pm_ops callbacks Modify acpi_bus_type so that it executes PM callbacks provided by drivers through their struct dev_pm_ops objects, if present, while still allowing the legacy ACPI PM callbacks to take precedence. This will make it possible to convert ACPI drivers one by one to handling PM through struct dev_pm_ops instead of the legacy way. The code added by this change is temporary and will be removed when all of the drivers in question have been switched over to the PM handling based on struct dev_pm_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7c37be5..af924ba 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -290,27 +290,61 @@ static void acpi_device_release(struct device *dev) kfree(acpi_dev); } -static int acpi_device_suspend(struct device *dev) +#define ACPI_DEV_PM_CALLBACK(dev, callback, legacy_cb) \ +({ \ + struct acpi_device *__acpi_dev = to_acpi_device(dev); \ + struct acpi_driver *__acpi_drv = __acpi_dev->driver; \ + struct device_driver *__drv = dev->driver; \ + int __ret; \ + \ + if (__acpi_drv && __acpi_drv->ops.legacy_cb) \ + __ret = __acpi_drv->ops.legacy_cb(__acpi_dev); \ + else if (__drv && __drv->pm && __drv->pm->callback) \ + __ret = __drv->pm->callback(dev); \ + else \ + __ret = 0; \ + \ + __ret; \ +}) + +static int acpi_pm_suspend(struct device *dev) { - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_driver *acpi_drv = acpi_dev->driver; + return ACPI_DEV_PM_CALLBACK(dev, suspend, suspend); +} - if (acpi_drv && acpi_drv->ops.suspend) - return acpi_drv->ops.suspend(acpi_dev); - return 0; +static int acpi_pm_resume(struct device *dev) +{ + return ACPI_DEV_PM_CALLBACK(dev, resume, resume); } -static int acpi_device_resume(struct device *dev) +static int acpi_pm_freeze(struct device *dev) { - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_driver *acpi_drv = acpi_dev->driver; + return ACPI_DEV_PM_CALLBACK(dev, freeze, suspend); +} - if (acpi_drv && acpi_drv->ops.resume) - return acpi_drv->ops.resume(acpi_dev); - return 0; +static int acpi_pm_thaw(struct device *dev) +{ + return ACPI_DEV_PM_CALLBACK(dev, thaw, resume); +} + +static int acpi_pm_poweroff(struct device *dev) +{ + return ACPI_DEV_PM_CALLBACK(dev, poweroff, suspend); } -static SIMPLE_DEV_PM_OPS(acpi_bus_pm, acpi_device_suspend, acpi_device_resume); +static int acpi_pm_restore(struct device *dev) +{ + return ACPI_DEV_PM_CALLBACK(dev, restore, resume); +} + +static const struct dev_pm_ops acpi_bus_pm = { + .suspend = acpi_pm_suspend, + .resume = acpi_pm_resume, + .freeze = acpi_pm_freeze, + .thaw = acpi_pm_thaw, + .poweroff = acpi_pm_poweroff, + .restore = acpi_pm_restore, +}; static int acpi_bus_match(struct device *dev, struct device_driver *drv) { -- cgit v0.10.2 From 62fcbdd95ccabcff7b41a859032496f6ca492045 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:07 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the fan driver Make the ACPI fan driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index ed1e58d..669d9ee 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -46,8 +46,6 @@ MODULE_LICENSE("GPL"); static int acpi_fan_add(struct acpi_device *device); static int acpi_fan_remove(struct acpi_device *device, int type); -static int acpi_fan_suspend(struct acpi_device *device); -static int acpi_fan_resume(struct acpi_device *device); static const struct acpi_device_id fan_device_ids[] = { {"PNP0C0B", 0}, @@ -55,6 +53,10 @@ static const struct acpi_device_id fan_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, fan_device_ids); +static int acpi_fan_suspend(struct device *dev); +static int acpi_fan_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(acpi_fan_pm, acpi_fan_suspend, acpi_fan_resume); + static struct acpi_driver acpi_fan_driver = { .name = "fan", .class = ACPI_FAN_CLASS, @@ -62,9 +64,8 @@ static struct acpi_driver acpi_fan_driver = { .ops = { .add = acpi_fan_add, .remove = acpi_fan_remove, - .suspend = acpi_fan_suspend, - .resume = acpi_fan_resume, }, + .drv.pm = &acpi_fan_pm, }; /* thermal cooling device callbacks */ @@ -183,24 +184,24 @@ static int acpi_fan_remove(struct acpi_device *device, int type) return 0; } -static int acpi_fan_suspend(struct acpi_device *device) +static int acpi_fan_suspend(struct device *dev) { - if (!device) + if (!dev) return -EINVAL; - acpi_bus_set_power(device->handle, ACPI_STATE_D0); + acpi_bus_set_power(to_acpi_device(dev)->handle, ACPI_STATE_D0); return AE_OK; } -static int acpi_fan_resume(struct acpi_device *device) +static int acpi_fan_resume(struct device *dev) { int result; - if (!device) + if (!dev) return -EINVAL; - result = acpi_bus_update_power(device->handle, NULL); + result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL); if (result) printk(KERN_ERR PREFIX "Error updating fan power state\n"); -- cgit v0.10.2 From 167cffb646aa4a7f3267b07eae1af16b54bc0e9b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:18 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the thermal driver Make the ACPI thermal driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 7dbebea..21dd4c2 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -98,7 +98,6 @@ MODULE_PARM_DESC(psv, "Disable or override all passive trip points."); static int acpi_thermal_add(struct acpi_device *device); static int acpi_thermal_remove(struct acpi_device *device, int type); -static int acpi_thermal_resume(struct acpi_device *device); static void acpi_thermal_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id thermal_device_ids[] = { @@ -107,6 +106,9 @@ static const struct acpi_device_id thermal_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, thermal_device_ids); +static int acpi_thermal_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, NULL, acpi_thermal_resume); + static struct acpi_driver acpi_thermal_driver = { .name = "thermal", .class = ACPI_THERMAL_CLASS, @@ -114,9 +116,9 @@ static struct acpi_driver acpi_thermal_driver = { .ops = { .add = acpi_thermal_add, .remove = acpi_thermal_remove, - .resume = acpi_thermal_resume, .notify = acpi_thermal_notify, }, + .drv.pm = &acpi_thermal_pm, }; struct acpi_thermal_state { @@ -1041,16 +1043,17 @@ static int acpi_thermal_remove(struct acpi_device *device, int type) return 0; } -static int acpi_thermal_resume(struct acpi_device *device) +static int acpi_thermal_resume(struct device *dev) { - struct acpi_thermal *tz = NULL; + struct acpi_thermal *tz; int i, j, power_state, result; - - if (!device || !acpi_driver_data(device)) + if (!dev) return -EINVAL; - tz = acpi_driver_data(device); + tz = acpi_driver_data(to_acpi_device(dev)); + if (!tz) + return -EINVAL; for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { if (!(&tz->trips.active[i])) -- cgit v0.10.2 From e8110b64af8b7cce96d1878276770c76cb9c01d5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:26 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in processor driver Make the ACPI processor driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 0734086..13103aeb 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -93,6 +93,9 @@ static const struct acpi_device_id processor_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, processor_device_ids); +static SIMPLE_DEV_PM_OPS(acpi_processor_pm, + acpi_processor_suspend, acpi_processor_resume); + static struct acpi_driver acpi_processor_driver = { .name = "processor", .class = ACPI_PROCESSOR_CLASS, @@ -100,10 +103,9 @@ static struct acpi_driver acpi_processor_driver = { .ops = { .add = acpi_processor_add, .remove = acpi_processor_remove, - .suspend = acpi_processor_suspend, - .resume = acpi_processor_resume, .notify = acpi_processor_notify, }, + .drv.pm = &acpi_processor_pm, }; #define INSTALL_NOTIFY_HANDLER 1 diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index e28af8d..6d9ec3e 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -241,7 +241,7 @@ static void acpi_idle_bm_rld_restore(void) acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, saved_bm_rld); } -int acpi_processor_suspend(struct acpi_device * device) +int acpi_processor_suspend(struct device *dev) { if (acpi_idle_suspend == 1) return 0; @@ -251,7 +251,7 @@ int acpi_processor_suspend(struct acpi_device * device) return 0; } -int acpi_processor_resume(struct acpi_device * device) +int acpi_processor_resume(struct device *dev) { if (acpi_idle_suspend == 0) return 0; diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 8a1894a..ac3bff6 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -334,8 +334,8 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr); int acpi_processor_hotplug(struct acpi_processor *pr); int acpi_processor_power_exit(struct acpi_processor *pr, struct acpi_device *device); -int acpi_processor_suspend(struct acpi_device * device); -int acpi_processor_resume(struct acpi_device * device); +int acpi_processor_suspend(struct device *dev); +int acpi_processor_resume(struct device *dev); extern struct cpuidle_driver acpi_idle_driver; /* in processor_thermal.c */ -- cgit v0.10.2 From ccda70698057fd8dae7beeac147cdeead33dd543 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:35 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the AC driver Make the ACPI AC adapter driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 6512b20..ff9f6bd 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -61,7 +61,6 @@ static int acpi_ac_open_fs(struct inode *inode, struct file *file); static int acpi_ac_add(struct acpi_device *device); static int acpi_ac_remove(struct acpi_device *device, int type); -static int acpi_ac_resume(struct acpi_device *device); static void acpi_ac_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id ac_device_ids[] = { @@ -70,6 +69,9 @@ static const struct acpi_device_id ac_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, ac_device_ids); +static int acpi_ac_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume); + static struct acpi_driver acpi_ac_driver = { .name = "ac", .class = ACPI_AC_CLASS, @@ -78,9 +80,9 @@ static struct acpi_driver acpi_ac_driver = { .ops = { .add = acpi_ac_add, .remove = acpi_ac_remove, - .resume = acpi_ac_resume, .notify = acpi_ac_notify, }, + .drv.pm = &acpi_ac_pm, }; struct acpi_ac { @@ -309,13 +311,18 @@ static int acpi_ac_add(struct acpi_device *device) return result; } -static int acpi_ac_resume(struct acpi_device *device) +static int acpi_ac_resume(struct device *dev) { struct acpi_ac *ac; unsigned old_state; - if (!device || !acpi_driver_data(device)) + + if (!dev) return -EINVAL; - ac = acpi_driver_data(device); + + ac = acpi_driver_data(to_acpi_device(dev)); + if (!ac) + return -EINVAL; + old_state = ac->state; if (acpi_ac_get_state(ac)) return 0; -- cgit v0.10.2 From a6f50dc8ef26819c510cda84477dd60e3d1c90bb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:43 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the battery driver Make the ACPI battery driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 7dd3f9f..023f9c8 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1044,17 +1044,24 @@ static int acpi_battery_remove(struct acpi_device *device, int type) } /* this is needed to learn about changes made in suspended state */ -static int acpi_battery_resume(struct acpi_device *device) +static int acpi_battery_resume(struct device *dev) { struct acpi_battery *battery; - if (!device) + + if (!dev) return -EINVAL; - battery = acpi_driver_data(device); + + battery = acpi_driver_data(to_acpi_device(dev)); + if (!battery) + return -EINVAL; + battery->update_time = 0; acpi_battery_update(battery); return 0; } +static SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume); + static struct acpi_driver acpi_battery_driver = { .name = "battery", .class = ACPI_BATTERY_CLASS, @@ -1062,10 +1069,10 @@ static struct acpi_driver acpi_battery_driver = { .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { .add = acpi_battery_add, - .resume = acpi_battery_resume, .remove = acpi_battery_remove, .notify = acpi_battery_notify, }, + .drv.pm = &acpi_battery_pm, }; static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie) -- cgit v0.10.2 From 1be532de834ab7b18ce20dc2e56e724770beb79d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:51 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the button driver Make the ACPI button driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index d27d072..79d4c22 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -76,19 +76,21 @@ MODULE_DEVICE_TABLE(acpi, button_device_ids); static int acpi_button_add(struct acpi_device *device); static int acpi_button_remove(struct acpi_device *device, int type); -static int acpi_button_resume(struct acpi_device *device); static void acpi_button_notify(struct acpi_device *device, u32 event); +static int acpi_button_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(acpi_button_pm, NULL, acpi_button_resume); + static struct acpi_driver acpi_button_driver = { .name = "button", .class = ACPI_BUTTON_CLASS, .ids = button_device_ids, .ops = { .add = acpi_button_add, - .resume = acpi_button_resume, .remove = acpi_button_remove, .notify = acpi_button_notify, }, + .drv.pm = &acpi_button_pm, }; struct acpi_button { @@ -308,8 +310,9 @@ static void acpi_button_notify(struct acpi_device *device, u32 event) } } -static int acpi_button_resume(struct acpi_device *device) +static int acpi_button_resume(struct device *dev) { + struct acpi_device *device = to_acpi_device(dev); struct acpi_button *button = acpi_driver_data(device); if (button->type == ACPI_BUTTON_TYPE_LID) -- cgit v0.10.2 From e579e2dd8f056cfeaf4d2b313a35ddafb3c4e531 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:59 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the power driver Make the ACPI power resource driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index dd6d6a3..894d45c 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -60,7 +60,6 @@ ACPI_MODULE_NAME("power"); static int acpi_power_add(struct acpi_device *device); static int acpi_power_remove(struct acpi_device *device, int type); -static int acpi_power_resume(struct acpi_device *device); static const struct acpi_device_id power_device_ids[] = { {ACPI_POWER_HID, 0}, @@ -68,6 +67,9 @@ static const struct acpi_device_id power_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, power_device_ids); +static int acpi_power_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(acpi_power_pm, NULL, acpi_power_resume); + static struct acpi_driver acpi_power_driver = { .name = "power", .class = ACPI_POWER_CLASS, @@ -75,8 +77,8 @@ static struct acpi_driver acpi_power_driver = { .ops = { .add = acpi_power_add, .remove = acpi_power_remove, - .resume = acpi_power_resume, }, + .drv.pm = &acpi_power_pm, }; /* @@ -771,14 +773,16 @@ static int acpi_power_remove(struct acpi_device *device, int type) return 0; } -static int acpi_power_resume(struct acpi_device *device) +static int acpi_power_resume(struct device *dev) { int result = 0, state; + struct acpi_device *device; struct acpi_power_resource *resource; - if (!device) + if (!dev) return -EINVAL; + device = to_acpi_device(dev); resource = acpi_driver_data(device); if (!resource) return -EINVAL; -- cgit v0.10.2 From d202f77d2eb707d03a16adf952a397675cb2fc81 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:07 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the SBS driver Make the ACPI Smart Battery System driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 6e36d0c..c0b9aa5 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -988,16 +988,18 @@ static void acpi_sbs_rmdirs(void) #endif } -static int acpi_sbs_resume(struct acpi_device *device) +static int acpi_sbs_resume(struct device *dev) { struct acpi_sbs *sbs; - if (!device) + if (!dev) return -EINVAL; - sbs = device->driver_data; + sbs = to_acpi_device(dev)->driver_data; acpi_sbs_callback(sbs); return 0; } +static SIMPLE_DEV_PM_OPS(acpi_sbs_pm, NULL, acpi_sbs_resume); + static struct acpi_driver acpi_sbs_driver = { .name = "sbs", .class = ACPI_SBS_CLASS, @@ -1005,8 +1007,8 @@ static struct acpi_driver acpi_sbs_driver = { .ops = { .add = acpi_sbs_add, .remove = acpi_sbs_remove, - .resume = acpi_sbs_resume, }, + .drv.pm = &acpi_sbs_pm, }; static int __init acpi_sbs_init(void) -- cgit v0.10.2 From 43d2fd3b9d5be9be879179bec4f08c0c47d1ccd7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:16 +0200 Subject: toshiba_acpi: Use struct dev_pm_ops for power management Make the toshiba_acpi driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index fd90b6d..c13ba5b 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -1296,9 +1296,9 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) } } -static int toshiba_acpi_suspend(struct acpi_device *acpi_dev) +static int toshiba_acpi_suspend(struct device *device) { - struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); u32 result; if (dev->hotkey_dev) @@ -1307,9 +1307,9 @@ static int toshiba_acpi_suspend(struct acpi_device *acpi_dev) return 0; } -static int toshiba_acpi_resume(struct acpi_device *acpi_dev) +static int toshiba_acpi_resume(struct device *device) { - struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); u32 result; if (dev->hotkey_dev) @@ -1318,6 +1318,9 @@ static int toshiba_acpi_resume(struct acpi_device *acpi_dev) return 0; } +static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm, + toshiba_acpi_suspend, toshiba_acpi_resume); + static struct acpi_driver toshiba_acpi_driver = { .name = "Toshiba ACPI driver", .owner = THIS_MODULE, @@ -1327,9 +1330,8 @@ static struct acpi_driver toshiba_acpi_driver = { .add = toshiba_acpi_add, .remove = toshiba_acpi_remove, .notify = toshiba_acpi_notify, - .suspend = toshiba_acpi_suspend, - .resume = toshiba_acpi_resume, }, + .drv.pm = &toshiba_acpi_pm, }; static int __init toshiba_acpi_init(void) -- cgit v0.10.2 From 4ce05b42603195386fa783c4f070ecc8682c189f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:25 +0200 Subject: hp_accel: Use struct dev_pm_ops for power management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the hp_accel driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki Reviewed-by: Éric Piel diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index c9e0520..f4d9115 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -353,20 +353,22 @@ static int lis3lv02d_remove(struct acpi_device *device, int type) #ifdef CONFIG_PM -static int lis3lv02d_suspend(struct acpi_device *device) +static int lis3lv02d_suspend(struct device *dev) { /* make sure the device is off when we suspend */ lis3lv02d_poweroff(&lis3_dev); return 0; } -static int lis3lv02d_resume(struct acpi_device *device) +static int lis3lv02d_resume(struct device *dev) { return lis3lv02d_poweron(&lis3_dev); } + +static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume); +#define HP_ACCEL_PM (&hp_accel_pm) #else -#define lis3lv02d_suspend NULL -#define lis3lv02d_resume NULL +#define HP_ACCEL_PM NULL #endif /* For the HP MDPS aka 3D Driveguard */ @@ -377,9 +379,8 @@ static struct acpi_driver lis3lv02d_driver = { .ops = { .add = lis3lv02d_add, .remove = lis3lv02d_remove, - .suspend = lis3lv02d_suspend, - .resume = lis3lv02d_resume, - } + }, + .drv.pm = HP_ACCEL_PM, }; static int __init lis3lv02d_init_module(void) -- cgit v0.10.2 From bb6b98d6e4f9aada309c54ebd6df1cc6151f3af1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:33 +0200 Subject: sony-laptop: Use struct dev_pm_ops for power management Make the sony-laptop driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 2b604f3..c7a2362 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1452,7 +1452,7 @@ static void sony_nc_function_resume(void) &result); } -static int sony_nc_resume(struct acpi_device *device) +static int sony_nc_resume(struct device *dev) { struct sony_nc_value *item; acpi_handle handle; @@ -1484,6 +1484,8 @@ static int sony_nc_resume(struct acpi_device *device) return 0; } +static SIMPLE_DEV_PM_OPS(sony_nc_pm, NULL, sony_nc_resume); + static void sony_nc_rfkill_cleanup(void) { int i; @@ -2728,9 +2730,9 @@ static struct acpi_driver sony_nc_driver = { .ops = { .add = sony_nc_add, .remove = sony_nc_remove, - .resume = sony_nc_resume, .notify = sony_nc_notify, }, + .drv.pm = &sony_nc_pm, }; /*********** SPIC (SNY6001) Device ***********/ @@ -4243,19 +4245,22 @@ err_free_resources: return result; } -static int sony_pic_suspend(struct acpi_device *device) +static int sony_pic_suspend(struct device *dev) { - if (sony_pic_disable(device)) + if (sony_pic_disable(to_acpi_device(dev))) return -ENXIO; return 0; } -static int sony_pic_resume(struct acpi_device *device) +static int sony_pic_resume(struct device *dev) { - sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); + sony_pic_enable(to_acpi_device(dev), + spic_dev.cur_ioport, spic_dev.cur_irq); return 0; } +static SIMPLE_DEV_PM_OPS(sony_pic_pm, sony_pic_suspend, sony_pic_resume); + static const struct acpi_device_id sony_pic_device_ids[] = { {SONY_PIC_HID, 0}, {"", 0}, @@ -4269,9 +4274,8 @@ static struct acpi_driver sony_pic_driver = { .ops = { .add = sony_pic_add, .remove = sony_pic_remove, - .suspend = sony_pic_suspend, - .resume = sony_pic_resume, }, + .drv.pm = &sony_pic_pm, }; static struct dmi_system_id __initdata sonypi_dmi_table[] = { -- cgit v0.10.2 From a7377419624c31d71e35485251f8548c728d5999 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:40 +0200 Subject: panasonic-laptop: Use struct dev_pm_ops for power management Make the panasonic-laptop driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index ffff8b4..2448007 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -177,7 +177,6 @@ enum SINF_BITS { SINF_NUM_BATTERIES = 0, static int acpi_pcc_hotkey_add(struct acpi_device *device); static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type); -static int acpi_pcc_hotkey_resume(struct acpi_device *device); static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id pcc_device_ids[] = { @@ -189,6 +188,9 @@ static const struct acpi_device_id pcc_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, pcc_device_ids); +static int acpi_pcc_hotkey_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume); + static struct acpi_driver acpi_pcc_driver = { .name = ACPI_PCC_DRIVER_NAME, .class = ACPI_PCC_CLASS, @@ -196,9 +198,9 @@ static struct acpi_driver acpi_pcc_driver = { .ops = { .add = acpi_pcc_hotkey_add, .remove = acpi_pcc_hotkey_remove, - .resume = acpi_pcc_hotkey_resume, .notify = acpi_pcc_hotkey_notify, }, + .drv.pm = &acpi_pcc_hotkey_pm, }; static const struct key_entry panasonic_keymap[] = { @@ -538,11 +540,15 @@ static void acpi_pcc_destroy_input(struct pcc_acpi *pcc) /* kernel module interface */ -static int acpi_pcc_hotkey_resume(struct acpi_device *device) +static int acpi_pcc_hotkey_resume(struct device *dev) { - struct pcc_acpi *pcc = acpi_driver_data(device); + struct pcc_acpi *pcc; + + if (!dev) + return -EINVAL; - if (device == NULL || pcc == NULL) + pcc = acpi_driver_data(to_acpi_device(dev)); + if (!pcc) return -EINVAL; ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n", -- cgit v0.10.2 From d69239ae5847b29ea1902b2dd86f5afa41144ba3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:48 +0200 Subject: toshiba_bluetooth: Use struct dev_pm_ops for power management Make the toshiba_bluetooth driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki Reviewed-by: Vikram Dhillon diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index 5fb7186..715a43c 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -34,7 +34,6 @@ MODULE_LICENSE("GPL"); static int toshiba_bt_rfkill_add(struct acpi_device *device); static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type); static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event); -static int toshiba_bt_resume(struct acpi_device *device); static const struct acpi_device_id bt_device_ids[] = { { "TOS6205", 0}, @@ -42,6 +41,9 @@ static const struct acpi_device_id bt_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, bt_device_ids); +static int toshiba_bt_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume); + static struct acpi_driver toshiba_bt_rfkill_driver = { .name = "Toshiba BT", .class = "Toshiba", @@ -50,9 +52,9 @@ static struct acpi_driver toshiba_bt_rfkill_driver = { .add = toshiba_bt_rfkill_add, .remove = toshiba_bt_rfkill_remove, .notify = toshiba_bt_rfkill_notify, - .resume = toshiba_bt_resume, }, .owner = THIS_MODULE, + .drv.pm = &toshiba_bt_pm, }; @@ -88,9 +90,9 @@ static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event) toshiba_bluetooth_enable(device->handle); } -static int toshiba_bt_resume(struct acpi_device *device) +static int toshiba_bt_resume(struct device *dev) { - return toshiba_bluetooth_enable(device->handle); + return toshiba_bluetooth_enable(to_acpi_device(dev)->handle); } static int toshiba_bt_rfkill_add(struct acpi_device *device) -- cgit v0.10.2 From 44cb98c48e19f506e178c519f5da2c23a156e002 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:55 +0200 Subject: xo15-ebook: Use struct dev_pm_ops for power management Make the xo15-ebook driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index fad153d..849c07c 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -77,11 +77,13 @@ static void ebook_switch_notify(struct acpi_device *device, u32 event) } } -static int ebook_switch_resume(struct acpi_device *device) +static int ebook_switch_resume(struct device *dev) { - return ebook_send_state(device); + return ebook_send_state(to_acpi_device(dev)); } +static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume); + static int ebook_switch_add(struct acpi_device *device) { struct ebook_switch *button; @@ -161,10 +163,10 @@ static struct acpi_driver xo15_ebook_driver = { .ids = ebook_device_ids, .ops = { .add = ebook_switch_add, - .resume = ebook_switch_resume, .remove = ebook_switch_remove, .notify = ebook_switch_notify, }, + .drv.pm = &ebook_switch_pm, }; static int __init xo15_ebook_init(void) -- cgit v0.10.2 From 81bc495401955bf7a62d04f8c794718476a2b093 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 29 Jun 2012 23:39:48 +0200 Subject: classmate-laptop: Use struct dev_pm_ops for power management Make the classmate-laptop driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 94f93b6..e2230a2 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -362,15 +362,18 @@ static int cmpc_tablet_remove(struct acpi_device *acpi, int type) return cmpc_remove_acpi_notify_device(acpi); } -static int cmpc_tablet_resume(struct acpi_device *acpi) +static int cmpc_tablet_resume(struct device *dev) { - struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); + struct input_dev *inputdev = dev_get_drvdata(dev); + unsigned long long val = 0; - if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) + if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) input_report_switch(inputdev, SW_TABLET_MODE, !val); return 0; } +static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume); + static const struct acpi_device_id cmpc_tablet_device_ids[] = { {CMPC_TABLET_HID, 0}, {"", 0} @@ -384,9 +387,9 @@ static struct acpi_driver cmpc_tablet_acpi_driver = { .ops = { .add = cmpc_tablet_add, .remove = cmpc_tablet_remove, - .resume = cmpc_tablet_resume, .notify = cmpc_tablet_handler, - } + }, + .drv.pm = &cmpc_tablet_pm, }; -- cgit v0.10.2 From d26c844bc1c5e9745deffccae8d205e9b77a0fb1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 29 Jun 2012 23:39:57 +0200 Subject: fujitsu-tablet: Use struct dev_pm_ops for power management Make the fujitsu-tablet driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index da267ea..d2e4173 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -440,12 +440,14 @@ static int __devexit acpi_fujitsu_remove(struct acpi_device *adev, int type) return 0; } -static int acpi_fujitsu_resume(struct acpi_device *adev) +static int acpi_fujitsu_resume(struct device *dev) { fujitsu_reset(); return 0; } +static SIMPLE_DEV_PM_OPS(acpi_fujitsu_pm, NULL, acpi_fujitsu_resume); + static struct acpi_driver acpi_fujitsu_driver = { .name = MODULENAME, .class = "hotkey", @@ -453,8 +455,8 @@ static struct acpi_driver acpi_fujitsu_driver = { .ops = { .add = acpi_fujitsu_add, .remove = acpi_fujitsu_remove, - .resume = acpi_fujitsu_resume, - } + }, + .drv.pm = &acpi_fujitsu_pm, }; static int __init fujitsu_module_init(void) -- cgit v0.10.2 From c5dec0182256361a3f823316e8fb85263f76efe7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 29 Jun 2012 23:40:05 +0200 Subject: acpi_power_meter: Use struct dev_pm_ops for power management Make the ACPI power meter driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 34ad5a2..e3fcf81 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -929,20 +929,25 @@ static int acpi_power_meter_remove(struct acpi_device *device, int type) return 0; } -static int acpi_power_meter_resume(struct acpi_device *device) +static int acpi_power_meter_resume(struct device *dev) { struct acpi_power_meter_resource *resource; - if (!device || !acpi_driver_data(device)) + if (!dev) + return -EINVAL; + + resource = acpi_driver_data(to_acpi_device(dev)); + if (!resource) return -EINVAL; - resource = acpi_driver_data(device); free_capabilities(resource); read_capabilities(resource); return 0; } +static SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL, acpi_power_meter_resume); + static struct acpi_driver acpi_power_meter_driver = { .name = "power_meter", .class = ACPI_POWER_METER_CLASS, @@ -950,9 +955,9 @@ static struct acpi_driver acpi_power_meter_driver = { .ops = { .add = acpi_power_meter_add, .remove = acpi_power_meter_remove, - .resume = acpi_power_meter_resume, .notify = acpi_power_meter_notify, }, + .drv.pm = &acpi_power_meter_pm, }; /* Module init/exit routines */ -- cgit v0.10.2 From 13db85528fd606b2dfd8f1a5952158e4ad6ce51a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 29 Jun 2012 23:40:13 +0200 Subject: ACPI / PM: Do not execute legacy driver PM callbacks Since all ACPI drivers in the tree should have been switched to power management handling based on struct dev_pm_ops, modify the ACPI bus type driver so that is doesn't execute legacy driver power management callbacks from the functions pointed to by the members of the acpi_bus_pm structure. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index af924ba..c384e59 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -290,51 +290,45 @@ static void acpi_device_release(struct device *dev) kfree(acpi_dev); } -#define ACPI_DEV_PM_CALLBACK(dev, callback, legacy_cb) \ +#define ACPI_DEV_PM_CALLBACK(dev, callback) \ ({ \ - struct acpi_device *__acpi_dev = to_acpi_device(dev); \ - struct acpi_driver *__acpi_drv = __acpi_dev->driver; \ struct device_driver *__drv = dev->driver; \ - int __ret; \ + int __ret = 0; \ \ - if (__acpi_drv && __acpi_drv->ops.legacy_cb) \ - __ret = __acpi_drv->ops.legacy_cb(__acpi_dev); \ - else if (__drv && __drv->pm && __drv->pm->callback) \ + if (__drv && __drv->pm && __drv->pm->callback) \ __ret = __drv->pm->callback(dev); \ - else \ - __ret = 0; \ \ __ret; \ }) static int acpi_pm_suspend(struct device *dev) { - return ACPI_DEV_PM_CALLBACK(dev, suspend, suspend); + return ACPI_DEV_PM_CALLBACK(dev, suspend); } static int acpi_pm_resume(struct device *dev) { - return ACPI_DEV_PM_CALLBACK(dev, resume, resume); + return ACPI_DEV_PM_CALLBACK(dev, resume); } static int acpi_pm_freeze(struct device *dev) { - return ACPI_DEV_PM_CALLBACK(dev, freeze, suspend); + return ACPI_DEV_PM_CALLBACK(dev, freeze); } static int acpi_pm_thaw(struct device *dev) { - return ACPI_DEV_PM_CALLBACK(dev, thaw, resume); + return ACPI_DEV_PM_CALLBACK(dev, thaw); } static int acpi_pm_poweroff(struct device *dev) { - return ACPI_DEV_PM_CALLBACK(dev, poweroff, suspend); + return ACPI_DEV_PM_CALLBACK(dev, poweroff); } static int acpi_pm_restore(struct device *dev) { - return ACPI_DEV_PM_CALLBACK(dev, restore, resume); + return ACPI_DEV_PM_CALLBACK(dev, restore); } static const struct dev_pm_ops acpi_bus_pm = { -- cgit v0.10.2 From a5cd33e1d4f3319e86f9f0fc667fa74ef9d9216c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 29 Jun 2012 23:40:22 +0200 Subject: ACPI / PM: Drop legacy driver PM callbacks that are not used any more Since the legacy ACPI driver PM callbacks included into struct acpi_device_ops are not used any more, drop them. Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index c2bbec7..18fd410 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -117,8 +117,6 @@ struct acpi_device; typedef int (*acpi_op_add) (struct acpi_device * device); typedef int (*acpi_op_remove) (struct acpi_device * device, int type); typedef int (*acpi_op_start) (struct acpi_device * device); -typedef int (*acpi_op_suspend) (struct acpi_device * device); -typedef int (*acpi_op_resume) (struct acpi_device * device); typedef int (*acpi_op_bind) (struct acpi_device * device); typedef int (*acpi_op_unbind) (struct acpi_device * device); typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event); @@ -132,8 +130,6 @@ struct acpi_device_ops { acpi_op_add add; acpi_op_remove remove; acpi_op_start start; - acpi_op_suspend suspend; - acpi_op_resume resume; acpi_op_bind bind; acpi_op_unbind unbind; acpi_op_notify notify; -- cgit v0.10.2 From d91ee328c1e8b3992ffbf84a1a984c2a7f4ebff7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 29 Jun 2012 23:40:29 +0200 Subject: ACPI / PM: Drop PM callbacks from the ACPI bus type Since the ACPI bus type's PM callbacks only execute the driver ones without doing anything else, they can be dropped, because the driver callbacks will be executed by the PM core directly if bus type (or other subsystem) callbacks are not present. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c384e59..fdda493 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -290,56 +290,6 @@ static void acpi_device_release(struct device *dev) kfree(acpi_dev); } -#define ACPI_DEV_PM_CALLBACK(dev, callback) \ -({ \ - struct device_driver *__drv = dev->driver; \ - int __ret = 0; \ - \ - if (__drv && __drv->pm && __drv->pm->callback) \ - __ret = __drv->pm->callback(dev); \ - \ - __ret; \ -}) - -static int acpi_pm_suspend(struct device *dev) -{ - return ACPI_DEV_PM_CALLBACK(dev, suspend); -} - -static int acpi_pm_resume(struct device *dev) -{ - return ACPI_DEV_PM_CALLBACK(dev, resume); -} - -static int acpi_pm_freeze(struct device *dev) -{ - return ACPI_DEV_PM_CALLBACK(dev, freeze); -} - -static int acpi_pm_thaw(struct device *dev) -{ - return ACPI_DEV_PM_CALLBACK(dev, thaw); -} - -static int acpi_pm_poweroff(struct device *dev) -{ - return ACPI_DEV_PM_CALLBACK(dev, poweroff); -} - -static int acpi_pm_restore(struct device *dev) -{ - return ACPI_DEV_PM_CALLBACK(dev, restore); -} - -static const struct dev_pm_ops acpi_bus_pm = { - .suspend = acpi_pm_suspend, - .resume = acpi_pm_resume, - .freeze = acpi_pm_freeze, - .thaw = acpi_pm_thaw, - .poweroff = acpi_pm_poweroff, - .restore = acpi_pm_restore, -}; - static int acpi_bus_match(struct device *dev, struct device_driver *drv) { struct acpi_device *acpi_dev = to_acpi_device(dev); @@ -475,7 +425,6 @@ struct bus_type acpi_bus_type = { .probe = acpi_device_probe, .remove = acpi_device_remove, .uevent = acpi_device_uevent, - .pm = &acpi_bus_pm, }; static int acpi_device_register(struct acpi_device *device) -- cgit v0.10.2 From fd3c3a424e43e4f85385de0c2609c490605611a6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:18:44 +0200 Subject: thinkpad_acpi: Drop pm_message_t arguments from suspend routines Multiple suspend routines in drivers/platform/x86/thinkpad_acpi.c use take pm_message_t arguments that aren't used by any of them. Make those routines take no arguments as that's what they should do. Signed-off-by: Rafael J. Wysocki Acked-by: Henrique de Moraes Holschuh Acked-by: Matthew Garrett diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 8b5610d..070e555 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -277,7 +277,7 @@ struct ibm_struct { int (*write) (char *); void (*exit) (void); void (*resume) (void); - void (*suspend) (pm_message_t state); + void (*suspend) (void); void (*shutdown) (void); struct list_head all_drivers; @@ -931,7 +931,7 @@ static int tpacpi_suspend_handler(struct platform_device *pdev, &tpacpi_all_drivers, all_drivers) { if (ibm->suspend) - (ibm->suspend)(state); + (ibm->suspend)(); } return 0; @@ -3758,7 +3758,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) } } -static void hotkey_suspend(pm_message_t state) +static void hotkey_suspend(void) { /* Do these on suspend, we get the events on early resume! */ hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE; @@ -6329,7 +6329,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) return 0; } -static void brightness_suspend(pm_message_t state) +static void brightness_suspend(void) { tpacpi_brightness_checkpoint_nvram(); } @@ -6748,7 +6748,7 @@ static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = { .get = volume_alsa_mute_get, }; -static void volume_suspend(pm_message_t state) +static void volume_suspend(void) { tpacpi_volume_checkpoint_nvram(); } @@ -8107,7 +8107,7 @@ static void fan_exit(void) flush_workqueue(tpacpi_wq); } -static void fan_suspend(pm_message_t state) +static void fan_suspend(void) { int rc; -- cgit v0.10.2 From 4959a781bc1bbd0d06092caa94e33fd41033a38d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:19:01 +0200 Subject: thinkpad_acpi: Use struct dev_pm_ops instead of legacy PM routines Make the thinkpad_acpi driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki Acked-by: Henrique de Moraes Holschuh Acked-by: Matthew Garrett diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 070e555..d5fd4a1 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -922,8 +922,7 @@ static struct input_dev *tpacpi_inputdev; static struct mutex tpacpi_inputdev_send_mutex; static LIST_HEAD(tpacpi_all_drivers); -static int tpacpi_suspend_handler(struct platform_device *pdev, - pm_message_t state) +static int tpacpi_suspend_handler(struct device *dev) { struct ibm_struct *ibm, *itmp; @@ -937,7 +936,7 @@ static int tpacpi_suspend_handler(struct platform_device *pdev, return 0; } -static int tpacpi_resume_handler(struct platform_device *pdev) +static int tpacpi_resume_handler(struct device *dev) { struct ibm_struct *ibm, *itmp; @@ -951,6 +950,9 @@ static int tpacpi_resume_handler(struct platform_device *pdev) return 0; } +static SIMPLE_DEV_PM_OPS(tpacpi_pm, + tpacpi_suspend_handler, tpacpi_resume_handler); + static void tpacpi_shutdown_handler(struct platform_device *pdev) { struct ibm_struct *ibm, *itmp; @@ -967,9 +969,8 @@ static struct platform_driver tpacpi_pdriver = { .driver = { .name = TPACPI_DRVR_NAME, .owner = THIS_MODULE, + .pm = &tpacpi_pm, }, - .suspend = tpacpi_suspend_handler, - .resume = tpacpi_resume_handler, .shutdown = tpacpi_shutdown_handler, }; -- cgit v0.10.2 From 818e14860615634646e1fe176d49e4357a4e1534 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:19:22 +0200 Subject: intel_ips: Remove empty legacy PM callbacks The legacy PM callbacks provided by the Intel IPS driver are empty routines returning 0, so they can be safely dropped. Signed-off-by: Rafael J. Wysocki Acked-by: Jesse Barnes diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index 0ffdb3c..bd89f3c 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -1697,21 +1697,6 @@ static void ips_remove(struct pci_dev *dev) dev_dbg(&dev->dev, "IPS driver removed\n"); } -#ifdef CONFIG_PM -static int ips_suspend(struct pci_dev *dev, pm_message_t state) -{ - return 0; -} - -static int ips_resume(struct pci_dev *dev) -{ - return 0; -} -#else -#define ips_suspend NULL -#define ips_resume NULL -#endif /* CONFIG_PM */ - static void ips_shutdown(struct pci_dev *dev) { } @@ -1721,8 +1706,6 @@ static struct pci_driver ips_pci_driver = { .id_table = ips_id_table, .probe = ips_probe, .remove = ips_remove, - .suspend = ips_suspend, - .resume = ips_resume, .shutdown = ips_shutdown, }; -- cgit v0.10.2 From 3c33be0b0dae390557f41a604ac621d0f3eabf9b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:19:35 +0200 Subject: acer-wmi: Use struct dev_pm_ops for power management Make the acer-wmi driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki Tested-by: Lee, Chun-Yi Acked-by: Matthew Garrett diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index ce875dc..c8f40c9 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1877,8 +1877,7 @@ static int acer_platform_remove(struct platform_device *device) return 0; } -static int acer_platform_suspend(struct platform_device *dev, -pm_message_t state) +static int acer_suspend(struct device *dev) { u32 value; struct acer_data *data = &interface->data; @@ -1900,7 +1899,7 @@ pm_message_t state) return 0; } -static int acer_platform_resume(struct platform_device *device) +static int acer_resume(struct device *dev) { struct acer_data *data = &interface->data; @@ -1916,6 +1915,8 @@ static int acer_platform_resume(struct platform_device *device) return 0; } +static SIMPLE_DEV_PM_OPS(acer_pm, acer_suspend, acer_resume); + static void acer_platform_shutdown(struct platform_device *device) { struct acer_data *data = &interface->data; @@ -1931,11 +1932,10 @@ static struct platform_driver acer_platform_driver = { .driver = { .name = "acer-wmi", .owner = THIS_MODULE, + .pm = &acer_pm, }, .probe = acer_platform_probe, .remove = acer_platform_remove, - .suspend = acer_platform_suspend, - .resume = acer_platform_resume, .shutdown = acer_platform_shutdown, }; -- cgit v0.10.2 From 8ff847fc21e53be0e2ebd476ce6269a201575152 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:19:45 +0200 Subject: intel_mid_thermal: Use struct dev_pm_ops for power management Make the intel_mid_thermal driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki Reviewed-by: Durgadoss R diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 5ae9cd9..ea7422f 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -418,23 +418,23 @@ static struct thermal_device_info *initialize_sensor(int index) /** * mid_thermal_resume - resume routine - * @pdev: platform device structure + * @dev: device structure * * mid thermal resume: re-initializes the adc. Can sleep. */ -static int mid_thermal_resume(struct platform_device *pdev) +static int mid_thermal_resume(struct device *dev) { - return mid_initialize_adc(&pdev->dev); + return mid_initialize_adc(dev); } /** * mid_thermal_suspend - suspend routine - * @pdev: platform device structure + * @dev: device structure * * mid thermal suspend implements the suspend functionality * by stopping the ADC. Can sleep. */ -static int mid_thermal_suspend(struct platform_device *pdev, pm_message_t mesg) +static int mid_thermal_suspend(struct device *dev) { /* * This just stops the ADC and does not disable it. @@ -444,6 +444,9 @@ static int mid_thermal_suspend(struct platform_device *pdev, pm_message_t mesg) return configure_adc(0); } +static SIMPLE_DEV_PM_OPS(mid_thermal_pm, + mid_thermal_suspend, mid_thermal_resume); + /** * read_curr_temp - reads the current temperature and stores in temp * @temp: holds the current temperature value after reading @@ -557,10 +560,9 @@ static struct platform_driver mid_thermal_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .pm = &mid_thermal_pm, }, .probe = mid_thermal_probe, - .suspend = mid_thermal_suspend, - .resume = mid_thermal_resume, .remove = __devexit_p(mid_thermal_remove), .id_table = therm_id_table, }; -- cgit v0.10.2 From 199ac0529eac0341b232b450e43fad71d1513e94 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:20:29 +0200 Subject: sonypi: Use struct dev_pm_ops for power management Make the sonypi driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki Acked-by: Mattia Dongili diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 45713f0..f877805 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1459,7 +1459,7 @@ static int __devexit sonypi_remove(struct platform_device *dev) #ifdef CONFIG_PM static int old_camera_power; -static int sonypi_suspend(struct platform_device *dev, pm_message_t state) +static int sonypi_suspend(struct device *dev) { old_camera_power = sonypi_device.camera_power; sonypi_disable(); @@ -1467,14 +1467,16 @@ static int sonypi_suspend(struct platform_device *dev, pm_message_t state) return 0; } -static int sonypi_resume(struct platform_device *dev) +static int sonypi_resume(struct device *dev) { sonypi_enable(old_camera_power); return 0; } + +static SIMPLE_DEV_PM_OPS(sonypi_pm, sonypi_suspend, sonypi_resume); +#define SONYPI_PM (&sonypi_pm) #else -#define sonypi_suspend NULL -#define sonypi_resume NULL +#define SONYPI_PM NULL #endif static void sonypi_shutdown(struct platform_device *dev) @@ -1486,12 +1488,11 @@ static struct platform_driver sonypi_driver = { .driver = { .name = "sonypi", .owner = THIS_MODULE, + .pm = SONYPI_PM, }, .probe = sonypi_probe, .remove = __devexit_p(sonypi_remove), .shutdown = sonypi_shutdown, - .suspend = sonypi_suspend, - .resume = sonypi_resume, }; static struct platform_device *sonypi_platform_device; -- cgit v0.10.2 From 62c552ccc3eda1198632a4f344aa32623d226bab Mon Sep 17 00:00:00 2001 From: Bojan Smojver Date: Sat, 16 Jun 2012 00:09:58 +0200 Subject: PM / Hibernate: Enable suspend to both for in-kernel hibernation. It is often useful to suspend to memory after hibernation image has been written to disk. If the battery runs out or power is otherwise lost, the computer will resume from the hibernated image. If not, it will resume from memory and hibernation image will be discarded. Signed-off-by: Bojan Smojver Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt index ac190cf..92341b8 100644 --- a/Documentation/power/swsusp.txt +++ b/Documentation/power/swsusp.txt @@ -33,6 +33,11 @@ echo shutdown > /sys/power/disk; echo disk > /sys/power/state echo platform > /sys/power/disk; echo disk > /sys/power/state +. If you would like to write hibernation image to swap and then suspend +to RAM (provided your platform supports it), you can try + +echo suspend > /sys/power/disk; echo disk > /sys/power/state + . If you have SATA disks, you'll need recent kernels with SATA suspend support. For suspend and resume to work, make sure your disk drivers are built into kernel -- not modules. [There's way to make diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 8b53db3..21ad3fe 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -5,6 +5,7 @@ * Copyright (c) 2003 Open Source Development Lab * Copyright (c) 2004 Pavel Machek * Copyright (c) 2009 Rafael J. Wysocki, Novell Inc. + * Copyright (C) 2012 Bojan Smojver * * This file is released under the GPLv2. */ @@ -46,6 +47,9 @@ enum { HIBERNATION_PLATFORM, HIBERNATION_SHUTDOWN, HIBERNATION_REBOOT, +#ifdef CONFIG_SUSPEND + HIBERNATION_SUSPEND, +#endif /* keep last */ __HIBERNATION_AFTER_LAST }; @@ -574,6 +578,10 @@ int hibernation_platform_enter(void) */ static void power_down(void) { +#ifdef CONFIG_SUSPEND + int error; +#endif + switch (hibernation_mode) { case HIBERNATION_REBOOT: kernel_restart(NULL); @@ -583,6 +591,25 @@ static void power_down(void) case HIBERNATION_SHUTDOWN: kernel_power_off(); break; +#ifdef CONFIG_SUSPEND + case HIBERNATION_SUSPEND: + error = suspend_devices_and_enter(PM_SUSPEND_MEM); + if (error) { + if (hibernation_ops) + hibernation_mode = HIBERNATION_PLATFORM; + else + hibernation_mode = HIBERNATION_SHUTDOWN; + power_down(); + } + /* + * Restore swap signature. + */ + error = swsusp_unmark(); + if (error) + printk(KERN_ERR "PM: Swap will be unusable! " + "Try swapon -a.\n"); + return; +#endif } kernel_halt(); /* @@ -827,6 +854,9 @@ static const char * const hibernation_modes[] = { [HIBERNATION_PLATFORM] = "platform", [HIBERNATION_SHUTDOWN] = "shutdown", [HIBERNATION_REBOOT] = "reboot", +#ifdef CONFIG_SUSPEND + [HIBERNATION_SUSPEND] = "suspend", +#endif }; /* @@ -867,6 +897,9 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr, switch (i) { case HIBERNATION_SHUTDOWN: case HIBERNATION_REBOOT: +#ifdef CONFIG_SUSPEND + case HIBERNATION_SUSPEND: +#endif break; case HIBERNATION_PLATFORM: if (hibernation_ops) @@ -907,6 +940,9 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, switch (mode) { case HIBERNATION_SHUTDOWN: case HIBERNATION_REBOOT: +#ifdef CONFIG_SUSPEND + case HIBERNATION_SUSPEND: +#endif hibernation_mode = mode; break; case HIBERNATION_PLATFORM: diff --git a/kernel/power/power.h b/kernel/power/power.h index b0bd4be..7d4b7ff 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -156,6 +156,9 @@ extern void swsusp_free(void); extern int swsusp_read(unsigned int *flags_p); extern int swsusp_write(unsigned int flags); extern void swsusp_close(fmode_t); +#ifdef CONFIG_SUSPEND +extern int swsusp_unmark(void); +#endif /* kernel/power/block_io.c */ extern struct block_device *hib_resume_bdev; diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 11e22c0..83d5051 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -1472,6 +1472,34 @@ void swsusp_close(fmode_t mode) blkdev_put(hib_resume_bdev, mode); } +/** + * swsusp_unmark - Unmark swsusp signature in the resume device + */ + +#ifdef CONFIG_SUSPEND +int swsusp_unmark(void) +{ + int error; + + hib_bio_read_page(swsusp_resume_block, swsusp_header, NULL); + if (!memcmp(HIBERNATE_SIG,swsusp_header->sig, 10)) { + memcpy(swsusp_header->sig,swsusp_header->orig_sig, 10); + error = hib_bio_write_page(swsusp_resume_block, + swsusp_header, NULL); + } else { + printk(KERN_ERR "PM: Cannot find swsusp signature!\n"); + error = -ENODEV; + } + + /* + * We just returned from suspend, we don't need the image any more. + */ + free_all_swap_pages(root_swap); + + return error; +} +#endif + static int swsusp_header_init(void) { swsusp_header = (struct swsusp_header*) __get_free_page(GFP_KERNEL); -- cgit v0.10.2 From 443772d408a25af62498793f6f805ce3c559309a Mon Sep 17 00:00:00 2001 From: "Srivatsa S. Bhat" Date: Sat, 16 Jun 2012 15:30:45 +0200 Subject: ftrace: Disable function tracing during suspend/resume and hibernation, again If function tracing is enabled for some of the low-level suspend/resume functions, it leads to triple fault during resume from suspend, ultimately ending up in a reboot instead of a resume (or a total refusal to come out of suspended state, on some machines). This issue was explained in more detail in commit f42ac38c59e0a03d (ftrace: disable tracing for suspend to ram). However, the changes made by that commit got reverted by commit cbe2f5a6e84eebb (tracing: allow tracing of suspend/resume & hibernation code again). So, unfortunately since things are not yet robust enough to allow tracing of low-level suspend/resume functions, suspend/resume is still broken when ftrace is enabled. So fix this by disabling function tracing during suspend/resume & hibernation. Signed-off-by: Srivatsa S. Bhat Cc: stable@vger.kernel.org Signed-off-by: Rafael J. Wysocki diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 21ad3fe..0d4b354 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -358,6 +358,7 @@ int hibernation_snapshot(int platform_mode) } suspend_console(); + ftrace_stop(); pm_restrict_gfp_mask(); error = dpm_suspend(PMSG_FREEZE); @@ -383,6 +384,7 @@ int hibernation_snapshot(int platform_mode) if (error || !in_suspend) pm_restore_gfp_mask(); + ftrace_start(); resume_console(); dpm_complete(msg); @@ -485,6 +487,7 @@ int hibernation_restore(int platform_mode) pm_prepare_console(); suspend_console(); + ftrace_stop(); pm_restrict_gfp_mask(); error = dpm_suspend_start(PMSG_QUIESCE); if (!error) { @@ -492,6 +495,7 @@ int hibernation_restore(int platform_mode) dpm_resume_end(PMSG_RECOVER); } pm_restore_gfp_mask(); + ftrace_start(); resume_console(); pm_restore_console(); return error; @@ -518,6 +522,7 @@ int hibernation_platform_enter(void) entering_platform_hibernation = true; suspend_console(); + ftrace_stop(); error = dpm_suspend_start(PMSG_HIBERNATE); if (error) { if (hibernation_ops->recover) @@ -561,6 +566,7 @@ int hibernation_platform_enter(void) Resume_devices: entering_platform_hibernation = false; dpm_resume_end(PMSG_RESTORE); + ftrace_start(); resume_console(); Close: diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 396d262..c8b7446 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "power.h" @@ -212,6 +213,7 @@ int suspend_devices_and_enter(suspend_state_t state) goto Close; } suspend_console(); + ftrace_stop(); suspend_test_start(); error = dpm_suspend_start(PMSG_SUSPEND); if (error) { @@ -231,6 +233,7 @@ int suspend_devices_and_enter(suspend_state_t state) suspend_test_start(); dpm_resume_end(PMSG_RESUME); suspend_test_finish("resume devices"); + ftrace_start(); resume_console(); Close: if (suspend_ops->end) -- cgit v0.10.2 From 4b7760ba0dd3319f66886ab2335a0fbecdbc808a Mon Sep 17 00:00:00 2001 From: Sameer Nanda Date: Tue, 19 Jun 2012 22:23:33 +0200 Subject: PM / Sleep: add knob for printing device resume times Added a new knob called /sys/power/pm_print_times. Setting it to 1 enables printing of time taken by devices to suspend and resume. Setting it to 0 disables this printing (unless overridden by initcall_debug kernel command line option). Signed-off-by: Sameer Nanda Acked-by: Greg KH Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power index 31725ff..2177726 100644 --- a/Documentation/ABI/testing/sysfs-power +++ b/Documentation/ABI/testing/sysfs-power @@ -231,3 +231,16 @@ Description: Reads from this file return a string consisting of the names of wakeup sources created with the help of /sys/power/wake_lock that are inactive at the moment, separated with spaces. + +What: /sys/power/pm_print_times +Date: May 2012 +Contact: Sameer Nanda +Description: + The /sys/power/pm_print_times file allows user space to + control whether the time taken by devices to suspend and + resume is printed. These prints are useful for hunting down + devices that take too long to suspend or resume. + + Writing a "1" enables this printing while writing a "0" + disables it. The default value is "0". Reading from this file + will display the current value. diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9cb845e..6e4db96 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -166,7 +166,7 @@ static ktime_t initcall_debug_start(struct device *dev) { ktime_t calltime = ktime_set(0, 0); - if (initcall_debug) { + if (pm_print_times) { pr_info("calling %s+ @ %i, parent: %s\n", dev_name(dev), task_pid_nr(current), dev->parent ? dev_name(dev->parent) : "none"); @@ -181,7 +181,7 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime, { ktime_t delta, rettime; - if (initcall_debug) { + if (pm_print_times) { rettime = ktime_get(); delta = ktime_sub(rettime, calltime); pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index eeb4bff..12c77b7 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -85,3 +85,14 @@ static inline int pm_qos_sysfs_add(struct device *dev) { return 0; } static inline void pm_qos_sysfs_remove(struct device *dev) {} #endif + +#ifdef CONFIG_PM_DEBUG + +extern int pm_print_times_enabled; +#define pm_print_times (initcall_debug || pm_print_times_enabled) + +#else /* CONFIG_PM_DEBUG */ + +#define pm_print_times initcall_debug + +#endif /* CONFIG_PM_DEBUG */ diff --git a/kernel/power/main.c b/kernel/power/main.c index 428f8a0..7beb3fb 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -132,6 +132,38 @@ static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr, } power_attr(pm_test); + +/* + * pm_print_times: print time taken by devices to suspend and resume. + * + * show() returns whether printing of suspend and resume times is enabled. + * store() accepts 0 or 1. 0 disables printing and 1 enables it. + */ +int pm_print_times_enabled; + +static ssize_t pm_print_times_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", pm_print_times_enabled); +} + +static ssize_t pm_print_times_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + if (val > 1) + return -EINVAL; + + pm_print_times_enabled = val; + return n; +} + +power_attr(pm_print_times); #endif /* CONFIG_PM_DEBUG */ #ifdef CONFIG_DEBUG_FS @@ -530,6 +562,7 @@ static struct attribute * g[] = { #endif #ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, + &pm_print_times_attr.attr, #endif #endif NULL, -- cgit v0.10.2 From b2df1d4f8b95d9d1e3f064cef02fc5c5116b05cf Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 21 Jun 2012 00:19:33 +0200 Subject: PM / Sleep: Separate printing suspend times from initcall_debug Change the behavior of the newly introduced /sys/power/pm_print_times attribute so that its initial value depends on initcall_debug, but setting it to 0 will cause device suspend/resume times not to be printed, even if initcall_debug has been set. This way, the people who use initcall_debug for reasons other than PM debugging will be able to switch the suspend/resume times printing off, if need be. Signed-off-by: Rafael J. Wysocki Reviewed-by: Srivatsa S. Bhat Acked-by: Greg Kroah-Hartman diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 6e4db96..df5f41d 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -166,7 +166,7 @@ static ktime_t initcall_debug_start(struct device *dev) { ktime_t calltime = ktime_set(0, 0); - if (pm_print_times) { + if (pm_print_times_enabled) { pr_info("calling %s+ @ %i, parent: %s\n", dev_name(dev), task_pid_nr(current), dev->parent ? dev_name(dev->parent) : "none"); @@ -181,7 +181,7 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime, { ktime_t delta, rettime; - if (pm_print_times) { + if (pm_print_times_enabled) { rettime = ktime_get(); delta = ktime_sub(rettime, calltime); pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 12c77b7..eeb4bff 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -85,14 +85,3 @@ static inline int pm_qos_sysfs_add(struct device *dev) { return 0; } static inline void pm_qos_sysfs_remove(struct device *dev) {} #endif - -#ifdef CONFIG_PM_DEBUG - -extern int pm_print_times_enabled; -#define pm_print_times (initcall_debug || pm_print_times_enabled) - -#else /* CONFIG_PM_DEBUG */ - -#define pm_print_times initcall_debug - -#endif /* CONFIG_PM_DEBUG */ diff --git a/include/linux/suspend.h b/include/linux/suspend.h index cd83059..0c808d7 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -408,6 +408,12 @@ static inline void unlock_system_sleep(void) {} #endif /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_SLEEP_DEBUG +extern bool pm_print_times_enabled; +#else +#define pm_print_times_enabled (false) +#endif + #ifdef CONFIG_PM_AUTOSLEEP /* kernel/power/autosleep.c */ diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 8f9b4eb..a70518c 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -175,7 +175,7 @@ config PM_TEST_SUSPEND You probably want to have your system's RTC driver statically linked, ensuring that it's available when this test runs. -config CAN_PM_TRACE +config PM_SLEEP_DEBUG def_bool y depends on PM_DEBUG && PM_SLEEP @@ -196,7 +196,7 @@ config PM_TRACE config PM_TRACE_RTC bool "Suspend/resume event tracing" - depends on CAN_PM_TRACE + depends on PM_SLEEP_DEBUG depends on X86 select PM_TRACE ---help--- diff --git a/kernel/power/main.c b/kernel/power/main.c index 7beb3fb..f458238 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -132,38 +132,6 @@ static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr, } power_attr(pm_test); - -/* - * pm_print_times: print time taken by devices to suspend and resume. - * - * show() returns whether printing of suspend and resume times is enabled. - * store() accepts 0 or 1. 0 disables printing and 1 enables it. - */ -int pm_print_times_enabled; - -static ssize_t pm_print_times_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", pm_print_times_enabled); -} - -static ssize_t pm_print_times_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t n) -{ - unsigned long val; - - if (kstrtoul(buf, 10, &val)) - return -EINVAL; - - if (val > 1) - return -EINVAL; - - pm_print_times_enabled = val; - return n; -} - -power_attr(pm_print_times); #endif /* CONFIG_PM_DEBUG */ #ifdef CONFIG_DEBUG_FS @@ -267,6 +235,47 @@ late_initcall(pm_debugfs_init); #endif /* CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_SLEEP_DEBUG +/* + * pm_print_times: print time taken by devices to suspend and resume. + * + * show() returns whether printing of suspend and resume times is enabled. + * store() accepts 0 or 1. 0 disables printing and 1 enables it. + */ +bool pm_print_times_enabled; + +static ssize_t pm_print_times_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", pm_print_times_enabled); +} + +static ssize_t pm_print_times_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + if (val > 1) + return -EINVAL; + + pm_print_times_enabled = !!val; + return n; +} + +power_attr(pm_print_times); + +static inline void pm_print_times_init(void) +{ + pm_print_times_enabled = !!initcall_debug; +} +#else /* !CONFIG_PP_SLEEP_DEBUG */ +static inline void pm_print_times_init(void) {} +#endif /* CONFIG_PM_SLEEP_DEBUG */ + struct kobject *power_kobj; /** @@ -562,6 +571,8 @@ static struct attribute * g[] = { #endif #ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, +#endif +#ifdef CONFIG_PM_SLEEP_DEBUG &pm_print_times_attr.attr, #endif #endif @@ -599,6 +610,7 @@ static int __init pm_init(void) error = sysfs_create_group(power_kobj, &attr_group); if (error) return error; + pm_print_times_init(); return pm_autosleep_init(); } -- cgit v0.10.2 From d8150d350408de6fb2b9ee7b7625ae8e2bb7aa4a Mon Sep 17 00:00:00 2001 From: Bojan Smojver Date: Thu, 21 Jun 2012 22:27:24 +0200 Subject: PM / Hibernate: Print hibernation/thaw progress indicator one line at a time. With the introduction of suspend to both into in-kernel hibernation code, dmesg was getting polluted with backspace characters printed as part of image saving progress indicator. This patch introduces printing of progress indicator on image save/load every 10% and one line at a time. As an additional benefit, all other messages emitted by the kernel during hibernation/thaw should now print cleanly as well. Signed-off-by: Bojan Smojver Signed-off-by: Rafael J. Wysocki diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 83d5051..3c9d764 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -448,9 +448,9 @@ static int save_image(struct swap_map_handle *handle, struct timeval start; struct timeval stop; - printk(KERN_INFO "PM: Saving image data pages (%u pages) ... ", + printk(KERN_INFO "PM: Saving image data pages (%u pages)...\n", nr_to_write); - m = nr_to_write / 100; + m = nr_to_write / 10; if (!m) m = 1; nr_pages = 0; @@ -464,7 +464,8 @@ static int save_image(struct swap_map_handle *handle, if (ret) break; if (!(nr_pages % m)) - printk(KERN_CONT "\b\b\b\b%3d%%", nr_pages / m); + printk(KERN_INFO "PM: Image saving progress: %3d%%\n", + nr_pages / m * 10); nr_pages++; } err2 = hib_wait_on_bio_chain(&bio); @@ -472,9 +473,7 @@ static int save_image(struct swap_map_handle *handle, if (!ret) ret = err2; if (!ret) - printk(KERN_CONT "\b\b\b\bdone\n"); - else - printk(KERN_CONT "\n"); + printk(KERN_INFO "PM: Image saving done.\n"); swsusp_show_speed(&start, &stop, nr_to_write, "Wrote"); return ret; } @@ -668,9 +667,9 @@ static int save_image_lzo(struct swap_map_handle *handle, printk(KERN_INFO "PM: Using %u thread(s) for compression.\n" - "PM: Compressing and saving image data (%u pages) ... ", + "PM: Compressing and saving image data (%u pages)...\n", nr_threads, nr_to_write); - m = nr_to_write / 100; + m = nr_to_write / 10; if (!m) m = 1; nr_pages = 0; @@ -690,8 +689,10 @@ static int save_image_lzo(struct swap_map_handle *handle, data_of(*snapshot), PAGE_SIZE); if (!(nr_pages % m)) - printk(KERN_CONT "\b\b\b\b%3d%%", - nr_pages / m); + printk(KERN_INFO + "PM: Image saving progress: " + "%3d%%\n", + nr_pages / m * 10); nr_pages++; } if (!off) @@ -761,11 +762,8 @@ out_finish: do_gettimeofday(&stop); if (!ret) ret = err2; - if (!ret) { - printk(KERN_CONT "\b\b\b\bdone\n"); - } else { - printk(KERN_CONT "\n"); - } + if (!ret) + printk(KERN_INFO "PM: Image saving done.\n"); swsusp_show_speed(&start, &stop, nr_to_write, "Wrote"); out_clean: if (crc) { @@ -973,9 +971,9 @@ static int load_image(struct swap_map_handle *handle, int err2; unsigned nr_pages; - printk(KERN_INFO "PM: Loading image data pages (%u pages) ... ", + printk(KERN_INFO "PM: Loading image data pages (%u pages)...\n", nr_to_read); - m = nr_to_read / 100; + m = nr_to_read / 10; if (!m) m = 1; nr_pages = 0; @@ -993,7 +991,8 @@ static int load_image(struct swap_map_handle *handle, if (ret) break; if (!(nr_pages % m)) - printk("\b\b\b\b%3d%%", nr_pages / m); + printk(KERN_INFO "PM: Image loading progress: %3d%%\n", + nr_pages / m * 10); nr_pages++; } err2 = hib_wait_on_bio_chain(&bio); @@ -1001,12 +1000,11 @@ static int load_image(struct swap_map_handle *handle, if (!ret) ret = err2; if (!ret) { - printk("\b\b\b\bdone\n"); + printk(KERN_INFO "PM: Image loading done.\n"); snapshot_write_finalize(snapshot); if (!snapshot_image_loaded(snapshot)) ret = -ENODATA; - } else - printk("\n"); + } swsusp_show_speed(&start, &stop, nr_to_read, "Read"); return ret; } @@ -1185,9 +1183,9 @@ static int load_image_lzo(struct swap_map_handle *handle, printk(KERN_INFO "PM: Using %u thread(s) for decompression.\n" - "PM: Loading and decompressing image data (%u pages) ... ", + "PM: Loading and decompressing image data (%u pages)...\n", nr_threads, nr_to_read); - m = nr_to_read / 100; + m = nr_to_read / 10; if (!m) m = 1; nr_pages = 0; @@ -1319,7 +1317,10 @@ static int load_image_lzo(struct swap_map_handle *handle, data[thr].unc + off, PAGE_SIZE); if (!(nr_pages % m)) - printk("\b\b\b\b%3d%%", nr_pages / m); + printk(KERN_INFO + "PM: Image loading progress: " + "%3d%%\n", + nr_pages / m * 10); nr_pages++; ret = snapshot_write_next(snapshot); @@ -1344,7 +1345,7 @@ out_finish: } do_gettimeofday(&stop); if (!ret) { - printk("\b\b\b\bdone\n"); + printk(KERN_INFO "PM: Image loading done.\n"); snapshot_write_finalize(snapshot); if (!snapshot_image_loaded(snapshot)) ret = -ENODATA; @@ -1357,8 +1358,7 @@ out_finish: } } } - } else - printk("\n"); + } swsusp_show_speed(&start, &stop, nr_to_read, "Read"); out_clean: for (i = 0; i < ring_size; i++) -- cgit v0.10.2 From 0b589741b8b83a33e25fb32bb714ea1b972af5aa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 16 Jun 2012 00:02:22 +0200 Subject: PM / Domains: Use subsystem runtime suspend/resume callbacks by default Currently, the default "save state" and "restore state" routines for generic PM domains, pm_genpd_default_save_state() and pm_genpd_default_restore_state(), respectively, only use runtime PM callbacks provided by device drivers, but in general those callbacks need not provide the entire necessary functionality. Namely, in general it may be necessary to execute subsystem (i.e. device type, device class or bus type) callbacks that will carry out all of the necessary operations. For this reason, modify pm_genpd_default_save_state() and pm_genpd_default_restore_state() to execute subsystem callbacks, if they are provided, and fall back to driver callbacks otherwise. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 83aa694..49fdd40 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1615,16 +1615,24 @@ EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); static int pm_genpd_default_save_state(struct device *dev) { int (*cb)(struct device *__dev); - struct device_driver *drv = dev->driver; cb = dev_gpd_data(dev)->ops.save_state; if (cb) return cb(dev); - if (drv && drv->pm && drv->pm->runtime_suspend) - return drv->pm->runtime_suspend(dev); + if (dev->type && dev->type->pm) + cb = dev->type->pm->runtime_suspend; + else if (dev->class && dev->class->pm) + cb = dev->class->pm->runtime_suspend; + else if (dev->bus && dev->bus->pm) + cb = dev->bus->pm->runtime_suspend; + else + cb = NULL; - return 0; + if (!cb && dev->driver && dev->driver->pm) + cb = dev->driver->pm->runtime_suspend; + + return cb ? cb(dev) : 0; } /** @@ -1634,16 +1642,24 @@ static int pm_genpd_default_save_state(struct device *dev) static int pm_genpd_default_restore_state(struct device *dev) { int (*cb)(struct device *__dev); - struct device_driver *drv = dev->driver; cb = dev_gpd_data(dev)->ops.restore_state; if (cb) return cb(dev); - if (drv && drv->pm && drv->pm->runtime_resume) - return drv->pm->runtime_resume(dev); + if (dev->type && dev->type->pm) + cb = dev->type->pm->runtime_resume; + else if (dev->class && dev->class->pm) + cb = dev->class->pm->runtime_resume; + else if (dev->bus && dev->bus->pm) + cb = dev->bus->pm->runtime_resume; + else + cb = NULL; - return 0; + if (!cb && dev->driver && dev->driver->pm) + cb = dev->driver->pm->runtime_resume; + + return cb ? cb(dev) : 0; } #ifdef CONFIG_PM_SLEEP -- cgit v0.10.2 From 80de3d7f416f1accd03f2e519ead32d6fde4fcf4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 16 Jun 2012 00:02:34 +0200 Subject: PM / Domains: Do not stop devices after restoring their states While resuming a device belonging to a PM domain, pm_genpd_runtime_resume() calls __pm_genpd_restore_device() to restore its state, if necessary. The latter starts the device, using genpd_start_dev(), restores its state, using genpd_restore_dev(), and then stops it, using genpd_stop_dev(). However, this last operation is not necessary, because the device is supposed to be operational after pm_genpd_runtime_resume() has returned and because of it pm_genpd_runtime_resume() has to call genpd_start_dev() once again for the "restored" device, which is inefficient. To make things more efficient, remove the call to genpd_stop_dev() from __pm_genpd_restore_device() and the direct call to genpd_start_dev() from pm_genpd_runtime_resume(). [Of course, genpd_start_dev() still has to be called by it for devices with the power.irq_safe flag set, because __pm_genpd_restore_device() is not executed for them.] This change has been tested on the SH7372 Mackerel board. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 49fdd40..da1d525 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -339,19 +339,16 @@ static void __pm_genpd_restore_device(struct pm_domain_data *pdd, { struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); struct device *dev = pdd->dev; + bool need_restore = gpd_data->need_restore; - if (!gpd_data->need_restore) - return; - + gpd_data->need_restore = false; mutex_unlock(&genpd->lock); genpd_start_dev(genpd, dev); - genpd_restore_dev(genpd, dev); - genpd_stop_dev(genpd, dev); + if (need_restore) + genpd_restore_dev(genpd, dev); mutex_lock(&genpd->lock); - - gpd_data->need_restore = false; } /** @@ -595,7 +592,7 @@ static int pm_genpd_runtime_resume(struct device *dev) /* If power.irq_safe, the PM domain is never powered off. */ if (dev->power.irq_safe) - goto out; + return genpd_start_dev(genpd, dev); mutex_lock(&genpd->lock); ret = __pm_genpd_poweron(genpd); @@ -628,9 +625,6 @@ static int pm_genpd_runtime_resume(struct device *dev) wake_up_all(&genpd->status_wait_queue); mutex_unlock(&genpd->lock); - out: - genpd_start_dev(genpd, dev); - return 0; } -- cgit v0.10.2 From 8d2c794108aed16602de1fcc37e485e9243ab5c0 Mon Sep 17 00:00:00 2001 From: Oskar Schirmer Date: Tue, 3 Jul 2012 09:27:24 +0000 Subject: PM / Documentation: fix typos in power management description Just two missing characters. Signed-off-by: Oskar Schirmer Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/power/devices.txt b/Documentation/power/devices.txt index 872815c..504dfe4 100644 --- a/Documentation/power/devices.txt +++ b/Documentation/power/devices.txt @@ -583,9 +583,10 @@ for the given device during all power transitions, instead of the respective subsystem-level callbacks. Specifically, if a device's pm_domain pointer is not NULL, the ->suspend() callback from the object pointed to by it will be executed instead of its subsystem's (e.g. bus type's) ->suspend() callback and -anlogously for all of the remaining callbacks. In other words, power management -domain callbacks, if defined for the given device, always take precedence over -the callbacks provided by the device's subsystem (e.g. bus type). +analogously for all of the remaining callbacks. In other words, power +management domain callbacks, if defined for the given device, always take +precedence over the callbacks provided by the device's subsystem (e.g. bus +type). The support for device power management domains is only relevant to platforms needing to use the same device driver power management callbacks in many @@ -598,7 +599,7 @@ it into account in any way. Device Low Power (suspend) States --------------------------------- Device low-power states aren't standard. One device might only handle -"on" and "off, while another might support a dozen different versions of +"on" and "off", while another might support a dozen different versions of "on" (how many engines are active?), plus a state that gets back to "on" faster than from a full "off". -- cgit v0.10.2 From e25d5c11eeeec610af2e0ff51e60d7cdadefe0f2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 30 Jun 2012 23:50:47 +0200 Subject: hdaps: Use struct dev_pm_ops for power management Make the HDAPS driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c index 24a3ae0..d9ab6f6 100644 --- a/drivers/platform/x86/hdaps.c +++ b/drivers/platform/x86/hdaps.c @@ -305,17 +305,19 @@ static int hdaps_probe(struct platform_device *dev) return 0; } -static int hdaps_resume(struct platform_device *dev) +static int hdaps_resume(struct device *dev) { return hdaps_device_init(); } +static SIMPLE_DEV_PM_OPS(hdaps_pm, NULL, hdaps_resume); + static struct platform_driver hdaps_driver = { .probe = hdaps_probe, - .resume = hdaps_resume, .driver = { .name = "hdaps", .owner = THIS_MODULE, + .pm = &hdaps_pm, }, }; -- cgit v0.10.2 From dc7fd275ae60ef8edf952aff2a62462f5d892fd4 Mon Sep 17 00:00:00 2001 From: ShuoX Liu Date: Tue, 3 Jul 2012 19:05:31 +0200 Subject: cpuidle: move field disable from per-driver to per-cpu Andrew J.Schorr raises a question. When he changes the disable setting on a single CPU, it affects all the other CPUs. Basically, currently, the disable field is per-driver instead of per-cpu. All the C states of the same driver are shared by all CPU in the same machine. The patch changes the `disable' field to per-cpu, so we could set this separately for each cpu. Signed-off-by: ShuoX Liu Reported-by: Andrew J.Schorr Reviewed-by: Yanmin Zhang Signed-off-by: Andrew Morton Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index d90519c..04e4b76 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -265,7 +265,6 @@ static void poll_idle_init(struct cpuidle_driver *drv) state->power_usage = -1; state->flags = 0; state->enter = poll_idle; - state->disable = 0; } #else static void poll_idle_init(struct cpuidle_driver *drv) {} diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 0633575..8391d93 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -281,7 +281,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) * unless the timer is happening really really soon. */ if (data->expected_us > 5 && - drv->states[CPUIDLE_DRIVER_STATE_START].disable == 0) + dev->states_usage[CPUIDLE_DRIVER_STATE_START].disable == 0) data->last_state_idx = CPUIDLE_DRIVER_STATE_START; /* @@ -290,8 +290,9 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) */ for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { struct cpuidle_state *s = &drv->states[i]; + struct cpuidle_state_usage *su = &dev->states_usage[i]; - if (s->disable) + if (su->disable) continue; if (s->target_residency > data->predicted_us) continue; diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 88032b4..5f809e3 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -217,7 +217,8 @@ struct cpuidle_state_attr { struct attribute attr; ssize_t (*show)(struct cpuidle_state *, \ struct cpuidle_state_usage *, char *); - ssize_t (*store)(struct cpuidle_state *, const char *, size_t); + ssize_t (*store)(struct cpuidle_state *, \ + struct cpuidle_state_usage *, const char *, size_t); }; #define define_one_state_ro(_name, show) \ @@ -233,21 +234,22 @@ static ssize_t show_state_##_name(struct cpuidle_state *state, \ return sprintf(buf, "%u\n", state->_name);\ } -#define define_store_state_function(_name) \ +#define define_store_state_ull_function(_name) \ static ssize_t store_state_##_name(struct cpuidle_state *state, \ + struct cpuidle_state_usage *state_usage, \ const char *buf, size_t size) \ { \ - long value; \ + unsigned long long value; \ int err; \ if (!capable(CAP_SYS_ADMIN)) \ return -EPERM; \ - err = kstrtol(buf, 0, &value); \ + err = kstrtoull(buf, 0, &value); \ if (err) \ return err; \ if (value) \ - state->disable = 1; \ + state_usage->_name = 1; \ else \ - state->disable = 0; \ + state_usage->_name = 0; \ return size; \ } @@ -273,8 +275,8 @@ define_show_state_ull_function(usage) define_show_state_ull_function(time) define_show_state_str_function(name) define_show_state_str_function(desc) -define_show_state_function(disable) -define_store_state_function(disable) +define_show_state_ull_function(disable) +define_store_state_ull_function(disable) define_one_state_ro(name, show_state_name); define_one_state_ro(desc, show_state_desc); @@ -318,10 +320,11 @@ static ssize_t cpuidle_state_store(struct kobject *kobj, { 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); if (cattr->store) - ret = cattr->store(state, buf, size); + ret = cattr->store(state, state_usage, buf, size); return ret; } diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 6c26a3d..8570012 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -34,6 +34,7 @@ struct cpuidle_driver; struct cpuidle_state_usage { void *driver_data; + unsigned long long disable; unsigned long long usage; unsigned long long time; /* in US */ }; @@ -46,7 +47,6 @@ struct cpuidle_state { unsigned int exit_latency; /* in US */ int power_usage; /* in mW */ unsigned int target_residency; /* in US */ - unsigned int disable; int (*enter) (struct cpuidle_device *dev, struct cpuidle_driver *drv, -- cgit v0.10.2 From 6e797a078824b30afbfae6cc4b1c2b21c51761ef Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 16 Jun 2012 15:20:11 +0200 Subject: PM / cpuidle: Add driver reference counter Add a reference counter for the cpuidle driver, so that it can't be unregistered when it is in use. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 40cd3f3..58bf3b1 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -16,6 +16,7 @@ static struct cpuidle_driver *cpuidle_curr_driver; DEFINE_SPINLOCK(cpuidle_driver_lock); +int cpuidle_driver_refcount; static void __cpuidle_register_driver(struct cpuidle_driver *drv) { @@ -89,8 +90,34 @@ void cpuidle_unregister_driver(struct cpuidle_driver *drv) } spin_lock(&cpuidle_driver_lock); - cpuidle_curr_driver = NULL; + + if (!WARN_ON(cpuidle_driver_refcount > 0)) + cpuidle_curr_driver = NULL; + spin_unlock(&cpuidle_driver_lock); } EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); + +struct cpuidle_driver *cpuidle_driver_ref(void) +{ + struct cpuidle_driver *drv; + + spin_lock(&cpuidle_driver_lock); + + drv = cpuidle_curr_driver; + cpuidle_driver_refcount++; + + spin_unlock(&cpuidle_driver_lock); + return drv; +} + +void cpuidle_driver_unref(void) +{ + spin_lock(&cpuidle_driver_lock); + + if (!WARN_ON(cpuidle_driver_refcount <= 0)) + cpuidle_driver_refcount--; + + spin_unlock(&cpuidle_driver_lock); +} diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 8570012..27cfced 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -136,7 +136,9 @@ struct cpuidle_driver { extern void disable_cpuidle(void); extern int cpuidle_idle_call(void); extern int cpuidle_register_driver(struct cpuidle_driver *drv); -struct cpuidle_driver *cpuidle_get_driver(void); +extern struct cpuidle_driver *cpuidle_get_driver(void); +extern struct cpuidle_driver *cpuidle_driver_ref(void); +extern void cpuidle_driver_unref(void); extern void cpuidle_unregister_driver(struct cpuidle_driver *drv); extern int cpuidle_register_device(struct cpuidle_device *dev); extern void cpuidle_unregister_device(struct cpuidle_device *dev); @@ -157,6 +159,8 @@ static inline int cpuidle_idle_call(void) { return -ENODEV; } static inline int cpuidle_register_driver(struct cpuidle_driver *drv) {return -ENODEV; } static inline struct cpuidle_driver *cpuidle_get_driver(void) {return NULL; } +static inline struct cpuidle_driver *cpuidle_driver_ref(void) {return NULL; } +static inline void cpuidle_driver_unref(void) {} static inline void cpuidle_unregister_driver(struct cpuidle_driver *drv) { } static inline int cpuidle_register_device(struct cpuidle_device *dev) {return -ENODEV; } -- cgit v0.10.2 From cbc9ef0287ab764d3da0129efa673808df641fe3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 3 Jul 2012 19:07:42 +0200 Subject: PM / Domains: Add preliminary support for cpuidle, v2 On some systems there are CPU cores located in the same power domains as I/O devices. Then, power can only be removed from the domain if all I/O devices in it are not in use and the CPU core is idle. Add preliminary support for that to the generic PM domains framework. First, the platform is expected to provide a cpuidle driver with one extra state designated for use with the generic PM domains code. This state should be initially disabled and its exit_latency value should be set to whatever time is needed to bring up the CPU core itself after restoring power to it, not including the domain's power on latency. Its .enter() callback should point to a procedure that will remove power from the domain containing the CPU core at the end of the CPU power transition. The remaining characteristics of the extra cpuidle state, referred to as the "domain" cpuidle state below, (e.g. power usage, target residency) should be populated in accordance with the properties of the hardware. Next, the platform should execute genpd_attach_cpuidle() on the PM domain containing the CPU core. That will cause the generic PM domains framework to treat that domain in a special way such that: * When all devices in the domain have been suspended and it is about to be turned off, the states of the devices will be saved, but power will not be removed from the domain. Instead, the "domain" cpuidle state will be enabled so that power can be removed from the domain when the CPU core is idle and the state has been chosen as the target by the cpuidle governor. * When the first I/O device in the domain is resumed and __pm_genpd_poweron(() is called for the first time after power has been removed from the domain, the "domain" cpuidle state will be disabled to avoid subsequent surprise power removals via cpuidle. The effective exit_latency value of the "domain" cpuidle state depends on the time needed to bring up the CPU core itself after restoring power to it as well as on the power on latency of the domain containing the CPU core. Thus the "domain" cpuidle state's exit_latency has to be recomputed every time the domain's power on latency is updated, which may happen every time power is restored to the domain, if the measured power on latency is greater than the latency stored in the corresponding generic_pm_domain structure. Signed-off-by: Rafael J. Wysocki Reviewed-by: Kevin Hilman diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index da1d525..4b5f090 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -139,6 +139,19 @@ static void genpd_set_active(struct generic_pm_domain *genpd) genpd->status = GPD_STATE_ACTIVE; } +static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) +{ + s64 usecs64; + + if (!genpd->cpu_data) + return; + + usecs64 = genpd->power_on_latency_ns; + do_div(usecs64, NSEC_PER_USEC); + usecs64 += genpd->cpu_data->saved_exit_latency; + genpd->cpu_data->idle_state->exit_latency = usecs64; +} + /** * __pm_genpd_poweron - Restore power to a given PM domain and its masters. * @genpd: PM domain to power up. @@ -176,6 +189,13 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) return 0; } + if (genpd->cpu_data) { + cpuidle_pause_and_lock(); + genpd->cpu_data->idle_state->disabled = true; + cpuidle_resume_and_unlock(); + goto out; + } + /* * The list is guaranteed not to change while the loop below is being * executed, unless one of the masters' .power_on() callbacks fiddles @@ -215,6 +235,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) if (elapsed_ns > genpd->power_on_latency_ns) { genpd->power_on_latency_ns = elapsed_ns; genpd->max_off_time_changed = true; + genpd_recalc_cpu_exit_latency(genpd); if (genpd->name) pr_warning("%s: Power-on latency exceeded, " "new value %lld ns\n", genpd->name, @@ -222,6 +243,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) } } + out: genpd_set_active(genpd); return 0; @@ -455,6 +477,21 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) } } + if (genpd->cpu_data) { + /* + * If cpu_data is set, cpuidle should turn the domain off when + * the CPU in it is idle. In that case we don't decrement the + * subdomain counts of the master domains, so that power is not + * removed from the current domain prematurely as a result of + * cutting off the masters' power. + */ + genpd->status = GPD_STATE_POWER_OFF; + cpuidle_pause_and_lock(); + genpd->cpu_data->idle_state->disabled = false; + cpuidle_resume_and_unlock(); + goto out; + } + if (genpd->power_off) { ktime_t time_start; s64 elapsed_ns; @@ -1600,6 +1637,86 @@ int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) } EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); +int genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state) +{ + struct cpuidle_driver *cpuidle_drv; + struct gpd_cpu_data *cpu_data; + struct cpuidle_state *idle_state; + int ret = 0; + + if (IS_ERR_OR_NULL(genpd) || state < 0) + return -EINVAL; + + genpd_acquire_lock(genpd); + + if (genpd->cpu_data) { + ret = -EEXIST; + goto out; + } + cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL); + if (!cpu_data) { + ret = -ENOMEM; + goto out; + } + cpuidle_drv = cpuidle_driver_ref(); + if (!cpuidle_drv) { + ret = -ENODEV; + goto out; + } + if (cpuidle_drv->state_count <= state) { + ret = -EINVAL; + goto err; + } + idle_state = &cpuidle_drv->states[state]; + if (!idle_state->disabled) { + ret = -EAGAIN; + goto err; + } + cpu_data->idle_state = idle_state; + cpu_data->saved_exit_latency = idle_state->exit_latency; + genpd->cpu_data = cpu_data; + genpd_recalc_cpu_exit_latency(genpd); + + out: + genpd_release_lock(genpd); + return ret; + + err: + cpuidle_driver_unref(); + goto out; +} + +int genpd_detach_cpuidle(struct generic_pm_domain *genpd) +{ + struct gpd_cpu_data *cpu_data; + struct cpuidle_state *idle_state; + int ret = 0; + + if (IS_ERR_OR_NULL(genpd)) + return -EINVAL; + + genpd_acquire_lock(genpd); + + cpu_data = genpd->cpu_data; + if (!cpu_data) { + ret = -ENODEV; + goto out; + } + idle_state = cpu_data->idle_state; + if (!idle_state->disabled) { + ret = -EAGAIN; + goto out; + } + idle_state->exit_latency = cpu_data->saved_exit_latency; + cpuidle_driver_unref(); + genpd->cpu_data = NULL; + kfree(cpu_data); + + out: + genpd_release_lock(genpd); + return ret; +} + /* Default device callbacks for generic PM domains. */ /** diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 04e4b76..0132706 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -265,6 +265,7 @@ static void poll_idle_init(struct cpuidle_driver *drv) state->power_usage = -1; state->flags = 0; state->enter = poll_idle; + state->disabled = false; } #else static void poll_idle_init(struct cpuidle_driver *drv) {} diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 8391d93..5b1f2c3 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -281,6 +281,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) * unless the timer is happening really really soon. */ if (data->expected_us > 5 && + !drv->states[CPUIDLE_DRIVER_STATE_START].disabled && dev->states_usage[CPUIDLE_DRIVER_STATE_START].disable == 0) data->last_state_idx = CPUIDLE_DRIVER_STATE_START; @@ -292,7 +293,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) struct cpuidle_state *s = &drv->states[i]; struct cpuidle_state_usage *su = &dev->states_usage[i]; - if (su->disable) + if (s->disabled || su->disable) continue; if (s->target_residency > data->predicted_us) continue; diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 27cfced..8684a0d 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -47,6 +47,7 @@ struct cpuidle_state { unsigned int exit_latency; /* in US */ int power_usage; /* in mW */ unsigned int target_residency; /* in US */ + bool disabled; /* disabled on all CPUs */ int (*enter) (struct cpuidle_device *dev, struct cpuidle_driver *drv, diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 30f794e..2febe31 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -15,6 +15,7 @@ #include #include #include +#include enum gpd_status { GPD_STATE_ACTIVE = 0, /* PM domain is active */ @@ -45,6 +46,11 @@ struct gpd_dev_ops { bool (*active_wakeup)(struct device *dev); }; +struct gpd_cpu_data { + unsigned int saved_exit_latency; + struct cpuidle_state *idle_state; +}; + struct generic_pm_domain { struct dev_pm_domain domain; /* PM domain operations */ struct list_head gpd_list_node; /* Node in the global PM domains list */ @@ -75,6 +81,7 @@ struct generic_pm_domain { bool max_off_time_changed; bool cached_power_down_ok; struct device_node *of_node; /* Node in device tree */ + struct gpd_cpu_data *cpu_data; }; static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd) @@ -155,6 +162,8 @@ extern int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, struct gpd_timing_data *td); extern int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td); +extern int genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state); +extern int genpd_detach_cpuidle(struct generic_pm_domain *genpd); extern void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off); @@ -211,6 +220,14 @@ static inline int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) { return -ENOSYS; } +static inline int genpd_attach_cpuidle(struct generic_pm_domain *genpd, int st) +{ + return -ENOSYS; +} +static inline int genpd_detach_cpuidle(struct generic_pm_domain *genpd) +{ + return -ENOSYS; +} static inline void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off) { -- cgit v0.10.2 From 1d5fcfec22ce5f69db0d29284d2b65ff8ab1bfaa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 5 Jul 2012 22:12:32 +0200 Subject: PM / Domains: Add device domain data reference counter Add a mechanism for counting references to the struct generic_pm_domain_data object pointed to by dev->power.subsys_data->domain_data if the device in question belongs to a generic PM domain. This change is necessary for a subsequent patch making it possible to allocate that object from within pm_genpd_add_callbacks(), so that drivers can attach their PM domain device callbacks to devices before those devices are added to PM domains. This patch has been tested on the SH7372 Mackerel board. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 4b5f090..45eb3b1 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -297,7 +297,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) { + if (pdd && pdd->dev) { to_gpd_data(pdd)->td.constraint_changed = true; genpd = dev_to_genpd(dev); } else { @@ -1266,6 +1266,27 @@ static void pm_genpd_complete(struct device *dev) #endif /* CONFIG_PM_SLEEP */ +static struct generic_pm_domain_data *__pm_genpd_alloc_dev_data(struct device *dev) +{ + struct generic_pm_domain_data *gpd_data; + + gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); + if (!gpd_data) + return NULL; + + mutex_init(&gpd_data->lock); + gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; + dev_pm_qos_add_notifier(dev, &gpd_data->nb); + return gpd_data; +} + +static void __pm_genpd_free_dev_data(struct device *dev, + struct generic_pm_domain_data *gpd_data) +{ + dev_pm_qos_remove_notifier(dev, &gpd_data->nb); + kfree(gpd_data); +} + /** * __pm_genpd_add_device - Add a device to an I/O PM domain. * @genpd: PM domain to add the device to. @@ -1275,7 +1296,7 @@ static void pm_genpd_complete(struct device *dev) int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, struct gpd_timing_data *td) { - struct generic_pm_domain_data *gpd_data; + struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; struct pm_domain_data *pdd; int ret = 0; @@ -1284,14 +1305,10 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) return -EINVAL; - gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); - if (!gpd_data) + gpd_data_new = __pm_genpd_alloc_dev_data(dev); + if (!gpd_data_new) return -ENOMEM; - mutex_init(&gpd_data->lock); - gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; - dev_pm_qos_add_notifier(dev, &gpd_data->nb); - genpd_acquire_lock(genpd); if (genpd->prepared_count > 0) { @@ -1305,35 +1322,42 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, goto out; } + ret = dev_pm_get_subsys_data(dev); + if (ret) + goto out; + genpd->device_count++; genpd->max_off_time_changed = true; - dev_pm_get_subsys_data(dev); - - mutex_lock(&gpd_data->lock); spin_lock_irq(&dev->power.lock); + dev->pm_domain = &genpd->domain; - dev->power.subsys_data->domain_data = &gpd_data->base; - gpd_data->base.dev = dev; - list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); - gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; + if (dev->power.subsys_data->domain_data) { + gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); + } else { + gpd_data = gpd_data_new; + dev->power.subsys_data->domain_data = &gpd_data->base; + } + gpd_data->refcount++; if (td) gpd_data->td = *td; + spin_unlock_irq(&dev->power.lock); + + mutex_lock(&gpd_data->lock); + gpd_data->base.dev = dev; + list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); + gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; gpd_data->td.constraint_changed = true; gpd_data->td.effective_constraint_ns = -1; - spin_unlock_irq(&dev->power.lock); mutex_unlock(&gpd_data->lock); - genpd_release_lock(genpd); - - return 0; - out: genpd_release_lock(genpd); - dev_pm_qos_remove_notifier(dev, &gpd_data->nb); - kfree(gpd_data); + if (gpd_data != gpd_data_new) + __pm_genpd_free_dev_data(dev, gpd_data_new); + return ret; } @@ -1379,6 +1403,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, { struct generic_pm_domain_data *gpd_data; struct pm_domain_data *pdd; + bool remove = false; int ret = 0; dev_dbg(dev, "%s()\n", __func__); @@ -1399,22 +1424,28 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, genpd->max_off_time_changed = true; spin_lock_irq(&dev->power.lock); + dev->pm_domain = NULL; pdd = dev->power.subsys_data->domain_data; list_del_init(&pdd->list_node); - dev->power.subsys_data->domain_data = NULL; + gpd_data = to_gpd_data(pdd); + if (--gpd_data->refcount == 0) { + dev->power.subsys_data->domain_data = NULL; + remove = true; + } + spin_unlock_irq(&dev->power.lock); - gpd_data = to_gpd_data(pdd); mutex_lock(&gpd_data->lock); pdd->dev = NULL; mutex_unlock(&gpd_data->lock); genpd_release_lock(genpd); - dev_pm_qos_remove_notifier(dev, &gpd_data->nb); - kfree(gpd_data); dev_pm_put_subsys_data(dev); + if (remove) + __pm_genpd_free_dev_data(dev, gpd_data); + return 0; out: diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 2febe31..a7d6172 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -112,6 +112,7 @@ struct generic_pm_domain_data { struct gpd_timing_data td; struct notifier_block nb; struct mutex lock; + unsigned int refcount; bool need_restore; bool always_on; }; -- cgit v0.10.2 From 62d4490294e3de7015f715c00af992d7b6a6e22c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 5 Jul 2012 22:12:54 +0200 Subject: PM / Domains: Allow device callbacks to be added at any time Make it possible to modify device callbacks used by the generic PM domains core code at any time, not only after the device has been added to a domain. This will allow device drivers to provide their own device PM domain callbacks even if they are registered before adding the devices to PM domains. For this purpose, use the observation that the struct generic_pm_domain_data object containing the relevant callback pointers may be allocated by pm_genpd_add_callbacks() and the callbacks may be set before __pm_genpd_add_device() is run for the given device. This object will then be used by __pm_genpd_add_device(), but it has to be protected from premature removal by reference counting. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 45eb3b1..9727bc5 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1603,33 +1603,52 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, * @dev: Device to add the callbacks to. * @ops: Set of callbacks to add. * @td: Timing data to add to the device along with the callbacks (optional). + * + * Every call to this routine should be balanced with a call to + * __pm_genpd_remove_callbacks() and they must not be nested. */ int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, struct gpd_timing_data *td) { - struct pm_domain_data *pdd; + struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; int ret = 0; - if (!(dev && dev->power.subsys_data && ops)) + if (!(dev && ops)) return -EINVAL; + gpd_data_new = __pm_genpd_alloc_dev_data(dev); + if (!gpd_data_new) + return -ENOMEM; + pm_runtime_disable(dev); device_pm_lock(); - pdd = dev->power.subsys_data->domain_data; - if (pdd) { - struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); + ret = dev_pm_get_subsys_data(dev); + if (ret) + goto out; + + spin_lock_irq(&dev->power.lock); - gpd_data->ops = *ops; - if (td) - gpd_data->td = *td; + if (dev->power.subsys_data->domain_data) { + gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); } else { - ret = -EINVAL; + gpd_data = gpd_data_new; + dev->power.subsys_data->domain_data = &gpd_data->base; } + gpd_data->refcount++; + gpd_data->ops = *ops; + if (td) + gpd_data->td = *td; + spin_unlock_irq(&dev->power.lock); + + out: device_pm_unlock(); pm_runtime_enable(dev); + if (gpd_data != gpd_data_new) + __pm_genpd_free_dev_data(dev, gpd_data_new); + return ret; } EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); @@ -1638,10 +1657,13 @@ EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device. * @dev: Device to remove the callbacks from. * @clear_td: If set, clear the device's timing data too. + * + * This routine can only be called after pm_genpd_add_callbacks(). */ int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) { - struct pm_domain_data *pdd; + struct generic_pm_domain_data *gpd_data = NULL; + bool remove = false; int ret = 0; if (!(dev && dev->power.subsys_data)) @@ -1650,21 +1672,35 @@ int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) pm_runtime_disable(dev); device_pm_lock(); - pdd = dev->power.subsys_data->domain_data; - if (pdd) { - struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); + spin_lock_irq(&dev->power.lock); + if (dev->power.subsys_data->domain_data) { + gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); gpd_data->ops = (struct gpd_dev_ops){ 0 }; if (clear_td) gpd_data->td = (struct gpd_timing_data){ 0 }; + + if (--gpd_data->refcount == 0) { + dev->power.subsys_data->domain_data = NULL; + remove = true; + } } else { ret = -EINVAL; } + spin_unlock_irq(&dev->power.lock); + device_pm_unlock(); pm_runtime_enable(dev); - return ret; + if (ret) + return ret; + + dev_pm_put_subsys_data(dev); + if (remove) + __pm_genpd_free_dev_data(dev, gpd_data); + + return 0; } EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); -- cgit v0.10.2 From 25ac77613aa8fca131599705e3d7da2a0eaa06a0 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 5 Jul 2012 15:23:25 +0200 Subject: ACPI: intel_idle : break dependency between modules When the system is booted with some cpus offline, the idle driver is not initialized. When a cpu is set online, the acpi code call the intel idle init function. Unfortunately this code introduce a dependency between intel_idle and acpi. This patch is intended to remove this dependency by using the notifier of intel_idle. This patch has the benefit of encapsulating the intel_idle driver and remove some exported functions. Signed-off-by: Daniel Lezcano Acked-by: Srivatsa S. Bhat Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 0734086..8648b29 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -427,18 +427,11 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb, * Initialize missing things */ if (pr->flags.need_hotplug_init) { - struct cpuidle_driver *idle_driver = - cpuidle_get_driver(); - printk(KERN_INFO "Will online and init hotplugged " "CPU: %d\n", pr->id); WARN(acpi_processor_start(pr), "Failed to start CPU:" " %d\n", pr->id); pr->flags.need_hotplug_init = 0; - if (idle_driver && !strcmp(idle_driver->name, - "intel_idle")) { - intel_idle_cpu_init(pr->id); - } /* Normal CPU soft online event */ } else { acpi_processor_ppc_has_changed(pr, 0); diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index d0f59c3..fe95d54 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -96,6 +96,7 @@ static const struct idle_cpu *icpu; static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; static int intel_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); +static int intel_idle_cpu_init(int cpu); static struct cpuidle_state *cpuidle_state_table; @@ -302,22 +303,35 @@ static void __setup_broadcast_timer(void *arg) clockevents_notify(reason, &cpu); } -static int setup_broadcast_cpuhp_notify(struct notifier_block *n, - unsigned long action, void *hcpu) +static int cpu_hotplug_notify(struct notifier_block *n, + unsigned long action, void *hcpu) { int hotcpu = (unsigned long)hcpu; + struct cpuidle_device *dev; switch (action & 0xf) { case CPU_ONLINE: - smp_call_function_single(hotcpu, __setup_broadcast_timer, - (void *)true, 1); + + if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) + smp_call_function_single(hotcpu, __setup_broadcast_timer, + (void *)true, 1); + + /* + * Some systems can hotplug a cpu at runtime after + * the kernel has booted, we have to initialize the + * driver in this case + */ + dev = per_cpu_ptr(intel_idle_cpuidle_devices, hotcpu); + if (!dev->registered) + intel_idle_cpu_init(hotcpu); + break; } return NOTIFY_OK; } -static struct notifier_block setup_broadcast_notifier = { - .notifier_call = setup_broadcast_cpuhp_notify, +static struct notifier_block cpu_hotplug_notifier = { + .notifier_call = cpu_hotplug_notify, }; static void auto_demotion_disable(void *dummy) @@ -405,10 +419,10 @@ static int intel_idle_probe(void) if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */ lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE; - else { + else on_each_cpu(__setup_broadcast_timer, (void *)true, 1); - register_cpu_notifier(&setup_broadcast_notifier); - } + + register_cpu_notifier(&cpu_hotplug_notifier); pr_debug(PREFIX "v" INTEL_IDLE_VERSION " model 0x%X\n", boot_cpu_data.x86_model); @@ -494,7 +508,7 @@ static int intel_idle_cpuidle_driver_init(void) * allocate, initialize, register cpuidle_devices * @cpu: cpu/core to initialize */ -int intel_idle_cpu_init(int cpu) +static int intel_idle_cpu_init(int cpu) { int cstate; struct cpuidle_device *dev; @@ -539,7 +553,6 @@ int intel_idle_cpu_init(int cpu) return 0; } -EXPORT_SYMBOL_GPL(intel_idle_cpu_init); static int __init intel_idle_init(void) { @@ -581,10 +594,10 @@ static void __exit intel_idle_exit(void) intel_idle_cpuidle_devices_uninit(); cpuidle_unregister_driver(&intel_idle_driver); - if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) { + + if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) on_each_cpu(__setup_broadcast_timer, (void *)false, 1); - unregister_cpu_notifier(&setup_broadcast_notifier); - } + unregister_cpu_notifier(&cpu_hotplug_notifier); return; } diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 27cfced..524bb6f 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -206,14 +206,7 @@ struct cpuidle_governor { extern int cpuidle_register_governor(struct cpuidle_governor *gov); extern void cpuidle_unregister_governor(struct cpuidle_governor *gov); -#ifdef CONFIG_INTEL_IDLE -extern int intel_idle_cpu_init(int cpu); #else -static inline int intel_idle_cpu_init(int cpu) { return -1; } -#endif - -#else -static inline int intel_idle_cpu_init(int cpu) { return -1; } static inline int cpuidle_register_governor(struct cpuidle_governor *gov) {return 0;} -- cgit v0.10.2 From 9033132ddff7d096f3e54704c4250384c5ab3aa2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 6 Jul 2012 19:06:19 +0200 Subject: msi-laptop: Use struct dev_pm_ops for power management Make the msi-laptop driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki Acked-by: Lee, Chun-Yi diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index bb51321..f644418 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -85,7 +85,8 @@ #define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4 #define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4) -static int msi_laptop_resume(struct platform_device *device); +static int msi_laptop_resume(struct device *device); +static SIMPLE_DEV_PM_OPS(msi_laptop_pm, NULL, msi_laptop_resume); #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f @@ -437,8 +438,8 @@ static struct platform_driver msipf_driver = { .driver = { .name = "msi-laptop-pf", .owner = THIS_MODULE, + .pm = &msi_laptop_pm, }, - .resume = msi_laptop_resume, }; static struct platform_device *msipf_device; @@ -752,7 +753,7 @@ err_bluetooth: return retval; } -static int msi_laptop_resume(struct platform_device *device) +static int msi_laptop_resume(struct device *device) { u8 data; int result; -- cgit v0.10.2 From 156ffcb42a3b3fbebbb7f0e08e679b0954c0829c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 5 Jul 2012 22:11:33 +0200 Subject: mg_disk: Use struct dev_pm_ops for power management Make the mg_disk driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c index 76fa3de..1788f491 100644 --- a/drivers/block/mg_disk.c +++ b/drivers/block/mg_disk.c @@ -780,9 +780,9 @@ static const struct block_device_operations mg_disk_ops = { .getgeo = mg_getgeo }; -static int mg_suspend(struct platform_device *plat_dev, pm_message_t state) +static int mg_suspend(struct device *dev) { - struct mg_drv_data *prv_data = plat_dev->dev.platform_data; + struct mg_drv_data *prv_data = dev->platform_data; struct mg_host *host = prv_data->host; if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) @@ -804,9 +804,9 @@ static int mg_suspend(struct platform_device *plat_dev, pm_message_t state) return 0; } -static int mg_resume(struct platform_device *plat_dev) +static int mg_resume(struct device *dev) { - struct mg_drv_data *prv_data = plat_dev->dev.platform_data; + struct mg_drv_data *prv_data = dev->platform_data; struct mg_host *host = prv_data->host; if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) @@ -825,6 +825,8 @@ static int mg_resume(struct platform_device *plat_dev) return 0; } +static SIMPLE_DEV_PM_OPS(mg_pm, mg_suspend, mg_resume); + static int mg_probe(struct platform_device *plat_dev) { struct mg_host *host; @@ -1074,11 +1076,10 @@ static int mg_remove(struct platform_device *plat_dev) static struct platform_driver mg_disk_driver = { .probe = mg_probe, .remove = mg_remove, - .suspend = mg_suspend, - .resume = mg_resume, .driver = { .name = MG_DEV_NAME, .owner = THIS_MODULE, + .pm = &mg_pm, } }; -- cgit v0.10.2 From 8651f97bd951d0bb1c10fa24e3fa3455193f3548 Mon Sep 17 00:00:00 2001 From: Preeti U Murthy Date: Mon, 9 Jul 2012 10:12:56 +0200 Subject: PM / cpuidle: System resume hang fix with cpuidle On certain bios, resume hangs if cpus are allowed to enter idle states during suspend [1]. This was fixed in apci idle driver [2].But intel_idle driver does not have this fix. Thus instead of replicating the fix in both the idle drivers, or in more platform specific idle drivers if needed, the more general cpuidle infrastructure could handle this. A suspend callback in cpuidle_driver could handle this fix. But a cpuidle_driver provides only basic functionalities like platform idle state detection capability and mechanisms to support entry and exit into CPU idle states. All other cpuidle functions are found in the cpuidle generic infrastructure for good reason that all cpuidle drivers, irrepective of their platforms will support these functions. One option therefore would be to register a suspend callback in cpuidle which handles this fix. This could be called through a PM_SUSPEND_PREPARE notifier. But this is too generic a notfier for a driver to handle. Also, ideally the job of cpuidle is not to handle side effects of suspend. It should expose the interfaces which "handle cpuidle 'during' suspend" or any other operation, which the subsystems call during that respective operation. The fix demands that during suspend, no cpus should be allowed to enter deep C-states. The interface cpuidle_uninstall_idle_handler() in cpuidle ensures that. Not just that it also kicks all the cpus which are already in idle out of their idle states which was being done during cpu hotplug through a CPU_DYING_FROZEN callbacks. Now the question arises about when during suspend should cpuidle_uninstall_idle_handler() be called. Since we are dealing with drivers it seems best to call this function during dpm_suspend(). Delaying the call till dpm_suspend_noirq() does no harm, as long as it is before cpu_hotplug_begin() to avoid race conditions with cpu hotpulg operations. In dpm_suspend_noirq(), it would be wise to place this call before suspend_device_irqs() to avoid ugly interactions with the same. Ananlogously, during resume. References: [1] https://bugs.launchpad.net/ubuntu/+source/linux/+bug/674075. [2] http://marc.info/?l=linux-pm&m=133958534231884&w=2 Reported-and-tested-by: Dave Hansen Signed-off-by: Preeti U Murthy Reviewed-by: Srivatsa S. Bhat Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 47a8caa..d8366ee 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -221,10 +221,6 @@ static void lapic_timer_state_broadcast(struct acpi_processor *pr, #endif -/* - * Suspend / resume control - */ -static int acpi_idle_suspend; static u32 saved_bm_rld; static void acpi_idle_bm_rld_save(void) @@ -243,21 +239,13 @@ static void acpi_idle_bm_rld_restore(void) int acpi_processor_suspend(struct acpi_device * device, pm_message_t state) { - if (acpi_idle_suspend == 1) - return 0; - acpi_idle_bm_rld_save(); - acpi_idle_suspend = 1; return 0; } int acpi_processor_resume(struct acpi_device * device) { - if (acpi_idle_suspend == 0) - return 0; - acpi_idle_bm_rld_restore(); - acpi_idle_suspend = 0; return 0; } @@ -763,11 +751,6 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, local_irq_disable(); - if (acpi_idle_suspend) { - local_irq_enable(); - cpu_relax(); - return -EBUSY; - } lapic_timer_state_broadcast(pr, cx, 1); kt1 = ktime_get_real(); @@ -838,11 +821,6 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, local_irq_disable(); - if (acpi_idle_suspend) { - local_irq_enable(); - cpu_relax(); - return -EBUSY; - } if (cx->entry_method != ACPI_CSTATE_FFH) { current_thread_info()->status &= ~TS_POLLING; @@ -928,8 +906,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, drv, drv->safe_state_index); } else { local_irq_disable(); - if (!acpi_idle_suspend) - acpi_safe_halt(); + acpi_safe_halt(); local_irq_enable(); return -EBUSY; } @@ -937,11 +914,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, local_irq_disable(); - if (acpi_idle_suspend) { - local_irq_enable(); - cpu_relax(); - return -EBUSY; - } if (cx->entry_method != ACPI_CSTATE_FFH) { current_thread_info()->status &= ~TS_POLLING; diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9cb845e..63048f7 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -28,7 +28,7 @@ #include #include #include - +#include #include "../base.h" #include "power.h" @@ -467,6 +467,7 @@ static void dpm_resume_noirq(pm_message_t state) mutex_unlock(&dpm_list_mtx); dpm_show_time(starttime, state, "noirq"); resume_device_irqs(); + cpuidle_resume(); } /** @@ -867,6 +868,7 @@ static int dpm_suspend_noirq(pm_message_t state) ktime_t starttime = ktime_get(); int error = 0; + cpuidle_pause(); suspend_device_irqs(); mutex_lock(&dpm_list_mtx); while (!list_empty(&dpm_late_early_list)) { diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 04e4b76..efa9a2c 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -201,6 +201,22 @@ void cpuidle_resume_and_unlock(void) EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock); +/* Currently used in suspend/resume path to suspend cpuidle */ +void cpuidle_pause(void) +{ + mutex_lock(&cpuidle_lock); + cpuidle_uninstall_idle_handler(); + mutex_unlock(&cpuidle_lock); +} + +/* Currently used in suspend/resume path to resume cpuidle */ +void cpuidle_resume(void) +{ + mutex_lock(&cpuidle_lock); + cpuidle_install_idle_handler(); + mutex_unlock(&cpuidle_lock); +} + /** * cpuidle_wrap_enter - performs timekeeping and irqen around enter function * @dev: pointer to a valid cpuidle_device object diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 524bb6f..ca6cdf5 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -145,6 +145,8 @@ extern void cpuidle_unregister_device(struct cpuidle_device *dev); extern void cpuidle_pause_and_lock(void); extern void cpuidle_resume_and_unlock(void); +extern void cpuidle_pause(void); +extern void cpuidle_resume(void); extern int cpuidle_enable_device(struct cpuidle_device *dev); extern void cpuidle_disable_device(struct cpuidle_device *dev); extern int cpuidle_wrap_enter(struct cpuidle_device *dev, @@ -168,6 +170,8 @@ static inline void cpuidle_unregister_device(struct cpuidle_device *dev) { } static inline void cpuidle_pause_and_lock(void) { } static inline void cpuidle_resume_and_unlock(void) { } +static inline void cpuidle_pause(void) { } +static inline void cpuidle_resume(void) { } static inline int cpuidle_enable_device(struct cpuidle_device *dev) {return -ENODEV; } static inline void cpuidle_disable_device(struct cpuidle_device *dev) { } -- cgit v0.10.2 From 7650572a8baa79d317ca5d37a7998593ff0767af Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 6 Jul 2012 19:08:53 +0200 Subject: omap-rng: Use struct dev_pm_ops for power management Make the omap-rng driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index 1412565..d706bd0e 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -162,22 +162,24 @@ static int __exit omap_rng_remove(struct platform_device *pdev) #ifdef CONFIG_PM -static int omap_rng_suspend(struct platform_device *pdev, pm_message_t message) +static int omap_rng_suspend(struct device *dev) { omap_rng_write_reg(RNG_MASK_REG, 0x0); return 0; } -static int omap_rng_resume(struct platform_device *pdev) +static int omap_rng_resume(struct device *dev) { omap_rng_write_reg(RNG_MASK_REG, 0x1); return 0; } +static SIMPLE_DEV_PM_OPS(omap_rng_pm, omap_rng_suspend, omap_rng_resume); +#define OMAP_RNG_PM (&omap_rng_pm) + #else -#define omap_rng_suspend NULL -#define omap_rng_resume NULL +#define OMAP_RNG_PM NULL #endif @@ -188,11 +190,10 @@ static struct platform_driver omap_rng_driver = { .driver = { .name = "omap_rng", .owner = THIS_MODULE, + .pm = OMAP_RNG_PM, }, .probe = omap_rng_probe, .remove = __exit_p(omap_rng_remove), - .suspend = omap_rng_suspend, - .resume = omap_rng_resume }; static int __init omap_rng_init(void) -- cgit v0.10.2 From 035e2ce8eb7412dbcb8522c19676a1dd52f3c024 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 6 Jul 2012 19:09:01 +0200 Subject: PM / TPM: Drop unused pm_message_t argument from tpm_pm_suspend() The tpm_pm_suspend()'s second argument of type pm_message_t is not used, so remove it. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index ad7c732..ae43ac5 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1274,7 +1274,7 @@ static struct tpm_input_header savestate_header = { * We are about to suspend. Save the TPM state * so that it can be restored. */ -int tpm_pm_suspend(struct device *dev, pm_message_t pm_state) +int tpm_pm_suspend(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); struct tpm_cmd_t cmd; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index b1c5280..917f727 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -299,7 +299,7 @@ extern ssize_t tpm_write(struct file *, const char __user *, size_t, loff_t *); extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *); extern void tpm_remove_hardware(struct device *); -extern int tpm_pm_suspend(struct device *, pm_message_t); +extern int tpm_pm_suspend(struct device *); extern int tpm_pm_resume(struct device *); extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long, wait_queue_head_t *); diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index c64a1bc..8244d87 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -170,7 +170,7 @@ static void atml_plat_remove(void) static int tpm_atml_suspend(struct platform_device *dev, pm_message_t msg) { - return tpm_pm_suspend(&dev->dev, msg); + return tpm_pm_suspend(&dev->dev); } static int tpm_atml_resume(struct platform_device *dev) diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 4d24648..8e99657 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -276,7 +276,7 @@ static void tpm_nsc_remove(struct device *dev) static int tpm_nsc_suspend(struct platform_device *dev, pm_message_t msg) { - return tpm_pm_suspend(&dev->dev, msg); + return tpm_pm_suspend(&dev->dev); } static int tpm_nsc_resume(struct platform_device *dev) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index d2a70ca..555a643 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -750,7 +750,7 @@ static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, static int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg) { - return tpm_pm_suspend(&dev->dev, msg); + return tpm_pm_suspend(&dev->dev); } static int tpm_tis_pnp_resume(struct pnp_dev *dev) @@ -808,7 +808,7 @@ MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe"); #endif static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg) { - return tpm_pm_suspend(&dev->dev, msg); + return tpm_pm_suspend(&dev->dev); } static int tpm_tis_resume(struct platform_device *dev) -- cgit v0.10.2 From 8324be05380be044df8b70cb4bfb0c0b50eec3e5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 6 Jul 2012 19:09:13 +0200 Subject: tpm_atmel: Use struct dev_pm_ops for power management Make the tpm_atmel driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. This allows the driver to use tpm_pm_suspend() and tpm_pm_resume() as its PM callbacks directly, without defining its own PM callback routines. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index 8244d87..678d570 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -168,22 +168,14 @@ static void atml_plat_remove(void) } } -static int tpm_atml_suspend(struct platform_device *dev, pm_message_t msg) -{ - return tpm_pm_suspend(&dev->dev); -} +static SIMPLE_DEV_PM_OPS(tpm_atml_pm, tpm_pm_suspend, tpm_pm_resume); -static int tpm_atml_resume(struct platform_device *dev) -{ - return tpm_pm_resume(&dev->dev); -} static struct platform_driver atml_drv = { .driver = { .name = "tpm_atmel", .owner = THIS_MODULE, + .pm = &tpm_atml_pm, }, - .suspend = tpm_atml_suspend, - .resume = tpm_atml_resume, }; static int __init init_atmel(void) -- cgit v0.10.2 From b633f0507e19224f1527921644722bfb36db9bb0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 6 Jul 2012 19:09:20 +0200 Subject: tpm_tis: Use struct dev_pm_ops for power management Make the tpm_tis driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. This allows the driver to use tpm_pm_suspend() as its suspend callback directly, without defining its own suspend callback routine. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 555a643..89682fa 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -806,27 +806,25 @@ module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id, sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444); MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe"); #endif -static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg) -{ - return tpm_pm_suspend(&dev->dev); -} -static int tpm_tis_resume(struct platform_device *dev) +static int tpm_tis_resume(struct device *dev) { - struct tpm_chip *chip = dev_get_drvdata(&dev->dev); + struct tpm_chip *chip = dev_get_drvdata(dev); if (chip->vendor.irq) tpm_tis_reenable_interrupts(chip); - return tpm_pm_resume(&dev->dev); + return tpm_pm_resume(dev); } + +static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume); + static struct platform_driver tis_drv = { .driver = { .name = "tpm_tis", .owner = THIS_MODULE, + .pm = &tpm_tis_pm, }, - .suspend = tpm_tis_suspend, - .resume = tpm_tis_resume, }; static struct platform_device *pdev; -- cgit v0.10.2 From ca9a2054fe041326888811a94f17459acab9f950 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 6 Jul 2012 19:09:28 +0200 Subject: tpm_nsc: Use struct dev_pm_ops for power management Make the tpm_nsc driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. This allows the driver to use tpm_pm_suspend() and tpm_pm_resume() as its PM callbacks directly, without defining its own PM callback routines. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 8e99657..640c9a4 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -274,22 +274,13 @@ static void tpm_nsc_remove(struct device *dev) } } -static int tpm_nsc_suspend(struct platform_device *dev, pm_message_t msg) -{ - return tpm_pm_suspend(&dev->dev); -} - -static int tpm_nsc_resume(struct platform_device *dev) -{ - return tpm_pm_resume(&dev->dev); -} +static SIMPLE_DEV_PM_OPS(tpm_nsc_pm, tpm_pm_suspend, tpm_pm_resume); static struct platform_driver nsc_drv = { - .suspend = tpm_nsc_suspend, - .resume = tpm_nsc_resume, .driver = { .name = "tpm_nsc", .owner = THIS_MODULE, + .pm = &tpm_nsc_pm, }, }; -- cgit v0.10.2 From afec8a770a6461d3e57a779484a10ef3ed0c800f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 8 Jul 2012 16:13:43 +0200 Subject: PM / IPMI: Remove empty legacy PCI PM callbacks The legacy PM callbacks provided by the IPMI PCI driver are empty routines returning 0, so they can be safely dropped. Signed-off-by: Rafael J. Wysocki Acked-by: Corey Minyard diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 1e638ff..83f85cf 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2503,18 +2503,6 @@ static void __devexit ipmi_pci_remove(struct pci_dev *pdev) cleanup_one_si(info); } -#ifdef CONFIG_PM -static int ipmi_pci_suspend(struct pci_dev *pdev, pm_message_t state) -{ - return 0; -} - -static int ipmi_pci_resume(struct pci_dev *pdev) -{ - return 0; -} -#endif - static struct pci_device_id ipmi_pci_devices[] = { { PCI_DEVICE(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID) }, { PCI_DEVICE_CLASS(PCI_ERMC_CLASSCODE, PCI_ERMC_CLASSCODE_MASK) }, @@ -2527,10 +2515,6 @@ static struct pci_driver ipmi_pci_driver = { .id_table = ipmi_pci_devices, .probe = ipmi_pci_probe, .remove = __devexit_p(ipmi_pci_remove), -#ifdef CONFIG_PM - .suspend = ipmi_pci_suspend, - .resume = ipmi_pci_resume, -#endif }; #endif /* CONFIG_PCI */ -- cgit v0.10.2 From 4f31f5b19eb0418a847b989abc9ac22af1991fe2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 9 Jul 2012 23:52:04 +0200 Subject: PM / crypto / ux500: Use struct dev_pm_ops for power management Make the ux500 crypto driver define its PM callbacks through struct dev_pm_ops objects rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki Acked-by: Linus Walleij diff --git a/drivers/crypto/ux500/cryp/cryp_core.c b/drivers/crypto/ux500/cryp/cryp_core.c index 7cac127..1c307e1 100644 --- a/drivers/crypto/ux500/cryp/cryp_core.c +++ b/drivers/crypto/ux500/cryp/cryp_core.c @@ -1661,27 +1661,26 @@ static void ux500_cryp_shutdown(struct platform_device *pdev) } -static int ux500_cryp_suspend(struct platform_device *pdev, pm_message_t state) +static int ux500_cryp_suspend(struct device *dev) { int ret; + struct platform_device *pdev = to_platform_device(dev); struct cryp_device_data *device_data; struct resource *res_irq; struct cryp_ctx *temp_ctx = NULL; - dev_dbg(&pdev->dev, "[%s]", __func__); + dev_dbg(dev, "[%s]", __func__); /* Handle state? */ device_data = platform_get_drvdata(pdev); if (!device_data) { - dev_err(&pdev->dev, "[%s]: platform_get_drvdata() failed!", - __func__); + dev_err(dev, "[%s]: platform_get_drvdata() failed!", __func__); return -ENOMEM; } res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res_irq) - dev_err(&pdev->dev, "[%s]: IORESOURCE_IRQ, unavailable", - __func__); + dev_err(dev, "[%s]: IORESOURCE_IRQ, unavailable", __func__); else disable_irq(res_irq->start); @@ -1692,32 +1691,32 @@ static int ux500_cryp_suspend(struct platform_device *pdev, pm_message_t state) if (device_data->current_ctx == ++temp_ctx) { if (down_interruptible(&driver_data.device_allocation)) - dev_dbg(&pdev->dev, "[%s]: down_interruptible() " - "failed", __func__); - ret = cryp_disable_power(&pdev->dev, device_data, false); + dev_dbg(dev, "[%s]: down_interruptible() failed", + __func__); + ret = cryp_disable_power(dev, device_data, false); } else - ret = cryp_disable_power(&pdev->dev, device_data, true); + ret = cryp_disable_power(dev, device_data, true); if (ret) - dev_err(&pdev->dev, "[%s]: cryp_disable_power()", __func__); + dev_err(dev, "[%s]: cryp_disable_power()", __func__); return ret; } -static int ux500_cryp_resume(struct platform_device *pdev) +static int ux500_cryp_resume(struct device *dev) { int ret = 0; + struct platform_device *pdev = to_platform_device(dev); struct cryp_device_data *device_data; struct resource *res_irq; struct cryp_ctx *temp_ctx = NULL; - dev_dbg(&pdev->dev, "[%s]", __func__); + dev_dbg(dev, "[%s]", __func__); device_data = platform_get_drvdata(pdev); if (!device_data) { - dev_err(&pdev->dev, "[%s]: platform_get_drvdata() failed!", - __func__); + dev_err(dev, "[%s]: platform_get_drvdata() failed!", __func__); return -ENOMEM; } @@ -1730,11 +1729,10 @@ static int ux500_cryp_resume(struct platform_device *pdev) if (!device_data->current_ctx) up(&driver_data.device_allocation); else - ret = cryp_enable_power(&pdev->dev, device_data, true); + ret = cryp_enable_power(dev, device_data, true); if (ret) - dev_err(&pdev->dev, "[%s]: cryp_enable_power() failed!", - __func__); + dev_err(dev, "[%s]: cryp_enable_power() failed!", __func__); else { res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res_irq) @@ -1744,15 +1742,16 @@ static int ux500_cryp_resume(struct platform_device *pdev) return ret; } +static SIMPLE_DEV_PM_OPS(ux500_cryp_pm, ux500_cryp_suspend, ux500_cryp_resume); + static struct platform_driver cryp_driver = { .probe = ux500_cryp_probe, .remove = ux500_cryp_remove, .shutdown = ux500_cryp_shutdown, - .suspend = ux500_cryp_suspend, - .resume = ux500_cryp_resume, .driver = { .owner = THIS_MODULE, .name = "cryp1" + .pm = &ux500_cryp_pm, } }; diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c index 6dbb9ec..08d5032 100644 --- a/drivers/crypto/ux500/hash/hash_core.c +++ b/drivers/crypto/ux500/hash/hash_core.c @@ -1894,19 +1894,17 @@ static void ux500_hash_shutdown(struct platform_device *pdev) /** * ux500_hash_suspend - Function that suspends the hash device. - * @pdev: The platform device. - * @state: - + * @dev: Device to suspend. */ -static int ux500_hash_suspend(struct platform_device *pdev, pm_message_t state) +static int ux500_hash_suspend(struct device *dev) { int ret; struct hash_device_data *device_data; struct hash_ctx *temp_ctx = NULL; - device_data = platform_get_drvdata(pdev); + device_data = dev_get_drvdata(dev); if (!device_data) { - dev_err(&pdev->dev, "[%s] platform_get_drvdata() failed!", - __func__); + dev_err(dev, "[%s] platform_get_drvdata() failed!", __func__); return -ENOMEM; } @@ -1917,33 +1915,32 @@ static int ux500_hash_suspend(struct platform_device *pdev, pm_message_t state) if (device_data->current_ctx == ++temp_ctx) { if (down_interruptible(&driver_data.device_allocation)) - dev_dbg(&pdev->dev, "[%s]: down_interruptible() " - "failed", __func__); + dev_dbg(dev, "[%s]: down_interruptible() failed", + __func__); ret = hash_disable_power(device_data, false); } else ret = hash_disable_power(device_data, true); if (ret) - dev_err(&pdev->dev, "[%s]: hash_disable_power()", __func__); + dev_err(dev, "[%s]: hash_disable_power()", __func__); return ret; } /** * ux500_hash_resume - Function that resume the hash device. - * @pdev: The platform device. + * @dev: Device to resume. */ -static int ux500_hash_resume(struct platform_device *pdev) +static int ux500_hash_resume(struct device *dev) { int ret = 0; struct hash_device_data *device_data; struct hash_ctx *temp_ctx = NULL; - device_data = platform_get_drvdata(pdev); + device_data = dev_get_drvdata(dev); if (!device_data) { - dev_err(&pdev->dev, "[%s] platform_get_drvdata() failed!", - __func__); + dev_err(dev, "[%s] platform_get_drvdata() failed!", __func__); return -ENOMEM; } @@ -1958,21 +1955,21 @@ static int ux500_hash_resume(struct platform_device *pdev) ret = hash_enable_power(device_data, true); if (ret) - dev_err(&pdev->dev, "[%s]: hash_enable_power() failed!", - __func__); + dev_err(dev, "[%s]: hash_enable_power() failed!", __func__); return ret; } +static SIMPLE_DEV_PM_OPS(ux500_hash_pm, ux500_hash_suspend, ux500_hash_resume); + static struct platform_driver hash_driver = { .probe = ux500_hash_probe, .remove = ux500_hash_remove, .shutdown = ux500_hash_shutdown, - .suspend = ux500_hash_suspend, - .resume = ux500_hash_resume, .driver = { .owner = THIS_MODULE, .name = "hash1", + .pm = &ux500_hash_pm, } }; -- cgit v0.10.2 From 8951ef021904752a843a81fee785f1b180f31c07 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 10 Jul 2012 21:47:07 +0200 Subject: PM / Domains: Add missing static storage class specifier in domain.c file Fixes the folloiwng sparse warning: drivers/base/power/domain.c:149:5: warning: symbol '__pm_genpd_poweron' was not declared. Should it be static? Signed-off-by: Sachin Kamat Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 9727bc5..4f8e0d2 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -159,7 +159,7 @@ static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) * Restore power to @genpd and all of its masters so that it is possible to * resume a device belonging to it. */ -int __pm_genpd_poweron(struct generic_pm_domain *genpd) +static int __pm_genpd_poweron(struct generic_pm_domain *genpd) __releases(&genpd->lock) __acquires(&genpd->lock) { struct gpd_link *link; -- cgit v0.10.2 From db79e53dd5957321ca78efe6c0cda578034e381b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 11 Jul 2012 12:25:49 +0200 Subject: PM / Domains: Replace plain integer with NULL pointer in domain.c file Fixes the following sparse warning: drivers/base/power/domain.c:1679:55: warning: Using plain integer as NULL pointer Signed-off-by: Sachin Kamat Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 4f8e0d2..8d67417 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1676,7 +1676,7 @@ int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) if (dev->power.subsys_data->domain_data) { gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); - gpd_data->ops = (struct gpd_dev_ops){ 0 }; + gpd_data->ops = (struct gpd_dev_ops){ NULL }; if (clear_td) gpd_data->td = (struct gpd_timing_data){ 0 }; -- cgit v0.10.2 From 18468843fac331dfbb700901c8012d17373adfec Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Jul 2012 22:36:28 +0200 Subject: olpc-xo15-sci: Use struct dev_pm_ops for power management Make the OLPC XO15 SCI driver define its resume callback through a struct dev_pm_ops object rather than by using a legacy PM hook in struct acpi_device_ops. Reported-by: Randy Dunlap Signed-off-by: Rafael J. Wysocki Acked-by: Randy Dunlap diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index 23e5b9d..599be49 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -203,7 +203,7 @@ static int xo15_sci_remove(struct acpi_device *device, int type) return 0; } -static int xo15_sci_resume(struct acpi_device *device) +static int xo15_sci_resume(struct device *dev) { /* Enable all EC events */ olpc_ec_mask_write(EC_SCI_SRC_ALL); @@ -215,6 +215,8 @@ static int xo15_sci_resume(struct acpi_device *device) return 0; } +static SIMPLE_DEV_PM_OPS(xo15_sci_pm, NULL, xo15_sci_resume); + static const struct acpi_device_id xo15_sci_device_ids[] = { {"XO15EC", 0}, {"", 0}, @@ -227,8 +229,8 @@ static struct acpi_driver xo15_sci_drv = { .ops = { .add = xo15_sci_add, .remove = xo15_sci_remove, - .resume = xo15_sci_resume, }, + .drv.pm = &xo15_sci_pm, }; static int __init xo15_sci_init(void) -- cgit v0.10.2 From 8e9afafdad59f5973a5e72e05db9802f82091398 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 11 Jul 2012 22:42:52 +0200 Subject: PM / Domains: Fix build warning for CONFIG_PM_RUNTIME unset The functions genpd_save_dev() and genpd_restore_dev() are not used for CONFIG_PM_RUNTIME unset, so move them under an appropriate #ifdef. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 8d67417..ba3487c 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -75,19 +75,6 @@ static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) start_latency_ns, "start"); } -static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) -{ - return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev, - save_state_latency_ns, "state save"); -} - -static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev) -{ - return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev, - restore_state_latency_ns, - "state restore"); -} - static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) { bool ret = false; @@ -273,6 +260,19 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd) #ifdef CONFIG_PM_RUNTIME +static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) +{ + return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev, + save_state_latency_ns, "state save"); +} + +static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev) +{ + return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev, + restore_state_latency_ns, + "state restore"); +} + static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, unsigned long val, void *ptr) { -- cgit v0.10.2 From d181b49eb3d76ed6a134cb599456176f466047c0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 11 Jul 2012 22:42:58 +0200 Subject: PM / Sleep: Fix build warning in sysfs.c for CONFIG_PM_SLEEP unset The power/async device sysfs attribute is only used if both CONFIG_PM_ADVANCED_DEBUG and CONFIG_PM_SLEEP are set, but the code implementing it doesn't depend on CONFIG_PM_SLEEP. As a result, a build warning appears if CONFIG_PM_ADVANCED_DEBUG is set and CONFIG_PM_SLEEP is not set. Fix it by adding a #ifdef CONFIG_PM_SLEEP around the code in question. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 48be2ad..b91dc6f 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -474,6 +474,8 @@ static DEVICE_ATTR(runtime_enabled, 0444, rtpm_enabled_show, NULL); #endif +#ifdef CONFIG_PM_SLEEP + static ssize_t async_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -500,6 +502,8 @@ static ssize_t async_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(async, 0644, async_show, async_store); + +#endif #endif /* CONFIG_PM_ADVANCED_DEBUG */ static struct attribute *power_attrs[] = { -- cgit v0.10.2 From b45bae36b805287c76366c908de58b5c2c0d46b0 Mon Sep 17 00:00:00 2001 From: Paul Fox Date: Mon, 16 Jul 2012 21:56:26 +0200 Subject: rtc-cmos: report wakeups from interrupt handler When suspending the system with an important RTC wake alarm active, it is possible that the RTC alarm will expire before the system has gone to sleep (e.g. short alarm timer, or an unusually long suspend routine). If this happens, the RTC alarm should trigger a wakeup event, possibly aborting system suspend. This condition can be detected in the form of an RTC alarm interrupt. Signed-off-by: Paul Fox Signed-off-by: Daniel Drake Signed-off-by: Rafael J. Wysocki diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 4267789..132333d 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -568,6 +568,7 @@ static irqreturn_t cmos_interrupt(int irq, void *p) hpet_mask_rtc_irq_bit(RTC_AIE); CMOS_READ(RTC_INTR_FLAGS); + pm_wakeup_event(cmos_rtc.dev, 0); } spin_unlock(&rtc_lock); -- cgit v0.10.2 From 64d45f07b433090ee1329cf74989d973875028cb Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 17 Jul 2012 22:15:24 +0200 Subject: cpuidle / ACPI : remove latency_ticks from acpi_processor_cx structure Remove the latency_ticks field as it is not used. Signed-off-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index d8366ee..b0d2bbe 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -583,7 +583,6 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr, */ cx->valid = 1; - cx->latency_ticks = cx->latency; /* * On older chipsets, BM_RLD needs to be set * in order for Bus Master activity to wake the @@ -616,7 +615,6 @@ static int acpi_processor_power_verify(struct acpi_processor *pr) if (!cx->address) break; cx->valid = 1; - cx->latency_ticks = cx->latency; /* Normalize latency */ break; case ACPI_STATE_C3: diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 9d65047..a70cbcc 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -59,7 +59,6 @@ struct acpi_processor_cx { u8 entry_method; u8 index; u32 latency; - u32 latency_ticks; u32 power; u32 usage; u64 time; -- cgit v0.10.2 From 53b70951d9ef68420775b69e59ba720cc15c2643 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 17 Jul 2012 22:16:00 +0200 Subject: cpuidle / ACPI: remove usage from acpi_processor_cx structure Remove the usage field as it is not used. Signed-off-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index b0d2bbe..f355f1a 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -760,7 +760,6 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, dev->last_residency = (int)idle_time; local_irq_enable(); - cx->usage++; lapic_timer_state_broadcast(pr, cx, 0); return index; @@ -863,8 +862,6 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, if (cx->entry_method != ACPI_CSTATE_FFH) current_thread_info()->status |= TS_POLLING; - cx->usage++; - lapic_timer_state_broadcast(pr, cx, 0); cx->time += idle_time; return index; @@ -984,8 +981,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, if (cx->entry_method != ACPI_CSTATE_FFH) current_thread_info()->status |= TS_POLLING; - cx->usage++; - lapic_timer_state_broadcast(pr, cx, 0); cx->time += idle_time; return index; diff --git a/include/acpi/processor.h b/include/acpi/processor.h index a70cbcc..022b2e8 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -60,7 +60,6 @@ struct acpi_processor_cx { u8 index; u32 latency; u32 power; - u32 usage; u64 time; u8 bm_sts_skip; char desc[ACPI_CX_DESC_LEN]; -- cgit v0.10.2 From aa713cc3b22ccd24dc55df4e0770490781fe6a76 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 17 Jul 2012 22:16:04 +0200 Subject: cpuilde / ACPI: remove time from acpi_processor_cx structure Remove the time field as it is not used. Signed-off-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index f355f1a..4cf9648 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -863,7 +863,6 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, current_thread_info()->status |= TS_POLLING; lapic_timer_state_broadcast(pr, cx, 0); - cx->time += idle_time; return index; } @@ -982,7 +981,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, current_thread_info()->status |= TS_POLLING; lapic_timer_state_broadcast(pr, cx, 0); - cx->time += idle_time; return index; } diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 022b2e8..c72a801 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -60,7 +60,6 @@ struct acpi_processor_cx { u8 index; u32 latency; u32 power; - u64 time; u8 bm_sts_skip; char desc[ACPI_CX_DESC_LEN]; }; -- cgit v0.10.2 From 7664e969359917ba3212c5d6efdec494eaf8586e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Jul 2012 22:38:08 +0200 Subject: PM / Sleep: Add missing static storage class specifiers in main.c Fix the following sparse warnings: drivers/base/power/main.c:48:1: warning: symbol 'dpm_prepared_list' was not declared. Should it be static? drivers/base/power/main.c:49:1: warning: symbol 'dpm_suspended_list' was not declared. Should it be static? drivers/base/power/main.c:50:1: warning: symbol 'dpm_late_early_list' was not declared. Should it be static? drivers/base/power/main.c:51:1: warning: symbol 'dpm_noirq_list' was not declared. Should it be static? Signed-off-by: Sachin Kamat Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index df5f41d..dbc3dbe 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -45,10 +45,10 @@ typedef int (*pm_callback_t)(struct device *); */ LIST_HEAD(dpm_list); -LIST_HEAD(dpm_prepared_list); -LIST_HEAD(dpm_suspended_list); -LIST_HEAD(dpm_late_early_list); -LIST_HEAD(dpm_noirq_list); +static LIST_HEAD(dpm_prepared_list); +static LIST_HEAD(dpm_suspended_list); +static LIST_HEAD(dpm_late_early_list); +static LIST_HEAD(dpm_noirq_list); struct suspend_stats suspend_stats; static DEFINE_MUTEX(dpm_list_mtx); -- cgit v0.10.2 From 11388c87d2abca1f01975ced28ce9eacea239104 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 19 Jul 2012 00:00:58 +0200 Subject: PM / Sleep: Require CAP_BLOCK_SUSPEND to use wake_lock/wake_unlock Require processes wanting to use the wake_lock/wake_unlock sysfs files to have the CAP_BLOCK_SUSPEND capability, which also is required for the eventpoll EPOLLWAKEUP flag to be effective, so that all interfaces related to blocking autosleep depend on the same capability. Signed-off-by: Rafael J. Wysocki Cc: stable@vger.kernel.org Acked-by: Michael Kerrisk diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c index c8fba33..8f50de3 100644 --- a/kernel/power/wakelock.c +++ b/kernel/power/wakelock.c @@ -9,6 +9,7 @@ * manipulate wakelocks on Android. */ +#include #include #include #include @@ -188,6 +189,9 @@ int pm_wake_lock(const char *buf) size_t len; int ret = 0; + if (!capable(CAP_BLOCK_SUSPEND)) + return -EPERM; + while (*str && !isspace(*str)) str++; @@ -231,6 +235,9 @@ int pm_wake_unlock(const char *buf) size_t len; int ret = 0; + if (!capable(CAP_BLOCK_SUSPEND)) + return -EPERM; + len = strlen(buf); if (!len) return -EINVAL; -- cgit v0.10.2 From 83618092645f970668f47b6ee2fec1afdca32efd Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Jul 2012 22:38:18 +0200 Subject: PM / QoS: Use NULL pointer instead of plain integer in pm_qos.h Fix the following sparse warning: include/linux/pm_qos.h:69:28: warning: Using plain integer as NULL pointer Signed-off-by: Sachin Kamat Signed-off-by: Rafael J. Wysocki diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 233149c..9924ea1 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -66,7 +66,7 @@ enum pm_qos_req_action { static inline int dev_pm_qos_request_active(struct dev_pm_qos_request *req) { - return req->dev != 0; + return req->dev != NULL; } int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, -- cgit v0.10.2 From ad0446eb11b6886a73e077bd087f98544b8c0403 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Jul 2012 22:38:26 +0200 Subject: PM / QoS: Use NULL pointer instead of plain integer in qos.c Fix the following sparse warning: drivers/base/power/qos.c:465:29: warning: Using plain integer as NULL pointer Signed-off-by: Sachin Kamat Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index fd849a2..74a67e0 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -462,7 +462,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); static void __dev_pm_qos_drop_user_request(struct device *dev) { dev_pm_qos_remove_request(dev->power.pq_req); - dev->power.pq_req = 0; + dev->power.pq_req = NULL; } /** -- cgit v0.10.2 From 064b021fbe470ecc9ca10f9f87af48c0fc0865fb Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 19 Jul 2012 10:38:06 +0200 Subject: PM / Sleep: call early resume handlers when suspend_noirq fails Commit cf579dfb82550e34de7ccf3ef090d8b834ccd3a9 (PM / Sleep: Introduce "late suspend" and "early resume" of devices) introduced a bug where suspend_late handlers would be called, but if dpm_suspend_noirq returned an error the early_resume handlers would never be called. All devices would end up on the dpm_late_early_list, and would never be resumed again. Fix it by calling dpm_resume_early when dpm_suspend_noirq returns an error. Signed-off-by: Colin Cross Cc: stable@vger.kernel.org Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index dbc3dbe..c642f8d 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -989,8 +989,16 @@ static int dpm_suspend_late(pm_message_t state) int dpm_suspend_end(pm_message_t state) { int error = dpm_suspend_late(state); + if (error) + return error; - return error ? : dpm_suspend_noirq(state); + error = dpm_suspend_noirq(state); + if (error) { + dpm_resume_early(state); + return error; + } + + return 0; } EXPORT_SYMBOL_GPL(dpm_suspend_end); -- cgit v0.10.2 From 53df1ad52545854fc34d336b26f3086b2fb2d6f7 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Fri, 20 Jul 2012 02:54:02 +0000 Subject: EXYNOS: bugfix on retrieving old_index from freqs.old The policy might have been changed since last call of target(). Thus, using cpufreq_frequency_table_target(), which depends on policy to find the corresponding index from a frequency, may return inconsistent index for freqs.old. Thus, old_index should be calculated not based on the current policy. We have been observing such issue when scaling_min/max_freq were updated and sometimes cuased system lockups deu to incorrectly configured voltages. Signed-off-by: MyungJoo Ham Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index b243a7e..af2d81e 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -62,8 +62,18 @@ static int exynos_target(struct cpufreq_policy *policy, goto out; } - if (cpufreq_frequency_table_target(policy, freq_table, - freqs.old, relation, &old_index)) { + /* + * The policy max have been changed so that we cannot get proper + * old_index with cpufreq_frequency_table_target(). Thus, ignore + * policy and get the index from the raw freqeuncy table. + */ + for (old_index = 0; + freq_table[old_index].frequency != CPUFREQ_TABLE_END; + old_index++) + if (freq_table[old_index].frequency == freqs.old) + break; + + if (freq_table[old_index].frequency == CPUFREQ_TABLE_END) { ret = -EINVAL; goto out; } -- cgit v0.10.2 From a9144436271583115a2230db15d0b6ae2c481d3c Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 20 Jul 2012 18:14:38 +0000 Subject: cpufreq: Fix sysfs deadlock with concurrent hotplug/frequency switch Running one program that continuously hotplugs and replugs a cpu concurrently with another program that continuously writes to the scaling_setspeed node eventually deadlocks with: ============================================= [ INFO: possible recursive locking detected ] 3.4.0 #37 Tainted: G W --------------------------------------------- filemonkey/122 is trying to acquire lock: (s_active#13){++++.+}, at: [] sysfs_remove_dir+0x9c/0xb4 but task is already holding lock: (s_active#13){++++.+}, at: [] sysfs_write_file+0xe8/0x140 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(s_active#13); lock(s_active#13); *** DEADLOCK *** May be due to missing lock nesting notation 2 locks held by filemonkey/122: #0: (&buffer->mutex){+.+.+.}, at: [] sysfs_write_file+0x28/0x140 #1: (s_active#13){++++.+}, at: [] sysfs_write_file+0xe8/0x140 stack backtrace: [] (unwind_backtrace+0x0/0x120) from [] (validate_chain+0x6f8/0x1054) [] (validate_chain+0x6f8/0x1054) from [] (__lock_acquire+0x81c/0x8d8) [] (__lock_acquire+0x81c/0x8d8) from [] (lock_acquire+0x18c/0x1e8) [] (lock_acquire+0x18c/0x1e8) from [] (sysfs_addrm_finish+0xd0/0x180) [] (sysfs_addrm_finish+0xd0/0x180) from [] (sysfs_remove_dir+0x9c/0xb4) [] (sysfs_remove_dir+0x9c/0xb4) from [] (kobject_del+0x10/0x38) [] (kobject_del+0x10/0x38) from [] (kobject_release+0xf0/0x194) [] (kobject_release+0xf0/0x194) from [] (cpufreq_cpu_put+0xc/0x24) [] (cpufreq_cpu_put+0xc/0x24) from [] (store+0x6c/0x74) [] (store+0x6c/0x74) from [] (sysfs_write_file+0x10c/0x140) [] (sysfs_write_file+0x10c/0x140) from [] (vfs_write+0xb0/0x128) [] (vfs_write+0xb0/0x128) from [] (sys_write+0x3c/0x68) [] (sys_write+0x3c/0x68) from [] (ret_fast_syscall+0x0/0x3c) This is because store() in cpufreq.c indirectly calls kobject_get() via cpufreq_cpu_get() and is the last one to call kobject_put() via cpufreq_cpu_put(). Sysfs code should not call kobject_get() or kobject_put() directly (see the comment around sysfs_schedule_callback() for more information). Fix this deadlock by introducing two new functions: struct cpufreq_policy *cpufreq_cpu_get_sysfs(unsigned int cpu) void cpufreq_cpu_put_sysfs(struct cpufreq_policy *data) which do the same thing as cpufreq_cpu_{get,put}() but don't call kobject functions. To easily trigger this deadlock you can insert an msleep() with a reasonably large value right after the fail label at the bottom of the store() function in cpufreq.c and then write scaling_setspeed in one task and offline the cpu in another. The first task will hang and be detected by the hung task detector. Signed-off-by: Stephen Boyd Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 7f2f149..fb8a527 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -138,7 +138,7 @@ void disable_cpufreq(void) static LIST_HEAD(cpufreq_governor_list); static DEFINE_MUTEX(cpufreq_governor_mutex); -struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) +static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs) { struct cpufreq_policy *data; unsigned long flags; @@ -162,7 +162,7 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) if (!data) goto err_out_put_module; - if (!kobject_get(&data->kobj)) + if (!sysfs && !kobject_get(&data->kobj)) goto err_out_put_module; spin_unlock_irqrestore(&cpufreq_driver_lock, flags); @@ -175,16 +175,35 @@ err_out_unlock: err_out: return NULL; } + +struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) +{ + return __cpufreq_cpu_get(cpu, false); +} EXPORT_SYMBOL_GPL(cpufreq_cpu_get); +static struct cpufreq_policy *cpufreq_cpu_get_sysfs(unsigned int cpu) +{ + return __cpufreq_cpu_get(cpu, true); +} -void cpufreq_cpu_put(struct cpufreq_policy *data) +static void __cpufreq_cpu_put(struct cpufreq_policy *data, bool sysfs) { - kobject_put(&data->kobj); + if (!sysfs) + kobject_put(&data->kobj); module_put(cpufreq_driver->owner); } + +void cpufreq_cpu_put(struct cpufreq_policy *data) +{ + __cpufreq_cpu_put(data, false); +} EXPORT_SYMBOL_GPL(cpufreq_cpu_put); +static void cpufreq_cpu_put_sysfs(struct cpufreq_policy *data) +{ + __cpufreq_cpu_put(data, true); +} /********************************************************************* * EXTERNALLY AFFECTING FREQUENCY CHANGES * @@ -617,7 +636,7 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) struct cpufreq_policy *policy = to_policy(kobj); struct freq_attr *fattr = to_attr(attr); ssize_t ret = -EINVAL; - policy = cpufreq_cpu_get(policy->cpu); + policy = cpufreq_cpu_get_sysfs(policy->cpu); if (!policy) goto no_policy; @@ -631,7 +650,7 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) unlock_policy_rwsem_read(policy->cpu); fail: - cpufreq_cpu_put(policy); + cpufreq_cpu_put_sysfs(policy); no_policy: return ret; } @@ -642,7 +661,7 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr, struct cpufreq_policy *policy = to_policy(kobj); struct freq_attr *fattr = to_attr(attr); ssize_t ret = -EINVAL; - policy = cpufreq_cpu_get(policy->cpu); + policy = cpufreq_cpu_get_sysfs(policy->cpu); if (!policy) goto no_policy; @@ -656,7 +675,7 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr, unlock_policy_rwsem_write(policy->cpu); fail: - cpufreq_cpu_put(policy); + cpufreq_cpu_put_sysfs(policy); no_policy: return ret; } -- cgit v0.10.2