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
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
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
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
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 
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_xfer_buf(priv, SPI_READ, regs->mac[port], packed_buf,
171 			      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 
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_xfer_buf(priv, SPI_READ, regs->mac_hl1[port], packed_buf,
189 			      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 
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_xfer_buf(priv, SPI_READ, regs->mac_hl2[port], packed_buf,
207 			      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_xfer_buf(priv, SPI_READ, regs->qlevel[port], packed_buf,
219 			      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 
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 
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 
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 
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