1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /* Statistics for Ocelot switch family
3  *
4  * Copyright (c) 2017 Microsemi Corporation
5  */
6 #include <linux/spinlock.h>
7 #include <linux/mutex.h>
8 #include <linux/workqueue.h>
9 #include "ocelot.h"
10 
11 /* Read the counters from hardware and keep them in region->buf.
12  * Caller must hold &ocelot->stat_view_lock.
13  */
14 static int ocelot_port_update_stats(struct ocelot *ocelot, int port)
15 {
16 	struct ocelot_stats_region *region;
17 	int err;
18 
19 	/* Configure the port to read the stats from */
20 	ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG);
21 
22 	list_for_each_entry(region, &ocelot->stats_regions, node) {
23 		err = ocelot_bulk_read(ocelot, region->base, region->buf,
24 				       region->count);
25 		if (err)
26 			return err;
27 	}
28 
29 	return 0;
30 }
31 
32 /* Transfer the counters from region->buf to ocelot->stats.
33  * Caller must hold &ocelot->stat_view_lock and &ocelot->stats_lock.
34  */
35 static void ocelot_port_transfer_stats(struct ocelot *ocelot, int port)
36 {
37 	unsigned int idx = port * OCELOT_NUM_STATS;
38 	struct ocelot_stats_region *region;
39 	int j;
40 
41 	list_for_each_entry(region, &ocelot->stats_regions, node) {
42 		for (j = 0; j < region->count; j++) {
43 			u64 *stat = &ocelot->stats[idx + j];
44 			u64 val = region->buf[j];
45 
46 			if (val < (*stat & U32_MAX))
47 				*stat += (u64)1 << 32;
48 
49 			*stat = (*stat & ~(u64)U32_MAX) + val;
50 		}
51 
52 		idx += region->count;
53 	}
54 }
55 
56 static void ocelot_check_stats_work(struct work_struct *work)
57 {
58 	struct delayed_work *del_work = to_delayed_work(work);
59 	struct ocelot *ocelot = container_of(del_work, struct ocelot,
60 					     stats_work);
61 	int port, err;
62 
63 	mutex_lock(&ocelot->stat_view_lock);
64 
65 	for (port = 0; port < ocelot->num_phys_ports; port++) {
66 		err = ocelot_port_update_stats(ocelot, port);
67 		if (err)
68 			break;
69 
70 		spin_lock(&ocelot->stats_lock);
71 		ocelot_port_transfer_stats(ocelot, port);
72 		spin_unlock(&ocelot->stats_lock);
73 	}
74 
75 	if (!err && ocelot->ops->update_stats)
76 		ocelot->ops->update_stats(ocelot);
77 
78 	mutex_unlock(&ocelot->stat_view_lock);
79 
80 	if (err)
81 		dev_err(ocelot->dev, "Error %d updating ethtool stats\n",  err);
82 
83 	queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
84 			   OCELOT_STATS_CHECK_DELAY);
85 }
86 
87 void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
88 {
89 	int i;
90 
91 	if (sset != ETH_SS_STATS)
92 		return;
93 
94 	for (i = 0; i < OCELOT_NUM_STATS; i++) {
95 		if (ocelot->stats_layout[i].name[0] == '\0')
96 			continue;
97 
98 		memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name,
99 		       ETH_GSTRING_LEN);
100 	}
101 }
102 EXPORT_SYMBOL(ocelot_get_strings);
103 
104 void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data)
105 {
106 	int i, err;
107 
108 	mutex_lock(&ocelot->stat_view_lock);
109 
110 	/* check and update now */
111 	err = ocelot_port_update_stats(ocelot, port);
112 
113 	spin_lock(&ocelot->stats_lock);
114 
115 	ocelot_port_transfer_stats(ocelot, port);
116 
117 	/* Copy all supported counters */
118 	for (i = 0; i < OCELOT_NUM_STATS; i++) {
119 		int index = port * OCELOT_NUM_STATS + i;
120 
121 		if (ocelot->stats_layout[i].name[0] == '\0')
122 			continue;
123 
124 		*data++ = ocelot->stats[index];
125 	}
126 
127 	spin_unlock(&ocelot->stats_lock);
128 
129 	mutex_unlock(&ocelot->stat_view_lock);
130 
131 	if (err)
132 		dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err);
133 }
134 EXPORT_SYMBOL(ocelot_get_ethtool_stats);
135 
136 int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
137 {
138 	int i, num_stats = 0;
139 
140 	if (sset != ETH_SS_STATS)
141 		return -EOPNOTSUPP;
142 
143 	for (i = 0; i < OCELOT_NUM_STATS; i++)
144 		if (ocelot->stats_layout[i].name[0] != '\0')
145 			num_stats++;
146 
147 	return num_stats;
148 }
149 EXPORT_SYMBOL(ocelot_get_sset_count);
150 
151 void ocelot_port_get_stats64(struct ocelot *ocelot, int port,
152 			     struct rtnl_link_stats64 *stats)
153 {
154 	u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
155 
156 	spin_lock(&ocelot->stats_lock);
157 
158 	/* Get Rx stats */
159 	stats->rx_bytes = s[OCELOT_STAT_RX_OCTETS];
160 	stats->rx_packets = s[OCELOT_STAT_RX_SHORTS] +
161 			    s[OCELOT_STAT_RX_FRAGMENTS] +
162 			    s[OCELOT_STAT_RX_JABBERS] +
163 			    s[OCELOT_STAT_RX_LONGS] +
164 			    s[OCELOT_STAT_RX_64] +
165 			    s[OCELOT_STAT_RX_65_127] +
166 			    s[OCELOT_STAT_RX_128_255] +
167 			    s[OCELOT_STAT_RX_256_511] +
168 			    s[OCELOT_STAT_RX_512_1023] +
169 			    s[OCELOT_STAT_RX_1024_1526] +
170 			    s[OCELOT_STAT_RX_1527_MAX];
171 	stats->multicast = s[OCELOT_STAT_RX_MULTICAST];
172 	stats->rx_missed_errors = s[OCELOT_STAT_DROP_TAIL];
173 	stats->rx_dropped = s[OCELOT_STAT_RX_RED_PRIO_0] +
174 			    s[OCELOT_STAT_RX_RED_PRIO_1] +
175 			    s[OCELOT_STAT_RX_RED_PRIO_2] +
176 			    s[OCELOT_STAT_RX_RED_PRIO_3] +
177 			    s[OCELOT_STAT_RX_RED_PRIO_4] +
178 			    s[OCELOT_STAT_RX_RED_PRIO_5] +
179 			    s[OCELOT_STAT_RX_RED_PRIO_6] +
180 			    s[OCELOT_STAT_RX_RED_PRIO_7] +
181 			    s[OCELOT_STAT_DROP_LOCAL] +
182 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_0] +
183 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_1] +
184 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_2] +
185 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_3] +
186 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_4] +
187 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_5] +
188 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_6] +
189 			    s[OCELOT_STAT_DROP_YELLOW_PRIO_7] +
190 			    s[OCELOT_STAT_DROP_GREEN_PRIO_0] +
191 			    s[OCELOT_STAT_DROP_GREEN_PRIO_1] +
192 			    s[OCELOT_STAT_DROP_GREEN_PRIO_2] +
193 			    s[OCELOT_STAT_DROP_GREEN_PRIO_3] +
194 			    s[OCELOT_STAT_DROP_GREEN_PRIO_4] +
195 			    s[OCELOT_STAT_DROP_GREEN_PRIO_5] +
196 			    s[OCELOT_STAT_DROP_GREEN_PRIO_6] +
197 			    s[OCELOT_STAT_DROP_GREEN_PRIO_7];
198 
199 	/* Get Tx stats */
200 	stats->tx_bytes = s[OCELOT_STAT_TX_OCTETS];
201 	stats->tx_packets = s[OCELOT_STAT_TX_64] +
202 			    s[OCELOT_STAT_TX_65_127] +
203 			    s[OCELOT_STAT_TX_128_255] +
204 			    s[OCELOT_STAT_TX_256_511] +
205 			    s[OCELOT_STAT_TX_512_1023] +
206 			    s[OCELOT_STAT_TX_1024_1526] +
207 			    s[OCELOT_STAT_TX_1527_MAX];
208 	stats->tx_dropped = s[OCELOT_STAT_TX_DROPS] +
209 			    s[OCELOT_STAT_TX_AGED];
210 	stats->collisions = s[OCELOT_STAT_TX_COLLISION];
211 
212 	spin_unlock(&ocelot->stats_lock);
213 }
214 EXPORT_SYMBOL(ocelot_port_get_stats64);
215 
216 static int ocelot_prepare_stats_regions(struct ocelot *ocelot)
217 {
218 	struct ocelot_stats_region *region = NULL;
219 	unsigned int last;
220 	int i;
221 
222 	INIT_LIST_HEAD(&ocelot->stats_regions);
223 
224 	for (i = 0; i < OCELOT_NUM_STATS; i++) {
225 		if (!ocelot->stats_layout[i].reg)
226 			continue;
227 
228 		if (region && ocelot->stats_layout[i].reg == last + 4) {
229 			region->count++;
230 		} else {
231 			region = devm_kzalloc(ocelot->dev, sizeof(*region),
232 					      GFP_KERNEL);
233 			if (!region)
234 				return -ENOMEM;
235 
236 			region->base = ocelot->stats_layout[i].reg;
237 			region->count = 1;
238 			list_add_tail(&region->node, &ocelot->stats_regions);
239 		}
240 
241 		last = ocelot->stats_layout[i].reg;
242 	}
243 
244 	list_for_each_entry(region, &ocelot->stats_regions, node) {
245 		region->buf = devm_kcalloc(ocelot->dev, region->count,
246 					   sizeof(*region->buf), GFP_KERNEL);
247 		if (!region->buf)
248 			return -ENOMEM;
249 	}
250 
251 	return 0;
252 }
253 
254 int ocelot_stats_init(struct ocelot *ocelot)
255 {
256 	char queue_name[32];
257 	int ret;
258 
259 	ocelot->stats = devm_kcalloc(ocelot->dev,
260 				     ocelot->num_phys_ports * OCELOT_NUM_STATS,
261 				     sizeof(u64), GFP_KERNEL);
262 	if (!ocelot->stats)
263 		return -ENOMEM;
264 
265 	snprintf(queue_name, sizeof(queue_name), "%s-stats",
266 		 dev_name(ocelot->dev));
267 	ocelot->stats_queue = create_singlethread_workqueue(queue_name);
268 	if (!ocelot->stats_queue)
269 		return -ENOMEM;
270 
271 	spin_lock_init(&ocelot->stats_lock);
272 	mutex_init(&ocelot->stat_view_lock);
273 
274 	ret = ocelot_prepare_stats_regions(ocelot);
275 	if (ret) {
276 		destroy_workqueue(ocelot->stats_queue);
277 		return ret;
278 	}
279 
280 	INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work);
281 	queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
282 			   OCELOT_STATS_CHECK_DELAY);
283 
284 	return 0;
285 }
286 
287 void ocelot_stats_deinit(struct ocelot *ocelot)
288 {
289 	cancel_delayed_work(&ocelot->stats_work);
290 	destroy_workqueue(ocelot->stats_queue);
291 }
292