Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 33226ffd authored by Haojian Zhuang's avatar Haojian Zhuang Committed by Grant Likely
Browse files

gpio/pca953x: Add support for pca9574 and pca9575 devices



PCA957x is i2c gpio expander, and similar to PCA953x. Although register
configurations are different between PCA957x and PCA953x. They can share
a lot of components, such as IRQ handling, GPIO IN/OUT. So updating PCA953x
driver to support PCA957x chips.

Signed-off-by: default avatarHaojian Zhuang <haojian.zhuang@marvell.com>
Signed-off-by: default avatarGrant Likely <grant.likely@secretlab.ca>
parent 073cc4e9
Loading
Loading
Loading
Loading
+191 −58
Original line number Original line Diff line number Diff line
@@ -29,28 +29,41 @@
#define PCA953X_INVERT		2
#define PCA953X_INVERT		2
#define PCA953X_DIRECTION	3
#define PCA953X_DIRECTION	3


#define PCA953X_GPIOS	       0x00FF
#define PCA957X_IN		0
#define PCA953X_INT	       0x0100
#define PCA957X_INVRT		1
#define PCA957X_BKEN		2
#define PCA957X_PUPD		3
#define PCA957X_CFG		4
#define PCA957X_OUT		5
#define PCA957X_MSK		6
#define PCA957X_INTS		7

#define PCA_GPIO_MASK		0x00FF
#define PCA_INT			0x0100
#define PCA953X_TYPE		0x1000
#define PCA957X_TYPE		0x2000


static const struct i2c_device_id pca953x_id[] = {
static const struct i2c_device_id pca953x_id[] = {
	{ "pca9534", 8  | PCA953X_INT, },
	{ "pca9534", 8  | PCA953X_TYPE | PCA_INT, },
	{ "pca9535", 16 | PCA953X_INT, },
	{ "pca9535", 16 | PCA953X_TYPE | PCA_INT, },
	{ "pca9536", 4, },
	{ "pca9536", 4  | PCA953X_TYPE, },
	{ "pca9537", 4  | PCA953X_INT, },
	{ "pca9537", 4  | PCA953X_TYPE | PCA_INT, },
	{ "pca9538", 8  | PCA953X_INT, },
	{ "pca9538", 8  | PCA953X_TYPE | PCA_INT, },
	{ "pca9539", 16 | PCA953X_INT, },
	{ "pca9539", 16 | PCA953X_TYPE | PCA_INT, },
	{ "pca9554", 8  | PCA953X_INT, },
	{ "pca9554", 8  | PCA953X_TYPE | PCA_INT, },
	{ "pca9555", 16 | PCA953X_INT, },
	{ "pca9555", 16 | PCA953X_TYPE | PCA_INT, },
	{ "pca9556", 8, },
	{ "pca9556", 8  | PCA953X_TYPE, },
	{ "pca9557", 8, },
	{ "pca9557", 8  | PCA953X_TYPE, },

	{ "pca9574", 8  | PCA957X_TYPE | PCA_INT, },
	{ "max7310", 8, },
	{ "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
	{ "max7312", 16 | PCA953X_INT, },

	{ "max7313", 16 | PCA953X_INT, },
	{ "max7310", 8  | PCA953X_TYPE, },
	{ "max7315", 8  | PCA953X_INT, },
	{ "max7312", 16 | PCA953X_TYPE | PCA_INT, },
	{ "pca6107", 8  | PCA953X_INT, },
	{ "max7313", 16 | PCA953X_TYPE | PCA_INT, },
	{ "tca6408", 8  | PCA953X_INT, },
	{ "max7315", 8  | PCA953X_TYPE | PCA_INT, },
	{ "tca6416", 16 | PCA953X_INT, },
	{ "pca6107", 8  | PCA953X_TYPE | PCA_INT, },
	{ "tca6408", 8  | PCA953X_TYPE | PCA_INT, },
	{ "tca6416", 16 | PCA953X_TYPE | PCA_INT, },
	/* NYET:  { "tca6424", 24, }, */
	/* NYET:  { "tca6424", 24, }, */
	{ }
	{ }
};
};
@@ -75,16 +88,32 @@ struct pca953x_chip {
	struct pca953x_platform_data *dyn_pdata;
	struct pca953x_platform_data *dyn_pdata;
	struct gpio_chip gpio_chip;
	struct gpio_chip gpio_chip;
	const char *const *names;
	const char *const *names;
	int	chip_type;
};
};


static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val)
static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val)
{
{
	int ret;
	int ret = 0;


	if (chip->gpio_chip.ngpio <= 8)
	if (chip->gpio_chip.ngpio <= 8)
		ret = i2c_smbus_write_byte_data(chip->client, reg, val);
		ret = i2c_smbus_write_byte_data(chip->client, reg, val);
	else
	else {
		ret = i2c_smbus_write_word_data(chip->client, reg << 1, val);
		switch (chip->chip_type) {
		case PCA953X_TYPE:
			ret = i2c_smbus_write_word_data(chip->client,
							reg << 1, val);
			break;
		case PCA957X_TYPE:
			ret = i2c_smbus_write_byte_data(chip->client, reg << 1,
							val & 0xff);
			if (ret < 0)
				break;
			ret = i2c_smbus_write_byte_data(chip->client,
							(reg << 1) + 1,
							(val & 0xff00) >> 8);
			break;
		}
	}


	if (ret < 0) {
	if (ret < 0) {
		dev_err(&chip->client->dev, "failed writing register\n");
		dev_err(&chip->client->dev, "failed writing register\n");
@@ -116,13 +145,22 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
{
{
	struct pca953x_chip *chip;
	struct pca953x_chip *chip;
	uint16_t reg_val;
	uint16_t reg_val;
	int ret;
	int ret, offset = 0;


	chip = container_of(gc, struct pca953x_chip, gpio_chip);
	chip = container_of(gc, struct pca953x_chip, gpio_chip);


	mutex_lock(&chip->i2c_lock);
	mutex_lock(&chip->i2c_lock);
	reg_val = chip->reg_direction | (1u << off);
	reg_val = chip->reg_direction | (1u << off);
	ret = pca953x_write_reg(chip, PCA953X_DIRECTION, reg_val);

	switch (chip->chip_type) {
	case PCA953X_TYPE:
		offset = PCA953X_DIRECTION;
		break;
	case PCA957X_TYPE:
		offset = PCA957X_CFG;
		break;
	}
	ret = pca953x_write_reg(chip, offset, reg_val);
	if (ret)
	if (ret)
		goto exit;
		goto exit;


@@ -138,7 +176,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
{
{
	struct pca953x_chip *chip;
	struct pca953x_chip *chip;
	uint16_t reg_val;
	uint16_t reg_val;
	int ret;
	int ret, offset = 0;


	chip = container_of(gc, struct pca953x_chip, gpio_chip);
	chip = container_of(gc, struct pca953x_chip, gpio_chip);


@@ -149,7 +187,15 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
	else
	else
		reg_val = chip->reg_output & ~(1u << off);
		reg_val = chip->reg_output & ~(1u << off);


	ret = pca953x_write_reg(chip, PCA953X_OUTPUT, reg_val);
	switch (chip->chip_type) {
	case PCA953X_TYPE:
		offset = PCA953X_OUTPUT;
		break;
	case PCA957X_TYPE:
		offset = PCA957X_OUT;
		break;
	}
	ret = pca953x_write_reg(chip, offset, reg_val);
	if (ret)
	if (ret)
		goto exit;
		goto exit;


@@ -157,7 +203,15 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,


	/* then direction */
	/* then direction */
	reg_val = chip->reg_direction & ~(1u << off);
	reg_val = chip->reg_direction & ~(1u << off);
	ret = pca953x_write_reg(chip, PCA953X_DIRECTION, reg_val);
	switch (chip->chip_type) {
	case PCA953X_TYPE:
		offset = PCA953X_DIRECTION;
		break;
	case PCA957X_TYPE:
		offset = PCA957X_CFG;
		break;
	}
	ret = pca953x_write_reg(chip, offset, reg_val);
	if (ret)
	if (ret)
		goto exit;
		goto exit;


@@ -172,12 +226,20 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
{
{
	struct pca953x_chip *chip;
	struct pca953x_chip *chip;
	uint16_t reg_val;
	uint16_t reg_val;
	int ret;
	int ret, offset = 0;


	chip = container_of(gc, struct pca953x_chip, gpio_chip);
	chip = container_of(gc, struct pca953x_chip, gpio_chip);


	mutex_lock(&chip->i2c_lock);
	mutex_lock(&chip->i2c_lock);
	ret = pca953x_read_reg(chip, PCA953X_INPUT, &reg_val);
	switch (chip->chip_type) {
	case PCA953X_TYPE:
		offset = PCA953X_INPUT;
		break;
	case PCA957X_TYPE:
		offset = PCA957X_IN;
		break;
	}
	ret = pca953x_read_reg(chip, offset, &reg_val);
	mutex_unlock(&chip->i2c_lock);
	mutex_unlock(&chip->i2c_lock);
	if (ret < 0) {
	if (ret < 0) {
		/* NOTE:  diagnostic already emitted; that's all we should
		/* NOTE:  diagnostic already emitted; that's all we should
@@ -194,7 +256,7 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
{
{
	struct pca953x_chip *chip;
	struct pca953x_chip *chip;
	uint16_t reg_val;
	uint16_t reg_val;
	int ret;
	int ret, offset = 0;


	chip = container_of(gc, struct pca953x_chip, gpio_chip);
	chip = container_of(gc, struct pca953x_chip, gpio_chip);


@@ -204,7 +266,15 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
	else
	else
		reg_val = chip->reg_output & ~(1u << off);
		reg_val = chip->reg_output & ~(1u << off);


	ret = pca953x_write_reg(chip, PCA953X_OUTPUT, reg_val);
	switch (chip->chip_type) {
	case PCA953X_TYPE:
		offset = PCA953X_OUTPUT;
		break;
	case PCA957X_TYPE:
		offset = PCA957X_OUT;
		break;
	}
	ret = pca953x_write_reg(chip, offset, reg_val);
	if (ret)
	if (ret)
		goto exit;
		goto exit;


@@ -322,9 +392,17 @@ static uint16_t pca953x_irq_pending(struct pca953x_chip *chip)
	uint16_t old_stat;
	uint16_t old_stat;
	uint16_t pending;
	uint16_t pending;
	uint16_t trigger;
	uint16_t trigger;
	int ret;
	int ret, offset = 0;


	ret = pca953x_read_reg(chip, PCA953X_INPUT, &cur_stat);
	switch (chip->chip_type) {
	case PCA953X_TYPE:
		offset = PCA953X_INPUT;
		break;
	case PCA957X_TYPE:
		offset = PCA957X_IN;
		break;
	}
	ret = pca953x_read_reg(chip, offset, &cur_stat);
	if (ret)
	if (ret)
		return 0;
		return 0;


@@ -372,14 +450,21 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
{
{
	struct i2c_client *client = chip->client;
	struct i2c_client *client = chip->client;
	struct pca953x_platform_data *pdata = client->dev.platform_data;
	struct pca953x_platform_data *pdata = client->dev.platform_data;
	int ret;
	int ret, offset = 0;


	if (pdata->irq_base != -1
	if (pdata->irq_base != -1
			&& (id->driver_data & PCA953X_INT)) {
			&& (id->driver_data & PCA_INT)) {
		int lvl;
		int lvl;


		ret = pca953x_read_reg(chip, PCA953X_INPUT,
		switch (chip->chip_type) {
				       &chip->irq_stat);
		case PCA953X_TYPE:
			offset = PCA953X_INPUT;
			break;
		case PCA957X_TYPE:
			offset = PCA957X_IN;
			break;
		}
		ret = pca953x_read_reg(chip, offset, &chip->irq_stat);
		if (ret)
		if (ret)
			goto out_failed;
			goto out_failed;


@@ -439,7 +524,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
	struct i2c_client *client = chip->client;
	struct i2c_client *client = chip->client;
	struct pca953x_platform_data *pdata = client->dev.platform_data;
	struct pca953x_platform_data *pdata = client->dev.platform_data;


	if (pdata->irq_base != -1 && (id->driver_data & PCA953X_INT))
	if (pdata->irq_base != -1 && (id->driver_data & PCA_INT))
		dev_warn(&client->dev, "interrupt support not compiled in\n");
		dev_warn(&client->dev, "interrupt support not compiled in\n");


	return 0;
	return 0;
@@ -499,12 +584,65 @@ pca953x_get_alt_pdata(struct i2c_client *client)
}
}
#endif
#endif


static int __devinit device_pca953x_init(struct pca953x_chip *chip, int invert)
{
	int ret;

	ret = pca953x_read_reg(chip, PCA953X_OUTPUT, &chip->reg_output);
	if (ret)
		goto out;

	ret = pca953x_read_reg(chip, PCA953X_DIRECTION,
			       &chip->reg_direction);
	if (ret)
		goto out;

	/* set platform specific polarity inversion */
	ret = pca953x_write_reg(chip, PCA953X_INVERT, invert);
	if (ret)
		goto out;
	return 0;
out:
	return ret;
}

static int __devinit device_pca957x_init(struct pca953x_chip *chip, int invert)
{
	int ret;
	uint16_t val = 0;

	/* 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);
	if (ret)
		goto out;
	ret = pca953x_read_reg(chip, PCA957X_OUT, &chip->reg_output);
	if (ret)
		goto out;
	ret = pca953x_read_reg(chip, PCA957X_CFG, &chip->reg_direction);
	if (ret)
		goto out;

	/* set platform specific polarity inversion */
	pca953x_write_reg(chip, PCA957X_INVRT, invert);

	/* To enable register 6, 7 to controll pull up and pull down */
	pca953x_write_reg(chip, PCA957X_BKEN, 0x202);

	return 0;
out:
	return ret;
}

static int __devinit pca953x_probe(struct i2c_client *client,
static int __devinit pca953x_probe(struct i2c_client *client,
				   const struct i2c_device_id *id)
				   const struct i2c_device_id *id)
{
{
	struct pca953x_platform_data *pdata;
	struct pca953x_platform_data *pdata;
	struct pca953x_chip *chip;
	struct pca953x_chip *chip;
	int ret;
	int ret = 0;


	chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL);
	chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL);
	if (chip == NULL)
	if (chip == NULL)
@@ -531,25 +669,20 @@ static int __devinit pca953x_probe(struct i2c_client *client,
	chip->gpio_start = pdata->gpio_base;
	chip->gpio_start = pdata->gpio_base;


	chip->names = pdata->names;
	chip->names = pdata->names;
	chip->chip_type = id->driver_data & (PCA953X_TYPE | PCA957X_TYPE);


	mutex_init(&chip->i2c_lock);
	mutex_init(&chip->i2c_lock);


	/* initialize cached registers from their original values.
	/* initialize cached registers from their original values.
	 * we can't share this chip with another i2c master.
	 * we can't share this chip with another i2c master.
	 */
	 */
	pca953x_setup_gpio(chip, id->driver_data & PCA953X_GPIOS);
	pca953x_setup_gpio(chip, id->driver_data & PCA_GPIO_MASK);

	ret = pca953x_read_reg(chip, PCA953X_OUTPUT, &chip->reg_output);
	if (ret)
		goto out_failed;

	ret = pca953x_read_reg(chip, PCA953X_DIRECTION, &chip->reg_direction);
	if (ret)
		goto out_failed;


	/* set platform specific polarity inversion */
	if (chip->chip_type == PCA953X_TYPE)
	ret = pca953x_write_reg(chip, PCA953X_INVERT, pdata->invert);
		device_pca953x_init(chip, pdata->invert);
	if (ret)
	else if (chip->chip_type == PCA957X_TYPE)
		device_pca957x_init(chip, pdata->invert);
	else
		goto out_failed;
		goto out_failed;


	ret = pca953x_irq_setup(chip, id);
	ret = pca953x_irq_setup(chip, id);