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 <sdliy...@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

-- 
_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to