1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | /* Statistics for Ocelot switch family |
3 | * |
4 | * Copyright (c) 2017 Microsemi Corporation |
5 | * Copyright 2022 NXP |
6 | */ |
7 | #include <linux/ethtool_netlink.h> |
8 | #include <linux/spinlock.h> |
9 | #include <linux/mutex.h> |
10 | #include <linux/workqueue.h> |
11 | #include "ocelot.h" |
12 | |
13 | enum ocelot_stat { |
14 | OCELOT_STAT_RX_OCTETS, |
15 | OCELOT_STAT_RX_UNICAST, |
16 | OCELOT_STAT_RX_MULTICAST, |
17 | OCELOT_STAT_RX_BROADCAST, |
18 | OCELOT_STAT_RX_SHORTS, |
19 | OCELOT_STAT_RX_FRAGMENTS, |
20 | OCELOT_STAT_RX_JABBERS, |
21 | OCELOT_STAT_RX_CRC_ALIGN_ERRS, |
22 | OCELOT_STAT_RX_SYM_ERRS, |
23 | OCELOT_STAT_RX_64, |
24 | OCELOT_STAT_RX_65_127, |
25 | OCELOT_STAT_RX_128_255, |
26 | OCELOT_STAT_RX_256_511, |
27 | OCELOT_STAT_RX_512_1023, |
28 | OCELOT_STAT_RX_1024_1526, |
29 | OCELOT_STAT_RX_1527_MAX, |
30 | OCELOT_STAT_RX_PAUSE, |
31 | OCELOT_STAT_RX_CONTROL, |
32 | OCELOT_STAT_RX_LONGS, |
33 | OCELOT_STAT_RX_CLASSIFIED_DROPS, |
34 | OCELOT_STAT_RX_RED_PRIO_0, |
35 | OCELOT_STAT_RX_RED_PRIO_1, |
36 | OCELOT_STAT_RX_RED_PRIO_2, |
37 | OCELOT_STAT_RX_RED_PRIO_3, |
38 | OCELOT_STAT_RX_RED_PRIO_4, |
39 | OCELOT_STAT_RX_RED_PRIO_5, |
40 | OCELOT_STAT_RX_RED_PRIO_6, |
41 | OCELOT_STAT_RX_RED_PRIO_7, |
42 | OCELOT_STAT_RX_YELLOW_PRIO_0, |
43 | OCELOT_STAT_RX_YELLOW_PRIO_1, |
44 | OCELOT_STAT_RX_YELLOW_PRIO_2, |
45 | OCELOT_STAT_RX_YELLOW_PRIO_3, |
46 | OCELOT_STAT_RX_YELLOW_PRIO_4, |
47 | OCELOT_STAT_RX_YELLOW_PRIO_5, |
48 | OCELOT_STAT_RX_YELLOW_PRIO_6, |
49 | OCELOT_STAT_RX_YELLOW_PRIO_7, |
50 | OCELOT_STAT_RX_GREEN_PRIO_0, |
51 | OCELOT_STAT_RX_GREEN_PRIO_1, |
52 | OCELOT_STAT_RX_GREEN_PRIO_2, |
53 | OCELOT_STAT_RX_GREEN_PRIO_3, |
54 | OCELOT_STAT_RX_GREEN_PRIO_4, |
55 | OCELOT_STAT_RX_GREEN_PRIO_5, |
56 | OCELOT_STAT_RX_GREEN_PRIO_6, |
57 | OCELOT_STAT_RX_GREEN_PRIO_7, |
58 | OCELOT_STAT_RX_ASSEMBLY_ERRS, |
59 | OCELOT_STAT_RX_SMD_ERRS, |
60 | OCELOT_STAT_RX_ASSEMBLY_OK, |
61 | OCELOT_STAT_RX_MERGE_FRAGMENTS, |
62 | OCELOT_STAT_RX_PMAC_OCTETS, |
63 | OCELOT_STAT_RX_PMAC_UNICAST, |
64 | OCELOT_STAT_RX_PMAC_MULTICAST, |
65 | OCELOT_STAT_RX_PMAC_BROADCAST, |
66 | OCELOT_STAT_RX_PMAC_SHORTS, |
67 | OCELOT_STAT_RX_PMAC_FRAGMENTS, |
68 | OCELOT_STAT_RX_PMAC_JABBERS, |
69 | OCELOT_STAT_RX_PMAC_CRC_ALIGN_ERRS, |
70 | OCELOT_STAT_RX_PMAC_SYM_ERRS, |
71 | OCELOT_STAT_RX_PMAC_64, |
72 | OCELOT_STAT_RX_PMAC_65_127, |
73 | OCELOT_STAT_RX_PMAC_128_255, |
74 | OCELOT_STAT_RX_PMAC_256_511, |
75 | OCELOT_STAT_RX_PMAC_512_1023, |
76 | OCELOT_STAT_RX_PMAC_1024_1526, |
77 | OCELOT_STAT_RX_PMAC_1527_MAX, |
78 | OCELOT_STAT_RX_PMAC_PAUSE, |
79 | OCELOT_STAT_RX_PMAC_CONTROL, |
80 | OCELOT_STAT_RX_PMAC_LONGS, |
81 | OCELOT_STAT_TX_OCTETS, |
82 | OCELOT_STAT_TX_UNICAST, |
83 | OCELOT_STAT_TX_MULTICAST, |
84 | OCELOT_STAT_TX_BROADCAST, |
85 | OCELOT_STAT_TX_COLLISION, |
86 | OCELOT_STAT_TX_DROPS, |
87 | OCELOT_STAT_TX_PAUSE, |
88 | OCELOT_STAT_TX_64, |
89 | OCELOT_STAT_TX_65_127, |
90 | OCELOT_STAT_TX_128_255, |
91 | OCELOT_STAT_TX_256_511, |
92 | OCELOT_STAT_TX_512_1023, |
93 | OCELOT_STAT_TX_1024_1526, |
94 | OCELOT_STAT_TX_1527_MAX, |
95 | OCELOT_STAT_TX_YELLOW_PRIO_0, |
96 | OCELOT_STAT_TX_YELLOW_PRIO_1, |
97 | OCELOT_STAT_TX_YELLOW_PRIO_2, |
98 | OCELOT_STAT_TX_YELLOW_PRIO_3, |
99 | OCELOT_STAT_TX_YELLOW_PRIO_4, |
100 | OCELOT_STAT_TX_YELLOW_PRIO_5, |
101 | OCELOT_STAT_TX_YELLOW_PRIO_6, |
102 | OCELOT_STAT_TX_YELLOW_PRIO_7, |
103 | OCELOT_STAT_TX_GREEN_PRIO_0, |
104 | OCELOT_STAT_TX_GREEN_PRIO_1, |
105 | OCELOT_STAT_TX_GREEN_PRIO_2, |
106 | OCELOT_STAT_TX_GREEN_PRIO_3, |
107 | OCELOT_STAT_TX_GREEN_PRIO_4, |
108 | OCELOT_STAT_TX_GREEN_PRIO_5, |
109 | OCELOT_STAT_TX_GREEN_PRIO_6, |
110 | OCELOT_STAT_TX_GREEN_PRIO_7, |
111 | OCELOT_STAT_TX_AGED, |
112 | OCELOT_STAT_TX_MM_HOLD, |
113 | OCELOT_STAT_TX_MERGE_FRAGMENTS, |
114 | OCELOT_STAT_TX_PMAC_OCTETS, |
115 | OCELOT_STAT_TX_PMAC_UNICAST, |
116 | OCELOT_STAT_TX_PMAC_MULTICAST, |
117 | OCELOT_STAT_TX_PMAC_BROADCAST, |
118 | OCELOT_STAT_TX_PMAC_PAUSE, |
119 | OCELOT_STAT_TX_PMAC_64, |
120 | OCELOT_STAT_TX_PMAC_65_127, |
121 | OCELOT_STAT_TX_PMAC_128_255, |
122 | OCELOT_STAT_TX_PMAC_256_511, |
123 | OCELOT_STAT_TX_PMAC_512_1023, |
124 | OCELOT_STAT_TX_PMAC_1024_1526, |
125 | OCELOT_STAT_TX_PMAC_1527_MAX, |
126 | OCELOT_STAT_DROP_LOCAL, |
127 | OCELOT_STAT_DROP_TAIL, |
128 | OCELOT_STAT_DROP_YELLOW_PRIO_0, |
129 | OCELOT_STAT_DROP_YELLOW_PRIO_1, |
130 | OCELOT_STAT_DROP_YELLOW_PRIO_2, |
131 | OCELOT_STAT_DROP_YELLOW_PRIO_3, |
132 | OCELOT_STAT_DROP_YELLOW_PRIO_4, |
133 | OCELOT_STAT_DROP_YELLOW_PRIO_5, |
134 | OCELOT_STAT_DROP_YELLOW_PRIO_6, |
135 | OCELOT_STAT_DROP_YELLOW_PRIO_7, |
136 | OCELOT_STAT_DROP_GREEN_PRIO_0, |
137 | OCELOT_STAT_DROP_GREEN_PRIO_1, |
138 | OCELOT_STAT_DROP_GREEN_PRIO_2, |
139 | OCELOT_STAT_DROP_GREEN_PRIO_3, |
140 | OCELOT_STAT_DROP_GREEN_PRIO_4, |
141 | OCELOT_STAT_DROP_GREEN_PRIO_5, |
142 | OCELOT_STAT_DROP_GREEN_PRIO_6, |
143 | OCELOT_STAT_DROP_GREEN_PRIO_7, |
144 | OCELOT_NUM_STATS, |
145 | }; |
146 | |
147 | struct ocelot_stat_layout { |
148 | enum ocelot_reg reg; |
149 | char name[ETH_GSTRING_LEN]; |
150 | }; |
151 | |
152 | /* 32-bit counter checked for wraparound by ocelot_port_update_stats() |
153 | * and copied to ocelot->stats. |
154 | */ |
155 | #define OCELOT_STAT(kind) \ |
156 | [OCELOT_STAT_ ## kind] = { .reg = SYS_COUNT_ ## kind } |
157 | /* Same as above, except also exported to ethtool -S. Standard counters should |
158 | * only be exposed to more specific interfaces rather than by their string name. |
159 | */ |
160 | #define OCELOT_STAT_ETHTOOL(kind, ethtool_name) \ |
161 | [OCELOT_STAT_ ## kind] = { .reg = SYS_COUNT_ ## kind, .name = ethtool_name } |
162 | |
163 | #define OCELOT_COMMON_STATS \ |
164 | OCELOT_STAT_ETHTOOL(RX_OCTETS, "rx_octets"), \ |
165 | OCELOT_STAT_ETHTOOL(RX_UNICAST, "rx_unicast"), \ |
166 | OCELOT_STAT_ETHTOOL(RX_MULTICAST, "rx_multicast"), \ |
167 | OCELOT_STAT_ETHTOOL(RX_BROADCAST, "rx_broadcast"), \ |
168 | OCELOT_STAT_ETHTOOL(RX_SHORTS, "rx_shorts"), \ |
169 | OCELOT_STAT_ETHTOOL(RX_FRAGMENTS, "rx_fragments"), \ |
170 | OCELOT_STAT_ETHTOOL(RX_JABBERS, "rx_jabbers"), \ |
171 | OCELOT_STAT_ETHTOOL(RX_CRC_ALIGN_ERRS, "rx_crc_align_errs"), \ |
172 | OCELOT_STAT_ETHTOOL(RX_SYM_ERRS, "rx_sym_errs"), \ |
173 | OCELOT_STAT_ETHTOOL(RX_64, "rx_frames_below_65_octets"), \ |
174 | OCELOT_STAT_ETHTOOL(RX_65_127, "rx_frames_65_to_127_octets"), \ |
175 | OCELOT_STAT_ETHTOOL(RX_128_255, "rx_frames_128_to_255_octets"), \ |
176 | OCELOT_STAT_ETHTOOL(RX_256_511, "rx_frames_256_to_511_octets"), \ |
177 | OCELOT_STAT_ETHTOOL(RX_512_1023, "rx_frames_512_to_1023_octets"), \ |
178 | OCELOT_STAT_ETHTOOL(RX_1024_1526, "rx_frames_1024_to_1526_octets"), \ |
179 | OCELOT_STAT_ETHTOOL(RX_1527_MAX, "rx_frames_over_1526_octets"), \ |
180 | OCELOT_STAT_ETHTOOL(RX_PAUSE, "rx_pause"), \ |
181 | OCELOT_STAT_ETHTOOL(RX_CONTROL, "rx_control"), \ |
182 | OCELOT_STAT_ETHTOOL(RX_LONGS, "rx_longs"), \ |
183 | OCELOT_STAT_ETHTOOL(RX_CLASSIFIED_DROPS, "rx_classified_drops"), \ |
184 | OCELOT_STAT_ETHTOOL(RX_RED_PRIO_0, "rx_red_prio_0"), \ |
185 | OCELOT_STAT_ETHTOOL(RX_RED_PRIO_1, "rx_red_prio_1"), \ |
186 | OCELOT_STAT_ETHTOOL(RX_RED_PRIO_2, "rx_red_prio_2"), \ |
187 | OCELOT_STAT_ETHTOOL(RX_RED_PRIO_3, "rx_red_prio_3"), \ |
188 | OCELOT_STAT_ETHTOOL(RX_RED_PRIO_4, "rx_red_prio_4"), \ |
189 | OCELOT_STAT_ETHTOOL(RX_RED_PRIO_5, "rx_red_prio_5"), \ |
190 | OCELOT_STAT_ETHTOOL(RX_RED_PRIO_6, "rx_red_prio_6"), \ |
191 | OCELOT_STAT_ETHTOOL(RX_RED_PRIO_7, "rx_red_prio_7"), \ |
192 | OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_0, "rx_yellow_prio_0"), \ |
193 | OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_1, "rx_yellow_prio_1"), \ |
194 | OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_2, "rx_yellow_prio_2"), \ |
195 | OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_3, "rx_yellow_prio_3"), \ |
196 | OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_4, "rx_yellow_prio_4"), \ |
197 | OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_5, "rx_yellow_prio_5"), \ |
198 | OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_6, "rx_yellow_prio_6"), \ |
199 | OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_7, "rx_yellow_prio_7"), \ |
200 | OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_0, "rx_green_prio_0"), \ |
201 | OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_1, "rx_green_prio_1"), \ |
202 | OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_2, "rx_green_prio_2"), \ |
203 | OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_3, "rx_green_prio_3"), \ |
204 | OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_4, "rx_green_prio_4"), \ |
205 | OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_5, "rx_green_prio_5"), \ |
206 | OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_6, "rx_green_prio_6"), \ |
207 | OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_7, "rx_green_prio_7"), \ |
208 | OCELOT_STAT_ETHTOOL(TX_OCTETS, "tx_octets"), \ |
209 | OCELOT_STAT_ETHTOOL(TX_UNICAST, "tx_unicast"), \ |
210 | OCELOT_STAT_ETHTOOL(TX_MULTICAST, "tx_multicast"), \ |
211 | OCELOT_STAT_ETHTOOL(TX_BROADCAST, "tx_broadcast"), \ |
212 | OCELOT_STAT_ETHTOOL(TX_COLLISION, "tx_collision"), \ |
213 | OCELOT_STAT_ETHTOOL(TX_DROPS, "tx_drops"), \ |
214 | OCELOT_STAT_ETHTOOL(TX_PAUSE, "tx_pause"), \ |
215 | OCELOT_STAT_ETHTOOL(TX_64, "tx_frames_below_65_octets"), \ |
216 | OCELOT_STAT_ETHTOOL(TX_65_127, "tx_frames_65_to_127_octets"), \ |
217 | OCELOT_STAT_ETHTOOL(TX_128_255, "tx_frames_128_255_octets"), \ |
218 | OCELOT_STAT_ETHTOOL(TX_256_511, "tx_frames_256_511_octets"), \ |
219 | OCELOT_STAT_ETHTOOL(TX_512_1023, "tx_frames_512_1023_octets"), \ |
220 | OCELOT_STAT_ETHTOOL(TX_1024_1526, "tx_frames_1024_1526_octets"), \ |
221 | OCELOT_STAT_ETHTOOL(TX_1527_MAX, "tx_frames_over_1526_octets"), \ |
222 | OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_0, "tx_yellow_prio_0"), \ |
223 | OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_1, "tx_yellow_prio_1"), \ |
224 | OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_2, "tx_yellow_prio_2"), \ |
225 | OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_3, "tx_yellow_prio_3"), \ |
226 | OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_4, "tx_yellow_prio_4"), \ |
227 | OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_5, "tx_yellow_prio_5"), \ |
228 | OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_6, "tx_yellow_prio_6"), \ |
229 | OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_7, "tx_yellow_prio_7"), \ |
230 | OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_0, "tx_green_prio_0"), \ |
231 | OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_1, "tx_green_prio_1"), \ |
232 | OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_2, "tx_green_prio_2"), \ |
233 | OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_3, "tx_green_prio_3"), \ |
234 | OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_4, "tx_green_prio_4"), \ |
235 | OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_5, "tx_green_prio_5"), \ |
236 | OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_6, "tx_green_prio_6"), \ |
237 | OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_7, "tx_green_prio_7"), \ |
238 | OCELOT_STAT_ETHTOOL(TX_AGED, "tx_aged"), \ |
239 | OCELOT_STAT_ETHTOOL(DROP_LOCAL, "drop_local"), \ |
240 | OCELOT_STAT_ETHTOOL(DROP_TAIL, "drop_tail"), \ |
241 | OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_0, "drop_yellow_prio_0"), \ |
242 | OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_1, "drop_yellow_prio_1"), \ |
243 | OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_2, "drop_yellow_prio_2"), \ |
244 | OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_3, "drop_yellow_prio_3"), \ |
245 | OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_4, "drop_yellow_prio_4"), \ |
246 | OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_5, "drop_yellow_prio_5"), \ |
247 | OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_6, "drop_yellow_prio_6"), \ |
248 | OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_7, "drop_yellow_prio_7"), \ |
249 | OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_0, "drop_green_prio_0"), \ |
250 | OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_1, "drop_green_prio_1"), \ |
251 | OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_2, "drop_green_prio_2"), \ |
252 | OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_3, "drop_green_prio_3"), \ |
253 | OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_4, "drop_green_prio_4"), \ |
254 | OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_5, "drop_green_prio_5"), \ |
255 | OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_6, "drop_green_prio_6"), \ |
256 | OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_7, "drop_green_prio_7") |
257 | |
258 | struct ocelot_stats_region { |
259 | struct list_head node; |
260 | enum ocelot_reg base; |
261 | enum ocelot_stat first_stat; |
262 | int count; |
263 | u32 *buf; |
264 | }; |
265 | |
266 | static const struct ocelot_stat_layout ocelot_stats_layout[OCELOT_NUM_STATS] = { |
267 | OCELOT_COMMON_STATS, |
268 | }; |
269 | |
270 | static const struct ocelot_stat_layout ocelot_mm_stats_layout[OCELOT_NUM_STATS] = { |
271 | OCELOT_COMMON_STATS, |
272 | OCELOT_STAT(RX_ASSEMBLY_ERRS), |
273 | OCELOT_STAT(RX_SMD_ERRS), |
274 | OCELOT_STAT(RX_ASSEMBLY_OK), |
275 | OCELOT_STAT(RX_MERGE_FRAGMENTS), |
276 | OCELOT_STAT(TX_MERGE_FRAGMENTS), |
277 | OCELOT_STAT(TX_MM_HOLD), |
278 | OCELOT_STAT(RX_PMAC_OCTETS), |
279 | OCELOT_STAT(RX_PMAC_UNICAST), |
280 | OCELOT_STAT(RX_PMAC_MULTICAST), |
281 | OCELOT_STAT(RX_PMAC_BROADCAST), |
282 | OCELOT_STAT(RX_PMAC_SHORTS), |
283 | OCELOT_STAT(RX_PMAC_FRAGMENTS), |
284 | OCELOT_STAT(RX_PMAC_JABBERS), |
285 | OCELOT_STAT(RX_PMAC_CRC_ALIGN_ERRS), |
286 | OCELOT_STAT(RX_PMAC_SYM_ERRS), |
287 | OCELOT_STAT(RX_PMAC_64), |
288 | OCELOT_STAT(RX_PMAC_65_127), |
289 | OCELOT_STAT(RX_PMAC_128_255), |
290 | OCELOT_STAT(RX_PMAC_256_511), |
291 | OCELOT_STAT(RX_PMAC_512_1023), |
292 | OCELOT_STAT(RX_PMAC_1024_1526), |
293 | OCELOT_STAT(RX_PMAC_1527_MAX), |
294 | OCELOT_STAT(RX_PMAC_PAUSE), |
295 | OCELOT_STAT(RX_PMAC_CONTROL), |
296 | OCELOT_STAT(RX_PMAC_LONGS), |
297 | OCELOT_STAT(TX_PMAC_OCTETS), |
298 | OCELOT_STAT(TX_PMAC_UNICAST), |
299 | OCELOT_STAT(TX_PMAC_MULTICAST), |
300 | OCELOT_STAT(TX_PMAC_BROADCAST), |
301 | OCELOT_STAT(TX_PMAC_PAUSE), |
302 | OCELOT_STAT(TX_PMAC_64), |
303 | OCELOT_STAT(TX_PMAC_65_127), |
304 | OCELOT_STAT(TX_PMAC_128_255), |
305 | OCELOT_STAT(TX_PMAC_256_511), |
306 | OCELOT_STAT(TX_PMAC_512_1023), |
307 | OCELOT_STAT(TX_PMAC_1024_1526), |
308 | OCELOT_STAT(TX_PMAC_1527_MAX), |
309 | }; |
310 | |
311 | static const struct ocelot_stat_layout * |
312 | ocelot_get_stats_layout(struct ocelot *ocelot) |
313 | { |
314 | if (ocelot->mm_supported) |
315 | return ocelot_mm_stats_layout; |
316 | |
317 | return ocelot_stats_layout; |
318 | } |
319 | |
320 | /* Read the counters from hardware and keep them in region->buf. |
321 | * Caller must hold &ocelot->stat_view_lock. |
322 | */ |
323 | static int ocelot_port_update_stats(struct ocelot *ocelot, int port) |
324 | { |
325 | struct ocelot_stats_region *region; |
326 | int err; |
327 | |
328 | /* Configure the port to read the stats from */ |
329 | ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG); |
330 | |
331 | list_for_each_entry(region, &ocelot->stats_regions, node) { |
332 | err = ocelot_bulk_read(ocelot, region->base, region->buf, |
333 | region->count); |
334 | if (err) |
335 | return err; |
336 | } |
337 | |
338 | return 0; |
339 | } |
340 | |
341 | /* Transfer the counters from region->buf to ocelot->stats. |
342 | * Caller must hold &ocelot->stat_view_lock and &ocelot->stats_lock. |
343 | */ |
344 | static void ocelot_port_transfer_stats(struct ocelot *ocelot, int port) |
345 | { |
346 | struct ocelot_stats_region *region; |
347 | int j; |
348 | |
349 | list_for_each_entry(region, &ocelot->stats_regions, node) { |
350 | unsigned int idx = port * OCELOT_NUM_STATS + region->first_stat; |
351 | |
352 | for (j = 0; j < region->count; j++) { |
353 | u64 *stat = &ocelot->stats[idx + j]; |
354 | u64 val = region->buf[j]; |
355 | |
356 | if (val < (*stat & U32_MAX)) |
357 | *stat += (u64)1 << 32; |
358 | |
359 | *stat = (*stat & ~(u64)U32_MAX) + val; |
360 | } |
361 | } |
362 | } |
363 | |
364 | static void ocelot_check_stats_work(struct work_struct *work) |
365 | { |
366 | struct delayed_work *del_work = to_delayed_work(work); |
367 | struct ocelot *ocelot = container_of(del_work, struct ocelot, |
368 | stats_work); |
369 | int port, err; |
370 | |
371 | mutex_lock(&ocelot->stat_view_lock); |
372 | |
373 | for (port = 0; port < ocelot->num_phys_ports; port++) { |
374 | err = ocelot_port_update_stats(ocelot, port); |
375 | if (err) |
376 | break; |
377 | |
378 | spin_lock(lock: &ocelot->stats_lock); |
379 | ocelot_port_transfer_stats(ocelot, port); |
380 | spin_unlock(lock: &ocelot->stats_lock); |
381 | } |
382 | |
383 | if (!err && ocelot->ops->update_stats) |
384 | ocelot->ops->update_stats(ocelot); |
385 | |
386 | mutex_unlock(lock: &ocelot->stat_view_lock); |
387 | |
388 | if (err) |
389 | dev_err(ocelot->dev, "Error %d updating ethtool stats\n" , err); |
390 | |
391 | queue_delayed_work(wq: ocelot->stats_queue, dwork: &ocelot->stats_work, |
392 | OCELOT_STATS_CHECK_DELAY); |
393 | } |
394 | |
395 | void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) |
396 | { |
397 | const struct ocelot_stat_layout *layout; |
398 | enum ocelot_stat i; |
399 | |
400 | if (sset != ETH_SS_STATS) |
401 | return; |
402 | |
403 | layout = ocelot_get_stats_layout(ocelot); |
404 | |
405 | for (i = 0; i < OCELOT_NUM_STATS; i++) { |
406 | if (layout[i].name[0] == '\0') |
407 | continue; |
408 | |
409 | memcpy(data, layout[i].name, ETH_GSTRING_LEN); |
410 | data += ETH_GSTRING_LEN; |
411 | } |
412 | } |
413 | EXPORT_SYMBOL(ocelot_get_strings); |
414 | |
415 | /* Update ocelot->stats for the given port and run the given callback */ |
416 | static void ocelot_port_stats_run(struct ocelot *ocelot, int port, void *priv, |
417 | void (*cb)(struct ocelot *ocelot, int port, |
418 | void *priv)) |
419 | { |
420 | int err; |
421 | |
422 | mutex_lock(&ocelot->stat_view_lock); |
423 | |
424 | err = ocelot_port_update_stats(ocelot, port); |
425 | if (err) { |
426 | dev_err(ocelot->dev, "Failed to update port %d stats: %pe\n" , |
427 | port, ERR_PTR(err)); |
428 | goto out_unlock; |
429 | } |
430 | |
431 | spin_lock(lock: &ocelot->stats_lock); |
432 | |
433 | ocelot_port_transfer_stats(ocelot, port); |
434 | cb(ocelot, port, priv); |
435 | |
436 | spin_unlock(lock: &ocelot->stats_lock); |
437 | |
438 | out_unlock: |
439 | mutex_unlock(lock: &ocelot->stat_view_lock); |
440 | } |
441 | |
442 | int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) |
443 | { |
444 | const struct ocelot_stat_layout *layout; |
445 | enum ocelot_stat i; |
446 | int num_stats = 0; |
447 | |
448 | if (sset != ETH_SS_STATS) |
449 | return -EOPNOTSUPP; |
450 | |
451 | layout = ocelot_get_stats_layout(ocelot); |
452 | |
453 | for (i = 0; i < OCELOT_NUM_STATS; i++) |
454 | if (layout[i].name[0] != '\0') |
455 | num_stats++; |
456 | |
457 | return num_stats; |
458 | } |
459 | EXPORT_SYMBOL(ocelot_get_sset_count); |
460 | |
461 | static void ocelot_port_ethtool_stats_cb(struct ocelot *ocelot, int port, |
462 | void *priv) |
463 | { |
464 | const struct ocelot_stat_layout *layout; |
465 | enum ocelot_stat i; |
466 | u64 *data = priv; |
467 | |
468 | layout = ocelot_get_stats_layout(ocelot); |
469 | |
470 | /* Copy all supported counters */ |
471 | for (i = 0; i < OCELOT_NUM_STATS; i++) { |
472 | int index = port * OCELOT_NUM_STATS + i; |
473 | |
474 | if (layout[i].name[0] == '\0') |
475 | continue; |
476 | |
477 | *data++ = ocelot->stats[index]; |
478 | } |
479 | } |
480 | |
481 | void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) |
482 | { |
483 | ocelot_port_stats_run(ocelot, port, priv: data, cb: ocelot_port_ethtool_stats_cb); |
484 | } |
485 | EXPORT_SYMBOL(ocelot_get_ethtool_stats); |
486 | |
487 | static void ocelot_port_pause_stats_cb(struct ocelot *ocelot, int port, void *priv) |
488 | { |
489 | u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; |
490 | struct ethtool_pause_stats *pause_stats = priv; |
491 | |
492 | pause_stats->tx_pause_frames = s[OCELOT_STAT_TX_PAUSE]; |
493 | pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PAUSE]; |
494 | } |
495 | |
496 | static void ocelot_port_pmac_pause_stats_cb(struct ocelot *ocelot, int port, |
497 | void *priv) |
498 | { |
499 | u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; |
500 | struct ethtool_pause_stats *pause_stats = priv; |
501 | |
502 | pause_stats->tx_pause_frames = s[OCELOT_STAT_TX_PMAC_PAUSE]; |
503 | pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PMAC_PAUSE]; |
504 | } |
505 | |
506 | static void ocelot_port_mm_stats_cb(struct ocelot *ocelot, int port, |
507 | void *priv) |
508 | { |
509 | u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; |
510 | struct ethtool_mm_stats *stats = priv; |
511 | |
512 | stats->MACMergeFrameAssErrorCount = s[OCELOT_STAT_RX_ASSEMBLY_ERRS]; |
513 | stats->MACMergeFrameSmdErrorCount = s[OCELOT_STAT_RX_SMD_ERRS]; |
514 | stats->MACMergeFrameAssOkCount = s[OCELOT_STAT_RX_ASSEMBLY_OK]; |
515 | stats->MACMergeFragCountRx = s[OCELOT_STAT_RX_MERGE_FRAGMENTS]; |
516 | stats->MACMergeFragCountTx = s[OCELOT_STAT_TX_MERGE_FRAGMENTS]; |
517 | stats->MACMergeHoldCount = s[OCELOT_STAT_TX_MM_HOLD]; |
518 | } |
519 | |
520 | void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port, |
521 | struct ethtool_pause_stats *pause_stats) |
522 | { |
523 | struct net_device *dev; |
524 | |
525 | switch (pause_stats->src) { |
526 | case ETHTOOL_MAC_STATS_SRC_EMAC: |
527 | ocelot_port_stats_run(ocelot, port, priv: pause_stats, |
528 | cb: ocelot_port_pause_stats_cb); |
529 | break; |
530 | case ETHTOOL_MAC_STATS_SRC_PMAC: |
531 | if (ocelot->mm_supported) |
532 | ocelot_port_stats_run(ocelot, port, priv: pause_stats, |
533 | cb: ocelot_port_pmac_pause_stats_cb); |
534 | break; |
535 | case ETHTOOL_MAC_STATS_SRC_AGGREGATE: |
536 | dev = ocelot->ops->port_to_netdev(ocelot, port); |
537 | ethtool_aggregate_pause_stats(dev, pause_stats); |
538 | break; |
539 | } |
540 | } |
541 | EXPORT_SYMBOL_GPL(ocelot_port_get_pause_stats); |
542 | |
543 | void ocelot_port_get_mm_stats(struct ocelot *ocelot, int port, |
544 | struct ethtool_mm_stats *stats) |
545 | { |
546 | if (!ocelot->mm_supported) |
547 | return; |
548 | |
549 | ocelot_port_stats_run(ocelot, port, priv: stats, cb: ocelot_port_mm_stats_cb); |
550 | } |
551 | EXPORT_SYMBOL_GPL(ocelot_port_get_mm_stats); |
552 | |
553 | static const struct ethtool_rmon_hist_range ocelot_rmon_ranges[] = { |
554 | { 64, 64 }, |
555 | { 65, 127 }, |
556 | { 128, 255 }, |
557 | { 256, 511 }, |
558 | { 512, 1023 }, |
559 | { 1024, 1526 }, |
560 | { 1527, 65535 }, |
561 | {}, |
562 | }; |
563 | |
564 | static void ocelot_port_rmon_stats_cb(struct ocelot *ocelot, int port, void *priv) |
565 | { |
566 | u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; |
567 | struct ethtool_rmon_stats *rmon_stats = priv; |
568 | |
569 | rmon_stats->undersize_pkts = s[OCELOT_STAT_RX_SHORTS]; |
570 | rmon_stats->oversize_pkts = s[OCELOT_STAT_RX_LONGS]; |
571 | rmon_stats->fragments = s[OCELOT_STAT_RX_FRAGMENTS]; |
572 | rmon_stats->jabbers = s[OCELOT_STAT_RX_JABBERS]; |
573 | |
574 | rmon_stats->hist[0] = s[OCELOT_STAT_RX_64]; |
575 | rmon_stats->hist[1] = s[OCELOT_STAT_RX_65_127]; |
576 | rmon_stats->hist[2] = s[OCELOT_STAT_RX_128_255]; |
577 | rmon_stats->hist[3] = s[OCELOT_STAT_RX_256_511]; |
578 | rmon_stats->hist[4] = s[OCELOT_STAT_RX_512_1023]; |
579 | rmon_stats->hist[5] = s[OCELOT_STAT_RX_1024_1526]; |
580 | rmon_stats->hist[6] = s[OCELOT_STAT_RX_1527_MAX]; |
581 | |
582 | rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_64]; |
583 | rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_65_127]; |
584 | rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_128_255]; |
585 | rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_128_255]; |
586 | rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_256_511]; |
587 | rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_512_1023]; |
588 | rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1024_1526]; |
589 | } |
590 | |
591 | static void ocelot_port_pmac_rmon_stats_cb(struct ocelot *ocelot, int port, |
592 | void *priv) |
593 | { |
594 | u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; |
595 | struct ethtool_rmon_stats *rmon_stats = priv; |
596 | |
597 | rmon_stats->undersize_pkts = s[OCELOT_STAT_RX_PMAC_SHORTS]; |
598 | rmon_stats->oversize_pkts = s[OCELOT_STAT_RX_PMAC_LONGS]; |
599 | rmon_stats->fragments = s[OCELOT_STAT_RX_PMAC_FRAGMENTS]; |
600 | rmon_stats->jabbers = s[OCELOT_STAT_RX_PMAC_JABBERS]; |
601 | |
602 | rmon_stats->hist[0] = s[OCELOT_STAT_RX_PMAC_64]; |
603 | rmon_stats->hist[1] = s[OCELOT_STAT_RX_PMAC_65_127]; |
604 | rmon_stats->hist[2] = s[OCELOT_STAT_RX_PMAC_128_255]; |
605 | rmon_stats->hist[3] = s[OCELOT_STAT_RX_PMAC_256_511]; |
606 | rmon_stats->hist[4] = s[OCELOT_STAT_RX_PMAC_512_1023]; |
607 | rmon_stats->hist[5] = s[OCELOT_STAT_RX_PMAC_1024_1526]; |
608 | rmon_stats->hist[6] = s[OCELOT_STAT_RX_PMAC_1527_MAX]; |
609 | |
610 | rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_PMAC_64]; |
611 | rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_PMAC_65_127]; |
612 | rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_PMAC_128_255]; |
613 | rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_PMAC_128_255]; |
614 | rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_PMAC_256_511]; |
615 | rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_PMAC_512_1023]; |
616 | rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_PMAC_1024_1526]; |
617 | } |
618 | |
619 | void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port, |
620 | struct ethtool_rmon_stats *rmon_stats, |
621 | const struct ethtool_rmon_hist_range **ranges) |
622 | { |
623 | struct net_device *dev; |
624 | |
625 | *ranges = ocelot_rmon_ranges; |
626 | |
627 | switch (rmon_stats->src) { |
628 | case ETHTOOL_MAC_STATS_SRC_EMAC: |
629 | ocelot_port_stats_run(ocelot, port, priv: rmon_stats, |
630 | cb: ocelot_port_rmon_stats_cb); |
631 | break; |
632 | case ETHTOOL_MAC_STATS_SRC_PMAC: |
633 | if (ocelot->mm_supported) |
634 | ocelot_port_stats_run(ocelot, port, priv: rmon_stats, |
635 | cb: ocelot_port_pmac_rmon_stats_cb); |
636 | break; |
637 | case ETHTOOL_MAC_STATS_SRC_AGGREGATE: |
638 | dev = ocelot->ops->port_to_netdev(ocelot, port); |
639 | ethtool_aggregate_rmon_stats(dev, rmon_stats); |
640 | break; |
641 | } |
642 | } |
643 | EXPORT_SYMBOL_GPL(ocelot_port_get_rmon_stats); |
644 | |
645 | static void ocelot_port_ctrl_stats_cb(struct ocelot *ocelot, int port, void *priv) |
646 | { |
647 | u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; |
648 | struct ethtool_eth_ctrl_stats *ctrl_stats = priv; |
649 | |
650 | ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_CONTROL]; |
651 | } |
652 | |
653 | static void ocelot_port_pmac_ctrl_stats_cb(struct ocelot *ocelot, int port, |
654 | void *priv) |
655 | { |
656 | u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; |
657 | struct ethtool_eth_ctrl_stats *ctrl_stats = priv; |
658 | |
659 | ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_PMAC_CONTROL]; |
660 | } |
661 | |
662 | void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port, |
663 | struct ethtool_eth_ctrl_stats *ctrl_stats) |
664 | { |
665 | struct net_device *dev; |
666 | |
667 | switch (ctrl_stats->src) { |
668 | case ETHTOOL_MAC_STATS_SRC_EMAC: |
669 | ocelot_port_stats_run(ocelot, port, priv: ctrl_stats, |
670 | cb: ocelot_port_ctrl_stats_cb); |
671 | break; |
672 | case ETHTOOL_MAC_STATS_SRC_PMAC: |
673 | if (ocelot->mm_supported) |
674 | ocelot_port_stats_run(ocelot, port, priv: ctrl_stats, |
675 | cb: ocelot_port_pmac_ctrl_stats_cb); |
676 | break; |
677 | case ETHTOOL_MAC_STATS_SRC_AGGREGATE: |
678 | dev = ocelot->ops->port_to_netdev(ocelot, port); |
679 | ethtool_aggregate_ctrl_stats(dev, ctrl_stats); |
680 | break; |
681 | } |
682 | } |
683 | EXPORT_SYMBOL_GPL(ocelot_port_get_eth_ctrl_stats); |
684 | |
685 | static void ocelot_port_mac_stats_cb(struct ocelot *ocelot, int port, void *priv) |
686 | { |
687 | u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; |
688 | struct ethtool_eth_mac_stats *mac_stats = priv; |
689 | |
690 | mac_stats->OctetsTransmittedOK = s[OCELOT_STAT_TX_OCTETS]; |
691 | mac_stats->FramesTransmittedOK = s[OCELOT_STAT_TX_64] + |
692 | s[OCELOT_STAT_TX_65_127] + |
693 | s[OCELOT_STAT_TX_128_255] + |
694 | s[OCELOT_STAT_TX_256_511] + |
695 | s[OCELOT_STAT_TX_512_1023] + |
696 | s[OCELOT_STAT_TX_1024_1526] + |
697 | s[OCELOT_STAT_TX_1527_MAX]; |
698 | mac_stats->OctetsReceivedOK = s[OCELOT_STAT_RX_OCTETS]; |
699 | mac_stats->FramesReceivedOK = s[OCELOT_STAT_RX_GREEN_PRIO_0] + |
700 | s[OCELOT_STAT_RX_GREEN_PRIO_1] + |
701 | s[OCELOT_STAT_RX_GREEN_PRIO_2] + |
702 | s[OCELOT_STAT_RX_GREEN_PRIO_3] + |
703 | s[OCELOT_STAT_RX_GREEN_PRIO_4] + |
704 | s[OCELOT_STAT_RX_GREEN_PRIO_5] + |
705 | s[OCELOT_STAT_RX_GREEN_PRIO_6] + |
706 | s[OCELOT_STAT_RX_GREEN_PRIO_7] + |
707 | s[OCELOT_STAT_RX_YELLOW_PRIO_0] + |
708 | s[OCELOT_STAT_RX_YELLOW_PRIO_1] + |
709 | s[OCELOT_STAT_RX_YELLOW_PRIO_2] + |
710 | s[OCELOT_STAT_RX_YELLOW_PRIO_3] + |
711 | s[OCELOT_STAT_RX_YELLOW_PRIO_4] + |
712 | s[OCELOT_STAT_RX_YELLOW_PRIO_5] + |
713 | s[OCELOT_STAT_RX_YELLOW_PRIO_6] + |
714 | s[OCELOT_STAT_RX_YELLOW_PRIO_7]; |
715 | mac_stats->MulticastFramesXmittedOK = s[OCELOT_STAT_TX_MULTICAST]; |
716 | mac_stats->BroadcastFramesXmittedOK = s[OCELOT_STAT_TX_BROADCAST]; |
717 | mac_stats->MulticastFramesReceivedOK = s[OCELOT_STAT_RX_MULTICAST]; |
718 | mac_stats->BroadcastFramesReceivedOK = s[OCELOT_STAT_RX_BROADCAST]; |
719 | mac_stats->FrameTooLongErrors = s[OCELOT_STAT_RX_LONGS]; |
720 | /* Sadly, C_RX_CRC is the sum of FCS and alignment errors, they are not |
721 | * counted individually. |
722 | */ |
723 | mac_stats->FrameCheckSequenceErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS]; |
724 | mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS]; |
725 | } |
726 | |
727 | static void ocelot_port_pmac_mac_stats_cb(struct ocelot *ocelot, int port, |
728 | void *priv) |
729 | { |
730 | u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; |
731 | struct ethtool_eth_mac_stats *mac_stats = priv; |
732 | |
733 | mac_stats->OctetsTransmittedOK = s[OCELOT_STAT_TX_PMAC_OCTETS]; |
734 | mac_stats->FramesTransmittedOK = s[OCELOT_STAT_TX_PMAC_64] + |
735 | s[OCELOT_STAT_TX_PMAC_65_127] + |
736 | s[OCELOT_STAT_TX_PMAC_128_255] + |
737 | s[OCELOT_STAT_TX_PMAC_256_511] + |
738 | s[OCELOT_STAT_TX_PMAC_512_1023] + |
739 | s[OCELOT_STAT_TX_PMAC_1024_1526] + |
740 | s[OCELOT_STAT_TX_PMAC_1527_MAX]; |
741 | mac_stats->OctetsReceivedOK = s[OCELOT_STAT_RX_PMAC_OCTETS]; |
742 | mac_stats->FramesReceivedOK = s[OCELOT_STAT_RX_PMAC_64] + |
743 | s[OCELOT_STAT_RX_PMAC_65_127] + |
744 | s[OCELOT_STAT_RX_PMAC_128_255] + |
745 | s[OCELOT_STAT_RX_PMAC_256_511] + |
746 | s[OCELOT_STAT_RX_PMAC_512_1023] + |
747 | s[OCELOT_STAT_RX_PMAC_1024_1526] + |
748 | s[OCELOT_STAT_RX_PMAC_1527_MAX]; |
749 | mac_stats->MulticastFramesXmittedOK = s[OCELOT_STAT_TX_PMAC_MULTICAST]; |
750 | mac_stats->BroadcastFramesXmittedOK = s[OCELOT_STAT_TX_PMAC_BROADCAST]; |
751 | mac_stats->MulticastFramesReceivedOK = s[OCELOT_STAT_RX_PMAC_MULTICAST]; |
752 | mac_stats->BroadcastFramesReceivedOK = s[OCELOT_STAT_RX_PMAC_BROADCAST]; |
753 | mac_stats->FrameTooLongErrors = s[OCELOT_STAT_RX_PMAC_LONGS]; |
754 | /* Sadly, C_RX_CRC is the sum of FCS and alignment errors, they are not |
755 | * counted individually. |
756 | */ |
757 | mac_stats->FrameCheckSequenceErrors = s[OCELOT_STAT_RX_PMAC_CRC_ALIGN_ERRS]; |
758 | mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_PMAC_CRC_ALIGN_ERRS]; |
759 | } |
760 | |
761 | void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port, |
762 | struct ethtool_eth_mac_stats *mac_stats) |
763 | { |
764 | struct net_device *dev; |
765 | |
766 | switch (mac_stats->src) { |
767 | case ETHTOOL_MAC_STATS_SRC_EMAC: |
768 | ocelot_port_stats_run(ocelot, port, priv: mac_stats, |
769 | cb: ocelot_port_mac_stats_cb); |
770 | break; |
771 | case ETHTOOL_MAC_STATS_SRC_PMAC: |
772 | if (ocelot->mm_supported) |
773 | ocelot_port_stats_run(ocelot, port, priv: mac_stats, |
774 | cb: ocelot_port_pmac_mac_stats_cb); |
775 | break; |
776 | case ETHTOOL_MAC_STATS_SRC_AGGREGATE: |
777 | dev = ocelot->ops->port_to_netdev(ocelot, port); |
778 | ethtool_aggregate_mac_stats(dev, mac_stats); |
779 | break; |
780 | } |
781 | } |
782 | EXPORT_SYMBOL_GPL(ocelot_port_get_eth_mac_stats); |
783 | |
784 | static void ocelot_port_phy_stats_cb(struct ocelot *ocelot, int port, void *priv) |
785 | { |
786 | u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; |
787 | struct ethtool_eth_phy_stats *phy_stats = priv; |
788 | |
789 | phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_SYM_ERRS]; |
790 | } |
791 | |
792 | static void ocelot_port_pmac_phy_stats_cb(struct ocelot *ocelot, int port, |
793 | void *priv) |
794 | { |
795 | u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; |
796 | struct ethtool_eth_phy_stats *phy_stats = priv; |
797 | |
798 | phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_PMAC_SYM_ERRS]; |
799 | } |
800 | |
801 | void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port, |
802 | struct ethtool_eth_phy_stats *phy_stats) |
803 | { |
804 | struct net_device *dev; |
805 | |
806 | switch (phy_stats->src) { |
807 | case ETHTOOL_MAC_STATS_SRC_EMAC: |
808 | ocelot_port_stats_run(ocelot, port, priv: phy_stats, |
809 | cb: ocelot_port_phy_stats_cb); |
810 | break; |
811 | case ETHTOOL_MAC_STATS_SRC_PMAC: |
812 | if (ocelot->mm_supported) |
813 | ocelot_port_stats_run(ocelot, port, priv: phy_stats, |
814 | cb: ocelot_port_pmac_phy_stats_cb); |
815 | break; |
816 | case ETHTOOL_MAC_STATS_SRC_AGGREGATE: |
817 | dev = ocelot->ops->port_to_netdev(ocelot, port); |
818 | ethtool_aggregate_phy_stats(dev, phy_stats); |
819 | break; |
820 | } |
821 | } |
822 | EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats); |
823 | |
824 | void ocelot_port_get_stats64(struct ocelot *ocelot, int port, |
825 | struct rtnl_link_stats64 *stats) |
826 | { |
827 | u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; |
828 | |
829 | spin_lock(lock: &ocelot->stats_lock); |
830 | |
831 | /* Get Rx stats */ |
832 | stats->rx_bytes = s[OCELOT_STAT_RX_OCTETS]; |
833 | stats->rx_packets = s[OCELOT_STAT_RX_SHORTS] + |
834 | s[OCELOT_STAT_RX_FRAGMENTS] + |
835 | s[OCELOT_STAT_RX_JABBERS] + |
836 | s[OCELOT_STAT_RX_LONGS] + |
837 | s[OCELOT_STAT_RX_64] + |
838 | s[OCELOT_STAT_RX_65_127] + |
839 | s[OCELOT_STAT_RX_128_255] + |
840 | s[OCELOT_STAT_RX_256_511] + |
841 | s[OCELOT_STAT_RX_512_1023] + |
842 | s[OCELOT_STAT_RX_1024_1526] + |
843 | s[OCELOT_STAT_RX_1527_MAX]; |
844 | stats->multicast = s[OCELOT_STAT_RX_MULTICAST]; |
845 | stats->rx_missed_errors = s[OCELOT_STAT_DROP_TAIL]; |
846 | stats->rx_dropped = s[OCELOT_STAT_RX_RED_PRIO_0] + |
847 | s[OCELOT_STAT_RX_RED_PRIO_1] + |
848 | s[OCELOT_STAT_RX_RED_PRIO_2] + |
849 | s[OCELOT_STAT_RX_RED_PRIO_3] + |
850 | s[OCELOT_STAT_RX_RED_PRIO_4] + |
851 | s[OCELOT_STAT_RX_RED_PRIO_5] + |
852 | s[OCELOT_STAT_RX_RED_PRIO_6] + |
853 | s[OCELOT_STAT_RX_RED_PRIO_7] + |
854 | s[OCELOT_STAT_DROP_LOCAL] + |
855 | s[OCELOT_STAT_DROP_YELLOW_PRIO_0] + |
856 | s[OCELOT_STAT_DROP_YELLOW_PRIO_1] + |
857 | s[OCELOT_STAT_DROP_YELLOW_PRIO_2] + |
858 | s[OCELOT_STAT_DROP_YELLOW_PRIO_3] + |
859 | s[OCELOT_STAT_DROP_YELLOW_PRIO_4] + |
860 | s[OCELOT_STAT_DROP_YELLOW_PRIO_5] + |
861 | s[OCELOT_STAT_DROP_YELLOW_PRIO_6] + |
862 | s[OCELOT_STAT_DROP_YELLOW_PRIO_7] + |
863 | s[OCELOT_STAT_DROP_GREEN_PRIO_0] + |
864 | s[OCELOT_STAT_DROP_GREEN_PRIO_1] + |
865 | s[OCELOT_STAT_DROP_GREEN_PRIO_2] + |
866 | s[OCELOT_STAT_DROP_GREEN_PRIO_3] + |
867 | s[OCELOT_STAT_DROP_GREEN_PRIO_4] + |
868 | s[OCELOT_STAT_DROP_GREEN_PRIO_5] + |
869 | s[OCELOT_STAT_DROP_GREEN_PRIO_6] + |
870 | s[OCELOT_STAT_DROP_GREEN_PRIO_7]; |
871 | |
872 | /* Get Tx stats */ |
873 | stats->tx_bytes = s[OCELOT_STAT_TX_OCTETS]; |
874 | stats->tx_packets = s[OCELOT_STAT_TX_64] + |
875 | s[OCELOT_STAT_TX_65_127] + |
876 | s[OCELOT_STAT_TX_128_255] + |
877 | s[OCELOT_STAT_TX_256_511] + |
878 | s[OCELOT_STAT_TX_512_1023] + |
879 | s[OCELOT_STAT_TX_1024_1526] + |
880 | s[OCELOT_STAT_TX_1527_MAX]; |
881 | stats->tx_dropped = s[OCELOT_STAT_TX_DROPS] + |
882 | s[OCELOT_STAT_TX_AGED]; |
883 | stats->collisions = s[OCELOT_STAT_TX_COLLISION]; |
884 | |
885 | spin_unlock(lock: &ocelot->stats_lock); |
886 | } |
887 | EXPORT_SYMBOL(ocelot_port_get_stats64); |
888 | |
889 | static int ocelot_prepare_stats_regions(struct ocelot *ocelot) |
890 | { |
891 | struct ocelot_stats_region *region = NULL; |
892 | const struct ocelot_stat_layout *layout; |
893 | enum ocelot_reg last = 0; |
894 | enum ocelot_stat i; |
895 | |
896 | INIT_LIST_HEAD(list: &ocelot->stats_regions); |
897 | |
898 | layout = ocelot_get_stats_layout(ocelot); |
899 | |
900 | for (i = 0; i < OCELOT_NUM_STATS; i++) { |
901 | if (!layout[i].reg) |
902 | continue; |
903 | |
904 | /* enum ocelot_stat must be kept sorted in the same order |
905 | * as the addresses behind layout[i].reg in order to have |
906 | * efficient bulking |
907 | */ |
908 | if (last) { |
909 | WARN(ocelot->map[SYS][last & REG_MASK] >= ocelot->map[SYS][layout[i].reg & REG_MASK], |
910 | "reg 0x%x had address 0x%x but reg 0x%x has address 0x%x, bulking broken!" , |
911 | last, ocelot->map[SYS][last & REG_MASK], |
912 | layout[i].reg, ocelot->map[SYS][layout[i].reg & REG_MASK]); |
913 | } |
914 | |
915 | if (region && ocelot->map[SYS][layout[i].reg & REG_MASK] == |
916 | ocelot->map[SYS][last & REG_MASK] + 4) { |
917 | region->count++; |
918 | } else { |
919 | region = devm_kzalloc(dev: ocelot->dev, size: sizeof(*region), |
920 | GFP_KERNEL); |
921 | if (!region) |
922 | return -ENOMEM; |
923 | |
924 | region->base = layout[i].reg; |
925 | region->first_stat = i; |
926 | region->count = 1; |
927 | list_add_tail(new: ®ion->node, head: &ocelot->stats_regions); |
928 | } |
929 | |
930 | last = layout[i].reg; |
931 | } |
932 | |
933 | list_for_each_entry(region, &ocelot->stats_regions, node) { |
934 | enum ocelot_target target; |
935 | u32 addr; |
936 | |
937 | ocelot_reg_to_target_addr(ocelot, reg: region->base, target: &target, |
938 | addr: &addr); |
939 | |
940 | dev_dbg(ocelot->dev, |
941 | "region of %d contiguous counters starting with SYS:STAT:CNT[0x%03x]\n" , |
942 | region->count, addr / 4); |
943 | region->buf = devm_kcalloc(dev: ocelot->dev, n: region->count, |
944 | size: sizeof(*region->buf), GFP_KERNEL); |
945 | if (!region->buf) |
946 | return -ENOMEM; |
947 | } |
948 | |
949 | return 0; |
950 | } |
951 | |
952 | int ocelot_stats_init(struct ocelot *ocelot) |
953 | { |
954 | char queue_name[32]; |
955 | int ret; |
956 | |
957 | ocelot->stats = devm_kcalloc(dev: ocelot->dev, |
958 | n: ocelot->num_phys_ports * OCELOT_NUM_STATS, |
959 | size: sizeof(u64), GFP_KERNEL); |
960 | if (!ocelot->stats) |
961 | return -ENOMEM; |
962 | |
963 | snprintf(buf: queue_name, size: sizeof(queue_name), fmt: "%s-stats" , |
964 | dev_name(dev: ocelot->dev)); |
965 | ocelot->stats_queue = create_singlethread_workqueue(queue_name); |
966 | if (!ocelot->stats_queue) |
967 | return -ENOMEM; |
968 | |
969 | spin_lock_init(&ocelot->stats_lock); |
970 | mutex_init(&ocelot->stat_view_lock); |
971 | |
972 | ret = ocelot_prepare_stats_regions(ocelot); |
973 | if (ret) { |
974 | destroy_workqueue(wq: ocelot->stats_queue); |
975 | return ret; |
976 | } |
977 | |
978 | INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work); |
979 | queue_delayed_work(wq: ocelot->stats_queue, dwork: &ocelot->stats_work, |
980 | OCELOT_STATS_CHECK_DELAY); |
981 | |
982 | return 0; |
983 | } |
984 | |
985 | void ocelot_stats_deinit(struct ocelot *ocelot) |
986 | { |
987 | cancel_delayed_work(dwork: &ocelot->stats_work); |
988 | destroy_workqueue(wq: ocelot->stats_queue); |
989 | } |
990 | |