[PATCH 1/2] gpio add interrupt support on pca953x
Marc Zyngier
maz at misterjones.org
Tue Dec 22 08:54:59 EST 2009
Eric, Haojian,
I indeed have something very similar, except it uses a threaded
interrupt handler, and is slightly more configurable.
I'll post the patch in a few hours, as I get back home.
Thanks,
M.
On Tue, 22 Dec 2009 21:41:51 +0800
Eric Miao <eric.y.miao at gmail.com> wrote:
> Haojian,
>
> Marc Zyngier was doing the similar thing, and submitted a patch to
> Andrew Morton already, but I prefer a full-functional IRQ chip based
> solution, so you may want to work with him together on this.
>
> Marc,
>
> Do you have the link to your patch?
>
> - eric
>
> On Tue, Dec 22, 2009 at 8:29 PM, Haojian Zhuang
> <haojian.zhuang at gmail.com> wrote:
> > From b8cc5f82ae9a1883ee4d6fa453bed63d4431b9e4 Mon Sep 17 00:00:00 2001
> > From: Haojian Zhuang <haojian.zhuang at marvell.com>
> > Date: Tue, 22 Dec 2009 15:08:41 -0500
> > Subject: [PATCH] gpio: add interrupt support on pca953x
> >
> > Support interrupt on pca953x gpio expander.
> >
> > Signed-off-by: Haojian Zhuang <haojian.zhuang at marvell.com>
> > ---
> > drivers/gpio/pca953x.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++-
> > 1 files changed, 119 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c
> > index 6a2fb3f..2d6b9e8 100644
> > --- a/drivers/gpio/pca953x.c
> > +++ b/drivers/gpio/pca953x.c
> > @@ -16,6 +16,7 @@
> > #include <linux/gpio.h>
> > #include <linux/i2c.h>
> > #include <linux/i2c/pca953x.h>
> > +#include <linux/irq.h>
> > #ifdef CONFIG_OF_GPIO
> > #include <linux/of_platform.h>
> > #include <linux/of_gpio.h>
> > @@ -50,12 +51,19 @@ MODULE_DEVICE_TABLE(i2c, pca953x_id);
> >
> > struct pca953x_chip {
> > unsigned gpio_start;
> > + unsigned irq_start;
> > uint16_t reg_output;
> > uint16_t reg_direction;
> > + uint16_t irq_mask;
> > + uint16_t irq_falling_edge;
> > + uint16_t irq_rising_edge;
> > + uint16_t last_input;
> >
> > - struct i2c_client *client;
> > struct pca953x_platform_data *dyn_pdata;
> > - struct gpio_chip gpio_chip;
> > + struct i2c_client *client;
> > + struct gpio_chip gpio_chip;
> > + struct mutex irq_lock;
> > + struct work_struct irq_work;
> > char **names;
> > };
> >
> > @@ -250,11 +258,113 @@ pca953x_get_alt_pdata(struct i2c_client *client)
> > }
> > #endif
> >
> > +#define PCA953X_NUM_IRQ 16
> > +
> > +static void pca953x_irq_work(struct work_struct *work)
> > +{
> > + DECLARE_BITMAP(rising, PCA953X_NUM_IRQ);
> > + DECLARE_BITMAP(falling, PCA953X_NUM_IRQ);
> > + struct pca953x_chip *chip;
> > + unsigned short input, mask;
> > + int ret, irq;
> > +
> > + chip = container_of(work, struct pca953x_chip, irq_work);
> > + ret = pca953x_read_reg(chip, PCA953X_INPUT, &input);
> > + if (ret < 0)
> > + return;
> > +
> > + mutex_lock(&chip->irq_lock);
> > + mask = (input ^ chip->last_input) & chip->irq_mask;
> > + rising[0] = (input & mask) & chip->irq_rising_edge;
> > + falling[0] = (~input & mask) & chip->irq_falling_edge;
> > +
> > + while (!bitmap_empty(rising, PCA953X_NUM_IRQ)) {
> > + irq = find_first_bit(rising, PCA953X_NUM_IRQ);
> > + clear_bit(irq, rising);
> > + generic_handle_irq(chip->irq_start + irq);
> > + }
> > + while (!bitmap_empty(falling, PCA953X_NUM_IRQ)) {
> > + irq = find_first_bit(falling, PCA953X_NUM_IRQ);
> > + clear_bit(irq, falling);
> > + generic_handle_irq(chip->irq_start + irq);
> > + }
> > + chip->last_input = input;
> > + mutex_unlock(&chip->irq_lock);
> > +}
> > +
> > +static void pca953x_irq_handler(unsigned int irq, struct irq_desc *desc)
> > +{
> > + struct pca953x_chip *chip = desc->handler_data;
> > +
> > + desc->chip->ack(irq);
> > + schedule_work(&chip->irq_work);
> > +}
> > +
> > +static void pca953x_irq_enable(unsigned int irq)
> > +{
> > + struct pca953x_chip *chip = get_irq_chip_data(irq);
> > +
> > + chip->irq_mask |= 1u << (irq - chip->irq_start);
> > +}
> > +
> > +static void pca953x_irq_disable(unsigned int irq)
> > +{
> > + struct pca953x_chip *chip = get_irq_chip_data(irq);
> > +
> > + chip->irq_mask &= ~(1u << (irq - chip->irq_start));
> > +}
> > +
> > +static int pca953x_irq_type(unsigned int irq, unsigned int trigger)
> > +{
> > + struct pca953x_chip *chip = get_irq_chip_data(irq);
> > + unsigned short mask = 1u << (irq - chip->irq_start);
> > +
> > + if (trigger & IRQ_TYPE_EDGE_RISING)
> > + chip->irq_rising_edge |= mask;
> > + else
> > + chip->irq_rising_edge &= ~mask;
> > + if (trigger & IRQ_TYPE_EDGE_FALLING)
> > + chip->irq_falling_edge |= mask;
> > + else
> > + chip->irq_falling_edge &= ~mask;
> > + return 0;
> > +}
> > +
> > +static struct irq_chip pca953x_irqchip = {
> > + .name = "GPIO",
> > + .enable = pca953x_irq_enable,
> > + .disable = pca953x_irq_disable,
> > + .set_type = pca953x_irq_type,
> > +};
> > +
> > +static void pca953x_setup_irq(struct i2c_client *client)
> > +{
> > + struct pca953x_chip *chip = i2c_get_clientdata(client);
> > + int i;
> > +
> > + if (!client->irq)
> > + return;
> > +
> > + mutex_init(&chip->irq_lock);
> > + chip->irq_start = gpio_to_irq(chip->gpio_start);
> > + for (i = 0; i < chip->gpio_chip.ngpio; i++) {
> > + set_irq_chip(chip->irq_start + i, &pca953x_irqchip);
> > + set_irq_chip_data(chip->irq_start + i, chip);
> > + set_irq_handler(chip->irq_start + i, handle_edge_irq);
> > + set_irq_flags(chip->irq_start + i, IRQF_VALID);
> > + }
> > + set_irq_type(client->irq, IRQ_TYPE_EDGE_FALLING);
> > + set_irq_data(client->irq, chip);
> > + set_irq_chained_handler(client->irq, pca953x_irq_handler);
> > + INIT_WORK(&chip->irq_work, pca953x_irq_work);
> > +}
> > +
> > static int __devinit pca953x_probe(struct i2c_client *client,
> > const struct i2c_device_id *id)
> > {
> > struct pca953x_platform_data *pdata;
> > struct pca953x_chip *chip;
> > + unsigned short input;
> > int ret;
> >
> > chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL);
> > @@ -282,12 +392,18 @@ static int __devinit pca953x_probe(struct
> > i2c_client *client,
> > chip->gpio_start = pdata->gpio_base;
> >
> > chip->names = pdata->names;
> > + chip->irq_mask = -1UL;
> >
> > /* initialize cached registers from their original values.
> > * we can't share this chip with another i2c master.
> > */
> > pca953x_setup_gpio(chip, id->driver_data);
> >
> > + /* clear interrupt status */
> > + ret = pca953x_read_reg(chip, PCA953X_INPUT, &input);
> > + if (ret)
> > + goto out_failed;
> > +
> > ret = pca953x_read_reg(chip, PCA953X_OUTPUT, &chip->reg_output);
> > if (ret)
> > goto out_failed;
> > @@ -314,6 +430,7 @@ static int __devinit pca953x_probe(struct
> > i2c_client *client,
> > }
> >
> > i2c_set_clientdata(client, chip);
> > + pca953x_setup_irq(client);
> > return 0;
> >
> > out_failed:
> > --
> > 1.5.6.5
> >
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--
Fast. Cheap. Reliable. Pick two.
More information about the linux-arm-kernel
mailing list