18bf993a5SJose Abreu // SPDX-License-Identifier: (GPL-2.0 OR MIT) 28bf993a5SJose Abreu // Copyright (c) 2017 Synopsys, Inc. and/or its affiliates. 38bf993a5SJose Abreu // stmmac Support for 5.xx Ethernet QoS cores 48bf993a5SJose Abreu 58bf993a5SJose Abreu #include <linux/bitops.h> 68bf993a5SJose Abreu #include <linux/iopoll.h> 78bf993a5SJose Abreu #include "common.h" 88bf993a5SJose Abreu #include "dwmac4.h" 98bf993a5SJose Abreu #include "dwmac5.h" 108bf993a5SJose Abreu 118bf993a5SJose Abreu struct dwmac5_error_desc { 128bf993a5SJose Abreu bool valid; 138bf993a5SJose Abreu const char *desc; 148bf993a5SJose Abreu const char *detailed_desc; 158bf993a5SJose Abreu }; 168bf993a5SJose Abreu 178bf993a5SJose Abreu #define STAT_OFF(field) offsetof(struct stmmac_safety_stats, field) 188bf993a5SJose Abreu 198bf993a5SJose Abreu static void dwmac5_log_error(struct net_device *ndev, u32 value, bool corr, 208bf993a5SJose Abreu const char *module_name, const struct dwmac5_error_desc *desc, 218bf993a5SJose Abreu unsigned long field_offset, struct stmmac_safety_stats *stats) 228bf993a5SJose Abreu { 238bf993a5SJose Abreu unsigned long loc, mask; 248bf993a5SJose Abreu u8 *bptr = (u8 *)stats; 258bf993a5SJose Abreu unsigned long *ptr; 268bf993a5SJose Abreu 278bf993a5SJose Abreu ptr = (unsigned long *)(bptr + field_offset); 288bf993a5SJose Abreu 298bf993a5SJose Abreu mask = value; 308bf993a5SJose Abreu for_each_set_bit(loc, &mask, 32) { 318bf993a5SJose Abreu netdev_err(ndev, "Found %s error in %s: '%s: %s'\n", corr ? 328bf993a5SJose Abreu "correctable" : "uncorrectable", module_name, 338bf993a5SJose Abreu desc[loc].desc, desc[loc].detailed_desc); 348bf993a5SJose Abreu 358bf993a5SJose Abreu /* Update counters */ 368bf993a5SJose Abreu ptr[loc]++; 378bf993a5SJose Abreu } 388bf993a5SJose Abreu } 398bf993a5SJose Abreu 408bf993a5SJose Abreu static const struct dwmac5_error_desc dwmac5_mac_errors[32]= { 418bf993a5SJose Abreu { true, "ATPES", "Application Transmit Interface Parity Check Error" }, 428bf993a5SJose Abreu { true, "TPES", "TSO Data Path Parity Check Error" }, 438bf993a5SJose Abreu { true, "RDPES", "Read Descriptor Parity Check Error" }, 448bf993a5SJose Abreu { true, "MPES", "MTL Data Path Parity Check Error" }, 458bf993a5SJose Abreu { true, "MTSPES", "MTL TX Status Data Path Parity Check Error" }, 468bf993a5SJose Abreu { true, "ARPES", "Application Receive Interface Data Path Parity Check Error" }, 478bf993a5SJose Abreu { true, "CWPES", "CSR Write Data Path Parity Check Error" }, 488bf993a5SJose Abreu { true, "ASRPES", "AXI Slave Read Data Path Parity Check Error" }, 498bf993a5SJose Abreu { true, "TTES", "TX FSM Timeout Error" }, 508bf993a5SJose Abreu { true, "RTES", "RX FSM Timeout Error" }, 518bf993a5SJose Abreu { true, "CTES", "CSR FSM Timeout Error" }, 528bf993a5SJose Abreu { true, "ATES", "APP FSM Timeout Error" }, 538bf993a5SJose Abreu { true, "PTES", "PTP FSM Timeout Error" }, 548bf993a5SJose Abreu { true, "T125ES", "TX125 FSM Timeout Error" }, 558bf993a5SJose Abreu { true, "R125ES", "RX125 FSM Timeout Error" }, 568bf993a5SJose Abreu { true, "RVCTES", "REV MDC FSM Timeout Error" }, 578bf993a5SJose Abreu { true, "MSTTES", "Master Read/Write Timeout Error" }, 588bf993a5SJose Abreu { true, "SLVTES", "Slave Read/Write Timeout Error" }, 598bf993a5SJose Abreu { true, "ATITES", "Application Timeout on ATI Interface Error" }, 608bf993a5SJose Abreu { true, "ARITES", "Application Timeout on ARI Interface Error" }, 618bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 20 */ 628bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 21 */ 638bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 22 */ 648bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 23 */ 658bf993a5SJose Abreu { true, "FSMPES", "FSM State Parity Error" }, 668bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 25 */ 678bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 26 */ 688bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 27 */ 698bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 28 */ 708bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 29 */ 718bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 30 */ 728bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 31 */ 738bf993a5SJose Abreu }; 748bf993a5SJose Abreu 758bf993a5SJose Abreu static void dwmac5_handle_mac_err(struct net_device *ndev, 768bf993a5SJose Abreu void __iomem *ioaddr, bool correctable, 778bf993a5SJose Abreu struct stmmac_safety_stats *stats) 788bf993a5SJose Abreu { 798bf993a5SJose Abreu u32 value; 808bf993a5SJose Abreu 818bf993a5SJose Abreu value = readl(ioaddr + MAC_DPP_FSM_INT_STATUS); 828bf993a5SJose Abreu writel(value, ioaddr + MAC_DPP_FSM_INT_STATUS); 838bf993a5SJose Abreu 848bf993a5SJose Abreu dwmac5_log_error(ndev, value, correctable, "MAC", dwmac5_mac_errors, 858bf993a5SJose Abreu STAT_OFF(mac_errors), stats); 868bf993a5SJose Abreu } 878bf993a5SJose Abreu 888bf993a5SJose Abreu static const struct dwmac5_error_desc dwmac5_mtl_errors[32]= { 898bf993a5SJose Abreu { true, "TXCES", "MTL TX Memory Error" }, 908bf993a5SJose Abreu { true, "TXAMS", "MTL TX Memory Address Mismatch Error" }, 918bf993a5SJose Abreu { true, "TXUES", "MTL TX Memory Error" }, 928bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 3 */ 938bf993a5SJose Abreu { true, "RXCES", "MTL RX Memory Error" }, 948bf993a5SJose Abreu { true, "RXAMS", "MTL RX Memory Address Mismatch Error" }, 958bf993a5SJose Abreu { true, "RXUES", "MTL RX Memory Error" }, 968bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 7 */ 978bf993a5SJose Abreu { true, "ECES", "MTL EST Memory Error" }, 988bf993a5SJose Abreu { true, "EAMS", "MTL EST Memory Address Mismatch Error" }, 998bf993a5SJose Abreu { true, "EUES", "MTL EST Memory Error" }, 1008bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 11 */ 1018bf993a5SJose Abreu { true, "RPCES", "MTL RX Parser Memory Error" }, 1028bf993a5SJose Abreu { true, "RPAMS", "MTL RX Parser Memory Address Mismatch Error" }, 1038bf993a5SJose Abreu { true, "RPUES", "MTL RX Parser Memory Error" }, 1048bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 15 */ 1058bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 16 */ 1068bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 17 */ 1078bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 18 */ 1088bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 19 */ 1098bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 20 */ 1108bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 21 */ 1118bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 22 */ 1128bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 23 */ 1138bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 24 */ 1148bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 25 */ 1158bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 26 */ 1168bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 27 */ 1178bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 28 */ 1188bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 29 */ 1198bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 30 */ 1208bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 31 */ 1218bf993a5SJose Abreu }; 1228bf993a5SJose Abreu 1238bf993a5SJose Abreu static void dwmac5_handle_mtl_err(struct net_device *ndev, 1248bf993a5SJose Abreu void __iomem *ioaddr, bool correctable, 1258bf993a5SJose Abreu struct stmmac_safety_stats *stats) 1268bf993a5SJose Abreu { 1278bf993a5SJose Abreu u32 value; 1288bf993a5SJose Abreu 1298bf993a5SJose Abreu value = readl(ioaddr + MTL_ECC_INT_STATUS); 1308bf993a5SJose Abreu writel(value, ioaddr + MTL_ECC_INT_STATUS); 1318bf993a5SJose Abreu 1328bf993a5SJose Abreu dwmac5_log_error(ndev, value, correctable, "MTL", dwmac5_mtl_errors, 1338bf993a5SJose Abreu STAT_OFF(mtl_errors), stats); 1348bf993a5SJose Abreu } 1358bf993a5SJose Abreu 1368bf993a5SJose Abreu static const struct dwmac5_error_desc dwmac5_dma_errors[32]= { 1378bf993a5SJose Abreu { true, "TCES", "DMA TSO Memory Error" }, 1388bf993a5SJose Abreu { true, "TAMS", "DMA TSO Memory Address Mismatch Error" }, 1398bf993a5SJose Abreu { true, "TUES", "DMA TSO Memory Error" }, 1408bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 3 */ 1418bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 4 */ 1428bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 5 */ 1438bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 6 */ 1448bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 7 */ 1458bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 8 */ 1468bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 9 */ 1478bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 10 */ 1488bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 11 */ 1498bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 12 */ 1508bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 13 */ 1518bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 14 */ 1528bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 15 */ 1538bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 16 */ 1548bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 17 */ 1558bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 18 */ 1568bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 19 */ 1578bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 20 */ 1588bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 21 */ 1598bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 22 */ 1608bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 23 */ 1618bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 24 */ 1628bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 25 */ 1638bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 26 */ 1648bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 27 */ 1658bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 28 */ 1668bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 29 */ 1678bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 30 */ 1688bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 31 */ 1698bf993a5SJose Abreu }; 1708bf993a5SJose Abreu 1718bf993a5SJose Abreu static void dwmac5_handle_dma_err(struct net_device *ndev, 1728bf993a5SJose Abreu void __iomem *ioaddr, bool correctable, 1738bf993a5SJose Abreu struct stmmac_safety_stats *stats) 1748bf993a5SJose Abreu { 1758bf993a5SJose Abreu u32 value; 1768bf993a5SJose Abreu 1778bf993a5SJose Abreu value = readl(ioaddr + DMA_ECC_INT_STATUS); 1788bf993a5SJose Abreu writel(value, ioaddr + DMA_ECC_INT_STATUS); 1798bf993a5SJose Abreu 1808bf993a5SJose Abreu dwmac5_log_error(ndev, value, correctable, "DMA", dwmac5_dma_errors, 1818bf993a5SJose Abreu STAT_OFF(dma_errors), stats); 1828bf993a5SJose Abreu } 1838bf993a5SJose Abreu 1848bf993a5SJose Abreu int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp) 1858bf993a5SJose Abreu { 1868bf993a5SJose Abreu u32 value; 1878bf993a5SJose Abreu 1888bf993a5SJose Abreu if (!asp) 1898bf993a5SJose Abreu return -EINVAL; 1908bf993a5SJose Abreu 1918bf993a5SJose Abreu /* 1. Enable Safety Features */ 1928bf993a5SJose Abreu value = readl(ioaddr + MTL_ECC_CONTROL); 1938bf993a5SJose Abreu value |= TSOEE; /* TSO ECC */ 1948bf993a5SJose Abreu value |= MRXPEE; /* MTL RX Parser ECC */ 1958bf993a5SJose Abreu value |= MESTEE; /* MTL EST ECC */ 1968bf993a5SJose Abreu value |= MRXEE; /* MTL RX FIFO ECC */ 1978bf993a5SJose Abreu value |= MTXEE; /* MTL TX FIFO ECC */ 1988bf993a5SJose Abreu writel(value, ioaddr + MTL_ECC_CONTROL); 1998bf993a5SJose Abreu 2008bf993a5SJose Abreu /* 2. Enable MTL Safety Interrupts */ 2018bf993a5SJose Abreu value = readl(ioaddr + MTL_ECC_INT_ENABLE); 2028bf993a5SJose Abreu value |= RPCEIE; /* RX Parser Memory Correctable Error */ 2038bf993a5SJose Abreu value |= ECEIE; /* EST Memory Correctable Error */ 2048bf993a5SJose Abreu value |= RXCEIE; /* RX Memory Correctable Error */ 2058bf993a5SJose Abreu value |= TXCEIE; /* TX Memory Correctable Error */ 2068bf993a5SJose Abreu writel(value, ioaddr + MTL_ECC_INT_ENABLE); 2078bf993a5SJose Abreu 2088bf993a5SJose Abreu /* 3. Enable DMA Safety Interrupts */ 2098bf993a5SJose Abreu value = readl(ioaddr + DMA_ECC_INT_ENABLE); 2108bf993a5SJose Abreu value |= TCEIE; /* TSO Memory Correctable Error */ 2118bf993a5SJose Abreu writel(value, ioaddr + DMA_ECC_INT_ENABLE); 2128bf993a5SJose Abreu 2138bf993a5SJose Abreu /* Only ECC Protection for External Memory feature is selected */ 2148bf993a5SJose Abreu if (asp <= 0x1) 2158bf993a5SJose Abreu return 0; 2168bf993a5SJose Abreu 2178bf993a5SJose Abreu /* 5. Enable Parity and Timeout for FSM */ 2188bf993a5SJose Abreu value = readl(ioaddr + MAC_FSM_CONTROL); 2198bf993a5SJose Abreu value |= PRTYEN; /* FSM Parity Feature */ 2208bf993a5SJose Abreu value |= TMOUTEN; /* FSM Timeout Feature */ 2218bf993a5SJose Abreu writel(value, ioaddr + MAC_FSM_CONTROL); 2228bf993a5SJose Abreu 2238bf993a5SJose Abreu /* 4. Enable Data Parity Protection */ 2248bf993a5SJose Abreu value = readl(ioaddr + MTL_DPP_CONTROL); 2258bf993a5SJose Abreu value |= EDPP; 2268bf993a5SJose Abreu writel(value, ioaddr + MTL_DPP_CONTROL); 2278bf993a5SJose Abreu 2288bf993a5SJose Abreu /* 2298bf993a5SJose Abreu * All the Automotive Safety features are selected without the "Parity 2308bf993a5SJose Abreu * Port Enable for external interface" feature. 2318bf993a5SJose Abreu */ 2328bf993a5SJose Abreu if (asp <= 0x2) 2338bf993a5SJose Abreu return 0; 2348bf993a5SJose Abreu 2358bf993a5SJose Abreu value |= EPSI; 2368bf993a5SJose Abreu writel(value, ioaddr + MTL_DPP_CONTROL); 2378bf993a5SJose Abreu return 0; 2388bf993a5SJose Abreu } 2398bf993a5SJose Abreu 2408bf993a5SJose Abreu bool dwmac5_safety_feat_irq_status(struct net_device *ndev, 2418bf993a5SJose Abreu void __iomem *ioaddr, unsigned int asp, 2428bf993a5SJose Abreu struct stmmac_safety_stats *stats) 2438bf993a5SJose Abreu { 2448bf993a5SJose Abreu bool ret = false, err, corr; 2458bf993a5SJose Abreu u32 mtl, dma; 2468bf993a5SJose Abreu 2478bf993a5SJose Abreu if (!asp) 2488bf993a5SJose Abreu return false; 2498bf993a5SJose Abreu 2508bf993a5SJose Abreu mtl = readl(ioaddr + MTL_SAFETY_INT_STATUS); 2518bf993a5SJose Abreu dma = readl(ioaddr + DMA_SAFETY_INT_STATUS); 2528bf993a5SJose Abreu 2538bf993a5SJose Abreu err = (mtl & MCSIS) || (dma & MCSIS); 2548bf993a5SJose Abreu corr = false; 2558bf993a5SJose Abreu if (err) { 2568bf993a5SJose Abreu dwmac5_handle_mac_err(ndev, ioaddr, corr, stats); 2578bf993a5SJose Abreu ret |= !corr; 2588bf993a5SJose Abreu } 2598bf993a5SJose Abreu 2608bf993a5SJose Abreu err = (mtl & (MEUIS | MECIS)) || (dma & (MSUIS | MSCIS)); 2618bf993a5SJose Abreu corr = (mtl & MECIS) || (dma & MSCIS); 2628bf993a5SJose Abreu if (err) { 2638bf993a5SJose Abreu dwmac5_handle_mtl_err(ndev, ioaddr, corr, stats); 2648bf993a5SJose Abreu ret |= !corr; 2658bf993a5SJose Abreu } 2668bf993a5SJose Abreu 2678bf993a5SJose Abreu err = dma & (DEUIS | DECIS); 2688bf993a5SJose Abreu corr = dma & DECIS; 2698bf993a5SJose Abreu if (err) { 2708bf993a5SJose Abreu dwmac5_handle_dma_err(ndev, ioaddr, corr, stats); 2718bf993a5SJose Abreu ret |= !corr; 2728bf993a5SJose Abreu } 2738bf993a5SJose Abreu 2748bf993a5SJose Abreu return ret; 2758bf993a5SJose Abreu } 2768bf993a5SJose Abreu 2778bf993a5SJose Abreu static const struct dwmac5_error { 2788bf993a5SJose Abreu const struct dwmac5_error_desc *desc; 2798bf993a5SJose Abreu } dwmac5_all_errors[] = { 2808bf993a5SJose Abreu { dwmac5_mac_errors }, 2818bf993a5SJose Abreu { dwmac5_mtl_errors }, 2828bf993a5SJose Abreu { dwmac5_dma_errors }, 2838bf993a5SJose Abreu }; 2848bf993a5SJose Abreu 2858bf993a5SJose Abreu const char *dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats, 2868bf993a5SJose Abreu int index, unsigned long *count) 2878bf993a5SJose Abreu { 2888bf993a5SJose Abreu int module = index / 32, offset = index % 32; 2898bf993a5SJose Abreu unsigned long *ptr = (unsigned long *)stats; 2908bf993a5SJose Abreu 2918bf993a5SJose Abreu if (module >= ARRAY_SIZE(dwmac5_all_errors)) 2928bf993a5SJose Abreu return NULL; 2938bf993a5SJose Abreu if (!dwmac5_all_errors[module].desc[offset].valid) 2948bf993a5SJose Abreu return NULL; 2958bf993a5SJose Abreu if (count) 2968bf993a5SJose Abreu *count = *(ptr + index); 2978bf993a5SJose Abreu return dwmac5_all_errors[module].desc[offset].desc; 2988bf993a5SJose Abreu } 299