1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
3 */
4 #include "sja1105.h"
5
6 #define SJA1105_SIZE_MAC_AREA (0x02 * 4)
7 #define SJA1105_SIZE_HL1_AREA (0x10 * 4)
8 #define SJA1105_SIZE_HL2_AREA (0x4 * 4)
9 #define SJA1105_SIZE_QLEVEL_AREA (0x8 * 4) /* 0x4 to 0xB */
10
11 struct sja1105_port_status_mac {
12 u64 n_runt;
13 u64 n_soferr;
14 u64 n_alignerr;
15 u64 n_miierr;
16 u64 typeerr;
17 u64 sizeerr;
18 u64 tctimeout;
19 u64 priorerr;
20 u64 nomaster;
21 u64 memov;
22 u64 memerr;
23 u64 invtyp;
24 u64 intcyov;
25 u64 domerr;
26 u64 pcfbagdrop;
27 u64 spcprior;
28 u64 ageprior;
29 u64 portdrop;
30 u64 lendrop;
31 u64 bagdrop;
32 u64 policeerr;
33 u64 drpnona664err;
34 u64 spcerr;
35 u64 agedrp;
36 };
37
38 struct sja1105_port_status_hl1 {
39 u64 n_n664err;
40 u64 n_vlanerr;
41 u64 n_unreleased;
42 u64 n_sizeerr;
43 u64 n_crcerr;
44 u64 n_vlnotfound;
45 u64 n_ctpolerr;
46 u64 n_polerr;
47 u64 n_rxfrmsh;
48 u64 n_rxfrm;
49 u64 n_rxbytesh;
50 u64 n_rxbyte;
51 u64 n_txfrmsh;
52 u64 n_txfrm;
53 u64 n_txbytesh;
54 u64 n_txbyte;
55 };
56
57 struct sja1105_port_status_hl2 {
58 u64 n_qfull;
59 u64 n_part_drop;
60 u64 n_egr_disabled;
61 u64 n_not_reach;
62 u64 qlevel_hwm[8]; /* Only for P/Q/R/S */
63 u64 qlevel[8]; /* Only for P/Q/R/S */
64 };
65
66 struct sja1105_port_status {
67 struct sja1105_port_status_mac mac;
68 struct sja1105_port_status_hl1 hl1;
69 struct sja1105_port_status_hl2 hl2;
70 };
71
72 static void
sja1105_port_status_mac_unpack(void * buf,struct sja1105_port_status_mac * status)73 sja1105_port_status_mac_unpack(void *buf,
74 struct sja1105_port_status_mac *status)
75 {
76 /* Make pointer arithmetic work on 4 bytes */
77 u32 *p = buf;
78
79 sja1105_unpack(p + 0x0, &status->n_runt, 31, 24, 4);
80 sja1105_unpack(p + 0x0, &status->n_soferr, 23, 16, 4);
81 sja1105_unpack(p + 0x0, &status->n_alignerr, 15, 8, 4);
82 sja1105_unpack(p + 0x0, &status->n_miierr, 7, 0, 4);
83 sja1105_unpack(p + 0x1, &status->typeerr, 27, 27, 4);
84 sja1105_unpack(p + 0x1, &status->sizeerr, 26, 26, 4);
85 sja1105_unpack(p + 0x1, &status->tctimeout, 25, 25, 4);
86 sja1105_unpack(p + 0x1, &status->priorerr, 24, 24, 4);
87 sja1105_unpack(p + 0x1, &status->nomaster, 23, 23, 4);
88 sja1105_unpack(p + 0x1, &status->memov, 22, 22, 4);
89 sja1105_unpack(p + 0x1, &status->memerr, 21, 21, 4);
90 sja1105_unpack(p + 0x1, &status->invtyp, 19, 19, 4);
91 sja1105_unpack(p + 0x1, &status->intcyov, 18, 18, 4);
92 sja1105_unpack(p + 0x1, &status->domerr, 17, 17, 4);
93 sja1105_unpack(p + 0x1, &status->pcfbagdrop, 16, 16, 4);
94 sja1105_unpack(p + 0x1, &status->spcprior, 15, 12, 4);
95 sja1105_unpack(p + 0x1, &status->ageprior, 11, 8, 4);
96 sja1105_unpack(p + 0x1, &status->portdrop, 6, 6, 4);
97 sja1105_unpack(p + 0x1, &status->lendrop, 5, 5, 4);
98 sja1105_unpack(p + 0x1, &status->bagdrop, 4, 4, 4);
99 sja1105_unpack(p + 0x1, &status->policeerr, 3, 3, 4);
100 sja1105_unpack(p + 0x1, &status->drpnona664err, 2, 2, 4);
101 sja1105_unpack(p + 0x1, &status->spcerr, 1, 1, 4);
102 sja1105_unpack(p + 0x1, &status->agedrp, 0, 0, 4);
103 }
104
105 static void
sja1105_port_status_hl1_unpack(void * buf,struct sja1105_port_status_hl1 * status)106 sja1105_port_status_hl1_unpack(void *buf,
107 struct sja1105_port_status_hl1 *status)
108 {
109 /* Make pointer arithmetic work on 4 bytes */
110 u32 *p = buf;
111
112 sja1105_unpack(p + 0xF, &status->n_n664err, 31, 0, 4);
113 sja1105_unpack(p + 0xE, &status->n_vlanerr, 31, 0, 4);
114 sja1105_unpack(p + 0xD, &status->n_unreleased, 31, 0, 4);
115 sja1105_unpack(p + 0xC, &status->n_sizeerr, 31, 0, 4);
116 sja1105_unpack(p + 0xB, &status->n_crcerr, 31, 0, 4);
117 sja1105_unpack(p + 0xA, &status->n_vlnotfound, 31, 0, 4);
118 sja1105_unpack(p + 0x9, &status->n_ctpolerr, 31, 0, 4);
119 sja1105_unpack(p + 0x8, &status->n_polerr, 31, 0, 4);
120 sja1105_unpack(p + 0x7, &status->n_rxfrmsh, 31, 0, 4);
121 sja1105_unpack(p + 0x6, &status->n_rxfrm, 31, 0, 4);
122 sja1105_unpack(p + 0x5, &status->n_rxbytesh, 31, 0, 4);
123 sja1105_unpack(p + 0x4, &status->n_rxbyte, 31, 0, 4);
124 sja1105_unpack(p + 0x3, &status->n_txfrmsh, 31, 0, 4);
125 sja1105_unpack(p + 0x2, &status->n_txfrm, 31, 0, 4);
126 sja1105_unpack(p + 0x1, &status->n_txbytesh, 31, 0, 4);
127 sja1105_unpack(p + 0x0, &status->n_txbyte, 31, 0, 4);
128 status->n_rxfrm += status->n_rxfrmsh << 32;
129 status->n_rxbyte += status->n_rxbytesh << 32;
130 status->n_txfrm += status->n_txfrmsh << 32;
131 status->n_txbyte += status->n_txbytesh << 32;
132 }
133
134 static void
sja1105_port_status_hl2_unpack(void * buf,struct sja1105_port_status_hl2 * status)135 sja1105_port_status_hl2_unpack(void *buf,
136 struct sja1105_port_status_hl2 *status)
137 {
138 /* Make pointer arithmetic work on 4 bytes */
139 u32 *p = buf;
140
141 sja1105_unpack(p + 0x3, &status->n_qfull, 31, 0, 4);
142 sja1105_unpack(p + 0x2, &status->n_part_drop, 31, 0, 4);
143 sja1105_unpack(p + 0x1, &status->n_egr_disabled, 31, 0, 4);
144 sja1105_unpack(p + 0x0, &status->n_not_reach, 31, 0, 4);
145 }
146
147 static void
sja1105pqrs_port_status_qlevel_unpack(void * buf,struct sja1105_port_status_hl2 * status)148 sja1105pqrs_port_status_qlevel_unpack(void *buf,
149 struct sja1105_port_status_hl2 *status)
150 {
151 /* Make pointer arithmetic work on 4 bytes */
152 u32 *p = buf;
153 int i;
154
155 for (i = 0; i < 8; i++) {
156 sja1105_unpack(p + i, &status->qlevel_hwm[i], 24, 16, 4);
157 sja1105_unpack(p + i, &status->qlevel[i], 8, 0, 4);
158 }
159 }
160
sja1105_port_status_get_mac(struct sja1105_private * priv,struct sja1105_port_status_mac * status,int port)161 static int sja1105_port_status_get_mac(struct sja1105_private *priv,
162 struct sja1105_port_status_mac *status,
163 int port)
164 {
165 const struct sja1105_regs *regs = priv->info->regs;
166 u8 packed_buf[SJA1105_SIZE_MAC_AREA] = {0};
167 int rc;
168
169 /* MAC area */
170 rc = sja1105_spi_send_packed_buf(priv, SPI_READ, regs->mac[port],
171 packed_buf, SJA1105_SIZE_MAC_AREA);
172 if (rc < 0)
173 return rc;
174
175 sja1105_port_status_mac_unpack(packed_buf, status);
176
177 return 0;
178 }
179
sja1105_port_status_get_hl1(struct sja1105_private * priv,struct sja1105_port_status_hl1 * status,int port)180 static int sja1105_port_status_get_hl1(struct sja1105_private *priv,
181 struct sja1105_port_status_hl1 *status,
182 int port)
183 {
184 const struct sja1105_regs *regs = priv->info->regs;
185 u8 packed_buf[SJA1105_SIZE_HL1_AREA] = {0};
186 int rc;
187
188 rc = sja1105_spi_send_packed_buf(priv, SPI_READ, regs->mac_hl1[port],
189 packed_buf, SJA1105_SIZE_HL1_AREA);
190 if (rc < 0)
191 return rc;
192
193 sja1105_port_status_hl1_unpack(packed_buf, status);
194
195 return 0;
196 }
197
sja1105_port_status_get_hl2(struct sja1105_private * priv,struct sja1105_port_status_hl2 * status,int port)198 static int sja1105_port_status_get_hl2(struct sja1105_private *priv,
199 struct sja1105_port_status_hl2 *status,
200 int port)
201 {
202 const struct sja1105_regs *regs = priv->info->regs;
203 u8 packed_buf[SJA1105_SIZE_QLEVEL_AREA] = {0};
204 int rc;
205
206 rc = sja1105_spi_send_packed_buf(priv, SPI_READ, regs->mac_hl2[port],
207 packed_buf, SJA1105_SIZE_HL2_AREA);
208 if (rc < 0)
209 return rc;
210
211 sja1105_port_status_hl2_unpack(packed_buf, status);
212
213 /* Code below is strictly P/Q/R/S specific. */
214 if (priv->info->device_id == SJA1105E_DEVICE_ID ||
215 priv->info->device_id == SJA1105T_DEVICE_ID)
216 return 0;
217
218 rc = sja1105_spi_send_packed_buf(priv, SPI_READ, regs->qlevel[port],
219 packed_buf, SJA1105_SIZE_QLEVEL_AREA);
220 if (rc < 0)
221 return rc;
222
223 sja1105pqrs_port_status_qlevel_unpack(packed_buf, status);
224
225 return 0;
226 }
227
sja1105_port_status_get(struct sja1105_private * priv,struct sja1105_port_status * status,int port)228 static int sja1105_port_status_get(struct sja1105_private *priv,
229 struct sja1105_port_status *status,
230 int port)
231 {
232 int rc;
233
234 rc = sja1105_port_status_get_mac(priv, &status->mac, port);
235 if (rc < 0)
236 return rc;
237 rc = sja1105_port_status_get_hl1(priv, &status->hl1, port);
238 if (rc < 0)
239 return rc;
240 rc = sja1105_port_status_get_hl2(priv, &status->hl2, port);
241 if (rc < 0)
242 return rc;
243
244 return 0;
245 }
246
247 static char sja1105_port_stats[][ETH_GSTRING_LEN] = {
248 /* MAC-Level Diagnostic Counters */
249 "n_runt",
250 "n_soferr",
251 "n_alignerr",
252 "n_miierr",
253 /* MAC-Level Diagnostic Flags */
254 "typeerr",
255 "sizeerr",
256 "tctimeout",
257 "priorerr",
258 "nomaster",
259 "memov",
260 "memerr",
261 "invtyp",
262 "intcyov",
263 "domerr",
264 "pcfbagdrop",
265 "spcprior",
266 "ageprior",
267 "portdrop",
268 "lendrop",
269 "bagdrop",
270 "policeerr",
271 "drpnona664err",
272 "spcerr",
273 "agedrp",
274 /* High-Level Diagnostic Counters */
275 "n_n664err",
276 "n_vlanerr",
277 "n_unreleased",
278 "n_sizeerr",
279 "n_crcerr",
280 "n_vlnotfound",
281 "n_ctpolerr",
282 "n_polerr",
283 "n_rxfrm",
284 "n_rxbyte",
285 "n_txfrm",
286 "n_txbyte",
287 "n_qfull",
288 "n_part_drop",
289 "n_egr_disabled",
290 "n_not_reach",
291 };
292
293 static char sja1105pqrs_extra_port_stats[][ETH_GSTRING_LEN] = {
294 /* Queue Levels */
295 "qlevel_hwm_0",
296 "qlevel_hwm_1",
297 "qlevel_hwm_2",
298 "qlevel_hwm_3",
299 "qlevel_hwm_4",
300 "qlevel_hwm_5",
301 "qlevel_hwm_6",
302 "qlevel_hwm_7",
303 "qlevel_0",
304 "qlevel_1",
305 "qlevel_2",
306 "qlevel_3",
307 "qlevel_4",
308 "qlevel_5",
309 "qlevel_6",
310 "qlevel_7",
311 };
312
sja1105_get_ethtool_stats(struct dsa_switch * ds,int port,u64 * data)313 void sja1105_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data)
314 {
315 struct sja1105_private *priv = ds->priv;
316 struct sja1105_port_status status;
317 int rc, i, k = 0;
318
319 memset(&status, 0, sizeof(status));
320
321 rc = sja1105_port_status_get(priv, &status, port);
322 if (rc < 0) {
323 dev_err(ds->dev, "Failed to read port %d counters: %d\n",
324 port, rc);
325 return;
326 }
327 memset(data, 0, ARRAY_SIZE(sja1105_port_stats) * sizeof(u64));
328 data[k++] = status.mac.n_runt;
329 data[k++] = status.mac.n_soferr;
330 data[k++] = status.mac.n_alignerr;
331 data[k++] = status.mac.n_miierr;
332 data[k++] = status.mac.typeerr;
333 data[k++] = status.mac.sizeerr;
334 data[k++] = status.mac.tctimeout;
335 data[k++] = status.mac.priorerr;
336 data[k++] = status.mac.nomaster;
337 data[k++] = status.mac.memov;
338 data[k++] = status.mac.memerr;
339 data[k++] = status.mac.invtyp;
340 data[k++] = status.mac.intcyov;
341 data[k++] = status.mac.domerr;
342 data[k++] = status.mac.pcfbagdrop;
343 data[k++] = status.mac.spcprior;
344 data[k++] = status.mac.ageprior;
345 data[k++] = status.mac.portdrop;
346 data[k++] = status.mac.lendrop;
347 data[k++] = status.mac.bagdrop;
348 data[k++] = status.mac.policeerr;
349 data[k++] = status.mac.drpnona664err;
350 data[k++] = status.mac.spcerr;
351 data[k++] = status.mac.agedrp;
352 data[k++] = status.hl1.n_n664err;
353 data[k++] = status.hl1.n_vlanerr;
354 data[k++] = status.hl1.n_unreleased;
355 data[k++] = status.hl1.n_sizeerr;
356 data[k++] = status.hl1.n_crcerr;
357 data[k++] = status.hl1.n_vlnotfound;
358 data[k++] = status.hl1.n_ctpolerr;
359 data[k++] = status.hl1.n_polerr;
360 data[k++] = status.hl1.n_rxfrm;
361 data[k++] = status.hl1.n_rxbyte;
362 data[k++] = status.hl1.n_txfrm;
363 data[k++] = status.hl1.n_txbyte;
364 data[k++] = status.hl2.n_qfull;
365 data[k++] = status.hl2.n_part_drop;
366 data[k++] = status.hl2.n_egr_disabled;
367 data[k++] = status.hl2.n_not_reach;
368
369 if (priv->info->device_id == SJA1105E_DEVICE_ID ||
370 priv->info->device_id == SJA1105T_DEVICE_ID)
371 return;
372
373 memset(data + k, 0, ARRAY_SIZE(sja1105pqrs_extra_port_stats) *
374 sizeof(u64));
375 for (i = 0; i < 8; i++) {
376 data[k++] = status.hl2.qlevel_hwm[i];
377 data[k++] = status.hl2.qlevel[i];
378 }
379 }
380
sja1105_get_strings(struct dsa_switch * ds,int port,u32 stringset,u8 * data)381 void sja1105_get_strings(struct dsa_switch *ds, int port,
382 u32 stringset, u8 *data)
383 {
384 struct sja1105_private *priv = ds->priv;
385 u8 *p = data;
386 int i;
387
388 switch (stringset) {
389 case ETH_SS_STATS:
390 for (i = 0; i < ARRAY_SIZE(sja1105_port_stats); i++) {
391 strlcpy(p, sja1105_port_stats[i], ETH_GSTRING_LEN);
392 p += ETH_GSTRING_LEN;
393 }
394 if (priv->info->device_id == SJA1105E_DEVICE_ID ||
395 priv->info->device_id == SJA1105T_DEVICE_ID)
396 return;
397 for (i = 0; i < ARRAY_SIZE(sja1105pqrs_extra_port_stats); i++) {
398 strlcpy(p, sja1105pqrs_extra_port_stats[i],
399 ETH_GSTRING_LEN);
400 p += ETH_GSTRING_LEN;
401 }
402 break;
403 }
404 }
405
sja1105_get_sset_count(struct dsa_switch * ds,int port,int sset)406 int sja1105_get_sset_count(struct dsa_switch *ds, int port, int sset)
407 {
408 int count = ARRAY_SIZE(sja1105_port_stats);
409 struct sja1105_private *priv = ds->priv;
410
411 if (sset != ETH_SS_STATS)
412 return -EOPNOTSUPP;
413
414 if (priv->info->device_id == SJA1105PR_DEVICE_ID ||
415 priv->info->device_id == SJA1105QS_DEVICE_ID)
416 count += ARRAY_SIZE(sja1105pqrs_extra_port_stats);
417
418 return count;
419 }
420