--- 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 */