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