PM: Runtime PM v16 for SuperH Mobile V3
Subsystems (6)
cover PM: Runtime PM v16 for SuperH Mobile V3
PM: Runtime PM v16 for SuperH Mobile V3
[PATCH 01/05] sh: Runtime PM for SuperH Mobile platform bus devices [PATCH 02/05] i2c: Runtime PM for SuperH Mobile I2C [PATCH 03/05] video: Runtime PM for SuperH Mobile LCDC [PATCH 04/05] v4l2: Runtime PM for SuperH Mobile CEU [PATCH 05/05] uio: Runtime PM for UIO devices
This is the SuperH Mobile platform bus Runtime PM implementation V3. Included is a SuperH Mobile specific Runtime PM implementation and some driver changes. The code has been tested with "Software Standby" mode on sh7722 and a multimedia playback workload exercising drivers for fbdev, uio, v4l2 and i2c.
Apply on top of Rafael's linux-next patches and
"PM: Introduce core framework for run-time PM of I/O devices (rev. 16)" "PM: Runtime PM v15 - Platform Device Bus Support"
---
arch/sh/include/asm/device.h | 9 arch/sh/include/asm/hwblk.h | 4 arch/sh/kernel/cpu/shmobile/Makefile | 1 arch/sh/kernel/cpu/shmobile/pm_runtime.c | 304 ++++++++++++++++++++++++++++ drivers/i2c/busses/i2c-sh_mobile.c | 39 +++ drivers/media/video/sh_mobile_ceu_camera.c | 41 ++- drivers/uio/uio_pdrv_genirq.c | 54 ++++ drivers/video/sh_mobile_lcdcfb.c | 154 ++++++++++---- 8 files changed, 542 insertions(+), 64 deletions(-)
PATCH 1/5 sh: Runtime PM for SuperH Mobile platform bus devices
From: Magnus Damm <[email protected]>
This patch is V3 of the SuperH Mobile Runtime PM platform bus implentation matching Rafael's Runtime PM v16.
The code gets invoked from the SuperH specific Runtime PM platform bus functions that override the weak symbols for:
- platform_pm_runtime_suspend()
- platform_pm_runtime_resume()
- platform_pm_runtime_idle()
This Runtime PM implementation performs two levels of power management. At the time of platform bus runtime suspend the clock to the device is stopped instantly. Later on if all devices within the power domain has their clocks stopped then the device driver ->runtime_suspend() callbacks are used to save hardware register state for each device.
Device driver ->runtime_suspend() calls are scheduled from cpuidle context using platform_pm_runtime_suspend_idle(). When all devices have been fully suspended the processor is allowed to enter deep sleep from cpuidle.
The runtime resume operation turns on clocks and also restores registers if needed. It is worth noting that the devices start in a suspended state and the device driver is responsible for calling runtime resume before accessing the actual hardware.
In this particular platform bus implementation runtime resume is not allowed from interrupt context. Runtime suspend is however allowed from interrupt context as long as the synchronous functions are avoided.
---
Changes since V2
- update to v16
- rewrite work queue code
- use mutex
Changes since V1:
- update from Runtime PM v9 to v11
- rework error handling
- implement platform_pm_runtime_idle()
- add might_sleep()
- use dev_pm_ops helpers
- use dev_dbg()
- use core_initcall()
arch/sh/include/asm/device.h | 9 arch/sh/include/asm/hwblk.h | 4 arch/sh/kernel/cpu/shmobile/Makefile | 1 arch/sh/kernel/cpu/shmobile/pm_runtime.c | 304 ++++++++++++++++++++++++++++++ 4 files changed, 317 insertions(+), 1 deletion(-)
arch/sh/include/asm/device.h +9 -0
@@ -14,6 +14,15 @@ int platform_resource_setup_memory(strucvoid plat_early_device_setup (void ); +#define PDEV_ARCHDATA_FLAG_INIT 0 +#define PDEV_ARCHDATA_FLAG_IDLE 1 +#define PDEV_ARCHDATA_FLAG_SUSP 2 +struct pdev_archdata {int hwblk_id; +#ifdef CONFIG_PM_RUNTIME +unsigned long flags; +struct list_head entry; +struct mutex mutex; +#endif };
arch/sh/include/asm/hwblk.h +3 -1
@@ -5,7 +5,9 @@#include <asm/io.h> #define HWBLK_CNT_USAGE 0 -#define HWBLK_CNT_NR 1 +#define HWBLK_CNT_IDLE 1 +#define HWBLK_CNT_DEVICES 2 +#define HWBLK_CNT_NR 3#define HWBLK_AREA_FLAG_PARENT (1 << 0)/* valid parent */
arch/sh/kernel/cpu/shmobile/Makefile +1 -0
@@ -5,3 +5,4 @@ # Power Management & Sleep mode obj-$(CONFIG_PM) += pm.o sleep.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o +obj-$(CONFIG_PM_RUNTIME) += pm_runtime.o
arch/sh/kernel/cpu/shmobile/pm_runtime.c new file +304 -0
304 lines not on this page · raw message
PATCH 2/5 i2c: Runtime PM for SuperH Mobile I2C
In reply to Magnus DammFrom: Magnus Damm <[email protected]>
This patch modifies the SuperH Mobile I2C driver to support Runtime PM. These changes is all that is needed for proper Runtime PM support in this driver. Driver callbacks for Runtime PM are empty because the device registers are always re-initialized after pm_runtime_get_sync().
---
Changes from V2:
- none
Changes from V1:
- use pm_runtime_get_sync() and pm_runtime_put_sync()
- use pm_runtime_disable()
- implement nop runtime pm functions for dev_pm_ops
- no need for pm_runtime_set_suspended()
drivers/i2c/busses/i2c-sh_mobile.c | 39 ++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-)
drivers/i2c/busses/i2c-sh_mobile.c +37 -2
@@ -28,6 +28,7 @@#include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/err.h> +#include <linux/pm_runtime.h> #include <linux/clk.h> #include <linux/io.h>
@@ -165,7 +166,8 @@ static void activate_ch(struct sh_mobileu_int32_t denom;u_int32_t tmp; -/* Make sure the clock is enabled */ +/* Wake up device and enable clock */ +pm_runtime_get_sync (pd->dev);clk_enable (pd->clk);/* Get clock rate after clock is enabled */
@@ -213,8 +215,9 @@ static void deactivate_ch(struct sh_mobi/* Disable channel */ iowrite8 (ioread8 (ICCR (pd)) & ~ICCR_ICE ,ICCR (pd)); -/* Disable clock */ +/* Disable clock and mark device as idle */ clk_disable (pd->clk); +pm_runtime_put_sync (pd->dev); } static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
@@ -572,6 +575,19 @@ static int sh_mobile_i2c_probe(struct pl goto err_irq; } +/* Enable Runtime PM for this device. +* +* Also tell the Runtime PM core to ignore children +* for this device since it is valid for us to suspend +* this I2C master driver even though the slave devices +* on the I2C bus may not be suspended. +* +* The state of the I2C hardware bus is unaffected by +* the Runtime PM state. +*/ +pm_suspend_ignore_children (&dev->dev, true); +pm_runtime_enable (&dev->dev); +/* setup the private data */ adap = &pd->adap;i2c_set_adapdata (adap, pd);
@@ -614,14 +630,33 @@ static int sh_mobile_i2c_remove(struct piounmap (pd->reg);sh_mobile_i2c_hook_irqs (dev,0 );clk_put (pd->clk); +pm_runtime_disable (&dev->dev);kfree (pd);return 0 ; } +static int sh_mobile_i2c_runtime_nop (struct device *dev) +{ +/* Runtime PM callback shared between ->runtime_suspend() +* and ->runtime_resume(). Simply returns success. +* +* This driver re-initializes all registers after +* pm_runtime_get_sync() anyway so there is no need +* to save and restore registers here. +*/ +return 0 ; +} + +static struct dev_pm_ops sh_mobile_i2c_dev_pm_ops = { + .runtime_suspend = sh_mobile_i2c_runtime_nop, + .runtime_resume = sh_mobile_i2c_runtime_nop, +}; +static struct platform_driver sh_mobile_i2c_driver = { .driver = { .name ="i2c-sh_mobile" , .owner =THIS_MODULE , + .pm = &sh_mobile_i2c_dev_pm_ops, }, .probe = sh_mobile_i2c_probe, .remove = sh_mobile_i2c_remove,
PATCH 3/5 video: Runtime PM for SuperH Mobile LCDC
In reply to Magnus DammFrom: Magnus Damm <[email protected]>
This patch modifies the SuperH Mobile LCDC framebuffer driver to support Runtime PM. The driver is using the functions
- pm_runtime_get_sync()
- pm_runtime_put_sync()
to inform the bus code if the hardware is idle or not. If the hardware is idle then the bus code may call the runtime dev_pm_ops callbacks to save and restore state. pm_runtime_resume() is used to allow the driver to access the hardware from probe().
---
Changes since V2:
- implemented proper device register save and restore
- use pm_runtime_resume() to support probe() hardware usage
Changes since V1:
- use pm_runtime_get_sync() and pm_runtime_put_sync()
- use pm_runtime_disable()
- no need for pm_runtime_set_suspended()
- incomplete runtime pm functions for dev_pm_ops
- work around with pm_runtime_put_noidle()/pm_runtime_get_noresume()
drivers/video/sh_mobile_lcdcfb.c | 154 ++++++++++++++++++++++++++------------ 1 file changed, 109 insertions(+), 45 deletions(-)
drivers/video/sh_mobile_lcdcfb.c +109 -45
@@ -14,6 +14,7 @@#include <linux/mm.h> #include <linux/fb.h> #include <linux/clk.h> +#include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h>
@@ -23,33 +24,6 @@#define PALETTE_NR 16 -struct sh_mobile_lcdc_priv ; -struct sh_mobile_lcdc_chan { -struct sh_mobile_lcdc_priv *lcdc; -unsigned long *reg_offs; -unsigned long ldmt1r_value; -unsigned long enabled;/* ME and SE in LDCNT2R */ -struct sh_mobile_lcdc_chan_cfg cfg; -u32 pseudo_palette[PALETTE_NR ]; -struct fb_info *info; -dma_addr_t dma_handle; -struct fb_deferred_io defio; -struct scatterlist *sglist; -unsigned long frame_end; -wait_queue_head_t frame_end_wait; -}; - -struct sh_mobile_lcdc_priv { -void __iomem *base; -int irq; -atomic_t clk_usecnt; -struct clk *dot_clk; -struct clk *clk; -unsigned long lddckr; -struct sh_mobile_lcdc_chan ch[2 ]; -int started; -}; -/* shared registers */ #define _LDDCKR 0x410#define _LDDCKSTPR 0x414
@@ -63,11 +37,23 @@ struct sh_mobile_lcdc_priv {#define _LDDWAR 0x900#define _LDDRAR 0x904 +/* shared registers and their order for context save/restore */ +static int lcdc_shared_regs[] = { + _LDDCKR, + _LDDCKSTPR, + _LDINTR, + _LDDDSR, + _LDCNT1R, + _LDCNT2R, +}; +#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) +/* per-channel registers */ enum {LDDCKPAT1R ,LDDCKPAT2R ,LDMT1R ,LDMT2R ,LDMT3R ,LDDFR ,LDSM1R , -LDSM2R ,LDSA1R ,LDMLSR ,LDHCNR ,LDHSYNR ,LDVLNR ,LDVSYNR ,LDPMR }; +LDSM2R ,LDSA1R ,LDMLSR ,LDHCNR ,LDHSYNR ,LDVLNR ,LDVSYNR ,LDPMR , +NR_CH_REGS }; -static unsigned long lcdc_offs_mainlcd[] = { +static unsigned long lcdc_offs_mainlcd[NR_CH_REGS ] = { [LDDCKPAT1R ] =0x400 , [LDDCKPAT2R ] =0x404 , [LDMT1R ] =0x418 ,
@@ -85,7 +71,7 @@ static unsigned long lcdc_offs_mainlcd[] [LDPMR] = 0x460, }; -static unsigned long lcdc_offs_sublcd[] = { +static unsigned long lcdc_offs_sublcd[NR_CH_REGS ] = { [LDDCKPAT1R ] =0x408 , [LDDCKPAT2R ] =0x40c , [LDMT1R ] =0x600 ,
@@ -110,6 +96,35 @@ static unsigned long lcdc_offs_sublcd[]#define LDINTR_FE 0x00000400#define LDINTR_FS 0x00000004 +struct sh_mobile_lcdc_priv ; +struct sh_mobile_lcdc_chan { +struct sh_mobile_lcdc_priv *lcdc; +unsigned long *reg_offs; +unsigned long ldmt1r_value; +unsigned long enabled;/* ME and SE in LDCNT2R */ +struct sh_mobile_lcdc_chan_cfg cfg; +u32 pseudo_palette[PALETTE_NR ]; +unsigned long saved_ch_regs[NR_CH_REGS ]; +struct fb_info *info; +dma_addr_t dma_handle; +struct fb_deferred_io defio; +struct scatterlist *sglist; +unsigned long frame_end; +wait_queue_head_t frame_end_wait; +}; + +struct sh_mobile_lcdc_priv { +void __iomem *base; +int irq; +atomic_t hw_usecnt; +struct device *dev; +struct clk *dot_clk; +unsigned long lddckr; +struct sh_mobile_lcdc_chan ch[2 ]; +unsigned long saved_shared_regs[NR_SHARED_REGS ]; +int started; +}; +static void lcdc_write_chan (struct sh_mobile_lcdc_chan *chan,int reg_nr,unsigned long data) {
@@ -188,8 +203,8 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobstatic void sh_mobile_lcdc_clk_on (struct sh_mobile_lcdc_priv *priv) { -if (atomic_inc_and_test (&priv->clk_usecnt)) { -clk_enable (priv->clk); +if (atomic_inc_and_test (&priv->hw_usecnt)) { +pm_runtime_get_sync (priv->dev);if (priv->dot_clk)clk_enable (priv->dot_clk); }
@@ -197,10 +212,10 @@ static void sh_mobile_lcdc_clk_on(structstatic void sh_mobile_lcdc_clk_off (struct sh_mobile_lcdc_priv *priv) { -if (atomic_sub_return (1 , &priv->clk_usecnt) ==-1 ) { +if (atomic_sub_return (1 , &priv->hw_usecnt) ==-1 ) {if (priv->dot_clk)clk_disable (priv->dot_clk); -clk_disable (priv->clk); +pm_runtime_put (priv->dev); } }
@@ -569,7 +584,6 @@ static int sh_mobile_lcdc_setup_clocks(sint clock_source, struct sh_mobile_lcdc_priv *priv) { - char clk_name[8]; char *str;int icksel;
@@ -583,23 +597,21 @@ static int sh_mobile_lcdc_setup_clocks(s priv->lddckr = icksel <<16 ; -atomic_set (&priv->clk_usecnt,-1 ); -snprintf (clk_name,sizeof (clk_name),"lcdc%d" , pdev->id); - priv->clk =clk_get (&pdev->dev, clk_name); -if (IS_ERR (priv->clk)) { -dev_err (&pdev->dev,"cannot get clock \"%s\"\n" , clk_name); -return PTR_ERR (priv->clk); - } -if (str) { priv->dot_clk =clk_get (&pdev->dev, str);if (IS_ERR (priv->dot_clk)) {dev_err (&pdev->dev,"cannot get dot clock %s\n" , str); -clk_put (priv->clk);return PTR_ERR (priv->dot_clk); } } +atomic_set (&priv->hw_usecnt,-1 ); +/* Runtime PM support involves two step for this driver: +* 1) Enable Runtime PM +* 2) Force Runtime PM Resume since hardware is accessed from probe() +*/ +pm_runtime_enable (priv->dev); +pm_runtime_resume (priv->dev);return 0 ; }
@@ -717,9 +729,59 @@ static int sh_mobile_lcdc_resume(structreturn sh_mobile_lcdc_start (platform_get_drvdata (pdev)); } +static int sh_mobile_lcdc_runtime_suspend (struct device *dev) +{ +struct platform_device *pdev =to_platform_device (dev); +struct sh_mobile_lcdc_priv *p =platform_get_drvdata (pdev); +struct sh_mobile_lcdc_chan *ch; +int k, n; + +/* save per-channel registers */ +for (k =0 ; k <ARRAY_SIZE (p->ch); k++) { + ch = &p->ch[k]; +if (!ch->enabled) +continue ; +for (n =0 ; n <NR_CH_REGS ; n++) + ch->saved_ch_regs[n] =lcdc_read_chan (ch, n); + } + +/* save shared registers */ +for (n =0 ; n <NR_SHARED_REGS ; n++) + p->saved_shared_regs[n] =lcdc_read (p, lcdc_shared_regs[n]); + +/* turn off LCDC hardware */ +lcdc_write (p, _LDCNT1R,0 ); +return 0 ; +} + +static int sh_mobile_lcdc_runtime_resume (struct device *dev) +{ +struct platform_device *pdev =to_platform_device (dev); +struct sh_mobile_lcdc_priv *p =platform_get_drvdata (pdev); +struct sh_mobile_lcdc_chan *ch; +int k, n; + +/* restore per-channel registers */ +for (k =0 ; k <ARRAY_SIZE (p->ch); k++) { + ch = &p->ch[k]; +if (!ch->enabled) +continue ; +for (n =0 ; n <NR_CH_REGS ; n++) +lcdc_write_chan (ch, n, ch->saved_ch_regs[n]); + } + +/* restore shared registers */ +for (n =0 ; n <NR_SHARED_REGS ; n++) +lcdc_write (p, lcdc_shared_regs[n], p->saved_shared_regs[n]); + +return 0 ; +} +static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { .suspend = sh_mobile_lcdc_suspend, .resume = sh_mobile_lcdc_resume, + .runtime_suspend = sh_mobile_lcdc_runtime_suspend, + .runtime_resume = sh_mobile_lcdc_runtime_resume, };static int sh_mobile_lcdc_remove (struct platform_device *pdev);
@@ -764,6 +826,7 @@ static int __init sh_mobile_lcdc_probe(s } priv->irq = i; + priv->dev = &pdev->dev;platform_set_drvdata (pdev, priv); pdata = pdev->dev.platform_data;
@@ -935,7 +998,8 @@ static int sh_mobile_lcdc_remove(structif (priv->dot_clk)clk_put (priv->dot_clk); -clk_put (priv->clk); + +pm_runtime_disable (priv->dev);if (priv->base)iounmap (priv->base);
PATCH 4/5 v4l2: Runtime PM for SuperH Mobile CEU
In reply to Magnus DammFrom: Magnus Damm <[email protected]>
This patch modifies the SuperH Mobile CEU driver to support Runtime PM. Driver callbacks for Runtime PM are empty because the device registers are always re-initialized after pm_runtime_get_sync(). The Runtime PM functions replaces the clock framework module stop bit handling in this driver.
---
drivers/media/video/sh_mobile_ceu_camera.c | 41 +++++++++++++++++----------- 1 file changed, 25 insertions(+), 16 deletions(-)
drivers/media/video/sh_mobile_ceu_camera.c +25 -16
@@ -30,7 +30,7 @@#include <linux/device.h> #include <linux/platform_device.h> #include <linux/videodev2.h> -#include <linux/clk.h> +#include <linux/pm_runtime.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h>
@@ -86,7 +86,6 @@ struct sh_mobile_ceu_dev {unsigned int irq;void __iomem *base; -struct clk *clk;unsigned long video_limit;/* lock used to protect videobuf */
@@ -364,7 +363,7 @@ static int sh_mobile_ceu_add_device(struif (ret) goto err; -clk_enable (pcdev->clk); +pm_runtime_get_sync (ici->dev);ceu_write (pcdev,CAPSR ,1 <<16 );/* reset */ while (ceu_read (pcdev,CSTSR ) &1 )
@@ -398,7 +397,7 @@ static void sh_mobile_ceu_remove_device( }spin_unlock_irqrestore (&pcdev->lock, flags); -clk_disable (pcdev->clk); +pm_runtime_put_sync (ici->dev); icd->ops->release (icd);
@@ -801,7 +800,6 @@ static int sh_mobile_ceu_probe(struct plstruct sh_mobile_ceu_dev *pcdev;struct resource *res;void __iomem *base; -char clk_name[8 ];unsigned int irq;int err =0 ;
@@ -865,13 +863,9 @@ static int sh_mobile_ceu_probe(struct pl goto exit_release_mem; } -snprintf (clk_name,sizeof (clk_name),"ceu%d" , pdev->id); - pcdev->clk =clk_get (&pdev->dev, clk_name); -if (IS_ERR (pcdev->clk)) { -dev_err (&pdev->dev,"cannot get clock \"%s\"\n" , clk_name); - err =PTR_ERR (pcdev->clk); - goto exit_free_irq; - } +pm_suspend_ignore_children (&pdev->dev, true); +pm_runtime_enable (&pdev->dev); +pm_runtime_resume (&pdev->dev); pcdev->ici.priv = pcdev; pcdev->ici.dev = &pdev->dev;
@@ -881,12 +875,10 @@ static int sh_mobile_ceu_probe(struct pl err =soc_camera_host_register (&pcdev->ici);if (err) - goto exit_free_clk; + goto exit_free_irq;return 0 ; -exit_free_clk: -clk_put (pcdev->clk); exit_free_irq:free_irq (pcdev->irq, pcdev); exit_release_mem:
@@ -907,7 +899,6 @@ static int sh_mobile_ceu_remove(struct p struct sh_mobile_ceu_dev, ici);soc_camera_host_unregister (soc_host); -clk_put (pcdev->clk);free_irq (pcdev->irq, pcdev);if (platform_get_resource (pdev,IORESOURCE_MEM ,1 ))dma_release_declared_memory (&pdev->dev);
@@ -916,9 +907,27 @@ static int sh_mobile_ceu_remove(struct preturn 0 ; } +static int sh_mobile_ceu_runtime_nop (struct device *dev) +{ +/* Runtime PM callback shared between ->runtime_suspend() +* and ->runtime_resume(). Simply returns success. +* +* This driver re-initializes all registers after +* pm_runtime_get_sync() anyway so there is no need +* to save and restore registers here. +*/ +return 0 ; +} + +static struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = { + .runtime_suspend = sh_mobile_ceu_runtime_nop, + .runtime_resume = sh_mobile_ceu_runtime_nop, +}; +static struct platform_driver sh_mobile_ceu_driver = { .driver = { .name ="sh_mobile_ceu" , + .pm = &sh_mobile_ceu_dev_pm_ops, }, .probe = sh_mobile_ceu_probe, .remove = sh_mobile_ceu_remove,
PATCH 5/5 uio: Runtime PM for UIO devices
In reply to Magnus DammFrom: Magnus Damm <[email protected]>
This patch modifies the uio_pdrv_genirq driver to support Runtime PM. The power management implementation simply runtime resumes the device at open() time and runtime suspends it at release() time. The user space driver is responsible for re-initializing the hardware after open().
---
Tested with the sh7722 VEU hardware block using the mplayer sh_veu vidix user space driver.
Changes since V2:
- updated comments
drivers/uio/uio_pdrv_genirq.c | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+)
drivers/uio/uio_pdrv_genirq.c +54 -0
@@ -20,6 +20,7 @@#include <linux/bitops.h> #include <linux/interrupt.h> #include <linux/stringify.h> +#include <linux/pm_runtime.h> #define DRIVER_NAME "uio_pdrv_genirq"
@@ -27,8 +28,27 @@ struct uio_pdrv_genirq_platdata {struct uio_info *uioinfo;spinlock_t lock;unsigned long flags; +struct platform_device *pdev; }; +static int uio_pdrv_genirq_open (struct uio_info *info,struct inode *inode) +{ +struct uio_pdrv_genirq_platdata *priv = info->priv; + +/* Wait until the Runtime PM code has woken up the device */ +pm_runtime_get_sync (&priv->pdev->dev); +return 0 ; +} + +static int uio_pdrv_genirq_release (struct uio_info *info,struct inode *inode) +{ +struct uio_pdrv_genirq_platdata *priv = info->priv; + +/* Tell the Runtime PM code that the device has become idle */ +pm_runtime_put_sync (&priv->pdev->dev); +return 0 ; +} +static irqreturn_t uio_pdrv_genirq_handler (int irq,struct uio_info *dev_info) {struct uio_pdrv_genirq_platdata *priv = dev_info->priv;
@@ -97,6 +117,7 @@ static int uio_pdrv_genirq_probe(struct priv->uioinfo = uioinfo;spin_lock_init (&priv->lock); priv->flags =0 ;/* interrupt is enabled to begin with */ + priv->pdev = pdev; uiomem = &uioinfo->mem[0 ];
@@ -136,8 +157,17 @@ static int uio_pdrv_genirq_probe(struct uioinfo->irq_flags |=IRQF_DISABLED ; uioinfo->handler = uio_pdrv_genirq_handler; uioinfo->irqcontrol = uio_pdrv_genirq_irqcontrol; + uioinfo->open = uio_pdrv_genirq_open; + uioinfo->release = uio_pdrv_genirq_release; uioinfo->priv = priv; +/* Enable Runtime PM for this device: +* The device starts in suspended state to allow the hardware to be +* turned off by default. The Runtime PM bus code should power on the +* hardware and enable clocks at open(). +*/ +pm_runtime_enable (&pdev->dev); + ret =uio_register_device (&pdev->dev, priv->uioinfo);if (ret) {dev_err (&pdev->dev,"unable to register uio device\n" );
@@ -157,16 +187,40 @@ static int uio_pdrv_genirq_remove(structstruct uio_pdrv_genirq_platdata *priv =platform_get_drvdata (pdev);uio_unregister_device (priv->uioinfo); +pm_runtime_disable (&pdev->dev);kfree (priv);return 0 ; } +static int uio_pdrv_genirq_runtime_nop (struct device *dev) +{ +/* Runtime PM callback shared between ->runtime_suspend() +* and ->runtime_resume(). Simply returns success. +* +* In this driver pm_runtime_get_sync() and pm_runtime_put_sync() +* are used at open() and release() time. This allows the +* Runtime PM code to turn off power to the device while the +* device is unused, ie before open() and after release(). +* +* This Runtime PM callback does not need to save or restore +* any registers since user space is responsbile for hardware +* register reinitialization after open(). +*/ +return 0 ; +} + +static struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = { + .runtime_suspend = uio_pdrv_genirq_runtime_nop, + .runtime_resume = uio_pdrv_genirq_runtime_nop, +}; +static struct platform_driver uio_pdrv_genirq = { .probe = uio_pdrv_genirq_probe, .remove = uio_pdrv_genirq_remove, .driver = { .name =DRIVER_NAME , .owner =THIS_MODULE , + .pm = &uio_pdrv_genirq_dev_pm_ops, }, };