* Tony Lindgren <[email protected]> [120203 12:25]:
> Add simple pinmux driver using device tree data.
> +#define REG_NAME_LEN                 (sizeof(unsigned long) + 1)

This is too short to contain the register name. Also the index
for smux->names won't increase properly, so the names are wrong
in debugfs.

> +static struct pinctrl_desc smux_pinctrl_desc = {
> +     .name = DRIVER_NAME,
> +     .pctlops = &smux_pinctrl_ops,
> +     .pmxops = &smux_pinmux_ops,
> +     .confops = &smux_pinconf_ops,
> +     .owner = THIS_MODULE,
> +};

This needs to be dynamically allocated to support multiple
driver instances as it also contains pins and npins.

Updated patch below.

Regards,

Tony

From: Tony Lindgren <[email protected]>
Date: Sat, 4 Feb 2012 09:56:24 -0800
Subject: [PATCH] pinctrl: Add simple pinmux driver using device tree data

Add simple pinmux driver using device tree data.

Currently this driver only works on omap2+ series of
processors, where there is either an 8 or 16-bit mux
register for each pin. Support for other similar pinmux
controllers could be added.

Note that this patch does not yet support pinconf_ops
or GPIO. Further, alternative mux modes are not yet
handled.

Signed-off-by: Tony Lindgren <[email protected]>

diff --git a/Documentation/devicetree/bindings/pinmux/pinctrl-simple.txt 
b/Documentation/devicetree/bindings/pinmux/pinctrl-simple.txt
new file mode 100644
index 0000000..ca1a48d
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinmux/pinctrl-simple.txt
@@ -0,0 +1,62 @@
+Generic simple device tree based pinmux driver
+
+Required properties:
+- compatible :  one of:
+       - "pinctrl-simple"
+       - "ti,omap2420-pinmux"
+       - "ti,omap2430-pinmux"
+       - "ti,omap3-pinmux"
+       - "ti,omap4-pinmux"
+- reg : offset and length of the register set for the mux registers
+- #pinmux-cells : width of the pinmux array, currently only 2 is supported
+- pinctrl-simple,register-width : pinmux register access width
+- pinctrl-simple,function-mask : mask of allowed pinmux function bits
+- pinctrl-simple,function-off : function off mode for disabled state
+- pinctrl-simple,pinconf-mask : mask of allowed pinconf bits
+
+Example:
+
+       /* SoC common file, such as omap4.dtsi */
+       omap4_pmx_core: pinmux@4a100040 {
+               compatible = "ti,omap4-pinmux";
+               reg = <0x4a100040 0x0196>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+               #pinmux-cells = <2>;
+               pinctrl-simple,register-width = <16>;
+               pinctrl-simple,function-mask = <0x7>;
+               pinctrl-simple,function-off = <0x7>;
+               pinctrl-simple,pinconf-mask = <0xfff8>;
+       };
+
+       omap4_pmx_wkup: pinmux@4a31e040 {
+               compatible = "ti,omap4-pinmux";
+               reg = <0x4a31e040 0x0038>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+               #pinmux-cells = <2>;
+               pinctrl-simple,register-width = <16>;
+               pinctrl-simple,function-mask = <0x7>;
+               pinctrl-simple,function-off = <0x7>;
+               pinctrl-simple,pinconf-mask = <0xfff8>;
+       };
+
+       uart3: serial@48020000 {
+               compatible = "ti,omap4-uart";
+               ti,hwmods = "uart3";
+               clock-frequency = <48000000>;
+       }
+
+       /* board specific .dts file, such as omap4-sdp.dts */
+       pinmux@4a100040 {
+               pmx_uart3: pinconfig-uart3 {
+                       mux = <0x0104 0x100
+                              0x0106 0x0>;
+                        };
+                };
+       };
+
+       serial@48020000 {
+               pinctrl = <&pmx_uart3>;
+       };
+
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index afaf885..73848b1 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -42,6 +42,12 @@ config PINCTRL_COH901
          COH 901 335 and COH 901 571/3. They contain 3, 5 or 7
          ports of 8 GPIO pins each.
 
+config PINCTRL_SIMPLE
+       tristate "Simple device tree based pinmux driver"
+       depends on OF
+       help
+         This selects the device tree based generic pinmux driver.
+
 endmenu
 
 endif
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 827601c..4b05649 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_PINCONF)           += pinconf.o
 obj-$(CONFIG_PINCTRL_SIRF)     += pinctrl-sirf.o
 obj-$(CONFIG_PINCTRL_U300)     += pinctrl-u300.o
 obj-$(CONFIG_PINCTRL_COH901)   += pinctrl-coh901.o
+obj-$(CONFIG_PINCTRL_SIMPLE)   += pinctrl-simple.o
diff --git a/drivers/pinctrl/pinctrl-simple.c b/drivers/pinctrl/pinctrl-simple.c
new file mode 100644
index 0000000..0018d67
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-simple.c
@@ -0,0 +1,1291 @@
+/*
+ * Generic simple device tree based pinmux driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/list.h>
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/machine.h>
+
+#include "core.h"
+
+#define PMX_MUX_NAME                   "mux"
+#define PMX_PINCTRL_NAME               "pinctrl"
+#define PMX_MUX_CELLS                  "#pinmux-cells"
+#define DRIVER_NAME                    "pictrl-simple"
+#define REG_NAME_LEN                   ((sizeof(unsigned long) * 2) + 1)
+
+static LIST_HEAD(smux_maps);           /* Global device to pinmux map */
+static LIST_HEAD(smux_pingroups);      /* Global list of pingroups */
+static LIST_HEAD(smux_functions);      /* Global list of functions */
+static DEFINE_MUTEX(smux_mutex);
+
+/**
+ * struct smux_pingroup - pingroups for a function
+ * @smux:      pinmux controller instance
+ * @np:                pinggroup device node pointer
+ * @name:      pingroup name
+ * @gpins:     array of the pins in the group
+ * @ngpins:    number of pins in the group
+ */
+struct smux_pingroup {
+       struct smux_device *smux;
+       struct device_node *np;
+       char *name;
+       int *gpins;
+       int ngpins;
+       struct list_head node;
+};
+
+/**
+ * struct smux_func_vals - mux function register offset and default value pair
+ * @reg:       register virtual address
+ * @defval:    default value
+ */
+struct smux_func_vals {
+       void __iomem *reg;
+       unsigned defval;
+};
+
+/**
+ * struct smux_function - pinmux function
+ * @smux:      pinmux controller instance
+ * @np:                mux device node pointer
+ * @name:      pinmux function name
+ * @vals:      register and default values array
+ * @nvals:     number of entries in vals array
+ * @pgnames:   array of pingroup names the function uses
+ * @npgnames:  number of pingroup names the function uses
+ * @node:      list node
+ *
+ * Note that np is needed to match pinctrl entry to mux entry.
+ */
+struct smux_function {
+       struct smux_device *smux;
+       struct device_node *np;
+       char *name;
+       struct smux_func_vals *vals;
+       unsigned nvals;
+       const char **pgnames;
+       int npgnames;
+       struct list_head node;
+};
+
+/**
+ * struct smux_map - wrapper for device to pinmux map
+ * @smux:      pinmux controller instance
+ * @map:       device to pinmux map
+ * @node:      list node
+ */
+struct smux_map {
+       struct smux_device *smux;
+       struct pinmux_map map;
+       struct list_head node;
+};
+
+/**
+ * struct smux_data - data arrays needed by pinctrl framework
+ * @pa:                pindesc array
+ * @ma:                pinmap array
+ * @cur:       index to current element
+ * @max:       last element in the array
+ *
+ */
+struct smux_data {
+       union {
+               struct pinctrl_pin_desc *pa;
+               struct pinmux_map *ma;
+       };
+       int cur;
+       int max;
+};
+
+/**
+ * struct smux_name - register name for a pin
+ * @name:      name of the pinmux register
+ */
+struct smux_name {
+       char name[REG_NAME_LEN];
+};
+
+/**
+ * struct smux_device - mux device instance
+ * @res:       resources
+ * @base:      virtual address of the controller
+ * @size:      size of the ioremapped area
+ * @dev:       device entry
+ * @pctl:      pin controller device
+ * @width:     bits per mux register
+ * @fmask:     function register mask
+ * @fshift:    function register shift
+ * @foff:      value to turn mux off
+ * @cmask:     pinconf mask
+ * @fmax:      max number of functions in fmask
+ * @cells:     width of the mux array
+ * @names:     array of register names for pins
+ * @pins:      physical pins on the SoC
+ * @maps:      device to mux function mappings
+ * @pgtree:    pingroup index radix tree
+ * @ftree:     function index radix tree
+ * @ngroups:   number of pingroups
+ * @nfuncs:    number of functions
+ * @desc:      pin controller descriptor
+ * @read:      register read function to use
+ * @write:     register write function to use
+ */
+struct smux_device {
+       struct resource *res;
+       void __iomem *base;
+       unsigned size;
+       struct device *dev;
+       struct pinctrl_dev *pctl;
+
+       unsigned width;
+       unsigned fmask;
+       unsigned fshift;
+       unsigned foff;
+       unsigned cmask;
+       unsigned fmax;
+       unsigned cells;
+
+       struct smux_name *names;
+       struct smux_data pins;
+       struct smux_data maps;
+       struct radix_tree_root pgtree;
+       struct radix_tree_root ftree;
+       unsigned ngroups;
+       unsigned nfuncs;
+
+       struct pinctrl_desc *desc;
+
+       unsigned (*read)(void __iomem *reg);
+       void (*write)(unsigned val, void __iomem *reg);
+};
+
+static unsigned __maybe_unused smux_readb(void __iomem *reg)
+{
+       return readb(reg);
+}
+
+static unsigned __maybe_unused smux_readw(void __iomem *reg)
+{
+       return readw(reg);
+}
+
+static unsigned __maybe_unused smux_readl(void __iomem *reg)
+{
+       return readl(reg);
+}
+
+static void __maybe_unused smux_writeb(unsigned val, void __iomem *reg)
+{
+       writeb(val, reg);
+}
+
+static void __maybe_unused smux_writew(unsigned val, void __iomem *reg)
+{
+       writew(val, reg);
+}
+
+static void __maybe_unused smux_writel(unsigned val, void __iomem *reg)
+{
+       writel(val, reg);
+}
+
+static int smux_list_groups(struct pinctrl_dev *pctldev, unsigned gselector)
+{
+       struct smux_device *smux;
+
+       smux = pinctrl_dev_get_drvdata(pctldev);
+       if (gselector >= smux->ngroups)
+               return -EINVAL;
+
+       return 0;
+}
+
+static const char *smux_get_group_name(struct pinctrl_dev *pctldev,
+                                       unsigned gselector)
+{
+       struct smux_device *smux;
+       struct smux_pingroup *group;
+
+       smux = pinctrl_dev_get_drvdata(pctldev);
+       group = radix_tree_lookup(&smux->pgtree, gselector);
+       if (!group) {
+               dev_err(smux->dev, "%s could not find pingroup%i\n",
+                                       __func__, gselector);
+               return NULL;
+       }
+
+       return group->name;
+}
+
+static int smux_get_group_pins(struct pinctrl_dev *pctldev,
+                                       unsigned gselector,
+                                       const unsigned **pins,
+                                       unsigned *npins)
+{
+       struct smux_device *smux;
+       struct smux_pingroup *group;
+
+       smux = pinctrl_dev_get_drvdata(pctldev);
+       group = radix_tree_lookup(&smux->pgtree, gselector);
+       if (!group) {
+               dev_err(smux->dev, "%s could not find pingroup%i\n",
+                                       __func__, gselector);
+               return -EINVAL;
+       }
+
+       *pins = group->gpins;
+       *npins = group->ngpins;
+
+       return 0;
+}
+
+static void smux_pin_dbg_show(struct pinctrl_dev *pctldev,
+                                       struct seq_file *s,
+                                       unsigned offset)
+{
+       seq_printf(s, " " DRIVER_NAME);
+}
+
+static struct pinctrl_ops smux_pinctrl_ops = {
+       .list_groups = smux_list_groups,
+       .get_group_name = smux_get_group_name,
+       .get_group_pins = smux_get_group_pins,
+       .pin_dbg_show = smux_pin_dbg_show,
+};
+
+static int smux_list_functions(struct pinctrl_dev *pctldev, unsigned fselector)
+{
+       struct smux_device *smux;
+
+       smux = pinctrl_dev_get_drvdata(pctldev);
+       if (fselector >= smux->nfuncs)
+               return -EINVAL;
+
+       return 0;
+}
+
+static const char *smux_get_function_name(struct pinctrl_dev *pctldev,
+                                               unsigned fselector)
+{
+       struct smux_device *smux;
+       struct smux_function *func;
+
+       smux = pinctrl_dev_get_drvdata(pctldev);
+       func = radix_tree_lookup(&smux->ftree, fselector);
+       if (!func) {
+               dev_err(smux->dev, "%s could not find function%i\n",
+                                       __func__, fselector);
+               return NULL;
+       }
+
+       return func->name;
+}
+
+static int smux_get_function_groups(struct pinctrl_dev *pctldev,
+                                       unsigned fselector,
+                                       const char * const **groups,
+                                       unsigned * const ngroups)
+{
+       struct smux_device *smux;
+       struct smux_function *func;
+
+       smux = pinctrl_dev_get_drvdata(pctldev);
+       func = radix_tree_lookup(&smux->ftree, fselector);
+       if (!func) {
+               dev_err(smux->dev, "%s could not find function%i\n",
+                                       __func__, fselector);
+               return -EINVAL;
+       }
+       *groups = func->pgnames;
+       *ngroups = func->npgnames;
+
+       return 0;
+}
+
+static int smux_enable(struct pinctrl_dev *pctldev, unsigned fselector,
+       unsigned group)
+{
+       struct smux_device *smux;
+       struct smux_function *func;
+       int i;
+
+       smux = pinctrl_dev_get_drvdata(pctldev);
+       func = radix_tree_lookup(&smux->ftree, fselector);
+       if (!func)
+               return -EINVAL;
+
+       dev_dbg(smux->dev, "enabling function%i %s\n",
+               fselector, func->name);
+
+       for (i = 0; i < func->nvals; i++) {
+               struct smux_func_vals *vals;
+               unsigned val;
+
+               vals = &func->vals[i];
+               val = smux->read(vals->reg);
+               val &= ~(smux->cmask | smux->fmask);
+               val |= vals->defval;
+               smux->write(val, vals->reg);
+       }
+
+       return 0;
+}
+
+/*
+ * REVISIT: We may not always have a single disable value for a register,
+ * but this can be handled with alternative modes once the DT binding is
+ * available for those.
+ */
+static void smux_disable(struct pinctrl_dev *pctldev, unsigned fselector,
+                                       unsigned group)
+{
+       struct smux_device *smux;
+       struct smux_function *func;
+       int i;
+
+       smux = pinctrl_dev_get_drvdata(pctldev);
+       func = radix_tree_lookup(&smux->ftree, fselector);
+       if (!func) {
+               dev_err(smux->dev, "%s could not find function%i\n",
+                                       __func__, fselector);
+               return;
+       }
+
+       dev_dbg(smux->dev, "disabling function%i %s\n",
+               fselector, func->name);
+
+       for (i = 0; i < func->nvals; i++) {
+               struct smux_func_vals *vals;
+               unsigned val;
+
+               vals = &func->vals[i];
+               val = smux->read(vals->reg);
+               val &= ~(smux->cmask | smux->fmask);
+               val |= smux->foff << smux->fshift;
+               smux->write(val, vals->reg);
+       }
+}
+
+static int smux_request_gpio(struct pinctrl_dev *pctldev,
+                       struct pinctrl_gpio_range *range, unsigned offset)
+{
+       return -ENOTSUPP;
+}
+
+static struct pinmux_ops smux_pinmux_ops = {
+       .list_functions = smux_list_functions,
+       .get_function_name = smux_get_function_name,
+       .get_function_groups = smux_get_function_groups,
+       .enable = smux_enable,
+       .disable = smux_disable,
+       .gpio_request_enable = smux_request_gpio,
+};
+
+static int smux_pinconf_get(struct pinctrl_dev *pctldev,
+                               unsigned pin, unsigned long *config)
+{
+       return -ENOTSUPP;
+}
+
+static int smux_pinconf_set(struct pinctrl_dev *pctldev,
+                               unsigned pin, unsigned long config)
+{
+       return -ENOTSUPP;
+}
+
+static int smux_pinconf_group_get(struct pinctrl_dev *pctldev,
+                               unsigned group, unsigned long *config)
+{
+       return -ENOTSUPP;
+}
+
+static int smux_pinconf_group_set(struct pinctrl_dev *pctldev,
+                               unsigned group, unsigned long config)
+{
+       return -ENOTSUPP;
+}
+
+static void smux_pinconf_dbg_show(struct pinctrl_dev *pctldev,
+                               struct seq_file *s, unsigned offset)
+{
+}
+
+static void smux_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
+                               struct seq_file *s, unsigned selector)
+{
+}
+
+static struct pinconf_ops smux_pinconf_ops = {
+       .pin_config_get = smux_pinconf_get,
+       .pin_config_set = smux_pinconf_set,
+       .pin_config_group_get = smux_pinconf_group_get,
+       .pin_config_group_set = smux_pinconf_group_set,
+       .pin_config_dbg_show = smux_pinconf_dbg_show,
+       .pin_config_group_dbg_show = smux_pinconf_group_dbg_show,
+};
+
+/**
+ * smux_add_pin() - add a pin to the static per controller pin array
+ * @smux: smux driver instance
+ * @offset: register offset from base
+ */
+static int __init smux_add_pin(struct smux_device *smux, unsigned offset)
+{
+       struct pinctrl_pin_desc *pin;
+       struct smux_name *pn;
+       char *name;
+       int i;
+
+       i = smux->pins.cur;
+       if (i > smux->pins.max + 1) {
+               dev_err(smux->dev, "too many pins, max %i\n",
+                                       smux->pins.max);
+               return -ENOMEM;
+       }
+
+       pin = &smux->pins.pa[i];
+       pn = &smux->names[i];
+       name = pn->name;
+       sprintf(name, "%lx",
+               (unsigned long)smux->res->start + offset);
+       pin->name = name;
+       pin->number = i;
+       smux->pins.cur++;
+
+       return i;
+}
+
+/**
+ * smux_allocate_pin_table() - adds all the pins for the pinmux controller
+ * @smux: smux driver instance
+ *
+ * In case of errors, resources are freed in smux_free_resources.
+ */
+static int __init smux_allocate_pin_table(struct smux_device *smux)
+{
+       int mux_bytes, i;
+
+       mux_bytes = smux->width / BITS_PER_BYTE;
+       smux->pins.max = smux->size / mux_bytes;
+
+       dev_dbg(smux->dev, "allocating %i muxable pins\n",
+                               smux->pins.max);
+       smux->pins.pa = devm_kzalloc(smux->dev,
+                               sizeof(*smux->pins.pa) * smux->pins.max,
+                               GFP_KERNEL);
+       if (!smux->pins.pa)
+               return -ENOMEM;
+
+       smux->names = devm_kzalloc(smux->dev,
+                               sizeof(struct smux_name) * smux->pins.max,
+                               GFP_KERNEL);
+       if (!smux->names)
+               return -ENOMEM;
+
+       smux->desc->pins = smux->pins.pa;
+       smux->desc->npins = smux->pins.max--;
+
+       for (i = 0; i <= smux->desc->npins; i++) {
+               unsigned offset;
+               int res;
+
+               offset = i * mux_bytes;
+               res = smux_add_pin(smux, offset);
+               if (res < 0) {
+                       dev_err(smux->dev, "error adding pins: %i\n", res);
+                       return res;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * smux_add_function() - adds a new function to the function list
+ * @smux: smux driver instance
+ * @np: device node of the mux entry
+ * @name: name of the function
+ * @vals: array of mux register value pairs used by the function
+ * @nvals: number of mux register value pairs
+ * @pgnames: array of pingroup names for the function
+ * @npgnames: number of pingroup names
+ */
+static int __init smux_add_function(struct smux_device *smux,
+                                       struct device_node *np,
+                                       const char *name,
+                                       struct smux_func_vals *vals,
+                                       unsigned nvals,
+                                       const char **pgnames,
+                                       unsigned npgnames)
+{
+       struct smux_function *function;
+
+       function = devm_kzalloc(smux->dev, sizeof(*function), GFP_KERNEL);
+       if (!function)
+               return -ENOMEM;
+
+       function->name = kstrdup(name, GFP_KERNEL);
+       if (!function->name) {
+               devm_kfree(smux->dev, function);
+               return -ENOMEM;
+       }
+
+       function->np = np;
+       function->smux = smux;
+       function->vals = vals;
+       function->nvals = nvals;
+       function->pgnames = pgnames;
+       function->npgnames = npgnames;
+
+       mutex_lock(&smux_mutex);
+       list_add_tail(&function->node, &smux_functions);
+       radix_tree_insert(&smux->ftree, smux->nfuncs, function);
+       smux->nfuncs++;
+       mutex_unlock(&smux_mutex);
+
+       return 0;
+}
+
+/**
+ * smux_add_pingroup() - add a pingroup to the pingroup list
+ * @smux: smux driver instance
+ * @np: device node of the mux entry
+ * @name: name of the pingroup
+ * @gpins: array of the pins that belong to the group
+ * @ngpins: number of pins in the group
+ */
+static int __init smux_add_pingroup(struct smux_device *smux,
+                                       struct device_node *np,
+                                       const char *name,
+                                       int *gpins,
+                                       int ngpins)
+{
+       struct smux_pingroup *pingroup;
+
+       pingroup = devm_kzalloc(smux->dev, sizeof(*pingroup), GFP_KERNEL);
+       if (!pingroup)
+               return -ENOMEM;
+
+       pingroup->name = kstrdup(name, GFP_KERNEL);
+       if (!pingroup->name) {
+               devm_kfree(smux->dev, pingroup);
+               return -ENOMEM;
+       }
+
+       pingroup->np = np;
+       pingroup->smux = smux;
+       pingroup->gpins = gpins;
+       pingroup->ngpins = ngpins;
+
+       mutex_lock(&smux_mutex);
+       list_add_tail(&pingroup->node, &smux_pingroups);
+       radix_tree_insert(&smux->pgtree, smux->ngroups, pingroup);
+       smux->ngroups++;
+       mutex_unlock(&smux_mutex);
+
+       return 0;
+}
+
+/**
+ * smux_get_pin_by_offset() - get a pin index based on the register offset
+ * @smux: smux driver instance
+ * @offset: register offset from the base
+ *
+ * Note that this is OK for now as the pins are in a static array and
+ * the radix tree number stays the same.
+ */
+static int __init smux_get_pin_by_offset(struct smux_device *smux,
+                                               unsigned offset)
+{
+       unsigned index;
+
+       if (offset >= smux->size) {
+               dev_err(smux->dev, "mux offset out of range: %04x (%04x)\n",
+                       offset, smux->size);
+               return -EINVAL;
+       }
+
+       index = offset / (smux->width / BITS_PER_BYTE);
+
+       return index;
+}
+
+/**
+ * smux_parse_one_pinmux_entry() - parses a device tree mux entry
+ * @smux: smux driver instance
+ * @np: device node of the mux entry
+ *
+ * Note that this currently supports only #pinmux-cells = 2.
+ * This could be improved to parse controllers that have multiple
+ * registers per mux.
+ */
+static int __init smux_parse_one_pinmux_entry(struct smux_device *smux,
+                                               struct device_node *np)
+{
+       struct smux_func_vals *vals;
+       const __be32 *mux;
+       int size, rows, *pins, index = 0, found = 0, res = -ENOMEM;
+       const char **pgnames;
+
+       if (smux->cells != 2) {
+               dev_err(smux->dev, "unhandled %s: %i\n",
+                                       PMX_MUX_CELLS,
+                                       smux->cells);
+               return -EINVAL;
+       }
+
+       mux = of_get_property(np, PMX_MUX_NAME, &size);
+       if ((!mux) || (size < sizeof(*mux) * smux->cells)) {
+               dev_err(smux->dev, "bad data for mux %s\n",
+                                       np->full_name);
+               return -EINVAL;
+       }
+       size /= sizeof(*mux);   /* Number of elements in array */
+       rows = size / smux->cells;
+
+       vals = devm_kzalloc(smux->dev, sizeof(*vals) * rows, GFP_KERNEL);
+       if (!vals)
+               return -ENOMEM;
+
+       pins = devm_kzalloc(smux->dev, sizeof(*pins) * rows, GFP_KERNEL);
+       if (!pins)
+               goto free_vals;
+
+       pgnames = devm_kzalloc(smux->dev, sizeof(*pgnames), GFP_KERNEL);
+       if (!pgnames)
+               goto free_pins;
+
+       while (index < size) {
+               unsigned offset, defval;
+               int pin;
+
+               offset = be32_to_cpup(mux + index++);
+               defval = be32_to_cpup(mux + index++);
+               vals[found].reg = smux->base + offset;
+               vals[found].defval = defval;
+
+               pin = smux_get_pin_by_offset(smux, offset);
+               if (pin < 0) {
+                       dev_info(smux->dev,
+                               "could not add functions for mux %ux\n",
+                                       offset);
+                       break;
+               }
+               pins[found++] = pin;
+       }
+
+       pgnames[0] = np->name;
+       res = smux_add_function(smux, np, np->name, vals, found, pgnames, 1);
+       if (res < 0)
+               goto free_pgnames;
+
+       res = smux_add_pingroup(smux, np, np->name, pins, found);
+       if (res < 0)
+               goto free_pgnames;
+
+       return 0;
+
+free_pgnames:
+       devm_kfree(smux->dev, pgnames);
+
+free_pins:
+       devm_kfree(smux->dev, pins);
+
+free_vals:
+       devm_kfree(smux->dev, vals);
+
+       return res;
+}
+
+/**
+ * smux_get_function_by_node() - find a function by node
+ * @smux: smux driver instance
+ * @np: device node of the mux entry
+ */
+static struct smux_function *
+__init smux_get_function_by_node(struct smux_device *smux,
+                                       struct device_node *np)
+{
+       struct smux_function *function;
+
+       mutex_lock(&smux_mutex);
+       list_for_each_entry(function, &smux_functions, node) {
+               if (smux != function->smux)
+                       continue;
+
+               if (function->np == np)
+                       goto unlock;
+       }
+       function = NULL;
+
+unlock:
+       mutex_unlock(&smux_mutex);
+
+       return function;
+}
+
+/**
+ * smux_rename_function() - renames a function added earlier
+ * @function: existing function
+ * @new_name: new name for the function
+ *
+ * This is needed to avoid adding multiple function entries.
+ * We first parse all the device tree mux entries, and then
+ * parse the device pinconfig entries. This allows us to rename
+ * mux entry to match the device pinconfig naming.
+ */
+static int __init smux_rename_function(struct smux_function *function,
+                                       const char *new_name)
+{
+       char *name;
+
+       if (!new_name)
+               return -EINVAL;
+
+       name = kstrdup(new_name, GFP_KERNEL);
+       if (!name)
+               return -ENOMEM;
+
+       mutex_lock(&smux_mutex);
+       kfree(function->name);
+       function->name = name;
+       mutex_unlock(&smux_mutex);
+
+       return 0;
+}
+
+/**
+ * smux_add_map() - adds a new map to the map list
+ * @smux: smux driver instance
+ * @np: device node of the mux entry
+ *
+ * Note that pctldev will get populated later on as it is
+ * not available until after pinctrl_register().
+ */
+static int __init smux_add_map(struct smux_device *smux,
+                               struct device_node *np)
+{
+       struct platform_device *pdev = NULL;
+       struct smux_map *smap;
+       struct pinmux_map *map;
+       const char *new_name;
+       int ret;
+
+       pdev = of_find_device_by_node(np);
+       new_name = np->full_name;
+
+       mutex_lock(&smux_mutex);
+       list_for_each_entry(smap, &smux_maps, node) {
+               const char *existing_name = smap->map.name;
+
+               if (smap->smux != smux)
+                       continue;
+
+               if (!strcmp(new_name, existing_name)) {
+                       dev_info(smux->dev, "map already exists for %s\n",
+                               existing_name);
+                       ret = -EEXIST;
+                       goto unlock;
+               }
+       }
+
+       smap = devm_kzalloc(smux->dev, sizeof(*smap), GFP_KERNEL);
+       if (!smap) {
+               ret = -ENOMEM;
+               goto unlock;
+       }
+       smap->smux = smux;
+       if (smux->pctl && smux->pctl->dev)
+               smap->map.ctrl_dev = smux->pctl->dev;
+       map = &smap->map;
+
+       map->dev = smux->dev;
+       map->name = kstrdup(new_name, GFP_KERNEL);
+       if (!map->name) {
+               ret = -ENOMEM;
+               devm_kfree(smux->dev, smap);
+               goto unlock;
+       }
+       map->function = map->name;
+       list_add_tail(&smap->node, &smux_maps);
+       ret = 0;
+
+unlock:
+       mutex_unlock(&smux_mutex);
+
+       return ret;
+}
+
+/**
+ * smux_parse_one_pinctrl_entry() - parses a device tree pinctrl entry
+ * @smux: smux driver instance
+ * @np: device node for mux entry
+ */
+static int __init smux_parse_one_pinctrl_entry(struct smux_device *smux,
+                                               struct device_node *np)
+{
+       int count = 0;
+
+       do {
+               struct device_node *mux_np;
+               struct smux_function *function;
+               int res;
+
+               mux_np = of_parse_phandle(np, PMX_PINCTRL_NAME,
+                                       count);
+               if (!mux_np)
+                       break;
+
+               function = smux_get_function_by_node(smux, mux_np);
+               if (!function)
+                       break;
+
+               res = smux_rename_function(function, np->full_name);
+               if (res < 0) {
+                       dev_err(smux->dev, "could not rename %s to %s\n",
+                               function->name, np->full_name);
+                       break;
+               }
+
+               res = smux_add_map(smux, np);
+               if (res < 0) {
+                       dev_err(smux->dev, "could not add mapping for %s\n",
+                                       np->full_name);
+                       break;
+               }
+       } while (++count);
+
+       return count;
+}
+
+/**
+ * smux_load_mux_register() - parses all the device tree mux entries
+ * @smux: smux driver instance
+ */
+static int __init smux_load_mux_registers(struct smux_device *smux)
+{
+       struct device_node *np;
+       int ret;
+
+       for_each_child_of_node(smux->dev->of_node, np) {
+               ret = smux_parse_one_pinmux_entry(smux, np);
+       }
+
+       for_each_node_with_property(np, PMX_PINCTRL_NAME) {
+               ret = smux_parse_one_pinctrl_entry(smux, np);
+       }
+
+       return 0;
+}
+
+/**
+ * smux_populate_map() - populates device map for pinmux_register_mappings()
+ * @smux: smux driver instance
+ *
+ * Note that we need to fill in the ctrl_dev here as it's not known earlier
+ * before pinctrl_register().
+ */
+static int __init smux_populate_map(struct smux_device *smux)
+{
+       struct smux_map *smap;
+       int i = 0, ret;
+
+       mutex_lock(&smux_mutex);
+       list_for_each_entry(smap, &smux_maps, node) {
+               if (smap->smux != smux)
+                       continue;
+               smap->map.ctrl_dev = smux->pctl->dev;
+               i++;
+       }
+       if (!i) {
+               dev_err(smux->dev, "no maps found\n");
+               ret = -ENODEV;
+               goto unlock;
+       }
+
+       dev_dbg(smux->dev, "allocating %i entries for map table\n", i);
+       smux->maps.ma = devm_kzalloc(smux->dev, sizeof(*smux->maps.ma) * i,
+                                       GFP_KERNEL);
+       if (!smux->maps.ma) {
+               ret = -ENOMEM;
+               goto unlock;
+       }
+
+       i = 0;
+       list_for_each_entry(smap, &smux_maps, node) {
+               struct pinmux_map *map = &smux->maps.ma[i];
+
+               if (smap->smux != smux)
+                       continue;
+
+               *map = smap->map;
+               i++;
+       }
+       smux->maps.max = i - 1;
+       ret = i;
+
+unlock:
+       mutex_unlock(&smux_mutex);
+
+       return ret;
+}
+
+/**
+ * smux_free_maps() - free memory used by device maps
+ * @smux: smux driver instance
+ */
+static void smux_free_maps(struct smux_device *smux)
+{
+       struct list_head *pos, *tmp;
+
+       mutex_lock(&smux_mutex);
+       list_for_each_safe(pos, tmp, &smux_maps) {
+               struct smux_map *smap;
+
+               smap = list_entry(pos, struct smux_map, node);
+               if (smap->smux != smux)
+                       continue;
+
+               kfree(smap->map.name);
+               list_del(&smap->node);
+               devm_kfree(smux->dev, smap);
+       }
+       if (smux->maps.ma)
+               devm_kfree(smux->dev, smux->maps.ma);
+       mutex_unlock(&smux_mutex);
+}
+
+/**
+ * smux_free_funcs() - free memory used by functions
+ * @smux: smux driver instance
+ */
+static void smux_free_funcs(struct smux_device *smux)
+{
+       struct list_head *pos, *tmp;
+       int i;
+
+       mutex_lock(&smux_mutex);
+       for (i = 0; i < smux->nfuncs; i++) {
+               struct smux_function *func;
+
+               func = radix_tree_lookup(&smux->ftree, i);
+               if (!func)
+                       continue;
+               radix_tree_delete(&smux->ftree, i);
+       }
+       list_for_each_safe(pos, tmp, &smux_functions) {
+               struct smux_function *function;
+
+               function = list_entry(pos, struct smux_function, node);
+               if (function->smux != smux)
+                       continue;
+
+               kfree(function->name);
+               if (function->vals)
+                       devm_kfree(smux->dev, function->vals);
+               if (function->pgnames)
+                       devm_kfree(smux->dev, function->pgnames);
+               list_del(&function->node);
+               devm_kfree(smux->dev, function);
+       }
+       mutex_unlock(&smux_mutex);
+}
+
+/**
+ * smux_free_pingroups() - free memory used by pingroups
+ * @smux: smux driver instance
+ */
+static void smux_free_pingroups(struct smux_device *smux)
+{
+       struct list_head *pos, *tmp;
+       int i;
+
+       mutex_lock(&smux_mutex);
+       for (i = 0; i < smux->ngroups; i++) {
+               struct smux_pingroup *pingroup;
+
+               pingroup = radix_tree_lookup(&smux->pgtree, i);
+               if (!pingroup)
+                       continue;
+               radix_tree_delete(&smux->pgtree, i);
+       }
+       list_for_each_safe(pos, tmp, &smux_pingroups) {
+               struct smux_pingroup *pingroup;
+
+               pingroup = list_entry(pos, struct smux_pingroup, node);
+               if (pingroup->smux != smux)
+                       continue;
+
+               kfree(pingroup->name);
+               list_del(&pingroup->node);
+               devm_kfree(smux->dev, pingroup);
+       }
+       mutex_unlock(&smux_mutex);
+}
+
+/**
+ * smux_free_resources() - free memory used by this driver
+ * @smux: smux driver instance
+ */
+static void smux_free_resources(struct smux_device *smux)
+{
+       if (smux->pctl)
+               pinctrl_unregister(smux->pctl);
+
+       smux_free_maps(smux);
+       smux_free_funcs(smux);
+       smux_free_pingroups(smux);
+
+       if (smux->desc)
+               devm_kfree(smux->dev, smux->desc);
+       if (smux->pins.pa)
+               devm_kfree(smux->dev, smux->pins.pa);
+       if (smux->names)
+               devm_kfree(smux->dev, smux->names);
+}
+
+/**
+ * smux_register() - initializes and registers with pinctrl framework
+ * @smux: smux driver instance
+ */
+static int __init smux_register(struct smux_device *smux)
+{
+       int ret;
+
+       if (!smux->dev->of_node)
+               return -ENODEV;
+
+       smux->desc = devm_kzalloc(smux->dev, sizeof(*smux->desc), GFP_KERNEL);
+       if (!smux->desc)
+               goto free;
+       smux->desc->name = DRIVER_NAME;
+       smux->desc->pctlops = &smux_pinctrl_ops;
+       smux->desc->pmxops = &smux_pinmux_ops;
+       smux->desc->confops = &smux_pinconf_ops;
+       smux->desc->owner = THIS_MODULE;
+
+       ret = smux_allocate_pin_table(smux);
+       if (ret < 0)
+               goto free;
+
+       ret = smux_load_mux_registers(smux);
+       if (ret < 0)
+               goto free;
+
+       smux->pctl = pinctrl_register(smux->desc, smux->dev, smux);
+       if (!smux->pctl) {
+               dev_err(smux->dev, "could not register simple pinmux driver\n");
+               ret = -EINVAL;
+               goto free;
+       }
+
+       ret = smux_populate_map(smux);
+       if (ret < 0)
+               goto free;
+
+       ret = pinmux_register_mappings(smux->maps.ma,
+                                       smux->maps.max + 1);
+       if (ret < 0)
+               goto free;
+
+       dev_info(smux->dev, "pins: %i pingroups: %i functions: %i maps: %i\n",
+                               smux->pins.max + 1, smux->ngroups,
+                               smux->nfuncs, smux->maps.max + 1);
+
+       return 0;
+
+free:
+       smux_free_resources(smux);
+
+       return ret;
+}
+
+static struct of_device_id smux_of_match[];
+static int __devinit smux_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *match;
+       const u32 *val;
+       struct resource res;
+       struct smux_device *smux;
+       int len, ret;
+
+       match = of_match_device(smux_of_match, &pdev->dev);
+       if (!match)
+               return -EINVAL;
+
+       smux = devm_kzalloc(&pdev->dev, sizeof(*smux), GFP_KERNEL);
+       if (!smux) {
+               dev_err(&pdev->dev, "could not allocate\n");
+               return -ENOMEM;
+       }
+       smux->dev = &pdev->dev;
+
+       val = of_get_property(pdev->dev.of_node,
+                               "pinctrl-simple,register-width", &len);
+       if (!val || len != 4) {
+               dev_err(smux->dev, "mux register width not specified\n");
+               ret = -EINVAL;
+               goto free;
+       }
+       smux->width = be32_to_cpup(val);
+
+       val = of_get_property(pdev->dev.of_node,
+                               "pinctrl-simple,function-mask", &len);
+       if (!val || len != 4) {
+               dev_err(smux->dev, "function register mask not specified\n");
+               ret = -EINVAL;
+               goto free;
+       }
+       smux->fmask = be32_to_cpup(val);
+       smux->fshift = ffs(smux->fmask) - 1;
+       smux->fmax = smux->fmask >> smux->fshift;
+
+       val = of_get_property(pdev->dev.of_node,
+                               "pinctrl-simple,function-off", &len);
+       if (!val || len != 4) {
+               dev_err(smux->dev, "function off mode not specified\n");
+               ret = -EINVAL;
+               goto free;
+       }
+       smux->foff = be32_to_cpup(val);
+
+       val = of_get_property(pdev->dev.of_node,
+                               "pinctrl-simple,pinconf-mask", &len);
+       if (!val || len != 4) {
+               dev_err(smux->dev, "pinconf mask not specified\n");
+               ret = -EINVAL;
+               goto free;
+       }
+       smux->cmask = be32_to_cpup(val);
+
+       val = of_get_property(pdev->dev.of_node, "#pinmux-cells", &len);
+       if (!val || len != 4) {
+               dev_err(smux->dev, "#pinmux-cells not specified\n");
+               ret = -EINVAL;
+               goto free;
+       }
+       smux->cells = be32_to_cpup(val);
+
+       ret = of_address_to_resource(pdev->dev.of_node, 0, &res);
+       if (ret) {
+               dev_err(smux->dev, "could not get resource\n");
+               goto free;
+       }
+
+       smux->res = devm_request_mem_region(smux->dev, res.start,
+                       resource_size(&res), DRIVER_NAME);
+       if (!smux->res) {
+               dev_err(smux->dev, "could not get mem_region\n");
+               ret = -EBUSY;
+               goto free;
+       }
+
+       smux->size = resource_size(smux->res);
+       smux->base = devm_ioremap(smux->dev, smux->res->start, smux->size);
+       if (!smux->base) {
+               dev_err(smux->dev, "could not ioremap\n");
+               ret = -ENODEV;
+               goto release;
+       }
+
+       INIT_RADIX_TREE(&smux->pgtree, GFP_KERNEL);
+       INIT_RADIX_TREE(&smux->ftree, GFP_KERNEL);
+       platform_set_drvdata(pdev, smux);
+
+       switch (smux->width) {
+       case 8:
+               smux->read = smux_readb;
+               smux->write = smux_writeb;
+               break;
+       case 16:
+               smux->read = smux_readw;
+               smux->write = smux_writew;
+               break;
+       case 32:
+               smux->read = smux_readl;
+               smux->write = smux_writel;
+               break;
+       default:
+               break;
+       }
+
+       ret = smux_register(smux);
+       if (ret < 0) {
+               dev_err(smux->dev, "could not add mux registers: %i\n", ret);
+               goto iounmap;
+       }
+
+       return 0;
+
+iounmap:
+       devm_iounmap(smux->dev, smux->base);
+release:
+       devm_release_mem_region(smux->dev,
+                       smux->res->start, resource_size(smux->res));
+free:
+       devm_kfree(smux->dev, smux);
+
+       return ret;
+}
+
+static int __devexit smux_remove(struct platform_device *pdev)
+{
+       struct smux_device *smux = platform_get_drvdata(pdev);
+
+       if (!smux)
+               return 0;
+
+       smux_free_resources(smux);
+       devm_iounmap(smux->dev, smux->base);
+       devm_release_mem_region(smux->dev,
+                       smux->res->start, resource_size(smux->res));
+       platform_set_drvdata(pdev, NULL);
+       devm_kfree(smux->dev, smux);
+
+       return 0;
+}
+
+static struct of_device_id smux_of_match[] __devinitdata = {
+       { .compatible = DRIVER_NAME, },
+       { .compatible = "ti,omap2420-pinmux", },
+       { .compatible = "ti,omap2430-pinmux", },
+       { .compatible = "ti,omap3-pinmux", },
+       { .compatible = "ti,omap4-pinmux", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, smux_of_match);
+
+static struct platform_driver smux_driver = {
+       .probe          = smux_probe,
+       .remove         = __devexit_p(smux_remove),
+       .driver = {
+               .owner          = THIS_MODULE,
+               .name           = DRIVER_NAME,
+               .of_match_table = smux_of_match,
+       },
+};
+
+module_platform_driver(smux_driver);
+
+MODULE_AUTHOR("Tony Lindgren <[email protected]>");
+MODULE_DESCRIPTION("Simple device tree pinctrl driver");
+MODULE_LICENSE("GPL");
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to