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