Add simple pinctrl driver using device tree data. Currently this driver only works on omap2+ series of processors, where there is either an 8 or 16-bit padconf 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. Signed-off-by: Tony Lindgren <tony@xxxxxxxxxxx> --- Here's this one finally updated to use the common pinctrl bindings. That sure cleaned up a bunch of the nasty things in this driver :) Nice job Stephen! --- diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-simple.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-simple.txt new file mode 100644 index 0000000..7b32263 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-simple.txt @@ -0,0 +1,125 @@ +Generic simple device tree based pinctrl driver + +Required properties: +- compatible : one of: + - "pinctrl-simple" + - "ti,omap2420-padconf" + - "ti,omap2430-padconf" + - "ti,omap3-padconf" + - "ti,omap4-padconf" +- reg : offset and length of the register set for the mux registers +- #pinctrl-cells : width of the pinctrl 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 + +This driver uses the common pinctrl bindings as specified in +pinctrl-bindings.txt document in this directory. The common bindings are used +to specify the client device states using pinctrl-0 and pinctrl-names entries. + +This driver supports parsing one or more pinctrl functions as the subnodes of +the pinctrl driver entry. One or more registers can be specified for each +function, see uart2 and uart3 examples below. If you are concerned about boot +time, parsing multiple registers in a single function is slightly faster. + +For setting all static board specific pins, see the pinmux_board_pins example +below. If you are concerned about the boot time, set up the static pins in +the bootloader, and only set up selected pins as device tree entries. + +This driver assumes currently that there is one register for each pin. If you +have some pins with more complicated configuration, you can set up a separate +hardware specific driver for those pins. + +Example: + +/* SoC common file, such as omap4.dtsi */ +omap4_pmx_core: pinmux@4a100040 { + compatible = "ti,omap4-padconf"; + reg = <0x4a100040 0x0196>; + #address-cells = <1>; + #size-cells = <0>; + #pinctrl-cells = <2>; + pinctrl-simple,register-width = <16>; + pinctrl-simple,function-mask = <0x7>; + pinctrl-simple,function-off = <0xffffffff>; + pinctrl-simple,pinconf-mask = <0xfff8>; +}; + +omap4_pmx_wkup: pinmux@4a31e040 { + compatible = "ti,omap4-padconf"; + reg = <0x4a31e040 0x0038>; + #address-cells = <1>; + #size-cells = <0>; + #pinctrl-cells = <2>; + pinctrl-simple,register-width = <16>; + pinctrl-simple,function-mask = <0x7>; + pinctrl-simple,function-off = <0xffffffff>; + pinctrl-simple,pinconf-mask = <0xfff8>; +}; + + +/* board specific .dts file, such as omap4-sdp.dts */ +&omap4_pmx_core { + + /* + * map all board specific static pins enabled by the pinctrl driver + * itself during the boot (or just set them up in the bootloader) + */ + pinctrl-names = "default"; + pinctrl-0 = <&board_pins>; + + board_pins: pinmux_board_pins { + board_static_pins { + pinctrl-simple,cells = < + 0x6c 0xf /* CSI21_DX3 OMAP_PIN_OUTPUT | OMAP_MUX_MODE7 */ + 0x6e 0xf /* CSI21_DY3 OMAP_PIN_OUTPUT | OMAP_MUX_MODE7 */ + 0x70 0xf /* CSI21_DX4 OMAP_PIN_OUTPUT | OMAP_MUX_MODE7 */ + 0x72 0xf /* CSI21_DY4 OMAP_PIN_OUTPUT | OMAP_MUX_MODE7 */ + >; + }; + }; + + + /* map all uart2 pins as a single function */ + uart2_pins: pinmux_uart2_pins { + uart2_pins { + pinctrl-simple,cells = < + 0xd8 0x118 /* UART2_CTS OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0 */ + 0xda 0 /* UART2_RTS OMAP_PIN_OUTPUT | OMAP_MUX_MODE0 */ + 0xdc 0x118 /* UART2_RX OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0 */ + 0xde 0 /* UART2_TX OMAP_PIN_OUTPUT | OMAP_MUX_MODE0 */ + >; + }; + }; + + /* map all uart3 pins as separate functions */ + uart3_pins: pinmux_uart3_pins { + uart3_cts_rctx.uart3_cts_rctx { + pinctrl-simple,cells = <0x100 0x118>; /* OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0 */ + }; + + uart3_rts_sd.uart3_rts_sd { + pinctrl-simple,cells = <0x102 0>; /* OMAP_PIN_OUTPUT | OMAP_MUX_MODE0 */ + }; + + uart3_rx_irrx.uart3_rx_irrx { + pinctrl-simple,cells = <0x104 0x100>; /* OMAP_PIN_INPUT | OMAP_MUX_MODE0 */ + }; + + uart3_tx_irtx.uart3_tx_irtx { + pinctrl-simple,cells = <0x106 0>; /* OMAP_PIN_OUTPUT | OMAP_MUX_MODE0 */ + }; + }; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins>; +}; + diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index f73a5ea..df01cc5 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -83,6 +83,14 @@ 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 pinctrl driver" + depends on OF + select PINMUX + select PINCONF + help + This selects the device tree based generic pinctrl driver. + endmenu endif diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 8e3c95a..1636faa 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.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..9d24109 --- /dev/null +++ b/drivers/pinctrl/pinctrl-simple.c @@ -0,0 +1,1027 @@ +/* + * Generic simple device tree based pinctrl 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 "core.h" + +#define DRIVER_NAME "pinctrl-simple" +#define PCS_MUX_NAME "pinctrl-simple,cells" +#define PCS_MUX_CELLS "#pinctrl-cells" +#define PCS_REG_NAME_LEN ((sizeof(unsigned long) * 2) + 1) + +/** + * struct pcs_pingroup - pingroups for a function + * @np: pingroup device node pointer + * @name: pingroup name + * @gpins: array of the pins in the group + * @ngpins: number of pins in the group + */ +struct pcs_pingroup { + struct device_node *np; + const char *name; + int *gpins; + int ngpins; + struct list_head node; +}; + +/** + * struct pcs_func_vals - mux function register offset and value pair + * @reg: register virtual address + * @defval: default value + */ +struct pcs_func_vals { + void __iomem *reg; + unsigned defval; +}; + +/** + * struct pcs_function - pinctrl function + * @name: pinctrl function name + * @vals: register and vals 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 + */ +struct pcs_function { + const char *name; + struct pcs_func_vals *vals; + unsigned nvals; + const char **pgnames; + int npgnames; + struct list_head node; +}; + +/** + * struct pcs_data - wrapper for data needed by pinctrl framework + * @pa: pindesc array + * @cur: index to current element + */ +struct pcs_data { + struct pinctrl_pin_desc *pa; + int cur; +}; + +/** + * struct pcs_name - register name for a pin + * @name: name of the pinctrl register + */ +struct pcs_name { + char name[PCS_REG_NAME_LEN]; +}; + +/** + * struct pcs_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 + * @mutex: mutex protecting the lists + * @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 + * @pgtree: pingroup index radix tree + * @ftree: function index radix tree + * @pingroups: list of pingroups + * @functions: list of functions + * @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 pcs_device { + struct resource *res; + void __iomem *base; + unsigned size; + struct device *dev; + struct pinctrl_dev *pctl; + struct mutex mutex; + + unsigned width; + unsigned fmask; + unsigned fshift; + unsigned foff; + unsigned cmask; + unsigned fmax; + unsigned cells; + + struct pcs_name *names; + struct pcs_data pins; + struct radix_tree_root pgtree; + struct radix_tree_root ftree; + + struct list_head pingroups; + struct list_head functions; + + unsigned ngroups; + unsigned nfuncs; + + struct pinctrl_desc *desc; + + unsigned (*read)(void __iomem *reg); + void (*write)(unsigned val, void __iomem *reg); +}; + +static unsigned __maybe_unused pcs_readb(void __iomem *reg) +{ + return readb(reg); +} + +static unsigned __maybe_unused pcs_readw(void __iomem *reg) +{ + return readw(reg); +} + +static unsigned __maybe_unused pcs_readl(void __iomem *reg) +{ + return readl(reg); +} + +static void __maybe_unused pcs_writeb(unsigned val, void __iomem *reg) +{ + writeb(val, reg); +} + +static void __maybe_unused pcs_writew(unsigned val, void __iomem *reg) +{ + writew(val, reg); +} + +static void __maybe_unused pcs_writel(unsigned val, void __iomem *reg) +{ + writel(val, reg); +} + +static int pcs_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct pcs_device *pcs; + + pcs = pinctrl_dev_get_drvdata(pctldev); + + return pcs->ngroups; +} + +static const char *pcs_get_group_name(struct pinctrl_dev *pctldev, + unsigned gselector) +{ + struct pcs_device *pcs; + struct pcs_pingroup *group; + + pcs = pinctrl_dev_get_drvdata(pctldev); + group = radix_tree_lookup(&pcs->pgtree, gselector); + if (!group) { + dev_err(pcs->dev, "%s could not find pingroup%i\n", + __func__, gselector); + return NULL; + } + + return group->name; +} + +static int pcs_get_group_pins(struct pinctrl_dev *pctldev, + unsigned gselector, + const unsigned **pins, + unsigned *npins) +{ + struct pcs_device *pcs; + struct pcs_pingroup *group; + + pcs = pinctrl_dev_get_drvdata(pctldev); + group = radix_tree_lookup(&pcs->pgtree, gselector); + if (!group) { + dev_err(pcs->dev, "%s could not find pingroup%i\n", + __func__, gselector); + return -EINVAL; + } + + *pins = group->gpins; + *npins = group->ngpins; + + return 0; +} + +static void pcs_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned offset) +{ + seq_printf(s, " " DRIVER_NAME); +} + +static void pcs_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + struct pcs_device *pcs; + + pcs = pinctrl_dev_get_drvdata(pctldev); + devm_kfree(pcs->dev, map); +} + +static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps); + +static struct pinctrl_ops pcs_pinctrl_ops = { + .get_groups_count = pcs_get_groups_count, + .get_group_name = pcs_get_group_name, + .get_group_pins = pcs_get_group_pins, + .pin_dbg_show = pcs_pin_dbg_show, + .dt_node_to_map = pcs_dt_node_to_map, + .dt_free_map = pcs_dt_free_map, +}; + +static int pcs_get_functions_count(struct pinctrl_dev *pctldev) +{ + struct pcs_device *pcs; + + pcs = pinctrl_dev_get_drvdata(pctldev); + + return pcs->nfuncs; +} + +static const char *pcs_get_function_name(struct pinctrl_dev *pctldev, + unsigned fselector) +{ + struct pcs_device *pcs; + struct pcs_function *func; + + pcs = pinctrl_dev_get_drvdata(pctldev); + func = radix_tree_lookup(&pcs->ftree, fselector); + if (!func) { + dev_err(pcs->dev, "%s could not find function%i\n", + __func__, fselector); + return NULL; + } + + return func->name; +} + +static int pcs_get_function_groups(struct pinctrl_dev *pctldev, + unsigned fselector, + const char * const **groups, + unsigned * const ngroups) +{ + struct pcs_device *pcs; + struct pcs_function *func; + + pcs = pinctrl_dev_get_drvdata(pctldev); + func = radix_tree_lookup(&pcs->ftree, fselector); + if (!func) { + dev_err(pcs->dev, "%s could not find function%i\n", + __func__, fselector); + return -EINVAL; + } + *groups = func->pgnames; + *ngroups = func->npgnames; + + return 0; +} + +static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector, + unsigned group) +{ + struct pcs_device *pcs; + struct pcs_function *func; + int i; + + pcs = pinctrl_dev_get_drvdata(pctldev); + func = radix_tree_lookup(&pcs->ftree, fselector); + if (!func) + return -EINVAL; + + dev_dbg(pcs->dev, "enabling function%i %s\n", + fselector, func->name); + + for (i = 0; i < func->nvals; i++) { + struct pcs_func_vals *vals; + unsigned val; + + vals = &func->vals[i]; + val = pcs->read(vals->reg); + val &= ~(pcs->cmask | pcs->fmask); + val |= vals->defval; + pcs->write(val, vals->reg); + } + + return 0; +} + +static void pcs_disable(struct pinctrl_dev *pctldev, unsigned fselector, + unsigned group) +{ + struct pcs_device *pcs; + struct pcs_function *func; + int i; + + pcs = pinctrl_dev_get_drvdata(pctldev); + func = radix_tree_lookup(&pcs->ftree, fselector); + if (!func) { + dev_err(pcs->dev, "%s could not find function%i\n", + __func__, fselector); + return; + } + + /* + * Do not touch modes if off mode is larger than supported + * modes. Some hardware does not have clearly defined off modes. + */ + if ((pcs->foff << pcs->fshift) > pcs->fshift) { + dev_dbg(pcs->dev, "not updating mode for disable\n"); + return; + } + + dev_dbg(pcs->dev, "disabling function%i %s\n", + fselector, func->name); + + for (i = 0; i < func->nvals; i++) { + struct pcs_func_vals *vals; + unsigned val; + + vals = &func->vals[i]; + val = pcs->read(vals->reg); + val &= ~(pcs->cmask | pcs->fmask); + val |= pcs->foff << pcs->fshift; + pcs->write(val, vals->reg); + } +} + +static int pcs_request_gpio(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned offset) +{ + return -ENOTSUPP; +} + +static struct pinmux_ops pcs_pinmux_ops = { + .get_functions_count = pcs_get_functions_count, + .get_function_name = pcs_get_function_name, + .get_function_groups = pcs_get_function_groups, + .enable = pcs_enable, + .disable = pcs_disable, + .gpio_request_enable = pcs_request_gpio, +}; + +static int pcs_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long *config) +{ + return -ENOTSUPP; +} + +static int pcs_pinconf_set(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long config) +{ + return -ENOTSUPP; +} + +static int pcs_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned group, unsigned long *config) +{ + return -ENOTSUPP; +} + +static int pcs_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned group, unsigned long config) +{ + return -ENOTSUPP; +} + +static void pcs_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned offset) +{ +} + +static void pcs_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned selector) +{ +} + +static struct pinconf_ops pcs_pinconf_ops = { + .pin_config_get = pcs_pinconf_get, + .pin_config_set = pcs_pinconf_set, + .pin_config_group_get = pcs_pinconf_group_get, + .pin_config_group_set = pcs_pinconf_group_set, + .pin_config_dbg_show = pcs_pinconf_dbg_show, + .pin_config_group_dbg_show = pcs_pinconf_group_dbg_show, +}; + +/** + * pcs_add_pin() - add a pin to the static per controller pin array + * @pcs: pcs driver instance + * @offset: register offset from base + */ +static int __devinit pcs_add_pin(struct pcs_device *pcs, unsigned offset) +{ + struct pinctrl_pin_desc *pin; + struct pcs_name *pn; + char *name; + int i; + + i = pcs->pins.cur; + if (i >= pcs->desc->npins) { + dev_err(pcs->dev, "too many pins, max %i\n", + pcs->desc->npins); + return -ENOMEM; + } + + pin = &pcs->pins.pa[i]; + pn = &pcs->names[i]; + name = pn->name; + sprintf(name, "%lx", + (unsigned long)pcs->res->start + offset); + pin->name = name; + pin->number = i; + pcs->pins.cur++; + + return i; +} + +/** + * pcs_allocate_pin_table() - adds all the pins for the pinctrl driver + * @pcs: pcs driver instance + * + * In case of errors, resources are freed in pcs_free_resources. + * + * If your hardware needs holes in the address space, then just set + * up multiple driver instances. + * + * REVISIT: Handle the address space better for multiple registers + * per pin type hardware. + */ +static int __devinit 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; + + dev_dbg(pcs->dev, "allocating %i pins\n", nr_pins); + pcs->pins.pa = devm_kzalloc(pcs->dev, + sizeof(*pcs->pins.pa) * nr_pins, + GFP_KERNEL); + if (!pcs->pins.pa) + return -ENOMEM; + + pcs->names = devm_kzalloc(pcs->dev, + sizeof(struct pcs_name) * nr_pins, + GFP_KERNEL); + if (!pcs->names) + return -ENOMEM; + + pcs->desc->pins = pcs->pins.pa; + pcs->desc->npins = nr_pins; + + for (i = 0; i < pcs->desc->npins; i++) { + unsigned offset; + int res; + + offset = i * mux_bytes; + res = pcs_add_pin(pcs, offset); + if (res < 0) { + dev_err(pcs->dev, "error adding pins: %i\n", res); + return res; + } + } + + return 0; +} + +/** + * pcs_add_function() - adds a new function to the function list + * @pcs: pcs 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 struct pcs_function *pcs_add_function(struct pcs_device *pcs, + struct device_node *np, + const char *name, + struct pcs_func_vals *vals, + unsigned nvals, + const char **pgnames, + unsigned npgnames) +{ + struct pcs_function *function; + + function = devm_kzalloc(pcs->dev, sizeof(*function), GFP_KERNEL); + if (!function) + return NULL; + + function->name = name; + function->vals = vals; + function->nvals = nvals; + function->pgnames = pgnames; + function->npgnames = npgnames; + + mutex_lock(&pcs->mutex); + list_add_tail(&function->node, &pcs->functions); + radix_tree_insert(&pcs->ftree, pcs->nfuncs, function); + pcs->nfuncs++; + mutex_unlock(&pcs->mutex); + + return function; +} + +static void pcs_remove_function(struct pcs_device *pcs, + struct pcs_function *function) +{ + int i; + + mutex_lock(&pcs->mutex); + for (i = 0; i < pcs->nfuncs; i++) { + struct pcs_function *found; + + found = radix_tree_lookup(&pcs->ftree, i); + if (found == function) + radix_tree_delete(&pcs->ftree, i); + } + list_del(&function->node); + mutex_unlock(&pcs->mutex); +} + +/** + * pcs_add_pingroup() - add a pingroup to the pingroup list + * @pcs: pcs 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 pcs_add_pingroup(struct pcs_device *pcs, + struct device_node *np, + const char *name, + int *gpins, + int ngpins) +{ + struct pcs_pingroup *pingroup; + + pingroup = devm_kzalloc(pcs->dev, sizeof(*pingroup), GFP_KERNEL); + if (!pingroup) + return -ENOMEM; + + pingroup->name = name; + pingroup->np = np; + pingroup->gpins = gpins; + pingroup->ngpins = ngpins; + + mutex_lock(&pcs->mutex); + list_add_tail(&pingroup->node, &pcs->pingroups); + radix_tree_insert(&pcs->pgtree, pcs->ngroups, pingroup); + pcs->ngroups++; + mutex_unlock(&pcs->mutex); + + return 0; +} + +/** + * pcs_get_pin_by_offset() - get a pin index based on the register offset + * @pcs: pcs driver instance + * @offset: register offset from the base + * + * Note that this is OK as long as the pins are in a static array. + */ +static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset) +{ + unsigned index; + + if (offset >= pcs->size) { + dev_err(pcs->dev, "mux offset out of range: 0x%x (0x%x)\n", + offset, pcs->size); + return -EINVAL; + } + + index = offset / (pcs->width / BITS_PER_BYTE); + + return index; +} + +/** + * smux_parse_one_pinctrl_entry() - parses a device tree mux entry + * @pcs: pinctrl driver instance + * @np: device node of the mux entry + * @map: map entry + * @pgnames: pingroup names + * + * Note that this currently supports only #pinctrl-cells = 2. + * This could be improved to parse controllers that have additional + * auxilary registers per mux. + */ +static int __init pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, + struct device_node *np, + struct pinctrl_map **map, + const char **pgnames) +{ + struct pcs_func_vals *vals; + const __be32 *mux; + int size, rows, *pins, index = 0, found = 0, res = -ENOMEM; + struct pcs_function *function; + + if (pcs->cells != 2) { + dev_err(pcs->dev, "unhandled %s: %i\n", PCS_MUX_CELLS, + pcs->cells); + return -EINVAL; + } + + mux = of_get_property(np, PCS_MUX_NAME, &size); + if ((!mux) || (size < sizeof(*mux) * pcs->cells)) { + dev_err(pcs->dev, "bad data for mux %s\n", + np->name); + return -EINVAL; + } + + size /= sizeof(*mux); /* Number of elements in array */ + rows = size / pcs->cells; + + vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows, GFP_KERNEL); + if (!vals) + return -ENOMEM; + + pins = devm_kzalloc(pcs->dev, sizeof(*pins) * rows, GFP_KERNEL); + if (!pins) + goto free_vals; + + while (index < size) { + unsigned offset, defval; + int pin; + + offset = be32_to_cpup(mux + index++); + defval = be32_to_cpup(mux + index++); + vals[found].reg = pcs->base + offset; + vals[found].defval = defval; + + 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; + } + + 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; + + return 0; + +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 + * @np_config: device tree pinmux entry + * @map: array of map entries + * @num_maps: number of maps + */ +static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct pcs_device *pcs; + struct device_node *np; + struct pinctrl_map *cur_map; + const char **pgnames, **cur_name; + int ret, found_maps = 0, added_maps = 0; + + pcs = pinctrl_dev_get_drvdata(pctldev); + + for_each_child_of_node(np_config, np) + found_maps++; + + *map = devm_kzalloc(pcs->dev, sizeof(**map) * found_maps, + GFP_KERNEL); + if (!map) + return -ENOMEM; + + cur_map = *map; + *num_maps = 0; + + pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames) * found_maps, + GFP_KERNEL); + if (!pgnames) { + devm_kfree(pcs->dev, *map); + return -ENOMEM; + } + + cur_name = pgnames; + + for_each_child_of_node(np_config, np) { + ret = pcs_parse_one_pinctrl_entry(pcs, np, &cur_map, cur_name); + if (ret < 0) { + dev_err(pcs->dev, "added only %i/%i entries for %s\n", + added_maps, found_maps, np_config->name); + break; + } + cur_map++; + cur_name++; + added_maps++; + } + + *num_maps = added_maps; + + return 0; +} + +/** + * pcs_free_funcs() - free memory used by functions + * @pcs: pcs driver instance + */ +static void pcs_free_funcs(struct pcs_device *pcs) +{ + struct list_head *pos, *tmp; + int i; + + mutex_lock(&pcs->mutex); + for (i = 0; i < pcs->nfuncs; i++) { + struct pcs_function *func; + + func = radix_tree_lookup(&pcs->ftree, i); + if (!func) + continue; + radix_tree_delete(&pcs->ftree, i); + } + list_for_each_safe(pos, tmp, &pcs->functions) { + struct pcs_function *function; + + function = list_entry(pos, struct pcs_function, node); + list_del(&function->node); + } + mutex_unlock(&pcs->mutex); +} + +/** + * pcs_free_pingroups() - free memory used by pingroups + * @pcs: pcs driver instance + */ +static void pcs_free_pingroups(struct pcs_device *pcs) +{ + struct list_head *pos, *tmp; + int i; + + mutex_lock(&pcs->mutex); + for (i = 0; i < pcs->ngroups; i++) { + struct pcs_pingroup *pingroup; + + pingroup = radix_tree_lookup(&pcs->pgtree, i); + if (!pingroup) + continue; + radix_tree_delete(&pcs->pgtree, i); + } + list_for_each_safe(pos, tmp, &pcs->pingroups) { + struct pcs_pingroup *pingroup; + + pingroup = list_entry(pos, struct pcs_pingroup, node); + list_del(&pingroup->node); + } + mutex_unlock(&pcs->mutex); +} + +/** + * pcs_free_resources() - free memory used by this driver + * @pcs: pcs driver instance + */ +static void pcs_free_resources(struct pcs_device *pcs) +{ + if (pcs->pctl) + pinctrl_unregister(pcs->pctl); + + pcs_free_funcs(pcs); + pcs_free_pingroups(pcs); +} + +/** + * pcs_register() - initializes and registers with pinctrl framework + * @pcs: pcs driver instance + */ +static int __devinit pcs_register(struct pcs_device *pcs) +{ + int ret; + + if (!pcs->dev->of_node) + return -ENODEV; + + pcs->desc = devm_kzalloc(pcs->dev, sizeof(*pcs->desc), GFP_KERNEL); + if (!pcs->desc) + return -ENOMEM; + pcs->desc->name = DRIVER_NAME; + pcs->desc->pctlops = &pcs_pinctrl_ops; + pcs->desc->pmxops = &pcs_pinmux_ops; + pcs->desc->confops = &pcs_pinconf_ops; + pcs->desc->owner = THIS_MODULE; + + ret = pcs_allocate_pin_table(pcs); + if (ret < 0) + goto free; + + pcs->pctl = pinctrl_register(pcs->desc, pcs->dev, pcs); + if (!pcs->pctl) { + dev_err(pcs->dev, "could not register simple pinctrl driver\n"); + ret = -EINVAL; + goto free; + } + + dev_info(pcs->dev, "%i pins at pa %p size %u\n", + pcs->desc->npins, pcs->base, pcs->size); + + return 0; + +free: + pcs_free_resources(pcs); + + return ret; +} + +#define PCS_GET_PROP_U32(name, reg, err) \ + do { \ + ret = of_property_read_u32(np, name, reg); \ + if (ret) { \ + dev_err(pcs->dev, err); \ + goto out; \ + } \ + } while (0); + +static struct of_device_id pcs_of_match[]; + +/* + * REVISIT: Handle hardware with multiple registers per pin + * better by adding other masks. + */ +static int __devinit pcs_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + struct resource res; + struct pcs_device *pcs; + int ret; + + match = of_match_device(pcs_of_match, &pdev->dev); + if (!match) + return -EINVAL; + + pcs = devm_kzalloc(&pdev->dev, sizeof(*pcs), GFP_KERNEL); + if (!pcs) { + dev_err(&pdev->dev, "could not allocate\n"); + return -ENOMEM; + } + pcs->dev = &pdev->dev; + mutex_init(&pcs->mutex); + INIT_LIST_HEAD(&pcs->pingroups); + INIT_LIST_HEAD(&pcs->functions); + + PCS_GET_PROP_U32("pinctrl-simple,register-width", &pcs->width, + "register width not specified\n"); + + PCS_GET_PROP_U32("pinctrl-simple,function-mask", &pcs->fmask, + "function register mask not specified\n"); + pcs->fshift = ffs(pcs->fmask) - 1; + pcs->fmax = pcs->fmask >> pcs->fshift; + + PCS_GET_PROP_U32("pinctrl-simple,function-off", &pcs->foff, + "function off mode not specified\n"); + + PCS_GET_PROP_U32("pinctrl-simple,pinconf-mask", &pcs->cmask, + "pinconf mask not specified\n"); + + PCS_GET_PROP_U32("#pinctrl-cells", &pcs->cells, + "#pinctrl-cells not specified\n"); + + ret = of_address_to_resource(pdev->dev.of_node, 0, &res); + if (ret) { + dev_err(pcs->dev, "could not get resource\n"); + goto out; + } + + pcs->res = devm_request_mem_region(pcs->dev, res.start, + resource_size(&res), DRIVER_NAME); + if (!pcs->res) { + dev_err(pcs->dev, "could not get mem_region\n"); + ret = -EBUSY; + goto out; + } + + pcs->size = resource_size(pcs->res); + pcs->base = devm_ioremap(pcs->dev, pcs->res->start, pcs->size); + if (!pcs->base) { + dev_err(pcs->dev, "could not ioremap\n"); + ret = -ENODEV; + goto out; + } + + INIT_RADIX_TREE(&pcs->pgtree, GFP_KERNEL); + INIT_RADIX_TREE(&pcs->ftree, GFP_KERNEL); + platform_set_drvdata(pdev, pcs); + + switch (pcs->width) { + case 8: + pcs->read = pcs_readb; + pcs->write = pcs_writeb; + break; + case 16: + pcs->read = pcs_readw; + pcs->write = pcs_writew; + break; + case 32: + pcs->read = pcs_readl; + pcs->write = pcs_writel; + break; + default: + break; + } + + ret = pcs_register(pcs); + if (ret < 0) { + dev_err(pcs->dev, "could not add mux registers: %i\n", ret); + goto out; + } + + return 0; + +out: + return ret; +} + +static int __devexit pcs_remove(struct platform_device *pdev) +{ + struct pcs_device *pcs = platform_get_drvdata(pdev); + + if (!pcs) + return 0; + + pcs_free_resources(pcs); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct of_device_id pcs_of_match[] __devinitdata = { + { .compatible = DRIVER_NAME, }, + { .compatible = "ti,omap2420-padconf", }, + { .compatible = "ti,omap2430-padconf", }, + { .compatible = "ti,omap3-padconf", }, + { .compatible = "ti,omap4-padconf", }, + { }, +}; +MODULE_DEVICE_TABLE(of, pcs_of_match); + +static struct platform_driver pcs_driver = { + .probe = pcs_probe, + .remove = __devexit_p(pcs_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = pcs_of_match, + }, +}; + +module_platform_driver(pcs_driver); + +MODULE_AUTHOR("Tony Lindgren <tony@xxxxxxxxxxx>"); +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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html