Commit 4e7e8017 authored by Manjunathappa, Prakash's avatar Manjunathappa, Prakash Committed by Linus Walleij

pinctrl: pinctrl-single: enhance to configure multiple pins of different modules

Add support to configure multiple pins in each register, existing
implementation added by [1] does not support full fledge multiple pin
configuration in single register, reports a pin clash when different
modules configure different bits of same register. The issue reported
and discussed here
http://www.spinics.net/lists/arm-kernel/msg235213.html

With pinctrl-single,bits-per-mux property specified, use function-mask
property to find out number pins to configure. Allocate and register
pin control functions based sub mask.

Tested on da850/omap-l138 EVM.
does not support variable submask for pins.
does not support pinconf.

[1] "pinctrl: pinctrl-single: Add pinctrl-single,bits type of mux"
(9e605cb6),
Signed-off-by: default avatarManjunathappa, Prakash <prakash.pm@ti.com>
Reported-by: default avatarLad, Prabhakar <prabhakar.csengg@gmail.com>
Tested-by: default avatarLad, Prabhakar <prabhakar.csengg@gmail.com>
Acked-by: default avatarHaojian Zhuang <haojian.zhuang@gmail.com>
Acked-by: default avatarTony Lindgren <tony@atomide.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent ac844b62
...@@ -18,7 +18,8 @@ Optional properties: ...@@ -18,7 +18,8 @@ Optional properties:
pin functions is ignored pin functions is ignored
- pinctrl-single,bit-per-mux : boolean to indicate that one register controls - pinctrl-single,bit-per-mux : boolean to indicate that one register controls
more than one pin more than one pin, for which "pinctrl-single,function-mask" property specifies
position mask of pin.
- pinctrl-single,drive-strength : array of value that are used to configure - pinctrl-single,drive-strength : array of value that are used to configure
drive strength in the pinmux register. They're value of drive strength drive strength in the pinmux register. They're value of drive strength
......
...@@ -163,6 +163,7 @@ struct pcs_name { ...@@ -163,6 +163,7 @@ struct pcs_name {
* @foff: value to turn mux off * @foff: value to turn mux off
* @fmax: max number of functions in fmask * @fmax: max number of functions in fmask
* @is_pinconf: whether supports pinconf * @is_pinconf: whether supports pinconf
* @bits_per_pin:number of bits per pin
* @names: array of register names for pins * @names: array of register names for pins
* @pins: physical pins on the SoC * @pins: physical pins on the SoC
* @pgtree: pingroup index radix tree * @pgtree: pingroup index radix tree
...@@ -190,6 +191,7 @@ struct pcs_device { ...@@ -190,6 +191,7 @@ struct pcs_device {
unsigned fmax; unsigned fmax;
bool bits_per_mux; bool bits_per_mux;
bool is_pinconf; bool is_pinconf;
unsigned bits_per_pin;
struct pcs_name *names; struct pcs_name *names;
struct pcs_data pins; struct pcs_data pins;
struct radix_tree_root pgtree; struct radix_tree_root pgtree;
...@@ -431,10 +433,11 @@ static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector, ...@@ -431,10 +433,11 @@ static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector,
vals = &func->vals[i]; vals = &func->vals[i];
val = pcs->read(vals->reg); val = pcs->read(vals->reg);
if (!vals->mask)
mask = pcs->fmask; if (pcs->bits_per_mux)
mask = vals->mask;
else else
mask = pcs->fmask & vals->mask; mask = pcs->fmask;
val &= ~mask; val &= ~mask;
val |= (vals->val & mask); val |= (vals->val & mask);
...@@ -779,7 +782,13 @@ static int pcs_allocate_pin_table(struct pcs_device *pcs) ...@@ -779,7 +782,13 @@ static int pcs_allocate_pin_table(struct pcs_device *pcs)
int mux_bytes, nr_pins, i; int mux_bytes, nr_pins, i;
mux_bytes = pcs->width / BITS_PER_BYTE; mux_bytes = pcs->width / BITS_PER_BYTE;
nr_pins = pcs->size / mux_bytes;
if (pcs->bits_per_mux) {
pcs->bits_per_pin = fls(pcs->fmask);
nr_pins = (pcs->size * BITS_PER_BYTE) / pcs->bits_per_pin;
} else {
nr_pins = pcs->size / mux_bytes;
}
dev_dbg(pcs->dev, "allocating %i pins\n", nr_pins); dev_dbg(pcs->dev, "allocating %i pins\n", nr_pins);
pcs->pins.pa = devm_kzalloc(pcs->dev, pcs->pins.pa = devm_kzalloc(pcs->dev,
...@@ -800,8 +809,14 @@ static int pcs_allocate_pin_table(struct pcs_device *pcs) ...@@ -800,8 +809,14 @@ static int pcs_allocate_pin_table(struct pcs_device *pcs)
for (i = 0; i < pcs->desc.npins; i++) { for (i = 0; i < pcs->desc.npins; i++) {
unsigned offset; unsigned offset;
int res; int res;
int byte_num;
offset = i * mux_bytes; if (pcs->bits_per_mux) {
byte_num = (pcs->bits_per_pin * i) / BITS_PER_BYTE;
offset = (byte_num / mux_bytes) * mux_bytes;
} else {
offset = i * mux_bytes;
}
res = pcs_add_pin(pcs, offset); res = pcs_add_pin(pcs, offset);
if (res < 0) { if (res < 0) {
dev_err(pcs->dev, "error adding pins: %i\n", res); dev_err(pcs->dev, "error adding pins: %i\n", res);
...@@ -919,7 +934,10 @@ static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset) ...@@ -919,7 +934,10 @@ static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset)
return -EINVAL; return -EINVAL;
} }
index = offset / (pcs->width / BITS_PER_BYTE); if (pcs->bits_per_mux)
index = (offset * BITS_PER_BYTE) / pcs->bits_per_pin;
else
index = offset / (pcs->width / BITS_PER_BYTE);
return index; return index;
} }
...@@ -1097,29 +1115,18 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, ...@@ -1097,29 +1115,18 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
{ {
struct pcs_func_vals *vals; struct pcs_func_vals *vals;
const __be32 *mux; const __be32 *mux;
int size, params, rows, *pins, index = 0, found = 0, res = -ENOMEM; int size, rows, *pins, index = 0, found = 0, res = -ENOMEM;
struct pcs_function *function; struct pcs_function *function;
if (pcs->bits_per_mux) { mux = of_get_property(np, PCS_MUX_PINS_NAME, &size);
params = 3; if ((!mux) || (size < sizeof(*mux) * 2)) {
mux = of_get_property(np, PCS_MUX_BITS_NAME, &size); dev_err(pcs->dev, "bad data for mux %s\n",
} else { np->name);
params = 2;
mux = of_get_property(np, PCS_MUX_PINS_NAME, &size);
}
if (!mux) {
dev_err(pcs->dev, "no valid property for %s\n", np->name);
return -EINVAL;
}
if (size < (sizeof(*mux) * params)) {
dev_err(pcs->dev, "bad data for %s\n", np->name);
return -EINVAL; return -EINVAL;
} }
size /= sizeof(*mux); /* Number of elements in array */ size /= sizeof(*mux); /* Number of elements in array */
rows = size / params; rows = size / 2;
vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows, GFP_KERNEL); vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows, GFP_KERNEL);
if (!vals) if (!vals)
...@@ -1137,10 +1144,6 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, ...@@ -1137,10 +1144,6 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
val = be32_to_cpup(mux + index++); val = be32_to_cpup(mux + index++);
vals[found].reg = pcs->base + offset; vals[found].reg = pcs->base + offset;
vals[found].val = val; vals[found].val = val;
if (params == 3) {
val = be32_to_cpup(mux + index++);
vals[found].mask = val;
}
pin = pcs_get_pin_by_offset(pcs, offset); pin = pcs_get_pin_by_offset(pcs, offset);
if (pin < 0) { if (pin < 0) {
...@@ -1184,6 +1187,125 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, ...@@ -1184,6 +1187,125 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
free_pins: free_pins:
devm_kfree(pcs->dev, pins); devm_kfree(pcs->dev, pins);
free_vals:
devm_kfree(pcs->dev, vals);
return res;
}
#define PARAMS_FOR_BITS_PER_MUX 3
static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs,
struct device_node *np,
struct pinctrl_map **map,
unsigned *num_maps,
const char **pgnames)
{
struct pcs_func_vals *vals;
const __be32 *mux;
int size, rows, *pins, index = 0, found = 0, res = -ENOMEM;
int npins_in_row;
struct pcs_function *function;
mux = of_get_property(np, PCS_MUX_BITS_NAME, &size);
if (!mux) {
dev_err(pcs->dev, "no valid property for %s\n", np->name);
return -EINVAL;
}
if (size < (sizeof(*mux) * PARAMS_FOR_BITS_PER_MUX)) {
dev_err(pcs->dev, "bad data for %s\n", np->name);
return -EINVAL;
}
/* Number of elements in array */
size /= sizeof(*mux);
rows = size / PARAMS_FOR_BITS_PER_MUX;
npins_in_row = pcs->width / pcs->bits_per_pin;
vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows * npins_in_row,
GFP_KERNEL);
if (!vals)
return -ENOMEM;
pins = devm_kzalloc(pcs->dev, sizeof(*pins) * rows * npins_in_row,
GFP_KERNEL);
if (!pins)
goto free_vals;
while (index < size) {
unsigned offset, val;
unsigned mask, bit_pos, val_pos, mask_pos, submask;
unsigned pin_num_from_lsb;
int pin;
offset = be32_to_cpup(mux + index++);
val = be32_to_cpup(mux + index++);
mask = be32_to_cpup(mux + index++);
/* Parse pins in each row from LSB */
while (mask) {
bit_pos = ffs(mask);
pin_num_from_lsb = bit_pos / pcs->bits_per_pin;
mask_pos = ((pcs->fmask) << (bit_pos - 1));
val_pos = val & mask_pos;
submask = mask & mask_pos;
mask &= ~mask_pos;
if (submask != mask_pos) {
dev_warn(pcs->dev,
"Invalid submask 0x%x for %s at 0x%x\n",
submask, np->name, offset);
continue;
}
vals[found].mask = submask;
vals[found].reg = pcs->base + offset;
vals[found].val = val_pos;
pin = pcs_get_pin_by_offset(pcs, offset);
if (pin < 0) {
dev_err(pcs->dev,
"could not add functions for %s %ux\n",
np->name, offset);
break;
}
pins[found++] = pin + pin_num_from_lsb;
}
}
pgnames[0] = np->name;
function = pcs_add_function(pcs, np, np->name, vals, found, pgnames, 1);
if (!function)
goto free_pins;
res = pcs_add_pingroup(pcs, np, np->name, pins, found);
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;
if (pcs->is_pinconf) {
dev_err(pcs->dev, "pinconf not supported\n");
goto free_pingroups;
}
*num_maps = 1;
return 0;
free_pingroups:
pcs_free_pingroups(pcs);
*num_maps = 1;
free_function:
pcs_remove_function(pcs, function);
free_pins:
devm_kfree(pcs->dev, pins);
free_vals: free_vals:
devm_kfree(pcs->dev, vals); devm_kfree(pcs->dev, vals);
...@@ -1219,12 +1341,22 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, ...@@ -1219,12 +1341,22 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev,
goto free_map; goto free_map;
} }
ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, num_maps, if (pcs->bits_per_mux) {
pgnames); ret = pcs_parse_bits_in_pinctrl_entry(pcs, np_config, map,
if (ret < 0) { num_maps, pgnames);
dev_err(pcs->dev, "no pins entries for %s\n", if (ret < 0) {
np_config->name); dev_err(pcs->dev, "no pins entries for %s\n",
goto free_pgnames; np_config->name);
goto free_pgnames;
}
} else {
ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map,
num_maps, pgnames);
if (ret < 0) {
dev_err(pcs->dev, "no pins entries for %s\n",
np_config->name);
goto free_pgnames;
}
} }
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment