| // 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 <axi.h> | |
| #include <command.h> | |
| #include <console.h> | |
| #include <display_options.h> | |
| #include <dm.h> | |
| #include <log.h> | |
| /* 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 = dectoul(argv[1], NULL); | |
| 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 = dectoul(argv[1], NULL); | |
| 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 = dectoul(argv[1], NULL); | |
| /* | |
| * Address is specified since argc >= 3 | |
| */ | |
| addr = hextoul(argv[2], NULL); | |
| /* | |
| * 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 = hextoul(argv[3], NULL); | |
| } | |
| 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 = dectoul(argv[1], NULL); | |
| 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 = hextoul(argv[2], NULL); | |
| /* Get the value to write */ | |
| writeval = hextoul(argv[3], NULL); | |
| /* Count ? */ | |
| if (argc == 5) | |
| count = hextoul(argv[4], NULL); | |
| 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; | |
| } | |
| U_BOOT_LONGHELP(axi, | |
| "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 | |
| ); |