diff mbox

[RFC,10/12] arm/tegra: Add device tree support to pinmux driver

Message ID 1313189697-21287-11-git-send-email-swarren@nvidia.com (mailing list archive)
State New, archived
Headers show

Commit Message

Stephen Warren Aug. 12, 2011, 10:54 p.m. UTC
Signed-off-by: Stephen Warren <swarren@nvidia.com>
---
 arch/arm/mach-tegra/pinmux.c |  115 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 115 insertions(+), 0 deletions(-)

Comments

Jamie Iles Aug. 13, 2011, 10:43 a.m. UTC | #1
Hi Stephen,

On Fri, Aug 12, 2011 at 04:54:55PM -0600, Stephen Warren wrote:
> Signed-off-by: Stephen Warren <swarren@nvidia.com>
> ---
>  arch/arm/mach-tegra/pinmux.c |  115 ++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 115 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c
> index 05fa1a3..33246c2 100644
> --- a/arch/arm/mach-tegra/pinmux.c
> +++ b/arch/arm/mach-tegra/pinmux.c
> @@ -20,6 +20,7 @@
>  #include <linux/errno.h>
>  #include <linux/spinlock.h>
>  #include <linux/io.h>
> +#include <linux/of.h>
>  #include <linux/platform_device.h>
>  
>  #include <mach/iomap.h>
> @@ -147,6 +148,41 @@ static const char *func_name(enum tegra_mux_func func)
>  	return tegra_mux_names[func];
>  }
>  
> +#ifdef CONFIG_OF
> +static int func_enum(const char *name, enum tegra_mux_func *func_out)
> +{
> +	int func;
> +
> +	if (!strcmp(name, "RSVD1")) {
> +		*func_out = TEGRA_MUX_RSVD1;
> +		return 0;
> +	}
> +	if (!strcmp(name, "RSVD2")) {
> +		*func_out = TEGRA_MUX_RSVD2;
> +		return 0;
> +	}
> +	if (!strcmp(name, "RSVD3")) {
> +		*func_out = TEGRA_MUX_RSVD3;
> +		return 0;
> +	}
> +	if (!strcmp(name, "RSVD4")) {
> +		*func_out = TEGRA_MUX_RSVD4;
> +		return 0;
> +	}
> +	if (!strcmp(name, "NONE")) {
> +		*func_out = TEGRA_MUX_NONE;
> +		return 0;
> +	}
> +
> +	for (func = 0; func < TEGRA_MAX_MUX; func++)
> +		if (!strcmp(name, tegra_mux_names[func])) {
> +			*func_out = func;
> +			return 0;
> +		}
> +
> +	return -EINVAL;
> +}
> +#endif
>  
>  static const char *tri_name(unsigned long val)
>  {
> @@ -666,15 +702,94 @@ void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *co
>  	}
>  }
>  
> +#ifdef CONFIG_OF
> +static void __init tegra_pinmux_probe_dt(struct platform_device *pdev)
> +{
> +	int pg;
> +
> +	for (pg = 0; pg < TEGRA_MAX_PINGROUP; pg++) {
> +		const char *pg_name = pingroup_name(pg);
> +		struct tegra_pingroup_config config;
> +		struct device_node *pg_node;
> +		int ret;
> +		const char *s;
> +
> +		pg_node = of_find_child_node_by_name(pdev->dev.of_node,
> +						     pg_name);
> +		if (pg_node == NULL)
> +			continue;

Rather than iterating over all of the mux names in the pinmux driver and 
searching for a matching DT node, could you not do it the other way 
round?  So do an for_each_child_of_node() on the pinmux node then find 
the matching pingroup keyed by the node name?  This would eliminate 
of_find_child_node_by_name().  You could also catch invalid 
configurations for non-existent pins this way.

Jamie
Jamie Iles Aug. 13, 2011, 10:48 a.m. UTC | #2
On Sat, Aug 13, 2011 at 11:43:23AM +0100, Jamie Iles wrote:
> Hi Stephen,
> 
> On Fri, Aug 12, 2011 at 04:54:55PM -0600, Stephen Warren wrote:
> > Signed-off-by: Stephen Warren <swarren@nvidia.com>
> > ---
> >  arch/arm/mach-tegra/pinmux.c |  115 ++++++++++++++++++++++++++++++++++++++++++
> >  1 files changed, 115 insertions(+), 0 deletions(-)
> > 
> > diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c
> > index 05fa1a3..33246c2 100644
> > --- a/arch/arm/mach-tegra/pinmux.c
> > +++ b/arch/arm/mach-tegra/pinmux.c
> > @@ -20,6 +20,7 @@
> >  #include <linux/errno.h>
> >  #include <linux/spinlock.h>
> >  #include <linux/io.h>
> > +#include <linux/of.h>
> >  #include <linux/platform_device.h>
> >  
> >  #include <mach/iomap.h>
> > @@ -147,6 +148,41 @@ static const char *func_name(enum tegra_mux_func func)
> >  	return tegra_mux_names[func];
> >  }
> >  
> > +#ifdef CONFIG_OF
> > +static int func_enum(const char *name, enum tegra_mux_func *func_out)
> > +{
> > +	int func;
> > +
> > +	if (!strcmp(name, "RSVD1")) {
> > +		*func_out = TEGRA_MUX_RSVD1;
> > +		return 0;
> > +	}
> > +	if (!strcmp(name, "RSVD2")) {
> > +		*func_out = TEGRA_MUX_RSVD2;
> > +		return 0;
> > +	}
> > +	if (!strcmp(name, "RSVD3")) {
> > +		*func_out = TEGRA_MUX_RSVD3;
> > +		return 0;
> > +	}
> > +	if (!strcmp(name, "RSVD4")) {
> > +		*func_out = TEGRA_MUX_RSVD4;
> > +		return 0;
> > +	}
> > +	if (!strcmp(name, "NONE")) {
> > +		*func_out = TEGRA_MUX_NONE;
> > +		return 0;
> > +	}
> > +
> > +	for (func = 0; func < TEGRA_MAX_MUX; func++)
> > +		if (!strcmp(name, tegra_mux_names[func])) {
> > +			*func_out = func;
> > +			return 0;
> > +		}
> > +
> > +	return -EINVAL;
> > +}
> > +#endif
> >  
> >  static const char *tri_name(unsigned long val)
> >  {
> > @@ -666,15 +702,94 @@ void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *co
> >  	}
> >  }
> >  
> > +#ifdef CONFIG_OF
> > +static void __init tegra_pinmux_probe_dt(struct platform_device *pdev)
> > +{
> > +	int pg;
> > +
> > +	for (pg = 0; pg < TEGRA_MAX_PINGROUP; pg++) {
> > +		const char *pg_name = pingroup_name(pg);
> > +		struct tegra_pingroup_config config;
> > +		struct device_node *pg_node;
> > +		int ret;
> > +		const char *s;
> > +
> > +		pg_node = of_find_child_node_by_name(pdev->dev.of_node,
> > +						     pg_name);
> > +		if (pg_node == NULL)
> > +			continue;
> 
> Rather than iterating over all of the mux names in the pinmux driver and 
> searching for a matching DT node, could you not do it the other way 
> round?  So do an for_each_child_of_node() on the pinmux node then find 
> the matching pingroup keyed by the node name?  This would eliminate 
> of_find_child_node_by_name().  You could also catch invalid 
> configurations for non-existent pins this way.

I just re-read your introduction email and saw you've already discussed 
this!  Would this require an explicit pin name property though or could 
you just key off of the pg_node->name?

Jamie
Stephen Warren Aug. 15, 2011, 4:09 p.m. UTC | #3
Jamie Iles wrote at Saturday, August 13, 2011 4:49 AM:
> On Sat, Aug 13, 2011 at 11:43:23AM +0100, Jamie Iles wrote:
> > Hi Stephen,
> >
> > On Fri, Aug 12, 2011 at 04:54:55PM -0600, Stephen Warren wrote:
...
> > > diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c
...
> > > +#ifdef CONFIG_OF
> > > +static void __init tegra_pinmux_probe_dt(struct platform_device *pdev)
> > > +{
> > > +	int pg;
> > > +
> > > +	for (pg = 0; pg < TEGRA_MAX_PINGROUP; pg++) {
> > > +		const char *pg_name = pingroup_name(pg);
> > > +		struct tegra_pingroup_config config;
> > > +		struct device_node *pg_node;
> > > +		int ret;
> > > +		const char *s;
> > > +
> > > +		pg_node = of_find_child_node_by_name(pdev->dev.of_node,
> > > +						     pg_name);
> > > +		if (pg_node == NULL)
> > > +			continue;
> >
> > Rather than iterating over all of the mux names in the pinmux driver and
> > searching for a matching DT node, could you not do it the other way
> > round?  So do an for_each_child_of_node() on the pinmux node then find
> > the matching pingroup keyed by the node name?  This would eliminate
> > of_find_child_node_by_name().  You could also catch invalid
> > configurations for non-existent pins this way.
> 
> I just re-read your introduction email and saw you've already discussed
> this!  Would this require an explicit pin name property though or could
> you just key off of the pg_node->name?

No, I think pg_node->name will work out fine; it's what of_find_child_node_by_name
is using anyway.
Jamie Iles Aug. 15, 2011, 8:07 p.m. UTC | #4
Hi Stephen,

On Fri, Aug 12, 2011 at 04:54:55PM -0600, Stephen Warren wrote:
> Signed-off-by: Stephen Warren <swarren@nvidia.com>
> ---
>  arch/arm/mach-tegra/pinmux.c |  115 ++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 115 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c
> index 05fa1a3..33246c2 100644
> --- a/arch/arm/mach-tegra/pinmux.c
> +++ b/arch/arm/mach-tegra/pinmux.c
> @@ -20,6 +20,7 @@
>  #include <linux/errno.h>
>  #include <linux/spinlock.h>
>  #include <linux/io.h>
> +#include <linux/of.h>
>  #include <linux/platform_device.h>
>  
>  #include <mach/iomap.h>
> @@ -147,6 +148,41 @@ static const char *func_name(enum tegra_mux_func func)
>  	return tegra_mux_names[func];
>  }
>  
[...]
>  
>  static const char *tri_name(unsigned long val)
>  {
> @@ -666,15 +702,94 @@ void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *co
>  	}
>  }
>  
> +#ifdef CONFIG_OF
> +static void __init tegra_pinmux_probe_dt(struct platform_device *pdev)
> +{
> +	int pg;
> +
> +	for (pg = 0; pg < TEGRA_MAX_PINGROUP; pg++) {
> +		const char *pg_name = pingroup_name(pg);
> +		struct tegra_pingroup_config config;
> +		struct device_node *pg_node;
> +		int ret;
> +		const char *s;
> +
> +		pg_node = of_find_child_node_by_name(pdev->dev.of_node,
> +						     pg_name);
> +		if (pg_node == NULL)
> +			continue;
> +
> +		config.pingroup = pg;
> +
> +		ret = of_property_read_string(pg_node, "nvidia,function", &s);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev,
> +				"%s: Missing property nvidia,function\n",
> +				pg_name);
> +			continue;
> +		}
> +		ret = func_enum(s, &config.func);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev,
> +				"%s: Invalid nvidia,function value %s\n",
> +				pg_name, s);
> +			continue;
> +		}
> +
> +		ret = of_property_read_string(pg_node, "nvidia,pull", &s);
> +		if (ret >= 0) {
> +			if (!strcmp(s, "up"))
> +				config.pupd = TEGRA_PUPD_PULL_UP;
> +			else if (!strcmp(s, "down"))
> +				config.pupd = TEGRA_PUPD_PULL_DOWN;
> +			else if (!strcmp(s, "normal"))
> +				config.pupd = TEGRA_PUPD_NORMAL;
> +			else {
> +				dev_err(&pdev->dev,
> +					"%s: Invalid nvidia,pull value %s\n",
> +					pg_name, s);
> +				continue;
> +			}
> +		} else
> +			config.pupd = TEGRA_PUPD_NORMAL;
> +
> +		if (of_find_property(pg_node, "nvidia,tristate", NULL))
> +			config.tristate = TEGRA_TRI_TRISTATE;
> +		else
> +			config.tristate = TEGRA_TRI_NORMAL;
> +
> +		dev_err(&pdev->dev, "%s: func %d (%s) pull %d tri %d\n",
> +			pg_name, config.func, func_name(config.func),
> +			config.pupd, config.tristate);
> +
> +		tegra_pinmux_config_pingroup(&config);
> +
> +		of_node_put(pg_node);
> +	}
> +}

I need to implement DT muxing configuration for my platform, and I believe 
that what you have here would work fine for me too, and to avoid duplicating 
the same thing, I wonder if this could be a little more generic.

So if the platform specific pinmux driver called the pinmux parser with a 
callback for a pingroup configuration function then this wouldn't need the 
nvidia specific properties.  I'd envisage the setup callback to be something 
like:

	int pingroup_configure(const char *name, unsigned long flags);

where the flags would be a bitmask of properties, so:

	PINMUX_F_TRISTATE
	PINMUX_F_PUPD
	etc

which would map to pinmux,tristate properties etc.  The tegra (or 
picoxcell...) specific driver would then map any regs and setup the 
pinmux tables and call the parser loop with the correct callback.  This 
would require looping over the child nodes as we've discussed before, 
and the decoding of the func_enum in the nvidia driver, but I think 
that's okay.

Jamie
Jamie Iles Aug. 15, 2011, 8:36 p.m. UTC | #5
On Mon, Aug 15, 2011 at 09:07:16PM +0100, Jamie Iles wrote:
> Hi Stephen,
> 
> On Fri, Aug 12, 2011 at 04:54:55PM -0600, Stephen Warren wrote:
> > Signed-off-by: Stephen Warren <swarren@nvidia.com>
> > ---
> >  arch/arm/mach-tegra/pinmux.c |  115 ++++++++++++++++++++++++++++++++++++++++++
> >  1 files changed, 115 insertions(+), 0 deletions(-)
> > 
> > diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c
> > index 05fa1a3..33246c2 100644
> > --- a/arch/arm/mach-tegra/pinmux.c
> > +++ b/arch/arm/mach-tegra/pinmux.c
> > @@ -20,6 +20,7 @@
> >  #include <linux/errno.h>
> >  #include <linux/spinlock.h>
> >  #include <linux/io.h>
> > +#include <linux/of.h>
> >  #include <linux/platform_device.h>
> >  
> >  #include <mach/iomap.h>
> > @@ -147,6 +148,41 @@ static const char *func_name(enum tegra_mux_func func)
> >  	return tegra_mux_names[func];
> >  }
> >  
> [...]
> >  
> >  static const char *tri_name(unsigned long val)
> >  {
> > @@ -666,15 +702,94 @@ void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *co
> >  	}
> >  }
> >  
> > +#ifdef CONFIG_OF
> > +static void __init tegra_pinmux_probe_dt(struct platform_device *pdev)
> > +{
> > +	int pg;
> > +
> > +	for (pg = 0; pg < TEGRA_MAX_PINGROUP; pg++) {
> > +		const char *pg_name = pingroup_name(pg);
> > +		struct tegra_pingroup_config config;
> > +		struct device_node *pg_node;
> > +		int ret;
> > +		const char *s;
> > +
> > +		pg_node = of_find_child_node_by_name(pdev->dev.of_node,
> > +						     pg_name);
> > +		if (pg_node == NULL)
> > +			continue;
> > +
> > +		config.pingroup = pg;
> > +
> > +		ret = of_property_read_string(pg_node, "nvidia,function", &s);
> > +		if (ret < 0) {
> > +			dev_err(&pdev->dev,
> > +				"%s: Missing property nvidia,function\n",
> > +				pg_name);
> > +			continue;
> > +		}
> > +		ret = func_enum(s, &config.func);
> > +		if (ret < 0) {
> > +			dev_err(&pdev->dev,
> > +				"%s: Invalid nvidia,function value %s\n",
> > +				pg_name, s);
> > +			continue;
> > +		}
> > +
> > +		ret = of_property_read_string(pg_node, "nvidia,pull", &s);
> > +		if (ret >= 0) {
> > +			if (!strcmp(s, "up"))
> > +				config.pupd = TEGRA_PUPD_PULL_UP;
> > +			else if (!strcmp(s, "down"))
> > +				config.pupd = TEGRA_PUPD_PULL_DOWN;
> > +			else if (!strcmp(s, "normal"))
> > +				config.pupd = TEGRA_PUPD_NORMAL;
> > +			else {
> > +				dev_err(&pdev->dev,
> > +					"%s: Invalid nvidia,pull value %s\n",
> > +					pg_name, s);
> > +				continue;
> > +			}
> > +		} else
> > +			config.pupd = TEGRA_PUPD_NORMAL;
> > +
> > +		if (of_find_property(pg_node, "nvidia,tristate", NULL))
> > +			config.tristate = TEGRA_TRI_TRISTATE;
> > +		else
> > +			config.tristate = TEGRA_TRI_NORMAL;
> > +
> > +		dev_err(&pdev->dev, "%s: func %d (%s) pull %d tri %d\n",
> > +			pg_name, config.func, func_name(config.func),
> > +			config.pupd, config.tristate);
> > +
> > +		tegra_pinmux_config_pingroup(&config);
> > +
> > +		of_node_put(pg_node);
> > +	}
> > +}
> 
> I need to implement DT muxing configuration for my platform, and I believe 
> that what you have here would work fine for me too, and to avoid duplicating 
> the same thing, I wonder if this could be a little more generic.
> 
> So if the platform specific pinmux driver called the pinmux parser with a 
> callback for a pingroup configuration function then this wouldn't need the 
> nvidia specific properties.  I'd envisage the setup callback to be something 
> like:
> 
> 	int pingroup_configure(const char *name, unsigned long flags);

and it if this took the device_node too then the platform specific bits could 
handle more esoteric properties if required.  I'll have a go at prototyping 
this tomorrow unless there are any obvious reasons that this is a stupid idea!

Jamie
Stephen Warren Aug. 15, 2011, 8:44 p.m. UTC | #6
Jamie Iles wrote at Monday, August 15, 2011 2:36 PM:
> On Mon, Aug 15, 2011 at 09:07:16PM +0100, Jamie Iles wrote:
> > Hi Stephen,
> >
> > On Fri, Aug 12, 2011 at 04:54:55PM -0600, Stephen Warren wrote:
> > > Signed-off-by: Stephen Warren <swarren@nvidia.com>
...
> > > diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c
...
> > > +static void __init tegra_pinmux_probe_dt(struct platform_device *pdev)
> > > +{
> > > +	int pg;
> > > +
> > > +	for (pg = 0; pg < TEGRA_MAX_PINGROUP; pg++) {
> > > +		const char *pg_name = pingroup_name(pg);
> > > +		struct tegra_pingroup_config config;
> > > +		struct device_node *pg_node;
> > > +		int ret;
> > > +		const char *s;
> > > +
> > > +		pg_node = of_find_child_node_by_name(pdev->dev.of_node,
> > > +						     pg_name);
> > > +		if (pg_node == NULL)
> > > +			continue;
> > > +
> > > +		config.pingroup = pg;
> > > +
> > > +		ret = of_property_read_string(pg_node, "nvidia,function", &s);
...
> > > +		ret = of_property_read_string(pg_node, "nvidia,pull", &s);
...
> > > +		if (of_find_property(pg_node, "nvidia,tristate", NULL))
...
> > > +		tegra_pinmux_config_pingroup(&config);
> > > +
> > > +		of_node_put(pg_node);
> > > +	}
> > > +}
> >
> > I need to implement DT muxing configuration for my platform, and I believe
> > that what you have here would work fine for me too, and to avoid duplicating
> > the same thing, I wonder if this could be a little more generic.
> >
> > So if the platform specific pinmux driver called the pinmux parser with a
> > callback for a pingroup configuration function then this wouldn't need the
> > nvidia specific properties.  I'd envisage the setup callback to be something
> > like:
> >
> > 	int pingroup_configure(const char *name, unsigned long flags);
> 
> and it if this took the device_node too then the platform specific bits could
> handle more esoteric properties if required.  I'll have a go at prototyping
> this tomorrow unless there are any obvious reasons that this is a stupid idea!

I expect some of the code could be shared.

The only worry I have is whether some SoCs don't configure things like
pinmux function in the same place as pad function (pullup/down, tristate),
and hence whether a generic binding is generally applicable. I suppose the
code could always ignore unused properties.

I wonder how much of this is relevant to Linus W's pinctrl API?

Note that in the updated patch series I just posted, I reworked the binding
a little; Tegra has two sets of pin-groups, one configuring muxing, pullup/
down, and tri-state, and the other configuring various driver strength/
rate properties. Hence, the tree is now e.g.:

	pinmux: pinmux@70000000 {
		compatible = "nvidia,tegra20-pinmux";
		reg = < 0x70000000 0xc00 >;
		nvidia,mux-groups {
			cdev1 {
				nvidia,function = "plla_out";
			};
			cdev2 {
				nvidia,function = "pllp_out4";
				nvidia,pull-down;
				nvidia,tristate;
			};
		};
		nvidia,drive-groups {
			sdio1 {
				nvidia,schmitt;
				nvidia,drive-power = <1>;
				nvidia,pull-down-strength = <31>;
				nvidia,pull-up-strength = <31>;
				nvidia,slew-rate-rising = <3>;
				nvidia,slew-rate-falling = <3>;
			};
		};
	};

But it's probably still reasonably easy to make the parser for the mux-groups
node generic. Perhaps it makes sense for all SoCs to have a "mux-settings"
node, even if they don't have any other custom nodes?
Jamie Iles Aug. 15, 2011, 8:50 p.m. UTC | #7
On Mon, Aug 15, 2011 at 01:44:53PM -0700, Stephen Warren wrote:
> Jamie Iles wrote at Monday, August 15, 2011 2:36 PM:
> > On Mon, Aug 15, 2011 at 09:07:16PM +0100, Jamie Iles wrote:
> > > Hi Stephen,
> > >
> > > On Fri, Aug 12, 2011 at 04:54:55PM -0600, Stephen Warren wrote:
> > > > Signed-off-by: Stephen Warren <swarren@nvidia.com>
> ...
> > > > diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c
> ...
> > > > +static void __init tegra_pinmux_probe_dt(struct platform_device *pdev)
> > > > +{
> > > > +	int pg;
> > > > +
> > > > +	for (pg = 0; pg < TEGRA_MAX_PINGROUP; pg++) {
> > > > +		const char *pg_name = pingroup_name(pg);
> > > > +		struct tegra_pingroup_config config;
> > > > +		struct device_node *pg_node;
> > > > +		int ret;
> > > > +		const char *s;
> > > > +
> > > > +		pg_node = of_find_child_node_by_name(pdev->dev.of_node,
> > > > +						     pg_name);
> > > > +		if (pg_node == NULL)
> > > > +			continue;
> > > > +
> > > > +		config.pingroup = pg;
> > > > +
> > > > +		ret = of_property_read_string(pg_node, "nvidia,function", &s);
> ...
> > > > +		ret = of_property_read_string(pg_node, "nvidia,pull", &s);
> ...
> > > > +		if (of_find_property(pg_node, "nvidia,tristate", NULL))
> ...
> > > > +		tegra_pinmux_config_pingroup(&config);
> > > > +
> > > > +		of_node_put(pg_node);
> > > > +	}
> > > > +}
> > >
> > > I need to implement DT muxing configuration for my platform, and I believe
> > > that what you have here would work fine for me too, and to avoid duplicating
> > > the same thing, I wonder if this could be a little more generic.
> > >
> > > So if the platform specific pinmux driver called the pinmux parser with a
> > > callback for a pingroup configuration function then this wouldn't need the
> > > nvidia specific properties.  I'd envisage the setup callback to be something
> > > like:
> > >
> > > 	int pingroup_configure(const char *name, unsigned long flags);
> > 
> > and it if this took the device_node too then the platform specific bits could
> > handle more esoteric properties if required.  I'll have a go at prototyping
> > this tomorrow unless there are any obvious reasons that this is a stupid idea!
> 
> I expect some of the code could be shared.
> 
> The only worry I have is whether some SoCs don't configure things like
> pinmux function in the same place as pad function (pullup/down, tristate),
> and hence whether a generic binding is generally applicable. I suppose the
> code could always ignore unused properties.

Yes, well our hardware doesn't support any of these features other than 
setting the function so in the picoxcell backend I'd just WARN_ON() invalid 
flags settings.

> I wonder how much of this is relevant to Linus W's pinctrl API?

Hmm, not sure on that one, it's been a while since I've looked at Linus' 
patches.

> Note that in the updated patch series I just posted, I reworked the binding
> a little; Tegra has two sets of pin-groups, one configuring muxing, pullup/
> down, and tri-state, and the other configuring various driver strength/
> rate properties. Hence, the tree is now e.g.:
> 
> 	pinmux: pinmux@70000000 {
> 		compatible = "nvidia,tegra20-pinmux";
> 		reg = < 0x70000000 0xc00 >;
> 		nvidia,mux-groups {
> 			cdev1 {
> 				nvidia,function = "plla_out";
> 			};
> 			cdev2 {
> 				nvidia,function = "pllp_out4";
> 				nvidia,pull-down;
> 				nvidia,tristate;
> 			};
> 		};
> 		nvidia,drive-groups {
> 			sdio1 {
> 				nvidia,schmitt;
> 				nvidia,drive-power = <1>;
> 				nvidia,pull-down-strength = <31>;
> 				nvidia,pull-up-strength = <31>;
> 				nvidia,slew-rate-rising = <3>;
> 				nvidia,slew-rate-falling = <3>;
> 			};
> 		};
> 	};
> 
> But it's probably still reasonably easy to make the parser for the mux-groups
> node generic. Perhaps it makes sense for all SoCs to have a "mux-settings"
> node, even if they don't have any other custom nodes?

You have a much more complex chip than I do!  I don't know if *all* SoC's have 
to have the same muxing binding, but it feels that this one should cover a lot 
of the most common bases.

Jamie
diff mbox

Patch

diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c
index 05fa1a3..33246c2 100644
--- a/arch/arm/mach-tegra/pinmux.c
+++ b/arch/arm/mach-tegra/pinmux.c
@@ -20,6 +20,7 @@ 
 #include <linux/errno.h>
 #include <linux/spinlock.h>
 #include <linux/io.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 
 #include <mach/iomap.h>
@@ -147,6 +148,41 @@  static const char *func_name(enum tegra_mux_func func)
 	return tegra_mux_names[func];
 }
 
+#ifdef CONFIG_OF
+static int func_enum(const char *name, enum tegra_mux_func *func_out)
+{
+	int func;
+
+	if (!strcmp(name, "RSVD1")) {
+		*func_out = TEGRA_MUX_RSVD1;
+		return 0;
+	}
+	if (!strcmp(name, "RSVD2")) {
+		*func_out = TEGRA_MUX_RSVD2;
+		return 0;
+	}
+	if (!strcmp(name, "RSVD3")) {
+		*func_out = TEGRA_MUX_RSVD3;
+		return 0;
+	}
+	if (!strcmp(name, "RSVD4")) {
+		*func_out = TEGRA_MUX_RSVD4;
+		return 0;
+	}
+	if (!strcmp(name, "NONE")) {
+		*func_out = TEGRA_MUX_NONE;
+		return 0;
+	}
+
+	for (func = 0; func < TEGRA_MAX_MUX; func++)
+		if (!strcmp(name, tegra_mux_names[func])) {
+			*func_out = func;
+			return 0;
+		}
+
+	return -EINVAL;
+}
+#endif
 
 static const char *tri_name(unsigned long val)
 {
@@ -666,15 +702,94 @@  void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *co
 	}
 }
 
+#ifdef CONFIG_OF
+static void __init tegra_pinmux_probe_dt(struct platform_device *pdev)
+{
+	int pg;
+
+	for (pg = 0; pg < TEGRA_MAX_PINGROUP; pg++) {
+		const char *pg_name = pingroup_name(pg);
+		struct tegra_pingroup_config config;
+		struct device_node *pg_node;
+		int ret;
+		const char *s;
+
+		pg_node = of_find_child_node_by_name(pdev->dev.of_node,
+						     pg_name);
+		if (pg_node == NULL)
+			continue;
+
+		config.pingroup = pg;
+
+		ret = of_property_read_string(pg_node, "nvidia,function", &s);
+		if (ret < 0) {
+			dev_err(&pdev->dev,
+				"%s: Missing property nvidia,function\n",
+				pg_name);
+			continue;
+		}
+		ret = func_enum(s, &config.func);
+		if (ret < 0) {
+			dev_err(&pdev->dev,
+				"%s: Invalid nvidia,function value %s\n",
+				pg_name, s);
+			continue;
+		}
+
+		ret = of_property_read_string(pg_node, "nvidia,pull", &s);
+		if (ret >= 0) {
+			if (!strcmp(s, "up"))
+				config.pupd = TEGRA_PUPD_PULL_UP;
+			else if (!strcmp(s, "down"))
+				config.pupd = TEGRA_PUPD_PULL_DOWN;
+			else if (!strcmp(s, "normal"))
+				config.pupd = TEGRA_PUPD_NORMAL;
+			else {
+				dev_err(&pdev->dev,
+					"%s: Invalid nvidia,pull value %s\n",
+					pg_name, s);
+				continue;
+			}
+		} else
+			config.pupd = TEGRA_PUPD_NORMAL;
+
+		if (of_find_property(pg_node, "nvidia,tristate", NULL))
+			config.tristate = TEGRA_TRI_TRISTATE;
+		else
+			config.tristate = TEGRA_TRI_NORMAL;
+
+		dev_err(&pdev->dev, "%s: func %d (%s) pull %d tri %d\n",
+			pg_name, config.func, func_name(config.func),
+			config.pupd, config.tristate);
+
+		tegra_pinmux_config_pingroup(&config);
+
+		of_node_put(pg_node);
+	}
+}
+#else
+static inline void __init tegra_pinmux_probe_dt(struct platform_device *pdev)
+{
+}
+#endif
+
 static int __init tegra_pinmux_probe(struct platform_device *pdev)
 {
+	tegra_pinmux_probe_dt(pdev);
+
 	return 0;
 }
 
+static struct of_device_id tegra_pinmux_of_match[] __devinitdata = {
+	{ .compatible = "nvidia,tegra20-pinmux", },
+	{ },
+};
+
 static struct platform_driver tegra_pinmux_driver = {
 	.driver		= {
 		.name	= "tegra-pinmux",
 		.owner	= THIS_MODULE,
+		.of_match_table = tegra_pinmux_of_match,
 	},
 	.probe		= tegra_pinmux_probe,
 };