Skip to content

Commit

Permalink
Merge pull request #1 from selsinork/rpi-3.2.21-gpio-interrupt
Browse files Browse the repository at this point in the history
Rpi 3.2.21 gpio interrupt
  • Loading branch information
bootc committed Jul 1, 2012
2 parents a1f8d7a + e6c795e commit 15a0e62
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 106 deletions.
203 changes: 108 additions & 95 deletions arch/arm/mach-bcm2708/bcm2708_gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/list.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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);
Expand Down Expand Up @@ -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));
Expand All @@ -125,121 +120,137 @@ 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<<gpio_field_offset, gpio->base + GPIOSET(gpio_bank));
else
writel(1<<gpio_field_offset, gpio->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<<gpio_field_offset);
else
gpioren &= ~(1<<gpio_field_offset);
if (trigger & (IRQ_TYPE_EDGE_FALLING))
gpiofen |= (1<<gpio_field_offset);
else
gpiofen &= ~(1<<gpio_field_offset);
if (trigger & (IRQ_TYPE_LEVEL_HIGH))
gpiohen |= (1<<gpio_field_offset);
else
gpiohen &= ~(1<<gpio_field_offset);
if (trigger & (IRQ_TYPE_LEVEL_LOW))
gpiolen |= (1<<gpio_field_offset);
else
gpiolen &= ~(1<<gpio_field_offset);
static void bcm2708_gpio_irq_mask(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));

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)
{
Expand All @@ -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;
Expand All @@ -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;
Expand Down
19 changes: 9 additions & 10 deletions arch/arm/mach-bcm2708/include/mach/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <asm-generic/gpio.h>

#include <mach/platform.h>
#include <mach/irqs.h>

#ifdef CONFIG_GPIOLIB

Expand All @@ -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 */

Expand Down
8 changes: 7 additions & 1 deletion arch/arm/mach-bcm2708/include/mach/irqs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */

0 comments on commit 15a0e62

Please sign in to comment.