diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 97236cfcbae4..ac621f44237a 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -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; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 5d933549dc49..1cebde80b101 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #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, diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 6315774d72b3..429d0ebcd5b1 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -15,6 +15,7 @@ #include #include #include +#include #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, ®); + 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 diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index b16d5f0e6e9c..5e1db1b221ca 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -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); diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index c582b2d7546c..581ce93ecaf9 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #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); diff --git a/include/net/dsa.h b/include/net/dsa.h index 462e9741b210..fdbd6082945d 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -20,12 +20,14 @@ #include #include #include +#include #include #include 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 diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index bbf2c82cf7b2..4183e4ba27a5 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -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. diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 053731473c99..3964c6f7a7c0 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -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 diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 746ab428a17a..1e3b6a6d8a40 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -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 - */ - 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); + 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). + */ + 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 - */ - if (!slave_dev->phydev) { + 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 + */ 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); }