Message ID | 20210507131406.2224177-2-hsinyi@chromium.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | add power control in i2c | expand |
On 07/05/2021 15:14, Hsin-Yi Wang wrote: > From: Bibby Hsieh <bibby.hsieh@mediatek.com> > > Although in the most platforms, the bus power of i2c > are alway on, some platforms disable the i2c bus power > in order to meet low power request. > > We can control bulk regulator if it is provided in i2c > adapter device. > > Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com> > Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> > Signed-off-by: Hsin-Yi Wang <hsinyi@chromium.org> Reviewed-by: Matthias Brugger <matthias.bgg@gmail.com> > --- > drivers/i2c/i2c-core-base.c | 95 +++++++++++++++++++++++++++++++++++++ > include/linux/i2c.h | 2 + > 2 files changed, 97 insertions(+) > > diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c > index 5a97e4a02fa2..23bc7c269184 100644 > --- a/drivers/i2c/i2c-core-base.c > +++ b/drivers/i2c/i2c-core-base.c > @@ -461,12 +461,14 @@ static int i2c_smbus_host_notify_to_irq(const struct i2c_client *client) > static int i2c_device_probe(struct device *dev) > { > struct i2c_client *client = i2c_verify_client(dev); > + struct i2c_adapter *adap; > struct i2c_driver *driver; > int status; > > if (!client) > return 0; > > + adap = client->adapter; > client->irq = client->init_irq; > > if (!client->irq) { > @@ -532,6 +534,14 @@ static int i2c_device_probe(struct device *dev) > > dev_dbg(dev, "probe\n"); > > + if (adap->bus_regulator) { > + status = regulator_enable(adap->bus_regulator); > + if (status < 0) { > + dev_err(&adap->dev, "Failed to enable bus regulator\n"); > + goto err_clear_wakeup_irq; > + } > + } > + > status = of_clk_set_defaults(dev->of_node, false); > if (status < 0) > goto err_clear_wakeup_irq; > @@ -589,8 +599,10 @@ static int i2c_device_probe(struct device *dev) > static int i2c_device_remove(struct device *dev) > { > struct i2c_client *client = to_i2c_client(dev); > + struct i2c_adapter *adap; > struct i2c_driver *driver; > > + adap = client->adapter; > driver = to_i2c_driver(dev->driver); > if (driver->remove) { > int status; > @@ -605,6 +617,8 @@ static int i2c_device_remove(struct device *dev) > devres_release_group(&client->dev, client->devres_group_id); > > dev_pm_domain_detach(&client->dev, true); > + if (!pm_runtime_status_suspended(&client->dev) && adap->bus_regulator) > + regulator_disable(adap->bus_regulator); > > dev_pm_clear_wake_irq(&client->dev); > device_init_wakeup(&client->dev, false); > @@ -617,6 +631,86 @@ static int i2c_device_remove(struct device *dev) > return 0; > } > > +#ifdef CONFIG_PM_SLEEP > +static int i2c_resume_early(struct device *dev) > +{ > + struct i2c_client *client = i2c_verify_client(dev); > + int err; > + > + if (!client) > + return 0; > + > + if (pm_runtime_status_suspended(&client->dev) && > + client->adapter->bus_regulator) { > + err = regulator_enable(client->adapter->bus_regulator); > + if (err) > + return err; > + } > + > + return pm_generic_resume_early(&client->dev); > +} > + > +static int i2c_suspend_late(struct device *dev) > +{ > + struct i2c_client *client = i2c_verify_client(dev); > + int err; > + > + if (!client) > + return 0; > + > + err = pm_generic_suspend_late(&client->dev); > + if (err) > + return err; > + > + if (!pm_runtime_status_suspended(&client->dev) && > + client->adapter->bus_regulator) > + return regulator_disable(client->adapter->bus_regulator); > + > + return 0; > +} > +#endif > + > +#ifdef CONFIG_PM > +static int i2c_runtime_resume(struct device *dev) > +{ > + struct i2c_client *client = i2c_verify_client(dev); > + int err; > + > + if (!client) > + return 0; > + > + if (client->adapter->bus_regulator) { > + err = regulator_enable(client->adapter->bus_regulator); > + if (err) > + return err; > + } > + > + return pm_generic_runtime_resume(&client->dev); > +} > + > +static int i2c_runtime_suspend(struct device *dev) > +{ > + struct i2c_client *client = i2c_verify_client(dev); > + int err; > + > + if (!client) > + return 0; > + > + err = pm_generic_runtime_suspend(&client->dev); > + if (err) > + return err; > + > + if (client->adapter->bus_regulator) > + return regulator_disable(client->adapter->bus_regulator); > + return 0; > +} > +#endif > + > +static const struct dev_pm_ops i2c_device_pm = { > + SET_LATE_SYSTEM_SLEEP_PM_OPS(i2c_suspend_late, i2c_resume_early) > + SET_RUNTIME_PM_OPS(i2c_runtime_suspend, i2c_runtime_resume, NULL) > +}; > + > static void i2c_device_shutdown(struct device *dev) > { > struct i2c_client *client = i2c_verify_client(dev); > @@ -674,6 +768,7 @@ struct bus_type i2c_bus_type = { > .probe = i2c_device_probe, > .remove = i2c_device_remove, > .shutdown = i2c_device_shutdown, > + .pm = &i2c_device_pm, > }; > EXPORT_SYMBOL_GPL(i2c_bus_type); > > diff --git a/include/linux/i2c.h b/include/linux/i2c.h > index e8f2ac8c9c3d..953a4eecb88f 100644 > --- a/include/linux/i2c.h > +++ b/include/linux/i2c.h > @@ -15,6 +15,7 @@ > #include <linux/device.h> /* for struct device */ > #include <linux/sched.h> /* for completion */ > #include <linux/mutex.h> > +#include <linux/regulator/consumer.h> > #include <linux/rtmutex.h> > #include <linux/irqdomain.h> /* for Host Notify IRQ */ > #include <linux/of.h> /* for struct device_node */ > @@ -729,6 +730,7 @@ struct i2c_adapter { > const struct i2c_adapter_quirks *quirks; > > struct irq_domain *host_notify_domain; > + struct regulator *bus_regulator; > }; > #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) > >
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 5a97e4a02fa2..23bc7c269184 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -461,12 +461,14 @@ static int i2c_smbus_host_notify_to_irq(const struct i2c_client *client) static int i2c_device_probe(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); + struct i2c_adapter *adap; struct i2c_driver *driver; int status; if (!client) return 0; + adap = client->adapter; client->irq = client->init_irq; if (!client->irq) { @@ -532,6 +534,14 @@ static int i2c_device_probe(struct device *dev) dev_dbg(dev, "probe\n"); + if (adap->bus_regulator) { + status = regulator_enable(adap->bus_regulator); + if (status < 0) { + dev_err(&adap->dev, "Failed to enable bus regulator\n"); + goto err_clear_wakeup_irq; + } + } + status = of_clk_set_defaults(dev->of_node, false); if (status < 0) goto err_clear_wakeup_irq; @@ -589,8 +599,10 @@ static int i2c_device_probe(struct device *dev) static int i2c_device_remove(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct i2c_adapter *adap; struct i2c_driver *driver; + adap = client->adapter; driver = to_i2c_driver(dev->driver); if (driver->remove) { int status; @@ -605,6 +617,8 @@ static int i2c_device_remove(struct device *dev) devres_release_group(&client->dev, client->devres_group_id); dev_pm_domain_detach(&client->dev, true); + if (!pm_runtime_status_suspended(&client->dev) && adap->bus_regulator) + regulator_disable(adap->bus_regulator); dev_pm_clear_wake_irq(&client->dev); device_init_wakeup(&client->dev, false); @@ -617,6 +631,86 @@ static int i2c_device_remove(struct device *dev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int i2c_resume_early(struct device *dev) +{ + struct i2c_client *client = i2c_verify_client(dev); + int err; + + if (!client) + return 0; + + if (pm_runtime_status_suspended(&client->dev) && + client->adapter->bus_regulator) { + err = regulator_enable(client->adapter->bus_regulator); + if (err) + return err; + } + + return pm_generic_resume_early(&client->dev); +} + +static int i2c_suspend_late(struct device *dev) +{ + struct i2c_client *client = i2c_verify_client(dev); + int err; + + if (!client) + return 0; + + err = pm_generic_suspend_late(&client->dev); + if (err) + return err; + + if (!pm_runtime_status_suspended(&client->dev) && + client->adapter->bus_regulator) + return regulator_disable(client->adapter->bus_regulator); + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int i2c_runtime_resume(struct device *dev) +{ + struct i2c_client *client = i2c_verify_client(dev); + int err; + + if (!client) + return 0; + + if (client->adapter->bus_regulator) { + err = regulator_enable(client->adapter->bus_regulator); + if (err) + return err; + } + + return pm_generic_runtime_resume(&client->dev); +} + +static int i2c_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = i2c_verify_client(dev); + int err; + + if (!client) + return 0; + + err = pm_generic_runtime_suspend(&client->dev); + if (err) + return err; + + if (client->adapter->bus_regulator) + return regulator_disable(client->adapter->bus_regulator); + return 0; +} +#endif + +static const struct dev_pm_ops i2c_device_pm = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(i2c_suspend_late, i2c_resume_early) + SET_RUNTIME_PM_OPS(i2c_runtime_suspend, i2c_runtime_resume, NULL) +}; + static void i2c_device_shutdown(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); @@ -674,6 +768,7 @@ struct bus_type i2c_bus_type = { .probe = i2c_device_probe, .remove = i2c_device_remove, .shutdown = i2c_device_shutdown, + .pm = &i2c_device_pm, }; EXPORT_SYMBOL_GPL(i2c_bus_type); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index e8f2ac8c9c3d..953a4eecb88f 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -15,6 +15,7 @@ #include <linux/device.h> /* for struct device */ #include <linux/sched.h> /* for completion */ #include <linux/mutex.h> +#include <linux/regulator/consumer.h> #include <linux/rtmutex.h> #include <linux/irqdomain.h> /* for Host Notify IRQ */ #include <linux/of.h> /* for struct device_node */ @@ -729,6 +730,7 @@ struct i2c_adapter { const struct i2c_adapter_quirks *quirks; struct irq_domain *host_notify_domain; + struct regulator *bus_regulator; }; #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)