Skip to content

Commit

Permalink
pinctrl: pinctrl-single: enhance to configure multiple pins of differ…
Browse files Browse the repository at this point in the history
…ent 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"
(9e605cb),

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>
  • Loading branch information
Manjunathappa, Prakash authored and linusw committed Jun 16, 2013
1 parent ac844b6 commit 4e7e801
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 34 deletions.
3 changes: 2 additions & 1 deletion Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ Optional properties:
pin functions is ignored

- 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
drive strength in the pinmux register. They're value of drive strength
Expand Down
198 changes: 165 additions & 33 deletions drivers/pinctrl/pinctrl-single.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ struct pcs_name {
* @foff: value to turn mux off
* @fmax: max number of functions in fmask
* @is_pinconf: whether supports pinconf
* @bits_per_pin:number of bits per pin
* @names: array of register names for pins
* @pins: physical pins on the SoC
* @pgtree: pingroup index radix tree
Expand Down Expand Up @@ -190,6 +191,7 @@ struct pcs_device {
unsigned fmax;
bool bits_per_mux;
bool is_pinconf;
unsigned bits_per_pin;
struct pcs_name *names;
struct pcs_data pins;
struct radix_tree_root pgtree;
Expand Down Expand Up @@ -431,10 +433,11 @@ static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector,

vals = &func->vals[i];
val = pcs->read(vals->reg);
if (!vals->mask)
mask = pcs->fmask;

if (pcs->bits_per_mux)
mask = vals->mask;
else
mask = pcs->fmask & vals->mask;
mask = pcs->fmask;

val &= ~mask;
val |= (vals->val & mask);
Expand Down Expand Up @@ -779,7 +782,13 @@ static int pcs_allocate_pin_table(struct pcs_device *pcs)
int mux_bytes, nr_pins, i;

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);
pcs->pins.pa = devm_kzalloc(pcs->dev,
Expand All @@ -800,8 +809,14 @@ static int pcs_allocate_pin_table(struct pcs_device *pcs)
for (i = 0; i < pcs->desc.npins; i++) {
unsigned offset;
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);
if (res < 0) {
dev_err(pcs->dev, "error adding pins: %i\n", res);
Expand Down Expand Up @@ -919,7 +934,10 @@ static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset)
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;
}
Expand Down Expand Up @@ -1097,29 +1115,18 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
{
struct pcs_func_vals *vals;
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;

if (pcs->bits_per_mux) {
params = 3;
mux = of_get_property(np, PCS_MUX_BITS_NAME, &size);
} else {
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);
mux = of_get_property(np, PCS_MUX_PINS_NAME, &size);
if ((!mux) || (size < sizeof(*mux) * 2)) {
dev_err(pcs->dev, "bad data for mux %s\n",
np->name);
return -EINVAL;
}

size /= sizeof(*mux); /* Number of elements in array */
rows = size / params;
rows = size / 2;

vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows, GFP_KERNEL);
if (!vals)
Expand All @@ -1137,10 +1144,6 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
val = be32_to_cpup(mux + index++);
vals[found].reg = pcs->base + offset;
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);
if (pin < 0) {
Expand Down Expand Up @@ -1184,6 +1187,125 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
free_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:
devm_kfree(pcs->dev, vals);

Expand Down Expand Up @@ -1219,12 +1341,22 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev,
goto free_map;
}

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;
if (pcs->bits_per_mux) {
ret = pcs_parse_bits_in_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;
}
} 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;
Expand Down

0 comments on commit 4e7e801

Please sign in to comment.