2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-19 02:34:01 +08:00

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"
(9e605cb68a),

Signed-off-by: Manjunathappa, Prakash <prakash.pm@ti.com>
Reported-by: Lad, Prabhakar <prabhakar.csengg@gmail.com>
Tested-by: Lad, Prabhakar <prabhakar.csengg@gmail.com>
Acked-by: Haojian Zhuang <haojian.zhuang@gmail.com>
Acked-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
Manjunathappa, Prakash 2013-05-21 19:38:00 +05:30 committed by Linus Walleij
parent ac844b6271
commit 4e7e8017a8
2 changed files with 167 additions and 34 deletions

View File

@ -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

View File

@ -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 {
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,
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)
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)
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)
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,
{ {
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,
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 @@ free_function:
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,
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;