diff --git a/arch/arm/mach-bcm2708/bcm2708_gpio.c b/arch/arm/mach-bcm2708/bcm2708_gpio.c index 59df912fc84d70..6230c2ead04130 100644 --- a/arch/arm/mach-bcm2708/bcm2708_gpio.c +++ b/arch/arm/mach-bcm2708/bcm2708_gpio.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,7 @@ #define BCM_GPIO_DRIVER_NAME "bcm2708_gpio" #define DRIVER_NAME BCM_GPIO_DRIVER_NAME -#define BCM_GPIO_USE_IRQ 0 +#define BCM_GPIO_USE_IRQ 1 #define GPIOFSEL(x) (0x00+(x)*4) #define GPIOSET(x) (0x1c+(x)*4) @@ -49,20 +50,14 @@ enum { GPIO_FSEL_INPUT, GPIO_FSEL_OUTPUT, * the IRQ code simpler. */ static DEFINE_SPINLOCK(lock); /* GPIO registers */ -static DEFINE_SPINLOCK(irq_lock); /* IRQ registers */ struct bcm2708_gpio { - /* We use a list of bcm2708_gpio structs for each trigger IRQ in the main - * interrupts controller of the system. We need this to support systems - * in which more that one bcm2708s are connected to the same IRQ. The ISR - * interates through this list to find the source of the interrupt. - */ struct list_head list; - void __iomem *base; - unsigned irq_base; struct gpio_chip gc; + unsigned long rising; + unsigned long falling; }; static int bcm2708_set_function(struct gpio_chip *gc, unsigned offset, int function) @@ -74,7 +69,7 @@ static int bcm2708_set_function(struct gpio_chip *gc, unsigned offset, int funct unsigned gpio_field_offset = (offset - 10*gpio_bank) * 3; //printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set_function %p (%d,%d)\n", gc, offset, function); - if (offset >= ARCH_NR_GPIOS) + if (offset >= BCM_NR_GPIOS) return -EINVAL; spin_lock_irqsave(&lock, flags); @@ -112,7 +107,7 @@ static int bcm2708_gpio_get(struct gpio_chip *gc, unsigned offset) unsigned gpio_field_offset = (offset - 32*gpio_bank); unsigned lev; - if (offset >= ARCH_NR_GPIOS) + if (offset >= BCM_NR_GPIOS) return 0; lev = readl(gpio->base + GPIOLEV(gpio_bank)); //printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_get %p (%d)=%d\n", gc, offset, 0x1 & (lev>>gpio_field_offset)); @@ -125,7 +120,7 @@ static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value) unsigned gpio_bank = offset/32; unsigned gpio_field_offset = (offset - 32*gpio_bank); //printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set %p (%d=%d)\n", gc, offset, value); - if (offset >= ARCH_NR_GPIOS) + if (offset >= BCM_NR_GPIOS) return; if (value) writel(1<base + GPIOSET(gpio_bank)); @@ -133,113 +128,129 @@ static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value) writel(1<base + GPIOCLR(gpio_bank)); } -/* +/************************************************************************************************************************* * bcm2708 GPIO IRQ */ -#if BCM_GPIO_USE_IRQ -static void bcm2708_irq_disable(unsigned irq) -{ - struct bcm2708_gpio *chip = get_irq_chip_data(irq); - //int offset = irq - gpio->irq_base; - unsigned long flags; - spin_lock_irqsave(&chip->irq_lock, flags); - // disable gpio interrupts here - spin_unlock_irqrestore(&chip->irq_lock, flags); -} +#if BCM_GPIO_USE_IRQ -static void bcm2708_irq_enable(unsigned irq) -{ - struct bcm2708_gpio *chip = get_irq_chip_data(irq); - //int offset = irq - chip->irq_base; - unsigned long flags; +#define IRQ_TO_GPIO(x) irq_to_gpio(x) - spin_lock_irqsave(&chip->irq_lock, flags); - // enable gpio interrupts here - spin_unlock_irqrestore(&chip->irq_lock, flags); +static int bcm2708_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) { + return gpio_to_irq(gpio); } -static int bcm2708_irq_type(unsigned irq, unsigned trigger) -{ - struct bcm2708_gpio *chip = get_irq_chip_data(irq); - int offset = irq - chip->irq_base; - unsigned long flags; - unsigned gpio_bank = offset/32; - unsigned gpio_field_offset = (offset - 32*gpio_bank); - unsigned gpioren, gpiofen, gpiohen, gpiolen; - if (offset < 0 || offset >= ARCH_NR_GPIOS) +static int bcm2708_gpio_irq_set_type(struct irq_data *d, unsigned type) { + unsigned irq = d->irq; + struct bcm2708_gpio *gpio=irq_get_chip_data(irq); + + if (type & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) return -EINVAL; - spin_lock_irqsave(&chip->irq_lock, flags); + if (type & IRQ_TYPE_EDGE_RISING) { + gpio->rising |= (1 << IRQ_TO_GPIO(irq)); + } else { + gpio->rising &= ~(1 << IRQ_TO_GPIO(irq)); + } - gpioren = readl(chip->base + GPIOREN(gpio_bank)); - gpiofen = readl(chip->base + GPIOFEN(gpio_bank)); - gpiohen = readl(chip->base + GPIOHEN(gpio_bank)); - gpiolen = readl(chip->base + GPIOLEN(gpio_bank)); + if (type & IRQ_TYPE_EDGE_FALLING) { + gpio->falling |= (1 << IRQ_TO_GPIO(irq)); + } else { + gpio->falling &= ~(1 << IRQ_TO_GPIO(irq)); + } + return 0; +} - if (trigger & (IRQ_TYPE_EDGE_RISING)) - gpioren |= (1<irq; + struct bcm2708_gpio *gpio=irq_get_chip_data(irq); + unsigned gn = IRQ_TO_GPIO(irq); + unsigned gb = gn/32; + unsigned long rising=readl(gpio->base + GPIOREN(gb)); + unsigned long falling=readl(gpio->base + GPIOFEN(gb)); - writel(gpioren, chip->base + GPIOREN(gpio_bank)); - writel(gpiofen, chip->base + GPIOFEN(gpio_bank)); - writel(gpiohen, chip->base + GPIOHEN(gpio_bank)); - writel(gpiolen, chip->base + GPIOLEN(gpio_bank)); + writel(rising & ~(1 << gn), gpio->base + GPIOREN(gb)); + writel(falling & ~(1 << gn), gpio->base + GPIOFEN(gb)); +} - spin_unlock_irqrestore(&chip->irq_lock, flags); +static void bcm2708_gpio_irq_unmask(struct irq_data *d) { + unsigned irq = d->irq; + struct bcm2708_gpio *gpio=irq_get_chip_data(irq); + unsigned gn = IRQ_TO_GPIO(irq); + unsigned gb = gn/32; + unsigned long rising=readl(gpio->base + GPIOREN(gb)); + unsigned long falling=readl(gpio->base + GPIOFEN(gb)); - return 0; + gn=gn%32; + + writel(1 << gn, gpio->base + GPIOEDS(gb)); + + if(gpio->rising & (1 << gn)) { + writel(rising | (1 << gn), gpio->base + GPIOREN(gb)); + } else { + writel(rising & ~(1 << gn), gpio->base + GPIOREN(gb)); + } + + if(gpio->falling & (1 << gn)) { + writel(falling | (1 << gn), gpio->base + GPIOFEN(gb)); + } else { + writel(falling & ~(1 << gn), gpio->base + GPIOFEN(gb)); + } } static struct irq_chip bcm2708_irqchip = { .name = "GPIO", - .enable = bcm2708_irq_enable, - .disable = bcm2708_irq_disable, - .set_type = bcm2708_irq_type, + .irq_enable = bcm2708_gpio_irq_unmask, + .irq_disable = bcm2708_gpio_irq_mask, + .irq_unmask = bcm2708_gpio_irq_unmask, + .irq_mask = bcm2708_gpio_irq_mask, + .irq_set_type = bcm2708_gpio_irq_set_type, }; -static void bcm2708_irq_handler(unsigned irq, struct irq_desc *desc) -{ - struct list_head *chip_list = get_irq_data(irq); - struct list_head *ptr; - struct bcm2708_gpio *chip; - unsigned gpio_bank; - - desc->chip->ack(irq); - list_for_each(ptr, chip_list) { - unsigned long pending; - int offset; - - chip = list_entry(ptr, struct bcm2708_gpio, list); - for (gpio_bank = 0; gpio_bank < ARCH_NR_GPIOS/32; gpio_bank++) { - pending = readl(chip->base + GPIOEDS(gpio_bank)); - writel(pending, chip->base + GPIOEDS(gpio_bank)); - - if (pending == 0) - continue; - - for_each_set_bit(offset, &pending, ARCH_NR_GPIOS) - generic_handle_irq(gpio_to_irq(offset+32*gpio_bank)); +static irqreturn_t bcm2708_gpio_interrupt(int irq, void *dev_id) { + unsigned long edsr; + unsigned bank; + int i; + unsigned gpio; + for(bank=0; bank<=1; bank++) { + edsr=readl(__io_address(GPIO_BASE)+GPIOEDS(bank)); + for_each_set_bit(i, &edsr, 32) { + gpio=i+bank*32; + generic_handle_irq(gpio_to_irq(gpio)); } + writel(0xffffffff, __io_address(GPIO_BASE)+GPIOEDS(bank)); + } + return IRQ_HANDLED; +} + +static struct irqaction bcm2708_gpio_irq = { + .name = "BCM2708 GPIO catchall handler", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = bcm2708_gpio_interrupt, +}; + + +static void bcm2708_gpio_irq_init(struct bcm2708_gpio *ucb) { + unsigned irq; + + ucb->gc.to_irq=bcm2708_gpio_to_irq; + + for (irq=GPIO_IRQ_START; irq<(GPIO_IRQ_START+GPIO_IRQS); irq++) { + irq_set_chip_data(irq, ucb); + irq_set_chip(irq, &bcm2708_irqchip); + set_irq_flags(irq, IRQF_VALID); } - desc->chip->unmask(irq); + setup_irq(IRQ_GPIO3, &bcm2708_gpio_irq); } -#endif /* #if BCM_GPIO_USE_IRQ */ + +#else + +static void bcm2708_gpio_irq_init(struct bcm2708_gpio *ucb) { +} + +#endif /* #if BCM_GPIO_USE_IRQ ******************************************************************************************************************/ static int bcm2708_gpio_probe(struct platform_device *dev) { @@ -264,7 +275,7 @@ static int bcm2708_gpio_probe(struct platform_device *dev) ucb->gc.label = "bcm2708_gpio"; ucb->gc.base = 0; - ucb->gc.ngpio = ARCH_NR_GPIOS; + ucb->gc.ngpio = BCM_NR_GPIOS; ucb->gc.owner = THIS_MODULE; ucb->gc.direction_input = bcm2708_gpio_dir_in; @@ -273,6 +284,8 @@ static int bcm2708_gpio_probe(struct platform_device *dev) ucb->gc.set = bcm2708_gpio_set; ucb->gc.can_sleep = 0; + bcm2708_gpio_irq_init(ucb); + err = gpiochip_add(&ucb->gc); if (err) goto err; diff --git a/arch/arm/mach-bcm2708/include/mach/gpio.h b/arch/arm/mach-bcm2708/include/mach/gpio.h index 526004ff8eafe0..034d1699ce2d1e 100644 --- a/arch/arm/mach-bcm2708/include/mach/gpio.h +++ b/arch/arm/mach-bcm2708/include/mach/gpio.h @@ -9,10 +9,11 @@ #ifndef __ASM_ARCH_GPIO_H #define __ASM_ARCH_GPIO_H -#define ARCH_NR_GPIOS 54 // number of gpio lines +#define BCM_NR_GPIOS 54 // number of gpio lines #include - +#include +#include #ifdef CONFIG_GPIOLIB @@ -31,17 +32,15 @@ static inline int gpio_cansleep(unsigned gpio) return __gpio_cansleep(gpio); } -static inline int gpio_to_irq(unsigned gpio) -{ - WARN_ON(1); - return -ENOSYS; + +static inline unsigned irq_to_gpio(unsigned irq) { + return (irq-GPIO_IRQ_START); } -static inline int irq_to_gpio(unsigned int irq) -{ - WARN_ON(1); - return -EINVAL; +static inline unsigned gpio_to_irq(unsigned gpio) { + return GPIO_IRQ_START+gpio; } +#define gpio_to_irq gpio_to_irq #endif /* CONFIG_GPIOLIB */ diff --git a/arch/arm/mach-bcm2708/include/mach/irqs.h b/arch/arm/mach-bcm2708/include/mach/irqs.h index f59d474bca8407..e8bb0686811ccd 100644 --- a/arch/arm/mach-bcm2708/include/mach/irqs.h +++ b/arch/arm/mach-bcm2708/include/mach/irqs.h @@ -185,6 +185,12 @@ #define FIQ_PENDING1 INT_PENDING1 #define FIQ_PENDING2 INT_PENDING2 -#define NR_IRQS (64 + 21) +#define HARD_IRQS (64 + 21) +#define GPIO_IRQ_START HARD_IRQS + +#define GPIO_IRQS 32*5 + +#define NR_IRQS HARD_IRQS+GPIO_IRQS + #endif /* _BCM2708_IRQS_H_ */