1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2016 4 * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc 5 * 6 * (C) Copyright 2017, 2018 7 * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc 8 * 9 * SPDX-License-Identifier: GPL-2.0+ 10 */ 11 12#include <common.h> 13#include <axi.h> 14#include <command.h> 15#include <console.h> 16#include <display_options.h> 17#include <dm.h> 18#include <log.h> 19 20/* Currently selected AXI bus device */ 21static struct udevice *axi_cur_bus; 22/* Transmission size from last command */ 23static uint dp_last_size; 24/* Address from last command */ 25static uint dp_last_addr; 26/* Number of bytes to display from last command; default = 64 */ 27static uint dp_last_length = 0x40; 28 29/** 30 * show_bus() - Show devices on a single AXI bus 31 * @bus: The AXI bus device to printt information for 32 */ 33static void show_bus(struct udevice *bus) 34{ 35 struct udevice *dev; 36 37 printf("Bus %d:\t%s", dev_seq(bus), bus->name); 38 if (device_active(bus)) 39 printf(" (active)"); 40 printf("\n"); 41 for (device_find_first_child(bus, &dev); 42 dev; 43 device_find_next_child(&dev)) 44 printf(" %s\n", dev->name); 45} 46 47/** 48 * axi_set_cur_bus() - Set the currently active AXI bus 49 * @busnum: The number of the bus (i.e. its sequence number) that should be 50 * made active 51 * 52 * The operations supplied by this command operate only on the currently active 53 * bus. 54 * 55 * Return: 0 if OK, -ve on error 56 */ 57static int axi_set_cur_bus(unsigned int busnum) 58{ 59 struct udevice *bus; 60 struct udevice *dummy; 61 int ret; 62 63 /* Make sure that all sequence numbers are initialized */ 64 for (uclass_first_device(UCLASS_AXI, &dummy); 65 dummy; 66 uclass_next_device(&dummy)) 67 ; 68 69 ret = uclass_get_device_by_seq(UCLASS_AXI, busnum, &bus); 70 if (ret) { 71 debug("%s: No bus %d\n", __func__, busnum); 72 return ret; 73 } 74 axi_cur_bus = bus; 75 76 return 0; 77} 78 79/** 80 * axi_get_cur_bus() - Retrieve the currently active AXI bus device 81 * @busp: Pointer to a struct udevice that receives the currently active bus 82 * device 83 * 84 * Return: 0 if OK, -ve on error 85 */ 86static int axi_get_cur_bus(struct udevice **busp) 87{ 88 if (!axi_cur_bus) { 89 puts("No AXI bus selected\n"); 90 return -ENODEV; 91 } 92 *busp = axi_cur_bus; 93 94 return 0; 95} 96 97/* 98 * Command handlers 99 */ 100 101static int do_axi_show_bus(struct cmd_tbl *cmdtp, int flag, int argc, 102 char *const argv[]) 103{ 104 struct udevice *dummy; 105 106 /* Make sure that all sequence numbers are initialized */ 107 for (uclass_first_device(UCLASS_AXI, &dummy); 108 dummy; 109 uclass_next_device(&dummy)) 110 ; 111 112 if (argc == 1) { 113 /* show all busses */ 114 struct udevice *bus; 115 116 for (uclass_first_device(UCLASS_AXI, &bus); 117 bus; 118 uclass_next_device(&bus)) 119 show_bus(bus); 120 } else { 121 int i; 122 123 /* show specific bus */ 124 i = dectoul(argv[1], NULL); 125 126 struct udevice *bus; 127 int ret; 128 129 ret = uclass_get_device_by_seq(UCLASS_AXI, i, &bus); 130 if (ret) { 131 printf("Invalid bus %d: err=%d\n", i, ret); 132 return CMD_RET_FAILURE; 133 } 134 show_bus(bus); 135 } 136 137 return 0; 138} 139 140static int do_axi_bus_num(struct cmd_tbl *cmdtp, int flag, int argc, 141 char *const argv[]) 142{ 143 int ret = 0; 144 int bus_no; 145 146 if (argc == 1) { 147 /* querying current setting */ 148 struct udevice *bus; 149 150 if (!axi_get_cur_bus(&bus)) 151 bus_no = dev_seq(bus); 152 else 153 bus_no = -1; 154 155 printf("Current bus is %d\n", bus_no); 156 } else { 157 bus_no = dectoul(argv[1], NULL); 158 printf("Setting bus to %d\n", bus_no); 159 160 ret = axi_set_cur_bus(bus_no); 161 if (ret) 162 printf("Failure changing bus number (%d)\n", ret); 163 } 164 165 return ret ? CMD_RET_FAILURE : 0; 166} 167 168static int do_axi_md(struct cmd_tbl *cmdtp, int flag, int argc, 169 char *const argv[]) 170{ 171 /* Print that many bytes per line */ 172 const uint DISP_LINE_LEN = 16; 173 u8 linebuf[DISP_LINE_LEN]; 174 unsigned int k; 175 ulong addr, length, size; 176 ulong nbytes; 177 enum axi_size_t axisize; 178 int unitsize; 179 180 /* 181 * We use the last specified parameters, unless new ones are 182 * entered. 183 */ 184 size = dp_last_size; 185 addr = dp_last_addr; 186 length = dp_last_length; 187 188 if (argc < 3) 189 return CMD_RET_USAGE; 190 191 if (!axi_cur_bus) { 192 puts("No AXI bus selected\n"); 193 return CMD_RET_FAILURE; 194 } 195 196 if ((flag & CMD_FLAG_REPEAT) == 0) { 197 size = dectoul(argv[1], NULL); 198 199 /* 200 * Address is specified since argc >= 3 201 */ 202 addr = hextoul(argv[2], NULL); 203 204 /* 205 * If there's another parameter, it is the length to display; 206 * length is the number of objects, not number of bytes 207 */ 208 if (argc > 3) 209 length = hextoul(argv[3], NULL); 210 } 211 212 switch (size) { 213 case 8: 214 axisize = AXI_SIZE_8; 215 unitsize = 1; 216 break; 217 case 16: 218 axisize = AXI_SIZE_16; 219 unitsize = 2; 220 break; 221 case 32: 222 axisize = AXI_SIZE_32; 223 unitsize = 4; 224 break; 225 default: 226 printf("Unknown read size '%lu'\n", size); 227 return CMD_RET_USAGE; 228 }; 229 230 nbytes = length * unitsize; 231 do { 232 ulong linebytes = (nbytes > DISP_LINE_LEN) ? 233 DISP_LINE_LEN : nbytes; 234 235 for (k = 0; k < linebytes / unitsize; ++k) { 236 int ret = axi_read(axi_cur_bus, addr + k * unitsize, 237 linebuf + k * unitsize, axisize); 238 239 if (!ret) /* Continue if axi_read was successful */ 240 continue; 241 242 if (ret == -ENOSYS) 243 printf("axi_read failed; read size not supported?\n"); 244 else 245 printf("axi_read failed: err = %d\n", ret); 246 247 return CMD_RET_FAILURE; 248 } 249 print_buffer(addr, (void *)linebuf, unitsize, 250 linebytes / unitsize, 251 DISP_LINE_LEN / unitsize); 252 253 nbytes -= max(linebytes, 1UL); 254 addr += linebytes; 255 256 if (ctrlc()) 257 break; 258 } while (nbytes > 0); 259 260 dp_last_size = size; 261 dp_last_addr = addr; 262 dp_last_length = length; 263 264 return 0; 265} 266 267static int do_axi_mw(struct cmd_tbl *cmdtp, int flag, int argc, 268 char *const argv[]) 269{ 270 u32 writeval; 271 ulong addr, count, size; 272 enum axi_size_t axisize; 273 274 if (argc <= 3 || argc >= 6) 275 return CMD_RET_USAGE; 276 277 size = dectoul(argv[1], NULL); 278 279 switch (size) { 280 case 8: 281 axisize = AXI_SIZE_8; 282 break; 283 case 16: 284 axisize = AXI_SIZE_16; 285 break; 286 case 32: 287 axisize = AXI_SIZE_32; 288 break; 289 default: 290 printf("Unknown write size '%lu'\n", size); 291 return CMD_RET_USAGE; 292 }; 293 294 /* Address is specified since argc > 4 */ 295 addr = hextoul(argv[2], NULL); 296 297 /* Get the value to write */ 298 writeval = hextoul(argv[3], NULL); 299 300 /* Count ? */ 301 if (argc == 5) 302 count = hextoul(argv[4], NULL); 303 else 304 count = 1; 305 306 while (count-- > 0) { 307 int ret = axi_write(axi_cur_bus, addr + count * sizeof(u32), 308 &writeval, axisize); 309 310 if (ret) { 311 printf("axi_write failed: err = %d\n", ret); 312 return CMD_RET_FAILURE; 313 } 314 } 315 316 return 0; 317} 318 319static struct cmd_tbl cmd_axi_sub[] = { 320 U_BOOT_CMD_MKENT(bus, 1, 1, do_axi_show_bus, "", ""), 321 U_BOOT_CMD_MKENT(dev, 1, 1, do_axi_bus_num, "", ""), 322 U_BOOT_CMD_MKENT(md, 4, 1, do_axi_md, "", ""), 323 U_BOOT_CMD_MKENT(mw, 5, 1, do_axi_mw, "", ""), 324}; 325 326static int do_ihs_axi(struct cmd_tbl *cmdtp, int flag, int argc, 327 char *const argv[]) 328{ 329 struct cmd_tbl *c; 330 331 if (argc < 2) 332 return CMD_RET_USAGE; 333 334 /* Strip off leading 'axi' command argument */ 335 argc--; 336 argv++; 337 338 /* Hand off rest of command line to sub-commands */ 339 c = find_cmd_tbl(argv[0], &cmd_axi_sub[0], ARRAY_SIZE(cmd_axi_sub)); 340 341 if (c) 342 return c->cmd(cmdtp, flag, argc, argv); 343 else 344 return CMD_RET_USAGE; 345} 346 347U_BOOT_LONGHELP(axi, 348 "bus - show AXI bus info\n" 349 "axi dev [bus] - show or set current AXI bus to bus number [bus]\n" 350 "axi md size addr [# of objects] - read from AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n" 351 "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"); 352 353U_BOOT_CMD(axi, 7, 1, do_ihs_axi, 354 "AXI sub-system", 355 axi_help_text 356); 357