From: Felix Fietkau Date: Sun, 7 Oct 2012 23:23:52 +0000 (+0000) Subject: cns3xxx: fix gpio access and add gpio IRQ support X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=6b778f57aad44a95e9171f512a285abd73ff3b65;p=openwrt%2Fstaging%2Fldir.git cns3xxx: fix gpio access and add gpio IRQ support Signed-off-by: Tim Harvey target/linux/cns3xxx/patches-3.3/470-gpio_irq.patch | 536 ++++++++++++++++++++ 1 file changed, 536 insertions(+) SVN-Revision: 33648 --- diff --git a/target/linux/cns3xxx/patches-3.3/470-gpio_irq.patch b/target/linux/cns3xxx/patches-3.3/470-gpio_irq.patch new file mode 100644 index 0000000000..c5de35fe42 --- /dev/null +++ b/target/linux/cns3xxx/patches-3.3/470-gpio_irq.patch @@ -0,0 +1,536 @@ +--- a/arch/arm/mach-cns3xxx/Makefile ++++ b/arch/arm/mach-cns3xxx/Makefile +@@ -1,4 +1,4 @@ +-obj-$(CONFIG_ARCH_CNS3XXX) += core.o pm.o devices.o ++obj-$(CONFIG_ARCH_CNS3XXX) += core.o gpio.o pm.o devices.o + obj-$(CONFIG_PCI) += pcie.o + obj-$(CONFIG_MACH_CNS3420VB) += cns3420vb.o + obj-$(CONFIG_MACH_GW2388) += laguna.o +--- a/arch/arm/mach-cns3xxx/cns3420vb.c ++++ b/arch/arm/mach-cns3xxx/cns3420vb.c +@@ -199,7 +199,10 @@ static void __init cns3420_init(void) + + cns3xxx_ahci_init(); + cns3xxx_sdhci_init(); +- ++ cns3xxx_gpio_init( 0, 32, CNS3XXX_GPIOA_BASE_VIRT, IRQ_CNS3XXX_GPIOA, ++ NR_IRQS_CNS3XXX); ++ cns3xxx_gpio_init(32, 32, CNS3XXX_GPIOB_BASE_VIRT, IRQ_CNS3XXX_GPIOB, ++ NR_IRQS_CNS3XXX + 32); + cns3xxx_pcie_init(0x3); + + pm_power_off = cns3xxx_power_off; +--- a/arch/arm/mach-cns3xxx/core.c ++++ b/arch/arm/mach-cns3xxx/core.c +@@ -21,7 +21,6 @@ + #include + #include + #include +-#include + #include + #include "core.h" + +@@ -83,73 +82,12 @@ static struct map_desc cns3xxx_io_desc[] + }, + }; + +-static inline void gpio_line_config(u8 line, u32 direction) +-{ +- u32 reg; +- if (direction) { +- if (line < 32) { +- reg = __raw_readl(CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_DIR); +- reg |= (1 << line); +- __raw_writel(reg, CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_DIR); +- } else { +- reg = __raw_readl(CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_DIR); +- reg |= (1 << (line - 32)); +- __raw_writel(reg, CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_DIR); +- } +- } else { +- if (line < 32) { +- reg = __raw_readl(CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_DIR); +- reg &= ~(1 << line); +- __raw_writel(reg, CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_DIR); +- } else { +- reg = __raw_readl(CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_DIR); +- reg &= ~(1 << (line - 32)); +- __raw_writel(reg, CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_DIR); +- } +- } +-} +- +-static int cns3xxx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +-{ +- gpio_line_config(gpio, CNS3XXX_GPIO_IN); +- return 0; +-} +- +-static int cns3xxx_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int level) +-{ +- gpio_line_set(gpio, level); +- gpio_line_config(gpio, CNS3XXX_GPIO_OUT); +- return 0; +-} +- +-static int cns3xxx_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +-{ +- return gpio_get_value(gpio); +-} +- +-static void cns3xxx_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value) +-{ +- gpio_set_value(gpio, value); +-} +- +-static struct gpio_chip cns3xxx_gpio_chip = { +- .label = "CNS3XXX_GPIO_CHIP", +- .direction_input = cns3xxx_gpio_direction_input, +- .direction_output = cns3xxx_gpio_direction_output, +- .get = cns3xxx_gpio_get_value, +- .set = cns3xxx_gpio_set_value, +- .base = 0, +- .ngpio = 64, +-}; +- + void __init cns3xxx_common_init(void) + { + #ifdef CONFIG_LOCAL_TIMERS + twd_base = (void __iomem *) CNS3XXX_TC11MP_TWD_BASE_VIRT; + #endif + iotable_init(cns3xxx_io_desc, ARRAY_SIZE(cns3xxx_io_desc)); +- +- gpiochip_add(&cns3xxx_gpio_chip); + } + + /* used by entry-macro.S */ +--- /dev/null ++++ b/arch/arm/mach-cns3xxx/gpio.c +@@ -0,0 +1,277 @@ ++/* ++ * Copyright 2012 Gateworks Corporation ++ * Chris Lang ++ * Tim Harvey ++ * ++ * This file 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* ++ * Registers ++ */ ++#define GPIO_INPUT 0x04 ++#define GPIO_DIR 0x08 ++#define GPIO_SET 0x10 ++#define GPIO_CLEAR 0x14 ++#define GPIO_INTERRUPT_ENABLE 0x20 ++#define GPIO_INTERRUPT_RAW_STATUS 0x24 ++#define GPIO_INTERRUPT_MASKED_STATUS 0x28 ++#define GPIO_INTERRUPT_MASK 0x2C ++#define GPIO_INTERRUPT_CLEAR 0x30 ++#define GPIO_INTERRUPT_TRIGGER_METHOD 0x34 ++#define GPIO_INTERRUPT_TRIGGER_BOTH_EDGES 0x38 ++#define GPIO_INTERRUPT_TRIGGER_TYPE 0x3C ++ ++#define GPIO_INTERRUPT_TRIGGER_METHOD_EDGE 0 ++#define GPIO_INTERRUPT_TRIGGER_METHOD_LEVEL 1 ++#define GPIO_INTERRUPT_TRIGGER_EDGE_SINGLE 0 ++#define GPIO_INTERRUPT_TRIGGER_EDGE_BOTH 1 ++#define GPIO_INTERRUPT_TRIGGER_TYPE_RISING 0 ++#define GPIO_INTERRUPT_TRIGGER_TYPE_FALLING 1 ++#define GPIO_INTERRUPT_TRIGGER_TYPE_HIGH 0 ++#define GPIO_INTERRUPT_TRIGGER_TYPE_LOW 1 ++ ++struct cns3xxx_gpio_chip { ++ struct gpio_chip chip; ++ spinlock_t lock; ++ void __iomem *base; ++ int secondary_irq_base; ++}; ++ ++static struct cns3xxx_gpio_chip cns3xxx_gpio_chips[2]; ++static int cns3xxx_gpio_chip_count; ++ ++static inline void ++__set_direction(struct cns3xxx_gpio_chip *cchip, unsigned pin, int input) ++{ ++ u32 reg; ++ ++ reg = __raw_readl(cchip->base + GPIO_DIR); ++ if (input) ++ reg |= 1 << pin; ++ else ++ reg &= !(1 << pin); ++ __raw_writel(reg, cchip->base + GPIO_DIR); ++} ++ ++/* ++ * GENERIC_GPIO primatives ++ */ ++static int cns3xxx_gpio_direction_input(struct gpio_chip *chip, unsigned pin) ++{ ++ struct cns3xxx_gpio_chip *cchip = ++ container_of(chip, struct cns3xxx_gpio_chip, chip); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cchip->lock, flags); ++ __set_direction(cchip, pin, 1); ++ spin_unlock_irqrestore(&cchip->lock, flags); ++ ++ return 0; ++} ++ ++static int cns3xxx_gpio_get(struct gpio_chip *chip, unsigned pin) ++{ ++ struct cns3xxx_gpio_chip *cchip = ++ container_of(chip, struct cns3xxx_gpio_chip, chip); ++ int val; ++ ++ val = ((__raw_readl(cchip->base + GPIO_INPUT) >> pin) & 0x1); ++ ++ return val; ++} ++ ++static int cns3xxx_gpio_direction_output(struct gpio_chip *chip, unsigned pin, int level) ++{ ++ struct cns3xxx_gpio_chip *cchip = ++ container_of(chip, struct cns3xxx_gpio_chip, chip); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cchip->lock, flags); ++ if (level) ++ __raw_writel(1 << pin, cchip->base + GPIO_SET); ++ else ++ __raw_writel(1 << pin, cchip->base + GPIO_CLEAR); ++ __set_direction(cchip, pin, 0); ++ spin_unlock_irqrestore(&cchip->lock, flags); ++ ++ return 0; ++} ++ ++static void cns3xxx_gpio_set(struct gpio_chip *chip, unsigned pin, ++ int level) ++{ ++ struct cns3xxx_gpio_chip *cchip = ++ container_of(chip, struct cns3xxx_gpio_chip, chip); ++ ++ if (level) ++ __raw_writel(1 << pin, cchip->base + GPIO_SET); ++ else ++ __raw_writel(1 << pin, cchip->base + GPIO_CLEAR); ++} ++ ++static int cns3xxx_gpio_to_irq(struct gpio_chip *chip, unsigned pin) ++{ ++ struct cns3xxx_gpio_chip *cchip = ++ container_of(chip, struct cns3xxx_gpio_chip, chip); ++ ++ return cchip->secondary_irq_base + pin; ++} ++ ++ ++/* ++ * IRQ support ++ */ ++ ++/* one interrupt per GPIO controller (GPIOA/GPIOB) ++ * this is called in task context, with IRQs enabled ++ */ ++static void cns3xxx_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) ++{ ++ struct cns3xxx_gpio_chip *cchip = irq_get_handler_data(irq); ++ struct irq_chip *chip = irq_get_chip(irq); ++ struct irq_chip_generic *gc = irq_desc_get_chip_data(desc); ++ struct irq_chip_type *ct = gc->chip_types; ++ u16 i; ++ u32 reg; ++ ++ chained_irq_enter(chip, desc); /* mask and ack the base interrupt */ ++ ++ /* see which pin(s) triggered the interrupt */ ++ reg = __raw_readl(cchip->base + GPIO_INTERRUPT_RAW_STATUS); ++ for (i = 0; i < 32; i++) { ++ if (reg & (1 << i)) { ++ /* let the generic IRQ layer handle an interrupt */ ++ generic_handle_irq(cchip->secondary_irq_base + i); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); /* unmask the base interrupt */ ++} ++ ++static int cns3xxx_gpio_irq_set_type(struct irq_data *d, u32 irqtype) ++{ ++ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); ++ struct cns3xxx_gpio_chip *cchip = gc->private; ++ u32 gpio = d->irq - cchip->secondary_irq_base; ++ unsigned long flags; ++ u32 method, edges, type; ++ ++ spin_lock_irqsave(&cchip->lock, flags); ++ method = __raw_readl(cchip->base + GPIO_INTERRUPT_TRIGGER_METHOD); ++ edges = __raw_readl(cchip->base + GPIO_INTERRUPT_TRIGGER_BOTH_EDGES); ++ type = __raw_readl(cchip->base + GPIO_INTERRUPT_TRIGGER_TYPE); ++ method &= ~(1 << gpio); ++ edges &= ~(1 << gpio); ++ type &= ~(1 << gpio); ++ ++ switch(irqtype) { ++ case IRQ_TYPE_EDGE_RISING: ++ method |= (GPIO_INTERRUPT_TRIGGER_METHOD_EDGE << gpio); ++ edges |= (GPIO_INTERRUPT_TRIGGER_EDGE_SINGLE << gpio); ++ type |= (GPIO_INTERRUPT_TRIGGER_TYPE_RISING << gpio); ++ break; ++ case IRQ_TYPE_EDGE_FALLING: ++ method |= (GPIO_INTERRUPT_TRIGGER_METHOD_EDGE << gpio); ++ edges |= (GPIO_INTERRUPT_TRIGGER_EDGE_SINGLE << gpio); ++ type |= (GPIO_INTERRUPT_TRIGGER_TYPE_FALLING << gpio); ++ break; ++ case IRQ_TYPE_EDGE_BOTH: ++ method |= (GPIO_INTERRUPT_TRIGGER_METHOD_EDGE << gpio); ++ edges |= (GPIO_INTERRUPT_TRIGGER_EDGE_BOTH << gpio); ++ break; ++ case IRQ_TYPE_LEVEL_LOW: ++ method |= (GPIO_INTERRUPT_TRIGGER_METHOD_LEVEL << gpio); ++ type |= (GPIO_INTERRUPT_TRIGGER_TYPE_LOW << gpio); ++ break; ++ case IRQ_TYPE_LEVEL_HIGH: ++ method |= (GPIO_INTERRUPT_TRIGGER_METHOD_LEVEL << gpio); ++ type |= (GPIO_INTERRUPT_TRIGGER_TYPE_HIGH << gpio); ++ break; ++ default: ++ printk(KERN_WARNING "No irq type\n"); ++ spin_unlock_irqrestore(&cchip->lock, flags); ++ return -EINVAL; ++ } ++ ++ __raw_writel(method, cchip->base + GPIO_INTERRUPT_TRIGGER_METHOD); ++ __raw_writel(edges, cchip->base + GPIO_INTERRUPT_TRIGGER_BOTH_EDGES); ++ __raw_writel(type, cchip->base + GPIO_INTERRUPT_TRIGGER_TYPE); ++ spin_unlock_irqrestore(&cchip->lock, flags); ++ ++ if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) ++ __irq_set_handler_locked(d->irq, handle_level_irq); ++ else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) ++ __irq_set_handler_locked(d->irq, handle_edge_irq); ++ ++ return 0; ++} ++ ++void __init cns3xxx_gpio_init(int gpio_base, int ngpio, ++ u32 base, int irq, int secondary_irq_base) ++{ ++ struct cns3xxx_gpio_chip *cchip; ++ struct irq_chip_generic *gc; ++ struct irq_chip_type *ct; ++ char gc_label[16]; ++ ++ if (cns3xxx_gpio_chip_count == ARRAY_SIZE(cns3xxx_gpio_chips)) ++ return; ++ ++ snprintf(gc_label, sizeof(gc_label), "cns3xxx_gpio%d", ++ cns3xxx_gpio_chip_count); ++ ++ cchip = cns3xxx_gpio_chips + cns3xxx_gpio_chip_count; ++ cchip->chip.label = kstrdup(gc_label, GFP_KERNEL); ++ cchip->chip.direction_input = cns3xxx_gpio_direction_input; ++ cchip->chip.get = cns3xxx_gpio_get; ++ cchip->chip.direction_output = cns3xxx_gpio_direction_output; ++ cchip->chip.set = cns3xxx_gpio_set; ++ cchip->chip.to_irq = cns3xxx_gpio_to_irq; ++ cchip->chip.base = gpio_base; ++ cchip->chip.ngpio = ngpio; ++ cchip->chip.can_sleep = 0; ++ spin_lock_init(&cchip->lock); ++ cchip->base = (void __iomem *)base; ++ cchip->secondary_irq_base = secondary_irq_base; ++ ++ BUG_ON(gpiochip_add(&cchip->chip) < 0); ++ cns3xxx_gpio_chip_count++; ++ ++ /* clear GPIO interrupts */ ++ __raw_writel(0xffff, cchip->base + GPIO_INTERRUPT_CLEAR); ++ ++ /* ++ * IRQ chip init ++ */ ++ gc = irq_alloc_generic_chip("cns3xxx_gpio_irq", 1, secondary_irq_base, ++ cchip->base, handle_edge_irq); ++ gc->private = cchip; ++ ++ ct = gc->chip_types; ++ ct->type = IRQ_TYPE_EDGE_FALLING; ++ ct->regs.ack = GPIO_INTERRUPT_CLEAR; ++ ct->regs.enable = GPIO_INTERRUPT_ENABLE; ++ ct->chip.irq_ack = irq_gc_ack_set_bit; ++ ct->chip.irq_enable = irq_gc_unmask_enable_reg; ++ ct->chip.irq_disable = irq_gc_mask_disable_reg; ++ ct->chip.irq_set_type = cns3xxx_gpio_irq_set_type; ++ ct->handler = handle_edge_irq; ++ ++ irq_setup_generic_chip(gc, IRQ_MSK(ngpio), IRQ_GC_INIT_MASK_CACHE, ++ IRQ_NOREQUEST, 0); ++ ++ irq_set_chained_handler(irq, cns3xxx_gpio_irq_handler); ++ irq_set_handler_data(irq, cchip); ++} +--- a/arch/arm/mach-cns3xxx/include/mach/gpio.h ++++ b/arch/arm/mach-cns3xxx/include/mach/gpio.h +@@ -1,98 +1,17 @@ + /* + * arch/arm/mach-cns3xxx/include/mach/gpio.h + * +- * CNS3xxx GPIO wrappers for arch-neutral GPIO calls +- * +- * Copyright 2011 Gateworks Corporation +- * Chris Lang +- * +- * Based on IXP implementation by Milan Svoboda +- * Based on PXA implementation by Philipp Zabel +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. + * + */ +- + #ifndef __ASM_ARCH_CNS3XXX_GPIO_H + #define __ASM_ARCH_CNS3XXX_GPIO_H + + #include +-#include +-#include +-#include /* cansleep wrappers */ +- +-#define NR_BUILTIN_GPIO 64 +- +-#define CNS3XXX_GPIO_IN 0x0 +-#define CNS3XXX_GPIO_OUT 0x1 +- +-#define CNS3XXX_GPIO_LO 0 +-#define CNS3XXX_GPIO_HI 1 +- +-#define CNS3XXX_GPIO_OUTPUT 0x00 +-#define CNS3XXX_GPIO_INPUT 0x04 +-#define CNS3XXX_GPIO_DIR 0x08 +-#define CNS3XXX_GPIO_SET 0x10 +-#define CNS3XXX_GPIO_CLEAR 0x14 +- +-static inline void gpio_line_get(u8 line, int *value) +-{ +- if (line < 32) +- *value = ((__raw_readl(CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_INPUT) >> line) & 0x1); +- else +- *value = ((__raw_readl(CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_INPUT) >> (line - 32)) & 0x1); +-} +- +-static inline void gpio_line_set(u8 line, int value) +-{ +- if (line < 32) { +- if (value) +- __raw_writel((1 << line), CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_SET); +- else +- __raw_writel((1 << line), CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_CLEAR); +- } else { +- if (value) +- __raw_writel((1 << line), CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_SET); +- else +- __raw_writel((1 << line), CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_CLEAR); +- } +-} +- +-static inline int gpio_get_value(unsigned gpio) +-{ +- if (gpio < NR_BUILTIN_GPIO) +- { +- int value; +- gpio_line_get(gpio, &value); +- return value; +- } +- else +- return __gpio_get_value(gpio); +-} +- +-static inline void gpio_set_value(unsigned gpio, int value) +-{ +- if (gpio < NR_BUILTIN_GPIO) +- gpio_line_set(gpio, value); +- else +- __gpio_set_value(gpio, value); +-} +- +-#define gpio_cansleep __gpio_cansleep + +-extern int gpio_to_irq(int gpio); +-extern int irq_to_gpio(int gpio); ++extern void __init cns3xxx_gpio_init(int gpio_base, int ngpio, ++ u32 base, int irq, int secondary_irq_base); + + #endif +--- a/arch/arm/mach-cns3xxx/laguna.c ++++ b/arch/arm/mach-cns3xxx/laguna.c +@@ -45,6 +45,7 @@ + #include + #include + #include ++#include + #include + #include "core.h" + #include "devices.h" +@@ -759,6 +760,10 @@ static int __init laguna_model_setup(voi + u8 pcie_bitmap = 0; + + printk("Running on Gateworks Laguna %s\n", laguna_info.model); ++ cns3xxx_gpio_init( 0, 32, CNS3XXX_GPIOA_BASE_VIRT, IRQ_CNS3XXX_GPIOA, ++ NR_IRQS_CNS3XXX); ++ cns3xxx_gpio_init(32, 32, CNS3XXX_GPIOB_BASE_VIRT, IRQ_CNS3XXX_GPIOB, ++ NR_IRQS_CNS3XXX + 32); + + if (strncmp(laguna_info.model, "GW", 2) == 0) { + if (laguna_info.config_bitmap & ETH0_LOAD) +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -366,7 +366,8 @@ config ARCH_CLPS711X + config ARCH_CNS3XXX + bool "Cavium Networks CNS3XXX family" + select CPU_V6K +- select ARCH_WANT_OPTIONAL_GPIOLIB ++ select ARCH_REQUIRE_GPIOLIB ++ select GENERIC_IRQ_CHIP + select GENERIC_CLOCKEVENTS + select ARM_GIC + select CLKDEV_LOOKUP +--- a/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h ++++ b/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h +@@ -627,7 +627,7 @@ int cns3xxx_cpu_clock(void); + + #if !defined(NR_IRQS) || (NR_IRQS < NR_IRQS_CNS3XXX) + #undef NR_IRQS +-#define NR_IRQS NR_IRQS_CNS3XXX ++#define NR_IRQS (NR_IRQS_CNS3XXX + 64) + #endif + + #endif /* __MACH_BOARD_CNS3XXX_H */