diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-sh_mobile.c')
-rw-r--r-- | drivers/i2c/busses/i2c-sh_mobile.c | 43 |
1 files changed, 39 insertions, 4 deletions
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 1c01083b01b..86a9d4e8147 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -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_mobile_i2c_data *pd) u_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_mobile_i2c_data *pd) /* 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, @@ -563,7 +566,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) goto err_irq; } - size = (res->end - res->start) + 1; + size = resource_size(res); pd->reg = ioremap(res->start, size); if (pd->reg == NULL) { @@ -572,6 +575,19 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) 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 platform_device *dev) iounmap(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, @@ -637,7 +672,7 @@ static void __exit sh_mobile_i2c_adap_exit(void) platform_driver_unregister(&sh_mobile_i2c_driver); } -module_init(sh_mobile_i2c_adap_init); +subsys_initcall(sh_mobile_i2c_adap_init); module_exit(sh_mobile_i2c_adap_exit); MODULE_DESCRIPTION("SuperH Mobile I2C Bus Controller driver"); |