command: Remove the cmd_tbl_t typedef
[oweals/u-boot.git] / 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 <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  */
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
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  */
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  */
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  */
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  */
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  */
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  */
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
216 static struct cmd_tbl cmd_tca642x[] = {
217         U_BOOT_CMD_MKENT(device, 3, 0, (void *)TCA642X_CMD_DEVICE, "", ""),
218         U_BOOT_CMD_MKENT(output, 4, 0, (void *)TCA642X_CMD_OUTPUT, "", ""),
219         U_BOOT_CMD_MKENT(input, 3, 0, (void *)TCA642X_CMD_INPUT, "", ""),
220         U_BOOT_CMD_MKENT(invert, 4, 0, (void *)TCA642X_CMD_INVERT, "", ""),
221         U_BOOT_CMD_MKENT(info, 2, 0, (void *)TCA642X_CMD_INFO, "", ""),
222 };
223
224 static int do_tca642x(struct cmd_tbl *cmdtp, int flag, int argc,
225                       char *const argv[])
226 {
227         static uchar chip = CONFIG_SYS_I2C_TCA642X_ADDR;
228         int ret = CMD_RET_USAGE, val;
229         uint8_t gpio_bank = 0;
230         uint8_t bank_shift;
231         ulong ul_arg2 = 0;
232         ulong ul_arg3 = 0;
233         struct cmd_tbl *c;
234
235         c = find_cmd_tbl(argv[1], cmd_tca642x, ARRAY_SIZE(cmd_tca642x));
236
237         /* All commands but "device" require 'maxargs' arguments */
238         if (!c ||
239             !((argc == (c->maxargs)) ||
240             (((int)c->cmd == TCA642X_CMD_DEVICE) &&
241             (argc == (c->maxargs - 1))))) {
242                 return CMD_RET_USAGE;
243         }
244
245         /* arg2 used as chip number or pin number */
246         if (argc > 2)
247                 ul_arg2 = simple_strtoul(argv[2], NULL, 10);
248
249         /* arg3 used as pin or invert value */
250         if (argc > 3) {
251                 ul_arg3 = simple_strtoul(argv[3], NULL, 10) & 0x1;
252                 if (ul_arg2 <= 7) {
253                         gpio_bank = 0;
254                 } else if ((ul_arg2 >= 10) && (ul_arg2 <= 17)) {
255                         gpio_bank = 1;
256                 } else if ((ul_arg2 >= 20) && (ul_arg2 <= 27)) {
257                         gpio_bank = 2;
258                 } else {
259                         printf("Requested pin is not available\n");
260                         ret = CMD_RET_FAILURE;
261                         goto error;
262                 }
263         }
264
265         switch ((int)c->cmd) {
266         case TCA642X_CMD_INFO:
267                 ret = tca642x_info(chip);
268                 if (ret)
269                         ret = CMD_RET_FAILURE;
270                 break;
271
272         case TCA642X_CMD_DEVICE:
273                 if (argc == 3)
274                         chip = (uint8_t)ul_arg2;
275                 printf("Current device address: 0x%x\n", chip);
276                 ret = CMD_RET_SUCCESS;
277                 break;
278
279         case TCA642X_CMD_INPUT:
280                 bank_shift = ul_arg2 - (gpio_bank * 10);
281                 ret = tca642x_set_dir(chip, gpio_bank, (1 << bank_shift),
282                                 TCA642X_DIR_IN << bank_shift);
283                 val = (tca642x_get_val(chip, gpio_bank) &
284                                 (1 << bank_shift)) != 0;
285
286                 if (ret)
287                         ret = CMD_RET_FAILURE;
288                 else
289                         printf("chip 0x%02x, pin 0x%lx = %d\n", chip,
290                                ul_arg2, val);
291                 break;
292
293         case TCA642X_CMD_OUTPUT:
294                 bank_shift = ul_arg2 - (gpio_bank * 10);
295                 ret = tca642x_set_dir(chip, gpio_bank, (1 << bank_shift),
296                                 (TCA642X_DIR_OUT << bank_shift));
297                 if (!ret)
298                         ret = tca642x_set_val(chip,
299                                               gpio_bank, (1 << bank_shift),
300                                               (ul_arg3 << bank_shift));
301                 if (ret)
302                         ret = CMD_RET_FAILURE;
303                 break;
304
305         case TCA642X_CMD_INVERT:
306                 bank_shift = ul_arg2 - (gpio_bank * 10);
307                 ret = tca642x_set_pol(chip, gpio_bank, (1 << bank_shift),
308                                         (ul_arg3 << bank_shift));
309                 if (ret)
310                         ret = CMD_RET_FAILURE;
311                 break;
312         }
313 error:
314         if (ret == CMD_RET_FAILURE)
315                 eprintf("Error talking to chip at 0x%x\n", chip);
316
317         return ret;
318 }
319
320 U_BOOT_CMD(
321         tca642x,        5,      1,      do_tca642x,
322         "tca642x gpio access",
323         "device [dev]\n"
324         "       - show or set current device address\n"
325         "tca642x info\n"
326         "       - display info for current chip\n"
327         "tca642x output pin 0|1\n"
328         "       - set pin as output and drive low or high\n"
329         "tca642x invert pin 0|1\n"
330         "       - disable/enable polarity inversion for reads\n"
331         "tca642x input pin\n"
332         "       - set pin as input and read value"
333 );
334
335 #endif /* CONFIG_CMD_TCA642X */