net: mvpp2: use the GoP interrupt for link status changes
authorAntoine Tenart <antoine.tenart@free-electrons.com>
Fri, 1 Sep 2017 09:04:54 +0000 (11:04 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 1 Sep 2017 17:08:35 +0000 (10:08 -0700)
This patch adds the GoP link interrupt support for when a port isn't
connected to a PHY. Because of this the phylib callback is never called
and the link status management isn't done. This patch use the GoP link
interrupt in such cases to still have a minimal link management. Without
this patch ports not connected to a PHY cannot work.

Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
Tested-by: Marcin Wojtas <mw@semihalf.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/marvell/mvpp2.c

index eebcf9c9e3d4dde736ff81f961cb3e91c4d78790..f37c05fed5bcf3c601ef64c912c9f58051fd7686 100644 (file)
 #define     MVPP2_GMAC_FLOW_CTRL_AUTONEG       BIT(11)
 #define     MVPP2_GMAC_CONFIG_FULL_DUPLEX      BIT(12)
 #define     MVPP2_GMAC_AN_DUPLEX_EN            BIT(13)
+#define MVPP2_GMAC_STATUS0                     0x10
+#define     MVPP2_GMAC_STATUS0_LINK_UP         BIT(0)
 #define MVPP2_GMAC_PORT_FIFO_CFG_1_REG         0x1c
 #define     MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS     6
 #define     MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0
 #define     MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(v)  (((v) << 6) & \
                                        MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK)
+#define MVPP22_GMAC_INT_STAT                   0x20
+#define     MVPP22_GMAC_INT_STAT_LINK          BIT(1)
+#define MVPP22_GMAC_INT_MASK                   0x24
+#define     MVPP22_GMAC_INT_MASK_LINK_STAT     BIT(1)
 #define MVPP22_GMAC_CTRL_4_REG                 0x90
 #define     MVPP22_CTRL4_EXT_PIN_GMII_SEL      BIT(0)
 #define     MVPP22_CTRL4_DP_CLK_SEL            BIT(5)
 #define     MVPP22_CTRL4_SYNC_BYPASS_DIS       BIT(6)
 #define     MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE  BIT(7)
+#define MVPP22_GMAC_INT_SUM_MASK               0xa4
+#define     MVPP22_GMAC_INT_SUM_MASK_LINK_STAT BIT(1)
 
 /* Per-port XGMAC registers. PPv2.2 only, only for GOP port 0,
  * relative to port->base.
 #define MVPP22_XLG_CTRL1_REG                   0x104
 #define     MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS       0
 #define     MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK       0x1fff
+#define MVPP22_XLG_STATUS                      0x10c
+#define     MVPP22_XLG_STATUS_LINK_UP          BIT(0)
+#define MVPP22_XLG_INT_STAT                    0x114
+#define     MVPP22_XLG_INT_STAT_LINK           BIT(1)
+#define MVPP22_XLG_INT_MASK                    0x118
+#define     MVPP22_XLG_INT_MASK_LINK           BIT(1)
 #define MVPP22_XLG_CTRL3_REG                   0x11c
 #define     MVPP22_XLG_CTRL3_MACMODESELECT_MASK        (7 << 13)
 #define     MVPP22_XLG_CTRL3_MACMODESELECT_GMAC        (0 << 13)
 #define     MVPP22_XLG_CTRL3_MACMODESELECT_10G (1 << 13)
-
+#define MVPP22_XLG_EXT_INT_MASK                        0x15c
+#define     MVPP22_XLG_EXT_INT_MASK_XLG                BIT(1)
+#define     MVPP22_XLG_EXT_INT_MASK_GIG                BIT(2)
 #define MVPP22_XLG_CTRL4_REG                   0x184
 #define     MVPP22_XLG_CTRL4_FWD_FC            BIT(5)
 #define     MVPP22_XLG_CTRL4_FWD_PFC           BIT(6)
@@ -837,6 +853,8 @@ struct mvpp2_port {
         */
        int gop_id;
 
+       int link_irq;
+
        struct mvpp2 *priv;
 
        /* Per-port registers' base address */
@@ -4422,6 +4440,68 @@ invalid_conf:
        return -EINVAL;
 }
 
+static void mvpp22_gop_unmask_irq(struct mvpp2_port *port)
+{
+       u32 val;
+
+       if (phy_interface_mode_is_rgmii(port->phy_interface) ||
+           port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
+               /* Enable the GMAC link status irq for this port */
+               val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK);
+               val |= MVPP22_GMAC_INT_SUM_MASK_LINK_STAT;
+               writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK);
+       }
+
+       if (port->gop_id == 0) {
+               /* Enable the XLG/GIG irqs for this port */
+               val = readl(port->base + MVPP22_XLG_EXT_INT_MASK);
+               if (port->phy_interface == PHY_INTERFACE_MODE_10GKR)
+                       val |= MVPP22_XLG_EXT_INT_MASK_XLG;
+               else
+                       val |= MVPP22_XLG_EXT_INT_MASK_GIG;
+               writel(val, port->base + MVPP22_XLG_EXT_INT_MASK);
+       }
+}
+
+static void mvpp22_gop_mask_irq(struct mvpp2_port *port)
+{
+       u32 val;
+
+       if (port->gop_id == 0) {
+               val = readl(port->base + MVPP22_XLG_EXT_INT_MASK);
+               val &= ~(MVPP22_XLG_EXT_INT_MASK_XLG |
+                        MVPP22_XLG_EXT_INT_MASK_GIG);
+               writel(val, port->base + MVPP22_XLG_EXT_INT_MASK);
+       }
+
+       if (phy_interface_mode_is_rgmii(port->phy_interface) ||
+           port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
+               val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK);
+               val &= ~MVPP22_GMAC_INT_SUM_MASK_LINK_STAT;
+               writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK);
+       }
+}
+
+static void mvpp22_gop_setup_irq(struct mvpp2_port *port)
+{
+       u32 val;
+
+       if (phy_interface_mode_is_rgmii(port->phy_interface) ||
+           port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
+               val = readl(port->base + MVPP22_GMAC_INT_MASK);
+               val |= MVPP22_GMAC_INT_MASK_LINK_STAT;
+               writel(val, port->base + MVPP22_GMAC_INT_MASK);
+       }
+
+       if (port->gop_id == 0) {
+               val = readl(port->base + MVPP22_XLG_INT_MASK);
+               val |= MVPP22_XLG_INT_MASK_LINK;
+               writel(val, port->base + MVPP22_XLG_INT_MASK);
+       }
+
+       mvpp22_gop_unmask_irq(port);
+}
+
 static int mvpp22_comphy_init(struct mvpp2_port *port)
 {
        enum phy_mode mode;
@@ -5726,6 +5806,60 @@ static irqreturn_t mvpp2_isr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+/* Per-port interrupt for link status changes */
+static irqreturn_t mvpp2_link_status_isr(int irq, void *dev_id)
+{
+       struct mvpp2_port *port = (struct mvpp2_port *)dev_id;
+       struct net_device *dev = port->dev;
+       bool event = false, link = false;
+       u32 val;
+
+       mvpp22_gop_mask_irq(port);
+
+       if (port->gop_id == 0 &&
+           port->phy_interface == PHY_INTERFACE_MODE_10GKR) {
+               val = readl(port->base + MVPP22_XLG_INT_STAT);
+               if (val & MVPP22_XLG_INT_STAT_LINK) {
+                       event = true;
+                       val = readl(port->base + MVPP22_XLG_STATUS);
+                       if (val & MVPP22_XLG_STATUS_LINK_UP)
+                               link = true;
+               }
+       } else if (phy_interface_mode_is_rgmii(port->phy_interface) ||
+                  port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
+               val = readl(port->base + MVPP22_GMAC_INT_STAT);
+               if (val & MVPP22_GMAC_INT_STAT_LINK) {
+                       event = true;
+                       val = readl(port->base + MVPP2_GMAC_STATUS0);
+                       if (val & MVPP2_GMAC_STATUS0_LINK_UP)
+                               link = true;
+               }
+       }
+
+       if (!netif_running(dev) || !event)
+               goto handled;
+
+       if (link) {
+               mvpp2_interrupts_enable(port);
+
+               mvpp2_egress_enable(port);
+               mvpp2_ingress_enable(port);
+               netif_carrier_on(dev);
+               netif_tx_wake_all_queues(dev);
+       } else {
+               netif_tx_stop_all_queues(dev);
+               netif_carrier_off(dev);
+               mvpp2_ingress_disable(port);
+               mvpp2_egress_disable(port);
+
+               mvpp2_interrupts_disable(port);
+       }
+
+handled:
+       mvpp22_gop_unmask_irq(port);
+       return IRQ_HANDLED;
+}
+
 static void mvpp2_gmac_set_autoneg(struct mvpp2_port *port,
                                   struct phy_device *phydev)
 {
@@ -5754,7 +5888,6 @@ static void mvpp2_gmac_set_autoneg(struct mvpp2_port *port,
                val |= MVPP2_GMAC_CONFIG_MII_SPEED;
 
        writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-
 }
 
 /* Adjust link */
@@ -6633,6 +6766,7 @@ static void mvpp2_irqs_deinit(struct mvpp2_port *port)
 static int mvpp2_open(struct net_device *dev)
 {
        struct mvpp2_port *port = netdev_priv(dev);
+       struct mvpp2 *priv = port->priv;
        unsigned char mac_bcast[ETH_ALEN] = {
                        0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
        int err;
@@ -6678,12 +6812,24 @@ static int mvpp2_open(struct net_device *dev)
                goto err_cleanup_txqs;
        }
 
+       if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) {
+               err = request_irq(port->link_irq, mvpp2_link_status_isr, 0,
+                                 dev->name, port);
+               if (err) {
+                       netdev_err(port->dev, "cannot request link IRQ %d\n",
+                                  port->link_irq);
+                       goto err_free_irq;
+               }
+
+               mvpp22_gop_setup_irq(port);
+       }
+
        /* In default link is down */
        netif_carrier_off(port->dev);
 
        err = mvpp2_phy_connect(port);
        if (err < 0)
-               goto err_free_irq;
+               goto err_free_link_irq;
 
        /* Unmask interrupts on all CPUs */
        on_each_cpu(mvpp2_interrupts_unmask, port, 1);
@@ -6693,6 +6839,9 @@ static int mvpp2_open(struct net_device *dev)
 
        return 0;
 
+err_free_link_irq:
+       if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq)
+               free_irq(port->link_irq, port);
 err_free_irq:
        mvpp2_irqs_deinit(port);
 err_cleanup_txqs:
@@ -6706,6 +6855,7 @@ static int mvpp2_stop(struct net_device *dev)
 {
        struct mvpp2_port *port = netdev_priv(dev);
        struct mvpp2_port_pcpu *port_pcpu;
+       struct mvpp2 *priv = port->priv;
        int cpu;
 
        mvpp2_stop_dev(port);
@@ -6715,6 +6865,9 @@ static int mvpp2_stop(struct net_device *dev)
        on_each_cpu(mvpp2_interrupts_mask, port, 1);
        mvpp2_shared_interrupt_mask_unmask(port, true);
 
+       if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq)
+               free_irq(port->link_irq, port);
+
        mvpp2_irqs_deinit(port);
        if (!port->has_tx_irqs) {
                for_each_present_cpu(cpu) {
@@ -7387,6 +7540,15 @@ static int mvpp2_port_probe(struct platform_device *pdev,
        if (err)
                goto err_free_netdev;
 
+       port->link_irq = of_irq_get_byname(port_node, "link");
+       if (port->link_irq == -EPROBE_DEFER) {
+               err = -EPROBE_DEFER;
+               goto err_deinit_qvecs;
+       }
+       if (port->link_irq <= 0)
+               /* the link irq is optional */
+               port->link_irq = 0;
+
        if (of_property_read_bool(port_node, "marvell,loopback"))
                port->flags |= MVPP2_F_LOOPBACK;
 
@@ -7405,7 +7567,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
                port->base = devm_ioremap_resource(&pdev->dev, res);
                if (IS_ERR(port->base)) {
                        err = PTR_ERR(port->base);
-                       goto err_deinit_qvecs;
+                       goto err_free_irq;
                }
        } else {
                if (of_property_read_u32(port_node, "gop-port-id",
@@ -7422,7 +7584,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
        port->stats = netdev_alloc_pcpu_stats(struct mvpp2_pcpu_stats);
        if (!port->stats) {
                err = -ENOMEM;
-               goto err_deinit_qvecs;
+               goto err_free_irq;
        }
 
        dt_mac_addr = of_get_mac_address(port_node);
@@ -7506,6 +7668,9 @@ err_free_txq_pcpu:
                free_percpu(port->txqs[i]->pcpu);
 err_free_stats:
        free_percpu(port->stats);
+err_free_irq:
+       if (port->link_irq)
+               irq_dispose_mapping(port->link_irq);
 err_deinit_qvecs:
        mvpp2_queue_vectors_deinit(port);
 err_free_netdev:
@@ -7526,6 +7691,8 @@ static void mvpp2_port_remove(struct mvpp2_port *port)
        for (i = 0; i < port->ntxqs; i++)
                free_percpu(port->txqs[i]->pcpu);
        mvpp2_queue_vectors_deinit(port);
+       if (port->link_irq)
+               irq_dispose_mapping(port->link_irq);
        free_netdev(port->dev);
 }