net: stmmac: fix FPE events losing
authorJianheng Zhang <Jianheng.Zhang@synopsys.com>
Fri, 1 Dec 2023 03:22:03 +0000 (03:22 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 13 Dec 2023 17:45:05 +0000 (18:45 +0100)
[ Upstream commit 37e4b8df27bc68340f3fc80dbb27e3549c7f881c ]

The status bits of register MAC_FPE_CTRL_STS are clear on read. Using
32-bit read for MAC_FPE_CTRL_STS in dwmac5_fpe_configure() and
dwmac5_fpe_send_mpacket() clear the status bits. Then the stmmac interrupt
handler missing FPE event status and leads to FPE handshaking failure and
retries.
To avoid clear status bits of MAC_FPE_CTRL_STS in dwmac5_fpe_configure()
and dwmac5_fpe_send_mpacket(), add fpe_csr to stmmac_fpe_cfg structure to
cache the control bits of MAC_FPE_CTRL_STS and to avoid reading
MAC_FPE_CTRL_STS in those methods.

Fixes: 5a5586112b92 ("net: stmmac: support FPE link partner hand-shaking procedure")
Reviewed-by: Serge Semin <fancer.lancer@gmail.com>
Signed-off-by: Jianheng Zhang <Jianheng.Zhang@synopsys.com>
Link: https://lore.kernel.org/r/CY5PR12MB637225A7CF529D5BE0FBE59CBF81A@CY5PR12MB6372.namprd12.prod.outlook.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/ethernet/stmicro/stmmac/dwmac5.c
drivers/net/ethernet/stmicro/stmmac/dwmac5.h
drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
drivers/net/ethernet/stmicro/stmmac/hwif.h
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
include/linux/stmmac.h

index e95d35f..8fd1675 100644 (file)
@@ -710,28 +710,22 @@ void dwmac5_est_irq_status(void __iomem *ioaddr, struct net_device *dev,
        }
 }
 
-void dwmac5_fpe_configure(void __iomem *ioaddr, u32 num_txq, u32 num_rxq,
+void dwmac5_fpe_configure(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg,
+                         u32 num_txq, u32 num_rxq,
                          bool enable)
 {
        u32 value;
 
-       if (!enable) {
-               value = readl(ioaddr + MAC_FPE_CTRL_STS);
-
-               value &= ~EFPE;
-
-               writel(value, ioaddr + MAC_FPE_CTRL_STS);
-               return;
+       if (enable) {
+               cfg->fpe_csr = EFPE;
+               value = readl(ioaddr + GMAC_RXQ_CTRL1);
+               value &= ~GMAC_RXQCTRL_FPRQ;
+               value |= (num_rxq - 1) << GMAC_RXQCTRL_FPRQ_SHIFT;
+               writel(value, ioaddr + GMAC_RXQ_CTRL1);
+       } else {
+               cfg->fpe_csr = 0;
        }
-
-       value = readl(ioaddr + GMAC_RXQ_CTRL1);
-       value &= ~GMAC_RXQCTRL_FPRQ;
-       value |= (num_rxq - 1) << GMAC_RXQCTRL_FPRQ_SHIFT;
-       writel(value, ioaddr + GMAC_RXQ_CTRL1);
-
-       value = readl(ioaddr + MAC_FPE_CTRL_STS);
-       value |= EFPE;
-       writel(value, ioaddr + MAC_FPE_CTRL_STS);
+       writel(cfg->fpe_csr, ioaddr + MAC_FPE_CTRL_STS);
 }
 
 int dwmac5_fpe_irq_status(void __iomem *ioaddr, struct net_device *dev)
@@ -741,6 +735,9 @@ int dwmac5_fpe_irq_status(void __iomem *ioaddr, struct net_device *dev)
 
        status = FPE_EVENT_UNKNOWN;
 
+       /* Reads from the MAC_FPE_CTRL_STS register should only be performed
+        * here, since the status flags of MAC_FPE_CTRL_STS are "clear on read"
+        */
        value = readl(ioaddr + MAC_FPE_CTRL_STS);
 
        if (value & TRSP) {
@@ -766,19 +763,15 @@ int dwmac5_fpe_irq_status(void __iomem *ioaddr, struct net_device *dev)
        return status;
 }
 
-void dwmac5_fpe_send_mpacket(void __iomem *ioaddr, enum stmmac_mpacket_type type)
+void dwmac5_fpe_send_mpacket(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg,
+                            enum stmmac_mpacket_type type)
 {
-       u32 value;
+       u32 value = cfg->fpe_csr;
 
-       value = readl(ioaddr + MAC_FPE_CTRL_STS);
-
-       if (type == MPACKET_VERIFY) {
-               value &= ~SRSP;
+       if (type == MPACKET_VERIFY)
                value |= SVER;
-       } else {
-               value &= ~SVER;
+       else if (type == MPACKET_RESPONSE)
                value |= SRSP;
-       }
 
        writel(value, ioaddr + MAC_FPE_CTRL_STS);
 }
index 53c138d..34e6207 100644 (file)
@@ -153,9 +153,11 @@ int dwmac5_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg,
                         unsigned int ptp_rate);
 void dwmac5_est_irq_status(void __iomem *ioaddr, struct net_device *dev,
                           struct stmmac_extra_stats *x, u32 txqcnt);
-void dwmac5_fpe_configure(void __iomem *ioaddr, u32 num_txq, u32 num_rxq,
+void dwmac5_fpe_configure(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg,
+                         u32 num_txq, u32 num_rxq,
                          bool enable);
 void dwmac5_fpe_send_mpacket(void __iomem *ioaddr,
+                            struct stmmac_fpe_cfg *cfg,
                             enum stmmac_mpacket_type type);
 int dwmac5_fpe_irq_status(void __iomem *ioaddr, struct net_device *dev);
 
index 453e88b..a74e71d 100644 (file)
@@ -1484,7 +1484,8 @@ static int dwxgmac3_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg,
        return 0;
 }
 
-static void dwxgmac3_fpe_configure(void __iomem *ioaddr, u32 num_txq,
+static void dwxgmac3_fpe_configure(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg,
+                                  u32 num_txq,
                                   u32 num_rxq, bool enable)
 {
        u32 value;
index b95d3e1..68aa2d5 100644 (file)
@@ -412,9 +412,11 @@ struct stmmac_ops {
                             unsigned int ptp_rate);
        void (*est_irq_status)(void __iomem *ioaddr, struct net_device *dev,
                               struct stmmac_extra_stats *x, u32 txqcnt);
-       void (*fpe_configure)(void __iomem *ioaddr, u32 num_txq, u32 num_rxq,
+       void (*fpe_configure)(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg,
+                             u32 num_txq, u32 num_rxq,
                              bool enable);
        void (*fpe_send_mpacket)(void __iomem *ioaddr,
+                                struct stmmac_fpe_cfg *cfg,
                                 enum stmmac_mpacket_type type);
        int (*fpe_irq_status)(void __iomem *ioaddr, struct net_device *dev);
 };
index 1fa4da9..69b9c71 100644 (file)
@@ -964,7 +964,8 @@ static void stmmac_fpe_link_state_handle(struct stmmac_priv *priv, bool is_up)
        bool *hs_enable = &fpe_cfg->hs_enable;
 
        if (is_up && *hs_enable) {
-               stmmac_fpe_send_mpacket(priv, priv->ioaddr, MPACKET_VERIFY);
+               stmmac_fpe_send_mpacket(priv, priv->ioaddr, fpe_cfg,
+                                       MPACKET_VERIFY);
        } else {
                *lo_state = FPE_STATE_OFF;
                *lp_state = FPE_STATE_OFF;
@@ -5803,6 +5804,7 @@ static void stmmac_fpe_event_status(struct stmmac_priv *priv, int status)
                /* If user has requested FPE enable, quickly response */
                if (*hs_enable)
                        stmmac_fpe_send_mpacket(priv, priv->ioaddr,
+                                               fpe_cfg,
                                                MPACKET_RESPONSE);
        }
 
@@ -7227,6 +7229,7 @@ static void stmmac_fpe_lp_task(struct work_struct *work)
                if (*lo_state == FPE_STATE_ENTERING_ON &&
                    *lp_state == FPE_STATE_ENTERING_ON) {
                        stmmac_fpe_configure(priv, priv->ioaddr,
+                                            fpe_cfg,
                                             priv->plat->tx_queues_to_use,
                                             priv->plat->rx_queues_to_use,
                                             *enable);
@@ -7245,6 +7248,7 @@ static void stmmac_fpe_lp_task(struct work_struct *work)
                        netdev_info(priv->dev, SEND_VERIFY_MPAKCET_FMT,
                                    *lo_state, *lp_state);
                        stmmac_fpe_send_mpacket(priv, priv->ioaddr,
+                                               fpe_cfg,
                                                MPACKET_VERIFY);
                }
                /* Sleep then retry */
@@ -7259,6 +7263,7 @@ void stmmac_fpe_handshake(struct stmmac_priv *priv, bool enable)
        if (priv->plat->fpe_cfg->hs_enable != enable) {
                if (enable) {
                        stmmac_fpe_send_mpacket(priv, priv->ioaddr,
+                                               priv->plat->fpe_cfg,
                                                MPACKET_VERIFY);
                } else {
                        priv->plat->fpe_cfg->lo_fpe_state = FPE_STATE_OFF;
@@ -7719,6 +7724,7 @@ int stmmac_suspend(struct device *dev)
        if (priv->dma_cap.fpesel) {
                /* Disable FPE */
                stmmac_fpe_configure(priv, priv->ioaddr,
+                                    priv->plat->fpe_cfg,
                                     priv->plat->tx_queues_to_use,
                                     priv->plat->rx_queues_to_use, false);
 
index ac41ef4..6ad3e0a 100644 (file)
@@ -1079,6 +1079,7 @@ disable:
 
        priv->plat->fpe_cfg->enable = false;
        stmmac_fpe_configure(priv, priv->ioaddr,
+                            priv->plat->fpe_cfg,
                             priv->plat->tx_queues_to_use,
                             priv->plat->rx_queues_to_use,
                             false);
index ce89cc3..e3f7ee1 100644 (file)
@@ -174,6 +174,7 @@ struct stmmac_fpe_cfg {
        bool hs_enable;                         /* FPE handshake enable */
        enum stmmac_fpe_state lp_fpe_state;     /* Link Partner FPE state */
        enum stmmac_fpe_state lo_fpe_state;     /* Local station FPE state */
+       u32 fpe_csr;                            /* MAC_FPE_CTRL_STS reg cache */
 };
 
 struct stmmac_safety_feature_cfg {