1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 // Copyright (c) 2017 Synopsys, Inc. and/or its affiliates.
3 // stmmac Support for 5.xx Ethernet QoS cores
4 
5 #include <linux/bitops.h>
6 #include <linux/iopoll.h>
7 #include "common.h"
8 #include "dwmac4.h"
9 #include "dwmac5.h"
10 #include "stmmac.h"
11 
12 struct dwmac5_error_desc {
13 	bool valid;
14 	const char *desc;
15 	const char *detailed_desc;
16 };
17 
18 #define STAT_OFF(field)		offsetof(struct stmmac_safety_stats, field)
19 
20 static void dwmac5_log_error(struct net_device *ndev, u32 value, bool corr,
21 		const char *module_name, const struct dwmac5_error_desc *desc,
22 		unsigned long field_offset, struct stmmac_safety_stats *stats)
23 {
24 	unsigned long loc, mask;
25 	u8 *bptr = (u8 *)stats;
26 	unsigned long *ptr;
27 
28 	ptr = (unsigned long *)(bptr + field_offset);
29 
30 	mask = value;
31 	for_each_set_bit(loc, &mask, 32) {
32 		netdev_err(ndev, "Found %s error in %s: '%s: %s'\n", corr ?
33 				"correctable" : "uncorrectable", module_name,
34 				desc[loc].desc, desc[loc].detailed_desc);
35 
36 		/* Update counters */
37 		ptr[loc]++;
38 	}
39 }
40 
41 static const struct dwmac5_error_desc dwmac5_mac_errors[32]= {
42 	{ true, "ATPES", "Application Transmit Interface Parity Check Error" },
43 	{ true, "TPES", "TSO Data Path Parity Check Error" },
44 	{ true, "RDPES", "Read Descriptor Parity Check Error" },
45 	{ true, "MPES", "MTL Data Path Parity Check Error" },
46 	{ true, "MTSPES", "MTL TX Status Data Path Parity Check Error" },
47 	{ true, "ARPES", "Application Receive Interface Data Path Parity Check Error" },
48 	{ true, "CWPES", "CSR Write Data Path Parity Check Error" },
49 	{ true, "ASRPES", "AXI Slave Read Data Path Parity Check Error" },
50 	{ true, "TTES", "TX FSM Timeout Error" },
51 	{ true, "RTES", "RX FSM Timeout Error" },
52 	{ true, "CTES", "CSR FSM Timeout Error" },
53 	{ true, "ATES", "APP FSM Timeout Error" },
54 	{ true, "PTES", "PTP FSM Timeout Error" },
55 	{ true, "T125ES", "TX125 FSM Timeout Error" },
56 	{ true, "R125ES", "RX125 FSM Timeout Error" },
57 	{ true, "RVCTES", "REV MDC FSM Timeout Error" },
58 	{ true, "MSTTES", "Master Read/Write Timeout Error" },
59 	{ true, "SLVTES", "Slave Read/Write Timeout Error" },
60 	{ true, "ATITES", "Application Timeout on ATI Interface Error" },
61 	{ true, "ARITES", "Application Timeout on ARI Interface Error" },
62 	{ false, "UNKNOWN", "Unknown Error" }, /* 20 */
63 	{ false, "UNKNOWN", "Unknown Error" }, /* 21 */
64 	{ false, "UNKNOWN", "Unknown Error" }, /* 22 */
65 	{ false, "UNKNOWN", "Unknown Error" }, /* 23 */
66 	{ true, "FSMPES", "FSM State Parity Error" },
67 	{ false, "UNKNOWN", "Unknown Error" }, /* 25 */
68 	{ false, "UNKNOWN", "Unknown Error" }, /* 26 */
69 	{ false, "UNKNOWN", "Unknown Error" }, /* 27 */
70 	{ false, "UNKNOWN", "Unknown Error" }, /* 28 */
71 	{ false, "UNKNOWN", "Unknown Error" }, /* 29 */
72 	{ false, "UNKNOWN", "Unknown Error" }, /* 30 */
73 	{ false, "UNKNOWN", "Unknown Error" }, /* 31 */
74 };
75 
76 static void dwmac5_handle_mac_err(struct net_device *ndev,
77 		void __iomem *ioaddr, bool correctable,
78 		struct stmmac_safety_stats *stats)
79 {
80 	u32 value;
81 
82 	value = readl(ioaddr + MAC_DPP_FSM_INT_STATUS);
83 	writel(value, ioaddr + MAC_DPP_FSM_INT_STATUS);
84 
85 	dwmac5_log_error(ndev, value, correctable, "MAC", dwmac5_mac_errors,
86 			STAT_OFF(mac_errors), stats);
87 }
88 
89 static const struct dwmac5_error_desc dwmac5_mtl_errors[32]= {
90 	{ true, "TXCES", "MTL TX Memory Error" },
91 	{ true, "TXAMS", "MTL TX Memory Address Mismatch Error" },
92 	{ true, "TXUES", "MTL TX Memory Error" },
93 	{ false, "UNKNOWN", "Unknown Error" }, /* 3 */
94 	{ true, "RXCES", "MTL RX Memory Error" },
95 	{ true, "RXAMS", "MTL RX Memory Address Mismatch Error" },
96 	{ true, "RXUES", "MTL RX Memory Error" },
97 	{ false, "UNKNOWN", "Unknown Error" }, /* 7 */
98 	{ true, "ECES", "MTL EST Memory Error" },
99 	{ true, "EAMS", "MTL EST Memory Address Mismatch Error" },
100 	{ true, "EUES", "MTL EST Memory Error" },
101 	{ false, "UNKNOWN", "Unknown Error" }, /* 11 */
102 	{ true, "RPCES", "MTL RX Parser Memory Error" },
103 	{ true, "RPAMS", "MTL RX Parser Memory Address Mismatch Error" },
104 	{ true, "RPUES", "MTL RX Parser Memory Error" },
105 	{ false, "UNKNOWN", "Unknown Error" }, /* 15 */
106 	{ false, "UNKNOWN", "Unknown Error" }, /* 16 */
107 	{ false, "UNKNOWN", "Unknown Error" }, /* 17 */
108 	{ false, "UNKNOWN", "Unknown Error" }, /* 18 */
109 	{ false, "UNKNOWN", "Unknown Error" }, /* 19 */
110 	{ false, "UNKNOWN", "Unknown Error" }, /* 20 */
111 	{ false, "UNKNOWN", "Unknown Error" }, /* 21 */
112 	{ false, "UNKNOWN", "Unknown Error" }, /* 22 */
113 	{ false, "UNKNOWN", "Unknown Error" }, /* 23 */
114 	{ false, "UNKNOWN", "Unknown Error" }, /* 24 */
115 	{ false, "UNKNOWN", "Unknown Error" }, /* 25 */
116 	{ false, "UNKNOWN", "Unknown Error" }, /* 26 */
117 	{ false, "UNKNOWN", "Unknown Error" }, /* 27 */
118 	{ false, "UNKNOWN", "Unknown Error" }, /* 28 */
119 	{ false, "UNKNOWN", "Unknown Error" }, /* 29 */
120 	{ false, "UNKNOWN", "Unknown Error" }, /* 30 */
121 	{ false, "UNKNOWN", "Unknown Error" }, /* 31 */
122 };
123 
124 static void dwmac5_handle_mtl_err(struct net_device *ndev,
125 		void __iomem *ioaddr, bool correctable,
126 		struct stmmac_safety_stats *stats)
127 {
128 	u32 value;
129 
130 	value = readl(ioaddr + MTL_ECC_INT_STATUS);
131 	writel(value, ioaddr + MTL_ECC_INT_STATUS);
132 
133 	dwmac5_log_error(ndev, value, correctable, "MTL", dwmac5_mtl_errors,
134 			STAT_OFF(mtl_errors), stats);
135 }
136 
137 static const struct dwmac5_error_desc dwmac5_dma_errors[32]= {
138 	{ true, "TCES", "DMA TSO Memory Error" },
139 	{ true, "TAMS", "DMA TSO Memory Address Mismatch Error" },
140 	{ true, "TUES", "DMA TSO Memory Error" },
141 	{ false, "UNKNOWN", "Unknown Error" }, /* 3 */
142 	{ false, "UNKNOWN", "Unknown Error" }, /* 4 */
143 	{ false, "UNKNOWN", "Unknown Error" }, /* 5 */
144 	{ false, "UNKNOWN", "Unknown Error" }, /* 6 */
145 	{ false, "UNKNOWN", "Unknown Error" }, /* 7 */
146 	{ false, "UNKNOWN", "Unknown Error" }, /* 8 */
147 	{ false, "UNKNOWN", "Unknown Error" }, /* 9 */
148 	{ false, "UNKNOWN", "Unknown Error" }, /* 10 */
149 	{ false, "UNKNOWN", "Unknown Error" }, /* 11 */
150 	{ false, "UNKNOWN", "Unknown Error" }, /* 12 */
151 	{ false, "UNKNOWN", "Unknown Error" }, /* 13 */
152 	{ false, "UNKNOWN", "Unknown Error" }, /* 14 */
153 	{ false, "UNKNOWN", "Unknown Error" }, /* 15 */
154 	{ false, "UNKNOWN", "Unknown Error" }, /* 16 */
155 	{ false, "UNKNOWN", "Unknown Error" }, /* 17 */
156 	{ false, "UNKNOWN", "Unknown Error" }, /* 18 */
157 	{ false, "UNKNOWN", "Unknown Error" }, /* 19 */
158 	{ false, "UNKNOWN", "Unknown Error" }, /* 20 */
159 	{ false, "UNKNOWN", "Unknown Error" }, /* 21 */
160 	{ false, "UNKNOWN", "Unknown Error" }, /* 22 */
161 	{ false, "UNKNOWN", "Unknown Error" }, /* 23 */
162 	{ false, "UNKNOWN", "Unknown Error" }, /* 24 */
163 	{ false, "UNKNOWN", "Unknown Error" }, /* 25 */
164 	{ false, "UNKNOWN", "Unknown Error" }, /* 26 */
165 	{ false, "UNKNOWN", "Unknown Error" }, /* 27 */
166 	{ false, "UNKNOWN", "Unknown Error" }, /* 28 */
167 	{ false, "UNKNOWN", "Unknown Error" }, /* 29 */
168 	{ false, "UNKNOWN", "Unknown Error" }, /* 30 */
169 	{ false, "UNKNOWN", "Unknown Error" }, /* 31 */
170 };
171 
172 static void dwmac5_handle_dma_err(struct net_device *ndev,
173 		void __iomem *ioaddr, bool correctable,
174 		struct stmmac_safety_stats *stats)
175 {
176 	u32 value;
177 
178 	value = readl(ioaddr + DMA_ECC_INT_STATUS);
179 	writel(value, ioaddr + DMA_ECC_INT_STATUS);
180 
181 	dwmac5_log_error(ndev, value, correctable, "DMA", dwmac5_dma_errors,
182 			STAT_OFF(dma_errors), stats);
183 }
184 
185 int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp)
186 {
187 	u32 value;
188 
189 	if (!asp)
190 		return -EINVAL;
191 
192 	/* 1. Enable Safety Features */
193 	value = readl(ioaddr + MTL_ECC_CONTROL);
194 	value |= TSOEE; /* TSO ECC */
195 	value |= MRXPEE; /* MTL RX Parser ECC */
196 	value |= MESTEE; /* MTL EST ECC */
197 	value |= MRXEE; /* MTL RX FIFO ECC */
198 	value |= MTXEE; /* MTL TX FIFO ECC */
199 	writel(value, ioaddr + MTL_ECC_CONTROL);
200 
201 	/* 2. Enable MTL Safety Interrupts */
202 	value = readl(ioaddr + MTL_ECC_INT_ENABLE);
203 	value |= RPCEIE; /* RX Parser Memory Correctable Error */
204 	value |= ECEIE; /* EST Memory Correctable Error */
205 	value |= RXCEIE; /* RX Memory Correctable Error */
206 	value |= TXCEIE; /* TX Memory Correctable Error */
207 	writel(value, ioaddr + MTL_ECC_INT_ENABLE);
208 
209 	/* 3. Enable DMA Safety Interrupts */
210 	value = readl(ioaddr + DMA_ECC_INT_ENABLE);
211 	value |= TCEIE; /* TSO Memory Correctable Error */
212 	writel(value, ioaddr + DMA_ECC_INT_ENABLE);
213 
214 	/* Only ECC Protection for External Memory feature is selected */
215 	if (asp <= 0x1)
216 		return 0;
217 
218 	/* 5. Enable Parity and Timeout for FSM */
219 	value = readl(ioaddr + MAC_FSM_CONTROL);
220 	value |= PRTYEN; /* FSM Parity Feature */
221 	value |= TMOUTEN; /* FSM Timeout Feature */
222 	writel(value, ioaddr + MAC_FSM_CONTROL);
223 
224 	/* 4. Enable Data Parity Protection */
225 	value = readl(ioaddr + MTL_DPP_CONTROL);
226 	value |= EDPP;
227 	writel(value, ioaddr + MTL_DPP_CONTROL);
228 
229 	/*
230 	 * All the Automotive Safety features are selected without the "Parity
231 	 * Port Enable for external interface" feature.
232 	 */
233 	if (asp <= 0x2)
234 		return 0;
235 
236 	value |= EPSI;
237 	writel(value, ioaddr + MTL_DPP_CONTROL);
238 	return 0;
239 }
240 
241 int dwmac5_safety_feat_irq_status(struct net_device *ndev,
242 		void __iomem *ioaddr, unsigned int asp,
243 		struct stmmac_safety_stats *stats)
244 {
245 	bool err, corr;
246 	u32 mtl, dma;
247 	int ret = 0;
248 
249 	if (!asp)
250 		return -EINVAL;
251 
252 	mtl = readl(ioaddr + MTL_SAFETY_INT_STATUS);
253 	dma = readl(ioaddr + DMA_SAFETY_INT_STATUS);
254 
255 	err = (mtl & MCSIS) || (dma & MCSIS);
256 	corr = false;
257 	if (err) {
258 		dwmac5_handle_mac_err(ndev, ioaddr, corr, stats);
259 		ret |= !corr;
260 	}
261 
262 	err = (mtl & (MEUIS | MECIS)) || (dma & (MSUIS | MSCIS));
263 	corr = (mtl & MECIS) || (dma & MSCIS);
264 	if (err) {
265 		dwmac5_handle_mtl_err(ndev, ioaddr, corr, stats);
266 		ret |= !corr;
267 	}
268 
269 	err = dma & (DEUIS | DECIS);
270 	corr = dma & DECIS;
271 	if (err) {
272 		dwmac5_handle_dma_err(ndev, ioaddr, corr, stats);
273 		ret |= !corr;
274 	}
275 
276 	return ret;
277 }
278 
279 static const struct dwmac5_error {
280 	const struct dwmac5_error_desc *desc;
281 } dwmac5_all_errors[] = {
282 	{ dwmac5_mac_errors },
283 	{ dwmac5_mtl_errors },
284 	{ dwmac5_dma_errors },
285 };
286 
287 int dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats,
288 			int index, unsigned long *count, const char **desc)
289 {
290 	int module = index / 32, offset = index % 32;
291 	unsigned long *ptr = (unsigned long *)stats;
292 
293 	if (module >= ARRAY_SIZE(dwmac5_all_errors))
294 		return -EINVAL;
295 	if (!dwmac5_all_errors[module].desc[offset].valid)
296 		return -EINVAL;
297 	if (count)
298 		*count = *(ptr + index);
299 	if (desc)
300 		*desc = dwmac5_all_errors[module].desc[offset].desc;
301 	return 0;
302 }
303 
304 static int dwmac5_rxp_disable(void __iomem *ioaddr)
305 {
306 	u32 val;
307 	int ret;
308 
309 	val = readl(ioaddr + MTL_OPERATION_MODE);
310 	val &= ~MTL_FRPE;
311 	writel(val, ioaddr + MTL_OPERATION_MODE);
312 
313 	ret = readl_poll_timeout(ioaddr + MTL_RXP_CONTROL_STATUS, val,
314 			val & RXPI, 1, 10000);
315 	if (ret)
316 		return ret;
317 	return 0;
318 }
319 
320 static void dwmac5_rxp_enable(void __iomem *ioaddr)
321 {
322 	u32 val;
323 
324 	val = readl(ioaddr + MTL_OPERATION_MODE);
325 	val |= MTL_FRPE;
326 	writel(val, ioaddr + MTL_OPERATION_MODE);
327 }
328 
329 static int dwmac5_rxp_update_single_entry(void __iomem *ioaddr,
330 					  struct stmmac_tc_entry *entry,
331 					  int pos)
332 {
333 	int ret, i;
334 
335 	for (i = 0; i < (sizeof(entry->val) / sizeof(u32)); i++) {
336 		int real_pos = pos * (sizeof(entry->val) / sizeof(u32)) + i;
337 		u32 val;
338 
339 		/* Wait for ready */
340 		ret = readl_poll_timeout(ioaddr + MTL_RXP_IACC_CTRL_STATUS,
341 				val, !(val & STARTBUSY), 1, 10000);
342 		if (ret)
343 			return ret;
344 
345 		/* Write data */
346 		val = *((u32 *)&entry->val + i);
347 		writel(val, ioaddr + MTL_RXP_IACC_DATA);
348 
349 		/* Write pos */
350 		val = real_pos & ADDR;
351 		writel(val, ioaddr + MTL_RXP_IACC_CTRL_STATUS);
352 
353 		/* Write OP */
354 		val |= WRRDN;
355 		writel(val, ioaddr + MTL_RXP_IACC_CTRL_STATUS);
356 
357 		/* Start Write */
358 		val |= STARTBUSY;
359 		writel(val, ioaddr + MTL_RXP_IACC_CTRL_STATUS);
360 
361 		/* Wait for done */
362 		ret = readl_poll_timeout(ioaddr + MTL_RXP_IACC_CTRL_STATUS,
363 				val, !(val & STARTBUSY), 1, 10000);
364 		if (ret)
365 			return ret;
366 	}
367 
368 	return 0;
369 }
370 
371 static struct stmmac_tc_entry *
372 dwmac5_rxp_get_next_entry(struct stmmac_tc_entry *entries, unsigned int count,
373 			  u32 curr_prio)
374 {
375 	struct stmmac_tc_entry *entry;
376 	u32 min_prio = ~0x0;
377 	int i, min_prio_idx;
378 	bool found = false;
379 
380 	for (i = count - 1; i >= 0; i--) {
381 		entry = &entries[i];
382 
383 		/* Do not update unused entries */
384 		if (!entry->in_use)
385 			continue;
386 		/* Do not update already updated entries (i.e. fragments) */
387 		if (entry->in_hw)
388 			continue;
389 		/* Let last entry be updated last */
390 		if (entry->is_last)
391 			continue;
392 		/* Do not return fragments */
393 		if (entry->is_frag)
394 			continue;
395 		/* Check if we already checked this prio */
396 		if (entry->prio < curr_prio)
397 			continue;
398 		/* Check if this is the minimum prio */
399 		if (entry->prio < min_prio) {
400 			min_prio = entry->prio;
401 			min_prio_idx = i;
402 			found = true;
403 		}
404 	}
405 
406 	if (found)
407 		return &entries[min_prio_idx];
408 	return NULL;
409 }
410 
411 int dwmac5_rxp_config(void __iomem *ioaddr, struct stmmac_tc_entry *entries,
412 		      unsigned int count)
413 {
414 	struct stmmac_tc_entry *entry, *frag;
415 	int i, ret, nve = 0;
416 	u32 curr_prio = 0;
417 	u32 old_val, val;
418 
419 	/* Force disable RX */
420 	old_val = readl(ioaddr + GMAC_CONFIG);
421 	val = old_val & ~GMAC_CONFIG_RE;
422 	writel(val, ioaddr + GMAC_CONFIG);
423 
424 	/* Disable RX Parser */
425 	ret = dwmac5_rxp_disable(ioaddr);
426 	if (ret)
427 		goto re_enable;
428 
429 	/* Set all entries as NOT in HW */
430 	for (i = 0; i < count; i++) {
431 		entry = &entries[i];
432 		entry->in_hw = false;
433 	}
434 
435 	/* Update entries by reverse order */
436 	while (1) {
437 		entry = dwmac5_rxp_get_next_entry(entries, count, curr_prio);
438 		if (!entry)
439 			break;
440 
441 		curr_prio = entry->prio;
442 		frag = entry->frag_ptr;
443 
444 		/* Set special fragment requirements */
445 		if (frag) {
446 			entry->val.af = 0;
447 			entry->val.rf = 0;
448 			entry->val.nc = 1;
449 			entry->val.ok_index = nve + 2;
450 		}
451 
452 		ret = dwmac5_rxp_update_single_entry(ioaddr, entry, nve);
453 		if (ret)
454 			goto re_enable;
455 
456 		entry->table_pos = nve++;
457 		entry->in_hw = true;
458 
459 		if (frag && !frag->in_hw) {
460 			ret = dwmac5_rxp_update_single_entry(ioaddr, frag, nve);
461 			if (ret)
462 				goto re_enable;
463 			frag->table_pos = nve++;
464 			frag->in_hw = true;
465 		}
466 	}
467 
468 	if (!nve)
469 		goto re_enable;
470 
471 	/* Update all pass entry */
472 	for (i = 0; i < count; i++) {
473 		entry = &entries[i];
474 		if (!entry->is_last)
475 			continue;
476 
477 		ret = dwmac5_rxp_update_single_entry(ioaddr, entry, nve);
478 		if (ret)
479 			goto re_enable;
480 
481 		entry->table_pos = nve++;
482 	}
483 
484 	/* Assume n. of parsable entries == n. of valid entries */
485 	val = (nve << 16) & NPE;
486 	val |= nve & NVE;
487 	writel(val, ioaddr + MTL_RXP_CONTROL_STATUS);
488 
489 	/* Enable RX Parser */
490 	dwmac5_rxp_enable(ioaddr);
491 
492 re_enable:
493 	/* Re-enable RX */
494 	writel(old_val, ioaddr + GMAC_CONFIG);
495 	return ret;
496 }
497