// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2018-2019, Vladimir Oltean */ #include "sja1105.h" enum sja1105_counter_index { __SJA1105_COUNTER_UNUSED, /* MAC */ N_RUNT, N_SOFERR, N_ALIGNERR, N_MIIERR, TYPEERR, SIZEERR, TCTIMEOUT, PRIORERR, NOMASTER, MEMOV, MEMERR, INVTYP, INTCYOV, DOMERR, PCFBAGDROP, SPCPRIOR, AGEPRIOR, PORTDROP, LENDROP, BAGDROP, POLICEERR, DRPNONA664ERR, SPCERR, AGEDRP, /* HL1 */ N_N664ERR, N_VLANERR, N_UNRELEASED, N_SIZEERR, N_CRCERR, N_VLNOTFOUND, N_CTPOLERR, N_POLERR, N_RXFRM, N_RXBYTE, N_TXFRM, N_TXBYTE, /* HL2 */ N_QFULL, N_PART_DROP, N_EGR_DISABLED, N_NOT_REACH, __MAX_SJA1105ET_PORT_COUNTER, /* P/Q/R/S only */ /* ETHER */ N_DROPS_NOLEARN = __MAX_SJA1105ET_PORT_COUNTER, N_DROPS_NOROUTE, N_DROPS_ILL_DTAG, N_DROPS_DTAG, N_DROPS_SOTAG, N_DROPS_SITAG, N_DROPS_UTAG, N_TX_BYTES_1024_2047, N_TX_BYTES_512_1023, N_TX_BYTES_256_511, N_TX_BYTES_128_255, N_TX_BYTES_65_127, N_TX_BYTES_64, N_TX_MCAST, N_TX_BCAST, N_RX_BYTES_1024_2047, N_RX_BYTES_512_1023, N_RX_BYTES_256_511, N_RX_BYTES_128_255, N_RX_BYTES_65_127, N_RX_BYTES_64, N_RX_MCAST, N_RX_BCAST, __MAX_SJA1105PQRS_PORT_COUNTER, }; struct sja1105_port_counter { enum sja1105_stats_area area; const char name[ETH_GSTRING_LEN]; int offset; int start; int end; bool is_64bit; }; static const struct sja1105_port_counter sja1105_port_counters[] = { /* MAC-Level Diagnostic Counters */ [N_RUNT] = { .area = MAC, .name = "n_runt", .offset = 0, .start = 31, .end = 24, }, [N_SOFERR] = { .area = MAC, .name = "n_soferr", .offset = 0x0, .start = 23, .end = 16, }, [N_ALIGNERR] = { .area = MAC, .name = "n_alignerr", .offset = 0x0, .start = 15, .end = 8, }, [N_MIIERR] = { .area = MAC, .name = "n_miierr", .offset = 0x0, .start = 7, .end = 0, }, /* MAC-Level Diagnostic Flags */ [TYPEERR] = { .area = MAC, .name = "typeerr", .offset = 0x1, .start = 27, .end = 27, }, [SIZEERR] = { .area = MAC, .name = "sizeerr", .offset = 0x1, .start = 26, .end = 26, }, [TCTIMEOUT] = { .area = MAC, .name = "tctimeout", .offset = 0x1, .start = 25, .end = 25, }, [PRIORERR] = { .area = MAC, .name = "priorerr", .offset = 0x1, .start = 24, .end = 24, }, [NOMASTER] = { .area = MAC, .name = "nomaster", .offset = 0x1, .start = 23, .end = 23, }, [MEMOV] = { .area = MAC, .name = "memov", .offset = 0x1, .start = 22, .end = 22, }, [MEMERR] = { .area = MAC, .name = "memerr", .offset = 0x1, .start = 21, .end = 21, }, [INVTYP] = { .area = MAC, .name = "invtyp", .offset = 0x1, .start = 19, .end = 19, }, [INTCYOV] = { .area = MAC, .name = "intcyov", .offset = 0x1, .start = 18, .end = 18, }, [DOMERR] = { .area = MAC, .name = "domerr", .offset = 0x1, .start = 17, .end = 17, }, [PCFBAGDROP] = { .area = MAC, .name = "pcfbagdrop", .offset = 0x1, .start = 16, .end = 16, }, [SPCPRIOR] = { .area = MAC, .name = "spcprior", .offset = 0x1, .start = 15, .end = 12, }, [AGEPRIOR] = { .area = MAC, .name = "ageprior", .offset = 0x1, .start = 11, .end = 8, }, [PORTDROP] = { .area = MAC, .name = "portdrop", .offset = 0x1, .start = 6, .end = 6, }, [LENDROP] = { .area = MAC, .name = "lendrop", .offset = 0x1, .start = 5, .end = 5, }, [BAGDROP] = { .area = MAC, .name = "bagdrop", .offset = 0x1, .start = 4, .end = 4, }, [POLICEERR] = { .area = MAC, .name = "policeerr", .offset = 0x1, .start = 3, .end = 3, }, [DRPNONA664ERR] = { .area = MAC, .name = "drpnona664err", .offset = 0x1, .start = 2, .end = 2, }, [SPCERR] = { .area = MAC, .name = "spcerr", .offset = 0x1, .start = 1, .end = 1, }, [AGEDRP] = { .area = MAC, .name = "agedrp", .offset = 0x1, .start = 0, .end = 0, }, /* High-Level Diagnostic Counters */ [N_N664ERR] = { .area = HL1, .name = "n_n664err", .offset = 0xF, .start = 31, .end = 0, }, [N_VLANERR] = { .area = HL1, .name = "n_vlanerr", .offset = 0xE, .start = 31, .end = 0, }, [N_UNRELEASED] = { .area = HL1, .name = "n_unreleased", .offset = 0xD, .start = 31, .end = 0, }, [N_SIZEERR] = { .area = HL1, .name = "n_sizeerr", .offset = 0xC, .start = 31, .end = 0, }, [N_CRCERR] = { .area = HL1, .name = "n_crcerr", .offset = 0xB, .start = 31, .end = 0, }, [N_VLNOTFOUND] = { .area = HL1, .name = "n_vlnotfound", .offset = 0xA, .start = 31, .end = 0, }, [N_CTPOLERR] = { .area = HL1, .name = "n_ctpolerr", .offset = 0x9, .start = 31, .end = 0, }, [N_POLERR] = { .area = HL1, .name = "n_polerr", .offset = 0x8, .start = 31, .end = 0, }, [N_RXFRM] = { .area = HL1, .name = "n_rxfrm", .offset = 0x6, .start = 31, .end = 0, .is_64bit = true, }, [N_RXBYTE] = { .area = HL1, .name = "n_rxbyte", .offset = 0x4, .start = 31, .end = 0, .is_64bit = true, }, [N_TXFRM] = { .area = HL1, .name = "n_txfrm", .offset = 0x2, .start = 31, .end = 0, .is_64bit = true, }, [N_TXBYTE] = { .area = HL1, .name = "n_txbyte", .offset = 0x0, .start = 31, .end = 0, .is_64bit = true, }, [N_QFULL] = { .area = HL2, .name = "n_qfull", .offset = 0x3, .start = 31, .end = 0, }, [N_PART_DROP] = { .area = HL2, .name = "n_part_drop", .offset = 0x2, .start = 31, .end = 0, }, [N_EGR_DISABLED] = { .area = HL2, .name = "n_egr_disabled", .offset = 0x1, .start = 31, .end = 0, }, [N_NOT_REACH] = { .area = HL2, .name = "n_not_reach", .offset = 0x0, .start = 31, .end = 0, }, /* Ether Stats */ [N_DROPS_NOLEARN] = { .area = ETHER, .name = "n_drops_nolearn", .offset = 0x16, .start = 31, .end = 0, }, [N_DROPS_NOROUTE] = { .area = ETHER, .name = "n_drops_noroute", .offset = 0x15, .start = 31, .end = 0, }, [N_DROPS_ILL_DTAG] = { .area = ETHER, .name = "n_drops_ill_dtag", .offset = 0x14, .start = 31, .end = 0, }, [N_DROPS_DTAG] = { .area = ETHER, .name = "n_drops_dtag", .offset = 0x13, .start = 31, .end = 0, }, [N_DROPS_SOTAG] = { .area = ETHER, .name = "n_drops_sotag", .offset = 0x12, .start = 31, .end = 0, }, [N_DROPS_SITAG] = { .area = ETHER, .name = "n_drops_sitag", .offset = 0x11, .start = 31, .end = 0, }, [N_DROPS_UTAG] = { .area = ETHER, .name = "n_drops_utag", .offset = 0x10, .start = 31, .end = 0, }, [N_TX_BYTES_1024_2047] = { .area = ETHER, .name = "n_tx_bytes_1024_2047", .offset = 0x0F, .start = 31, .end = 0, }, [N_TX_BYTES_512_1023] = { .area = ETHER, .name = "n_tx_bytes_512_1023", .offset = 0x0E, .start = 31, .end = 0, }, [N_TX_BYTES_256_511] = { .area = ETHER, .name = "n_tx_bytes_256_511", .offset = 0x0D, .start = 31, .end = 0, }, [N_TX_BYTES_128_255] = { .area = ETHER, .name = "n_tx_bytes_128_255", .offset = 0x0C, .start = 31, .end = 0, }, [N_TX_BYTES_65_127] = { .area = ETHER, .name = "n_tx_bytes_65_127", .offset = 0x0B, .start = 31, .end = 0, }, [N_TX_BYTES_64] = { .area = ETHER, .name = "n_tx_bytes_64", .offset = 0x0A, .start = 31, .end = 0, }, [N_TX_MCAST] = { .area = ETHER, .name = "n_tx_mcast", .offset = 0x09, .start = 31, .end = 0, }, [N_TX_BCAST] = { .area = ETHER, .name = "n_tx_bcast", .offset = 0x08, .start = 31, .end = 0, }, [N_RX_BYTES_1024_2047] = { .area = ETHER, .name = "n_rx_bytes_1024_2047", .offset = 0x07, .start = 31, .end = 0, }, [N_RX_BYTES_512_1023] = { .area = ETHER, .name = "n_rx_bytes_512_1023", .offset = 0x06, .start = 31, .end = 0, }, [N_RX_BYTES_256_511] = { .area = ETHER, .name = "n_rx_bytes_256_511", .offset = 0x05, .start = 31, .end = 0, }, [N_RX_BYTES_128_255] = { .area = ETHER, .name = "n_rx_bytes_128_255", .offset = 0x04, .start = 31, .end = 0, }, [N_RX_BYTES_65_127] = { .area = ETHER, .name = "n_rx_bytes_65_127", .offset = 0x03, .start = 31, .end = 0, }, [N_RX_BYTES_64] = { .area = ETHER, .name = "n_rx_bytes_64", .offset = 0x02, .start = 31, .end = 0, }, [N_RX_MCAST] = { .area = ETHER, .name = "n_rx_mcast", .offset = 0x01, .start = 31, .end = 0, }, [N_RX_BCAST] = { .area = ETHER, .name = "n_rx_bcast", .offset = 0x00, .start = 31, .end = 0, }, }; static int sja1105_port_counter_read(struct sja1105_private *priv, int port, enum sja1105_counter_index idx, u64 *ctr) { const struct sja1105_port_counter *c = &sja1105_port_counters[idx]; size_t size = c->is_64bit ? 8 : 4; u8 buf[8] = {0}; u64 regs; int rc; regs = priv->info->regs->stats[c->area][port]; rc = sja1105_xfer_buf(priv, SPI_READ, regs + c->offset, buf, size); if (rc) return rc; sja1105_unpack(buf, ctr, c->start, c->end, size); return 0; } void sja1105_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) { struct sja1105_private *priv = ds->priv; enum sja1105_counter_index max_ctr, i; int rc, k = 0; if (priv->info->device_id == SJA1105E_DEVICE_ID || priv->info->device_id == SJA1105T_DEVICE_ID) max_ctr = __MAX_SJA1105ET_PORT_COUNTER; else max_ctr = __MAX_SJA1105PQRS_PORT_COUNTER; for (i = 0; i < max_ctr; i++) { rc = sja1105_port_counter_read(priv, port, i, &data[k++]); if (rc) { dev_err(ds->dev, "Failed to read port %d counters: %d\n", port, rc); break; } } } void sja1105_get_strings(struct dsa_switch *ds, int port, u32 stringset, u8 *data) { struct sja1105_private *priv = ds->priv; enum sja1105_counter_index max_ctr, i; char *p = data; if (stringset != ETH_SS_STATS) return; if (priv->info->device_id == SJA1105E_DEVICE_ID || priv->info->device_id == SJA1105T_DEVICE_ID) max_ctr = __MAX_SJA1105ET_PORT_COUNTER; else max_ctr = __MAX_SJA1105PQRS_PORT_COUNTER; for (i = 0; i < max_ctr; i++) { strscpy(p, sja1105_port_counters[i].name, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } } int sja1105_get_sset_count(struct dsa_switch *ds, int port, int sset) { struct sja1105_private *priv = ds->priv; enum sja1105_counter_index max_ctr, i; int sset_count = 0; if (sset != ETH_SS_STATS) return -EOPNOTSUPP; if (priv->info->device_id == SJA1105E_DEVICE_ID || priv->info->device_id == SJA1105T_DEVICE_ID) max_ctr = __MAX_SJA1105ET_PORT_COUNTER; else max_ctr = __MAX_SJA1105PQRS_PORT_COUNTER; for (i = 0; i < max_ctr; i++) { if (!strlen(sja1105_port_counters[i].name)) continue; sset_count++; } return sset_count; }