mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 10:00:17 +00:00
pinctrl-bcm2835: Fix interrupt handling for GPIOs 28-31 and 46-53
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.
This commit is contained in:
@@ -47,6 +47,7 @@
|
|||||||
#define MODULE_NAME "pinctrl-bcm2835"
|
#define MODULE_NAME "pinctrl-bcm2835"
|
||||||
#define BCM2835_NUM_GPIOS 54
|
#define BCM2835_NUM_GPIOS 54
|
||||||
#define BCM2835_NUM_BANKS 2
|
#define BCM2835_NUM_BANKS 2
|
||||||
|
#define BCM2835_NUM_IRQS 3
|
||||||
|
|
||||||
#define BCM2835_PIN_BITMAP_SZ \
|
#define BCM2835_PIN_BITMAP_SZ \
|
||||||
DIV_ROUND_UP(BCM2835_NUM_GPIOS, sizeof(unsigned long) * 8)
|
DIV_ROUND_UP(BCM2835_NUM_GPIOS, sizeof(unsigned long) * 8)
|
||||||
@@ -88,13 +89,13 @@ enum bcm2835_pinconf_pull {
|
|||||||
|
|
||||||
struct bcm2835_gpio_irqdata {
|
struct bcm2835_gpio_irqdata {
|
||||||
struct bcm2835_pinctrl *pc;
|
struct bcm2835_pinctrl *pc;
|
||||||
int bank;
|
int irqgroup;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bcm2835_pinctrl {
|
struct bcm2835_pinctrl {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
int irq[BCM2835_NUM_BANKS];
|
int irq[BCM2835_NUM_IRQS];
|
||||||
|
|
||||||
/* note: locking assumes each bank will have its own unsigned long */
|
/* note: locking assumes each bank will have its own unsigned long */
|
||||||
unsigned long enabled_irq_map[BCM2835_NUM_BANKS];
|
unsigned long enabled_irq_map[BCM2835_NUM_BANKS];
|
||||||
@@ -105,7 +106,7 @@ struct bcm2835_pinctrl {
|
|||||||
struct gpio_chip gpio_chip;
|
struct gpio_chip gpio_chip;
|
||||||
struct pinctrl_gpio_range gpio_range;
|
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];
|
spinlock_t irq_lock[BCM2835_NUM_BANKS];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -391,17 +392,16 @@ static struct gpio_chip bcm2835_gpio_chip = {
|
|||||||
.can_sleep = false,
|
.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 long events;
|
||||||
unsigned offset;
|
unsigned offset;
|
||||||
unsigned gpio;
|
unsigned gpio;
|
||||||
unsigned int type;
|
unsigned int type;
|
||||||
|
|
||||||
events = bcm2835_gpio_rd(pc, GPEDS0 + bank * 4);
|
events = bcm2835_gpio_rd(pc, GPEDS0 + bank * 4);
|
||||||
|
events &= mask;
|
||||||
events &= pc->enabled_irq_map[bank];
|
events &= pc->enabled_irq_map[bank];
|
||||||
for_each_set_bit(offset, &events, 32) {
|
for_each_set_bit(offset, &events, 32) {
|
||||||
gpio = (32 * bank) + offset;
|
gpio = (32 * bank) + offset;
|
||||||
@@ -409,7 +409,30 @@ static irqreturn_t bcm2835_gpio_irq_handler(int irq, void *dev_id)
|
|||||||
|
|
||||||
generic_handle_irq(irq_linear_revmap(pc->irq_domain, gpio));
|
generic_handle_irq(irq_linear_revmap(pc->irq_domain, 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,
|
static inline void __bcm2835_gpio_irq_config(struct bcm2835_pinctrl *pc,
|
||||||
@@ -998,8 +1021,6 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
|
|||||||
for (i = 0; i < BCM2835_NUM_BANKS; i++) {
|
for (i = 0; i < BCM2835_NUM_BANKS; i++) {
|
||||||
unsigned long events;
|
unsigned long events;
|
||||||
unsigned offset;
|
unsigned offset;
|
||||||
int len;
|
|
||||||
char *name;
|
|
||||||
|
|
||||||
/* clear event detection flags */
|
/* clear event detection flags */
|
||||||
bcm2835_gpio_wr(pc, GPREN0 + i * 4, 0);
|
bcm2835_gpio_wr(pc, GPREN0 + i * 4, 0);
|
||||||
@@ -1014,10 +1035,15 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
|
|||||||
for_each_set_bit(offset, &events, 32)
|
for_each_set_bit(offset, &events, 32)
|
||||||
bcm2835_gpio_wr(pc, GPEDS0 + i * 4, BIT(offset));
|
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[i] = irq_of_parse_and_map(np, i);
|
||||||
pc->irq_data[i].pc = pc;
|
pc->irq_data[i].pc = pc;
|
||||||
pc->irq_data[i].bank = i;
|
pc->irq_data[i].irqgroup = i;
|
||||||
spin_lock_init(&pc->irq_lock[i]);
|
|
||||||
|
|
||||||
len = strlen(dev_name(pc->dev)) + 16;
|
len = strlen(dev_name(pc->dev)) + 16;
|
||||||
name = devm_kzalloc(pc->dev, len, GFP_KERNEL);
|
name = devm_kzalloc(pc->dev, len, GFP_KERNEL);
|
||||||
@@ -1074,6 +1100,7 @@ static struct platform_driver bcm2835_pinctrl_driver = {
|
|||||||
.remove = bcm2835_pinctrl_remove,
|
.remove = bcm2835_pinctrl_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = MODULE_NAME,
|
.name = MODULE_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
.of_match_table = bcm2835_pinctrl_match,
|
.of_match_table = bcm2835_pinctrl_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user