From f8b410e3695a86686f4075b997bc53c8a178e4e2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 27 Sep 2019 13:48:33 +0200 Subject: gpio: aspeed-sgpio: Rename and add Kconfig/Makefile This renames the "gpio-aspeed" driver to conform with other GPIO drivers as "gpio-aspeed-sgpio.c". All GPIO drivers should start with the string "gpio-" no special exceptions. Also the Kconfig and Makefile entries should normally go with the driver but I missed this in my review, sorry for mistake. "CONFIG_GPIO_ASPEED_SGPIO" is used to activate this driver. Cc: Hongwei Zhang Fixes: 7db47faae79b ("gpio: aspeed: Add SGPIO driver") Signed-off-by: Linus Walleij Acked-by: Andrew Jeffery Link: https://lore.kernel.org/r/20190927114833.12551-1-linus.walleij@linaro.org --- drivers/gpio/Kconfig | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/gpio/Kconfig') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 38e096e6925f..7138290cdd36 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -120,6 +120,14 @@ config GPIO_ASPEED help Say Y here to support Aspeed AST2400 and AST2500 GPIO controllers. +config GPIO_ASPEED_SGPIO + bool "Aspeed SGPIO support" + depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Say Y here to support Aspeed AST2500 SGPIO functionality. + config GPIO_ATH79 tristate "Atheros AR71XX/AR724X/AR913X GPIO support" default y if ATH79 -- cgit v1.2.3 From 2a36550567307b881ce570a81189682ae1c9d08d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 2 Oct 2019 16:45:02 +0200 Subject: gpio: tegra186: Implement wake event support The GPIO controller doesn't have any controls to enable the system to wake up from low power states based on activity on GPIO pins. An extra hardware block that is part of the power management controller (PMC) contains these controls. In order for the GPIO controller to be able to cooperate with the PMC, obtain a reference to the PMC's IRQ domain and make it a parent to the GPIO controller's IRQ domain. This way the PMC gets an opportunity to program the additional registers required to enable wakeup sources on suspend. Based on additional work by Bitan Biswas . Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20191002144502.156393-2-thierry.reding@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-tegra186.c | 97 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 80 insertions(+), 18 deletions(-) (limited to 'drivers/gpio/Kconfig') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7138290cdd36..7365cf150ae5 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -539,6 +539,7 @@ config GPIO_TEGRA186 depends on ARCH_TEGRA_186_SOC || COMPILE_TEST depends on OF_GPIO select GPIOLIB_IRQCHIP + select IRQ_DOMAIN_HIERARCHY help Say yes here to support GPIO pins on NVIDIA Tegra186 SoCs. diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index a9058fda187e..8a2a69178925 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -53,6 +53,7 @@ struct tegra_gpio_soc { const struct tegra_gpio_port *ports; unsigned int num_ports; const char *name; + unsigned int instance; }; struct tegra_gpio { @@ -327,7 +328,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) else irq_set_handler_locked(data, handle_edge_irq); - return 0; + return irq_chip_set_type_parent(data, type); } static void tegra186_gpio_irq(struct irq_desc *desc) @@ -367,39 +368,80 @@ skip: chained_irq_exit(chip, desc); } -static int tegra186_gpio_irq_domain_xlate(struct irq_domain *domain, - struct device_node *np, - const u32 *spec, unsigned int size, - unsigned long *hwirq, - unsigned int *type) +static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) { struct tegra_gpio *gpio = gpiochip_get_data(domain->host_data); unsigned int port, pin, i, offset = 0; - if (size < 2) + if (WARN_ON(gpio->gpio.of_gpio_n_cells < 2)) + return -EINVAL; + + if (WARN_ON(fwspec->param_count < gpio->gpio.of_gpio_n_cells)) return -EINVAL; - port = spec[0] / 8; - pin = spec[0] % 8; + port = fwspec->param[0] / 8; + pin = fwspec->param[0] % 8; - if (port >= gpio->soc->num_ports) { - dev_err(gpio->gpio.parent, "invalid port number: %u\n", port); + if (port >= gpio->soc->num_ports) return -EINVAL; - } for (i = 0; i < port; i++) offset += gpio->soc->ports[i].pins; - *type = spec[1] & IRQ_TYPE_SENSE_MASK; + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; *hwirq = offset + pin; return 0; } -static const struct irq_domain_ops tegra186_gpio_irq_domain_ops = { - .map = gpiochip_irq_map, - .unmap = gpiochip_irq_unmap, - .xlate = tegra186_gpio_irq_domain_xlate, +static void tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip, + struct irq_fwspec *fwspec, + unsigned int parent_hwirq, + unsigned int parent_type) +{ + struct tegra_gpio *gpio = gpiochip_get_data(chip); + + fwspec->param_count = 3; + fwspec->param[0] = gpio->soc->instance; + fwspec->param[1] = parent_hwirq; + fwspec->param[2] = parent_type; +} + +static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip, + unsigned int hwirq, + unsigned int type, + unsigned int *parent_hwirq, + unsigned int *parent_type) +{ + *parent_hwirq = chip->irq.child_offset_to_irq(chip, hwirq); + *parent_type = type; + + return 0; +} + +static unsigned int tegra186_gpio_child_offset_to_irq(struct gpio_chip *chip, + unsigned int offset) +{ + struct tegra_gpio *gpio = gpiochip_get_data(chip); + unsigned int i; + + for (i = 0; i < gpio->soc->num_ports; i++) { + if (offset < gpio->soc->ports[i].pins) + break; + + offset -= gpio->soc->ports[i].pins; + } + + return offset + i * 8; +} + +static const struct of_device_id tegra186_pmc_of_match[] = { + { .compatible = "nvidia,tegra186-pmc" }, + { .compatible = "nvidia,tegra194-pmc" }, + { /* sentinel */ } }; static int tegra186_gpio_probe(struct platform_device *pdev) @@ -407,6 +449,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) unsigned int i, j, offset; struct gpio_irq_chip *irq; struct tegra_gpio *gpio; + struct device_node *np; struct resource *res; char **names; int err; @@ -487,10 +530,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->intc.irq_mask = tegra186_irq_mask; gpio->intc.irq_unmask = tegra186_irq_unmask; gpio->intc.irq_set_type = tegra186_irq_set_type; + gpio->intc.irq_set_wake = irq_chip_set_wake_parent; irq = &gpio->gpio.irq; irq->chip = &gpio->intc; - irq->domain_ops = &tegra186_gpio_irq_domain_ops; + irq->fwnode = of_node_to_fwnode(pdev->dev.of_node); + irq->child_to_parent_hwirq = tegra186_gpio_child_to_parent_hwirq; + irq->populate_parent_fwspec = tegra186_gpio_populate_parent_fwspec; + irq->child_offset_to_irq = tegra186_gpio_child_offset_to_irq; + irq->child_irq_domain_ops.translate = tegra186_gpio_irq_domain_translate; irq->handler = handle_simple_irq; irq->default_type = IRQ_TYPE_NONE; irq->parent_handler = tegra186_gpio_irq; @@ -498,6 +546,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->num_parents = gpio->num_irq; irq->parents = gpio->irq; + np = of_find_matching_node(NULL, tegra186_pmc_of_match); + if (np) { + irq->parent_domain = irq_find_host(np); + of_node_put(np); + + if (!irq->parent_domain) + return -EPROBE_DEFER; + } + irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio, sizeof(*irq->map), GFP_KERNEL); if (!irq->map) @@ -564,6 +621,7 @@ static const struct tegra_gpio_soc tegra186_main_soc = { .num_ports = ARRAY_SIZE(tegra186_main_ports), .ports = tegra186_main_ports, .name = "tegra186-gpio", + .instance = 0, }; #define TEGRA186_AON_GPIO_PORT(port, base, count, controller) \ @@ -589,6 +647,7 @@ static const struct tegra_gpio_soc tegra186_aon_soc = { .num_ports = ARRAY_SIZE(tegra186_aon_ports), .ports = tegra186_aon_ports, .name = "tegra186-gpio-aon", + .instance = 1, }; #define TEGRA194_MAIN_GPIO_PORT(port, base, count, controller) \ @@ -634,6 +693,7 @@ static const struct tegra_gpio_soc tegra194_main_soc = { .num_ports = ARRAY_SIZE(tegra194_main_ports), .ports = tegra194_main_ports, .name = "tegra194-gpio", + .instance = 0, }; #define TEGRA194_AON_GPIO_PORT(port, base, count, controller) \ @@ -656,6 +716,7 @@ static const struct tegra_gpio_soc tegra194_aon_soc = { .num_ports = ARRAY_SIZE(tegra194_aon_ports), .ports = tegra194_aon_ports, .name = "tegra194-gpio-aon", + .instance = 1, }; static const struct of_device_id tegra186_gpio_of_match[] = { -- cgit v1.2.3 From 5f07224e0fae2a7d475eb21a79ee5a08251d9c44 Mon Sep 17 00:00:00 2001 From: Jonathan Neuschäfer Date: Fri, 4 Oct 2019 18:40:55 +0200 Subject: docs: driver-api: Move bt8xxgpio to the gpio directory Let's declutter Documentation/driver-api a bit. Signed-off-by: Jonathan Neuschäfer Link: https://lore.kernel.org/r/20191004164059.10397-1-j.neuschaefer@gmx.net Signed-off-by: Linus Walleij --- Documentation/driver-api/bt8xxgpio.rst | 62 ----------------------------- Documentation/driver-api/gpio/bt8xxgpio.rst | 62 +++++++++++++++++++++++++++++ Documentation/driver-api/gpio/index.rst | 1 + Documentation/driver-api/index.rst | 1 - drivers/gpio/Kconfig | 2 +- 5 files changed, 64 insertions(+), 64 deletions(-) delete mode 100644 Documentation/driver-api/bt8xxgpio.rst create mode 100644 Documentation/driver-api/gpio/bt8xxgpio.rst (limited to 'drivers/gpio/Kconfig') diff --git a/Documentation/driver-api/bt8xxgpio.rst b/Documentation/driver-api/bt8xxgpio.rst deleted file mode 100644 index a845feb074de..000000000000 --- a/Documentation/driver-api/bt8xxgpio.rst +++ /dev/null @@ -1,62 +0,0 @@ -=================================================================== -A driver for a selfmade cheap BT8xx based PCI GPIO-card (bt8xxgpio) -=================================================================== - -For advanced documentation, see http://www.bu3sch.de/btgpio.php - -A generic digital 24-port PCI GPIO card can be built out of an ordinary -Brooktree bt848, bt849, bt878 or bt879 based analog TV tuner card. The -Brooktree chip is used in old analog Hauppauge WinTV PCI cards. You can easily -find them used for low prices on the net. - -The bt8xx chip does have 24 digital GPIO ports. -These ports are accessible via 24 pins on the SMD chip package. - - -How to physically access the GPIO pins -====================================== - -The are several ways to access these pins. One might unsolder the whole chip -and put it on a custom PCI board, or one might only unsolder each individual -GPIO pin and solder that to some tiny wire. As the chip package really is tiny -there are some advanced soldering skills needed in any case. - -The physical pinouts are drawn in the following ASCII art. -The GPIO pins are marked with G00-G23:: - - G G G G G G G G G G G G G G G G G G - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 - | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - --------------------------------------------------------------------------- - --| ^ ^ |-- - --| pin 86 pin 67 |-- - --| |-- - --| pin 61 > |-- G18 - --| |-- G19 - --| |-- G20 - --| |-- G21 - --| |-- G22 - --| pin 56 > |-- G23 - --| |-- - --| Brooktree 878/879 |-- - --| |-- - --| |-- - --| |-- - --| |-- - --| |-- - --| |-- - --| |-- - --| |-- - --| |-- - --| |-- - --| |-- - --| |-- - --| |-- - --| O |-- - --| |-- - --------------------------------------------------------------------------- - | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - ^ - This is pin 1 - diff --git a/Documentation/driver-api/gpio/bt8xxgpio.rst b/Documentation/driver-api/gpio/bt8xxgpio.rst new file mode 100644 index 000000000000..a845feb074de --- /dev/null +++ b/Documentation/driver-api/gpio/bt8xxgpio.rst @@ -0,0 +1,62 @@ +=================================================================== +A driver for a selfmade cheap BT8xx based PCI GPIO-card (bt8xxgpio) +=================================================================== + +For advanced documentation, see http://www.bu3sch.de/btgpio.php + +A generic digital 24-port PCI GPIO card can be built out of an ordinary +Brooktree bt848, bt849, bt878 or bt879 based analog TV tuner card. The +Brooktree chip is used in old analog Hauppauge WinTV PCI cards. You can easily +find them used for low prices on the net. + +The bt8xx chip does have 24 digital GPIO ports. +These ports are accessible via 24 pins on the SMD chip package. + + +How to physically access the GPIO pins +====================================== + +The are several ways to access these pins. One might unsolder the whole chip +and put it on a custom PCI board, or one might only unsolder each individual +GPIO pin and solder that to some tiny wire. As the chip package really is tiny +there are some advanced soldering skills needed in any case. + +The physical pinouts are drawn in the following ASCII art. +The GPIO pins are marked with G00-G23:: + + G G G G G G G G G G G G G G G G G G + 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + --------------------------------------------------------------------------- + --| ^ ^ |-- + --| pin 86 pin 67 |-- + --| |-- + --| pin 61 > |-- G18 + --| |-- G19 + --| |-- G20 + --| |-- G21 + --| |-- G22 + --| pin 56 > |-- G23 + --| |-- + --| Brooktree 878/879 |-- + --| |-- + --| |-- + --| |-- + --| |-- + --| |-- + --| |-- + --| |-- + --| |-- + --| |-- + --| |-- + --| |-- + --| |-- + --| |-- + --| O |-- + --| |-- + --------------------------------------------------------------------------- + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + ^ + This is pin 1 + diff --git a/Documentation/driver-api/gpio/index.rst b/Documentation/driver-api/gpio/index.rst index c5b8467f9104..5b61032aa4ea 100644 --- a/Documentation/driver-api/gpio/index.rst +++ b/Documentation/driver-api/gpio/index.rst @@ -13,6 +13,7 @@ Contents: board drivers-on-gpio legacy + bt8xxgpio Core ==== diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index 38e638abe3eb..2b3b6949381e 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -69,7 +69,6 @@ available subsections can be seen below. fpga/index acpi/index backlight/lp855x-driver.rst - bt8xxgpio connector console dcdbas diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7365cf150ae5..088a8a0f8add 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1329,7 +1329,7 @@ config GPIO_BT8XX The card needs to be physically altered for using it as a GPIO card. For more information on how to build a GPIO card from a BT8xx TV card, see the documentation file at - Documentation/driver-api/bt8xxgpio.rst + Documentation/driver-api/gpio/bt8xxgpio.rst If unsure, say N. -- cgit v1.2.3 From d57eb825e0dc6f0b5be78251d69cbf1bdd1db622 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 21 Oct 2019 12:14:12 +0530 Subject: gpio: Add RDA Micro GPIO controller support Add support for GPIO controller from RDA Micro. This GPIO controller is an in house IP, developed by RDA Micro (now Unisoc) for the use in RDA88* series of SoCs. There are multiple GPIO ports present in all SoCs, each capable of addressing 32 GPIOs. But only first 8 pins have the interrupt capability. Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20191021064413.19840-4-manivannan.sadhasivam@linaro.org Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 9 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-rda.c | 294 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 304 insertions(+) create mode 100644 drivers/gpio/gpio-rda.c (limited to 'drivers/gpio/Kconfig') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 088a8a0f8add..8ec1f041c98d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -443,6 +443,15 @@ config GPIO_RCAR help Say yes here to support GPIO on Renesas R-Car SoCs. +config GPIO_RDA + bool "RDA Micro GPIO controller support" + depends on ARCH_RDA || COMPILE_TEST + depends on OF_GPIO + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Say Y here to support RDA Micro GPIO controller. + config GPIO_REG bool help diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index e4599f90f702..84e05701f500 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -116,6 +116,7 @@ obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o +obj-$(CONFIG_GPIO_RDA) += gpio-rda.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_REG) += gpio-reg.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o diff --git a/drivers/gpio/gpio-rda.c b/drivers/gpio/gpio-rda.c new file mode 100644 index 000000000000..28dcbb58b76b --- /dev/null +++ b/drivers/gpio/gpio-rda.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RDA Micro GPIO driver + * + * Copyright (C) 2012 RDA Micro Inc. + * Copyright (C) 2019 Manivannan Sadhasivam + */ + +#include +#include +#include +#include +#include +#include + +#define RDA_GPIO_OEN_VAL 0x00 +#define RDA_GPIO_OEN_SET_OUT 0x04 +#define RDA_GPIO_OEN_SET_IN 0x08 +#define RDA_GPIO_VAL 0x0c +#define RDA_GPIO_SET 0x10 +#define RDA_GPIO_CLR 0x14 +#define RDA_GPIO_INT_CTRL_SET 0x18 +#define RDA_GPIO_INT_CTRL_CLR 0x1c +#define RDA_GPIO_INT_CLR 0x20 +#define RDA_GPIO_INT_STATUS 0x24 + +#define RDA_GPIO_IRQ_RISE_SHIFT 0 +#define RDA_GPIO_IRQ_FALL_SHIFT 8 +#define RDA_GPIO_DEBOUCE_SHIFT 16 +#define RDA_GPIO_LEVEL_SHIFT 24 + +#define RDA_GPIO_IRQ_MASK 0xff + +/* Each bank consists of 32 GPIOs */ +#define RDA_GPIO_BANK_NR 32 + +struct rda_gpio { + struct gpio_chip chip; + void __iomem *base; + spinlock_t lock; + struct irq_chip irq_chip; + int irq; +}; + +static inline void rda_gpio_update(struct gpio_chip *chip, unsigned int offset, + u16 reg, int val) +{ + struct rda_gpio *rda_gpio = gpiochip_get_data(chip); + void __iomem *base = rda_gpio->base; + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&rda_gpio->lock, flags); + tmp = readl_relaxed(base + reg); + + if (val) + tmp |= BIT(offset); + else + tmp &= ~BIT(offset); + + writel_relaxed(tmp, base + reg); + spin_unlock_irqrestore(&rda_gpio->lock, flags); +} + +static void rda_gpio_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct rda_gpio *rda_gpio = gpiochip_get_data(chip); + void __iomem *base = rda_gpio->base; + u32 offset = irqd_to_hwirq(data); + u32 value; + + value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT; + value |= BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT; + + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR); +} + +static void rda_gpio_irq_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + u32 offset = irqd_to_hwirq(data); + + rda_gpio_update(chip, offset, RDA_GPIO_INT_CLR, 1); +} + +static int rda_gpio_set_irq(struct gpio_chip *chip, u32 offset, + unsigned int flow_type) +{ + struct rda_gpio *rda_gpio = gpiochip_get_data(chip); + void __iomem *base = rda_gpio->base; + u32 value; + + switch (flow_type) { + case IRQ_TYPE_EDGE_RISING: + /* Set rising edge trigger */ + value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); + + /* Switch to edge trigger interrupt */ + value = BIT(offset) << RDA_GPIO_LEVEL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR); + break; + + case IRQ_TYPE_EDGE_FALLING: + /* Set falling edge trigger */ + value = BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); + + /* Switch to edge trigger interrupt */ + value = BIT(offset) << RDA_GPIO_LEVEL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR); + break; + + case IRQ_TYPE_EDGE_BOTH: + /* Set both edge trigger */ + value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT; + value |= BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); + + /* Switch to edge trigger interrupt */ + value = BIT(offset) << RDA_GPIO_LEVEL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR); + break; + + case IRQ_TYPE_LEVEL_HIGH: + /* Set high level trigger */ + value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT; + + /* Switch to level trigger interrupt */ + value |= BIT(offset) << RDA_GPIO_LEVEL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); + break; + + case IRQ_TYPE_LEVEL_LOW: + /* Set low level trigger */ + value = BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT; + + /* Switch to level trigger interrupt */ + value |= BIT(offset) << RDA_GPIO_LEVEL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static void rda_gpio_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + u32 offset = irqd_to_hwirq(data); + u32 trigger = irqd_get_trigger_type(data); + + rda_gpio_set_irq(chip, offset, trigger); +} + +static int rda_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + u32 offset = irqd_to_hwirq(data); + int ret; + + ret = rda_gpio_set_irq(chip, offset, flow_type); + if (ret) + return ret; + + if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) + irq_set_handler_locked(data, handle_level_irq); + else if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + irq_set_handler_locked(data, handle_edge_irq); + + return 0; +} + +static void rda_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct irq_chip *ic = irq_desc_get_chip(desc); + struct rda_gpio *rda_gpio = gpiochip_get_data(chip); + unsigned long status; + u32 n, girq; + + chained_irq_enter(ic, desc); + + status = readl_relaxed(rda_gpio->base + RDA_GPIO_INT_STATUS); + /* Only lower 8 bits are capable of generating interrupts */ + status &= RDA_GPIO_IRQ_MASK; + + for_each_set_bit(n, &status, RDA_GPIO_BANK_NR) { + girq = irq_find_mapping(chip->irq.domain, n); + generic_handle_irq(girq); + } + + chained_irq_exit(ic, desc); +} + +static int rda_gpio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct gpio_irq_chip *girq; + struct rda_gpio *rda_gpio; + u32 ngpios; + int ret; + + rda_gpio = devm_kzalloc(dev, sizeof(*rda_gpio), GFP_KERNEL); + if (!rda_gpio) + return -ENOMEM; + + ret = device_property_read_u32(dev, "ngpios", &ngpios); + if (ret < 0) + return ret; + + /* + * Not all ports have interrupt capability. For instance, on + * RDA8810PL, GPIOC doesn't support interrupt. So we must handle + * those also. + */ + rda_gpio->irq = platform_get_irq(pdev, 0); + + rda_gpio->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rda_gpio->base)) + return PTR_ERR(rda_gpio->base); + + spin_lock_init(&rda_gpio->lock); + + ret = bgpio_init(&rda_gpio->chip, dev, 4, + rda_gpio->base + RDA_GPIO_VAL, + rda_gpio->base + RDA_GPIO_SET, + rda_gpio->base + RDA_GPIO_CLR, + rda_gpio->base + RDA_GPIO_OEN_SET_OUT, + rda_gpio->base + RDA_GPIO_OEN_SET_IN, + BGPIOF_READ_OUTPUT_REG_SET); + if (ret) { + dev_err(dev, "bgpio_init failed\n"); + return ret; + } + + rda_gpio->chip.label = dev_name(dev); + rda_gpio->chip.ngpio = ngpios; + rda_gpio->chip.base = -1; + rda_gpio->chip.parent = dev; + rda_gpio->chip.of_node = np; + + if (rda_gpio->irq >= 0) { + rda_gpio->irq_chip.name = "rda-gpio", + rda_gpio->irq_chip.irq_ack = rda_gpio_irq_ack, + rda_gpio->irq_chip.irq_mask = rda_gpio_irq_mask, + rda_gpio->irq_chip.irq_unmask = rda_gpio_irq_unmask, + rda_gpio->irq_chip.irq_set_type = rda_gpio_irq_set_type, + rda_gpio->irq_chip.flags = IRQCHIP_SKIP_SET_WAKE, + + girq = &rda_gpio->chip.irq; + girq->chip = &rda_gpio->irq_chip; + girq->handler = handle_bad_irq; + girq->default_type = IRQ_TYPE_NONE; + girq->parent_handler = rda_gpio_irq_handler; + girq->parent_handler_data = rda_gpio; + girq->num_parents = 1; + girq->parents = devm_kcalloc(dev, 1, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = rda_gpio->irq; + } + + platform_set_drvdata(pdev, rda_gpio); + + return devm_gpiochip_add_data(dev, &rda_gpio->chip, rda_gpio); +} + +static const struct of_device_id rda_gpio_of_match[] = { + { .compatible = "rda,8810pl-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rda_gpio_of_match); + +static struct platform_driver rda_gpio_driver = { + .probe = rda_gpio_probe, + .driver = { + .name = "rda-gpio", + .of_match_table = rda_gpio_of_match, + }, +}; + +module_platform_driver_probe(rda_gpio_driver, rda_gpio_probe); + +MODULE_DESCRIPTION("RDA Micro GPIO driver"); +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 6a41b6c5fc20abced88fa0eed42ae5e5cb70b280 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Fri, 25 Oct 2019 09:27:03 +1300 Subject: gpio: Add xgs-iproc driver This driver supports the Chip Common A GPIO controller present on a number of Broadcom switch ASICs with integrated SoCs. The controller is similar to the pinctrl-nsp-gpio and pinctrl-iproc-gpio blocks but different enough that a separate driver is required. This has been ported from Broadcom's XLDK 5.0.3 retaining only the CCA support (pinctrl-iproc-gpio covers CCB). Signed-off-by: Chris Packham Link: https://lore.kernel.org/r/20191024202703.8017-3-chris.packham@alliedtelesis.co.nz Acked-by: Scott Branden Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 9 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-xgs-iproc.c | 321 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 331 insertions(+) create mode 100644 drivers/gpio/gpio-xgs-iproc.c (limited to 'drivers/gpio/Kconfig') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8ec1f041c98d..e9516393c971 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -155,6 +155,15 @@ config GPIO_BCM_KONA help Turn on GPIO support for Broadcom "Kona" chips. +config GPIO_BCM_XGS_IPROC + tristate "BRCM XGS iProc GPIO support" + depends on OF_GPIO && (ARCH_BCM_IPROC || COMPILE_TEST) + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + default ARCH_BCM_IPROC + help + Say yes here to enable GPIO support for Broadcom XGS iProc SoCs. + config GPIO_BRCMSTB tristate "BRCMSTB GPIO support" default y if (ARCH_BRCMSTB || BMIPS_GENERIC) diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 84e05701f500..34eb8b2b12dd 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o obj-$(CONFIG_GPIO_ASPEED_SGPIO) += gpio-aspeed-sgpio.o obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o +obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o obj-$(CONFIG_GPIO_BD70528) += gpio-bd70528.o obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o diff --git a/drivers/gpio/gpio-xgs-iproc.c b/drivers/gpio/gpio-xgs-iproc.c new file mode 100644 index 000000000000..a3fdd95cc9e6 --- /dev/null +++ b/drivers/gpio/gpio-xgs-iproc.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017 Broadcom + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPROC_CCA_INT_F_GPIOINT BIT(0) +#define IPROC_CCA_INT_STS 0x20 +#define IPROC_CCA_INT_MASK 0x24 + +#define IPROC_GPIO_CCA_DIN 0x0 +#define IPROC_GPIO_CCA_DOUT 0x4 +#define IPROC_GPIO_CCA_OUT_EN 0x8 +#define IPROC_GPIO_CCA_INT_LEVEL 0x10 +#define IPROC_GPIO_CCA_INT_LEVEL_MASK 0x14 +#define IPROC_GPIO_CCA_INT_EVENT 0x18 +#define IPROC_GPIO_CCA_INT_EVENT_MASK 0x1C +#define IPROC_GPIO_CCA_INT_EDGE 0x24 + +struct iproc_gpio_chip { + struct irq_chip irqchip; + struct gpio_chip gc; + spinlock_t lock; + struct device *dev; + void __iomem *base; + void __iomem *intr; +}; + +static inline struct iproc_gpio_chip * +to_iproc_gpio(struct gpio_chip *gc) +{ + return container_of(gc, struct iproc_gpio_chip, gc); +} + +static void iproc_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio_chip *chip = to_iproc_gpio(gc); + int pin = d->hwirq; + unsigned long flags; + u32 irq = d->irq; + u32 irq_type, event_status = 0; + + spin_lock_irqsave(&chip->lock, flags); + irq_type = irq_get_trigger_type(irq); + if (irq_type & IRQ_TYPE_EDGE_BOTH) { + event_status |= BIT(pin); + writel_relaxed(event_status, + chip->base + IPROC_GPIO_CCA_INT_EVENT); + } + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void iproc_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio_chip *chip = to_iproc_gpio(gc); + int pin = d->hwirq; + unsigned long flags; + u32 irq = d->irq; + u32 int_mask, irq_type, event_mask; + + spin_lock_irqsave(&chip->lock, flags); + irq_type = irq_get_trigger_type(irq); + event_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); + int_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); + + if (irq_type & IRQ_TYPE_EDGE_BOTH) { + event_mask |= 1 << pin; + writel_relaxed(event_mask, + chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); + } else { + int_mask |= 1 << pin; + writel_relaxed(int_mask, + chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); + } + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void iproc_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio_chip *chip = to_iproc_gpio(gc); + int pin = d->hwirq; + unsigned long flags; + u32 irq = d->irq; + u32 irq_type, int_mask, event_mask; + + spin_lock_irqsave(&chip->lock, flags); + irq_type = irq_get_trigger_type(irq); + event_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); + int_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); + + if (irq_type & IRQ_TYPE_EDGE_BOTH) { + event_mask &= ~BIT(pin); + writel_relaxed(event_mask, + chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); + } else { + int_mask &= ~BIT(pin); + writel_relaxed(int_mask, + chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); + } + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int iproc_gpio_irq_set_type(struct irq_data *d, u32 type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio_chip *chip = to_iproc_gpio(gc); + int pin = d->hwirq; + unsigned long flags; + u32 irq = d->irq; + u32 event_pol, int_pol; + int ret = 0; + + spin_lock_irqsave(&chip->lock, flags); + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + event_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EDGE); + event_pol &= ~BIT(pin); + writel_relaxed(event_pol, chip->base + IPROC_GPIO_CCA_INT_EDGE); + break; + case IRQ_TYPE_EDGE_FALLING: + event_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EDGE); + event_pol |= BIT(pin); + writel_relaxed(event_pol, chip->base + IPROC_GPIO_CCA_INT_EDGE); + break; + case IRQ_TYPE_LEVEL_HIGH: + int_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL); + int_pol &= ~BIT(pin); + writel_relaxed(int_pol, chip->base + IPROC_GPIO_CCA_INT_LEVEL); + break; + case IRQ_TYPE_LEVEL_LOW: + int_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL); + int_pol |= BIT(pin); + writel_relaxed(int_pol, chip->base + IPROC_GPIO_CCA_INT_LEVEL); + break; + default: + /* should not come here */ + ret = -EINVAL; + goto out_unlock; + } + + if (type & IRQ_TYPE_LEVEL_MASK) + irq_set_handler_locked(irq_get_irq_data(irq), handle_level_irq); + else if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(irq_get_irq_data(irq), handle_edge_irq); + +out_unlock: + spin_unlock_irqrestore(&chip->lock, flags); + + return ret; +} + +static irqreturn_t iproc_gpio_irq_handler(int irq, void *data) +{ + struct gpio_chip *gc = (struct gpio_chip *)data; + struct iproc_gpio_chip *chip = to_iproc_gpio(gc); + int bit; + unsigned long int_bits = 0; + u32 int_status; + + /* go through the entire GPIOs and handle all interrupts */ + int_status = readl_relaxed(chip->intr + IPROC_CCA_INT_STS); + if (int_status & IPROC_CCA_INT_F_GPIOINT) { + u32 event, level; + + /* Get level and edge interrupts */ + event = + readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); + event &= readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT); + level = readl_relaxed(chip->base + IPROC_GPIO_CCA_DIN); + level ^= readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL); + level &= + readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); + int_bits = level | event; + + for_each_set_bit(bit, &int_bits, gc->ngpio) + generic_handle_irq(irq_linear_revmap(gc->irq.domain, bit)); + } + + return int_bits ? IRQ_HANDLED : IRQ_NONE; +} + +static int iproc_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = pdev->dev.of_node; + struct iproc_gpio_chip *chip; + u32 num_gpios; + int irq, ret; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = dev; + platform_set_drvdata(pdev, chip); + spin_lock_init(&chip->lock); + + chip->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(chip->base)) + return PTR_ERR(chip->base); + + ret = bgpio_init(&chip->gc, dev, 4, + chip->base + IPROC_GPIO_CCA_DIN, + chip->base + IPROC_GPIO_CCA_DOUT, + NULL, + chip->base + IPROC_GPIO_CCA_OUT_EN, + NULL, + 0); + if (ret) { + dev_err(dev, "unable to init GPIO chip\n"); + return ret; + } + + chip->gc.label = dev_name(dev); + if (of_property_read_u32(dn, "ngpios", &num_gpios)) + chip->gc.ngpio = num_gpios; + + irq = platform_get_irq(pdev, 0); + if (irq > 0) { + struct gpio_irq_chip *girq; + struct irq_chip *irqc; + u32 val; + + irqc = &chip->irqchip; + irqc->name = dev_name(dev); + irqc->irq_ack = iproc_gpio_irq_ack; + irqc->irq_mask = iproc_gpio_irq_mask; + irqc->irq_unmask = iproc_gpio_irq_unmask; + irqc->irq_set_type = iproc_gpio_irq_set_type; + + chip->intr = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(chip->intr)) + return PTR_ERR(chip->intr); + + /* Enable GPIO interrupts for CCA GPIO */ + val = readl_relaxed(chip->intr + IPROC_CCA_INT_MASK); + val |= IPROC_CCA_INT_F_GPIOINT; + writel_relaxed(val, chip->intr + IPROC_CCA_INT_MASK); + + /* + * Directly request the irq here instead of passing + * a flow-handler to gpiochip_set_chained_irqchip, + * because the irq is shared. + */ + ret = devm_request_irq(dev, irq, iproc_gpio_irq_handler, + IRQF_SHARED, chip->gc.label, &chip->gc); + if (ret) { + dev_err(dev, "Fail to request IRQ%d: %d\n", irq, ret); + return ret; + } + + girq = &chip->gc.irq; + girq->chip = irqc; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + } + + ret = devm_gpiochip_add_data(dev, &chip->gc, chip); + if (ret) { + dev_err(dev, "unable to add GPIO chip\n"); + return ret; + } + + return 0; +} + +static int __exit iproc_gpio_remove(struct platform_device *pdev) +{ + struct iproc_gpio_chip *chip; + + chip = platform_get_drvdata(pdev); + if (!chip) + return -ENODEV; + + if (chip->intr) { + u32 val; + + val = readl_relaxed(chip->intr + IPROC_CCA_INT_MASK); + val &= ~IPROC_CCA_INT_F_GPIOINT; + writel_relaxed(val, chip->intr + IPROC_CCA_INT_MASK); + } + + return 0; +} + +static const struct of_device_id bcm_iproc_gpio_of_match[] __initconst = { + { .compatible = "brcm,iproc-gpio-cca" }, + {} +}; +MODULE_DEVICE_TABLE(of, bcm_iproc_gpio_of_match); + +static struct platform_driver bcm_iproc_gpio_driver = { + .driver = { + .name = "iproc-xgs-gpio", + .owner = THIS_MODULE, + .of_match_table = bcm_iproc_gpio_of_match, + }, + .probe = iproc_gpio_probe, + .remove = iproc_gpio_remove, +}; + +module_platform_driver(bcm_iproc_gpio_driver); + +MODULE_DESCRIPTION("XGS IPROC GPIO driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3