diff mbox

[net-next,10/18] net: mvpp2: use the GoP interrupt for link status changes

Message ID 20170724134848.19330-11-antoine.tenart@free-electrons.com (mailing list archive)
State New, archived
Headers show

Commit Message

Antoine Tenart July 24, 2017, 1:48 p.m. UTC
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>
---
 drivers/net/ethernet/marvell/mvpp2.c | 157 ++++++++++++++++++++++++++++++++++-
 1 file changed, 154 insertions(+), 3 deletions(-)

Comments

Thomas Petazzoni July 25, 2017, 1:17 p.m. UTC | #1
Hello,

On Mon, 24 Jul 2017 15:48:40 +0200, Antoine Tenart wrote:
> +
> +		port->link_irq = of_irq_get_byname(port_node, "link");
> +		if (port->link_irq == -EPROBE_DEFER) {
> +			err = -EPROBE_DEFER;
> +			goto err_free_irq;
> +		}
> +		if (port->link_irq <= 0)
> +			/* the link irq is optional */
> +			port->link_irq = 0;

You need to add the irq_dispose_mapping() call corresponding to this
of_irq_get_by_name() in the error path and in the remove path.

Best regards,

Thomas
Antoine Tenart July 26, 2017, 12:07 a.m. UTC | #2
Hi Thomas,

On Tue, Jul 25, 2017 at 03:17:48PM +0200, Thomas Petazzoni wrote:
> On Mon, 24 Jul 2017 15:48:40 +0200, Antoine Tenart wrote:
> > +
> > +		port->link_irq = of_irq_get_byname(port_node, "link");
> > +		if (port->link_irq == -EPROBE_DEFER) {
> > +			err = -EPROBE_DEFER;
> > +			goto err_free_irq;
> > +		}
> > +		if (port->link_irq <= 0)
> > +			/* the link irq is optional */
> > +			port->link_irq = 0;
> 
> You need to add the irq_dispose_mapping() call corresponding to this
> of_irq_get_by_name() in the error path and in the remove path.

That's right. I'll fix that in v2.

Thanks!
Antoine
Andrew Lunn July 26, 2017, 4:26 p.m. UTC | #3
On Mon, Jul 24, 2017 at 03:48:40PM +0200, Antoine Tenart wrote:
> 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.

Hi Antoine

When is a GoP link interrupt signalled? When is a port without a PHY
actually up/down?

With SFF/SFP ports, you generally need a gpio line the fibre module
can use to indicate if it has link. Fixed-phy has such support, and
your link_change function will get called when the link changes.

And this is another bit of code you probably need to change in a while
with phylink lands.

     Andrew
Russell King (Oracle) July 26, 2017, 7:38 p.m. UTC | #4
On Wed, Jul 26, 2017 at 06:26:48PM +0200, Andrew Lunn wrote:
> And this is another bit of code you probably need to change in a while
> with phylink lands.

The way the MAC driver handles link up/down and configuration events
changes significantly when a MAC driver switches to phylink, since
a directly connected SFP cage needs to have the MAC reconfigured
between SGMII and 1000base-X modes.  If you add SFP+ into that, also
10Gbase-KR as well.

Note also that the "link up" condition for SFP (and probably SFF) is
more complex than just "is the module reporting that it's receiving
a signal" - especially with 1000base-X, there's negotiation to be
performed, so you also need to know (if the module is connected
directly to the MAC) whether the Serdes is in sync and has finished
negotiation (and itself says it has link with the remote end.)

With the Marvell 88x3310 PHY, the MAC driver already needs to switch
between 10Gbase-KR and SGMII modes, as the 88x3310 automatically
makes that switch on its MAC facing interface without software
intervention.
Antoine Tenart Aug. 23, 2017, 8:25 a.m. UTC | #5
Hi Andrew, Russell,

On Wed, Jul 26, 2017 at 06:26:48PM +0200, Andrew Lunn wrote:
> On Mon, Jul 24, 2017 at 03:48:40PM +0200, Antoine Tenart wrote:
> > 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.
> 
> When is a GoP link interrupt signalled? When is a port without a PHY
> actually up/down?

When the cable is connected (there is signal) and the serdes is in sync
and AN succeeded.

> With SFF/SFP ports, you generally need a gpio line the fibre module
> can use to indicate if it has link. Fixed-phy has such support, and
> your link_change function will get called when the link changes.

So that would work when using SFP modules but I wonder if the GoP irq
isn't needed when using passive cable, in which case this patch would
still be needed (and of course we should support the new Russell phylib
capabilities).

What's your thoughts on this?

Thanks!
Antoine
Stefan Chulski Aug. 23, 2017, 3:24 p.m. UTC | #6
> When the cable is connected (there is signal) and the serdes is in sync and AN
> succeeded.
> 
> > With SFF/SFP ports, you generally need a gpio line the fibre module
> > can use to indicate if it has link. Fixed-phy has such support, and
> > your link_change function will get called when the link changes.
> 
> So that would work when using SFP modules but I wonder if the GoP irq isn't
> needed when using passive cable, in which case this patch would still be needed
> (and of course we should support the new Russell phylib capabilities).
> 
> What's your thoughts on this?
> 
> Thanks!
> Antoine
> 

Even if new phylib driver supports passive direct cables connection, 
GoP IRQ required for SOHO/Peridot external switch support.
SOHO/Peridot external switch could be connected directly to Serdes line.

Regards,
Stefan
Antoine Tenart Aug. 23, 2017, 4:04 p.m. UTC | #7
On Wed, Aug 23, 2017 at 03:24:55PM +0000, Stefan Chulski wrote:
> > When the cable is connected (there is signal) and the serdes is in sync and AN
> > succeeded.
> > 
> > > With SFF/SFP ports, you generally need a gpio line the fibre module
> > > can use to indicate if it has link. Fixed-phy has such support, and
> > > your link_change function will get called when the link changes.
> > 
> > So that would work when using SFP modules but I wonder if the GoP irq isn't
> > needed when using passive cable, in which case this patch would still be needed
> > (and of course we should support the new Russell phylib capabilities).
> 
> Even if new phylib driver supports passive direct cables connection, 
> GoP IRQ required for SOHO/Peridot external switch support.
> SOHO/Peridot external switch could be connected directly to Serdes line.

So I guess the GoP link irq patches are needed. Should I resend them
then?

We'll have to discuss how to handle fixed-phy vs GoP IRQ, but I guess we
can do this when adding the fixed-phy support later.

Thanks,
Antoine
Marcin Wojtas Aug. 23, 2017, 9:05 p.m. UTC | #8
Hi Antoine,

2017-08-23 18:04 GMT+02:00 Antoine Tenart <antoine.tenart@free-electrons.com>:
> On Wed, Aug 23, 2017 at 03:24:55PM +0000, Stefan Chulski wrote:
>> > When the cable is connected (there is signal) and the serdes is in sync and AN
>> > succeeded.
>> >
>> > > With SFF/SFP ports, you generally need a gpio line the fibre module
>> > > can use to indicate if it has link. Fixed-phy has such support, and
>> > > your link_change function will get called when the link changes.
>> >
>> > So that would work when using SFP modules but I wonder if the GoP irq isn't
>> > needed when using passive cable, in which case this patch would still be needed
>> > (and of course we should support the new Russell phylib capabilities).
>>
>> Even if new phylib driver supports passive direct cables connection,
>> GoP IRQ required for SOHO/Peridot external switch support.
>> SOHO/Peridot external switch could be connected directly to Serdes line.
>
> So I guess the GoP link irq patches are needed. Should I resend them
> then?
>
> We'll have to discuss how to handle fixed-phy vs GoP IRQ, but I guess we
> can do this when adding the fixed-phy support later.
>

Please check mvneta.c - you can find there coexistence of normal
libphy support, fixed phy and link irq for the inband management mode.
IMO it's exactly, what you need here.

Best regards,
Marcin
Antoine Tenart Aug. 24, 2017, 10:59 a.m. UTC | #9
Hi Marcin,

On Wed, Aug 23, 2017 at 11:05:33PM +0200, Marcin Wojtas wrote:
> 2017-08-23 18:04 GMT+02:00 Antoine Tenart <antoine.tenart@free-electrons.com>:
> > On Wed, Aug 23, 2017 at 03:24:55PM +0000, Stefan Chulski wrote:
> >> > When the cable is connected (there is signal) and the serdes is in sync and AN
> >> > succeeded.
> >> >
> >> > > With SFF/SFP ports, you generally need a gpio line the fibre module
> >> > > can use to indicate if it has link. Fixed-phy has such support, and
> >> > > your link_change function will get called when the link changes.
> >> >
> >> > So that would work when using SFP modules but I wonder if the GoP irq isn't
> >> > needed when using passive cable, in which case this patch would still be needed
> >> > (and of course we should support the new Russell phylib capabilities).
> >>
> >> Even if new phylib driver supports passive direct cables connection,
> >> GoP IRQ required for SOHO/Peridot external switch support.
> >> SOHO/Peridot external switch could be connected directly to Serdes line.
> >
> > So I guess the GoP link irq patches are needed. Should I resend them
> > then?
> >
> > We'll have to discuss how to handle fixed-phy vs GoP IRQ, but I guess we
> > can do this when adding the fixed-phy support later.
> >
> 
> Please check mvneta.c - you can find there coexistence of normal
> libphy support, fixed phy and link irq for the inband management mode.
> IMO it's exactly, what you need here.

From what I see it should be pretty easy to support phy, fixed-phy and
the GoP irq without breaking anything: if no phy is found in the dt,
call of_phy_register_fixed_link() and if the fixed-phy property isn't
found either use the GoP link IRQ. This way all the possibilities would
be supported.

So we should be able to support fixed-phy in a future series.

Antoine
diff mbox

Patch

diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 77eef2cc40a1..33a7eb834855 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -339,16 +339,24 @@ 
 #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.
@@ -358,12 +366,19 @@ 
 #define     MVPP22_XLG_CTRL0_MAC_RESET_DIS	BIT(1)
 #define     MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN	BIT(7)
 #define     MVPP22_XLG_CTRL0_MIB_CNT_DIS	BIT(14)
-
+#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)
@@ -814,6 +829,7 @@  struct mvpp2_port {
 	int gop_id;
 
 	int irq;
+	int link_irq;
 
 	struct mvpp2 *priv;
 
@@ -4330,6 +4346,63 @@  static int mvpp22_gop_init(struct mvpp2_port *port)
 	return -EINVAL;
 }
 
+static void mvpp22_gop_unmask_irq(struct mvpp2_port *port)
+{
+	u32 val;
+
+	if (port->phy_interface == PHY_INTERFACE_MODE_RGMII ||
+	    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);
+	}
+
+	/* Enable the XLG/GIG irqs for this port */
+	val = readl(port->base + MVPP22_XLG_EXT_INT_MASK);
+	if (port->gop_id == 0 &&
+	    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;
+
+	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 (port->phy_interface == PHY_INTERFACE_MODE_RGMII ||
+	    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 (port->phy_interface == PHY_INTERFACE_MODE_RGMII ||
+	    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);
+	}
+
+	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 void mvpp2_port_mii_gmac_configure_mode(struct mvpp2_port *port)
 {
 	u32 val;
@@ -5529,6 +5602,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 (port->phy_interface == PHY_INTERFACE_MODE_RGMII ||
+		   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;
+}
+
 /* Adjust link */
 static void mvpp2_link_event(struct net_device *dev)
 {
@@ -6221,6 +6348,7 @@  static void mvpp2_phy_disconnect(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;
@@ -6266,12 +6394,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);
@@ -6280,6 +6420,8 @@  static int mvpp2_open(struct net_device *dev)
 
 	return 0;
 
+err_free_link_irq:
+	free_irq(port->link_irq, port);
 err_free_irq:
 	free_irq(port->irq, port);
 err_cleanup_txqs:
@@ -6796,6 +6938,15 @@  static int mvpp2_port_probe(struct platform_device *pdev,
 			      -EPROBE_DEFER : -EINVAL;
 			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_free_irq;
+		}
+		if (port->link_irq <= 0)
+			/* the link irq is optional */
+			port->link_irq = 0;
 	} else {
 		/* kept for dt compatibility */
 		port->irq = irq_of_parse_and_map(port_node, 0);