[linux-yocto] [PATCH] gpio-pca953x: add PCAL9535 Interrupt support

Yong Li sdliyong at gmail.com
Tue Mar 29 00:59:30 PDT 2016


Galileo gen 2 uses the PCAL9535 as the GPIO expansion,
it is different from PCA9535 and includes interrupt mask/status registers,
The current driver does not support the interrupt registers configuration,
it causes some gpio pins cannot trigger interrupt events,
this patch fix this issue.

Signed-off-by: Yong Li <sdliyong at gmail.com>
---
 drivers/gpio/gpio-pca953x.c | 106 +++++++++++++++++++++++++++++++++-----------
 1 file changed, 81 insertions(+), 25 deletions(-)

diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index f99706f..ad3e0c1 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -39,13 +39,17 @@
 #define PCA957X_MSK		6
 #define PCA957X_INTS		7
 
+#define PCAL953X_IN_LATCH	34
 #define PCA953X_PUPD_EN	35
 #define PCA953X_PUPD_SEL	36
+#define PCAL953X_INT_MASK	37
+#define PCAL953X_INT_STAT	38
 
 #define PCA_GPIO_MASK		0x00FF
 #define PCA_INT			0x0100
 #define PCA953X_TYPE		0x1000
 #define PCA957X_TYPE		0x2000
+#define PCAL953X_TYPE		0x4000
 #define PCA_TYPE_MASK		0xF000
 
 #define PCA_CHIP_TYPE(x)	((x) & PCA_TYPE_MASK)
@@ -54,6 +58,7 @@ static const struct i2c_device_id pca953x_id[] = {
 	{ "pca9505", 40 | PCA953X_TYPE | PCA_INT, },
 	{ "pca9534", 8  | PCA953X_TYPE | PCA_INT, },
 	{ "pca9535", 16 | PCA953X_TYPE | PCA_INT, },
+	{ "pcal9535", 16 | PCAL953X_TYPE | PCA_INT, },
 	{ "pca9536", 4  | PCA953X_TYPE, },
 	{ "pca9537", 4  | PCA953X_TYPE | PCA_INT, },
 	{ "pca9538", 8  | PCA953X_TYPE | PCA_INT, },
@@ -125,7 +130,7 @@ static void pca953x_setup_int3491(struct pca953x_chip *chip)
 }
 
 static const struct pca953x_info pca953x_info_int3491 = {
-	.driver_data = 16 | PCA953X_TYPE | PCA_INT,
+	.driver_data = 16 | PCAL953X_TYPE | PCA_INT,
 	.setup = pca953x_setup_int3491,
 };
 
@@ -191,8 +196,9 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
 	} else {
 		switch (chip->chip_type) {
 		case PCA953X_TYPE:
+		case PCAL953X_TYPE:
 			ret = i2c_smbus_write_word_data(chip->client,
-							reg << 1, (u16) *val);
+							reg << 1, *(u16 *)val);
 			break;
 		case PCA957X_TYPE:
 			ret = i2c_smbus_write_byte_data(chip->client, reg << 1,
@@ -251,6 +257,7 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
 
 	switch (chip->chip_type) {
 	case PCA953X_TYPE:
+	case PCAL953X_TYPE:
 		offset = PCA953X_DIRECTION;
 		break;
 	case PCA957X_TYPE:
@@ -286,6 +293,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
 
 	switch (chip->chip_type) {
 	case PCA953X_TYPE:
+	case PCAL953X_TYPE:
 		offset = PCA953X_OUTPUT;
 		break;
 	case PCA957X_TYPE:
@@ -302,6 +310,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
 	reg_val = chip->reg_direction[off / BANK_SZ] & ~(1u << (off % BANK_SZ));
 	switch (chip->chip_type) {
 	case PCA953X_TYPE:
+	case PCAL953X_TYPE:
 		offset = PCA953X_DIRECTION;
 		break;
 	case PCA957X_TYPE:
@@ -328,6 +337,7 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
 	mutex_lock(&chip->i2c_lock);
 	switch (chip->chip_type) {
 	case PCA953X_TYPE:
+	case PCAL953X_TYPE:
 		offset = PCA953X_INPUT;
 		break;
 	case PCA957X_TYPE:
@@ -363,6 +373,7 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
 
 	switch (chip->chip_type) {
 	case PCA953X_TYPE:
+	case PCAL953X_TYPE:
 		offset = PCA953X_OUTPUT;
 		break;
 	case PCA957X_TYPE:
@@ -387,7 +398,7 @@ static int pca953x_gpio_set_drive(struct gpio_chip *gc,
 
 	chip = container_of(gc, struct pca953x_chip, gpio_chip);
 
-	if (chip->chip_type != PCA953X_TYPE)
+	if (chip->chip_type != PCAL953X_TYPE)
 		return -EINVAL;
 
 	mutex_lock(&chip->i2c_lock);
@@ -434,7 +445,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
 	gc->owner = THIS_MODULE;
 	gc->names = chip->names;
 
-	if (chip->chip_type == PCA953X_TYPE)
+	if (chip->chip_type == PCAL953X_TYPE)
 		gc->set_drive = pca953x_gpio_set_drive;
 }
 
@@ -469,6 +480,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
 	struct pca953x_chip *chip = to_pca(gc);
 	u8 new_irqs;
 	int level, i;
+	u8 invert_irq_mask[MAX_BANK];
 
 	/* Look for any newly setup interrupt */
 	for (i = 0; i < NBANK(chip); i++) {
@@ -483,6 +495,17 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
 		}
 	}
 
+	if (chip->chip_type == PCAL953X_TYPE) {
+		/* Enable latch on interrupt-enabled inputs */
+		pca953x_write_regs(chip, PCAL953X_IN_LATCH, chip->irq_mask);
+
+		for (i = 0; i < NBANK(chip); i++)
+			invert_irq_mask[i] = ~chip->irq_mask[i];
+
+		/* Unmask enabled interrupts */
+		pca953x_write_regs(chip, PCAL953X_INT_MASK, invert_irq_mask);
+	}
+
 	mutex_unlock(&chip->irq_lock);
 }
 
@@ -530,6 +553,29 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
 	u8 trigger[MAX_BANK];
 	int ret, i, offset = 0;
 
+	if (chip->chip_type == PCAL953X_TYPE) {
+		/* Read the current interrupt status from the device */
+		ret = pca953x_read_regs(chip, PCAL953X_INT_STAT, trigger);
+		if (ret)
+			return 0;
+
+		/* Check latched inputs and clear interrupt status */
+		ret = pca953x_read_regs(chip, PCA953X_INPUT, cur_stat);
+		if (ret)
+			return 0;
+
+		for (i = 0; i < NBANK(chip); i++) {
+			/* Apply filter for rising/falling edge selection */
+			pending[i] = (~cur_stat[i] & chip->irq_trig_fall[i]) |
+				(cur_stat[i] & chip->irq_trig_raise[i]);
+			pending[i] &= trigger[i];
+			if (pending[i])
+				pending_seen = true;
+		}
+
+		return pending_seen;
+	}
+
 	switch (chip->chip_type) {
 	case PCA953X_TYPE:
 		offset = PCA953X_INPUT;
@@ -599,38 +645,48 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
 {
 	struct i2c_client *client = chip->client;
 	int ret, i, offset = 0;
+	unsigned long flags;
 
 	if (client->irq && irq_base != -1
 			&& (chip->driver_data & PCA_INT)) {
 
-		switch (chip->chip_type) {
-		case PCA953X_TYPE:
-			offset = PCA953X_INPUT;
-			break;
-		case PCA957X_TYPE:
-			offset = PCA957X_IN;
-			break;
+		if (chip->chip_type != PCAL953X_TYPE) {
+			switch (chip->chip_type) {
+			case PCA953X_TYPE:
+				offset = PCA953X_INPUT;
+				break;
+			case PCA957X_TYPE:
+				offset = PCA957X_IN;
+				break;
+			}
+			ret = pca953x_read_regs(chip, offset, chip->irq_stat);
+			if (ret)
+				return ret;
+
+			/*
+			 * There is no way to know which GPIO line generated the
+			 * interrupt.  We have to rely on the previous read for
+			 * this purpose.
+			 */
+			for (i = 0; i < NBANK(chip); i++)
+				chip->irq_stat[i] &= chip->reg_direction[i];
 		}
-		ret = pca953x_read_regs(chip, offset, chip->irq_stat);
-		if (ret)
-			return ret;
-
-		/*
-		 * There is no way to know which GPIO line generated the
-		 * interrupt.  We have to rely on the previous read for
-		 * this purpose.
-		 */
-		for (i = 0; i < NBANK(chip); i++)
-			chip->irq_stat[i] &= chip->reg_direction[i];
 		mutex_init(&chip->irq_lock);
 
+		if (chip->chip_type == PCAL953X_TYPE)
+			flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
+				IRQF_SHARED;
+		else
+			flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT |
+				IRQF_SHARED;
+
 		ret = devm_request_threaded_irq(&client->dev,
 					client->irq,
 					   NULL,
 					   pca953x_irq_handler,
-					   IRQF_TRIGGER_LOW | IRQF_ONESHOT |
-						   IRQF_SHARED,
+					   flags,
 					   dev_name(&client->dev), chip);
+
 		if (ret) {
 			dev_err(&client->dev, "failed to request irq %d\n",
 				client->irq);
@@ -781,7 +837,7 @@ static int pca953x_probe(struct i2c_client *client,
 	 */
 	pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK);
 
-	if (chip->chip_type == PCA953X_TYPE)
+	if (chip->chip_type & (PCA953X_TYPE | PCAL953X_TYPE))
 		ret = device_pca953x_init(chip, invert);
 	else
 		ret = device_pca957x_init(chip, invert);
-- 
2.1.4



More information about the linux-yocto mailing list