Skip to content

Commit

Permalink
pinctrl-bcm2835: Fix interrupt handling for GPIOs 28-31 and 46-53
Browse files Browse the repository at this point in the history
Contrary to the documentation, the BCM2835 GPIO controller actually has
four interrupt lines - one each for the three IRQ groups and one common. Rather
confusingly, the GPIO interrupt groups don't correspond directly with the GPIO
control banks. Instead, GPIOs 0-27 generate IRQ GPIO0, 28-45 GPIO1 and
46-53 GPIO2.

Awkwardly, the GPIOS for IRQ GPIO1 straddle two 32-entry GPIO banks, so it is
cleaner to split out a function to process the interrupts for a single GPIO
bank.

This bug has only just been observed because GPIOs above 27 can only be
accessed on an old Raspberry Pi with the optional P5 header fitted, where
the pins are often used for I2S instead.
  • Loading branch information
Phil Elwell authored and popcornmix committed Jun 7, 2015
1 parent 9bdca5d commit 9276516
Showing 1 changed file with 39 additions and 12 deletions.
51 changes: 39 additions & 12 deletions drivers/pinctrl/pinctrl-bcm2835.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#define MODULE_NAME "pinctrl-bcm2835"
#define BCM2835_NUM_GPIOS 54
#define BCM2835_NUM_BANKS 2
#define BCM2835_NUM_IRQS 3

#define BCM2835_PIN_BITMAP_SZ \
DIV_ROUND_UP(BCM2835_NUM_GPIOS, sizeof(unsigned long) * 8)
Expand Down Expand Up @@ -88,13 +89,13 @@ enum bcm2835_pinconf_pull {

struct bcm2835_gpio_irqdata {
struct bcm2835_pinctrl *pc;
int bank;
int irqgroup;
};

struct bcm2835_pinctrl {
struct device *dev;
void __iomem *base;
int irq[BCM2835_NUM_BANKS];
int irq[BCM2835_NUM_IRQS];

/* note: locking assumes each bank will have its own unsigned long */
unsigned long enabled_irq_map[BCM2835_NUM_BANKS];
Expand All @@ -105,7 +106,7 @@ struct bcm2835_pinctrl {
struct gpio_chip gpio_chip;
struct pinctrl_gpio_range gpio_range;

struct bcm2835_gpio_irqdata irq_data[BCM2835_NUM_BANKS];
struct bcm2835_gpio_irqdata irq_data[BCM2835_NUM_IRQS];
spinlock_t irq_lock[BCM2835_NUM_BANKS];
};

Expand Down Expand Up @@ -394,17 +395,16 @@ static struct gpio_chip bcm2835_gpio_chip = {
.can_sleep = false,
};

static irqreturn_t bcm2835_gpio_irq_handler(int irq, void *dev_id)
static int bcm2835_gpio_irq_handle_bank(struct bcm2835_pinctrl *pc,
unsigned int bank, u32 mask)
{
struct bcm2835_gpio_irqdata *irqdata = dev_id;
struct bcm2835_pinctrl *pc = irqdata->pc;
int bank = irqdata->bank;
unsigned long events;
unsigned offset;
unsigned gpio;
unsigned int type;

events = bcm2835_gpio_rd(pc, GPEDS0 + bank * 4);
events &= mask;
events &= pc->enabled_irq_map[bank];
for_each_set_bit(offset, &events, 32) {
gpio = (32 * bank) + offset;
Expand All @@ -420,7 +420,30 @@ static irqreturn_t bcm2835_gpio_irq_handler(int irq, void *dev_id)
if (type & IRQ_TYPE_LEVEL_MASK)
bcm2835_gpio_set_bit(pc, GPEDS0, gpio);
}
return events ? IRQ_HANDLED : IRQ_NONE;

return (events != 0);
}

static irqreturn_t bcm2835_gpio_irq_handler(int irq, void *dev_id)
{
struct bcm2835_gpio_irqdata *irqdata = dev_id;
struct bcm2835_pinctrl *pc = irqdata->pc;
int handled = 0;

switch (irqdata->irqgroup) {
case 0: /* IRQ0 covers GPIOs 0-27 */
handled = bcm2835_gpio_irq_handle_bank(pc, 0, 0x0fffffff);
break;
case 1: /* IRQ1 covers GPIOs 28-45 */
handled = bcm2835_gpio_irq_handle_bank(pc, 0, 0xf0000000) |
bcm2835_gpio_irq_handle_bank(pc, 1, 0x00003fff);
break;
case 2: /* IRQ2 covers GPIOs 46-53 */
handled = bcm2835_gpio_irq_handle_bank(pc, 1, 0x003fc000);
break;
}

return handled ? IRQ_HANDLED : IRQ_NONE;
}

static inline void __bcm2835_gpio_irq_config(struct bcm2835_pinctrl *pc,
Expand Down Expand Up @@ -992,8 +1015,6 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
for (i = 0; i < BCM2835_NUM_BANKS; i++) {
unsigned long events;
unsigned offset;
int len;
char *name;

/* clear event detection flags */
bcm2835_gpio_wr(pc, GPREN0 + i * 4, 0);
Expand All @@ -1008,10 +1029,15 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
for_each_set_bit(offset, &events, 32)
bcm2835_gpio_wr(pc, GPEDS0 + i * 4, BIT(offset));

spin_lock_init(&pc->irq_lock[i]);
}

for (i = 0; i < BCM2835_NUM_IRQS; i++) {
int len;
char *name;
pc->irq[i] = irq_of_parse_and_map(np, i);
pc->irq_data[i].pc = pc;
pc->irq_data[i].bank = i;
spin_lock_init(&pc->irq_lock[i]);
pc->irq_data[i].irqgroup = i;

len = strlen(dev_name(pc->dev)) + 16;
name = devm_kzalloc(pc->dev, len, GFP_KERNEL);
Expand Down Expand Up @@ -1069,6 +1095,7 @@ static struct platform_driver bcm2835_pinctrl_driver = {
.remove = bcm2835_pinctrl_remove,
.driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
.of_match_table = bcm2835_pinctrl_match,
},
};
Expand Down

0 comments on commit 9276516

Please sign in to comment.