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: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2018-05-11 12:03:06 -04:00
commit 4cbd7a7d3c
9 changed files with 463 additions and 212 deletions

View File

@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <linux/phylink.h>
#include <linux/mii.h>
#include <linux/of.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,
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_mask;
@ -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)
{
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_mask;
intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR);
if (priv->irq1_stat & P_LINK_UP_IRQ(P7_IRQ_OFF))
priv->port_sts[7].link = 1;
if (priv->irq1_stat & P_LINK_DOWN_IRQ(P7_IRQ_OFF))
priv->port_sts[7].link = 0;
if (priv->irq1_stat & P_LINK_UP_IRQ(P7_IRQ_OFF)) {
priv->port_sts[7].link = true;
dsa_port_phylink_mac_change(ds, 7, true);
}
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;
}
@ -473,13 +480,56 @@ static u32 bcm_sf2_sw_get_phy_flags(struct dsa_switch *ds, int port)
return priv->hw_params.gphy_rev;
}
static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port,
struct phy_device *phydev)
static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port,
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 ethtool_eee *p = &priv->dev->ports[port].eee;
u32 id_mode_dis = 0, port_mode;
const char *str = NULL;
u32 reg, offset;
if (priv->type == BCM7445_DEVICE_ID)
@ -487,62 +537,48 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port,
else
offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
switch (phydev->interface) {
switch (state->interface) {
case PHY_INTERFACE_MODE_RGMII:
str = "RGMII (no delay)";
id_mode_dis = 1;
/* fallthrough */
case PHY_INTERFACE_MODE_RGMII_TXID:
if (!str)
str = "RGMII (TX delay)";
port_mode = EXT_GPHY;
break;
case PHY_INTERFACE_MODE_MII:
str = "MII";
port_mode = EXT_EPHY;
break;
case PHY_INTERFACE_MODE_REVMII:
str = "Reverse MII";
port_mode = EXT_REVMII;
break;
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;
}
/* Clear id_mode_dis bit, and the existing port mode, but
* make sure we enable the RGMII block for data to pass
/* Clear id_mode_dis bit, and the existing port mode, let
* RGMII_MODE_EN bet set by mac_link_{up,down}
*/
reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
reg &= ~ID_MODE_DIS;
reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT);
reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN);
reg |= port_mode | RGMII_MODE_EN;
reg |= port_mode;
if (id_mode_dis)
reg |= ID_MODE_DIS;
if (phydev->pause) {
if (phydev->asym_pause)
if (state->pause & MLO_PAUSE_TXRX_MASK) {
if (state->pause & MLO_PAUSE_TX)
reg |= TX_PAUSE_EN;
reg |= RX_PAUSE_EN;
}
reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
pr_info("Port %d configured for %s\n", port, str);
force_link:
/* Force link settings detected from the PHY */
reg = SW_OVERRIDE;
switch (phydev->speed) {
switch (state->speed) {
case SPEED_1000:
reg |= SPDSTS_1000 << SPEED_SHIFT;
break;
@ -551,33 +587,61 @@ force_link:
break;
}
if (phydev->link)
if (state->link)
reg |= LINK_STS;
if (phydev->duplex == DUPLEX_FULL)
if (state->duplex == DUPLEX_FULL)
reg |= DUPLX_MODE;
core_writel(priv, reg, offset);
}
if (!phydev->is_pseudo_fixed_link)
static void bcm_sf2_sw_mac_link_set(struct dsa_switch *ds, int port,
phy_interface_t interface, bool link)
{
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
u32 reg;
if (!phy_interface_mode_is_rgmii(interface) &&
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
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;
bcm_sf2_sw_mac_link_set(ds, port, interface, true);
if (mode == MLO_AN_PHY && phydev)
p->eee_enabled = b53_eee_init(ds, port, phydev);
}
static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
struct fixed_phy_status *status)
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);
u32 duplex, pause, offset;
u32 reg;
if (priv->type == BCM7445_DEVICE_ID)
offset = CORE_STS_OVERRIDE_GMIIP_PORT(port);
else
offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
duplex = core_readl(priv, CORE_DUPSTS);
pause = core_readl(priv, CORE_PAUSESTS);
status->link = 0;
status->link = false;
/* 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
@ -596,28 +660,10 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
*/
if (!status->link)
netif_carrier_off(ds->ports[port].slave);
status->duplex = 1;
status->duplex = DUPLEX_FULL;
} else {
status->link = 1;
status->duplex = !!(duplex & (1 << port));
status->link = true;
}
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)
@ -861,8 +907,11 @@ static const struct dsa_switch_ops bcm_sf2_ops = {
.get_sset_count = b53_get_sset_count,
.get_ethtool_phy_stats = b53_get_ethtool_phy_stats,
.get_phy_flags = bcm_sf2_sw_get_phy_flags,
.adjust_link = bcm_sf2_sw_adjust_link,
.fixed_link_update = bcm_sf2_sw_fixed_link_update,
.phylink_validate = bcm_sf2_sw_validate,
.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,
.resume = bcm_sf2_sw_resume,
.get_wol = bcm_sf2_sw_get_wol,
@ -1065,14 +1114,14 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)
bcm_sf2_intr_disable(priv);
ret = devm_request_irq(&pdev->dev, priv->irq0, bcm_sf2_switch_0_isr, 0,
"switch_0", priv);
"switch_0", ds);
if (ret < 0) {
pr_err("failed to request switch_0 IRQ\n");
goto out_mdio;
}
ret = devm_request_irq(&pdev->dev, priv->irq1, bcm_sf2_switch_1_isr, 0,
"switch_1", priv);
"switch_1", ds);
if (ret < 0) {
pr_err("failed to request switch_1 IRQ\n");
goto out_mdio;

View File

@ -31,6 +31,7 @@
#include <linux/netdevice.h>
#include <linux/gpio/consumer.h>
#include <linux/phy.h>
#include <linux/phylink.h>
#include <net/dsa.h>
#include "chip.h"
@ -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);
}
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)
{
if (!chip->info->ops->stats_snapshot)
@ -4169,6 +4247,11 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.get_tag_protocol = mv88e6xxx_get_tag_protocol,
.setup = mv88e6xxx_setup,
.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_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6xxx_get_sset_count,

View File

@ -15,6 +15,7 @@
#include <linux/bitfield.h>
#include <linux/if_bridge.h>
#include <linux/phy.h>
#include <linux/phylink.h>
#include "chip.h"
#include "port.h"
@ -378,6 +379,44 @@ int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
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
*
* Do not limit the period of time that this port can be paused for by

View File

@ -29,6 +29,7 @@
#define MV88E6XXX_PORT_STS_SPEED_10 0x0000
#define MV88E6XXX_PORT_STS_SPEED_100 0x0100
#define MV88E6XXX_PORT_STS_SPEED_1000 0x0200
#define MV88E6XXX_PORT_STS_SPEED_10000 0x0300
#define MV88E6352_PORT_STS_EEE 0x0040
#define MV88E6165_PORT_STS_AM_DIS 0x0040
#define MV88E6185_PORT_STS_MGMII 0x0040
@ -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,
phy_interface_t mode);
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 mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
int upstream_port);

View File

@ -19,6 +19,7 @@
#include <linux/phylink.h>
#include <linux/rtnetlink.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include "sfp.h"
@ -54,6 +55,7 @@ struct phylink {
/* The link configuration settings */
struct phylink_link_state link_config;
struct gpio_desc *link_gpio;
struct timer_list link_poll;
void (*get_fixed_state)(struct net_device *dev,
struct phylink_link_state *s);
@ -360,7 +362,7 @@ static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_stat
if (pl->get_fixed_state)
pl->get_fixed_state(pl->netdev, state);
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
@ -500,6 +502,15 @@ static void phylink_run_resolve(struct phylink *pl)
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 int phylink_register_sfp(struct phylink *pl,
@ -572,6 +583,7 @@ struct phylink *phylink_create(struct net_device *ndev,
pl->link_config.an_enabled = true;
pl->ops = ops;
__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);
linkmode_copy(pl->link_config.advertising, pl->supported);
@ -612,6 +624,8 @@ void phylink_destroy(struct phylink *pl)
{
if (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);
kfree(pl);
@ -903,6 +917,8 @@ void phylink_start(struct phylink *pl)
clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
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)
sfp_upstream_start(pl->sfp_bus);
if (pl->phydev)
@ -927,6 +943,8 @@ void phylink_stop(struct phylink *pl)
phy_stop(pl->phydev);
if (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);
queue_work(system_power_efficient_wq, &pl->resolve);

View File

@ -20,12 +20,14 @@
#include <linux/of.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
#include <linux/phy.h>
#include <net/devlink.h>
#include <net/switchdev.h>
struct tc_action;
struct phy_device;
struct fixed_phy_status;
struct phylink_link_state;
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = 0,
@ -199,6 +201,7 @@ struct dsa_port {
u8 stp_state;
struct net_device *bridge_dev;
struct devlink_port devlink_port;
struct phylink *pl;
/*
* Original copy of the master netdev ethtool_ops
*/
@ -353,6 +356,27 @@ struct dsa_switch_ops {
void (*fixed_link_update)(struct dsa_switch *ds, int port,
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.
*/
@ -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_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data);
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

View File

@ -9,7 +9,7 @@ config NET_DSA
depends on HAVE_NET_DSA && MAY_USE_DEVLINK
depends on BRIDGE || BRIDGE=n
select NET_SWITCHDEV
select PHYLIB
select PHYLINK
---help---
Say Y if you want to enable support for the hardware switches supported
by the Distributed Switch Architecture.

View File

@ -75,15 +75,6 @@ struct dsa_slave_priv {
/* DSA port data, such as switch, port index, etc. */
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
struct netpoll *netpoll;
#endif

View File

@ -13,6 +13,7 @@
#include <linux/netdevice.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <linux/phylink.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include <linux/mdio.h>
@ -97,8 +98,7 @@ static int dsa_slave_open(struct net_device *dev)
if (err)
goto clear_promisc;
if (dev->phydev)
phy_start(dev->phydev);
phylink_start(dp->pl);
return 0;
@ -120,8 +120,7 @@ static int dsa_slave_close(struct net_device *dev)
struct net_device *master = dsa_slave_to_master(dev);
struct dsa_port *dp = dsa_slave_to_port(dev);
if (dev->phydev)
phy_stop(dev->phydev);
phylink_stop(dp->pl);
dsa_port_disable(dp, dev->phydev);
@ -272,10 +271,7 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
}
if (!dev->phydev)
return -ENODEV;
return phy_mii_ioctl(dev->phydev, ifr, cmd);
return phylink_mii_ioctl(p->dp->pl, ifr, cmd);
}
static int dsa_slave_port_attr_set(struct net_device *dev,
@ -498,14 +494,11 @@ dsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p)
ds->ops->get_regs(ds, dp->index, regs, _p);
}
static u32 dsa_slave_get_link(struct net_device *dev)
static int dsa_slave_nway_reset(struct net_device *dev)
{
if (!dev->phydev)
return -ENODEV;
struct dsa_port *dp = dsa_slave_to_port(dev);
genphy_update_link(dev->phydev);
return dev->phydev->link;
return phylink_ethtool_nway_reset(dp->pl);
}
static int dsa_slave_get_eeprom_len(struct net_device *dev)
@ -619,6 +612,8 @@ static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds;
phylink_ethtool_get_wol(dp->pl, w);
if (ds->ops->get_wol)
ds->ops->get_wol(ds, dp->index, w);
}
@ -629,6 +624,8 @@ static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
struct dsa_switch *ds = dp->ds;
int ret = -EOPNOTSUPP;
phylink_ethtool_set_wol(dp->pl, w);
if (ds->ops->set_wol)
ret = ds->ops->set_wol(ds, dp->index, w);
@ -652,13 +649,7 @@ static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
if (ret)
return ret;
if (e->eee_enabled) {
ret = phy_init_eee(dev->phydev, 0);
if (ret)
return ret;
}
return phy_ethtool_set_eee(dev->phydev, e);
return phylink_ethtool_set_eee(dp->pl, e);
}
static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
@ -678,7 +669,23 @@ static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
if (ret)
return ret;
return phy_ethtool_get_eee(dev->phydev, e);
return phylink_ethtool_get_eee(dp->pl, e);
}
static int dsa_slave_get_link_ksettings(struct net_device *dev,
struct ethtool_link_ksettings *cmd)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
return phylink_ethtool_ksettings_get(dp->pl, cmd);
}
static int dsa_slave_set_link_ksettings(struct net_device *dev,
const struct ethtool_link_ksettings *cmd)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
return phylink_ethtool_ksettings_set(dp->pl, cmd);
}
#ifdef CONFIG_NET_POLL_CONTROLLER
@ -981,8 +988,8 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
.get_drvinfo = dsa_slave_get_drvinfo,
.get_regs_len = dsa_slave_get_regs_len,
.get_regs = dsa_slave_get_regs,
.nway_reset = phy_ethtool_nway_reset,
.get_link = dsa_slave_get_link,
.nway_reset = dsa_slave_nway_reset,
.get_link = ethtool_op_get_link,
.get_eeprom_len = dsa_slave_get_eeprom_len,
.get_eeprom = dsa_slave_get_eeprom,
.set_eeprom = dsa_slave_set_eeprom,
@ -993,8 +1000,8 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
.get_wol = dsa_slave_get_wol,
.set_eee = dsa_slave_set_eee,
.get_eee = dsa_slave_get_eee,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings,
.get_link_ksettings = dsa_slave_get_link_ksettings,
.set_link_ksettings = dsa_slave_set_link_ksettings,
.get_rxnfc = dsa_slave_get_rxnfc,
.set_rxnfc = dsa_slave_set_rxnfc,
.get_ts_info = dsa_slave_get_ts_info,
@ -1053,56 +1060,122 @@ static struct device_type dsa_type = {
.name = "dsa",
};
static void dsa_slave_adjust_link(struct net_device *dev)
static void dsa_slave_phylink_validate(struct net_device *dev,
unsigned long *supported,
struct phylink_link_state *state)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = dp->ds;
unsigned int status_changed = 0;
if (p->old_link != dev->phydev->link) {
status_changed = 1;
p->old_link = dev->phydev->link;
}
if (!ds->ops->phylink_validate)
return;
if (p->old_duplex != dev->phydev->duplex) {
status_changed = 1;
p->old_duplex = dev->phydev->duplex;
}
if (p->old_pause != dev->phydev->pause) {
status_changed = 1;
p->old_pause = dev->phydev->pause;
}
if (ds->ops->adjust_link && status_changed)
ds->ops->adjust_link(ds, dp->index, dev->phydev);
if (status_changed)
phy_print_status(dev->phydev);
ds->ops->phylink_validate(ds, dp->index, supported, state);
}
static int dsa_slave_fixed_link_update(struct net_device *dev,
struct fixed_phy_status *status)
static int dsa_slave_phylink_mac_link_state(struct net_device *dev,
struct phylink_link_state *state)
{
struct dsa_switch *ds;
struct dsa_port *dp;
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds;
if (dev) {
dp = dsa_slave_to_port(dev);
ds = dp->ds;
if (ds->ops->fixed_link_update)
ds->ops->fixed_link_update(ds, dp->index, status);
/* Only called for SGMII and 802.3z */
if (!ds->ops->phylink_mac_link_state)
return -EOPNOTSUPP;
return ds->ops->phylink_mac_link_state(ds, dp->index, state);
}
static void dsa_slave_phylink_mac_config(struct net_device *dev,
unsigned int mode,
const struct phylink_link_state *state)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds;
if (!ds->ops->phylink_mac_config)
return;
ds->ops->phylink_mac_config(ds, dp->index, mode, state);
}
static void dsa_slave_phylink_mac_an_restart(struct net_device *dev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds;
if (!ds->ops->phylink_mac_an_restart)
return;
ds->ops->phylink_mac_an_restart(ds, dp->index);
}
static void dsa_slave_phylink_mac_link_down(struct net_device *dev,
unsigned int mode,
phy_interface_t interface)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds;
if (!ds->ops->phylink_mac_link_down) {
if (ds->ops->adjust_link && dev->phydev)
ds->ops->adjust_link(ds, dp->index, dev->phydev);
return;
}
return 0;
ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface);
}
static void dsa_slave_phylink_mac_link_up(struct net_device *dev,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds;
if (!ds->ops->phylink_mac_link_up) {
if (ds->ops->adjust_link && dev->phydev)
ds->ops->adjust_link(ds, dp->index, dev->phydev);
return;
}
ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev);
}
static const struct phylink_mac_ops dsa_slave_phylink_mac_ops = {
.validate = dsa_slave_phylink_validate,
.mac_link_state = dsa_slave_phylink_mac_link_state,
.mac_config = dsa_slave_phylink_mac_config,
.mac_an_restart = dsa_slave_phylink_mac_an_restart,
.mac_link_down = dsa_slave_phylink_mac_link_down,
.mac_link_up = dsa_slave_phylink_mac_link_up,
};
void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up)
{
const struct dsa_port *dp = dsa_to_port(ds, port);
phylink_mac_change(dp->pl, up);
}
EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_change);
static void dsa_slave_phylink_fixed_state(struct net_device *dev,
struct phylink_link_state *state)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds;
/* No need to check that this operation is valid, the callback would
* not be called if it was not.
*/
ds->ops->phylink_fixed_state(ds, dp->index, state);
}
/* slave device setup *******************************************************/
static int dsa_slave_phy_connect(struct net_device *slave_dev, int addr)
{
struct dsa_port *dp = dsa_slave_to_port(slave_dev);
struct dsa_slave_priv *p = netdev_priv(slave_dev);
struct dsa_switch *ds = dp->ds;
slave_dev->phydev = mdiobus_get_phy(ds->slave_mii_bus, addr);
@ -1111,75 +1184,54 @@ static int dsa_slave_phy_connect(struct net_device *slave_dev, int addr)
return -ENODEV;
}
/* Use already configured phy mode */
if (p->phy_interface == PHY_INTERFACE_MODE_NA)
p->phy_interface = slave_dev->phydev->interface;
return phy_connect_direct(slave_dev, slave_dev->phydev,
dsa_slave_adjust_link, p->phy_interface);
return phylink_connect_phy(dp->pl, slave_dev->phydev);
}
static int dsa_slave_phy_setup(struct net_device *slave_dev)
{
struct dsa_port *dp = dsa_slave_to_port(slave_dev);
struct dsa_slave_priv *p = netdev_priv(slave_dev);
struct device_node *port_dn = dp->dn;
struct dsa_switch *ds = dp->ds;
struct device_node *phy_dn;
bool phy_is_fixed = false;
u32 phy_flags = 0;
int mode, ret;
mode = of_get_phy_mode(port_dn);
if (mode < 0)
mode = PHY_INTERFACE_MODE_NA;
p->phy_interface = mode;
phy_dn = of_parse_phandle(port_dn, "phy-handle", 0);
if (!phy_dn && of_phy_is_fixed_link(port_dn)) {
/* In the case of a fixed PHY, the DT node associated
* to the fixed PHY is the Port DT node
dp->pl = phylink_create(slave_dev, of_fwnode_handle(port_dn), mode,
&dsa_slave_phylink_mac_ops);
if (IS_ERR(dp->pl)) {
netdev_err(slave_dev,
"error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
return PTR_ERR(dp->pl);
}
/* Register only if the switch provides such a callback, since this
* callback takes precedence over polling the link GPIO in PHYLINK
* (see phylink_get_fixed_state).
*/
ret = of_phy_register_fixed_link(port_dn);
if (ret) {
netdev_err(slave_dev, "failed to register fixed PHY: %d\n", ret);
return ret;
}
phy_is_fixed = true;
phy_dn = of_node_get(port_dn);
}
if (ds->ops->phylink_fixed_state)
phylink_fixed_state_cb(dp->pl, dsa_slave_phylink_fixed_state);
if (ds->ops->get_phy_flags)
phy_flags = ds->ops->get_phy_flags(ds, dp->index);
if (phy_dn) {
slave_dev->phydev = of_phy_connect(slave_dev, phy_dn,
dsa_slave_adjust_link,
phy_flags,
p->phy_interface);
of_node_put(phy_dn);
}
if (slave_dev->phydev && phy_is_fixed)
fixed_phy_set_link_update(slave_dev->phydev,
dsa_slave_fixed_link_update);
/* We could not connect to a designated PHY, so use the switch internal
* MDIO bus instead
ret = phylink_of_phy_connect(dp->pl, port_dn, phy_flags);
if (ret == -ENODEV) {
/* We could not connect to a designated PHY or SFP, so use the
* switch internal MDIO bus instead
*/
if (!slave_dev->phydev) {
ret = dsa_slave_phy_connect(slave_dev, dp->index);
if (ret) {
netdev_err(slave_dev, "failed to connect to port %d: %d\n",
netdev_err(slave_dev,
"failed to connect to port %d: %d\n",
dp->index, ret);
if (phy_is_fixed)
of_phy_deregister_fixed_link(port_dn);
phylink_destroy(dp->pl);
return ret;
}
}
phy_attached_info(slave_dev->phydev);
return 0;
}
@ -1194,29 +1246,26 @@ static void dsa_slave_set_lockdep_class_one(struct net_device *dev,
int dsa_slave_suspend(struct net_device *slave_dev)
{
struct dsa_slave_priv *p = netdev_priv(slave_dev);
struct dsa_port *dp = dsa_slave_to_port(slave_dev);
netif_device_detach(slave_dev);
if (slave_dev->phydev) {
phy_stop(slave_dev->phydev);
p->old_pause = -1;
p->old_link = -1;
p->old_duplex = -1;
phy_suspend(slave_dev->phydev);
}
rtnl_lock();
phylink_stop(dp->pl);
rtnl_unlock();
return 0;
}
int dsa_slave_resume(struct net_device *slave_dev)
{
struct dsa_port *dp = dsa_slave_to_port(slave_dev);
netif_device_attach(slave_dev);
if (slave_dev->phydev) {
phy_resume(slave_dev->phydev);
phy_start(slave_dev->phydev);
}
rtnl_lock();
phylink_start(dp->pl);
rtnl_unlock();
return 0;
}
@ -1281,11 +1330,6 @@ int dsa_slave_create(struct dsa_port *port)
p->dp = port;
INIT_LIST_HEAD(&p->mall_tc_list);
p->xmit = cpu_dp->tag_ops->xmit;
p->old_pause = -1;
p->old_link = -1;
p->old_duplex = -1;
port->slave = slave_dev;
netif_carrier_off(slave_dev);
@ -1308,9 +1352,10 @@ int dsa_slave_create(struct dsa_port *port)
return 0;
out_phy:
phy_disconnect(slave_dev->phydev);
if (of_phy_is_fixed_link(port->dn))
of_phy_deregister_fixed_link(port->dn);
rtnl_lock();
phylink_disconnect_phy(p->dp->pl);
rtnl_unlock();
phylink_destroy(p->dp->pl);
out_free:
free_percpu(p->stats64);
free_netdev(slave_dev);
@ -1322,17 +1367,15 @@ void dsa_slave_destroy(struct net_device *slave_dev)
{
struct dsa_port *dp = dsa_slave_to_port(slave_dev);
struct dsa_slave_priv *p = netdev_priv(slave_dev);
struct device_node *port_dn = dp->dn;
netif_carrier_off(slave_dev);
if (slave_dev->phydev) {
phy_disconnect(slave_dev->phydev);
rtnl_lock();
phylink_disconnect_phy(dp->pl);
rtnl_unlock();
if (of_phy_is_fixed_link(port_dn))
of_phy_deregister_fixed_link(port_dn);
}
dsa_slave_notify(slave_dev, DSA_PORT_UNREGISTER);
unregister_netdev(slave_dev);
phylink_destroy(dp->pl);
free_percpu(p->stats64);
free_netdev(slave_dev);
}