[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