diff mbox

[LEDE-DEV] ar71xx: GPIO driver backport

Message ID 46449899.Df8CWF06sN@tool
State Rejected
Headers show

Commit Message

Daniel González Cabanelas Jan. 31, 2017, 5:19 p.m. UTC
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@gmail.com>

Comments

John Crispin Feb. 9, 2017, 8:34 a.m. UTC | #1
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@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@free.fr>
> +  *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
> +  *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
> +  *  Copyright (C) 2008 Imre Kaloz <kaloz@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
>
diff mbox

Patch

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@free.fr>
+  *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
+  *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+  *  Copyright (C) 2008 Imre Kaloz <kaloz@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