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