Re: Right amount of info in the DT

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 04/24/2017 06:45 PM, Linus Walleij wrote:
> Isn't the best approach to add a pin controller as a subnode of the ethernet
> controller in the device tree, so that the probe() of you main driver looks
> for it and instantiates a pin controller that is then referenced by the
> driver itself?

I guess that could do it, but I'm not confident enough in my kernel abilities
to modify such a big driver just yet (drivers/net/ethernet/aurora).
I've decided not to support ethernet pins, at least for the first iteration
of the driver.

I've put together a first draft, would you mind taking a look at it?
I haven't documented the DT binding yet, though.

Regards,
Yves.



From: Yves Lefloch <YvesMarie_Lefloch@xxxxxxxxxxxxxxxx>
Date: Tue, 25 Apr 2017 14:06:46 +0200
Subject: [PATCH] drivers: pinctrl: Add a pinctrl for mach-tango

Signed-off-by: Yves Lefloch <YvesMarie_Lefloch@xxxxxxxxxxxxxxxx>
---
 arch/arm/boot/dts/tango4-common.dtsi |  98 ++++++++
 arch/arm/configs/tango4_defconfig    |   2 +
 arch/arm/mach-tango/Kconfig          |   1 +
 drivers/pinctrl/Kconfig              |   7 +
 drivers/pinctrl/Makefile             |   1 +
 drivers/pinctrl/pinctrl-tango.c      | 421 +++++++++++++++++++++++++++++++++++
 6 files changed, 530 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-tango.c

diff --git a/arch/arm/boot/dts/tango4-common.dtsi b/arch/arm/boot/dts/tango4-common.dtsi
index a3e81ab..5e89af9 100644
--- a/arch/arm/boot/dts/tango4-common.dtsi
+++ b/arch/arm/boot/dts/tango4-common.dtsi
@@ -18,6 +18,104 @@
 	#address-cells = <1>;
 	#size-cells = <1>;

+	pins {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		pinctrl@10500 {
+			reg = <0x10500 0x8>;
+			compatible = "sigma,smp8758-pinctrl";
+			label = "sys";
+			tango,pins = <0 16>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			gpio {
+				gpio-controller;
+				#gpio-cells = <1>;
+			};
+		};
+
+		pinctrl@15b8e0 {
+			compatible = "sigma,smp8758-pinctrl";
+			label = "tdmux";
+			reg = <0x15b8e0 0xc>;
+			tango,pins = <16 2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			gpio {
+				gpio-controller;
+				#gpio-cells = <1>;
+			};
+
+			alt_function {
+				function = "tdmux";
+				groups = "tdmux";
+			};
+		};
+
+		pinctrl@6c130 {
+			compatible = "sigma,smp8758-pinctrl";
+			label = "spi";
+			reg = <0x6c130 0xc>;
+			tango,pins = <18 8>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			gpio {
+				gpio-controller;
+				#gpio-cells = <1>;
+			};
+
+			alt_function {
+				function = "spi";
+				groups = "spi";
+			};
+		};
+
+		pinctrl@6c230 {
+			compatible = "sigma,smp8758-pinctrl";
+			label = "uart1";
+			reg = <0x6c230 0xc>;
+			tango,pins = <26 7>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			gpio {
+				gpio-controller;
+				#gpio-cells = <1>;
+			};
+
+			alt_function {
+				function = "uart1";
+				groups = "uart1";
+			};
+
+		};
+
+		pinctrl@6c358 {
+			compatible = "sigma,smp8758-pinctrl";
+			label = "smcard";
+			reg = <0x6c368 0xc>;
+			tango,pins = <71 8>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			gpio {
+				gpio-controller;
+				#gpio-cells = <1>;
+			};
+
+			alt_function {
+				function = "smcard";
+				groups = "smcard";
+			};
+		};
+	};
+
 	periph_clk: periph_clk {
 		compatible = "fixed-factor-clock";
 		clocks = <&clkgen CPU_CLK>;
diff --git a/arch/arm/configs/tango4_defconfig b/arch/arm/configs/tango4_defconfig
index b26bb4e..c973758 100644
--- a/arch/arm/configs/tango4_defconfig
+++ b/arch/arm/configs/tango4_defconfig
@@ -68,7 +68,9 @@ CONFIG_SERIAL_OF_PLATFORM=y
 # CONFIG_HW_RANDOM is not set
 CONFIG_I2C=y
 CONFIG_I2C_XLR=y
+CONFIG_PINCTRL_TANGO=m
 CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
 CONFIG_THERMAL=y
 CONFIG_CPU_THERMAL=y
 CONFIG_TANGO_THERMAL=y
diff --git a/arch/arm/mach-tango/Kconfig b/arch/arm/mach-tango/Kconfig
index ebe15b9..479c9aa 100644
--- a/arch/arm/mach-tango/Kconfig
+++ b/arch/arm/mach-tango/Kconfig
@@ -11,3 +11,4 @@ config ARCH_TANGO
 	select HAVE_ARM_SCU
 	select HAVE_ARM_TWD
 	select TANGO_IRQ
+	select PINCTRL
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 0e75d94..5648e76 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -179,6 +179,13 @@ config PINCTRL_ST
 	select PINCONF
 	select GPIOLIB_IRQCHIP

+config PINCTRL_TANGO
+	tristate "Tango pin control driver"
+	depends on OF
+	select PINMUX
+	help
+	  Say yes to activate the pinctrl/gpio driver for Tango chips.
+
 config PINCTRL_TZ1090
 	bool "Toumaz Xenif TZ1090 pin control driver"
 	depends on SOC_TZ1090
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 11bad37..d5563e49 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_PINCTRL_ROCKCHIP)	+= pinctrl-rockchip.o
 obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_SIRF)	+= sirf/
 obj-$(CONFIG_ARCH_TEGRA)	+= tegra/
+obj-$(CONFIG_PINCTRL_TANGO)	+= pinctrl-tango.o
 obj-$(CONFIG_PINCTRL_TZ1090)	+= pinctrl-tz1090.o
 obj-$(CONFIG_PINCTRL_TZ1090_PDC)	+= pinctrl-tz1090-pdc.o
 obj-$(CONFIG_PINCTRL_U300)	+= pinctrl-u300.o
diff --git a/drivers/pinctrl/pinctrl-tango.c b/drivers/pinctrl/pinctrl-tango.c
new file mode 100644
index 0000000..1112047
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tango.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2017 Sigma Designs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+
+/* Pin regs are laid out this way:
+ * ______________________________
+ * | bitwise mask | bitwise val |
+ * 32             16            0
+ *
+ * To set bit 1, one must write (1 << (16+1) | 1).
+ *
+ * Because of this layout, there are no more than 16 pins in a reg.
+ */
+
+/*
+ * Offsets from `addr' in `struct tango_pinctrl'.
+ * The `mod' register isn't there for pins dedicated to GPIO.
+ */
+#define GPIO_OFF_DIR 0x0
+#define GPIO_OFF_VAL 0x4
+#define GPIO_OFF_MOD 0x8
+
+struct tango_pinctrl {
+	const char *label;
+	u8 id;
+	u8 len;
+	bool dedicated;
+	void __iomem *addr;
+
+	struct pinctrl_desc pd;
+	struct pinctrl_gpio_range gr;
+};
+
+static int pinreg_get_bit(void __iomem *addr, unsigned offset)
+{
+	unsigned long val = readl(addr);
+
+	/* TODO remove me later */
+#if IS_ENABLED(CONFIG_DEBUG)
+	BUG_ON(val & GENMASK(31, 16));
+#endif
+
+	return test_bit(offset, &val);
+}
+
+static void pinreg_set_bit(void __iomem *addr, unsigned offset, int value)
+{
+	unsigned long val = 0;
+
+	/* Enable change by writing upper half-word too. */
+	set_bit(offset + 16, &val);
+	if (value)
+		set_bit(offset, &val);
+
+	writel(val, addr);
+
+	/* TODO remove me later */
+#if IS_ENABLED(CONFIG_DEBUG)
+	BUG_ON(pinreg_get_bit(addr, offset) != value);
+#endif
+}
+
+static void pinreg_set_multiple(void __iomem *addr, u16 mask, u16 bits)
+{
+	unsigned long val = (mask << 16) | bits;
+
+	writel(val, addr);
+}
+
+static int gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+	struct tango_pinctrl *data = gpiochip_get_data(gc);
+
+	if (!data)
+		return -EINVAL;
+
+	return pinreg_get_bit(data->addr + GPIO_OFF_VAL, offset);
+}
+
+static void gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+	struct tango_pinctrl *data = gpiochip_get_data(gc);
+
+	if (!data)
+		return;
+
+	pinreg_set_bit(data->addr + GPIO_OFF_VAL, offset, value);
+}
+
+static int gpio_get_direction(struct gpio_chip *gc, unsigned offset)
+{
+	struct tango_pinctrl *data = gpiochip_get_data(gc);
+
+	if (!data)
+		return -EINVAL;
+
+	/* Bit set means output. */
+	return !pinreg_get_bit(data->addr + GPIO_OFF_DIR, offset);
+}
+
+static int gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+	struct tango_pinctrl *data = gpiochip_get_data(gc);
+
+	if (!data)
+		return -EINVAL;
+
+	/* Bit set means output. */
+	pinreg_set_bit(data->addr + GPIO_OFF_DIR, offset, 0);
+
+	return 0;
+}
+
+static int gpio_direction_output(struct gpio_chip *gc, unsigned offset, int value)
+{
+	struct tango_pinctrl *data = gpiochip_get_data(gc);
+
+	if (!data)
+		return -EINVAL;
+
+	/* The output value can be changed before the direction is. */
+	pinreg_set_bit(data->addr + GPIO_OFF_VAL, offset, value);
+
+	/* Bit set means output. */
+	pinreg_set_bit(data->addr + GPIO_OFF_DIR, offset, 1);
+
+	return 0;
+}
+
+static void gpio_set_multiple(struct gpio_chip *gc,
+		unsigned long *mask, unsigned long *bits)
+{
+	u16 _mask = (u16) *mask;
+	u16 _bits = (u16) *bits;
+	struct tango_pinctrl *data = gpiochip_get_data(gc);
+	if (!data)
+		return;
+
+	pinreg_set_multiple(data->addr + GPIO_OFF_VAL, _mask, _bits);
+}
+
+static const unsigned int all_pins[] = {
+	0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
+	10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+	20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+	30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+	40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+	50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+	60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+	70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+	80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+	90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+};
+
+static int get_group_count(struct pinctrl_dev *pctldev)
+{
+	struct tango_pinctrl *data;
+
+	data = pinctrl_dev_get_drvdata(pctldev);
+	if (!data)
+		return -EINVAL;
+
+	return data->dedicated ? 0 : 1;
+}
+
+static const char *get_group_name(struct pinctrl_dev *pctldev, unsigned selector)
+{
+	struct tango_pinctrl *data;
+
+	data = pinctrl_dev_get_drvdata(pctldev);
+	if (!data)
+		return ERR_PTR(-EINVAL);
+
+	return data->label;
+}
+
+static int get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
+		const unsigned **pins, unsigned *num_pins)
+{
+	struct tango_pinctrl *data;
+
+	data = pinctrl_dev_get_drvdata(pctldev);
+	if (!data)
+		return -EINVAL;
+
+	*pins = all_pins + data->id;
+	*num_pins = data->len;
+
+	return 0;
+}
+
+static const struct pinctrl_ops pctlops = {
+	.get_groups_count = get_group_count,
+	.get_group_name = get_group_name,
+	.get_group_pins = get_group_pins,
+};
+
+int get_function_groups(struct pinctrl_dev *pctldev, unsigned selector,
+		const char * const **groups, unsigned *num_groups)
+{
+	struct tango_pinctrl *data;
+
+	data = pinctrl_dev_get_drvdata(pctldev);
+	if (!data)
+		return -EINVAL;
+
+	*groups = &data->label;
+	*num_groups = 1;
+
+	return 0;
+}
+
+int set_mux(struct pinctrl_dev *pctldev, unsigned func_selector,
+		unsigned group_selector)
+{
+	/* With only one possible alt function, muxing is only
+	 * necessary for GPIO request/free.
+	 */
+	return 0;
+}
+
+int gpio_request_enable(struct pinctrl_dev *pctldev,
+		struct pinctrl_gpio_range *range, unsigned offset)
+{
+	struct tango_pinctrl *data;
+
+	data = pinctrl_dev_get_drvdata(pctldev);
+	if (!data)
+		return -EINVAL;
+
+	if (offset >= data->len)
+		return -EINVAL;
+
+	if (!data->dedicated)
+		pinreg_set_bit(data->addr + GPIO_OFF_MOD, offset, 1);
+
+	return 0;
+}
+
+void gpio_disable_free(struct pinctrl_dev *pctldev,
+		struct pinctrl_gpio_range *range, unsigned offset)
+{
+	struct tango_pinctrl *data;
+
+	data = pinctrl_dev_get_drvdata(pctldev);
+	if (!data)
+		return;
+
+	if (offset >= data->len)
+		return;
+
+	if (!data->dedicated)
+		pinreg_set_bit(data->addr + GPIO_OFF_MOD, offset, 0);
+}
+
+const static struct pinmux_ops pmxops = {
+	.get_functions_count = get_group_count,
+	.get_function_name = get_group_name,
+	.get_function_groups = get_function_groups,
+	.set_mux = set_mux,
+	.gpio_request_enable = gpio_request_enable,
+	.gpio_disable_free = gpio_disable_free,
+	.strict = true,
+};
+
+static int get_node_info(struct device_node *np, struct tango_pinctrl *data)
+{
+	/* Extract info from the pinctrl node:
+	 * - the human-friendly name;
+	 * - the base num and the num of pins;
+	 * - whether pins are dedicated to GPIO (no alt state).
+	 */
+	int ret;
+	u32 tango_pins[2];
+
+	ret = of_property_read_string(np, "label", &data->label);
+	if (ret)
+		return ret;
+	ret = of_property_read_u32_array(np, "tango,pins", tango_pins, 2);
+	if (ret)
+		return ret;
+	data->id = tango_pins[0];
+	data->len = tango_pins[1];
+	data->dedicated = !of_get_property(np, "alt_function", NULL);
+
+	return 0;
+}
+
+static int init_pd(struct device *dev, struct tango_pinctrl *data)
+{
+	int i;
+	struct pinctrl_pin_desc *pdesc;
+
+	pdesc = devm_kzalloc(dev, data->len * sizeof(*pdesc), GFP_KERNEL);
+	if (!pdesc)
+		return -ENOMEM;
+	for (i = 0; i < data->len; i++)
+		pdesc[i].number = data->id + i;
+
+	data->pd.name = data->label;
+	data->pd.pins = pdesc;
+	data->pd.npins = data->len;
+	data->pd.pctlops = &pctlops;
+	data->pd.pmxops = &pmxops;
+	data->pd.owner = THIS_MODULE;
+
+	return 0;
+}
+
+static int init_gr(struct device *dev, struct tango_pinctrl *data)
+{
+	struct gpio_chip *gc;
+
+	gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
+	if (!gc)
+		return -ENOMEM;
+
+	data->gr.name = data->label;
+	data->gr.id = data->id;
+	data->gr.base = data->id;
+	data->gr.pin_base = data->id;
+	data->gr.npins = data->len;
+	data->gr.gc = gc;
+
+	gc->label = data->label;
+	gc->parent = dev;
+	gc->owner = THIS_MODULE;
+	gc->get_direction = gpio_get_direction;
+	gc->direction_input = gpio_direction_input;
+	gc->direction_output = gpio_direction_output;
+	gc->get = gpio_get;
+	gc->set = gpio_set;
+	gc->set_multiple = gpio_set_multiple;
+	gc->base = data->id;
+	gc->ngpio = data->len;
+
+	return devm_gpiochip_add_data(dev, gc, data);
+}
+
+static int tango_pinctrl_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct resource *res;
+	struct tango_pinctrl *data;
+	struct pinctrl_dev *pindev;
+	struct device_node *np = pdev->dev.of_node;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, data);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+	data->addr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(data->addr))
+		return PTR_ERR(data->addr);
+
+	ret = get_node_info(np, data);
+	if (ret)
+		return ret;
+	dev_dbg(&pdev->dev, "%8s id=%d len=%d\n",
+			data->label, data->id, data->len);
+
+	ret = init_pd(&pdev->dev, data);
+	if (ret)
+		return ret;
+
+	pindev = devm_pinctrl_register(&pdev->dev, &data->pd, data);
+	if (IS_ERR(pindev))
+		return PTR_ERR(pindev);
+
+	ret = init_gr(&pdev->dev, data);
+	if (ret)
+		return ret;
+	pinctrl_add_gpio_range(pindev, &data->gr);
+
+	return 0;
+}
+
+static const struct of_device_id tango_pinctrl_of_match[] = {
+	{ .compatible =  "sigma,smp8758-pinctrl", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, tango_pinctrl_of_match);
+
+static struct platform_driver tango_pinctrl_driver = {
+	.driver = {
+		.name = "pinctrl-tango",
+		.of_match_table = of_match_ptr(tango_pinctrl_of_match),
+	},
+	.probe = tango_pinctrl_probe,
+};
+
+static int __init tango_pinctrl_init(void)
+{
+	return platform_driver_register(&tango_pinctrl_driver);
+}
+module_init(tango_pinctrl_init);
+
+static void __exit tango_pinctrl_exit(void)
+{
+	platform_driver_unregister(&tango_pinctrl_driver);
+}
+module_exit(tango_pinctrl_exit);
+
+MODULE_AUTHOR("Sigma Designs");
+MODULE_DESCRIPTION("Tango pinctrl driver");
+MODULE_LICENSE("GPL");
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux