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