]> git.sur5r.net Git - u-boot/blob - drivers/gpio/tca642x.c
Merge branch 'master' of git://git.denx.de/u-boot-sh
[u-boot] / drivers / gpio / tca642x.c
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  */
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
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  */
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  */
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  */
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  */
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  */
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  */
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
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 */