diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index ff3f83b86d10..972e4ef6d414 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -4,7 +4,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \ chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \ dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \ mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o dwmac4_descs.o \ - dwmac4_dma.o dwmac4_lib.o dwmac4_core.o $(stmmac-y) + dwmac4_dma.o dwmac4_lib.o dwmac4_core.o dwmac5.o $(stmmac-y) # Ordering matters. Generic driver must be last. obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 2ffe76c0ff74..ad2388aee463 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -38,6 +38,8 @@ #define DWMAC_CORE_3_40 0x34 #define DWMAC_CORE_3_50 0x35 #define DWMAC_CORE_4_00 0x40 +#define DWMAC_CORE_5_00 0x50 +#define DWMAC_CORE_5_10 0x51 #define STMMAC_CHAN0 0 /* Always supported and default for all chips */ /* These need to be power of two, and >= 4 */ @@ -174,6 +176,17 @@ struct stmmac_extra_stats { unsigned long tx_tso_nfrags; }; +/* Safety Feature statistics exposed by ethtool */ +struct stmmac_safety_stats { + unsigned long mac_errors[32]; + unsigned long mtl_errors[32]; + unsigned long dma_errors[32]; +}; + +/* Number of fields in Safety Stats */ +#define STMMAC_SAFETY_FEAT_SIZE \ + (sizeof(struct stmmac_safety_stats) / sizeof(unsigned long)) + /* CSR Frequency Access Defines*/ #define CSR_F_35M 35000000 #define CSR_F_60M 60000000 @@ -336,6 +349,8 @@ struct dma_features { /* TX and RX FIFO sizes */ unsigned int tx_fifo_size; unsigned int rx_fifo_size; + /* Automotive Safety Package */ + unsigned int asp; }; /* GMAC TX FIFO is 8K, Rx FIFO is 16K */ @@ -532,6 +547,13 @@ struct stmmac_ops { bool loopback); void (*pcs_rane)(void __iomem *ioaddr, bool restart); void (*pcs_get_adv_lp)(void __iomem *ioaddr, struct rgmii_adv *adv); + /* Safety Features */ + int (*safety_feat_config)(void __iomem *ioaddr, unsigned int asp); + bool (*safety_feat_irq_status)(struct net_device *ndev, + void __iomem *ioaddr, unsigned int asp, + struct stmmac_safety_stats *stats); + const char *(*safety_feat_dump)(struct stmmac_safety_stats *stats, + int index, unsigned long *count); }; /* PTP and HW Timer helpers */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 7761a26ec9c5..c7bff596c665 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -39,6 +39,7 @@ #define GMAC_HW_FEATURE0 0x0000011c #define GMAC_HW_FEATURE1 0x00000120 #define GMAC_HW_FEATURE2 0x00000124 +#define GMAC_HW_FEATURE3 0x00000128 #define GMAC_MDIO_ADDR 0x00000200 #define GMAC_MDIO_DATA 0x00000204 #define GMAC_ADDR_HIGH(reg) (0x300 + reg * 8) @@ -192,6 +193,9 @@ enum power_event { #define GMAC_HW_FEAT_TXQCNT GENMASK(9, 6) #define GMAC_HW_FEAT_RXQCNT GENMASK(3, 0) +/* MAC HW features3 bitmap */ +#define GMAC_HW_FEAT_ASP GENMASK(29, 28) + /* MAC HW ADDR regs */ #define GMAC_HI_DCS GENMASK(18, 16) #define GMAC_HI_DCS_SHIFT 16 diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 46b9ae20ff6c..a3af92ebbca8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -20,6 +20,7 @@ #include #include "stmmac_pcs.h" #include "dwmac4.h" +#include "dwmac5.h" static void dwmac4_core_init(struct mac_device_info *hw, struct net_device *dev) @@ -768,6 +769,40 @@ static const struct stmmac_ops dwmac410_ops = { .set_filter = dwmac4_set_filter, }; +static const struct stmmac_ops dwmac510_ops = { + .core_init = dwmac4_core_init, + .set_mac = stmmac_dwmac4_set_mac, + .rx_ipc = dwmac4_rx_ipc_enable, + .rx_queue_enable = dwmac4_rx_queue_enable, + .rx_queue_prio = dwmac4_rx_queue_priority, + .tx_queue_prio = dwmac4_tx_queue_priority, + .rx_queue_routing = dwmac4_rx_queue_routing, + .prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms, + .prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms, + .set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight, + .map_mtl_to_dma = dwmac4_map_mtl_dma, + .config_cbs = dwmac4_config_cbs, + .dump_regs = dwmac4_dump_regs, + .host_irq_status = dwmac4_irq_status, + .host_mtl_irq_status = dwmac4_irq_mtl_status, + .flow_ctrl = dwmac4_flow_ctrl, + .pmt = dwmac4_pmt, + .set_umac_addr = dwmac4_set_umac_addr, + .get_umac_addr = dwmac4_get_umac_addr, + .set_eee_mode = dwmac4_set_eee_mode, + .reset_eee_mode = dwmac4_reset_eee_mode, + .set_eee_timer = dwmac4_set_eee_timer, + .set_eee_pls = dwmac4_set_eee_pls, + .pcs_ctrl_ane = dwmac4_ctrl_ane, + .pcs_rane = dwmac4_rane, + .pcs_get_adv_lp = dwmac4_get_adv_lp, + .debug = dwmac4_debug, + .set_filter = dwmac4_set_filter, + .safety_feat_config = dwmac5_safety_feat_config, + .safety_feat_irq_status = dwmac5_safety_feat_irq_status, + .safety_feat_dump = dwmac5_safety_feat_dump, +}; + struct mac_device_info *dwmac4_setup(void __iomem *ioaddr, int mcbins, int perfect_uc_entries, int *synopsys_id) { @@ -808,7 +843,9 @@ struct mac_device_info *dwmac4_setup(void __iomem *ioaddr, int mcbins, else mac->dma = &dwmac4_dma_ops; - if (*synopsys_id >= DWMAC_CORE_4_00) + if (*synopsys_id >= DWMAC_CORE_5_10) + mac->mac = &dwmac510_ops; + else if (*synopsys_id >= DWMAC_CORE_4_00) mac->mac = &dwmac410_ops; else mac->mac = &dwmac4_ops; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index c110f6850ffa..d37d457306d1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -373,6 +373,12 @@ static void dwmac4_get_hw_feature(void __iomem *ioaddr, /* IEEE 1588-2002 */ dma_cap->time_stamp = 0; + + /* MAC HW feature3 */ + hw_cap = readl(ioaddr + GMAC_HW_FEATURE3); + + /* 5.10 Features */ + dma_cap->asp = (hw_cap & GMAC_HW_FEAT_ASP) >> 28; } /* Enable/disable TSO feature and set MSS */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c new file mode 100644 index 000000000000..860de39999c7 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// Copyright (c) 2017 Synopsys, Inc. and/or its affiliates. +// stmmac Support for 5.xx Ethernet QoS cores + +#include +#include +#include "common.h" +#include "dwmac4.h" +#include "dwmac5.h" + +struct dwmac5_error_desc { + bool valid; + const char *desc; + const char *detailed_desc; +}; + +#define STAT_OFF(field) offsetof(struct stmmac_safety_stats, field) + +static void dwmac5_log_error(struct net_device *ndev, u32 value, bool corr, + const char *module_name, const struct dwmac5_error_desc *desc, + unsigned long field_offset, struct stmmac_safety_stats *stats) +{ + unsigned long loc, mask; + u8 *bptr = (u8 *)stats; + unsigned long *ptr; + + ptr = (unsigned long *)(bptr + field_offset); + + mask = value; + for_each_set_bit(loc, &mask, 32) { + netdev_err(ndev, "Found %s error in %s: '%s: %s'\n", corr ? + "correctable" : "uncorrectable", module_name, + desc[loc].desc, desc[loc].detailed_desc); + + /* Update counters */ + ptr[loc]++; + } +} + +static const struct dwmac5_error_desc dwmac5_mac_errors[32]= { + { true, "ATPES", "Application Transmit Interface Parity Check Error" }, + { true, "TPES", "TSO Data Path Parity Check Error" }, + { true, "RDPES", "Read Descriptor Parity Check Error" }, + { true, "MPES", "MTL Data Path Parity Check Error" }, + { true, "MTSPES", "MTL TX Status Data Path Parity Check Error" }, + { true, "ARPES", "Application Receive Interface Data Path Parity Check Error" }, + { true, "CWPES", "CSR Write Data Path Parity Check Error" }, + { true, "ASRPES", "AXI Slave Read Data Path Parity Check Error" }, + { true, "TTES", "TX FSM Timeout Error" }, + { true, "RTES", "RX FSM Timeout Error" }, + { true, "CTES", "CSR FSM Timeout Error" }, + { true, "ATES", "APP FSM Timeout Error" }, + { true, "PTES", "PTP FSM Timeout Error" }, + { true, "T125ES", "TX125 FSM Timeout Error" }, + { true, "R125ES", "RX125 FSM Timeout Error" }, + { true, "RVCTES", "REV MDC FSM Timeout Error" }, + { true, "MSTTES", "Master Read/Write Timeout Error" }, + { true, "SLVTES", "Slave Read/Write Timeout Error" }, + { true, "ATITES", "Application Timeout on ATI Interface Error" }, + { true, "ARITES", "Application Timeout on ARI Interface Error" }, + { false, "UNKNOWN", "Unknown Error" }, /* 20 */ + { false, "UNKNOWN", "Unknown Error" }, /* 21 */ + { false, "UNKNOWN", "Unknown Error" }, /* 22 */ + { false, "UNKNOWN", "Unknown Error" }, /* 23 */ + { true, "FSMPES", "FSM State Parity Error" }, + { false, "UNKNOWN", "Unknown Error" }, /* 25 */ + { false, "UNKNOWN", "Unknown Error" }, /* 26 */ + { false, "UNKNOWN", "Unknown Error" }, /* 27 */ + { false, "UNKNOWN", "Unknown Error" }, /* 28 */ + { false, "UNKNOWN", "Unknown Error" }, /* 29 */ + { false, "UNKNOWN", "Unknown Error" }, /* 30 */ + { false, "UNKNOWN", "Unknown Error" }, /* 31 */ +}; + +static void dwmac5_handle_mac_err(struct net_device *ndev, + void __iomem *ioaddr, bool correctable, + struct stmmac_safety_stats *stats) +{ + u32 value; + + value = readl(ioaddr + MAC_DPP_FSM_INT_STATUS); + writel(value, ioaddr + MAC_DPP_FSM_INT_STATUS); + + dwmac5_log_error(ndev, value, correctable, "MAC", dwmac5_mac_errors, + STAT_OFF(mac_errors), stats); +} + +static const struct dwmac5_error_desc dwmac5_mtl_errors[32]= { + { true, "TXCES", "MTL TX Memory Error" }, + { true, "TXAMS", "MTL TX Memory Address Mismatch Error" }, + { true, "TXUES", "MTL TX Memory Error" }, + { false, "UNKNOWN", "Unknown Error" }, /* 3 */ + { true, "RXCES", "MTL RX Memory Error" }, + { true, "RXAMS", "MTL RX Memory Address Mismatch Error" }, + { true, "RXUES", "MTL RX Memory Error" }, + { false, "UNKNOWN", "Unknown Error" }, /* 7 */ + { true, "ECES", "MTL EST Memory Error" }, + { true, "EAMS", "MTL EST Memory Address Mismatch Error" }, + { true, "EUES", "MTL EST Memory Error" }, + { false, "UNKNOWN", "Unknown Error" }, /* 11 */ + { true, "RPCES", "MTL RX Parser Memory Error" }, + { true, "RPAMS", "MTL RX Parser Memory Address Mismatch Error" }, + { true, "RPUES", "MTL RX Parser Memory Error" }, + { false, "UNKNOWN", "Unknown Error" }, /* 15 */ + { false, "UNKNOWN", "Unknown Error" }, /* 16 */ + { false, "UNKNOWN", "Unknown Error" }, /* 17 */ + { false, "UNKNOWN", "Unknown Error" }, /* 18 */ + { false, "UNKNOWN", "Unknown Error" }, /* 19 */ + { false, "UNKNOWN", "Unknown Error" }, /* 20 */ + { false, "UNKNOWN", "Unknown Error" }, /* 21 */ + { false, "UNKNOWN", "Unknown Error" }, /* 22 */ + { false, "UNKNOWN", "Unknown Error" }, /* 23 */ + { false, "UNKNOWN", "Unknown Error" }, /* 24 */ + { false, "UNKNOWN", "Unknown Error" }, /* 25 */ + { false, "UNKNOWN", "Unknown Error" }, /* 26 */ + { false, "UNKNOWN", "Unknown Error" }, /* 27 */ + { false, "UNKNOWN", "Unknown Error" }, /* 28 */ + { false, "UNKNOWN", "Unknown Error" }, /* 29 */ + { false, "UNKNOWN", "Unknown Error" }, /* 30 */ + { false, "UNKNOWN", "Unknown Error" }, /* 31 */ +}; + +static void dwmac5_handle_mtl_err(struct net_device *ndev, + void __iomem *ioaddr, bool correctable, + struct stmmac_safety_stats *stats) +{ + u32 value; + + value = readl(ioaddr + MTL_ECC_INT_STATUS); + writel(value, ioaddr + MTL_ECC_INT_STATUS); + + dwmac5_log_error(ndev, value, correctable, "MTL", dwmac5_mtl_errors, + STAT_OFF(mtl_errors), stats); +} + +static const struct dwmac5_error_desc dwmac5_dma_errors[32]= { + { true, "TCES", "DMA TSO Memory Error" }, + { true, "TAMS", "DMA TSO Memory Address Mismatch Error" }, + { true, "TUES", "DMA TSO Memory Error" }, + { false, "UNKNOWN", "Unknown Error" }, /* 3 */ + { false, "UNKNOWN", "Unknown Error" }, /* 4 */ + { false, "UNKNOWN", "Unknown Error" }, /* 5 */ + { false, "UNKNOWN", "Unknown Error" }, /* 6 */ + { false, "UNKNOWN", "Unknown Error" }, /* 7 */ + { false, "UNKNOWN", "Unknown Error" }, /* 8 */ + { false, "UNKNOWN", "Unknown Error" }, /* 9 */ + { false, "UNKNOWN", "Unknown Error" }, /* 10 */ + { false, "UNKNOWN", "Unknown Error" }, /* 11 */ + { false, "UNKNOWN", "Unknown Error" }, /* 12 */ + { false, "UNKNOWN", "Unknown Error" }, /* 13 */ + { false, "UNKNOWN", "Unknown Error" }, /* 14 */ + { false, "UNKNOWN", "Unknown Error" }, /* 15 */ + { false, "UNKNOWN", "Unknown Error" }, /* 16 */ + { false, "UNKNOWN", "Unknown Error" }, /* 17 */ + { false, "UNKNOWN", "Unknown Error" }, /* 18 */ + { false, "UNKNOWN", "Unknown Error" }, /* 19 */ + { false, "UNKNOWN", "Unknown Error" }, /* 20 */ + { false, "UNKNOWN", "Unknown Error" }, /* 21 */ + { false, "UNKNOWN", "Unknown Error" }, /* 22 */ + { false, "UNKNOWN", "Unknown Error" }, /* 23 */ + { false, "UNKNOWN", "Unknown Error" }, /* 24 */ + { false, "UNKNOWN", "Unknown Error" }, /* 25 */ + { false, "UNKNOWN", "Unknown Error" }, /* 26 */ + { false, "UNKNOWN", "Unknown Error" }, /* 27 */ + { false, "UNKNOWN", "Unknown Error" }, /* 28 */ + { false, "UNKNOWN", "Unknown Error" }, /* 29 */ + { false, "UNKNOWN", "Unknown Error" }, /* 30 */ + { false, "UNKNOWN", "Unknown Error" }, /* 31 */ +}; + +static void dwmac5_handle_dma_err(struct net_device *ndev, + void __iomem *ioaddr, bool correctable, + struct stmmac_safety_stats *stats) +{ + u32 value; + + value = readl(ioaddr + DMA_ECC_INT_STATUS); + writel(value, ioaddr + DMA_ECC_INT_STATUS); + + dwmac5_log_error(ndev, value, correctable, "DMA", dwmac5_dma_errors, + STAT_OFF(dma_errors), stats); +} + +int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp) +{ + u32 value; + + if (!asp) + return -EINVAL; + + /* 1. Enable Safety Features */ + value = readl(ioaddr + MTL_ECC_CONTROL); + value |= TSOEE; /* TSO ECC */ + value |= MRXPEE; /* MTL RX Parser ECC */ + value |= MESTEE; /* MTL EST ECC */ + value |= MRXEE; /* MTL RX FIFO ECC */ + value |= MTXEE; /* MTL TX FIFO ECC */ + writel(value, ioaddr + MTL_ECC_CONTROL); + + /* 2. Enable MTL Safety Interrupts */ + value = readl(ioaddr + MTL_ECC_INT_ENABLE); + value |= RPCEIE; /* RX Parser Memory Correctable Error */ + value |= ECEIE; /* EST Memory Correctable Error */ + value |= RXCEIE; /* RX Memory Correctable Error */ + value |= TXCEIE; /* TX Memory Correctable Error */ + writel(value, ioaddr + MTL_ECC_INT_ENABLE); + + /* 3. Enable DMA Safety Interrupts */ + value = readl(ioaddr + DMA_ECC_INT_ENABLE); + value |= TCEIE; /* TSO Memory Correctable Error */ + writel(value, ioaddr + DMA_ECC_INT_ENABLE); + + /* Only ECC Protection for External Memory feature is selected */ + if (asp <= 0x1) + return 0; + + /* 5. Enable Parity and Timeout for FSM */ + value = readl(ioaddr + MAC_FSM_CONTROL); + value |= PRTYEN; /* FSM Parity Feature */ + value |= TMOUTEN; /* FSM Timeout Feature */ + writel(value, ioaddr + MAC_FSM_CONTROL); + + /* 4. Enable Data Parity Protection */ + value = readl(ioaddr + MTL_DPP_CONTROL); + value |= EDPP; + writel(value, ioaddr + MTL_DPP_CONTROL); + + /* + * All the Automotive Safety features are selected without the "Parity + * Port Enable for external interface" feature. + */ + if (asp <= 0x2) + return 0; + + value |= EPSI; + writel(value, ioaddr + MTL_DPP_CONTROL); + return 0; +} + +bool dwmac5_safety_feat_irq_status(struct net_device *ndev, + void __iomem *ioaddr, unsigned int asp, + struct stmmac_safety_stats *stats) +{ + bool ret = false, err, corr; + u32 mtl, dma; + + if (!asp) + return false; + + mtl = readl(ioaddr + MTL_SAFETY_INT_STATUS); + dma = readl(ioaddr + DMA_SAFETY_INT_STATUS); + + err = (mtl & MCSIS) || (dma & MCSIS); + corr = false; + if (err) { + dwmac5_handle_mac_err(ndev, ioaddr, corr, stats); + ret |= !corr; + } + + err = (mtl & (MEUIS | MECIS)) || (dma & (MSUIS | MSCIS)); + corr = (mtl & MECIS) || (dma & MSCIS); + if (err) { + dwmac5_handle_mtl_err(ndev, ioaddr, corr, stats); + ret |= !corr; + } + + err = dma & (DEUIS | DECIS); + corr = dma & DECIS; + if (err) { + dwmac5_handle_dma_err(ndev, ioaddr, corr, stats); + ret |= !corr; + } + + return ret; +} + +static const struct dwmac5_error { + const struct dwmac5_error_desc *desc; +} dwmac5_all_errors[] = { + { dwmac5_mac_errors }, + { dwmac5_mtl_errors }, + { dwmac5_dma_errors }, +}; + +const char *dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats, + int index, unsigned long *count) +{ + int module = index / 32, offset = index % 32; + unsigned long *ptr = (unsigned long *)stats; + + if (module >= ARRAY_SIZE(dwmac5_all_errors)) + return NULL; + if (!dwmac5_all_errors[module].desc[offset].valid) + return NULL; + if (count) + *count = *(ptr + index); + return dwmac5_all_errors[module].desc[offset].desc; +} diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h new file mode 100644 index 000000000000..a0d2c44711b9 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// Copyright (c) 2017 Synopsys, Inc. and/or its affiliates. +// stmmac Support for 5.xx Ethernet QoS cores + +#ifndef __DWMAC5_H__ +#define __DWMAC5_H__ + +#define MAC_DPP_FSM_INT_STATUS 0x00000140 +#define MAC_AXI_SLV_DPE_ADDR_STATUS 0x00000144 +#define MAC_FSM_CONTROL 0x00000148 +#define PRTYEN BIT(1) +#define TMOUTEN BIT(0) + +#define MTL_ECC_CONTROL 0x00000cc0 +#define TSOEE BIT(4) +#define MRXPEE BIT(3) +#define MESTEE BIT(2) +#define MRXEE BIT(1) +#define MTXEE BIT(0) + +#define MTL_SAFETY_INT_STATUS 0x00000cc4 +#define MCSIS BIT(31) +#define MEUIS BIT(1) +#define MECIS BIT(0) +#define MTL_ECC_INT_ENABLE 0x00000cc8 +#define RPCEIE BIT(12) +#define ECEIE BIT(8) +#define RXCEIE BIT(4) +#define TXCEIE BIT(0) +#define MTL_ECC_INT_STATUS 0x00000ccc +#define MTL_DPP_CONTROL 0x00000ce0 +#define EPSI BIT(2) +#define OPE BIT(1) +#define EDPP BIT(0) + +#define DMA_SAFETY_INT_STATUS 0x00001080 +#define MSUIS BIT(29) +#define MSCIS BIT(28) +#define DEUIS BIT(1) +#define DECIS BIT(0) +#define DMA_ECC_INT_ENABLE 0x00001084 +#define TCEIE BIT(0) +#define DMA_ECC_INT_STATUS 0x00001088 + +int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp); +bool dwmac5_safety_feat_irq_status(struct net_device *ndev, + void __iomem *ioaddr, unsigned int asp, + struct stmmac_safety_stats *stats); +const char *dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats, + int index, unsigned long *count); + +#endif /* __DWMAC5_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 1485d8fcfaa9..da50451f8999 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -114,6 +114,7 @@ struct stmmac_priv { int mii_irq[PHY_MAX_ADDR]; struct stmmac_extra_stats xstats ____cacheline_aligned_in_smp; + struct stmmac_safety_stats sstats; struct plat_stmmacenet_data *plat; struct dma_features dma_cap; struct stmmac_counters mmc; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index af30b4857c3b..2c6ed47704fc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -523,11 +523,23 @@ stmmac_set_pauseparam(struct net_device *netdev, static void stmmac_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 *data) { + const char *(*dump)(struct stmmac_safety_stats *stats, int index, + unsigned long *count); struct stmmac_priv *priv = netdev_priv(dev); u32 rx_queues_count = priv->plat->rx_queues_to_use; u32 tx_queues_count = priv->plat->tx_queues_to_use; + unsigned long count; int i, j = 0; + if (priv->dma_cap.asp && priv->hw->mac->safety_feat_dump) { + dump = priv->hw->mac->safety_feat_dump; + + for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) { + if (dump(&priv->sstats, i, &count)) + data[j++] = count; + } + } + /* Update the DMA HW counters for dwmac10/100 */ if (priv->hw->dma->dma_diagnostic_fr) priv->hw->dma->dma_diagnostic_fr(&dev->stats, @@ -569,7 +581,9 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, static int stmmac_get_sset_count(struct net_device *netdev, int sset) { struct stmmac_priv *priv = netdev_priv(netdev); - int len; + const char *(*dump)(struct stmmac_safety_stats *stats, int index, + unsigned long *count); + int i, len, safety_len = 0; switch (sset) { case ETH_SS_STATS: @@ -577,6 +591,16 @@ static int stmmac_get_sset_count(struct net_device *netdev, int sset) if (priv->dma_cap.rmon) len += STMMAC_MMC_STATS_LEN; + if (priv->dma_cap.asp && priv->hw->mac->safety_feat_dump) { + dump = priv->hw->mac->safety_feat_dump; + + for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) { + if (dump(&priv->sstats, i, NULL)) + safety_len++; + } + + len += safety_len; + } return len; default: @@ -589,9 +613,22 @@ static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data) int i; u8 *p = data; struct stmmac_priv *priv = netdev_priv(dev); + const char *(*dump)(struct stmmac_safety_stats *stats, int index, + unsigned long *count); switch (stringset) { case ETH_SS_STATS: + if (priv->dma_cap.asp && priv->hw->mac->safety_feat_dump) { + dump = priv->hw->mac->safety_feat_dump; + for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) { + const char *desc = dump(&priv->sstats, i, NULL); + + if (desc) { + memcpy(p, desc, ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + } + } if (priv->dma_cap.rmon) for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) { memcpy(p, stmmac_mmc[i].stat_string, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b75ecf3d19fe..9a16931ce39d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2014,6 +2014,22 @@ static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode, } } +static bool stmmac_safety_feat_interrupt(struct stmmac_priv *priv) +{ + bool ret = false; + + /* Safety features are only available in cores >= 5.10 */ + if (priv->synopsys_id < DWMAC_CORE_5_10) + return ret; + if (priv->hw->mac->safety_feat_irq_status) + ret = priv->hw->mac->safety_feat_irq_status(priv->dev, + priv->ioaddr, priv->dma_cap.asp, &priv->sstats); + + if (ret) + stmmac_global_err(priv); + return ret; +} + /** * stmmac_dma_interrupt - DMA ISR * @priv: driver private structure @@ -2503,6 +2519,17 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv) stmmac_mac_config_rx_queues_routing(priv); } +static void stmmac_safety_feat_configuration(struct stmmac_priv *priv) +{ + if (priv->hw->mac->safety_feat_config && priv->dma_cap.asp) { + netdev_info(priv->dev, "Enabling Safety Features\n"); + priv->hw->mac->safety_feat_config(priv->ioaddr, + priv->dma_cap.asp); + } else { + netdev_info(priv->dev, "No Safety Features support found\n"); + } +} + /** * stmmac_hw_setup - setup mac in a usable state. * @dev : pointer to the device structure. @@ -2554,6 +2581,10 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) if (priv->synopsys_id >= DWMAC_CORE_4_00) stmmac_mtl_configuration(priv); + /* Initialize Safety Features */ + if (priv->synopsys_id >= DWMAC_CORE_5_10) + stmmac_safety_feat_configuration(priv); + ret = priv->hw->mac->rx_ipc(priv->hw); if (!ret) { netdev_warn(priv->dev, "RX IPC Checksum Offload disabled\n"); @@ -3729,6 +3760,9 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) /* Check if adapter is up */ if (test_bit(STMMAC_DOWN, &priv->state)) return IRQ_HANDLED; + /* Check if a fatal error happened */ + if (stmmac_safety_feat_interrupt(priv)) + return IRQ_HANDLED; /* To handle GMAC own interrupts */ if ((priv->plat->has_gmac) || (priv->plat->has_gmac4)) {