xref: /linux/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c (revision ebf68996de0ab250c5d520eb2291ab65643e9a1e)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Driver for Marvell PPv2 network controller for Armada 375 SoC.
4  *
5  * Copyright (C) 2018 Marvell
6  */
7 
8 #include <linux/kernel.h>
9 #include <linux/slab.h>
10 #include <linux/debugfs.h>
11 
12 #include "mvpp2.h"
13 #include "mvpp2_prs.h"
14 #include "mvpp2_cls.h"
15 
16 struct mvpp2_dbgfs_prs_entry {
17 	int tid;
18 	struct mvpp2 *priv;
19 };
20 
21 struct mvpp2_dbgfs_c2_entry {
22 	int id;
23 	struct mvpp2 *priv;
24 };
25 
26 struct mvpp2_dbgfs_flow_entry {
27 	int flow;
28 	struct mvpp2 *priv;
29 };
30 
31 struct mvpp2_dbgfs_flow_tbl_entry {
32 	int id;
33 	struct mvpp2 *priv;
34 };
35 
36 struct mvpp2_dbgfs_port_flow_entry {
37 	struct mvpp2_port *port;
38 	struct mvpp2_dbgfs_flow_entry *dbg_fe;
39 };
40 
41 struct mvpp2_dbgfs_entries {
42 	/* Entries for Header Parser debug info */
43 	struct mvpp2_dbgfs_prs_entry prs_entries[MVPP2_PRS_TCAM_SRAM_SIZE];
44 
45 	/* Entries for Classifier C2 engine debug info */
46 	struct mvpp2_dbgfs_c2_entry c2_entries[MVPP22_CLS_C2_N_ENTRIES];
47 
48 	/* Entries for Classifier Flow Table debug info */
49 	struct mvpp2_dbgfs_flow_tbl_entry flt_entries[MVPP2_CLS_FLOWS_TBL_SIZE];
50 
51 	/* Entries for Classifier flows debug info */
52 	struct mvpp2_dbgfs_flow_entry flow_entries[MVPP2_N_PRS_FLOWS];
53 
54 	/* Entries for per-port flows debug info */
55 	struct mvpp2_dbgfs_port_flow_entry port_flow_entries[MVPP2_MAX_PORTS];
56 };
57 
58 static int mvpp2_dbgfs_flow_flt_hits_show(struct seq_file *s, void *unused)
59 {
60 	struct mvpp2_dbgfs_flow_tbl_entry *entry = s->private;
61 
62 	u32 hits = mvpp2_cls_flow_hits(entry->priv, entry->id);
63 
64 	seq_printf(s, "%u\n", hits);
65 
66 	return 0;
67 }
68 
69 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_flt_hits);
70 
71 static int mvpp2_dbgfs_flow_dec_hits_show(struct seq_file *s, void *unused)
72 {
73 	struct mvpp2_dbgfs_flow_entry *entry = s->private;
74 
75 	u32 hits = mvpp2_cls_lookup_hits(entry->priv, entry->flow);
76 
77 	seq_printf(s, "%u\n", hits);
78 
79 	return 0;
80 }
81 
82 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_dec_hits);
83 
84 static int mvpp2_dbgfs_flow_type_show(struct seq_file *s, void *unused)
85 {
86 	struct mvpp2_dbgfs_flow_entry *entry = s->private;
87 	const struct mvpp2_cls_flow *f;
88 	const char *flow_name;
89 
90 	f = mvpp2_cls_flow_get(entry->flow);
91 	if (!f)
92 		return -EINVAL;
93 
94 	switch (f->flow_type) {
95 	case IPV4_FLOW:
96 		flow_name = "ipv4";
97 		break;
98 	case IPV6_FLOW:
99 		flow_name = "ipv6";
100 		break;
101 	case TCP_V4_FLOW:
102 		flow_name = "tcp4";
103 		break;
104 	case TCP_V6_FLOW:
105 		flow_name = "tcp6";
106 		break;
107 	case UDP_V4_FLOW:
108 		flow_name = "udp4";
109 		break;
110 	case UDP_V6_FLOW:
111 		flow_name = "udp6";
112 		break;
113 	default:
114 		flow_name = "other";
115 	}
116 
117 	seq_printf(s, "%s\n", flow_name);
118 
119 	return 0;
120 }
121 
122 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_type);
123 
124 static int mvpp2_dbgfs_flow_id_show(struct seq_file *s, void *unused)
125 {
126 	const struct mvpp2_dbgfs_flow_entry *entry = s->private;
127 	const struct mvpp2_cls_flow *f;
128 
129 	f = mvpp2_cls_flow_get(entry->flow);
130 	if (!f)
131 		return -EINVAL;
132 
133 	seq_printf(s, "%d\n", f->flow_id);
134 
135 	return 0;
136 }
137 
138 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_id);
139 
140 static int mvpp2_dbgfs_port_flow_hash_opt_show(struct seq_file *s, void *unused)
141 {
142 	struct mvpp2_dbgfs_port_flow_entry *entry = s->private;
143 	struct mvpp2_port *port = entry->port;
144 	struct mvpp2_cls_flow_entry fe;
145 	const struct mvpp2_cls_flow *f;
146 	int flow_index;
147 	u16 hash_opts;
148 
149 	f = mvpp2_cls_flow_get(entry->dbg_fe->flow);
150 	if (!f)
151 		return -EINVAL;
152 
153 	flow_index = MVPP2_CLS_FLT_HASH_ENTRY(entry->port->id, f->flow_id);
154 
155 	mvpp2_cls_flow_read(port->priv, flow_index, &fe);
156 
157 	hash_opts = mvpp2_flow_get_hek_fields(&fe);
158 
159 	seq_printf(s, "0x%04x\n", hash_opts);
160 
161 	return 0;
162 }
163 
164 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_port_flow_hash_opt);
165 
166 static int mvpp2_dbgfs_port_flow_engine_show(struct seq_file *s, void *unused)
167 {
168 	struct mvpp2_dbgfs_port_flow_entry *entry = s->private;
169 	struct mvpp2_port *port = entry->port;
170 	struct mvpp2_cls_flow_entry fe;
171 	const struct mvpp2_cls_flow *f;
172 	int flow_index, engine;
173 
174 	f = mvpp2_cls_flow_get(entry->dbg_fe->flow);
175 	if (!f)
176 		return -EINVAL;
177 
178 	flow_index = MVPP2_CLS_FLT_HASH_ENTRY(entry->port->id, f->flow_id);
179 
180 	mvpp2_cls_flow_read(port->priv, flow_index, &fe);
181 
182 	engine = mvpp2_cls_flow_eng_get(&fe);
183 
184 	seq_printf(s, "%d\n", engine);
185 
186 	return 0;
187 }
188 
189 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_port_flow_engine);
190 
191 static int mvpp2_dbgfs_flow_c2_hits_show(struct seq_file *s, void *unused)
192 {
193 	struct mvpp2_dbgfs_c2_entry *entry = s->private;
194 	u32 hits;
195 
196 	hits = mvpp2_cls_c2_hit_count(entry->priv, entry->id);
197 
198 	seq_printf(s, "%u\n", hits);
199 
200 	return 0;
201 }
202 
203 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_c2_hits);
204 
205 static int mvpp2_dbgfs_flow_c2_rxq_show(struct seq_file *s, void *unused)
206 {
207 	struct mvpp2_dbgfs_c2_entry *entry = s->private;
208 	struct mvpp2_cls_c2_entry c2;
209 	u8 qh, ql;
210 
211 	mvpp2_cls_c2_read(entry->priv, entry->id, &c2);
212 
213 	qh = (c2.attr[0] >> MVPP22_CLS_C2_ATTR0_QHIGH_OFFS) &
214 	     MVPP22_CLS_C2_ATTR0_QHIGH_MASK;
215 
216 	ql = (c2.attr[0] >> MVPP22_CLS_C2_ATTR0_QLOW_OFFS) &
217 	     MVPP22_CLS_C2_ATTR0_QLOW_MASK;
218 
219 	seq_printf(s, "%d\n", (qh << 3 | ql));
220 
221 	return 0;
222 }
223 
224 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_c2_rxq);
225 
226 static int mvpp2_dbgfs_flow_c2_enable_show(struct seq_file *s, void *unused)
227 {
228 	struct mvpp2_dbgfs_c2_entry *entry = s->private;
229 	struct mvpp2_cls_c2_entry c2;
230 	int enabled;
231 
232 	mvpp2_cls_c2_read(entry->priv, entry->id, &c2);
233 
234 	enabled = !!(c2.attr[2] & MVPP22_CLS_C2_ATTR2_RSS_EN);
235 
236 	seq_printf(s, "%d\n", enabled);
237 
238 	return 0;
239 }
240 
241 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_c2_enable);
242 
243 static int mvpp2_dbgfs_port_vid_show(struct seq_file *s, void *unused)
244 {
245 	struct mvpp2_port *port = s->private;
246 	unsigned char byte[2], enable[2];
247 	struct mvpp2 *priv = port->priv;
248 	struct mvpp2_prs_entry pe;
249 	unsigned long pmap;
250 	u16 rvid;
251 	int tid;
252 
253 	for (tid = MVPP2_PRS_VID_PORT_FIRST(port->id);
254 	     tid <= MVPP2_PRS_VID_PORT_LAST(port->id); tid++) {
255 		mvpp2_prs_init_from_hw(priv, &pe, tid);
256 
257 		pmap = mvpp2_prs_tcam_port_map_get(&pe);
258 
259 		if (!priv->prs_shadow[tid].valid)
260 			continue;
261 
262 		if (!test_bit(port->id, &pmap))
263 			continue;
264 
265 		mvpp2_prs_tcam_data_byte_get(&pe, 2, &byte[0], &enable[0]);
266 		mvpp2_prs_tcam_data_byte_get(&pe, 3, &byte[1], &enable[1]);
267 
268 		rvid = ((byte[0] & 0xf) << 8) + byte[1];
269 
270 		seq_printf(s, "%u\n", rvid);
271 	}
272 
273 	return 0;
274 }
275 
276 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_port_vid);
277 
278 static int mvpp2_dbgfs_port_parser_show(struct seq_file *s, void *unused)
279 {
280 	struct mvpp2_port *port = s->private;
281 	struct mvpp2 *priv = port->priv;
282 	struct mvpp2_prs_entry pe;
283 	unsigned long pmap;
284 	int i;
285 
286 	for (i = 0; i < MVPP2_PRS_TCAM_SRAM_SIZE; i++) {
287 		mvpp2_prs_init_from_hw(port->priv, &pe, i);
288 
289 		pmap = mvpp2_prs_tcam_port_map_get(&pe);
290 		if (priv->prs_shadow[i].valid && test_bit(port->id, &pmap))
291 			seq_printf(s, "%03d\n", i);
292 	}
293 
294 	return 0;
295 }
296 
297 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_port_parser);
298 
299 static int mvpp2_dbgfs_filter_show(struct seq_file *s, void *unused)
300 {
301 	struct mvpp2_port *port = s->private;
302 	struct mvpp2 *priv = port->priv;
303 	struct mvpp2_prs_entry pe;
304 	unsigned long pmap;
305 	int index, tid;
306 
307 	for (tid = MVPP2_PE_MAC_RANGE_START;
308 	     tid <= MVPP2_PE_MAC_RANGE_END; tid++) {
309 		unsigned char da[ETH_ALEN], da_mask[ETH_ALEN];
310 
311 		if (!priv->prs_shadow[tid].valid ||
312 		    priv->prs_shadow[tid].lu != MVPP2_PRS_LU_MAC ||
313 		    priv->prs_shadow[tid].udf != MVPP2_PRS_UDF_MAC_DEF)
314 			continue;
315 
316 		mvpp2_prs_init_from_hw(priv, &pe, tid);
317 
318 		pmap = mvpp2_prs_tcam_port_map_get(&pe);
319 
320 		/* We only want entries active on this port */
321 		if (!test_bit(port->id, &pmap))
322 			continue;
323 
324 		/* Read mac addr from entry */
325 		for (index = 0; index < ETH_ALEN; index++)
326 			mvpp2_prs_tcam_data_byte_get(&pe, index, &da[index],
327 						     &da_mask[index]);
328 
329 		seq_printf(s, "%pM\n", da);
330 	}
331 
332 	return 0;
333 }
334 
335 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_filter);
336 
337 static int mvpp2_dbgfs_prs_lu_show(struct seq_file *s, void *unused)
338 {
339 	struct mvpp2_dbgfs_prs_entry *entry = s->private;
340 	struct mvpp2 *priv = entry->priv;
341 
342 	seq_printf(s, "%x\n", priv->prs_shadow[entry->tid].lu);
343 
344 	return 0;
345 }
346 
347 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_lu);
348 
349 static int mvpp2_dbgfs_prs_pmap_show(struct seq_file *s, void *unused)
350 {
351 	struct mvpp2_dbgfs_prs_entry *entry = s->private;
352 	struct mvpp2_prs_entry pe;
353 	unsigned int pmap;
354 
355 	mvpp2_prs_init_from_hw(entry->priv, &pe, entry->tid);
356 
357 	pmap = mvpp2_prs_tcam_port_map_get(&pe);
358 	pmap &= MVPP2_PRS_PORT_MASK;
359 
360 	seq_printf(s, "%02x\n", pmap);
361 
362 	return 0;
363 }
364 
365 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_pmap);
366 
367 static int mvpp2_dbgfs_prs_ai_show(struct seq_file *s, void *unused)
368 {
369 	struct mvpp2_dbgfs_prs_entry *entry = s->private;
370 	struct mvpp2_prs_entry pe;
371 	unsigned char ai, ai_mask;
372 
373 	mvpp2_prs_init_from_hw(entry->priv, &pe, entry->tid);
374 
375 	ai = pe.tcam[MVPP2_PRS_TCAM_AI_WORD] & MVPP2_PRS_AI_MASK;
376 	ai_mask = (pe.tcam[MVPP2_PRS_TCAM_AI_WORD] >> 16) & MVPP2_PRS_AI_MASK;
377 
378 	seq_printf(s, "%02x %02x\n", ai, ai_mask);
379 
380 	return 0;
381 }
382 
383 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_ai);
384 
385 static int mvpp2_dbgfs_prs_hdata_show(struct seq_file *s, void *unused)
386 {
387 	struct mvpp2_dbgfs_prs_entry *entry = s->private;
388 	struct mvpp2_prs_entry pe;
389 	unsigned char data[8], mask[8];
390 	int i;
391 
392 	mvpp2_prs_init_from_hw(entry->priv, &pe, entry->tid);
393 
394 	for (i = 0; i < 8; i++)
395 		mvpp2_prs_tcam_data_byte_get(&pe, i, &data[i], &mask[i]);
396 
397 	seq_printf(s, "%*phN %*phN\n", 8, data, 8, mask);
398 
399 	return 0;
400 }
401 
402 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_hdata);
403 
404 static int mvpp2_dbgfs_prs_sram_show(struct seq_file *s, void *unused)
405 {
406 	struct mvpp2_dbgfs_prs_entry *entry = s->private;
407 	struct mvpp2_prs_entry pe;
408 
409 	mvpp2_prs_init_from_hw(entry->priv, &pe, entry->tid);
410 
411 	seq_printf(s, "%*phN\n", 14, pe.sram);
412 
413 	return 0;
414 }
415 
416 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_sram);
417 
418 static int mvpp2_dbgfs_prs_hits_show(struct seq_file *s, void *unused)
419 {
420 	struct mvpp2_dbgfs_prs_entry *entry = s->private;
421 	int val;
422 
423 	val = mvpp2_prs_hits(entry->priv, entry->tid);
424 	if (val < 0)
425 		return val;
426 
427 	seq_printf(s, "%d\n", val);
428 
429 	return 0;
430 }
431 
432 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_hits);
433 
434 static int mvpp2_dbgfs_prs_valid_show(struct seq_file *s, void *unused)
435 {
436 	struct mvpp2_dbgfs_prs_entry *entry = s->private;
437 	struct mvpp2 *priv = entry->priv;
438 	int tid = entry->tid;
439 
440 	seq_printf(s, "%d\n", priv->prs_shadow[tid].valid ? 1 : 0);
441 
442 	return 0;
443 }
444 
445 DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_valid);
446 
447 static int mvpp2_dbgfs_flow_port_init(struct dentry *parent,
448 				      struct mvpp2_port *port,
449 				      struct mvpp2_dbgfs_flow_entry *entry)
450 {
451 	struct mvpp2_dbgfs_port_flow_entry *port_entry;
452 	struct dentry *port_dir;
453 
454 	port_dir = debugfs_create_dir(port->dev->name, parent);
455 	if (IS_ERR(port_dir))
456 		return PTR_ERR(port_dir);
457 
458 	port_entry = &port->priv->dbgfs_entries->port_flow_entries[port->id];
459 
460 	port_entry->port = port;
461 	port_entry->dbg_fe = entry;
462 
463 	debugfs_create_file("hash_opts", 0444, port_dir, port_entry,
464 			    &mvpp2_dbgfs_port_flow_hash_opt_fops);
465 
466 	debugfs_create_file("engine", 0444, port_dir, port_entry,
467 			    &mvpp2_dbgfs_port_flow_engine_fops);
468 
469 	return 0;
470 }
471 
472 static int mvpp2_dbgfs_flow_entry_init(struct dentry *parent,
473 				       struct mvpp2 *priv, int flow)
474 {
475 	struct mvpp2_dbgfs_flow_entry *entry;
476 	struct dentry *flow_entry_dir;
477 	char flow_entry_name[10];
478 	int i, ret;
479 
480 	sprintf(flow_entry_name, "%02d", flow);
481 
482 	flow_entry_dir = debugfs_create_dir(flow_entry_name, parent);
483 	if (!flow_entry_dir)
484 		return -ENOMEM;
485 
486 	entry = &priv->dbgfs_entries->flow_entries[flow];
487 
488 	entry->flow = flow;
489 	entry->priv = priv;
490 
491 	debugfs_create_file("dec_hits", 0444, flow_entry_dir, entry,
492 			    &mvpp2_dbgfs_flow_dec_hits_fops);
493 
494 	debugfs_create_file("type", 0444, flow_entry_dir, entry,
495 			    &mvpp2_dbgfs_flow_type_fops);
496 
497 	debugfs_create_file("id", 0444, flow_entry_dir, entry,
498 			    &mvpp2_dbgfs_flow_id_fops);
499 
500 	/* Create entry for each port */
501 	for (i = 0; i < priv->port_count; i++) {
502 		ret = mvpp2_dbgfs_flow_port_init(flow_entry_dir,
503 						 priv->port_list[i], entry);
504 		if (ret)
505 			return ret;
506 	}
507 
508 	return 0;
509 }
510 
511 static int mvpp2_dbgfs_flow_init(struct dentry *parent, struct mvpp2 *priv)
512 {
513 	struct dentry *flow_dir;
514 	int i, ret;
515 
516 	flow_dir = debugfs_create_dir("flows", parent);
517 	if (!flow_dir)
518 		return -ENOMEM;
519 
520 	for (i = 0; i < MVPP2_N_PRS_FLOWS; i++) {
521 		ret = mvpp2_dbgfs_flow_entry_init(flow_dir, priv, i);
522 		if (ret)
523 			return ret;
524 	}
525 
526 	return 0;
527 }
528 
529 static int mvpp2_dbgfs_prs_entry_init(struct dentry *parent,
530 				      struct mvpp2 *priv, int tid)
531 {
532 	struct mvpp2_dbgfs_prs_entry *entry;
533 	struct dentry *prs_entry_dir;
534 	char prs_entry_name[10];
535 
536 	if (tid >= MVPP2_PRS_TCAM_SRAM_SIZE)
537 		return -EINVAL;
538 
539 	sprintf(prs_entry_name, "%03d", tid);
540 
541 	prs_entry_dir = debugfs_create_dir(prs_entry_name, parent);
542 	if (!prs_entry_dir)
543 		return -ENOMEM;
544 
545 	entry = &priv->dbgfs_entries->prs_entries[tid];
546 
547 	entry->tid = tid;
548 	entry->priv = priv;
549 
550 	/* Create each attr */
551 	debugfs_create_file("sram", 0444, prs_entry_dir, entry,
552 			    &mvpp2_dbgfs_prs_sram_fops);
553 
554 	debugfs_create_file("valid", 0644, prs_entry_dir, entry,
555 			    &mvpp2_dbgfs_prs_valid_fops);
556 
557 	debugfs_create_file("lookup_id", 0644, prs_entry_dir, entry,
558 			    &mvpp2_dbgfs_prs_lu_fops);
559 
560 	debugfs_create_file("ai", 0644, prs_entry_dir, entry,
561 			    &mvpp2_dbgfs_prs_ai_fops);
562 
563 	debugfs_create_file("header_data", 0644, prs_entry_dir, entry,
564 			    &mvpp2_dbgfs_prs_hdata_fops);
565 
566 	debugfs_create_file("hits", 0444, prs_entry_dir, entry,
567 			    &mvpp2_dbgfs_prs_hits_fops);
568 
569 	return 0;
570 }
571 
572 static int mvpp2_dbgfs_prs_init(struct dentry *parent, struct mvpp2 *priv)
573 {
574 	struct dentry *prs_dir;
575 	int i, ret;
576 
577 	prs_dir = debugfs_create_dir("parser", parent);
578 	if (!prs_dir)
579 		return -ENOMEM;
580 
581 	for (i = 0; i < MVPP2_PRS_TCAM_SRAM_SIZE; i++) {
582 		ret = mvpp2_dbgfs_prs_entry_init(prs_dir, priv, i);
583 		if (ret)
584 			return ret;
585 	}
586 
587 	return 0;
588 }
589 
590 static int mvpp2_dbgfs_c2_entry_init(struct dentry *parent,
591 				     struct mvpp2 *priv, int id)
592 {
593 	struct mvpp2_dbgfs_c2_entry *entry;
594 	struct dentry *c2_entry_dir;
595 	char c2_entry_name[10];
596 
597 	if (id >= MVPP22_CLS_C2_N_ENTRIES)
598 		return -EINVAL;
599 
600 	sprintf(c2_entry_name, "%03d", id);
601 
602 	c2_entry_dir = debugfs_create_dir(c2_entry_name, parent);
603 	if (!c2_entry_dir)
604 		return -ENOMEM;
605 
606 	entry = &priv->dbgfs_entries->c2_entries[id];
607 
608 	entry->id = id;
609 	entry->priv = priv;
610 
611 	debugfs_create_file("hits", 0444, c2_entry_dir, entry,
612 			    &mvpp2_dbgfs_flow_c2_hits_fops);
613 
614 	debugfs_create_file("default_rxq", 0444, c2_entry_dir, entry,
615 			    &mvpp2_dbgfs_flow_c2_rxq_fops);
616 
617 	debugfs_create_file("rss_enable", 0444, c2_entry_dir, entry,
618 			    &mvpp2_dbgfs_flow_c2_enable_fops);
619 
620 	return 0;
621 }
622 
623 static int mvpp2_dbgfs_flow_tbl_entry_init(struct dentry *parent,
624 					   struct mvpp2 *priv, int id)
625 {
626 	struct mvpp2_dbgfs_flow_tbl_entry *entry;
627 	struct dentry *flow_tbl_entry_dir;
628 	char flow_tbl_entry_name[10];
629 
630 	if (id >= MVPP2_CLS_FLOWS_TBL_SIZE)
631 		return -EINVAL;
632 
633 	sprintf(flow_tbl_entry_name, "%03d", id);
634 
635 	flow_tbl_entry_dir = debugfs_create_dir(flow_tbl_entry_name, parent);
636 	if (!flow_tbl_entry_dir)
637 		return -ENOMEM;
638 
639 	entry = &priv->dbgfs_entries->flt_entries[id];
640 
641 	entry->id = id;
642 	entry->priv = priv;
643 
644 	debugfs_create_file("hits", 0444, flow_tbl_entry_dir, entry,
645 			    &mvpp2_dbgfs_flow_flt_hits_fops);
646 
647 	return 0;
648 }
649 
650 static int mvpp2_dbgfs_cls_init(struct dentry *parent, struct mvpp2 *priv)
651 {
652 	struct dentry *cls_dir, *c2_dir, *flow_tbl_dir;
653 	int i, ret;
654 
655 	cls_dir = debugfs_create_dir("classifier", parent);
656 	if (!cls_dir)
657 		return -ENOMEM;
658 
659 	c2_dir = debugfs_create_dir("c2", cls_dir);
660 	if (!c2_dir)
661 		return -ENOMEM;
662 
663 	for (i = 0; i < MVPP22_CLS_C2_N_ENTRIES; i++) {
664 		ret = mvpp2_dbgfs_c2_entry_init(c2_dir, priv, i);
665 		if (ret)
666 			return ret;
667 	}
668 
669 	flow_tbl_dir = debugfs_create_dir("flow_table", cls_dir);
670 	if (!flow_tbl_dir)
671 		return -ENOMEM;
672 
673 	for (i = 0; i < MVPP2_CLS_FLOWS_TBL_SIZE; i++) {
674 		ret = mvpp2_dbgfs_flow_tbl_entry_init(flow_tbl_dir, priv, i);
675 		if (ret)
676 			return ret;
677 	}
678 
679 	return 0;
680 }
681 
682 static int mvpp2_dbgfs_port_init(struct dentry *parent,
683 				 struct mvpp2_port *port)
684 {
685 	struct dentry *port_dir;
686 
687 	port_dir = debugfs_create_dir(port->dev->name, parent);
688 	if (IS_ERR(port_dir))
689 		return PTR_ERR(port_dir);
690 
691 	debugfs_create_file("parser_entries", 0444, port_dir, port,
692 			    &mvpp2_dbgfs_port_parser_fops);
693 
694 	debugfs_create_file("mac_filter", 0444, port_dir, port,
695 			    &mvpp2_dbgfs_filter_fops);
696 
697 	debugfs_create_file("vid_filter", 0444, port_dir, port,
698 			    &mvpp2_dbgfs_port_vid_fops);
699 
700 	return 0;
701 }
702 
703 void mvpp2_dbgfs_cleanup(struct mvpp2 *priv)
704 {
705 	debugfs_remove_recursive(priv->dbgfs_dir);
706 
707 	kfree(priv->dbgfs_entries);
708 }
709 
710 void mvpp2_dbgfs_init(struct mvpp2 *priv, const char *name)
711 {
712 	struct dentry *mvpp2_dir, *mvpp2_root;
713 	int ret, i;
714 
715 	mvpp2_root = debugfs_lookup(MVPP2_DRIVER_NAME, NULL);
716 	if (!mvpp2_root) {
717 		mvpp2_root = debugfs_create_dir(MVPP2_DRIVER_NAME, NULL);
718 		if (IS_ERR(mvpp2_root))
719 			return;
720 	}
721 
722 	mvpp2_dir = debugfs_create_dir(name, mvpp2_root);
723 	if (IS_ERR(mvpp2_dir))
724 		return;
725 
726 	priv->dbgfs_dir = mvpp2_dir;
727 	priv->dbgfs_entries = kzalloc(sizeof(*priv->dbgfs_entries), GFP_KERNEL);
728 	if (!priv->dbgfs_entries)
729 		goto err;
730 
731 	ret = mvpp2_dbgfs_prs_init(mvpp2_dir, priv);
732 	if (ret)
733 		goto err;
734 
735 	ret = mvpp2_dbgfs_cls_init(mvpp2_dir, priv);
736 	if (ret)
737 		goto err;
738 
739 	for (i = 0; i < priv->port_count; i++) {
740 		ret = mvpp2_dbgfs_port_init(mvpp2_dir, priv->port_list[i]);
741 		if (ret)
742 			goto err;
743 	}
744 
745 	ret = mvpp2_dbgfs_flow_init(mvpp2_dir, priv);
746 	if (ret)
747 		goto err;
748 
749 	return;
750 err:
751 	mvpp2_dbgfs_cleanup(priv);
752 }
753