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(priv: entry->priv, index: entry->id); |
63 | |
64 | seq_printf(m: s, fmt: "%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(priv: entry->priv, index: entry->flow); |
76 | |
77 | seq_printf(m: s, fmt: "%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(flow: 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(m: s, fmt: "%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(flow: entry->flow); |
130 | if (!f) |
131 | return -EINVAL; |
132 | |
133 | seq_printf(m: s, fmt: "%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(flow: 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(priv: port->priv, index: flow_index, fe: &fe); |
156 | |
157 | hash_opts = mvpp2_flow_get_hek_fields(fe: &fe); |
158 | |
159 | seq_printf(m: s, fmt: "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(flow: 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(priv: port->priv, index: flow_index, fe: &fe); |
181 | |
182 | engine = mvpp2_cls_flow_eng_get(fe: &fe); |
183 | |
184 | seq_printf(m: s, fmt: "%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(priv: entry->priv, c2_index: entry->id); |
197 | |
198 | seq_printf(m: s, fmt: "%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(priv: entry->priv, index: entry->id, c2: &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(m: s, fmt: "%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(priv: entry->priv, index: entry->id, c2: &c2); |
233 | |
234 | enabled = !!(c2.attr[2] & MVPP22_CLS_C2_ATTR2_RSS_EN); |
235 | |
236 | seq_printf(m: s, fmt: "%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: &pe, tid); |
256 | |
257 | pmap = mvpp2_prs_tcam_port_map_get(pe: &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: &pe, offs: 2, byte: &byte[0], enable: &enable[0]); |
266 | mvpp2_prs_tcam_data_byte_get(pe: &pe, offs: 3, byte: &byte[1], enable: &enable[1]); |
267 | |
268 | rvid = ((byte[0] & 0xf) << 8) + byte[1]; |
269 | |
270 | seq_printf(m: s, fmt: "%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(priv: port->priv, pe: &pe, tid: i); |
288 | |
289 | pmap = mvpp2_prs_tcam_port_map_get(pe: &pe); |
290 | if (priv->prs_shadow[i].valid && test_bit(port->id, &pmap)) |
291 | seq_printf(m: s, fmt: "%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: &pe, tid); |
317 | |
318 | pmap = mvpp2_prs_tcam_port_map_get(pe: &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: &pe, offs: index, byte: &da[index], |
327 | enable: &da_mask[index]); |
328 | |
329 | seq_printf(m: s, fmt: "%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(m: s, fmt: "%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(priv: entry->priv, pe: &pe, tid: entry->tid); |
356 | |
357 | pmap = mvpp2_prs_tcam_port_map_get(pe: &pe); |
358 | pmap &= MVPP2_PRS_PORT_MASK; |
359 | |
360 | seq_printf(m: s, fmt: "%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(priv: entry->priv, pe: &pe, tid: 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(m: s, fmt: "%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(priv: entry->priv, pe: &pe, tid: entry->tid); |
393 | |
394 | for (i = 0; i < 8; i++) |
395 | mvpp2_prs_tcam_data_byte_get(pe: &pe, offs: i, byte: &data[i], enable: &mask[i]); |
396 | |
397 | seq_printf(m: s, fmt: "%*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(priv: entry->priv, pe: &pe, tid: entry->tid); |
410 | |
411 | seq_printf(m: s, fmt: "%*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(priv: entry->priv, index: entry->tid); |
424 | if (val < 0) |
425 | return val; |
426 | |
427 | seq_printf(m: s, fmt: "%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(m: s, fmt: "%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(name: port->dev->name, parent); |
455 | |
456 | port_entry = &port->priv->dbgfs_entries->port_flow_entries[port->id]; |
457 | |
458 | port_entry->port = port; |
459 | port_entry->dbg_fe = entry; |
460 | |
461 | debugfs_create_file(name: "hash_opts" , mode: 0444, parent: port_dir, data: port_entry, |
462 | fops: &mvpp2_dbgfs_port_flow_hash_opt_fops); |
463 | |
464 | debugfs_create_file(name: "engine" , mode: 0444, parent: port_dir, data: port_entry, |
465 | fops: &mvpp2_dbgfs_port_flow_engine_fops); |
466 | |
467 | return 0; |
468 | } |
469 | |
470 | static int mvpp2_dbgfs_flow_entry_init(struct dentry *parent, |
471 | struct mvpp2 *priv, int flow) |
472 | { |
473 | struct mvpp2_dbgfs_flow_entry *entry; |
474 | struct dentry *flow_entry_dir; |
475 | char flow_entry_name[10]; |
476 | int i, ret; |
477 | |
478 | sprintf(buf: flow_entry_name, fmt: "%02d" , flow); |
479 | |
480 | flow_entry_dir = debugfs_create_dir(name: flow_entry_name, parent); |
481 | |
482 | entry = &priv->dbgfs_entries->flow_entries[flow]; |
483 | |
484 | entry->flow = flow; |
485 | entry->priv = priv; |
486 | |
487 | debugfs_create_file(name: "dec_hits" , mode: 0444, parent: flow_entry_dir, data: entry, |
488 | fops: &mvpp2_dbgfs_flow_dec_hits_fops); |
489 | |
490 | debugfs_create_file(name: "type" , mode: 0444, parent: flow_entry_dir, data: entry, |
491 | fops: &mvpp2_dbgfs_flow_type_fops); |
492 | |
493 | debugfs_create_file(name: "id" , mode: 0444, parent: flow_entry_dir, data: entry, |
494 | fops: &mvpp2_dbgfs_flow_id_fops); |
495 | |
496 | /* Create entry for each port */ |
497 | for (i = 0; i < priv->port_count; i++) { |
498 | ret = mvpp2_dbgfs_flow_port_init(parent: flow_entry_dir, |
499 | port: priv->port_list[i], entry); |
500 | if (ret) |
501 | return ret; |
502 | } |
503 | |
504 | return 0; |
505 | } |
506 | |
507 | static int mvpp2_dbgfs_flow_init(struct dentry *parent, struct mvpp2 *priv) |
508 | { |
509 | struct dentry *flow_dir; |
510 | int i, ret; |
511 | |
512 | flow_dir = debugfs_create_dir(name: "flows" , parent); |
513 | |
514 | for (i = 0; i < MVPP2_N_PRS_FLOWS; i++) { |
515 | ret = mvpp2_dbgfs_flow_entry_init(parent: flow_dir, priv, flow: i); |
516 | if (ret) |
517 | return ret; |
518 | } |
519 | |
520 | return 0; |
521 | } |
522 | |
523 | static int mvpp2_dbgfs_prs_entry_init(struct dentry *parent, |
524 | struct mvpp2 *priv, int tid) |
525 | { |
526 | struct mvpp2_dbgfs_prs_entry *entry; |
527 | struct dentry *prs_entry_dir; |
528 | char prs_entry_name[10]; |
529 | |
530 | if (tid >= MVPP2_PRS_TCAM_SRAM_SIZE) |
531 | return -EINVAL; |
532 | |
533 | sprintf(buf: prs_entry_name, fmt: "%03d" , tid); |
534 | |
535 | prs_entry_dir = debugfs_create_dir(name: prs_entry_name, parent); |
536 | |
537 | entry = &priv->dbgfs_entries->prs_entries[tid]; |
538 | |
539 | entry->tid = tid; |
540 | entry->priv = priv; |
541 | |
542 | /* Create each attr */ |
543 | debugfs_create_file(name: "sram" , mode: 0444, parent: prs_entry_dir, data: entry, |
544 | fops: &mvpp2_dbgfs_prs_sram_fops); |
545 | |
546 | debugfs_create_file(name: "valid" , mode: 0644, parent: prs_entry_dir, data: entry, |
547 | fops: &mvpp2_dbgfs_prs_valid_fops); |
548 | |
549 | debugfs_create_file(name: "lookup_id" , mode: 0644, parent: prs_entry_dir, data: entry, |
550 | fops: &mvpp2_dbgfs_prs_lu_fops); |
551 | |
552 | debugfs_create_file(name: "ai" , mode: 0644, parent: prs_entry_dir, data: entry, |
553 | fops: &mvpp2_dbgfs_prs_ai_fops); |
554 | |
555 | debugfs_create_file(name: "header_data" , mode: 0644, parent: prs_entry_dir, data: entry, |
556 | fops: &mvpp2_dbgfs_prs_hdata_fops); |
557 | |
558 | debugfs_create_file(name: "hits" , mode: 0444, parent: prs_entry_dir, data: entry, |
559 | fops: &mvpp2_dbgfs_prs_hits_fops); |
560 | |
561 | debugfs_create_file(name: "pmap" , mode: 0444, parent: prs_entry_dir, data: entry, |
562 | fops: &mvpp2_dbgfs_prs_pmap_fops); |
563 | |
564 | return 0; |
565 | } |
566 | |
567 | static int mvpp2_dbgfs_prs_init(struct dentry *parent, struct mvpp2 *priv) |
568 | { |
569 | struct dentry *prs_dir; |
570 | int i, ret; |
571 | |
572 | prs_dir = debugfs_create_dir(name: "parser" , parent); |
573 | |
574 | for (i = 0; i < MVPP2_PRS_TCAM_SRAM_SIZE; i++) { |
575 | ret = mvpp2_dbgfs_prs_entry_init(parent: prs_dir, priv, tid: i); |
576 | if (ret) |
577 | return ret; |
578 | } |
579 | |
580 | return 0; |
581 | } |
582 | |
583 | static int mvpp2_dbgfs_c2_entry_init(struct dentry *parent, |
584 | struct mvpp2 *priv, int id) |
585 | { |
586 | struct mvpp2_dbgfs_c2_entry *entry; |
587 | struct dentry *c2_entry_dir; |
588 | char c2_entry_name[10]; |
589 | |
590 | if (id >= MVPP22_CLS_C2_N_ENTRIES) |
591 | return -EINVAL; |
592 | |
593 | sprintf(buf: c2_entry_name, fmt: "%03d" , id); |
594 | |
595 | c2_entry_dir = debugfs_create_dir(name: c2_entry_name, parent); |
596 | |
597 | entry = &priv->dbgfs_entries->c2_entries[id]; |
598 | |
599 | entry->id = id; |
600 | entry->priv = priv; |
601 | |
602 | debugfs_create_file(name: "hits" , mode: 0444, parent: c2_entry_dir, data: entry, |
603 | fops: &mvpp2_dbgfs_flow_c2_hits_fops); |
604 | |
605 | debugfs_create_file(name: "default_rxq" , mode: 0444, parent: c2_entry_dir, data: entry, |
606 | fops: &mvpp2_dbgfs_flow_c2_rxq_fops); |
607 | |
608 | debugfs_create_file(name: "rss_enable" , mode: 0444, parent: c2_entry_dir, data: entry, |
609 | fops: &mvpp2_dbgfs_flow_c2_enable_fops); |
610 | |
611 | return 0; |
612 | } |
613 | |
614 | static int mvpp2_dbgfs_flow_tbl_entry_init(struct dentry *parent, |
615 | struct mvpp2 *priv, int id) |
616 | { |
617 | struct mvpp2_dbgfs_flow_tbl_entry *entry; |
618 | struct dentry *flow_tbl_entry_dir; |
619 | char flow_tbl_entry_name[10]; |
620 | |
621 | if (id >= MVPP2_CLS_FLOWS_TBL_SIZE) |
622 | return -EINVAL; |
623 | |
624 | sprintf(buf: flow_tbl_entry_name, fmt: "%03d" , id); |
625 | |
626 | flow_tbl_entry_dir = debugfs_create_dir(name: flow_tbl_entry_name, parent); |
627 | |
628 | entry = &priv->dbgfs_entries->flt_entries[id]; |
629 | |
630 | entry->id = id; |
631 | entry->priv = priv; |
632 | |
633 | debugfs_create_file(name: "hits" , mode: 0444, parent: flow_tbl_entry_dir, data: entry, |
634 | fops: &mvpp2_dbgfs_flow_flt_hits_fops); |
635 | |
636 | return 0; |
637 | } |
638 | |
639 | static int mvpp2_dbgfs_cls_init(struct dentry *parent, struct mvpp2 *priv) |
640 | { |
641 | struct dentry *cls_dir, *c2_dir, *flow_tbl_dir; |
642 | int i, ret; |
643 | |
644 | cls_dir = debugfs_create_dir(name: "classifier" , parent); |
645 | |
646 | c2_dir = debugfs_create_dir(name: "c2" , parent: cls_dir); |
647 | |
648 | for (i = 0; i < MVPP22_CLS_C2_N_ENTRIES; i++) { |
649 | ret = mvpp2_dbgfs_c2_entry_init(parent: c2_dir, priv, id: i); |
650 | if (ret) |
651 | return ret; |
652 | } |
653 | |
654 | flow_tbl_dir = debugfs_create_dir(name: "flow_table" , parent: cls_dir); |
655 | |
656 | for (i = 0; i < MVPP2_CLS_FLOWS_TBL_SIZE; i++) { |
657 | ret = mvpp2_dbgfs_flow_tbl_entry_init(parent: flow_tbl_dir, priv, id: i); |
658 | if (ret) |
659 | return ret; |
660 | } |
661 | |
662 | return 0; |
663 | } |
664 | |
665 | static int mvpp2_dbgfs_port_init(struct dentry *parent, |
666 | struct mvpp2_port *port) |
667 | { |
668 | struct dentry *port_dir; |
669 | |
670 | port_dir = debugfs_create_dir(name: port->dev->name, parent); |
671 | |
672 | debugfs_create_file(name: "parser_entries" , mode: 0444, parent: port_dir, data: port, |
673 | fops: &mvpp2_dbgfs_port_parser_fops); |
674 | |
675 | debugfs_create_file(name: "mac_filter" , mode: 0444, parent: port_dir, data: port, |
676 | fops: &mvpp2_dbgfs_filter_fops); |
677 | |
678 | debugfs_create_file(name: "vid_filter" , mode: 0444, parent: port_dir, data: port, |
679 | fops: &mvpp2_dbgfs_port_vid_fops); |
680 | |
681 | return 0; |
682 | } |
683 | |
684 | static struct dentry *mvpp2_root; |
685 | |
686 | void mvpp2_dbgfs_exit(void) |
687 | { |
688 | debugfs_remove(dentry: mvpp2_root); |
689 | } |
690 | |
691 | void mvpp2_dbgfs_cleanup(struct mvpp2 *priv) |
692 | { |
693 | debugfs_remove_recursive(dentry: priv->dbgfs_dir); |
694 | |
695 | kfree(objp: priv->dbgfs_entries); |
696 | } |
697 | |
698 | void mvpp2_dbgfs_init(struct mvpp2 *priv, const char *name) |
699 | { |
700 | struct dentry *mvpp2_dir; |
701 | int ret, i; |
702 | |
703 | if (!mvpp2_root) |
704 | mvpp2_root = debugfs_create_dir(MVPP2_DRIVER_NAME, NULL); |
705 | |
706 | mvpp2_dir = debugfs_create_dir(name, parent: mvpp2_root); |
707 | |
708 | priv->dbgfs_dir = mvpp2_dir; |
709 | priv->dbgfs_entries = kzalloc(size: sizeof(*priv->dbgfs_entries), GFP_KERNEL); |
710 | if (!priv->dbgfs_entries) |
711 | goto err; |
712 | |
713 | ret = mvpp2_dbgfs_prs_init(parent: mvpp2_dir, priv); |
714 | if (ret) |
715 | goto err; |
716 | |
717 | ret = mvpp2_dbgfs_cls_init(parent: mvpp2_dir, priv); |
718 | if (ret) |
719 | goto err; |
720 | |
721 | for (i = 0; i < priv->port_count; i++) { |
722 | ret = mvpp2_dbgfs_port_init(parent: mvpp2_dir, port: priv->port_list[i]); |
723 | if (ret) |
724 | goto err; |
725 | } |
726 | |
727 | ret = mvpp2_dbgfs_flow_init(parent: mvpp2_dir, priv); |
728 | if (ret) |
729 | goto err; |
730 | |
731 | return; |
732 | err: |
733 | mvpp2_dbgfs_cleanup(priv); |
734 | } |
735 | |