Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [RFC PATCH 14/15] regulator: pwm: implement ->enable(), ->disable() and ->is_enabled methods
From: Boris Brezillon @ 2015-07-01 12:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <3530235.H11CYEfk28@diego>

On Wed, 01 Jul 2015 14:08:18 +0200
Heiko Stübner <heiko@sntech.de> wrote:

> Am Mittwoch, 1. Juli 2015, 14:05:31 schrieb Boris Brezillon:
> > Hi Heiko,
> > 
> > On Wed, 01 Jul 2015 13:58:09 +0200
> > 
> > Heiko Stübner <heiko@sntech.de> wrote:
> > > Am Mittwoch, 1. Juli 2015, 10:22:00 schrieb Boris Brezillon:
> > > > Implement the ->enable(), ->disable() and ->is_enabled methods and
> > > > remove
> > > > the PWM call in ->set_voltage_sel().
> > > > This is particularly important for critical regulators tagged as
> > > > always-on,
> > > > because not claiming the PWM (and its dependencies) might lead to
> > > > unpredictable behavior (like a system hang because the PWM clk is only
> > > > claimed when the PWM device is enabled).
> > > > 
> > > > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > > > ---
> > > > 
> > > >  drivers/regulator/pwm-regulator.c | 32 ++++++++++++++++++++++++++------
> > > >  1 file changed, 26 insertions(+), 6 deletions(-)
> > > > 
> > > > diff --git a/drivers/regulator/pwm-regulator.c
> > > > b/drivers/regulator/pwm-regulator.c index 12b4d9d..8159518 100644
> > > > --- a/drivers/regulator/pwm-regulator.c
> > > > +++ b/drivers/regulator/pwm-regulator.c
> > > > @@ -59,12 +59,6 @@ static int pwm_regulator_set_voltage_sel(struct
> > > > regulator_dev *rdev,
> > > > 
> > > >  	drvdata->state = selector;
> > > > 
> > > > -	ret = pwm_enable(drvdata->pwm);
> > > > -	if (ret) {
> > > > -		dev_err(&rdev->dev, "Failed to enable PWM\n");
> > > > -		return ret;
> > > > -	}
> > > > -
> > > > 
> > > >  	return 0;
> > > >  
> > > >  }
> > > > 
> > > > @@ -79,11 +73,37 @@ static int pwm_regulator_list_voltage(struct
> > > > regulator_dev *rdev, return drvdata->duty_cycle_table[selector].uV;
> > > > 
> > > >  }
> > > > 
> > > > +static int pwm_regulator_enable(struct regulator_dev *dev)
> > > > +{
> > > > +	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
> > > > +
> > > > +	return pwm_enable(drvdata->pwm);
> > > > +}
> > > > +
> > > > +static int pwm_regulator_disable(struct regulator_dev *dev)
> > > > +{
> > > > +	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
> > > > +
> > > > +	pwm_disable(drvdata->pwm);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int pwm_regulator_is_enabled(struct regulator_dev *dev)
> > > > +{
> > > > +       struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
> > > > +
> > > > +       return pwm_is_enabled(drvdata->pwm);
> > > > +}
> > > 
> > > nit: indentation is wrong in pwm_regulator_is_enabled (spaces instead of
> > > tabs)
> > Yep, I noticed checkpatch warnings/errors before sending the patch, but
> > since this is just an RFC I decided to fix them for the next version ;-)
> 
> ok, so I'll just skip over any more style issues for now. Making my way 
> through your series and trying it on my veyron right now :-)

Also note that I haven't tested the series on a real board (just compile
tested) because I don't have the board with me right now, but I wanted
to post the RFC early so that we can discuss the concepts.

Anyway, any feedback on the implementation (including bug reports) is
welcome.

This is the version I actually tested on the veyron board:

https://github.com/bbrezillon/linux-rk/tree/rk-3.14

Best Regards,

Boris

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* Re: [RFC PATCH 14/15] regulator: pwm: implement ->enable(), ->disable() and ->is_enabled methods
From: Heiko Stübner @ 2015-07-01 12:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20150701140531.2041b05c@bbrezillon>

Am Mittwoch, 1. Juli 2015, 14:05:31 schrieb Boris Brezillon:
> Hi Heiko,
> 
> On Wed, 01 Jul 2015 13:58:09 +0200
> 
> Heiko Stübner <heiko@sntech.de> wrote:
> > Am Mittwoch, 1. Juli 2015, 10:22:00 schrieb Boris Brezillon:
> > > Implement the ->enable(), ->disable() and ->is_enabled methods and
> > > remove
> > > the PWM call in ->set_voltage_sel().
> > > This is particularly important for critical regulators tagged as
> > > always-on,
> > > because not claiming the PWM (and its dependencies) might lead to
> > > unpredictable behavior (like a system hang because the PWM clk is only
> > > claimed when the PWM device is enabled).
> > > 
> > > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > > ---
> > > 
> > >  drivers/regulator/pwm-regulator.c | 32 ++++++++++++++++++++++++++------
> > >  1 file changed, 26 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/drivers/regulator/pwm-regulator.c
> > > b/drivers/regulator/pwm-regulator.c index 12b4d9d..8159518 100644
> > > --- a/drivers/regulator/pwm-regulator.c
> > > +++ b/drivers/regulator/pwm-regulator.c
> > > @@ -59,12 +59,6 @@ static int pwm_regulator_set_voltage_sel(struct
> > > regulator_dev *rdev,
> > > 
> > >  	drvdata->state = selector;
> > > 
> > > -	ret = pwm_enable(drvdata->pwm);
> > > -	if (ret) {
> > > -		dev_err(&rdev->dev, "Failed to enable PWM\n");
> > > -		return ret;
> > > -	}
> > > -
> > > 
> > >  	return 0;
> > >  
> > >  }
> > > 
> > > @@ -79,11 +73,37 @@ static int pwm_regulator_list_voltage(struct
> > > regulator_dev *rdev, return drvdata->duty_cycle_table[selector].uV;
> > > 
> > >  }
> > > 
> > > +static int pwm_regulator_enable(struct regulator_dev *dev)
> > > +{
> > > +	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
> > > +
> > > +	return pwm_enable(drvdata->pwm);
> > > +}
> > > +
> > > +static int pwm_regulator_disable(struct regulator_dev *dev)
> > > +{
> > > +	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
> > > +
> > > +	pwm_disable(drvdata->pwm);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int pwm_regulator_is_enabled(struct regulator_dev *dev)
> > > +{
> > > +       struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
> > > +
> > > +       return pwm_is_enabled(drvdata->pwm);
> > > +}
> > 
> > nit: indentation is wrong in pwm_regulator_is_enabled (spaces instead of
> > tabs)
> Yep, I noticed checkpatch warnings/errors before sending the patch, but
> since this is just an RFC I decided to fix them for the next version ;-)

ok, so I'll just skip over any more style issues for now. Making my way 
through your series and trying it on my veyron right now :-)


Heiko

^ permalink raw reply

* Re: [RFC PATCH 14/15] regulator: pwm: implement ->enable(), ->disable() and ->is_enabled methods
From: Boris Brezillon @ 2015-07-01 12:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <3495901.qMzLCVWCDl@diego>

Hi Heiko,

On Wed, 01 Jul 2015 13:58:09 +0200
Heiko Stübner <heiko@sntech.de> wrote:

> Am Mittwoch, 1. Juli 2015, 10:22:00 schrieb Boris Brezillon:
> > Implement the ->enable(), ->disable() and ->is_enabled methods and remove
> > the PWM call in ->set_voltage_sel().
> > This is particularly important for critical regulators tagged as always-on,
> > because not claiming the PWM (and its dependencies) might lead to
> > unpredictable behavior (like a system hang because the PWM clk is only
> > claimed when the PWM device is enabled).
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > ---
> >  drivers/regulator/pwm-regulator.c | 32 ++++++++++++++++++++++++++------
> >  1 file changed, 26 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/regulator/pwm-regulator.c
> > b/drivers/regulator/pwm-regulator.c index 12b4d9d..8159518 100644
> > --- a/drivers/regulator/pwm-regulator.c
> > +++ b/drivers/regulator/pwm-regulator.c
> > @@ -59,12 +59,6 @@ static int pwm_regulator_set_voltage_sel(struct
> > regulator_dev *rdev,
> > 
> >  	drvdata->state = selector;
> > 
> > -	ret = pwm_enable(drvdata->pwm);
> > -	if (ret) {
> > -		dev_err(&rdev->dev, "Failed to enable PWM\n");
> > -		return ret;
> > -	}
> > -
> >  	return 0;
> >  }
> > 
> > @@ -79,11 +73,37 @@ static int pwm_regulator_list_voltage(struct
> > regulator_dev *rdev, return drvdata->duty_cycle_table[selector].uV;
> >  }
> > 
> > +static int pwm_regulator_enable(struct regulator_dev *dev)
> > +{
> > +	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
> > +
> > +	return pwm_enable(drvdata->pwm);
> > +}
> > +
> > +static int pwm_regulator_disable(struct regulator_dev *dev)
> > +{
> > +	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
> > +
> > +	pwm_disable(drvdata->pwm);
> > +
> > +	return 0;
> > +}
> > +
> > +static int pwm_regulator_is_enabled(struct regulator_dev *dev)
> > +{
> > +       struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
> > +
> > +       return pwm_is_enabled(drvdata->pwm);
> > +}
> 
> nit: indentation is wrong in pwm_regulator_is_enabled (spaces instead of tabs)

Yep, I noticed checkpatch warnings/errors before sending the patch, but
since this is just an RFC I decided to fix them for the next version ;-)

Best Regards,

Boris

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* Re: [RFC PATCH 14/15] regulator: pwm: implement ->enable(), ->disable() and ->is_enabled methods
From: Heiko Stübner @ 2015-07-01 11:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1435738921-25027-15-git-send-email-boris.brezillon@free-electrons.com>

Am Mittwoch, 1. Juli 2015, 10:22:00 schrieb Boris Brezillon:
> Implement the ->enable(), ->disable() and ->is_enabled methods and remove
> the PWM call in ->set_voltage_sel().
> This is particularly important for critical regulators tagged as always-on,
> because not claiming the PWM (and its dependencies) might lead to
> unpredictable behavior (like a system hang because the PWM clk is only
> claimed when the PWM device is enabled).
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> ---
>  drivers/regulator/pwm-regulator.c | 32 ++++++++++++++++++++++++++------
>  1 file changed, 26 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/regulator/pwm-regulator.c
> b/drivers/regulator/pwm-regulator.c index 12b4d9d..8159518 100644
> --- a/drivers/regulator/pwm-regulator.c
> +++ b/drivers/regulator/pwm-regulator.c
> @@ -59,12 +59,6 @@ static int pwm_regulator_set_voltage_sel(struct
> regulator_dev *rdev,
> 
>  	drvdata->state = selector;
> 
> -	ret = pwm_enable(drvdata->pwm);
> -	if (ret) {
> -		dev_err(&rdev->dev, "Failed to enable PWM\n");
> -		return ret;
> -	}
> -
>  	return 0;
>  }
> 
> @@ -79,11 +73,37 @@ static int pwm_regulator_list_voltage(struct
> regulator_dev *rdev, return drvdata->duty_cycle_table[selector].uV;
>  }
> 
> +static int pwm_regulator_enable(struct regulator_dev *dev)
> +{
> +	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
> +
> +	return pwm_enable(drvdata->pwm);
> +}
> +
> +static int pwm_regulator_disable(struct regulator_dev *dev)
> +{
> +	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
> +
> +	pwm_disable(drvdata->pwm);
> +
> +	return 0;
> +}
> +
> +static int pwm_regulator_is_enabled(struct regulator_dev *dev)
> +{
> +       struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
> +
> +       return pwm_is_enabled(drvdata->pwm);
> +}

nit: indentation is wrong in pwm_regulator_is_enabled (spaces instead of tabs)

^ permalink raw reply

* [PATCH v3] video-lp8788: Delete a check before backlight_device_unregister()
From: SF Markus Elfring @ 2015-07-01 10:30 UTC (permalink / raw)
  To: Lee Jones, Jingoo Han, Jean-Christophe Plagniol-Villard,
	Tomi Valkeinen, linux-fbdev
  Cc: Linux Kernel Mailing List, kernel-janitors, Julia Lawall
In-Reply-To: <20150701080612.GG3210@x1>

From: Markus Elfring <elfring@users.sourceforge.net>
Date: Wed, 1 Jul 2015 12:08:31 +0200

The backlight_device_unregister() function tests whether its argument
is NULL and then returns immediately.
Thus the test around the call is not needed.

This issue was detected by using the Coccinelle software.

Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
---
 drivers/video/backlight/lp8788_bl.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/video/backlight/lp8788_bl.c b/drivers/video/backlight/lp8788_bl.c
index e418d5b..5d583d7 100644
--- a/drivers/video/backlight/lp8788_bl.c
+++ b/drivers/video/backlight/lp8788_bl.c
@@ -221,8 +221,7 @@ static void lp8788_backlight_unregister(struct lp8788_bl *bl)
 {
 	struct backlight_device *bl_dev = bl->bl_dev;
 
-	if (bl_dev)
-		backlight_device_unregister(bl_dev);
+	backlight_device_unregister(bl_dev);
 }
 
 static ssize_t lp8788_get_bl_ctl_mode(struct device *dev,
-- 
2.4.5


^ permalink raw reply related

* [PATCH v2 12/12] driver-core: probe dependencies before probing
From: Tomeu Vizoso @ 2015-07-01  9:41 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, linux-fbdev, Tomeu Vizoso, linux-gpio,
	Greg Kroah-Hartman, Rafael J. Wysocki, alsa-devel, dri-devel,
	linux-acpi, Mark Brown, linux-pwm
In-Reply-To: <1435743667-11987-1-git-send-email-tomeu.vizoso@collabora.com>

Before actually probing a device, find out what dependencies it has and
do our best to ensure that they are available at this point.

This is accomplished by finding out what platform devices need to be
probed and probing them. Non-platform devices will be probed when the
closest ancestor that is a platform device is probed.

If any dependencies are still unavailable after that (most probably a
missing driver or an error in the HW description from the firmware), we
print a nice error message so that people don't have to add a zillion of
printks to find out why a device asked for its probe to be deferred.

Dependencies are discovered with the help of the code that is already
implementing the specification of the firmware bindings, via the
callbacks registered with fwnode_add_dependency_parser().

Currently the dependencies list is discarded but it could be stored for
later usage.

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
tegra, kernel, usb
---

Changes in v2:
- Allocate the list of dependencies and pass it to the function that
  fills it.

 drivers/base/dd.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index a638bbb..c8a1aff 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -25,6 +25,9 @@
 #include <linux/async.h>
 #include <linux/pm_runtime.h>
 #include <linux/pinctrl/devinfo.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
 
 #include "base.h"
 #include "power/power.h"
@@ -54,6 +57,140 @@ static LIST_HEAD(deferred_probe_active_list);
 static struct workqueue_struct *deferred_wq;
 static atomic_t deferred_trigger_count = ATOMIC_INIT(0);
 
+static bool device_is_bound(struct device *dev)
+{
+	return klist_node_attached(&dev->p->knode_driver);
+}
+
+static int fwnode_match(struct device *dev, void *data)
+{
+	return dev->fwnode = data;
+}
+
+static bool fwnode_is_bound(struct fwnode_handle *fwnode)
+{
+	struct device *dev;
+
+	dev = bus_find_device(&platform_bus_type, NULL, fwnode, fwnode_match);
+
+	/* Check whether device is bound or is being probed right now */
+	return dev ? dev->driver : false;
+}
+
+static bool fwnode_is_platform(struct fwnode_handle *fwnode)
+{
+	struct fwnode_handle *parent;
+	const char *compatible;
+	int count;
+
+	count = fwnode_property_read_string_array(fwnode, "compatible", NULL,
+						  0);
+
+	/* The node has to have a compatible string */
+	if (!count)
+		return false;
+
+	/* But it cannot be only simple-bus */
+	if ((count = 1) &&
+	    !fwnode_property_read_string(fwnode, "compatible", &compatible) &&
+	    !strcmp(compatible, "simple-bus"))
+		return false;
+
+	parent = fwnode_get_parent(fwnode);
+
+	/* Node is immediately below root */
+	if (!fwnode_get_parent(parent))
+		return true;
+
+	/* If its parent is a simple-bus */
+	if (fwnode_is_compatible(parent, "simple-bus"))
+		return true;
+
+	return false;
+}
+
+static struct fwnode_handle *get_enclosing_platform_dev(
+						struct fwnode_handle *fwnode)
+{
+	struct fwnode_handle *iter, *node = NULL;
+
+	for (iter = fwnode;
+	     iter && fwnode_get_parent(iter);
+	     iter = fwnode_get_parent(iter)) {
+
+		/*
+		 * If we already have a platform device and an ancestor is
+		 * already bound, the first is the one we want to probe.
+		 */
+		if (node && fwnode_is_bound(iter))
+			break;
+
+		if (fwnode_is_platform(iter))
+			node = iter;
+	}
+
+	return node;
+}
+
+static bool check_dependency(struct fwnode_handle *fwnode)
+{
+	struct fwnode_handle *target;
+	struct device *dev;
+
+	if (!fwnode)
+		return true;
+
+	target = get_enclosing_platform_dev(fwnode);
+	if (!target)
+		return true;
+
+	dev = bus_find_device(&platform_bus_type, NULL, target, fwnode_match);
+	if (!dev) {
+		pr_debug("Couldn't find device for %s\n",
+			 fwnode_get_name(fwnode));
+		return false;
+	}
+
+	/*
+	 * Device is bound or is being probed right now. If we have bad luck
+	 * and the dependency isn't ready when it's needed, deferred probe
+	 * will save us.
+	 */
+	if (dev->driver)
+		return true;
+
+	bus_probe_device(dev);
+
+	/* If the dependency hasn't finished probing, we'll want a warning */
+	return device_is_bound(dev);
+}
+
+static void check_dependencies(struct device *dev)
+{
+	struct fwnode_dependency *dep, *tmp;
+	LIST_HEAD(deps);
+
+	if (dev->parent && !check_dependency(dev->parent->fwnode))
+		pr_debug("Parent '%s' of device '%s' not available\n",
+			 dev_name(dev->parent), dev_name(dev));
+
+	if (!dev->fwnode) {
+		pr_debug("Device '%s' doesn't have a fwnode\n", dev_name(dev));
+		return;
+	}
+
+	fwnode_get_dependencies(dev->fwnode, &deps);
+
+	list_for_each_entry_safe(dep, tmp, &deps, dependency) {
+		if (!check_dependency(dep->fwnode))
+			pr_debug("Dependency '%s' not available\n",
+				 fwnode_get_name(dep->fwnode));
+
+		list_del(&dep->dependency);
+		kfree(dep);
+	}
+}
+
 /*
  * deferred_probe_work_func() - Retry probing devices in the active list.
  */
@@ -287,6 +424,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
 
 	dev->driver = drv;
 
+	check_dependencies(dev);
+
 	/* If using pinctrl, bind pins now before probing */
 	ret = pinctrl_bind_pins(dev);
 	if (ret)
-- 
2.4.1


^ permalink raw reply related

* [PATCH v2 11/12] ASoC: tegra: register dependency parser for firmware nodes
From: Tomeu Vizoso @ 2015-07-01  9:41 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, linux-fbdev, Tomeu Vizoso, linux-gpio, Liam Girdwood,
	Stephen Warren, Rafael J. Wysocki, alsa-devel, dri-devel,
	Jaroslav Kysela, linux-acpi, Mark Brown, linux-pwm, linux-tegra,
	Alexandre Courbot
In-Reply-To: <1435743667-11987-1-git-send-email-tomeu.vizoso@collabora.com>

So others can find out what dependencies a nvidia,tegra-audio-max98090
device has, as specified in
bindings/sound/nvidia,tegra-audio-max98090.txt.

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---

Changes in v2: None

 sound/soc/tegra/tegra_max98090.c | 42 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 41 insertions(+), 1 deletion(-)

diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c
index 902da36..0f7cbf3 100644
--- a/sound/soc/tegra/tegra_max98090.c
+++ b/sound/soc/tegra/tegra_max98090.c
@@ -316,7 +316,47 @@ static struct platform_driver tegra_max98090_driver = {
 	.probe = tegra_max98090_probe,
 	.remove = tegra_max98090_remove,
 };
-module_platform_driver(tegra_max98090_driver);
+
+static void add_dependency(struct fwnode_handle *fwnode,
+			   const char *property,
+			   struct list_head *deps)
+{
+	struct device_node *np;
+
+	np = of_parse_phandle(to_of_node(fwnode), property, 0);
+	if (!np)
+		return;
+
+	fwnode_add_dependency(&np->fwnode, deps);
+}
+
+static void tegra_max98090_get_dependencies(struct fwnode_handle *fwnode,
+					    struct list_head *deps)
+{
+	add_dependency(fwnode, "nvidia,i2s-controller", deps);
+	add_dependency(fwnode, "nvidia,audio-codec", deps);
+}
+
+static int __init tegra_max98090_init(void)
+{
+	int err;
+
+	err = platform_driver_register(&tegra_max98090_driver);
+	if (err < 0)
+		return err;
+
+	fwnode_add_dependency_parser(tegra_max98090_get_dependencies);
+
+	return 0;
+}
+module_init(tegra_max98090_init);
+
+static void __exit tegra_max98090_exit(void)
+{
+	fwnode_remove_dependency_parser(tegra_max98090_get_dependencies);
+	platform_driver_unregister(&tegra_max98090_driver);
+}
+module_exit(tegra_max98090_exit);
 
 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
 MODULE_DESCRIPTION("Tegra max98090 machine ASoC driver");
-- 
2.4.1


^ permalink raw reply related

* [PATCH v2 10/12] pwm: register dependency parser for firmware nodes
From: Tomeu Vizoso @ 2015-07-01  9:41 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, linux-fbdev, Tomeu Vizoso, linux-gpio,
	Rafael J. Wysocki, alsa-devel, dri-devel, linux-acpi, Mark Brown,
	linux-pwm
In-Reply-To: <1435743667-11987-1-git-send-email-tomeu.vizoso@collabora.com>

So others can find out what depends on pwm controllers, as specified
in bindings/pwm/pwm.txt.

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---

Changes in v2: None

 drivers/pwm/core.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 3a7769f..81b4fc0 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -917,11 +917,39 @@ static const struct file_operations pwm_debugfs_ops = {
 	.release = seq_release,
 };
 
+static void pwm_get_dependencies(struct fwnode_handle *fwnode,
+				 struct list_head *deps)
+{
+	struct device_node *np;
+	struct of_phandle_args pspec;
+	int count, i, ret;
+
+	np = to_of_node(fwnode);
+	if (!np)
+		return;
+
+	count = of_count_phandle_with_args(np, "pwms",
+					   "#pwm-cells");
+	for (i = 0; i < count; i++) {
+		ret = of_parse_phandle_with_args(np, "pwms",
+						 "#pwm-cells", i,
+						 &pspec);
+		if (ret || !pspec.np)
+			continue;
+
+		fwnode_add_dependency(&pspec.np->fwnode, deps);
+
+		of_node_put(pspec.np);
+	}
+}
+
 static int __init pwm_debugfs_init(void)
 {
 	debugfs_create_file("pwm", S_IFREG | S_IRUGO, NULL, NULL,
 			    &pwm_debugfs_ops);
 
+	fwnode_add_dependency_parser(pwm_get_dependencies);
+
 	return 0;
 }
 
-- 
2.4.1


^ permalink raw reply related

* [PATCH v2 09/12] regulator: register dependency parser for firmware nodes
From: Tomeu Vizoso @ 2015-07-01  9:41 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, linux-fbdev, Tomeu Vizoso, linux-gpio,
	Rafael J. Wysocki, alsa-devel, dri-devel, Liam Girdwood,
	linux-acpi, Mark Brown, linux-pwm
In-Reply-To: <1435743667-11987-1-git-send-email-tomeu.vizoso@collabora.com>

So others can find out what depends on regulators, as specified
in bindings/regulator/regulator.txt.

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---

Changes in v2: None

 drivers/regulator/core.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index c9f7201..535cad0 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -4112,6 +4112,31 @@ static const struct file_operations regulator_summary_fops = {
 #endif
 };
 
+static void regulator_get_dependencies(struct fwnode_handle *fwnode,
+				       struct list_head *deps)
+{
+	struct device_node *np;
+	struct property *pp;
+	struct device_node *dep;
+
+	np = to_of_node(fwnode);
+	if (!np)
+		return;
+
+	for_each_property_of_node(np, pp) {
+		if (!strends(pp->name, "-supply"))
+			continue;
+
+		dep = of_parse_phandle(np, pp->name, 0);
+		if (!dep)
+			continue;
+
+		fwnode_add_dependency(&dep->fwnode, deps);
+
+		of_node_put(dep);
+	}
+}
+
 static int __init regulator_init(void)
 {
 	int ret;
@@ -4130,6 +4155,8 @@ static int __init regulator_init(void)
 
 	regulator_dummy_init();
 
+	fwnode_add_dependency_parser(regulator_get_dependencies);
+
 	return ret;
 }
 
-- 
2.4.1


^ permalink raw reply related

* [PATCH v2 08/12] USB: EHCI: register dependency parser for firmware nodes
From: Tomeu Vizoso @ 2015-07-01  9:41 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, linux-fbdev, linux-tegra, Tomeu Vizoso, linux-gpio,
	Greg Kroah-Hartman, linux-usb, Rafael J. Wysocki, alsa-devel,
	dri-devel, linux-acpi, Mark Brown, linux-pwm, Stephen Warren,
	Alan Stern, Alexandre Courbot
In-Reply-To: <1435743667-11987-1-git-send-email-tomeu.vizoso@collabora.com>

So others can find out whether a firmware node depends on a phy as
specified in bindings/usb/nvidia,tegra20-ehci.txt.

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---

Changes in v2: None

 drivers/usb/host/ehci-tegra.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 4031b37..3665eaa 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -589,6 +589,18 @@ static const struct ehci_driver_overrides tegra_overrides __initconst = {
 	.reset			= tegra_ehci_reset,
 };
 
+static void tegra_ehci_get_dependencies(struct fwnode_handle *fwnode,
+					struct list_head *deps)
+{
+	struct device_node *np;
+
+	np = of_parse_phandle(to_of_node(fwnode), "nvidia,phy", 0);
+	if (!np)
+		return;
+
+	fwnode_add_dependency(&np->fwnode, deps);
+}
+
 static int __init ehci_tegra_init(void)
 {
 	if (usb_disabled())
@@ -611,6 +623,8 @@ static int __init ehci_tegra_init(void)
 	tegra_ehci_hc_driver.unmap_urb_for_dma = tegra_ehci_unmap_urb_for_dma;
 	tegra_ehci_hc_driver.hub_control = tegra_ehci_hub_control;
 
+	fwnode_add_dependency_parser(tegra_ehci_get_dependencies);
+
 	return platform_driver_register(&tegra_ehci_driver);
 }
 module_init(ehci_tegra_init);
@@ -618,6 +632,8 @@ module_init(ehci_tegra_init);
 static void __exit ehci_tegra_cleanup(void)
 {
 	platform_driver_unregister(&tegra_ehci_driver);
+
+	fwnode_remove_dependency_parser(tegra_ehci_get_dependencies);
 }
 module_exit(ehci_tegra_cleanup);
 
-- 
2.4.1


^ permalink raw reply related

* [PATCH v2 07/12] backlight: register dependency parser for firmware nodes
From: Tomeu Vizoso @ 2015-07-01  9:41 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, linux-fbdev, Tomeu Vizoso, linux-gpio, Tomi Valkeinen,
	Rafael J. Wysocki, alsa-devel, dri-devel, linux-acpi, Mark Brown,
	linux-pwm, Jingoo Han, Lee Jones,
	Jean-Christophe Plagniol-Villard
In-Reply-To: <1435743667-11987-1-git-send-email-tomeu.vizoso@collabora.com>

So others can find out what depends on backlight devices, as specified
in bindings/video/backlight/backlight.txt.

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---

Changes in v2: None

 drivers/video/backlight/backlight.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
index bddc8b1..ab8f5e7 100644
--- a/drivers/video/backlight/backlight.c
+++ b/drivers/video/backlight/backlight.c
@@ -566,8 +566,22 @@ struct backlight_device *of_find_backlight_by_node(struct device_node *node)
 EXPORT_SYMBOL(of_find_backlight_by_node);
 #endif
 
+static void backlight_get_dependencies(struct fwnode_handle *fwnode,
+					  struct list_head *deps)
+{
+	struct device_node *np;
+
+	np = of_parse_phandle(to_of_node(fwnode), "backlight", 0);
+	if (!np)
+		return;
+
+	fwnode_add_dependency(&np->fwnode, deps);
+}
+
 static void __exit backlight_class_exit(void)
 {
+	fwnode_remove_dependency_parser(backlight_get_dependencies);
+
 	class_destroy(backlight_class);
 }
 
@@ -586,6 +600,8 @@ static int __init backlight_class_init(void)
 	mutex_init(&backlight_dev_list_mutex);
 	BLOCKING_INIT_NOTIFIER_HEAD(&backlight_notifier);
 
+	fwnode_add_dependency_parser(backlight_get_dependencies);
+
 	return 0;
 }
 
-- 
2.4.1


^ permalink raw reply related

* [PATCH v2 06/12] backlight: Document consumers of backlight nodes
From: Tomeu Vizoso @ 2015-07-01  9:41 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mark Rutland, devicetree, linux-fbdev, Pawel Moll, Rob Herring,
	Tomeu Vizoso, linux-gpio, Ian Campbell, Rafael J. Wysocki,
	alsa-devel, dri-devel, linux-acpi, Mark Brown, linux-pwm,
	Kumar Gala
In-Reply-To: <1435743667-11987-1-git-send-email-tomeu.vizoso@collabora.com>

Add a small note that makes explicit that properties named 'backlight'
contain phandles to backlight nodes.

This is needed so that we can automatically extract dependencies on
backlight devices by assuming that a property with that name contains a
phandle to such a device.

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---

Changes in v2:
- Document that consumers of backlight devices can use the 'backlight'
  property to hold a phandle to the backlight device.

 .../bindings/video/backlight/backlight.txt         | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/backlight/backlight.txt

diff --git a/Documentation/devicetree/bindings/video/backlight/backlight.txt b/Documentation/devicetree/bindings/video/backlight/backlight.txt
new file mode 100644
index 0000000..309949d
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/backlight/backlight.txt
@@ -0,0 +1,22 @@
+Specifying backlight information for devices
+======================
+
+Backlight user nodes
+--------------------
+
+Nodes such as display panels that refer to backlight devices can do so by simply having a property named 'backlight' that contains a phandle to a backlight node.
+
+Example:
+
+	backlight: backlight {
+		compatible = "gpio-backlight";
+		gpios = <&gpio3 4 GPIO_ACTIVE_HIGH>;
+	};
+
+	[...]
+
+	panel: panel {
+		compatible = "cptt,claa101wb01";
+
+		backlight = <&backlight>;
+	};
-- 
2.4.1


^ permalink raw reply related

* [PATCH v2 05/12] gpu: host1x: register dependency parser for firmware nodes
From: Tomeu Vizoso @ 2015-07-01  9:41 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, linux-fbdev, Terje Bergström, Tomeu Vizoso,
	linux-gpio, Rafael J. Wysocki, alsa-devel, dri-devel, linux-acpi,
	Mark Brown, linux-pwm, linux-tegra
In-Reply-To: <1435743667-11987-1-git-send-email-tomeu.vizoso@collabora.com>

So others can find out dependencies of host1x clients, as specified in
bindings/gpu/nvidia,tegra20-host1x.txt.

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---

Changes in v2: None

 drivers/gpu/host1x/dev.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index 53d3d1d..5bb10b8 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -212,6 +212,29 @@ static struct platform_driver tegra_host1x_driver = {
 	.remove = host1x_remove,
 };
 
+static void add_dependency(struct fwnode_handle *fwnode,
+			   const char *property,
+			   struct list_head *deps)
+{
+	struct device_node *np;
+
+	np = of_parse_phandle(to_of_node(fwnode), property, 0);
+	if (!np)
+		return;
+
+	fwnode_add_dependency(&np->fwnode, deps);
+}
+
+static void host1x_get_dependencies(struct fwnode_handle *fwnode,
+				    struct list_head *deps)
+{
+	add_dependency(fwnode, "nvidia,dpaux", deps);
+	add_dependency(fwnode, "nvidia,panel", deps);
+	add_dependency(fwnode, "nvidia,ddc-i2c-bus", deps);
+	add_dependency(fwnode, "nvidia,hpd-gpio", deps);
+	add_dependency(fwnode, "ddc-i2c-bus", deps);
+}
+
 static int __init tegra_host1x_init(void)
 {
 	int err;
@@ -228,6 +251,8 @@ static int __init tegra_host1x_init(void)
 	if (err < 0)
 		goto unregister_host1x;
 
+	fwnode_add_dependency_parser(host1x_get_dependencies);
+
 	return 0;
 
 unregister_host1x:
@@ -240,6 +265,7 @@ module_init(tegra_host1x_init);
 
 static void __exit tegra_host1x_exit(void)
 {
+	fwnode_remove_dependency_parser(host1x_get_dependencies);
 	platform_driver_unregister(&tegra_mipi_driver);
 	platform_driver_unregister(&tegra_host1x_driver);
 	bus_unregister(&host1x_bus_type);
-- 
2.4.1


^ permalink raw reply related

* [PATCH v2 04/12] gpio: register dependency parser for firmware nodes
From: Tomeu Vizoso @ 2015-07-01  9:40 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, linux-fbdev, Tomeu Vizoso, linux-gpio,
	Rafael J. Wysocki, alsa-devel, dri-devel, linux-acpi, Mark Brown,
	linux-pwm, Alexandre Courbot
In-Reply-To: <1435743667-11987-1-git-send-email-tomeu.vizoso@collabora.com>

So the GPIO subsystem can be queried about the dependencies of nodes
that consume GPIOs, as specified in bindings/gpio/gpio.txt.

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---

Changes in v2: None

 drivers/gpio/gpiolib.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index bf4bd1d..6a3e83f 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2388,4 +2388,58 @@ static int __init gpiolib_debugfs_init(void)
 }
 subsys_initcall(gpiolib_debugfs_init);
 
+static void gpio_get_dependencies(struct fwnode_handle *fwnode,
+				  struct list_head *deps)
+{
+	struct device_node *np;
+	struct property *pp;
+	struct of_phandle_args pspec;
+	int count, i, ret;
+
+	np = to_of_node(fwnode);
+	if (!np)
+		return;
+
+	for_each_property_of_node(np, pp) {
+		if (strcmp(pp->name, "gpio") &&
+		    strcmp(pp->name, "gpios") &&
+		    !strends(pp->name, "-gpios") &&
+		    !strends(pp->name, "-gpio"))
+			continue;
+
+		count = of_count_phandle_with_args(np, pp->name,
+						   "#gpio-cells");
+		for (i = 0; i < count; i++) {
+			ret = of_parse_phandle_with_args(np, pp->name,
+							 "#gpio-cells", i,
+							 &pspec);
+			if (ret || !pspec.np)
+				continue;
+
+			fwnode_add_dependency(&pspec.np->fwnode, deps);
+
+			of_node_put(pspec.np);
+		}
+	}
+
+	for (i = 0;; i++) {
+		ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3,
+						       i, &pspec);
+		if (ret)
+			break;
+
+		fwnode_add_dependency(&pspec.np->fwnode, deps);
+
+		of_node_put(pspec.np);
+	}
+}
+
+static int __init gpiolib_init(void)
+{
+	fwnode_add_dependency_parser(gpio_get_dependencies);
+
+	return 0;
+}
+device_initcall(gpiolib_init);
+
 #endif	/* DEBUG_FS */
-- 
2.4.1


^ permalink raw reply related

* [PATCH v2 03/12] string: Introduce strends()
From: Tomeu Vizoso @ 2015-07-01  9:40 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, linux-fbdev, Tomeu Vizoso, linux-gpio,
	Rafael J. Wysocki, alsa-devel, dri-devel, linux-acpi, Mark Brown,
	linux-pwm
In-Reply-To: <1435743667-11987-1-git-send-email-tomeu.vizoso@collabora.com>

To avoid duplicating code in upcoming patches that will check for
postfixes in strings, add strends().

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---

Changes in v2:
- Move strends to string.h

 include/linux/string.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/include/linux/string.h b/include/linux/string.h
index d5dfe3e..4244363 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -146,6 +146,19 @@ static inline bool strstarts(const char *str, const char *prefix)
 	return strncmp(str, prefix, strlen(prefix)) = 0;
 }
 
+/**
+ * strends - does @str end with @postfix?
+ * @str: string to examine
+ * @postfix: postfix to look for
+ */
+static inline bool strends(const char *str, const char *postfix)
+{
+	if (strlen(str) < strlen(postfix))
+		return false;
+
+	return strcmp(str + strlen(str) - strlen(postfix), postfix) = 0;
+}
+
 size_t memweight(const void *ptr, size_t bytes);
 void memzero_explicit(void *s, size_t count);
 
-- 
2.4.1


^ permalink raw reply related

* [PATCH v2 02/12] device: property: find dependencies of a firmware node
From: Tomeu Vizoso @ 2015-07-01  9:40 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, linux-fbdev, Tomeu Vizoso, linux-gpio,
	Greg Kroah-Hartman, Rafael J. Wysocki, alsa-devel, dri-devel,
	linux-acpi, Mark Brown, linux-pwm
In-Reply-To: <1435743667-11987-1-git-send-email-tomeu.vizoso@collabora.com>

Adds API that allows callers to find out what other firmware nodes a
node depends on.

Implementors of bindings documentation can register callbacks that
return the dependencies of a node.

Dependency information can be used to change the order in which devices
are probed, or to print a warning when a device node is going to be
probed without all its dependencies fulfilled.

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---

Changes in v2:
- Allow bindings implementations register a function instead of using
  class callbacks, as not only subsystems implement firmware bindings.

 drivers/base/property.c  | 91 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fwnode.h   |  5 +++
 include/linux/property.h | 12 +++++++
 3 files changed, 108 insertions(+)

diff --git a/drivers/base/property.c b/drivers/base/property.c
index 8ead1ba..9d38ede 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -19,7 +19,13 @@
 #include <linux/platform_device.h>
 #include <linux/property.h>
 
+struct dependency_parser {
+	struct list_head parser;
+	void (*func)(struct fwnode_handle *fwnode, struct list_head *deps);
+};
+
 static bool fwnode_match_enable = false;
+static LIST_HEAD(dependency_parsers);
 
 /**
  * device_add_property_set - Add a collection of properties to a device object.
@@ -553,6 +559,27 @@ bool device_dma_is_coherent(struct device *dev)
 EXPORT_SYMBOL_GPL(device_dma_is_coherent);
 
 /**
+ * fwnode_add_dependency - add firmware node to the passed dependency list
+ * @fwnode: Firmware node to add to dependency list
+ * @list: Dependency list to add the fwnode to
+ */
+void fwnode_add_dependency(struct fwnode_handle *fwnode,
+			   struct list_head *list)
+{
+	struct fwnode_dependency *dep;
+
+	dep = kzalloc(sizeof(*dep), GFP_KERNEL);
+	if (!dep)
+		return;
+
+	INIT_LIST_HEAD(&dep->dependency);
+	dep->fwnode = fwnode;
+
+	list_add_tail(&dep->dependency, list);
+}
+EXPORT_SYMBOL_GPL(fwnode_add_dependency);
+
+/**
  * fwnode_get_parent - return the parent node of a device node
  * @fwnode: Device node to find the parent node of
  */
@@ -600,6 +627,70 @@ bool fwnode_is_compatible(struct fwnode_handle *fwnode, const char *compatible)
 EXPORT_SYMBOL_GPL(fwnode_is_compatible);
 
 /**
+ * fwnode_add_dependency_parser - register dependency parser
+ * @func: Function that will be called to find out dependencies of a node
+ *
+ * Registers a callback that will be called when collecting the dependencies
+ * of a firmware node. The callback should inspect the properties of the node
+ * and call fwnode_add_dependency() for each dependency it recognizes, from
+ * the bindings documentation.
+ */
+void fwnode_add_dependency_parser(
+	void (*func)(struct fwnode_handle *fwnode, struct list_head *deps))
+{
+	struct dependency_parser *parser;
+
+	parser = kzalloc(sizeof(*parser), GFP_KERNEL);
+	if (!parser)
+		return;
+
+	INIT_LIST_HEAD(&parser->parser);
+	parser->func = func;
+
+	list_add_tail(&parser->parser, &dependency_parsers);
+}
+EXPORT_SYMBOL_GPL(fwnode_add_dependency_parser);
+
+/**
+ * fwnode_remove_dependency_parser - unregister dependency parser
+ * @func: Function that was to be called to find out dependencies of a node
+ */
+void fwnode_remove_dependency_parser(
+	void (*func)(struct fwnode_handle *fwnode, struct list_head *deps))
+{
+	struct dependency_parser *parser, *tmp;
+
+	list_for_each_entry_safe(parser, tmp, &dependency_parsers, parser) {
+		if (parser->func = func) {
+			list_del(&parser->parser);
+			kfree(parser);
+			return;
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(fwnode_remove_dependency_parser);
+
+/**
+ * fwnode_get_dependencies - find out what dependencies a firmware node has
+ * @fwnode: firmware node to find its dependencies
+ * @deps: list of struct fwnode_dependency in which dependencies will be placed
+ */
+void fwnode_get_dependencies(struct fwnode_handle *fwnode,
+			     struct list_head *deps)
+{
+	struct dependency_parser *parser;
+	struct fwnode_handle *child;
+
+	list_for_each_entry(parser, &dependency_parsers, parser)
+		parser->func(fwnode, deps);
+
+	/* Some device nodes will have dependencies in non-device sub-nodes */
+	fwnode_for_each_child_node(fwnode, child)
+		if (!fwnode_property_present(child, "compatible"))
+			fwnode_get_dependencies(child, deps);
+}
+
+/**
  * fwnode_driver_match_device - Tell if a driver matches a device.
  * @drv: the device_driver structure to test
  * @dev: the device structure to match against
diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h
index 0408545..68ab558 100644
--- a/include/linux/fwnode.h
+++ b/include/linux/fwnode.h
@@ -24,4 +24,9 @@ struct fwnode_handle {
 	struct fwnode_handle *secondary;
 };
 
+struct fwnode_dependency {
+	struct fwnode_handle *fwnode;
+	struct list_head dependency;
+};
+
 #endif
diff --git a/include/linux/property.h b/include/linux/property.h
index 4e453c4..b8b86ea 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -86,6 +86,18 @@ bool fwnode_is_compatible(struct fwnode_handle *fwnode, const char *compatible);
 bool fwnode_driver_match_device(struct device *dev,
 				const struct device_driver *drv);
 
+void fwnode_add_dependency(struct fwnode_handle *fwnode,
+			   struct list_head *list);
+
+void fwnode_add_dependency_parser(
+	void (*func)(struct fwnode_handle *fwnode, struct list_head *deps));
+
+void fwnode_remove_dependency_parser(
+	void (*func)(struct fwnode_handle *fwnode, struct list_head *deps));
+
+void fwnode_get_dependencies(struct fwnode_handle *fwnode,
+			     struct list_head *list);
+
 unsigned int device_get_child_node_count(struct device *dev);
 
 static inline bool device_property_read_bool(struct device *dev,
-- 
2.4.1


^ permalink raw reply related

* [PATCH v2 01/12] device: property: delay device-driver matches
From: Tomeu Vizoso @ 2015-07-01  9:40 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree, linux-fbdev, Tomeu Vizoso, linux-gpio,
	Greg Kroah-Hartman, Rafael J. Wysocki, alsa-devel, dri-devel,
	linux-acpi, Mark Brown, linux-pwm
In-Reply-To: <1435743667-11987-1-git-send-email-tomeu.vizoso@collabora.com>

Delay matches of platform devices until late_initcall, when we are sure
that all built-in drivers have been registered already. This is needed
to prevent deferred probes because of some dependencies' drivers not
having registered yet.

This reduces the total amount of work that the kernel does during boot
because it won't try to match devices to drivers when built-in drivers
are still registering but also reduces some parallelism, so total boot
time might slightly increase or decrease depending on the platform and
kernel configuration.

This change will make make possible to prevent any deferred probes once
devices are probed in dependency order.

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---

Changes in v2:
- Instead of delaying all probes until late_initcall, only delay matches
  of platform devices that have a firmware node attached.

 drivers/base/property.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/drivers/base/property.c b/drivers/base/property.c
index 8528eb9..8ead1ba 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -16,8 +16,11 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
+#include <linux/platform_device.h>
 #include <linux/property.h>
 
+static bool fwnode_match_enable = false;
+
 /**
  * device_add_property_set - Add a collection of properties to a device object.
  * @dev: Device to add properties to.
@@ -604,6 +607,15 @@ EXPORT_SYMBOL_GPL(fwnode_is_compatible);
 bool fwnode_driver_match_device(struct device *dev,
 				const struct device_driver *drv)
 {
+	/*
+	 * Delay matches of platform devices until late_initcall, when we are
+	 * sure that all built-in drivers have been registered already. This
+	 * is needed to prevent deferred probes because of some drivers
+	 * not having registered yet.
+	 */
+	if(dev->bus = &platform_bus_type && !fwnode_match_enable)
+		return false;
+
 	if (is_of_node(dev->fwnode))
 		return of_driver_match_device(dev, drv);
 	else if (is_acpi_node(dev->fwnode))
@@ -612,3 +624,20 @@ bool fwnode_driver_match_device(struct device *dev,
 	return false;
 }
 EXPORT_SYMBOL_GPL(fwnode_driver_match_device);
+
+static int __device_attach(struct device *dev, void *data)
+{
+	device_initial_probe(dev);
+
+	return 0;
+}
+
+static int fwnode_match_initcall(void)
+{
+	fwnode_match_enable = true;
+
+	bus_for_each_dev(&platform_bus_type, NULL, NULL, __device_attach);
+
+	return 0;
+}
+late_initcall(fwnode_match_initcall);
-- 
2.4.1


^ permalink raw reply related

* [PATCH v2 0/12] Discover and probe dependencies
From: Tomeu Vizoso @ 2015-07-01  9:40 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mark Brown, linux-acpi, dri-devel, linux-fbdev, linux-gpio,
	devicetree, linux-pwm, Rafael J. Wysocki, alsa-devel,
	Tomeu Vizoso, Alan Stern, Linus Walleij, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Ian Campbell, Jingoo Han,
	Tomi Valkeinen, Pawel Moll, Greg Kroah-Hartman, Alexandre Courbot,
	Thierry Reding, Liam Girdwood, Terje Bergström

Hi,

this is version 2 of a series that probes devices in dependency order so
as to avoid deferred probes. While deferred probing is a powerful
solution that makes sure that you eventually get a working system at the
end of the boot, can make it very time consuming to find out why a
device didn't probe and can also introduce big delays in when a device
actually probes by sending it to the end of the deferred queue.

So far I have only tested on a Tegra124 Chromebook.

Thanks,

Tomeu

Changes in v2:
- Instead of delaying all probes until late_initcall, only delay matches
  of platform devices that have a firmware node attached.
- Allow bindings implementations register a function instead of using
  class callbacks, as not only subsystems implement firmware bindings.
- Move strends to string.h
- Document that consumers of backlight devices can use the 'backlight'
  property to hold a phandle to the backlight device.
- Allocate the list of dependencies and pass it to the function that
  fills it.

Tomeu Vizoso (12):
  device: property: delay device-driver matches
  device: property: find dependencies of a firmware node
  string: Introduce strends()
  gpio: register dependency parser for firmware nodes
  gpu: host1x: register dependency parser for firmware nodes
  backlight: Document consumers of backlight nodes
  backlight: register dependency parser for firmware nodes
  USB: EHCI: register dependency parser for firmware nodes
  regulator: register dependency parser for firmware nodes
  pwm: register dependency parser for firmware nodes
  ASoC: tegra: register dependency parser for firmware nodes
  driver-core: probe dependencies before probing

 .../bindings/video/backlight/backlight.txt         |  22 ++++
 drivers/base/dd.c                                  | 139 +++++++++++++++++++++
 drivers/base/property.c                            | 120 ++++++++++++++++++
 drivers/gpio/gpiolib.c                             |  54 ++++++++
 drivers/gpu/host1x/dev.c                           |  26 ++++
 drivers/pwm/core.c                                 |  28 +++++
 drivers/regulator/core.c                           |  27 ++++
 drivers/usb/host/ehci-tegra.c                      |  16 +++
 drivers/video/backlight/backlight.c                |  16 +++
 include/linux/fwnode.h                             |   5 +
 include/linux/property.h                           |  12 ++
 include/linux/string.h                             |  13 ++
 sound/soc/tegra/tegra_max98090.c                   |  42 ++++++-
 13 files changed, 519 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/video/backlight/backlight.txt

-- 
2.4.1


^ permalink raw reply

* [RFC PATCH 15/15] regulator: pwm: properly initialize the ->state field
From: Boris Brezillon @ 2015-07-01  8:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1435738921-25027-1-git-send-email-boris.brezillon@free-electrons.com>

The ->state field is currently initialized to 0, thus referencing the
voltage selector at index 0, which might not reflect the current voltage
value.
If possible, retrieve the current voltage selector from the PWM state, else
return -EINVAL.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/regulator/pwm-regulator.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
index 8159518..4e29717 100644
--- a/drivers/regulator/pwm-regulator.c
+++ b/drivers/regulator/pwm-regulator.c
@@ -31,10 +31,35 @@ struct pwm_voltages {
 	unsigned int dutycycle;
 };
 
+static void pwm_regulator_init_state(struct regulator_dev *rdev)
+{
+	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
+	struct pwm_state pwm_state;
+	unsigned int dutycycle;
+	int i;
+
+	pwm_get_state(drvdata->pwm, &pwm_state);
+
+	if (!pwm_state.period)
+		return;
+
+	dutycycle = (pwm_state.duty_cycle * 100) / pwm_state.period;
+
+	for (i = 0; i < rdev->desc->n_voltages; i++) {
+		if (dutycycle = drvdata->duty_cycle_table[i].dutycycle) {
+			drvdata->state = i;
+			return;
+		}
+	}
+}
+
 static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev)
 {
 	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
 
+	if (drvdata->state < 0)
+		pwm_regulator_init_state(rdev);
+
 	return drvdata->state;
 }
 
@@ -170,6 +195,7 @@ static int pwm_regulator_probe(struct platform_device *pdev)
 	config.of_node = np;
 	config.dev = &pdev->dev;
 	config.driver_data = drvdata;
+	drvdata->state = -EINVAL;
 
 	drvdata->pwm = devm_pwm_get(&pdev->dev, NULL);
 	if (IS_ERR(drvdata->pwm)) {
-- 
1.9.1


^ permalink raw reply related

* [RFC PATCH 14/15] regulator: pwm: implement ->enable(), ->disable() and ->is_enabled methods
From: Boris Brezillon @ 2015-07-01  8:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1435738921-25027-1-git-send-email-boris.brezillon@free-electrons.com>

Implement the ->enable(), ->disable() and ->is_enabled methods and remove
the PWM call in ->set_voltage_sel().
This is particularly important for critical regulators tagged as always-on,
because not claiming the PWM (and its dependencies) might lead to
unpredictable behavior (like a system hang because the PWM clk is only
claimed when the PWM device is enabled).

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/regulator/pwm-regulator.c | 32 ++++++++++++++++++++++++++------
 1 file changed, 26 insertions(+), 6 deletions(-)

diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
index 12b4d9d..8159518 100644
--- a/drivers/regulator/pwm-regulator.c
+++ b/drivers/regulator/pwm-regulator.c
@@ -59,12 +59,6 @@ static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev,
 
 	drvdata->state = selector;
 
-	ret = pwm_enable(drvdata->pwm);
-	if (ret) {
-		dev_err(&rdev->dev, "Failed to enable PWM\n");
-		return ret;
-	}
-
 	return 0;
 }
 
@@ -79,11 +73,37 @@ static int pwm_regulator_list_voltage(struct regulator_dev *rdev,
 	return drvdata->duty_cycle_table[selector].uV;
 }
 
+static int pwm_regulator_enable(struct regulator_dev *dev)
+{
+	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+
+	return pwm_enable(drvdata->pwm);
+}
+
+static int pwm_regulator_disable(struct regulator_dev *dev)
+{
+	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+
+	pwm_disable(drvdata->pwm);
+
+	return 0;
+}
+
+static int pwm_regulator_is_enabled(struct regulator_dev *dev)
+{
+       struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+
+       return pwm_is_enabled(drvdata->pwm);
+}
+
 static struct regulator_ops pwm_regulator_voltage_ops = {
 	.set_voltage_sel = pwm_regulator_set_voltage_sel,
 	.get_voltage_sel = pwm_regulator_get_voltage_sel,
 	.list_voltage    = pwm_regulator_list_voltage,
 	.map_voltage     = regulator_map_voltage_iterate,
+	.enable          = pwm_regulator_enable,
+	.disable         = pwm_regulator_disable,
+	.is_enabled      = pwm_regulator_is_enabled,
 };
 
 static struct regulator_desc pwm_regulator_desc = {
-- 
1.9.1


^ permalink raw reply related

* [RFC PATCH 13/15] pwm: rockchip: add support for atomic update
From: Boris Brezillon @ 2015-07-01  8:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1435738921-25027-1-git-send-email-boris.brezillon@free-electrons.com>

Implement the ->apply() function to add support for atomic update.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/pwm/pwm-rockchip.c | 75 ++++++++++++++++++++++++++++++----------------
 1 file changed, 49 insertions(+), 26 deletions(-)

diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c
index 11e932d..cbe619bf 100644
--- a/drivers/pwm/pwm-rockchip.c
+++ b/drivers/pwm/pwm-rockchip.c
@@ -50,7 +50,8 @@ struct rockchip_pwm_data {
 	const struct pwm_ops *ops;
 
 	void (*set_enable)(struct pwm_chip *chip,
-			   struct pwm_device *pwm, bool enable);
+			   struct pwm_device *pwm, bool enable,
+			   enum pwm_polarity polarity);
 	void (*init)(struct pwm_chip *chip, struct pwm_device *pwm);
 };
 
@@ -60,7 +61,8 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c)
 }
 
 static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip,
-				       struct pwm_device *pwm, bool enable)
+				       struct pwm_device *pwm, bool enable,
+				       enum pwm_polarity polarity)
 {
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 	u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN;
@@ -88,14 +90,15 @@ static void rockchip_pwm_init_v1(struct pwm_chip *chip, struct pwm_device *pwm)
 }
 
 static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip,
-				       struct pwm_device *pwm, bool enable)
+				       struct pwm_device *pwm, bool enable,
+				       enum pwm_polarity polarity)
 {
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 	u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
 			  PWM_CONTINUOUS;
 	u32 val;
 
-	if (pwm_get_polarity(pwm) = PWM_POLARITY_INVERSED)
+	if (polarity = PWM_POLARITY_INVERSED)
 		enable_conf |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE;
 	else
 		enable_conf |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE;
@@ -110,6 +113,26 @@ static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip,
 	writel_relaxed(val, pc->base + pc->data->regs.ctrl);
 }
 
+static void rockchip_pwm_init_v2(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+	u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
+			  PWM_CONTINUOUS;
+	u32 val;
+
+	val = readl(pc->base + pc->data->regs.ctrl);
+
+	if ((val & enable_conf) != enable_conf)
+		return;
+
+	pwm->state.enabled = true;
+
+	enable_conf = PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE;
+
+	if ((val & enable_conf) = enable_conf)
+		pwm->state.polarity = PWM_POLARITY_INVERSED;
+}
+
 static void rockchip_pwm_init_state(struct pwm_chip *chip,
 				    struct pwm_device *pwm)
 {
@@ -146,7 +169,6 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 	unsigned long period, duty;
 	u64 clk_rate, div;
-	int ret;
 
 	clk_rate = clk_get_rate(pc->clk);
 
@@ -163,15 +185,8 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	do_div(div, pc->data->prescaler * NSEC_PER_SEC);
 	duty = div;
 
-	ret = clk_enable(pc->clk);
-	if (ret)
-		return ret;
-
 	writel(period, pc->base + pc->data->regs.period);
 	writel(duty, pc->base + pc->data->regs.duty);
-	writel(0, pc->base + pc->data->regs.cntr);
-
-	clk_disable(pc->clk);
 
 	return 0;
 }
@@ -189,43 +204,51 @@ static int rockchip_pwm_set_polarity(struct pwm_chip *chip,
 	return 0;
 }
 
-static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			      const struct pwm_state *state)
 {
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+	bool enabled = pwm_is_enabled(pwm);
 	int ret;
 
 	ret = clk_enable(pc->clk);
 	if (ret)
 		return ret;
 
-	pc->data->set_enable(chip, pwm, true);
+	if (state->polarity != pwm_get_polarity(pwm) && enabled) {
+		pc->data->set_enable(chip, pwm, false, state->polarity);
+		enabled = false;
+	}
 
-	return 0;
-}
+	ret = rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period);
+	if (ret) {
+		if (enabled != pwm_is_enabled(pwm))
+			pc->data->set_enable(chip, pwm, !enabled,
+					     state->polarity);
 
-static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
-	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+		goto out;
+	}
 
-	pc->data->set_enable(chip, pwm, false);
+	if (state->enabled != enabled)
+		pc->data->set_enable(chip, pwm, state->enabled,
+				     state->polarity);
 
+out:
 	clk_disable(pc->clk);
+
+	return ret;
 }
 
 static const struct pwm_ops rockchip_pwm_ops_v1 = {
 	.init_state = rockchip_pwm_init_state,
-	.config = rockchip_pwm_config,
-	.enable = rockchip_pwm_enable,
-	.disable = rockchip_pwm_disable,
+	.apply = rockchip_pwm_apply,
 	.owner = THIS_MODULE,
 };
 
 static const struct pwm_ops rockchip_pwm_ops_v2 = {
 	.init_state = rockchip_pwm_init_state,
-	.config = rockchip_pwm_config,
+	.apply = rockchip_pwm_apply,
 	.set_polarity = rockchip_pwm_set_polarity,
-	.enable = rockchip_pwm_enable,
-	.disable = rockchip_pwm_disable,
 	.owner = THIS_MODULE,
 };
 
-- 
1.9.1


^ permalink raw reply related

* [RFC PATCH 12/15] pwm: rockchip: add initial state retrieval
From: Boris Brezillon @ 2015-07-01  8:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1435738921-25027-1-git-send-email-boris.brezillon@free-electrons.com>

Implement the ->init_state() function to expose initial state.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/pwm/pwm-rockchip.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c
index 7d9cc90..11e932d 100644
--- a/drivers/pwm/pwm-rockchip.c
+++ b/drivers/pwm/pwm-rockchip.c
@@ -51,6 +51,7 @@ struct rockchip_pwm_data {
 
 	void (*set_enable)(struct pwm_chip *chip,
 			   struct pwm_device *pwm, bool enable);
+	void (*init)(struct pwm_chip *chip, struct pwm_device *pwm);
 };
 
 static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c)
@@ -75,6 +76,17 @@ static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip,
 	writel_relaxed(val, pc->base + pc->data->regs.ctrl);
 }
 
+static void rockchip_pwm_init_v1(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+	u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN;
+	u32 val;
+
+	val = readl(pc->base + pc->data->regs.ctrl);
+	if ((val & enable_conf) = enable_conf)
+		pwm->state.enabled = true;
+}
+
 static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip,
 				       struct pwm_device *pwm, bool enable)
 {
@@ -98,6 +110,36 @@ static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip,
 	writel_relaxed(val, pc->base + pc->data->regs.ctrl);
 }
 
+static void rockchip_pwm_init_state(struct pwm_chip *chip,
+				    struct pwm_device *pwm)
+{
+	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+	unsigned long clk_rate;
+	u64 tmp;
+	int ret;
+
+	ret = clk_enable(pc->clk);
+	if (ret)
+		return;
+
+	clk_rate = clk_get_rate(pc->clk);
+
+	tmp = readl(pc->base + pc->data->regs.period);
+	tmp *= pc->data->prescaler * NSEC_PER_SEC;
+	tmp = do_div(tmp, clk_rate);
+	pwm->state.period = tmp;
+
+	tmp = readl(pc->base + pc->data->regs.duty);
+	tmp *= pc->data->prescaler * NSEC_PER_SEC;
+	tmp = do_div(tmp, clk_rate);
+	pwm->state.duty_cycle = tmp;
+
+	pc->data->init(chip, chip->pwms);
+
+	if (!pwm_is_enabled(pwm))
+		clk_disable(pc->clk);
+}
+
 static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 			       int duty_ns, int period_ns)
 {
@@ -171,6 +213,7 @@ static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 }
 
 static const struct pwm_ops rockchip_pwm_ops_v1 = {
+	.init_state = rockchip_pwm_init_state,
 	.config = rockchip_pwm_config,
 	.enable = rockchip_pwm_enable,
 	.disable = rockchip_pwm_disable,
@@ -178,6 +221,7 @@ static const struct pwm_ops rockchip_pwm_ops_v1 = {
 };
 
 static const struct pwm_ops rockchip_pwm_ops_v2 = {
+	.init_state = rockchip_pwm_init_state,
 	.config = rockchip_pwm_config,
 	.set_polarity = rockchip_pwm_set_polarity,
 	.enable = rockchip_pwm_enable,
@@ -195,6 +239,7 @@ static const struct rockchip_pwm_data pwm_data_v1 = {
 	.prescaler = 2,
 	.ops = &rockchip_pwm_ops_v1,
 	.set_enable = rockchip_pwm_set_enable_v1,
+	.init = rockchip_pwm_init_v1,
 };
 
 static const struct rockchip_pwm_data pwm_data_v2 = {
@@ -207,6 +252,7 @@ static const struct rockchip_pwm_data pwm_data_v2 = {
 	.prescaler = 1,
 	.ops = &rockchip_pwm_ops_v2,
 	.set_enable = rockchip_pwm_set_enable_v2,
+	.init = rockchip_pwm_init_v2,
 };
 
 static const struct rockchip_pwm_data pwm_data_vop = {
@@ -219,6 +265,7 @@ static const struct rockchip_pwm_data pwm_data_vop = {
 	.prescaler = 1,
 	.ops = &rockchip_pwm_ops_v2,
 	.set_enable = rockchip_pwm_set_enable_v2,
+	.init = rockchip_pwm_init_v2,
 };
 
 static const struct of_device_id rockchip_pwm_dt_ids[] = {
-- 
1.9.1


^ permalink raw reply related

* [RFC PATCH 11/15] pwm: add the core infrastructure to allow atomic update
From: Boris Brezillon @ 2015-07-01  8:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1435738921-25027-1-git-send-email-boris.brezillon@free-electrons.com>

Add an ->apply() method to the pwm_ops struct to allow PWM drivers to
implement atomic update.
This method will be prefered over the ->enable(), ->disable() and
->config() methods if available.

Add the pwm_get_state(), pwm_get_default_state() and pwm_apply_state()
functions for PWM users to be able to use the atomic update feature.

Note that the pwm_apply_state() does not guarantee the atomicity of the
update operation, it all depends on the availability and implementation
of the ->apply() method.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/pwm/core.c  | 110 ++++++++++++++++++++++++++++++++++++++++++++++------
 include/linux/pwm.h |  26 +++++++++++++
 2 files changed, 124 insertions(+), 12 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 30631f5..6dafd8e 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -238,8 +238,9 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
 	unsigned int i;
 	int ret;
 
-	if (!chip || !chip->dev || !chip->ops || !chip->ops->config ||
-	    !chip->ops->enable || !chip->ops->disable || !chip->npwm)
+	if (!chip || !chip->dev || !chip->ops || (!chip->ops->apply &&
+	    (!chip->ops->config || !chip->ops->enable ||
+	     !chip->ops->disable)) || !chip->npwm)
 		return -EINVAL;
 
 	mutex_lock(&pwm_lock);
@@ -430,7 +431,17 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	if (!pwm || duty_ns < 0 || period_ns <= 0 || duty_ns > period_ns)
 		return -EINVAL;
 
-	err = pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
+	if (pwm->chip->ops->apply) {
+		struct pwm_state state = pwm->state;
+
+		state.period = period_ns;
+		state.duty_cycle = duty_ns;
+
+		err = pwm->chip->ops->apply(pwm->chip, pwm, &state);
+	} else {
+		err = pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
+	}
+
 	if (err)
 		return err;
 
@@ -455,6 +466,17 @@ int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity)
 	if (!pwm || !pwm->chip->ops)
 		return -EINVAL;
 
+	if (pwm->chip->ops->apply) {
+		struct pwm_state state = pwm->state;
+
+		state.polarity = polarity;
+		err = pwm->chip->ops->apply(pwm->chip, pwm, &state);
+		if (!err)
+			pwm->state.polarity = polarity;
+
+		return err;
+	}
+
 	if (!pwm->chip->ops->set_polarity)
 		return -ENOSYS;
 
@@ -477,17 +499,27 @@ EXPORT_SYMBOL_GPL(pwm_set_polarity);
  */
 int pwm_enable(struct pwm_device *pwm)
 {
-	if (pwm && !pwm_is_enabled(pwm)) {
-		int err;
+	int err;
 
-		err = pwm->chip->ops->enable(pwm->chip, pwm);
-		if (!err)
-			pwm->state.enabled = true;
+	if (!pwm)
+		return -EINVAL;
 
-		return err;
+	if (pwm_is_enabled(pwm))
+		return 0;
+
+	if (pwm->chip->ops->apply) {
+		struct pwm_state state = pwm->state;
+
+		state.enabled = true;
+		err = pwm->chip->ops->apply(pwm->chip, pwm, &state);
+	} else {
+		err = pwm->chip->ops->enable(pwm->chip, pwm);
 	}
 
-	return pwm ? 0 : -EINVAL;
+	if (!err)
+		pwm->state.enabled = true;
+
+	return err;
 }
 EXPORT_SYMBOL_GPL(pwm_enable);
 
@@ -497,13 +529,67 @@ EXPORT_SYMBOL_GPL(pwm_enable);
  */
 void pwm_disable(struct pwm_device *pwm)
 {
-	if (pwm && pwm_is_enabled(pwm)) {
+	if (!pwm || !pwm_is_enabled(pwm))
+		return;
+
+	if (pwm->chip->ops->apply) {
+		struct pwm_state state = pwm->state;
+
+		state.enabled = false;
+		pwm->chip->ops->apply(pwm->chip, pwm, &state);
+	} else {
 		pwm->chip->ops->disable(pwm->chip, pwm);
-		pwm->state.enabled = false;
 	}
+
+	pwm->state.enabled = false;
 }
 EXPORT_SYMBOL_GPL(pwm_disable);
 
+int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
+{
+	int err = 0;
+
+	if (!pwm)
+		return -EINVAL;
+
+	if (!memcmp(state, &pwm->state, sizeof(*state)))
+		return 0;
+
+	if (pwm->chip->ops->apply) {
+		err = pwm->chip->ops->apply(pwm->chip, pwm, state);
+		if (!err)
+			pwm->state = *state;
+	} else {
+		/*
+		 * FIXME: restore the initial state in case of error.
+		 */
+		if (state->polarity != pwm->state.polarity) {
+			pwm_disable(pwm);
+			err = pwm_set_polarity(pwm, state->polarity);
+			if (err)
+				goto out;
+		}
+
+		if (state->period != pwm->state.period ||
+		    state->duty_cycle != pwm->state.duty_cycle) {
+			err = pwm_config(pwm, state->period, state->duty_cycle);
+			if (err)
+				goto out;
+		}
+
+		if (state->enabled != pwm->state.enabled) {
+			if (state->enabled)
+				err = pwm_enable(pwm);
+			else
+				pwm_disable(pwm);
+		}
+	}
+
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(pwm_apply_state);
+
 static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
 {
 	struct pwm_chip *chip;
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index b47244a..7e99679 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -151,6 +151,29 @@ static inline enum pwm_polarity pwm_get_polarity(const struct pwm_device *pwm)
 	return pwm ? pwm->state.polarity : PWM_POLARITY_NORMAL;
 }
 
+/*
+ * pwm_apply_state - apply a new state to the PWM device
+ */
+int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state);
+
+/*
+ * pwm_get_state - retrieve the current PWM state
+ */
+static inline void pwm_get_state(struct pwm_device *pwm,
+				 struct pwm_state *state)
+{
+	*state = pwm->state;
+}
+
+/*
+ * pwm_get_default_state - retrieve the default PWM state
+ */
+static inline void pwm_get_default_state(struct pwm_device *pwm,
+					 struct pwm_state *state)
+{
+	*state = pwm->default_state;
+}
+
 /**
  * struct pwm_ops - PWM controller operations
  * @request: optional hook for requesting a PWM
@@ -177,6 +200,9 @@ struct pwm_ops {
 					  struct pwm_device *pwm);
 	void			(*disable)(struct pwm_chip *chip,
 					   struct pwm_device *pwm);
+	int			(*apply)(struct pwm_chip *chip,
+					 struct pwm_device *pwm,
+					 const struct pwm_state *state);
 	void			(*init_state)(struct pwm_chip *chip,
 					      struct pwm_device *pwm);
 #ifdef CONFIG_DEBUG_FS
-- 
1.9.1


^ permalink raw reply related

* [RFC PATCH 10/15] pwm: add the PWM initial state retrieval infra
From: Boris Brezillon @ 2015-07-01  8:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1435738921-25027-1-git-send-email-boris.brezillon@free-electrons.com>

Add a ->init_state() function to the pwm_ops struct to let PWM drivers
initialize the PWM state attached to a PWM device.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/pwm/core.c  | 3 +++
 include/linux/pwm.h | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 3e830ce..30631f5 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -264,6 +264,9 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
 		pwm->hwpwm = i;
 		pwm_set_default_polarity(pwm, polarity);
 
+		if (chip->ops->init_state)
+			chip->ops->init_state(chip, pwm);
+
 		radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
 	}
 
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 0f36a06..b47244a 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -177,6 +177,8 @@ struct pwm_ops {
 					  struct pwm_device *pwm);
 	void			(*disable)(struct pwm_chip *chip,
 					   struct pwm_device *pwm);
+	void			(*init_state)(struct pwm_chip *chip,
+					      struct pwm_device *pwm);
 #ifdef CONFIG_DEBUG_FS
 	void			(*dbg_show)(struct pwm_chip *chip,
 					    struct seq_file *s);
-- 
1.9.1


^ permalink raw reply related

* [RFC PATCH 09/15] pwm: declare a default PWM state
From: Boris Brezillon @ 2015-07-01  8:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1435738921-25027-1-git-send-email-boris.brezillon@free-electrons.com>

Prepare the addition of the PWM initial state retrieval by adding a default
state where all the parameters retrieved from DT, platform data or
statically forced by the hardware will be stored.
Once done we will be able to store the initial state in the ->state field
without risking to loose the default parameters.

Update the pwm_set/get_default_xxx helpers accordingly.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 include/linux/pwm.h | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index fd3e0f0..0f36a06 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -94,6 +94,7 @@ struct pwm_device {
 	void			*chip_data;
 
 	struct pwm_state	state;
+	struct pwm_state	default_state;
 };
 
 static inline bool pwm_is_enabled(const struct pwm_device *pwm)
@@ -109,7 +110,8 @@ static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
 
 static inline void pwm_set_default_period(struct pwm_device *pwm, unsigned int period)
 {
-	pwm_set_period(pwm, period);
+	if (pwm)
+		pwm->default_state.period = period;
 }
 
 static inline unsigned int pwm_get_period(const struct pwm_device *pwm)
@@ -119,7 +121,7 @@ static inline unsigned int pwm_get_period(const struct pwm_device *pwm)
 
 static inline unsigned int pwm_get_default_period(const struct pwm_device *pwm)
 {
-	return pwm_get_period(pwm);
+	return pwm ? pwm->default_state.period : 0;
 }
 
 static inline void pwm_set_duty_cycle(struct pwm_device *pwm, unsigned int duty)
@@ -140,7 +142,8 @@ int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity);
 
 static inline void pwm_set_default_polarity(struct pwm_device *pwm, enum pwm_polarity polarity)
 {
-	pwm_set_polarity(pwm, polarity);
+	if (pwm)
+		pwm->default_state.polarity = polarity;
 }
 
 static inline enum pwm_polarity pwm_get_polarity(const struct pwm_device *pwm)
-- 
1.9.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox