2017-08-31 07:15:41

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH net-next 0/3] net: mvpp2: optional PHYs and GoP link irq

Hi all,

This series aims at make the driver work when no PHY is connected
between a port and the physical layer and not described as a fixed-phy.
This is useful for some usecases such as when a switch is connected
directly to the serdes lanes. It can also be used for SFP ports on the
7k-db and 8k-db while waiting for the phylink support to land in (which
should be part of another series).

This series makes the phy optional in the PPv2 driver, and then adds
the support for the GoP port link interrupt to handle link status
changes on such ports.

This was tested using the SFP ports on the 7k-db and 8k-db boards.

Thanks!
Antoine

Antoine Tenart (3):
net: mvpp2: make the phy optional
net: mvpp2: use the GoP interrupt for link status changes
Documentation/bindings: net: marvell-pp2: add the link interrupt

.../devicetree/bindings/net/marvell-pp2.txt | 2 +-
drivers/net/ethernet/marvell/mvpp2.c | 208 +++++++++++++++++++--
2 files changed, 196 insertions(+), 14 deletions(-)

--
2.13.5


2017-08-31 07:15:05

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH net-next 2/3] net: mvpp2: use the GoP interrupt for link status changes

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 <[email protected]>
Tested-by: Marcin Wojtas <[email protected]>
---
drivers/net/ethernet/marvell/mvpp2.c | 189 ++++++++++++++++++++++++++++++++++-
1 file changed, 184 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 1916a4035ea0..4a4560d9151a 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -348,16 +348,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.
@@ -370,11 +378,19 @@
#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,77 @@ 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_RGMII_ID ||
+ port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
+ port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID ||
+ 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 (port->phy_interface == PHY_INTERFACE_MODE_RGMII ||
+ port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
+ port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID ||
+ 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_RGMII_ID ||
+ port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
+ port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID ||
+ 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;
@@ -5735,6 +5824,63 @@ 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_RGMII_ID ||
+ port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
+ port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID ||
+ 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)
{
@@ -5763,7 +5909,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 */
@@ -6642,6 +6787,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;
@@ -6687,12 +6833,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);
@@ -6702,6 +6860,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:
@@ -6715,6 +6876,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);
@@ -6724,6 +6886,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) {
@@ -7422,6 +7587,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;

@@ -7440,7 +7614,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",
@@ -7457,7 +7631,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;
}

mvpp2_port_copy_mac_addr(dev, priv, port_node, &mac_from);
@@ -7527,6 +7701,9 @@ static int mvpp2_port_probe(struct platform_device *pdev,
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:
@@ -7547,6 +7724,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);
}

--
2.13.5

2017-08-31 07:15:04

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH net-next 1/3] net: mvpp2: make the phy optional

There is not necessarily a PHY between the GoP and the physical port.
However, the driver currently makes the "phy" property mandatory,
contrary to what is stated in the device tree bindings. This patch makes
the PHY optional, and aligns the PPv2 driver on its device tree
documentation. However if a PHY is provided, the GoP link interrupt
won't be used.

With this patch switches directly connected to the serdes lanes and SFP
ports on the Armada 8040-db and Armada 7040-db can be used if the link
interrupt is described in the device tree.

Signed-off-by: Antoine Tenart <[email protected]>
Tested-by: Marcin Wojtas <[email protected]>
---
drivers/net/ethernet/marvell/mvpp2.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 9e64b1ba3d43..1916a4035ea0 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -6484,7 +6484,8 @@ static void mvpp2_start_dev(struct mvpp2_port *port)

mvpp2_port_mii_set(port);
mvpp2_port_enable(port);
- phy_start(ndev->phydev);
+ if (ndev->phydev)
+ phy_start(ndev->phydev);
netif_tx_start_all_queues(port->dev);
}

@@ -6510,7 +6511,8 @@ static void mvpp2_stop_dev(struct mvpp2_port *port)

mvpp2_egress_disable(port);
mvpp2_port_disable(port);
- phy_stop(ndev->phydev);
+ if (ndev->phydev)
+ phy_stop(ndev->phydev);
phy_power_off(port->comphy);
}

@@ -6567,6 +6569,10 @@ static int mvpp2_phy_connect(struct mvpp2_port *port)
{
struct phy_device *phy_dev;

+ /* No PHY is attached */
+ if (!port->phy_node)
+ return 0;
+
phy_dev = of_phy_connect(port->dev, port->phy_node, mvpp2_link_event, 0,
port->phy_interface);
if (!phy_dev) {
@@ -6587,6 +6593,9 @@ static void mvpp2_phy_disconnect(struct mvpp2_port *port)
{
struct net_device *ndev = port->dev;

+ if (!ndev->phydev)
+ return;
+
phy_disconnect(ndev->phydev);
}

@@ -7375,12 +7384,6 @@ static int mvpp2_port_probe(struct platform_device *pdev,
return -ENOMEM;

phy_node = of_parse_phandle(port_node, "phy", 0);
- if (!phy_node) {
- dev_err(&pdev->dev, "missing phy\n");
- err = -ENODEV;
- goto err_free_netdev;
- }
-
phy_mode = of_get_phy_mode(port_node);
if (phy_mode < 0) {
dev_err(&pdev->dev, "incorrect phy mode\n");
--
2.13.5

2017-08-31 07:16:03

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH net-next 3/3] Documentation/bindings: net: marvell-pp2: add the link interrupt

A link interrupt can be described. Document this valid interrupt name.

Signed-off-by: Antoine Tenart <[email protected]>
Tested-by: Marcin Wojtas <[email protected]>
---
Documentation/devicetree/bindings/net/marvell-pp2.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/net/marvell-pp2.txt b/Documentation/devicetree/bindings/net/marvell-pp2.txt
index 49484db81583..c78f3187dfea 100644
--- a/Documentation/devicetree/bindings/net/marvell-pp2.txt
+++ b/Documentation/devicetree/bindings/net/marvell-pp2.txt
@@ -44,7 +44,7 @@ Optional properties (port):
- interrupt-names: if more than a single interrupt for rx is given, must
be the name associated to the interrupts listed. Valid
names are: "tx-cpu0", "tx-cpu1", "tx-cpu2", "tx-cpu3",
- "rx-shared".
+ "rx-shared", "link".
- marvell,system-controller: a phandle to the system controller.

Example for marvell,armada-375-pp2:
--
2.13.5

2017-08-31 13:47:35

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next 2/3] net: mvpp2: use the GoP interrupt for link status changes

On Thu, Aug 31, 2017 at 09:12:55AM +0200, Antoine Tenart wrote:
> +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_RGMII_ID ||
> + port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
> + port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID ||

Hi Antoine

phy_interface_mode_is_rgmii()

Andrew

2017-08-31 13:53:09

by Antoine Tenart

[permalink] [raw]
Subject: Re: [PATCH net-next 2/3] net: mvpp2: use the GoP interrupt for link status changes

Hi Andrew,

On Thu, Aug 31, 2017 at 03:47:24PM +0200, Andrew Lunn wrote:
> On Thu, Aug 31, 2017 at 09:12:55AM +0200, Antoine Tenart wrote:
> > +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_RGMII_ID ||
> > + port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
> > + port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID ||
>
> phy_interface_mode_is_rgmii()

Right, and you already made that comment before... I'll fix it.

Thanks!
Antoine

--
Antoine T?nart, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com


Attachments:
(No filename) (705.00 B)
signature.asc (833.00 B)
Download all attachments