[PATCH v5 05/10] pinctrl: single: support pinconf generic
Haojian Zhuang
haojian.zhuang at gmail.com
Sat Nov 17 23:51:55 EST 2012
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);
>> + 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