xref: /openbmc/u-boot/drivers/gpio/tca642x.c (revision 61c1775f)
1*61c1775fSDan Murphy /*
2*61c1775fSDan Murphy  * Copyright 2013 Texas Instruments, Inc.
3*61c1775fSDan Murphy  * Author: Dan Murphy <dmurphy@ti.com>
4*61c1775fSDan Murphy  *
5*61c1775fSDan Murphy  * Derived work from the pca953x.c driver
6*61c1775fSDan Murphy  *
7*61c1775fSDan Murphy  * This program is free software; you can redistribute it and/or
8*61c1775fSDan Murphy  * modify it under the terms of the GNU General Public License as
9*61c1775fSDan Murphy  * published by the Free Software Foundation; either version 2 of
10*61c1775fSDan Murphy  * the License, or (at your option) any later version.
11*61c1775fSDan Murphy  *
12*61c1775fSDan Murphy  * This program is distributed in the hope that it will be useful,
13*61c1775fSDan Murphy  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14*61c1775fSDan Murphy  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15*61c1775fSDan Murphy  * GNU General Public License for more details.
16*61c1775fSDan Murphy  *
17*61c1775fSDan Murphy  * You should have received a copy of the GNU General Public License
18*61c1775fSDan Murphy  * along with this program; if not, write to the Free Software
19*61c1775fSDan Murphy  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20*61c1775fSDan Murphy  * MA 02111-1307 USA
21*61c1775fSDan Murphy  */
22*61c1775fSDan Murphy 
23*61c1775fSDan Murphy #include <common.h>
24*61c1775fSDan Murphy #include <i2c.h>
25*61c1775fSDan Murphy #include <tca642x.h>
26*61c1775fSDan Murphy 
27*61c1775fSDan Murphy /* tca642x register address definitions */
28*61c1775fSDan Murphy struct tca642x_bank_info tca642x_regs[] = {
29*61c1775fSDan Murphy 	{ .input_reg = 0x00,
30*61c1775fSDan Murphy 	  .output_reg = 0x04,
31*61c1775fSDan Murphy 	  .polarity_reg = 0x08,
32*61c1775fSDan Murphy 	  .configuration_reg = 0x0c },
33*61c1775fSDan Murphy 	{ .input_reg = 0x01,
34*61c1775fSDan Murphy 	  .output_reg = 0x05,
35*61c1775fSDan Murphy 	  .polarity_reg = 0x09,
36*61c1775fSDan Murphy 	  .configuration_reg = 0x0d },
37*61c1775fSDan Murphy 	{ .input_reg = 0x02,
38*61c1775fSDan Murphy 	  .output_reg = 0x06,
39*61c1775fSDan Murphy 	  .polarity_reg = 0x0a,
40*61c1775fSDan Murphy 	  .configuration_reg = 0x0e },
41*61c1775fSDan Murphy };
42*61c1775fSDan Murphy 
43*61c1775fSDan Murphy /*
44*61c1775fSDan Murphy  * Modify masked bits in register
45*61c1775fSDan Murphy  */
46*61c1775fSDan Murphy static int tca642x_reg_write(uchar chip, uint8_t addr,
47*61c1775fSDan Murphy 		uint8_t reg_bit, uint8_t data)
48*61c1775fSDan Murphy {
49*61c1775fSDan Murphy 	uint8_t valw;
50*61c1775fSDan Murphy 	int org_bus_num;
51*61c1775fSDan Murphy 	int ret;
52*61c1775fSDan Murphy 
53*61c1775fSDan Murphy 	org_bus_num = i2c_get_bus_num();
54*61c1775fSDan Murphy 	i2c_set_bus_num(CONFIG_SYS_I2C_TCA642X_BUS_NUM);
55*61c1775fSDan Murphy 
56*61c1775fSDan Murphy 	if (i2c_read(chip, addr, 1, (uint8_t *)&valw, 1)) {
57*61c1775fSDan Murphy 		printf("Could not read before writing\n");
58*61c1775fSDan Murphy 		ret = -1;
59*61c1775fSDan Murphy 		goto error;
60*61c1775fSDan Murphy 	}
61*61c1775fSDan Murphy 	valw &= ~reg_bit;
62*61c1775fSDan Murphy 	valw |= data;
63*61c1775fSDan Murphy 
64*61c1775fSDan Murphy 	ret = i2c_write(chip, addr, 1, (u8 *)&valw, 1);
65*61c1775fSDan Murphy 
66*61c1775fSDan Murphy error:
67*61c1775fSDan Murphy 	i2c_set_bus_num(org_bus_num);
68*61c1775fSDan Murphy 	return ret;
69*61c1775fSDan Murphy }
70*61c1775fSDan Murphy 
71*61c1775fSDan Murphy static int tca642x_reg_read(uchar chip, uint8_t addr, uint8_t *data)
72*61c1775fSDan Murphy {
73*61c1775fSDan Murphy 	uint8_t valw;
74*61c1775fSDan Murphy 	int org_bus_num;
75*61c1775fSDan Murphy 	int ret = 0;
76*61c1775fSDan Murphy 
77*61c1775fSDan Murphy 	org_bus_num = i2c_get_bus_num();
78*61c1775fSDan Murphy 	i2c_set_bus_num(CONFIG_SYS_I2C_TCA642X_BUS_NUM);
79*61c1775fSDan Murphy 	if (i2c_read(chip, addr, 1, (u8 *)&valw, 1)) {
80*61c1775fSDan Murphy 		ret = -1;
81*61c1775fSDan Murphy 		goto error;
82*61c1775fSDan Murphy 	}
83*61c1775fSDan Murphy 
84*61c1775fSDan Murphy 	*data = valw;
85*61c1775fSDan Murphy 
86*61c1775fSDan Murphy error:
87*61c1775fSDan Murphy 	i2c_set_bus_num(org_bus_num);
88*61c1775fSDan Murphy 	return ret;
89*61c1775fSDan Murphy }
90*61c1775fSDan Murphy 
91*61c1775fSDan Murphy /*
92*61c1775fSDan Murphy  * Set output value of IO pins in 'reg_bit' to corresponding value in 'data'
93*61c1775fSDan Murphy  * 0 = low, 1 = high
94*61c1775fSDan Murphy  */
95*61c1775fSDan Murphy int tca642x_set_val(uchar chip, uint8_t gpio_bank,
96*61c1775fSDan Murphy 					uint8_t reg_bit, uint8_t data)
97*61c1775fSDan Murphy {
98*61c1775fSDan Murphy 	uint8_t out_reg = tca642x_regs[gpio_bank].output_reg;
99*61c1775fSDan Murphy 
100*61c1775fSDan Murphy 	return tca642x_reg_write(chip, out_reg, reg_bit, data);
101*61c1775fSDan Murphy }
102*61c1775fSDan Murphy 
103*61c1775fSDan Murphy /*
104*61c1775fSDan Murphy  * Set read polarity of IO pins in 'reg_bit' to corresponding value in 'data'
105*61c1775fSDan Murphy  * 0 = read pin value, 1 = read inverted pin value
106*61c1775fSDan Murphy  */
107*61c1775fSDan Murphy int tca642x_set_pol(uchar chip, uint8_t gpio_bank,
108*61c1775fSDan Murphy 					uint8_t reg_bit, uint8_t data)
109*61c1775fSDan Murphy {
110*61c1775fSDan Murphy 	uint8_t pol_reg = tca642x_regs[gpio_bank].polarity_reg;
111*61c1775fSDan Murphy 
112*61c1775fSDan Murphy 	return tca642x_reg_write(chip, pol_reg, reg_bit, data);
113*61c1775fSDan Murphy }
114*61c1775fSDan Murphy 
115*61c1775fSDan Murphy /*
116*61c1775fSDan Murphy  * Set direction of IO pins in 'reg_bit' to corresponding value in 'data'
117*61c1775fSDan Murphy  * 0 = output, 1 = input
118*61c1775fSDan Murphy  */
119*61c1775fSDan Murphy int tca642x_set_dir(uchar chip, uint8_t gpio_bank,
120*61c1775fSDan Murphy 					uint8_t reg_bit, uint8_t data)
121*61c1775fSDan Murphy {
122*61c1775fSDan Murphy 	uint8_t config_reg = tca642x_regs[gpio_bank].configuration_reg;
123*61c1775fSDan Murphy 
124*61c1775fSDan Murphy 	return tca642x_reg_write(chip, config_reg, reg_bit, data);
125*61c1775fSDan Murphy }
126*61c1775fSDan Murphy 
127*61c1775fSDan Murphy /*
128*61c1775fSDan Murphy  * Read current logic level of all IO pins
129*61c1775fSDan Murphy  */
130*61c1775fSDan Murphy int tca642x_get_val(uchar chip, uint8_t gpio_bank)
131*61c1775fSDan Murphy {
132*61c1775fSDan Murphy 	uint8_t val;
133*61c1775fSDan Murphy 	uint8_t in_reg = tca642x_regs[gpio_bank].input_reg;
134*61c1775fSDan Murphy 
135*61c1775fSDan Murphy 	if (tca642x_reg_read(chip, in_reg, &val) < 0)
136*61c1775fSDan Murphy 		return -1;
137*61c1775fSDan Murphy 
138*61c1775fSDan Murphy 	return (int)val;
139*61c1775fSDan Murphy }
140*61c1775fSDan Murphy 
141*61c1775fSDan Murphy /*
142*61c1775fSDan Murphy  * Set the inital register states for the tca642x gpio expander
143*61c1775fSDan Murphy  */
144*61c1775fSDan Murphy int tca642x_set_inital_state(uchar chip, struct tca642x_bank_info init_data[])
145*61c1775fSDan Murphy {
146*61c1775fSDan Murphy 	int i, ret;
147*61c1775fSDan Murphy 	uint8_t config_reg;
148*61c1775fSDan Murphy 	uint8_t polarity_reg;
149*61c1775fSDan Murphy 	uint8_t output_reg;
150*61c1775fSDan Murphy 
151*61c1775fSDan Murphy 	for (i = 0; i < 3; i++) {
152*61c1775fSDan Murphy 		config_reg = tca642x_regs[i].configuration_reg;
153*61c1775fSDan Murphy 		ret = tca642x_reg_write(chip, config_reg, 0xff,
154*61c1775fSDan Murphy 				init_data[i].configuration_reg);
155*61c1775fSDan Murphy 		polarity_reg = tca642x_regs[i].polarity_reg;
156*61c1775fSDan Murphy 		ret = tca642x_reg_write(chip, polarity_reg, 0xff,
157*61c1775fSDan Murphy 				init_data[i].polarity_reg);
158*61c1775fSDan Murphy 		output_reg = tca642x_regs[i].output_reg;
159*61c1775fSDan Murphy 		ret = tca642x_reg_write(chip, output_reg, 0xff,
160*61c1775fSDan Murphy 				init_data[i].output_reg);
161*61c1775fSDan Murphy 	}
162*61c1775fSDan Murphy 
163*61c1775fSDan Murphy 	return ret;
164*61c1775fSDan Murphy }
165*61c1775fSDan Murphy 
166*61c1775fSDan Murphy #ifdef CONFIG_CMD_TCA642X
167*61c1775fSDan Murphy /*
168*61c1775fSDan Murphy  * Display tca642x information
169*61c1775fSDan Murphy  */
170*61c1775fSDan Murphy static int tca642x_info(uchar chip)
171*61c1775fSDan Murphy {
172*61c1775fSDan Murphy 	int i, j;
173*61c1775fSDan Murphy 	uint8_t data;
174*61c1775fSDan Murphy 
175*61c1775fSDan Murphy 	printf("tca642x@ 0x%x (%d pins):\n", chip, 24);
176*61c1775fSDan Murphy 	for (i = 0; i < 3; i++) {
177*61c1775fSDan Murphy 		printf("Bank %i\n", i);
178*61c1775fSDan Murphy 		if (tca642x_reg_read(chip,
179*61c1775fSDan Murphy 				     tca642x_regs[i].configuration_reg,
180*61c1775fSDan Murphy 				     &data) < 0)
181*61c1775fSDan Murphy 			return -1;
182*61c1775fSDan Murphy 		printf("\tConfiguration: ");
183*61c1775fSDan Murphy 		for (j = 7; j >= 0; j--)
184*61c1775fSDan Murphy 			printf("%c", data & (1 << j) ? 'i' : 'o');
185*61c1775fSDan Murphy 		printf("\n");
186*61c1775fSDan Murphy 
187*61c1775fSDan Murphy 		if (tca642x_reg_read(chip,
188*61c1775fSDan Murphy 				     tca642x_regs[i].polarity_reg, &data) < 0)
189*61c1775fSDan Murphy 			return -1;
190*61c1775fSDan Murphy 		printf("\tPolarity: ");
191*61c1775fSDan Murphy 		for (j = 7; j >= 0; j--)
192*61c1775fSDan Murphy 			printf("%c", data & (1 << j) ? '1' : '0');
193*61c1775fSDan Murphy 		printf("\n");
194*61c1775fSDan Murphy 
195*61c1775fSDan Murphy 		if (tca642x_reg_read(chip,
196*61c1775fSDan Murphy 				     tca642x_regs[i].input_reg, &data) < 0)
197*61c1775fSDan Murphy 			return -1;
198*61c1775fSDan Murphy 		printf("\tInput value: ");
199*61c1775fSDan Murphy 		for (j = 7; j >= 0; j--)
200*61c1775fSDan Murphy 			printf("%c", data & (1 << j) ? '1' : '0');
201*61c1775fSDan Murphy 		printf("\n");
202*61c1775fSDan Murphy 
203*61c1775fSDan Murphy 		if (tca642x_reg_read(chip,
204*61c1775fSDan Murphy 				     tca642x_regs[i].output_reg, &data) < 0)
205*61c1775fSDan Murphy 			return -1;
206*61c1775fSDan Murphy 		printf("\tOutput value: ");
207*61c1775fSDan Murphy 		for (j = 7; j >= 0; j--)
208*61c1775fSDan Murphy 			printf("%c", data & (1 << j) ? '1' : '0');
209*61c1775fSDan Murphy 		printf("\n");
210*61c1775fSDan Murphy 	}
211*61c1775fSDan Murphy 
212*61c1775fSDan Murphy 	return 0;
213*61c1775fSDan Murphy }
214*61c1775fSDan Murphy 
215*61c1775fSDan Murphy cmd_tbl_t cmd_tca642x[] = {
216*61c1775fSDan Murphy 	U_BOOT_CMD_MKENT(device, 3, 0, (void *)TCA642X_CMD_DEVICE, "", ""),
217*61c1775fSDan Murphy 	U_BOOT_CMD_MKENT(output, 4, 0, (void *)TCA642X_CMD_OUTPUT, "", ""),
218*61c1775fSDan Murphy 	U_BOOT_CMD_MKENT(input, 3, 0, (void *)TCA642X_CMD_INPUT, "", ""),
219*61c1775fSDan Murphy 	U_BOOT_CMD_MKENT(invert, 4, 0, (void *)TCA642X_CMD_INVERT, "", ""),
220*61c1775fSDan Murphy 	U_BOOT_CMD_MKENT(info, 2, 0, (void *)TCA642X_CMD_INFO, "", ""),
221*61c1775fSDan Murphy };
222*61c1775fSDan Murphy 
223*61c1775fSDan Murphy int do_tca642x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
224*61c1775fSDan Murphy {
225*61c1775fSDan Murphy 	static uchar chip = CONFIG_SYS_I2C_TCA642X_ADDR;
226*61c1775fSDan Murphy 	int ret = CMD_RET_USAGE, val;
227*61c1775fSDan Murphy 	uint8_t gpio_bank = 0;
228*61c1775fSDan Murphy 	uint8_t bank_shift;
229*61c1775fSDan Murphy 	ulong ul_arg2 = 0;
230*61c1775fSDan Murphy 	ulong ul_arg3 = 0;
231*61c1775fSDan Murphy 	cmd_tbl_t *c;
232*61c1775fSDan Murphy 
233*61c1775fSDan Murphy 	c = find_cmd_tbl(argv[1], cmd_tca642x, ARRAY_SIZE(cmd_tca642x));
234*61c1775fSDan Murphy 
235*61c1775fSDan Murphy 	/* All commands but "device" require 'maxargs' arguments */
236*61c1775fSDan Murphy 	if (!c ||
237*61c1775fSDan Murphy 	    !((argc == (c->maxargs)) ||
238*61c1775fSDan Murphy 	    (((int)c->cmd == TCA642X_CMD_DEVICE) &&
239*61c1775fSDan Murphy 	    (argc == (c->maxargs - 1))))) {
240*61c1775fSDan Murphy 		return CMD_RET_USAGE;
241*61c1775fSDan Murphy 	}
242*61c1775fSDan Murphy 
243*61c1775fSDan Murphy 	/* arg2 used as chip number or pin number */
244*61c1775fSDan Murphy 	if (argc > 2)
245*61c1775fSDan Murphy 		ul_arg2 = simple_strtoul(argv[2], NULL, 10);
246*61c1775fSDan Murphy 
247*61c1775fSDan Murphy 	/* arg3 used as pin or invert value */
248*61c1775fSDan Murphy 	if (argc > 3) {
249*61c1775fSDan Murphy 		ul_arg3 = simple_strtoul(argv[3], NULL, 10) & 0x1;
250*61c1775fSDan Murphy 		if (ul_arg2 <= 7) {
251*61c1775fSDan Murphy 			gpio_bank = 0;
252*61c1775fSDan Murphy 		} else if ((ul_arg2 >= 10) && (ul_arg2 <= 17)) {
253*61c1775fSDan Murphy 			gpio_bank = 1;
254*61c1775fSDan Murphy 		} else if ((ul_arg2 >= 20) && (ul_arg2 <= 27)) {
255*61c1775fSDan Murphy 			gpio_bank = 2;
256*61c1775fSDan Murphy 		} else {
257*61c1775fSDan Murphy 			printf("Requested pin is not available\n");
258*61c1775fSDan Murphy 			ret = CMD_RET_FAILURE;
259*61c1775fSDan Murphy 			goto error;
260*61c1775fSDan Murphy 		}
261*61c1775fSDan Murphy 	}
262*61c1775fSDan Murphy 
263*61c1775fSDan Murphy 	switch ((int)c->cmd) {
264*61c1775fSDan Murphy 	case TCA642X_CMD_INFO:
265*61c1775fSDan Murphy 		ret = tca642x_info(chip);
266*61c1775fSDan Murphy 		if (ret)
267*61c1775fSDan Murphy 			ret = CMD_RET_FAILURE;
268*61c1775fSDan Murphy 		break;
269*61c1775fSDan Murphy 
270*61c1775fSDan Murphy 	case TCA642X_CMD_DEVICE:
271*61c1775fSDan Murphy 		if (argc == 3)
272*61c1775fSDan Murphy 			chip = (uint8_t)ul_arg2;
273*61c1775fSDan Murphy 		printf("Current device address: 0x%x\n", chip);
274*61c1775fSDan Murphy 		ret = CMD_RET_SUCCESS;
275*61c1775fSDan Murphy 		break;
276*61c1775fSDan Murphy 
277*61c1775fSDan Murphy 	case TCA642X_CMD_INPUT:
278*61c1775fSDan Murphy 		bank_shift = ul_arg2 - (gpio_bank * 10);
279*61c1775fSDan Murphy 		ret = tca642x_set_dir(chip, gpio_bank, (1 << bank_shift),
280*61c1775fSDan Murphy 				TCA642X_DIR_IN << bank_shift);
281*61c1775fSDan Murphy 		val = (tca642x_get_val(chip, gpio_bank) &
282*61c1775fSDan Murphy 				(1 << bank_shift)) != 0;
283*61c1775fSDan Murphy 
284*61c1775fSDan Murphy 		if (ret)
285*61c1775fSDan Murphy 			ret = CMD_RET_FAILURE;
286*61c1775fSDan Murphy 		else
287*61c1775fSDan Murphy 			printf("chip 0x%02x, pin 0x%lx = %d\n", chip,
288*61c1775fSDan Murphy 			       ul_arg2, val);
289*61c1775fSDan Murphy 		break;
290*61c1775fSDan Murphy 
291*61c1775fSDan Murphy 	case TCA642X_CMD_OUTPUT:
292*61c1775fSDan Murphy 		bank_shift = ul_arg2 - (gpio_bank * 10);
293*61c1775fSDan Murphy 		ret = tca642x_set_dir(chip, gpio_bank, (1 << bank_shift),
294*61c1775fSDan Murphy 				(TCA642X_DIR_OUT << bank_shift));
295*61c1775fSDan Murphy 		if (!ret)
296*61c1775fSDan Murphy 			ret = tca642x_set_val(chip,
297*61c1775fSDan Murphy 					      gpio_bank, (1 << bank_shift),
298*61c1775fSDan Murphy 					      (ul_arg3 << bank_shift));
299*61c1775fSDan Murphy 		if (ret)
300*61c1775fSDan Murphy 			ret = CMD_RET_FAILURE;
301*61c1775fSDan Murphy 		break;
302*61c1775fSDan Murphy 
303*61c1775fSDan Murphy 	case TCA642X_CMD_INVERT:
304*61c1775fSDan Murphy 		bank_shift = ul_arg2 - (gpio_bank * 10);
305*61c1775fSDan Murphy 		ret = tca642x_set_pol(chip, gpio_bank, (1 << bank_shift),
306*61c1775fSDan Murphy 					(ul_arg3 << bank_shift));
307*61c1775fSDan Murphy 		if (ret)
308*61c1775fSDan Murphy 			ret = CMD_RET_FAILURE;
309*61c1775fSDan Murphy 		break;
310*61c1775fSDan Murphy 	}
311*61c1775fSDan Murphy error:
312*61c1775fSDan Murphy 	if (ret == CMD_RET_FAILURE)
313*61c1775fSDan Murphy 		eprintf("Error talking to chip at 0x%x\n", chip);
314*61c1775fSDan Murphy 
315*61c1775fSDan Murphy 	return ret;
316*61c1775fSDan Murphy }
317*61c1775fSDan Murphy 
318*61c1775fSDan Murphy U_BOOT_CMD(
319*61c1775fSDan Murphy 	tca642x,	5,	1,	do_tca642x,
320*61c1775fSDan Murphy 	"tca642x gpio access",
321*61c1775fSDan Murphy 	"device [dev]\n"
322*61c1775fSDan Murphy 	"	- show or set current device address\n"
323*61c1775fSDan Murphy 	"tca642x info\n"
324*61c1775fSDan Murphy 	"	- display info for current chip\n"
325*61c1775fSDan Murphy 	"tca642x output pin 0|1\n"
326*61c1775fSDan Murphy 	"	- set pin as output and drive low or high\n"
327*61c1775fSDan Murphy 	"tca642x invert pin 0|1\n"
328*61c1775fSDan Murphy 	"	- disable/enable polarity inversion for reads\n"
329*61c1775fSDan Murphy 	"tca642x input pin\n"
330*61c1775fSDan Murphy 	"	- set pin as input and read value"
331*61c1775fSDan Murphy );
332*61c1775fSDan Murphy 
333*61c1775fSDan Murphy #endif /* CONFIG_CMD_TCA642X */
334