[LEDE-DEV] [PATCH] ar71xx: GPIO driver backport
Daniel Gonzalez Cabanelas
dgcbueu at gmail.com
Tue Jan 31 09:19:40 PST 2017
Backport the ath79 GPIO driver to kernel 4.4.
Benefits this driver has compared with the old one:
- support for the interrupt controller
- module removable
The new code using the generic MMIO GPIO driver starts in
kernel 4.6, therefore this patch won't be useful anymore
when this version is adopted. The intention is to integrate
the patch into the stable release 17.01
Patch tested in an AR7240 based board.
Signed-off-by: Daniel Gonzalez Cabanelas <dgcbueu at gmail.com>
diff --git a/target/linux/ar71xx/config-4.4 b/target/linux/ar71xx/config-4.4
index 4b2f736..d3ecf4d 100644
--- a/target/linux/ar71xx/config-4.4
+++ b/target/linux/ar71xx/config-4.4
@@ -267,6 +267,7 @@ CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_GENERIC_TIME_VSYSCALL=y
CONFIG_GPIOLIB=y
CONFIG_GPIOLIB_IRQCHIP=y
+CONFIG_GPIO_ATH79=y
CONFIG_GPIO_DEVRES=y
# CONFIG_GPIO_LATCH is not set
CONFIG_GPIO_NXP_74HC153=y
diff --git a/target/linux/ar71xx/patches-4.4/455-gpio-ath79-driver_backport-irqchip.patch b/target/linux/ar71xx/patches-4.4/455-gpio-ath79-driver_backport-irqchip.patch
new file mode 100644
index 0000000..e0f8622
--- /dev/null
+++ b/target/linux/ar71xx/patches-4.4/455-gpio-ath79-driver_backport-irqchip.patch
@@ -0,0 +1,469 @@
+--- a/drivers/gpio/gpio-ath79.c
++++ b/drivers/gpio/gpio-ath79.c
+@@ -1,145 +1,237 @@
+ /*
+ * Atheros AR71XX/AR724X/AR913X GPIO API support
+ *
++ * Copyright (C) 2015 Alban Bedel <albeu at free.fr>
+ * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan at atheros.com>
+ * Copyright (C) 2008-2011 Gabor Juhos <juhosg at openwrt.org>
+ * Copyright (C) 2008 Imre Kaloz <kaloz at openwrt.org>
+ *
+- * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
+- *
+ * 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/gpio/driver.h>
++#include <linux/basic_mmio_gpio.h>
+ #include <linux/platform_data/gpio-ath79.h>
+ #include <linux/of_device.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/irq.h>
+
+-#include <asm/mach-ath79/ar71xx_regs.h>
++#define AR71XX_GPIO_REG_OE 0x00
++#define AR71XX_GPIO_REG_IN 0x04
++#define AR71XX_GPIO_REG_SET 0x0c
++#define AR71XX_GPIO_REG_CLEAR 0x10
++
++#define AR71XX_GPIO_REG_INT_ENABLE 0x14
++#define AR71XX_GPIO_REG_INT_TYPE 0x18
++#define AR71XX_GPIO_REG_INT_POLARITY 0x1c
++#define AR71XX_GPIO_REG_INT_PENDING 0x20
++#define AR71XX_GPIO_REG_INT_MASK 0x24
+
+ struct ath79_gpio_ctrl {
+- struct gpio_chip chip;
++ struct bgpio_chip bgc;
+ void __iomem *base;
+ spinlock_t lock;
++ unsigned long both_edges;
+ };
+
+-#define to_ath79_gpio_ctrl(c) container_of(c, struct ath79_gpio_ctrl, chip)
+
+-static void ath79_gpio_set_value(struct gpio_chip *chip,
+- unsigned gpio, int value)
++static inline struct ath79_gpio_ctrl *to_ath79_gpio_ctrl(struct gpio_chip *gc)
+ {
+- struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(chip);
++ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+- if (value)
+- __raw_writel(BIT(gpio), ctrl->base + AR71XX_GPIO_REG_SET);
+- else
+- __raw_writel(BIT(gpio), ctrl->base + AR71XX_GPIO_REG_CLEAR);
++ return container_of(bgc, struct ath79_gpio_ctrl, bgc);
+ }
+
+-static int ath79_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
++static struct ath79_gpio_ctrl *irq_data_to_ath79_gpio(struct irq_data *data)
+ {
+- struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(chip);
++ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
++ return to_ath79_gpio_ctrl(gc);
++}
+
+- return (__raw_readl(ctrl->base + AR71XX_GPIO_REG_IN) >> gpio) & 1;
++static u32 ath79_gpio_read(struct ath79_gpio_ctrl *ctrl, unsigned reg)
++{
++ return readl(ctrl->base + reg);
+ }
+
+-static int ath79_gpio_direction_input(struct gpio_chip *chip,
+- unsigned offset)
++static void ath79_gpio_write(struct ath79_gpio_ctrl *ctrl,
++ unsigned reg, u32 val)
+ {
+- struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(chip);
+- unsigned long flags;
++ return writel(val, ctrl->base + reg);
++}
+
+- spin_lock_irqsave(&ctrl->lock, flags);
++static bool ath79_gpio_update_bits(
++ struct ath79_gpio_ctrl *ctrl, unsigned reg, u32 mask, u32 bits)
++{
++ u32 old_val, new_val;
+
+- __raw_writel(
+- __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) & ~BIT(offset),
+- ctrl->base + AR71XX_GPIO_REG_OE);
++ old_val = ath79_gpio_read(ctrl, reg);
++ new_val = (old_val & ~mask) | (bits & mask);
+
+- spin_unlock_irqrestore(&ctrl->lock, flags);
++ if (new_val != old_val)
++ ath79_gpio_write(ctrl, reg, new_val);
+
+- return 0;
++ return new_val != old_val;
+ }
+
+-static int ath79_gpio_direction_output(struct gpio_chip *chip,
+- unsigned offset, int value)
++static void ath79_gpio_irq_unmask(struct irq_data *data)
+ {
+- struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(chip);
++ struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
++ u32 mask = BIT(irqd_to_hwirq(data));
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctrl->lock, flags);
++ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, mask);
++ spin_unlock_irqrestore(&ctrl->lock, flags);
++}
+
+- if (value)
+- __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_SET);
+- else
+- __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_CLEAR);
+-
+- __raw_writel(
+- __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) | BIT(offset),
+- ctrl->base + AR71XX_GPIO_REG_OE);
++static void ath79_gpio_irq_mask(struct irq_data *data)
++{
++ struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
++ u32 mask = BIT(irqd_to_hwirq(data));
++ unsigned long flags;
+
++ spin_lock_irqsave(&ctrl->lock, flags);
++ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, 0);
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+-
+- return 0;
+ }
+
+-static int ar934x_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
++static void ath79_gpio_irq_enable(struct irq_data *data)
+ {
+- struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(chip);
++ struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
++ u32 mask = BIT(irqd_to_hwirq(data));
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctrl->lock, flags);
++ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, mask);
++ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, mask);
++ spin_unlock_irqrestore(&ctrl->lock, flags);
++}
+
+- __raw_writel(
+- __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) | BIT(offset),
+- ctrl->base + AR71XX_GPIO_REG_OE);
++static void ath79_gpio_irq_disable(struct irq_data *data)
++{
++ struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
++ u32 mask = BIT(irqd_to_hwirq(data));
++ unsigned long flags;
+
++ spin_lock_irqsave(&ctrl->lock, flags);
++ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, 0);
++ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, 0);
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+-
+- return 0;
+ }
+
+-static int ar934x_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+- int value)
++static int ath79_gpio_irq_set_type(struct irq_data *data,
++ unsigned int flow_type)
+ {
+- struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(chip);
++ struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
++ u32 mask = BIT(irqd_to_hwirq(data));
++ u32 type = 0, polarity = 0;
+ unsigned long flags;
++ bool disabled;
++
++ switch (flow_type) {
++ case IRQ_TYPE_EDGE_RISING:
++ polarity |= mask;
++ case IRQ_TYPE_EDGE_FALLING:
++ case IRQ_TYPE_EDGE_BOTH:
++ break;
++
++ case IRQ_TYPE_LEVEL_HIGH:
++ polarity |= mask;
++ case IRQ_TYPE_LEVEL_LOW:
++ type |= mask;
++ break;
++
++ default:
++ return -EINVAL;
++ }
+
+ spin_lock_irqsave(&ctrl->lock, flags);
+
+- if (value)
+- __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_SET);
+- else
+- __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_CLEAR);
++ if (flow_type == IRQ_TYPE_EDGE_BOTH) {
++ ctrl->both_edges |= mask;
++ polarity = ~ath79_gpio_read(ctrl, AR71XX_GPIO_REG_IN);
++ } else {
++ ctrl->both_edges &= ~mask;
++ }
+
+- __raw_writel(
+- __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) & ~BIT(offset),
+- ctrl->base + AR71XX_GPIO_REG_OE);
++ /* As the IRQ configuration can't be loaded atomically we
++ * have to disable the interrupt while the configuration state
++ * is invalid.
++ */
++ disabled = ath79_gpio_update_bits(
++ ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, 0);
++
++ ath79_gpio_update_bits(
++ ctrl, AR71XX_GPIO_REG_INT_TYPE, mask, type);
++ ath79_gpio_update_bits(
++ ctrl, AR71XX_GPIO_REG_INT_POLARITY, mask, polarity);
++
++ if (disabled)
++ ath79_gpio_update_bits(
++ ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, mask);
+
+ spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ return 0;
+ }
+
+-static const struct gpio_chip ath79_gpio_chip = {
+- .label = "ath79",
+- .get = ath79_gpio_get_value,
+- .set = ath79_gpio_set_value,
+- .direction_input = ath79_gpio_direction_input,
+- .direction_output = ath79_gpio_direction_output,
+- .base = 0,
++static struct irq_chip ath79_gpio_irqchip = {
++ .name = "gpio-ath79",
++ .irq_enable = ath79_gpio_irq_enable,
++ .irq_disable = ath79_gpio_irq_disable,
++ .irq_mask = ath79_gpio_irq_mask,
++ .irq_unmask = ath79_gpio_irq_unmask,
++ .irq_set_type = ath79_gpio_irq_set_type,
+ };
+
++static void ath79_gpio_irq_handler(struct irq_desc *desc)
++{
++ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
++ struct irq_chip *irqchip = irq_desc_get_chip(desc);
++ struct ath79_gpio_ctrl *ctrl = to_ath79_gpio_ctrl(gc);
++ unsigned long flags, pending;
++ u32 both_edges, state;
++ int irq;
++
++ chained_irq_enter(irqchip, desc);
++
++ spin_lock_irqsave(&ctrl->lock, flags);
++
++ pending = ath79_gpio_read(ctrl, AR71XX_GPIO_REG_INT_PENDING);
++
++ /* Update the polarity of the both edges irqs */
++ both_edges = ctrl->both_edges & pending;
++ if (both_edges) {
++ state = ath79_gpio_read(ctrl, AR71XX_GPIO_REG_IN);
++ ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_POLARITY,
++ both_edges, ~state);
++ }
++
++ spin_unlock_irqrestore(&ctrl->lock, flags);
++
++ if (pending) {
++ for_each_set_bit(irq, &pending, gc->ngpio)
++ generic_handle_irq(
++ irq_linear_revmap(gc->irqdomain, irq));
++ }
++
++ chained_irq_exit(irqchip, desc);
++}
++
+ static const struct of_device_id ath79_gpio_of_match[] = {
+ { .compatible = "qca,ar7100-gpio" },
+ { .compatible = "qca,ar9340-gpio" },
+ {},
+ };
++MODULE_DEVICE_TABLE(of, ath79_gpio_of_match);
+
+ static int ath79_gpio_probe(struct platform_device *pdev)
+ {
+- struct ath79_gpio_platform_data *pdata = pdev->dev.platform_data;
++ struct ath79_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node *np = pdev->dev.of_node;
+ struct ath79_gpio_ctrl *ctrl;
+ struct resource *res;
+ u32 ath79_gpio_count;
+@@ -148,19 +240,16 @@
+
+ ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
++ platform_set_drvdata(pdev, ctrl);
+
+ if (np) {
+ err = of_property_read_u32(np, "ngpios", &ath79_gpio_count);
+ if (err) {
+ dev_err(&pdev->dev, "ngpios property is not valid\n");
+ return err;
+ }
+- if (ath79_gpio_count >= 32) {
+- dev_err(&pdev->dev, "ngpios must be less than 32\n");
+- return -EINVAL;
+- }
+ oe_inverted = of_device_is_compatible(np, "qca,ar9340-gpio");
+ } else if (pdata) {
+ ath79_gpio_count = pdata->ngpios;
+ oe_inverted = pdata->oe_inverted;
+@@ -168,30 +257,68 @@
+ dev_err(&pdev->dev, "No DT node or platform data found\n");
+ return -EINVAL;
+ }
+
++ if (ath79_gpio_count >= 32) {
++ dev_err(&pdev->dev, "ngpios must be less than 32\n");
++ return -EINVAL;
++ }
++
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ctrl->base = devm_ioremap_nocache(
+ &pdev->dev, res->start, resource_size(res));
+ if (!ctrl->base)
+ return -ENOMEM;
+
+ spin_lock_init(&ctrl->lock);
+- memcpy(&ctrl->chip, &ath79_gpio_chip, sizeof(ctrl->chip));
+- ctrl->chip.dev = &pdev->dev;
+- ctrl->chip.ngpio = ath79_gpio_count;
+- if (oe_inverted) {
+- ctrl->chip.direction_input = ar934x_gpio_direction_input;
+- ctrl->chip.direction_output = ar934x_gpio_direction_output;
++ err = bgpio_init(&ctrl->bgc, &pdev->dev, 4,
++ ctrl->base + AR71XX_GPIO_REG_IN,
++ ctrl->base + AR71XX_GPIO_REG_SET,
++ ctrl->base + AR71XX_GPIO_REG_CLEAR,
++ oe_inverted ? NULL : ctrl->base + AR71XX_GPIO_REG_OE,
++ oe_inverted ? ctrl->base + AR71XX_GPIO_REG_OE : NULL,
++ 0);
++ if (err) {
++ dev_err(&pdev->dev, "bgpio_init failed\n");
++ return err;
+ }
++ /* Use base 0 to stay compatible with legacy platforms */
++ ctrl->bgc.gc.base = 0;
++ ctrl->bgc.gc.ngpio = ath79_gpio_count;
+
+- err = gpiochip_add(&ctrl->chip);
++ err = gpiochip_add(&ctrl->bgc.gc);
+ if (err) {
+ dev_err(&pdev->dev,
+ "cannot add AR71xx GPIO chip, error=%d", err);
+ return err;
+ }
+
++ if (np && !of_property_read_bool(np, "interrupt-controller"))
++ return 0;
++
++ err = gpiochip_irqchip_add(&ctrl->bgc.gc, &ath79_gpio_irqchip, 0,
++ handle_simple_irq, IRQ_TYPE_NONE);
++ if (err) {
++ dev_err(&pdev->dev, "failed to add gpiochip_irqchip\n");
++ goto gpiochip_remove;
++ }
++
++ gpiochip_set_chained_irqchip(&ctrl->bgc.gc, &ath79_gpio_irqchip,
++ platform_get_irq(pdev, 0),
++ ath79_gpio_irq_handler);
++
++ return 0;
++
++gpiochip_remove:
++ gpiochip_remove(&ctrl->bgc.gc);
++ return err;
++}
++
++static int ath79_gpio_remove(struct platform_device *pdev)
++{
++ struct ath79_gpio_ctrl *ctrl = platform_get_drvdata(pdev);
++
++ gpiochip_remove(&ctrl->bgc.gc);
+ return 0;
+ }
+
+ static struct platform_driver ath79_gpio_driver = {
+@@ -199,8 +326,9 @@
+ .name = "ath79-gpio",
+ .of_match_table = ath79_gpio_of_match,
+ },
+ .probe = ath79_gpio_probe,
++ .remove = ath79_gpio_remove,
+ };
+
+ static int __init ath79_gpio_init(void)
+ {
+--- a/arch/mips/include/asm/mach-ath79/irq.h
++++ b/arch/mips/include/asm/mach-ath79/irq.h
+@@ -7,13 +7,13 @@
+ * by the Free Software Foundation.
+ */
+ #ifndef __ASM_MACH_ATH79_IRQ_H
+ #define __ASM_MACH_ATH79_IRQ_H
+
+ #define MIPS_CPU_IRQ_BASE 0
+-#define NR_IRQS 51
++#define NR_IRQS 83
+
+ #define ATH79_CPU_IRQ(_x) (MIPS_CPU_IRQ_BASE + (_x))
+
+ #define ATH79_MISC_IRQ_BASE 8
+ #define ATH79_MISC_IRQ_COUNT 32
+ #define ATH79_MISC_IRQ(_x) (ATH79_MISC_IRQ_BASE + (_x))
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -124,12 +124,22 @@
+ tristate "AMD Promontory GPIO support"
+ depends on ACPI
+ help
+ driver for GPIO functionality on Promontory IOHub
+ Require ACPI ASL code to enumerate as a platform device.
+
++config GPIO_ATH79
++ tristate "Atheros AR71XX/AR724X/AR913X GPIO support"
++ default y if ATH79
++ depends on ATH79 || COMPILE_TEST
++ select GPIO_GENERIC
++ select GPIOLIB_IRQCHIP
++ help
++ Select this option to enable GPIO driver for
++ Atheros AR71XX/AR724X/AR913X SoC devices.
++
+ config GPIO_BCM_KONA
+ bool "Broadcom Kona GPIO"
+ depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST)
+ help
+ Turn on GPIO support for Broadcom "Kona" chips.
+
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -19,13 +19,13 @@
+ obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
+ obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
+ obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o
+ obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
+ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o
+ obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
+-obj-$(CONFIG_ATH79) += gpio-ath79.o
++obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o
+ obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
+ obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
+ obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
+ obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
+ obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
+ obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o
More information about the Lede-dev
mailing list