diff mbox series

[8/9] i2c: qcom-cci: add support of optional vbus-supply regulators

Message ID 20220203164711.1712090-1-vladimir.zapolskiy@linaro.org
State New
Headers show
Series i2c: qcom-cci: fixes and updates | expand

Commit Message

Vladimir Zapolskiy Feb. 3, 2022, 4:47 p.m. UTC
The change adds handling of optional vbus regulators in the driver.

Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
---
 drivers/i2c/busses/i2c-qcom-cci.c | 49 +++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

Comments

Robert Foss Feb. 4, 2022, 11:03 a.m. UTC | #1
On Thu, 3 Feb 2022 at 17:47, Vladimir Zapolskiy
<vladimir.zapolskiy@linaro.org> wrote:
>
> The change adds handling of optional vbus regulators in the driver.
>
> Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
> ---
>  drivers/i2c/busses/i2c-qcom-cci.c | 49 +++++++++++++++++++++++++++++++
>  1 file changed, 49 insertions(+)
>
> diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
> index 775945f7b4cd..2fc7f1f2616f 100644
> --- a/drivers/i2c/busses/i2c-qcom-cci.c
> +++ b/drivers/i2c/busses/i2c-qcom-cci.c
> @@ -11,6 +11,7 @@
>  #include <linux/of.h>
>  #include <linux/platform_device.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/regulator/consumer.h>
>
>  #define CCI_HW_VERSION                         0x0
>  #define CCI_RESET_CMD                          0x004
> @@ -480,6 +481,20 @@ static void cci_disable_clocks(struct cci *cci)
>  static int __maybe_unused cci_suspend_runtime(struct device *dev)
>  {
>         struct cci *cci = dev_get_drvdata(dev);
> +       struct regulator *bus_regulator;
> +       unsigned int i;
> +
> +       for (i = 0; i < cci->data->num_masters; i++) {
> +               if (!cci->master[i].cci)
> +                       continue;
> +
> +               bus_regulator = cci->master[i].adap.bus_regulator;
> +               if (!bus_regulator)
> +                       continue;
> +
> +               if (regulator_is_enabled(bus_regulator) > 0)

Is this check needed? No matter the current status of the regulator,
we'd like to disable it, and from my reading regulator_disable can be
called for already disabled regulators.

> +                       regulator_disable(bus_regulator);
> +       }
>
>         cci_disable_clocks(cci);
>         return 0;
> @@ -488,12 +503,30 @@ static int __maybe_unused cci_suspend_runtime(struct device *dev)
>  static int __maybe_unused cci_resume_runtime(struct device *dev)
>  {
>         struct cci *cci = dev_get_drvdata(dev);
> +       struct regulator *bus_regulator;
> +       unsigned int i;
>         int ret;
>
>         ret = cci_enable_clocks(cci);
>         if (ret)
>                 return ret;
>
> +       for (i = 0; i < cci->data->num_masters; i++) {
> +               if (!cci->master[i].cci)
> +                       continue;
> +
> +               bus_regulator = cci->master[i].adap.bus_regulator;
> +               if (!bus_regulator)
> +                       continue;
> +
> +               if (!regulator_is_enabled(bus_regulator)) {
> +                       ret = regulator_enable(bus_regulator);
> +                       if (ret)
> +                               dev_err(dev, "failed to enable regulator: %d\n",
> +                                       ret);
> +               }
> +       }
> +
>         cci_init(cci);
>         return 0;
>  }
> @@ -593,6 +626,7 @@ static int cci_probe(struct platform_device *pdev)
>         dev_dbg(dev, "CCI HW version = 0x%08x", val);
>
>         for_each_available_child_of_node(dev->of_node, child) {
> +               struct regulator *bus_regulator;
>                 struct cci_master *master;
>                 u32 idx;
>
> @@ -637,6 +671,21 @@ static int cci_probe(struct platform_device *pdev)
>                         master->cci = NULL;
>                         goto error_i2c;
>                 }
> +
> +               /*
> +                * It might be possible to find an optional vbus supply, but
> +                * it requires to pass the registration of an I2C adapter
> +                * device and its association with a bus device tree node.
> +                */
> +               bus_regulator = devm_regulator_get_optional(&master->adap.dev,
> +                                                           "vbus");
> +               if (IS_ERR(bus_regulator)) {
> +                       ret = PTR_ERR(bus_regulator);
> +                       if (ret == -EPROBE_DEFER)
> +                               goto error_i2c;
> +                       bus_regulator = NULL;
> +               }
> +               master->adap.bus_regulator = bus_regulator;
>         }
>
>         ret = cci_reset(cci);
> --
> 2.33.0
>

With the above nit sorted, feel free to add my r-b.

Reviewed-by: Robert Foss <robert.foss@linaro.org>
Loic Poulain Feb. 4, 2022, 11:41 a.m. UTC | #2
On Fri, 4 Feb 2022 at 12:03, Robert Foss <robert.foss@linaro.org> wrote:
>
> On Thu, 3 Feb 2022 at 17:47, Vladimir Zapolskiy
> <vladimir.zapolskiy@linaro.org> wrote:
> >
> > The change adds handling of optional vbus regulators in the driver.
> >
> > Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
> > ---
> >  drivers/i2c/busses/i2c-qcom-cci.c | 49 +++++++++++++++++++++++++++++++
> >  1 file changed, 49 insertions(+)
> >
> > diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
> > index 775945f7b4cd..2fc7f1f2616f 100644
> > --- a/drivers/i2c/busses/i2c-qcom-cci.c
> > +++ b/drivers/i2c/busses/i2c-qcom-cci.c
> > @@ -11,6 +11,7 @@
> >  #include <linux/of.h>
> >  #include <linux/platform_device.h>
> >  #include <linux/pm_runtime.h>
> > +#include <linux/regulator/consumer.h>
> >
> >  #define CCI_HW_VERSION                         0x0
> >  #define CCI_RESET_CMD                          0x004
> > @@ -480,6 +481,20 @@ static void cci_disable_clocks(struct cci *cci)
> >  static int __maybe_unused cci_suspend_runtime(struct device *dev)
> >  {
> >         struct cci *cci = dev_get_drvdata(dev);
> > +       struct regulator *bus_regulator;
> > +       unsigned int i;
> > +
> > +       for (i = 0; i < cci->data->num_masters; i++) {
> > +               if (!cci->master[i].cci)
> > +                       continue;
> > +
> > +               bus_regulator = cci->master[i].adap.bus_regulator;
> > +               if (!bus_regulator)
> > +                       continue;
> > +
> > +               if (regulator_is_enabled(bus_regulator) > 0)
>
> Is this check needed? No matter the current status of the regulator,
> we'd like to disable it, and from my reading regulator_disable can be
> called for already disabled regulators.

+1, but why do we even assign this regulator to each adapter, a
simpler solution would be to have the regulator part of the cci
struct, and simply disable/enable it on runtime suspend/resume,
without extra loop/check. I2C core does nothing with
adap.bus_regulator anyway (5a7b95fb993e has been partially reverted).

>
> > +                       regulator_disable(bus_regulator);
> > +       }
> >
> >         cci_disable_clocks(cci);
> >         return 0;
> > @@ -488,12 +503,30 @@ static int __maybe_unused cci_suspend_runtime(struct device *dev)
> >  static int __maybe_unused cci_resume_runtime(struct device *dev)
> >  {
> >         struct cci *cci = dev_get_drvdata(dev);
> > +       struct regulator *bus_regulator;
> > +       unsigned int i;
> >         int ret;
> >
> >         ret = cci_enable_clocks(cci);
> >         if (ret)
> >                 return ret;
> >
> > +       for (i = 0; i < cci->data->num_masters; i++) {
> > +               if (!cci->master[i].cci)
> > +                       continue;
> > +
> > +               bus_regulator = cci->master[i].adap.bus_regulator;
> > +               if (!bus_regulator)
> > +                       continue;
> > +
> > +               if (!regulator_is_enabled(bus_regulator)) {
> > +                       ret = regulator_enable(bus_regulator);
> > +                       if (ret)
> > +                               dev_err(dev, "failed to enable regulator: %d\n",
> > +                                       ret);
> > +               }
> > +       }
> > +
> >         cci_init(cci);
> >         return 0;
> >  }
> > @@ -593,6 +626,7 @@ static int cci_probe(struct platform_device *pdev)
> >         dev_dbg(dev, "CCI HW version = 0x%08x", val);
> >
> >         for_each_available_child_of_node(dev->of_node, child) {
> > +               struct regulator *bus_regulator;
> >                 struct cci_master *master;
> >                 u32 idx;
> >
> > @@ -637,6 +671,21 @@ static int cci_probe(struct platform_device *pdev)
> >                         master->cci = NULL;
> >                         goto error_i2c;
> >                 }
> > +
> > +               /*
> > +                * It might be possible to find an optional vbus supply, but
> > +                * it requires to pass the registration of an I2C adapter
> > +                * device and its association with a bus device tree node.
> > +                */
> > +               bus_regulator = devm_regulator_get_optional(&master->adap.dev,
> > +                                                           "vbus");
> > +               if (IS_ERR(bus_regulator)) {
> > +                       ret = PTR_ERR(bus_regulator);
> > +                       if (ret == -EPROBE_DEFER)
> > +                               goto error_i2c;
> > +                       bus_regulator = NULL;
> > +               }
> > +               master->adap.bus_regulator = bus_regulator;
> >         }
> >
> >         ret = cci_reset(cci);
> > --
> > 2.33.0
> >
>
> With the above nit sorted, feel free to add my r-b.
>
> Reviewed-by: Robert Foss <robert.foss@linaro.org>
Bjorn Andersson Feb. 4, 2022, 6:21 p.m. UTC | #3
On Thu 03 Feb 08:47 PST 2022, Vladimir Zapolskiy wrote:

> The change adds handling of optional vbus regulators in the driver.
> 
> Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
> ---
>  drivers/i2c/busses/i2c-qcom-cci.c | 49 +++++++++++++++++++++++++++++++
>  1 file changed, 49 insertions(+)
> 
> diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
> index 775945f7b4cd..2fc7f1f2616f 100644
> --- a/drivers/i2c/busses/i2c-qcom-cci.c
> +++ b/drivers/i2c/busses/i2c-qcom-cci.c
> @@ -11,6 +11,7 @@
>  #include <linux/of.h>
>  #include <linux/platform_device.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/regulator/consumer.h>
>  
>  #define CCI_HW_VERSION				0x0
>  #define CCI_RESET_CMD				0x004
> @@ -480,6 +481,20 @@ static void cci_disable_clocks(struct cci *cci)
>  static int __maybe_unused cci_suspend_runtime(struct device *dev)
>  {
>  	struct cci *cci = dev_get_drvdata(dev);
> +	struct regulator *bus_regulator;
> +	unsigned int i;
> +
> +	for (i = 0; i < cci->data->num_masters; i++) {
> +		if (!cci->master[i].cci)
> +			continue;
> +
> +		bus_regulator = cci->master[i].adap.bus_regulator;
> +		if (!bus_regulator)
> +			continue;
> +
> +		if (regulator_is_enabled(bus_regulator) > 0)
> +			regulator_disable(bus_regulator);
> +	}
>  
>  	cci_disable_clocks(cci);
>  	return 0;
> @@ -488,12 +503,30 @@ static int __maybe_unused cci_suspend_runtime(struct device *dev)
>  static int __maybe_unused cci_resume_runtime(struct device *dev)
>  {
>  	struct cci *cci = dev_get_drvdata(dev);
> +	struct regulator *bus_regulator;
> +	unsigned int i;
>  	int ret;
>  
>  	ret = cci_enable_clocks(cci);
>  	if (ret)
>  		return ret;
>  
> +	for (i = 0; i < cci->data->num_masters; i++) {
> +		if (!cci->master[i].cci)
> +			continue;
> +
> +		bus_regulator = cci->master[i].adap.bus_regulator;
> +		if (!bus_regulator)
> +			continue;
> +
> +		if (!regulator_is_enabled(bus_regulator)) {

regulator_is_enabled() tests if the regulator is enabled, not if you
have enabled it. So if this is a shared regulator you might learn that
it's already on, skip your regulator_enable() and then the other
consumer turns it off behind your back.

If you want the regulator to be on, you should regulator_enable().

> +			ret = regulator_enable(bus_regulator);
> +			if (ret)
> +				dev_err(dev, "failed to enable regulator: %d\n",
> +					ret);
> +		}
> +	}
> +
>  	cci_init(cci);
>  	return 0;
>  }
> @@ -593,6 +626,7 @@ static int cci_probe(struct platform_device *pdev)
>  	dev_dbg(dev, "CCI HW version = 0x%08x", val);
>  
>  	for_each_available_child_of_node(dev->of_node, child) {
> +		struct regulator *bus_regulator;
>  		struct cci_master *master;
>  		u32 idx;
>  
> @@ -637,6 +671,21 @@ static int cci_probe(struct platform_device *pdev)
>  			master->cci = NULL;
>  			goto error_i2c;
>  		}
> +
> +		/*
> +		 * It might be possible to find an optional vbus supply, but
> +		 * it requires to pass the registration of an I2C adapter
> +		 * device and its association with a bus device tree node.
> +		 */

I'm afraid that I don't understand this comment. The regulator is
optional because most of the time we don't control it explicitly.

So a comment like "Control of IO supply is optional" seems more
relevant.

Regards,
Bjorn

> +		bus_regulator = devm_regulator_get_optional(&master->adap.dev,
> +							    "vbus");
> +		if (IS_ERR(bus_regulator)) {
> +			ret = PTR_ERR(bus_regulator);
> +			if (ret == -EPROBE_DEFER)
> +				goto error_i2c;
> +			bus_regulator = NULL;
> +		}
> +		master->adap.bus_regulator = bus_regulator;
>  	}
>  
>  	ret = cci_reset(cci);
> -- 
> 2.33.0
>
Bjorn Andersson Feb. 4, 2022, 6:28 p.m. UTC | #4
On Fri 04 Feb 03:41 PST 2022, Loic Poulain wrote:

> On Fri, 4 Feb 2022 at 12:03, Robert Foss <robert.foss@linaro.org> wrote:
> >
> > On Thu, 3 Feb 2022 at 17:47, Vladimir Zapolskiy
> > <vladimir.zapolskiy@linaro.org> wrote:
> > >
> > > The change adds handling of optional vbus regulators in the driver.
> > >
> > > Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
> > > ---
> > >  drivers/i2c/busses/i2c-qcom-cci.c | 49 +++++++++++++++++++++++++++++++
> > >  1 file changed, 49 insertions(+)
> > >
> > > diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
> > > index 775945f7b4cd..2fc7f1f2616f 100644
> > > --- a/drivers/i2c/busses/i2c-qcom-cci.c
> > > +++ b/drivers/i2c/busses/i2c-qcom-cci.c
> > > @@ -11,6 +11,7 @@
> > >  #include <linux/of.h>
> > >  #include <linux/platform_device.h>
> > >  #include <linux/pm_runtime.h>
> > > +#include <linux/regulator/consumer.h>
> > >
> > >  #define CCI_HW_VERSION                         0x0
> > >  #define CCI_RESET_CMD                          0x004
> > > @@ -480,6 +481,20 @@ static void cci_disable_clocks(struct cci *cci)
> > >  static int __maybe_unused cci_suspend_runtime(struct device *dev)
> > >  {
> > >         struct cci *cci = dev_get_drvdata(dev);
> > > +       struct regulator *bus_regulator;
> > > +       unsigned int i;
> > > +
> > > +       for (i = 0; i < cci->data->num_masters; i++) {
> > > +               if (!cci->master[i].cci)
> > > +                       continue;
> > > +
> > > +               bus_regulator = cci->master[i].adap.bus_regulator;
> > > +               if (!bus_regulator)
> > > +                       continue;
> > > +
> > > +               if (regulator_is_enabled(bus_regulator) > 0)
> >
> > Is this check needed? No matter the current status of the regulator,
> > we'd like to disable it, and from my reading regulator_disable can be
> > called for already disabled regulators.
> 
> +1, but why do we even assign this regulator to each adapter, a
> simpler solution would be to have the regulator part of the cci
> struct, and simply disable/enable it on runtime suspend/resume,
> without extra loop/check.

But that implies that you always will have the same io-supply for your
two busses. Something that seems likely but I don't see that it's a
requirement.

Although as proposed "vbus" is acquired from the cci node and not the
individual bus anyways...

> I2C core does nothing with
> adap.bus_regulator anyway (5a7b95fb993e has been partially reverted).
> 

Thanks for the pointer, that looks like a much better and generic
solution. In particular if we specify the regulator per bus.

Regards,
Bjorn

> >
> > > +                       regulator_disable(bus_regulator);
> > > +       }
> > >
> > >         cci_disable_clocks(cci);
> > >         return 0;
> > > @@ -488,12 +503,30 @@ static int __maybe_unused cci_suspend_runtime(struct device *dev)
> > >  static int __maybe_unused cci_resume_runtime(struct device *dev)
> > >  {
> > >         struct cci *cci = dev_get_drvdata(dev);
> > > +       struct regulator *bus_regulator;
> > > +       unsigned int i;
> > >         int ret;
> > >
> > >         ret = cci_enable_clocks(cci);
> > >         if (ret)
> > >                 return ret;
> > >
> > > +       for (i = 0; i < cci->data->num_masters; i++) {
> > > +               if (!cci->master[i].cci)
> > > +                       continue;
> > > +
> > > +               bus_regulator = cci->master[i].adap.bus_regulator;
> > > +               if (!bus_regulator)
> > > +                       continue;
> > > +
> > > +               if (!regulator_is_enabled(bus_regulator)) {
> > > +                       ret = regulator_enable(bus_regulator);
> > > +                       if (ret)
> > > +                               dev_err(dev, "failed to enable regulator: %d\n",
> > > +                                       ret);
> > > +               }
> > > +       }
> > > +
> > >         cci_init(cci);
> > >         return 0;
> > >  }
> > > @@ -593,6 +626,7 @@ static int cci_probe(struct platform_device *pdev)
> > >         dev_dbg(dev, "CCI HW version = 0x%08x", val);
> > >
> > >         for_each_available_child_of_node(dev->of_node, child) {
> > > +               struct regulator *bus_regulator;
> > >                 struct cci_master *master;
> > >                 u32 idx;
> > >
> > > @@ -637,6 +671,21 @@ static int cci_probe(struct platform_device *pdev)
> > >                         master->cci = NULL;
> > >                         goto error_i2c;
> > >                 }
> > > +
> > > +               /*
> > > +                * It might be possible to find an optional vbus supply, but
> > > +                * it requires to pass the registration of an I2C adapter
> > > +                * device and its association with a bus device tree node.
> > > +                */
> > > +               bus_regulator = devm_regulator_get_optional(&master->adap.dev,
> > > +                                                           "vbus");
> > > +               if (IS_ERR(bus_regulator)) {
> > > +                       ret = PTR_ERR(bus_regulator);
> > > +                       if (ret == -EPROBE_DEFER)
> > > +                               goto error_i2c;
> > > +                       bus_regulator = NULL;
> > > +               }
> > > +               master->adap.bus_regulator = bus_regulator;
> > >         }
> > >
> > >         ret = cci_reset(cci);
> > > --
> > > 2.33.0
> > >
> >
> > With the above nit sorted, feel free to add my r-b.
> >
> > Reviewed-by: Robert Foss <robert.foss@linaro.org>
Dmitry Baryshkov Feb. 10, 2022, 3:37 p.m. UTC | #5
On Fri, 4 Feb 2022 at 14:42, Loic Poulain <loic.poulain@linaro.org> wrote:
>
> On Fri, 4 Feb 2022 at 12:03, Robert Foss <robert.foss@linaro.org> wrote:
> >
> > On Thu, 3 Feb 2022 at 17:47, Vladimir Zapolskiy
> > <vladimir.zapolskiy@linaro.org> wrote:
> > >
> > > The change adds handling of optional vbus regulators in the driver.
> > >
> > > Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
> > > ---
> > >  drivers/i2c/busses/i2c-qcom-cci.c | 49 +++++++++++++++++++++++++++++++
> > >  1 file changed, 49 insertions(+)
> > >
> > > diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
> > > index 775945f7b4cd..2fc7f1f2616f 100644
> > > --- a/drivers/i2c/busses/i2c-qcom-cci.c
> > > +++ b/drivers/i2c/busses/i2c-qcom-cci.c
> > > @@ -11,6 +11,7 @@
> > >  #include <linux/of.h>
> > >  #include <linux/platform_device.h>
> > >  #include <linux/pm_runtime.h>
> > > +#include <linux/regulator/consumer.h>
> > >
> > >  #define CCI_HW_VERSION                         0x0
> > >  #define CCI_RESET_CMD                          0x004
> > > @@ -480,6 +481,20 @@ static void cci_disable_clocks(struct cci *cci)
> > >  static int __maybe_unused cci_suspend_runtime(struct device *dev)
> > >  {
> > >         struct cci *cci = dev_get_drvdata(dev);
> > > +       struct regulator *bus_regulator;
> > > +       unsigned int i;
> > > +
> > > +       for (i = 0; i < cci->data->num_masters; i++) {
> > > +               if (!cci->master[i].cci)
> > > +                       continue;
> > > +
> > > +               bus_regulator = cci->master[i].adap.bus_regulator;
> > > +               if (!bus_regulator)
> > > +                       continue;
> > > +
> > > +               if (regulator_is_enabled(bus_regulator) > 0)
> >
> > Is this check needed? No matter the current status of the regulator,
> > we'd like to disable it, and from my reading regulator_disable can be
> > called for already disabled regulators.
>
> +1, but why do we even assign this regulator to each adapter, a
> simpler solution would be to have the regulator part of the cci
> struct, and simply disable/enable it on runtime suspend/resume,
> without extra loop/check. I2C core does nothing with
> adap.bus_regulator anyway (5a7b95fb993e has been partially reverted).

I like the idea of having the regulator per bus (rather than per
controller). However instead of pushing handling these changes to the
CCI controller, I'd suggest to move this code to the i2c-core itself.
The original patch tried to do the regulator control per client.
Instead it should be done per adapter. I think this should also solve
the reported issue for AMD controllers (since that i2c adapters won't
have vbus-supply).

>
> >
> > > +                       regulator_disable(bus_regulator);
> > > +       }
> > >
> > >         cci_disable_clocks(cci);
> > >         return 0;
> > > @@ -488,12 +503,30 @@ static int __maybe_unused cci_suspend_runtime(struct device *dev)
> > >  static int __maybe_unused cci_resume_runtime(struct device *dev)
> > >  {
> > >         struct cci *cci = dev_get_drvdata(dev);
> > > +       struct regulator *bus_regulator;
> > > +       unsigned int i;
> > >         int ret;
> > >
> > >         ret = cci_enable_clocks(cci);
> > >         if (ret)
> > >                 return ret;
> > >
> > > +       for (i = 0; i < cci->data->num_masters; i++) {
> > > +               if (!cci->master[i].cci)
> > > +                       continue;
> > > +
> > > +               bus_regulator = cci->master[i].adap.bus_regulator;
> > > +               if (!bus_regulator)
> > > +                       continue;
> > > +
> > > +               if (!regulator_is_enabled(bus_regulator)) {
> > > +                       ret = regulator_enable(bus_regulator);
> > > +                       if (ret)
> > > +                               dev_err(dev, "failed to enable regulator: %d\n",
> > > +                                       ret);
> > > +               }
> > > +       }
> > > +
> > >         cci_init(cci);
> > >         return 0;
> > >  }
> > > @@ -593,6 +626,7 @@ static int cci_probe(struct platform_device *pdev)
> > >         dev_dbg(dev, "CCI HW version = 0x%08x", val);
> > >
> > >         for_each_available_child_of_node(dev->of_node, child) {
> > > +               struct regulator *bus_regulator;
> > >                 struct cci_master *master;
> > >                 u32 idx;
> > >
> > > @@ -637,6 +671,21 @@ static int cci_probe(struct platform_device *pdev)
> > >                         master->cci = NULL;
> > >                         goto error_i2c;
> > >                 }
> > > +
> > > +               /*
> > > +                * It might be possible to find an optional vbus supply, but
> > > +                * it requires to pass the registration of an I2C adapter
> > > +                * device and its association with a bus device tree node.
> > > +                */
> > > +               bus_regulator = devm_regulator_get_optional(&master->adap.dev,
> > > +                                                           "vbus");
> > > +               if (IS_ERR(bus_regulator)) {
> > > +                       ret = PTR_ERR(bus_regulator);
> > > +                       if (ret == -EPROBE_DEFER)
> > > +                               goto error_i2c;
> > > +                       bus_regulator = NULL;
> > > +               }
> > > +               master->adap.bus_regulator = bus_regulator;
> > >         }
> > >
> > >         ret = cci_reset(cci);
> > > --
> > > 2.33.0
> > >
> >
> > With the above nit sorted, feel free to add my r-b.
> >
> > Reviewed-by: Robert Foss <robert.foss@linaro.org>
diff mbox series

Patch

diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
index 775945f7b4cd..2fc7f1f2616f 100644
--- a/drivers/i2c/busses/i2c-qcom-cci.c
+++ b/drivers/i2c/busses/i2c-qcom-cci.c
@@ -11,6 +11,7 @@ 
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
 
 #define CCI_HW_VERSION				0x0
 #define CCI_RESET_CMD				0x004
@@ -480,6 +481,20 @@  static void cci_disable_clocks(struct cci *cci)
 static int __maybe_unused cci_suspend_runtime(struct device *dev)
 {
 	struct cci *cci = dev_get_drvdata(dev);
+	struct regulator *bus_regulator;
+	unsigned int i;
+
+	for (i = 0; i < cci->data->num_masters; i++) {
+		if (!cci->master[i].cci)
+			continue;
+
+		bus_regulator = cci->master[i].adap.bus_regulator;
+		if (!bus_regulator)
+			continue;
+
+		if (regulator_is_enabled(bus_regulator) > 0)
+			regulator_disable(bus_regulator);
+	}
 
 	cci_disable_clocks(cci);
 	return 0;
@@ -488,12 +503,30 @@  static int __maybe_unused cci_suspend_runtime(struct device *dev)
 static int __maybe_unused cci_resume_runtime(struct device *dev)
 {
 	struct cci *cci = dev_get_drvdata(dev);
+	struct regulator *bus_regulator;
+	unsigned int i;
 	int ret;
 
 	ret = cci_enable_clocks(cci);
 	if (ret)
 		return ret;
 
+	for (i = 0; i < cci->data->num_masters; i++) {
+		if (!cci->master[i].cci)
+			continue;
+
+		bus_regulator = cci->master[i].adap.bus_regulator;
+		if (!bus_regulator)
+			continue;
+
+		if (!regulator_is_enabled(bus_regulator)) {
+			ret = regulator_enable(bus_regulator);
+			if (ret)
+				dev_err(dev, "failed to enable regulator: %d\n",
+					ret);
+		}
+	}
+
 	cci_init(cci);
 	return 0;
 }
@@ -593,6 +626,7 @@  static int cci_probe(struct platform_device *pdev)
 	dev_dbg(dev, "CCI HW version = 0x%08x", val);
 
 	for_each_available_child_of_node(dev->of_node, child) {
+		struct regulator *bus_regulator;
 		struct cci_master *master;
 		u32 idx;
 
@@ -637,6 +671,21 @@  static int cci_probe(struct platform_device *pdev)
 			master->cci = NULL;
 			goto error_i2c;
 		}
+
+		/*
+		 * It might be possible to find an optional vbus supply, but
+		 * it requires to pass the registration of an I2C adapter
+		 * device and its association with a bus device tree node.
+		 */
+		bus_regulator = devm_regulator_get_optional(&master->adap.dev,
+							    "vbus");
+		if (IS_ERR(bus_regulator)) {
+			ret = PTR_ERR(bus_regulator);
+			if (ret == -EPROBE_DEFER)
+				goto error_i2c;
+			bus_regulator = NULL;
+		}
+		master->adap.bus_regulator = bus_regulator;
 	}
 
 	ret = cci_reset(cci);