xref: /openbmc/u-boot/drivers/gpio/tca642x.c (revision ab21ecef)
161c1775fSDan Murphy /*
261c1775fSDan Murphy  * Copyright 2013 Texas Instruments, Inc.
361c1775fSDan Murphy  * Author: Dan Murphy <dmurphy@ti.com>
461c1775fSDan Murphy  *
561c1775fSDan Murphy  * Derived work from the pca953x.c driver
661c1775fSDan Murphy  *
761c1775fSDan Murphy  * This program is free software; you can redistribute it and/or
861c1775fSDan Murphy  * modify it under the terms of the GNU General Public License as
961c1775fSDan Murphy  * published by the Free Software Foundation; either version 2 of
1061c1775fSDan Murphy  * the License, or (at your option) any later version.
1161c1775fSDan Murphy  *
1261c1775fSDan Murphy  * This program is distributed in the hope that it will be useful,
1361c1775fSDan Murphy  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1461c1775fSDan Murphy  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1561c1775fSDan Murphy  * GNU General Public License for more details.
1661c1775fSDan Murphy  *
1761c1775fSDan Murphy  * You should have received a copy of the GNU General Public License
1861c1775fSDan Murphy  * along with this program; if not, write to the Free Software
1961c1775fSDan Murphy  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
2061c1775fSDan Murphy  * MA 02111-1307 USA
2161c1775fSDan Murphy  */
2261c1775fSDan Murphy 
2361c1775fSDan Murphy #include <common.h>
2461c1775fSDan Murphy #include <i2c.h>
2561c1775fSDan Murphy #include <tca642x.h>
2661c1775fSDan Murphy 
2761c1775fSDan Murphy /* tca642x register address definitions */
2861c1775fSDan Murphy struct tca642x_bank_info tca642x_regs[] = {
2961c1775fSDan Murphy 	{ .input_reg = 0x00,
3061c1775fSDan Murphy 	  .output_reg = 0x04,
3161c1775fSDan Murphy 	  .polarity_reg = 0x08,
3261c1775fSDan Murphy 	  .configuration_reg = 0x0c },
3361c1775fSDan Murphy 	{ .input_reg = 0x01,
3461c1775fSDan Murphy 	  .output_reg = 0x05,
3561c1775fSDan Murphy 	  .polarity_reg = 0x09,
3661c1775fSDan Murphy 	  .configuration_reg = 0x0d },
3761c1775fSDan Murphy 	{ .input_reg = 0x02,
3861c1775fSDan Murphy 	  .output_reg = 0x06,
3961c1775fSDan Murphy 	  .polarity_reg = 0x0a,
4061c1775fSDan Murphy 	  .configuration_reg = 0x0e },
4161c1775fSDan Murphy };
4261c1775fSDan Murphy 
4361c1775fSDan Murphy /*
4461c1775fSDan Murphy  * Modify masked bits in register
4561c1775fSDan Murphy  */
tca642x_reg_write(uchar chip,uint8_t addr,uint8_t reg_bit,uint8_t data)4661c1775fSDan Murphy static int tca642x_reg_write(uchar chip, uint8_t addr,
4761c1775fSDan Murphy 		uint8_t reg_bit, uint8_t data)
4861c1775fSDan Murphy {
4961c1775fSDan Murphy 	uint8_t valw;
5061c1775fSDan Murphy 	int org_bus_num;
5161c1775fSDan Murphy 	int ret;
5261c1775fSDan Murphy 
5361c1775fSDan Murphy 	org_bus_num = i2c_get_bus_num();
5461c1775fSDan Murphy 	i2c_set_bus_num(CONFIG_SYS_I2C_TCA642X_BUS_NUM);
5561c1775fSDan Murphy 
5661c1775fSDan Murphy 	if (i2c_read(chip, addr, 1, (uint8_t *)&valw, 1)) {
5761c1775fSDan Murphy 		printf("Could not read before writing\n");
5861c1775fSDan Murphy 		ret = -1;
5961c1775fSDan Murphy 		goto error;
6061c1775fSDan Murphy 	}
6161c1775fSDan Murphy 	valw &= ~reg_bit;
6261c1775fSDan Murphy 	valw |= data;
6361c1775fSDan Murphy 
6461c1775fSDan Murphy 	ret = i2c_write(chip, addr, 1, (u8 *)&valw, 1);
6561c1775fSDan Murphy 
6661c1775fSDan Murphy error:
6761c1775fSDan Murphy 	i2c_set_bus_num(org_bus_num);
6861c1775fSDan Murphy 	return ret;
6961c1775fSDan Murphy }
7061c1775fSDan Murphy 
tca642x_reg_read(uchar chip,uint8_t addr,uint8_t * data)7161c1775fSDan Murphy static int tca642x_reg_read(uchar chip, uint8_t addr, uint8_t *data)
7261c1775fSDan Murphy {
7361c1775fSDan Murphy 	uint8_t valw;
7461c1775fSDan Murphy 	int org_bus_num;
7561c1775fSDan Murphy 	int ret = 0;
7661c1775fSDan Murphy 
7761c1775fSDan Murphy 	org_bus_num = i2c_get_bus_num();
7861c1775fSDan Murphy 	i2c_set_bus_num(CONFIG_SYS_I2C_TCA642X_BUS_NUM);
7961c1775fSDan Murphy 	if (i2c_read(chip, addr, 1, (u8 *)&valw, 1)) {
8061c1775fSDan Murphy 		ret = -1;
8161c1775fSDan Murphy 		goto error;
8261c1775fSDan Murphy 	}
8361c1775fSDan Murphy 
8461c1775fSDan Murphy 	*data = valw;
8561c1775fSDan Murphy 
8661c1775fSDan Murphy error:
8761c1775fSDan Murphy 	i2c_set_bus_num(org_bus_num);
8861c1775fSDan Murphy 	return ret;
8961c1775fSDan Murphy }
9061c1775fSDan Murphy 
9161c1775fSDan Murphy /*
9261c1775fSDan Murphy  * Set output value of IO pins in 'reg_bit' to corresponding value in 'data'
9361c1775fSDan Murphy  * 0 = low, 1 = high
9461c1775fSDan Murphy  */
tca642x_set_val(uchar chip,uint8_t gpio_bank,uint8_t reg_bit,uint8_t data)9561c1775fSDan Murphy int tca642x_set_val(uchar chip, uint8_t gpio_bank,
9661c1775fSDan Murphy 					uint8_t reg_bit, uint8_t data)
9761c1775fSDan Murphy {
9861c1775fSDan Murphy 	uint8_t out_reg = tca642x_regs[gpio_bank].output_reg;
9961c1775fSDan Murphy 
10061c1775fSDan Murphy 	return tca642x_reg_write(chip, out_reg, reg_bit, data);
10161c1775fSDan Murphy }
10261c1775fSDan Murphy 
10361c1775fSDan Murphy /*
10461c1775fSDan Murphy  * Set read polarity of IO pins in 'reg_bit' to corresponding value in 'data'
10561c1775fSDan Murphy  * 0 = read pin value, 1 = read inverted pin value
10661c1775fSDan Murphy  */
tca642x_set_pol(uchar chip,uint8_t gpio_bank,uint8_t reg_bit,uint8_t data)10761c1775fSDan Murphy int tca642x_set_pol(uchar chip, uint8_t gpio_bank,
10861c1775fSDan Murphy 					uint8_t reg_bit, uint8_t data)
10961c1775fSDan Murphy {
11061c1775fSDan Murphy 	uint8_t pol_reg = tca642x_regs[gpio_bank].polarity_reg;
11161c1775fSDan Murphy 
11261c1775fSDan Murphy 	return tca642x_reg_write(chip, pol_reg, reg_bit, data);
11361c1775fSDan Murphy }
11461c1775fSDan Murphy 
11561c1775fSDan Murphy /*
11661c1775fSDan Murphy  * Set direction of IO pins in 'reg_bit' to corresponding value in 'data'
11761c1775fSDan Murphy  * 0 = output, 1 = input
11861c1775fSDan Murphy  */
tca642x_set_dir(uchar chip,uint8_t gpio_bank,uint8_t reg_bit,uint8_t data)11961c1775fSDan Murphy int tca642x_set_dir(uchar chip, uint8_t gpio_bank,
12061c1775fSDan Murphy 					uint8_t reg_bit, uint8_t data)
12161c1775fSDan Murphy {
12261c1775fSDan Murphy 	uint8_t config_reg = tca642x_regs[gpio_bank].configuration_reg;
12361c1775fSDan Murphy 
12461c1775fSDan Murphy 	return tca642x_reg_write(chip, config_reg, reg_bit, data);
12561c1775fSDan Murphy }
12661c1775fSDan Murphy 
12761c1775fSDan Murphy /*
12861c1775fSDan Murphy  * Read current logic level of all IO pins
12961c1775fSDan Murphy  */
tca642x_get_val(uchar chip,uint8_t gpio_bank)13061c1775fSDan Murphy int tca642x_get_val(uchar chip, uint8_t gpio_bank)
13161c1775fSDan Murphy {
13261c1775fSDan Murphy 	uint8_t val;
13361c1775fSDan Murphy 	uint8_t in_reg = tca642x_regs[gpio_bank].input_reg;
13461c1775fSDan Murphy 
13561c1775fSDan Murphy 	if (tca642x_reg_read(chip, in_reg, &val) < 0)
13661c1775fSDan Murphy 		return -1;
13761c1775fSDan Murphy 
13861c1775fSDan Murphy 	return (int)val;
13961c1775fSDan Murphy }
14061c1775fSDan Murphy 
14161c1775fSDan Murphy /*
14261c1775fSDan Murphy  * Set the inital register states for the tca642x gpio expander
14361c1775fSDan Murphy  */
tca642x_set_inital_state(uchar chip,struct tca642x_bank_info init_data[])14461c1775fSDan Murphy int tca642x_set_inital_state(uchar chip, struct tca642x_bank_info init_data[])
14561c1775fSDan Murphy {
14661c1775fSDan Murphy 	int i, ret;
14761c1775fSDan Murphy 	uint8_t config_reg;
14861c1775fSDan Murphy 	uint8_t polarity_reg;
14961c1775fSDan Murphy 	uint8_t output_reg;
15061c1775fSDan Murphy 
15161c1775fSDan Murphy 	for (i = 0; i < 3; i++) {
15261c1775fSDan Murphy 		config_reg = tca642x_regs[i].configuration_reg;
15361c1775fSDan Murphy 		ret = tca642x_reg_write(chip, config_reg, 0xff,
15461c1775fSDan Murphy 				init_data[i].configuration_reg);
15561c1775fSDan Murphy 		polarity_reg = tca642x_regs[i].polarity_reg;
15661c1775fSDan Murphy 		ret = tca642x_reg_write(chip, polarity_reg, 0xff,
15761c1775fSDan Murphy 				init_data[i].polarity_reg);
15861c1775fSDan Murphy 		output_reg = tca642x_regs[i].output_reg;
15961c1775fSDan Murphy 		ret = tca642x_reg_write(chip, output_reg, 0xff,
16061c1775fSDan Murphy 				init_data[i].output_reg);
16161c1775fSDan Murphy 	}
16261c1775fSDan Murphy 
16361c1775fSDan Murphy 	return ret;
16461c1775fSDan Murphy }
16561c1775fSDan Murphy 
166*bb2277b3STom Rini #if defined(CONFIG_CMD_TCA642X) && !defined(CONFIG_SPL_BUILD)
16761c1775fSDan Murphy /*
16861c1775fSDan Murphy  * Display tca642x information
16961c1775fSDan Murphy  */
tca642x_info(uchar chip)17061c1775fSDan Murphy static int tca642x_info(uchar chip)
17161c1775fSDan Murphy {
17261c1775fSDan Murphy 	int i, j;
17361c1775fSDan Murphy 	uint8_t data;
17461c1775fSDan Murphy 
17561c1775fSDan Murphy 	printf("tca642x@ 0x%x (%d pins):\n", chip, 24);
17661c1775fSDan Murphy 	for (i = 0; i < 3; i++) {
17761c1775fSDan Murphy 		printf("Bank %i\n", i);
17861c1775fSDan Murphy 		if (tca642x_reg_read(chip,
17961c1775fSDan Murphy 				     tca642x_regs[i].configuration_reg,
18061c1775fSDan Murphy 				     &data) < 0)
18161c1775fSDan Murphy 			return -1;
18261c1775fSDan Murphy 		printf("\tConfiguration: ");
18361c1775fSDan Murphy 		for (j = 7; j >= 0; j--)
18461c1775fSDan Murphy 			printf("%c", data & (1 << j) ? 'i' : 'o');
18561c1775fSDan Murphy 		printf("\n");
18661c1775fSDan Murphy 
18761c1775fSDan Murphy 		if (tca642x_reg_read(chip,
18861c1775fSDan Murphy 				     tca642x_regs[i].polarity_reg, &data) < 0)
18961c1775fSDan Murphy 			return -1;
19061c1775fSDan Murphy 		printf("\tPolarity: ");
19161c1775fSDan Murphy 		for (j = 7; j >= 0; j--)
19261c1775fSDan Murphy 			printf("%c", data & (1 << j) ? '1' : '0');
19361c1775fSDan Murphy 		printf("\n");
19461c1775fSDan Murphy 
19561c1775fSDan Murphy 		if (tca642x_reg_read(chip,
19661c1775fSDan Murphy 				     tca642x_regs[i].input_reg, &data) < 0)
19761c1775fSDan Murphy 			return -1;
19861c1775fSDan Murphy 		printf("\tInput value: ");
19961c1775fSDan Murphy 		for (j = 7; j >= 0; j--)
20061c1775fSDan Murphy 			printf("%c", data & (1 << j) ? '1' : '0');
20161c1775fSDan Murphy 		printf("\n");
20261c1775fSDan Murphy 
20361c1775fSDan Murphy 		if (tca642x_reg_read(chip,
20461c1775fSDan Murphy 				     tca642x_regs[i].output_reg, &data) < 0)
20561c1775fSDan Murphy 			return -1;
20661c1775fSDan Murphy 		printf("\tOutput value: ");
20761c1775fSDan Murphy 		for (j = 7; j >= 0; j--)
20861c1775fSDan Murphy 			printf("%c", data & (1 << j) ? '1' : '0');
20961c1775fSDan Murphy 		printf("\n");
21061c1775fSDan Murphy 	}
21161c1775fSDan Murphy 
21261c1775fSDan Murphy 	return 0;
21361c1775fSDan Murphy }
21461c1775fSDan Murphy 
215*bb2277b3STom Rini static cmd_tbl_t cmd_tca642x[] = {
21661c1775fSDan Murphy 	U_BOOT_CMD_MKENT(device, 3, 0, (void *)TCA642X_CMD_DEVICE, "", ""),
21761c1775fSDan Murphy 	U_BOOT_CMD_MKENT(output, 4, 0, (void *)TCA642X_CMD_OUTPUT, "", ""),
21861c1775fSDan Murphy 	U_BOOT_CMD_MKENT(input, 3, 0, (void *)TCA642X_CMD_INPUT, "", ""),
21961c1775fSDan Murphy 	U_BOOT_CMD_MKENT(invert, 4, 0, (void *)TCA642X_CMD_INVERT, "", ""),
22061c1775fSDan Murphy 	U_BOOT_CMD_MKENT(info, 2, 0, (void *)TCA642X_CMD_INFO, "", ""),
22161c1775fSDan Murphy };
22261c1775fSDan Murphy 
do_tca642x(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])223*bb2277b3STom Rini static int do_tca642x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
22461c1775fSDan Murphy {
22561c1775fSDan Murphy 	static uchar chip = CONFIG_SYS_I2C_TCA642X_ADDR;
22661c1775fSDan Murphy 	int ret = CMD_RET_USAGE, val;
22761c1775fSDan Murphy 	uint8_t gpio_bank = 0;
22861c1775fSDan Murphy 	uint8_t bank_shift;
22961c1775fSDan Murphy 	ulong ul_arg2 = 0;
23061c1775fSDan Murphy 	ulong ul_arg3 = 0;
23161c1775fSDan Murphy 	cmd_tbl_t *c;
23261c1775fSDan Murphy 
23361c1775fSDan Murphy 	c = find_cmd_tbl(argv[1], cmd_tca642x, ARRAY_SIZE(cmd_tca642x));
23461c1775fSDan Murphy 
23561c1775fSDan Murphy 	/* All commands but "device" require 'maxargs' arguments */
23661c1775fSDan Murphy 	if (!c ||
23761c1775fSDan Murphy 	    !((argc == (c->maxargs)) ||
23861c1775fSDan Murphy 	    (((int)c->cmd == TCA642X_CMD_DEVICE) &&
23961c1775fSDan Murphy 	    (argc == (c->maxargs - 1))))) {
24061c1775fSDan Murphy 		return CMD_RET_USAGE;
24161c1775fSDan Murphy 	}
24261c1775fSDan Murphy 
24361c1775fSDan Murphy 	/* arg2 used as chip number or pin number */
24461c1775fSDan Murphy 	if (argc > 2)
24561c1775fSDan Murphy 		ul_arg2 = simple_strtoul(argv[2], NULL, 10);
24661c1775fSDan Murphy 
24761c1775fSDan Murphy 	/* arg3 used as pin or invert value */
24861c1775fSDan Murphy 	if (argc > 3) {
24961c1775fSDan Murphy 		ul_arg3 = simple_strtoul(argv[3], NULL, 10) & 0x1;
25061c1775fSDan Murphy 		if (ul_arg2 <= 7) {
25161c1775fSDan Murphy 			gpio_bank = 0;
25261c1775fSDan Murphy 		} else if ((ul_arg2 >= 10) && (ul_arg2 <= 17)) {
25361c1775fSDan Murphy 			gpio_bank = 1;
25461c1775fSDan Murphy 		} else if ((ul_arg2 >= 20) && (ul_arg2 <= 27)) {
25561c1775fSDan Murphy 			gpio_bank = 2;
25661c1775fSDan Murphy 		} else {
25761c1775fSDan Murphy 			printf("Requested pin is not available\n");
25861c1775fSDan Murphy 			ret = CMD_RET_FAILURE;
25961c1775fSDan Murphy 			goto error;
26061c1775fSDan Murphy 		}
26161c1775fSDan Murphy 	}
26261c1775fSDan Murphy 
26361c1775fSDan Murphy 	switch ((int)c->cmd) {
26461c1775fSDan Murphy 	case TCA642X_CMD_INFO:
26561c1775fSDan Murphy 		ret = tca642x_info(chip);
26661c1775fSDan Murphy 		if (ret)
26761c1775fSDan Murphy 			ret = CMD_RET_FAILURE;
26861c1775fSDan Murphy 		break;
26961c1775fSDan Murphy 
27061c1775fSDan Murphy 	case TCA642X_CMD_DEVICE:
27161c1775fSDan Murphy 		if (argc == 3)
27261c1775fSDan Murphy 			chip = (uint8_t)ul_arg2;
27361c1775fSDan Murphy 		printf("Current device address: 0x%x\n", chip);
27461c1775fSDan Murphy 		ret = CMD_RET_SUCCESS;
27561c1775fSDan Murphy 		break;
27661c1775fSDan Murphy 
27761c1775fSDan Murphy 	case TCA642X_CMD_INPUT:
27861c1775fSDan Murphy 		bank_shift = ul_arg2 - (gpio_bank * 10);
27961c1775fSDan Murphy 		ret = tca642x_set_dir(chip, gpio_bank, (1 << bank_shift),
28061c1775fSDan Murphy 				TCA642X_DIR_IN << bank_shift);
28161c1775fSDan Murphy 		val = (tca642x_get_val(chip, gpio_bank) &
28261c1775fSDan Murphy 				(1 << bank_shift)) != 0;
28361c1775fSDan Murphy 
28461c1775fSDan Murphy 		if (ret)
28561c1775fSDan Murphy 			ret = CMD_RET_FAILURE;
28661c1775fSDan Murphy 		else
28761c1775fSDan Murphy 			printf("chip 0x%02x, pin 0x%lx = %d\n", chip,
28861c1775fSDan Murphy 			       ul_arg2, val);
28961c1775fSDan Murphy 		break;
29061c1775fSDan Murphy 
29161c1775fSDan Murphy 	case TCA642X_CMD_OUTPUT:
29261c1775fSDan Murphy 		bank_shift = ul_arg2 - (gpio_bank * 10);
29361c1775fSDan Murphy 		ret = tca642x_set_dir(chip, gpio_bank, (1 << bank_shift),
29461c1775fSDan Murphy 				(TCA642X_DIR_OUT << bank_shift));
29561c1775fSDan Murphy 		if (!ret)
29661c1775fSDan Murphy 			ret = tca642x_set_val(chip,
29761c1775fSDan Murphy 					      gpio_bank, (1 << bank_shift),
29861c1775fSDan Murphy 					      (ul_arg3 << bank_shift));
29961c1775fSDan Murphy 		if (ret)
30061c1775fSDan Murphy 			ret = CMD_RET_FAILURE;
30161c1775fSDan Murphy 		break;
30261c1775fSDan Murphy 
30361c1775fSDan Murphy 	case TCA642X_CMD_INVERT:
30461c1775fSDan Murphy 		bank_shift = ul_arg2 - (gpio_bank * 10);
30561c1775fSDan Murphy 		ret = tca642x_set_pol(chip, gpio_bank, (1 << bank_shift),
30661c1775fSDan Murphy 					(ul_arg3 << bank_shift));
30761c1775fSDan Murphy 		if (ret)
30861c1775fSDan Murphy 			ret = CMD_RET_FAILURE;
30961c1775fSDan Murphy 		break;
31061c1775fSDan Murphy 	}
31161c1775fSDan Murphy error:
31261c1775fSDan Murphy 	if (ret == CMD_RET_FAILURE)
31361c1775fSDan Murphy 		eprintf("Error talking to chip at 0x%x\n", chip);
31461c1775fSDan Murphy 
31561c1775fSDan Murphy 	return ret;
31661c1775fSDan Murphy }
31761c1775fSDan Murphy 
31861c1775fSDan Murphy U_BOOT_CMD(
31961c1775fSDan Murphy 	tca642x,	5,	1,	do_tca642x,
32061c1775fSDan Murphy 	"tca642x gpio access",
32161c1775fSDan Murphy 	"device [dev]\n"
32261c1775fSDan Murphy 	"	- show or set current device address\n"
32361c1775fSDan Murphy 	"tca642x info\n"
32461c1775fSDan Murphy 	"	- display info for current chip\n"
32561c1775fSDan Murphy 	"tca642x output pin 0|1\n"
32661c1775fSDan Murphy 	"	- set pin as output and drive low or high\n"
32761c1775fSDan Murphy 	"tca642x invert pin 0|1\n"
32861c1775fSDan Murphy 	"	- disable/enable polarity inversion for reads\n"
32961c1775fSDan Murphy 	"tca642x input pin\n"
33061c1775fSDan Murphy 	"	- set pin as input and read value"
33161c1775fSDan Murphy );
33261c1775fSDan Murphy 
33361c1775fSDan Murphy #endif /* CONFIG_CMD_TCA642X */
334