[PATCH] gpio: pca953x: add irq_chip for irq demuxing

From: Alek Du
Date: Wed Jul 01 2009 - 05:07:00 EST


This patch is required when using this gpio driver with gpio_keys driver

Signed-off-by: Alek Du <alek.du@xxxxxxxxx>
CC: David Brownell <david-b@xxxxxxxxxxx>
---
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@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/