pinctrl: pinctrl-single: enhance to configure multiple pins of different modules
authorManjunathappa, Prakash <prakash.pm@ti.com>
Tue, 21 May 2013 14:08:00 +0000 (19:38 +0530)
committerLinus Walleij <linus.walleij@linaro.org>
Sun, 16 Jun 2013 09:56:52 +0000 (11:56 +0200)
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"
(9e605cb68a21d5704839a192a46ebcf387773704),

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>
Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
drivers/pinctrl/pinctrl-single.c

index 08f0c3d..5a02e30 100644 (file)
@@ -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
index b9fa046..9a1ea65 100644 (file)
@@ -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
@@ -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;
@@ -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);
@@ -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,
@@ -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);
@@ -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;
 }
@@ -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)
@@ -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) {
@@ -1189,6 +1192,125 @@ free_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);
+
+       return res;
+}
 /**
  * pcs_dt_node_to_map() - allocates and parses pinctrl maps
  * @pctldev: pinctrl instance
@@ -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;