|
@@ -46,6 +46,7 @@
|
|
|
#define PCA957X_TYPE 0x2000
|
|
|
|
|
|
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, },
|
|
|
{ "pca9536", 4 | PCA953X_TYPE, },
|
|
@@ -71,19 +72,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;
|
|
|
- int irq_base;
|
|
|
+ u8 irq_mask[MAX_BANK];
|
|
|
+ u8 irq_stat[MAX_BANK];
|
|
|
+ u8 irq_trig_raise[MAX_BANK];
|
|
|
+ u8 irq_trig_fall[MAX_BANK];
|
|
|
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, ®_val);
|
|
|
+ ret = pca953x_read_single(chip, offset, ®_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);
|
|
|
}
|
|
@@ -328,21 +371,21 @@ static int pca953x_gpio_to_irq(struct gpio_chip *gc, unsigned off)
|
|
|
struct pca953x_chip *chip;
|
|
|
|
|
|
chip = container_of(gc, struct pca953x_chip, gpio_chip);
|
|
|
- return chip->irq_base + off;
|
|
|
+ return irq_create_mapping(chip->domain, off);
|
|
|
}
|
|
|
|
|
|
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,60 +467,88 @@ 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];
|
|
|
+
|
|
|
+ memcpy(old_stat, chip->irq_stat, NBANK(chip));
|
|
|
|
|
|
- old_stat = chip->irq_stat;
|
|
|
- trigger = (cur_stat ^ old_stat) & chip->irq_mask;
|
|
|
+ for (i = 0; i < NBANK(chip); i++) {
|
|
|
+ trigger[i] = (cur_stat[i] ^ old_stat[i]) & chip->irq_mask[i];
|
|
|
+ triggers += trigger[i];
|
|
|
+ }
|
|
|
|
|
|
- if (!trigger)
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
+static int pca953x_gpio_irq_map(struct irq_domain *d, unsigned int irq,
|
|
|
+ irq_hw_number_t hwirq)
|
|
|
+{
|
|
|
+ irq_clear_status_flags(irq, IRQ_NOREQUEST);
|
|
|
+ irq_set_chip_data(irq, d->host_data);
|
|
|
+ irq_set_chip(irq, &pca953x_irq_chip);
|
|
|
+ irq_set_nested_thread(irq, true);
|
|
|
+#ifdef CONFIG_ARM
|
|
|
+ set_irq_flags(irq, IRQF_VALID);
|
|
|
+#else
|
|
|
+ irq_set_noprobe(irq);
|
|
|
+#endif
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct irq_domain_ops pca953x_irq_simple_ops = {
|
|
|
+ .map = pca953x_gpio_irq_map,
|
|
|
+ .xlate = irq_domain_xlate_twocell,
|
|
|
+};
|
|
|
+
|
|
|
static int pca953x_irq_setup(struct pca953x_chip *chip,
|
|
|
const struct i2c_device_id *id,
|
|
|
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)) {
|
|
|
- int lvl;
|
|
|
|
|
|
switch (chip->chip_type) {
|
|
|
case PCA953X_TYPE:
|
|
@@ -483,49 +558,29 @@ 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;
|
|
|
+ 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.
|
|
|
*/
|
|
|
- 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);
|
|
|
- if (chip->irq_base < 0)
|
|
|
- goto out_failed;
|
|
|
-
|
|
|
- chip->domain = irq_domain_add_legacy(client->dev.of_node,
|
|
|
+ chip->domain = irq_domain_add_simple(client->dev.of_node,
|
|
|
chip->gpio_chip.ngpio,
|
|
|
- chip->irq_base,
|
|
|
- 0,
|
|
|
- &irq_domain_simple_ops,
|
|
|
+ irq_base,
|
|
|
+ &pca953x_irq_simple_ops,
|
|
|
NULL);
|
|
|
- if (!chip->domain) {
|
|
|
- ret = -ENODEV;
|
|
|
- goto out_irqdesc_free;
|
|
|
- }
|
|
|
+ if (!chip->domain)
|
|
|
+ return -ENODEV;
|
|
|
|
|
|
- for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) {
|
|
|
- int irq = lvl + chip->irq_base;
|
|
|
-
|
|
|
- irq_clear_status_flags(irq, IRQ_NOREQUEST);
|
|
|
- irq_set_chip_data(irq, chip);
|
|
|
- irq_set_chip(irq, &pca953x_irq_chip);
|
|
|
- irq_set_nested_thread(irq, true);
|
|
|
-#ifdef CONFIG_ARM
|
|
|
- set_irq_flags(irq, IRQF_VALID);
|
|
|
-#else
|
|
|
- irq_set_noprobe(irq);
|
|
|
-#endif
|
|
|
- }
|
|
|
-
|
|
|
- ret = request_threaded_irq(client->irq,
|
|
|
+ ret = devm_request_threaded_irq(&client->dev,
|
|
|
+ client->irq,
|
|
|
NULL,
|
|
|
pca953x_irq_handler,
|
|
|
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
|
@@ -533,28 +588,15 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
|
|
|
if (ret) {
|
|
|
dev_err(&client->dev, "failed to request irq %d\n",
|
|
|
client->irq);
|
|
|
- goto out_irqdesc_free;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
chip->gpio_chip.to_irq = pca953x_gpio_to_irq;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
-
|
|
|
-out_irqdesc_free:
|
|
|
- irq_free_descs(chip->irq_base, chip->gpio_chip.ngpio);
|
|
|
-out_failed:
|
|
|
- chip->irq_base = -1;
|
|
|
- return ret;
|
|
|
}
|
|
|
|
|
|
-static void pca953x_irq_teardown(struct pca953x_chip *chip)
|
|
|
-{
|
|
|
- if (chip->irq_base != -1) {
|
|
|
- irq_free_descs(chip->irq_base, chip->gpio_chip.ngpio);
|
|
|
- free_irq(chip->client->irq, chip);
|
|
|
- }
|
|
|
-}
|
|
|
#else /* CONFIG_GPIO_PCA953X_IRQ */
|
|
|
static int pca953x_irq_setup(struct pca953x_chip *chip,
|
|
|
const struct i2c_device_id *id,
|
|
@@ -567,10 +609,6 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
-
|
|
|
-static void pca953x_irq_teardown(struct pca953x_chip *chip)
|
|
|
-{
|
|
|
-}
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
@@ -619,18 +657,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 +682,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:
|
|
@@ -675,7 +727,8 @@ static int pca953x_probe(struct i2c_client *client,
|
|
|
int ret;
|
|
|
u32 invert = 0;
|
|
|
|
|
|
- chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL);
|
|
|
+ chip = devm_kzalloc(&client->dev,
|
|
|
+ sizeof(struct pca953x_chip), GFP_KERNEL);
|
|
|
if (chip == NULL)
|
|
|
return -ENOMEM;
|
|
|
|
|
@@ -710,15 +763,15 @@ static int pca953x_probe(struct i2c_client *client,
|
|
|
else
|
|
|
ret = device_pca957x_init(chip, invert);
|
|
|
if (ret)
|
|
|
- goto out_failed;
|
|
|
+ return ret;
|
|
|
|
|
|
ret = pca953x_irq_setup(chip, id, irq_base);
|
|
|
if (ret)
|
|
|
- goto out_failed;
|
|
|
+ return ret;
|
|
|
|
|
|
ret = gpiochip_add(&chip->gpio_chip);
|
|
|
if (ret)
|
|
|
- goto out_failed_irq;
|
|
|
+ return ret;
|
|
|
|
|
|
if (pdata && pdata->setup) {
|
|
|
ret = pdata->setup(client, chip->gpio_chip.base,
|
|
@@ -729,12 +782,6 @@ static int pca953x_probe(struct i2c_client *client,
|
|
|
|
|
|
i2c_set_clientdata(client, chip);
|
|
|
return 0;
|
|
|
-
|
|
|
-out_failed_irq:
|
|
|
- pca953x_irq_teardown(chip);
|
|
|
-out_failed:
|
|
|
- kfree(chip);
|
|
|
- return ret;
|
|
|
}
|
|
|
|
|
|
static int pca953x_remove(struct i2c_client *client)
|
|
@@ -760,12 +807,11 @@ static int pca953x_remove(struct i2c_client *client)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- pca953x_irq_teardown(chip);
|
|
|
- kfree(chip);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static const struct of_device_id pca953x_dt_ids[] = {
|
|
|
+ { .compatible = "nxp,pca9505", },
|
|
|
{ .compatible = "nxp,pca9534", },
|
|
|
{ .compatible = "nxp,pca9535", },
|
|
|
{ .compatible = "nxp,pca9536", },
|