1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com> |
3 | */ |
4 | #include "sja1105.h" |
5 | |
6 | enum sja1105_counter_index { |
7 | __SJA1105_COUNTER_UNUSED, |
8 | /* MAC */ |
9 | N_RUNT, |
10 | N_SOFERR, |
11 | N_ALIGNERR, |
12 | N_MIIERR, |
13 | TYPEERR, |
14 | SIZEERR, |
15 | TCTIMEOUT, |
16 | PRIORERR, |
17 | NOMASTER, |
18 | MEMOV, |
19 | MEMERR, |
20 | INVTYP, |
21 | INTCYOV, |
22 | DOMERR, |
23 | PCFBAGDROP, |
24 | SPCPRIOR, |
25 | AGEPRIOR, |
26 | PORTDROP, |
27 | LENDROP, |
28 | BAGDROP, |
29 | POLICEERR, |
30 | DRPNONA664ERR, |
31 | SPCERR, |
32 | AGEDRP, |
33 | /* HL1 */ |
34 | N_N664ERR, |
35 | N_VLANERR, |
36 | N_UNRELEASED, |
37 | N_SIZEERR, |
38 | N_CRCERR, |
39 | N_VLNOTFOUND, |
40 | N_CTPOLERR, |
41 | N_POLERR, |
42 | N_RXFRM, |
43 | N_RXBYTE, |
44 | N_TXFRM, |
45 | N_TXBYTE, |
46 | /* HL2 */ |
47 | N_QFULL, |
48 | N_PART_DROP, |
49 | N_EGR_DISABLED, |
50 | N_NOT_REACH, |
51 | __MAX_SJA1105ET_PORT_COUNTER, |
52 | /* P/Q/R/S only */ |
53 | /* ETHER */ |
54 | N_DROPS_NOLEARN = __MAX_SJA1105ET_PORT_COUNTER, |
55 | N_DROPS_NOROUTE, |
56 | N_DROPS_ILL_DTAG, |
57 | N_DROPS_DTAG, |
58 | N_DROPS_SOTAG, |
59 | N_DROPS_SITAG, |
60 | N_DROPS_UTAG, |
61 | N_TX_BYTES_1024_2047, |
62 | N_TX_BYTES_512_1023, |
63 | N_TX_BYTES_256_511, |
64 | N_TX_BYTES_128_255, |
65 | N_TX_BYTES_65_127, |
66 | N_TX_BYTES_64, |
67 | N_TX_MCAST, |
68 | N_TX_BCAST, |
69 | N_RX_BYTES_1024_2047, |
70 | N_RX_BYTES_512_1023, |
71 | N_RX_BYTES_256_511, |
72 | N_RX_BYTES_128_255, |
73 | N_RX_BYTES_65_127, |
74 | N_RX_BYTES_64, |
75 | N_RX_MCAST, |
76 | N_RX_BCAST, |
77 | __MAX_SJA1105PQRS_PORT_COUNTER, |
78 | }; |
79 | |
80 | struct sja1105_port_counter { |
81 | enum sja1105_stats_area area; |
82 | const char name[ETH_GSTRING_LEN]; |
83 | int offset; |
84 | int start; |
85 | int end; |
86 | bool is_64bit; |
87 | }; |
88 | |
89 | static const struct sja1105_port_counter sja1105_port_counters[] = { |
90 | /* MAC-Level Diagnostic Counters */ |
91 | [N_RUNT] = { |
92 | .area = MAC, |
93 | .name = "n_runt" , |
94 | .offset = 0, |
95 | .start = 31, |
96 | .end = 24, |
97 | }, |
98 | [N_SOFERR] = { |
99 | .area = MAC, |
100 | .name = "n_soferr" , |
101 | .offset = 0x0, |
102 | .start = 23, |
103 | .end = 16, |
104 | }, |
105 | [N_ALIGNERR] = { |
106 | .area = MAC, |
107 | .name = "n_alignerr" , |
108 | .offset = 0x0, |
109 | .start = 15, |
110 | .end = 8, |
111 | }, |
112 | [N_MIIERR] = { |
113 | .area = MAC, |
114 | .name = "n_miierr" , |
115 | .offset = 0x0, |
116 | .start = 7, |
117 | .end = 0, |
118 | }, |
119 | /* MAC-Level Diagnostic Flags */ |
120 | [TYPEERR] = { |
121 | .area = MAC, |
122 | .name = "typeerr" , |
123 | .offset = 0x1, |
124 | .start = 27, |
125 | .end = 27, |
126 | }, |
127 | [SIZEERR] = { |
128 | .area = MAC, |
129 | .name = "sizeerr" , |
130 | .offset = 0x1, |
131 | .start = 26, |
132 | .end = 26, |
133 | }, |
134 | [TCTIMEOUT] = { |
135 | .area = MAC, |
136 | .name = "tctimeout" , |
137 | .offset = 0x1, |
138 | .start = 25, |
139 | .end = 25, |
140 | }, |
141 | [PRIORERR] = { |
142 | .area = MAC, |
143 | .name = "priorerr" , |
144 | .offset = 0x1, |
145 | .start = 24, |
146 | .end = 24, |
147 | }, |
148 | [NOMASTER] = { |
149 | .area = MAC, |
150 | .name = "nomaster" , |
151 | .offset = 0x1, |
152 | .start = 23, |
153 | .end = 23, |
154 | }, |
155 | [MEMOV] = { |
156 | .area = MAC, |
157 | .name = "memov" , |
158 | .offset = 0x1, |
159 | .start = 22, |
160 | .end = 22, |
161 | }, |
162 | [MEMERR] = { |
163 | .area = MAC, |
164 | .name = "memerr" , |
165 | .offset = 0x1, |
166 | .start = 21, |
167 | .end = 21, |
168 | }, |
169 | [INVTYP] = { |
170 | .area = MAC, |
171 | .name = "invtyp" , |
172 | .offset = 0x1, |
173 | .start = 19, |
174 | .end = 19, |
175 | }, |
176 | [INTCYOV] = { |
177 | .area = MAC, |
178 | .name = "intcyov" , |
179 | .offset = 0x1, |
180 | .start = 18, |
181 | .end = 18, |
182 | }, |
183 | [DOMERR] = { |
184 | .area = MAC, |
185 | .name = "domerr" , |
186 | .offset = 0x1, |
187 | .start = 17, |
188 | .end = 17, |
189 | }, |
190 | [PCFBAGDROP] = { |
191 | .area = MAC, |
192 | .name = "pcfbagdrop" , |
193 | .offset = 0x1, |
194 | .start = 16, |
195 | .end = 16, |
196 | }, |
197 | [SPCPRIOR] = { |
198 | .area = MAC, |
199 | .name = "spcprior" , |
200 | .offset = 0x1, |
201 | .start = 15, |
202 | .end = 12, |
203 | }, |
204 | [AGEPRIOR] = { |
205 | .area = MAC, |
206 | .name = "ageprior" , |
207 | .offset = 0x1, |
208 | .start = 11, |
209 | .end = 8, |
210 | }, |
211 | [PORTDROP] = { |
212 | .area = MAC, |
213 | .name = "portdrop" , |
214 | .offset = 0x1, |
215 | .start = 6, |
216 | .end = 6, |
217 | }, |
218 | [LENDROP] = { |
219 | .area = MAC, |
220 | .name = "lendrop" , |
221 | .offset = 0x1, |
222 | .start = 5, |
223 | .end = 5, |
224 | }, |
225 | [BAGDROP] = { |
226 | .area = MAC, |
227 | .name = "bagdrop" , |
228 | .offset = 0x1, |
229 | .start = 4, |
230 | .end = 4, |
231 | }, |
232 | [POLICEERR] = { |
233 | .area = MAC, |
234 | .name = "policeerr" , |
235 | .offset = 0x1, |
236 | .start = 3, |
237 | .end = 3, |
238 | }, |
239 | [DRPNONA664ERR] = { |
240 | .area = MAC, |
241 | .name = "drpnona664err" , |
242 | .offset = 0x1, |
243 | .start = 2, |
244 | .end = 2, |
245 | }, |
246 | [SPCERR] = { |
247 | .area = MAC, |
248 | .name = "spcerr" , |
249 | .offset = 0x1, |
250 | .start = 1, |
251 | .end = 1, |
252 | }, |
253 | [AGEDRP] = { |
254 | .area = MAC, |
255 | .name = "agedrp" , |
256 | .offset = 0x1, |
257 | .start = 0, |
258 | .end = 0, |
259 | }, |
260 | /* High-Level Diagnostic Counters */ |
261 | [N_N664ERR] = { |
262 | .area = HL1, |
263 | .name = "n_n664err" , |
264 | .offset = 0xF, |
265 | .start = 31, |
266 | .end = 0, |
267 | }, |
268 | [N_VLANERR] = { |
269 | .area = HL1, |
270 | .name = "n_vlanerr" , |
271 | .offset = 0xE, |
272 | .start = 31, |
273 | .end = 0, |
274 | }, |
275 | [N_UNRELEASED] = { |
276 | .area = HL1, |
277 | .name = "n_unreleased" , |
278 | .offset = 0xD, |
279 | .start = 31, |
280 | .end = 0, |
281 | }, |
282 | [N_SIZEERR] = { |
283 | .area = HL1, |
284 | .name = "n_sizeerr" , |
285 | .offset = 0xC, |
286 | .start = 31, |
287 | .end = 0, |
288 | }, |
289 | [N_CRCERR] = { |
290 | .area = HL1, |
291 | .name = "n_crcerr" , |
292 | .offset = 0xB, |
293 | .start = 31, |
294 | .end = 0, |
295 | }, |
296 | [N_VLNOTFOUND] = { |
297 | .area = HL1, |
298 | .name = "n_vlnotfound" , |
299 | .offset = 0xA, |
300 | .start = 31, |
301 | .end = 0, |
302 | }, |
303 | [N_CTPOLERR] = { |
304 | .area = HL1, |
305 | .name = "n_ctpolerr" , |
306 | .offset = 0x9, |
307 | .start = 31, |
308 | .end = 0, |
309 | }, |
310 | [N_POLERR] = { |
311 | .area = HL1, |
312 | .name = "n_polerr" , |
313 | .offset = 0x8, |
314 | .start = 31, |
315 | .end = 0, |
316 | }, |
317 | [N_RXFRM] = { |
318 | .area = HL1, |
319 | .name = "n_rxfrm" , |
320 | .offset = 0x6, |
321 | .start = 31, |
322 | .end = 0, |
323 | .is_64bit = true, |
324 | }, |
325 | [N_RXBYTE] = { |
326 | .area = HL1, |
327 | .name = "n_rxbyte" , |
328 | .offset = 0x4, |
329 | .start = 31, |
330 | .end = 0, |
331 | .is_64bit = true, |
332 | }, |
333 | [N_TXFRM] = { |
334 | .area = HL1, |
335 | .name = "n_txfrm" , |
336 | .offset = 0x2, |
337 | .start = 31, |
338 | .end = 0, |
339 | .is_64bit = true, |
340 | }, |
341 | [N_TXBYTE] = { |
342 | .area = HL1, |
343 | .name = "n_txbyte" , |
344 | .offset = 0x0, |
345 | .start = 31, |
346 | .end = 0, |
347 | .is_64bit = true, |
348 | }, |
349 | [N_QFULL] = { |
350 | .area = HL2, |
351 | .name = "n_qfull" , |
352 | .offset = 0x3, |
353 | .start = 31, |
354 | .end = 0, |
355 | }, |
356 | [N_PART_DROP] = { |
357 | .area = HL2, |
358 | .name = "n_part_drop" , |
359 | .offset = 0x2, |
360 | .start = 31, |
361 | .end = 0, |
362 | }, |
363 | [N_EGR_DISABLED] = { |
364 | .area = HL2, |
365 | .name = "n_egr_disabled" , |
366 | .offset = 0x1, |
367 | .start = 31, |
368 | .end = 0, |
369 | }, |
370 | [N_NOT_REACH] = { |
371 | .area = HL2, |
372 | .name = "n_not_reach" , |
373 | .offset = 0x0, |
374 | .start = 31, |
375 | .end = 0, |
376 | }, |
377 | /* Ether Stats */ |
378 | [N_DROPS_NOLEARN] = { |
379 | .area = ETHER, |
380 | .name = "n_drops_nolearn" , |
381 | .offset = 0x16, |
382 | .start = 31, |
383 | .end = 0, |
384 | }, |
385 | [N_DROPS_NOROUTE] = { |
386 | .area = ETHER, |
387 | .name = "n_drops_noroute" , |
388 | .offset = 0x15, |
389 | .start = 31, |
390 | .end = 0, |
391 | }, |
392 | [N_DROPS_ILL_DTAG] = { |
393 | .area = ETHER, |
394 | .name = "n_drops_ill_dtag" , |
395 | .offset = 0x14, |
396 | .start = 31, |
397 | .end = 0, |
398 | }, |
399 | [N_DROPS_DTAG] = { |
400 | .area = ETHER, |
401 | .name = "n_drops_dtag" , |
402 | .offset = 0x13, |
403 | .start = 31, |
404 | .end = 0, |
405 | }, |
406 | [N_DROPS_SOTAG] = { |
407 | .area = ETHER, |
408 | .name = "n_drops_sotag" , |
409 | .offset = 0x12, |
410 | .start = 31, |
411 | .end = 0, |
412 | }, |
413 | [N_DROPS_SITAG] = { |
414 | .area = ETHER, |
415 | .name = "n_drops_sitag" , |
416 | .offset = 0x11, |
417 | .start = 31, |
418 | .end = 0, |
419 | }, |
420 | [N_DROPS_UTAG] = { |
421 | .area = ETHER, |
422 | .name = "n_drops_utag" , |
423 | .offset = 0x10, |
424 | .start = 31, |
425 | .end = 0, |
426 | }, |
427 | [N_TX_BYTES_1024_2047] = { |
428 | .area = ETHER, |
429 | .name = "n_tx_bytes_1024_2047" , |
430 | .offset = 0x0F, |
431 | .start = 31, |
432 | .end = 0, |
433 | }, |
434 | [N_TX_BYTES_512_1023] = { |
435 | .area = ETHER, |
436 | .name = "n_tx_bytes_512_1023" , |
437 | .offset = 0x0E, |
438 | .start = 31, |
439 | .end = 0, |
440 | }, |
441 | [N_TX_BYTES_256_511] = { |
442 | .area = ETHER, |
443 | .name = "n_tx_bytes_256_511" , |
444 | .offset = 0x0D, |
445 | .start = 31, |
446 | .end = 0, |
447 | }, |
448 | [N_TX_BYTES_128_255] = { |
449 | .area = ETHER, |
450 | .name = "n_tx_bytes_128_255" , |
451 | .offset = 0x0C, |
452 | .start = 31, |
453 | .end = 0, |
454 | }, |
455 | [N_TX_BYTES_65_127] = { |
456 | .area = ETHER, |
457 | .name = "n_tx_bytes_65_127" , |
458 | .offset = 0x0B, |
459 | .start = 31, |
460 | .end = 0, |
461 | }, |
462 | [N_TX_BYTES_64] = { |
463 | .area = ETHER, |
464 | .name = "n_tx_bytes_64" , |
465 | .offset = 0x0A, |
466 | .start = 31, |
467 | .end = 0, |
468 | }, |
469 | [N_TX_MCAST] = { |
470 | .area = ETHER, |
471 | .name = "n_tx_mcast" , |
472 | .offset = 0x09, |
473 | .start = 31, |
474 | .end = 0, |
475 | }, |
476 | [N_TX_BCAST] = { |
477 | .area = ETHER, |
478 | .name = "n_tx_bcast" , |
479 | .offset = 0x08, |
480 | .start = 31, |
481 | .end = 0, |
482 | }, |
483 | [N_RX_BYTES_1024_2047] = { |
484 | .area = ETHER, |
485 | .name = "n_rx_bytes_1024_2047" , |
486 | .offset = 0x07, |
487 | .start = 31, |
488 | .end = 0, |
489 | }, |
490 | [N_RX_BYTES_512_1023] = { |
491 | .area = ETHER, |
492 | .name = "n_rx_bytes_512_1023" , |
493 | .offset = 0x06, |
494 | .start = 31, |
495 | .end = 0, |
496 | }, |
497 | [N_RX_BYTES_256_511] = { |
498 | .area = ETHER, |
499 | .name = "n_rx_bytes_256_511" , |
500 | .offset = 0x05, |
501 | .start = 31, |
502 | .end = 0, |
503 | }, |
504 | [N_RX_BYTES_128_255] = { |
505 | .area = ETHER, |
506 | .name = "n_rx_bytes_128_255" , |
507 | .offset = 0x04, |
508 | .start = 31, |
509 | .end = 0, |
510 | }, |
511 | [N_RX_BYTES_65_127] = { |
512 | .area = ETHER, |
513 | .name = "n_rx_bytes_65_127" , |
514 | .offset = 0x03, |
515 | .start = 31, |
516 | .end = 0, |
517 | }, |
518 | [N_RX_BYTES_64] = { |
519 | .area = ETHER, |
520 | .name = "n_rx_bytes_64" , |
521 | .offset = 0x02, |
522 | .start = 31, |
523 | .end = 0, |
524 | }, |
525 | [N_RX_MCAST] = { |
526 | .area = ETHER, |
527 | .name = "n_rx_mcast" , |
528 | .offset = 0x01, |
529 | .start = 31, |
530 | .end = 0, |
531 | }, |
532 | [N_RX_BCAST] = { |
533 | .area = ETHER, |
534 | .name = "n_rx_bcast" , |
535 | .offset = 0x00, |
536 | .start = 31, |
537 | .end = 0, |
538 | }, |
539 | }; |
540 | |
541 | static int sja1105_port_counter_read(struct sja1105_private *priv, int port, |
542 | enum sja1105_counter_index idx, u64 *ctr) |
543 | { |
544 | const struct sja1105_port_counter *c = &sja1105_port_counters[idx]; |
545 | size_t size = c->is_64bit ? 8 : 4; |
546 | u8 buf[8] = {0}; |
547 | u64 regs; |
548 | int rc; |
549 | |
550 | regs = priv->info->regs->stats[c->area][port]; |
551 | |
552 | rc = sja1105_xfer_buf(priv, rw: SPI_READ, reg_addr: regs + c->offset, buf, len: size); |
553 | if (rc) |
554 | return rc; |
555 | |
556 | sja1105_unpack(buf, val: ctr, start: c->start, end: c->end, len: size); |
557 | |
558 | return 0; |
559 | } |
560 | |
561 | void sja1105_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) |
562 | { |
563 | struct sja1105_private *priv = ds->priv; |
564 | enum sja1105_counter_index max_ctr, i; |
565 | int rc, k = 0; |
566 | |
567 | if (priv->info->device_id == SJA1105E_DEVICE_ID || |
568 | priv->info->device_id == SJA1105T_DEVICE_ID) |
569 | max_ctr = __MAX_SJA1105ET_PORT_COUNTER; |
570 | else |
571 | max_ctr = __MAX_SJA1105PQRS_PORT_COUNTER; |
572 | |
573 | for (i = 0; i < max_ctr; i++) { |
574 | rc = sja1105_port_counter_read(priv, port, idx: i, ctr: &data[k++]); |
575 | if (rc) { |
576 | dev_err(ds->dev, |
577 | "Failed to read port %d counters: %d\n" , |
578 | port, rc); |
579 | break; |
580 | } |
581 | } |
582 | } |
583 | |
584 | void sja1105_get_strings(struct dsa_switch *ds, int port, |
585 | u32 stringset, u8 *data) |
586 | { |
587 | struct sja1105_private *priv = ds->priv; |
588 | enum sja1105_counter_index max_ctr, i; |
589 | char *p = data; |
590 | |
591 | if (stringset != ETH_SS_STATS) |
592 | return; |
593 | |
594 | if (priv->info->device_id == SJA1105E_DEVICE_ID || |
595 | priv->info->device_id == SJA1105T_DEVICE_ID) |
596 | max_ctr = __MAX_SJA1105ET_PORT_COUNTER; |
597 | else |
598 | max_ctr = __MAX_SJA1105PQRS_PORT_COUNTER; |
599 | |
600 | for (i = 0; i < max_ctr; i++) { |
601 | strscpy(p, sja1105_port_counters[i].name, ETH_GSTRING_LEN); |
602 | p += ETH_GSTRING_LEN; |
603 | } |
604 | } |
605 | |
606 | int sja1105_get_sset_count(struct dsa_switch *ds, int port, int sset) |
607 | { |
608 | struct sja1105_private *priv = ds->priv; |
609 | enum sja1105_counter_index max_ctr, i; |
610 | int sset_count = 0; |
611 | |
612 | if (sset != ETH_SS_STATS) |
613 | return -EOPNOTSUPP; |
614 | |
615 | if (priv->info->device_id == SJA1105E_DEVICE_ID || |
616 | priv->info->device_id == SJA1105T_DEVICE_ID) |
617 | max_ctr = __MAX_SJA1105ET_PORT_COUNTER; |
618 | else |
619 | max_ctr = __MAX_SJA1105PQRS_PORT_COUNTER; |
620 | |
621 | for (i = 0; i < max_ctr; i++) { |
622 | if (!strlen(sja1105_port_counters[i].name)) |
623 | continue; |
624 | |
625 | sset_count++; |
626 | } |
627 | |
628 | return sset_count; |
629 | } |
630 | |