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