[PATCH 1/2] gpio add interrupt support on pca953x
Eric Miao
eric.y.miao at gmail.com
Tue Dec 22 08:41:51 EST 2009
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
>
More information about the linux-arm-kernel
mailing list