From 4a256b5fc028e6dad0fd1b59745f743ee528c944 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 8 Jul 2009 13:22:18 +0200 Subject: DMA: Rework dw_dmac suspend_late()/resume_early() This patch reworks platform driver power management code for dw_dmac from legacy late/early callbacks to dev_pm_ops. The callbacks are converted for CONFIG_SUSPEND like this: suspend_late() -> suspend_noirq() resume_early() -> resume_noirq() Signed-off-by: Magnus Damm Acked-by: Greg Kroah-Hartman Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki --- drivers/dma/dw_dmac.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 98c9a847bf51..933c143b6a74 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1399,8 +1399,9 @@ static void dw_shutdown(struct platform_device *pdev) clk_disable(dw->clk); } -static int dw_suspend_late(struct platform_device *pdev, pm_message_t mesg) +static int dw_suspend_noirq(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct dw_dma *dw = platform_get_drvdata(pdev); dw_dma_off(platform_get_drvdata(pdev)); @@ -1408,23 +1409,27 @@ static int dw_suspend_late(struct platform_device *pdev, pm_message_t mesg) return 0; } -static int dw_resume_early(struct platform_device *pdev) +static int dw_resume_noirq(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct dw_dma *dw = platform_get_drvdata(pdev); clk_enable(dw->clk); dma_writel(dw, CFG, DW_CFG_DMA_EN); return 0; - } +static struct dev_pm_ops dw_dev_pm_ops = { + .suspend_noirq = dw_suspend_noirq, + .resume_noirq = dw_resume_noirq, +}; + static struct platform_driver dw_driver = { .remove = __exit_p(dw_remove), .shutdown = dw_shutdown, - .suspend_late = dw_suspend_late, - .resume_early = dw_resume_early, .driver = { .name = "dw_dmac", + .pm = &dw_dev_pm_ops, }, }; -- cgit v1.2.3 From 4aebac2fb9645e897864e12cdb8d1e4aadf5b2a4 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 8 Jul 2009 13:22:27 +0200 Subject: DMA: Rework txx9dmac suspend_late()/resume_early() This patch reworks platform driver power management code for txx9dmac from legacy late/early callbacks to dev_pm_ops. The callbacks are converted for CONFIG_SUSPEND like this: suspend_late() -> suspend_noirq() resume_early() -> resume_noirq() Signed-off-by: Magnus Damm Acked-by: Greg Kroah-Hartman Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki --- drivers/dma/txx9dmac.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index 88dab52926f4..7837930146a4 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -1291,17 +1291,18 @@ static void txx9dmac_shutdown(struct platform_device *pdev) txx9dmac_off(ddev); } -static int txx9dmac_suspend_late(struct platform_device *pdev, - pm_message_t mesg) +static int txx9dmac_suspend_noirq(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct txx9dmac_dev *ddev = platform_get_drvdata(pdev); txx9dmac_off(ddev); return 0; } -static int txx9dmac_resume_early(struct platform_device *pdev) +static int txx9dmac_resume_noirq(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct txx9dmac_dev *ddev = platform_get_drvdata(pdev); struct txx9dmac_platform_data *pdata = pdev->dev.platform_data; u32 mcr; @@ -1314,6 +1315,11 @@ static int txx9dmac_resume_early(struct platform_device *pdev) } +static struct dev_pm_ops txx9dmac_dev_pm_ops = { + .suspend_noirq = txx9dmac_suspend_noirq, + .resume_noirq = txx9dmac_resume_noirq, +}; + static struct platform_driver txx9dmac_chan_driver = { .remove = __exit_p(txx9dmac_chan_remove), .driver = { @@ -1324,10 +1330,9 @@ static struct platform_driver txx9dmac_chan_driver = { static struct platform_driver txx9dmac_driver = { .remove = __exit_p(txx9dmac_remove), .shutdown = txx9dmac_shutdown, - .suspend_late = txx9dmac_suspend_late, - .resume_early = txx9dmac_resume_early, .driver = { .name = "txx9dmac", + .pm = &txx9dmac_dev_pm_ops, }, }; -- cgit v1.2.3 From 57f4d4f1b72983f8c76e2f232e064730aeffe599 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 8 Jul 2009 13:22:39 +0200 Subject: I2C: Rework i2c-pxa suspend_late()/resume_early() This patch reworks platform driver power management code for i2c-pxa from legacy late/early callbacks to dev_pm_ops. The callbacks are converted for CONFIG_SUSPEND like this: suspend_late() -> suspend_noirq() resume_early() -> resume_noirq() Signed-off-by: Magnus Damm Acked-by: Greg Kroah-Hartman Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki --- drivers/i2c/busses/i2c-pxa.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 762e1e530882..049555777f67 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -1134,35 +1134,44 @@ static int __exit i2c_pxa_remove(struct platform_device *dev) } #ifdef CONFIG_PM -static int i2c_pxa_suspend_late(struct platform_device *dev, pm_message_t state) +static int i2c_pxa_suspend_noirq(struct device *dev) { - struct pxa_i2c *i2c = platform_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct pxa_i2c *i2c = platform_get_drvdata(pdev); + clk_disable(i2c->clk); + return 0; } -static int i2c_pxa_resume_early(struct platform_device *dev) +static int i2c_pxa_resume_noirq(struct device *dev) { - struct pxa_i2c *i2c = platform_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct pxa_i2c *i2c = platform_get_drvdata(pdev); clk_enable(i2c->clk); i2c_pxa_reset(i2c); return 0; } + +static struct dev_pm_ops i2c_pxa_dev_pm_ops = { + .suspend_noirq = i2c_pxa_suspend_noirq, + .resume_noirq = i2c_pxa_resume_noirq, +}; + +#define I2C_PXA_DEV_PM_OPS (&i2c_pxa_dev_pm_ops) #else -#define i2c_pxa_suspend_late NULL -#define i2c_pxa_resume_early NULL +#define I2C_PXA_DEV_PM_OPS NULL #endif static struct platform_driver i2c_pxa_driver = { .probe = i2c_pxa_probe, .remove = __exit_p(i2c_pxa_remove), - .suspend_late = i2c_pxa_suspend_late, - .resume_early = i2c_pxa_resume_early, .driver = { .name = "pxa2xx-i2c", .owner = THIS_MODULE, + .pm = I2C_PXA_DEV_PM_OPS, }, .id_table = i2c_pxa_id_table, }; -- cgit v1.2.3 From 6a6c6189b1fef97de9b8b936131fe2085ac42a5a Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 8 Jul 2009 13:22:47 +0200 Subject: I2C: Rework i2c-s3c2410 suspend_late()/resume() V2 This is V2 of the i2c-s3c2410 dev_pm_ops patch. The callbacks are converted for CONFIG_SUSPEND like this: suspend_late() -> suspend_noirq() resume() -> resume() Signed-off-by: Magnus Damm Acked-by: Greg Kroah-Hartman Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki --- drivers/i2c/busses/i2c-s3c2410.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 8f42a4536cdf..3be0d6a4d630 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -951,17 +951,20 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int s3c24xx_i2c_suspend_late(struct platform_device *dev, - pm_message_t msg) +static int s3c24xx_i2c_suspend_noirq(struct device *dev) { - struct s3c24xx_i2c *i2c = platform_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + i2c->suspended = 1; + return 0; } -static int s3c24xx_i2c_resume(struct platform_device *dev) +static int s3c24xx_i2c_resume(struct device *dev) { - struct s3c24xx_i2c *i2c = platform_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); i2c->suspended = 0; s3c24xx_i2c_init(i2c); @@ -969,9 +972,14 @@ static int s3c24xx_i2c_resume(struct platform_device *dev) return 0; } +static struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = { + .suspend_noirq = s3c24xx_i2c_suspend_noirq, + .resume = s3c24xx_i2c_resume, +}; + +#define S3C24XX_DEV_PM_OPS (&s3c24xx_i2c_dev_pm_ops) #else -#define s3c24xx_i2c_suspend_late NULL -#define s3c24xx_i2c_resume NULL +#define S3C24XX_DEV_PM_OPS NULL #endif /* device driver for platform bus bits */ @@ -990,12 +998,11 @@ MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); static struct platform_driver s3c24xx_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove = s3c24xx_i2c_remove, - .suspend_late = s3c24xx_i2c_suspend_late, - .resume = s3c24xx_i2c_resume, .id_table = s3c24xx_driver_ids, .driver = { .owner = THIS_MODULE, .name = "s3c-i2c", + .pm = S3C24XX_DEV_PM_OPS, }, }; -- cgit v1.2.3 From 48fea9659ec1a35641db3832adaf3860ecabb6ba Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 8 Jul 2009 13:22:56 +0200 Subject: USB: Rework musb suspend()/resume_early() This patch reworks platform driver power management code for musb from legacy callbacks to dev_pm_ops. The callbacks are converted for CONFIG_SUSPEND like this: suspend() -> suspend() resume_early() -> resume_noirq() Signed-off-by: Magnus Damm Acked-by: Greg Kroah-Hartman Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki --- drivers/usb/musb/musb_core.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 554a414f65d1..1191928902f4 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2168,8 +2168,9 @@ static int __devexit musb_remove(struct platform_device *pdev) #ifdef CONFIG_PM -static int musb_suspend(struct platform_device *pdev, pm_message_t message) +static int musb_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); unsigned long flags; struct musb *musb = dev_to_musb(&pdev->dev); @@ -2196,8 +2197,9 @@ static int musb_suspend(struct platform_device *pdev, pm_message_t message) return 0; } -static int musb_resume_early(struct platform_device *pdev) +static int musb_resume_noirq(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct musb *musb = dev_to_musb(&pdev->dev); if (!musb->clock) @@ -2215,9 +2217,14 @@ static int musb_resume_early(struct platform_device *pdev) return 0; } +static struct dev_pm_ops musb_dev_pm_ops = { + .suspend = musb_suspend, + .resume_noirq = musb_resume_noirq, +}; + +#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops) #else -#define musb_suspend NULL -#define musb_resume_early NULL +#define MUSB_DEV_PM_OPS NULL #endif static struct platform_driver musb_driver = { @@ -2225,11 +2232,10 @@ static struct platform_driver musb_driver = { .name = (char *)musb_driver_name, .bus = &platform_bus_type, .owner = THIS_MODULE, + .pm = MUSB_DEV_PM_OPS, }, .remove = __devexit_p(musb_remove), .shutdown = musb_shutdown, - .suspend = musb_suspend, - .resume_early = musb_resume_early, }; /*-------------------------------------------------------------------------*/ -- cgit v1.2.3 From 511647ff58fd0f1c1f415d2c757d841650edac91 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 8 Jul 2009 13:23:07 +0200 Subject: PM: Remove platform device suspend_late()/resume_early() V2 This is V2 of the platform driver power management late/early callback removal patch. The callbacks ->suspend_late() and ->resume_early() are removed since all in-tree users now have been migrated to dev_pm_ops. Signed-off-by: Magnus Damm Acked-by: Greg Kroah-Hartman Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki --- drivers/base/platform.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) (limited to 'drivers') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 81cb01bfc356..455e55971d0e 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -628,30 +628,6 @@ static int platform_legacy_suspend(struct device *dev, pm_message_t mesg) return ret; } -static int platform_legacy_suspend_late(struct device *dev, pm_message_t mesg) -{ - struct platform_driver *pdrv = to_platform_driver(dev->driver); - struct platform_device *pdev = to_platform_device(dev); - int ret = 0; - - if (dev->driver && pdrv->suspend_late) - ret = pdrv->suspend_late(pdev, mesg); - - return ret; -} - -static int platform_legacy_resume_early(struct device *dev) -{ - struct platform_driver *pdrv = to_platform_driver(dev->driver); - struct platform_device *pdev = to_platform_device(dev); - int ret = 0; - - if (dev->driver && pdrv->resume_early) - ret = pdrv->resume_early(pdev); - - return ret; -} - static int platform_legacy_resume(struct device *dev) { struct platform_driver *pdrv = to_platform_driver(dev->driver); @@ -714,8 +690,6 @@ static int platform_pm_suspend_noirq(struct device *dev) if (drv->pm) { if (drv->pm->suspend_noirq) ret = drv->pm->suspend_noirq(dev); - } else { - ret = platform_legacy_suspend_late(dev, PMSG_SUSPEND); } return ret; @@ -750,8 +724,6 @@ static int platform_pm_resume_noirq(struct device *dev) if (drv->pm) { if (drv->pm->resume_noirq) ret = drv->pm->resume_noirq(dev); - } else { - ret = platform_legacy_resume_early(dev); } return ret; @@ -797,8 +769,6 @@ static int platform_pm_freeze_noirq(struct device *dev) if (drv->pm) { if (drv->pm->freeze_noirq) ret = drv->pm->freeze_noirq(dev); - } else { - ret = platform_legacy_suspend_late(dev, PMSG_FREEZE); } return ret; @@ -833,8 +803,6 @@ static int platform_pm_thaw_noirq(struct device *dev) if (drv->pm) { if (drv->pm->thaw_noirq) ret = drv->pm->thaw_noirq(dev); - } else { - ret = platform_legacy_resume_early(dev); } return ret; @@ -869,8 +837,6 @@ static int platform_pm_poweroff_noirq(struct device *dev) if (drv->pm) { if (drv->pm->poweroff_noirq) ret = drv->pm->poweroff_noirq(dev); - } else { - ret = platform_legacy_suspend_late(dev, PMSG_HIBERNATE); } return ret; @@ -905,8 +871,6 @@ static int platform_pm_restore_noirq(struct device *dev) if (drv->pm) { if (drv->pm->restore_noirq) ret = drv->pm->restore_noirq(dev); - } else { - ret = platform_legacy_resume_early(dev); } return ret; -- cgit v1.2.3 From d9ab77161d811ffb0bccf396f7155cc905c1b9e1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 22 Jul 2009 00:37:25 +0200 Subject: Driver Core: Make PM operations a const pointer They are not supposed to be modified by drivers, so make them const. Signed-off-by: Dmitry Torokhov Acked-by: Greg Kroah-Hartman Signed-off-by: Rafael J. Wysocki --- drivers/base/platform.c | 2 +- drivers/base/power/main.c | 8 +++++--- drivers/pci/pci-driver.c | 16 ++++++++-------- 3 files changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 455e55971d0e..ae5c4aa6d269 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -889,7 +889,7 @@ static int platform_pm_restore_noirq(struct device *dev) #endif /* !CONFIG_HIBERNATION */ -static struct dev_pm_ops platform_dev_pm_ops = { +static const struct dev_pm_ops platform_dev_pm_ops = { .prepare = platform_pm_prepare, .complete = platform_pm_complete, .suspend = platform_pm_suspend, diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 58a3e572f2c9..1b1a786b7dec 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -157,8 +157,9 @@ void device_pm_move_last(struct device *dev) * @ops: PM operations to choose from. * @state: PM transition of the system being carried out. */ -static int pm_op(struct device *dev, struct dev_pm_ops *ops, - pm_message_t state) +static int pm_op(struct device *dev, + const struct dev_pm_ops *ops, + pm_message_t state) { int error = 0; @@ -220,7 +221,8 @@ static int pm_op(struct device *dev, struct dev_pm_ops *ops, * The operation is executed with interrupts disabled by the only remaining * functional CPU in the system. */ -static int pm_noirq_op(struct device *dev, struct dev_pm_ops *ops, +static int pm_noirq_op(struct device *dev, + const struct dev_pm_ops *ops, pm_message_t state) { int error = 0; diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index d76c4c85367e..0c2ea44ae5e1 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -575,7 +575,7 @@ static void pci_pm_complete(struct device *dev) static int pci_pm_suspend(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_SUSPEND); @@ -613,7 +613,7 @@ static int pci_pm_suspend(struct device *dev) static int pci_pm_suspend_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend_late(dev, PMSG_SUSPEND); @@ -672,7 +672,7 @@ static int pci_pm_resume_noirq(struct device *dev) static int pci_pm_resume(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; /* @@ -711,7 +711,7 @@ static int pci_pm_resume(struct device *dev) static int pci_pm_freeze(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_FREEZE); @@ -780,7 +780,7 @@ static int pci_pm_thaw_noirq(struct device *dev) static int pci_pm_thaw(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; if (pci_has_legacy_pm_support(pci_dev)) @@ -799,7 +799,7 @@ static int pci_pm_thaw(struct device *dev) static int pci_pm_poweroff(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_HIBERNATE); @@ -872,7 +872,7 @@ static int pci_pm_restore_noirq(struct device *dev) static int pci_pm_restore(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; /* @@ -910,7 +910,7 @@ static int pci_pm_restore(struct device *dev) #endif /* !CONFIG_HIBERNATION */ -struct dev_pm_ops pci_dev_pm_ops = { +const struct dev_pm_ops pci_dev_pm_ops = { .prepare = pci_pm_prepare, .complete = pci_pm_complete, .suspend = pci_pm_suspend, -- cgit v1.2.3 From 5e928f77a09a07f9dd595bb8a489965d69a83458 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 18 Aug 2009 23:38:32 +0200 Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 17) Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things. Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback. Signed-off-by: Rafael J. Wysocki Acked-by: Magnus Damm --- drivers/base/dd.c | 11 + drivers/base/power/Makefile | 1 + drivers/base/power/main.c | 22 +- drivers/base/power/power.h | 31 +- drivers/base/power/runtime.c | 1011 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1068 insertions(+), 8 deletions(-) create mode 100644 drivers/base/power/runtime.c (limited to 'drivers') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index f0106875f01d..7b34b3a48f67 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "base.h" #include "power/power.h" @@ -202,7 +203,10 @@ int driver_probe_device(struct device_driver *drv, struct device *dev) pr_debug("bus: '%s': %s: matched device %s with driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); + pm_runtime_get_noresume(dev); + pm_runtime_barrier(dev); ret = really_probe(dev, drv); + pm_runtime_put_sync(dev); return ret; } @@ -245,7 +249,9 @@ int device_attach(struct device *dev) ret = 0; } } else { + pm_runtime_get_noresume(dev); ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + pm_runtime_put_sync(dev); } up(&dev->sem); return ret; @@ -306,6 +312,9 @@ static void __device_release_driver(struct device *dev) drv = dev->driver; if (drv) { + pm_runtime_get_noresume(dev); + pm_runtime_barrier(dev); + driver_sysfs_remove(dev); if (dev->bus) @@ -324,6 +333,8 @@ static void __device_release_driver(struct device *dev) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_UNBOUND_DRIVER, dev); + + pm_runtime_put_sync(dev); } } diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 911208b89259..3ce3519e8f30 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_PM) += sysfs.o obj-$(CONFIG_PM_SLEEP) += main.o +obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 1b1a786b7dec..86990011277b 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,16 @@ static DEFINE_MUTEX(dpm_list_mtx); */ static bool transition_started; +/** + * device_pm_init - Initialize the PM-related part of a device object + * @dev: Device object being initialized. + */ +void device_pm_init(struct device *dev) +{ + dev->power.status = DPM_ON; + pm_runtime_init(dev); +} + /** * device_pm_lock - lock the list of active devices used by the PM core */ @@ -105,6 +116,7 @@ void device_pm_remove(struct device *dev) mutex_lock(&dpm_list_mtx); list_del_init(&dev->power.entry); mutex_unlock(&dpm_list_mtx); + pm_runtime_remove(dev); } /** @@ -512,6 +524,7 @@ static void dpm_complete(pm_message_t state) mutex_unlock(&dpm_list_mtx); device_complete(dev, state); + pm_runtime_put_noidle(dev); mutex_lock(&dpm_list_mtx); } @@ -757,7 +770,14 @@ static int dpm_prepare(pm_message_t state) dev->power.status = DPM_PREPARING; mutex_unlock(&dpm_list_mtx); - error = device_prepare(dev, state); + pm_runtime_get_noresume(dev); + if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) { + /* Wake-up requested during system sleep transition. */ + pm_runtime_put_noidle(dev); + error = -EBUSY; + } else { + error = device_prepare(dev, state); + } mutex_lock(&dpm_list_mtx); if (error) { diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index c7cb4fc3735c..b8fa1aa5225a 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -1,7 +1,14 @@ -static inline void device_pm_init(struct device *dev) -{ - dev->power.status = DPM_ON; -} +#ifdef CONFIG_PM_RUNTIME + +extern void pm_runtime_init(struct device *dev); +extern void pm_runtime_remove(struct device *dev); + +#else /* !CONFIG_PM_RUNTIME */ + +static inline void pm_runtime_init(struct device *dev) {} +static inline void pm_runtime_remove(struct device *dev) {} + +#endif /* !CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP @@ -16,23 +23,33 @@ static inline struct device *to_device(struct list_head *entry) return container_of(entry, struct device, power.entry); } +extern void device_pm_init(struct device *dev); extern void device_pm_add(struct device *); extern void device_pm_remove(struct device *); extern void device_pm_move_before(struct device *, struct device *); extern void device_pm_move_after(struct device *, struct device *); extern void device_pm_move_last(struct device *); -#else /* CONFIG_PM_SLEEP */ +#else /* !CONFIG_PM_SLEEP */ + +static inline void device_pm_init(struct device *dev) +{ + pm_runtime_init(dev); +} + +static inline void device_pm_remove(struct device *dev) +{ + pm_runtime_remove(dev); +} static inline void device_pm_add(struct device *dev) {} -static inline void device_pm_remove(struct device *dev) {} static inline void device_pm_move_before(struct device *deva, struct device *devb) {} static inline void device_pm_move_after(struct device *deva, struct device *devb) {} static inline void device_pm_move_last(struct device *dev) {} -#endif +#endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_PM diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c new file mode 100644 index 000000000000..38556f6cc22d --- /dev/null +++ b/drivers/base/power/runtime.c @@ -0,0 +1,1011 @@ +/* + * drivers/base/power/runtime.c - Helper functions for device run-time PM + * + * Copyright (c) 2009 Rafael J. Wysocki , Novell Inc. + * + * This file is released under the GPLv2. + */ + +#include +#include +#include + +static int __pm_runtime_resume(struct device *dev, bool from_wq); +static int __pm_request_idle(struct device *dev); +static int __pm_request_resume(struct device *dev); + +/** + * pm_runtime_deactivate_timer - Deactivate given device's suspend timer. + * @dev: Device to handle. + */ +static void pm_runtime_deactivate_timer(struct device *dev) +{ + if (dev->power.timer_expires > 0) { + del_timer(&dev->power.suspend_timer); + dev->power.timer_expires = 0; + } +} + +/** + * pm_runtime_cancel_pending - Deactivate suspend timer and cancel requests. + * @dev: Device to handle. + */ +static void pm_runtime_cancel_pending(struct device *dev) +{ + pm_runtime_deactivate_timer(dev); + /* + * In case there's a request pending, make sure its work function will + * return without doing anything. + */ + dev->power.request = RPM_REQ_NONE; +} + +/** + * __pm_runtime_idle - Notify device bus type if the device can be suspended. + * @dev: Device to notify the bus type about. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +static int __pm_runtime_idle(struct device *dev) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{ + int retval = 0; + + dev_dbg(dev, "__pm_runtime_idle()!\n"); + + if (dev->power.runtime_error) + retval = -EINVAL; + else if (dev->power.idle_notification) + retval = -EINPROGRESS; + else if (atomic_read(&dev->power.usage_count) > 0 + || dev->power.disable_depth > 0 + || dev->power.runtime_status != RPM_ACTIVE) + retval = -EAGAIN; + else if (!pm_children_suspended(dev)) + retval = -EBUSY; + if (retval) + goto out; + + if (dev->power.request_pending) { + /* + * If an idle notification request is pending, cancel it. Any + * other pending request takes precedence over us. + */ + if (dev->power.request == RPM_REQ_IDLE) { + dev->power.request = RPM_REQ_NONE; + } else if (dev->power.request != RPM_REQ_NONE) { + retval = -EAGAIN; + goto out; + } + } + + dev->power.idle_notification = true; + + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) { + spin_unlock_irq(&dev->power.lock); + + dev->bus->pm->runtime_idle(dev); + + spin_lock_irq(&dev->power.lock); + } + + dev->power.idle_notification = false; + wake_up_all(&dev->power.wait_queue); + + out: + dev_dbg(dev, "__pm_runtime_idle() returns %d!\n", retval); + + return retval; +} + +/** + * pm_runtime_idle - Notify device bus type if the device can be suspended. + * @dev: Device to notify the bus type about. + */ +int pm_runtime_idle(struct device *dev) +{ + int retval; + + spin_lock_irq(&dev->power.lock); + retval = __pm_runtime_idle(dev); + spin_unlock_irq(&dev->power.lock); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_runtime_idle); + +/** + * __pm_runtime_suspend - Carry out run-time suspend of given device. + * @dev: Device to suspend. + * @from_wq: If set, the function has been called via pm_wq. + * + * Check if the device can be suspended and run the ->runtime_suspend() callback + * provided by its bus type. If another suspend has been started earlier, wait + * for it to finish. If an idle notification or suspend request is pending or + * scheduled, cancel it. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +int __pm_runtime_suspend(struct device *dev, bool from_wq) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{ + struct device *parent = NULL; + bool notify = false; + int retval = 0; + + dev_dbg(dev, "__pm_runtime_suspend()%s!\n", + from_wq ? " from workqueue" : ""); + + repeat: + if (dev->power.runtime_error) { + retval = -EINVAL; + goto out; + } + + /* Pending resume requests take precedence over us. */ + if (dev->power.request_pending + && dev->power.request == RPM_REQ_RESUME) { + retval = -EAGAIN; + goto out; + } + + /* Other scheduled or pending requests need to be canceled. */ + pm_runtime_cancel_pending(dev); + + if (dev->power.runtime_status == RPM_SUSPENDED) + retval = 1; + else if (dev->power.runtime_status == RPM_RESUMING + || dev->power.disable_depth > 0 + || atomic_read(&dev->power.usage_count) > 0) + retval = -EAGAIN; + else if (!pm_children_suspended(dev)) + retval = -EBUSY; + if (retval) + goto out; + + if (dev->power.runtime_status == RPM_SUSPENDING) { + DEFINE_WAIT(wait); + + if (from_wq) { + retval = -EINPROGRESS; + goto out; + } + + /* Wait for the other suspend running in parallel with us. */ + for (;;) { + prepare_to_wait(&dev->power.wait_queue, &wait, + TASK_UNINTERRUPTIBLE); + if (dev->power.runtime_status != RPM_SUSPENDING) + break; + + spin_unlock_irq(&dev->power.lock); + + schedule(); + + spin_lock_irq(&dev->power.lock); + } + finish_wait(&dev->power.wait_queue, &wait); + goto repeat; + } + + dev->power.runtime_status = RPM_SUSPENDING; + + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { + spin_unlock_irq(&dev->power.lock); + + retval = dev->bus->pm->runtime_suspend(dev); + + spin_lock_irq(&dev->power.lock); + dev->power.runtime_error = retval; + } else { + retval = -ENOSYS; + } + + if (retval) { + dev->power.runtime_status = RPM_ACTIVE; + pm_runtime_cancel_pending(dev); + dev->power.deferred_resume = false; + + if (retval == -EAGAIN || retval == -EBUSY) { + notify = true; + dev->power.runtime_error = 0; + } + } else { + dev->power.runtime_status = RPM_SUSPENDED; + + if (dev->parent) { + parent = dev->parent; + atomic_add_unless(&parent->power.child_count, -1, 0); + } + } + wake_up_all(&dev->power.wait_queue); + + if (dev->power.deferred_resume) { + dev->power.deferred_resume = false; + __pm_runtime_resume(dev, false); + retval = -EAGAIN; + goto out; + } + + if (notify) + __pm_runtime_idle(dev); + + if (parent && !parent->power.ignore_children) { + spin_unlock_irq(&dev->power.lock); + + pm_request_idle(parent); + + spin_lock_irq(&dev->power.lock); + } + + out: + dev_dbg(dev, "__pm_runtime_suspend() returns %d!\n", retval); + + return retval; +} + +/** + * pm_runtime_suspend - Carry out run-time suspend of given device. + * @dev: Device to suspend. + */ +int pm_runtime_suspend(struct device *dev) +{ + int retval; + + spin_lock_irq(&dev->power.lock); + retval = __pm_runtime_suspend(dev, false); + spin_unlock_irq(&dev->power.lock); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_runtime_suspend); + +/** + * __pm_runtime_resume - Carry out run-time resume of given device. + * @dev: Device to resume. + * @from_wq: If set, the function has been called via pm_wq. + * + * Check if the device can be woken up and run the ->runtime_resume() callback + * provided by its bus type. If another resume has been started earlier, wait + * for it to finish. If there's a suspend running in parallel with this + * function, wait for it to finish and resume the device. Cancel any scheduled + * or pending requests. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +int __pm_runtime_resume(struct device *dev, bool from_wq) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{ + struct device *parent = NULL; + int retval = 0; + + dev_dbg(dev, "__pm_runtime_resume()%s!\n", + from_wq ? " from workqueue" : ""); + + repeat: + if (dev->power.runtime_error) { + retval = -EINVAL; + goto out; + } + + pm_runtime_cancel_pending(dev); + + if (dev->power.runtime_status == RPM_ACTIVE) + retval = 1; + else if (dev->power.disable_depth > 0) + retval = -EAGAIN; + if (retval) + goto out; + + if (dev->power.runtime_status == RPM_RESUMING + || dev->power.runtime_status == RPM_SUSPENDING) { + DEFINE_WAIT(wait); + + if (from_wq) { + if (dev->power.runtime_status == RPM_SUSPENDING) + dev->power.deferred_resume = true; + retval = -EINPROGRESS; + goto out; + } + + /* Wait for the operation carried out in parallel with us. */ + for (;;) { + prepare_to_wait(&dev->power.wait_queue, &wait, + TASK_UNINTERRUPTIBLE); + if (dev->power.runtime_status != RPM_RESUMING + && dev->power.runtime_status != RPM_SUSPENDING) + break; + + spin_unlock_irq(&dev->power.lock); + + schedule(); + + spin_lock_irq(&dev->power.lock); + } + finish_wait(&dev->power.wait_queue, &wait); + goto repeat; + } + + if (!parent && dev->parent) { + /* + * Increment the parent's resume counter and resume it if + * necessary. + */ + parent = dev->parent; + spin_unlock_irq(&dev->power.lock); + + pm_runtime_get_noresume(parent); + + spin_lock_irq(&parent->power.lock); + /* + * We can resume if the parent's run-time PM is disabled or it + * is set to ignore children. + */ + if (!parent->power.disable_depth + && !parent->power.ignore_children) { + __pm_runtime_resume(parent, false); + if (parent->power.runtime_status != RPM_ACTIVE) + retval = -EBUSY; + } + spin_unlock_irq(&parent->power.lock); + + spin_lock_irq(&dev->power.lock); + if (retval) + goto out; + goto repeat; + } + + dev->power.runtime_status = RPM_RESUMING; + + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { + spin_unlock_irq(&dev->power.lock); + + retval = dev->bus->pm->runtime_resume(dev); + + spin_lock_irq(&dev->power.lock); + dev->power.runtime_error = retval; + } else { + retval = -ENOSYS; + } + + if (retval) { + dev->power.runtime_status = RPM_SUSPENDED; + pm_runtime_cancel_pending(dev); + } else { + dev->power.runtime_status = RPM_ACTIVE; + if (parent) + atomic_inc(&parent->power.child_count); + } + wake_up_all(&dev->power.wait_queue); + + if (!retval) + __pm_request_idle(dev); + + out: + if (parent) { + spin_unlock_irq(&dev->power.lock); + + pm_runtime_put(parent); + + spin_lock_irq(&dev->power.lock); + } + + dev_dbg(dev, "__pm_runtime_resume() returns %d!\n", retval); + + return retval; +} + +/** + * pm_runtime_resume - Carry out run-time resume of given device. + * @dev: Device to suspend. + */ +int pm_runtime_resume(struct device *dev) +{ + int retval; + + spin_lock_irq(&dev->power.lock); + retval = __pm_runtime_resume(dev, false); + spin_unlock_irq(&dev->power.lock); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_runtime_resume); + +/** + * pm_runtime_work - Universal run-time PM work function. + * @work: Work structure used for scheduling the execution of this function. + * + * Use @work to get the device object the work is to be done for, determine what + * is to be done and execute the appropriate run-time PM function. + */ +static void pm_runtime_work(struct work_struct *work) +{ + struct device *dev = container_of(work, struct device, power.work); + enum rpm_request req; + + spin_lock_irq(&dev->power.lock); + + if (!dev->power.request_pending) + goto out; + + req = dev->power.request; + dev->power.request = RPM_REQ_NONE; + dev->power.request_pending = false; + + switch (req) { + case RPM_REQ_NONE: + break; + case RPM_REQ_IDLE: + __pm_runtime_idle(dev); + break; + case RPM_REQ_SUSPEND: + __pm_runtime_suspend(dev, true); + break; + case RPM_REQ_RESUME: + __pm_runtime_resume(dev, true); + break; + } + + out: + spin_unlock_irq(&dev->power.lock); +} + +/** + * __pm_request_idle - Submit an idle notification request for given device. + * @dev: Device to handle. + * + * Check if the device's run-time PM status is correct for suspending the device + * and queue up a request to run __pm_runtime_idle() for it. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +static int __pm_request_idle(struct device *dev) +{ + int retval = 0; + + if (dev->power.runtime_error) + retval = -EINVAL; + else if (atomic_read(&dev->power.usage_count) > 0 + || dev->power.disable_depth > 0 + || dev->power.runtime_status == RPM_SUSPENDED + || dev->power.runtime_status == RPM_SUSPENDING) + retval = -EAGAIN; + else if (!pm_children_suspended(dev)) + retval = -EBUSY; + if (retval) + return retval; + + if (dev->power.request_pending) { + /* Any requests other then RPM_REQ_IDLE take precedence. */ + if (dev->power.request == RPM_REQ_NONE) + dev->power.request = RPM_REQ_IDLE; + else if (dev->power.request != RPM_REQ_IDLE) + retval = -EAGAIN; + return retval; + } + + dev->power.request = RPM_REQ_IDLE; + dev->power.request_pending = true; + queue_work(pm_wq, &dev->power.work); + + return retval; +} + +/** + * pm_request_idle - Submit an idle notification request for given device. + * @dev: Device to handle. + */ +int pm_request_idle(struct device *dev) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&dev->power.lock, flags); + retval = __pm_request_idle(dev); + spin_unlock_irqrestore(&dev->power.lock, flags); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_request_idle); + +/** + * __pm_request_suspend - Submit a suspend request for given device. + * @dev: Device to suspend. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +static int __pm_request_suspend(struct device *dev) +{ + int retval = 0; + + if (dev->power.runtime_error) + return -EINVAL; + + if (dev->power.runtime_status == RPM_SUSPENDED) + retval = 1; + else if (atomic_read(&dev->power.usage_count) > 0 + || dev->power.disable_depth > 0) + retval = -EAGAIN; + else if (dev->power.runtime_status == RPM_SUSPENDING) + retval = -EINPROGRESS; + else if (!pm_children_suspended(dev)) + retval = -EBUSY; + if (retval < 0) + return retval; + + pm_runtime_deactivate_timer(dev); + + if (dev->power.request_pending) { + /* + * Pending resume requests take precedence over us, but we can + * overtake any other pending request. + */ + if (dev->power.request == RPM_REQ_RESUME) + retval = -EAGAIN; + else if (dev->power.request != RPM_REQ_SUSPEND) + dev->power.request = retval ? + RPM_REQ_NONE : RPM_REQ_SUSPEND; + return retval; + } else if (retval) { + return retval; + } + + dev->power.request = RPM_REQ_SUSPEND; + dev->power.request_pending = true; + queue_work(pm_wq, &dev->power.work); + + return 0; +} + +/** + * pm_suspend_timer_fn - Timer function for pm_schedule_suspend(). + * @data: Device pointer passed by pm_schedule_suspend(). + * + * Check if the time is right and execute __pm_request_suspend() in that case. + */ +static void pm_suspend_timer_fn(unsigned long data) +{ + struct device *dev = (struct device *)data; + unsigned long flags; + unsigned long expires; + + spin_lock_irqsave(&dev->power.lock, flags); + + expires = dev->power.timer_expires; + /* If 'expire' is after 'jiffies' we've been called too early. */ + if (expires > 0 && !time_after(expires, jiffies)) { + dev->power.timer_expires = 0; + __pm_request_suspend(dev); + } + + spin_unlock_irqrestore(&dev->power.lock, flags); +} + +/** + * pm_schedule_suspend - Set up a timer to submit a suspend request in future. + * @dev: Device to suspend. + * @delay: Time to wait before submitting a suspend request, in milliseconds. + */ +int pm_schedule_suspend(struct device *dev, unsigned int delay) +{ + unsigned long flags; + int retval = 0; + + spin_lock_irqsave(&dev->power.lock, flags); + + if (dev->power.runtime_error) { + retval = -EINVAL; + goto out; + } + + if (!delay) { + retval = __pm_request_suspend(dev); + goto out; + } + + pm_runtime_deactivate_timer(dev); + + if (dev->power.request_pending) { + /* + * Pending resume requests take precedence over us, but any + * other pending requests have to be canceled. + */ + if (dev->power.request == RPM_REQ_RESUME) { + retval = -EAGAIN; + goto out; + } + dev->power.request = RPM_REQ_NONE; + } + + if (dev->power.runtime_status == RPM_SUSPENDED) + retval = 1; + else if (dev->power.runtime_status == RPM_SUSPENDING) + retval = -EINPROGRESS; + else if (atomic_read(&dev->power.usage_count) > 0 + || dev->power.disable_depth > 0) + retval = -EAGAIN; + else if (!pm_children_suspended(dev)) + retval = -EBUSY; + if (retval) + goto out; + + dev->power.timer_expires = jiffies + msecs_to_jiffies(delay); + mod_timer(&dev->power.suspend_timer, dev->power.timer_expires); + + out: + spin_unlock_irqrestore(&dev->power.lock, flags); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_schedule_suspend); + +/** + * pm_request_resume - Submit a resume request for given device. + * @dev: Device to resume. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +static int __pm_request_resume(struct device *dev) +{ + int retval = 0; + + if (dev->power.runtime_error) + return -EINVAL; + + if (dev->power.runtime_status == RPM_ACTIVE) + retval = 1; + else if (dev->power.runtime_status == RPM_RESUMING) + retval = -EINPROGRESS; + else if (dev->power.disable_depth > 0) + retval = -EAGAIN; + if (retval < 0) + return retval; + + pm_runtime_deactivate_timer(dev); + + if (dev->power.request_pending) { + /* If non-resume request is pending, we can overtake it. */ + dev->power.request = retval ? RPM_REQ_NONE : RPM_REQ_RESUME; + return retval; + } else if (retval) { + return retval; + } + + dev->power.request = RPM_REQ_RESUME; + dev->power.request_pending = true; + queue_work(pm_wq, &dev->power.work); + + return retval; +} + +/** + * pm_request_resume - Submit a resume request for given device. + * @dev: Device to resume. + */ +int pm_request_resume(struct device *dev) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&dev->power.lock, flags); + retval = __pm_request_resume(dev); + spin_unlock_irqrestore(&dev->power.lock, flags); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_request_resume); + +/** + * __pm_runtime_get - Reference count a device and wake it up, if necessary. + * @dev: Device to handle. + * @sync: If set and the device is suspended, resume it synchronously. + * + * Increment the usage count of the device and if it was zero previously, + * resume it or submit a resume request for it, depending on the value of @sync. + */ +int __pm_runtime_get(struct device *dev, bool sync) +{ + int retval = 1; + + if (atomic_add_return(1, &dev->power.usage_count) == 1) + retval = sync ? pm_runtime_resume(dev) : pm_request_resume(dev); + + return retval; +} +EXPORT_SYMBOL_GPL(__pm_runtime_get); + +/** + * __pm_runtime_put - Decrement the device's usage counter and notify its bus. + * @dev: Device to handle. + * @sync: If the device's bus type is to be notified, do that synchronously. + * + * Decrement the usage count of the device and if it reaches zero, carry out a + * synchronous idle notification or submit an idle notification request for it, + * depending on the value of @sync. + */ +int __pm_runtime_put(struct device *dev, bool sync) +{ + int retval = 0; + + if (atomic_dec_and_test(&dev->power.usage_count)) + retval = sync ? pm_runtime_idle(dev) : pm_request_idle(dev); + + return retval; +} +EXPORT_SYMBOL_GPL(__pm_runtime_put); + +/** + * __pm_runtime_set_status - Set run-time PM status of a device. + * @dev: Device to handle. + * @status: New run-time PM status of the device. + * + * If run-time PM of the device is disabled or its power.runtime_error field is + * different from zero, the status may be changed either to RPM_ACTIVE, or to + * RPM_SUSPENDED, as long as that reflects the actual state of the device. + * However, if the device has a parent and the parent is not active, and the + * parent's power.ignore_children flag is unset, the device's status cannot be + * set to RPM_ACTIVE, so -EBUSY is returned in that case. + * + * If successful, __pm_runtime_set_status() clears the power.runtime_error field + * and the device parent's counter of unsuspended children is modified to + * reflect the new status. If the new status is RPM_SUSPENDED, an idle + * notification request for the parent is submitted. + */ +int __pm_runtime_set_status(struct device *dev, unsigned int status) +{ + struct device *parent = dev->parent; + unsigned long flags; + bool notify_parent = false; + int error = 0; + + if (status != RPM_ACTIVE && status != RPM_SUSPENDED) + return -EINVAL; + + spin_lock_irqsave(&dev->power.lock, flags); + + if (!dev->power.runtime_error && !dev->power.disable_depth) { + error = -EAGAIN; + goto out; + } + + if (dev->power.runtime_status == status) + goto out_set; + + if (status == RPM_SUSPENDED) { + /* It always is possible to set the status to 'suspended'. */ + if (parent) { + atomic_add_unless(&parent->power.child_count, -1, 0); + notify_parent = !parent->power.ignore_children; + } + goto out_set; + } + + if (parent) { + spin_lock_irq(&parent->power.lock); + + /* + * It is invalid to put an active child under a parent that is + * not active, has run-time PM enabled and the + * 'power.ignore_children' flag unset. + */ + if (!parent->power.disable_depth + && !parent->power.ignore_children + && parent->power.runtime_status != RPM_ACTIVE) { + error = -EBUSY; + } else { + if (dev->power.runtime_status == RPM_SUSPENDED) + atomic_inc(&parent->power.child_count); + } + + spin_unlock_irq(&parent->power.lock); + + if (error) + goto out; + } + + out_set: + dev->power.runtime_status = status; + dev->power.runtime_error = 0; + out: + spin_unlock_irqrestore(&dev->power.lock, flags); + + if (notify_parent) + pm_request_idle(parent); + + return error; +} +EXPORT_SYMBOL_GPL(__pm_runtime_set_status); + +/** + * __pm_runtime_barrier - Cancel pending requests and wait for completions. + * @dev: Device to handle. + * + * Flush all pending requests for the device from pm_wq and wait for all + * run-time PM operations involving the device in progress to complete. + * + * Should be called under dev->power.lock with interrupts disabled. + */ +static void __pm_runtime_barrier(struct device *dev) +{ + pm_runtime_deactivate_timer(dev); + + if (dev->power.request_pending) { + dev->power.request = RPM_REQ_NONE; + spin_unlock_irq(&dev->power.lock); + + cancel_work_sync(&dev->power.work); + + spin_lock_irq(&dev->power.lock); + dev->power.request_pending = false; + } + + if (dev->power.runtime_status == RPM_SUSPENDING + || dev->power.runtime_status == RPM_RESUMING + || dev->power.idle_notification) { + DEFINE_WAIT(wait); + + /* Suspend, wake-up or idle notification in progress. */ + for (;;) { + prepare_to_wait(&dev->power.wait_queue, &wait, + TASK_UNINTERRUPTIBLE); + if (dev->power.runtime_status != RPM_SUSPENDING + && dev->power.runtime_status != RPM_RESUMING + && !dev->power.idle_notification) + break; + spin_unlock_irq(&dev->power.lock); + + schedule(); + + spin_lock_irq(&dev->power.lock); + } + finish_wait(&dev->power.wait_queue, &wait); + } +} + +/** + * pm_runtime_barrier - Flush pending requests and wait for completions. + * @dev: Device to handle. + * + * Prevent the device from being suspended by incrementing its usage counter and + * if there's a pending resume request for the device, wake the device up. + * Next, make sure that all pending requests for the device have been flushed + * from pm_wq and wait for all run-time PM operations involving the device in + * progress to complete. + * + * Return value: + * 1, if there was a resume request pending and the device had to be woken up, + * 0, otherwise + */ +int pm_runtime_barrier(struct device *dev) +{ + int retval = 0; + + pm_runtime_get_noresume(dev); + spin_lock_irq(&dev->power.lock); + + if (dev->power.request_pending + && dev->power.request == RPM_REQ_RESUME) { + __pm_runtime_resume(dev, false); + retval = 1; + } + + __pm_runtime_barrier(dev); + + spin_unlock_irq(&dev->power.lock); + pm_runtime_put_noidle(dev); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_runtime_barrier); + +/** + * __pm_runtime_disable - Disable run-time PM of a device. + * @dev: Device to handle. + * @check_resume: If set, check if there's a resume request for the device. + * + * Increment power.disable_depth for the device and if was zero previously, + * cancel all pending run-time PM requests for the device and wait for all + * operations in progress to complete. The device can be either active or + * suspended after its run-time PM has been disabled. + * + * If @check_resume is set and there's a resume request pending when + * __pm_runtime_disable() is called and power.disable_depth is zero, the + * function will wake up the device before disabling its run-time PM. + */ +void __pm_runtime_disable(struct device *dev, bool check_resume) +{ + spin_lock_irq(&dev->power.lock); + + if (dev->power.disable_depth > 0) { + dev->power.disable_depth++; + goto out; + } + + /* + * Wake up the device if there's a resume request pending, because that + * means there probably is some I/O to process and disabling run-time PM + * shouldn't prevent the device from processing the I/O. + */ + if (check_resume && dev->power.request_pending + && dev->power.request == RPM_REQ_RESUME) { + /* + * Prevent suspends and idle notifications from being carried + * out after we have woken up the device. + */ + pm_runtime_get_noresume(dev); + + __pm_runtime_resume(dev, false); + + pm_runtime_put_noidle(dev); + } + + if (!dev->power.disable_depth++) + __pm_runtime_barrier(dev); + + out: + spin_unlock_irq(&dev->power.lock); +} +EXPORT_SYMBOL_GPL(__pm_runtime_disable); + +/** + * pm_runtime_enable - Enable run-time PM of a device. + * @dev: Device to handle. + */ +void pm_runtime_enable(struct device *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->power.lock, flags); + + if (dev->power.disable_depth > 0) + dev->power.disable_depth--; + else + dev_warn(dev, "Unbalanced %s!\n", __func__); + + spin_unlock_irqrestore(&dev->power.lock, flags); +} +EXPORT_SYMBOL_GPL(pm_runtime_enable); + +/** + * pm_runtime_init - Initialize run-time PM fields in given device object. + * @dev: Device object to initialize. + */ +void pm_runtime_init(struct device *dev) +{ + spin_lock_init(&dev->power.lock); + + dev->power.runtime_status = RPM_SUSPENDED; + dev->power.idle_notification = false; + + dev->power.disable_depth = 1; + atomic_set(&dev->power.usage_count, 0); + + dev->power.runtime_error = 0; + + atomic_set(&dev->power.child_count, 0); + pm_suspend_ignore_children(dev, false); + + dev->power.request_pending = false; + dev->power.request = RPM_REQ_NONE; + dev->power.deferred_resume = false; + INIT_WORK(&dev->power.work, pm_runtime_work); + + dev->power.timer_expires = 0; + setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, + (unsigned long)dev); + + init_waitqueue_head(&dev->power.wait_queue); +} + +/** + * pm_runtime_remove - Prepare for removing a device from device hierarchy. + * @dev: Device object being removed from device hierarchy. + */ +void pm_runtime_remove(struct device *dev) +{ + __pm_runtime_disable(dev, false); + + /* Change the status back to 'suspended' to match the initial status. */ + if (dev->power.runtime_status == RPM_ACTIVE) + pm_runtime_set_suspended(dev); +} -- cgit v1.2.3 From 9d7302299ee96ca954fe4ab8ca640333b6e19ad0 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 20 Aug 2009 20:25:32 +0200 Subject: PM: Run-time PM platform device bus support This patch adds default Runtime PM callbacks to the dev_pm_ops belonging to the platform bus. The callbacks are weak symbols that architecture specific code can override. Allows Runtime PM even though CONFIG_PM_SLEEP=n. Signed-off-by: Magnus Damm Acked-by: Greg Kroah-Hartman Signed-off-by: Rafael J. Wysocki --- drivers/base/platform.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index d28c289e4a3f..0f7d434ce983 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "base.h" @@ -656,6 +657,13 @@ static void platform_pm_complete(struct device *dev) drv->pm->complete(dev); } +#else /* !CONFIG_PM_SLEEP */ + +#define platform_pm_prepare NULL +#define platform_pm_complete NULL + +#endif /* !CONFIG_PM_SLEEP */ + #ifdef CONFIG_SUSPEND static int platform_pm_suspend(struct device *dev) @@ -886,6 +894,31 @@ static int platform_pm_restore_noirq(struct device *dev) #endif /* !CONFIG_HIBERNATION */ +#ifdef CONFIG_PM_RUNTIME + +int __weak platform_pm_runtime_suspend(struct device *dev) +{ + return -ENOSYS; +}; + +int __weak platform_pm_runtime_resume(struct device *dev) +{ + return -ENOSYS; +}; + +int __weak platform_pm_runtime_idle(struct device *dev) +{ + return -ENOSYS; +}; + +#else /* !CONFIG_PM_RUNTIME */ + +#define platform_pm_runtime_suspend NULL +#define platform_pm_runtime_resume NULL +#define platform_pm_runtime_idle NULL + +#endif /* !CONFIG_PM_RUNTIME */ + static const struct dev_pm_ops platform_dev_pm_ops = { .prepare = platform_pm_prepare, .complete = platform_pm_complete, @@ -901,22 +934,17 @@ static const struct dev_pm_ops platform_dev_pm_ops = { .thaw_noirq = platform_pm_thaw_noirq, .poweroff_noirq = platform_pm_poweroff_noirq, .restore_noirq = platform_pm_restore_noirq, + .runtime_suspend = platform_pm_runtime_suspend, + .runtime_resume = platform_pm_runtime_resume, + .runtime_idle = platform_pm_runtime_idle, }; -#define PLATFORM_PM_OPS_PTR (&platform_dev_pm_ops) - -#else /* !CONFIG_PM_SLEEP */ - -#define PLATFORM_PM_OPS_PTR NULL - -#endif /* !CONFIG_PM_SLEEP */ - struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, - .pm = PLATFORM_PM_OPS_PTR, + .pm = &platform_dev_pm_ops, }; EXPORT_SYMBOL_GPL(platform_bus_type); -- cgit v1.2.3 From c90cd332d3dbf9fc78934b8bda271c1f909216ea Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Sat, 25 Jul 2009 22:24:54 +0200 Subject: floppy: Switch driver to dev_pm_ops Gets rid of the following warning: Platform driver 'floppy' needs updating - please use dev_pm_ops [rjw: Fixed up the definition of floppy_pm_ops.] Signed-off-by: Frans Pop Signed-off-by: Andrew Morton Signed-off-by: Rafael J. Wysocki --- drivers/block/floppy.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 91b753013780..2b387c2260d8 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -4151,7 +4151,7 @@ static void floppy_device_release(struct device *dev) { } -static int floppy_resume(struct platform_device *dev) +static int floppy_resume(struct device *dev) { int fdc; @@ -4162,10 +4162,15 @@ static int floppy_resume(struct platform_device *dev) return 0; } -static struct platform_driver floppy_driver = { +static struct dev_pm_ops floppy_pm_ops = { .resume = floppy_resume, + .restore = floppy_resume, +}; + +static struct platform_driver floppy_driver = { .driver = { .name = "floppy", + .pm = &floppy_pm_ops, }, }; -- cgit v1.2.3 From 8dd2b42665f9d4ca4fded99f048631b60f669dc5 Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Thu, 20 Aug 2009 20:38:13 +0200 Subject: hp-wmi: Switch driver to dev_pm_ops Gets rid of the following warning: Platform driver 'hp-wmi' needs updating - please use dev_pm_ops I tested that the resume handler still works on my HP 2510p notebook. [rjw: Fixed up the definition of hp_wmi_pm_ops.] Signed-off-by: Frans Pop Signed-off-by: Andrew Morton Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/hp-wmi.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index a2ad53e15874..af04f5b049db 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -53,7 +53,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); static int __init hp_wmi_bios_setup(struct platform_device *device); static int __exit hp_wmi_bios_remove(struct platform_device *device); -static int hp_wmi_resume_handler(struct platform_device *device); +static int hp_wmi_resume_handler(struct device *device); struct bios_args { u32 signature; @@ -94,14 +94,19 @@ static struct rfkill *wifi_rfkill; static struct rfkill *bluetooth_rfkill; static struct rfkill *wwan_rfkill; +static struct dev_pm_ops hp_wmi_pm_ops = { + .resume = hp_wmi_resume_handler, + .restore = hp_wmi_resume_handler, +}; + static struct platform_driver hp_wmi_driver = { .driver = { - .name = "hp-wmi", - .owner = THIS_MODULE, + .name = "hp-wmi", + .owner = THIS_MODULE, + .pm = &hp_wmi_pm_ops, }, .probe = hp_wmi_bios_setup, .remove = hp_wmi_bios_remove, - .resume = hp_wmi_resume_handler, }; static int hp_wmi_perform_query(int query, int write, int value) @@ -512,7 +517,7 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device) return 0; } -static int hp_wmi_resume_handler(struct platform_device *device) +static int hp_wmi_resume_handler(struct device *device) { /* * Hardware state may have changed while suspended, so trigger -- cgit v1.2.3 From 20d652d7d1c2f202215ecf0306ccecc3569df9ba Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 20 Aug 2009 20:25:52 +0200 Subject: PM: Update kerneldoc comments in drivers/base/power/main.c The kerneldoc comments in drivers/base/power/main.c are generally outdated and some of them don't describe the functions very accurately. Update them and standardize the format to use spaces instead of tabs. Signed-off-by: Rafael J. Wysocki Acked-by: Randy Dunlap Acked-by: Greg Kroah-Hartman Acked-by: Alan Stern --- drivers/base/power/main.c | 169 +++++++++++++++++++++++----------------------- 1 file changed, 84 insertions(+), 85 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 86990011277b..b0899118569b 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -50,7 +50,7 @@ static DEFINE_MUTEX(dpm_list_mtx); static bool transition_started; /** - * device_pm_init - Initialize the PM-related part of a device object + * device_pm_init - Initialize the PM-related part of a device object. * @dev: Device object being initialized. */ void device_pm_init(struct device *dev) @@ -60,7 +60,7 @@ void device_pm_init(struct device *dev) } /** - * device_pm_lock - lock the list of active devices used by the PM core + * device_pm_lock - Lock the list of active devices used by the PM core. */ void device_pm_lock(void) { @@ -68,7 +68,7 @@ void device_pm_lock(void) } /** - * device_pm_unlock - unlock the list of active devices used by the PM core + * device_pm_unlock - Unlock the list of active devices used by the PM core. */ void device_pm_unlock(void) { @@ -76,8 +76,8 @@ void device_pm_unlock(void) } /** - * device_pm_add - add a device to the list of active devices - * @dev: Device to be added to the list + * device_pm_add - Add a device to the PM core's list of active devices. + * @dev: Device to add to the list. */ void device_pm_add(struct device *dev) { @@ -103,10 +103,8 @@ void device_pm_add(struct device *dev) } /** - * device_pm_remove - remove a device from the list of active devices - * @dev: Device to be removed from the list - * - * This function also removes the device's PM-related sysfs attributes. + * device_pm_remove - Remove a device from the PM core's list of active devices. + * @dev: Device to be removed from the list. */ void device_pm_remove(struct device *dev) { @@ -120,9 +118,9 @@ void device_pm_remove(struct device *dev) } /** - * device_pm_move_before - move device in dpm_list - * @deva: Device to move in dpm_list - * @devb: Device @deva should come before + * device_pm_move_before - Move device in the PM core's list of active devices. + * @deva: Device to move in dpm_list. + * @devb: Device @deva should come before. */ void device_pm_move_before(struct device *deva, struct device *devb) { @@ -136,9 +134,9 @@ void device_pm_move_before(struct device *deva, struct device *devb) } /** - * device_pm_move_after - move device in dpm_list - * @deva: Device to move in dpm_list - * @devb: Device @deva should come after + * device_pm_move_after - Move device in the PM core's list of active devices. + * @deva: Device to move in dpm_list. + * @devb: Device @deva should come after. */ void device_pm_move_after(struct device *deva, struct device *devb) { @@ -152,8 +150,8 @@ void device_pm_move_after(struct device *deva, struct device *devb) } /** - * device_pm_move_last - move device to end of dpm_list - * @dev: Device to move in dpm_list + * device_pm_move_last - Move device to end of the PM core's list of devices. + * @dev: Device to move in dpm_list. */ void device_pm_move_last(struct device *dev) { @@ -164,10 +162,10 @@ void device_pm_move_last(struct device *dev) } /** - * pm_op - execute the PM operation appropiate for given PM event - * @dev: Device. - * @ops: PM operations to choose from. - * @state: PM transition of the system being carried out. + * pm_op - Execute the PM operation appropriate for given PM event. + * @dev: Device to handle. + * @ops: PM operations to choose from. + * @state: PM transition of the system being carried out. */ static int pm_op(struct device *dev, const struct dev_pm_ops *ops, @@ -225,13 +223,13 @@ static int pm_op(struct device *dev, } /** - * pm_noirq_op - execute the PM operation appropiate for given PM event - * @dev: Device. - * @ops: PM operations to choose from. - * @state: PM transition of the system being carried out. + * pm_noirq_op - Execute the PM operation appropriate for given PM event. + * @dev: Device to handle. + * @ops: PM operations to choose from. + * @state: PM transition of the system being carried out. * - * The operation is executed with interrupts disabled by the only remaining - * functional CPU in the system. + * The driver of @dev will not receive interrupts while this function is being + * executed. */ static int pm_noirq_op(struct device *dev, const struct dev_pm_ops *ops, @@ -329,11 +327,12 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info, /*------------------------- Resume routines -------------------------*/ /** - * device_resume_noirq - Power on one device (early resume). - * @dev: Device. - * @state: PM transition of the system being carried out. + * device_resume_noirq - Execute an "early resume" callback for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. * - * Must be called with interrupts disabled. + * The driver of @dev will not receive interrupts while this function is being + * executed. */ static int device_resume_noirq(struct device *dev, pm_message_t state) { @@ -355,14 +354,11 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) } /** - * dpm_resume_noirq - Power on all regular (non-sysdev) devices. - * @state: PM transition of the system being carried out. - * - * Call the "noirq" resume handlers for all devices marked as - * DPM_OFF_IRQ and enable device drivers to receive interrupts. + * dpm_resume_noirq - Execute "early resume" callbacks for non-sysdev devices. + * @state: PM transition of the system being carried out. * - * Must be called under dpm_list_mtx. Device drivers should not receive - * interrupts while it's being executed. + * Call the "noirq" resume handlers for all devices marked as DPM_OFF_IRQ and + * enable device drivers to receive interrupts. */ void dpm_resume_noirq(pm_message_t state) { @@ -384,9 +380,9 @@ void dpm_resume_noirq(pm_message_t state) EXPORT_SYMBOL_GPL(dpm_resume_noirq); /** - * device_resume - Restore state for one device. - * @dev: Device. - * @state: PM transition of the system being carried out. + * device_resume - Execute "resume" callbacks for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. */ static int device_resume(struct device *dev, pm_message_t state) { @@ -435,11 +431,11 @@ static int device_resume(struct device *dev, pm_message_t state) } /** - * dpm_resume - Resume every device. - * @state: PM transition of the system being carried out. + * dpm_resume - Execute "resume" callbacks for non-sysdev devices. + * @state: PM transition of the system being carried out. * - * Execute the appropriate "resume" callback for all devices the status of - * which indicates that they are inactive. + * Execute the appropriate "resume" callback for all devices whose status + * indicates that they are suspended. */ static void dpm_resume(pm_message_t state) { @@ -476,9 +472,9 @@ static void dpm_resume(pm_message_t state) } /** - * device_complete - Complete a PM transition for given device - * @dev: Device. - * @state: PM transition of the system being carried out. + * device_complete - Complete a PM transition for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. */ static void device_complete(struct device *dev, pm_message_t state) { @@ -503,11 +499,11 @@ static void device_complete(struct device *dev, pm_message_t state) } /** - * dpm_complete - Complete a PM transition for all devices. - * @state: PM transition of the system being carried out. + * dpm_complete - Complete a PM transition for all non-sysdev devices. + * @state: PM transition of the system being carried out. * - * Execute the ->complete() callbacks for all devices that are not marked - * as DPM_ON. + * Execute the ->complete() callbacks for all devices whose PM status is not + * DPM_ON (this allows new devices to be registered). */ static void dpm_complete(pm_message_t state) { @@ -537,11 +533,11 @@ static void dpm_complete(pm_message_t state) } /** - * dpm_resume_end - Restore state of each device in system. - * @state: PM transition of the system being carried out. + * dpm_resume_end - Execute "resume" callbacks and complete system transition. + * @state: PM transition of the system being carried out. * - * Resume all the devices, unlock them all, and allow new - * devices to be registered once again. + * Execute "resume" callbacks for all devices and complete the PM transition of + * the system. */ void dpm_resume_end(pm_message_t state) { @@ -555,9 +551,11 @@ EXPORT_SYMBOL_GPL(dpm_resume_end); /*------------------------- Suspend routines -------------------------*/ /** - * resume_event - return a PM message representing the resume event - * corresponding to given sleep state. - * @sleep_state: PM message representing a sleep state. + * resume_event - Return a "resume" message for given "suspend" sleep state. + * @sleep_state: PM message representing a sleep state. + * + * Return a PM message representing the resume event corresponding to given + * sleep state. */ static pm_message_t resume_event(pm_message_t sleep_state) { @@ -574,11 +572,12 @@ static pm_message_t resume_event(pm_message_t sleep_state) } /** - * device_suspend_noirq - Shut down one device (late suspend). - * @dev: Device. - * @state: PM transition of the system being carried out. + * device_suspend_noirq - Execute a "late suspend" callback for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. * - * This is called with interrupts off and only a single CPU running. + * The driver of @dev will not receive interrupts while this function is being + * executed. */ static int device_suspend_noirq(struct device *dev, pm_message_t state) { @@ -595,13 +594,11 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state) } /** - * dpm_suspend_noirq - Power down all regular (non-sysdev) devices. - * @state: PM transition of the system being carried out. + * dpm_suspend_noirq - Execute "late suspend" callbacks for non-sysdev devices. + * @state: PM transition of the system being carried out. * - * Prevent device drivers from receiving interrupts and call the "noirq" - * suspend handlers. - * - * Must be called under dpm_list_mtx. + * Prevent device drivers from receiving interrupts and call the "noirq" suspend + * handlers for all non-sysdev devices. */ int dpm_suspend_noirq(pm_message_t state) { @@ -626,9 +623,9 @@ int dpm_suspend_noirq(pm_message_t state) EXPORT_SYMBOL_GPL(dpm_suspend_noirq); /** - * device_suspend - Save state of one device. - * @dev: Device. - * @state: PM transition of the system being carried out. + * device_suspend - Execute "suspend" callbacks for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. */ static int device_suspend(struct device *dev, pm_message_t state) { @@ -675,10 +672,8 @@ static int device_suspend(struct device *dev, pm_message_t state) } /** - * dpm_suspend - Suspend every device. - * @state: PM transition of the system being carried out. - * - * Execute the appropriate "suspend" callbacks for all devices. + * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. + * @state: PM transition of the system being carried out. */ static int dpm_suspend(pm_message_t state) { @@ -712,9 +707,12 @@ static int dpm_suspend(pm_message_t state) } /** - * device_prepare - Execute the ->prepare() callback(s) for given device. - * @dev: Device. - * @state: PM transition of the system being carried out. + * device_prepare - Prepare a device for system power transition. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. + * + * Execute the ->prepare() callback(s) for given device. No new children of the + * device may be registered after this function has returned. */ static int device_prepare(struct device *dev, pm_message_t state) { @@ -750,10 +748,10 @@ static int device_prepare(struct device *dev, pm_message_t state) } /** - * dpm_prepare - Prepare all devices for a PM transition. - * @state: PM transition of the system being carried out. + * dpm_prepare - Prepare all non-sysdev devices for a system PM transition. + * @state: PM transition of the system being carried out. * - * Execute the ->prepare() callback for all devices. + * Execute the ->prepare() callback(s) for all devices. */ static int dpm_prepare(pm_message_t state) { @@ -804,10 +802,11 @@ static int dpm_prepare(pm_message_t state) } /** - * dpm_suspend_start - Save state and stop all devices in system. - * @state: PM transition of the system being carried out. + * dpm_suspend_start - Prepare devices for PM transition and suspend them. + * @state: PM transition of the system being carried out. * - * Prepare and suspend all devices. + * Prepare all non-sysdev devices for system PM transition and execute "suspend" + * callbacks for them. */ int dpm_suspend_start(pm_message_t state) { -- cgit v1.2.3 From 3eb132c986f04f64b9c360abd67a1e0d18d6d5b4 Mon Sep 17 00:00:00 2001 From: Xiaotian Feng Date: Thu, 20 Aug 2009 20:28:33 +0200 Subject: PM: Reset transition_started at dpm_resume_noirq transition_started should be set once the preparation of devices for a PM has started, reset before starting to resume devices. When resuming devices, kernel calls dpm_resume_noirq then dpm_resume_end(dpm_resume). Thus we should reset transition_started at dpm_resume_noirq. This patch fixes ACPI warning when resuming from suspend/hibernate: ACPI: \_SB_.PCI0.IDE1.PRI1.MAS1 - docking ------------[ cut here ]------------ WARNING: at drivers/base/power/main.c:87 device_pm_add+0x8b/0xcc() Hardware name: OptiPlex 760 Device: acpi Parentless device registered during a PM transaction [rjw: Fixed up the changelog.] Signed-off-by: Xiaotian Feng Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index b0899118569b..e0dc4071e088 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -365,6 +365,7 @@ void dpm_resume_noirq(pm_message_t state) struct device *dev; mutex_lock(&dpm_list_mtx); + transition_started = false; list_for_each_entry(dev, &dpm_list, power.entry) if (dev->power.status > DPM_OFF) { int error; @@ -443,7 +444,6 @@ static void dpm_resume(pm_message_t state) INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); - transition_started = false; while (!list_empty(&dpm_list)) { struct device *dev = to_device(dpm_list.next); -- cgit v1.2.3 From 33f82d141c897f39cd8bce592d88cb3c5af58342 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 10 Sep 2009 00:06:44 +0200 Subject: at_hdmac: Rework suspend_late()/resume_early() This patch reworks platform driver power management code for at_hdmac from legacy late/early callbacks to dev_pm_ops. The callbacks are converted for CONFIG_SUSPEND like this: suspend_late() -> suspend_noirq() resume_early() -> resume_noirq() Signed-off-by: Dan Williams Signed-off-by: Rafael J. Wysocki --- drivers/dma/at_hdmac.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 9a1e5fb412ed..c8522e6f1ad2 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1166,32 +1166,37 @@ static void at_dma_shutdown(struct platform_device *pdev) clk_disable(atdma->clk); } -static int at_dma_suspend_late(struct platform_device *pdev, pm_message_t mesg) +static int at_dma_suspend_noirq(struct device *dev) { - struct at_dma *atdma = platform_get_drvdata(pdev); + struct platform_device *pdev = to_platform_device(dev); + struct at_dma *atdma = platform_get_drvdata(pdev); at_dma_off(platform_get_drvdata(pdev)); clk_disable(atdma->clk); return 0; } -static int at_dma_resume_early(struct platform_device *pdev) +static int at_dma_resume_noirq(struct device *dev) { - struct at_dma *atdma = platform_get_drvdata(pdev); + struct platform_device *pdev = to_platform_device(dev); + struct at_dma *atdma = platform_get_drvdata(pdev); clk_enable(atdma->clk); dma_writel(atdma, EN, AT_DMA_ENABLE); return 0; - } +static struct dev_pm_ops at_dma_dev_pm_ops = { + .suspend_noirq = at_dma_suspend_noirq, + .resume_noirq = at_dma_resume_noirq, +}; + static struct platform_driver at_dma_driver = { .remove = __exit_p(at_dma_remove), .shutdown = at_dma_shutdown, - .suspend_late = at_dma_suspend_late, - .resume_early = at_dma_resume_early, .driver = { .name = "at_hdmac", + .pm = &at_dma_dev_pm_ops, }, }; -- cgit v1.2.3