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