[PATCH 4/5] pinctrl: dw: add pinctrl support for dwapb gpio driver

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

 



Synopsys designware gpio controller also has pinmuxing functionnality.
Pinmuxing allows to choose between software and hardware mode. When
using hardware mode, an external signal controls the pin output.

This patch adds support for pinctrl framework in the gpio driver. This
support is conditionned by the snps,has-pinctrl device tree property.
Indeed, the functionnality can be detected only if the gpio IP has been
configured using paremeters encoding which is not always present. If
property is present, then the pinctrl will be registered and will
allow switching to the "hw" functionnality and hence enable the
alternate function.

Signed-off-by: Clement Leger <cleger@xxxxxxxxx>
---
 drivers/pinctrl/dw/Kconfig               |   2 +
 drivers/pinctrl/dw/pinctrl-dwapb.c       | 263 ++++++++++++++++++++++++++++++-
 include/linux/platform_data/gpio-dwapb.h |   1 +
 3 files changed, 260 insertions(+), 6 deletions(-)

diff --git a/drivers/pinctrl/dw/Kconfig b/drivers/pinctrl/dw/Kconfig
index 8b57c27eab1f..2bcc51813d80 100644
--- a/drivers/pinctrl/dw/Kconfig
+++ b/drivers/pinctrl/dw/Kconfig
@@ -4,6 +4,8 @@ config GPIO_DWAPB
 	select GPIOLIB
 	select GPIOLIB_IRQCHIP
 	select GPIO_GENERIC
+	select PINMUX
+	select GENERIC_PINCONF
 	help
 	  Say Y or M here to build support for the Synopsys DesignWare APB
 	  GPIO block.
diff --git a/drivers/pinctrl/dw/pinctrl-dwapb.c b/drivers/pinctrl/dw/pinctrl-dwapb.c
index 3750205408e3..063bc36319d7 100644
--- a/drivers/pinctrl/dw/pinctrl-dwapb.c
+++ b/drivers/pinctrl/dw/pinctrl-dwapb.c
@@ -25,15 +25,22 @@
 #include <linux/spinlock.h>
 #include <linux/platform_data/gpio-dwapb.h>
 #include <linux/slab.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include "../pinctrl-utils.h"
 
 #define GPIO_SWPORTA_DR		0x00
 #define GPIO_SWPORTA_DDR	0x04
+#define GPIO_SWPORTA_CTL	0x08
 #define GPIO_SWPORTB_DR		0x0c
 #define GPIO_SWPORTB_DDR	0x10
+#define GPIO_SWPORTB_CTL	0x14
 #define GPIO_SWPORTC_DR		0x18
 #define GPIO_SWPORTC_DDR	0x1c
+#define GPIO_SWPORTC_CTL	0x20
 #define GPIO_SWPORTD_DR		0x24
 #define GPIO_SWPORTD_DDR	0x28
+#define GPIO_SWPORTD_CTL	0x2c
 #define GPIO_INTEN		0x30
 #define GPIO_INTMASK		0x34
 #define GPIO_INTTYPE_LEVEL	0x38
@@ -45,11 +52,16 @@
 #define GPIO_EXT_PORTB		0x54
 #define GPIO_EXT_PORTC		0x58
 #define GPIO_EXT_PORTD		0x5c
+#define GPIO_CONFIG_REG1	0x74
+
+#define GPIO_CONFIG_REG1_HW_BIT		8
+#define GPIO_CONFIG_REG1_SINGLE_CTL_BIT	4
 
 #define DWAPB_MAX_PORTS		4
 #define GPIO_EXT_PORT_STRIDE	0x04 /* register stride 32 bits */
 #define GPIO_SWPORT_DR_STRIDE	0x0c /* register stride 3*32 bits */
 #define GPIO_SWPORT_DDR_STRIDE	0x0c /* register stride 3*32 bits */
+#define GPIO_SWPORT_CTL_STRIDE	0x0c /* register stride 3*32 bits */
 
 #define GPIO_REG_OFFSET_V2	1
 
@@ -77,13 +89,16 @@ struct dwapb_context {
 #endif
 
 struct dwapb_gpio_port {
-	struct gpio_chip	gc;
-	bool			is_registered;
-	struct dwapb_gpio	*gpio;
+	struct gpio_chip		gc;
+	bool				is_registered;
+	struct dwapb_gpio		*gpio;
 #ifdef CONFIG_PM_SLEEP
-	struct dwapb_context	*ctx;
+	struct dwapb_context		*ctx;
 #endif
-	unsigned int		idx;
+	unsigned int			idx;
+	struct pinctrl_dev		*pctl;
+	struct pinctrl_desc		pctldesc;
+	struct pinctrl_gpio_range	range;
 };
 
 struct dwapb_gpio {
@@ -97,6 +112,89 @@ struct dwapb_gpio {
 	struct clk		*clk;
 };
 
+enum dwapb_gpio_func {
+	DWAPB_GPIO_SW_FUNC = 0,
+	DWAPB_GPIO_HW_FUNC = 1,
+};
+
+#define DWAPB_PINCTRL_PIN(pin) PINCTRL_PIN(pin, "pin" #pin)
+
+static const struct pinctrl_pin_desc dwapb_pins[] = {
+	DWAPB_PINCTRL_PIN(0),
+	DWAPB_PINCTRL_PIN(1),
+	DWAPB_PINCTRL_PIN(2),
+	DWAPB_PINCTRL_PIN(3),
+	DWAPB_PINCTRL_PIN(4),
+	DWAPB_PINCTRL_PIN(5),
+	DWAPB_PINCTRL_PIN(6),
+	DWAPB_PINCTRL_PIN(7),
+	DWAPB_PINCTRL_PIN(8),
+	DWAPB_PINCTRL_PIN(9),
+	DWAPB_PINCTRL_PIN(10),
+	DWAPB_PINCTRL_PIN(11),
+	DWAPB_PINCTRL_PIN(12),
+	DWAPB_PINCTRL_PIN(13),
+	DWAPB_PINCTRL_PIN(14),
+	DWAPB_PINCTRL_PIN(15),
+	DWAPB_PINCTRL_PIN(16),
+	DWAPB_PINCTRL_PIN(17),
+	DWAPB_PINCTRL_PIN(18),
+	DWAPB_PINCTRL_PIN(19),
+	DWAPB_PINCTRL_PIN(20),
+	DWAPB_PINCTRL_PIN(21),
+	DWAPB_PINCTRL_PIN(22),
+	DWAPB_PINCTRL_PIN(23),
+	DWAPB_PINCTRL_PIN(24),
+	DWAPB_PINCTRL_PIN(25),
+	DWAPB_PINCTRL_PIN(26),
+	DWAPB_PINCTRL_PIN(27),
+	DWAPB_PINCTRL_PIN(28),
+	DWAPB_PINCTRL_PIN(29),
+	DWAPB_PINCTRL_PIN(30),
+	DWAPB_PINCTRL_PIN(31)
+};
+
+/* One pin per group */
+static const char * const dwapb_gpio_groups[] = {
+	"pin0",
+	"pin1",
+	"pin2",
+	"pin3",
+	"pin4",
+	"pin5",
+	"pin6",
+	"pin7",
+	"pin8",
+	"pin9",
+	"pin10",
+	"pin11",
+	"pin12",
+	"pin13",
+	"pin14",
+	"pin15",
+	"pin16",
+	"pin17",
+	"pin18",
+	"pin19",
+	"pin20",
+	"pin21",
+	"pin22",
+	"pin23",
+	"pin24",
+	"pin25",
+	"pin26",
+	"pin27",
+	"pin28",
+	"pin29",
+	"pin30",
+	"pin31"
+};
+
+static const char *const dwapb_gpio_functions[] = {
+	[DWAPB_GPIO_SW_FUNC] = "sw",
+	[DWAPB_GPIO_HW_FUNC] = "hw",
+};
+
 static inline u32 gpio_reg_v2_convert(unsigned int offset)
 {
 	switch (offset) {
@@ -484,6 +582,151 @@ static void dwapb_irq_teardown(struct dwapb_gpio *gpio)
 	gpio->domain = NULL;
 }
 
+static int dwapb_pctl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	struct dwapb_gpio_port *port = pinctrl_dev_get_drvdata(pctldev);
+
+	return port->gc.ngpio;
+}
+
+static const char *dwapb_pctl_get_group_name(struct pinctrl_dev *pctldev,
+		unsigned selector)
+{
+	struct dwapb_gpio_port *port = pinctrl_dev_get_drvdata(pctldev);
+
+	/* Exactly one group per pin */
+	return port->pctldesc.pins[selector].name;
+}
+
+static int dwapb_pctl_get_group_pins(struct pinctrl_dev *pctldev,
+		unsigned selector,
+		const unsigned **pins,
+		unsigned *num_pins)
+{
+	struct dwapb_gpio_port *port = pinctrl_dev_get_drvdata(pctldev);
+
+	*pins = &port->pctldesc.pins[selector].number;
+	*num_pins = 1;
+
+	return 0;
+}
+
+static const struct pinctrl_ops dwapb_pctl_ops = {
+	.get_groups_count = dwapb_pctl_get_groups_count,
+	.get_group_name = dwapb_pctl_get_group_name,
+	.get_group_pins = dwapb_pctl_get_group_pins,
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+	.dt_free_map = pinctrl_utils_free_map,
+};
+
+static int dwapb_gpio_get_function_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(dwapb_gpio_functions);
+}
+
+static const char *dwapb_gpio_get_function_name(struct pinctrl_dev *pctldev,
+						unsigned selector)
+{
+	return dwapb_gpio_functions[selector];
+}
+
+static int dwapb_gpio_get_function_groups(struct pinctrl_dev *pctldev,
+					  unsigned selector,
+					  const char * const **groups,
+					  unsigned * const num_groups)
+{
+	struct dwapb_gpio_port *port = pinctrl_dev_get_drvdata(pctldev);
+
+	*groups = dwapb_gpio_groups;
+	*num_groups = port->gc.ngpio;
+
+	return 0;
+}
+
+static int dwapb_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned selector,
+		unsigned group)
+{
+	struct dwapb_gpio_port *port = pinctrl_dev_get_drvdata(pctldev);
+	struct dwapb_gpio *gpio = port->gpio;
+	struct gpio_chip *gc = &port->gc;
+	unsigned int reg_off;
+	unsigned long flags;
+	u32 val;
+
+	dev_info(gpio->dev, "Setting func %s on pin %d",
+		dwapb_gpio_functions[selector], group);
+
+	reg_off = GPIO_SWPORTA_CTL + (port->idx * GPIO_SWPORT_CTL_STRIDE);
+	spin_lock_irqsave(&gc->bgpio_lock, flags);
+	val = dwapb_read(gpio, reg_off);
+	if (selector == 0)
+		val &= ~BIT(group);
+	else
+		val |= BIT(group);
+	dwapb_write(gpio, reg_off, val);
+	spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+
+	return 0;
+}
+
+static int dwapb_gpio_pmx_gpio_request(struct pinctrl_dev *pctldev,
+				    struct pinctrl_gpio_range *range,
+				    unsigned int pin)
+{
+	return dwapb_gpio_set_mux(pctldev, DWAPB_GPIO_SW_FUNC, pin);
+}
+
+struct pinmux_ops dwapb_pmux_ops = {
+	.get_functions_count = dwapb_gpio_get_function_count,
+	.get_function_name = dwapb_gpio_get_function_name,
+	.get_function_groups = dwapb_gpio_get_function_groups,
+	.set_mux = dwapb_gpio_set_mux,
+	.gpio_request_enable = dwapb_gpio_pmx_gpio_request,
+};
+
+static int dwapb_gpio_add_pinctrl(struct dwapb_gpio *gpio,
+				  struct dwapb_port_property *pp,
+				  struct dwapb_gpio_port *port)
+{
+	struct pinctrl_desc *desc;
+	struct pinctrl_gpio_range *range;
+	int ret;
+
+	desc = &port->pctldesc;
+
+	desc->pins = dwapb_pins;
+	desc->npins = pp->ngpio;
+	desc->name = dev_name(gpio->dev);
+	desc->owner = THIS_MODULE;
+	desc->pctlops = &dwapb_pctl_ops;
+	desc->pmxops = &dwapb_pmux_ops;
+
+	ret = devm_pinctrl_register_and_init(gpio->dev, &port->pctldesc, port,
+					     &port->pctl);
+	if (ret) {
+		dev_err(gpio->dev, "Failed to register pinctrl device\n");
+		return ret;
+	}
+
+	ret = pinctrl_enable(port->pctl);
+	if (ret) {
+		dev_err(gpio->dev, "pinctrl enable failed\n");
+		return ret;
+	}
+
+	range = &port->range;
+	range->name = dev_name(gpio->dev);
+	range->id = port->idx;
+	range->pin_base = 0;
+	range->base = port->gc.base;
+	range->npins = pp->ngpio;
+	range->gc = &port->gc;
+
+	pinctrl_add_gpio_range(port->pctl, range);
+
+	return 0;
+}
+
 static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
 			       struct dwapb_port_property *pp,
 			       unsigned int offs)
@@ -536,6 +779,12 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
 	else
 		port->is_registered = true;
 
+	if (!err && pp->has_pinctrl) {
+		err = dwapb_gpio_add_pinctrl(gpio, pp, port);
+		if (err)
+			return err;
+	}
+
 	/* Add GPIO-signaled ACPI event support */
 	if (pp->has_irq)
 		acpi_gpiochip_request_interrupts(&port->gc);
@@ -589,6 +838,8 @@ dwapb_gpio_get_pdata(struct device *dev)
 			pp->ngpio = 32;
 		}
 
+		pp->has_pinctrl = fwnode_property_read_bool(fwnode,
+						"snps,has-pinctrl");
 		pp->irq_shared	= false;
 		pp->gpio_base	= -1;
 
@@ -822,7 +1073,7 @@ static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend,
 
 static struct platform_driver dwapb_gpio_driver = {
 	.driver		= {
-		.name	= "gpio-dwapb",
+		.name	= "pinctrl-dwapb",
 		.pm	= &dwapb_gpio_pm_ops,
 		.of_match_table = of_match_ptr(dwapb_of_match),
 		.acpi_match_table = ACPI_PTR(dwapb_acpi_match),
diff --git a/include/linux/platform_data/gpio-dwapb.h b/include/linux/platform_data/gpio-dwapb.h
index 3c606c450d05..2633bb745317 100644
--- a/include/linux/platform_data/gpio-dwapb.h
+++ b/include/linux/platform_data/gpio-dwapb.h
@@ -14,6 +14,7 @@ struct dwapb_port_property {
 	int		irq[32];
 	bool		has_irq;
 	bool		irq_shared;
+	bool		has_pinctrl;
 };
 
 struct dwapb_platform_data {
-- 
2.15.0.276.g89ea799




[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