blob: fc46ec81249b66ccb4a63b1ab31ae194f0f7c7c7 [file] [log] [blame]
Maxime Chevallier21da57a2018-07-14 13:29:25 +02001// 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"
Maxime Chevallierdba1d912018-07-14 13:29:27 +020014#include "mvpp2_cls.h"
Maxime Chevallier21da57a2018-07-14 13:29:25 +020015
16struct mvpp2_dbgfs_prs_entry {
17 int tid;
18 struct mvpp2 *priv;
19};
20
Maxime Chevallierdba1d912018-07-14 13:29:27 +020021struct mvpp2_dbgfs_flow_entry {
22 int flow;
23 struct mvpp2 *priv;
24};
25
26struct mvpp2_dbgfs_port_flow_entry {
27 struct mvpp2_port *port;
28 struct mvpp2_dbgfs_flow_entry *dbg_fe;
29};
30
31static int mvpp2_dbgfs_flow_type_show(struct seq_file *s, void *unused)
32{
33 struct mvpp2_dbgfs_flow_entry *entry = s->private;
34 struct mvpp2_cls_flow *f;
35 const char *flow_name;
36
37 f = mvpp2_cls_flow_get(entry->flow);
38 if (!f)
39 return -EINVAL;
40
41 switch (f->flow_type) {
42 case IPV4_FLOW:
43 flow_name = "ipv4";
44 break;
45 case IPV6_FLOW:
46 flow_name = "ipv6";
47 break;
48 case TCP_V4_FLOW:
49 flow_name = "tcp4";
50 break;
51 case TCP_V6_FLOW:
52 flow_name = "tcp6";
53 break;
54 case UDP_V4_FLOW:
55 flow_name = "udp4";
56 break;
57 case UDP_V6_FLOW:
58 flow_name = "udp6";
59 break;
60 default:
61 flow_name = "other";
62 }
63
64 seq_printf(s, "%s\n", flow_name);
65
66 return 0;
67}
68
69static int mvpp2_dbgfs_flow_type_open(struct inode *inode, struct file *file)
70{
71 return single_open(file, mvpp2_dbgfs_flow_type_show, inode->i_private);
72}
73
74static int mvpp2_dbgfs_flow_type_release(struct inode *inode, struct file *file)
75{
76 struct seq_file *seq = file->private_data;
77 struct mvpp2_dbgfs_flow_entry *flow_entry = seq->private;
78
79 kfree(flow_entry);
80 return single_release(inode, file);
81}
82
83static const struct file_operations mvpp2_dbgfs_flow_type_fops = {
84 .open = mvpp2_dbgfs_flow_type_open,
85 .read = seq_read,
86 .release = mvpp2_dbgfs_flow_type_release,
87};
88
89static int mvpp2_dbgfs_flow_id_show(struct seq_file *s, void *unused)
90{
91 struct mvpp2_dbgfs_flow_entry *entry = s->private;
92 struct mvpp2_cls_flow *f;
93
94 f = mvpp2_cls_flow_get(entry->flow);
95 if (!f)
96 return -EINVAL;
97
98 seq_printf(s, "%d\n", f->flow_id);
99
100 return 0;
101}
102
103DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_id);
104
105static int mvpp2_dbgfs_port_flow_hash_opt_show(struct seq_file *s, void *unused)
106{
107 struct mvpp2_dbgfs_port_flow_entry *entry = s->private;
108 struct mvpp2_port *port = entry->port;
109 struct mvpp2_cls_flow_entry fe;
110 struct mvpp2_cls_flow *f;
111 int flow_index;
112 u16 hash_opts;
113
114 f = mvpp2_cls_flow_get(entry->dbg_fe->flow);
115 if (!f)
116 return -EINVAL;
117
118 flow_index = MVPP2_PORT_FLOW_HASH_ENTRY(entry->port->id, f->flow_id);
119
120 mvpp2_cls_flow_read(port->priv, flow_index, &fe);
121
122 hash_opts = mvpp2_flow_get_hek_fields(&fe);
123
124 seq_printf(s, "0x%04x\n", hash_opts);
125
126 return 0;
127}
128
129static int mvpp2_dbgfs_port_flow_hash_opt_open(struct inode *inode,
130 struct file *file)
131{
132 return single_open(file, mvpp2_dbgfs_port_flow_hash_opt_show,
133 inode->i_private);
134}
135
136static int mvpp2_dbgfs_port_flow_hash_opt_release(struct inode *inode,
137 struct file *file)
138{
139 struct seq_file *seq = file->private_data;
140 struct mvpp2_dbgfs_port_flow_entry *flow_entry = seq->private;
141
142 kfree(flow_entry);
143 return single_release(inode, file);
144}
145
146static const struct file_operations mvpp2_dbgfs_port_flow_hash_opt_fops = {
147 .open = mvpp2_dbgfs_port_flow_hash_opt_open,
148 .read = seq_read,
149 .release = mvpp2_dbgfs_port_flow_hash_opt_release,
150};
151
152static int mvpp2_dbgfs_port_flow_engine_show(struct seq_file *s, void *unused)
153{
154 struct mvpp2_dbgfs_port_flow_entry *entry = s->private;
155 struct mvpp2_port *port = entry->port;
156 struct mvpp2_cls_flow_entry fe;
157 struct mvpp2_cls_flow *f;
158 int flow_index, engine;
159
160 f = mvpp2_cls_flow_get(entry->dbg_fe->flow);
161 if (!f)
162 return -EINVAL;
163
164 flow_index = MVPP2_PORT_FLOW_HASH_ENTRY(entry->port->id, f->flow_id);
165
166 mvpp2_cls_flow_read(port->priv, flow_index, &fe);
167
168 engine = mvpp2_cls_flow_eng_get(&fe);
169
170 seq_printf(s, "%d\n", engine);
171
172 return 0;
173}
174
175DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_port_flow_engine);
176
177static int mvpp2_dbgfs_flow_c2_rxq_show(struct seq_file *s, void *unused)
178{
179 struct mvpp2_port *port = s->private;
180 struct mvpp2_cls_c2_entry c2;
181 u8 qh, ql;
182
183 mvpp2_cls_c2_read(port->priv, MVPP22_CLS_C2_RSS_ENTRY(port->id), &c2);
184
185 qh = (c2.attr[0] >> MVPP22_CLS_C2_ATTR0_QHIGH_OFFS) &
186 MVPP22_CLS_C2_ATTR0_QHIGH_MASK;
187
188 ql = (c2.attr[0] >> MVPP22_CLS_C2_ATTR0_QLOW_OFFS) &
189 MVPP22_CLS_C2_ATTR0_QLOW_MASK;
190
191 seq_printf(s, "%d\n", (qh << 3 | ql));
192
193 return 0;
194}
195
196DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_c2_rxq);
197
198static int mvpp2_dbgfs_flow_c2_enable_show(struct seq_file *s, void *unused)
199{
200 struct mvpp2_port *port = s->private;
201 struct mvpp2_cls_c2_entry c2;
202 int enabled;
203
204 mvpp2_cls_c2_read(port->priv, MVPP22_CLS_C2_RSS_ENTRY(port->id), &c2);
205
206 enabled = !!(c2.attr[2] | MVPP22_CLS_C2_ATTR2_RSS_EN);
207
208 seq_printf(s, "%d\n", enabled);
209
210 return 0;
211}
212
213DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_c2_enable);
214
Maxime Chevallier21da57a2018-07-14 13:29:25 +0200215static int mvpp2_dbgfs_port_vid_show(struct seq_file *s, void *unused)
216{
217 struct mvpp2_port *port = s->private;
218 unsigned char byte[2], enable[2];
219 struct mvpp2 *priv = port->priv;
220 struct mvpp2_prs_entry pe;
221 unsigned long pmap;
222 u16 rvid;
223 int tid;
224
225 for (tid = MVPP2_PRS_VID_PORT_FIRST(port->id);
226 tid <= MVPP2_PRS_VID_PORT_LAST(port->id); tid++) {
227 mvpp2_prs_init_from_hw(priv, &pe, tid);
228
229 pmap = mvpp2_prs_tcam_port_map_get(&pe);
230
231 if (!priv->prs_shadow[tid].valid)
232 continue;
233
234 if (!test_bit(port->id, &pmap))
235 continue;
236
237 mvpp2_prs_tcam_data_byte_get(&pe, 2, &byte[0], &enable[0]);
238 mvpp2_prs_tcam_data_byte_get(&pe, 3, &byte[1], &enable[1]);
239
240 rvid = ((byte[0] & 0xf) << 8) + byte[1];
241
242 seq_printf(s, "%u\n", rvid);
243 }
244
245 return 0;
246}
247
248DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_port_vid);
249
250static int mvpp2_dbgfs_port_parser_show(struct seq_file *s, void *unused)
251{
252 struct mvpp2_port *port = s->private;
253 struct mvpp2 *priv = port->priv;
254 struct mvpp2_prs_entry pe;
255 unsigned long pmap;
256 int i;
257
258 for (i = 0; i < MVPP2_PRS_TCAM_SRAM_SIZE; i++) {
259 mvpp2_prs_init_from_hw(port->priv, &pe, i);
260
261 pmap = mvpp2_prs_tcam_port_map_get(&pe);
262 if (priv->prs_shadow[i].valid && test_bit(port->id, &pmap))
263 seq_printf(s, "%03d\n", i);
264 }
265
266 return 0;
267}
268
269DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_port_parser);
270
271static int mvpp2_dbgfs_filter_show(struct seq_file *s, void *unused)
272{
273 struct mvpp2_port *port = s->private;
274 struct mvpp2 *priv = port->priv;
275 struct mvpp2_prs_entry pe;
276 unsigned long pmap;
277 int index, tid;
278
279 for (tid = MVPP2_PE_MAC_RANGE_START;
280 tid <= MVPP2_PE_MAC_RANGE_END; tid++) {
281 unsigned char da[ETH_ALEN], da_mask[ETH_ALEN];
282
283 if (!priv->prs_shadow[tid].valid ||
284 priv->prs_shadow[tid].lu != MVPP2_PRS_LU_MAC ||
285 priv->prs_shadow[tid].udf != MVPP2_PRS_UDF_MAC_DEF)
286 continue;
287
288 mvpp2_prs_init_from_hw(priv, &pe, tid);
289
290 pmap = mvpp2_prs_tcam_port_map_get(&pe);
291
292 /* We only want entries active on this port */
293 if (!test_bit(port->id, &pmap))
294 continue;
295
296 /* Read mac addr from entry */
297 for (index = 0; index < ETH_ALEN; index++)
298 mvpp2_prs_tcam_data_byte_get(&pe, index, &da[index],
299 &da_mask[index]);
300
301 seq_printf(s, "%pM\n", da);
302 }
303
304 return 0;
305}
306
307DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_filter);
308
309static int mvpp2_dbgfs_prs_lu_show(struct seq_file *s, void *unused)
310{
311 struct mvpp2_dbgfs_prs_entry *entry = s->private;
312 struct mvpp2 *priv = entry->priv;
313
314 seq_printf(s, "%x\n", priv->prs_shadow[entry->tid].lu);
315
316 return 0;
317}
318
319DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_lu);
320
321static int mvpp2_dbgfs_prs_pmap_show(struct seq_file *s, void *unused)
322{
323 struct mvpp2_dbgfs_prs_entry *entry = s->private;
324 struct mvpp2_prs_entry pe;
325 unsigned int pmap;
326
327 mvpp2_prs_init_from_hw(entry->priv, &pe, entry->tid);
328
329 pmap = mvpp2_prs_tcam_port_map_get(&pe);
330 pmap &= MVPP2_PRS_PORT_MASK;
331
332 seq_printf(s, "%02x\n", pmap);
333
334 return 0;
335}
336
337DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_pmap);
338
339static int mvpp2_dbgfs_prs_ai_show(struct seq_file *s, void *unused)
340{
341 struct mvpp2_dbgfs_prs_entry *entry = s->private;
342 struct mvpp2_prs_entry pe;
343 unsigned char ai, ai_mask;
344
345 mvpp2_prs_init_from_hw(entry->priv, &pe, entry->tid);
346
347 ai = pe.tcam[MVPP2_PRS_TCAM_AI_WORD] & MVPP2_PRS_AI_MASK;
348 ai_mask = (pe.tcam[MVPP2_PRS_TCAM_AI_WORD] >> 16) & MVPP2_PRS_AI_MASK;
349
350 seq_printf(s, "%02x %02x\n", ai, ai_mask);
351
352 return 0;
353}
354
355DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_ai);
356
357static int mvpp2_dbgfs_prs_hdata_show(struct seq_file *s, void *unused)
358{
359 struct mvpp2_dbgfs_prs_entry *entry = s->private;
360 struct mvpp2_prs_entry pe;
361 unsigned char data[8], mask[8];
362 int i;
363
364 mvpp2_prs_init_from_hw(entry->priv, &pe, entry->tid);
365
366 for (i = 0; i < 8; i++)
367 mvpp2_prs_tcam_data_byte_get(&pe, i, &data[i], &mask[i]);
368
369 seq_printf(s, "%*phN %*phN\n", 8, data, 8, mask);
370
371 return 0;
372}
373
374DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_hdata);
375
376static int mvpp2_dbgfs_prs_sram_show(struct seq_file *s, void *unused)
377{
378 struct mvpp2_dbgfs_prs_entry *entry = s->private;
379 struct mvpp2_prs_entry pe;
380
381 mvpp2_prs_init_from_hw(entry->priv, &pe, entry->tid);
382
383 seq_printf(s, "%*phN\n", 14, pe.sram);
384
385 return 0;
386}
387
388DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_sram);
389
Maxime Chevallier12033412018-07-14 13:29:26 +0200390static int mvpp2_dbgfs_prs_hits_show(struct seq_file *s, void *unused)
391{
392 struct mvpp2_dbgfs_prs_entry *entry = s->private;
393 int val;
394
395 val = mvpp2_prs_hits(entry->priv, entry->tid);
396 if (val < 0)
397 return val;
398
399 seq_printf(s, "%d\n", val);
400
401 return 0;
402}
403
404DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_hits);
405
Maxime Chevallier21da57a2018-07-14 13:29:25 +0200406static int mvpp2_dbgfs_prs_valid_show(struct seq_file *s, void *unused)
407{
408 struct mvpp2_dbgfs_prs_entry *entry = s->private;
409 struct mvpp2 *priv = entry->priv;
410 int tid = entry->tid;
411
412 seq_printf(s, "%d\n", priv->prs_shadow[tid].valid ? 1 : 0);
413
414 return 0;
415}
416
417static int mvpp2_dbgfs_prs_valid_open(struct inode *inode, struct file *file)
418{
419 return single_open(file, mvpp2_dbgfs_prs_valid_show, inode->i_private);
420}
421
422static int mvpp2_dbgfs_prs_valid_release(struct inode *inode, struct file *file)
423{
424 struct seq_file *seq = file->private_data;
425 struct mvpp2_dbgfs_prs_entry *entry = seq->private;
426
427 kfree(entry);
428 return single_release(inode, file);
429}
430
431static const struct file_operations mvpp2_dbgfs_prs_valid_fops = {
432 .open = mvpp2_dbgfs_prs_valid_open,
433 .read = seq_read,
434 .release = mvpp2_dbgfs_prs_valid_release,
435};
436
Maxime Chevallierdba1d912018-07-14 13:29:27 +0200437static int mvpp2_dbgfs_flow_port_init(struct dentry *parent,
438 struct mvpp2_port *port,
439 struct mvpp2_dbgfs_flow_entry *entry)
440{
441 struct mvpp2_dbgfs_port_flow_entry *port_entry;
442 struct dentry *port_dir;
443
444 port_dir = debugfs_create_dir(port->dev->name, parent);
445 if (IS_ERR(port_dir))
446 return PTR_ERR(port_dir);
447
448 /* This will be freed by 'hash_opts' release op */
449 port_entry = kmalloc(sizeof(*port_entry), GFP_KERNEL);
450 if (!port_entry)
451 return -ENOMEM;
452
453 port_entry->port = port;
454 port_entry->dbg_fe = entry;
455
456 debugfs_create_file("hash_opts", 0444, port_dir, port_entry,
457 &mvpp2_dbgfs_port_flow_hash_opt_fops);
458
459 debugfs_create_file("engine", 0444, port_dir, port_entry,
460 &mvpp2_dbgfs_port_flow_engine_fops);
461
462 return 0;
463}
464
465static int mvpp2_dbgfs_flow_entry_init(struct dentry *parent,
466 struct mvpp2 *priv, int flow)
467{
468 struct mvpp2_dbgfs_flow_entry *entry;
469 struct dentry *flow_entry_dir;
470 char flow_entry_name[10];
471 int i, ret;
472
473 sprintf(flow_entry_name, "%02d", flow);
474
475 flow_entry_dir = debugfs_create_dir(flow_entry_name, parent);
476 if (!flow_entry_dir)
477 return -ENOMEM;
478
479 /* This will be freed by 'type' release op */
480 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
481 if (!entry)
482 return -ENOMEM;
483
484 entry->flow = flow;
485 entry->priv = priv;
486
487 debugfs_create_file("type", 0444, flow_entry_dir, entry,
488 &mvpp2_dbgfs_flow_type_fops);
489
490 debugfs_create_file("id", 0444, flow_entry_dir, entry,
491 &mvpp2_dbgfs_flow_id_fops);
492
493 /* Create entry for each port */
494 for (i = 0; i < priv->port_count; i++) {
495 ret = mvpp2_dbgfs_flow_port_init(flow_entry_dir,
496 priv->port_list[i], entry);
497 if (ret)
498 return ret;
499 }
500 return 0;
501}
502
503static int mvpp2_dbgfs_flow_init(struct dentry *parent, struct mvpp2 *priv)
504{
505 struct dentry *flow_dir;
506 int i, ret;
507
508 flow_dir = debugfs_create_dir("flows", parent);
509 if (!flow_dir)
510 return -ENOMEM;
511
512 for (i = 0; i < MVPP2_N_FLOWS; i++) {
513 ret = mvpp2_dbgfs_flow_entry_init(flow_dir, priv, i);
514 if (ret)
515 return ret;
516 }
517
518 return 0;
519}
520
Maxime Chevallier21da57a2018-07-14 13:29:25 +0200521static int mvpp2_dbgfs_prs_entry_init(struct dentry *parent,
522 struct mvpp2 *priv, int tid)
523{
524 struct mvpp2_dbgfs_prs_entry *entry;
525 struct dentry *prs_entry_dir;
526 char prs_entry_name[10];
527
528 if (tid >= MVPP2_PRS_TCAM_SRAM_SIZE)
529 return -EINVAL;
530
531 sprintf(prs_entry_name, "%03d", tid);
532
533 prs_entry_dir = debugfs_create_dir(prs_entry_name, parent);
534 if (!prs_entry_dir)
535 return -ENOMEM;
536
537 /* The 'valid' entry's ops will free that */
538 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
539 if (!entry)
540 return -ENOMEM;
541
542 entry->tid = tid;
543 entry->priv = priv;
544
545 /* Create each attr */
546 debugfs_create_file("sram", 0444, prs_entry_dir, entry,
547 &mvpp2_dbgfs_prs_sram_fops);
548
549 debugfs_create_file("valid", 0644, prs_entry_dir, entry,
550 &mvpp2_dbgfs_prs_valid_fops);
551
552 debugfs_create_file("lookup_id", 0644, prs_entry_dir, entry,
553 &mvpp2_dbgfs_prs_lu_fops);
554
555 debugfs_create_file("ai", 0644, prs_entry_dir, entry,
556 &mvpp2_dbgfs_prs_ai_fops);
557
558 debugfs_create_file("header_data", 0644, prs_entry_dir, entry,
559 &mvpp2_dbgfs_prs_hdata_fops);
560
Maxime Chevallier12033412018-07-14 13:29:26 +0200561 debugfs_create_file("hits", 0444, prs_entry_dir, entry,
562 &mvpp2_dbgfs_prs_hits_fops);
563
Maxime Chevallier21da57a2018-07-14 13:29:25 +0200564 return 0;
565}
566
567static 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("parser", parent);
573 if (!prs_dir)
574 return -ENOMEM;
575
576 for (i = 0; i < MVPP2_PRS_TCAM_SRAM_SIZE; i++) {
577 ret = mvpp2_dbgfs_prs_entry_init(prs_dir, priv, i);
578 if (ret)
579 return ret;
580 }
581
582 return 0;
583}
584
585static int mvpp2_dbgfs_port_init(struct dentry *parent,
586 struct mvpp2_port *port)
587{
588 struct dentry *port_dir;
589
590 port_dir = debugfs_create_dir(port->dev->name, parent);
591 if (IS_ERR(port_dir))
592 return PTR_ERR(port_dir);
593
594 debugfs_create_file("parser_entries", 0444, port_dir, port,
595 &mvpp2_dbgfs_port_parser_fops);
596
597 debugfs_create_file("mac_filter", 0444, port_dir, port,
598 &mvpp2_dbgfs_filter_fops);
599
600 debugfs_create_file("vid_filter", 0444, port_dir, port,
601 &mvpp2_dbgfs_port_vid_fops);
602
Maxime Chevallierdba1d912018-07-14 13:29:27 +0200603 debugfs_create_file("default_rxq", 0444, port_dir, port,
604 &mvpp2_dbgfs_flow_c2_rxq_fops);
605
606 debugfs_create_file("rss_enable", 0444, port_dir, port,
607 &mvpp2_dbgfs_flow_c2_enable_fops);
608
Maxime Chevallier21da57a2018-07-14 13:29:25 +0200609 return 0;
610}
611
612void mvpp2_dbgfs_cleanup(struct mvpp2 *priv)
613{
614 debugfs_remove_recursive(priv->dbgfs_dir);
615}
616
617void mvpp2_dbgfs_init(struct mvpp2 *priv, const char *name)
618{
619 struct dentry *mvpp2_dir, *mvpp2_root;
620 int ret, i;
621
622 mvpp2_root = debugfs_lookup(MVPP2_DRIVER_NAME, NULL);
623 if (!mvpp2_root) {
624 mvpp2_root = debugfs_create_dir(MVPP2_DRIVER_NAME, NULL);
625 if (IS_ERR(mvpp2_root))
626 return;
627 }
628
629 mvpp2_dir = debugfs_create_dir(name, mvpp2_root);
630 if (IS_ERR(mvpp2_dir))
631 return;
632
633 priv->dbgfs_dir = mvpp2_dir;
634
635 ret = mvpp2_dbgfs_prs_init(mvpp2_dir, priv);
636 if (ret)
637 goto err;
638
639 for (i = 0; i < priv->port_count; i++) {
640 ret = mvpp2_dbgfs_port_init(mvpp2_dir, priv->port_list[i]);
641 if (ret)
642 goto err;
643 }
644
Maxime Chevallierdba1d912018-07-14 13:29:27 +0200645 ret = mvpp2_dbgfs_flow_init(mvpp2_dir, priv);
646 if (ret)
647 goto err;
648
Maxime Chevallier21da57a2018-07-14 13:29:25 +0200649 return;
650err:
651 mvpp2_dbgfs_cleanup(priv);
652}