pinctrl: bcm2835: switch to GPIOLIB_IRQCHIP
It should be possible to use the GPIOLIB_IRQCHIP helper library with the BCM2835 driver since it is a pretty straight forward cascaded irqchip. The only difference from other drivers is that the BCM2835 has several banks for a single gpiochip, and each bank has a separate IRQ line. Instead of creating one gpiochip per bank, a single gpiochip covers all banks GPIO lines. This makes it necessary to resolve the bank ID in the IRQ handler. The GPIOLIB_IRQCHIP allows several IRQs to be cascaded off the same gpiochip by calling gpiochip_set_chained_irqchip() repeatedly, but we have been a bit short on examples for how this should be handled in practice, so this is intended as an example of how this can be achieved. The old code did not model the chip as a chained interrupt handler, but this patch also rectifies that situation. Cc: Stephen Warren <swarren@wwwdotorg.org> Tested-by: Stefan Wahren <stefan.wahren@i2se.com> Tested-by: Eric Anholt <eric@anholt.net> Acked-by: Eric Anholt <eric@anholt.net> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
Родитель
1ff91f0ae3
Коммит
85ae9e512f
|
@ -20,6 +20,7 @@ config PINCTRL_BCM2835
|
||||||
bool
|
bool
|
||||||
select PINMUX
|
select PINMUX
|
||||||
select PINCONF
|
select PINCONF
|
||||||
|
select GPIOLIB_IRQCHIP
|
||||||
|
|
||||||
config PINCTRL_IPROC_GPIO
|
config PINCTRL_IPROC_GPIO
|
||||||
bool "Broadcom iProc GPIO (with PINCONF) driver"
|
bool "Broadcom iProc GPIO (with PINCONF) driver"
|
||||||
|
|
|
@ -24,11 +24,9 @@
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/gpio/driver.h>
|
#include <linux/gpio/driver.h>
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/irqdesc.h>
|
#include <linux/irqdesc.h>
|
||||||
#include <linux/irqdomain.h>
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
@ -87,11 +85,6 @@ enum bcm2835_pinconf_pull {
|
||||||
#define BCM2835_PINCONF_UNPACK_PARAM(_conf_) ((_conf_) >> 16)
|
#define BCM2835_PINCONF_UNPACK_PARAM(_conf_) ((_conf_) >> 16)
|
||||||
#define BCM2835_PINCONF_UNPACK_ARG(_conf_) ((_conf_) & 0xffff)
|
#define BCM2835_PINCONF_UNPACK_ARG(_conf_) ((_conf_) & 0xffff)
|
||||||
|
|
||||||
struct bcm2835_gpio_irqdata {
|
|
||||||
struct bcm2835_pinctrl *pc;
|
|
||||||
int irqgroup;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bcm2835_pinctrl {
|
struct bcm2835_pinctrl {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
@ -102,16 +95,13 @@ struct bcm2835_pinctrl {
|
||||||
unsigned int irq_type[BCM2835_NUM_GPIOS];
|
unsigned int irq_type[BCM2835_NUM_GPIOS];
|
||||||
|
|
||||||
struct pinctrl_dev *pctl_dev;
|
struct pinctrl_dev *pctl_dev;
|
||||||
struct irq_domain *irq_domain;
|
|
||||||
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_IRQS];
|
int irq_group[BCM2835_NUM_IRQS];
|
||||||
spinlock_t irq_lock[BCM2835_NUM_BANKS];
|
spinlock_t irq_lock[BCM2835_NUM_BANKS];
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct lock_class_key gpio_lock_class;
|
|
||||||
|
|
||||||
/* pins are just named GPIO0..GPIO53 */
|
/* pins are just named GPIO0..GPIO53 */
|
||||||
#define BCM2835_GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a)
|
#define BCM2835_GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a)
|
||||||
static struct pinctrl_pin_desc bcm2835_gpio_pins[] = {
|
static struct pinctrl_pin_desc bcm2835_gpio_pins[] = {
|
||||||
|
@ -369,13 +359,6 @@ static int bcm2835_gpio_direction_output(struct gpio_chip *chip,
|
||||||
return pinctrl_gpio_direction_output(chip->base + offset);
|
return pinctrl_gpio_direction_output(chip->base + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bcm2835_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
|
||||||
{
|
|
||||||
struct bcm2835_pinctrl *pc = gpiochip_get_data(chip);
|
|
||||||
|
|
||||||
return irq_linear_revmap(pc->irq_domain, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct gpio_chip bcm2835_gpio_chip = {
|
static struct gpio_chip bcm2835_gpio_chip = {
|
||||||
.label = MODULE_NAME,
|
.label = MODULE_NAME,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
@ -386,13 +369,12 @@ static struct gpio_chip bcm2835_gpio_chip = {
|
||||||
.get_direction = bcm2835_gpio_get_direction,
|
.get_direction = bcm2835_gpio_get_direction,
|
||||||
.get = bcm2835_gpio_get,
|
.get = bcm2835_gpio_get,
|
||||||
.set = bcm2835_gpio_set,
|
.set = bcm2835_gpio_set,
|
||||||
.to_irq = bcm2835_gpio_to_irq,
|
|
||||||
.base = -1,
|
.base = -1,
|
||||||
.ngpio = BCM2835_NUM_GPIOS,
|
.ngpio = BCM2835_NUM_GPIOS,
|
||||||
.can_sleep = false,
|
.can_sleep = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int bcm2835_gpio_irq_handle_bank(struct bcm2835_pinctrl *pc,
|
static void bcm2835_gpio_irq_handle_bank(struct bcm2835_pinctrl *pc,
|
||||||
unsigned int bank, u32 mask)
|
unsigned int bank, u32 mask)
|
||||||
{
|
{
|
||||||
unsigned long events;
|
unsigned long events;
|
||||||
|
@ -405,34 +387,49 @@ static int bcm2835_gpio_irq_handle_bank(struct bcm2835_pinctrl *pc,
|
||||||
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;
|
||||||
|
/* FIXME: no clue why the code looks up the type here */
|
||||||
type = pc->irq_type[gpio];
|
type = pc->irq_type[gpio];
|
||||||
|
|
||||||
generic_handle_irq(irq_linear_revmap(pc->irq_domain, gpio));
|
generic_handle_irq(irq_linear_revmap(pc->gpio_chip.irqdomain,
|
||||||
|
gpio));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (events != 0);
|
static void bcm2835_gpio_irq_handler(struct irq_desc *desc)
|
||||||
}
|
|
||||||
|
|
||||||
static irqreturn_t bcm2835_gpio_irq_handler(int irq, void *dev_id)
|
|
||||||
{
|
{
|
||||||
struct bcm2835_gpio_irqdata *irqdata = dev_id;
|
struct gpio_chip *chip = irq_desc_get_handler_data(desc);
|
||||||
struct bcm2835_pinctrl *pc = irqdata->pc;
|
struct bcm2835_pinctrl *pc = gpiochip_get_data(chip);
|
||||||
int handled = 0;
|
struct irq_chip *host_chip = irq_desc_get_chip(desc);
|
||||||
|
int irq = irq_desc_get_irq(desc);
|
||||||
|
int group;
|
||||||
|
int i;
|
||||||
|
|
||||||
switch (irqdata->irqgroup) {
|
for (i = 0; i < ARRAY_SIZE(pc->irq); i++) {
|
||||||
|
if (pc->irq[i] == irq) {
|
||||||
|
group = pc->irq_group[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* This should not happen, every IRQ has a bank */
|
||||||
|
if (i == ARRAY_SIZE(pc->irq))
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
chained_irq_enter(host_chip, desc);
|
||||||
|
|
||||||
|
switch (group) {
|
||||||
case 0: /* IRQ0 covers GPIOs 0-27 */
|
case 0: /* IRQ0 covers GPIOs 0-27 */
|
||||||
handled = bcm2835_gpio_irq_handle_bank(pc, 0, 0x0fffffff);
|
bcm2835_gpio_irq_handle_bank(pc, 0, 0x0fffffff);
|
||||||
break;
|
break;
|
||||||
case 1: /* IRQ1 covers GPIOs 28-45 */
|
case 1: /* IRQ1 covers GPIOs 28-45 */
|
||||||
handled = bcm2835_gpio_irq_handle_bank(pc, 0, 0xf0000000) |
|
bcm2835_gpio_irq_handle_bank(pc, 0, 0xf0000000);
|
||||||
bcm2835_gpio_irq_handle_bank(pc, 1, 0x00003fff);
|
bcm2835_gpio_irq_handle_bank(pc, 1, 0x00003fff);
|
||||||
break;
|
break;
|
||||||
case 2: /* IRQ2 covers GPIOs 46-53 */
|
case 2: /* IRQ2 covers GPIOs 46-53 */
|
||||||
handled = bcm2835_gpio_irq_handle_bank(pc, 1, 0x003fc000);
|
bcm2835_gpio_irq_handle_bank(pc, 1, 0x003fc000);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return handled ? IRQ_HANDLED : IRQ_NONE;
|
chained_irq_exit(host_chip, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void __bcm2835_gpio_irq_config(struct bcm2835_pinctrl *pc,
|
static inline void __bcm2835_gpio_irq_config(struct bcm2835_pinctrl *pc,
|
||||||
|
@ -478,7 +475,8 @@ static void bcm2835_gpio_irq_config(struct bcm2835_pinctrl *pc,
|
||||||
|
|
||||||
static void bcm2835_gpio_irq_enable(struct irq_data *data)
|
static void bcm2835_gpio_irq_enable(struct irq_data *data)
|
||||||
{
|
{
|
||||||
struct bcm2835_pinctrl *pc = irq_data_get_irq_chip_data(data);
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||||
|
struct bcm2835_pinctrl *pc = gpiochip_get_data(chip);
|
||||||
unsigned gpio = irqd_to_hwirq(data);
|
unsigned gpio = irqd_to_hwirq(data);
|
||||||
unsigned offset = GPIO_REG_SHIFT(gpio);
|
unsigned offset = GPIO_REG_SHIFT(gpio);
|
||||||
unsigned bank = GPIO_REG_OFFSET(gpio);
|
unsigned bank = GPIO_REG_OFFSET(gpio);
|
||||||
|
@ -492,7 +490,8 @@ static void bcm2835_gpio_irq_enable(struct irq_data *data)
|
||||||
|
|
||||||
static void bcm2835_gpio_irq_disable(struct irq_data *data)
|
static void bcm2835_gpio_irq_disable(struct irq_data *data)
|
||||||
{
|
{
|
||||||
struct bcm2835_pinctrl *pc = irq_data_get_irq_chip_data(data);
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||||
|
struct bcm2835_pinctrl *pc = gpiochip_get_data(chip);
|
||||||
unsigned gpio = irqd_to_hwirq(data);
|
unsigned gpio = irqd_to_hwirq(data);
|
||||||
unsigned offset = GPIO_REG_SHIFT(gpio);
|
unsigned offset = GPIO_REG_SHIFT(gpio);
|
||||||
unsigned bank = GPIO_REG_OFFSET(gpio);
|
unsigned bank = GPIO_REG_OFFSET(gpio);
|
||||||
|
@ -598,7 +597,8 @@ static int __bcm2835_gpio_irq_set_type_enabled(struct bcm2835_pinctrl *pc,
|
||||||
|
|
||||||
static int bcm2835_gpio_irq_set_type(struct irq_data *data, unsigned int type)
|
static int bcm2835_gpio_irq_set_type(struct irq_data *data, unsigned int type)
|
||||||
{
|
{
|
||||||
struct bcm2835_pinctrl *pc = irq_data_get_irq_chip_data(data);
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||||
|
struct bcm2835_pinctrl *pc = gpiochip_get_data(chip);
|
||||||
unsigned gpio = irqd_to_hwirq(data);
|
unsigned gpio = irqd_to_hwirq(data);
|
||||||
unsigned offset = GPIO_REG_SHIFT(gpio);
|
unsigned offset = GPIO_REG_SHIFT(gpio);
|
||||||
unsigned bank = GPIO_REG_OFFSET(gpio);
|
unsigned bank = GPIO_REG_OFFSET(gpio);
|
||||||
|
@ -624,7 +624,8 @@ static int bcm2835_gpio_irq_set_type(struct irq_data *data, unsigned int type)
|
||||||
|
|
||||||
static void bcm2835_gpio_irq_ack(struct irq_data *data)
|
static void bcm2835_gpio_irq_ack(struct irq_data *data)
|
||||||
{
|
{
|
||||||
struct bcm2835_pinctrl *pc = irq_data_get_irq_chip_data(data);
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||||
|
struct bcm2835_pinctrl *pc = gpiochip_get_data(chip);
|
||||||
unsigned gpio = irqd_to_hwirq(data);
|
unsigned gpio = irqd_to_hwirq(data);
|
||||||
|
|
||||||
bcm2835_gpio_set_bit(pc, GPEDS0, gpio);
|
bcm2835_gpio_set_bit(pc, GPEDS0, gpio);
|
||||||
|
@ -667,10 +668,11 @@ static void bcm2835_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
|
||||||
unsigned offset)
|
unsigned offset)
|
||||||
{
|
{
|
||||||
struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
|
struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
|
||||||
|
struct gpio_chip *chip = &pc->gpio_chip;
|
||||||
enum bcm2835_fsel fsel = bcm2835_pinctrl_fsel_get(pc, offset);
|
enum bcm2835_fsel fsel = bcm2835_pinctrl_fsel_get(pc, offset);
|
||||||
const char *fname = bcm2835_functions[fsel];
|
const char *fname = bcm2835_functions[fsel];
|
||||||
int value = bcm2835_gpio_get_bit(pc, GPLEV0, offset);
|
int value = bcm2835_gpio_get_bit(pc, GPLEV0, offset);
|
||||||
int irq = irq_find_mapping(pc->irq_domain, offset);
|
int irq = irq_find_mapping(chip->irqdomain, offset);
|
||||||
|
|
||||||
seq_printf(s, "function %s in %s; irq %d (%s)",
|
seq_printf(s, "function %s in %s; irq %d (%s)",
|
||||||
fname, value ? "hi" : "lo",
|
fname, value ? "hi" : "lo",
|
||||||
|
@ -1016,21 +1018,6 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
|
||||||
pc->gpio_chip.parent = dev;
|
pc->gpio_chip.parent = dev;
|
||||||
pc->gpio_chip.of_node = np;
|
pc->gpio_chip.of_node = np;
|
||||||
|
|
||||||
pc->irq_domain = irq_domain_add_linear(np, BCM2835_NUM_GPIOS,
|
|
||||||
&irq_domain_simple_ops, NULL);
|
|
||||||
if (!pc->irq_domain) {
|
|
||||||
dev_err(dev, "could not create IRQ domain\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < BCM2835_NUM_GPIOS; i++) {
|
|
||||||
int irq = irq_create_mapping(pc->irq_domain, i);
|
|
||||||
irq_set_lockdep_class(irq, &gpio_lock_class);
|
|
||||||
irq_set_chip_and_handler(irq, &bcm2835_gpio_irq_chip,
|
|
||||||
handle_level_irq);
|
|
||||||
irq_set_chip_data(irq, pc);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -1051,34 +1038,35 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
|
||||||
spin_lock_init(&pc->irq_lock[i]);
|
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].irqgroup = i;
|
|
||||||
|
|
||||||
len = strlen(dev_name(pc->dev)) + 16;
|
|
||||||
name = devm_kzalloc(pc->dev, len, GFP_KERNEL);
|
|
||||||
if (!name)
|
|
||||||
return -ENOMEM;
|
|
||||||
snprintf(name, len, "%s:bank%d", dev_name(pc->dev), i);
|
|
||||||
|
|
||||||
err = devm_request_irq(dev, pc->irq[i],
|
|
||||||
bcm2835_gpio_irq_handler, IRQF_SHARED,
|
|
||||||
name, &pc->irq_data[i]);
|
|
||||||
if (err) {
|
|
||||||
dev_err(dev, "unable to request IRQ %d\n", pc->irq[i]);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = gpiochip_add_data(&pc->gpio_chip, pc);
|
err = gpiochip_add_data(&pc->gpio_chip, pc);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(dev, "could not add GPIO chip\n");
|
dev_err(dev, "could not add GPIO chip\n");
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = gpiochip_irqchip_add(&pc->gpio_chip, &bcm2835_gpio_irq_chip,
|
||||||
|
0, handle_level_irq, IRQ_TYPE_NONE);
|
||||||
|
if (err) {
|
||||||
|
dev_info(dev, "could not add irqchip\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < BCM2835_NUM_IRQS; i++) {
|
||||||
|
pc->irq[i] = irq_of_parse_and_map(np, i);
|
||||||
|
pc->irq_group[i] = i;
|
||||||
|
/*
|
||||||
|
* Use the same handler for all groups: this is necessary
|
||||||
|
* since we use one gpiochip to cover all lines - the
|
||||||
|
* irq handler then needs to figure out which group and
|
||||||
|
* bank that was firing the IRQ and look up the per-group
|
||||||
|
* and bank data.
|
||||||
|
*/
|
||||||
|
gpiochip_set_chained_irqchip(&pc->gpio_chip,
|
||||||
|
&bcm2835_gpio_irq_chip,
|
||||||
|
pc->irq[i],
|
||||||
|
bcm2835_gpio_irq_handler);
|
||||||
|
}
|
||||||
|
|
||||||
pc->pctl_dev = devm_pinctrl_register(dev, &bcm2835_pinctrl_desc, pc);
|
pc->pctl_dev = devm_pinctrl_register(dev, &bcm2835_pinctrl_desc, pc);
|
||||||
if (IS_ERR(pc->pctl_dev)) {
|
if (IS_ERR(pc->pctl_dev)) {
|
||||||
gpiochip_remove(&pc->gpio_chip);
|
gpiochip_remove(&pc->gpio_chip);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче