From 6c1c4facc387093dec4b7325442378b752378515 Mon Sep 17 00:00:00 2001 From: Selsinork Date: Sun, 17 Jun 2012 16:36:15 +0100 Subject: [PATCH 1/4] bcm_2708: don't use ARCH_NR_GPIOS bcm2708_gpio.c claims all gpios up to ARCH_NR_GPIOS this is a bug as the SoC only has 54 gpio pins and claiming all gpios prevents other drivers registering any additional gpios. Move the driver over to using BCM_NR_GPIOS instead --- arch/arm/mach-bcm2708/bcm2708_gpio.c | 14 +++++++------- arch/arm/mach-bcm2708/include/mach/gpio.h | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-bcm2708/bcm2708_gpio.c b/arch/arm/mach-bcm2708/bcm2708_gpio.c index 59df912fc84d70..aa0dec9b209841 100644 --- a/arch/arm/mach-bcm2708/bcm2708_gpio.c +++ b/arch/arm/mach-bcm2708/bcm2708_gpio.c @@ -74,7 +74,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 +112,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 +125,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)); @@ -169,7 +169,7 @@ static int bcm2708_irq_type(unsigned irq, unsigned trigger) unsigned gpio_field_offset = (offset - 32*gpio_bank); unsigned gpioren, gpiofen, gpiohen, gpiolen; - if (offset < 0 || offset >= ARCH_NR_GPIOS) + if (offset < 0 || offset >= BCM_NR_GPIOS) return -EINVAL; spin_lock_irqsave(&chip->irq_lock, flags); @@ -226,14 +226,14 @@ static void bcm2708_irq_handler(unsigned irq, struct irq_desc *desc) int offset; chip = list_entry(ptr, struct bcm2708_gpio, list); - for (gpio_bank = 0; gpio_bank < ARCH_NR_GPIOS/32; gpio_bank++) { + for (gpio_bank = 0; gpio_bank < BCM_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) + for_each_set_bit(offset, &pending, BCM_NR_GPIOS) generic_handle_irq(gpio_to_irq(offset+32*gpio_bank)); } } @@ -264,7 +264,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; diff --git a/arch/arm/mach-bcm2708/include/mach/gpio.h b/arch/arm/mach-bcm2708/include/mach/gpio.h index 526004ff8eafe0..5965d0c3e688d4 100644 --- a/arch/arm/mach-bcm2708/include/mach/gpio.h +++ b/arch/arm/mach-bcm2708/include/mach/gpio.h @@ -10,6 +10,7 @@ #define __ASM_ARCH_GPIO_H #define ARCH_NR_GPIOS 54 // number of gpio lines +#define BCM_NR_GPIOS 54 // number of gpio lines #include From f57e28262d788e2c9e5234e4d6eacc570a7508f2 Mon Sep 17 00:00:00 2001 From: Selsinork Date: Sun, 17 Jun 2012 16:40:24 +0100 Subject: [PATCH 2/4] bcm_2708: remove ARCH_NR_GPIOS ARCH_NR_GPIOS is a subsystem bug. Remove it. --- arch/arm/mach-bcm2708/include/mach/gpio.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-bcm2708/include/mach/gpio.h b/arch/arm/mach-bcm2708/include/mach/gpio.h index 5965d0c3e688d4..7a87089626ceb2 100644 --- a/arch/arm/mach-bcm2708/include/mach/gpio.h +++ b/arch/arm/mach-bcm2708/include/mach/gpio.h @@ -9,7 +9,6 @@ #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 From 229f8dcc0d69964924f1c2c39ce86818de3c363b Mon Sep 17 00:00:00 2001 From: Selsinork Date: Sun, 10 Jun 2012 17:48:45 +0100 Subject: [PATCH 3/4] bcm2708_gpio: enable gpio interrupts from Mrkva http://www.raspberrypi.org/phpBB3/viewtopic.php?f=44&t=7509 http://dev.mrkva.eu/rpi/linux-gpio-irq.patch this allows to enable interrupts for onboard gpio --- arch/arm/mach-bcm2708/bcm2708_gpio.c | 195 ++++++++++++---------- arch/arm/mach-bcm2708/include/mach/gpio.h | 17 +- arch/arm/mach-bcm2708/include/mach/irqs.h | 8 +- 3 files changed, 118 insertions(+), 102 deletions(-) diff --git a/arch/arm/mach-bcm2708/bcm2708_gpio.c b/arch/arm/mach-bcm2708/bcm2708_gpio.c index aa0dec9b209841..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) @@ -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 >= BCM_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 < BCM_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, BCM_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) { @@ -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 7a87089626ceb2..6e64bae6b4f236 100644 --- a/arch/arm/mach-bcm2708/include/mach/gpio.h +++ b/arch/arm/mach-bcm2708/include/mach/gpio.h @@ -12,7 +12,8 @@ #define BCM_NR_GPIOS 54 // number of gpio lines #include - +#include +#include #ifdef CONFIG_GPIOLIB @@ -31,18 +32,14 @@ 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 int irq_to_gpio(unsigned int irq) -{ - WARN_ON(1); - return -EINVAL; +static inline unsigned irq_to_gpio(unsigned irq) { + return (irq-GPIO_IRQ_START); } +static inline unsigned gpio_to_irq(unsigned gpio) { + return GPIO_IRQ_START+gpio; +} #endif /* CONFIG_GPIOLIB */ #endif 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_ */ From e6c795e25b3e30682a0b1d02d24ea1b177760598 Mon Sep 17 00:00:00 2001 From: Selsinork Date: Sun, 1 Jul 2012 14:52:30 +0100 Subject: [PATCH 4/4] bcm2708: allow gpio interrupts to actually work gpio_to_irq is provided as a generic function by gpiolib now, so we must override it with our own version to prevent an infinite recursive loop --- arch/arm/mach-bcm2708/include/mach/gpio.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-bcm2708/include/mach/gpio.h b/arch/arm/mach-bcm2708/include/mach/gpio.h index 6e64bae6b4f236..034d1699ce2d1e 100644 --- a/arch/arm/mach-bcm2708/include/mach/gpio.h +++ b/arch/arm/mach-bcm2708/include/mach/gpio.h @@ -40,6 +40,8 @@ static inline unsigned irq_to_gpio(unsigned irq) { static inline unsigned gpio_to_irq(unsigned gpio) { return GPIO_IRQ_START+gpio; } +#define gpio_to_irq gpio_to_irq + #endif /* CONFIG_GPIOLIB */ #endif