[LEDE-DEV] [PATCH] ar71xx: GPIO driver backport
John Crispin
john at phrozen.org
Thu Feb 9 00:34:15 PST 2017
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 <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
>
>
> _______________________________________________
> Lede-dev mailing list
> Lede-dev at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/lede-dev
>
More information about the Lede-dev
mailing list