[PATCH RFC v2 2/2] pinctrl: add pinctrl gpio binding support

Dong Aisheng b29396 at freescale.com
Fri May 18 09:12:35 EDT 2012


From: Dong Aisheng <dong.aisheng at linaro.org>

The gpio ranges standard dt binding format is
<&gpio $gpio_offset $pin_offset $npin>

The core will parse and register the pinctrl gpio ranges
from device tree.

TODO: add binding doc.

Signed-off-by: Dong Aisheng <dong.aisheng at linaro.org>

---
ChangeLog v1->v2:
* introduce standard binding for gpio range.
---
 drivers/pinctrl/devicetree.c    |   95 +++++++++++++++++++++++++++++++++++++++
 drivers/pinctrl/devicetree.h    |    3 -
 include/linux/pinctrl/pinctrl.h |   22 +++++++++
 3 files changed, 117 insertions(+), 3 deletions(-)

diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c
index fcb1de4..c1b87e6 100644
--- a/drivers/pinctrl/devicetree.c
+++ b/drivers/pinctrl/devicetree.c
@@ -17,7 +17,9 @@
  */
 
 #include <linux/device.h>
+#include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_gpio.h>
 #include <linux/pinctrl/pinctrl.h>
 #include <linux/slab.h>
 
@@ -247,3 +249,96 @@ err:
 	pinctrl_dt_free_maps(p);
 	return ret;
 }
+
+/*
+ * pinctrl_dt_add_gpio_range() - parse and register GPIO ranges from device tree
+ * @pctldev: pin controller device to add the range to
+ * @np: the device node contains the property @propname
+ * @propname: a list of phandles of gpios and corresponding data.
+ *  The format is: <&gpio0 $gpio_offset $pin_offset $count>
+ */
+int pinctrl_dt_add_gpio_ranges(struct pinctrl_dev *pctldev,
+				struct device_node *np,
+				const char *propname)
+{
+	struct pinctrl_gpio_range *ranges;
+	unsigned int gpio_offset, pin_offset, npins;
+	struct device_node *np_gpio;
+	struct property *prop;
+	const __be32 *list;
+	phandle phandle;
+	int nranges;
+	int size;
+	int i;
+
+	if (!np || !propname) {
+		dev_err(pctldev->dev, "no device node or gpios propname\n");
+		return -EINVAL;
+	}
+
+	prop = of_find_property(np, propname, &size);
+	if (!prop)
+		return -ENODEV;
+
+	list = prop->value;
+	size /= sizeof(*list);
+	if (size % 4) {
+		dev_err(pctldev->dev, "wrong gpio range table format\n");
+		return -EINVAL;
+	}
+
+	nranges = size /4;
+	ranges = devm_kzalloc(pctldev->dev, nranges * sizeof(*ranges),
+				GFP_KERNEL);
+	for (i = 0; i < nranges; i++) {
+			phandle = be32_to_cpup(list++);
+			np_gpio = of_find_node_by_phandle(phandle);
+			if (!np_gpio) {
+				dev_err(pctldev->dev,
+					"failed to find gpio node(%s)\n",
+					np_gpio->name);
+				return -ENODEV;
+			}
+
+			ranges[i].gc = of_node_to_gpiochip(np_gpio);
+			if (!ranges[i].gc) {
+				dev_err(pctldev->dev,
+					"can not find gpio chip of node(%s)\n",
+					np->name);
+				of_node_put(np_gpio);
+				return -ENODEV;
+			}
+
+			gpio_offset = be32_to_cpu(*list++);
+			pin_offset = be32_to_cpu(*list++);
+			npins = be32_to_cpu(*list++);
+
+			if (gpio_offset < 0 ||
+			    gpio_offset > ranges[i].gc->ngpio ||
+			    pin_offset < 0 || npins < 0) {
+				dev_err(pctldev->dev,
+					"wrong data in the gpio range table\n");
+				of_node_put(np_gpio);
+				return -ENODEV;
+			}
+
+			ranges[i].name = dev_name(pctldev->dev);
+			ranges[i].base = ranges[i].gc->base + gpio_offset;
+			ranges[i].pin_base = pin_offset;
+			ranges[i].npins = npins;
+			of_node_put(np_gpio);
+	}
+
+	pinctrl_add_gpio_ranges(pctldev, ranges, nranges);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pinctrl_dt_add_gpio_ranges);
+
+void pinctrl_dt_remove_gpio_ranges(struct pinctrl_dev *pctldev,
+				   struct pinctrl_gpio_range *ranges,
+				   unsigned nranges)
+{
+	pinctrl_remove_gpio_ranges(pctldev, ranges, nranges);
+}
+EXPORT_SYMBOL_GPL(pinctrl_dt_remove_gpio_ranges);
diff --git a/drivers/pinctrl/devicetree.h b/drivers/pinctrl/devicetree.h
index 760bc49..9f79657 100644
--- a/drivers/pinctrl/devicetree.h
+++ b/drivers/pinctrl/devicetree.h
@@ -20,9 +20,7 @@
 
 void pinctrl_dt_free_maps(struct pinctrl *p);
 int pinctrl_dt_to_map(struct pinctrl *p);
-
 #else
-
 static inline int pinctrl_dt_to_map(struct pinctrl *p)
 {
 	return 0;
@@ -31,5 +29,4 @@ static inline int pinctrl_dt_to_map(struct pinctrl *p)
 static inline void pinctrl_dt_free_maps(struct pinctrl *p)
 {
 }
-
 #endif
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 6a29965..98d77b4 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -141,6 +141,28 @@ extern void pinctrl_remove_gpio_ranges(struct pinctrl_dev *pctldev,
 				unsigned nranges);
 extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev);
 extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev);
+
+#ifdef CONFIG_OF
+extern int pinctrl_dt_add_gpio_ranges(struct pinctrl_dev *pctldev,
+				struct device_node *np,
+				const char *propname);
+extern void pinctrl_dt_remove_gpio_ranges(struct pinctrl_dev *pctldev,
+				   struct pinctrl_gpio_range *ranges,
+				   unsigned nranges);
+#else
+static inline int pinctrl_dt_add_gpio_ranges(struct pinctrl_dev *pctldev,
+				struct device_node *np,
+				const char *propname)
+{
+	return 0;
+}
+static void pinctrl_dt_remove_gpio_ranges(struct pinctrl_dev *pctldev,
+				   struct pinctrl_gpio_range *ranges,
+				   unsigned nranges)
+{
+}
+#endif	/* !CONFIG_OF */
+
 #else
 
 struct pinctrl_dev;
-- 
1.7.0.4





More information about the linux-arm-kernel mailing list