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" 104dbbe8ddSJose Abreu #include "stmmac.h" 118bf993a5SJose Abreu 128bf993a5SJose Abreu struct dwmac5_error_desc { 138bf993a5SJose Abreu bool valid; 148bf993a5SJose Abreu const char *desc; 158bf993a5SJose Abreu const char *detailed_desc; 168bf993a5SJose Abreu }; 178bf993a5SJose Abreu 188bf993a5SJose Abreu #define STAT_OFF(field) offsetof(struct stmmac_safety_stats, field) 198bf993a5SJose Abreu 208bf993a5SJose Abreu static void dwmac5_log_error(struct net_device *ndev, u32 value, bool corr, 218bf993a5SJose Abreu const char *module_name, const struct dwmac5_error_desc *desc, 228bf993a5SJose Abreu unsigned long field_offset, struct stmmac_safety_stats *stats) 238bf993a5SJose Abreu { 248bf993a5SJose Abreu unsigned long loc, mask; 258bf993a5SJose Abreu u8 *bptr = (u8 *)stats; 268bf993a5SJose Abreu unsigned long *ptr; 278bf993a5SJose Abreu 288bf993a5SJose Abreu ptr = (unsigned long *)(bptr + field_offset); 298bf993a5SJose Abreu 308bf993a5SJose Abreu mask = value; 318bf993a5SJose Abreu for_each_set_bit(loc, &mask, 32) { 328bf993a5SJose Abreu netdev_err(ndev, "Found %s error in %s: '%s: %s'\n", corr ? 338bf993a5SJose Abreu "correctable" : "uncorrectable", module_name, 348bf993a5SJose Abreu desc[loc].desc, desc[loc].detailed_desc); 358bf993a5SJose Abreu 368bf993a5SJose Abreu /* Update counters */ 378bf993a5SJose Abreu ptr[loc]++; 388bf993a5SJose Abreu } 398bf993a5SJose Abreu } 408bf993a5SJose Abreu 418bf993a5SJose Abreu static const struct dwmac5_error_desc dwmac5_mac_errors[32]= { 428bf993a5SJose Abreu { true, "ATPES", "Application Transmit Interface Parity Check Error" }, 438bf993a5SJose Abreu { true, "TPES", "TSO Data Path Parity Check Error" }, 448bf993a5SJose Abreu { true, "RDPES", "Read Descriptor Parity Check Error" }, 458bf993a5SJose Abreu { true, "MPES", "MTL Data Path Parity Check Error" }, 468bf993a5SJose Abreu { true, "MTSPES", "MTL TX Status Data Path Parity Check Error" }, 478bf993a5SJose Abreu { true, "ARPES", "Application Receive Interface Data Path Parity Check Error" }, 488bf993a5SJose Abreu { true, "CWPES", "CSR Write Data Path Parity Check Error" }, 498bf993a5SJose Abreu { true, "ASRPES", "AXI Slave Read Data Path Parity Check Error" }, 508bf993a5SJose Abreu { true, "TTES", "TX FSM Timeout Error" }, 518bf993a5SJose Abreu { true, "RTES", "RX FSM Timeout Error" }, 528bf993a5SJose Abreu { true, "CTES", "CSR FSM Timeout Error" }, 538bf993a5SJose Abreu { true, "ATES", "APP FSM Timeout Error" }, 548bf993a5SJose Abreu { true, "PTES", "PTP FSM Timeout Error" }, 558bf993a5SJose Abreu { true, "T125ES", "TX125 FSM Timeout Error" }, 568bf993a5SJose Abreu { true, "R125ES", "RX125 FSM Timeout Error" }, 578bf993a5SJose Abreu { true, "RVCTES", "REV MDC FSM Timeout Error" }, 588bf993a5SJose Abreu { true, "MSTTES", "Master Read/Write Timeout Error" }, 598bf993a5SJose Abreu { true, "SLVTES", "Slave Read/Write Timeout Error" }, 608bf993a5SJose Abreu { true, "ATITES", "Application Timeout on ATI Interface Error" }, 618bf993a5SJose Abreu { true, "ARITES", "Application Timeout on ARI Interface Error" }, 628bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 20 */ 638bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 21 */ 648bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 22 */ 658bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 23 */ 668bf993a5SJose Abreu { true, "FSMPES", "FSM State Parity Error" }, 678bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 25 */ 688bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 26 */ 698bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 27 */ 708bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 28 */ 718bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 29 */ 728bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 30 */ 738bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 31 */ 748bf993a5SJose Abreu }; 758bf993a5SJose Abreu 768bf993a5SJose Abreu static void dwmac5_handle_mac_err(struct net_device *ndev, 778bf993a5SJose Abreu void __iomem *ioaddr, bool correctable, 788bf993a5SJose Abreu struct stmmac_safety_stats *stats) 798bf993a5SJose Abreu { 808bf993a5SJose Abreu u32 value; 818bf993a5SJose Abreu 828bf993a5SJose Abreu value = readl(ioaddr + MAC_DPP_FSM_INT_STATUS); 838bf993a5SJose Abreu writel(value, ioaddr + MAC_DPP_FSM_INT_STATUS); 848bf993a5SJose Abreu 858bf993a5SJose Abreu dwmac5_log_error(ndev, value, correctable, "MAC", dwmac5_mac_errors, 868bf993a5SJose Abreu STAT_OFF(mac_errors), stats); 878bf993a5SJose Abreu } 888bf993a5SJose Abreu 898bf993a5SJose Abreu static const struct dwmac5_error_desc dwmac5_mtl_errors[32]= { 908bf993a5SJose Abreu { true, "TXCES", "MTL TX Memory Error" }, 918bf993a5SJose Abreu { true, "TXAMS", "MTL TX Memory Address Mismatch Error" }, 928bf993a5SJose Abreu { true, "TXUES", "MTL TX Memory Error" }, 938bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 3 */ 948bf993a5SJose Abreu { true, "RXCES", "MTL RX Memory Error" }, 958bf993a5SJose Abreu { true, "RXAMS", "MTL RX Memory Address Mismatch Error" }, 968bf993a5SJose Abreu { true, "RXUES", "MTL RX Memory Error" }, 978bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 7 */ 988bf993a5SJose Abreu { true, "ECES", "MTL EST Memory Error" }, 998bf993a5SJose Abreu { true, "EAMS", "MTL EST Memory Address Mismatch Error" }, 1008bf993a5SJose Abreu { true, "EUES", "MTL EST Memory Error" }, 1018bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 11 */ 1028bf993a5SJose Abreu { true, "RPCES", "MTL RX Parser Memory Error" }, 1038bf993a5SJose Abreu { true, "RPAMS", "MTL RX Parser Memory Address Mismatch Error" }, 1048bf993a5SJose Abreu { true, "RPUES", "MTL RX Parser Memory Error" }, 1058bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 15 */ 1068bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 16 */ 1078bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 17 */ 1088bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 18 */ 1098bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 19 */ 1108bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 20 */ 1118bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 21 */ 1128bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 22 */ 1138bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 23 */ 1148bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 24 */ 1158bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 25 */ 1168bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 26 */ 1178bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 27 */ 1188bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 28 */ 1198bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 29 */ 1208bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 30 */ 1218bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 31 */ 1228bf993a5SJose Abreu }; 1238bf993a5SJose Abreu 1248bf993a5SJose Abreu static void dwmac5_handle_mtl_err(struct net_device *ndev, 1258bf993a5SJose Abreu void __iomem *ioaddr, bool correctable, 1268bf993a5SJose Abreu struct stmmac_safety_stats *stats) 1278bf993a5SJose Abreu { 1288bf993a5SJose Abreu u32 value; 1298bf993a5SJose Abreu 1308bf993a5SJose Abreu value = readl(ioaddr + MTL_ECC_INT_STATUS); 1318bf993a5SJose Abreu writel(value, ioaddr + MTL_ECC_INT_STATUS); 1328bf993a5SJose Abreu 1338bf993a5SJose Abreu dwmac5_log_error(ndev, value, correctable, "MTL", dwmac5_mtl_errors, 1348bf993a5SJose Abreu STAT_OFF(mtl_errors), stats); 1358bf993a5SJose Abreu } 1368bf993a5SJose Abreu 1378bf993a5SJose Abreu static const struct dwmac5_error_desc dwmac5_dma_errors[32]= { 1388bf993a5SJose Abreu { true, "TCES", "DMA TSO Memory Error" }, 1398bf993a5SJose Abreu { true, "TAMS", "DMA TSO Memory Address Mismatch Error" }, 1408bf993a5SJose Abreu { true, "TUES", "DMA TSO Memory Error" }, 1418bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 3 */ 1428bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 4 */ 1438bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 5 */ 1448bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 6 */ 1458bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 7 */ 1468bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 8 */ 1478bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 9 */ 1488bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 10 */ 1498bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 11 */ 1508bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 12 */ 1518bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 13 */ 1528bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 14 */ 1538bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 15 */ 1548bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 16 */ 1558bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 17 */ 1568bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 18 */ 1578bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 19 */ 1588bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 20 */ 1598bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 21 */ 1608bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 22 */ 1618bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 23 */ 1628bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 24 */ 1638bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 25 */ 1648bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 26 */ 1658bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 27 */ 1668bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 28 */ 1678bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 29 */ 1688bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 30 */ 1698bf993a5SJose Abreu { false, "UNKNOWN", "Unknown Error" }, /* 31 */ 1708bf993a5SJose Abreu }; 1718bf993a5SJose Abreu 1728bf993a5SJose Abreu static void dwmac5_handle_dma_err(struct net_device *ndev, 1738bf993a5SJose Abreu void __iomem *ioaddr, bool correctable, 1748bf993a5SJose Abreu struct stmmac_safety_stats *stats) 1758bf993a5SJose Abreu { 1768bf993a5SJose Abreu u32 value; 1778bf993a5SJose Abreu 1788bf993a5SJose Abreu value = readl(ioaddr + DMA_ECC_INT_STATUS); 1798bf993a5SJose Abreu writel(value, ioaddr + DMA_ECC_INT_STATUS); 1808bf993a5SJose Abreu 1818bf993a5SJose Abreu dwmac5_log_error(ndev, value, correctable, "DMA", dwmac5_dma_errors, 1828bf993a5SJose Abreu STAT_OFF(dma_errors), stats); 1838bf993a5SJose Abreu } 1848bf993a5SJose Abreu 1858bf993a5SJose Abreu int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp) 1868bf993a5SJose Abreu { 1878bf993a5SJose Abreu u32 value; 1888bf993a5SJose Abreu 1898bf993a5SJose Abreu if (!asp) 1908bf993a5SJose Abreu return -EINVAL; 1918bf993a5SJose Abreu 1928bf993a5SJose Abreu /* 1. Enable Safety Features */ 1938bf993a5SJose Abreu value = readl(ioaddr + MTL_ECC_CONTROL); 1948bf993a5SJose Abreu value |= TSOEE; /* TSO ECC */ 1958bf993a5SJose Abreu value |= MRXPEE; /* MTL RX Parser ECC */ 1968bf993a5SJose Abreu value |= MESTEE; /* MTL EST ECC */ 1978bf993a5SJose Abreu value |= MRXEE; /* MTL RX FIFO ECC */ 1988bf993a5SJose Abreu value |= MTXEE; /* MTL TX FIFO ECC */ 1998bf993a5SJose Abreu writel(value, ioaddr + MTL_ECC_CONTROL); 2008bf993a5SJose Abreu 2018bf993a5SJose Abreu /* 2. Enable MTL Safety Interrupts */ 2028bf993a5SJose Abreu value = readl(ioaddr + MTL_ECC_INT_ENABLE); 2038bf993a5SJose Abreu value |= RPCEIE; /* RX Parser Memory Correctable Error */ 2048bf993a5SJose Abreu value |= ECEIE; /* EST Memory Correctable Error */ 2058bf993a5SJose Abreu value |= RXCEIE; /* RX Memory Correctable Error */ 2068bf993a5SJose Abreu value |= TXCEIE; /* TX Memory Correctable Error */ 2078bf993a5SJose Abreu writel(value, ioaddr + MTL_ECC_INT_ENABLE); 2088bf993a5SJose Abreu 2098bf993a5SJose Abreu /* 3. Enable DMA Safety Interrupts */ 2108bf993a5SJose Abreu value = readl(ioaddr + DMA_ECC_INT_ENABLE); 2118bf993a5SJose Abreu value |= TCEIE; /* TSO Memory Correctable Error */ 2128bf993a5SJose Abreu writel(value, ioaddr + DMA_ECC_INT_ENABLE); 2138bf993a5SJose Abreu 2148bf993a5SJose Abreu /* Only ECC Protection for External Memory feature is selected */ 2158bf993a5SJose Abreu if (asp <= 0x1) 2168bf993a5SJose Abreu return 0; 2178bf993a5SJose Abreu 2188bf993a5SJose Abreu /* 5. Enable Parity and Timeout for FSM */ 2198bf993a5SJose Abreu value = readl(ioaddr + MAC_FSM_CONTROL); 2208bf993a5SJose Abreu value |= PRTYEN; /* FSM Parity Feature */ 2218bf993a5SJose Abreu value |= TMOUTEN; /* FSM Timeout Feature */ 2228bf993a5SJose Abreu writel(value, ioaddr + MAC_FSM_CONTROL); 2238bf993a5SJose Abreu 2248bf993a5SJose Abreu /* 4. Enable Data Parity Protection */ 2258bf993a5SJose Abreu value = readl(ioaddr + MTL_DPP_CONTROL); 2268bf993a5SJose Abreu value |= EDPP; 2278bf993a5SJose Abreu writel(value, ioaddr + MTL_DPP_CONTROL); 2288bf993a5SJose Abreu 2298bf993a5SJose Abreu /* 2308bf993a5SJose Abreu * All the Automotive Safety features are selected without the "Parity 2318bf993a5SJose Abreu * Port Enable for external interface" feature. 2328bf993a5SJose Abreu */ 2338bf993a5SJose Abreu if (asp <= 0x2) 2348bf993a5SJose Abreu return 0; 2358bf993a5SJose Abreu 2368bf993a5SJose Abreu value |= EPSI; 2378bf993a5SJose Abreu writel(value, ioaddr + MTL_DPP_CONTROL); 2388bf993a5SJose Abreu return 0; 2398bf993a5SJose Abreu } 2408bf993a5SJose Abreu 241c10d4c82SJose Abreu int dwmac5_safety_feat_irq_status(struct net_device *ndev, 2428bf993a5SJose Abreu void __iomem *ioaddr, unsigned int asp, 2438bf993a5SJose Abreu struct stmmac_safety_stats *stats) 2448bf993a5SJose Abreu { 245c10d4c82SJose Abreu bool err, corr; 2468bf993a5SJose Abreu u32 mtl, dma; 247c10d4c82SJose Abreu int ret = 0; 2488bf993a5SJose Abreu 2498bf993a5SJose Abreu if (!asp) 250c10d4c82SJose Abreu return -EINVAL; 2518bf993a5SJose Abreu 2528bf993a5SJose Abreu mtl = readl(ioaddr + MTL_SAFETY_INT_STATUS); 2538bf993a5SJose Abreu dma = readl(ioaddr + DMA_SAFETY_INT_STATUS); 2548bf993a5SJose Abreu 2558bf993a5SJose Abreu err = (mtl & MCSIS) || (dma & MCSIS); 2568bf993a5SJose Abreu corr = false; 2578bf993a5SJose Abreu if (err) { 2588bf993a5SJose Abreu dwmac5_handle_mac_err(ndev, ioaddr, corr, stats); 2598bf993a5SJose Abreu ret |= !corr; 2608bf993a5SJose Abreu } 2618bf993a5SJose Abreu 2628bf993a5SJose Abreu err = (mtl & (MEUIS | MECIS)) || (dma & (MSUIS | MSCIS)); 2638bf993a5SJose Abreu corr = (mtl & MECIS) || (dma & MSCIS); 2648bf993a5SJose Abreu if (err) { 2658bf993a5SJose Abreu dwmac5_handle_mtl_err(ndev, ioaddr, corr, stats); 2668bf993a5SJose Abreu ret |= !corr; 2678bf993a5SJose Abreu } 2688bf993a5SJose Abreu 2698bf993a5SJose Abreu err = dma & (DEUIS | DECIS); 2708bf993a5SJose Abreu corr = dma & DECIS; 2718bf993a5SJose Abreu if (err) { 2728bf993a5SJose Abreu dwmac5_handle_dma_err(ndev, ioaddr, corr, stats); 2738bf993a5SJose Abreu ret |= !corr; 2748bf993a5SJose Abreu } 2758bf993a5SJose Abreu 2768bf993a5SJose Abreu return ret; 2778bf993a5SJose Abreu } 2788bf993a5SJose Abreu 2798bf993a5SJose Abreu static const struct dwmac5_error { 2808bf993a5SJose Abreu const struct dwmac5_error_desc *desc; 2818bf993a5SJose Abreu } dwmac5_all_errors[] = { 2828bf993a5SJose Abreu { dwmac5_mac_errors }, 2838bf993a5SJose Abreu { dwmac5_mtl_errors }, 2848bf993a5SJose Abreu { dwmac5_dma_errors }, 2858bf993a5SJose Abreu }; 2868bf993a5SJose Abreu 287c10d4c82SJose Abreu int dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats, 288c10d4c82SJose Abreu int index, unsigned long *count, const char **desc) 2898bf993a5SJose Abreu { 2908bf993a5SJose Abreu int module = index / 32, offset = index % 32; 2918bf993a5SJose Abreu unsigned long *ptr = (unsigned long *)stats; 2928bf993a5SJose Abreu 2938bf993a5SJose Abreu if (module >= ARRAY_SIZE(dwmac5_all_errors)) 294c10d4c82SJose Abreu return -EINVAL; 2958bf993a5SJose Abreu if (!dwmac5_all_errors[module].desc[offset].valid) 296c10d4c82SJose Abreu return -EINVAL; 2978bf993a5SJose Abreu if (count) 2988bf993a5SJose Abreu *count = *(ptr + index); 299c10d4c82SJose Abreu if (desc) 300c10d4c82SJose Abreu *desc = dwmac5_all_errors[module].desc[offset].desc; 301c10d4c82SJose Abreu return 0; 3028bf993a5SJose Abreu } 3034dbbe8ddSJose Abreu 3044dbbe8ddSJose Abreu static int dwmac5_rxp_disable(void __iomem *ioaddr) 3054dbbe8ddSJose Abreu { 3064dbbe8ddSJose Abreu u32 val; 3074dbbe8ddSJose Abreu int ret; 3084dbbe8ddSJose Abreu 3094dbbe8ddSJose Abreu val = readl(ioaddr + MTL_OPERATION_MODE); 3104dbbe8ddSJose Abreu val &= ~MTL_FRPE; 3114dbbe8ddSJose Abreu writel(val, ioaddr + MTL_OPERATION_MODE); 3124dbbe8ddSJose Abreu 3134dbbe8ddSJose Abreu ret = readl_poll_timeout(ioaddr + MTL_RXP_CONTROL_STATUS, val, 3144dbbe8ddSJose Abreu val & RXPI, 1, 10000); 3154dbbe8ddSJose Abreu if (ret) 3164dbbe8ddSJose Abreu return ret; 3174dbbe8ddSJose Abreu return 0; 3184dbbe8ddSJose Abreu } 3194dbbe8ddSJose Abreu 3204dbbe8ddSJose Abreu static void dwmac5_rxp_enable(void __iomem *ioaddr) 3214dbbe8ddSJose Abreu { 3224dbbe8ddSJose Abreu u32 val; 3234dbbe8ddSJose Abreu 3244dbbe8ddSJose Abreu val = readl(ioaddr + MTL_OPERATION_MODE); 3254dbbe8ddSJose Abreu val |= MTL_FRPE; 3264dbbe8ddSJose Abreu writel(val, ioaddr + MTL_OPERATION_MODE); 3274dbbe8ddSJose Abreu } 3284dbbe8ddSJose Abreu 3294dbbe8ddSJose Abreu static int dwmac5_rxp_update_single_entry(void __iomem *ioaddr, 3304dbbe8ddSJose Abreu struct stmmac_tc_entry *entry, 3314dbbe8ddSJose Abreu int pos) 3324dbbe8ddSJose Abreu { 3334dbbe8ddSJose Abreu int ret, i; 3344dbbe8ddSJose Abreu 3354dbbe8ddSJose Abreu for (i = 0; i < (sizeof(entry->val) / sizeof(u32)); i++) { 3364dbbe8ddSJose Abreu int real_pos = pos * (sizeof(entry->val) / sizeof(u32)) + i; 3374dbbe8ddSJose Abreu u32 val; 3384dbbe8ddSJose Abreu 3394dbbe8ddSJose Abreu /* Wait for ready */ 3404dbbe8ddSJose Abreu ret = readl_poll_timeout(ioaddr + MTL_RXP_IACC_CTRL_STATUS, 3414dbbe8ddSJose Abreu val, !(val & STARTBUSY), 1, 10000); 3424dbbe8ddSJose Abreu if (ret) 3434dbbe8ddSJose Abreu return ret; 3444dbbe8ddSJose Abreu 3454dbbe8ddSJose Abreu /* Write data */ 3464dbbe8ddSJose Abreu val = *((u32 *)&entry->val + i); 3474dbbe8ddSJose Abreu writel(val, ioaddr + MTL_RXP_IACC_DATA); 3484dbbe8ddSJose Abreu 3494dbbe8ddSJose Abreu /* Write pos */ 3504dbbe8ddSJose Abreu val = real_pos & ADDR; 3514dbbe8ddSJose Abreu writel(val, ioaddr + MTL_RXP_IACC_CTRL_STATUS); 3524dbbe8ddSJose Abreu 3534dbbe8ddSJose Abreu /* Write OP */ 3544dbbe8ddSJose Abreu val |= WRRDN; 3554dbbe8ddSJose Abreu writel(val, ioaddr + MTL_RXP_IACC_CTRL_STATUS); 3564dbbe8ddSJose Abreu 3574dbbe8ddSJose Abreu /* Start Write */ 3584dbbe8ddSJose Abreu val |= STARTBUSY; 3594dbbe8ddSJose Abreu writel(val, ioaddr + MTL_RXP_IACC_CTRL_STATUS); 3604dbbe8ddSJose Abreu 3614dbbe8ddSJose Abreu /* Wait for done */ 3624dbbe8ddSJose Abreu ret = readl_poll_timeout(ioaddr + MTL_RXP_IACC_CTRL_STATUS, 3634dbbe8ddSJose Abreu val, !(val & STARTBUSY), 1, 10000); 3644dbbe8ddSJose Abreu if (ret) 3654dbbe8ddSJose Abreu return ret; 3664dbbe8ddSJose Abreu } 3674dbbe8ddSJose Abreu 3684dbbe8ddSJose Abreu return 0; 3694dbbe8ddSJose Abreu } 3704dbbe8ddSJose Abreu 3714dbbe8ddSJose Abreu static struct stmmac_tc_entry * 3724dbbe8ddSJose Abreu dwmac5_rxp_get_next_entry(struct stmmac_tc_entry *entries, unsigned int count, 3734dbbe8ddSJose Abreu u32 curr_prio) 3744dbbe8ddSJose Abreu { 3754dbbe8ddSJose Abreu struct stmmac_tc_entry *entry; 3764dbbe8ddSJose Abreu u32 min_prio = ~0x0; 3774dbbe8ddSJose Abreu int i, min_prio_idx; 3784dbbe8ddSJose Abreu bool found = false; 3794dbbe8ddSJose Abreu 3804dbbe8ddSJose Abreu for (i = count - 1; i >= 0; i--) { 3814dbbe8ddSJose Abreu entry = &entries[i]; 3824dbbe8ddSJose Abreu 3834dbbe8ddSJose Abreu /* Do not update unused entries */ 3844dbbe8ddSJose Abreu if (!entry->in_use) 3854dbbe8ddSJose Abreu continue; 3864dbbe8ddSJose Abreu /* Do not update already updated entries (i.e. fragments) */ 3874dbbe8ddSJose Abreu if (entry->in_hw) 3884dbbe8ddSJose Abreu continue; 3894dbbe8ddSJose Abreu /* Let last entry be updated last */ 3904dbbe8ddSJose Abreu if (entry->is_last) 3914dbbe8ddSJose Abreu continue; 3924dbbe8ddSJose Abreu /* Do not return fragments */ 3934dbbe8ddSJose Abreu if (entry->is_frag) 3944dbbe8ddSJose Abreu continue; 3954dbbe8ddSJose Abreu /* Check if we already checked this prio */ 3964dbbe8ddSJose Abreu if (entry->prio < curr_prio) 3974dbbe8ddSJose Abreu continue; 3984dbbe8ddSJose Abreu /* Check if this is the minimum prio */ 3994dbbe8ddSJose Abreu if (entry->prio < min_prio) { 4004dbbe8ddSJose Abreu min_prio = entry->prio; 4014dbbe8ddSJose Abreu min_prio_idx = i; 4024dbbe8ddSJose Abreu found = true; 4034dbbe8ddSJose Abreu } 4044dbbe8ddSJose Abreu } 4054dbbe8ddSJose Abreu 4064dbbe8ddSJose Abreu if (found) 4074dbbe8ddSJose Abreu return &entries[min_prio_idx]; 4084dbbe8ddSJose Abreu return NULL; 4094dbbe8ddSJose Abreu } 4104dbbe8ddSJose Abreu 4114dbbe8ddSJose Abreu int dwmac5_rxp_config(void __iomem *ioaddr, struct stmmac_tc_entry *entries, 4124dbbe8ddSJose Abreu unsigned int count) 4134dbbe8ddSJose Abreu { 4144dbbe8ddSJose Abreu struct stmmac_tc_entry *entry, *frag; 4154dbbe8ddSJose Abreu int i, ret, nve = 0; 4164dbbe8ddSJose Abreu u32 curr_prio = 0; 4174dbbe8ddSJose Abreu u32 old_val, val; 4184dbbe8ddSJose Abreu 4194dbbe8ddSJose Abreu /* Force disable RX */ 4204dbbe8ddSJose Abreu old_val = readl(ioaddr + GMAC_CONFIG); 4214dbbe8ddSJose Abreu val = old_val & ~GMAC_CONFIG_RE; 4224dbbe8ddSJose Abreu writel(val, ioaddr + GMAC_CONFIG); 4234dbbe8ddSJose Abreu 4244dbbe8ddSJose Abreu /* Disable RX Parser */ 4254dbbe8ddSJose Abreu ret = dwmac5_rxp_disable(ioaddr); 4264dbbe8ddSJose Abreu if (ret) 4274dbbe8ddSJose Abreu goto re_enable; 4284dbbe8ddSJose Abreu 4294dbbe8ddSJose Abreu /* Set all entries as NOT in HW */ 4304dbbe8ddSJose Abreu for (i = 0; i < count; i++) { 4314dbbe8ddSJose Abreu entry = &entries[i]; 4324dbbe8ddSJose Abreu entry->in_hw = false; 4334dbbe8ddSJose Abreu } 4344dbbe8ddSJose Abreu 4354dbbe8ddSJose Abreu /* Update entries by reverse order */ 4364dbbe8ddSJose Abreu while (1) { 4374dbbe8ddSJose Abreu entry = dwmac5_rxp_get_next_entry(entries, count, curr_prio); 4384dbbe8ddSJose Abreu if (!entry) 4394dbbe8ddSJose Abreu break; 4404dbbe8ddSJose Abreu 4414dbbe8ddSJose Abreu curr_prio = entry->prio; 4424dbbe8ddSJose Abreu frag = entry->frag_ptr; 4434dbbe8ddSJose Abreu 4444dbbe8ddSJose Abreu /* Set special fragment requirements */ 4454dbbe8ddSJose Abreu if (frag) { 4464dbbe8ddSJose Abreu entry->val.af = 0; 4474dbbe8ddSJose Abreu entry->val.rf = 0; 4484dbbe8ddSJose Abreu entry->val.nc = 1; 4494dbbe8ddSJose Abreu entry->val.ok_index = nve + 2; 4504dbbe8ddSJose Abreu } 4514dbbe8ddSJose Abreu 4524dbbe8ddSJose Abreu ret = dwmac5_rxp_update_single_entry(ioaddr, entry, nve); 4534dbbe8ddSJose Abreu if (ret) 4544dbbe8ddSJose Abreu goto re_enable; 4554dbbe8ddSJose Abreu 4564dbbe8ddSJose Abreu entry->table_pos = nve++; 4574dbbe8ddSJose Abreu entry->in_hw = true; 4584dbbe8ddSJose Abreu 4594dbbe8ddSJose Abreu if (frag && !frag->in_hw) { 4604dbbe8ddSJose Abreu ret = dwmac5_rxp_update_single_entry(ioaddr, frag, nve); 4614dbbe8ddSJose Abreu if (ret) 4624dbbe8ddSJose Abreu goto re_enable; 4634dbbe8ddSJose Abreu frag->table_pos = nve++; 4644dbbe8ddSJose Abreu frag->in_hw = true; 4654dbbe8ddSJose Abreu } 4664dbbe8ddSJose Abreu } 4674dbbe8ddSJose Abreu 4684dbbe8ddSJose Abreu if (!nve) 4694dbbe8ddSJose Abreu goto re_enable; 4704dbbe8ddSJose Abreu 4714dbbe8ddSJose Abreu /* Update all pass entry */ 4724dbbe8ddSJose Abreu for (i = 0; i < count; i++) { 4734dbbe8ddSJose Abreu entry = &entries[i]; 4744dbbe8ddSJose Abreu if (!entry->is_last) 4754dbbe8ddSJose Abreu continue; 4764dbbe8ddSJose Abreu 4774dbbe8ddSJose Abreu ret = dwmac5_rxp_update_single_entry(ioaddr, entry, nve); 4784dbbe8ddSJose Abreu if (ret) 4794dbbe8ddSJose Abreu goto re_enable; 4804dbbe8ddSJose Abreu 4814dbbe8ddSJose Abreu entry->table_pos = nve++; 4824dbbe8ddSJose Abreu } 4834dbbe8ddSJose Abreu 4844dbbe8ddSJose Abreu /* Assume n. of parsable entries == n. of valid entries */ 4854dbbe8ddSJose Abreu val = (nve << 16) & NPE; 4864dbbe8ddSJose Abreu val |= nve & NVE; 4874dbbe8ddSJose Abreu writel(val, ioaddr + MTL_RXP_CONTROL_STATUS); 4884dbbe8ddSJose Abreu 4894dbbe8ddSJose Abreu /* Enable RX Parser */ 4904dbbe8ddSJose Abreu dwmac5_rxp_enable(ioaddr); 4914dbbe8ddSJose Abreu 4924dbbe8ddSJose Abreu re_enable: 4934dbbe8ddSJose Abreu /* Re-enable RX */ 4944dbbe8ddSJose Abreu writel(old_val, ioaddr + GMAC_CONFIG); 4954dbbe8ddSJose Abreu return ret; 4964dbbe8ddSJose Abreu } 497