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