net: stmmac: introduce MSI Interrupt routines for mac, safety, RX & TX
authorOng Boon Leong <boon.leong.ong@intel.com>
Thu, 25 Mar 2021 17:39:14 +0000 (01:39 +0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 26 Mar 2021 00:37:30 +0000 (17:37 -0700)
Now we introduce MSI interrupt service routines and hook these routines
up if stmmac_open() sees valid irq line being requested:-

stmmac_mac_interrupt()    :- MAC (dev->irq), WOL (wol_irq), LPI (lpi_irq)
stmmac_safety_interrupt() :- Safety Feat Correctible Error (sfty_ce_irq)
                             & Uncorrectible Error (sfty_ue_irq)
stmmac_msi_intr_rx()      :- For all RX MSI irq (rx_irq)
stmmac_msi_intr_tx()      :- For all TX MSI irq (tx_irq)

Each of IRQs will have its unique name so that we can differentiate
them easily under /proc/interrupts.

Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com>
Signed-off-by: Voon Weifeng <weifeng.voon@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/stmicro/stmmac/common.h
drivers/net/ethernet/stmicro/stmmac/stmmac.h
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
include/linux/stmmac.h

index 5afb36a..c54a56b 100644 (file)
@@ -259,6 +259,9 @@ struct stmmac_safety_stats {
 #define DMA_HW_FEAT_ACTPHYIF   0x70000000      /* Active/selected PHY iface */
 #define DEFAULT_DMA_PBL                8
 
+/* MSI defines */
+#define STMMAC_MSI_VEC_MAX     32
+
 /* PCS status and mask defines */
 #define        PCS_ANE_IRQ             BIT(2)  /* PCS Auto-Negotiation */
 #define        PCS_LINK_IRQ            BIT(1)  /* PCS Link */
@@ -315,6 +318,18 @@ enum dma_irq_dir {
        DMA_DIR_RXTX = 0x3,
 };
 
+enum request_irq_err {
+       REQ_IRQ_ERR_ALL,
+       REQ_IRQ_ERR_TX,
+       REQ_IRQ_ERR_RX,
+       REQ_IRQ_ERR_SFTY_UE,
+       REQ_IRQ_ERR_SFTY_CE,
+       REQ_IRQ_ERR_LPI,
+       REQ_IRQ_ERR_WOL,
+       REQ_IRQ_ERR_MAC,
+       REQ_IRQ_ERR_NO,
+};
+
 /* EEE and LPI defines */
 #define        CORE_IRQ_TX_PATH_IN_LPI_MODE    (1 << 0)
 #define        CORE_IRQ_TX_PATH_EXIT_LPI_MODE  (1 << 1)
index 4faad33..9966f6f 100644 (file)
@@ -30,6 +30,10 @@ struct stmmac_resources {
        int wol_irq;
        int lpi_irq;
        int irq;
+       int sfty_ce_irq;
+       int sfty_ue_irq;
+       int rx_irq[MTL_MAX_RX_QUEUES];
+       int tx_irq[MTL_MAX_TX_QUEUES];
 };
 
 struct stmmac_tx_info {
@@ -225,6 +229,18 @@ struct stmmac_priv {
        void __iomem *mmcaddr;
        void __iomem *ptpaddr;
        unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+       int sfty_ce_irq;
+       int sfty_ue_irq;
+       int rx_irq[MTL_MAX_RX_QUEUES];
+       int tx_irq[MTL_MAX_TX_QUEUES];
+       /*irq name */
+       char int_name_mac[IFNAMSIZ + 9];
+       char int_name_wol[IFNAMSIZ + 9];
+       char int_name_lpi[IFNAMSIZ + 9];
+       char int_name_sfty_ce[IFNAMSIZ + 10];
+       char int_name_sfty_ue[IFNAMSIZ + 10];
+       char int_name_rx_irq[MTL_MAX_TX_QUEUES][IFNAMSIZ + 14];
+       char int_name_tx_irq[MTL_MAX_TX_QUEUES][IFNAMSIZ + 18];
 
 #ifdef CONFIG_DEBUG_FS
        struct dentry *dbgfs_dir;
index abe990b..459477d 100644 (file)
@@ -105,6 +105,11 @@ module_param(chain_mode, int, 0444);
 MODULE_PARM_DESC(chain_mode, "To use chain instead of ring mode");
 
 static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
+/* For MSI interrupts handling */
+static irqreturn_t stmmac_mac_interrupt(int irq, void *dev_id);
+static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id);
+static irqreturn_t stmmac_msi_intr_tx(int irq, void *data);
+static irqreturn_t stmmac_msi_intr_rx(int irq, void *data);
 
 #ifdef CONFIG_DEBUG_FS
 static const struct net_device_ops stmmac_netdev_ops;
@@ -2986,6 +2991,260 @@ static void stmmac_hw_teardown(struct net_device *dev)
        clk_disable_unprepare(priv->plat->clk_ptp_ref);
 }
 
+static void stmmac_free_irq(struct net_device *dev,
+                           enum request_irq_err irq_err, int irq_idx)
+{
+       struct stmmac_priv *priv = netdev_priv(dev);
+       int j;
+
+       switch (irq_err) {
+       case REQ_IRQ_ERR_ALL:
+               irq_idx = priv->plat->tx_queues_to_use;
+               fallthrough;
+       case REQ_IRQ_ERR_TX:
+               for (j = irq_idx - 1; j >= 0; j--) {
+                       if (priv->tx_irq[j] > 0)
+                               free_irq(priv->tx_irq[j], &priv->tx_queue[j]);
+               }
+               irq_idx = priv->plat->rx_queues_to_use;
+               fallthrough;
+       case REQ_IRQ_ERR_RX:
+               for (j = irq_idx - 1; j >= 0; j--) {
+                       if (priv->rx_irq[j] > 0)
+                               free_irq(priv->rx_irq[j], &priv->rx_queue[j]);
+               }
+
+               if (priv->sfty_ue_irq > 0 && priv->sfty_ue_irq != dev->irq)
+                       free_irq(priv->sfty_ue_irq, dev);
+               fallthrough;
+       case REQ_IRQ_ERR_SFTY_UE:
+               if (priv->sfty_ce_irq > 0 && priv->sfty_ce_irq != dev->irq)
+                       free_irq(priv->sfty_ce_irq, dev);
+               fallthrough;
+       case REQ_IRQ_ERR_SFTY_CE:
+               if (priv->lpi_irq > 0 && priv->lpi_irq != dev->irq)
+                       free_irq(priv->lpi_irq, dev);
+               fallthrough;
+       case REQ_IRQ_ERR_LPI:
+               if (priv->wol_irq > 0 && priv->wol_irq != dev->irq)
+                       free_irq(priv->wol_irq, dev);
+               fallthrough;
+       case REQ_IRQ_ERR_WOL:
+               free_irq(dev->irq, dev);
+               fallthrough;
+       case REQ_IRQ_ERR_MAC:
+       case REQ_IRQ_ERR_NO:
+               /* If MAC IRQ request error, no more IRQ to free */
+               break;
+       }
+}
+
+static int stmmac_request_irq_multi_msi(struct net_device *dev)
+{
+       enum request_irq_err irq_err = REQ_IRQ_ERR_NO;
+       struct stmmac_priv *priv = netdev_priv(dev);
+       int irq_idx = 0;
+       char *int_name;
+       int ret;
+       int i;
+
+       /* For common interrupt */
+       int_name = priv->int_name_mac;
+       sprintf(int_name, "%s:%s", dev->name, "mac");
+       ret = request_irq(dev->irq, stmmac_mac_interrupt,
+                         0, int_name, dev);
+       if (unlikely(ret < 0)) {
+               netdev_err(priv->dev,
+                          "%s: alloc mac MSI %d (error: %d)\n",
+                          __func__, dev->irq, ret);
+               irq_err = REQ_IRQ_ERR_MAC;
+               goto irq_error;
+       }
+
+       /* Request the Wake IRQ in case of another line
+        * is used for WoL
+        */
+       if (priv->wol_irq > 0 && priv->wol_irq != dev->irq) {
+               int_name = priv->int_name_wol;
+               sprintf(int_name, "%s:%s", dev->name, "wol");
+               ret = request_irq(priv->wol_irq,
+                                 stmmac_mac_interrupt,
+                                 0, int_name, dev);
+               if (unlikely(ret < 0)) {
+                       netdev_err(priv->dev,
+                                  "%s: alloc wol MSI %d (error: %d)\n",
+                                  __func__, priv->wol_irq, ret);
+                       irq_err = REQ_IRQ_ERR_WOL;
+                       goto irq_error;
+               }
+       }
+
+       /* Request the LPI IRQ in case of another line
+        * is used for LPI
+        */
+       if (priv->lpi_irq > 0 && priv->lpi_irq != dev->irq) {
+               int_name = priv->int_name_lpi;
+               sprintf(int_name, "%s:%s", dev->name, "lpi");
+               ret = request_irq(priv->lpi_irq,
+                                 stmmac_mac_interrupt,
+                                 0, int_name, dev);
+               if (unlikely(ret < 0)) {
+                       netdev_err(priv->dev,
+                                  "%s: alloc lpi MSI %d (error: %d)\n",
+                                  __func__, priv->lpi_irq, ret);
+                       irq_err = REQ_IRQ_ERR_LPI;
+                       goto irq_error;
+               }
+       }
+
+       /* Request the Safety Feature Correctible Error line in
+        * case of another line is used
+        */
+       if (priv->sfty_ce_irq > 0 && priv->sfty_ce_irq != dev->irq) {
+               int_name = priv->int_name_sfty_ce;
+               sprintf(int_name, "%s:%s", dev->name, "safety-ce");
+               ret = request_irq(priv->sfty_ce_irq,
+                                 stmmac_safety_interrupt,
+                                 0, int_name, dev);
+               if (unlikely(ret < 0)) {
+                       netdev_err(priv->dev,
+                                  "%s: alloc sfty ce MSI %d (error: %d)\n",
+                                  __func__, priv->sfty_ce_irq, ret);
+                       irq_err = REQ_IRQ_ERR_SFTY_CE;
+                       goto irq_error;
+               }
+       }
+
+       /* Request the Safety Feature Uncorrectible Error line in
+        * case of another line is used
+        */
+       if (priv->sfty_ue_irq > 0 && priv->sfty_ue_irq != dev->irq) {
+               int_name = priv->int_name_sfty_ue;
+               sprintf(int_name, "%s:%s", dev->name, "safety-ue");
+               ret = request_irq(priv->sfty_ue_irq,
+                                 stmmac_safety_interrupt,
+                                 0, int_name, dev);
+               if (unlikely(ret < 0)) {
+                       netdev_err(priv->dev,
+                                  "%s: alloc sfty ue MSI %d (error: %d)\n",
+                                  __func__, priv->sfty_ue_irq, ret);
+                       irq_err = REQ_IRQ_ERR_SFTY_UE;
+                       goto irq_error;
+               }
+       }
+
+       /* Request Rx MSI irq */
+       for (i = 0; i < priv->plat->rx_queues_to_use; i++) {
+               if (priv->rx_irq[i] == 0)
+                       continue;
+
+               int_name = priv->int_name_rx_irq[i];
+               sprintf(int_name, "%s:%s-%d", dev->name, "rx", i);
+               ret = request_irq(priv->rx_irq[i],
+                                 stmmac_msi_intr_rx,
+                                 0, int_name, &priv->rx_queue[i]);
+               if (unlikely(ret < 0)) {
+                       netdev_err(priv->dev,
+                                  "%s: alloc rx-%d  MSI %d (error: %d)\n",
+                                  __func__, i, priv->rx_irq[i], ret);
+                       irq_err = REQ_IRQ_ERR_RX;
+                       irq_idx = i;
+                       goto irq_error;
+               }
+       }
+
+       /* Request Tx MSI irq */
+       for (i = 0; i < priv->plat->tx_queues_to_use; i++) {
+               if (priv->tx_irq[i] == 0)
+                       continue;
+
+               int_name = priv->int_name_tx_irq[i];
+               sprintf(int_name, "%s:%s-%d", dev->name, "tx", i);
+               ret = request_irq(priv->tx_irq[i],
+                                 stmmac_msi_intr_tx,
+                                 0, int_name, &priv->tx_queue[i]);
+               if (unlikely(ret < 0)) {
+                       netdev_err(priv->dev,
+                                  "%s: alloc tx-%d  MSI %d (error: %d)\n",
+                                  __func__, i, priv->tx_irq[i], ret);
+                       irq_err = REQ_IRQ_ERR_TX;
+                       irq_idx = i;
+                       goto irq_error;
+               }
+       }
+
+       return 0;
+
+irq_error:
+       stmmac_free_irq(dev, irq_err, irq_idx);
+       return ret;
+}
+
+static int stmmac_request_irq_single(struct net_device *dev)
+{
+       enum request_irq_err irq_err = REQ_IRQ_ERR_NO;
+       struct stmmac_priv *priv = netdev_priv(dev);
+       int ret;
+
+       ret = request_irq(dev->irq, stmmac_interrupt,
+                         IRQF_SHARED, dev->name, dev);
+       if (unlikely(ret < 0)) {
+               netdev_err(priv->dev,
+                          "%s: ERROR: allocating the IRQ %d (error: %d)\n",
+                          __func__, dev->irq, ret);
+               irq_err = REQ_IRQ_ERR_MAC;
+               return ret;
+       }
+
+       /* Request the Wake IRQ in case of another line
+        * is used for WoL
+        */
+       if (priv->wol_irq > 0 && priv->wol_irq != dev->irq) {
+               ret = request_irq(priv->wol_irq, stmmac_interrupt,
+                                 IRQF_SHARED, dev->name, dev);
+               if (unlikely(ret < 0)) {
+                       netdev_err(priv->dev,
+                                  "%s: ERROR: allocating the WoL IRQ %d (%d)\n",
+                                  __func__, priv->wol_irq, ret);
+                       irq_err = REQ_IRQ_ERR_WOL;
+                       return ret;
+               }
+       }
+
+       /* Request the IRQ lines */
+       if (priv->lpi_irq > 0 && priv->lpi_irq != dev->irq) {
+               ret = request_irq(priv->lpi_irq, stmmac_interrupt,
+                                 IRQF_SHARED, dev->name, dev);
+               if (unlikely(ret < 0)) {
+                       netdev_err(priv->dev,
+                                  "%s: ERROR: allocating the LPI IRQ %d (%d)\n",
+                                  __func__, priv->lpi_irq, ret);
+                       irq_err = REQ_IRQ_ERR_LPI;
+                       goto irq_error;
+               }
+       }
+
+       return 0;
+
+irq_error:
+       stmmac_free_irq(dev, irq_err, 0);
+       return ret;
+}
+
+static int stmmac_request_irq(struct net_device *dev)
+{
+       struct stmmac_priv *priv = netdev_priv(dev);
+       int ret;
+
+       /* Request the IRQ lines */
+       if (priv->plat->multi_msi_en)
+               ret = stmmac_request_irq_multi_msi(dev);
+       else
+               ret = stmmac_request_irq_single(dev);
+
+       return ret;
+}
+
 /**
  *  stmmac_open - open entry point of the driver
  *  @dev : pointer to the device structure.
@@ -3077,50 +3336,15 @@ static int stmmac_open(struct net_device *dev)
        /* We may have called phylink_speed_down before */
        phylink_speed_up(priv->phylink);
 
-       /* Request the IRQ lines */
-       ret = request_irq(dev->irq, stmmac_interrupt,
-                         IRQF_SHARED, dev->name, dev);
-       if (unlikely(ret < 0)) {
-               netdev_err(priv->dev,
-                          "%s: ERROR: allocating the IRQ %d (error: %d)\n",
-                          __func__, dev->irq, ret);
+       ret = stmmac_request_irq(dev);
+       if (ret)
                goto irq_error;
-       }
-
-       /* Request the Wake IRQ in case of another line is used for WoL */
-       if (priv->wol_irq != dev->irq) {
-               ret = request_irq(priv->wol_irq, stmmac_interrupt,
-                                 IRQF_SHARED, dev->name, dev);
-               if (unlikely(ret < 0)) {
-                       netdev_err(priv->dev,
-                                  "%s: ERROR: allocating the WoL IRQ %d (%d)\n",
-                                  __func__, priv->wol_irq, ret);
-                       goto wolirq_error;
-               }
-       }
-
-       /* Request the IRQ lines */
-       if (priv->lpi_irq > 0) {
-               ret = request_irq(priv->lpi_irq, stmmac_interrupt, IRQF_SHARED,
-                                 dev->name, dev);
-               if (unlikely(ret < 0)) {
-                       netdev_err(priv->dev,
-                                  "%s: ERROR: allocating the LPI IRQ %d (%d)\n",
-                                  __func__, priv->lpi_irq, ret);
-                       goto lpiirq_error;
-               }
-       }
 
        stmmac_enable_all_queues(priv);
        netif_tx_start_all_queues(priv->dev);
 
        return 0;
 
-lpiirq_error:
-       if (priv->wol_irq != dev->irq)
-               free_irq(priv->wol_irq, dev);
-wolirq_error:
-       free_irq(dev->irq, dev);
 irq_error:
        phylink_stop(priv->phylink);
 
@@ -3170,11 +3394,7 @@ static int stmmac_release(struct net_device *dev)
                hrtimer_cancel(&priv->tx_queue[chan].txtimer);
 
        /* Free the IRQ lines */
-       free_irq(dev->irq, dev);
-       if (priv->wol_irq != dev->irq)
-               free_irq(priv->wol_irq, dev);
-       if (priv->lpi_irq > 0)
-               free_irq(priv->lpi_irq, dev);
+       stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
 
        if (priv->eee_enabled) {
                priv->tx_path_in_lpi_mode = false;
@@ -4477,15 +4697,136 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t stmmac_mac_interrupt(int irq, void *dev_id)
+{
+       struct net_device *dev = (struct net_device *)dev_id;
+       struct stmmac_priv *priv = netdev_priv(dev);
+
+       if (unlikely(!dev)) {
+               netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__);
+               return IRQ_NONE;
+       }
+
+       /* Check if adapter is up */
+       if (test_bit(STMMAC_DOWN, &priv->state))
+               return IRQ_HANDLED;
+
+       /* To handle Common interrupts */
+       stmmac_common_interrupt(priv);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id)
+{
+       struct net_device *dev = (struct net_device *)dev_id;
+       struct stmmac_priv *priv = netdev_priv(dev);
+
+       if (unlikely(!dev)) {
+               netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__);
+               return IRQ_NONE;
+       }
+
+       /* Check if adapter is up */
+       if (test_bit(STMMAC_DOWN, &priv->state))
+               return IRQ_HANDLED;
+
+       /* Check if a fatal error happened */
+       stmmac_safety_feat_interrupt(priv);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t stmmac_msi_intr_tx(int irq, void *data)
+{
+       struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)data;
+       int chan = tx_q->queue_index;
+       struct stmmac_priv *priv;
+       int status;
+
+       priv = container_of(tx_q, struct stmmac_priv, tx_queue[chan]);
+
+       if (unlikely(!data)) {
+               netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__);
+               return IRQ_NONE;
+       }
+
+       /* Check if adapter is up */
+       if (test_bit(STMMAC_DOWN, &priv->state))
+               return IRQ_HANDLED;
+
+       status = stmmac_napi_check(priv, chan, DMA_DIR_TX);
+
+       if (unlikely(status & tx_hard_error_bump_tc)) {
+               /* Try to bump up the dma threshold on this failure */
+               if (unlikely(priv->xstats.threshold != SF_DMA_MODE) &&
+                   tc <= 256) {
+                       tc += 64;
+                       if (priv->plat->force_thresh_dma_mode)
+                               stmmac_set_dma_operation_mode(priv,
+                                                             tc,
+                                                             tc,
+                                                             chan);
+                       else
+                               stmmac_set_dma_operation_mode(priv,
+                                                             tc,
+                                                             SF_DMA_MODE,
+                                                             chan);
+                       priv->xstats.threshold = tc;
+               }
+       } else if (unlikely(status == tx_hard_error)) {
+               stmmac_tx_err(priv, chan);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t stmmac_msi_intr_rx(int irq, void *data)
+{
+       struct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)data;
+       int chan = rx_q->queue_index;
+       struct stmmac_priv *priv;
+
+       priv = container_of(rx_q, struct stmmac_priv, rx_queue[chan]);
+
+       if (unlikely(!data)) {
+               netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__);
+               return IRQ_NONE;
+       }
+
+       /* Check if adapter is up */
+       if (test_bit(STMMAC_DOWN, &priv->state))
+               return IRQ_HANDLED;
+
+       stmmac_napi_check(priv, chan, DMA_DIR_RX);
+
+       return IRQ_HANDLED;
+}
+
 #ifdef CONFIG_NET_POLL_CONTROLLER
 /* Polling receive - used by NETCONSOLE and other diagnostic tools
  * to allow network I/O with interrupts disabled.
  */
 static void stmmac_poll_controller(struct net_device *dev)
 {
-       disable_irq(dev->irq);
-       stmmac_interrupt(dev->irq, dev);
-       enable_irq(dev->irq);
+       struct stmmac_priv *priv = netdev_priv(dev);
+       int i;
+
+       /* If adapter is down, do nothing */
+       if (test_bit(STMMAC_DOWN, &priv->state))
+               return;
+
+       if (priv->plat->multi_msi_en) {
+               for (i = 0; i < priv->plat->rx_queues_to_use; i++)
+                       stmmac_msi_intr_rx(0, &priv->rx_queue[i]);
+
+               for (i = 0; i < priv->plat->tx_queues_to_use; i++)
+                       stmmac_msi_intr_tx(0, &priv->tx_queue[i]);
+       } else {
+               disable_irq(dev->irq);
+               stmmac_interrupt(dev->irq, dev);
+               enable_irq(dev->irq);
+       }
 }
 #endif
 
@@ -5283,6 +5624,12 @@ int stmmac_dvr_probe(struct device *device,
        priv->dev->irq = res->irq;
        priv->wol_irq = res->wol_irq;
        priv->lpi_irq = res->lpi_irq;
+       priv->sfty_ce_irq = res->sfty_ce_irq;
+       priv->sfty_ue_irq = res->sfty_ue_irq;
+       for (i = 0; i < MTL_MAX_RX_QUEUES; i++)
+               priv->rx_irq[i] = res->rx_irq[i];
+       for (i = 0; i < MTL_MAX_TX_QUEUES; i++)
+               priv->tx_irq[i] = res->tx_irq[i];
 
        if (!IS_ERR_OR_NULL(res->mac))
                memcpy(priv->dev->dev_addr, res->mac, ETH_ALEN);
index febdb43..afc12b9 100644 (file)
@@ -237,5 +237,13 @@ struct plat_stmmacenet_data {
        struct pci_dev *pdev;
        bool has_crossts;
        int int_snapshot_num;
+       bool multi_msi_en;
+       int msi_mac_vec;
+       int msi_wol_vec;
+       int msi_lpi_vec;
+       int msi_sfty_ce_vec;
+       int msi_sfty_ue_vec;
+       int msi_rx_base_vec;
+       int msi_tx_base_vec;
 };
 #endif