1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright 2025 Gateworks Corporation
4 */
5
6 #include <command.h>
7 #include <hexdump.h>
8 #include <i2c.h>
9 #include <dm.h>
10 #include <dm/device.h>
11 #include <dm/device_compat.h>
12 #include <dm/device-internal.h> // device_remove/device_unbind
13 #include <asm-generic/gpio.h>
14 #include <fdt_support.h>
15 #include <linux/delay.h>
16
17 #include "fsa.h"
18
19 static int fsa;
20 static struct udevice *fsa_gpiodevs[FSA_MAX] = { NULL };
21
22 /* find the ofnode of the FSA i2c bus */
fsa_get_ofnode(int fsa)23 static ofnode fsa_get_ofnode(int fsa)
24 {
25 char str[32];
26
27 /* by alias */
28 snprintf(str, sizeof(str), "fsa%d", fsa);
29 return ofnode_get_aliases_node(str);
30 }
31
fsa_get_dtnode(void * fdt,int fsa)32 static int fsa_get_dtnode(void *fdt, int fsa)
33 {
34 char str[32];
35
36 /* by alias */
37 snprintf(str, sizeof(str), "fsa%d", fsa);
38 return fdt_path_offset(fdt, fdt_get_alias(fdt, str));
39 }
40
41 static const char * const fsa_gpio_config_names[] = { "NC", "", "input", "output-low",
42 "output-high" };
43
fsa_gpio_config_name(struct fsa_gpio_desc * desc)44 static const char *fsa_gpio_config_name(struct fsa_gpio_desc *desc)
45 {
46 if (desc->config < ARRAY_SIZE(fsa_gpio_config_names))
47 return fsa_gpio_config_names[desc->config];
48 return NULL;
49 };
50
fsa_get_gpio_desc(struct fsa_gpio_desc * desc,char * str,int sz)51 static char *fsa_get_gpio_desc(struct fsa_gpio_desc *desc, char *str, int sz)
52 {
53 str[0] = 0;
54 if (desc->source == 0xff) {
55 snprintf(str, sz, "fsa_gpio%d : %s %s",
56 desc->offset + 1,
57 desc->name,
58 fsa_gpio_config_name(desc));
59 } else if (desc->config) {
60 snprintf(str, sz, "gpio@%02x_%02d: %s %s",
61 desc->source,
62 desc->offset,
63 desc->name,
64 fsa_gpio_config_name(desc));
65 }
66 return str;
67 }
68
fsa_show_gpio_descs(const char * prefix,int fsa,struct fsa_board_info * board_info,struct fsa_user_info * user_info)69 static void fsa_show_gpio_descs(const char *prefix, int fsa, struct fsa_board_info *board_info,
70 struct fsa_user_info *user_info)
71 {
72 char str[128];
73 int i;
74
75 /* display slot specific gpios */
76 for (i = 0; i < board_info->sockgpios; i++) {
77 fsa_get_gpio_desc(&user_info->gpios[i], str, sizeof(str));
78 printf("%s%-2d: %s\n", prefix, i, str);
79 }
80 /* display io-expander specific gpios */
81 if (fsa_gpiodevs[fsa]) {
82 for (i = board_info->sockgpios;
83 i < (board_info->sockgpios + board_info->ioexpgpios);
84 i++) {
85 fsa_get_gpio_desc(&user_info->gpios[i], str, sizeof(str));
86 printf("%s%-2d: %s\n", prefix, i, str);
87 }
88 }
89 }
90
91 /* detect gpio expander by address and deal with enabling/disabling/adding gpio expander to dt */
fsa_get_gpiodev(int fsa,int addr,struct udevice ** devp)92 static int fsa_get_gpiodev(int fsa, int addr, struct udevice **devp)
93 {
94 struct udevice *bus, *dev;
95 char gpio_name[32];
96 int ret;
97
98 ret = device_get_global_by_ofnode(fsa_get_ofnode(fsa), &bus);
99 if (ret)
100 return ret;
101
102 sprintf(gpio_name, "gpio@%02x", addr);
103
104 /* probe device on i2c bus */
105 ret = dm_i2c_probe(bus, addr, 0, &dev);
106 switch (ret) {
107 case -EREMOTEIO: /* chip is not present on i2c bus */
108 /* if device is in dt remove/unbind/disable it */
109 ret = device_find_child_by_name(bus, gpio_name, &dev);
110 if (ret)
111 return ret;
112 ret = ofnode_set_enabled(dev_ofnode(dev), false);
113 if (ret)
114 return ret;
115 ret = device_unbind(dev);
116 if (ret)
117 return ret;
118 ret = device_remove(dev, DM_REMOVE_NORMAL);
119 if (ret)
120 return ret;
121 return ret;
122 case -ENOSYS: /* chip found but driver invalid */
123 /* if device is in not in dt add/bind it */
124 return ret;
125 case 0: /* chip responded and driver bound */
126 break;
127 }
128
129 if (devp)
130 *devp = dev;
131 return 0;
132 }
133
134 /* add gpio's to gpio device: GPIO device must be probed before you can manipulate it */
fsa_config_gpios(int fsa,struct fsa_user_info * info,int gpios,struct udevice * dev)135 static int fsa_config_gpios(int fsa, struct fsa_user_info *info, int gpios, struct udevice *dev)
136 {
137 struct fsa_gpio_desc *desc;
138 struct gpio_desc gdesc;
139 struct udevice *gdev;
140 int i, ret, flags;
141 char name[32];
142
143 /* configure GPIO's */
144 for (i = 0; i < gpios; i++) {
145 desc = &info->gpios[i];
146 if (desc->config < FSA_GPIO_INPUT)
147 continue;
148 memset(&gdesc, 0, sizeof(gdesc));
149
150 if (desc->source == 0xff) {
151 /* Board specific IMX8M GPIO's: find dev of controller by line-name */
152 sprintf(name, "fsa%d_gpio%d", fsa, desc->offset + 1);
153 uclass_foreach_dev_probe(UCLASS_GPIO, gdev) {
154 ret = dev_read_stringlist_search(gdev, "gpio-line-names", name);
155 if (ret >= 0) {
156 gdesc.dev = gdev;
157 gdesc.offset = ret;
158 break;
159 }
160 }
161 } else {
162 /* port expander GPIOs */
163 gdesc.dev = dev;
164 gdesc.offset = desc->offset;
165 }
166
167 if (!gdesc.dev)
168 continue;
169
170 sprintf(name, "fsa%d_%s", fsa, desc->name);
171 switch (desc->config) {
172 case FSA_GPIO_INPUT:
173 flags = GPIOD_IS_IN;
174 break;
175 case FSA_GPIO_OUTPUT_LOW:
176 flags = GPIOD_IS_OUT;
177 break;
178 case FSA_GPIO_OUTPUT_HIGH:
179 flags = GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE;
180 break;
181 }
182 if (!dm_gpio_request(&gdesc, name))
183 dm_gpio_clrset_flags(&gdesc, GPIOD_MASK_DIR, flags);
184 }
185
186 return 0;
187 }
188
fsa_read_board_config(int fsa,struct fsa_board_info * info)189 static int fsa_read_board_config(int fsa, struct fsa_board_info *info)
190 {
191 struct udevice *dev;
192 int chksum;
193 int i, ret;
194 ofnode node;
195
196 /* find eeprom dev */
197 node = ofnode_find_subnode(fsa_get_ofnode(fsa), "eeprom@54");
198 if (!ofnode_valid(node))
199 return -EINVAL;
200 ret = device_get_global_by_ofnode(node, &dev);
201 if (ret)
202 return ret;
203
204 /* read eeprom */
205 ret = dm_i2c_read(dev, 0, (uint8_t *)info, sizeof(*info));
206 if (ret) {
207 dev_err(dev, "read failed: %d\n", ret);
208 return ret;
209 }
210
211 /* validate checksum */
212 for (chksum = 0, i = 0; i < (int)sizeof(*info) - 2; i++)
213 chksum += ((unsigned char *)info)[i];
214 if ((info->chksum[0] != ((chksum >> 8) & 0xff)) ||
215 (info->chksum[1] != (chksum & 0xff))) {
216 dev_err(dev, "FSA%d EEPROM: Invalid User Config Checksum\n", fsa);
217 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, info, sizeof(*info));
218 memset(info, 0, sizeof(*info));
219 return -EINVAL;
220 }
221
222 return 0;
223 }
224
fsa_read_user_config(int fsa,struct fsa_user_info * info)225 static int fsa_read_user_config(int fsa, struct fsa_user_info *info)
226 {
227 struct udevice *dev;
228 int chksum;
229 int i, ret;
230 ofnode node;
231
232 /* find eeprom dev */
233 node = ofnode_find_subnode(fsa_get_ofnode(fsa), "eeprom@55");
234 if (!ofnode_valid(node))
235 return -EINVAL;
236 ret = device_get_global_by_ofnode(node, &dev);
237 if (ret)
238 return ret;
239
240 /* read eeprom */
241 ret = dm_i2c_read(dev, 0, (uint8_t *)info, sizeof(*info));
242 if (ret) {
243 dev_err(dev, "read failed: %d\n", ret);
244 return ret;
245 }
246
247 /* validate checksum */
248 for (chksum = 0, i = 0; i < (int)sizeof(*info) - 2; i++)
249 chksum += ((unsigned char *)info)[i];
250 if ((info->chksum[0] != ((chksum >> 8) & 0xff)) ||
251 (info->chksum[1] != (chksum & 0xff))) {
252 dev_err(dev, "FSA%d EEPROM: Invalid User Config Checksum\n", fsa);
253 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, info, sizeof(*info));
254 memset(info, 0, sizeof(*info));
255 return -EINVAL;
256 }
257
258 return 0;
259 }
260
fsa_write_user_config(int fsa,struct fsa_user_info * info)261 static int fsa_write_user_config(int fsa, struct fsa_user_info *info)
262 {
263 struct udevice *bus, *dev;
264 int i, n, chunk, slave, base, ret;
265 ofnode node;
266 int chksum;
267
268 /* create checksum */
269 for (chksum = 0, i = 0; i < (int)sizeof(*info) - 2; i++)
270 chksum += ((unsigned char *)info)[i];
271 info->chksum[0] = chksum >> 8;
272 info->chksum[1] = chksum & 0xff;
273
274 /* find eeprom dev */
275 node = ofnode_find_subnode(fsa_get_ofnode(fsa), "eeprom@55");
276 ret = device_get_global_by_ofnode(node, &dev);
277 if (ret)
278 return ret;
279 bus = dev->parent;
280 base = dev_read_addr(dev);
281
282 /* write in 16byte chunks (multi-byte writes fail larger than that) */
283 chunk = 16;
284 slave = -1;
285 for (i = 0; i < sizeof(*info); i += chunk) {
286 /* select device based on offset */
287 if ((base + (i / 256)) != slave) {
288 slave = base + (i / 256);
289 ret = i2c_get_chip(bus, slave, 1, &dev);
290 if (ret) {
291 dev_err(bus, "failed to get eeprom@0x%02x: %d\n", slave, ret);
292 return ret;
293 }
294 }
295 /* select byte count */
296 n = sizeof(*info) - i;
297 if (n > chunk)
298 n = chunk;
299 ret = dm_i2c_write(dev, i % 256, (uint8_t *)info + i, n);
300 if (ret) {
301 dev_err(dev, "write failed: %d\n", ret);
302 return ret;
303 }
304 mdelay(11);
305 }
306
307 return ret;
308 }
309
fsa_detect(int fsa,struct fsa_board_info * board_info,struct fsa_user_info * user_info,bool gpio)310 static int fsa_detect(int fsa, struct fsa_board_info *board_info, struct fsa_user_info *user_info,
311 bool gpio)
312 {
313 int ret;
314
315 ret = fsa_read_board_config(fsa, board_info);
316 if (ret)
317 return ret;
318 if (user_info) {
319 ret = fsa_read_user_config(fsa, user_info);
320 if (ret)
321 return ret;
322 /* detect port expander */
323 if (gpio && !fsa_get_gpiodev(fsa, 0x20, &fsa_gpiodevs[fsa]))
324 fsa_config_gpios(fsa, user_info,
325 board_info->sockgpios + board_info->ioexpgpios,
326 fsa_gpiodevs[fsa]);
327 }
328
329 return ret;
330 }
331
ft_fixup_stringlist_elem(void * fdt,int offset,const char * prop,int elem,const char * val)332 static int ft_fixup_stringlist_elem(void *fdt, int offset, const char *prop, int elem,
333 const char *val)
334 {
335 const char *list, *end;
336 char *new, *buf;
337 int length;
338 int sz = 0;
339 int i = 0;
340 int ret;
341
342 if (offset < 0 || elem < 0 || !val) {
343 printf("%s -EINVAL\n", __func__);
344 return -EINVAL;
345 }
346
347 list = fdt_getprop(fdt, offset, prop, &length);
348
349 /* no property or invalid params */
350 if (!list || length < 0) {
351 printf("%s failed - no property\n", __func__);
352 return -EINVAL;
353 }
354
355 /* create new buffer with enough space */
356 buf = calloc(1, length + strlen(val));
357 new = buf;
358
359 /* iterate over current stringlist and build new list into buf */
360 end = list + length;
361 while (list < end) {
362 length = strnlen(list, end - list) + 1;
363 sz += length;
364 /* insert new value into buf */
365 if (elem == i) {
366 strcpy(new, val);
367 new += strlen(val) + 1;
368 } else {
369 strcpy(new, list);
370 new += length;
371 }
372 list += length;
373 i++;
374 }
375 length = new - buf;
376 ret = fdt_setprop(fdt, offset, prop, buf, length);
377 free(buf);
378 if (ret)
379 printf("%s failed %d\n", __func__, ret);
380
381 return ret;
382 }
383
ft_fixup_fsa_gpio_name(void * fdt,int offset,int fsa,int gpio,const char * name)384 static int ft_fixup_fsa_gpio_name(void *fdt, int offset, int fsa, int gpio, const char *name)
385 {
386 const char *prop = "gpio-line-names";
387 char str[32];
388
389 sprintf(str, "fsa%d_%s", fsa, name);
390
391 if (!fdt_getprop(fdt, offset, prop, NULL)) {
392 char buf[16] = { 0 };
393
394 fdt_setprop(fdt, offset, prop, &buf, sizeof(buf));
395 }
396
397 return ft_fixup_stringlist_elem(fdt, offset, prop, gpio, str);
398 }
399
fsa_show_details(int fsa,struct fsa_board_info * board,struct fsa_user_info * user)400 static void fsa_show_details(int fsa, struct fsa_board_info *board, struct fsa_user_info *user)
401 {
402 printf("FSA%d: %s\n", fsa, board->model);
403 printf("description: %s\n", user->desc);
404 printf("overlay: %s\n", user->overlay);
405 fsa_show_gpio_descs("\t", fsa, board, user);
406 }
407
fsa_init(void)408 int fsa_init(void)
409 {
410 struct fsa_board_info board_info;
411 struct fsa_user_info user_info;
412 int fsa, ret;
413
414 for (fsa = 1; fsa < FSA_MAX; fsa++) {
415 ret = fsa_detect(fsa, &board_info, &user_info, true);
416 if (!ret)
417 printf("FSA%d: %s %s\n", fsa, board_info.model, user_info.desc);
418 }
419
420 return 0;
421 }
422
fsa_show(void)423 int fsa_show(void)
424 {
425 struct fsa_board_info board_info;
426 int fsa, ret;
427
428 for (fsa = 1; fsa < FSA_MAX; fsa++) {
429 ret = fsa_detect(fsa, &board_info, NULL, false);
430 if (!ret) {
431 printf("FSA%d : %s %d %02x-%02x-%02x%02x\n", fsa,
432 board_info.model, board_info.serial,
433 board_info.mfgdate[0], board_info.mfgdate[1],
434 board_info.mfgdate[2], board_info.mfgdate[3]);
435 }
436 }
437 return 0;
438 }
439
440 /* fixup gpio line names for fsa gpios */
fsa_ft_fixup(void * fdt)441 int fsa_ft_fixup(void *fdt)
442 {
443 struct fsa_board_info board_info;
444 struct fsa_user_info user_info;
445 int fsa, i, ret;
446 char path[128];
447 char str[32];
448 ofnode node;
449 int off;
450
451 /* iterate over FSA's and rename gpio's */
452 for (fsa = 1; fsa < FSA_MAX; fsa++) {
453 /* disable FSA ioexp node if disabled in controlling dt */
454 off = fdt_subnode_offset(fdt, fsa_get_dtnode(fdt, fsa), "gpio@20");
455 if (off >= 0) {
456 if (!fdt_get_path(fdt, off, path, sizeof(path))) {
457 node = ofnode_path(path);
458 if (ofnode_valid(node) && !ofnode_is_enabled(node))
459 fdt_setprop_string(fdt, off, "status", "disabled");
460 }
461 }
462
463 /* detect FSA eeprom */
464 if (fsa_detect(fsa, &board_info, &user_info, false))
465 continue;
466
467 /* configure GPIO's */
468 for (i = 0; i < board_info.sockgpios + board_info.ioexpgpios; i++) {
469 if (user_info.gpios[i].config < FSA_GPIO_INPUT)
470 continue;
471
472 if (user_info.gpios[i].source == 0xff) {
473 /* Board specific IMX8M GPIO's */
474 for (off = fdt_node_offset_by_prop_value(fdt, 0,
475 "gpio-controller", NULL,
476 0);
477 off >= 0;
478 off = fdt_node_offset_by_prop_value(fdt, off,
479 "gpio-controller", NULL,
480 0)
481 ) {
482 sprintf(str, "fsa%d_gpio%d", fsa,
483 user_info.gpios[i].offset + 1);
484 ret = fdt_stringlist_search(fdt, off, "gpio-line-names",
485 str);
486 if (ret >= 0) {
487 ft_fixup_fsa_gpio_name(fdt, off, fsa, ret,
488 user_info.gpios[i].name);
489 break;
490 }
491 }
492 } else {
493 /* port expander GPIOs */
494 off = fdt_subnode_offset(fdt, fsa_get_dtnode(fdt, fsa), "gpio@20");
495 ft_fixup_fsa_gpio_name(fdt, off, fsa, user_info.gpios[i].offset,
496 user_info.gpios[i].name);
497 }
498 }
499 }
500
501 return 0;
502 }
503
do_fsa_dev(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])504 static int do_fsa_dev(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
505 {
506 struct fsa_board_info board_info;
507 struct fsa_user_info user_info;
508 int i;
509
510 if (argc < 2) {
511 /* list FSAs */
512 printf("detecting FSA Adapters:\n");
513 for (i = 1; i < FSA_MAX; i++) {
514 if (!fsa_read_board_config(i, &board_info) &&
515 !fsa_read_user_config(i, &user_info))
516 printf("FSA%d : %s %s\n", i, board_info.model, user_info.desc);
517 }
518 } else {
519 /* select FSA */
520 fsa = simple_strtoul(argv[1], NULL, 10);
521 }
522
523 if (fsa) {
524 /* read FSA */
525 if (!fsa_read_board_config(fsa, &board_info) &&
526 !fsa_read_user_config(fsa, &user_info)) {
527 printf("selected:\n");
528 fsa_show_details(fsa, &board_info, &user_info);
529 } else {
530 printf("FSA%d not detected\n", fsa);
531 fsa = 0;
532 }
533 } else {
534 printf("no FSA currently selected\n");
535 }
536
537 return 0;
538 }
539
do_fsa_desc(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])540 static int do_fsa_desc(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
541 {
542 struct fsa_board_info board_info;
543 struct fsa_user_info user_info;
544
545 /* strip off leading cmd arg */
546 argc--;
547 argv++;
548
549 if (!fsa) {
550 printf("No FSA selected\n");
551 return CMD_RET_USAGE;
552 }
553
554 if (fsa_read_board_config(fsa, &board_info) || fsa_read_user_config(fsa, &user_info)) {
555 printf("can't detect FSA%d\n", fsa);
556 return CMD_RET_USAGE;
557 }
558
559 /* set */
560 if (argc) {
561 strlcpy(user_info.desc, argv[0], sizeof(user_info.desc));
562 if (fsa_write_user_config(fsa, &user_info))
563 return CMD_RET_FAILURE;
564 }
565
566 /* show */
567 fsa_show_details(fsa, &board_info, &user_info);
568
569 return CMD_RET_SUCCESS;
570 }
571
do_fsa_overlay(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])572 static int do_fsa_overlay(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
573 {
574 struct fsa_board_info board_info;
575 struct fsa_user_info user_info;
576
577 /* strip off leading cmd arg */
578 argc--;
579 argv++;
580
581 if (!fsa) {
582 printf("No FSA selected\n");
583 return CMD_RET_USAGE;
584 }
585
586 if (fsa_read_board_config(fsa, &board_info) || fsa_read_user_config(fsa, &user_info)) {
587 printf("can't detect FSA%d\n", fsa);
588 return CMD_RET_USAGE;
589 }
590
591 /* set */
592 if (argc) {
593 strlcpy(user_info.overlay, argv[0], sizeof(user_info.overlay));
594 if (fsa_write_user_config(fsa, &user_info))
595 return CMD_RET_FAILURE;
596 }
597
598 /* show */
599 fsa_show_details(fsa, &board_info, &user_info);
600
601 return CMD_RET_SUCCESS;
602 }
603
do_fsa_gpio(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])604 static int do_fsa_gpio(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
605 {
606 struct fsa_board_info board_info;
607 struct fsa_user_info user_info;
608 struct fsa_gpio_desc desc;
609 char str[64];
610 int i, j;
611
612 /* strip off leading cmd arg */
613 argc--;
614 argv++;
615
616 if (!fsa) {
617 printf("No FSA selected\n");
618 return CMD_RET_USAGE;
619 }
620
621 if (fsa_read_board_config(fsa, &board_info) || fsa_read_user_config(fsa, &user_info)) {
622 printf("can't detect FSA%d\n", fsa);
623 return CMD_RET_USAGE;
624 }
625
626 if (!argc) {
627 /* show all gpios */
628 fsa_show_gpio_descs("\t", fsa, &board_info, &user_info);
629 return CMD_RET_SUCCESS;
630 }
631
632 if (!isdigit(argv[0][0])) {
633 printf("invalid gpio offset: %s\n", argv[0]);
634 return CMD_RET_USAGE;
635 }
636
637 memset(&desc, 0, sizeof(desc));
638 i = simple_strtoul(argv[0], NULL, 10);
639
640 if (i >= 0 && i < board_info.sockgpios) {
641 desc.offset = i;
642 desc.source = 0xff;
643 } else if (i >= board_info.sockgpios &&
644 i < (board_info.sockgpios + board_info.ioexpgpios) &&
645 fsa_gpiodevs[fsa]) {
646 desc.offset = i - board_info.sockgpios;
647 desc.source = 0x20;
648 } else {
649 printf("invalid index %d", i);
650 return CMD_RET_FAILURE;
651 }
652
653 if (argc > 1) {
654 if (user_info.gpios[i].config == FSA_GPIO_NC) {
655 printf("can not alter NC gpio\n");
656 return CMD_RET_FAILURE;
657 }
658 strlcpy(desc.name, argv[1], sizeof(desc.name));
659 if (!*desc.name) {
660 printf("FSA%d %s erasing gpio %d\n", fsa, board_info.model, i);
661 memset(&user_info.gpios[i], 0, sizeof(desc));
662 if (fsa_write_user_config(fsa, &user_info))
663 return CMD_RET_FAILURE;
664 return CMD_RET_SUCCESS;
665 }
666 }
667 if (argc > 2) {
668 if (user_info.gpios[i].config == FSA_GPIO_NC) {
669 printf("can not alter NC gpio\n");
670 return CMD_RET_FAILURE;
671 }
672 for (j = 1; j < ARRAY_SIZE(fsa_gpio_config_names); j++) {
673 if (!strcasecmp(argv[2], fsa_gpio_config_names[j])) {
674 desc.config = j;
675 break;
676 }
677 };
678 if (j >= ARRAY_SIZE(fsa_gpio_config_names)) {
679 printf("invalid config type '%s\n", argv[2]);
680 return CMD_RET_FAILURE;
681 }
682 }
683
684 /* show a specific gpio */
685 if (argc == 1) {
686 printf("FSA%d %s showing gpio %d\n", fsa, board_info.model, i);
687 printf("%s\n", fsa_get_gpio_desc(&user_info.gpios[i], str, sizeof(str)));
688 return CMD_RET_SUCCESS;
689 }
690
691 /* set a specific gpio */
692 else if (argc == 3) {
693 printf("FSA%d %s updating gpio %d\n", fsa, board_info.model, i);
694 memcpy(&user_info.gpios[i], &desc, sizeof(desc));
695 if (fsa_write_user_config(fsa, &user_info))
696 return CMD_RET_FAILURE;
697 return CMD_RET_SUCCESS;
698 }
699
700 return CMD_RET_USAGE;
701 }
702
703 static struct cmd_tbl cmd_fsa_sub[] = {
704 U_BOOT_CMD_MKENT(dev, 1, 1, do_fsa_dev, "", ""),
705 U_BOOT_CMD_MKENT(gpio, 4, 1, do_fsa_gpio, "", ""),
706 U_BOOT_CMD_MKENT(description, 1, 1, do_fsa_desc, "", ""),
707 U_BOOT_CMD_MKENT(overlay, 1, 1, do_fsa_overlay, "", ""),
708 };
709
do_fsa(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])710 static int do_fsa(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
711 {
712 struct cmd_tbl *c;
713
714 /* strip off leading fsa arg */
715 argc--;
716 argv++;
717
718 c = find_cmd_tbl(argv[0], cmd_fsa_sub, ARRAY_SIZE(cmd_fsa_sub));
719 if (c)
720 return c->cmd(cmdtp, flag, argc, argv);
721 return CMD_RET_USAGE;
722 }
723
724 U_BOOT_LONGHELP(fsa,
725 "dev [dev] - show or set current FSA adapter\n"
726 "fsa gpio - show current gpio descriptors\n"
727 "fsa gpio [<offset>]|[<offset> <source>] - show a specific gpio descriptor\n"
728 "fsa gpio [<offset> <name> <input|output-low|output-high> [source]] - set a gpio descriptor\n"
729 "fsa description [description] - show or set the FSA user description string\n"
730 "fsa overlay [overlay] - show or set the FSA overlay string\n"
731 );
732
733 U_BOOT_CMD(fsa, 6, 1, do_fsa,
734 "Flexible Socket Adapter",
735 fsa_help_text
736 );
737