I tryed to connect two can-controller to a RaspberryPi and had problems with the interrupts.
I started in this post: http://www.raspberrypi.org/phpBB3/viewt ... =44&t=7027
but it looks like this topic is worth to make an own post.
I found out, that a second interrupt is not handled, when it occures during the low
time of the first interrupt.
I made some tests and found out some interresting things.
The gpio-interrupt handler is programmed in:
/usr/src/linux/arch/arm/mach-bcm2708/bcm2708_gpio.c
Code: Select all
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);
}
setup_irq(IRQ_GPIO3, &bcm2708_gpio_irq);
}
That means there is a "BCM2708 GPIO catchall handler" that is executed, when
interrupt IRQ_GPIO3 is fired.
This handler reads the GPIOEDS registers and fires a generic interrupt for each set bit.
I found in the Broadcom manual, that there are 54 GPIOs organized in two banks and
3 interrupt lines, one for each bank and one in common.
Unfortunatly it is not said, how these banks are organized.
I assumed, that both banks have 27 GPIOs, but maybe it is different. In the schematic
for the RPi, there are two blocks, one with GPIO0 - GPIO27 (that is 28 GPIOs) and
the second with GPIO28 - GPIO53 (that is 26 GPIOs).
At another part of the manual, there is a table that shows 4 GPIO interrupts GPIO0 to GPIO3.
First thing I tested was, to change the interrupt source for the GPIO catchall handler and to test
it with an interrupt on GPIO17 and GPIO31 (from P5 header)
Code: Select all
...
setup_irq(IRQ_GPIO0, &bcm2708_gpio_irq);
//setup_irq(IRQ_GPIO1, &bcm2708_gpio_irq);
//setup_irq(IRQ_GPIO2, &bcm2708_gpio_irq);
//setup_irq(IRQ_GPIO3, &bcm2708_gpio_irq);
...
GPIO17 fires IRQ_GPIO0 or IRQ_GPIO3. If both are active, only IRQ_GPIO0 is fired.
GPIO31 fires IRQ_GPIO1 or IRQ_GPIO3. If both are active, only IRQ_GPIO1 is fired.
That means, IRQ_GPIO0 is the interrupt for bank0, IRQ_GPIO1 is the interrupt for bank1 and
IRQ_GPIO3 is the interrupt for both banks in common.
Then I found a bug in the handler. At the beginning of the handler, the GPIOEDS register is read.
Then there are generic interrupts fired for each set bit and after that, the whole register is cleared.
This causes a timing problem. All interrupts that appear in the short time between reading the
register and clearing the register will be cleared without beeing handled.
So I changed the code a little bit, now each bit is cleared seperatly.
Code: Select all
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(1<<i, __io_address(GPIO_BASE) + GPIOEDS(bank));
}
}
return IRQ_HANDLED;
}
and check if all bits are cleared, before returning IRQ_HANDLED.
A second thing I tryed was to make two interrupt handlers, one for each bank.
Code: Select all
#define GPIO_BANK0_SIZE 27 //estimated size
static irqreturn_t bcm2708_gpio_interrupt_1(int irq, void *dev_id)
{
unsigned long edsr;
unsigned bank;
int i;
unsigned gpio;
//might be nicer code when reading out 64bit at once if possible
bank = 0;
i = GPIO_BANK0_SIZE+1;
edsr = readl(__io_address(GPIO_BASE) + GPIOEDS(bank));
for_each_set_bit_from(i, &edsr, 32) {
gpio = i + bank * 32;
generic_handle_irq(gpio_to_irq(gpio));
writel(1<<i,__io_address(GPIO_BASE) + GPIOEDS(bank));
}
// writel(GPIO_BANK1_MASK, __io_address(GPIO_BASE) + GPIOEDS(bank));
// bank = 1;
// i = 0;
// 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 irqreturn_t bcm2708_gpio_interrupt_0(int irq, void *dev_id)
{
unsigned long edsr;
unsigned bank;
int i;
unsigned gpio;
bank = 0;
edsr = readl(__io_address(GPIO_BASE) + GPIOEDS(bank));
for_each_set_bit(i, &edsr, GPIO_BANK0_SIZE) {
gpio = i + bank * 32;
generic_handle_irq(gpio_to_irq(gpio));
writel(1<<i,__io_address(GPIO_BASE) + GPIOEDS(bank));
}
// writel(GPIO_BANK0_MASK, __io_address(GPIO_BASE) + GPIOEDS(bank));
return IRQ_HANDLED;
}
static struct irqaction bcm2708_gpio_irq_1 = {
.name = "BCM2708 GPIO catch bank 1 handler",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = bcm2708_gpio_interrupt_1,
};
static struct irqaction bcm2708_gpio_irq_0 = {
.name = "BCM2708 GPIO catch bank 0 handler",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = bcm2708_gpio_interrupt_0,
};
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);
}
setup_irq(IRQ_GPIO1, &bcm2708_gpio_irq_1);
setup_irq(IRQ_GPIO0, &bcm2708_gpio_irq_0);
}
A third thing I want to have a look at is the generic_handle_irq().
Perhaps it is possible to connect the my interrupt service routine (MCP2515)
directly to GPIO_IRQ0 and GPIO_IRQ1 and not through the generic_handle.
Maybe it is much faster.
I hope we can improve the interrupt behaviour a bit more.
Greetings maddin