[PATCH v5 05/10] pinctrl: single: support pinconf generic
Haojian Zhuang
haojian.zhuang at gmail.com
Sun Nov 18 09:23:05 EST 2012
On Sun, Nov 18, 2012 at 12:51 PM, Haojian Zhuang
<haojian.zhuang at gmail.com> wrote:
> On Sat, Nov 17, 2012 at 8:43 AM, Tony Lindgren <tony at 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
More information about the linux-arm-kernel
mailing list