diff mbox

[v5,05/10] pinctrl: single: support pinconf generic

Message ID 1352968600-15345-6-git-send-email-haojian.zhuang@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Haojian Zhuang Nov. 15, 2012, 8:36 a.m. UTC
Add pinconf generic support with POWER SOURCE, BIAS PULL.

Signed-off-by: Haojian Zhuang <haojian.zhuang@gmail.com>
---
 drivers/pinctrl/Kconfig          |    1 +
 drivers/pinctrl/pinctrl-single.c |  322 +++++++++++++++++++++++++++++++++++---
 2 files changed, 305 insertions(+), 18 deletions(-)

Comments

Tony Lindgren Nov. 17, 2012, 12:43 a.m. UTC | #1
Hi,

>  static int pcs_pinconf_get(struct pinctrl_dev *pctldev,
>  				unsigned pin, unsigned long *config)
>  {
> -	return -ENOTSUPP;
> +	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
> +	enum pin_config_param config_param = pinconf_to_config_param(*config);
> +	union pcs_pinconf_argument_t arg;
> +	u32 offset;
> +	unsigned data;
> +	int ret;
> +
> +	if (!pcs->conf.nconfs)
> +		return -ENOTSUPP;
> +
> +	ret = pcs_pinconf_get_config(pctldev, pin, config_param);
> +	if (ret < 0)
> +		return ret;
> +
> +	offset = pin * (pcs->width / BITS_PER_BYTE);
> +	data = pcs->read(pcs->base + offset);
> +
> +	arg.data = pinconf_to_config_argument(ret);
> +	data = (data >> arg.bits.shift) & arg.bits.mask;

Shouldn't you just return data here?

	*config = data;

	return 0;

> +	arg.bits.value = data;
> +	*config = pinconf_to_config_packed(config_param, arg.data);
> +	return 0;

Instead of these? Or how else is the consumer driver supposed
to use the value?

>  }
>  
>  static int pcs_pinconf_set(struct pinctrl_dev *pctldev,
>  				unsigned pin, unsigned long config)
>  {
> -	return -ENOTSUPP;
> +	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
> +	enum pin_config_param config_param = pinconf_to_config_param(config);
> +	union pcs_pinconf_argument_t arg;
> +	u32 offset;
> +	unsigned data;
> +	int ret;
> +
> +	if (!pcs->conf.nconfs)
> +		return -ENOTSUPP;
> +
> +	ret = pcs_pinconf_get_config(pctldev, pin, config_param);
> +	if (ret < 0)
> +		return ret;
> +
> +	offset = pin * (pcs->width / BITS_PER_BYTE);
> +	data = pcs->read(pcs->base + offset);
> +
> +	arg.data = pinconf_to_config_argument(config);
> +	data = (data >> arg.bits.shift) & arg.bits.mask;
> +	arg.bits.value = data;
> +	data = pinconf_to_config_packed(config_param, arg.data);
> +	pcs->write(data, pcs->base + offset);
> +	return 0;
>  }

I need to look at this more to understand how this actually relates
to what gets written to the register with my test case :)
  
> @@ -765,12 +958,82 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
>  	if (res < 0)
>  		goto free_function;
>  
> -	(*map)->type = PIN_MAP_TYPE_MUX_GROUP;
> -	(*map)->data.mux.group = np->name;
> -	(*map)->data.mux.function = np->name;
> +	m->type = PIN_MAP_TYPE_MUX_GROUP;
> +	m->data.mux.group = np->name;
> +	m->data.mux.function = np->name;
> +
> +	if (!nconfs)
> +		return 0;
> +
> +	conf = devm_kzalloc(pcs->dev, sizeof(*conf) * nconfs, GFP_KERNEL);
> +	if (!conf) {
> +		res = -ENOMEM;
> +		goto free_pingroup;
> +	}
> +	i = 0;
> +	if (!of_property_read_u32_array(np, "pinctrl-single,bias",
> +					value, 5)) {
> +		/* array: bias value, mask, disable, pull down, pull up */
> +		data = value[0] & value[1];
> +		arg.bits.shift = ffs(value[1]) - 1;
> +		arg.bits.mask = value[1] >> arg.bits.shift;
> +		if (pcs_config_match(data, value[2])) {
> +			arg.bits.value = value[2] >> arg.bits.shift;
> +			conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_BIAS_DISABLE,
> +						    arg.data);
> +		}
> +		if (pcs_config_match(data, value[3])) {
> +			arg.bits.value = value[3] >> arg.bits.shift;
> +			conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_DOWN,
> +						    arg.data);
> +		}
> +		if (pcs_config_match(data, value[4])) {
> +			arg.bits.value = value[4] >> arg.bits.shift;
> +			conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_UP,
> +						    arg.data);
> +		}
> +	}

Doesn't this mean you only get one of the above working? What happens
if you initially need to set BIAS_DISABLE, but then the consumer driver
wants to set BIAS_PULL_DOWN or something?

Maybe we need separate pinctrl-single,bias-disable, pinctrl-single,bias-pull-down
and pinctrl-single,bias-pull-up?

> +	if (!of_property_read_u32_array(np, "pinctrl-single,power-source",
> +					value, 2)) {
> +		/* array: power source value, mask */
> +		data = value[0] & value[1];
> +		arg.bits.shift = ffs(value[1]) - 1;
> +		arg.bits.mask = value[1] >> arg.bits.shift;
> +		arg.bits.value = data >> arg.bits.shift;
> +		conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_POWER_SOURCE, arg.data);
> +	}
> +	if (!of_property_read_u32_array(np, "pinctrl-single,input-schmitt",
> +					value, 3)) {
> +		/* array: input schmitt value, mask, disable */
> +		data = value[0] & value[1];
> +		arg.bits.shift = ffs(value[1]) - 1;
> +		arg.bits.mask = value[1] >> arg.bits.shift;
> +		if (pcs_config_match(data, value[2])) {
> +			arg.bits.value = value[2] >> arg.bits.shift;
> +			conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_INPUT_SCHMITT_DISABLE,
> +						    arg.data);
> +		} else {
> +			arg.bits.value = data >> arg.bits.shift;
> +			conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_INPUT_SCHMITT,
> +						    arg.data);
> +		}
> +	}

And I think this has a similar problem? What if the consumer driver wants
to set INPUT_SCHMITT with pin_config_set() and the initial value is
SCHMITT_DISABLE? Doesn't the consumer driver get -ENOTSUPP in that case?

I'll look at this more early next week and try to get my testcase working
with it.

Regards,

Tony
Haojian Zhuang Nov. 18, 2012, 4:51 a.m. UTC | #2
On Sat, Nov 17, 2012 at 8:43 AM, Tony Lindgren <tony@atomide.com> wrote:
> Hi,
>
>>  static int pcs_pinconf_get(struct pinctrl_dev *pctldev,
>>                               unsigned pin, unsigned long *config)
>>  {
>> -     return -ENOTSUPP;
>> +     struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
>> +     enum pin_config_param config_param = pinconf_to_config_param(*config);
>> +     union pcs_pinconf_argument_t arg;
>> +     u32 offset;
>> +     unsigned data;
>> +     int ret;
>> +
>> +     if (!pcs->conf.nconfs)
>> +             return -ENOTSUPP;
>> +
>> +     ret = pcs_pinconf_get_config(pctldev, pin, config_param);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     offset = pin * (pcs->width / BITS_PER_BYTE);
>> +     data = pcs->read(pcs->base + offset);
>> +
>> +     arg.data = pinconf_to_config_argument(ret);
>> +     data = (data >> arg.bits.shift) & arg.bits.mask;
>
> Shouldn't you just return data here?
>
>         *config = data;
>
>         return 0;
>

Since we're using pinconf-generic, let's review how
pinconf_generic_dump_pin() works.

pinconf_generic_dump_pin():
        config = pinconf_to_config_packed(conf_items[i].param, 0);
        The lower 16-bit values are parameters. and the upper 16-bit
values are pre-set with 0 for argument.

        ret = pin_config_get_for_pin(pctldev, pin, &config);
        It'll invoke pcs_pinconf_get() that we're taking care. I want
to return the value with both argument
and parameters. Since the argument may be any location in 32-bit
register, I organize them into packed
structure. If I return the value field directly, it may conflict with
the lower 16-bit config parameter.

        if (conf_items[i].format && pinconf_to_config_argument(config) != 0)
            seq_printf(s, " (%u %s)",
pinconf_to_config_argument(config), conf_items[i].format);
        At here, it loads the upper 16-bit config argument. It also
means that returning value field directly
will break this.

        So how about change it in below.
        if (conf_items[i].format)
            seq_printf(s, " (%u %s)", config, conf_items[i].format);

>> +     arg.bits.value = data;
>> +     *config = pinconf_to_config_packed(config_param, arg.data);
>> +     return 0;
>
> Instead of these? Or how else is the consumer driver supposed
> to use the value?
>

I agree that returning data directly would be easy for the usage in
device driver. Let's define it as "data >> shift".
Is it OK?

>>  }
>>
>>  static int pcs_pinconf_set(struct pinctrl_dev *pctldev,
>>                               unsigned pin, unsigned long config)
>>  {
>> -     return -ENOTSUPP;
>> +     struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
>> +     enum pin_config_param config_param = pinconf_to_config_param(config);
>> +     union pcs_pinconf_argument_t arg;
>> +     u32 offset;
>> +     unsigned data;
>> +     int ret;
>> +
>> +     if (!pcs->conf.nconfs)
>> +             return -ENOTSUPP;
>> +
>> +     ret = pcs_pinconf_get_config(pctldev, pin, config_param);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     offset = pin * (pcs->width / BITS_PER_BYTE);
>> +     data = pcs->read(pcs->base + offset);
>> +
>> +     arg.data = pinconf_to_config_argument(config);
>> +     data = (data >> arg.bits.shift) & arg.bits.mask;
>> +     arg.bits.value = data;
>> +     data = pinconf_to_config_packed(config_param, arg.data);
>> +     pcs->write(data, pcs->base + offset);
>> +     return 0;
>>  }
>
> I need to look at this more to understand how this actually relates
> to what gets written to the register with my test case :)

No problem.
>
>> @@ -765,12 +958,82 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
>>       if (res < 0)
>>               goto free_function;
>>
>> -     (*map)->type = PIN_MAP_TYPE_MUX_GROUP;
>> -     (*map)->data.mux.group = np->name;
>> -     (*map)->data.mux.function = np->name;
>> +     m->type = PIN_MAP_TYPE_MUX_GROUP;
>> +     m->data.mux.group = np->name;
>> +     m->data.mux.function = np->name;
>> +
>> +     if (!nconfs)
>> +             return 0;
>> +
>> +     conf = devm_kzalloc(pcs->dev, sizeof(*conf) * nconfs, GFP_KERNEL);
>> +     if (!conf) {
>> +             res = -ENOMEM;
>> +             goto free_pingroup;
>> +     }
>> +     i = 0;
>> +     if (!of_property_read_u32_array(np, "pinctrl-single,bias",
>> +                                     value, 5)) {
>> +             /* array: bias value, mask, disable, pull down, pull up */
>> +             data = value[0] & value[1];
>> +             arg.bits.shift = ffs(value[1]) - 1;
>> +             arg.bits.mask = value[1] >> arg.bits.shift;
>> +             if (pcs_config_match(data, value[2])) {
>> +                     arg.bits.value = value[2] >> arg.bits.shift;
>> +                     conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_BIAS_DISABLE,
>> +                                                 arg.data);
>> +             }
>> +             if (pcs_config_match(data, value[3])) {
>> +                     arg.bits.value = value[3] >> arg.bits.shift;
>> +                     conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_DOWN,
>> +                                                 arg.data);
>> +             }
>> +             if (pcs_config_match(data, value[4])) {
>> +                     arg.bits.value = value[4] >> arg.bits.shift;
>> +                     conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_UP,
>> +                                                 arg.data);
>> +             }
>> +     }
>
> Doesn't this mean you only get one of the above working? What happens
> if you initially need to set BIAS_DISABLE, but then the consumer driver
> wants to set BIAS_PULL_DOWN or something?
>
> Maybe we need separate pinctrl-single,bias-disable, pinctrl-single,bias-pull-down
> and pinctrl-single,bias-pull-up?
>
Good point. I'll separate them.

>> +     if (!of_property_read_u32_array(np, "pinctrl-single,power-source",
>> +                                     value, 2)) {
>> +             /* array: power source value, mask */
>> +             data = value[0] & value[1];
>> +             arg.bits.shift = ffs(value[1]) - 1;
>> +             arg.bits.mask = value[1] >> arg.bits.shift;
>> +             arg.bits.value = data >> arg.bits.shift;
>> +             conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_POWER_SOURCE, arg.data);
>> +     }
>> +     if (!of_property_read_u32_array(np, "pinctrl-single,input-schmitt",
>> +                                     value, 3)) {
>> +             /* array: input schmitt value, mask, disable */
>> +             data = value[0] & value[1];
>> +             arg.bits.shift = ffs(value[1]) - 1;
>> +             arg.bits.mask = value[1] >> arg.bits.shift;
>> +             if (pcs_config_match(data, value[2])) {
>> +                     arg.bits.value = value[2] >> arg.bits.shift;
>> +                     conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_INPUT_SCHMITT_DISABLE,
>> +                                                 arg.data);
>> +             } else {
>> +                     arg.bits.value = data >> arg.bits.shift;
>> +                     conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_INPUT_SCHMITT,
>> +                                                 arg.data);
>> +             }
>> +     }
>
> And I think this has a similar problem? What if the consumer driver wants
> to set INPUT_SCHMITT with pin_config_set() and the initial value is
> SCHMITT_DISABLE? Doesn't the consumer driver get -ENOTSUPP in that case?
>
OK. I'll update them.

> I'll look at this more early next week and try to get my testcase working
> with it.
>
> Regards,
>
> Tony
Haojian Zhuang Nov. 18, 2012, 2:23 p.m. UTC | #3
On Sun, Nov 18, 2012 at 12:51 PM, Haojian Zhuang
<haojian.zhuang@gmail.com> wrote:
> On Sat, Nov 17, 2012 at 8:43 AM, Tony Lindgren <tony@atomide.com> wrote:
>> Hi,
>>
>>>  static int pcs_pinconf_get(struct pinctrl_dev *pctldev,
>>>                               unsigned pin, unsigned long *config)
>>>  {
>>> -     return -ENOTSUPP;
>>> +     struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
>>> +     enum pin_config_param config_param = pinconf_to_config_param(*config);
>>> +     union pcs_pinconf_argument_t arg;
>>> +     u32 offset;
>>> +     unsigned data;
>>> +     int ret;
>>> +
>>> +     if (!pcs->conf.nconfs)
>>> +             return -ENOTSUPP;
>>> +
>>> +     ret = pcs_pinconf_get_config(pctldev, pin, config_param);
>>> +     if (ret < 0)
>>> +             return ret;
>>> +
>>> +     offset = pin * (pcs->width / BITS_PER_BYTE);
>>> +     data = pcs->read(pcs->base + offset);
>>> +
>>> +     arg.data = pinconf_to_config_argument(ret);
>>> +     data = (data >> arg.bits.shift) & arg.bits.mask;
>>
>> Shouldn't you just return data here?
>>
>>         *config = data;
>>
>>         return 0;
>>
>
> Since we're using pinconf-generic, let's review how
> pinconf_generic_dump_pin() works.
>
> pinconf_generic_dump_pin():
>         config = pinconf_to_config_packed(conf_items[i].param, 0);
>         The lower 16-bit values are parameters. and the upper 16-bit
> values are pre-set with 0 for argument.
>
>         ret = pin_config_get_for_pin(pctldev, pin, &config);
>         It'll invoke pcs_pinconf_get() that we're taking care. I want
> to return the value with both argument
> and parameters. Since the argument may be any location in 32-bit
> register, I organize them into packed
> structure. If I return the value field directly, it may conflict with
> the lower 16-bit config parameter.
>
>         if (conf_items[i].format && pinconf_to_config_argument(config) != 0)
>             seq_printf(s, " (%u %s)",
> pinconf_to_config_argument(config), conf_items[i].format);
>         At here, it loads the upper 16-bit config argument. It also
> means that returning value field directly
> will break this.
>
>         So how about change it in below.
>         if (conf_items[i].format)
>             seq_printf(s, " (%u %s)", config, conf_items[i].format);
>

Oh. I messed with something. Let's the usage case again.

pin_config_set() sets the config value into pinmux register. The upper 16-bit
is config argument, and the lower 16-bit is config parameter. The
pinmux register
is 32-bit in Marvell's silicon. I think it's 32-bit value in most silicons.

And real field value could be lower 16-bit. So I have to pack mask,
shift, config value
& config parameter into 32-bit data. The limitation is mask, shift,
value are all 5-bit
value. And this is also the reason that I define pinctrl_lookup_map()
into consumer.h.
How could consumer driver know mask & shift? It has to lookup map to
get the real
configuration.

We also need to keep pin_config_get() compatible with pin_config_set(). I define
the config data as packed mask, shift, config value & config parameter.

The conclusion is that we have two choices.
1. Use packed way. It's a little complex.
2. Change the interface of pin_config_set()/pin_config_get(). We need
to use the new
interface in below.
    pin_config_set(const char *dev_name, const char *name, unsigned
long parameter,
                          unsigned long config);
    pin_config_get(const char *dev_name, const char *name, unsigned
long parameter,
                          unsigned long *config);
    And the config should be "value >> shift".

What's your opinion? I prefer the second method since it's easy to
understand and maintain.
Then I could hide pinctrl_lookup_map() in core.h.

>>> +     arg.bits.value = data;
>>> +     *config = pinconf_to_config_packed(config_param, arg.data);
>>> +     return 0;
>>
>> Instead of these? Or how else is the consumer driver supposed
>> to use the value?
>>
>
> I agree that returning data directly would be easy for the usage in
> device driver. Let's define it as "data >> shift".
> Is it OK?
>
>>>  }
>>>
>>>  static int pcs_pinconf_set(struct pinctrl_dev *pctldev,
>>>                               unsigned pin, unsigned long config)
>>>  {
>>> -     return -ENOTSUPP;
>>> +     struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
>>> +     enum pin_config_param config_param = pinconf_to_config_param(config);
>>> +     union pcs_pinconf_argument_t arg;
>>> +     u32 offset;
>>> +     unsigned data;
>>> +     int ret;
>>> +
>>> +     if (!pcs->conf.nconfs)
>>> +             return -ENOTSUPP;
>>> +
>>> +     ret = pcs_pinconf_get_config(pctldev, pin, config_param);
>>> +     if (ret < 0)
>>> +             return ret;
>>> +
>>> +     offset = pin * (pcs->width / BITS_PER_BYTE);
>>> +     data = pcs->read(pcs->base + offset);
>>> +
>>> +     arg.data = pinconf_to_config_argument(config);
>>> +     data = (data >> arg.bits.shift) & arg.bits.mask;
>>> +     arg.bits.value = data;
>>> +     data = pinconf_to_config_packed(config_param, arg.data);
>>> +     pcs->write(data, pcs->base + offset);
>>> +     return 0;
>>>  }
>>
>> I need to look at this more to understand how this actually relates
>> to what gets written to the register with my test case :)
>
> No problem.
>>
>>> @@ -765,12 +958,82 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
>>>       if (res < 0)
>>>               goto free_function;
>>>
>>> -     (*map)->type = PIN_MAP_TYPE_MUX_GROUP;
>>> -     (*map)->data.mux.group = np->name;
>>> -     (*map)->data.mux.function = np->name;
>>> +     m->type = PIN_MAP_TYPE_MUX_GROUP;
>>> +     m->data.mux.group = np->name;
>>> +     m->data.mux.function = np->name;
>>> +
>>> +     if (!nconfs)
>>> +             return 0;
>>> +
>>> +     conf = devm_kzalloc(pcs->dev, sizeof(*conf) * nconfs, GFP_KERNEL);
>>> +     if (!conf) {
>>> +             res = -ENOMEM;
>>> +             goto free_pingroup;
>>> +     }
>>> +     i = 0;
>>> +     if (!of_property_read_u32_array(np, "pinctrl-single,bias",
>>> +                                     value, 5)) {
>>> +             /* array: bias value, mask, disable, pull down, pull up */
>>> +             data = value[0] & value[1];
>>> +             arg.bits.shift = ffs(value[1]) - 1;
>>> +             arg.bits.mask = value[1] >> arg.bits.shift;
>>> +             if (pcs_config_match(data, value[2])) {
>>> +                     arg.bits.value = value[2] >> arg.bits.shift;
>>> +                     conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_BIAS_DISABLE,
>>> +                                                 arg.data);
>>> +             }
>>> +             if (pcs_config_match(data, value[3])) {
>>> +                     arg.bits.value = value[3] >> arg.bits.shift;
>>> +                     conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_DOWN,
>>> +                                                 arg.data);
>>> +             }
>>> +             if (pcs_config_match(data, value[4])) {
>>> +                     arg.bits.value = value[4] >> arg.bits.shift;
>>> +                     conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_UP,
>>> +                                                 arg.data);
>>> +             }
>>> +     }
>>
>> Doesn't this mean you only get one of the above working? What happens
>> if you initially need to set BIAS_DISABLE, but then the consumer driver
>> wants to set BIAS_PULL_DOWN or something?
>>
>> Maybe we need separate pinctrl-single,bias-disable, pinctrl-single,bias-pull-down
>> and pinctrl-single,bias-pull-up?
>>
> Good point. I'll separate them.
>
>>> +     if (!of_property_read_u32_array(np, "pinctrl-single,power-source",
>>> +                                     value, 2)) {
>>> +             /* array: power source value, mask */
>>> +             data = value[0] & value[1];
>>> +             arg.bits.shift = ffs(value[1]) - 1;
>>> +             arg.bits.mask = value[1] >> arg.bits.shift;
>>> +             arg.bits.value = data >> arg.bits.shift;
>>> +             conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_POWER_SOURCE, arg.data);
>>> +     }
>>> +     if (!of_property_read_u32_array(np, "pinctrl-single,input-schmitt",
>>> +                                     value, 3)) {
>>> +             /* array: input schmitt value, mask, disable */
>>> +             data = value[0] & value[1];
>>> +             arg.bits.shift = ffs(value[1]) - 1;
>>> +             arg.bits.mask = value[1] >> arg.bits.shift;
>>> +             if (pcs_config_match(data, value[2])) {
>>> +                     arg.bits.value = value[2] >> arg.bits.shift;
>>> +                     conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_INPUT_SCHMITT_DISABLE,
>>> +                                                 arg.data);
>>> +             } else {
>>> +                     arg.bits.value = data >> arg.bits.shift;
>>> +                     conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_INPUT_SCHMITT,
>>> +                                                 arg.data);
>>> +             }
>>> +     }
>>
>> And I think this has a similar problem? What if the consumer driver wants
>> to set INPUT_SCHMITT with pin_config_set() and the initial value is
>> SCHMITT_DISABLE? Doesn't the consumer driver get -ENOTSUPP in that case?
>>
> OK. I'll update them.
>
>> I'll look at this more early next week and try to get my testcase working
>> with it.
>>
>> Regards,
>>
>> Tony
Tony Lindgren Nov. 22, 2012, 12:08 a.m. UTC | #4
* Haojian Zhuang <haojian.zhuang@gmail.com> [121117 20:53]:
> On Sat, Nov 17, 2012 at 8:43 AM, Tony Lindgren <tony@atomide.com> wrote:
> 
> I agree that returning data directly would be easy for the usage in
> device driver. Let's define it as "data >> shift".
> Is it OK?

Yes returning data >> shift makes sense to me from consumer driver
point of view, so I guess then it's just what I too suggested:

data = (data >> arg.bits.shift) & arg.bits.mask;
*config = data;

Or did I miss something? 

Regards,

Tony
Tony Lindgren Nov. 22, 2012, 12:08 a.m. UTC | #5
* Haojian Zhuang <haojian.zhuang@gmail.com> [121118 06:25]:
> 
> pin_config_set() sets the config value into pinmux register. The upper 16-bit
> is config argument, and the lower 16-bit is config parameter. The
> pinmux register
> is 32-bit in Marvell's silicon. I think it's 32-bit value in most silicons.

Well what if we soon have a 64-bit mux register? We should probably
reserve more than 5 bits for the shift to handle those cases as
well.
 
> And real field value could be lower 16-bit. So I have to pack mask,
> shift, config value
> & config parameter into 32-bit data. The limitation is mask, shift,
> value are all 5-bit
> value. And this is also the reason that I define pinctrl_lookup_map()
> into consumer.h.

Yes this might be problematic for 64-bit systems..

> How could consumer driver know mask & shift? It has to lookup map to
> get the real
> configuration.

I think the consumer driver should not know anything about the
mask & shift.
 
> We also need to keep pin_config_get() compatible with pin_config_set(). I define
> the config data as packed mask, shift, config value & config parameter.
> 
> The conclusion is that we have two choices.
> 1. Use packed way. It's a little complex.

I think the packed way won't work well for consumer drivers.

> 2. Change the interface of pin_config_set()/pin_config_get(). We need
> to use the new
> interface in below.
>     pin_config_set(const char *dev_name, const char *name, unsigned
> long parameter,
>                           unsigned long config);
>     pin_config_get(const char *dev_name, const char *name, unsigned
> long parameter,
>                           unsigned long *config);
>     And the config should be "value >> shift".
> 
> What's your opinion? I prefer the second method since it's easy to
> understand and maintain.
> Then I could hide pinctrl_lookup_map() in core.h.

Yes something like that makes sense to me. Except IMHO we should
use some cookie for a pin instead of dev_name + name, and have
some struct for the config. Something like this perhaps:

pin_config_get(struct pin *pin, struct pin_config *config,
		unsigned long parameter);

Regards,

Tony
diff mbox

Patch

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 7bf914d..e9f2d2d 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -139,6 +139,7 @@  config PINCTRL_SINGLE
 	depends on OF
 	select PINMUX
 	select PINCONF
+	select GENERIC_PINCONF
 	help
 	  This selects the device tree based generic pinctrl driver.
 
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index 2476a68..be12aca 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -20,6 +20,8 @@ 
 #include <linux/of_device.h>
 #include <linux/of_address.h>
 
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinconf-generic.h>
 #include <linux/pinctrl/pinctrl.h>
 #include <linux/pinctrl/pinmux.h>
 
@@ -33,6 +35,26 @@ 
 #define PCS_MAX_GPIO_VALUES		2
 
 /**
+ * union pcs_pinconf_argument_t -- upper 16-bit in the pinconf data
+ *
+ * The 32-bit pinmux register value could be divided into some different
+ * config arguments. Each config parameter binds to one config argument.
+ * And each config argument can't exceed 5-bit value.
+ * @data:	argument of config in pinconf generic
+ * @value:	5-bit value
+ * @mask:	5-bit mask
+ * @shift:	shift of value/mask field in pinmux register
+ */
+union pcs_pinconf_argument_t {
+	u16 data;
+	struct {
+		unsigned value:5;
+		unsigned mask:5;
+		unsigned shift:5;
+	} bits;
+};
+
+/**
  * struct pcs_pingroup - pingroups for a function
  * @np:		pingroup device node pointer
  * @name:	pingroup name
@@ -88,6 +110,16 @@  struct pcs_gpio_range {
 };
 
 /**
+ * struct pcs_conf - configuration of pinctrl device
+ * @nconf:	number of configuration that is defined for pinconf
+ * @is_generic:	for pin controller that want to use the generic interface
+ */
+struct pcs_conf {
+	unsigned nconfs;
+	bool is_generic;
+};
+
+/**
  * struct pcs_data - wrapper for data needed by pinctrl framework
  * @pa:		pindesc array
  * @cur:	index to current element
@@ -130,6 +162,7 @@  struct pcs_name {
  * @fmax:	max number of functions in fmask
  * @names:	array of register names for pins
  * @pins:	physical pins on the SoC
+ * @conf:	value of pinconf
  * @pgtree:	pingroup index radix tree
  * @ftree:	function index radix tree
  * @pingroups:	list of pingroups
@@ -155,6 +188,7 @@  struct pcs_device {
 	bool bits_per_mux;
 	struct pcs_name *names;
 	struct pcs_data pins;
+	struct pcs_conf conf;
 	struct radix_tree_root pgtree;
 	struct radix_tree_root ftree;
 	struct list_head pingroups;
@@ -445,28 +479,168 @@  static struct pinmux_ops pcs_pinmux_ops = {
 	.gpio_request_enable = pcs_request_gpio,
 };
 
+static void pcs_free_pingroups(struct pcs_device *pcs);
+
+static int pcs_pinconf_get_config(struct pinctrl_dev *pctldev, unsigned pin,
+				  enum pin_config_param config_param)
+{
+	const struct pinctrl_map *map;
+	enum pin_config_param param;
+	const unsigned *pins = NULL;
+	const char *name;
+	unsigned long *conf;
+	unsigned count, group, i, npins;
+	int found = 0;
+
+	count = pcs_get_groups_count(pctldev);
+	for (group = 0; group < count; group++) {
+		if (pcs_get_group_pins(pctldev, group, &pins, &npins))
+			return -EINVAL;
+		for (i = 0; i < npins; i++) {
+			if (pins[i] == pin) {
+				found = 1;
+				break;
+			}
+		}
+		if (found)
+			break;
+	}
+	if (!found)
+		return -ENOTSUPP;
+
+	name = pcs_get_group_name(pctldev, group);
+	if (!name)
+		return -EINVAL;
+	map = pinctrl_lookup_map(pctldev->p, name, PIN_MAP_TYPE_CONFIGS_GROUP);
+	if (!map)
+		return -ENOTSUPP;
+
+	conf = map->data.configs.configs;
+	for (i = 0; i < map->data.configs.num_configs; i++) {
+		param = pinconf_to_config_param(conf[i]);
+		if (config_param == param)
+			return conf[i];
+	}
+	return -ENOTSUPP;
+}
+
 static int pcs_pinconf_get(struct pinctrl_dev *pctldev,
 				unsigned pin, unsigned long *config)
 {
-	return -ENOTSUPP;
+	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param config_param = pinconf_to_config_param(*config);
+	union pcs_pinconf_argument_t arg;
+	u32 offset;
+	unsigned data;
+	int ret;
+
+	if (!pcs->conf.nconfs)
+		return -ENOTSUPP;
+
+	ret = pcs_pinconf_get_config(pctldev, pin, config_param);
+	if (ret < 0)
+		return ret;
+
+	offset = pin * (pcs->width / BITS_PER_BYTE);
+	data = pcs->read(pcs->base + offset);
+
+	arg.data = pinconf_to_config_argument(ret);
+	data = (data >> arg.bits.shift) & arg.bits.mask;
+	arg.bits.value = data;
+	*config = pinconf_to_config_packed(config_param, arg.data);
+	return 0;
 }
 
 static int pcs_pinconf_set(struct pinctrl_dev *pctldev,
 				unsigned pin, unsigned long config)
 {
-	return -ENOTSUPP;
+	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param config_param = pinconf_to_config_param(config);
+	union pcs_pinconf_argument_t arg;
+	u32 offset;
+	unsigned data;
+	int ret;
+
+	if (!pcs->conf.nconfs)
+		return -ENOTSUPP;
+
+	ret = pcs_pinconf_get_config(pctldev, pin, config_param);
+	if (ret < 0)
+		return ret;
+
+	offset = pin * (pcs->width / BITS_PER_BYTE);
+	data = pcs->read(pcs->base + offset);
+
+	arg.data = pinconf_to_config_argument(config);
+	data = (data >> arg.bits.shift) & arg.bits.mask;
+	arg.bits.value = data;
+	data = pinconf_to_config_packed(config_param, arg.data);
+	pcs->write(data, pcs->base + offset);
+	return 0;
 }
 
 static int pcs_pinconf_group_get(struct pinctrl_dev *pctldev,
 				unsigned group, unsigned long *config)
 {
+	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
+	const struct pinctrl_map *map;
+	enum pin_config_param param, config_param;
+	union pcs_pinconf_argument_t arg;
+	const char *name;
+	unsigned long *conf;
+	int i;
+
+	if (!pcs->conf.nconfs)
+		return -ENOTSUPP;
+
+	name = pcs_get_group_name(pctldev, group);
+	if (!name)
+		return -EINVAL;
+	map = pinctrl_lookup_map(pctldev->p, name, PIN_MAP_TYPE_CONFIGS_GROUP);
+	if (!map)
+		return -ENOTSUPP;
+	config_param = pinconf_to_config_param(*config);
+	conf = map->data.configs.configs;
+	for (i = 0; i < map->data.configs.num_configs; i++) {
+		param = pinconf_to_config_param(conf[i]);
+		if (config_param == param) {
+			arg.data = pinconf_to_config_argument(conf[i]);
+			*config = pinconf_to_config_packed(param, arg.data);
+			return 0;
+		}
+	}
 	return -ENOTSUPP;
 }
 
 static int pcs_pinconf_group_set(struct pinctrl_dev *pctldev,
 				unsigned group, unsigned long config)
 {
-	return -ENOTSUPP;
+	struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
+	struct pcs_pingroup *pins;
+	union pcs_pinconf_argument_t arg;
+	u32 offset, data;
+	unsigned ret, mask;
+	int i, mux_bytes;
+
+	if (!pcs->conf.nconfs)
+		return -ENOTSUPP;
+
+	pins = radix_tree_lookup(&pcs->pgtree, group);
+	if (!pins) {
+		dev_err(pcs->dev, "%s could not find pingroup%i\n",
+			__func__, group);
+		return -EINVAL;
+	}
+	mux_bytes = pcs->width / BITS_PER_BYTE;
+	arg.data = pinconf_to_config_argument(config);
+	mask = arg.bits.mask << arg.bits.shift;
+	data = arg.bits.value << arg.bits.shift;
+	for (i = 0; i < pins->ngpins; i++) {
+		offset = pins->gpins[i] * mux_bytes;
+		ret = pcs->read(pcs->base + offset) & ~mask;
+		pcs->write(ret | data, pcs->base + offset);
+	}
+	return 0;
 }
 
 static void pcs_pinconf_dbg_show(struct pinctrl_dev *pctldev,
@@ -676,8 +850,22 @@  static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset)
 	return index;
 }
 
+static int pcs_config_match(unsigned data, unsigned match)
+{
+	int ret = 0;
+
+	if (!match) {
+		if (!data)
+			ret = 1;
+	} else {
+		if ((data & match) == match)
+			ret = 1;
+	}
+	return ret;
+}
+
 /**
- * smux_parse_one_pinctrl_entry() - parses a device tree mux entry
+ * pcs_parse_one_pinctrl_entry() - parses a device tree mux entry
  * @pcs: pinctrl driver instance
  * @np: device node of the mux entry
  * @map: map entry
@@ -700,9 +888,13 @@  static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
 						const char **pgnames)
 {
 	struct pcs_func_vals *vals;
+	struct pinctrl_map *m = *map;
 	const __be32 *mux;
-	int size, params, rows, *pins, index = 0, found = 0, res = -ENOMEM;
+	int size, params, rows, *pins, i = 0, found = 0, res = -ENOMEM;
 	struct pcs_function *function;
+	unsigned long *conf;
+	union pcs_pinconf_argument_t arg;
+	u32 value[8], data, nconfs;
 
 	if (pcs->bits_per_mux) {
 		params = 3;
@@ -733,16 +925,16 @@  static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
 	if (!pins)
 		goto free_vals;
 
-	while (index < size) {
+	while (i < size) {
 		unsigned offset, val;
 		int pin;
 
-		offset = be32_to_cpup(mux + index++);
-		val = be32_to_cpup(mux + index++);
+		offset = be32_to_cpup(mux + i++);
+		val = be32_to_cpup(mux + i++);
 		vals[found].reg = pcs->base + offset;
 		vals[found].val = val;
 		if (params == 3) {
-			val = be32_to_cpup(mux + index++);
+			val = be32_to_cpup(mux + i++);
 			vals[found].mask = val;
 		}
 
@@ -756,6 +948,7 @@  static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
 		pins[found++] = pin;
 	}
 
+	nconfs = pcs->conf.nconfs;
 	pgnames[0] = np->name;
 	function = pcs_add_function(pcs, np, np->name, vals, found, pgnames, 1);
 	if (!function)
@@ -765,12 +958,82 @@  static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
 	if (res < 0)
 		goto free_function;
 
-	(*map)->type = PIN_MAP_TYPE_MUX_GROUP;
-	(*map)->data.mux.group = np->name;
-	(*map)->data.mux.function = np->name;
+	m->type = PIN_MAP_TYPE_MUX_GROUP;
+	m->data.mux.group = np->name;
+	m->data.mux.function = np->name;
+
+	if (!nconfs)
+		return 0;
+
+	conf = devm_kzalloc(pcs->dev, sizeof(*conf) * nconfs, GFP_KERNEL);
+	if (!conf) {
+		res = -ENOMEM;
+		goto free_pingroup;
+	}
+	i = 0;
+	if (!of_property_read_u32_array(np, "pinctrl-single,bias",
+					value, 5)) {
+		/* array: bias value, mask, disable, pull down, pull up */
+		data = value[0] & value[1];
+		arg.bits.shift = ffs(value[1]) - 1;
+		arg.bits.mask = value[1] >> arg.bits.shift;
+		if (pcs_config_match(data, value[2])) {
+			arg.bits.value = value[2] >> arg.bits.shift;
+			conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_BIAS_DISABLE,
+						    arg.data);
+		}
+		if (pcs_config_match(data, value[3])) {
+			arg.bits.value = value[3] >> arg.bits.shift;
+			conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_DOWN,
+						    arg.data);
+		}
+		if (pcs_config_match(data, value[4])) {
+			arg.bits.value = value[4] >> arg.bits.shift;
+			conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_UP,
+						    arg.data);
+		}
+	}
+	if (!of_property_read_u32_array(np, "pinctrl-single,power-source",
+					value, 2)) {
+		/* array: power source value, mask */
+		data = value[0] & value[1];
+		arg.bits.shift = ffs(value[1]) - 1;
+		arg.bits.mask = value[1] >> arg.bits.shift;
+		arg.bits.value = data >> arg.bits.shift;
+		conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_POWER_SOURCE, arg.data);
+	}
+	if (!of_property_read_u32_array(np, "pinctrl-single,input-schmitt",
+					value, 3)) {
+		/* array: input schmitt value, mask, disable */
+		data = value[0] & value[1];
+		arg.bits.shift = ffs(value[1]) - 1;
+		arg.bits.mask = value[1] >> arg.bits.shift;
+		if (pcs_config_match(data, value[2])) {
+			arg.bits.value = value[2] >> arg.bits.shift;
+			conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_INPUT_SCHMITT_DISABLE,
+						    arg.data);
+		} else {
+			arg.bits.value = data >> arg.bits.shift;
+			conf[i++] = PIN_CONF_PACKED(PIN_CONFIG_INPUT_SCHMITT,
+						    arg.data);
+		}
+	}
+	m++;
+	m->type = PIN_MAP_TYPE_CONFIGS_GROUP;
+	m->data.configs.group_or_pin = np->name;
+	m->data.configs.configs = conf;
+	if (i > nconfs) {
+		dev_err(pcs->dev, "pinconf items (%d) more than nconfs (%d)\n",
+			i, nconfs);
+		i = nconfs;
+	}
+	m->data.configs.num_configs = i;
 
 	return 0;
 
+free_pingroup:
+	pcs_free_pingroups(pcs);
+
 free_function:
 	pcs_remove_function(pcs, function);
 
@@ -782,6 +1045,7 @@  free_vals:
 
 	return res;
 }
+
 /**
  * pcs_dt_node_to_map() - allocates and parses pinctrl maps
  * @pctldev: pinctrl instance
@@ -797,13 +1061,17 @@  static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev,
 	const char **pgnames;
 	int ret;
 
+
 	pcs = pinctrl_dev_get_drvdata(pctldev);
 
-	*map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL);
-	if (!map)
-		return -ENOMEM;
+	if (!pcs->conf.nconfs)
+		*num_maps = 1;
+	else
+		*num_maps = 2;
 
-	*num_maps = 0;
+	*map = devm_kzalloc(pcs->dev, sizeof(**map) * (*num_maps), GFP_KERNEL);
+	if (!*map)
+		return -ENOMEM;
 
 	pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL);
 	if (!pgnames) {
@@ -817,7 +1085,6 @@  static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev,
 			np_config->name);
 		goto free_pgnames;
 	}
-	*num_maps = 1;
 
 	return 0;
 
@@ -957,11 +1224,13 @@  static int __devinit pcs_probe(struct platform_device *pdev)
 	const struct of_device_id *match;
 	struct resource *res;
 	struct pcs_device *pcs;
+	const struct pcs_conf *conf;
 	int ret;
 
 	match = of_match_device(pcs_of_match, &pdev->dev);
 	if (!match)
 		return -EINVAL;
+	conf = match->data;
 
 	pcs = devm_kzalloc(&pdev->dev, sizeof(*pcs), GFP_KERNEL);
 	if (!pcs) {
@@ -969,6 +1238,7 @@  static int __devinit pcs_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	}
 	pcs->dev = &pdev->dev;
+	memcpy(&pcs->conf, conf, sizeof(*conf));
 	mutex_init(&pcs->mutex);
 	INIT_LIST_HEAD(&pcs->pingroups);
 	INIT_LIST_HEAD(&pcs->functions);
@@ -989,6 +1259,9 @@  static int __devinit pcs_probe(struct platform_device *pdev)
 	pcs->bits_per_mux = of_property_read_bool(np,
 						  "pinctrl-single,bit-per-mux");
 
+	if (conf->nconfs)
+		pcs_pinconf_ops.is_generic = true;
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res) {
 		dev_err(pcs->dev, "could not get resource\n");
@@ -1074,8 +1347,21 @@  static int __devexit pcs_remove(struct platform_device *pdev)
 	return 0;
 }
 
+/* PINCONF isn't supported */
+static struct pcs_conf pinctrl_conf __devinitdata = {
+	.nconfs = 0,
+	.is_generic = false,
+};
+
+/* Generic PINCONF is supported. */
+static struct pcs_conf pinconf_conf __devinitdata = {
+	.nconfs = 7,
+	.is_generic = true,
+};
+
 static struct of_device_id pcs_of_match[] __devinitdata = {
-	{ .compatible = DRIVER_NAME, },
+	{ .compatible = "pinctrl-single", .data = &pinctrl_conf },
+	{ .compatible = "pinconf-single", .data = &pinconf_conf },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, pcs_of_match);