1 /* 2 * Copyright 2015-2016 Freescale Semiconductor, Inc. 3 * Copyright 2017 NXP 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 /* 9 * @file 10 * @brief PFE utility commands 11 */ 12 13 #include <net/pfe_eth/pfe_eth.h> 14 15 static inline void pfe_command_help(void) 16 { 17 printf("Usage: pfe [pe | status | expt ] <options>\n"); 18 } 19 20 static void pfe_command_pe(int argc, char * const argv[]) 21 { 22 if (argc >= 3 && strcmp(argv[2], "pmem") == 0) { 23 if (argc >= 4 && strcmp(argv[3], "read") == 0) { 24 int i; 25 int num; 26 int id; 27 u32 addr; 28 u32 size; 29 u32 val; 30 31 if (argc == 7) { 32 num = simple_strtoul(argv[6], NULL, 0); 33 } else if (argc == 6) { 34 num = 1; 35 } else { 36 printf("Usage: pfe pe pmem read <id> <addr> [<num>]\n"); 37 return; 38 } 39 40 id = simple_strtoul(argv[4], NULL, 0); 41 addr = simple_strtoul(argv[5], NULL, 16); 42 size = 4; 43 44 for (i = 0; i < num; i++, addr += 4) { 45 val = pe_pmem_read(id, addr, size); 46 val = be32_to_cpu(val); 47 if (!(i & 3)) 48 printf("%08x: ", addr); 49 printf("%08x%s", val, i == num - 1 || (i & 3) 50 == 3 ? "\n" : " "); 51 } 52 53 } else { 54 printf("Usage: pfe pe pmem read <parameters>\n"); 55 } 56 } else if (argc >= 3 && strcmp(argv[2], "dmem") == 0) { 57 if (argc >= 4 && strcmp(argv[3], "read") == 0) { 58 int i; 59 int num; 60 int id; 61 u32 addr; 62 u32 size; 63 u32 val; 64 65 if (argc == 7) { 66 num = simple_strtoul(argv[6], NULL, 0); 67 } else if (argc == 6) { 68 num = 1; 69 } else { 70 printf("Usage: pfe pe dmem read <id> <addr> [<num>]\n"); 71 return; 72 } 73 74 id = simple_strtoul(argv[4], NULL, 0); 75 addr = simple_strtoul(argv[5], NULL, 16); 76 size = 4; 77 78 for (i = 0; i < num; i++, addr += 4) { 79 val = pe_dmem_read(id, addr, size); 80 val = be32_to_cpu(val); 81 if (!(i & 3)) 82 printf("%08x: ", addr); 83 printf("%08x%s", val, i == num - 1 || (i & 3) 84 == 3 ? "\n" : " "); 85 } 86 87 } else if (argc >= 4 && strcmp(argv[3], "write") == 0) { 88 int id; 89 u32 val; 90 u32 addr; 91 u32 size; 92 93 if (argc != 7) { 94 printf("Usage: pfe pe dmem write <id> <val> <addr>\n"); 95 return; 96 } 97 98 id = simple_strtoul(argv[4], NULL, 0); 99 val = simple_strtoul(argv[5], NULL, 16); 100 val = cpu_to_be32(val); 101 addr = simple_strtoul(argv[6], NULL, 16); 102 size = 4; 103 pe_dmem_write(id, val, addr, size); 104 } else { 105 printf("Usage: pfe pe dmem [read | write] <parameters>\n"); 106 } 107 } else if (argc >= 3 && strcmp(argv[2], "lmem") == 0) { 108 if (argc >= 4 && strcmp(argv[3], "read") == 0) { 109 int i; 110 int num; 111 u32 val; 112 u32 offset; 113 114 if (argc == 6) { 115 num = simple_strtoul(argv[5], NULL, 0); 116 } else if (argc == 5) { 117 num = 1; 118 } else { 119 printf("Usage: pfe pe lmem read <offset> [<num>]\n"); 120 return; 121 } 122 123 offset = simple_strtoul(argv[4], NULL, 16); 124 125 for (i = 0; i < num; i++, offset += 4) { 126 pe_lmem_read(&val, 4, offset); 127 val = be32_to_cpu(val); 128 printf("%08x%s", val, i == num - 1 || (i & 7) 129 == 7 ? "\n" : " "); 130 } 131 132 } else if (argc >= 4 && strcmp(argv[3], "write") == 0) { 133 u32 val; 134 u32 offset; 135 136 if (argc != 6) { 137 printf("Usage: pfe pe lmem write <val> <offset>\n"); 138 return; 139 } 140 141 val = simple_strtoul(argv[4], NULL, 16); 142 val = cpu_to_be32(val); 143 offset = simple_strtoul(argv[5], NULL, 16); 144 pe_lmem_write(&val, 4, offset); 145 } else { 146 printf("Usage: pfe pe lmem [read | write] <parameters>\n"); 147 } 148 } else { 149 if (strcmp(argv[2], "help") != 0) 150 printf("Unknown option: %s\n", argv[2]); 151 152 printf("Usage: pfe pe <parameters>\n"); 153 } 154 } 155 156 #define NUM_QUEUES 16 157 158 /* 159 * qm_read_drop_stat 160 * This function is used to read the drop statistics from the TMU 161 * hw drop counter. Since the hw counter is always cleared afer 162 * reading, this function maintains the previous drop count, and 163 * adds the new value to it. That value can be retrieved by 164 * passing a pointer to it with the total_drops arg. 165 * 166 * @param tmu TMU number (0 - 3) 167 * @param queue queue number (0 - 15) 168 * @param total_drops pointer to location to store total drops (or NULL) 169 * @param do_reset if TRUE, clear total drops after updating 170 * 171 */ 172 u32 qm_read_drop_stat(u32 tmu, u32 queue, u32 *total_drops, int do_reset) 173 { 174 static u32 qtotal[TMU_MAX_ID + 1][NUM_QUEUES]; 175 u32 val; 176 177 writel((tmu << 8) | queue, TMU_TEQ_CTRL); 178 writel((tmu << 8) | queue, TMU_LLM_CTRL); 179 val = readl(TMU_TEQ_DROP_STAT); 180 qtotal[tmu][queue] += val; 181 if (total_drops) 182 *total_drops = qtotal[tmu][queue]; 183 if (do_reset) 184 qtotal[tmu][queue] = 0; 185 return val; 186 } 187 188 static ssize_t tmu_queue_stats(char *buf, int tmu, int queue) 189 { 190 ssize_t len = 0; 191 u32 drops; 192 193 printf("%d-%02d, ", tmu, queue); 194 195 drops = qm_read_drop_stat(tmu, queue, NULL, 0); 196 197 /* Select queue */ 198 writel((tmu << 8) | queue, TMU_TEQ_CTRL); 199 writel((tmu << 8) | queue, TMU_LLM_CTRL); 200 201 printf("(teq) drop: %10u, tx: %10u (llm) head: %08x, tail: %08x, drop: %10u\n", 202 drops, readl(TMU_TEQ_TRANS_STAT), 203 readl(TMU_LLM_QUE_HEADPTR), readl(TMU_LLM_QUE_TAILPTR), 204 readl(TMU_LLM_QUE_DROPCNT)); 205 206 return len; 207 } 208 209 static ssize_t tmu_queues(char *buf, int tmu) 210 { 211 ssize_t len = 0; 212 int queue; 213 214 for (queue = 0; queue < 16; queue++) 215 len += tmu_queue_stats(buf + len, tmu, queue); 216 217 return len; 218 } 219 220 static inline void hif_status(void) 221 { 222 printf("hif:\n"); 223 224 printf(" tx curr bd: %x\n", readl(HIF_TX_CURR_BD_ADDR)); 225 printf(" tx status: %x\n", readl(HIF_TX_STATUS)); 226 printf(" tx dma status: %x\n", readl(HIF_TX_DMA_STATUS)); 227 228 printf(" rx curr bd: %x\n", readl(HIF_RX_CURR_BD_ADDR)); 229 printf(" rx status: %x\n", readl(HIF_RX_STATUS)); 230 printf(" rx dma status: %x\n", readl(HIF_RX_DMA_STATUS)); 231 232 printf("hif nocopy:\n"); 233 234 printf(" tx curr bd: %x\n", readl(HIF_NOCPY_TX_CURR_BD_ADDR)); 235 printf(" tx status: %x\n", readl(HIF_NOCPY_TX_STATUS)); 236 printf(" tx dma status: %x\n", readl(HIF_NOCPY_TX_DMA_STATUS)); 237 238 printf(" rx curr bd: %x\n", readl(HIF_NOCPY_RX_CURR_BD_ADDR)); 239 printf(" rx status: %x\n", readl(HIF_NOCPY_RX_STATUS)); 240 printf(" rx dma status: %x\n", readl(HIF_NOCPY_RX_DMA_STATUS)); 241 } 242 243 static void gpi(int id, void *base) 244 { 245 u32 val; 246 247 printf("%s%d:\n", __func__, id); 248 249 printf(" tx under stick: %x\n", readl(base + GPI_FIFO_STATUS)); 250 val = readl(base + GPI_FIFO_DEBUG); 251 printf(" tx pkts: %x\n", (val >> 23) & 0x3f); 252 printf(" rx pkts: %x\n", (val >> 18) & 0x3f); 253 printf(" tx bytes: %x\n", (val >> 9) & 0x1ff); 254 printf(" rx bytes: %x\n", (val >> 0) & 0x1ff); 255 printf(" overrun: %x\n", readl(base + GPI_OVERRUN_DROPCNT)); 256 } 257 258 static void bmu(int id, void *base) 259 { 260 printf("%s%d:\n", __func__, id); 261 262 printf(" buf size: %x\n", (1 << readl(base + BMU_BUF_SIZE))); 263 printf(" buf count: %x\n", readl(base + BMU_BUF_CNT)); 264 printf(" buf rem: %x\n", readl(base + BMU_REM_BUF_CNT)); 265 printf(" buf curr: %x\n", readl(base + BMU_CURR_BUF_CNT)); 266 printf(" free err: %x\n", readl(base + BMU_FREE_ERR_ADDR)); 267 } 268 269 #define PESTATUS_ADDR_CLASS 0x800 270 #define PEMBOX_ADDR_CLASS 0x890 271 #define PESTATUS_ADDR_TMU 0x80 272 #define PEMBOX_ADDR_TMU 0x290 273 #define PESTATUS_ADDR_UTIL 0x0 274 275 static void pfe_pe_status(int argc, char * const argv[]) 276 { 277 int do_clear = 0; 278 u32 id; 279 u32 dmem_addr; 280 u32 cpu_state; 281 u32 activity_counter; 282 u32 rx; 283 u32 tx; 284 u32 drop; 285 char statebuf[5]; 286 u32 class_debug_reg = 0; 287 288 if (argc == 4 && strcmp(argv[3], "clear") == 0) 289 do_clear = 1; 290 291 for (id = CLASS0_ID; id < MAX_PE; id++) { 292 if (id >= TMU0_ID) { 293 if (id == TMU2_ID) 294 continue; 295 if (id == TMU0_ID) 296 printf("tmu:\n"); 297 dmem_addr = PESTATUS_ADDR_TMU; 298 } else { 299 if (id == CLASS0_ID) 300 printf("class:\n"); 301 dmem_addr = PESTATUS_ADDR_CLASS; 302 class_debug_reg = readl(CLASS_PE0_DEBUG + id * 4); 303 } 304 305 cpu_state = pe_dmem_read(id, dmem_addr, 4); 306 dmem_addr += 4; 307 memcpy(statebuf, (char *)&cpu_state, 4); 308 statebuf[4] = '\0'; 309 activity_counter = pe_dmem_read(id, dmem_addr, 4); 310 dmem_addr += 4; 311 rx = pe_dmem_read(id, dmem_addr, 4); 312 if (do_clear) 313 pe_dmem_write(id, 0, dmem_addr, 4); 314 dmem_addr += 4; 315 tx = pe_dmem_read(id, dmem_addr, 4); 316 if (do_clear) 317 pe_dmem_write(id, 0, dmem_addr, 4); 318 dmem_addr += 4; 319 drop = pe_dmem_read(id, dmem_addr, 4); 320 if (do_clear) 321 pe_dmem_write(id, 0, dmem_addr, 4); 322 dmem_addr += 4; 323 324 if (id >= TMU0_ID) { 325 printf("%d: state=%4s ctr=%08x rx=%x qstatus=%x\n", 326 id - TMU0_ID, statebuf, 327 cpu_to_be32(activity_counter), 328 cpu_to_be32(rx), cpu_to_be32(tx)); 329 } else { 330 printf("%d: pc=1%04x ldst=%04x state=%4s ctr=%08x rx=%x tx=%x drop=%x\n", 331 id - CLASS0_ID, class_debug_reg & 0xFFFF, 332 class_debug_reg >> 16, 333 statebuf, cpu_to_be32(activity_counter), 334 cpu_to_be32(rx), cpu_to_be32(tx), 335 cpu_to_be32(drop)); 336 } 337 } 338 } 339 340 static void pfe_command_status(int argc, char * const argv[]) 341 { 342 if (argc >= 3 && strcmp(argv[2], "pe") == 0) { 343 pfe_pe_status(argc, argv); 344 } else if (argc == 3 && strcmp(argv[2], "bmu") == 0) { 345 bmu(1, BMU1_BASE_ADDR); 346 bmu(2, BMU2_BASE_ADDR); 347 } else if (argc == 3 && strcmp(argv[2], "hif") == 0) { 348 hif_status(); 349 } else if (argc == 3 && strcmp(argv[2], "gpi") == 0) { 350 gpi(0, EGPI1_BASE_ADDR); 351 gpi(1, EGPI2_BASE_ADDR); 352 gpi(3, HGPI_BASE_ADDR); 353 } else if (argc == 3 && strcmp(argv[2], "tmu0_queues") == 0) { 354 tmu_queues(NULL, 0); 355 } else if (argc == 3 && strcmp(argv[2], "tmu1_queues") == 0) { 356 tmu_queues(NULL, 1); 357 } else if (argc == 3 && strcmp(argv[2], "tmu3_queues") == 0) { 358 tmu_queues(NULL, 3); 359 } else { 360 printf("Usage: pfe status [pe <clear> | bmu | gpi | hif | tmuX_queues ]\n"); 361 } 362 } 363 364 #define EXPT_DUMP_ADDR 0x1fa8 365 #define EXPT_REG_COUNT 20 366 static const char *register_names[EXPT_REG_COUNT] = { 367 " pc", "ECAS", " EID", " ED", 368 " sp", " r1", " r2", " r3", 369 " r4", " r5", " r6", " r7", 370 " r8", " r9", " r10", " r11", 371 " r12", " r13", " r14", " r15" 372 }; 373 374 static void pfe_command_expt(int argc, char * const argv[]) 375 { 376 unsigned int id, i, val, addr; 377 378 if (argc == 3) { 379 id = simple_strtoul(argv[2], NULL, 0); 380 addr = EXPT_DUMP_ADDR; 381 printf("Exception information for PE %d:\n", id); 382 for (i = 0; i < EXPT_REG_COUNT; i++) { 383 val = pe_dmem_read(id, addr, 4); 384 val = be32_to_cpu(val); 385 printf("%s:%08x%s", register_names[i], val, 386 (i & 3) == 3 ? "\n" : " "); 387 addr += 4; 388 } 389 } else { 390 printf("Usage: pfe expt <id>\n"); 391 } 392 } 393 394 #ifdef PFE_RESET_WA 395 /*This function sends a dummy packet to HIF through TMU3 */ 396 static void send_dummy_pkt_to_hif(void) 397 { 398 u32 buf; 399 static u32 dummy_pkt[] = { 400 0x4200800a, 0x01000003, 0x00018100, 0x00000000, 401 0x33221100, 0x2b785544, 0xd73093cb, 0x01000608, 402 0x04060008, 0x2b780200, 0xd73093cb, 0x0a01a8c0, 403 0x33221100, 0xa8c05544, 0x00000301, 0x00000000, 404 0x00000000, 0x00000000, 0x00000000, 0xbe86c51f }; 405 406 /*Allocate BMU2 buffer */ 407 buf = readl(BMU2_BASE_ADDR + BMU_ALLOC_CTRL); 408 409 debug("Sending a dummy pkt to HIF %x\n", buf); 410 buf += 0x80; 411 memcpy((void *)DDR_PFE_TO_VIRT(buf), dummy_pkt, sizeof(dummy_pkt)); 412 413 /*Write length and pkt to TMU*/ 414 writel(0x03000042, TMU_PHY_INQ_PKTPTR); 415 writel(buf, TMU_PHY_INQ_PKTINFO); 416 } 417 418 static void pfe_command_stop(int argc, char * const argv[]) 419 { 420 int pfe_pe_id, hif_stop_loop = 10; 421 u32 rx_status; 422 423 printf("Stopping PFE...\n"); 424 425 /*Mark all descriptors as LAST_BD */ 426 hif_rx_desc_disable(); 427 428 /*If HIF Rx BDP is busy send a dummy packet */ 429 do { 430 rx_status = readl(HIF_RX_STATUS); 431 if (rx_status & BDP_CSR_RX_DMA_ACTV) 432 send_dummy_pkt_to_hif(); 433 udelay(10); 434 } while (hif_stop_loop--); 435 436 if (readl(HIF_RX_STATUS) & BDP_CSR_RX_DMA_ACTV) 437 printf("Unable to stop HIF\n"); 438 439 /*Disable Class PEs */ 440 for (pfe_pe_id = CLASS0_ID; pfe_pe_id <= CLASS_MAX_ID; pfe_pe_id++) { 441 /*Inform PE to stop */ 442 pe_dmem_write(pfe_pe_id, cpu_to_be32(1), PEMBOX_ADDR_CLASS, 4); 443 udelay(10); 444 445 /*Read status */ 446 if (!pe_dmem_read(pfe_pe_id, PEMBOX_ADDR_CLASS + 4, 4)) 447 printf("Failed to stop PE%d\n", pfe_pe_id); 448 } 449 450 /*Disable TMU PEs */ 451 for (pfe_pe_id = TMU0_ID; pfe_pe_id <= TMU_MAX_ID; pfe_pe_id++) { 452 if (pfe_pe_id == TMU2_ID) 453 continue; 454 455 /*Inform PE to stop */ 456 pe_dmem_write(pfe_pe_id, 1, PEMBOX_ADDR_TMU, 4); 457 udelay(10); 458 459 /*Read status */ 460 if (!pe_dmem_read(pfe_pe_id, PEMBOX_ADDR_TMU + 4, 4)) 461 printf("Failed to stop PE%d\n", pfe_pe_id); 462 } 463 } 464 #endif 465 466 static int pfe_command(cmd_tbl_t *cmdtp, int flag, int argc, 467 char * const argv[]) 468 { 469 if (argc == 1 || strcmp(argv[1], "help") == 0) { 470 pfe_command_help(); 471 return CMD_RET_SUCCESS; 472 } 473 474 if (strcmp(argv[1], "pe") == 0) { 475 pfe_command_pe(argc, argv); 476 } else if (strcmp(argv[1], "status") == 0) { 477 pfe_command_status(argc, argv); 478 } else if (strcmp(argv[1], "expt") == 0) { 479 pfe_command_expt(argc, argv); 480 #ifdef PFE_RESET_WA 481 } else if (strcmp(argv[1], "stop") == 0) { 482 pfe_command_stop(argc, argv); 483 #endif 484 } else { 485 printf("Unknown option: %s\n", argv[1]); 486 pfe_command_help(); 487 return CMD_RET_FAILURE; 488 } 489 return CMD_RET_SUCCESS; 490 } 491 492 U_BOOT_CMD( 493 pfe, 7, 1, pfe_command, 494 "Performs PFE lib utility functions", 495 "Usage:\n" 496 "pfe <options>" 497 ); 498