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