diff options
author | David S. Miller <davem@davemloft.net> | 2020-03-27 16:07:25 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2020-03-27 16:07:25 -0700 |
commit | 1a147b74c2fd4058dea0133cb2471724c3b3de09 (patch) | |
tree | 3ae828b214df599a0f3f2b463acbb23c9ebfea45 /drivers | |
parent | 22f33971aa47a9868453850c434bbc080b804133 (diff) | |
parent | 0b912fc93a680ab6105a63c36222923fbe09065e (diff) |
Merge branch 'DSA-mtu'
Vladimir Oltean says:
====================
Configure the MTU on DSA switches
This series adds support for configuring the MTU on front-panel switch
ports, while seamlessly adapting the CPU port and the DSA master to the
largest value plus the tagger overhead.
It also implements bridge MTU auto-normalization within the DSA core, as
resulted after the feedback of the implementation of this feature inside
the bridge driver in v2.
Support was added for quite a number of switches, in the hope that this
series would gain some traction:
- sja1105
- felix
- vsc73xx
- b53 and rest of the platform
V3 of this series was submitted here:
https://patchwork.ozlabs.org/cover/1262394/
V2 of this series was submitted here:
https://patchwork.ozlabs.org/cover/1261471/
V1 of this series was submitted here:
https://patchwork.ozlabs.org/cover/1199868/
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/dsa/b53/b53_common.c | 27 | ||||
-rw-r--r-- | drivers/net/dsa/ocelot/felix.c | 19 | ||||
-rw-r--r-- | drivers/net/dsa/sja1105/sja1105.h | 1 | ||||
-rw-r--r-- | drivers/net/dsa/sja1105/sja1105_main.c | 50 | ||||
-rw-r--r-- | drivers/net/dsa/vitesse-vsc73xx-core.c | 30 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bgmac.c | 12 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bgmac.h | 5 | ||||
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot.c | 45 | ||||
-rw-r--r-- | drivers/net/phy/bcm-phy-lib.c | 22 | ||||
-rw-r--r-- | drivers/net/phy/bcm-phy-lib.h | 1 | ||||
-rw-r--r-- | drivers/net/phy/bcm7xxx.c | 4 |
11 files changed, 185 insertions, 31 deletions
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index ceafce446317..39ae4ed87d1d 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -699,9 +699,6 @@ int b53_configure_vlan(struct dsa_switch *ds) b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(i), def_vid); - if (!is5325(dev) && !is5365(dev)) - b53_set_jumbo(dev, dev->enable_jumbo, false); - return 0; } EXPORT_SYMBOL(b53_configure_vlan); @@ -807,8 +804,6 @@ static int b53_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) static int b53_reset_switch(struct b53_device *priv) { /* reset vlans */ - priv->enable_jumbo = false; - memset(priv->vlans, 0, sizeof(*priv->vlans) * priv->num_vlans); memset(priv->ports, 0, sizeof(*priv->ports) * priv->num_ports); @@ -2065,6 +2060,26 @@ int b53_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) } EXPORT_SYMBOL(b53_set_mac_eee); +static int b53_change_mtu(struct dsa_switch *ds, int port, int mtu) +{ + struct b53_device *dev = ds->priv; + bool enable_jumbo; + bool allow_10_100; + + if (is5325(dev) || is5365(dev)) + return -EOPNOTSUPP; + + enable_jumbo = (mtu >= JMS_MIN_SIZE); + allow_10_100 = (dev->chip_id == BCM583XX_DEVICE_ID); + + return b53_set_jumbo(dev, enable_jumbo, allow_10_100); +} + +static int b53_get_max_mtu(struct dsa_switch *ds, int port) +{ + return JMS_MAX_SIZE; +} + static const struct dsa_switch_ops b53_switch_ops = { .get_tag_protocol = b53_get_tag_protocol, .setup = b53_setup, @@ -2102,6 +2117,8 @@ static const struct dsa_switch_ops b53_switch_ops = { .port_mdb_prepare = b53_mdb_prepare, .port_mdb_add = b53_mdb_add, .port_mdb_del = b53_mdb_del, + .port_max_mtu = b53_get_max_mtu, + .port_change_mtu = b53_change_mtu, }; struct b53_chip_data { diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 69546383a382..e2effeaa685e 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -532,6 +532,7 @@ static int felix_setup(struct dsa_switch *ds) ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports, 0)), ANA_PGID_PGID, PGID_UC); + ds->mtu_enforcement_ingress = true; /* It looks like the MAC/PCS interrupt register - PM0_IEVENT (0x8040) * isn't instantiated for the Felix PF. * In-band AN may take a few ms to complete, so we need to poll. @@ -609,6 +610,22 @@ static bool felix_txtstamp(struct dsa_switch *ds, int port, return false; } +static int felix_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + struct ocelot *ocelot = ds->priv; + + ocelot_port_set_maxlen(ocelot, port, new_mtu); + + return 0; +} + +static int felix_get_max_mtu(struct dsa_switch *ds, int port) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_get_max_mtu(ocelot, port); +} + static int felix_cls_flower_add(struct dsa_switch *ds, int port, struct flow_cls_offload *cls, bool ingress) { @@ -664,6 +681,8 @@ static const struct dsa_switch_ops felix_switch_ops = { .port_hwtstamp_set = felix_hwtstamp_set, .port_rxtstamp = felix_rxtstamp, .port_txtstamp = felix_txtstamp, + .port_change_mtu = felix_change_mtu, + .port_max_mtu = felix_get_max_mtu, .cls_flower_add = felix_cls_flower_add, .cls_flower_del = felix_cls_flower_del, .cls_flower_stats = felix_cls_flower_stats, diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index a358fc89a6db..0e5b739b2fe8 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -126,6 +126,7 @@ enum sja1105_reset_reason { SJA1105_RX_HWTSTAMPING, SJA1105_AGEING_TIME, SJA1105_SCHEDULING, + SJA1105_BEST_EFFORT_POLICING, }; int sja1105_static_config_reload(struct sja1105_private *priv, diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index e0c99bb63cdf..763ae1d3bca8 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -519,12 +519,12 @@ static int sja1105_init_avb_params(struct sja1105_private *priv) #define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000) static void sja1105_setup_policer(struct sja1105_l2_policing_entry *policing, - int index) + int index, int mtu) { policing[index].sharindx = index; policing[index].smax = 65535; /* Burst size in bytes */ policing[index].rate = SJA1105_RATE_MBPS(1000); - policing[index].maxlen = ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN; + policing[index].maxlen = mtu; policing[index].partition = 0; } @@ -556,12 +556,16 @@ static int sja1105_init_l2_policing(struct sja1105_private *priv) */ for (i = 0, k = 0; i < SJA1105_NUM_PORTS; i++) { int bcast = (SJA1105_NUM_PORTS * SJA1105_NUM_TC) + i; + int mtu = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN; + + if (dsa_is_cpu_port(priv->ds, i)) + mtu += VLAN_HLEN; for (j = 0; j < SJA1105_NUM_TC; j++, k++) - sja1105_setup_policer(policing, k); + sja1105_setup_policer(policing, k, mtu); /* Set up this port's policer for broadcast traffic */ - sja1105_setup_policer(policing, bcast); + sja1105_setup_policer(policing, bcast, mtu); } return 0; } @@ -1544,6 +1548,7 @@ static const char * const sja1105_reset_reasons[] = { [SJA1105_RX_HWTSTAMPING] = "RX timestamping", [SJA1105_AGEING_TIME] = "Ageing time", [SJA1105_SCHEDULING] = "Time-aware scheduling", + [SJA1105_BEST_EFFORT_POLICING] = "Best-effort policing", }; /* For situations where we need to change a setting at runtime that is only @@ -1952,6 +1957,8 @@ static int sja1105_setup(struct dsa_switch *ds) /* Advertise the 8 egress queues */ ds->num_tx_queues = SJA1105_NUM_TC; + ds->mtu_enforcement_ingress = true; + /* The DSA/switchdev model brings up switch ports in standalone mode by * default, and that means vlan_filtering is 0 since they're not under * a bridge, so it's safe to set up switch tagging at this time. @@ -2120,6 +2127,39 @@ static int sja1105_set_ageing_time(struct dsa_switch *ds, return sja1105_static_config_reload(priv, SJA1105_AGEING_TIME); } +static int sja1105_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + int bcast = (SJA1105_NUM_PORTS * SJA1105_NUM_TC) + port; + struct sja1105_l2_policing_entry *policing; + struct sja1105_private *priv = ds->priv; + int tc; + + new_mtu += VLAN_ETH_HLEN + ETH_FCS_LEN; + + if (dsa_is_cpu_port(ds, port)) + new_mtu += VLAN_HLEN; + + policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; + + /* We set all 9 port policers to the same value, so just checking the + * broadcast one is fine. + */ + if (policing[bcast].maxlen == new_mtu) + return 0; + + for (tc = 0; tc < SJA1105_NUM_TC; tc++) + policing[port * SJA1105_NUM_TC + tc].maxlen = new_mtu; + + policing[bcast].maxlen = new_mtu; + + return sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); +} + +static int sja1105_get_max_mtu(struct dsa_switch *ds, int port) +{ + return 2043 - VLAN_ETH_HLEN - ETH_FCS_LEN; +} + static int sja1105_port_setup_tc(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data) @@ -2215,6 +2255,8 @@ static const struct dsa_switch_ops sja1105_switch_ops = { .setup = sja1105_setup, .teardown = sja1105_teardown, .set_ageing_time = sja1105_set_ageing_time, + .port_change_mtu = sja1105_change_mtu, + .port_max_mtu = sja1105_get_max_mtu, .phylink_validate = sja1105_phylink_validate, .phylink_mac_link_state = sja1105_mac_pcs_get_state, .phylink_mac_config = sja1105_mac_config, diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index 6e21a2a5cf01..19ce4aa0973b 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-core.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -664,16 +664,6 @@ static void vsc73xx_init_port(struct vsc73xx *vsc, int port) VSC73XX_MAC_CFG_TX_EN | VSC73XX_MAC_CFG_RX_EN); - /* Max length, we can do up to 9.6 KiB, so allow that. - * According to application not "VSC7398 Jumbo Frames" setting - * up the MTU to 9.6 KB does not affect the performance on standard - * frames, so just enable it. It is clear from the application note - * that "9.6 kilobytes" == 9600 bytes. - */ - vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, - port, - VSC73XX_MAXLEN, 9600); - /* Flow control for the CPU port: * Use a zero delay pause frame when pause condition is left * Obey pause control frames @@ -1030,6 +1020,24 @@ static void vsc73xx_get_ethtool_stats(struct dsa_switch *ds, int port, } } +static int vsc73xx_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + struct vsc73xx *vsc = ds->priv; + + return vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, + VSC73XX_MAXLEN, new_mtu); +} + +/* According to application not "VSC7398 Jumbo Frames" setting + * up the MTU to 9.6 KB does not affect the performance on standard + * frames. It is clear from the application note that + * "9.6 kilobytes" == 9600 bytes. + */ +static int vsc73xx_get_max_mtu(struct dsa_switch *ds, int port) +{ + return 9600; +} + static const struct dsa_switch_ops vsc73xx_ds_ops = { .get_tag_protocol = vsc73xx_get_tag_protocol, .setup = vsc73xx_setup, @@ -1041,6 +1049,8 @@ static const struct dsa_switch_ops vsc73xx_ds_ops = { .get_sset_count = vsc73xx_get_sset_count, .port_enable = vsc73xx_port_enable, .port_disable = vsc73xx_port_disable, + .port_change_mtu = vsc73xx_change_mtu, + .port_max_mtu = vsc73xx_get_max_mtu, }; static int vsc73xx_gpio_get(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 1bb07a5d82c9..98ec1b8a7d8e 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -1248,6 +1248,14 @@ static int bgmac_set_mac_address(struct net_device *net_dev, void *addr) return 0; } +static int bgmac_change_mtu(struct net_device *net_dev, int mtu) +{ + struct bgmac *bgmac = netdev_priv(net_dev); + + bgmac_write(bgmac, BGMAC_RXMAX_LENGTH, 32 + mtu); + return 0; +} + static const struct net_device_ops bgmac_netdev_ops = { .ndo_open = bgmac_open, .ndo_stop = bgmac_stop, @@ -1256,6 +1264,7 @@ static const struct net_device_ops bgmac_netdev_ops = { .ndo_set_mac_address = bgmac_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = phy_do_ioctl_running, + .ndo_change_mtu = bgmac_change_mtu, }; /************************************************** @@ -1530,6 +1539,9 @@ int bgmac_enet_probe(struct bgmac *bgmac) net_dev->hw_features = net_dev->features; net_dev->vlan_features = net_dev->features; + /* Omit FCS from max MTU size */ + net_dev->max_mtu = BGMAC_RX_MAX_FRAME_SIZE - ETH_FCS_LEN; + err = register_netdev(bgmac->net_dev); if (err) { dev_err(bgmac->dev, "Cannot register net device\n"); diff --git a/drivers/net/ethernet/broadcom/bgmac.h b/drivers/net/ethernet/broadcom/bgmac.h index 40d02fec2747..351c598a3ec6 100644 --- a/drivers/net/ethernet/broadcom/bgmac.h +++ b/drivers/net/ethernet/broadcom/bgmac.h @@ -351,7 +351,7 @@ #define BGMAC_DESC_CTL0_IOC 0x20000000 /* IRQ on complete */ #define BGMAC_DESC_CTL0_EOF 0x40000000 /* End of frame */ #define BGMAC_DESC_CTL0_SOF 0x80000000 /* Start of frame */ -#define BGMAC_DESC_CTL1_LEN 0x00001FFF +#define BGMAC_DESC_CTL1_LEN 0x00003FFF #define BGMAC_PHY_NOREGS BRCM_PSEUDO_PHY_ADDR #define BGMAC_PHY_MASK 0x1F @@ -366,7 +366,8 @@ #define BGMAC_RX_FRAME_OFFSET 30 /* There are 2 unused bytes between header and real data */ #define BGMAC_RX_BUF_OFFSET (NET_SKB_PAD + NET_IP_ALIGN - \ BGMAC_RX_FRAME_OFFSET) -#define BGMAC_RX_MAX_FRAME_SIZE 1536 /* Copied from b44/tg3 */ +/* Jumbo frame size with FCS */ +#define BGMAC_RX_MAX_FRAME_SIZE 9724 #define BGMAC_RX_BUF_SIZE (BGMAC_RX_FRAME_OFFSET + BGMAC_RX_MAX_FRAME_SIZE) #define BGMAC_RX_ALLOC_SIZE (SKB_DATA_ALIGN(BGMAC_RX_BUF_SIZE + BGMAC_RX_BUF_OFFSET) + \ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index dc0e27328661..b4731df186f4 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -2185,13 +2185,25 @@ static int ocelot_init_timestamp(struct ocelot *ocelot) /* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu. * The length of VLAN tags is accounted for automatically via DEV_MAC_TAGS_CFG. + * In the special case that it's the NPI port that we're configuring, the + * length of the tag and optional prefix needs to be accounted for privately, + * in order to be able to sustain communication at the requested @sdu. */ -static void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu) +void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu) { struct ocelot_port *ocelot_port = ocelot->ports[port]; int maxlen = sdu + ETH_HLEN + ETH_FCS_LEN; int atop_wm; + if (port == ocelot->npi) { + maxlen += OCELOT_TAG_LEN; + + if (ocelot->inj_prefix == OCELOT_TAG_PREFIX_SHORT) + maxlen += OCELOT_SHORT_PREFIX_LEN; + else if (ocelot->inj_prefix == OCELOT_TAG_PREFIX_LONG) + maxlen += OCELOT_LONG_PREFIX_LEN; + } + ocelot_port_writel(ocelot_port, maxlen, DEV_MAC_MAXLEN_CFG); /* Set Pause WM hysteresis @@ -2209,6 +2221,24 @@ static void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu) SYS_ATOP, port); ocelot_write(ocelot, ocelot_wm_enc(atop_wm), SYS_ATOP_TOT_CFG); } +EXPORT_SYMBOL(ocelot_port_set_maxlen); + +int ocelot_get_max_mtu(struct ocelot *ocelot, int port) +{ + int max_mtu = 65535 - ETH_HLEN - ETH_FCS_LEN; + + if (port == ocelot->npi) { + max_mtu -= OCELOT_TAG_LEN; + + if (ocelot->inj_prefix == OCELOT_TAG_PREFIX_SHORT) + max_mtu -= OCELOT_SHORT_PREFIX_LEN; + else if (ocelot->inj_prefix == OCELOT_TAG_PREFIX_LONG) + max_mtu -= OCELOT_LONG_PREFIX_LEN; + } + + return max_mtu; +} +EXPORT_SYMBOL(ocelot_get_max_mtu); void ocelot_init_port(struct ocelot *ocelot, int port) { @@ -2318,6 +2348,10 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi, { int cpu = ocelot->num_phys_ports; + ocelot->npi = npi; + ocelot->inj_prefix = injection; + ocelot->xtr_prefix = extraction; + /* The unicast destination PGID for the CPU port module is unused */ ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, cpu); /* Instead set up a multicast destination PGID for traffic copied to @@ -2330,19 +2364,10 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi, ANA_PORT_PORT_CFG, cpu); if (npi >= 0 && npi < ocelot->num_phys_ports) { - int sdu = ETH_DATA_LEN + OCELOT_TAG_LEN; - ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M | QSYS_EXT_CPU_CFG_EXT_CPU_PORT(npi), QSYS_EXT_CPU_CFG); - if (injection == OCELOT_TAG_PREFIX_SHORT) - sdu += OCELOT_SHORT_PREFIX_LEN; - else if (injection == OCELOT_TAG_PREFIX_LONG) - sdu += OCELOT_LONG_PREFIX_LEN; - - ocelot_port_set_maxlen(ocelot, npi, sdu); - /* Enable NPI port */ ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE | diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index e0d3310957ff..e77b274a09fd 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -423,6 +423,28 @@ int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev) } EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init); +int bcm_phy_enable_jumbo(struct phy_device *phydev) +{ + int ret; + + ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL); + if (ret < 0) + return ret; + + /* Enable extended length packet reception */ + ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, + ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN); + if (ret < 0) + return ret; + + /* Enable the elastic FIFO for raising the transmission limit from + * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation + * latency. + */ + return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE); +} +EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo); + MODULE_DESCRIPTION("Broadcom PHY Library"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Broadcom Corporation"); diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h index c86fb9d1240c..129df819be8c 100644 --- a/drivers/net/phy/bcm-phy-lib.h +++ b/drivers/net/phy/bcm-phy-lib.h @@ -65,5 +65,6 @@ void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, struct ethtool_stats *stats, u64 *data); void bcm_phy_r_rc_cal_reset(struct phy_device *phydev); int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev); +int bcm_phy_enable_jumbo(struct phy_device *phydev); #endif /* _LINUX_BCM_PHY_LIB_H */ diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index af8eabe7a6d4..692048d86ab1 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -181,6 +181,10 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev) if (ret) return ret; + ret = bcm_phy_enable_jumbo(phydev); + if (ret) + return ret; + ret = bcm_phy_downshift_get(phydev, &count); if (ret) return ret; |