]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/blobdiff - drivers/gpio/gpio-pca953x.c
gpio: pca953x: make the register access by GPIO bank
[sojka/nv-tegra/linux-3.10.git] / drivers / gpio / gpio-pca953x.c
index cc102d25ee249e15f775d61aa8814fd394ab40ef..b35ba06901631c5659126d702fb055be56bf49b4 100644 (file)
@@ -71,18 +71,23 @@ static const struct i2c_device_id pca953x_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, pca953x_id);
 
+#define MAX_BANK 5
+#define BANK_SZ 8
+
+#define NBANK(chip) (chip->gpio_chip.ngpio / BANK_SZ)
+
 struct pca953x_chip {
        unsigned gpio_start;
-       u32 reg_output;
-       u32 reg_direction;
+       u8 reg_output[MAX_BANK];
+       u8 reg_direction[MAX_BANK];
        struct mutex i2c_lock;
 
 #ifdef CONFIG_GPIO_PCA953X_IRQ
        struct mutex irq_lock;
-       u32 irq_mask;
-       u32 irq_stat;
-       u32 irq_trig_raise;
-       u32 irq_trig_fall;
+       u8 irq_mask[MAX_BANK];
+       u8 irq_stat[MAX_BANK];
+       u8 irq_trig_raise[MAX_BANK];
+       u8 irq_trig_fall[MAX_BANK];
        int      irq_base;
        struct irq_domain *domain;
 #endif
@@ -93,33 +98,69 @@ struct pca953x_chip {
        int     chip_type;
 };
 
-static int pca953x_write_reg(struct pca953x_chip *chip, int reg, u32 val)
+static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val,
+                               int off)
+{
+       int ret;
+       int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+       int offset = off / BANK_SZ;
+
+       ret = i2c_smbus_read_byte_data(chip->client,
+                               (reg << bank_shift) + offset);
+       *val = ret;
+
+       if (ret < 0) {
+               dev_err(&chip->client->dev, "failed reading register\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val,
+                               int off)
+{
+       int ret = 0;
+       int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+       int offset = off / BANK_SZ;
+
+       ret = i2c_smbus_write_byte_data(chip->client,
+                                       (reg << bank_shift) + offset, val);
+
+       if (ret < 0) {
+               dev_err(&chip->client->dev, "failed writing register\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
 {
        int ret = 0;
 
        if (chip->gpio_chip.ngpio <= 8)
-               ret = i2c_smbus_write_byte_data(chip->client, reg, val);
-       else if (chip->gpio_chip.ngpio == 24) {
-               cpu_to_le32s(&val);
+               ret = i2c_smbus_write_byte_data(chip->client, reg, *val);
+       else if (chip->gpio_chip.ngpio >= 24) {
+               int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
                ret = i2c_smbus_write_i2c_block_data(chip->client,
-                                               (reg << 2) | REG_ADDR_AI,
-                                               3,
-                                               (u8 *) &val);
+                                       (reg << bank_shift) | REG_ADDR_AI,
+                                       NBANK(chip), val);
        }
        else {
                switch (chip->chip_type) {
                case PCA953X_TYPE:
                        ret = i2c_smbus_write_word_data(chip->client,
-                                                       reg << 1, val);
+                                                       reg << 1, (u16) *val);
                        break;
                case PCA957X_TYPE:
                        ret = i2c_smbus_write_byte_data(chip->client, reg << 1,
-                                                       val & 0xff);
+                                                       val[0]);
                        if (ret < 0)
                                break;
                        ret = i2c_smbus_write_byte_data(chip->client,
                                                        (reg << 1) + 1,
-                                                       (val & 0xff00) >> 8);
+                                                       val[1]);
                        break;
                }
        }
@@ -132,26 +173,24 @@ static int pca953x_write_reg(struct pca953x_chip *chip, int reg, u32 val)
        return 0;
 }
 
-static int pca953x_read_reg(struct pca953x_chip *chip, int reg, u32 *val)
+static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val)
 {
        int ret;
 
        if (chip->gpio_chip.ngpio <= 8) {
                ret = i2c_smbus_read_byte_data(chip->client, reg);
                *val = ret;
-       }
-       else if (chip->gpio_chip.ngpio == 24) {
-               *val = 0;
+       } else if (chip->gpio_chip.ngpio >= 24) {
+               int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+
                ret = i2c_smbus_read_i2c_block_data(chip->client,
-                                               (reg << 2) | REG_ADDR_AI,
-                                               3,
-                                               (u8 *) val);
-               le32_to_cpus(val);
+                                       (reg << bank_shift) | REG_ADDR_AI,
+                                       NBANK(chip), val);
        } else {
                ret = i2c_smbus_read_word_data(chip->client, reg << 1);
-               *val = ret;
+               val[0] = (u16)ret & 0xFF;
+               val[1] = (u16)ret >> 8;
        }
-
        if (ret < 0) {
                dev_err(&chip->client->dev, "failed reading register\n");
                return ret;
@@ -163,13 +202,13 @@ static int pca953x_read_reg(struct pca953x_chip *chip, int reg, u32 *val)
 static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
 {
        struct pca953x_chip *chip;
-       uint reg_val;
+       u8 reg_val;
        int ret, offset = 0;
 
        chip = container_of(gc, struct pca953x_chip, gpio_chip);
 
        mutex_lock(&chip->i2c_lock);
-       reg_val = chip->reg_direction | (1u << off);
+       reg_val = chip->reg_direction[off / BANK_SZ] | (1u << (off % BANK_SZ));
 
        switch (chip->chip_type) {
        case PCA953X_TYPE:
@@ -179,11 +218,11 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
                offset = PCA957X_CFG;
                break;
        }
-       ret = pca953x_write_reg(chip, offset, reg_val);
+       ret = pca953x_write_single(chip, offset, reg_val, off);
        if (ret)
                goto exit;
 
-       chip->reg_direction = reg_val;
+       chip->reg_direction[off / BANK_SZ] = reg_val;
        ret = 0;
 exit:
        mutex_unlock(&chip->i2c_lock);
@@ -194,7 +233,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
                unsigned off, int val)
 {
        struct pca953x_chip *chip;
-       uint reg_val;
+       u8 reg_val;
        int ret, offset = 0;
 
        chip = container_of(gc, struct pca953x_chip, gpio_chip);
@@ -202,9 +241,11 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
        mutex_lock(&chip->i2c_lock);
        /* set output level */
        if (val)
-               reg_val = chip->reg_output | (1u << off);
+               reg_val = chip->reg_output[off / BANK_SZ]
+                       | (1u << (off % BANK_SZ));
        else
-               reg_val = chip->reg_output & ~(1u << off);
+               reg_val = chip->reg_output[off / BANK_SZ]
+                       & ~(1u << (off % BANK_SZ));
 
        switch (chip->chip_type) {
        case PCA953X_TYPE:
@@ -214,14 +255,14 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
                offset = PCA957X_OUT;
                break;
        }
-       ret = pca953x_write_reg(chip, offset, reg_val);
+       ret = pca953x_write_single(chip, offset, reg_val, off);
        if (ret)
                goto exit;
 
-       chip->reg_output = reg_val;
+       chip->reg_output[off / BANK_SZ] = reg_val;
 
        /* then direction */
-       reg_val = chip->reg_direction & ~(1u << off);
+       reg_val = chip->reg_direction[off / BANK_SZ] & ~(1u << (off % BANK_SZ));
        switch (chip->chip_type) {
        case PCA953X_TYPE:
                offset = PCA953X_DIRECTION;
@@ -230,11 +271,11 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
                offset = PCA957X_CFG;
                break;
        }
-       ret = pca953x_write_reg(chip, offset, reg_val);
+       ret = pca953x_write_single(chip, offset, reg_val, off);
        if (ret)
                goto exit;
 
-       chip->reg_direction = reg_val;
+       chip->reg_direction[off / BANK_SZ] = reg_val;
        ret = 0;
 exit:
        mutex_unlock(&chip->i2c_lock);
@@ -258,7 +299,7 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
                offset = PCA957X_IN;
                break;
        }
-       ret = pca953x_read_reg(chip, offset, &reg_val);
+       ret = pca953x_read_single(chip, offset, &reg_val, off);
        mutex_unlock(&chip->i2c_lock);
        if (ret < 0) {
                /* NOTE:  diagnostic already emitted; that's all we should
@@ -274,16 +315,18 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
 static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
 {
        struct pca953x_chip *chip;
-       u32 reg_val;
+       u8 reg_val;
        int ret, offset = 0;
 
        chip = container_of(gc, struct pca953x_chip, gpio_chip);
 
        mutex_lock(&chip->i2c_lock);
        if (val)
-               reg_val = chip->reg_output | (1u << off);
+               reg_val = chip->reg_output[off / BANK_SZ]
+                       | (1u << (off % BANK_SZ));
        else
-               reg_val = chip->reg_output & ~(1u << off);
+               reg_val = chip->reg_output[off / BANK_SZ]
+                       & ~(1u << (off % BANK_SZ));
 
        switch (chip->chip_type) {
        case PCA953X_TYPE:
@@ -293,11 +336,11 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
                offset = PCA957X_OUT;
                break;
        }
-       ret = pca953x_write_reg(chip, offset, reg_val);
+       ret = pca953x_write_single(chip, offset, reg_val, off);
        if (ret)
                goto exit;
 
-       chip->reg_output = reg_val;
+       chip->reg_output[off / BANK_SZ] = reg_val;
 exit:
        mutex_unlock(&chip->i2c_lock);
 }
@@ -335,14 +378,14 @@ static void pca953x_irq_mask(struct irq_data *d)
 {
        struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
 
-       chip->irq_mask &= ~(1 << d->hwirq);
+       chip->irq_mask[d->hwirq / BANK_SZ] &= ~(1 << (d->hwirq % BANK_SZ));
 }
 
 static void pca953x_irq_unmask(struct irq_data *d)
 {
        struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
 
-       chip->irq_mask |= 1 << d->hwirq;
+       chip->irq_mask[d->hwirq / BANK_SZ] |= 1 << (d->hwirq % BANK_SZ);
 }
 
 static void pca953x_irq_bus_lock(struct irq_data *d)
@@ -355,17 +398,20 @@ static void pca953x_irq_bus_lock(struct irq_data *d)
 static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
 {
        struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
-       u32 new_irqs;
-       u32 level;
+       u8 new_irqs;
+       int level, i;
 
        /* Look for any newly setup interrupt */
-       new_irqs = chip->irq_trig_fall | chip->irq_trig_raise;
-       new_irqs &= ~chip->reg_direction;
-
-       while (new_irqs) {
-               level = __ffs(new_irqs);
-               pca953x_gpio_direction_input(&chip->gpio_chip, level);
-               new_irqs &= ~(1 << level);
+       for (i = 0; i < NBANK(chip); i++) {
+               new_irqs = chip->irq_trig_fall[i] | chip->irq_trig_raise[i];
+               new_irqs &= ~chip->reg_direction[i];
+
+               while (new_irqs) {
+                       level = __ffs(new_irqs);
+                       pca953x_gpio_direction_input(&chip->gpio_chip,
+                                                       level + (BANK_SZ * i));
+                       new_irqs &= ~(1 << level);
+               }
        }
 
        mutex_unlock(&chip->irq_lock);
@@ -374,7 +420,8 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
 static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
 {
        struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
-       u32 mask = 1 << d->hwirq;
+       int bank_nb = d->hwirq / BANK_SZ;
+       u8 mask = 1 << (d->hwirq % BANK_SZ);
 
        if (!(type & IRQ_TYPE_EDGE_BOTH)) {
                dev_err(&chip->client->dev, "irq %d: unsupported type %d\n",
@@ -383,14 +430,14 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
        }
 
        if (type & IRQ_TYPE_EDGE_FALLING)
-               chip->irq_trig_fall |= mask;
+               chip->irq_trig_fall[bank_nb] |= mask;
        else
-               chip->irq_trig_fall &= ~mask;
+               chip->irq_trig_fall[bank_nb] &= ~mask;
 
        if (type & IRQ_TYPE_EDGE_RISING)
-               chip->irq_trig_raise |= mask;
+               chip->irq_trig_raise[bank_nb] |= mask;
        else
-               chip->irq_trig_raise &= ~mask;
+               chip->irq_trig_raise[bank_nb] &= ~mask;
 
        return 0;
 }
@@ -404,13 +451,13 @@ static struct irq_chip pca953x_irq_chip = {
        .irq_set_type           = pca953x_irq_set_type,
 };
 
-static u32 pca953x_irq_pending(struct pca953x_chip *chip)
+static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
 {
-       u32 cur_stat;
-       u32 old_stat;
-       u32 pending;
-       u32 trigger;
-       int ret, offset = 0;
+       u8 cur_stat[MAX_BANK];
+       u8 old_stat[MAX_BANK];
+       u8 pendings = 0;
+       u8 trigger[MAX_BANK], triggers = 0;
+       int ret, i, offset = 0;
 
        switch (chip->chip_type) {
        case PCA953X_TYPE:
@@ -420,45 +467,54 @@ static u32 pca953x_irq_pending(struct pca953x_chip *chip)
                offset = PCA957X_IN;
                break;
        }
-       ret = pca953x_read_reg(chip, offset, &cur_stat);
+       ret = pca953x_read_regs(chip, offset, cur_stat);
        if (ret)
                return 0;
 
        /* Remove output pins from the equation */
-       cur_stat &= chip->reg_direction;
+       for (i = 0; i < NBANK(chip); i++)
+               cur_stat[i] &= chip->reg_direction[i];
 
-       old_stat = chip->irq_stat;
-       trigger = (cur_stat ^ old_stat) & chip->irq_mask;
+       memcpy(old_stat, chip->irq_stat, NBANK(chip));
 
-       if (!trigger)
+       for (i = 0; i < NBANK(chip); i++) {
+               trigger[i] = (cur_stat[i] ^ old_stat[i]) & chip->irq_mask[i];
+               triggers += trigger[i];
+       }
+
+       if (!triggers)
                return 0;
 
-       chip->irq_stat = cur_stat;
+       memcpy(chip->irq_stat, cur_stat, NBANK(chip));
 
-       pending = (old_stat & chip->irq_trig_fall) |
-                 (cur_stat & chip->irq_trig_raise);
-       pending &= trigger;
+       for (i = 0; i < NBANK(chip); i++) {
+               pending[i] = (old_stat[i] & chip->irq_trig_fall[i]) |
+                       (cur_stat[i] & chip->irq_trig_raise[i]);
+               pending[i] &= trigger[i];
+               pendings += pending[i];
+       }
 
-       return pending;
+       return pendings;
 }
 
 static irqreturn_t pca953x_irq_handler(int irq, void *devid)
 {
        struct pca953x_chip *chip = devid;
-       u32 pending;
-       u32 level;
-
-       pending = pca953x_irq_pending(chip);
+       u8 pending[MAX_BANK];
+       u8 level;
+       int i;
 
-       if (!pending)
+       if (!pca953x_irq_pending(chip, pending))
                return IRQ_HANDLED;
 
-       do {
-               level = __ffs(pending);
-               handle_nested_irq(irq_find_mapping(chip->domain, level));
-
-               pending &= ~(1 << level);
-       } while (pending);
+       for (i = 0; i < NBANK(chip); i++) {
+               while (pending[i]) {
+                       level = __ffs(pending[i]);
+                       handle_nested_irq(irq_find_mapping(chip->domain,
+                                                       level + (BANK_SZ * i)));
+                       pending[i] &= ~(1 << level);
+               }
+       }
 
        return IRQ_HANDLED;
 }
@@ -468,8 +524,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
                             int irq_base)
 {
        struct i2c_client *client = chip->client;
-       int ret, offset = 0;
-       u32 temporary;
+       int ret, i, offset = 0;
 
        if (irq_base != -1
                        && (id->driver_data & PCA_INT)) {
@@ -483,8 +538,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
                        offset = PCA957X_IN;
                        break;
                }
-               ret = pca953x_read_reg(chip, offset, &temporary);
-               chip->irq_stat = temporary;
+               ret = pca953x_read_regs(chip, offset, chip->irq_stat);
                if (ret)
                        goto out_failed;
 
@@ -493,7 +547,8 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
                 * interrupt.  We have to rely on the previous read for
                 * this purpose.
                 */
-               chip->irq_stat &= chip->reg_direction;
+               for (i = 0; i < NBANK(chip); i++)
+                       chip->irq_stat[i] &= chip->reg_direction[i];
                mutex_init(&chip->irq_lock);
 
                chip->irq_base = irq_alloc_descs(-1, irq_base, chip->gpio_chip.ngpio, -1);
@@ -619,18 +674,24 @@ pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, u32 *invert)
 static int device_pca953x_init(struct pca953x_chip *chip, u32 invert)
 {
        int ret;
+       u8 val[MAX_BANK];
 
-       ret = pca953x_read_reg(chip, PCA953X_OUTPUT, &chip->reg_output);
+       ret = pca953x_read_regs(chip, PCA953X_OUTPUT, chip->reg_output);
        if (ret)
                goto out;
 
-       ret = pca953x_read_reg(chip, PCA953X_DIRECTION,
-                              &chip->reg_direction);
+       ret = pca953x_read_regs(chip, PCA953X_DIRECTION,
+                              chip->reg_direction);
        if (ret)
                goto out;
 
        /* set platform specific polarity inversion */
-       ret = pca953x_write_reg(chip, PCA953X_INVERT, invert);
+       if (invert)
+               memset(val, 0xFF, NBANK(chip));
+       else
+               memset(val, 0, NBANK(chip));
+
+       ret = pca953x_write_regs(chip, PCA953X_INVERT, val);
 out:
        return ret;
 }
@@ -638,28 +699,36 @@ out:
 static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
 {
        int ret;
-       u32 val = 0;
+       u8 val[MAX_BANK];
 
        /* Let every port in proper state, that could save power */
-       pca953x_write_reg(chip, PCA957X_PUPD, 0x0);
-       pca953x_write_reg(chip, PCA957X_CFG, 0xffff);
-       pca953x_write_reg(chip, PCA957X_OUT, 0x0);
-
-       ret = pca953x_read_reg(chip, PCA957X_IN, &val);
+       memset(val, 0, NBANK(chip));
+       pca953x_write_regs(chip, PCA957X_PUPD, val);
+       memset(val, 0xFF, NBANK(chip));
+       pca953x_write_regs(chip, PCA957X_CFG, val);
+       memset(val, 0, NBANK(chip));
+       pca953x_write_regs(chip, PCA957X_OUT, val);
+
+       ret = pca953x_read_regs(chip, PCA957X_IN, val);
        if (ret)
                goto out;
-       ret = pca953x_read_reg(chip, PCA957X_OUT, &chip->reg_output);
+       ret = pca953x_read_regs(chip, PCA957X_OUT, chip->reg_output);
        if (ret)
                goto out;
-       ret = pca953x_read_reg(chip, PCA957X_CFG, &chip->reg_direction);
+       ret = pca953x_read_regs(chip, PCA957X_CFG, chip->reg_direction);
        if (ret)
                goto out;
 
        /* set platform specific polarity inversion */
-       pca953x_write_reg(chip, PCA957X_INVRT, invert);
+       if (invert)
+               memset(val, 0xFF, NBANK(chip));
+       else
+               memset(val, 0, NBANK(chip));
+       pca953x_write_regs(chip, PCA957X_INVRT, val);
 
        /* To enable register 6, 7 to controll pull up and pull down */
-       pca953x_write_reg(chip, PCA957X_BKEN, 0x202);
+       memset(val, 0x02, NBANK(chip));
+       pca953x_write_regs(chip, PCA957X_BKEN, val);
 
        return 0;
 out: