On 31/01/2017 18:19, Daniel Gonzalez Cabanelas wrote: > 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.
Hi, with trunk now moving to v4.9 i would vote to not apply this patch. It will be part of the v4.9 update anyhow. i would prefer if we simply test the new driver as me move to the new kernel. John > > Signed-off-by: Daniel Gonzalez Cabanelas <dgcb...@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 <al...@free.fr> > + * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnaraya...@atheros.com> > + * Copyright (C) 2008-2011 Gabor Juhos <juh...@openwrt.org> > + * Copyright (C) 2008 Imre Kaloz <ka...@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 > > > _______________________________________________ > Lede-dev mailing list > Lede-dev@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/lede-dev > _______________________________________________ Lede-dev mailing list Lede-dev@lists.infradead.org http://lists.infradead.org/mailman/listinfo/lede-dev