// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2016 * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc * * (C) Copyright 2017, 2018 * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include /* Currently selected AXI bus device */ static struct udevice *axi_cur_bus; /* Transmission size from last command */ static uint dp_last_size; /* Address from last command */ static uint dp_last_addr; /* Number of bytes to display from last command; default = 64 */ static uint dp_last_length = 0x40; /** * show_bus() - Show devices on a single AXI bus * @bus: The AXI bus device to printt information for */ static void show_bus(struct udevice *bus) { struct udevice *dev; printf("Bus %d:\t%s", dev_seq(bus), bus->name); if (device_active(bus)) printf(" (active)"); printf("\n"); for (device_find_first_child(bus, &dev); dev; device_find_next_child(&dev)) printf(" %s\n", dev->name); } /** * axi_set_cur_bus() - Set the currently active AXI bus * @busnum: The number of the bus (i.e. its sequence number) that should be * made active * * The operations supplied by this command operate only on the currently active * bus. * * Return: 0 if OK, -ve on error */ static int axi_set_cur_bus(unsigned int busnum) { struct udevice *bus; struct udevice *dummy; int ret; /* Make sure that all sequence numbers are initialized */ for (uclass_first_device(UCLASS_AXI, &dummy); dummy; uclass_next_device(&dummy)) ; ret = uclass_get_device_by_seq(UCLASS_AXI, busnum, &bus); if (ret) { debug("%s: No bus %d\n", __func__, busnum); return ret; } axi_cur_bus = bus; return 0; } /** * axi_get_cur_bus() - Retrieve the currently active AXI bus device * @busp: Pointer to a struct udevice that receives the currently active bus * device * * Return: 0 if OK, -ve on error */ static int axi_get_cur_bus(struct udevice **busp) { if (!axi_cur_bus) { puts("No AXI bus selected\n"); return -ENODEV; } *busp = axi_cur_bus; return 0; } /* * Command handlers */ static int do_axi_show_bus(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct udevice *dummy; /* Make sure that all sequence numbers are initialized */ for (uclass_first_device(UCLASS_AXI, &dummy); dummy; uclass_next_device(&dummy)) ; if (argc == 1) { /* show all busses */ struct udevice *bus; for (uclass_first_device(UCLASS_AXI, &bus); bus; uclass_next_device(&bus)) show_bus(bus); } else { int i; /* show specific bus */ i = simple_strtoul(argv[1], NULL, 10); struct udevice *bus; int ret; ret = uclass_get_device_by_seq(UCLASS_AXI, i, &bus); if (ret) { printf("Invalid bus %d: err=%d\n", i, ret); return CMD_RET_FAILURE; } show_bus(bus); } return 0; } static int do_axi_bus_num(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { int ret = 0; int bus_no; if (argc == 1) { /* querying current setting */ struct udevice *bus; if (!axi_get_cur_bus(&bus)) bus_no = dev_seq(bus); else bus_no = -1; printf("Current bus is %d\n", bus_no); } else { bus_no = simple_strtoul(argv[1], NULL, 10); printf("Setting bus to %d\n", bus_no); ret = axi_set_cur_bus(bus_no); if (ret) printf("Failure changing bus number (%d)\n", ret); } return ret ? CMD_RET_FAILURE : 0; } static int do_axi_md(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { /* Print that many bytes per line */ const uint DISP_LINE_LEN = 16; u8 linebuf[DISP_LINE_LEN]; unsigned int k; ulong addr, length, size; ulong nbytes; enum axi_size_t axisize; int unitsize; /* * We use the last specified parameters, unless new ones are * entered. */ size = dp_last_size; addr = dp_last_addr; length = dp_last_length; if (argc < 3) return CMD_RET_USAGE; if (!axi_cur_bus) { puts("No AXI bus selected\n"); return CMD_RET_FAILURE; } if ((flag & CMD_FLAG_REPEAT) == 0) { size = simple_strtoul(argv[1], NULL, 10); /* * Address is specified since argc >= 3 */ addr = simple_strtoul(argv[2], NULL, 16); /* * If there's another parameter, it is the length to display; * length is the number of objects, not number of bytes */ if (argc > 3) length = simple_strtoul(argv[3], NULL, 16); } switch (size) { case 8: axisize = AXI_SIZE_8; unitsize = 1; break; case 16: axisize = AXI_SIZE_16; unitsize = 2; break; case 32: axisize = AXI_SIZE_32; unitsize = 4; break; default: printf("Unknown read size '%lu'\n", size); return CMD_RET_USAGE; }; nbytes = length * unitsize; do { ulong linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes; for (k = 0; k < linebytes / unitsize; ++k) { int ret = axi_read(axi_cur_bus, addr + k * unitsize, linebuf + k * unitsize, axisize); if (!ret) /* Continue if axi_read was successful */ continue; if (ret == -ENOSYS) printf("axi_read failed; read size not supported?\n"); else printf("axi_read failed: err = %d\n", ret); return CMD_RET_FAILURE; } print_buffer(addr, (void *)linebuf, unitsize, linebytes / unitsize, DISP_LINE_LEN / unitsize); nbytes -= max(linebytes, 1UL); addr += linebytes; if (ctrlc()) break; } while (nbytes > 0); dp_last_size = size; dp_last_addr = addr; dp_last_length = length; return 0; } static int do_axi_mw(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { u32 writeval; ulong addr, count, size; enum axi_size_t axisize; if (argc <= 3 || argc >= 6) return CMD_RET_USAGE; size = simple_strtoul(argv[1], NULL, 10); switch (size) { case 8: axisize = AXI_SIZE_8; break; case 16: axisize = AXI_SIZE_16; break; case 32: axisize = AXI_SIZE_32; break; default: printf("Unknown write size '%lu'\n", size); return CMD_RET_USAGE; }; /* Address is specified since argc > 4 */ addr = simple_strtoul(argv[2], NULL, 16); /* Get the value to write */ writeval = simple_strtoul(argv[3], NULL, 16); /* Count ? */ if (argc == 5) count = simple_strtoul(argv[4], NULL, 16); else count = 1; while (count-- > 0) { int ret = axi_write(axi_cur_bus, addr + count * sizeof(u32), &writeval, axisize); if (ret) { printf("axi_write failed: err = %d\n", ret); return CMD_RET_FAILURE; } } return 0; } static struct cmd_tbl cmd_axi_sub[] = { U_BOOT_CMD_MKENT(bus, 1, 1, do_axi_show_bus, "", ""), U_BOOT_CMD_MKENT(dev, 1, 1, do_axi_bus_num, "", ""), U_BOOT_CMD_MKENT(md, 4, 1, do_axi_md, "", ""), U_BOOT_CMD_MKENT(mw, 5, 1, do_axi_mw, "", ""), }; static int do_ihs_axi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct cmd_tbl *c; if (argc < 2) return CMD_RET_USAGE; /* Strip off leading 'axi' command argument */ argc--; argv++; /* Hand off rest of command line to sub-commands */ c = find_cmd_tbl(argv[0], &cmd_axi_sub[0], ARRAY_SIZE(cmd_axi_sub)); if (c) return c->cmd(cmdtp, flag, argc, argv); else return CMD_RET_USAGE; } static char axi_help_text[] = "bus - show AXI bus info\n" "axi dev [bus] - show or set current AXI bus to bus number [bus]\n" "axi md size addr [# of objects] - read from AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n" "axi mw size addr value [count] - write data [value] to AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n"; U_BOOT_CMD(axi, 7, 1, do_ihs_axi, "AXI sub-system", axi_help_text );