xref: /openbmc/linux/drivers/net/ethernet/mscc/ocelot_stats.c (revision e32036e1ae7bb71b64559313f9ea8790a0acaa51)
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(&region->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