1fe90104cSVladimir Oltean // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2fe90104cSVladimir Oltean /* Statistics for Ocelot switch family 3fe90104cSVladimir Oltean * 4fe90104cSVladimir Oltean * Copyright (c) 2017 Microsemi Corporation 5*e32036e1SVladimir Oltean * Copyright 2022 NXP 6fe90104cSVladimir Oltean */ 7fe90104cSVladimir Oltean #include <linux/spinlock.h> 8fe90104cSVladimir Oltean #include <linux/mutex.h> 9fe90104cSVladimir Oltean #include <linux/workqueue.h> 10fe90104cSVladimir Oltean #include "ocelot.h" 11fe90104cSVladimir Oltean 12fe90104cSVladimir Oltean /* Read the counters from hardware and keep them in region->buf. 13fe90104cSVladimir Oltean * Caller must hold &ocelot->stat_view_lock. 14fe90104cSVladimir Oltean */ 15fe90104cSVladimir Oltean static int ocelot_port_update_stats(struct ocelot *ocelot, int port) 16fe90104cSVladimir Oltean { 17fe90104cSVladimir Oltean struct ocelot_stats_region *region; 18fe90104cSVladimir Oltean int err; 19fe90104cSVladimir Oltean 20fe90104cSVladimir Oltean /* Configure the port to read the stats from */ 21fe90104cSVladimir Oltean ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG); 22fe90104cSVladimir Oltean 23fe90104cSVladimir Oltean list_for_each_entry(region, &ocelot->stats_regions, node) { 24fe90104cSVladimir Oltean err = ocelot_bulk_read(ocelot, region->base, region->buf, 25fe90104cSVladimir Oltean region->count); 26fe90104cSVladimir Oltean if (err) 27fe90104cSVladimir Oltean return err; 28fe90104cSVladimir Oltean } 29fe90104cSVladimir Oltean 30fe90104cSVladimir Oltean return 0; 31fe90104cSVladimir Oltean } 32fe90104cSVladimir Oltean 33fe90104cSVladimir Oltean /* Transfer the counters from region->buf to ocelot->stats. 34fe90104cSVladimir Oltean * Caller must hold &ocelot->stat_view_lock and &ocelot->stats_lock. 35fe90104cSVladimir Oltean */ 36fe90104cSVladimir Oltean static void ocelot_port_transfer_stats(struct ocelot *ocelot, int port) 37fe90104cSVladimir Oltean { 38fe90104cSVladimir Oltean unsigned int idx = port * OCELOT_NUM_STATS; 39fe90104cSVladimir Oltean struct ocelot_stats_region *region; 40fe90104cSVladimir Oltean int j; 41fe90104cSVladimir Oltean 42fe90104cSVladimir Oltean list_for_each_entry(region, &ocelot->stats_regions, node) { 43fe90104cSVladimir Oltean for (j = 0; j < region->count; j++) { 44fe90104cSVladimir Oltean u64 *stat = &ocelot->stats[idx + j]; 45fe90104cSVladimir Oltean u64 val = region->buf[j]; 46fe90104cSVladimir Oltean 47fe90104cSVladimir Oltean if (val < (*stat & U32_MAX)) 48fe90104cSVladimir Oltean *stat += (u64)1 << 32; 49fe90104cSVladimir Oltean 50fe90104cSVladimir Oltean *stat = (*stat & ~(u64)U32_MAX) + val; 51fe90104cSVladimir Oltean } 52fe90104cSVladimir Oltean 53fe90104cSVladimir Oltean idx += region->count; 54fe90104cSVladimir Oltean } 55fe90104cSVladimir Oltean } 56fe90104cSVladimir Oltean 57fe90104cSVladimir Oltean static void ocelot_check_stats_work(struct work_struct *work) 58fe90104cSVladimir Oltean { 59fe90104cSVladimir Oltean struct delayed_work *del_work = to_delayed_work(work); 60fe90104cSVladimir Oltean struct ocelot *ocelot = container_of(del_work, struct ocelot, 61fe90104cSVladimir Oltean stats_work); 62fe90104cSVladimir Oltean int port, err; 63fe90104cSVladimir Oltean 64fe90104cSVladimir Oltean mutex_lock(&ocelot->stat_view_lock); 65fe90104cSVladimir Oltean 66fe90104cSVladimir Oltean for (port = 0; port < ocelot->num_phys_ports; port++) { 67fe90104cSVladimir Oltean err = ocelot_port_update_stats(ocelot, port); 68fe90104cSVladimir Oltean if (err) 69fe90104cSVladimir Oltean break; 70fe90104cSVladimir Oltean 71fe90104cSVladimir Oltean spin_lock(&ocelot->stats_lock); 72fe90104cSVladimir Oltean ocelot_port_transfer_stats(ocelot, port); 73fe90104cSVladimir Oltean spin_unlock(&ocelot->stats_lock); 74fe90104cSVladimir Oltean } 75fe90104cSVladimir Oltean 76fe90104cSVladimir Oltean if (!err && ocelot->ops->update_stats) 77fe90104cSVladimir Oltean ocelot->ops->update_stats(ocelot); 78fe90104cSVladimir Oltean 79fe90104cSVladimir Oltean mutex_unlock(&ocelot->stat_view_lock); 80fe90104cSVladimir Oltean 81fe90104cSVladimir Oltean if (err) 82fe90104cSVladimir Oltean dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err); 83fe90104cSVladimir Oltean 84fe90104cSVladimir Oltean queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, 85fe90104cSVladimir Oltean OCELOT_STATS_CHECK_DELAY); 86fe90104cSVladimir Oltean } 87fe90104cSVladimir Oltean 88fe90104cSVladimir Oltean void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) 89fe90104cSVladimir Oltean { 90fe90104cSVladimir Oltean int i; 91fe90104cSVladimir Oltean 92fe90104cSVladimir Oltean if (sset != ETH_SS_STATS) 93fe90104cSVladimir Oltean return; 94fe90104cSVladimir Oltean 95fe90104cSVladimir Oltean for (i = 0; i < OCELOT_NUM_STATS; i++) { 96fe90104cSVladimir Oltean if (ocelot->stats_layout[i].name[0] == '\0') 97fe90104cSVladimir Oltean continue; 98fe90104cSVladimir Oltean 99fe90104cSVladimir Oltean memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name, 100fe90104cSVladimir Oltean ETH_GSTRING_LEN); 101fe90104cSVladimir Oltean } 102fe90104cSVladimir Oltean } 103fe90104cSVladimir Oltean EXPORT_SYMBOL(ocelot_get_strings); 104fe90104cSVladimir Oltean 105*e32036e1SVladimir Oltean /* Update ocelot->stats for the given port and run the given callback */ 106*e32036e1SVladimir Oltean static void ocelot_port_stats_run(struct ocelot *ocelot, int port, void *priv, 107*e32036e1SVladimir Oltean void (*cb)(struct ocelot *ocelot, int port, 108*e32036e1SVladimir Oltean void *priv)) 109fe90104cSVladimir Oltean { 110*e32036e1SVladimir Oltean int err; 111fe90104cSVladimir Oltean 112fe90104cSVladimir Oltean mutex_lock(&ocelot->stat_view_lock); 113fe90104cSVladimir Oltean 114fe90104cSVladimir Oltean err = ocelot_port_update_stats(ocelot, port); 115*e32036e1SVladimir Oltean if (err) { 116*e32036e1SVladimir Oltean dev_err(ocelot->dev, "Failed to update port %d stats: %pe\n", 117*e32036e1SVladimir Oltean port, ERR_PTR(err)); 118*e32036e1SVladimir Oltean goto out_unlock; 119*e32036e1SVladimir Oltean } 120fe90104cSVladimir Oltean 121fe90104cSVladimir Oltean spin_lock(&ocelot->stats_lock); 122fe90104cSVladimir Oltean 123fe90104cSVladimir Oltean ocelot_port_transfer_stats(ocelot, port); 124*e32036e1SVladimir Oltean cb(ocelot, port, priv); 125fe90104cSVladimir Oltean 126fe90104cSVladimir Oltean spin_unlock(&ocelot->stats_lock); 127fe90104cSVladimir Oltean 128*e32036e1SVladimir Oltean out_unlock: 129fe90104cSVladimir Oltean mutex_unlock(&ocelot->stat_view_lock); 130fe90104cSVladimir Oltean } 131fe90104cSVladimir Oltean 132fe90104cSVladimir Oltean int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) 133fe90104cSVladimir Oltean { 134fe90104cSVladimir Oltean int i, num_stats = 0; 135fe90104cSVladimir Oltean 136fe90104cSVladimir Oltean if (sset != ETH_SS_STATS) 137fe90104cSVladimir Oltean return -EOPNOTSUPP; 138fe90104cSVladimir Oltean 139fe90104cSVladimir Oltean for (i = 0; i < OCELOT_NUM_STATS; i++) 140fe90104cSVladimir Oltean if (ocelot->stats_layout[i].name[0] != '\0') 141fe90104cSVladimir Oltean num_stats++; 142fe90104cSVladimir Oltean 143fe90104cSVladimir Oltean return num_stats; 144fe90104cSVladimir Oltean } 145fe90104cSVladimir Oltean EXPORT_SYMBOL(ocelot_get_sset_count); 146fe90104cSVladimir Oltean 147*e32036e1SVladimir Oltean static void ocelot_port_ethtool_stats_cb(struct ocelot *ocelot, int port, 148*e32036e1SVladimir Oltean void *priv) 149*e32036e1SVladimir Oltean { 150*e32036e1SVladimir Oltean u64 *data = priv; 151*e32036e1SVladimir Oltean int i; 152*e32036e1SVladimir Oltean 153*e32036e1SVladimir Oltean /* Copy all supported counters */ 154*e32036e1SVladimir Oltean for (i = 0; i < OCELOT_NUM_STATS; i++) { 155*e32036e1SVladimir Oltean int index = port * OCELOT_NUM_STATS + i; 156*e32036e1SVladimir Oltean 157*e32036e1SVladimir Oltean if (ocelot->stats_layout[i].name[0] == '\0') 158*e32036e1SVladimir Oltean continue; 159*e32036e1SVladimir Oltean 160*e32036e1SVladimir Oltean *data++ = ocelot->stats[index]; 161*e32036e1SVladimir Oltean } 162*e32036e1SVladimir Oltean } 163*e32036e1SVladimir Oltean 164*e32036e1SVladimir Oltean void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) 165*e32036e1SVladimir Oltean { 166*e32036e1SVladimir Oltean ocelot_port_stats_run(ocelot, port, data, ocelot_port_ethtool_stats_cb); 167*e32036e1SVladimir Oltean } 168*e32036e1SVladimir Oltean EXPORT_SYMBOL(ocelot_get_ethtool_stats); 169*e32036e1SVladimir Oltean 170*e32036e1SVladimir Oltean static void ocelot_port_pause_stats_cb(struct ocelot *ocelot, int port, void *priv) 171*e32036e1SVladimir Oltean { 172*e32036e1SVladimir Oltean u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 173*e32036e1SVladimir Oltean struct ethtool_pause_stats *pause_stats = priv; 174*e32036e1SVladimir Oltean 175*e32036e1SVladimir Oltean pause_stats->tx_pause_frames = s[OCELOT_STAT_TX_PAUSE]; 176*e32036e1SVladimir Oltean pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PAUSE]; 177*e32036e1SVladimir Oltean } 178*e32036e1SVladimir Oltean 179*e32036e1SVladimir Oltean void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port, 180*e32036e1SVladimir Oltean struct ethtool_pause_stats *pause_stats) 181*e32036e1SVladimir Oltean { 182*e32036e1SVladimir Oltean ocelot_port_stats_run(ocelot, port, pause_stats, 183*e32036e1SVladimir Oltean ocelot_port_pause_stats_cb); 184*e32036e1SVladimir Oltean } 185*e32036e1SVladimir Oltean EXPORT_SYMBOL_GPL(ocelot_port_get_pause_stats); 186*e32036e1SVladimir Oltean 187*e32036e1SVladimir Oltean static const struct ethtool_rmon_hist_range ocelot_rmon_ranges[] = { 188*e32036e1SVladimir Oltean { 64, 64 }, 189*e32036e1SVladimir Oltean { 65, 127 }, 190*e32036e1SVladimir Oltean { 128, 255 }, 191*e32036e1SVladimir Oltean { 256, 511 }, 192*e32036e1SVladimir Oltean { 512, 1023 }, 193*e32036e1SVladimir Oltean { 1024, 1526 }, 194*e32036e1SVladimir Oltean { 1527, 65535 }, 195*e32036e1SVladimir Oltean {}, 196*e32036e1SVladimir Oltean }; 197*e32036e1SVladimir Oltean 198*e32036e1SVladimir Oltean static void ocelot_port_rmon_stats_cb(struct ocelot *ocelot, int port, void *priv) 199*e32036e1SVladimir Oltean { 200*e32036e1SVladimir Oltean u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 201*e32036e1SVladimir Oltean struct ethtool_rmon_stats *rmon_stats = priv; 202*e32036e1SVladimir Oltean 203*e32036e1SVladimir Oltean rmon_stats->undersize_pkts = s[OCELOT_STAT_RX_SHORTS]; 204*e32036e1SVladimir Oltean rmon_stats->oversize_pkts = s[OCELOT_STAT_RX_LONGS]; 205*e32036e1SVladimir Oltean rmon_stats->fragments = s[OCELOT_STAT_RX_FRAGMENTS]; 206*e32036e1SVladimir Oltean rmon_stats->jabbers = s[OCELOT_STAT_RX_JABBERS]; 207*e32036e1SVladimir Oltean 208*e32036e1SVladimir Oltean rmon_stats->hist[0] = s[OCELOT_STAT_RX_64]; 209*e32036e1SVladimir Oltean rmon_stats->hist[1] = s[OCELOT_STAT_RX_65_127]; 210*e32036e1SVladimir Oltean rmon_stats->hist[2] = s[OCELOT_STAT_RX_128_255]; 211*e32036e1SVladimir Oltean rmon_stats->hist[3] = s[OCELOT_STAT_RX_256_511]; 212*e32036e1SVladimir Oltean rmon_stats->hist[4] = s[OCELOT_STAT_RX_512_1023]; 213*e32036e1SVladimir Oltean rmon_stats->hist[5] = s[OCELOT_STAT_RX_1024_1526]; 214*e32036e1SVladimir Oltean rmon_stats->hist[6] = s[OCELOT_STAT_RX_1527_MAX]; 215*e32036e1SVladimir Oltean 216*e32036e1SVladimir Oltean rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_64]; 217*e32036e1SVladimir Oltean rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_65_127]; 218*e32036e1SVladimir Oltean rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_128_255]; 219*e32036e1SVladimir Oltean rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_128_255]; 220*e32036e1SVladimir Oltean rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_256_511]; 221*e32036e1SVladimir Oltean rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_512_1023]; 222*e32036e1SVladimir Oltean rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1024_1526]; 223*e32036e1SVladimir Oltean } 224*e32036e1SVladimir Oltean 225*e32036e1SVladimir Oltean void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port, 226*e32036e1SVladimir Oltean struct ethtool_rmon_stats *rmon_stats, 227*e32036e1SVladimir Oltean const struct ethtool_rmon_hist_range **ranges) 228*e32036e1SVladimir Oltean { 229*e32036e1SVladimir Oltean *ranges = ocelot_rmon_ranges; 230*e32036e1SVladimir Oltean 231*e32036e1SVladimir Oltean ocelot_port_stats_run(ocelot, port, rmon_stats, 232*e32036e1SVladimir Oltean ocelot_port_rmon_stats_cb); 233*e32036e1SVladimir Oltean } 234*e32036e1SVladimir Oltean EXPORT_SYMBOL_GPL(ocelot_port_get_rmon_stats); 235*e32036e1SVladimir Oltean 236*e32036e1SVladimir Oltean static void ocelot_port_ctrl_stats_cb(struct ocelot *ocelot, int port, void *priv) 237*e32036e1SVladimir Oltean { 238*e32036e1SVladimir Oltean u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 239*e32036e1SVladimir Oltean struct ethtool_eth_ctrl_stats *ctrl_stats = priv; 240*e32036e1SVladimir Oltean 241*e32036e1SVladimir Oltean ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_CONTROL]; 242*e32036e1SVladimir Oltean } 243*e32036e1SVladimir Oltean 244*e32036e1SVladimir Oltean void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port, 245*e32036e1SVladimir Oltean struct ethtool_eth_ctrl_stats *ctrl_stats) 246*e32036e1SVladimir Oltean { 247*e32036e1SVladimir Oltean ocelot_port_stats_run(ocelot, port, ctrl_stats, 248*e32036e1SVladimir Oltean ocelot_port_ctrl_stats_cb); 249*e32036e1SVladimir Oltean } 250*e32036e1SVladimir Oltean EXPORT_SYMBOL_GPL(ocelot_port_get_eth_ctrl_stats); 251*e32036e1SVladimir Oltean 252*e32036e1SVladimir Oltean static void ocelot_port_mac_stats_cb(struct ocelot *ocelot, int port, void *priv) 253*e32036e1SVladimir Oltean { 254*e32036e1SVladimir Oltean u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 255*e32036e1SVladimir Oltean struct ethtool_eth_mac_stats *mac_stats = priv; 256*e32036e1SVladimir Oltean 257*e32036e1SVladimir Oltean mac_stats->OctetsTransmittedOK = s[OCELOT_STAT_TX_OCTETS]; 258*e32036e1SVladimir Oltean mac_stats->FramesTransmittedOK = s[OCELOT_STAT_TX_64] + 259*e32036e1SVladimir Oltean s[OCELOT_STAT_TX_65_127] + 260*e32036e1SVladimir Oltean s[OCELOT_STAT_TX_128_255] + 261*e32036e1SVladimir Oltean s[OCELOT_STAT_TX_256_511] + 262*e32036e1SVladimir Oltean s[OCELOT_STAT_TX_512_1023] + 263*e32036e1SVladimir Oltean s[OCELOT_STAT_TX_1024_1526] + 264*e32036e1SVladimir Oltean s[OCELOT_STAT_TX_1527_MAX]; 265*e32036e1SVladimir Oltean mac_stats->OctetsReceivedOK = s[OCELOT_STAT_RX_OCTETS]; 266*e32036e1SVladimir Oltean mac_stats->FramesReceivedOK = s[OCELOT_STAT_RX_GREEN_PRIO_0] + 267*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_GREEN_PRIO_1] + 268*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_GREEN_PRIO_2] + 269*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_GREEN_PRIO_3] + 270*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_GREEN_PRIO_4] + 271*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_GREEN_PRIO_5] + 272*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_GREEN_PRIO_6] + 273*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_GREEN_PRIO_7] + 274*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_YELLOW_PRIO_0] + 275*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_YELLOW_PRIO_1] + 276*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_YELLOW_PRIO_2] + 277*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_YELLOW_PRIO_3] + 278*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_YELLOW_PRIO_4] + 279*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_YELLOW_PRIO_5] + 280*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_YELLOW_PRIO_6] + 281*e32036e1SVladimir Oltean s[OCELOT_STAT_RX_YELLOW_PRIO_7]; 282*e32036e1SVladimir Oltean mac_stats->MulticastFramesXmittedOK = s[OCELOT_STAT_TX_MULTICAST]; 283*e32036e1SVladimir Oltean mac_stats->BroadcastFramesXmittedOK = s[OCELOT_STAT_TX_BROADCAST]; 284*e32036e1SVladimir Oltean mac_stats->MulticastFramesReceivedOK = s[OCELOT_STAT_RX_MULTICAST]; 285*e32036e1SVladimir Oltean mac_stats->BroadcastFramesReceivedOK = s[OCELOT_STAT_RX_BROADCAST]; 286*e32036e1SVladimir Oltean mac_stats->FrameTooLongErrors = s[OCELOT_STAT_RX_LONGS]; 287*e32036e1SVladimir Oltean /* Sadly, C_RX_CRC is the sum of FCS and alignment errors, they are not 288*e32036e1SVladimir Oltean * counted individually. 289*e32036e1SVladimir Oltean */ 290*e32036e1SVladimir Oltean mac_stats->FrameCheckSequenceErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS]; 291*e32036e1SVladimir Oltean mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS]; 292*e32036e1SVladimir Oltean } 293*e32036e1SVladimir Oltean 294*e32036e1SVladimir Oltean void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port, 295*e32036e1SVladimir Oltean struct ethtool_eth_mac_stats *mac_stats) 296*e32036e1SVladimir Oltean { 297*e32036e1SVladimir Oltean ocelot_port_stats_run(ocelot, port, mac_stats, 298*e32036e1SVladimir Oltean ocelot_port_mac_stats_cb); 299*e32036e1SVladimir Oltean } 300*e32036e1SVladimir Oltean EXPORT_SYMBOL_GPL(ocelot_port_get_eth_mac_stats); 301*e32036e1SVladimir Oltean 302*e32036e1SVladimir Oltean static void ocelot_port_phy_stats_cb(struct ocelot *ocelot, int port, void *priv) 303*e32036e1SVladimir Oltean { 304*e32036e1SVladimir Oltean u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 305*e32036e1SVladimir Oltean struct ethtool_eth_phy_stats *phy_stats = priv; 306*e32036e1SVladimir Oltean 307*e32036e1SVladimir Oltean phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_SYM_ERRS]; 308*e32036e1SVladimir Oltean } 309*e32036e1SVladimir Oltean 310*e32036e1SVladimir Oltean void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port, 311*e32036e1SVladimir Oltean struct ethtool_eth_phy_stats *phy_stats) 312*e32036e1SVladimir Oltean { 313*e32036e1SVladimir Oltean ocelot_port_stats_run(ocelot, port, phy_stats, 314*e32036e1SVladimir Oltean ocelot_port_phy_stats_cb); 315*e32036e1SVladimir Oltean } 316*e32036e1SVladimir Oltean EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats); 317*e32036e1SVladimir Oltean 318776b71e5SVladimir Oltean void ocelot_port_get_stats64(struct ocelot *ocelot, int port, 319776b71e5SVladimir Oltean struct rtnl_link_stats64 *stats) 320776b71e5SVladimir Oltean { 321776b71e5SVladimir Oltean u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 322776b71e5SVladimir Oltean 323776b71e5SVladimir Oltean spin_lock(&ocelot->stats_lock); 324776b71e5SVladimir Oltean 325776b71e5SVladimir Oltean /* Get Rx stats */ 326776b71e5SVladimir Oltean stats->rx_bytes = s[OCELOT_STAT_RX_OCTETS]; 327776b71e5SVladimir Oltean stats->rx_packets = s[OCELOT_STAT_RX_SHORTS] + 328776b71e5SVladimir Oltean s[OCELOT_STAT_RX_FRAGMENTS] + 329776b71e5SVladimir Oltean s[OCELOT_STAT_RX_JABBERS] + 330776b71e5SVladimir Oltean s[OCELOT_STAT_RX_LONGS] + 331776b71e5SVladimir Oltean s[OCELOT_STAT_RX_64] + 332776b71e5SVladimir Oltean s[OCELOT_STAT_RX_65_127] + 333776b71e5SVladimir Oltean s[OCELOT_STAT_RX_128_255] + 334776b71e5SVladimir Oltean s[OCELOT_STAT_RX_256_511] + 335776b71e5SVladimir Oltean s[OCELOT_STAT_RX_512_1023] + 336776b71e5SVladimir Oltean s[OCELOT_STAT_RX_1024_1526] + 337776b71e5SVladimir Oltean s[OCELOT_STAT_RX_1527_MAX]; 338776b71e5SVladimir Oltean stats->multicast = s[OCELOT_STAT_RX_MULTICAST]; 339776b71e5SVladimir Oltean stats->rx_missed_errors = s[OCELOT_STAT_DROP_TAIL]; 340776b71e5SVladimir Oltean stats->rx_dropped = s[OCELOT_STAT_RX_RED_PRIO_0] + 341776b71e5SVladimir Oltean s[OCELOT_STAT_RX_RED_PRIO_1] + 342776b71e5SVladimir Oltean s[OCELOT_STAT_RX_RED_PRIO_2] + 343776b71e5SVladimir Oltean s[OCELOT_STAT_RX_RED_PRIO_3] + 344776b71e5SVladimir Oltean s[OCELOT_STAT_RX_RED_PRIO_4] + 345776b71e5SVladimir Oltean s[OCELOT_STAT_RX_RED_PRIO_5] + 346776b71e5SVladimir Oltean s[OCELOT_STAT_RX_RED_PRIO_6] + 347776b71e5SVladimir Oltean s[OCELOT_STAT_RX_RED_PRIO_7] + 348776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_LOCAL] + 349776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_YELLOW_PRIO_0] + 350776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_YELLOW_PRIO_1] + 351776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_YELLOW_PRIO_2] + 352776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_YELLOW_PRIO_3] + 353776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_YELLOW_PRIO_4] + 354776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_YELLOW_PRIO_5] + 355776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_YELLOW_PRIO_6] + 356776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_YELLOW_PRIO_7] + 357776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_GREEN_PRIO_0] + 358776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_GREEN_PRIO_1] + 359776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_GREEN_PRIO_2] + 360776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_GREEN_PRIO_3] + 361776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_GREEN_PRIO_4] + 362776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_GREEN_PRIO_5] + 363776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_GREEN_PRIO_6] + 364776b71e5SVladimir Oltean s[OCELOT_STAT_DROP_GREEN_PRIO_7]; 365776b71e5SVladimir Oltean 366776b71e5SVladimir Oltean /* Get Tx stats */ 367776b71e5SVladimir Oltean stats->tx_bytes = s[OCELOT_STAT_TX_OCTETS]; 368776b71e5SVladimir Oltean stats->tx_packets = s[OCELOT_STAT_TX_64] + 369776b71e5SVladimir Oltean s[OCELOT_STAT_TX_65_127] + 370776b71e5SVladimir Oltean s[OCELOT_STAT_TX_128_255] + 371776b71e5SVladimir Oltean s[OCELOT_STAT_TX_256_511] + 372776b71e5SVladimir Oltean s[OCELOT_STAT_TX_512_1023] + 373776b71e5SVladimir Oltean s[OCELOT_STAT_TX_1024_1526] + 374776b71e5SVladimir Oltean s[OCELOT_STAT_TX_1527_MAX]; 375776b71e5SVladimir Oltean stats->tx_dropped = s[OCELOT_STAT_TX_DROPS] + 376776b71e5SVladimir Oltean s[OCELOT_STAT_TX_AGED]; 377776b71e5SVladimir Oltean stats->collisions = s[OCELOT_STAT_TX_COLLISION]; 378776b71e5SVladimir Oltean 379776b71e5SVladimir Oltean spin_unlock(&ocelot->stats_lock); 380776b71e5SVladimir Oltean } 381776b71e5SVladimir Oltean EXPORT_SYMBOL(ocelot_port_get_stats64); 382776b71e5SVladimir Oltean 383fe90104cSVladimir Oltean static int ocelot_prepare_stats_regions(struct ocelot *ocelot) 384fe90104cSVladimir Oltean { 385fe90104cSVladimir Oltean struct ocelot_stats_region *region = NULL; 386fe90104cSVladimir Oltean unsigned int last; 387fe90104cSVladimir Oltean int i; 388fe90104cSVladimir Oltean 389fe90104cSVladimir Oltean INIT_LIST_HEAD(&ocelot->stats_regions); 390fe90104cSVladimir Oltean 391fe90104cSVladimir Oltean for (i = 0; i < OCELOT_NUM_STATS; i++) { 392d3e75f16SVladimir Oltean if (!ocelot->stats_layout[i].reg) 393fe90104cSVladimir Oltean continue; 394fe90104cSVladimir Oltean 395fe90104cSVladimir Oltean if (region && ocelot->stats_layout[i].reg == last + 4) { 396fe90104cSVladimir Oltean region->count++; 397fe90104cSVladimir Oltean } else { 398fe90104cSVladimir Oltean region = devm_kzalloc(ocelot->dev, sizeof(*region), 399fe90104cSVladimir Oltean GFP_KERNEL); 400fe90104cSVladimir Oltean if (!region) 401fe90104cSVladimir Oltean return -ENOMEM; 402fe90104cSVladimir Oltean 403fe90104cSVladimir Oltean region->base = ocelot->stats_layout[i].reg; 404fe90104cSVladimir Oltean region->count = 1; 405fe90104cSVladimir Oltean list_add_tail(®ion->node, &ocelot->stats_regions); 406fe90104cSVladimir Oltean } 407fe90104cSVladimir Oltean 408fe90104cSVladimir Oltean last = ocelot->stats_layout[i].reg; 409fe90104cSVladimir Oltean } 410fe90104cSVladimir Oltean 411fe90104cSVladimir Oltean list_for_each_entry(region, &ocelot->stats_regions, node) { 412fe90104cSVladimir Oltean region->buf = devm_kcalloc(ocelot->dev, region->count, 413fe90104cSVladimir Oltean sizeof(*region->buf), GFP_KERNEL); 414fe90104cSVladimir Oltean if (!region->buf) 415fe90104cSVladimir Oltean return -ENOMEM; 416fe90104cSVladimir Oltean } 417fe90104cSVladimir Oltean 418fe90104cSVladimir Oltean return 0; 419fe90104cSVladimir Oltean } 420fe90104cSVladimir Oltean 421fe90104cSVladimir Oltean int ocelot_stats_init(struct ocelot *ocelot) 422fe90104cSVladimir Oltean { 423fe90104cSVladimir Oltean char queue_name[32]; 424fe90104cSVladimir Oltean int ret; 425fe90104cSVladimir Oltean 426fe90104cSVladimir Oltean ocelot->stats = devm_kcalloc(ocelot->dev, 427fe90104cSVladimir Oltean ocelot->num_phys_ports * OCELOT_NUM_STATS, 428fe90104cSVladimir Oltean sizeof(u64), GFP_KERNEL); 429fe90104cSVladimir Oltean if (!ocelot->stats) 430fe90104cSVladimir Oltean return -ENOMEM; 431fe90104cSVladimir Oltean 432fe90104cSVladimir Oltean snprintf(queue_name, sizeof(queue_name), "%s-stats", 433fe90104cSVladimir Oltean dev_name(ocelot->dev)); 434fe90104cSVladimir Oltean ocelot->stats_queue = create_singlethread_workqueue(queue_name); 435fe90104cSVladimir Oltean if (!ocelot->stats_queue) 436fe90104cSVladimir Oltean return -ENOMEM; 437fe90104cSVladimir Oltean 438fe90104cSVladimir Oltean spin_lock_init(&ocelot->stats_lock); 439fe90104cSVladimir Oltean mutex_init(&ocelot->stat_view_lock); 440fe90104cSVladimir Oltean 441fe90104cSVladimir Oltean ret = ocelot_prepare_stats_regions(ocelot); 442fe90104cSVladimir Oltean if (ret) { 443fe90104cSVladimir Oltean destroy_workqueue(ocelot->stats_queue); 444fe90104cSVladimir Oltean return ret; 445fe90104cSVladimir Oltean } 446fe90104cSVladimir Oltean 447fe90104cSVladimir Oltean INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work); 448fe90104cSVladimir Oltean queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, 449fe90104cSVladimir Oltean OCELOT_STATS_CHECK_DELAY); 450fe90104cSVladimir Oltean 451fe90104cSVladimir Oltean return 0; 452fe90104cSVladimir Oltean } 453fe90104cSVladimir Oltean 454fe90104cSVladimir Oltean void ocelot_stats_deinit(struct ocelot *ocelot) 455fe90104cSVladimir Oltean { 456fe90104cSVladimir Oltean cancel_delayed_work(&ocelot->stats_work); 457fe90104cSVladimir Oltean destroy_workqueue(ocelot->stats_queue); 458fe90104cSVladimir Oltean } 459