diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
index f1bb981cab7c..4ab7a3df98c6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
@@ -2,6 +2,7 @@
// Copyright (c) 2017 Synopsys, Inc. and/or its affiliates.
// stmmac Support for 5.xx Ethernet QoS cores
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/iopoll.h>
#include "common.h"
@@ -184,7 +185,8 @@ static void dwmac5_handle_dma_err(struct net_device *ndev,
}
int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp,
- struct stmmac_safety_feature_cfg *safety_feat_cfg)
+ struct stmmac_safety_feature_cfg *safety_feat_cfg,
+ unsigned int csr_clk_mhz)
{
struct stmmac_safety_feature_cfg all_safety_feats = {
.tsoee = 1,@@ -245,6 +247,22 @@ int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp,
value |= TMOUTEN; /* FSM Timeout Feature */
writel(value, ioaddr + MAC_FSM_CONTROL);
+ /* Program the FSM active timer so the timeout checker actually fires.
+ * TMR is the number of CSR clocks per 1us tic, i.e. csr_clk in MHz.
+ * Default to a 1s timeout when the platform did not select one.
+ */
+ if (safety_feat_cfg->tmouten && csr_clk_mhz) {
+ u32 ntmrmd = safety_feat_cfg->ntmrmd;
+
+ ntmrmd = (ntmrmd >= STMMAC_NTMRMD_1US &&
+ ntmrmd <= STMMAC_NTMRMD_64SEC) ?
+ ntmrmd : STMMAC_NTMRMD_1SEC;
+
+ writel(FIELD_PREP(MAC_FSM_ACT_TIMER_NTMRMD, ntmrmd) |
+ FIELD_PREP(MAC_FSM_ACT_TIMER_TMR, csr_clk_mhz),
+ ioaddr + MAC_FSM_ACT_TIMER);
+ }
+
/* 4. Enable Data Parity Protection */
value = readl(ioaddr + MTL_DPP_CONTROL);
if (safety_feat_cfg->edpp)diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h
index 00b151b3b688..734e7630f28d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h
@@ -8,8 +8,11 @@
#define MAC_DPP_FSM_INT_STATUS 0x00000140
#define MAC_AXI_SLV_DPE_ADDR_STATUS 0x00000144
#define MAC_FSM_CONTROL 0x00000148
+#define MAC_FSM_ACT_TIMER 0x0000014C
#define PRTYEN BIT(1)
#define TMOUTEN BIT(0)
+#define MAC_FSM_ACT_TIMER_TMR GENMASK(9, 0)
+#define MAC_FSM_ACT_TIMER_NTMRMD GENMASK(19, 16)
#define MAC_PPS_CONTROL 0x00000b70
#define PPS_MAXIDX(x) ((((x) + 1) * 8) - 1)
@@ -82,7 +85,8 @@
#define GMAC_INT_FPE_EN BIT(17)
int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp,
- struct stmmac_safety_feature_cfg *safety_cfg);
+ struct stmmac_safety_feature_cfg *safety_cfg,
+ unsigned int csr_clk_mhz);
int dwmac5_safety_feat_irq_status(struct net_device *ndev,
void __iomem *ioaddr, unsigned int asp,
struct stmmac_safety_stats *stats);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
index f8ab347f7b5b..1fd501728b18 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
@@ -169,8 +169,11 @@
#define XGMAC_HWFEAT_PCSEL GENMASK(1, 0)
#define XGMAC_MAC_DPP_FSM_INT_STATUS 0x00000150
#define XGMAC_MAC_FSM_CONTROL 0x00000158
+#define XGMAC_MAC_FSM_ACT_TIMER 0x0000015C
#define XGMAC_PRTYEN BIT(1)
#define XGMAC_TMOUTEN BIT(0)
+#define XGMAC_MAC_FSM_ACT_TIMER_TMR GENMASK(9, 0)
+#define XGMAC_MAC_FSM_ACT_TIMER_NTMRMD GENMASK(19, 16)
#define XGMAC_MDIO_ADDR 0x00000200
#define XGMAC_MDIO_DATA 0x00000204
#define XGMAC_MDIO_C22P 0x00000220
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index 7ad08651675e..dac85431e740 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -4,6 +4,7 @@
* stmmac XGMAC support.
*/
+#include <linux/bitfield.h>
#include <linux/bitrev.h>
#include <linux/crc32.h>
#include <linux/iopoll.h>
@@ -837,7 +838,8 @@ static void dwxgmac3_handle_dma_err(struct net_device *ndev,
static int
dwxgmac3_safety_feat_config(void __iomem *ioaddr, unsigned int asp,
- struct stmmac_safety_feature_cfg *safety_cfg)
+ struct stmmac_safety_feature_cfg *safety_cfg,
+ unsigned int csr_clk_mhz)
{
u32 value;
@@ -873,6 +875,22 @@ dwxgmac3_safety_feat_config(void __iomem *ioaddr, unsigned int asp,
value |= XGMAC_TMOUTEN; /* FSM Timeout Feature */
writel(value, ioaddr + XGMAC_MAC_FSM_CONTROL);
+ /* Program the FSM active timer so the timeout checker actually fires.
+ * TMR is the number of CSR clocks per 1us tic, i.e. csr_clk in MHz.
+ * Default to a 1s timeout when the platform did not select one.
+ */
+ if (safety_cfg && safety_cfg->tmouten && csr_clk_mhz) {
+ u32 ntmrmd = safety_cfg->ntmrmd;
+
+ ntmrmd = (ntmrmd >= STMMAC_NTMRMD_1US &&
+ ntmrmd <= STMMAC_NTMRMD_64SEC) ?
+ ntmrmd : STMMAC_NTMRMD_1SEC;
+
+ writel(FIELD_PREP(XGMAC_MAC_FSM_ACT_TIMER_NTMRMD, ntmrmd) |
+ FIELD_PREP(XGMAC_MAC_FSM_ACT_TIMER_TMR, csr_clk_mhz),
+ ioaddr + XGMAC_MAC_FSM_ACT_TIMER);
+ }
+
/* 5. Enable Data Path Parity Protection */
value = readl(ioaddr + XGMAC_MTL_DPP_CONTROL);
/* already enabled by default, explicit enable it again */diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index 8ae95e91a36a..395655b01ea8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -378,7 +378,8 @@ struct stmmac_ops {
bool srgmi_ral);
/* Safety Features */
int (*safety_feat_config)(void __iomem *ioaddr, unsigned int asp,
- struct stmmac_safety_feature_cfg *safety_cfg);
+ struct stmmac_safety_feature_cfg *safety_cfg,
+ unsigned int csr_clk_mhz);
int (*safety_feat_irq_status)(struct net_device *ndev,
void __iomem *ioaddr, unsigned int asp,
struct stmmac_safety_stats *stats);diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 552840d29668..1b3dd30db6dc 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -3691,10 +3691,16 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv)
static void stmmac_safety_feat_configuration(struct stmmac_priv *priv)
{
+ unsigned int csr_clk_mhz = 0;
+
if (priv->dma_cap.asp) {
netdev_info(priv->dev, "Enabling Safety Features\n");
+ if (priv->plat->stmmac_clk)
+ csr_clk_mhz = clk_get_rate(priv->plat->stmmac_clk) /
+ 1000000;
stmmac_safety_feat_config(priv, priv->ioaddr, priv->dma_cap.asp,
- priv->plat->safety_feat_cfg);
+ priv->plat->safety_feat_cfg,
+ csr_clk_mhz);
} else {
netdev_info(priv->dev, "No Safety Features support found\n");
}diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index 00be2df63d22..cb470a153bf8 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -168,8 +168,25 @@ struct stmmac_safety_feature_cfg {
u32 edpp;
u32 prtyen;
u32 tmouten;
+ /* FSM normal-mode timeout value (MAC_FSM_ACT_Timer.NTMRMD). The TMR
+ * field (CSR clocks per 1us) is derived from the CSR clock by stmmac.
+ * 0 selects the default 1s timeout.
+ */
+ u32 ntmrmd;
};
+/* MAC_FSM_ACT_Timer.NTMRMD timeout durations */
+#define STMMAC_NTMRMD_1US 0x1
+#define STMMAC_NTMRMD_4MS 0x2
+#define STMMAC_NTMRMD_16MS 0x3
+#define STMMAC_NTMRMD_64MS 0x4
+#define STMMAC_NTMRMD_256MS 0x5
+#define STMMAC_NTMRMD_1SEC 0x6
+#define STMMAC_NTMRMD_4SEC 0x7
+#define STMMAC_NTMRMD_16SEC 0x8
+#define STMMAC_NTMRMD_32SEC 0x9
+#define STMMAC_NTMRMD_64SEC 0xa
+
/* Addresses that may be customized by a platform */
struct dwmac4_addrs {
u32 dma_chan;
--