All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-11 13:58 ` Tony Lindgren
  0 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-11 13:58 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-kernel, linux-arm-kernel, linux-omap, devicetree-discuss,
	Stephen Warren

Add one-register-per-pin type device tree based pinctrl driver.

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 can be added.

Signed-off-by: Tony Lindgren <tony@atomide.com>

---

Here's this one updated, I renamed it from pinctrl-simple to pinctrl-single
to limit it to one-register-per-mux type MMIO controllers. The bindings
for mapping bits in random registers started looking a bit too complex for
this patch to solve. Hopefully this should also solve the issues of different
expectations of what simple means :)

---
diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt
new file mode 100644
index 0000000..21b183e
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt
@@ -0,0 +1,12 @@
+Pinctrl driver for omap2plus using pinctrl-single driver
+
+Required properties:
+- compatible:  one of:
+	- "ti,omap2420-padconf"
+	- "ti,omap2430-padconf"
+	- "ti,omap3-padconf"
+	- "ti,omap4-padconf"
+
+All omaps starting with omap2 are using pinctrl-single driver for
+the padconf registers. See pinctrl-single.txt in this directory for
+more information.
diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
new file mode 100644
index 0000000..6a6abf9
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
@@ -0,0 +1,102 @@
+One-register-per-pin type device tree based pinctrl driver
+
+Required properties:
+- compatible :  one of:
+	- "pinctrl-single"
+- reg : offset and length of the register set for the mux registers
+- pinctrl-single,register-width : pinmux register access width
+- pinctrl-single,function-mask : mask of allowed pinmux function bits
+- pinctrl-single,function-off : function off mode for disabled state
+- pinctrl-single,pinconf-mask : mask of allowed pinconf bits
+
+This driver uses the common pinctrl bindings as specified in the
+pinctrl-bindings.txt document in this directory.
+
+The pinctrl register offsets and default values are specified as pairs
+using pinctrl-single,pins. For example, setting uart2_rx pin on omap2plus
+can be done with:
+
+	pinctrl-single,pins = <0xdc 0x118>;
+
+Where 0xdc is the offset from the pinctrl ioremapped area for the
+uart2_rx register, and 0x118 contains the desired default value for
+for the pin setting it to INPUT_PULLUP | MODE0. See the uart example and
+static board pins example below for more information.
+
+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 that there is only 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.
+
+Note that this driver tries to avoid understanding pin and function
+names because of the extra bloat they would cause especially in the
+omap2plus case. This driver just sets what is specified for the board
+in the .dts file. Further user space debugging tools can be developed
+to decipher the pin and function names using debugfs.
+
+Example:
+
+/* SoC common file, such as omap4.dtsi */
+
+/* first controller instance for pins in core domain */
+omap4_pmx_core: pinmux@4a100040 {
+	compatible = "ti,omap4-padconf";
+	reg = <0x4a100040 0x0196>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0x7>;
+	pinctrl-single,function-off = <0xffffffff>;
+	pinctrl-single,pinconf-mask = <0xfff8>;
+};
+
+/* second controller instance for pins in wkup domain */
+omap4_pmx_wkup: pinmux@4a31e040 {
+	compatible = "ti,omap4-padconf";
+	reg = <0x4a31e040 0x0038>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0x7>;
+	pinctrl-single,function-off = <0xffffffff>;
+	pinctrl-single,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 {
+		pinctrl-single,pins = <
+			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
+			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
+			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
+			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */
+		>;
+	};
+
+	/* map uart2 pins */
+	uart2_pins: pinmux_uart2_pins {
+		pinctrl-single,pins = <
+			0xd8 0x118	/* uart2_cts INPUT_PULLUP | MODE0 */
+			0xda 0		/* uart2_rts OUTPUT | MODE0 */
+			0xdc 0x118	/* uart2_rx INPUT_PULLUP | MODE0 */
+			0xde 0		/* uart2_tx OUTPUT | MODE0 */
+		>;
+	};
+};
+
+&uart2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&uart2_pins>;
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index c6e6ae0..8071a31 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -102,6 +102,14 @@ config PINCTRL_PXA910
 	select PINCTRL_PXA3xx
 	select PINCONF
 
+config PINCTRL_SINGLE
+	tristate "One-register-per-pin type device tree based pinctrl driver"
+	depends on OF
+	select PINMUX
+	select PINCONF
+	help
+	  This selects the device tree based generic pinctrl driver.
+
 config PINCTRL_SIRF
 	bool "CSR SiRFprimaII pin controller driver"
 	depends on ARCH_PRIMA2
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 8c07437..f40b1f8 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK)	+= pinctrl-nomadik.o
 obj-$(CONFIG_PINCTRL_DB8500)	+= pinctrl-nomadik-db8500.o
 obj-$(CONFIG_PINCTRL_PXA168)	+= pinctrl-pxa168.o
 obj-$(CONFIG_PINCTRL_PXA910)	+= pinctrl-pxa910.o
+obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_SIRF)	+= pinctrl-sirf.o
 obj-$(CONFIG_PINCTRL_TEGRA)	+= pinctrl-tegra.o
 obj-$(CONFIG_PINCTRL_TEGRA20)	+= pinctrl-tegra20.o
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
new file mode 100644
index 0000000..531c3f4
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -0,0 +1,1012 @@
+/*
+ * Generic device tree based pinctrl driver for one register per pin
+ * type pinmux controllers
+ *
+ * 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-single"
+#define PCS_MUX_NAME			"pinctrl-single,pins"
+#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
+ * @node:	list node
+ */
+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
+ *
+ * REVISIT: We should be able to drop this eventually by adding
+ * support for registering pins individually in the pinctrl
+ * framework for those drivers that don't need a static array.
+ */
+struct pcs_data {
+	struct pinctrl_pin_desc *pa;
+	int cur;
+};
+
+/**
+ * struct pcs_name - register name for a pin
+ * @name:	name of the pinctrl register
+ *
+ * REVISIT: We may want to make names optional in the pinctrl
+ * framework as some drivers may not care about pin names to
+ * avoid kernel bloat. The pin names can be deciphered by user
+ * space tools using debugfs based on the register address and
+ * SoC packaging information.
+ */
+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
+ * @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;
+	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);
+};
+
+/*
+ * REVISIT: Reads and writes could eventually use regmap or something
+ * generic. But at least on omaps, some mux registers are performance
+ * critical as they may need to be remuxed every time before and after
+ * idle. Adding tests for register access width for every read and
+ * write like regmap is doing is not desired, and caching the registers
+ * does not help in this case.
+ */
+
+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.
+ */
+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 sets of one register + value.
+ */
+static int 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;
+
+	mux = of_get_property(np, PCS_MUX_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 / 2;	/* Each row is a key value pair */
+
+	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;
+	const char **pgnames;
+	int ret;
+
+	pcs = pinctrl_dev_get_drvdata(pctldev);
+
+	*map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL);
+	if (!map)
+		return -ENOMEM;
+
+	*num_maps = 0;
+
+	pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL);
+	if (!pgnames) {
+		ret = -ENOMEM;
+		goto free_map;
+	}
+
+	ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, pgnames);
+	if (ret < 0) {
+		dev_err(pcs->dev, "no pins entries for %s\n",
+			np_config->name);
+		goto free_pgnames;
+	}
+	*num_maps = 1;
+
+	return 0;
+
+free_pgnames:
+	devm_kfree(pcs->dev, pgnames);
+free_map:
+	devm_kfree(pcs->dev, *map);
+
+	return ret;
+}
+
+/**
+ * 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 single 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[];
+
+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-single,register-width", &pcs->width,
+			 "register width not specified\n");
+
+	PCS_GET_PROP_U32("pinctrl-single,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-single,function-off", &pcs->foff,
+			 "function off mode not specified\n");
+
+	PCS_GET_PROP_U32("pinctrl-single,pinconf-mask", &pcs->cmask,
+			 "pinconf mask 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@atomide.com>");
+MODULE_DESCRIPTION("One-register-per-pin type device tree based pinctrl driver");
+MODULE_LICENSE("GPL");

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-11 13:58 ` Tony Lindgren
  0 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-11 13:58 UTC (permalink / raw)
  To: linux-arm-kernel

Add one-register-per-pin type device tree based pinctrl driver.

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 can be added.

Signed-off-by: Tony Lindgren <tony@atomide.com>

---

Here's this one updated, I renamed it from pinctrl-simple to pinctrl-single
to limit it to one-register-per-mux type MMIO controllers. The bindings
for mapping bits in random registers started looking a bit too complex for
this patch to solve. Hopefully this should also solve the issues of different
expectations of what simple means :)

---
diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt
new file mode 100644
index 0000000..21b183e
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt
@@ -0,0 +1,12 @@
+Pinctrl driver for omap2plus using pinctrl-single driver
+
+Required properties:
+- compatible:  one of:
+	- "ti,omap2420-padconf"
+	- "ti,omap2430-padconf"
+	- "ti,omap3-padconf"
+	- "ti,omap4-padconf"
+
+All omaps starting with omap2 are using pinctrl-single driver for
+the padconf registers. See pinctrl-single.txt in this directory for
+more information.
diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
new file mode 100644
index 0000000..6a6abf9
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
@@ -0,0 +1,102 @@
+One-register-per-pin type device tree based pinctrl driver
+
+Required properties:
+- compatible :  one of:
+	- "pinctrl-single"
+- reg : offset and length of the register set for the mux registers
+- pinctrl-single,register-width : pinmux register access width
+- pinctrl-single,function-mask : mask of allowed pinmux function bits
+- pinctrl-single,function-off : function off mode for disabled state
+- pinctrl-single,pinconf-mask : mask of allowed pinconf bits
+
+This driver uses the common pinctrl bindings as specified in the
+pinctrl-bindings.txt document in this directory.
+
+The pinctrl register offsets and default values are specified as pairs
+using pinctrl-single,pins. For example, setting uart2_rx pin on omap2plus
+can be done with:
+
+	pinctrl-single,pins = <0xdc 0x118>;
+
+Where 0xdc is the offset from the pinctrl ioremapped area for the
+uart2_rx register, and 0x118 contains the desired default value for
+for the pin setting it to INPUT_PULLUP | MODE0. See the uart example and
+static board pins example below for more information.
+
+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 that there is only 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.
+
+Note that this driver tries to avoid understanding pin and function
+names because of the extra bloat they would cause especially in the
+omap2plus case. This driver just sets what is specified for the board
+in the .dts file. Further user space debugging tools can be developed
+to decipher the pin and function names using debugfs.
+
+Example:
+
+/* SoC common file, such as omap4.dtsi */
+
+/* first controller instance for pins in core domain */
+omap4_pmx_core: pinmux at 4a100040 {
+	compatible = "ti,omap4-padconf";
+	reg = <0x4a100040 0x0196>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0x7>;
+	pinctrl-single,function-off = <0xffffffff>;
+	pinctrl-single,pinconf-mask = <0xfff8>;
+};
+
+/* second controller instance for pins in wkup domain */
+omap4_pmx_wkup: pinmux at 4a31e040 {
+	compatible = "ti,omap4-padconf";
+	reg = <0x4a31e040 0x0038>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0x7>;
+	pinctrl-single,function-off = <0xffffffff>;
+	pinctrl-single,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 {
+		pinctrl-single,pins = <
+			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
+			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
+			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
+			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */
+		>;
+	};
+
+	/* map uart2 pins */
+	uart2_pins: pinmux_uart2_pins {
+		pinctrl-single,pins = <
+			0xd8 0x118	/* uart2_cts INPUT_PULLUP | MODE0 */
+			0xda 0		/* uart2_rts OUTPUT | MODE0 */
+			0xdc 0x118	/* uart2_rx INPUT_PULLUP | MODE0 */
+			0xde 0		/* uart2_tx OUTPUT | MODE0 */
+		>;
+	};
+};
+
+&uart2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&uart2_pins>;
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index c6e6ae0..8071a31 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -102,6 +102,14 @@ config PINCTRL_PXA910
 	select PINCTRL_PXA3xx
 	select PINCONF
 
+config PINCTRL_SINGLE
+	tristate "One-register-per-pin type device tree based pinctrl driver"
+	depends on OF
+	select PINMUX
+	select PINCONF
+	help
+	  This selects the device tree based generic pinctrl driver.
+
 config PINCTRL_SIRF
 	bool "CSR SiRFprimaII pin controller driver"
 	depends on ARCH_PRIMA2
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 8c07437..f40b1f8 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK)	+= pinctrl-nomadik.o
 obj-$(CONFIG_PINCTRL_DB8500)	+= pinctrl-nomadik-db8500.o
 obj-$(CONFIG_PINCTRL_PXA168)	+= pinctrl-pxa168.o
 obj-$(CONFIG_PINCTRL_PXA910)	+= pinctrl-pxa910.o
+obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_SIRF)	+= pinctrl-sirf.o
 obj-$(CONFIG_PINCTRL_TEGRA)	+= pinctrl-tegra.o
 obj-$(CONFIG_PINCTRL_TEGRA20)	+= pinctrl-tegra20.o
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
new file mode 100644
index 0000000..531c3f4
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -0,0 +1,1012 @@
+/*
+ * Generic device tree based pinctrl driver for one register per pin
+ * type pinmux controllers
+ *
+ * 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-single"
+#define PCS_MUX_NAME			"pinctrl-single,pins"
+#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
+ * @node:	list node
+ */
+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
+ *
+ * REVISIT: We should be able to drop this eventually by adding
+ * support for registering pins individually in the pinctrl
+ * framework for those drivers that don't need a static array.
+ */
+struct pcs_data {
+	struct pinctrl_pin_desc *pa;
+	int cur;
+};
+
+/**
+ * struct pcs_name - register name for a pin
+ * @name:	name of the pinctrl register
+ *
+ * REVISIT: We may want to make names optional in the pinctrl
+ * framework as some drivers may not care about pin names to
+ * avoid kernel bloat. The pin names can be deciphered by user
+ * space tools using debugfs based on the register address and
+ * SoC packaging information.
+ */
+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
+ * @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;
+	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);
+};
+
+/*
+ * REVISIT: Reads and writes could eventually use regmap or something
+ * generic. But at least on omaps, some mux registers are performance
+ * critical as they may need to be remuxed every time before and after
+ * idle. Adding tests for register access width for every read and
+ * write like regmap is doing is not desired, and caching the registers
+ * does not help in this case.
+ */
+
+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.
+ */
+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 sets of one register + value.
+ */
+static int 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;
+
+	mux = of_get_property(np, PCS_MUX_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 / 2;	/* Each row is a key value pair */
+
+	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;
+	const char **pgnames;
+	int ret;
+
+	pcs = pinctrl_dev_get_drvdata(pctldev);
+
+	*map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL);
+	if (!map)
+		return -ENOMEM;
+
+	*num_maps = 0;
+
+	pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL);
+	if (!pgnames) {
+		ret = -ENOMEM;
+		goto free_map;
+	}
+
+	ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, pgnames);
+	if (ret < 0) {
+		dev_err(pcs->dev, "no pins entries for %s\n",
+			np_config->name);
+		goto free_pgnames;
+	}
+	*num_maps = 1;
+
+	return 0;
+
+free_pgnames:
+	devm_kfree(pcs->dev, pgnames);
+free_map:
+	devm_kfree(pcs->dev, *map);
+
+	return ret;
+}
+
+/**
+ * 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 single 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[];
+
+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-single,register-width", &pcs->width,
+			 "register width not specified\n");
+
+	PCS_GET_PROP_U32("pinctrl-single,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-single,function-off", &pcs->foff,
+			 "function off mode not specified\n");
+
+	PCS_GET_PROP_U32("pinctrl-single,pinconf-mask", &pcs->cmask,
+			 "pinconf mask 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@atomide.com>");
+MODULE_DESCRIPTION("One-register-per-pin type device tree based pinctrl driver");
+MODULE_LICENSE("GPL");

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-14 23:12   ` Stephen Warren
  0 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-14 23:12 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Linus Walleij, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren

On 06/11/2012 07:58 AM, Tony Lindgren wrote:
> Add one-register-per-pin type device tree based pinctrl driver.
> 
> 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 can be added.

> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt

I'm not sure why you'd need an explicit OMAP2 binding document or
compatible values if it just uses the plain pinctrl-single binding
unmodified.

> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt

> +- pinctrl-single,function-off : function off mode for disabled state

Might that not vary per register? Admittedly, Tegra isn't something that
could be covered by pinctrl-single, but the safe/off/non-conflicting mux
selection values for Tegra certainly are not all the same value.

> +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits
> +
> +This driver uses the common pinctrl bindings as specified in the
> +pinctrl-bindings.txt document in this directory.
> +
> +The pinctrl register offsets and default values are specified as pairs

Why "default values" not just "values"; they can only be "default" if
you're talking about something that can be changed by some other
optional mechanism, which I don't believe is the case here.

> +using pinctrl-single,pins. For example, setting uart2_rx pin on omap2plus
> +can be done with:
> +
> +	pinctrl-single,pins = <0xdc 0x118>;
> +
> +Where 0xdc is the offset from the pinctrl ioremapped area for the

"ioremapped area" is a Linux-specific term. "register base address"
would be more generic.

> +uart2_rx register, and 0x118 contains the desired default value for
> +for the pin setting it to INPUT_PULLUP | MODE0. See the uart example and

Any mention of "uart2_rx" or "setting it to INPUT_PULLUP | MODE0" is
OMAP-specific; I'd say "0xdc" and just delete "setting it ...".

> +static board pins example below for more information.
> +
> +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.

I'm not sure if it's really appropriate to state that kind of thing in a
DT binding document.

> +This driver assumes that there is only one register for each pin. If you
> +have some pins with more complicated configuration,

OK ...

> you can set up a separate
> +hardware specific driver for those pins.

But for that, it seems more correct to say that pinctrl-single simply
isn't an appropriate model for your HW.

> +Note that this driver tries to avoid understanding pin and function
> +names because of the extra bloat they would cause especially in the
> +omap2plus case. This driver just sets what is specified for the board

Again, I'd remove reference to any specific SoC from a generic binding.

> +in the .dts file. Further user space debugging tools can be developed
> +to decipher the pin and function names using debugfs.
> +
> +Example:
> +
> +/* SoC common file, such as omap4.dtsi */

But just to be clear, having an example for a specific SoC seems fine to
me; just not the generic descriptive text.

I don't see anywhere that describes how the function-mask and
pinconf-mask properties interact with the values in pinctrl-single,pins
property; are both masks OR'd together, then AND'd with the pins values,
then written to the registers? If so, why have 2 masks?

> diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c

> +struct pcs_device {

> +	struct pinctrl_desc *desc;

Why not make that a struct rather than a pointer. That way, you wouldn't
have to dynamically allocate it separately. It's always needed.

> +static int __devinit pcs_probe(struct platform_device *pdev)

> +	ret = of_address_to_resource(pdev->dev.of_node, 0, &res);

It's much more common to use platform_get_resource(); doesn't calling
of_address_to_resource() duplicate all the work that the OF core already
did to set up the platform resource structures?

> +	ret = pcs_register(pcs);

Why not just inline that function here; it's not shared with anything.

> +static int __devexit pcs_remove(struct platform_device *pdev)

> +	platform_set_drvdata(pdev, NULL);

There's no need to explicitly clear that; nothing should be using that
value when the device is removed, so the value shouldn't matter.

> +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", },
> +	{ },
> +};

Shouldn't that contain just one entry for "pinctrl-single", and no others?

> +MODULE_LICENSE("GPL");

The license header implies that should be "GPL v2" not just "GPL" (which
means GPLv2+)

One final comment: A little while before Linaro Connect in San
Francisco, we were all having difficulty coming up with any kind of DT
binding for pinctrl. I had suggested the possibility of creating a
binding which just said "write value X to register Y, write value P to
register Q, ...". This was rejected at connect, because a raw list of
register writes didn't document anything or describe semantics - it was
too much of a binary blob. This driver seems to be doing almost the
exact same thing. In order to avoid that assertion, it'd need to truly
have individual representations of which pins exist, which pingroups
exist, which functions exist, and which functions can be selected onto
which pins/pingroups. That would be a radically different binding. I
wonder what's changed such that this kind of driver wasn't acceptable
then, but is now?

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-14 23:12   ` Stephen Warren
  0 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-14 23:12 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On 06/11/2012 07:58 AM, Tony Lindgren wrote:
> Add one-register-per-pin type device tree based pinctrl driver.
> 
> 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 can be added.

> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt

I'm not sure why you'd need an explicit OMAP2 binding document or
compatible values if it just uses the plain pinctrl-single binding
unmodified.

> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt

> +- pinctrl-single,function-off : function off mode for disabled state

Might that not vary per register? Admittedly, Tegra isn't something that
could be covered by pinctrl-single, but the safe/off/non-conflicting mux
selection values for Tegra certainly are not all the same value.

> +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits
> +
> +This driver uses the common pinctrl bindings as specified in the
> +pinctrl-bindings.txt document in this directory.
> +
> +The pinctrl register offsets and default values are specified as pairs

Why "default values" not just "values"; they can only be "default" if
you're talking about something that can be changed by some other
optional mechanism, which I don't believe is the case here.

> +using pinctrl-single,pins. For example, setting uart2_rx pin on omap2plus
> +can be done with:
> +
> +	pinctrl-single,pins = <0xdc 0x118>;
> +
> +Where 0xdc is the offset from the pinctrl ioremapped area for the

"ioremapped area" is a Linux-specific term. "register base address"
would be more generic.

> +uart2_rx register, and 0x118 contains the desired default value for
> +for the pin setting it to INPUT_PULLUP | MODE0. See the uart example and

Any mention of "uart2_rx" or "setting it to INPUT_PULLUP | MODE0" is
OMAP-specific; I'd say "0xdc" and just delete "setting it ...".

> +static board pins example below for more information.
> +
> +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.

I'm not sure if it's really appropriate to state that kind of thing in a
DT binding document.

> +This driver assumes that there is only one register for each pin. If you
> +have some pins with more complicated configuration,

OK ...

> you can set up a separate
> +hardware specific driver for those pins.

But for that, it seems more correct to say that pinctrl-single simply
isn't an appropriate model for your HW.

> +Note that this driver tries to avoid understanding pin and function
> +names because of the extra bloat they would cause especially in the
> +omap2plus case. This driver just sets what is specified for the board

Again, I'd remove reference to any specific SoC from a generic binding.

> +in the .dts file. Further user space debugging tools can be developed
> +to decipher the pin and function names using debugfs.
> +
> +Example:
> +
> +/* SoC common file, such as omap4.dtsi */

But just to be clear, having an example for a specific SoC seems fine to
me; just not the generic descriptive text.

I don't see anywhere that describes how the function-mask and
pinconf-mask properties interact with the values in pinctrl-single,pins
property; are both masks OR'd together, then AND'd with the pins values,
then written to the registers? If so, why have 2 masks?

> diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c

> +struct pcs_device {

> +	struct pinctrl_desc *desc;

Why not make that a struct rather than a pointer. That way, you wouldn't
have to dynamically allocate it separately. It's always needed.

> +static int __devinit pcs_probe(struct platform_device *pdev)

> +	ret = of_address_to_resource(pdev->dev.of_node, 0, &res);

It's much more common to use platform_get_resource(); doesn't calling
of_address_to_resource() duplicate all the work that the OF core already
did to set up the platform resource structures?

> +	ret = pcs_register(pcs);

Why not just inline that function here; it's not shared with anything.

> +static int __devexit pcs_remove(struct platform_device *pdev)

> +	platform_set_drvdata(pdev, NULL);

There's no need to explicitly clear that; nothing should be using that
value when the device is removed, so the value shouldn't matter.

> +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", },
> +	{ },
> +};

Shouldn't that contain just one entry for "pinctrl-single", and no others?

> +MODULE_LICENSE("GPL");

The license header implies that should be "GPL v2" not just "GPL" (which
means GPLv2+)

One final comment: A little while before Linaro Connect in San
Francisco, we were all having difficulty coming up with any kind of DT
binding for pinctrl. I had suggested the possibility of creating a
binding which just said "write value X to register Y, write value P to
register Q, ...". This was rejected at connect, because a raw list of
register writes didn't document anything or describe semantics - it was
too much of a binary blob. This driver seems to be doing almost the
exact same thing. In order to avoid that assertion, it'd need to truly
have individual representations of which pins exist, which pingroups
exist, which functions exist, and which functions can be selected onto
which pins/pingroups. That would be a radically different binding. I
wonder what's changed such that this kind of driver wasn't acceptable
then, but is now?

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-14 23:12   ` Stephen Warren
  0 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-14 23:12 UTC (permalink / raw)
  To: linux-arm-kernel

On 06/11/2012 07:58 AM, Tony Lindgren wrote:
> Add one-register-per-pin type device tree based pinctrl driver.
> 
> 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 can be added.

> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt

I'm not sure why you'd need an explicit OMAP2 binding document or
compatible values if it just uses the plain pinctrl-single binding
unmodified.

> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt

> +- pinctrl-single,function-off : function off mode for disabled state

Might that not vary per register? Admittedly, Tegra isn't something that
could be covered by pinctrl-single, but the safe/off/non-conflicting mux
selection values for Tegra certainly are not all the same value.

> +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits
> +
> +This driver uses the common pinctrl bindings as specified in the
> +pinctrl-bindings.txt document in this directory.
> +
> +The pinctrl register offsets and default values are specified as pairs

Why "default values" not just "values"; they can only be "default" if
you're talking about something that can be changed by some other
optional mechanism, which I don't believe is the case here.

> +using pinctrl-single,pins. For example, setting uart2_rx pin on omap2plus
> +can be done with:
> +
> +	pinctrl-single,pins = <0xdc 0x118>;
> +
> +Where 0xdc is the offset from the pinctrl ioremapped area for the

"ioremapped area" is a Linux-specific term. "register base address"
would be more generic.

> +uart2_rx register, and 0x118 contains the desired default value for
> +for the pin setting it to INPUT_PULLUP | MODE0. See the uart example and

Any mention of "uart2_rx" or "setting it to INPUT_PULLUP | MODE0" is
OMAP-specific; I'd say "0xdc" and just delete "setting it ...".

> +static board pins example below for more information.
> +
> +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.

I'm not sure if it's really appropriate to state that kind of thing in a
DT binding document.

> +This driver assumes that there is only one register for each pin. If you
> +have some pins with more complicated configuration,

OK ...

> you can set up a separate
> +hardware specific driver for those pins.

But for that, it seems more correct to say that pinctrl-single simply
isn't an appropriate model for your HW.

> +Note that this driver tries to avoid understanding pin and function
> +names because of the extra bloat they would cause especially in the
> +omap2plus case. This driver just sets what is specified for the board

Again, I'd remove reference to any specific SoC from a generic binding.

> +in the .dts file. Further user space debugging tools can be developed
> +to decipher the pin and function names using debugfs.
> +
> +Example:
> +
> +/* SoC common file, such as omap4.dtsi */

But just to be clear, having an example for a specific SoC seems fine to
me; just not the generic descriptive text.

I don't see anywhere that describes how the function-mask and
pinconf-mask properties interact with the values in pinctrl-single,pins
property; are both masks OR'd together, then AND'd with the pins values,
then written to the registers? If so, why have 2 masks?

> diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c

> +struct pcs_device {

> +	struct pinctrl_desc *desc;

Why not make that a struct rather than a pointer. That way, you wouldn't
have to dynamically allocate it separately. It's always needed.

> +static int __devinit pcs_probe(struct platform_device *pdev)

> +	ret = of_address_to_resource(pdev->dev.of_node, 0, &res);

It's much more common to use platform_get_resource(); doesn't calling
of_address_to_resource() duplicate all the work that the OF core already
did to set up the platform resource structures?

> +	ret = pcs_register(pcs);

Why not just inline that function here; it's not shared with anything.

> +static int __devexit pcs_remove(struct platform_device *pdev)

> +	platform_set_drvdata(pdev, NULL);

There's no need to explicitly clear that; nothing should be using that
value when the device is removed, so the value shouldn't matter.

> +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", },
> +	{ },
> +};

Shouldn't that contain just one entry for "pinctrl-single", and no others?

> +MODULE_LICENSE("GPL");

The license header implies that should be "GPL v2" not just "GPL" (which
means GPLv2+)

One final comment: A little while before Linaro Connect in San
Francisco, we were all having difficulty coming up with any kind of DT
binding for pinctrl. I had suggested the possibility of creating a
binding which just said "write value X to register Y, write value P to
register Q, ...". This was rejected at connect, because a raw list of
register writes didn't document anything or describe semantics - it was
too much of a binary blob. This driver seems to be doing almost the
exact same thing. In order to avoid that assertion, it'd need to truly
have individual representations of which pins exist, which pingroups
exist, which functions exist, and which functions can be selected onto
which pins/pingroups. That would be a radically different binding. I
wonder what's changed such that this kind of driver wasn't acceptable
then, but is now?

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
  2012-06-14 23:12   ` Stephen Warren
@ 2012-06-15  9:49     ` Tony Lindgren
  -1 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-15  9:49 UTC (permalink / raw)
  To: Stephen Warren
  Cc: Linus Walleij, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren

Hi,

* Stephen Warren <swarren@wwwdotorg.org> [120614 16:16]:
> On 06/11/2012 07:58 AM, Tony Lindgren wrote:
> > Add one-register-per-pin type device tree based pinctrl driver.
> > 
> > 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 can be added.
> 
> > diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt
> 
> I'm not sure why you'd need an explicit OMAP2 binding document or
> compatible values if it just uses the plain pinctrl-single binding
> unmodified.

Hmm I thought that's what you wanted with your earlier comments at [1]:

* Stephen Warren <swarren@wwwdotorg.org> [120504 12:27]:
>
> If this is truly intended to be generic, I would not document any of the
> ti compatible values here. Instead, I'd create a binding document for
> the TI controllers that basically just says "this uses the bindings in
> pinctrl-simple.txt, with the following additions", and list the
> compatible values as an addition.

Care to clarify a bit what you had in mind there?
 
> > diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> 
> > +- pinctrl-single,function-off : function off mode for disabled state
> 
> Might that not vary per register? Admittedly, Tegra isn't something that
> could be covered by pinctrl-single, but the safe/off/non-conflicting mux
> selection values for Tegra certainly are not all the same value.

Yeah it might be best to remove function-off, and just rely on alternative
pin modes for setting this up in a register specific way. For example,
omaps don't have clearly defined off mode, there is safe mode for most
registers, but not all of them.
 
> > +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits
> > +
> > +This driver uses the common pinctrl bindings as specified in the
> > +pinctrl-bindings.txt document in this directory.
> > +
> > +The pinctrl register offsets and default values are specified as pairs
> 
> Why "default values" not just "values"; they can only be "default" if
> you're talking about something that can be changed by some other
> optional mechanism, which I don't believe is the case here.

Sure let's do that.
 
> > +using pinctrl-single,pins. For example, setting uart2_rx pin on omap2plus
> > +can be done with:
> > +
> > +	pinctrl-single,pins = <0xdc 0x118>;
> > +
> > +Where 0xdc is the offset from the pinctrl ioremapped area for the
> 
> "ioremapped area" is a Linux-specific term. "register base address"
> would be more generic.

OK
 
> > +uart2_rx register, and 0x118 contains the desired default value for
> > +for the pin setting it to INPUT_PULLUP | MODE0. See the uart example and
> 
> Any mention of "uart2_rx" or "setting it to INPUT_PULLUP | MODE0" is
> OMAP-specific; I'd say "0xdc" and just delete "setting it ...".

Alright, I'll make it anonymous.
 
> > +static board pins example below for more information.
> > +
> > +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.
> 
> I'm not sure if it's really appropriate to state that kind of thing in a
> DT binding document.

Sure, I can move that to comments in the driver.
 
> > +This driver assumes that there is only one register for each pin. If you
> > +have some pins with more complicated configuration,
> 
> OK ...
> 
> > you can set up a separate
> > +hardware specific driver for those pins.
> 
> But for that, it seems more correct to say that pinctrl-single simply
> isn't an appropriate model for your HW.

It is possible to create a wrapper pinctrl driver that requests pins from
pinctrl-single and adds functionality to handle the extra registers.
Let's say you have 200 similar registers, and only few have additional
configuration registers. You may still want to handle the basic configuration
using this driver. But I can move that to the driver comments too.

> > +Note that this driver tries to avoid understanding pin and function
> > +names because of the extra bloat they would cause especially in the
> > +omap2plus case. This driver just sets what is specified for the board
> 
> Again, I'd remove reference to any specific SoC from a generic binding.

OK
 
> > +in the .dts file. Further user space debugging tools can be developed
> > +to decipher the pin and function names using debugfs.
> > +
> > +Example:
> > +
> > +/* SoC common file, such as omap4.dtsi */
> 
> But just to be clear, having an example for a specific SoC seems fine to
> me; just not the generic descriptive text.

Sure.
 
> I don't see anywhere that describes how the function-mask and
> pinconf-mask properties interact with the values in pinctrl-single,pins
> property; are both masks OR'd together, then AND'd with the pins values,
> then written to the registers? If so, why have 2 masks?

I'll add some more documentation. The reason why I set two masks is
if the driver wants to modify settings with pin_config_set. I'll do some
testing on that, let's see if it's actually needed.
 
> > diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
> 
> > +struct pcs_device {
> 
> > +	struct pinctrl_desc *desc;
> 
> Why not make that a struct rather than a pointer. That way, you wouldn't
> have to dynamically allocate it separately. It's always needed.

Good point, will check.
 
> > +static int __devinit pcs_probe(struct platform_device *pdev)
> 
> > +	ret = of_address_to_resource(pdev->dev.of_node, 0, &res);
> 
> It's much more common to use platform_get_resource(); doesn't calling
> of_address_to_resource() duplicate all the work that the OF core already
> did to set up the platform resource structures?

OK sounds right.
 
> > +	ret = pcs_register(pcs);
> 
> Why not just inline that function here; it's not shared with anything.

Yeah good point, pcs_register is pretty minimal now, no need to have
it as a separate function any longer.
 
> > +static int __devexit pcs_remove(struct platform_device *pdev)
> 
> > +	platform_set_drvdata(pdev, NULL);
> 
> There's no need to explicitly clear that; nothing should be using that
> value when the device is removed, so the value shouldn't matter.

Will check if that's needed. There are certainly quite a few calls like
that in the kernel.
 
> > +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", },
> > +	{ },
> > +};
> 
> Shouldn't that contain just one entry for "pinctrl-single", and no others?

It's best to describe the hardware in case we need to set up some hardware
specific things in the driver.
 
> > +MODULE_LICENSE("GPL");
> 
> The license header implies that should be "GPL v2" not just "GPL" (which
> means GPLv2+)

Sure, that's what it says at top of the file.
 
> One final comment: A little while before Linaro Connect in San
> Francisco, we were all having difficulty coming up with any kind of DT
> binding for pinctrl. I had suggested the possibility of creating a
> binding which just said "write value X to register Y, write value P to
> register Q, ...". This was rejected at connect, because a raw list of
> register writes didn't document anything or describe semantics - it was
> too much of a binary blob. This driver seems to be doing almost the
> exact same thing. In order to avoid that assertion, it'd need to truly
> have individual representations of which pins exist, which pingroups
> exist, which functions exist, and which functions can be selected onto
> which pins/pingroups. That would be a radically different binding. I
> wonder what's changed such that this kind of driver wasn't acceptable
> then, but is now?

Well that's why I had two bindings earlier: The current binding for the
bulk pins that's faster to parse, and a more verbose binding for drivers that
allows naming the functions.

In your previous comments at [1] you seemed to prefer this current binding
based on the "Why not only allow (1)?" comment. Maybe you had something else
in mind, care to clarify that a bit too?

Using either binding is fine with me, this is just faster to parse.

For your question regarding pins, pingroups and functions, the logic in
this driver is following:

1. The pin names are package specific, and not needed in the kernel except
   for debugging. And that can be done with user space tools using debugfs.

2. The pingroup names can be the names of devices requesting the pins in
   this case. Trying to define any other pingroup names would be artificial
   and would have too many permutations at least in the omap case to list.

3. The pin functions and pinconf values become readable if we ever get
   the .dts preprocessing patches merged. If we want to further define
   function names, then using binding 2 at [1] would need to be used. But
   again, kernel does not need to know the function names, those too can
   be debugged with user space tools via debugfs.

Regards,

Tony

[1] http://article.gmane.org/gmane.linux.kernel/1291838

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-15  9:49     ` Tony Lindgren
  0 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-15  9:49 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

* Stephen Warren <swarren@wwwdotorg.org> [120614 16:16]:
> On 06/11/2012 07:58 AM, Tony Lindgren wrote:
> > Add one-register-per-pin type device tree based pinctrl driver.
> > 
> > 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 can be added.
> 
> > diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt
> 
> I'm not sure why you'd need an explicit OMAP2 binding document or
> compatible values if it just uses the plain pinctrl-single binding
> unmodified.

Hmm I thought that's what you wanted with your earlier comments at [1]:

* Stephen Warren <swarren@wwwdotorg.org> [120504 12:27]:
>
> If this is truly intended to be generic, I would not document any of the
> ti compatible values here. Instead, I'd create a binding document for
> the TI controllers that basically just says "this uses the bindings in
> pinctrl-simple.txt, with the following additions", and list the
> compatible values as an addition.

Care to clarify a bit what you had in mind there?
 
> > diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> 
> > +- pinctrl-single,function-off : function off mode for disabled state
> 
> Might that not vary per register? Admittedly, Tegra isn't something that
> could be covered by pinctrl-single, but the safe/off/non-conflicting mux
> selection values for Tegra certainly are not all the same value.

Yeah it might be best to remove function-off, and just rely on alternative
pin modes for setting this up in a register specific way. For example,
omaps don't have clearly defined off mode, there is safe mode for most
registers, but not all of them.
 
> > +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits
> > +
> > +This driver uses the common pinctrl bindings as specified in the
> > +pinctrl-bindings.txt document in this directory.
> > +
> > +The pinctrl register offsets and default values are specified as pairs
> 
> Why "default values" not just "values"; they can only be "default" if
> you're talking about something that can be changed by some other
> optional mechanism, which I don't believe is the case here.

Sure let's do that.
 
> > +using pinctrl-single,pins. For example, setting uart2_rx pin on omap2plus
> > +can be done with:
> > +
> > +	pinctrl-single,pins = <0xdc 0x118>;
> > +
> > +Where 0xdc is the offset from the pinctrl ioremapped area for the
> 
> "ioremapped area" is a Linux-specific term. "register base address"
> would be more generic.

OK
 
> > +uart2_rx register, and 0x118 contains the desired default value for
> > +for the pin setting it to INPUT_PULLUP | MODE0. See the uart example and
> 
> Any mention of "uart2_rx" or "setting it to INPUT_PULLUP | MODE0" is
> OMAP-specific; I'd say "0xdc" and just delete "setting it ...".

Alright, I'll make it anonymous.
 
> > +static board pins example below for more information.
> > +
> > +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.
> 
> I'm not sure if it's really appropriate to state that kind of thing in a
> DT binding document.

Sure, I can move that to comments in the driver.
 
> > +This driver assumes that there is only one register for each pin. If you
> > +have some pins with more complicated configuration,
> 
> OK ...
> 
> > you can set up a separate
> > +hardware specific driver for those pins.
> 
> But for that, it seems more correct to say that pinctrl-single simply
> isn't an appropriate model for your HW.

It is possible to create a wrapper pinctrl driver that requests pins from
pinctrl-single and adds functionality to handle the extra registers.
Let's say you have 200 similar registers, and only few have additional
configuration registers. You may still want to handle the basic configuration
using this driver. But I can move that to the driver comments too.

> > +Note that this driver tries to avoid understanding pin and function
> > +names because of the extra bloat they would cause especially in the
> > +omap2plus case. This driver just sets what is specified for the board
> 
> Again, I'd remove reference to any specific SoC from a generic binding.

OK
 
> > +in the .dts file. Further user space debugging tools can be developed
> > +to decipher the pin and function names using debugfs.
> > +
> > +Example:
> > +
> > +/* SoC common file, such as omap4.dtsi */
> 
> But just to be clear, having an example for a specific SoC seems fine to
> me; just not the generic descriptive text.

Sure.
 
> I don't see anywhere that describes how the function-mask and
> pinconf-mask properties interact with the values in pinctrl-single,pins
> property; are both masks OR'd together, then AND'd with the pins values,
> then written to the registers? If so, why have 2 masks?

I'll add some more documentation. The reason why I set two masks is
if the driver wants to modify settings with pin_config_set. I'll do some
testing on that, let's see if it's actually needed.
 
> > diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
> 
> > +struct pcs_device {
> 
> > +	struct pinctrl_desc *desc;
> 
> Why not make that a struct rather than a pointer. That way, you wouldn't
> have to dynamically allocate it separately. It's always needed.

Good point, will check.
 
> > +static int __devinit pcs_probe(struct platform_device *pdev)
> 
> > +	ret = of_address_to_resource(pdev->dev.of_node, 0, &res);
> 
> It's much more common to use platform_get_resource(); doesn't calling
> of_address_to_resource() duplicate all the work that the OF core already
> did to set up the platform resource structures?

OK sounds right.
 
> > +	ret = pcs_register(pcs);
> 
> Why not just inline that function here; it's not shared with anything.

Yeah good point, pcs_register is pretty minimal now, no need to have
it as a separate function any longer.
 
> > +static int __devexit pcs_remove(struct platform_device *pdev)
> 
> > +	platform_set_drvdata(pdev, NULL);
> 
> There's no need to explicitly clear that; nothing should be using that
> value when the device is removed, so the value shouldn't matter.

Will check if that's needed. There are certainly quite a few calls like
that in the kernel.
 
> > +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", },
> > +	{ },
> > +};
> 
> Shouldn't that contain just one entry for "pinctrl-single", and no others?

It's best to describe the hardware in case we need to set up some hardware
specific things in the driver.
 
> > +MODULE_LICENSE("GPL");
> 
> The license header implies that should be "GPL v2" not just "GPL" (which
> means GPLv2+)

Sure, that's what it says at top of the file.
 
> One final comment: A little while before Linaro Connect in San
> Francisco, we were all having difficulty coming up with any kind of DT
> binding for pinctrl. I had suggested the possibility of creating a
> binding which just said "write value X to register Y, write value P to
> register Q, ...". This was rejected at connect, because a raw list of
> register writes didn't document anything or describe semantics - it was
> too much of a binary blob. This driver seems to be doing almost the
> exact same thing. In order to avoid that assertion, it'd need to truly
> have individual representations of which pins exist, which pingroups
> exist, which functions exist, and which functions can be selected onto
> which pins/pingroups. That would be a radically different binding. I
> wonder what's changed such that this kind of driver wasn't acceptable
> then, but is now?

Well that's why I had two bindings earlier: The current binding for the
bulk pins that's faster to parse, and a more verbose binding for drivers that
allows naming the functions.

In your previous comments at [1] you seemed to prefer this current binding
based on the "Why not only allow (1)?" comment. Maybe you had something else
in mind, care to clarify that a bit too?

Using either binding is fine with me, this is just faster to parse.

For your question regarding pins, pingroups and functions, the logic in
this driver is following:

1. The pin names are package specific, and not needed in the kernel except
   for debugging. And that can be done with user space tools using debugfs.

2. The pingroup names can be the names of devices requesting the pins in
   this case. Trying to define any other pingroup names would be artificial
   and would have too many permutations at least in the omap case to list.

3. The pin functions and pinconf values become readable if we ever get
   the .dts preprocessing patches merged. If we want to further define
   function names, then using binding 2 at [1] would need to be used. But
   again, kernel does not need to know the function names, those too can
   be debugged with user space tools via debugfs.

Regards,

Tony

[1] http://article.gmane.org/gmane.linux.kernel/1291838

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
  2012-06-15  9:49     ` Tony Lindgren
@ 2012-06-15 16:17       ` Stephen Warren
  -1 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-15 16:17 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Linus Walleij, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren, Arnd Bergmann, Grant Likely,
	Rob Herring

On 06/15/2012 03:49 AM, Tony Lindgren wrote:

(Arnd, Grant, Rob, CC'ing you mainly re: the very last set of comments
in this email; can you take a look at Tony's patch and comment on the
binding)

> * Stephen Warren <swarren@wwwdotorg.org> [120614 16:16]:
>> On 06/11/2012 07:58 AM, Tony Lindgren wrote:
>>> Add one-register-per-pin type device tree based pinctrl driver.
>>>
>>> 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 can be added.
>>
>>> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt
>>
>> I'm not sure why you'd need an explicit OMAP2 binding document or
>> compatible values if it just uses the plain pinctrl-single binding
>> unmodified.
> 
> Hmm I thought that's what you wanted with your earlier comments at [1]:
> 
> * Stephen Warren <swarren@wwwdotorg.org> [120504 12:27]:
>>
>> If this is truly intended to be generic, I would not document any of the
>> ti compatible values here. Instead, I'd create a binding document for
>> the TI controllers that basically just says "this uses the bindings in
>> pinctrl-simple.txt, with the following additions", and list the
>> compatible values as an addition.
> 
> Care to clarify a bit what you had in mind there?

Basically I meant:

1) Remove anything TI-/OMAP-specific from the generic binding document

2) For any TI-/OMAP-specific additions, create a binding document that
describes those separately to the generic binding documentation.

However, given that there are no TI-/OMAP-specific additions; OMAP2 just
uses the base generic bindings exactly as defined, I don't think you
actually need to create an TI-/OMAP-specific document, since there's
nothing for it to document.

>>> diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c

>>> +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", },
>>> +	{ },
>>> +};
>>
>> Shouldn't that contain just one entry for "pinctrl-single", and no others?
> 
> It's best to describe the hardware in case we need to set up some hardware
> specific things in the driver.

There's a difference between the (set of) compatible value(s) that
appears in the .dts file, and the compatible value(s) that the driver
binds to.

Since this driver is purely implementing the pinctrl-single binding, I
believe only that should be listed in the driver's of_match table.

However, since the .dts file is describing HW that is both a specific
OMAP instance, and compatible with pinctrl-single, the .dts file can
certainly list both. If in the future, the driver ever needs to
specifically know the difference between the specific OMAP versions, or
OMAP-vs-something-else, the compatible value will be there already in
the .dts file, so this will all work. However, I'd argue that if the
driver has to ever know that, it's probably not appropriate to say it's
compatible with pinctrl-single any more...

>> One final comment: A little while before Linaro Connect in San
>> Francisco, we were all having difficulty coming up with any kind of DT
>> binding for pinctrl. I had suggested the possibility of creating a
>> binding which just said "write value X to register Y, write value P to
>> register Q, ...". This was rejected at connect, because a raw list of
>> register writes didn't document anything or describe semantics - it was
>> too much of a binary blob. This driver seems to be doing almost the
>> exact same thing. In order to avoid that assertion, it'd need to truly
>> have individual representations of which pins exist, which pingroups
>> exist, which functions exist, and which functions can be selected onto
>> which pins/pingroups. That would be a radically different binding. I
>> wonder what's changed such that this kind of driver wasn't acceptable
>> then, but is now?
> 
> Well that's why I had two bindings earlier: The current binding for the
> bulk pins that's faster to parse, and a more verbose binding for drivers that
> allows naming the functions.
> 
> In your previous comments at [1] you seemed to prefer this current binding
> based on the "Why not only allow (1)?" comment. Maybe you had something else
> in mind, care to clarify that a bit too?

Well, first I thought about semantics vs. just banging a register list a
tiny bit more, and remembered the previous discussion.

But my comment at [1] wasn't so much about "why not just have a raw
register list", but more regarding why support two different
representations within the same binding; it wasn't really clear to me
from reading the original email what the difference between the two
representations; why have two representations, why/when-to use one vs.
the other, that the difference was about providing a "semantic" list of
pins/pingroups/functions vs. providing a "raw" register list.

IIRC, the pushback on a raw "bang these registers" representation came
from Grant and/or Arnd. It'd be a good idea if they (and indeed Rob)
could comment on this binding proposal. If they're OK with this kind of
pretty raw representation, I'm not going to object, but I have a feeling
they might not like it?

> [1] http://article.gmane.org/gmane.linux.kernel/1291838

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-15 16:17       ` Stephen Warren
  0 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-15 16:17 UTC (permalink / raw)
  To: linux-arm-kernel

On 06/15/2012 03:49 AM, Tony Lindgren wrote:

(Arnd, Grant, Rob, CC'ing you mainly re: the very last set of comments
in this email; can you take a look at Tony's patch and comment on the
binding)

> * Stephen Warren <swarren@wwwdotorg.org> [120614 16:16]:
>> On 06/11/2012 07:58 AM, Tony Lindgren wrote:
>>> Add one-register-per-pin type device tree based pinctrl driver.
>>>
>>> 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 can be added.
>>
>>> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-omap2plus.txt
>>
>> I'm not sure why you'd need an explicit OMAP2 binding document or
>> compatible values if it just uses the plain pinctrl-single binding
>> unmodified.
> 
> Hmm I thought that's what you wanted with your earlier comments at [1]:
> 
> * Stephen Warren <swarren@wwwdotorg.org> [120504 12:27]:
>>
>> If this is truly intended to be generic, I would not document any of the
>> ti compatible values here. Instead, I'd create a binding document for
>> the TI controllers that basically just says "this uses the bindings in
>> pinctrl-simple.txt, with the following additions", and list the
>> compatible values as an addition.
> 
> Care to clarify a bit what you had in mind there?

Basically I meant:

1) Remove anything TI-/OMAP-specific from the generic binding document

2) For any TI-/OMAP-specific additions, create a binding document that
describes those separately to the generic binding documentation.

However, given that there are no TI-/OMAP-specific additions; OMAP2 just
uses the base generic bindings exactly as defined, I don't think you
actually need to create an TI-/OMAP-specific document, since there's
nothing for it to document.

>>> diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c

>>> +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", },
>>> +	{ },
>>> +};
>>
>> Shouldn't that contain just one entry for "pinctrl-single", and no others?
> 
> It's best to describe the hardware in case we need to set up some hardware
> specific things in the driver.

There's a difference between the (set of) compatible value(s) that
appears in the .dts file, and the compatible value(s) that the driver
binds to.

Since this driver is purely implementing the pinctrl-single binding, I
believe only that should be listed in the driver's of_match table.

However, since the .dts file is describing HW that is both a specific
OMAP instance, and compatible with pinctrl-single, the .dts file can
certainly list both. If in the future, the driver ever needs to
specifically know the difference between the specific OMAP versions, or
OMAP-vs-something-else, the compatible value will be there already in
the .dts file, so this will all work. However, I'd argue that if the
driver has to ever know that, it's probably not appropriate to say it's
compatible with pinctrl-single any more...

>> One final comment: A little while before Linaro Connect in San
>> Francisco, we were all having difficulty coming up with any kind of DT
>> binding for pinctrl. I had suggested the possibility of creating a
>> binding which just said "write value X to register Y, write value P to
>> register Q, ...". This was rejected at connect, because a raw list of
>> register writes didn't document anything or describe semantics - it was
>> too much of a binary blob. This driver seems to be doing almost the
>> exact same thing. In order to avoid that assertion, it'd need to truly
>> have individual representations of which pins exist, which pingroups
>> exist, which functions exist, and which functions can be selected onto
>> which pins/pingroups. That would be a radically different binding. I
>> wonder what's changed such that this kind of driver wasn't acceptable
>> then, but is now?
> 
> Well that's why I had two bindings earlier: The current binding for the
> bulk pins that's faster to parse, and a more verbose binding for drivers that
> allows naming the functions.
> 
> In your previous comments at [1] you seemed to prefer this current binding
> based on the "Why not only allow (1)?" comment. Maybe you had something else
> in mind, care to clarify that a bit too?

Well, first I thought about semantics vs. just banging a register list a
tiny bit more, and remembered the previous discussion.

But my comment at [1] wasn't so much about "why not just have a raw
register list", but more regarding why support two different
representations within the same binding; it wasn't really clear to me
from reading the original email what the difference between the two
representations; why have two representations, why/when-to use one vs.
the other, that the difference was about providing a "semantic" list of
pins/pingroups/functions vs. providing a "raw" register list.

IIRC, the pushback on a raw "bang these registers" representation came
from Grant and/or Arnd. It'd be a good idea if they (and indeed Rob)
could comment on this binding proposal. If they're OK with this kind of
pretty raw representation, I'm not going to object, but I have a feeling
they might not like it?

> [1] http://article.gmane.org/gmane.linux.kernel/1291838

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
  2012-06-15 16:17       ` Stephen Warren
@ 2012-06-18  5:50         ` Tony Lindgren
  -1 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-18  5:50 UTC (permalink / raw)
  To: Stephen Warren
  Cc: Linus Walleij, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren, Arnd Bergmann, Grant Likely,
	Rob Herring

* Stephen Warren <swarren@wwwdotorg.org> [120615 09:21]:
> On 06/15/2012 03:49 AM, Tony Lindgren wrote:
> 
> (Arnd, Grant, Rob, CC'ing you mainly re: the very last set of comments
> in this email; can you take a look at Tony's patch and comment on the
> binding)

Yes please take a look.
 
> > Care to clarify a bit what you had in mind there?
> 
> Basically I meant:
> 
> 1) Remove anything TI-/OMAP-specific from the generic binding document
> 
> 2) For any TI-/OMAP-specific additions, create a binding document that
> describes those separately to the generic binding documentation.
> 
> However, given that there are no TI-/OMAP-specific additions; OMAP2 just
> uses the base generic bindings exactly as defined, I don't think you
> actually need to create an TI-/OMAP-specific document, since there's
> nothing for it to document.

OK so basically you just want the pinctrl-single binding document to be
generic. Yes I'll do that.
 
> > It's best to describe the hardware in case we need to set up some hardware
> > specific things in the driver.
> 
> There's a difference between the (set of) compatible value(s) that
> appears in the .dts file, and the compatible value(s) that the driver
> binds to.
> 
> Since this driver is purely implementing the pinctrl-single binding, I
> believe only that should be listed in the driver's of_match table.
> 
> However, since the .dts file is describing HW that is both a specific
> OMAP instance, and compatible with pinctrl-single, the .dts file can
> certainly list both. If in the future, the driver ever needs to
> specifically know the difference between the specific OMAP versions, or
> OMAP-vs-something-else, the compatible value will be there already in
> the .dts file, so this will all work. However, I'd argue that if the
> driver has to ever know that, it's probably not appropriate to say it's
> compatible with pinctrl-single any more...

Yes OK I see what you mean now. Sounds good to me, and if/when we need
some hardware specific things, we'll add specific parsing for the other
compatible values.
 
> >> One final comment: A little while before Linaro Connect in San
> >> Francisco, we were all having difficulty coming up with any kind of DT
> >> binding for pinctrl. I had suggested the possibility of creating a
> >> binding which just said "write value X to register Y, write value P to
> >> register Q, ...". This was rejected at connect, because a raw list of
> >> register writes didn't document anything or describe semantics - it was
> >> too much of a binary blob. This driver seems to be doing almost the
> >> exact same thing. In order to avoid that assertion, it'd need to truly
> >> have individual representations of which pins exist, which pingroups
> >> exist, which functions exist, and which functions can be selected onto
> >> which pins/pingroups. That would be a radically different binding. I
> >> wonder what's changed such that this kind of driver wasn't acceptable
> >> then, but is now?
> > 
> > Well that's why I had two bindings earlier: The current binding for the
> > bulk pins that's faster to parse, and a more verbose binding for drivers that
> > allows naming the functions.
> > 
> > In your previous comments at [1] you seemed to prefer this current binding
> > based on the "Why not only allow (1)?" comment. Maybe you had something else
> > in mind, care to clarify that a bit too?
> 
> Well, first I thought about semantics vs. just banging a register list a
> tiny bit more, and remembered the previous discussion.
> 
> But my comment at [1] wasn't so much about "why not just have a raw
> register list", but more regarding why support two different
> representations within the same binding; it wasn't really clear to me
> from reading the original email what the difference between the two
> representations; why have two representations, why/when-to use one vs.
> the other, that the difference was about providing a "semantic" list of
> pins/pingroups/functions vs. providing a "raw" register list.

I too would prefer more verbose .dts files. Unfortunately the parsing of
several hundred board specific pins adds quite a bit of overhead.
 
> IIRC, the pushback on a raw "bang these registers" representation came
> from Grant and/or Arnd. It'd be a good idea if they (and indeed Rob)
> could comment on this binding proposal. If they're OK with this kind of
> pretty raw representation, I'm not going to object, but I have a feeling
> they might not like it?

Yes let's see if anybody has better ideas.

Regards,

Tony

 
> > [1] http://article.gmane.org/gmane.linux.kernel/1291838

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-18  5:50         ` Tony Lindgren
  0 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-18  5:50 UTC (permalink / raw)
  To: linux-arm-kernel

* Stephen Warren <swarren@wwwdotorg.org> [120615 09:21]:
> On 06/15/2012 03:49 AM, Tony Lindgren wrote:
> 
> (Arnd, Grant, Rob, CC'ing you mainly re: the very last set of comments
> in this email; can you take a look at Tony's patch and comment on the
> binding)

Yes please take a look.
 
> > Care to clarify a bit what you had in mind there?
> 
> Basically I meant:
> 
> 1) Remove anything TI-/OMAP-specific from the generic binding document
> 
> 2) For any TI-/OMAP-specific additions, create a binding document that
> describes those separately to the generic binding documentation.
> 
> However, given that there are no TI-/OMAP-specific additions; OMAP2 just
> uses the base generic bindings exactly as defined, I don't think you
> actually need to create an TI-/OMAP-specific document, since there's
> nothing for it to document.

OK so basically you just want the pinctrl-single binding document to be
generic. Yes I'll do that.
 
> > It's best to describe the hardware in case we need to set up some hardware
> > specific things in the driver.
> 
> There's a difference between the (set of) compatible value(s) that
> appears in the .dts file, and the compatible value(s) that the driver
> binds to.
> 
> Since this driver is purely implementing the pinctrl-single binding, I
> believe only that should be listed in the driver's of_match table.
> 
> However, since the .dts file is describing HW that is both a specific
> OMAP instance, and compatible with pinctrl-single, the .dts file can
> certainly list both. If in the future, the driver ever needs to
> specifically know the difference between the specific OMAP versions, or
> OMAP-vs-something-else, the compatible value will be there already in
> the .dts file, so this will all work. However, I'd argue that if the
> driver has to ever know that, it's probably not appropriate to say it's
> compatible with pinctrl-single any more...

Yes OK I see what you mean now. Sounds good to me, and if/when we need
some hardware specific things, we'll add specific parsing for the other
compatible values.
 
> >> One final comment: A little while before Linaro Connect in San
> >> Francisco, we were all having difficulty coming up with any kind of DT
> >> binding for pinctrl. I had suggested the possibility of creating a
> >> binding which just said "write value X to register Y, write value P to
> >> register Q, ...". This was rejected at connect, because a raw list of
> >> register writes didn't document anything or describe semantics - it was
> >> too much of a binary blob. This driver seems to be doing almost the
> >> exact same thing. In order to avoid that assertion, it'd need to truly
> >> have individual representations of which pins exist, which pingroups
> >> exist, which functions exist, and which functions can be selected onto
> >> which pins/pingroups. That would be a radically different binding. I
> >> wonder what's changed such that this kind of driver wasn't acceptable
> >> then, but is now?
> > 
> > Well that's why I had two bindings earlier: The current binding for the
> > bulk pins that's faster to parse, and a more verbose binding for drivers that
> > allows naming the functions.
> > 
> > In your previous comments at [1] you seemed to prefer this current binding
> > based on the "Why not only allow (1)?" comment. Maybe you had something else
> > in mind, care to clarify that a bit too?
> 
> Well, first I thought about semantics vs. just banging a register list a
> tiny bit more, and remembered the previous discussion.
> 
> But my comment at [1] wasn't so much about "why not just have a raw
> register list", but more regarding why support two different
> representations within the same binding; it wasn't really clear to me
> from reading the original email what the difference between the two
> representations; why have two representations, why/when-to use one vs.
> the other, that the difference was about providing a "semantic" list of
> pins/pingroups/functions vs. providing a "raw" register list.

I too would prefer more verbose .dts files. Unfortunately the parsing of
several hundred board specific pins adds quite a bit of overhead.
 
> IIRC, the pushback on a raw "bang these registers" representation came
> from Grant and/or Arnd. It'd be a good idea if they (and indeed Rob)
> could comment on this binding proposal. If they're OK with this kind of
> pretty raw representation, I'm not going to object, but I have a feeling
> they might not like it?

Yes let's see if anybody has better ideas.

Regards,

Tony

 
> > [1] http://article.gmane.org/gmane.linux.kernel/1291838

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
  2012-06-18  5:50         ` Tony Lindgren
@ 2012-06-19 13:56           ` Tony Lindgren
  -1 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-19 13:56 UTC (permalink / raw)
  To: Stephen Warren
  Cc: Linus Walleij, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren, Arnd Bergmann, Grant Likely,
	Rob Herring

Hi,

Below is the pinctrl-single patch updated with hopefully all the Stephen's
comments addressed. The binding still needs to be looked at, see relevant
parts of the discussion below.

* Tony Lindgren <tony@atomide.com> [120617 22:55]:
> * Stephen Warren <swarren@wwwdotorg.org> [120615 09:21]:
> > On 06/15/2012 03:49 AM, Tony Lindgren wrote:
> > 
> > (Arnd, Grant, Rob, CC'ing you mainly re: the very last set of comments
> > in this email; can you take a look at Tony's patch and comment on the
> > binding)
> 
> Yes please take a look.
>  
> > >> One final comment: A little while before Linaro Connect in San
> > >> Francisco, we were all having difficulty coming up with any kind of DT
> > >> binding for pinctrl. I had suggested the possibility of creating a
> > >> binding which just said "write value X to register Y, write value P to
> > >> register Q, ...". This was rejected at connect, because a raw list of
> > >> register writes didn't document anything or describe semantics - it was
> > >> too much of a binary blob. This driver seems to be doing almost the
> > >> exact same thing. In order to avoid that assertion, it'd need to truly
> > >> have individual representations of which pins exist, which pingroups
> > >> exist, which functions exist, and which functions can be selected onto
> > >> which pins/pingroups. That would be a radically different binding. I
> > >> wonder what's changed such that this kind of driver wasn't acceptable
> > >> then, but is now?
> > > 
> > > Well that's why I had two bindings earlier: The current binding for the
> > > bulk pins that's faster to parse, and a more verbose binding for drivers that
> > > allows naming the functions.
> > > 
> > > In your previous comments at [1] you seemed to prefer this current binding
> > > based on the "Why not only allow (1)?" comment. Maybe you had something else
> > > in mind, care to clarify that a bit too?
> > 
> > Well, first I thought about semantics vs. just banging a register list a
> > tiny bit more, and remembered the previous discussion.
> > 
> > But my comment at [1] wasn't so much about "why not just have a raw
> > register list", but more regarding why support two different
> > representations within the same binding; it wasn't really clear to me
> > from reading the original email what the difference between the two
> > representations; why have two representations, why/when-to use one vs.
> > the other, that the difference was about providing a "semantic" list of
> > pins/pingroups/functions vs. providing a "raw" register list.
> 
> I too would prefer more verbose .dts files. Unfortunately the parsing of
> several hundred board specific pins adds quite a bit of overhead.
>  
> > IIRC, the pushback on a raw "bang these registers" representation came
> > from Grant and/or Arnd. It'd be a good idea if they (and indeed Rob)
> > could comment on this binding proposal. If they're OK with this kind of
> > pretty raw representation, I'm not going to object, but I have a feeling
> > they might not like it?
> 
> Yes let's see if anybody has better ideas.
> 
> Regards,
> 
> Tony
> 
>  
> > > [1] http://article.gmane.org/gmane.linux.kernel/1291838


From: Tony Lindgren <tony@atomide.com>
Date: Wed, 6 Jun 2012 04:18:18 -0700
Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver

Add one-register-per-pin type device tree based pinctrl driver.

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 can be added.

Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
new file mode 100644
index 0000000..929254c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
@@ -0,0 +1,106 @@
+One-register-per-pin type device tree based pinctrl driver
+
+Required properties:
+- compatible : "pinctrl-single"
+
+- reg : offset and length of the register set for the mux registers
+
+- pinctrl-single,register-width : pinmux register access width in bits
+
+- pinctrl-single,function-mask : mask of allowed pinmux function bits
+  in the pinmux register
+
+- pinctrl-single,pinconf-mask : mask of allowed pinconf bits in the
+  pinmux register; this gets combined with pinconf mask but is a separate
+  mask to allow the option of setting pinconf separatately from the
+  function
+
+- pinctrl-single,function-off : function off mode for disabled state if
+  available and same for all registers; if not, use a value larger than
+  function-mask to ignore disabling of registers
+
+This driver assumes that there is only one register for each pin,
+and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt
+document in this directory.
+
+The pinctrl register offsets and default values are specified as pairs
+using pinctrl-single,pins. For example, setting a pin for a device
+could be done with:
+
+	pinctrl-single,pins = <0xdc 0x118>;
+
+Where 0xdc is the offset from the pinctrl register base address for the
+device pinctrl register, and 0x118 contains the desired value of the
+pinctrl register. See the device example and static board pins example
+below for more information.
+
+This driver tries to avoid understanding pin and function names because of
+the extra bloat they would cause especially in the case of a large number
+of pins. This driver just sets what is specified for the board in the .dts file.
+Further user space debugging tools can be developed to decipher the pin and
+function names using debugfs.
+
+Example:
+
+/* SoC common file */
+
+/* first controller instance for pins in core domain */
+pmx_core: pinmux@4a100040 {
+	compatible = "pinctrl-single";
+	reg = <0x4a100040 0x0196>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0x7>;
+	pinctrl-single,function-off = <0xffffffff>;
+	pinctrl-single,pinconf-mask = <0xfff8>;
+};
+
+/* second controller instance for pins in wkup domain */
+pmx_wkup: pinmux@4a31e040 {
+	compatible = "pinctrl-single;
+	reg = <0x4a31e040 0x0038>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0x7>;
+	pinctrl-single,function-off = <0xffffffff>;
+	pinctrl-single,pinconf-mask = <0xfff8>;
+};
+
+
+/* board specific .dts file */
+
+&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 {
+		pinctrl-single,pins = <
+			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
+			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
+			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
+			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */
+		>;
+	};
+
+	/* map uart2 pins */
+	uart2_pins: pinmux_uart2_pins {
+		pinctrl-single,pins = <
+			0xd8 0x118	/* uart2_cts INPUT_PULLUP | MODE0 */
+			0xda 0		/* uart2_rts OUTPUT | MODE0 */
+			0xdc 0x118	/* uart2_rx INPUT_PULLUP | MODE0 */
+			0xde 0		/* uart2_tx OUTPUT | MODE0 */
+		>;
+	};
+};
+
+&uart2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&uart2_pins>;
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index c6e6ae0..8071a31 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -102,6 +102,14 @@ config PINCTRL_PXA910
 	select PINCTRL_PXA3xx
 	select PINCONF
 
+config PINCTRL_SINGLE
+	tristate "One-register-per-pin type device tree based pinctrl driver"
+	depends on OF
+	select PINMUX
+	select PINCONF
+	help
+	  This selects the device tree based generic pinctrl driver.
+
 config PINCTRL_SIRF
 	bool "CSR SiRFprimaII pin controller driver"
 	depends on ARCH_PRIMA2
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 8c07437..f40b1f8 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK)	+= pinctrl-nomadik.o
 obj-$(CONFIG_PINCTRL_DB8500)	+= pinctrl-nomadik-db8500.o
 obj-$(CONFIG_PINCTRL_PXA168)	+= pinctrl-pxa168.o
 obj-$(CONFIG_PINCTRL_PXA910)	+= pinctrl-pxa910.o
+obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_SIRF)	+= pinctrl-sirf.o
 obj-$(CONFIG_PINCTRL_TEGRA)	+= pinctrl-tegra.o
 obj-$(CONFIG_PINCTRL_TEGRA20)	+= pinctrl-tegra20.o
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
new file mode 100644
index 0000000..76e83fb
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -0,0 +1,983 @@
+/*
+ * Generic device tree based pinctrl driver for one register per pin
+ * type pinmux controllers
+ *
+ * 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-single"
+#define PCS_MUX_NAME			"pinctrl-single,pins"
+#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
+ * @node:	list node
+ */
+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
+ * @val:	register value
+ */
+struct pcs_func_vals {
+	void __iomem *reg;
+	unsigned val;
+};
+
+/**
+ * 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
+ *
+ * REVISIT: We should be able to drop this eventually by adding
+ * support for registering pins individually in the pinctrl
+ * framework for those drivers that don't need a static array.
+ */
+struct pcs_data {
+	struct pinctrl_pin_desc *pa;
+	int cur;
+};
+
+/**
+ * struct pcs_name - register name for a pin
+ * @name:	name of the pinctrl register
+ *
+ * REVISIT: We may want to make names optional in the pinctrl
+ * framework as some drivers may not care about pin names to
+ * avoid kernel bloat. The pin names can be deciphered by user
+ * space tools using debugfs based on the register address and
+ * SoC packaging information.
+ */
+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, separate from fmask to allow pin_config_set
+ * @fmax:	max number of functions in fmask
+ * @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;
+	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);
+};
+
+/*
+ * REVISIT: Reads and writes could eventually use regmap or something
+ * generic. But at least on omaps, some mux registers are performance
+ * critical as they may need to be remuxed every time before and after
+ * idle. Adding tests for register access width for every read and
+ * write like regmap is doing is not desired, and caching the registers
+ * does not help in this case.
+ */
+
+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 %s function%i\n",
+		func->name, fselector);
+
+	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->val;
+		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 pinconf if function-off is larger than function-mask.
+	 * Some hardware does not have clearly defined disable function, so we
+	 * just ignore disable for those cases. For pin specific off modes,
+	 * use alternate named states as described in pinctrl-bindings.txt.
+	 */
+	if ((pcs->foff << pcs->fshift) > pcs->fshift) {
+		dev_dbg(pcs->dev, "ignoring disable for %s function%i\n",
+			func->name, fselector);
+		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;
+	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];
+	sprintf(pn->name, "%lx",
+		(unsigned long)pcs->res->start + offset);
+	pin->name = pn->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.
+ */
+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 sets of one register + value.
+ *
+ * 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.
+ */
+static int 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;
+
+	mux = of_get_property(np, PCS_MUX_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 / 2;	/* Each row is a key value pair */
+
+	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, val;
+		int pin;
+
+		offset = be32_to_cpup(mux + index++);
+		val = be32_to_cpup(mux + index++);
+		vals[found].reg = pcs->base + offset;
+		vals[found].val = val;
+
+		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;
+	const char **pgnames;
+	int ret;
+
+	pcs = pinctrl_dev_get_drvdata(pctldev);
+
+	*map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL);
+	if (!map)
+		return -ENOMEM;
+
+	*num_maps = 0;
+
+	pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL);
+	if (!pgnames) {
+		ret = -ENOMEM;
+		goto free_map;
+	}
+
+	ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, pgnames);
+	if (ret < 0) {
+		dev_err(pcs->dev, "no pins entries for %s\n",
+			np_config->name);
+		goto free_pgnames;
+	}
+	*num_maps = 1;
+
+	return 0;
+
+free_pgnames:
+	devm_kfree(pcs->dev, pgnames);
+free_map:
+	devm_kfree(pcs->dev, *map);
+
+	return ret;
+}
+
+/**
+ * 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);
+}
+
+#define PCS_GET_PROP_U32(name, reg, err)				\
+	do {								\
+		ret = of_property_read_u32(np, name, reg);		\
+		if (ret) {						\
+			dev_err(pcs->dev, err);				\
+			return ret;					\
+		}							\
+	} while (0);
+
+static struct of_device_id pcs_of_match[];
+
+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-single,register-width", &pcs->width,
+			 "register width not specified\n");
+
+	PCS_GET_PROP_U32("pinctrl-single,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-single,function-off", &pcs->foff,
+			 "function off mode not specified\n");
+
+	PCS_GET_PROP_U32("pinctrl-single,pinconf-mask", &pcs->cmask,
+			 "pinconf mask not specified\n");
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(pcs->dev, "could not get resource\n");
+		return -ENODEV;
+	}
+
+	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");
+		return -EBUSY;
+	}
+
+	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");
+		return -ENODEV;
+	}
+
+	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;
+	}
+
+	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 single 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;
+}
+
+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);
+
+	return 0;
+}
+
+static struct of_device_id pcs_of_match[] __devinitdata = {
+	{ .compatible = DRIVER_NAME, },
+	{ },
+};
+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@atomide.com>");
+MODULE_DESCRIPTION("One-register-per-pin type device tree based pinctrl driver");
+MODULE_LICENSE("GPL v2");

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-19 13:56           ` Tony Lindgren
  0 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-19 13:56 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

Below is the pinctrl-single patch updated with hopefully all the Stephen's
comments addressed. The binding still needs to be looked at, see relevant
parts of the discussion below.

* Tony Lindgren <tony@atomide.com> [120617 22:55]:
> * Stephen Warren <swarren@wwwdotorg.org> [120615 09:21]:
> > On 06/15/2012 03:49 AM, Tony Lindgren wrote:
> > 
> > (Arnd, Grant, Rob, CC'ing you mainly re: the very last set of comments
> > in this email; can you take a look at Tony's patch and comment on the
> > binding)
> 
> Yes please take a look.
>  
> > >> One final comment: A little while before Linaro Connect in San
> > >> Francisco, we were all having difficulty coming up with any kind of DT
> > >> binding for pinctrl. I had suggested the possibility of creating a
> > >> binding which just said "write value X to register Y, write value P to
> > >> register Q, ...". This was rejected at connect, because a raw list of
> > >> register writes didn't document anything or describe semantics - it was
> > >> too much of a binary blob. This driver seems to be doing almost the
> > >> exact same thing. In order to avoid that assertion, it'd need to truly
> > >> have individual representations of which pins exist, which pingroups
> > >> exist, which functions exist, and which functions can be selected onto
> > >> which pins/pingroups. That would be a radically different binding. I
> > >> wonder what's changed such that this kind of driver wasn't acceptable
> > >> then, but is now?
> > > 
> > > Well that's why I had two bindings earlier: The current binding for the
> > > bulk pins that's faster to parse, and a more verbose binding for drivers that
> > > allows naming the functions.
> > > 
> > > In your previous comments at [1] you seemed to prefer this current binding
> > > based on the "Why not only allow (1)?" comment. Maybe you had something else
> > > in mind, care to clarify that a bit too?
> > 
> > Well, first I thought about semantics vs. just banging a register list a
> > tiny bit more, and remembered the previous discussion.
> > 
> > But my comment at [1] wasn't so much about "why not just have a raw
> > register list", but more regarding why support two different
> > representations within the same binding; it wasn't really clear to me
> > from reading the original email what the difference between the two
> > representations; why have two representations, why/when-to use one vs.
> > the other, that the difference was about providing a "semantic" list of
> > pins/pingroups/functions vs. providing a "raw" register list.
> 
> I too would prefer more verbose .dts files. Unfortunately the parsing of
> several hundred board specific pins adds quite a bit of overhead.
>  
> > IIRC, the pushback on a raw "bang these registers" representation came
> > from Grant and/or Arnd. It'd be a good idea if they (and indeed Rob)
> > could comment on this binding proposal. If they're OK with this kind of
> > pretty raw representation, I'm not going to object, but I have a feeling
> > they might not like it?
> 
> Yes let's see if anybody has better ideas.
> 
> Regards,
> 
> Tony
> 
>  
> > > [1] http://article.gmane.org/gmane.linux.kernel/1291838


From: Tony Lindgren <tony@atomide.com>
Date: Wed, 6 Jun 2012 04:18:18 -0700
Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver

Add one-register-per-pin type device tree based pinctrl driver.

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 can be added.

Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
new file mode 100644
index 0000000..929254c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
@@ -0,0 +1,106 @@
+One-register-per-pin type device tree based pinctrl driver
+
+Required properties:
+- compatible : "pinctrl-single"
+
+- reg : offset and length of the register set for the mux registers
+
+- pinctrl-single,register-width : pinmux register access width in bits
+
+- pinctrl-single,function-mask : mask of allowed pinmux function bits
+  in the pinmux register
+
+- pinctrl-single,pinconf-mask : mask of allowed pinconf bits in the
+  pinmux register; this gets combined with pinconf mask but is a separate
+  mask to allow the option of setting pinconf separatately from the
+  function
+
+- pinctrl-single,function-off : function off mode for disabled state if
+  available and same for all registers; if not, use a value larger than
+  function-mask to ignore disabling of registers
+
+This driver assumes that there is only one register for each pin,
+and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt
+document in this directory.
+
+The pinctrl register offsets and default values are specified as pairs
+using pinctrl-single,pins. For example, setting a pin for a device
+could be done with:
+
+	pinctrl-single,pins = <0xdc 0x118>;
+
+Where 0xdc is the offset from the pinctrl register base address for the
+device pinctrl register, and 0x118 contains the desired value of the
+pinctrl register. See the device example and static board pins example
+below for more information.
+
+This driver tries to avoid understanding pin and function names because of
+the extra bloat they would cause especially in the case of a large number
+of pins. This driver just sets what is specified for the board in the .dts file.
+Further user space debugging tools can be developed to decipher the pin and
+function names using debugfs.
+
+Example:
+
+/* SoC common file */
+
+/* first controller instance for pins in core domain */
+pmx_core: pinmux at 4a100040 {
+	compatible = "pinctrl-single";
+	reg = <0x4a100040 0x0196>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0x7>;
+	pinctrl-single,function-off = <0xffffffff>;
+	pinctrl-single,pinconf-mask = <0xfff8>;
+};
+
+/* second controller instance for pins in wkup domain */
+pmx_wkup: pinmux at 4a31e040 {
+	compatible = "pinctrl-single;
+	reg = <0x4a31e040 0x0038>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0x7>;
+	pinctrl-single,function-off = <0xffffffff>;
+	pinctrl-single,pinconf-mask = <0xfff8>;
+};
+
+
+/* board specific .dts file */
+
+&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 {
+		pinctrl-single,pins = <
+			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
+			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
+			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
+			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */
+		>;
+	};
+
+	/* map uart2 pins */
+	uart2_pins: pinmux_uart2_pins {
+		pinctrl-single,pins = <
+			0xd8 0x118	/* uart2_cts INPUT_PULLUP | MODE0 */
+			0xda 0		/* uart2_rts OUTPUT | MODE0 */
+			0xdc 0x118	/* uart2_rx INPUT_PULLUP | MODE0 */
+			0xde 0		/* uart2_tx OUTPUT | MODE0 */
+		>;
+	};
+};
+
+&uart2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&uart2_pins>;
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index c6e6ae0..8071a31 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -102,6 +102,14 @@ config PINCTRL_PXA910
 	select PINCTRL_PXA3xx
 	select PINCONF
 
+config PINCTRL_SINGLE
+	tristate "One-register-per-pin type device tree based pinctrl driver"
+	depends on OF
+	select PINMUX
+	select PINCONF
+	help
+	  This selects the device tree based generic pinctrl driver.
+
 config PINCTRL_SIRF
 	bool "CSR SiRFprimaII pin controller driver"
 	depends on ARCH_PRIMA2
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 8c07437..f40b1f8 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK)	+= pinctrl-nomadik.o
 obj-$(CONFIG_PINCTRL_DB8500)	+= pinctrl-nomadik-db8500.o
 obj-$(CONFIG_PINCTRL_PXA168)	+= pinctrl-pxa168.o
 obj-$(CONFIG_PINCTRL_PXA910)	+= pinctrl-pxa910.o
+obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_SIRF)	+= pinctrl-sirf.o
 obj-$(CONFIG_PINCTRL_TEGRA)	+= pinctrl-tegra.o
 obj-$(CONFIG_PINCTRL_TEGRA20)	+= pinctrl-tegra20.o
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
new file mode 100644
index 0000000..76e83fb
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -0,0 +1,983 @@
+/*
+ * Generic device tree based pinctrl driver for one register per pin
+ * type pinmux controllers
+ *
+ * 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-single"
+#define PCS_MUX_NAME			"pinctrl-single,pins"
+#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
+ * @node:	list node
+ */
+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
+ * @val:	register value
+ */
+struct pcs_func_vals {
+	void __iomem *reg;
+	unsigned val;
+};
+
+/**
+ * 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
+ *
+ * REVISIT: We should be able to drop this eventually by adding
+ * support for registering pins individually in the pinctrl
+ * framework for those drivers that don't need a static array.
+ */
+struct pcs_data {
+	struct pinctrl_pin_desc *pa;
+	int cur;
+};
+
+/**
+ * struct pcs_name - register name for a pin
+ * @name:	name of the pinctrl register
+ *
+ * REVISIT: We may want to make names optional in the pinctrl
+ * framework as some drivers may not care about pin names to
+ * avoid kernel bloat. The pin names can be deciphered by user
+ * space tools using debugfs based on the register address and
+ * SoC packaging information.
+ */
+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, separate from fmask to allow pin_config_set
+ * @fmax:	max number of functions in fmask
+ * @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;
+	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);
+};
+
+/*
+ * REVISIT: Reads and writes could eventually use regmap or something
+ * generic. But at least on omaps, some mux registers are performance
+ * critical as they may need to be remuxed every time before and after
+ * idle. Adding tests for register access width for every read and
+ * write like regmap is doing is not desired, and caching the registers
+ * does not help in this case.
+ */
+
+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 %s function%i\n",
+		func->name, fselector);
+
+	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->val;
+		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 pinconf if function-off is larger than function-mask.
+	 * Some hardware does not have clearly defined disable function, so we
+	 * just ignore disable for those cases. For pin specific off modes,
+	 * use alternate named states as described in pinctrl-bindings.txt.
+	 */
+	if ((pcs->foff << pcs->fshift) > pcs->fshift) {
+		dev_dbg(pcs->dev, "ignoring disable for %s function%i\n",
+			func->name, fselector);
+		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;
+	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];
+	sprintf(pn->name, "%lx",
+		(unsigned long)pcs->res->start + offset);
+	pin->name = pn->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.
+ */
+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 sets of one register + value.
+ *
+ * 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.
+ */
+static int 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;
+
+	mux = of_get_property(np, PCS_MUX_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 / 2;	/* Each row is a key value pair */
+
+	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, val;
+		int pin;
+
+		offset = be32_to_cpup(mux + index++);
+		val = be32_to_cpup(mux + index++);
+		vals[found].reg = pcs->base + offset;
+		vals[found].val = val;
+
+		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;
+	const char **pgnames;
+	int ret;
+
+	pcs = pinctrl_dev_get_drvdata(pctldev);
+
+	*map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL);
+	if (!map)
+		return -ENOMEM;
+
+	*num_maps = 0;
+
+	pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL);
+	if (!pgnames) {
+		ret = -ENOMEM;
+		goto free_map;
+	}
+
+	ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, pgnames);
+	if (ret < 0) {
+		dev_err(pcs->dev, "no pins entries for %s\n",
+			np_config->name);
+		goto free_pgnames;
+	}
+	*num_maps = 1;
+
+	return 0;
+
+free_pgnames:
+	devm_kfree(pcs->dev, pgnames);
+free_map:
+	devm_kfree(pcs->dev, *map);
+
+	return ret;
+}
+
+/**
+ * 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);
+}
+
+#define PCS_GET_PROP_U32(name, reg, err)				\
+	do {								\
+		ret = of_property_read_u32(np, name, reg);		\
+		if (ret) {						\
+			dev_err(pcs->dev, err);				\
+			return ret;					\
+		}							\
+	} while (0);
+
+static struct of_device_id pcs_of_match[];
+
+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-single,register-width", &pcs->width,
+			 "register width not specified\n");
+
+	PCS_GET_PROP_U32("pinctrl-single,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-single,function-off", &pcs->foff,
+			 "function off mode not specified\n");
+
+	PCS_GET_PROP_U32("pinctrl-single,pinconf-mask", &pcs->cmask,
+			 "pinconf mask not specified\n");
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(pcs->dev, "could not get resource\n");
+		return -ENODEV;
+	}
+
+	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");
+		return -EBUSY;
+	}
+
+	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");
+		return -ENODEV;
+	}
+
+	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;
+	}
+
+	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 single 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;
+}
+
+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);
+
+	return 0;
+}
+
+static struct of_device_id pcs_of_match[] __devinitdata = {
+	{ .compatible = DRIVER_NAME, },
+	{ },
+};
+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@atomide.com>");
+MODULE_DESCRIPTION("One-register-per-pin type device tree based pinctrl driver");
+MODULE_LICENSE("GPL v2");

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
  2012-06-19 13:56           ` Tony Lindgren
@ 2012-06-21  8:09             ` Linus Walleij
  -1 siblings, 0 replies; 37+ messages in thread
From: Linus Walleij @ 2012-06-21  8:09 UTC (permalink / raw)
  To: Tony Lindgren, Grant Likely, Rob Herring
  Cc: Stephen Warren, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren, Arnd Bergmann

On Tue, Jun 19, 2012 at 3:56 PM, Tony Lindgren <tony@atomide.com> wrote:

> Below is the pinctrl-single patch updated with hopefully all the Stephen's
> comments addressed. The binding still needs to be looked at, see relevant
> parts of the discussion below.

I am happy with the code and the way it interacts with the pinctrl subsystem!

Now all I need to merge this is a few ACKs from select people, and
since this has a deep impact on the way device tree is used I would
*really* like for Grant or Rob to ACK it.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-21  8:09             ` Linus Walleij
  0 siblings, 0 replies; 37+ messages in thread
From: Linus Walleij @ 2012-06-21  8:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jun 19, 2012 at 3:56 PM, Tony Lindgren <tony@atomide.com> wrote:

> Below is the pinctrl-single patch updated with hopefully all the Stephen's
> comments addressed. The binding still needs to be looked at, see relevant
> parts of the discussion below.

I am happy with the code and the way it interacts with the pinctrl subsystem!

Now all I need to merge this is a few ACKs from select people, and
since this has a deep impact on the way device tree is used I would
*really* like for Grant or Rob to ACK it.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-21 22:13             ` Stephen Warren
  0 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-21 22:13 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Linus Walleij, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren, Arnd Bergmann, Grant Likely,
	Rob Herring

On 06/19/2012 07:56 AM, Tony Lindgren wrote:
> Hi,
> 
> Below is the pinctrl-single patch updated with hopefully all the Stephen's
> comments addressed. The binding still needs to be looked at, see relevant
> parts of the discussion below.
...
> From: Tony Lindgren <tony@atomide.com>
> Date: Wed, 6 Jun 2012 04:18:18 -0700
> Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
> 
> Add one-register-per-pin type device tree based pinctrl driver.
> 
> 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 can be added.
> 
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> 
> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> new file mode 100644
> index 0000000..929254c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> @@ -0,0 +1,106 @@
> +One-register-per-pin type device tree based pinctrl driver
> +
> +Required properties:
> +- compatible : "pinctrl-single"
> +
> +- reg : offset and length of the register set for the mux registers
> +
> +- pinctrl-single,register-width : pinmux register access width in bits
> +
> +- pinctrl-single,function-mask : mask of allowed pinmux function bits
> +  in the pinmux register
> +
> +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits in the
> +  pinmux register; this gets combined with pinconf mask but is a separate
> +  mask to allow the option of setting pinconf separatately from the
> +  function

Given that this binding doesn't allow describing pin configuration at
present, I would simply remove all mention of that property in the
binding documentation. It can be added back if/when that feature is
added. Any future driver using this binding can refuse to allow pin
configuration if that property is missing.

> +- pinctrl-single,function-off : function off mode for disabled state if
> +  available and same for all registers; if not, use a value larger than
> +  function-mask to ignore disabling of registers

Rather than requiring an invalid value in this property, shouldn't the
lack of a valid function-off value be represented by the property not
being present in the DT?

> +This driver assumes that there is only one register for each pin,
> +and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt
> +document in this directory.

At this point in the file, I think you need to mention that you're
switching from describing the top-level device node to describing pin
configuration nodes.

> +The pinctrl register offsets and default values are specified as pairs

I thought we were going to remove "default" here?

> +using pinctrl-single,pins. For example, setting a pin for a device
> +could be done with:
> +
> +	pinctrl-single,pins = <0xdc 0x118>;
> +
> +Where 0xdc is the offset from the pinctrl register base address for the
> +device pinctrl register, and 0x118 contains the desired value of the
> +pinctrl register. See the device example and static board pins example
> +below for more information.

There should be some explanation only the portion of this value covered
by the pinctrl-single,function-mask value is updated in the register.

> +This driver tries to avoid understanding pin and function names because of
> +the extra bloat they would cause especially in the case of a large number
> +of pins. This driver just sets what is specified for the board in the .dts file.
> +Further user space debugging tools can be developed to decipher the pin and
> +function names using debugfs.

There shouldn't be any discussion of a driver here; the binding is a HW
description.

> +Example:

I only reviewed the binding document, not the code.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-21 22:13             ` Stephen Warren
  0 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-21 22:13 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On 06/19/2012 07:56 AM, Tony Lindgren wrote:
> Hi,
> 
> Below is the pinctrl-single patch updated with hopefully all the Stephen's
> comments addressed. The binding still needs to be looked at, see relevant
> parts of the discussion below.
...
> From: Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>
> Date: Wed, 6 Jun 2012 04:18:18 -0700
> Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
> 
> Add one-register-per-pin type device tree based pinctrl driver.
> 
> 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 can be added.
> 
> Signed-off-by: Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>
> 
> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> new file mode 100644
> index 0000000..929254c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> @@ -0,0 +1,106 @@
> +One-register-per-pin type device tree based pinctrl driver
> +
> +Required properties:
> +- compatible : "pinctrl-single"
> +
> +- reg : offset and length of the register set for the mux registers
> +
> +- pinctrl-single,register-width : pinmux register access width in bits
> +
> +- pinctrl-single,function-mask : mask of allowed pinmux function bits
> +  in the pinmux register
> +
> +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits in the
> +  pinmux register; this gets combined with pinconf mask but is a separate
> +  mask to allow the option of setting pinconf separatately from the
> +  function

Given that this binding doesn't allow describing pin configuration at
present, I would simply remove all mention of that property in the
binding documentation. It can be added back if/when that feature is
added. Any future driver using this binding can refuse to allow pin
configuration if that property is missing.

> +- pinctrl-single,function-off : function off mode for disabled state if
> +  available and same for all registers; if not, use a value larger than
> +  function-mask to ignore disabling of registers

Rather than requiring an invalid value in this property, shouldn't the
lack of a valid function-off value be represented by the property not
being present in the DT?

> +This driver assumes that there is only one register for each pin,
> +and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt
> +document in this directory.

At this point in the file, I think you need to mention that you're
switching from describing the top-level device node to describing pin
configuration nodes.

> +The pinctrl register offsets and default values are specified as pairs

I thought we were going to remove "default" here?

> +using pinctrl-single,pins. For example, setting a pin for a device
> +could be done with:
> +
> +	pinctrl-single,pins = <0xdc 0x118>;
> +
> +Where 0xdc is the offset from the pinctrl register base address for the
> +device pinctrl register, and 0x118 contains the desired value of the
> +pinctrl register. See the device example and static board pins example
> +below for more information.

There should be some explanation only the portion of this value covered
by the pinctrl-single,function-mask value is updated in the register.

> +This driver tries to avoid understanding pin and function names because of
> +the extra bloat they would cause especially in the case of a large number
> +of pins. This driver just sets what is specified for the board in the .dts file.
> +Further user space debugging tools can be developed to decipher the pin and
> +function names using debugfs.

There shouldn't be any discussion of a driver here; the binding is a HW
description.

> +Example:

I only reviewed the binding document, not the code.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-21 22:13             ` Stephen Warren
  0 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-21 22:13 UTC (permalink / raw)
  To: linux-arm-kernel

On 06/19/2012 07:56 AM, Tony Lindgren wrote:
> Hi,
> 
> Below is the pinctrl-single patch updated with hopefully all the Stephen's
> comments addressed. The binding still needs to be looked at, see relevant
> parts of the discussion below.
...
> From: Tony Lindgren <tony@atomide.com>
> Date: Wed, 6 Jun 2012 04:18:18 -0700
> Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
> 
> Add one-register-per-pin type device tree based pinctrl driver.
> 
> 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 can be added.
> 
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> 
> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> new file mode 100644
> index 0000000..929254c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> @@ -0,0 +1,106 @@
> +One-register-per-pin type device tree based pinctrl driver
> +
> +Required properties:
> +- compatible : "pinctrl-single"
> +
> +- reg : offset and length of the register set for the mux registers
> +
> +- pinctrl-single,register-width : pinmux register access width in bits
> +
> +- pinctrl-single,function-mask : mask of allowed pinmux function bits
> +  in the pinmux register
> +
> +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits in the
> +  pinmux register; this gets combined with pinconf mask but is a separate
> +  mask to allow the option of setting pinconf separatately from the
> +  function

Given that this binding doesn't allow describing pin configuration at
present, I would simply remove all mention of that property in the
binding documentation. It can be added back if/when that feature is
added. Any future driver using this binding can refuse to allow pin
configuration if that property is missing.

> +- pinctrl-single,function-off : function off mode for disabled state if
> +  available and same for all registers; if not, use a value larger than
> +  function-mask to ignore disabling of registers

Rather than requiring an invalid value in this property, shouldn't the
lack of a valid function-off value be represented by the property not
being present in the DT?

> +This driver assumes that there is only one register for each pin,
> +and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt
> +document in this directory.

At this point in the file, I think you need to mention that you're
switching from describing the top-level device node to describing pin
configuration nodes.

> +The pinctrl register offsets and default values are specified as pairs

I thought we were going to remove "default" here?

> +using pinctrl-single,pins. For example, setting a pin for a device
> +could be done with:
> +
> +	pinctrl-single,pins = <0xdc 0x118>;
> +
> +Where 0xdc is the offset from the pinctrl register base address for the
> +device pinctrl register, and 0x118 contains the desired value of the
> +pinctrl register. See the device example and static board pins example
> +below for more information.

There should be some explanation only the portion of this value covered
by the pinctrl-single,function-mask value is updated in the register.

> +This driver tries to avoid understanding pin and function names because of
> +the extra bloat they would cause especially in the case of a large number
> +of pins. This driver just sets what is specified for the board in the .dts file.
> +Further user space debugging tools can be developed to decipher the pin and
> +function names using debugfs.

There shouldn't be any discussion of a driver here; the binding is a HW
description.

> +Example:

I only reviewed the binding document, not the code.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-22  8:39               ` Tony Lindgren
  0 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-22  8:39 UTC (permalink / raw)
  To: Stephen Warren
  Cc: Linus Walleij, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren, Arnd Bergmann, Grant Likely,
	Rob Herring

Hi,

* Stephen Warren <swarren@wwwdotorg.org> [120621 15:17]:
> On 06/19/2012 07:56 AM, Tony Lindgren wrote:
> > +
> > +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits in the
> > +  pinmux register; this gets combined with pinconf mask but is a separate
> > +  mask to allow the option of setting pinconf separatately from the
> > +  function
> 
> Given that this binding doesn't allow describing pin configuration at
> present, I would simply remove all mention of that property in the
> binding documentation. It can be added back if/when that feature is
> added. Any future driver using this binding can refuse to allow pin
> configuration if that property is missing.

It might be better to just add support for pin_config_get/set to avoid
changing the binding later:

 static int pcs_pinconf_get(struct pinctrl_dev *pctldev,
                                unsigned pin, unsigned long *config)
 {
-	return -ENOTSUPP;
+	struct pcs_device *pcs;
+	void __iomem *reg;
+	int res;
+
+	pcs = pinctrl_dev_get_drvdata(pctldev);
+	res = pcs_pin_to_reg(pcs, pin, &reg);
+	if (res)
+		return res;
+
+	return pcs->read(reg) & pcs->cmask;
 }

A have not done that yet as currently the pcs_pin_to_reg() would need to be
sorted out in somewhat clean manner.
 
> > +- pinctrl-single,function-off : function off mode for disabled state if
> > +  available and same for all registers; if not, use a value larger than
> > +  function-mask to ignore disabling of registers
> 
> Rather than requiring an invalid value in this property, shouldn't the
> lack of a valid function-off value be represented by the property not
> being present in the DT?

Heh good point :) Will change.
 
> > +This driver assumes that there is only one register for each pin,
> > +and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt
> > +document in this directory.
> 
> At this point in the file, I think you need to mention that you're
> switching from describing the top-level device node to describing pin
> configuration nodes.

Will add.
 
> > +The pinctrl register offsets and default values are specified as pairs
> 
> I thought we were going to remove "default" here?

Oops, looks like one was left, will remove.
 
> > +using pinctrl-single,pins. For example, setting a pin for a device
> > +could be done with:
> > +
> > +	pinctrl-single,pins = <0xdc 0x118>;
> > +
> > +Where 0xdc is the offset from the pinctrl register base address for the
> > +device pinctrl register, and 0x118 contains the desired value of the
> > +pinctrl register. See the device example and static board pins example
> > +below for more information.
> 
> There should be some explanation only the portion of this value covered
> by the pinctrl-single,function-mask value is updated in the register.

OK
 
> > +This driver tries to avoid understanding pin and function names because of
> > +the extra bloat they would cause especially in the case of a large number
> > +of pins. This driver just sets what is specified for the board in the .dts file.
> > +Further user space debugging tools can be developed to decipher the pin and
> > +function names using debugfs.
> 
> There shouldn't be any discussion of a driver here; the binding is a HW
> description.

Will move that to the driver comments.
 
> > +Example:
> 
> I only reviewed the binding document, not the code.

Thanks,

Tony

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-22  8:39               ` Tony Lindgren
  0 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-22  8:39 UTC (permalink / raw)
  To: Stephen Warren
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi,

* Stephen Warren <swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org> [120621 15:17]:
> On 06/19/2012 07:56 AM, Tony Lindgren wrote:
> > +
> > +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits in the
> > +  pinmux register; this gets combined with pinconf mask but is a separate
> > +  mask to allow the option of setting pinconf separatately from the
> > +  function
> 
> Given that this binding doesn't allow describing pin configuration at
> present, I would simply remove all mention of that property in the
> binding documentation. It can be added back if/when that feature is
> added. Any future driver using this binding can refuse to allow pin
> configuration if that property is missing.

It might be better to just add support for pin_config_get/set to avoid
changing the binding later:

 static int pcs_pinconf_get(struct pinctrl_dev *pctldev,
                                unsigned pin, unsigned long *config)
 {
-	return -ENOTSUPP;
+	struct pcs_device *pcs;
+	void __iomem *reg;
+	int res;
+
+	pcs = pinctrl_dev_get_drvdata(pctldev);
+	res = pcs_pin_to_reg(pcs, pin, &reg);
+	if (res)
+		return res;
+
+	return pcs->read(reg) & pcs->cmask;
 }

A have not done that yet as currently the pcs_pin_to_reg() would need to be
sorted out in somewhat clean manner.
 
> > +- pinctrl-single,function-off : function off mode for disabled state if
> > +  available and same for all registers; if not, use a value larger than
> > +  function-mask to ignore disabling of registers
> 
> Rather than requiring an invalid value in this property, shouldn't the
> lack of a valid function-off value be represented by the property not
> being present in the DT?

Heh good point :) Will change.
 
> > +This driver assumes that there is only one register for each pin,
> > +and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt
> > +document in this directory.
> 
> At this point in the file, I think you need to mention that you're
> switching from describing the top-level device node to describing pin
> configuration nodes.

Will add.
 
> > +The pinctrl register offsets and default values are specified as pairs
> 
> I thought we were going to remove "default" here?

Oops, looks like one was left, will remove.
 
> > +using pinctrl-single,pins. For example, setting a pin for a device
> > +could be done with:
> > +
> > +	pinctrl-single,pins = <0xdc 0x118>;
> > +
> > +Where 0xdc is the offset from the pinctrl register base address for the
> > +device pinctrl register, and 0x118 contains the desired value of the
> > +pinctrl register. See the device example and static board pins example
> > +below for more information.
> 
> There should be some explanation only the portion of this value covered
> by the pinctrl-single,function-mask value is updated in the register.

OK
 
> > +This driver tries to avoid understanding pin and function names because of
> > +the extra bloat they would cause especially in the case of a large number
> > +of pins. This driver just sets what is specified for the board in the .dts file.
> > +Further user space debugging tools can be developed to decipher the pin and
> > +function names using debugfs.
> 
> There shouldn't be any discussion of a driver here; the binding is a HW
> description.

Will move that to the driver comments.
 
> > +Example:
> 
> I only reviewed the binding document, not the code.

Thanks,

Tony

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-22  8:39               ` Tony Lindgren
  0 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-22  8:39 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

* Stephen Warren <swarren@wwwdotorg.org> [120621 15:17]:
> On 06/19/2012 07:56 AM, Tony Lindgren wrote:
> > +
> > +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits in the
> > +  pinmux register; this gets combined with pinconf mask but is a separate
> > +  mask to allow the option of setting pinconf separatately from the
> > +  function
> 
> Given that this binding doesn't allow describing pin configuration at
> present, I would simply remove all mention of that property in the
> binding documentation. It can be added back if/when that feature is
> added. Any future driver using this binding can refuse to allow pin
> configuration if that property is missing.

It might be better to just add support for pin_config_get/set to avoid
changing the binding later:

 static int pcs_pinconf_get(struct pinctrl_dev *pctldev,
                                unsigned pin, unsigned long *config)
 {
-	return -ENOTSUPP;
+	struct pcs_device *pcs;
+	void __iomem *reg;
+	int res;
+
+	pcs = pinctrl_dev_get_drvdata(pctldev);
+	res = pcs_pin_to_reg(pcs, pin, &reg);
+	if (res)
+		return res;
+
+	return pcs->read(reg) & pcs->cmask;
 }

A have not done that yet as currently the pcs_pin_to_reg() would need to be
sorted out in somewhat clean manner.
 
> > +- pinctrl-single,function-off : function off mode for disabled state if
> > +  available and same for all registers; if not, use a value larger than
> > +  function-mask to ignore disabling of registers
> 
> Rather than requiring an invalid value in this property, shouldn't the
> lack of a valid function-off value be represented by the property not
> being present in the DT?

Heh good point :) Will change.
 
> > +This driver assumes that there is only one register for each pin,
> > +and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt
> > +document in this directory.
> 
> At this point in the file, I think you need to mention that you're
> switching from describing the top-level device node to describing pin
> configuration nodes.

Will add.
 
> > +The pinctrl register offsets and default values are specified as pairs
> 
> I thought we were going to remove "default" here?

Oops, looks like one was left, will remove.
 
> > +using pinctrl-single,pins. For example, setting a pin for a device
> > +could be done with:
> > +
> > +	pinctrl-single,pins = <0xdc 0x118>;
> > +
> > +Where 0xdc is the offset from the pinctrl register base address for the
> > +device pinctrl register, and 0x118 contains the desired value of the
> > +pinctrl register. See the device example and static board pins example
> > +below for more information.
> 
> There should be some explanation only the portion of this value covered
> by the pinctrl-single,function-mask value is updated in the register.

OK
 
> > +This driver tries to avoid understanding pin and function names because of
> > +the extra bloat they would cause especially in the case of a large number
> > +of pins. This driver just sets what is specified for the board in the .dts file.
> > +Further user space debugging tools can be developed to decipher the pin and
> > +function names using debugfs.
> 
> There shouldn't be any discussion of a driver here; the binding is a HW
> description.

Will move that to the driver comments.
 
> > +Example:
> 
> I only reviewed the binding document, not the code.

Thanks,

Tony

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
  2012-06-22  8:39               ` Tony Lindgren
@ 2012-06-22 17:32                 ` Stephen Warren
  -1 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-22 17:32 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Linus Walleij, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren, Arnd Bergmann, Grant Likely,
	Rob Herring

On 06/22/2012 02:39 AM, Tony Lindgren wrote:
> Hi,
> 
> * Stephen Warren <swarren@wwwdotorg.org> [120621 15:17]:
>> On 06/19/2012 07:56 AM, Tony Lindgren wrote:
>>> +
>>> +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits in the
>>> +  pinmux register; this gets combined with pinconf mask but is a separate
>>> +  mask to allow the option of setting pinconf separatately from the
>>> +  function
>>
>> Given that this binding doesn't allow describing pin configuration at
>> present, I would simply remove all mention of that property in the
>> binding documentation. It can be added back if/when that feature is
>> added. Any future driver using this binding can refuse to allow pin
>> configuration if that property is missing.
> 
> It might be better to just add support for pin_config_get/set to avoid
> changing the binding later:
> 
>  static int pcs_pinconf_get(struct pinctrl_dev *pctldev,
>                                 unsigned pin, unsigned long *config)
>  {
> -	return -ENOTSUPP;
> +	struct pcs_device *pcs;
> +	void __iomem *reg;
> +	int res;
> +
> +	pcs = pinctrl_dev_get_drvdata(pctldev);
> +	res = pcs_pin_to_reg(pcs, pin, &reg);
> +	if (res)
> +		return res;
> +
> +	return pcs->read(reg) & pcs->cmask;
>  }
> 
> A have not done that yet as currently the pcs_pin_to_reg() would need to be
> sorted out in somewhat clean manner.

Yes, implementing pinconf in the driver and binding would be a fine
alternative; I just assumed you'd want to defer that. How would pinconf
be represented in DT; just extra bits set in the value portion of the
pins property? Thinking quickly, I guess that would work fine, since the
binding's assumption is presumably that there's a 1:1 mapping between
the set of pins that functions can be mux'd onto and the set of pins
that can have pinconf applied, and the register to do both muxing and
pinconf is the same.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-22 17:32                 ` Stephen Warren
  0 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-22 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

On 06/22/2012 02:39 AM, Tony Lindgren wrote:
> Hi,
> 
> * Stephen Warren <swarren@wwwdotorg.org> [120621 15:17]:
>> On 06/19/2012 07:56 AM, Tony Lindgren wrote:
>>> +
>>> +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits in the
>>> +  pinmux register; this gets combined with pinconf mask but is a separate
>>> +  mask to allow the option of setting pinconf separatately from the
>>> +  function
>>
>> Given that this binding doesn't allow describing pin configuration at
>> present, I would simply remove all mention of that property in the
>> binding documentation. It can be added back if/when that feature is
>> added. Any future driver using this binding can refuse to allow pin
>> configuration if that property is missing.
> 
> It might be better to just add support for pin_config_get/set to avoid
> changing the binding later:
> 
>  static int pcs_pinconf_get(struct pinctrl_dev *pctldev,
>                                 unsigned pin, unsigned long *config)
>  {
> -	return -ENOTSUPP;
> +	struct pcs_device *pcs;
> +	void __iomem *reg;
> +	int res;
> +
> +	pcs = pinctrl_dev_get_drvdata(pctldev);
> +	res = pcs_pin_to_reg(pcs, pin, &reg);
> +	if (res)
> +		return res;
> +
> +	return pcs->read(reg) & pcs->cmask;
>  }
> 
> A have not done that yet as currently the pcs_pin_to_reg() would need to be
> sorted out in somewhat clean manner.

Yes, implementing pinconf in the driver and binding would be a fine
alternative; I just assumed you'd want to defer that. How would pinconf
be represented in DT; just extra bits set in the value portion of the
pins property? Thinking quickly, I guess that would work fine, since the
binding's assumption is presumably that there's a 1:1 mapping between
the set of pins that functions can be mux'd onto and the set of pins
that can have pinconf applied, and the register to do both muxing and
pinconf is the same.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
  2012-06-22 17:32                 ` Stephen Warren
@ 2012-06-26 13:43                   ` Tony Lindgren
  -1 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-26 13:43 UTC (permalink / raw)
  To: Stephen Warren
  Cc: Linus Walleij, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren, Arnd Bergmann, Grant Likely,
	Rob Herring

* Stephen Warren <swarren@wwwdotorg.org> [120622 10:37]:
> On 06/22/2012 02:39 AM, Tony Lindgren wrote:
> > Hi,
> > 
> > * Stephen Warren <swarren@wwwdotorg.org> [120621 15:17]:
> >> On 06/19/2012 07:56 AM, Tony Lindgren wrote:
> >>> +
> >>> +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits in the
> >>> +  pinmux register; this gets combined with pinconf mask but is a separate
> >>> +  mask to allow the option of setting pinconf separatately from the
> >>> +  function
> >>
> >> Given that this binding doesn't allow describing pin configuration at
> >> present, I would simply remove all mention of that property in the
> >> binding documentation. It can be added back if/when that feature is
> >> added. Any future driver using this binding can refuse to allow pin
> >> configuration if that property is missing.
> > 
> > It might be better to just add support for pin_config_get/set to avoid
> > changing the binding later:
> > 
> >  static int pcs_pinconf_get(struct pinctrl_dev *pctldev,
> >                                 unsigned pin, unsigned long *config)
> >  {
> > -	return -ENOTSUPP;
> > +	struct pcs_device *pcs;
> > +	void __iomem *reg;
> > +	int res;
> > +
> > +	pcs = pinctrl_dev_get_drvdata(pctldev);
> > +	res = pcs_pin_to_reg(pcs, pin, &reg);
> > +	if (res)
> > +		return res;
> > +
> > +	return pcs->read(reg) & pcs->cmask;
> >  }
> > 
> > A have not done that yet as currently the pcs_pin_to_reg() would need to be
> > sorted out in somewhat clean manner.
> 
> Yes, implementing pinconf in the driver and binding would be a fine
> alternative; I just assumed you'd want to defer that. How would pinconf
> be represented in DT; just extra bits set in the value portion of the
> pins property? Thinking quickly, I guess that would work fine, since the
> binding's assumption is presumably that there's a 1:1 mapping between
> the set of pins that functions can be mux'd onto and the set of pins
> that can have pinconf applied, and the register to do both muxing and
> pinconf is the same.

Yes that's what I was thinking too.. But after some experiments, I'm
now thinking that we are better off dropping pinconf-mask for now and
only using one mask like you suggested earlier.

The reasoning for doing this is following:

1. Currently pin_config_get() requires the use of driver name and pin
   name, and we don't have a clean way of finding a pin register
   information without using names. That means that implementing
   pcs_pin_to_reg() in pinctrl-single is going to be ugly.

2. Eventually I'd like to make pin names optional in the pinctrl
   framework to save some parsing and memory in the DT case for SoCs
   that have a large number of pins. So adding more dependency to
   the pin names will just make that more complex.

3. We can already do the dynamic remuxing with named states and
   pinctrl_select_state().

4. If needed, we can add support for the pinconf-mask later on like
   you mentioned earlier.

Updated patch below.

Regards,

Tony


From: Tony Lindgren <tony@atomide.com>
Date: Tue, 26 Jun 2012 04:27:15 -0700
Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver

Add one-register-per-pin type device tree based pinctrl driver.

This driver has been tested 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 can be added.

Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
new file mode 100644
index 0000000..4623c50
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
@@ -0,0 +1,93 @@
+One-register-per-pin type device tree based pinctrl driver
+
+Required properties:
+- compatible : "pinctrl-single"
+
+- reg : offset and length of the register set for the mux registers
+
+- pinctrl-single,register-width : pinmux register access width in bits
+
+- pinctrl-single,function-mask : mask of allowed pinmux function bits
+  in the pinmux register
+
+Optional properties:
+- pinctrl-single,function-off : function off mode for disabled state if
+  available and same for all registers; if not specified, disabling of
+  pin functions is ignored
+
+This driver assumes that there is only one register for each pin,
+and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt
+document in this directory.
+
+The pin configuration nodes for pinctrl-single are specified as pinctrl
+register offset and value pairs using pinctrl-single,pins. Only the bits
+specified in pinctrl-single,function-mask are updated. For example, setting
+a pin for a device could be done with:
+
+	pinctrl-single,pins = <0xdc 0x118>;
+
+Where 0xdc is the offset from the pinctrl register base address for the
+device pinctrl register, and 0x118 contains the desired value of the
+pinctrl register. See the device example and static board pins example
+below for more information.
+
+Example:
+
+/* SoC common file */
+
+/* first controller instance for pins in core domain */
+pmx_core: pinmux@4a100040 {
+	compatible = "pinctrl-single";
+	reg = <0x4a100040 0x0196>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0xffff>;
+};
+
+/* second controller instance for pins in wkup domain */
+pmx_wkup: pinmux@4a31e040 {
+	compatible = "pinctrl-single;
+	reg = <0x4a31e040 0x0038>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0xffff>;
+};
+
+
+/* board specific .dts file */
+
+&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 {
+		pinctrl-single,pins = <
+			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
+			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
+			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
+			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */
+		>;
+	};
+
+	/* map uart2 pins */
+	uart2_pins: pinmux_uart2_pins {
+		pinctrl-single,pins = <
+			0xd8 0x118	/* uart2_cts INPUT_PULLUP | MODE0 */
+			0xda 0		/* uart2_rts OUTPUT | MODE0 */
+			0xdc 0x118	/* uart2_rx INPUT_PULLUP | MODE0 */
+			0xde 0		/* uart2_tx OUTPUT | MODE0 */
+		>;
+	};
+};
+
+&uart2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&uart2_pins>;
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index c6e6ae0..8071a31 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -102,6 +102,14 @@ config PINCTRL_PXA910
 	select PINCTRL_PXA3xx
 	select PINCONF
 
+config PINCTRL_SINGLE
+	tristate "One-register-per-pin type device tree based pinctrl driver"
+	depends on OF
+	select PINMUX
+	select PINCONF
+	help
+	  This selects the device tree based generic pinctrl driver.
+
 config PINCTRL_SIRF
 	bool "CSR SiRFprimaII pin controller driver"
 	depends on ARCH_PRIMA2
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 8c07437..f40b1f8 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK)	+= pinctrl-nomadik.o
 obj-$(CONFIG_PINCTRL_DB8500)	+= pinctrl-nomadik-db8500.o
 obj-$(CONFIG_PINCTRL_PXA168)	+= pinctrl-pxa168.o
 obj-$(CONFIG_PINCTRL_PXA910)	+= pinctrl-pxa910.o
+obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_SIRF)	+= pinctrl-sirf.o
 obj-$(CONFIG_PINCTRL_TEGRA)	+= pinctrl-tegra.o
 obj-$(CONFIG_PINCTRL_TEGRA20)	+= pinctrl-tegra20.o
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
new file mode 100644
index 0000000..76a4260
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -0,0 +1,987 @@
+/*
+ * Generic device tree based pinctrl driver for one register per pin
+ * type pinmux controllers
+ *
+ * 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-single"
+#define PCS_MUX_NAME			"pinctrl-single,pins"
+#define PCS_REG_NAME_LEN		((sizeof(unsigned long) * 2) + 1)
+#define PCS_OFF_DISABLED		~0U
+
+/**
+ * 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
+ * @node:	list node
+ */
+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
+ * @val:	register value
+ */
+struct pcs_func_vals {
+	void __iomem *reg;
+	unsigned val;
+};
+
+/**
+ * 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
+ *
+ * REVISIT: We should be able to drop this eventually by adding
+ * support for registering pins individually in the pinctrl
+ * framework for those drivers that don't need a static array.
+ */
+struct pcs_data {
+	struct pinctrl_pin_desc *pa;
+	int cur;
+};
+
+/**
+ * struct pcs_name - register name for a pin
+ * @name:	name of the pinctrl register
+ *
+ * REVISIT: We may want to make names optional in the pinctrl
+ * framework as some drivers may not care about pin names to
+ * avoid kernel bloat. The pin names can be deciphered by user
+ * space tools using debugfs based on the register address and
+ * SoC packaging information.
+ */
+struct pcs_name {
+	char name[PCS_REG_NAME_LEN];
+};
+
+/**
+ * struct pcs_device - pinctrl 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
+ * @fmax:	max number of functions in fmask
+ * @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 fmax;
+	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);
+};
+
+/*
+ * REVISIT: Reads and writes could eventually use regmap or something
+ * generic. But at least on omaps, some mux registers are performance
+ * critical as they may need to be remuxed every time before and after
+ * idle. Adding tests for register access width for every read and
+ * write like regmap is doing is not desired, and caching the registers
+ * does not help in this case.
+ */
+
+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 %s function%i\n",
+		func->name, fselector);
+
+	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->fmask;
+		val |= vals->val;
+		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;
+	}
+
+	/*
+	 * Ignore disable if function-off is not specified. Some hardware
+	 * does not have clearly defined disable function. For pin specific
+	 * off modes, you can use alternate named states as described in
+	 * pinctrl-bindings.txt.
+	 */
+	if (pcs->foff == PCS_OFF_DISABLED) {
+		dev_dbg(pcs->dev, "ignoring disable for %s function%i\n",
+			func->name, fselector);
+		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->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;
+	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];
+	sprintf(pn->name, "%lx",
+		(unsigned long)pcs->res->start + offset);
+	pin->name = pn->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.
+ */
+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 binding currently supports only sets of one register + value.
+ *
+ * Also note that this driver tries to avoid understanding pin and function
+ * names because of the extra bloat they would cause especially in the case of
+ * a large number of pins. This driver just sets what is specified for the board
+ * in the .dts file. Further user space debugging tools can be developed to
+ * decipher the pin and function names using debugfs.
+ *
+ * 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.
+ */
+static int 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;
+
+	mux = of_get_property(np, PCS_MUX_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 / 2;	/* Each row is a key value pair */
+
+	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, val;
+		int pin;
+
+		offset = be32_to_cpup(mux + index++);
+		val = be32_to_cpup(mux + index++);
+		vals[found].reg = pcs->base + offset;
+		vals[found].val = val;
+
+		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;
+	const char **pgnames;
+	int ret;
+
+	pcs = pinctrl_dev_get_drvdata(pctldev);
+
+	*map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL);
+	if (!map)
+		return -ENOMEM;
+
+	*num_maps = 0;
+
+	pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL);
+	if (!pgnames) {
+		ret = -ENOMEM;
+		goto free_map;
+	}
+
+	ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, pgnames);
+	if (ret < 0) {
+		dev_err(pcs->dev, "no pins entries for %s\n",
+			np_config->name);
+		goto free_pgnames;
+	}
+	*num_maps = 1;
+
+	return 0;
+
+free_pgnames:
+	devm_kfree(pcs->dev, pgnames);
+free_map:
+	devm_kfree(pcs->dev, *map);
+
+	return ret;
+}
+
+/**
+ * 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);
+}
+
+#define PCS_GET_PROP_U32(name, reg, err)				\
+	do {								\
+		ret = of_property_read_u32(np, name, reg);		\
+		if (ret) {						\
+			dev_err(pcs->dev, err);				\
+			return ret;					\
+		}							\
+	} while (0);
+
+static struct of_device_id pcs_of_match[];
+
+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-single,register-width", &pcs->width,
+			 "register width not specified\n");
+
+	PCS_GET_PROP_U32("pinctrl-single,function-mask", &pcs->fmask,
+			 "function register mask not specified\n");
+	pcs->fshift = ffs(pcs->fmask) - 1;
+	pcs->fmax = pcs->fmask >> pcs->fshift;
+
+	ret = of_property_read_u32(np, "pinctrl-single,function-off",
+					&pcs->foff);
+	if (ret)
+		pcs->foff = PCS_OFF_DISABLED;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(pcs->dev, "could not get resource\n");
+		return -ENODEV;
+	}
+
+	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");
+		return -EBUSY;
+	}
+
+	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");
+		return -ENODEV;
+	}
+
+	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;
+	}
+
+	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 single 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;
+}
+
+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);
+
+	return 0;
+}
+
+static struct of_device_id pcs_of_match[] __devinitdata = {
+	{ .compatible = DRIVER_NAME, },
+	{ },
+};
+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@atomide.com>");
+MODULE_DESCRIPTION("One-register-per-pin type device tree based pinctrl driver");
+MODULE_LICENSE("GPL v2");

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-26 13:43                   ` Tony Lindgren
  0 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-26 13:43 UTC (permalink / raw)
  To: linux-arm-kernel

* Stephen Warren <swarren@wwwdotorg.org> [120622 10:37]:
> On 06/22/2012 02:39 AM, Tony Lindgren wrote:
> > Hi,
> > 
> > * Stephen Warren <swarren@wwwdotorg.org> [120621 15:17]:
> >> On 06/19/2012 07:56 AM, Tony Lindgren wrote:
> >>> +
> >>> +- pinctrl-single,pinconf-mask : mask of allowed pinconf bits in the
> >>> +  pinmux register; this gets combined with pinconf mask but is a separate
> >>> +  mask to allow the option of setting pinconf separatately from the
> >>> +  function
> >>
> >> Given that this binding doesn't allow describing pin configuration at
> >> present, I would simply remove all mention of that property in the
> >> binding documentation. It can be added back if/when that feature is
> >> added. Any future driver using this binding can refuse to allow pin
> >> configuration if that property is missing.
> > 
> > It might be better to just add support for pin_config_get/set to avoid
> > changing the binding later:
> > 
> >  static int pcs_pinconf_get(struct pinctrl_dev *pctldev,
> >                                 unsigned pin, unsigned long *config)
> >  {
> > -	return -ENOTSUPP;
> > +	struct pcs_device *pcs;
> > +	void __iomem *reg;
> > +	int res;
> > +
> > +	pcs = pinctrl_dev_get_drvdata(pctldev);
> > +	res = pcs_pin_to_reg(pcs, pin, &reg);
> > +	if (res)
> > +		return res;
> > +
> > +	return pcs->read(reg) & pcs->cmask;
> >  }
> > 
> > A have not done that yet as currently the pcs_pin_to_reg() would need to be
> > sorted out in somewhat clean manner.
> 
> Yes, implementing pinconf in the driver and binding would be a fine
> alternative; I just assumed you'd want to defer that. How would pinconf
> be represented in DT; just extra bits set in the value portion of the
> pins property? Thinking quickly, I guess that would work fine, since the
> binding's assumption is presumably that there's a 1:1 mapping between
> the set of pins that functions can be mux'd onto and the set of pins
> that can have pinconf applied, and the register to do both muxing and
> pinconf is the same.

Yes that's what I was thinking too.. But after some experiments, I'm
now thinking that we are better off dropping pinconf-mask for now and
only using one mask like you suggested earlier.

The reasoning for doing this is following:

1. Currently pin_config_get() requires the use of driver name and pin
   name, and we don't have a clean way of finding a pin register
   information without using names. That means that implementing
   pcs_pin_to_reg() in pinctrl-single is going to be ugly.

2. Eventually I'd like to make pin names optional in the pinctrl
   framework to save some parsing and memory in the DT case for SoCs
   that have a large number of pins. So adding more dependency to
   the pin names will just make that more complex.

3. We can already do the dynamic remuxing with named states and
   pinctrl_select_state().

4. If needed, we can add support for the pinconf-mask later on like
   you mentioned earlier.

Updated patch below.

Regards,

Tony


From: Tony Lindgren <tony@atomide.com>
Date: Tue, 26 Jun 2012 04:27:15 -0700
Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver

Add one-register-per-pin type device tree based pinctrl driver.

This driver has been tested 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 can be added.

Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
new file mode 100644
index 0000000..4623c50
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
@@ -0,0 +1,93 @@
+One-register-per-pin type device tree based pinctrl driver
+
+Required properties:
+- compatible : "pinctrl-single"
+
+- reg : offset and length of the register set for the mux registers
+
+- pinctrl-single,register-width : pinmux register access width in bits
+
+- pinctrl-single,function-mask : mask of allowed pinmux function bits
+  in the pinmux register
+
+Optional properties:
+- pinctrl-single,function-off : function off mode for disabled state if
+  available and same for all registers; if not specified, disabling of
+  pin functions is ignored
+
+This driver assumes that there is only one register for each pin,
+and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt
+document in this directory.
+
+The pin configuration nodes for pinctrl-single are specified as pinctrl
+register offset and value pairs using pinctrl-single,pins. Only the bits
+specified in pinctrl-single,function-mask are updated. For example, setting
+a pin for a device could be done with:
+
+	pinctrl-single,pins = <0xdc 0x118>;
+
+Where 0xdc is the offset from the pinctrl register base address for the
+device pinctrl register, and 0x118 contains the desired value of the
+pinctrl register. See the device example and static board pins example
+below for more information.
+
+Example:
+
+/* SoC common file */
+
+/* first controller instance for pins in core domain */
+pmx_core: pinmux at 4a100040 {
+	compatible = "pinctrl-single";
+	reg = <0x4a100040 0x0196>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0xffff>;
+};
+
+/* second controller instance for pins in wkup domain */
+pmx_wkup: pinmux at 4a31e040 {
+	compatible = "pinctrl-single;
+	reg = <0x4a31e040 0x0038>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0xffff>;
+};
+
+
+/* board specific .dts file */
+
+&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 {
+		pinctrl-single,pins = <
+			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
+			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
+			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
+			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */
+		>;
+	};
+
+	/* map uart2 pins */
+	uart2_pins: pinmux_uart2_pins {
+		pinctrl-single,pins = <
+			0xd8 0x118	/* uart2_cts INPUT_PULLUP | MODE0 */
+			0xda 0		/* uart2_rts OUTPUT | MODE0 */
+			0xdc 0x118	/* uart2_rx INPUT_PULLUP | MODE0 */
+			0xde 0		/* uart2_tx OUTPUT | MODE0 */
+		>;
+	};
+};
+
+&uart2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&uart2_pins>;
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index c6e6ae0..8071a31 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -102,6 +102,14 @@ config PINCTRL_PXA910
 	select PINCTRL_PXA3xx
 	select PINCONF
 
+config PINCTRL_SINGLE
+	tristate "One-register-per-pin type device tree based pinctrl driver"
+	depends on OF
+	select PINMUX
+	select PINCONF
+	help
+	  This selects the device tree based generic pinctrl driver.
+
 config PINCTRL_SIRF
 	bool "CSR SiRFprimaII pin controller driver"
 	depends on ARCH_PRIMA2
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 8c07437..f40b1f8 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK)	+= pinctrl-nomadik.o
 obj-$(CONFIG_PINCTRL_DB8500)	+= pinctrl-nomadik-db8500.o
 obj-$(CONFIG_PINCTRL_PXA168)	+= pinctrl-pxa168.o
 obj-$(CONFIG_PINCTRL_PXA910)	+= pinctrl-pxa910.o
+obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_SIRF)	+= pinctrl-sirf.o
 obj-$(CONFIG_PINCTRL_TEGRA)	+= pinctrl-tegra.o
 obj-$(CONFIG_PINCTRL_TEGRA20)	+= pinctrl-tegra20.o
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
new file mode 100644
index 0000000..76a4260
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -0,0 +1,987 @@
+/*
+ * Generic device tree based pinctrl driver for one register per pin
+ * type pinmux controllers
+ *
+ * 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-single"
+#define PCS_MUX_NAME			"pinctrl-single,pins"
+#define PCS_REG_NAME_LEN		((sizeof(unsigned long) * 2) + 1)
+#define PCS_OFF_DISABLED		~0U
+
+/**
+ * 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
+ * @node:	list node
+ */
+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
+ * @val:	register value
+ */
+struct pcs_func_vals {
+	void __iomem *reg;
+	unsigned val;
+};
+
+/**
+ * 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
+ *
+ * REVISIT: We should be able to drop this eventually by adding
+ * support for registering pins individually in the pinctrl
+ * framework for those drivers that don't need a static array.
+ */
+struct pcs_data {
+	struct pinctrl_pin_desc *pa;
+	int cur;
+};
+
+/**
+ * struct pcs_name - register name for a pin
+ * @name:	name of the pinctrl register
+ *
+ * REVISIT: We may want to make names optional in the pinctrl
+ * framework as some drivers may not care about pin names to
+ * avoid kernel bloat. The pin names can be deciphered by user
+ * space tools using debugfs based on the register address and
+ * SoC packaging information.
+ */
+struct pcs_name {
+	char name[PCS_REG_NAME_LEN];
+};
+
+/**
+ * struct pcs_device - pinctrl 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
+ * @fmax:	max number of functions in fmask
+ * @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 fmax;
+	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);
+};
+
+/*
+ * REVISIT: Reads and writes could eventually use regmap or something
+ * generic. But at least on omaps, some mux registers are performance
+ * critical as they may need to be remuxed every time before and after
+ * idle. Adding tests for register access width for every read and
+ * write like regmap is doing is not desired, and caching the registers
+ * does not help in this case.
+ */
+
+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 %s function%i\n",
+		func->name, fselector);
+
+	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->fmask;
+		val |= vals->val;
+		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;
+	}
+
+	/*
+	 * Ignore disable if function-off is not specified. Some hardware
+	 * does not have clearly defined disable function. For pin specific
+	 * off modes, you can use alternate named states as described in
+	 * pinctrl-bindings.txt.
+	 */
+	if (pcs->foff == PCS_OFF_DISABLED) {
+		dev_dbg(pcs->dev, "ignoring disable for %s function%i\n",
+			func->name, fselector);
+		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->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;
+	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];
+	sprintf(pn->name, "%lx",
+		(unsigned long)pcs->res->start + offset);
+	pin->name = pn->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.
+ */
+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 binding currently supports only sets of one register + value.
+ *
+ * Also note that this driver tries to avoid understanding pin and function
+ * names because of the extra bloat they would cause especially in the case of
+ * a large number of pins. This driver just sets what is specified for the board
+ * in the .dts file. Further user space debugging tools can be developed to
+ * decipher the pin and function names using debugfs.
+ *
+ * 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.
+ */
+static int 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;
+
+	mux = of_get_property(np, PCS_MUX_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 / 2;	/* Each row is a key value pair */
+
+	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, val;
+		int pin;
+
+		offset = be32_to_cpup(mux + index++);
+		val = be32_to_cpup(mux + index++);
+		vals[found].reg = pcs->base + offset;
+		vals[found].val = val;
+
+		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;
+	const char **pgnames;
+	int ret;
+
+	pcs = pinctrl_dev_get_drvdata(pctldev);
+
+	*map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL);
+	if (!map)
+		return -ENOMEM;
+
+	*num_maps = 0;
+
+	pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL);
+	if (!pgnames) {
+		ret = -ENOMEM;
+		goto free_map;
+	}
+
+	ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, pgnames);
+	if (ret < 0) {
+		dev_err(pcs->dev, "no pins entries for %s\n",
+			np_config->name);
+		goto free_pgnames;
+	}
+	*num_maps = 1;
+
+	return 0;
+
+free_pgnames:
+	devm_kfree(pcs->dev, pgnames);
+free_map:
+	devm_kfree(pcs->dev, *map);
+
+	return ret;
+}
+
+/**
+ * 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);
+}
+
+#define PCS_GET_PROP_U32(name, reg, err)				\
+	do {								\
+		ret = of_property_read_u32(np, name, reg);		\
+		if (ret) {						\
+			dev_err(pcs->dev, err);				\
+			return ret;					\
+		}							\
+	} while (0);
+
+static struct of_device_id pcs_of_match[];
+
+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-single,register-width", &pcs->width,
+			 "register width not specified\n");
+
+	PCS_GET_PROP_U32("pinctrl-single,function-mask", &pcs->fmask,
+			 "function register mask not specified\n");
+	pcs->fshift = ffs(pcs->fmask) - 1;
+	pcs->fmax = pcs->fmask >> pcs->fshift;
+
+	ret = of_property_read_u32(np, "pinctrl-single,function-off",
+					&pcs->foff);
+	if (ret)
+		pcs->foff = PCS_OFF_DISABLED;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(pcs->dev, "could not get resource\n");
+		return -ENODEV;
+	}
+
+	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");
+		return -EBUSY;
+	}
+
+	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");
+		return -ENODEV;
+	}
+
+	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;
+	}
+
+	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 single 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;
+}
+
+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);
+
+	return 0;
+}
+
+static struct of_device_id pcs_of_match[] __devinitdata = {
+	{ .compatible = DRIVER_NAME, },
+	{ },
+};
+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@atomide.com>");
+MODULE_DESCRIPTION("One-register-per-pin type device tree based pinctrl driver");
+MODULE_LICENSE("GPL v2");

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
  2012-06-26 13:43                   ` Tony Lindgren
  (?)
@ 2012-06-26 17:05                     ` Stephen Warren
  -1 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-26 17:05 UTC (permalink / raw)
  To: Tony Lindgren, Grant Likely, Rob Herring, Olof Johansson, Arnd Bergmann
  Cc: Linus Walleij, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren, Arnd Bergmann

On 06/26/2012 07:43 AM, Tony Lindgren wrote:
...
> Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
> 
> Add one-register-per-pin type device tree based pinctrl driver.
> 
> This driver has been tested 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 can be added.

> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt

> +/* board specific .dts file */
> +
> +&pmx_core {

> +	board_pins: pinmux_board_pins {
> +		pinctrl-single,pins = <
> +			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
> +			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
> +			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
> +			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */

If you're removing the pinconf mask, I think the comments in the example
should reflect just setting a particular mux function, and remove any
references to pinconf settings in that field. While the binding can be
abused to do that, I think the docs shouldn't encourage it:-)

Other than that, the binding looks reasonable to me, given what it's
intended to do.

However, I'd still like Grant and Rob (and any other DT experts) to
explicitly sign off on this binding, because it's doing exactly
something that was rejected at Linaro Connect in February (albeit the
binding is slightly more oriented at specifically being for pinmux
rather than a fully general "blast in these register values", but that
distinction seems minor to me).

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-26 17:05                     ` Stephen Warren
  0 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-26 17:05 UTC (permalink / raw)
  To: Tony Lindgren, Grant Likely, Rob Herring, Olof Johansson
  Cc: Linus Walleij, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren, Arnd Bergmann

On 06/26/2012 07:43 AM, Tony Lindgren wrote:
...
> Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
> 
> Add one-register-per-pin type device tree based pinctrl driver.
> 
> This driver has been tested 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 can be added.

> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt

> +/* board specific .dts file */
> +
> +&pmx_core {

> +	board_pins: pinmux_board_pins {
> +		pinctrl-single,pins = <
> +			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
> +			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
> +			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
> +			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */

If you're removing the pinconf mask, I think the comments in the example
should reflect just setting a particular mux function, and remove any
references to pinconf settings in that field. While the binding can be
abused to do that, I think the docs shouldn't encourage it:-)

Other than that, the binding looks reasonable to me, given what it's
intended to do.

However, I'd still like Grant and Rob (and any other DT experts) to
explicitly sign off on this binding, because it's doing exactly
something that was rejected at Linaro Connect in February (albeit the
binding is slightly more oriented at specifically being for pinmux
rather than a fully general "blast in these register values", but that
distinction seems minor to me).

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-26 17:05                     ` Stephen Warren
  0 siblings, 0 replies; 37+ messages in thread
From: Stephen Warren @ 2012-06-26 17:05 UTC (permalink / raw)
  To: linux-arm-kernel

On 06/26/2012 07:43 AM, Tony Lindgren wrote:
...
> Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
> 
> Add one-register-per-pin type device tree based pinctrl driver.
> 
> This driver has been tested 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 can be added.

> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt

> +/* board specific .dts file */
> +
> +&pmx_core {

> +	board_pins: pinmux_board_pins {
> +		pinctrl-single,pins = <
> +			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
> +			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
> +			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
> +			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */

If you're removing the pinconf mask, I think the comments in the example
should reflect just setting a particular mux function, and remove any
references to pinconf settings in that field. While the binding can be
abused to do that, I think the docs shouldn't encourage it:-)

Other than that, the binding looks reasonable to me, given what it's
intended to do.

However, I'd still like Grant and Rob (and any other DT experts) to
explicitly sign off on this binding, because it's doing exactly
something that was rejected at Linaro Connect in February (albeit the
binding is slightly more oriented at specifically being for pinmux
rather than a fully general "blast in these register values", but that
distinction seems minor to me).

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
  2012-06-26 17:05                     ` Stephen Warren
@ 2012-06-27 10:28                       ` Tony Lindgren
  -1 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-27 10:28 UTC (permalink / raw)
  To: Stephen Warren
  Cc: Grant Likely, Rob Herring, Olof Johansson, Arnd Bergmann,
	Linus Walleij, linux-kernel, linux-arm-kernel, linux-omap,
	devicetree-discuss, Stephen Warren

* Stephen Warren <swarren@wwwdotorg.org> [120626 10:10]:
> On 06/26/2012 07:43 AM, Tony Lindgren wrote:
> ...
> > Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
> > 
> > Add one-register-per-pin type device tree based pinctrl driver.
> > 
> > This driver has been tested 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 can be added.
> 
> > diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> 
> > +/* board specific .dts file */
> > +
> > +&pmx_core {
> 
> > +	board_pins: pinmux_board_pins {
> > +		pinctrl-single,pins = <
> > +			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
> > +			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
> > +			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
> > +			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */
> 
> If you're removing the pinconf mask, I think the comments in the example
> should reflect just setting a particular mux function, and remove any
> references to pinconf settings in that field. While the binding can be
>  abused to do that, I think the docs shouldn't encourage it:-)

I can certainly leave it out of the binding doc :)

The pinconf part is essential for the driver to work at least in
the omap2/3/4 cases though, so as long as people are OK with that it
works for me. If people don't like this, then it's probably best to
just add back the separate pinconf mask.

> Other than that, the binding looks reasonable to me, given what it's
> intended to do.
> 
> However, I'd still like Grant and Rob (and any other DT experts) to
> explicitly sign off on this binding, because it's doing exactly
> something that was rejected at Linaro Connect in February (albeit the
> binding is slightly more oriented at specifically being for pinmux
> rather than a fully general "blast in these register values", but that
> distinction seems minor to me).

Just for the record, another alternative I thought about is to
describe mux register bits a bit like the suggested clock framework
binding does.

The problem trying to define named pin states is that we need to
add new column for any pinconf value that may be orred together
with other pinconf values.

Currently there are pinconf settings for active pin states and off
mode pin states. And there is wake-up enable setting. So that would
be a total of five columns already per pin with the offset and mux
function.. Then if something new gets added, let's say signal
strength, we'll be needing yet another column.

To me it seem we should rather count on the preprocessor to handle
cases like this eventually. Otherwise we'll end up with bloated DT
data that becomes slow to parse.

The analogy here that I think is closest to this binding is the
keymap binding. In this case the mux data is basically just a map
of few hundred board specific pin settings.

Just for an example, below is what it would look like for omaps
using names to map out the register bits. There are eight functions
for each register. Then there are four active pinconf settings,
and six off mode pinconf settings that make sense. And then there's
the wakeup enable setting.

Regards,

Tony

pmx_core: pinmux@4a100040 {
	compatible = "pinctrl-single";
	reg = <0x4a100040 0x0196>;
	#address-cells = <1>;
	#size-cells = <0>;
	pinctrl-single,register-width = <16>;

	/* 8 available pinmux functions */
	mode0: pinctrl_mode0 {
		pinctrl-single,function = <0>;
		pinctrl-single,mask = <0x7>;
	};

	mode1: pinctrl_mode1 {
		pinctrl-single,function = <0x1>;
		pinctrl-single,mask = <0x7>;
	};

	mode2: pinctrl_mode2 {
		pinctrl-single,function = <0x2>;
		pinctrl-single,mask = <0x7>;
	};
	...
	mode7: pinctrl_mode7 {
		pinctrl-single,function = <0x7>;
		pinctrl-single,mask = <0x7>;
	};

	/* 4 active state pinconf settings */
	pin_output: pinctrl_pin_output {
		pinctrl-single,pinconf = <0>;
		pinctrl-single,mask = <0x1f8>;
	};

	pin_input: pinctrl_pin_input {
		pinctrl-single,pinconf = <0x20>;
		pinctrl-single,mask = <0x1f8>;
	};

	pin_input_pullup: pinctrl_pin_input_pullup {
		pinctrl-single,pinconf = <0x23>;
		pinctrl-single,mask = <0x1f8>;
	};

	pin_input_pulldown: pinctrl_pin_input_pulldown {
		pinctrl-single,pinconf = <0x21>;
		pinctrl-single,mask = <0x1f8>;
	};

	/* 5 off mode pinconf settings */
	pin_off_none: pinctrl_pin_off_none {
		pinctrl-single,pinconf = <0>;
		pinctrl-single,mask = <0x7e00>;
	};

	pin_off_output_high: pinctrl_pin_off_output_high {
		pinctrl-single,pinconf = 0xb;
		pinctrl-single,mask = <0x7e00>;
	}
	...

	/* wake-up capability */
	pin_off_wakeup_ena: pinctrl_pin_off_wakeup_ena {
		pinctrl-single,pinconf = 0xb;
		pinctrl-single,mask = <0x8000>;
	}

};

&pmx_core {
	uart2_pins: pinmux_uart2_pins {
		pinctrl-single,pins = <
			0xd8 &mode0 &pin_input_pullup &off_mode_none 0
			0xda &mode0 &pin_output &off_mode_none 0
			0xdc &mode0 &pin_input_pullup &off_mode_none &pin_off_wakeup_ena
			0xde &mode0 &pin_output &off_mode_none 0
		>;
	};
};

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-06-27 10:28                       ` Tony Lindgren
  0 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-06-27 10:28 UTC (permalink / raw)
  To: linux-arm-kernel

* Stephen Warren <swarren@wwwdotorg.org> [120626 10:10]:
> On 06/26/2012 07:43 AM, Tony Lindgren wrote:
> ...
> > Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
> > 
> > Add one-register-per-pin type device tree based pinctrl driver.
> > 
> > This driver has been tested 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 can be added.
> 
> > diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> 
> > +/* board specific .dts file */
> > +
> > +&pmx_core {
> 
> > +	board_pins: pinmux_board_pins {
> > +		pinctrl-single,pins = <
> > +			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
> > +			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
> > +			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
> > +			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */
> 
> If you're removing the pinconf mask, I think the comments in the example
> should reflect just setting a particular mux function, and remove any
> references to pinconf settings in that field. While the binding can be
>  abused to do that, I think the docs shouldn't encourage it:-)

I can certainly leave it out of the binding doc :)

The pinconf part is essential for the driver to work at least in
the omap2/3/4 cases though, so as long as people are OK with that it
works for me. If people don't like this, then it's probably best to
just add back the separate pinconf mask.

> Other than that, the binding looks reasonable to me, given what it's
> intended to do.
> 
> However, I'd still like Grant and Rob (and any other DT experts) to
> explicitly sign off on this binding, because it's doing exactly
> something that was rejected at Linaro Connect in February (albeit the
> binding is slightly more oriented at specifically being for pinmux
> rather than a fully general "blast in these register values", but that
> distinction seems minor to me).

Just for the record, another alternative I thought about is to
describe mux register bits a bit like the suggested clock framework
binding does.

The problem trying to define named pin states is that we need to
add new column for any pinconf value that may be orred together
with other pinconf values.

Currently there are pinconf settings for active pin states and off
mode pin states. And there is wake-up enable setting. So that would
be a total of five columns already per pin with the offset and mux
function.. Then if something new gets added, let's say signal
strength, we'll be needing yet another column.

To me it seem we should rather count on the preprocessor to handle
cases like this eventually. Otherwise we'll end up with bloated DT
data that becomes slow to parse.

The analogy here that I think is closest to this binding is the
keymap binding. In this case the mux data is basically just a map
of few hundred board specific pin settings.

Just for an example, below is what it would look like for omaps
using names to map out the register bits. There are eight functions
for each register. Then there are four active pinconf settings,
and six off mode pinconf settings that make sense. And then there's
the wakeup enable setting.

Regards,

Tony

pmx_core: pinmux at 4a100040 {
	compatible = "pinctrl-single";
	reg = <0x4a100040 0x0196>;
	#address-cells = <1>;
	#size-cells = <0>;
	pinctrl-single,register-width = <16>;

	/* 8 available pinmux functions */
	mode0: pinctrl_mode0 {
		pinctrl-single,function = <0>;
		pinctrl-single,mask = <0x7>;
	};

	mode1: pinctrl_mode1 {
		pinctrl-single,function = <0x1>;
		pinctrl-single,mask = <0x7>;
	};

	mode2: pinctrl_mode2 {
		pinctrl-single,function = <0x2>;
		pinctrl-single,mask = <0x7>;
	};
	...
	mode7: pinctrl_mode7 {
		pinctrl-single,function = <0x7>;
		pinctrl-single,mask = <0x7>;
	};

	/* 4 active state pinconf settings */
	pin_output: pinctrl_pin_output {
		pinctrl-single,pinconf = <0>;
		pinctrl-single,mask = <0x1f8>;
	};

	pin_input: pinctrl_pin_input {
		pinctrl-single,pinconf = <0x20>;
		pinctrl-single,mask = <0x1f8>;
	};

	pin_input_pullup: pinctrl_pin_input_pullup {
		pinctrl-single,pinconf = <0x23>;
		pinctrl-single,mask = <0x1f8>;
	};

	pin_input_pulldown: pinctrl_pin_input_pulldown {
		pinctrl-single,pinconf = <0x21>;
		pinctrl-single,mask = <0x1f8>;
	};

	/* 5 off mode pinconf settings */
	pin_off_none: pinctrl_pin_off_none {
		pinctrl-single,pinconf = <0>;
		pinctrl-single,mask = <0x7e00>;
	};

	pin_off_output_high: pinctrl_pin_off_output_high {
		pinctrl-single,pinconf = 0xb;
		pinctrl-single,mask = <0x7e00>;
	}
	...

	/* wake-up capability */
	pin_off_wakeup_ena: pinctrl_pin_off_wakeup_ena {
		pinctrl-single,pinconf = 0xb;
		pinctrl-single,mask = <0x8000>;
	}

};

&pmx_core {
	uart2_pins: pinmux_uart2_pins {
		pinctrl-single,pins = <
			0xd8 &mode0 &pin_input_pullup &off_mode_none 0
			0xda &mode0 &pin_output &off_mode_none 0
			0xdc &mode0 &pin_input_pullup &off_mode_none &pin_off_wakeup_ena
			0xde &mode0 &pin_output &off_mode_none 0
		>;
	};
};

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
  2012-06-27 10:28                       ` Tony Lindgren
@ 2012-07-10  9:11                         ` Tony Lindgren
  -1 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-07-10  9:11 UTC (permalink / raw)
  To: Stephen Warren
  Cc: Stephen Warren, Arnd Bergmann, Linus Walleij, linux-kernel,
	Rob Herring, Grant Likely, Olof Johansson, linux-omap,
	devicetree-discuss, linux-arm-kernel

* Tony Lindgren <tony@atomide.com> [120627 03:37]:
> * Stephen Warren <swarren@wwwdotorg.org> [120626 10:10]:
> > On 06/26/2012 07:43 AM, Tony Lindgren wrote:
> > ...
> > > Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
> > > 
> > > Add one-register-per-pin type device tree based pinctrl driver.
> > > 
> > > This driver has been tested 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 can be added.
> > 
> > > diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> > 
> > > +/* board specific .dts file */
> > > +
> > > +&pmx_core {
> > 
> > > +	board_pins: pinmux_board_pins {
> > > +		pinctrl-single,pins = <
> > > +			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
> > > +			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
> > > +			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
> > > +			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */
> > 
> > If you're removing the pinconf mask, I think the comments in the example
> > should reflect just setting a particular mux function, and remove any
> > references to pinconf settings in that field. While the binding can be
> >  abused to do that, I think the docs shouldn't encourage it:-)
> 
> I can certainly leave it out of the binding doc :)
> 
> The pinconf part is essential for the driver to work at least in
> the omap2/3/4 cases though, so as long as people are OK with that it
> works for me. If people don't like this, then it's probably best to
> just add back the separate pinconf mask.

OK so no comments for a while. Here's the patch updated to leave out
the comments in the binding example.

> > Other than that, the binding looks reasonable to me, given what it's
> > intended to do.
> > 
> > However, I'd still like Grant and Rob (and any other DT experts) to
> > explicitly sign off on this binding, because it's doing exactly
> > something that was rejected at Linaro Connect in February (albeit the
> > binding is slightly more oriented at specifically being for pinmux
> > rather than a fully general "blast in these register values", but that
> > distinction seems minor to me).

It seems that we may be the only ones who care :)

Tony


From: Tony Lindgren <tony@atomide.com>
Date: Tue, 10 Jul 2012 02:05:46 -0700
Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver

Add one-register-per-pin type device tree based pinctrl driver.

This driver has been tested 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 can be added.

Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
new file mode 100644
index 0000000..5187f0d
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
@@ -0,0 +1,93 @@
+One-register-per-pin type device tree based pinctrl driver
+
+Required properties:
+- compatible : "pinctrl-single"
+
+- reg : offset and length of the register set for the mux registers
+
+- pinctrl-single,register-width : pinmux register access width in bits
+
+- pinctrl-single,function-mask : mask of allowed pinmux function bits
+  in the pinmux register
+
+Optional properties:
+- pinctrl-single,function-off : function off mode for disabled state if
+  available and same for all registers; if not specified, disabling of
+  pin functions is ignored
+
+This driver assumes that there is only one register for each pin,
+and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt
+document in this directory.
+
+The pin configuration nodes for pinctrl-single are specified as pinctrl
+register offset and value pairs using pinctrl-single,pins. Only the bits
+specified in pinctrl-single,function-mask are updated. For example, setting
+a pin for a device could be done with:
+
+	pinctrl-single,pins = <0xdc 0x118>;
+
+Where 0xdc is the offset from the pinctrl register base address for the
+device pinctrl register, and 0x118 contains the desired value of the
+pinctrl register. See the device example and static board pins example
+below for more information.
+
+Example:
+
+/* SoC common file */
+
+/* first controller instance for pins in core domain */
+pmx_core: pinmux@4a100040 {
+	compatible = "pinctrl-single";
+	reg = <0x4a100040 0x0196>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0xffff>;
+};
+
+/* second controller instance for pins in wkup domain */
+pmx_wkup: pinmux@4a31e040 {
+	compatible = "pinctrl-single;
+	reg = <0x4a31e040 0x0038>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0xffff>;
+};
+
+
+/* board specific .dts file */
+
+&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 {
+		pinctrl-single,pins = <
+			0x6c 0xf
+			0x6e 0xf
+			0x70 0xf
+			0x72 0xf
+		>;
+	};
+
+	/* map uart2 pins */
+	uart2_pins: pinmux_uart2_pins {
+		pinctrl-single,pins = <
+			0xd8 0x118
+			0xda 0
+			0xdc 0x118
+			0xde 0
+		>;
+	};
+};
+
+&uart2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&uart2_pins>;
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index c6e6ae0..8071a31 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -102,6 +102,14 @@ config PINCTRL_PXA910
 	select PINCTRL_PXA3xx
 	select PINCONF
 
+config PINCTRL_SINGLE
+	tristate "One-register-per-pin type device tree based pinctrl driver"
+	depends on OF
+	select PINMUX
+	select PINCONF
+	help
+	  This selects the device tree based generic pinctrl driver.
+
 config PINCTRL_SIRF
 	bool "CSR SiRFprimaII pin controller driver"
 	depends on ARCH_PRIMA2
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 8c07437..f40b1f8 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK)	+= pinctrl-nomadik.o
 obj-$(CONFIG_PINCTRL_DB8500)	+= pinctrl-nomadik-db8500.o
 obj-$(CONFIG_PINCTRL_PXA168)	+= pinctrl-pxa168.o
 obj-$(CONFIG_PINCTRL_PXA910)	+= pinctrl-pxa910.o
+obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_SIRF)	+= pinctrl-sirf.o
 obj-$(CONFIG_PINCTRL_TEGRA)	+= pinctrl-tegra.o
 obj-$(CONFIG_PINCTRL_TEGRA20)	+= pinctrl-tegra20.o
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
new file mode 100644
index 0000000..76a4260
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -0,0 +1,987 @@
+/*
+ * Generic device tree based pinctrl driver for one register per pin
+ * type pinmux controllers
+ *
+ * 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-single"
+#define PCS_MUX_NAME			"pinctrl-single,pins"
+#define PCS_REG_NAME_LEN		((sizeof(unsigned long) * 2) + 1)
+#define PCS_OFF_DISABLED		~0U
+
+/**
+ * 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
+ * @node:	list node
+ */
+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
+ * @val:	register value
+ */
+struct pcs_func_vals {
+	void __iomem *reg;
+	unsigned val;
+};
+
+/**
+ * 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
+ *
+ * REVISIT: We should be able to drop this eventually by adding
+ * support for registering pins individually in the pinctrl
+ * framework for those drivers that don't need a static array.
+ */
+struct pcs_data {
+	struct pinctrl_pin_desc *pa;
+	int cur;
+};
+
+/**
+ * struct pcs_name - register name for a pin
+ * @name:	name of the pinctrl register
+ *
+ * REVISIT: We may want to make names optional in the pinctrl
+ * framework as some drivers may not care about pin names to
+ * avoid kernel bloat. The pin names can be deciphered by user
+ * space tools using debugfs based on the register address and
+ * SoC packaging information.
+ */
+struct pcs_name {
+	char name[PCS_REG_NAME_LEN];
+};
+
+/**
+ * struct pcs_device - pinctrl 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
+ * @fmax:	max number of functions in fmask
+ * @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 fmax;
+	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);
+};
+
+/*
+ * REVISIT: Reads and writes could eventually use regmap or something
+ * generic. But at least on omaps, some mux registers are performance
+ * critical as they may need to be remuxed every time before and after
+ * idle. Adding tests for register access width for every read and
+ * write like regmap is doing is not desired, and caching the registers
+ * does not help in this case.
+ */
+
+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 %s function%i\n",
+		func->name, fselector);
+
+	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->fmask;
+		val |= vals->val;
+		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;
+	}
+
+	/*
+	 * Ignore disable if function-off is not specified. Some hardware
+	 * does not have clearly defined disable function. For pin specific
+	 * off modes, you can use alternate named states as described in
+	 * pinctrl-bindings.txt.
+	 */
+	if (pcs->foff == PCS_OFF_DISABLED) {
+		dev_dbg(pcs->dev, "ignoring disable for %s function%i\n",
+			func->name, fselector);
+		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->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;
+	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];
+	sprintf(pn->name, "%lx",
+		(unsigned long)pcs->res->start + offset);
+	pin->name = pn->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.
+ */
+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 binding currently supports only sets of one register + value.
+ *
+ * Also note that this driver tries to avoid understanding pin and function
+ * names because of the extra bloat they would cause especially in the case of
+ * a large number of pins. This driver just sets what is specified for the board
+ * in the .dts file. Further user space debugging tools can be developed to
+ * decipher the pin and function names using debugfs.
+ *
+ * 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.
+ */
+static int 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;
+
+	mux = of_get_property(np, PCS_MUX_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 / 2;	/* Each row is a key value pair */
+
+	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, val;
+		int pin;
+
+		offset = be32_to_cpup(mux + index++);
+		val = be32_to_cpup(mux + index++);
+		vals[found].reg = pcs->base + offset;
+		vals[found].val = val;
+
+		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;
+	const char **pgnames;
+	int ret;
+
+	pcs = pinctrl_dev_get_drvdata(pctldev);
+
+	*map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL);
+	if (!map)
+		return -ENOMEM;
+
+	*num_maps = 0;
+
+	pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL);
+	if (!pgnames) {
+		ret = -ENOMEM;
+		goto free_map;
+	}
+
+	ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, pgnames);
+	if (ret < 0) {
+		dev_err(pcs->dev, "no pins entries for %s\n",
+			np_config->name);
+		goto free_pgnames;
+	}
+	*num_maps = 1;
+
+	return 0;
+
+free_pgnames:
+	devm_kfree(pcs->dev, pgnames);
+free_map:
+	devm_kfree(pcs->dev, *map);
+
+	return ret;
+}
+
+/**
+ * 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);
+}
+
+#define PCS_GET_PROP_U32(name, reg, err)				\
+	do {								\
+		ret = of_property_read_u32(np, name, reg);		\
+		if (ret) {						\
+			dev_err(pcs->dev, err);				\
+			return ret;					\
+		}							\
+	} while (0);
+
+static struct of_device_id pcs_of_match[];
+
+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-single,register-width", &pcs->width,
+			 "register width not specified\n");
+
+	PCS_GET_PROP_U32("pinctrl-single,function-mask", &pcs->fmask,
+			 "function register mask not specified\n");
+	pcs->fshift = ffs(pcs->fmask) - 1;
+	pcs->fmax = pcs->fmask >> pcs->fshift;
+
+	ret = of_property_read_u32(np, "pinctrl-single,function-off",
+					&pcs->foff);
+	if (ret)
+		pcs->foff = PCS_OFF_DISABLED;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(pcs->dev, "could not get resource\n");
+		return -ENODEV;
+	}
+
+	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");
+		return -EBUSY;
+	}
+
+	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");
+		return -ENODEV;
+	}
+
+	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;
+	}
+
+	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 single 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;
+}
+
+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);
+
+	return 0;
+}
+
+static struct of_device_id pcs_of_match[] __devinitdata = {
+	{ .compatible = DRIVER_NAME, },
+	{ },
+};
+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@atomide.com>");
+MODULE_DESCRIPTION("One-register-per-pin type device tree based pinctrl driver");
+MODULE_LICENSE("GPL v2");

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-07-10  9:11                         ` Tony Lindgren
  0 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-07-10  9:11 UTC (permalink / raw)
  To: linux-arm-kernel

* Tony Lindgren <tony@atomide.com> [120627 03:37]:
> * Stephen Warren <swarren@wwwdotorg.org> [120626 10:10]:
> > On 06/26/2012 07:43 AM, Tony Lindgren wrote:
> > ...
> > > Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
> > > 
> > > Add one-register-per-pin type device tree based pinctrl driver.
> > > 
> > > This driver has been tested 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 can be added.
> > 
> > > diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> > 
> > > +/* board specific .dts file */
> > > +
> > > +&pmx_core {
> > 
> > > +	board_pins: pinmux_board_pins {
> > > +		pinctrl-single,pins = <
> > > +			0x6c 0xf	/* csi21_dx3 OUTPUT | MODE7 */
> > > +			0x6e 0xf	/* csi21_dy3 OUTPUT | MODE7 */
> > > +			0x70 0xf	/* csi21_dx4 OUTPUT | MODE7 */
> > > +			0x72 0xf	/* csi21_dy4 OUTPUT | MODE7 */
> > 
> > If you're removing the pinconf mask, I think the comments in the example
> > should reflect just setting a particular mux function, and remove any
> > references to pinconf settings in that field. While the binding can be
> >  abused to do that, I think the docs shouldn't encourage it:-)
> 
> I can certainly leave it out of the binding doc :)
> 
> The pinconf part is essential for the driver to work at least in
> the omap2/3/4 cases though, so as long as people are OK with that it
> works for me. If people don't like this, then it's probably best to
> just add back the separate pinconf mask.

OK so no comments for a while. Here's the patch updated to leave out
the comments in the binding example.

> > Other than that, the binding looks reasonable to me, given what it's
> > intended to do.
> > 
> > However, I'd still like Grant and Rob (and any other DT experts) to
> > explicitly sign off on this binding, because it's doing exactly
> > something that was rejected at Linaro Connect in February (albeit the
> > binding is slightly more oriented at specifically being for pinmux
> > rather than a fully general "blast in these register values", but that
> > distinction seems minor to me).

It seems that we may be the only ones who care :)

Tony


From: Tony Lindgren <tony@atomide.com>
Date: Tue, 10 Jul 2012 02:05:46 -0700
Subject: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver

Add one-register-per-pin type device tree based pinctrl driver.

This driver has been tested 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 can be added.

Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
new file mode 100644
index 0000000..5187f0d
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
@@ -0,0 +1,93 @@
+One-register-per-pin type device tree based pinctrl driver
+
+Required properties:
+- compatible : "pinctrl-single"
+
+- reg : offset and length of the register set for the mux registers
+
+- pinctrl-single,register-width : pinmux register access width in bits
+
+- pinctrl-single,function-mask : mask of allowed pinmux function bits
+  in the pinmux register
+
+Optional properties:
+- pinctrl-single,function-off : function off mode for disabled state if
+  available and same for all registers; if not specified, disabling of
+  pin functions is ignored
+
+This driver assumes that there is only one register for each pin,
+and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt
+document in this directory.
+
+The pin configuration nodes for pinctrl-single are specified as pinctrl
+register offset and value pairs using pinctrl-single,pins. Only the bits
+specified in pinctrl-single,function-mask are updated. For example, setting
+a pin for a device could be done with:
+
+	pinctrl-single,pins = <0xdc 0x118>;
+
+Where 0xdc is the offset from the pinctrl register base address for the
+device pinctrl register, and 0x118 contains the desired value of the
+pinctrl register. See the device example and static board pins example
+below for more information.
+
+Example:
+
+/* SoC common file */
+
+/* first controller instance for pins in core domain */
+pmx_core: pinmux at 4a100040 {
+	compatible = "pinctrl-single";
+	reg = <0x4a100040 0x0196>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0xffff>;
+};
+
+/* second controller instance for pins in wkup domain */
+pmx_wkup: pinmux at 4a31e040 {
+	compatible = "pinctrl-single;
+	reg = <0x4a31e040 0x0038>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-single,register-width = <16>;
+	pinctrl-single,function-mask = <0xffff>;
+};
+
+
+/* board specific .dts file */
+
+&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 {
+		pinctrl-single,pins = <
+			0x6c 0xf
+			0x6e 0xf
+			0x70 0xf
+			0x72 0xf
+		>;
+	};
+
+	/* map uart2 pins */
+	uart2_pins: pinmux_uart2_pins {
+		pinctrl-single,pins = <
+			0xd8 0x118
+			0xda 0
+			0xdc 0x118
+			0xde 0
+		>;
+	};
+};
+
+&uart2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&uart2_pins>;
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index c6e6ae0..8071a31 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -102,6 +102,14 @@ config PINCTRL_PXA910
 	select PINCTRL_PXA3xx
 	select PINCONF
 
+config PINCTRL_SINGLE
+	tristate "One-register-per-pin type device tree based pinctrl driver"
+	depends on OF
+	select PINMUX
+	select PINCONF
+	help
+	  This selects the device tree based generic pinctrl driver.
+
 config PINCTRL_SIRF
 	bool "CSR SiRFprimaII pin controller driver"
 	depends on ARCH_PRIMA2
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 8c07437..f40b1f8 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK)	+= pinctrl-nomadik.o
 obj-$(CONFIG_PINCTRL_DB8500)	+= pinctrl-nomadik-db8500.o
 obj-$(CONFIG_PINCTRL_PXA168)	+= pinctrl-pxa168.o
 obj-$(CONFIG_PINCTRL_PXA910)	+= pinctrl-pxa910.o
+obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_SIRF)	+= pinctrl-sirf.o
 obj-$(CONFIG_PINCTRL_TEGRA)	+= pinctrl-tegra.o
 obj-$(CONFIG_PINCTRL_TEGRA20)	+= pinctrl-tegra20.o
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
new file mode 100644
index 0000000..76a4260
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -0,0 +1,987 @@
+/*
+ * Generic device tree based pinctrl driver for one register per pin
+ * type pinmux controllers
+ *
+ * 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-single"
+#define PCS_MUX_NAME			"pinctrl-single,pins"
+#define PCS_REG_NAME_LEN		((sizeof(unsigned long) * 2) + 1)
+#define PCS_OFF_DISABLED		~0U
+
+/**
+ * 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
+ * @node:	list node
+ */
+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
+ * @val:	register value
+ */
+struct pcs_func_vals {
+	void __iomem *reg;
+	unsigned val;
+};
+
+/**
+ * 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
+ *
+ * REVISIT: We should be able to drop this eventually by adding
+ * support for registering pins individually in the pinctrl
+ * framework for those drivers that don't need a static array.
+ */
+struct pcs_data {
+	struct pinctrl_pin_desc *pa;
+	int cur;
+};
+
+/**
+ * struct pcs_name - register name for a pin
+ * @name:	name of the pinctrl register
+ *
+ * REVISIT: We may want to make names optional in the pinctrl
+ * framework as some drivers may not care about pin names to
+ * avoid kernel bloat. The pin names can be deciphered by user
+ * space tools using debugfs based on the register address and
+ * SoC packaging information.
+ */
+struct pcs_name {
+	char name[PCS_REG_NAME_LEN];
+};
+
+/**
+ * struct pcs_device - pinctrl 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
+ * @fmax:	max number of functions in fmask
+ * @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 fmax;
+	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);
+};
+
+/*
+ * REVISIT: Reads and writes could eventually use regmap or something
+ * generic. But at least on omaps, some mux registers are performance
+ * critical as they may need to be remuxed every time before and after
+ * idle. Adding tests for register access width for every read and
+ * write like regmap is doing is not desired, and caching the registers
+ * does not help in this case.
+ */
+
+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 %s function%i\n",
+		func->name, fselector);
+
+	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->fmask;
+		val |= vals->val;
+		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;
+	}
+
+	/*
+	 * Ignore disable if function-off is not specified. Some hardware
+	 * does not have clearly defined disable function. For pin specific
+	 * off modes, you can use alternate named states as described in
+	 * pinctrl-bindings.txt.
+	 */
+	if (pcs->foff == PCS_OFF_DISABLED) {
+		dev_dbg(pcs->dev, "ignoring disable for %s function%i\n",
+			func->name, fselector);
+		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->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;
+	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];
+	sprintf(pn->name, "%lx",
+		(unsigned long)pcs->res->start + offset);
+	pin->name = pn->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.
+ */
+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 binding currently supports only sets of one register + value.
+ *
+ * Also note that this driver tries to avoid understanding pin and function
+ * names because of the extra bloat they would cause especially in the case of
+ * a large number of pins. This driver just sets what is specified for the board
+ * in the .dts file. Further user space debugging tools can be developed to
+ * decipher the pin and function names using debugfs.
+ *
+ * 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.
+ */
+static int 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;
+
+	mux = of_get_property(np, PCS_MUX_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 / 2;	/* Each row is a key value pair */
+
+	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, val;
+		int pin;
+
+		offset = be32_to_cpup(mux + index++);
+		val = be32_to_cpup(mux + index++);
+		vals[found].reg = pcs->base + offset;
+		vals[found].val = val;
+
+		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;
+	const char **pgnames;
+	int ret;
+
+	pcs = pinctrl_dev_get_drvdata(pctldev);
+
+	*map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL);
+	if (!map)
+		return -ENOMEM;
+
+	*num_maps = 0;
+
+	pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL);
+	if (!pgnames) {
+		ret = -ENOMEM;
+		goto free_map;
+	}
+
+	ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, pgnames);
+	if (ret < 0) {
+		dev_err(pcs->dev, "no pins entries for %s\n",
+			np_config->name);
+		goto free_pgnames;
+	}
+	*num_maps = 1;
+
+	return 0;
+
+free_pgnames:
+	devm_kfree(pcs->dev, pgnames);
+free_map:
+	devm_kfree(pcs->dev, *map);
+
+	return ret;
+}
+
+/**
+ * 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);
+}
+
+#define PCS_GET_PROP_U32(name, reg, err)				\
+	do {								\
+		ret = of_property_read_u32(np, name, reg);		\
+		if (ret) {						\
+			dev_err(pcs->dev, err);				\
+			return ret;					\
+		}							\
+	} while (0);
+
+static struct of_device_id pcs_of_match[];
+
+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-single,register-width", &pcs->width,
+			 "register width not specified\n");
+
+	PCS_GET_PROP_U32("pinctrl-single,function-mask", &pcs->fmask,
+			 "function register mask not specified\n");
+	pcs->fshift = ffs(pcs->fmask) - 1;
+	pcs->fmax = pcs->fmask >> pcs->fshift;
+
+	ret = of_property_read_u32(np, "pinctrl-single,function-off",
+					&pcs->foff);
+	if (ret)
+		pcs->foff = PCS_OFF_DISABLED;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(pcs->dev, "could not get resource\n");
+		return -ENODEV;
+	}
+
+	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");
+		return -EBUSY;
+	}
+
+	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");
+		return -ENODEV;
+	}
+
+	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;
+	}
+
+	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 single 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;
+}
+
+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);
+
+	return 0;
+}
+
+static struct of_device_id pcs_of_match[] __devinitdata = {
+	{ .compatible = DRIVER_NAME, },
+	{ },
+};
+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@atomide.com>");
+MODULE_DESCRIPTION("One-register-per-pin type device tree based pinctrl driver");
+MODULE_LICENSE("GPL v2");

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-07-14 20:16                           ` Linus Walleij
  0 siblings, 0 replies; 37+ messages in thread
From: Linus Walleij @ 2012-07-14 20:16 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Stephen Warren, Stephen Warren, Arnd Bergmann, linux-kernel,
	Rob Herring, Grant Likely, Olof Johansson, linux-omap,
	devicetree-discuss, linux-arm-kernel

On Tue, Jul 10, 2012 at 11:11 AM, Tony Lindgren <tony@atomide.com> wrote:

> OK so no comments for a while. Here's the patch updated to leave out
> the comments in the binding example.

I reason like this:

- My fears is that the code gets hopeless to understand the mux, the
 only way to understand that aspect of the system will be to read the DTS
 and have the data sheet ready at hand.

But:

- Tony knows what he's doing and what is best for OMAP. And this gets
 (hopefully) all that OMAP mux code out of arch/arm.

- Surely it will be better to go through this subsystem if we're refactoring
  it all again later, and all drivers can be transferred to the abstract
  pinctrl API which is a big win in itself, bringing coherency to the
  drivers/* at large.

So applied it, so it can be evaluated in real operating environments.

But if I don't see OMAP transferred to use this I'll simply delete it
again. :-)

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-07-14 20:16                           ` Linus Walleij
  0 siblings, 0 replies; 37+ messages in thread
From: Linus Walleij @ 2012-07-14 20:16 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Stephen Warren, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Tue, Jul 10, 2012 at 11:11 AM, Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> wrote:

> OK so no comments for a while. Here's the patch updated to leave out
> the comments in the binding example.

I reason like this:

- My fears is that the code gets hopeless to understand the mux, the
 only way to understand that aspect of the system will be to read the DTS
 and have the data sheet ready at hand.

But:

- Tony knows what he's doing and what is best for OMAP. And this gets
 (hopefully) all that OMAP mux code out of arch/arm.

- Surely it will be better to go through this subsystem if we're refactoring
  it all again later, and all drivers can be transferred to the abstract
  pinctrl API which is a big win in itself, bringing coherency to the
  drivers/* at large.

So applied it, so it can be evaluated in real operating environments.

But if I don't see OMAP transferred to use this I'll simply delete it
again. :-)

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-07-14 20:16                           ` Linus Walleij
  0 siblings, 0 replies; 37+ messages in thread
From: Linus Walleij @ 2012-07-14 20:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jul 10, 2012 at 11:11 AM, Tony Lindgren <tony@atomide.com> wrote:

> OK so no comments for a while. Here's the patch updated to leave out
> the comments in the binding example.

I reason like this:

- My fears is that the code gets hopeless to understand the mux, the
 only way to understand that aspect of the system will be to read the DTS
 and have the data sheet ready at hand.

But:

- Tony knows what he's doing and what is best for OMAP. And this gets
 (hopefully) all that OMAP mux code out of arch/arm.

- Surely it will be better to go through this subsystem if we're refactoring
  it all again later, and all drivers can be transferred to the abstract
  pinctrl API which is a big win in itself, bringing coherency to the
  drivers/* at large.

So applied it, so it can be evaluated in real operating environments.

But if I don't see OMAP transferred to use this I'll simply delete it
again. :-)

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
  2012-07-14 20:16                           ` Linus Walleij
@ 2012-07-16  7:10                             ` Tony Lindgren
  -1 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-07-16  7:10 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Stephen Warren, Stephen Warren, Arnd Bergmann, linux-kernel,
	Rob Herring, Grant Likely, Olof Johansson, linux-omap,
	devicetree-discuss, linux-arm-kernel

* Linus Walleij <linus.walleij@linaro.org> [120714 13:21]:
> On Tue, Jul 10, 2012 at 11:11 AM, Tony Lindgren <tony@atomide.com> wrote:
> 
> > OK so no comments for a while. Here's the patch updated to leave out
> > the comments in the binding example.
> 
> I reason like this:
> 
> - My fears is that the code gets hopeless to understand the mux, the
>  only way to understand that aspect of the system will be to read the DTS
>  and have the data sheet ready at hand.

Yes that is a valid concern. Once we have the preprocessing available
for DTS files reading should be easier. Also user space tools can be
developed for showing the package specific data in detailed form.
 
> But:
> 
> - Tony knows what he's doing and what is best for OMAP. And this gets
>  (hopefully) all that OMAP mux code out of arch/arm.

Heh thanks for the positive feedback ;) Yes this should allow us to
drop close to 8k lines of data and code from arch/arm/mach-omap2 
once the conversion is done. I'm planning to drop the unused entries
first to cut down the data quite a bit. And already omap5 and am33xx
are DT only and using pinctrl-single to start with. The remaining
legacy mux code can be dropped once we are done with the DT conversion.

> - Surely it will be better to go through this subsystem if we're refactoring
>   it all again later, and all drivers can be transferred to the abstract
>   pinctrl API which is a big win in itself, bringing coherency to the
>   drivers/* at large.
> 
> So applied it, so it can be evaluated in real operating environments.
> 
> But if I don't see OMAP transferred to use this I'll simply delete it
> again. :-)

OK thanks, for reference some am33xx patches for beagle bone already
got posted using an earlier revision of this binding:

http://www.mail-archive.com/linux-omap@vger.kernel.org/msg71695.html

Regards,

Tony

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver
@ 2012-07-16  7:10                             ` Tony Lindgren
  0 siblings, 0 replies; 37+ messages in thread
From: Tony Lindgren @ 2012-07-16  7:10 UTC (permalink / raw)
  To: linux-arm-kernel

* Linus Walleij <linus.walleij@linaro.org> [120714 13:21]:
> On Tue, Jul 10, 2012 at 11:11 AM, Tony Lindgren <tony@atomide.com> wrote:
> 
> > OK so no comments for a while. Here's the patch updated to leave out
> > the comments in the binding example.
> 
> I reason like this:
> 
> - My fears is that the code gets hopeless to understand the mux, the
>  only way to understand that aspect of the system will be to read the DTS
>  and have the data sheet ready at hand.

Yes that is a valid concern. Once we have the preprocessing available
for DTS files reading should be easier. Also user space tools can be
developed for showing the package specific data in detailed form.
 
> But:
> 
> - Tony knows what he's doing and what is best for OMAP. And this gets
>  (hopefully) all that OMAP mux code out of arch/arm.

Heh thanks for the positive feedback ;) Yes this should allow us to
drop close to 8k lines of data and code from arch/arm/mach-omap2 
once the conversion is done. I'm planning to drop the unused entries
first to cut down the data quite a bit. And already omap5 and am33xx
are DT only and using pinctrl-single to start with. The remaining
legacy mux code can be dropped once we are done with the DT conversion.

> - Surely it will be better to go through this subsystem if we're refactoring
>   it all again later, and all drivers can be transferred to the abstract
>   pinctrl API which is a big win in itself, bringing coherency to the
>   drivers/* at large.
> 
> So applied it, so it can be evaluated in real operating environments.
> 
> But if I don't see OMAP transferred to use this I'll simply delete it
> again. :-)

OK thanks, for reference some am33xx patches for beagle bone already
got posted using an earlier revision of this binding:

http://www.mail-archive.com/linux-omap at vger.kernel.org/msg71695.html

Regards,

Tony

^ permalink raw reply	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2012-07-16  7:10 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-06-11 13:58 [PATCH] pinctrl: Add one-register-per-pin type device tree based pinctrl driver Tony Lindgren
2012-06-11 13:58 ` Tony Lindgren
2012-06-14 23:12 ` Stephen Warren
2012-06-14 23:12   ` Stephen Warren
2012-06-14 23:12   ` Stephen Warren
2012-06-15  9:49   ` Tony Lindgren
2012-06-15  9:49     ` Tony Lindgren
2012-06-15 16:17     ` Stephen Warren
2012-06-15 16:17       ` Stephen Warren
2012-06-18  5:50       ` Tony Lindgren
2012-06-18  5:50         ` Tony Lindgren
2012-06-19 13:56         ` Tony Lindgren
2012-06-19 13:56           ` Tony Lindgren
2012-06-21  8:09           ` Linus Walleij
2012-06-21  8:09             ` Linus Walleij
2012-06-21 22:13           ` Stephen Warren
2012-06-21 22:13             ` Stephen Warren
2012-06-21 22:13             ` Stephen Warren
2012-06-22  8:39             ` Tony Lindgren
2012-06-22  8:39               ` Tony Lindgren
2012-06-22  8:39               ` Tony Lindgren
2012-06-22 17:32               ` Stephen Warren
2012-06-22 17:32                 ` Stephen Warren
2012-06-26 13:43                 ` Tony Lindgren
2012-06-26 13:43                   ` Tony Lindgren
2012-06-26 17:05                   ` Stephen Warren
2012-06-26 17:05                     ` Stephen Warren
2012-06-26 17:05                     ` Stephen Warren
2012-06-27 10:28                     ` Tony Lindgren
2012-06-27 10:28                       ` Tony Lindgren
2012-07-10  9:11                       ` Tony Lindgren
2012-07-10  9:11                         ` Tony Lindgren
2012-07-14 20:16                         ` Linus Walleij
2012-07-14 20:16                           ` Linus Walleij
2012-07-14 20:16                           ` Linus Walleij
2012-07-16  7:10                           ` Tony Lindgren
2012-07-16  7:10                             ` Tony Lindgren

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.