Commit 4cbd7a7d authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-Plug-in-PHYLINK-support'

Florian Fainelli says:

====================
net: dsa: Plug in PHYLINK support

This patch series adds PHYLINK support to DSA which is necessary to support more
complex PHY and pluggable modules setups.

Patch series can be found here:

https://github.com/ffainelli/linux/commits/dsa-phylink-v2

This was tested on:

- dsa-loop
- bcm_sf2
- mv88e6xxx
- b53

With a variety of test cases:
- internal & external MDIO PHYs
- MoCA with link notification through interrupt/MMIO register
- built-in PHYs
- ifconfig up/down for several cycles works
- bind/unbind of the drivers

Changes in v2:

- fixed link configuration for mv88e6xxx (Andrew) after introducing polling

This is technically v2 of what was posted back in March 2018, changes from last
time:

- fixed probe/remove of drivers
- fixed missing gpiod_put() for link GPIOs
- fixed polling of link GPIOs (Russell I would need your SoB on the patch you
  provided offline initially, added some modifications to it)
- tested across a wider set of platforms

And everything should still work as expected. Please be aware of the following:

- switch drivers (like bcm_sf2) which may have user-facing network ports using
  fixed links would need to implement phylink_mac_ops to remain functional.
  PHYLINK does not create a phy_device for fixed links, therefore our
  call to adjust_link() from phylink_mac_link_{up,down} would not be calling
  into the driver. This *should not* affect CPU/DSA ports which are configured
  through adjust_link() but have no network devices

- support for SFP/SFF is now possible, but switch drivers will still need some
  modifications to properly support those, including, but not limited to using
  the correct binding information. This will be submitted on top of this series

Please do test on your respective platforms/switches and let me know if you
find any issues, hopefully everything still works like before.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ec9efb52 58d56fcc
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/phy_fixed.h> #include <linux/phy_fixed.h>
#include <linux/phylink.h>
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
...@@ -306,7 +307,8 @@ static int bcm_sf2_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, ...@@ -306,7 +307,8 @@ static int bcm_sf2_sw_mdio_write(struct mii_bus *bus, int addr, int regnum,
static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
{ {
struct bcm_sf2_priv *priv = dev_id; struct dsa_switch *ds = dev_id;
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) & priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
~priv->irq0_mask; ~priv->irq0_mask;
...@@ -317,16 +319,21 @@ static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) ...@@ -317,16 +319,21 @@ static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id) static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id)
{ {
struct bcm_sf2_priv *priv = dev_id; struct dsa_switch *ds = dev_id;
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
priv->irq1_stat = intrl2_1_readl(priv, INTRL2_CPU_STATUS) & priv->irq1_stat = intrl2_1_readl(priv, INTRL2_CPU_STATUS) &
~priv->irq1_mask; ~priv->irq1_mask;
intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR); intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR);
if (priv->irq1_stat & P_LINK_UP_IRQ(P7_IRQ_OFF)) if (priv->irq1_stat & P_LINK_UP_IRQ(P7_IRQ_OFF)) {
priv->port_sts[7].link = 1; priv->port_sts[7].link = true;
if (priv->irq1_stat & P_LINK_DOWN_IRQ(P7_IRQ_OFF)) dsa_port_phylink_mac_change(ds, 7, true);
priv->port_sts[7].link = 0; }
if (priv->irq1_stat & P_LINK_DOWN_IRQ(P7_IRQ_OFF)) {
priv->port_sts[7].link = false;
dsa_port_phylink_mac_change(ds, 7, false);
}
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -473,13 +480,56 @@ static u32 bcm_sf2_sw_get_phy_flags(struct dsa_switch *ds, int port) ...@@ -473,13 +480,56 @@ static u32 bcm_sf2_sw_get_phy_flags(struct dsa_switch *ds, int port)
return priv->hw_params.gphy_rev; return priv->hw_params.gphy_rev;
} }
static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port, static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port,
struct phy_device *phydev) unsigned long *supported,
struct phylink_link_state *state)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
if (!phy_interface_mode_is_rgmii(state->interface) &&
state->interface != PHY_INTERFACE_MODE_MII &&
state->interface != PHY_INTERFACE_MODE_REVMII &&
state->interface != PHY_INTERFACE_MODE_GMII &&
state->interface != PHY_INTERFACE_MODE_INTERNAL &&
state->interface != PHY_INTERFACE_MODE_MOCA) {
bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
dev_err(ds->dev,
"Unsupported interface: %d\n", state->interface);
return;
}
/* Allow all the expected bits */
phylink_set(mask, Autoneg);
phylink_set_port_modes(mask);
phylink_set(mask, Pause);
phylink_set(mask, Asym_Pause);
/* With the exclusion of MII and Reverse MII, we support Gigabit,
* including Half duplex
*/
if (state->interface != PHY_INTERFACE_MODE_MII &&
state->interface != PHY_INTERFACE_MODE_REVMII) {
phylink_set(mask, 1000baseT_Full);
phylink_set(mask, 1000baseT_Half);
}
phylink_set(mask, 10baseT_Half);
phylink_set(mask, 10baseT_Full);
phylink_set(mask, 100baseT_Half);
phylink_set(mask, 100baseT_Full);
bitmap_and(supported, supported, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_and(state->advertising, state->advertising, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS);
}
static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port,
unsigned int mode,
const struct phylink_link_state *state)
{ {
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
struct ethtool_eee *p = &priv->dev->ports[port].eee;
u32 id_mode_dis = 0, port_mode; u32 id_mode_dis = 0, port_mode;
const char *str = NULL;
u32 reg, offset; u32 reg, offset;
if (priv->type == BCM7445_DEVICE_ID) if (priv->type == BCM7445_DEVICE_ID)
...@@ -487,62 +537,48 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port, ...@@ -487,62 +537,48 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port,
else else
offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
switch (phydev->interface) { switch (state->interface) {
case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII:
str = "RGMII (no delay)";
id_mode_dis = 1; id_mode_dis = 1;
/* fallthrough */
case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_RGMII_TXID:
if (!str)
str = "RGMII (TX delay)";
port_mode = EXT_GPHY; port_mode = EXT_GPHY;
break; break;
case PHY_INTERFACE_MODE_MII: case PHY_INTERFACE_MODE_MII:
str = "MII";
port_mode = EXT_EPHY; port_mode = EXT_EPHY;
break; break;
case PHY_INTERFACE_MODE_REVMII: case PHY_INTERFACE_MODE_REVMII:
str = "Reverse MII";
port_mode = EXT_REVMII; port_mode = EXT_REVMII;
break; break;
default: default:
/* All other PHYs: internal and MoCA */ /* all other PHYs: internal and MoCA */
goto force_link;
}
/* If the link is down, just disable the interface to conserve power */
if (!phydev->link) {
reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
reg &= ~RGMII_MODE_EN;
reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
goto force_link; goto force_link;
} }
/* Clear id_mode_dis bit, and the existing port mode, but /* Clear id_mode_dis bit, and the existing port mode, let
* make sure we enable the RGMII block for data to pass * RGMII_MODE_EN bet set by mac_link_{up,down}
*/ */
reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
reg &= ~ID_MODE_DIS; reg &= ~ID_MODE_DIS;
reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT); reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT);
reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN); reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN);
reg |= port_mode | RGMII_MODE_EN; reg |= port_mode;
if (id_mode_dis) if (id_mode_dis)
reg |= ID_MODE_DIS; reg |= ID_MODE_DIS;
if (phydev->pause) { if (state->pause & MLO_PAUSE_TXRX_MASK) {
if (phydev->asym_pause) if (state->pause & MLO_PAUSE_TX)
reg |= TX_PAUSE_EN; reg |= TX_PAUSE_EN;
reg |= RX_PAUSE_EN; reg |= RX_PAUSE_EN;
} }
reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
pr_info("Port %d configured for %s\n", port, str);
force_link: force_link:
/* Force link settings detected from the PHY */ /* Force link settings detected from the PHY */
reg = SW_OVERRIDE; reg = SW_OVERRIDE;
switch (phydev->speed) { switch (state->speed) {
case SPEED_1000: case SPEED_1000:
reg |= SPDSTS_1000 << SPEED_SHIFT; reg |= SPDSTS_1000 << SPEED_SHIFT;
break; break;
...@@ -551,33 +587,61 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port, ...@@ -551,33 +587,61 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port,
break; break;
} }
if (phydev->link) if (state->link)
reg |= LINK_STS; reg |= LINK_STS;
if (phydev->duplex == DUPLEX_FULL) if (state->duplex == DUPLEX_FULL)
reg |= DUPLX_MODE; reg |= DUPLX_MODE;
core_writel(priv, reg, offset); core_writel(priv, reg, offset);
if (!phydev->is_pseudo_fixed_link)
p->eee_enabled = b53_eee_init(ds, port, phydev);
} }
static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, static void bcm_sf2_sw_mac_link_set(struct dsa_switch *ds, int port,
struct fixed_phy_status *status) phy_interface_t interface, bool link)
{ {
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
u32 duplex, pause, offset;
u32 reg; u32 reg;
if (priv->type == BCM7445_DEVICE_ID) if (!phy_interface_mode_is_rgmii(interface) &&
offset = CORE_STS_OVERRIDE_GMIIP_PORT(port); interface != PHY_INTERFACE_MODE_MII &&
interface != PHY_INTERFACE_MODE_REVMII)
return;
/* If the link is down, just disable the interface to conserve power */
reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
if (link)
reg |= RGMII_MODE_EN;
else else
offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); reg &= ~RGMII_MODE_EN;
reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
}
static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface)
{
bcm_sf2_sw_mac_link_set(ds, port, interface, false);
}
static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev)
{
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
struct ethtool_eee *p = &priv->dev->ports[port].eee;
duplex = core_readl(priv, CORE_DUPSTS); bcm_sf2_sw_mac_link_set(ds, port, interface, true);
pause = core_readl(priv, CORE_PAUSESTS);
status->link = 0; if (mode == MLO_AN_PHY && phydev)
p->eee_enabled = b53_eee_init(ds, port, phydev);
}
static void bcm_sf2_sw_fixed_state(struct dsa_switch *ds, int port,
struct phylink_link_state *status)
{
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
status->link = false;
/* MoCA port is special as we do not get link status from CORE_LNKSTS, /* MoCA port is special as we do not get link status from CORE_LNKSTS,
* which means that we need to force the link at the port override * which means that we need to force the link at the port override
...@@ -596,28 +660,10 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, ...@@ -596,28 +660,10 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
*/ */
if (!status->link) if (!status->link)
netif_carrier_off(ds->ports[port].slave); netif_carrier_off(ds->ports[port].slave);
status->duplex = 1; status->duplex = DUPLEX_FULL;
} else { } else {
status->link = 1; status->link = true;
status->duplex = !!(duplex & (1 << port));
}
reg = core_readl(priv, offset);
reg |= SW_OVERRIDE;
if (status->link)
reg |= LINK_STS;
else
reg &= ~LINK_STS;
core_writel(priv, reg, offset);
if ((pause & (1 << port)) &&
(pause & (1 << (port + PAUSESTS_TX_PAUSE_SHIFT)))) {
status->asym_pause = 1;
status->pause = 1;
} }
if (pause & (1 << port))
status->pause = 1;
} }
static void bcm_sf2_enable_acb(struct dsa_switch *ds) static void bcm_sf2_enable_acb(struct dsa_switch *ds)
...@@ -861,8 +907,11 @@ static const struct dsa_switch_ops bcm_sf2_ops = { ...@@ -861,8 +907,11 @@ static const struct dsa_switch_ops bcm_sf2_ops = {
.get_sset_count = b53_get_sset_count, .get_sset_count = b53_get_sset_count,
.get_ethtool_phy_stats = b53_get_ethtool_phy_stats, .get_ethtool_phy_stats = b53_get_ethtool_phy_stats,
.get_phy_flags = bcm_sf2_sw_get_phy_flags, .get_phy_flags = bcm_sf2_sw_get_phy_flags,
.adjust_link = bcm_sf2_sw_adjust_link, .phylink_validate = bcm_sf2_sw_validate,
.fixed_link_update = bcm_sf2_sw_fixed_link_update, .phylink_mac_config = bcm_sf2_sw_mac_config,
.phylink_mac_link_down = bcm_sf2_sw_mac_link_down,
.phylink_mac_link_up = bcm_sf2_sw_mac_link_up,
.phylink_fixed_state = bcm_sf2_sw_fixed_state,
.suspend = bcm_sf2_sw_suspend, .suspend = bcm_sf2_sw_suspend,
.resume = bcm_sf2_sw_resume, .resume = bcm_sf2_sw_resume,
.get_wol = bcm_sf2_sw_get_wol, .get_wol = bcm_sf2_sw_get_wol,
...@@ -1065,14 +1114,14 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev) ...@@ -1065,14 +1114,14 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)
bcm_sf2_intr_disable(priv); bcm_sf2_intr_disable(priv);
ret = devm_request_irq(&pdev->dev, priv->irq0, bcm_sf2_switch_0_isr, 0, ret = devm_request_irq(&pdev->dev, priv->irq0, bcm_sf2_switch_0_isr, 0,
"switch_0", priv); "switch_0", ds);
if (ret < 0) { if (ret < 0) {
pr_err("failed to request switch_0 IRQ\n"); pr_err("failed to request switch_0 IRQ\n");
goto out_mdio; goto out_mdio;
} }
ret = devm_request_irq(&pdev->dev, priv->irq1, bcm_sf2_switch_1_isr, 0, ret = devm_request_irq(&pdev->dev, priv->irq1, bcm_sf2_switch_1_isr, 0,
"switch_1", priv); "switch_1", ds);
if (ret < 0) { if (ret < 0) {
pr_err("failed to request switch_1 IRQ\n"); pr_err("failed to request switch_1 IRQ\n");
goto out_mdio; goto out_mdio;
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/phylink.h>
#include <net/dsa.h> #include <net/dsa.h>
#include "chip.h" #include "chip.h"
...@@ -580,6 +581,83 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, ...@@ -580,6 +581,83 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
dev_err(ds->dev, "p%d: failed to configure MAC\n", port); dev_err(ds->dev, "p%d: failed to configure MAC\n", port);
} }
static void mv88e6xxx_validate(struct dsa_switch *ds, int port,
unsigned long *supported,
struct phylink_link_state *state)
{
}
static int mv88e6xxx_link_state(struct dsa_switch *ds, int port,
struct phylink_link_state *state)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err;
mutex_lock(&chip->reg_lock);
err = mv88e6xxx_port_link_state(chip, port, state);
mutex_unlock(&chip->reg_lock);
return err;
}
static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port,
unsigned int mode,
const struct phylink_link_state *state)
{
struct mv88e6xxx_chip *chip = ds->priv;
int speed, duplex, link, err;
if (mode == MLO_AN_PHY)
return;
if (mode == MLO_AN_FIXED) {
link = LINK_FORCED_UP;
speed = state->speed;
duplex = state->duplex;
} else {
speed = SPEED_UNFORCED;
duplex = DUPLEX_UNFORCED;
link = LINK_UNFORCED;
}
mutex_lock(&chip->reg_lock);
err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex,
state->interface);
mutex_unlock(&chip->reg_lock);
if (err && err != -EOPNOTSUPP)
dev_err(ds->dev, "p%d: failed to configure MAC\n", port);
}
static void mv88e6xxx_mac_link_force(struct dsa_switch *ds, int port, int link)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err;
mutex_lock(&chip->reg_lock);
err = chip->info->ops->port_set_link(chip, port, link);
mutex_unlock(&chip->reg_lock);
if (err)
dev_err(chip->dev, "p%d: failed to force MAC link\n", port);
}
static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface)
{
if (mode == MLO_AN_FIXED)
mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_DOWN);
}
static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode, phy_interface_t interface,
struct phy_device *phydev)
{
if (mode == MLO_AN_FIXED)
mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_UP);
}
static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
{ {
if (!chip->info->ops->stats_snapshot) if (!chip->info->ops->stats_snapshot)
...@@ -4169,6 +4247,11 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { ...@@ -4169,6 +4247,11 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.get_tag_protocol = mv88e6xxx_get_tag_protocol, .get_tag_protocol = mv88e6xxx_get_tag_protocol,
.setup = mv88e6xxx_setup, .setup = mv88e6xxx_setup,
.adjust_link = mv88e6xxx_adjust_link, .adjust_link = mv88e6xxx_adjust_link,
.phylink_validate = mv88e6xxx_validate,
.phylink_mac_link_state = mv88e6xxx_link_state,
.phylink_mac_config = mv88e6xxx_mac_config,
.phylink_mac_link_down = mv88e6xxx_mac_link_down,
.phylink_mac_link_up = mv88e6xxx_mac_link_up,
.get_strings = mv88e6xxx_get_strings, .get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6xxx_get_sset_count, .get_sset_count = mv88e6xxx_get_sset_count,
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/phylink.h>
#include "chip.h" #include "chip.h"
#include "port.h" #include "port.h"
...@@ -378,6 +379,44 @@ int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) ...@@ -378,6 +379,44 @@ int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
return 0; return 0;
} }
int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port,
struct phylink_link_state *state)
{
int err;
u16 reg;
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
if (err)
return err;
switch (reg & MV88E6XXX_PORT_STS_SPEED_MASK) {
case MV88E6XXX_PORT_STS_SPEED_10:
state->speed = SPEED_10;
break;
case MV88E6XXX_PORT_STS_SPEED_100:
state->speed = SPEED_100;
break;
case MV88E6XXX_PORT_STS_SPEED_1000:
state->speed = SPEED_1000;
break;
case MV88E6XXX_PORT_STS_SPEED_10000:
if ((reg &MV88E6XXX_PORT_STS_CMODE_MASK) ==
MV88E6XXX_PORT_STS_CMODE_2500BASEX)
state->speed = SPEED_2500;
else
state->speed = SPEED_10000;
break;
}
state->duplex = reg & MV88E6XXX_PORT_STS_DUPLEX ?
DUPLEX_FULL : DUPLEX_HALF;
state->link = !!(reg & MV88E6XXX_PORT_STS_LINK);
state->an_enabled = 1;
state->an_complete = state->link;
return 0;
}
/* Offset 0x02: Jamming Control /* Offset 0x02: Jamming Control
* *
* Do not limit the period of time that this port can be paused for by * Do not limit the period of time that this port can be paused for by
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#define MV88E6XXX_PORT_STS_SPEED_10 0x0000 #define MV88E6XXX_PORT_STS_SPEED_10 0x0000
#define MV88E6XXX_PORT_STS_SPEED_100 0x0100 #define MV88E6XXX_PORT_STS_SPEED_100 0x0100
#define MV88E6XXX_PORT_STS_SPEED_1000 0x0200 #define MV88E6XXX_PORT_STS_SPEED_1000 0x0200
#define MV88E6XXX_PORT_STS_SPEED_10000 0x0300
#define MV88E6352_PORT_STS_EEE 0x0040 #define MV88E6352_PORT_STS_EEE 0x0040
#define MV88E6165_PORT_STS_AM_DIS 0x0040 #define MV88E6165_PORT_STS_AM_DIS 0x0040
#define MV88E6185_PORT_STS_MGMII 0x0040 #define MV88E6185_PORT_STS_MGMII 0x0040
...@@ -295,6 +296,8 @@ int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in, ...@@ -295,6 +296,8 @@ int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode); phy_interface_t mode);
int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port,
struct phylink_link_state *state);
int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port); int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);
int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
int upstream_port); int upstream_port);
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/phylink.h> #include <linux/phylink.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include "sfp.h" #include "sfp.h"
...@@ -54,6 +55,7 @@ struct phylink { ...@@ -54,6 +55,7 @@ struct phylink {
/* The link configuration settings */ /* The link configuration settings */
struct phylink_link_state link_config; struct phylink_link_state link_config;
struct gpio_desc *link_gpio; struct gpio_desc *link_gpio;
struct timer_list link_poll;
void (*get_fixed_state)(struct net_device *dev, void (*get_fixed_state)(struct net_device *dev,
struct phylink_link_state *s); struct phylink_link_state *s);
...@@ -360,7 +362,7 @@ static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_stat ...@@ -360,7 +362,7 @@ static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_stat
if (pl->get_fixed_state) if (pl->get_fixed_state)
pl->get_fixed_state(pl->netdev, state); pl->get_fixed_state(pl->netdev, state);
else if (pl->link_gpio) else if (pl->link_gpio)
state->link = !!gpiod_get_value(pl->link_gpio); state->link = !!gpiod_get_value_cansleep(pl->link_gpio);
} }
/* Flow control is resolved according to our and the link partners /* Flow control is resolved according to our and the link partners
...@@ -500,6 +502,15 @@ static void phylink_run_resolve(struct phylink *pl) ...@@ -500,6 +502,15 @@ static void phylink_run_resolve(struct phylink *pl)
queue_work(system_power_efficient_wq, &pl->resolve); queue_work(system_power_efficient_wq, &pl->resolve);
} }
static void phylink_fixed_poll(struct timer_list *t)
{
struct phylink *pl = container_of(t, struct phylink, link_poll);
mod_timer(t, jiffies + HZ);
phylink_run_resolve(pl);
}
static const struct sfp_upstream_ops sfp_phylink_ops; static const struct sfp_upstream_ops sfp_phylink_ops;
static int phylink_register_sfp(struct phylink *pl, static int phylink_register_sfp(struct phylink *pl,
...@@ -572,6 +583,7 @@ struct phylink *phylink_create(struct net_device *ndev, ...@@ -572,6 +583,7 @@ struct phylink *phylink_create(struct net_device *ndev,
pl->link_config.an_enabled = true; pl->link_config.an_enabled = true;
pl->ops = ops; pl->ops = ops;
__set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
timer_setup(&pl->link_poll, phylink_fixed_poll, 0);
bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS); bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
linkmode_copy(pl->link_config.advertising, pl->supported); linkmode_copy(pl->link_config.advertising, pl->supported);
...@@ -612,6 +624,8 @@ void phylink_destroy(struct phylink *pl) ...@@ -612,6 +624,8 @@ void phylink_destroy(struct phylink *pl)
{ {
if (pl->sfp_bus) if (pl->sfp_bus)
sfp_unregister_upstream(pl->sfp_bus); sfp_unregister_upstream(pl->sfp_bus);
if (!IS_ERR(pl->link_gpio))
gpiod_put(pl->link_gpio);
cancel_work_sync(&pl->resolve); cancel_work_sync(&pl->resolve);
kfree(pl); kfree(pl);
...@@ -903,6 +917,8 @@ void phylink_start(struct phylink *pl) ...@@ -903,6 +917,8 @@ void phylink_start(struct phylink *pl)
clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
phylink_run_resolve(pl); phylink_run_resolve(pl);
if (pl->link_an_mode == MLO_AN_FIXED && !IS_ERR(pl->link_gpio))
mod_timer(&pl->link_poll, jiffies + HZ);
if (pl->sfp_bus) if (pl->sfp_bus)
sfp_upstream_start(pl->sfp_bus); sfp_upstream_start(pl->sfp_bus);
if (pl->phydev) if (pl->phydev)
...@@ -927,6 +943,8 @@ void phylink_stop(struct phylink *pl) ...@@ -927,6 +943,8 @@ void phylink_stop(struct phylink *pl)
phy_stop(pl->phydev); phy_stop(pl->phydev);
if (pl->sfp_bus) if (pl->sfp_bus)
sfp_upstream_stop(pl->sfp_bus); sfp_upstream_stop(pl->sfp_bus);
if (pl->link_an_mode == MLO_AN_FIXED && !IS_ERR(pl->link_gpio))
del_timer_sync(&pl->link_poll);
set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
queue_work(system_power_efficient_wq, &pl->resolve); queue_work(system_power_efficient_wq, &pl->resolve);
......
...@@ -20,12 +20,14 @@ ...@@ -20,12 +20,14 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/net_tstamp.h> #include <linux/net_tstamp.h>
#include <linux/phy.h>
#include <net/devlink.h> #include <net/devlink.h>
#include <net/switchdev.h> #include <net/switchdev.h>
struct tc_action; struct tc_action;
struct phy_device; struct phy_device;
struct fixed_phy_status; struct fixed_phy_status;
struct phylink_link_state;
enum dsa_tag_protocol { enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = 0, DSA_TAG_PROTO_NONE = 0,
...@@ -199,6 +201,7 @@ struct dsa_port { ...@@ -199,6 +201,7 @@ struct dsa_port {
u8 stp_state; u8 stp_state;
struct net_device *bridge_dev; struct net_device *bridge_dev;
struct devlink_port devlink_port; struct devlink_port devlink_port;
struct phylink *pl;
/* /*
* Original copy of the master netdev ethtool_ops * Original copy of the master netdev ethtool_ops
*/ */
...@@ -353,6 +356,27 @@ struct dsa_switch_ops { ...@@ -353,6 +356,27 @@ struct dsa_switch_ops {
void (*fixed_link_update)(struct dsa_switch *ds, int port, void (*fixed_link_update)(struct dsa_switch *ds, int port,
struct fixed_phy_status *st); struct fixed_phy_status *st);
/*
* PHYLINK integration
*/
void (*phylink_validate)(struct dsa_switch *ds, int port,
unsigned long *supported,
struct phylink_link_state *state);
int (*phylink_mac_link_state)(struct dsa_switch *ds, int port,
struct phylink_link_state *state);
void (*phylink_mac_config)(struct dsa_switch *ds, int port,
unsigned int mode,
const struct phylink_link_state *state);
void (*phylink_mac_an_restart)(struct dsa_switch *ds, int port);
void (*phylink_mac_link_down)(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface);
void (*phylink_mac_link_up)(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev);
void (*phylink_fixed_state)(struct dsa_switch *ds, int port,
struct phylink_link_state *state);
/* /*
* ethtool hardware statistics. * ethtool hardware statistics.
*/ */
...@@ -595,5 +619,6 @@ static inline int call_dsa_notifiers(unsigned long val, struct net_device *dev, ...@@ -595,5 +619,6 @@ static inline int call_dsa_notifiers(unsigned long val, struct net_device *dev,
int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data); int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data);
int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data); int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data);
int dsa_port_get_phy_sset_count(struct dsa_port *dp); int dsa_port_get_phy_sset_count(struct dsa_port *dp);
void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up);
#endif #endif
...@@ -9,7 +9,7 @@ config NET_DSA ...@@ -9,7 +9,7 @@ config NET_DSA
depends on HAVE_NET_DSA && MAY_USE_DEVLINK depends on HAVE_NET_DSA && MAY_USE_DEVLINK
depends on BRIDGE || BRIDGE=n depends on BRIDGE || BRIDGE=n
select NET_SWITCHDEV select NET_SWITCHDEV
select PHYLIB select PHYLINK
---help--- ---help---
Say Y if you want to enable support for the hardware switches supported Say Y if you want to enable support for the hardware switches supported
by the Distributed Switch Architecture. by the Distributed Switch Architecture.
......
...@@ -75,15 +75,6 @@ struct dsa_slave_priv { ...@@ -75,15 +75,6 @@ struct dsa_slave_priv {
/* DSA port data, such as switch, port index, etc. */ /* DSA port data, such as switch, port index, etc. */
struct dsa_port *dp; struct dsa_port *dp;
/*
* The phylib phy_device pointer for the PHY connected
* to this port.
*/
phy_interface_t phy_interface;
int old_link;
int old_pause;
int old_duplex;
#ifdef CONFIG_NET_POLL_CONTROLLER #ifdef CONFIG_NET_POLL_CONTROLLER
struct netpoll *netpoll; struct netpoll *netpoll;
#endif #endif
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment