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
5 #include <linux/bitops.h>
6 #include <linux/iopoll.h>
11 #include "stmmac_ptp.h"
13 struct dwmac5_error_desc
{
16 const char *detailed_desc
;
19 #define STAT_OFF(field) offsetof(struct stmmac_safety_stats, field)
21 static void dwmac5_log_error(struct net_device
*ndev
, u32 value
, bool corr
,
22 const char *module_name
, const struct dwmac5_error_desc
*desc
,
23 unsigned long field_offset
, struct stmmac_safety_stats
*stats
)
25 unsigned long loc
, mask
;
26 u8
*bptr
= (u8
*)stats
;
29 ptr
= (unsigned long *)(bptr
+ field_offset
);
32 for_each_set_bit(loc
, &mask
, 32) {
33 netdev_err(ndev
, "Found %s error in %s: '%s: %s'\n", corr
?
34 "correctable" : "uncorrectable", module_name
,
35 desc
[loc
].desc
, desc
[loc
].detailed_desc
);
42 static const struct dwmac5_error_desc dwmac5_mac_errors
[32]= {
43 { true, "ATPES", "Application Transmit Interface Parity Check Error" },
44 { true, "TPES", "TSO Data Path Parity Check Error" },
45 { true, "RDPES", "Read Descriptor Parity Check Error" },
46 { true, "MPES", "MTL Data Path Parity Check Error" },
47 { true, "MTSPES", "MTL TX Status Data Path Parity Check Error" },
48 { true, "ARPES", "Application Receive Interface Data Path Parity Check Error" },
49 { true, "CWPES", "CSR Write Data Path Parity Check Error" },
50 { true, "ASRPES", "AXI Slave Read Data Path Parity Check Error" },
51 { true, "TTES", "TX FSM Timeout Error" },
52 { true, "RTES", "RX FSM Timeout Error" },
53 { true, "CTES", "CSR FSM Timeout Error" },
54 { true, "ATES", "APP FSM Timeout Error" },
55 { true, "PTES", "PTP FSM Timeout Error" },
56 { true, "T125ES", "TX125 FSM Timeout Error" },
57 { true, "R125ES", "RX125 FSM Timeout Error" },
58 { true, "RVCTES", "REV MDC FSM Timeout Error" },
59 { true, "MSTTES", "Master Read/Write Timeout Error" },
60 { true, "SLVTES", "Slave Read/Write Timeout Error" },
61 { true, "ATITES", "Application Timeout on ATI Interface Error" },
62 { true, "ARITES", "Application Timeout on ARI Interface Error" },
63 { false, "UNKNOWN", "Unknown Error" }, /* 20 */
64 { false, "UNKNOWN", "Unknown Error" }, /* 21 */
65 { false, "UNKNOWN", "Unknown Error" }, /* 22 */
66 { false, "UNKNOWN", "Unknown Error" }, /* 23 */
67 { true, "FSMPES", "FSM State Parity Error" },
68 { false, "UNKNOWN", "Unknown Error" }, /* 25 */
69 { false, "UNKNOWN", "Unknown Error" }, /* 26 */
70 { false, "UNKNOWN", "Unknown Error" }, /* 27 */
71 { false, "UNKNOWN", "Unknown Error" }, /* 28 */
72 { false, "UNKNOWN", "Unknown Error" }, /* 29 */
73 { false, "UNKNOWN", "Unknown Error" }, /* 30 */
74 { false, "UNKNOWN", "Unknown Error" }, /* 31 */
77 static void dwmac5_handle_mac_err(struct net_device
*ndev
,
78 void __iomem
*ioaddr
, bool correctable
,
79 struct stmmac_safety_stats
*stats
)
83 value
= readl(ioaddr
+ MAC_DPP_FSM_INT_STATUS
);
84 writel(value
, ioaddr
+ MAC_DPP_FSM_INT_STATUS
);
86 dwmac5_log_error(ndev
, value
, correctable
, "MAC", dwmac5_mac_errors
,
87 STAT_OFF(mac_errors
), stats
);
90 static const struct dwmac5_error_desc dwmac5_mtl_errors
[32]= {
91 { true, "TXCES", "MTL TX Memory Error" },
92 { true, "TXAMS", "MTL TX Memory Address Mismatch Error" },
93 { true, "TXUES", "MTL TX Memory Error" },
94 { false, "UNKNOWN", "Unknown Error" }, /* 3 */
95 { true, "RXCES", "MTL RX Memory Error" },
96 { true, "RXAMS", "MTL RX Memory Address Mismatch Error" },
97 { true, "RXUES", "MTL RX Memory Error" },
98 { false, "UNKNOWN", "Unknown Error" }, /* 7 */
99 { true, "ECES", "MTL EST Memory Error" },
100 { true, "EAMS", "MTL EST Memory Address Mismatch Error" },
101 { true, "EUES", "MTL EST Memory Error" },
102 { false, "UNKNOWN", "Unknown Error" }, /* 11 */
103 { true, "RPCES", "MTL RX Parser Memory Error" },
104 { true, "RPAMS", "MTL RX Parser Memory Address Mismatch Error" },
105 { true, "RPUES", "MTL RX Parser Memory Error" },
106 { false, "UNKNOWN", "Unknown Error" }, /* 15 */
107 { false, "UNKNOWN", "Unknown Error" }, /* 16 */
108 { false, "UNKNOWN", "Unknown Error" }, /* 17 */
109 { false, "UNKNOWN", "Unknown Error" }, /* 18 */
110 { false, "UNKNOWN", "Unknown Error" }, /* 19 */
111 { false, "UNKNOWN", "Unknown Error" }, /* 20 */
112 { false, "UNKNOWN", "Unknown Error" }, /* 21 */
113 { false, "UNKNOWN", "Unknown Error" }, /* 22 */
114 { false, "UNKNOWN", "Unknown Error" }, /* 23 */
115 { false, "UNKNOWN", "Unknown Error" }, /* 24 */
116 { false, "UNKNOWN", "Unknown Error" }, /* 25 */
117 { false, "UNKNOWN", "Unknown Error" }, /* 26 */
118 { false, "UNKNOWN", "Unknown Error" }, /* 27 */
119 { false, "UNKNOWN", "Unknown Error" }, /* 28 */
120 { false, "UNKNOWN", "Unknown Error" }, /* 29 */
121 { false, "UNKNOWN", "Unknown Error" }, /* 30 */
122 { false, "UNKNOWN", "Unknown Error" }, /* 31 */
125 static void dwmac5_handle_mtl_err(struct net_device
*ndev
,
126 void __iomem
*ioaddr
, bool correctable
,
127 struct stmmac_safety_stats
*stats
)
131 value
= readl(ioaddr
+ MTL_ECC_INT_STATUS
);
132 writel(value
, ioaddr
+ MTL_ECC_INT_STATUS
);
134 dwmac5_log_error(ndev
, value
, correctable
, "MTL", dwmac5_mtl_errors
,
135 STAT_OFF(mtl_errors
), stats
);
138 static const struct dwmac5_error_desc dwmac5_dma_errors
[32]= {
139 { true, "TCES", "DMA TSO Memory Error" },
140 { true, "TAMS", "DMA TSO Memory Address Mismatch Error" },
141 { true, "TUES", "DMA TSO Memory Error" },
142 { false, "UNKNOWN", "Unknown Error" }, /* 3 */
143 { false, "UNKNOWN", "Unknown Error" }, /* 4 */
144 { false, "UNKNOWN", "Unknown Error" }, /* 5 */
145 { false, "UNKNOWN", "Unknown Error" }, /* 6 */
146 { false, "UNKNOWN", "Unknown Error" }, /* 7 */
147 { false, "UNKNOWN", "Unknown Error" }, /* 8 */
148 { false, "UNKNOWN", "Unknown Error" }, /* 9 */
149 { false, "UNKNOWN", "Unknown Error" }, /* 10 */
150 { false, "UNKNOWN", "Unknown Error" }, /* 11 */
151 { false, "UNKNOWN", "Unknown Error" }, /* 12 */
152 { false, "UNKNOWN", "Unknown Error" }, /* 13 */
153 { false, "UNKNOWN", "Unknown Error" }, /* 14 */
154 { false, "UNKNOWN", "Unknown Error" }, /* 15 */
155 { false, "UNKNOWN", "Unknown Error" }, /* 16 */
156 { false, "UNKNOWN", "Unknown Error" }, /* 17 */
157 { false, "UNKNOWN", "Unknown Error" }, /* 18 */
158 { false, "UNKNOWN", "Unknown Error" }, /* 19 */
159 { false, "UNKNOWN", "Unknown Error" }, /* 20 */
160 { false, "UNKNOWN", "Unknown Error" }, /* 21 */
161 { false, "UNKNOWN", "Unknown Error" }, /* 22 */
162 { false, "UNKNOWN", "Unknown Error" }, /* 23 */
163 { false, "UNKNOWN", "Unknown Error" }, /* 24 */
164 { false, "UNKNOWN", "Unknown Error" }, /* 25 */
165 { false, "UNKNOWN", "Unknown Error" }, /* 26 */
166 { false, "UNKNOWN", "Unknown Error" }, /* 27 */
167 { false, "UNKNOWN", "Unknown Error" }, /* 28 */
168 { false, "UNKNOWN", "Unknown Error" }, /* 29 */
169 { false, "UNKNOWN", "Unknown Error" }, /* 30 */
170 { false, "UNKNOWN", "Unknown Error" }, /* 31 */
173 static void dwmac5_handle_dma_err(struct net_device
*ndev
,
174 void __iomem
*ioaddr
, bool correctable
,
175 struct stmmac_safety_stats
*stats
)
179 value
= readl(ioaddr
+ DMA_ECC_INT_STATUS
);
180 writel(value
, ioaddr
+ DMA_ECC_INT_STATUS
);
182 dwmac5_log_error(ndev
, value
, correctable
, "DMA", dwmac5_dma_errors
,
183 STAT_OFF(dma_errors
), stats
);
186 int dwmac5_safety_feat_config(void __iomem
*ioaddr
, unsigned int asp
)
193 /* 1. Enable Safety Features */
194 value
= readl(ioaddr
+ MTL_ECC_CONTROL
);
195 value
|= TSOEE
; /* TSO ECC */
196 value
|= MRXPEE
; /* MTL RX Parser ECC */
197 value
|= MESTEE
; /* MTL EST ECC */
198 value
|= MRXEE
; /* MTL RX FIFO ECC */
199 value
|= MTXEE
; /* MTL TX FIFO ECC */
200 writel(value
, ioaddr
+ MTL_ECC_CONTROL
);
202 /* 2. Enable MTL Safety Interrupts */
203 value
= readl(ioaddr
+ MTL_ECC_INT_ENABLE
);
204 value
|= RPCEIE
; /* RX Parser Memory Correctable Error */
205 value
|= ECEIE
; /* EST Memory Correctable Error */
206 value
|= RXCEIE
; /* RX Memory Correctable Error */
207 value
|= TXCEIE
; /* TX Memory Correctable Error */
208 writel(value
, ioaddr
+ MTL_ECC_INT_ENABLE
);
210 /* 3. Enable DMA Safety Interrupts */
211 value
= readl(ioaddr
+ DMA_ECC_INT_ENABLE
);
212 value
|= TCEIE
; /* TSO Memory Correctable Error */
213 writel(value
, ioaddr
+ DMA_ECC_INT_ENABLE
);
215 /* Only ECC Protection for External Memory feature is selected */
219 /* 5. Enable Parity and Timeout for FSM */
220 value
= readl(ioaddr
+ MAC_FSM_CONTROL
);
221 value
|= PRTYEN
; /* FSM Parity Feature */
222 value
|= TMOUTEN
; /* FSM Timeout Feature */
223 writel(value
, ioaddr
+ MAC_FSM_CONTROL
);
225 /* 4. Enable Data Parity Protection */
226 value
= readl(ioaddr
+ MTL_DPP_CONTROL
);
228 writel(value
, ioaddr
+ MTL_DPP_CONTROL
);
231 * All the Automotive Safety features are selected without the "Parity
232 * Port Enable for external interface" feature.
238 writel(value
, ioaddr
+ MTL_DPP_CONTROL
);
242 int dwmac5_safety_feat_irq_status(struct net_device
*ndev
,
243 void __iomem
*ioaddr
, unsigned int asp
,
244 struct stmmac_safety_stats
*stats
)
253 mtl
= readl(ioaddr
+ MTL_SAFETY_INT_STATUS
);
254 dma
= readl(ioaddr
+ DMA_SAFETY_INT_STATUS
);
256 err
= (mtl
& MCSIS
) || (dma
& MCSIS
);
259 dwmac5_handle_mac_err(ndev
, ioaddr
, corr
, stats
);
263 err
= (mtl
& (MEUIS
| MECIS
)) || (dma
& (MSUIS
| MSCIS
));
264 corr
= (mtl
& MECIS
) || (dma
& MSCIS
);
266 dwmac5_handle_mtl_err(ndev
, ioaddr
, corr
, stats
);
270 err
= dma
& (DEUIS
| DECIS
);
273 dwmac5_handle_dma_err(ndev
, ioaddr
, corr
, stats
);
280 static const struct dwmac5_error
{
281 const struct dwmac5_error_desc
*desc
;
282 } dwmac5_all_errors
[] = {
283 { dwmac5_mac_errors
},
284 { dwmac5_mtl_errors
},
285 { dwmac5_dma_errors
},
288 int dwmac5_safety_feat_dump(struct stmmac_safety_stats
*stats
,
289 int index
, unsigned long *count
, const char **desc
)
291 int module
= index
/ 32, offset
= index
% 32;
292 unsigned long *ptr
= (unsigned long *)stats
;
294 if (module
>= ARRAY_SIZE(dwmac5_all_errors
))
296 if (!dwmac5_all_errors
[module
].desc
[offset
].valid
)
299 *count
= *(ptr
+ index
);
301 *desc
= dwmac5_all_errors
[module
].desc
[offset
].desc
;
305 static int dwmac5_rxp_disable(void __iomem
*ioaddr
)
309 val
= readl(ioaddr
+ MTL_OPERATION_MODE
);
311 writel(val
, ioaddr
+ MTL_OPERATION_MODE
);
313 return readl_poll_timeout(ioaddr
+ MTL_RXP_CONTROL_STATUS
, val
,
314 val
& RXPI
, 1, 10000);
317 static void dwmac5_rxp_enable(void __iomem
*ioaddr
)
321 val
= readl(ioaddr
+ MTL_OPERATION_MODE
);
323 writel(val
, ioaddr
+ MTL_OPERATION_MODE
);
326 static int dwmac5_rxp_update_single_entry(void __iomem
*ioaddr
,
327 struct stmmac_tc_entry
*entry
,
332 for (i
= 0; i
< (sizeof(entry
->val
) / sizeof(u32
)); i
++) {
333 int real_pos
= pos
* (sizeof(entry
->val
) / sizeof(u32
)) + i
;
337 ret
= readl_poll_timeout(ioaddr
+ MTL_RXP_IACC_CTRL_STATUS
,
338 val
, !(val
& STARTBUSY
), 1, 10000);
343 val
= *((u32
*)&entry
->val
+ i
);
344 writel(val
, ioaddr
+ MTL_RXP_IACC_DATA
);
347 val
= real_pos
& ADDR
;
348 writel(val
, ioaddr
+ MTL_RXP_IACC_CTRL_STATUS
);
352 writel(val
, ioaddr
+ MTL_RXP_IACC_CTRL_STATUS
);
356 writel(val
, ioaddr
+ MTL_RXP_IACC_CTRL_STATUS
);
359 ret
= readl_poll_timeout(ioaddr
+ MTL_RXP_IACC_CTRL_STATUS
,
360 val
, !(val
& STARTBUSY
), 1, 10000);
368 static struct stmmac_tc_entry
*
369 dwmac5_rxp_get_next_entry(struct stmmac_tc_entry
*entries
, unsigned int count
,
372 struct stmmac_tc_entry
*entry
;
377 for (i
= count
- 1; i
>= 0; i
--) {
380 /* Do not update unused entries */
383 /* Do not update already updated entries (i.e. fragments) */
386 /* Let last entry be updated last */
389 /* Do not return fragments */
392 /* Check if we already checked this prio */
393 if (entry
->prio
< curr_prio
)
395 /* Check if this is the minimum prio */
396 if (entry
->prio
< min_prio
) {
397 min_prio
= entry
->prio
;
404 return &entries
[min_prio_idx
];
408 int dwmac5_rxp_config(void __iomem
*ioaddr
, struct stmmac_tc_entry
*entries
,
411 struct stmmac_tc_entry
*entry
, *frag
;
416 /* Force disable RX */
417 old_val
= readl(ioaddr
+ GMAC_CONFIG
);
418 val
= old_val
& ~GMAC_CONFIG_RE
;
419 writel(val
, ioaddr
+ GMAC_CONFIG
);
421 /* Disable RX Parser */
422 ret
= dwmac5_rxp_disable(ioaddr
);
426 /* Set all entries as NOT in HW */
427 for (i
= 0; i
< count
; i
++) {
429 entry
->in_hw
= false;
432 /* Update entries by reverse order */
434 entry
= dwmac5_rxp_get_next_entry(entries
, count
, curr_prio
);
438 curr_prio
= entry
->prio
;
439 frag
= entry
->frag_ptr
;
441 /* Set special fragment requirements */
446 entry
->val
.ok_index
= nve
+ 2;
449 ret
= dwmac5_rxp_update_single_entry(ioaddr
, entry
, nve
);
453 entry
->table_pos
= nve
++;
456 if (frag
&& !frag
->in_hw
) {
457 ret
= dwmac5_rxp_update_single_entry(ioaddr
, frag
, nve
);
460 frag
->table_pos
= nve
++;
468 /* Update all pass entry */
469 for (i
= 0; i
< count
; i
++) {
474 ret
= dwmac5_rxp_update_single_entry(ioaddr
, entry
, nve
);
478 entry
->table_pos
= nve
++;
481 /* Assume n. of parsable entries == n. of valid entries */
482 val
= (nve
<< 16) & NPE
;
484 writel(val
, ioaddr
+ MTL_RXP_CONTROL_STATUS
);
486 /* Enable RX Parser */
487 dwmac5_rxp_enable(ioaddr
);
491 writel(old_val
, ioaddr
+ GMAC_CONFIG
);
495 int dwmac5_flex_pps_config(void __iomem
*ioaddr
, int index
,
496 struct stmmac_pps_cfg
*cfg
, bool enable
,
497 u32 sub_second_inc
, u32 systime_flags
)
499 u32 tnsec
= readl(ioaddr
+ MAC_PPSx_TARGET_TIME_NSEC(index
));
500 u32 val
= readl(ioaddr
+ MAC_PPS_CONTROL
);
505 if (tnsec
& TRGTBUSY0
)
507 if (!sub_second_inc
|| !systime_flags
)
510 val
&= ~PPSx_MASK(index
);
513 val
|= PPSCMDx(index
, 0x5);
515 writel(val
, ioaddr
+ MAC_PPS_CONTROL
);
519 val
|= PPSCMDx(index
, 0x2);
520 val
|= TRGTMODSELx(index
, 0x2);
523 writel(cfg
->start
.tv_sec
, ioaddr
+ MAC_PPSx_TARGET_TIME_SEC(index
));
525 if (!(systime_flags
& PTP_TCR_TSCTRLSSR
))
526 cfg
->start
.tv_nsec
= (cfg
->start
.tv_nsec
* 1000) / 465;
527 writel(cfg
->start
.tv_nsec
, ioaddr
+ MAC_PPSx_TARGET_TIME_NSEC(index
));
529 period
= cfg
->period
.tv_sec
* 1000000000;
530 period
+= cfg
->period
.tv_nsec
;
532 do_div(period
, sub_second_inc
);
537 writel(period
- 1, ioaddr
+ MAC_PPSx_INTERVAL(index
));
543 writel(period
- 1, ioaddr
+ MAC_PPSx_WIDTH(index
));
545 /* Finally, activate it */
546 writel(val
, ioaddr
+ MAC_PPS_CONTROL
);
550 static int dwmac5_est_write(void __iomem
*ioaddr
, u32 reg
, u32 val
, bool gcl
)
554 writel(val
, ioaddr
+ MTL_EST_GCL_DATA
);
556 ctrl
= (reg
<< ADDR_SHIFT
);
557 ctrl
|= gcl
? 0 : GCRR
;
559 writel(ctrl
, ioaddr
+ MTL_EST_GCL_CONTROL
);
562 writel(ctrl
, ioaddr
+ MTL_EST_GCL_CONTROL
);
564 return readl_poll_timeout(ioaddr
+ MTL_EST_GCL_CONTROL
,
565 ctrl
, !(ctrl
& SRWO
), 100, 5000);
568 int dwmac5_est_configure(void __iomem
*ioaddr
, struct stmmac_est
*cfg
,
569 unsigned int ptp_rate
)
571 u32 speed
, total_offset
, offset
, ctrl
, ctr_low
;
572 u32 extcfg
= readl(ioaddr
+ GMAC_EXT_CONFIG
);
573 u32 mac_cfg
= readl(ioaddr
+ GMAC_CONFIG
);
577 if (extcfg
& GMAC_CONFIG_EIPG_EN
) {
578 offset
= (extcfg
& GMAC_CONFIG_EIPG
) >> GMAC_CONFIG_EIPG_SHIFT
;
579 offset
= 104 + (offset
* 8);
581 offset
= (mac_cfg
& GMAC_CONFIG_IPG
) >> GMAC_CONFIG_IPG_SHIFT
;
582 offset
= 96 - (offset
* 8);
585 speed
= mac_cfg
& (GMAC_CONFIG_PS
| GMAC_CONFIG_FES
);
586 speed
= speed
>> GMAC_CONFIG_FES_SHIFT
;
590 offset
= offset
* 1000; /* 1G */
593 offset
= offset
* 400; /* 2.5G */
596 offset
= offset
* 100000; /* 10M */
599 offset
= offset
* 10000; /* 100M */
605 offset
= offset
/ 1000;
607 ret
|= dwmac5_est_write(ioaddr
, BTR_LOW
, cfg
->btr
[0], false);
608 ret
|= dwmac5_est_write(ioaddr
, BTR_HIGH
, cfg
->btr
[1], false);
609 ret
|= dwmac5_est_write(ioaddr
, TER
, cfg
->ter
, false);
610 ret
|= dwmac5_est_write(ioaddr
, LLR
, cfg
->gcl_size
, false);
615 for (i
= 0; i
< cfg
->gcl_size
; i
++) {
616 ret
= dwmac5_est_write(ioaddr
, i
, cfg
->gcl
[i
] + offset
, true);
620 total_offset
+= offset
;
623 total_ctr
= cfg
->ctr
[0] + cfg
->ctr
[1] * 1000000000ULL;
624 total_ctr
+= total_offset
;
626 ctr_low
= do_div(total_ctr
, 1000000000);
628 ret
|= dwmac5_est_write(ioaddr
, CTR_LOW
, ctr_low
, false);
629 ret
|= dwmac5_est_write(ioaddr
, CTR_HIGH
, total_ctr
, false);
633 ctrl
= readl(ioaddr
+ MTL_EST_CONTROL
);
635 ctrl
|= ((1000000000 / ptp_rate
) * 6) << PTOV_SHIFT
;
641 writel(ctrl
, ioaddr
+ MTL_EST_CONTROL
);
645 void dwmac5_fpe_configure(void __iomem
*ioaddr
, u32 num_txq
, u32 num_rxq
,
651 value
= readl(ioaddr
+ MAC_FPE_CTRL_STS
);
655 writel(value
, ioaddr
+ MAC_FPE_CTRL_STS
);
659 value
= readl(ioaddr
+ GMAC_RXQ_CTRL1
);
660 value
&= ~GMAC_RXQCTRL_FPRQ
;
661 value
|= (num_rxq
- 1) << GMAC_RXQCTRL_FPRQ_SHIFT
;
662 writel(value
, ioaddr
+ GMAC_RXQ_CTRL1
);
664 value
= readl(ioaddr
+ MAC_FPE_CTRL_STS
);
666 writel(value
, ioaddr
+ MAC_FPE_CTRL_STS
);