[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20090703211034.47fc0880@dxy.sh.intel.com>
Date: Fri, 3 Jul 2009 21:10:34 +0800
From: Alek Du <alek.du@...el.com>
To: David Brownell <david-b@...bell.net>,
Kernel Mailing List <linux-kernel@...r.kernel.org>
Subject: [PATCH] gpio: pca953x: add irq_chip for irq demuxing
>From 02227060687d8ce8254714d9812e19b815463dd4 Mon Sep 17 00:00:00 2001
From: Alek Du <alek.du@...el.com>
Date: Wed, 1 Jul 2009 17:07:00 +0800
Subject: [PATCH] gpio: pca953x: add irq_chip for irq demuxing
This patch is required when using this gpio driver with gpio_keys driver
Signed-off-by: Alek Du <alek.du@...el.com>
CC: David Brownell <david-b@...bell.net>
---
drivers/gpio/pca953x.c | 54 +++++++++++++++++++++++++++++++++++++++++++
include/linux/i2c/pca953x.h | 3 ++
2 files changed, 57 insertions(+), 0 deletions(-)
diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c
index 8ab1308..47c1d99 100644
--- a/drivers/gpio/pca953x.c
+++ b/drivers/gpio/pca953x.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/i2c/pca953x.h>
#ifdef CONFIG_OF_GPIO
@@ -51,6 +52,7 @@ MODULE_DEVICE_TABLE(i2c, pca953x_id);
struct pca953x_chip {
unsigned gpio_start;
+ unsigned irq_base;
uint16_t reg_output;
uint16_t reg_direction;
@@ -183,6 +185,13 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
chip->reg_output = reg_val;
}
+static int pca953x_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct pca953x_chip *chip = container_of(gc, struct pca953x_chip,
+ gpio_chip);
+ return chip->irq_base + offset;
+}
+
static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
{
struct gpio_chip *gc;
@@ -193,6 +202,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
gc->direction_output = pca953x_gpio_direction_output;
gc->get = pca953x_gpio_get_value;
gc->set = pca953x_gpio_set_value;
+ gc->to_irq = pca953x_gpio_to_irq;
gc->can_sleep = 1;
gc->base = chip->gpio_start;
@@ -251,6 +261,34 @@ pca953x_get_alt_pdata(struct i2c_client *client)
}
#endif
+/* the irq_chip at least needs one handler */
+static void pca953x_irq_unmask(unsigned irq)
+{
+}
+
+static struct irq_chip pca953x_irqchip = {
+ .name = "pca953x_irqchip",
+ .unmask = pca953x_irq_unmask,
+};
+
+static void pca953x_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ struct pca953x_chip *chip = (struct pca953x_chip *)get_irq_data(irq);
+ int i;
+
+ if (desc->chip->ack)
+ desc->chip->ack(irq);
+ /* we must call all sub-irqs, since there is no way to read
+ * I2C gpio expander's status in irq context. The driver itself
+ * would be reponsible to check if the irq is for him.
+ */
+ for (i = 0; i < chip->gpio_chip.ngpio; i++)
+ generic_handle_irq(chip->irq_base + i);
+
+ if (desc->chip->unmask)
+ desc->chip->unmask(irq);
+}
+
static int __devinit pca953x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -284,6 +322,8 @@ static int __devinit pca953x_probe(struct i2c_client *client,
chip->names = pdata->names;
+ chip->irq_base = pdata->irq_base;
+
/* initialize cached registers from their original values.
* we can't share this chip with another i2c master.
*/
@@ -315,6 +355,20 @@ static int __devinit pca953x_probe(struct i2c_client *client,
}
i2c_set_clientdata(client, chip);
+
+ if (chip->irq_base) {
+ int i;
+
+ set_irq_type(client->irq,
+ IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING);
+ set_irq_chained_handler(client->irq, pca953x_irq_handler);
+ set_irq_data(client->irq, chip);
+ for (i = 0; i < chip->gpio_chip.ngpio; i++) {
+ set_irq_chip_and_handler_name(i + chip->irq_base,
+ &pca953x_irqchip, handle_simple_irq, "demux");
+ set_irq_chip_data(i + chip->irq_base, chip);
+ }
+ }
return 0;
out_failed:
diff --git a/include/linux/i2c/pca953x.h b/include/linux/i2c/pca953x.h
index 81736d6..bf7c0a3 100644
--- a/include/linux/i2c/pca953x.h
+++ b/include/linux/i2c/pca953x.h
@@ -4,6 +4,9 @@ struct pca953x_platform_data {
/* number of the first GPIO */
unsigned gpio_base;
+ /* number of the first IRQ */
+ unsigned irq_base;
+
/* initial polarity inversion setting */
uint16_t invert;
--
1.6.0.4
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists