From 6bb1688a11e018651b4e18ca31599ab5abf0eb7c Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 Oct 2015 09:13:05 +0100 Subject: cpuidle: mvebu: indicate failure to enter deeper sleep states The cpuidle ->enter method expects the return value to be the sleep state we entered. Returning negative numbers or other codes is not permissible since coupled CPU idle was merged. At least some of the mvebu_v7_cpu_suspend() implementations return the value from cpu_suspend(), which returns zero if the CPU vectors back into the kernel via cpu_resume() (the success case), or the non-zero return value of the suspend actor, or one (failure cases). We do not want to be returning the failure case value back to CPU idle as that indicates that we successfully entered one of the deeper idle states. Always return zero instead, indicating that we slept for the shortest amount of time. Signed-off-by: Russell King --- drivers/cpuidle/cpuidle-mvebu-v7.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/cpuidle/cpuidle-mvebu-v7.c b/drivers/cpuidle/cpuidle-mvebu-v7.c index 01a856971f05..18ded9e7cb34 100644 --- a/drivers/cpuidle/cpuidle-mvebu-v7.c +++ b/drivers/cpuidle/cpuidle-mvebu-v7.c @@ -39,8 +39,12 @@ static int mvebu_v7_enter_idle(struct cpuidle_device *dev, ret = mvebu_v7_cpu_suspend(deepidle); cpu_pm_exit(); + /* + * If we failed to enter the desired state, indicate that we + * slept lightly. + */ if (ret) - return ret; + return 0; return index; } -- cgit From 0f4008ecc5420fb1f1e3d1b50e93f24e492da155 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Mar 2020 11:57:39 +0000 Subject: net: sfp: add interface modes bitmap We currently parse the SFP EEPROM to a bitmap of ethtool link modes, and then attempt to convert the link modes to a PHY interface mode. While this works at present, there are cases where this is sub-optimal. For example, where a module can operate with several different PHY interface modes. To start addressing this, arrange for the SFP EEPROM parsing to also provide a bitmap of the possible PHY interface modes. Signed-off-by: Russell King --- drivers/net/phy/marvell10g.c | 3 +- drivers/net/phy/phylink.c | 4 ++- drivers/net/phy/sfp-bus.c | 76 +++++++++++++++++++++++++++++++++----------- include/linux/sfp.h | 5 +-- 4 files changed, 65 insertions(+), 23 deletions(-) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index b1bb9b8e1e4e..37c766517f9c 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -343,9 +343,10 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) { struct phy_device *phydev = upstream; __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; + DECLARE_PHY_INTERFACE_MASK(interfaces); phy_interface_t iface; - sfp_parse_support(phydev->sfp_bus, id, support); + sfp_parse_support(phydev->sfp_bus, id, support, interfaces); iface = sfp_select_interface(phydev->sfp_bus, support); if (iface != PHY_INTERFACE_MODE_10GBASER) { diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index dc2800beacc3..84d70d1812d7 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -75,6 +75,7 @@ struct phylink { struct sfp_bus *sfp_bus; bool sfp_may_have_phy; + DECLARE_PHY_INTERFACE_MASK(sfp_interfaces); __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); u8 sfp_port; }; @@ -2112,7 +2113,8 @@ static int phylink_sfp_module_insert(void *upstream, ASSERT_RTNL(); linkmode_zero(support); - sfp_parse_support(pl->sfp_bus, id, support); + phy_interface_zero(pl->sfp_interfaces); + sfp_parse_support(pl->sfp_bus, id, support, pl->sfp_interfaces); pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support); /* If this module may have a PHY connecting later, defer until later */ diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 2e11176c6b94..5a04ed6737a1 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -13,7 +13,8 @@ struct sfp_quirk { const char *vendor; const char *part; - void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); + void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes, + unsigned long *interfaces); }; /** @@ -39,13 +40,15 @@ struct sfp_bus { }; static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, - unsigned long *modes) + unsigned long *modes, unsigned long *interfaces) { phylink_set(modes, 2500baseX_Full); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); } static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, - unsigned long *modes) + unsigned long *modes, + unsigned long *interfaces) { /* Ubiquiti U-Fiber Instant module claims that support all transceiver * types including 10G Ethernet which is not truth. So clear all claimed @@ -226,12 +229,14 @@ EXPORT_SYMBOL_GPL(sfp_may_have_phy); * @bus: a pointer to the &struct sfp_bus structure for the sfp module * @id: a pointer to the module's &struct sfp_eeprom_id * @support: pointer to an array of unsigned long for the ethtool support mask + * @interfaces: pointer to an array of unsigned long for phy interface modes + * mask * * Parse the EEPROM identification information and derive the supported * ethtool link modes for the module. */ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, - unsigned long *support) + unsigned long *support, unsigned long *interfaces) { unsigned int br_min, br_nom, br_max; __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; @@ -258,27 +263,41 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, } /* Set ethtool support from the compliance fields. */ - if (id->base.e10g_base_sr) + if (id->base.e10g_base_sr) { phylink_set(modes, 10000baseSR_Full); - if (id->base.e10g_base_lr) + __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); + } + if (id->base.e10g_base_lr) { phylink_set(modes, 10000baseLR_Full); - if (id->base.e10g_base_lrm) + __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); + } + if (id->base.e10g_base_lrm) { phylink_set(modes, 10000baseLRM_Full); - if (id->base.e10g_base_er) + __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); + } + if (id->base.e10g_base_er) { phylink_set(modes, 10000baseER_Full); + __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); + } if (id->base.e1000_base_sx || id->base.e1000_base_lx || - id->base.e1000_base_cx) + id->base.e1000_base_cx) { phylink_set(modes, 1000baseX_Full); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); + } if (id->base.e1000_base_t) { phylink_set(modes, 1000baseT_Half); phylink_set(modes, 1000baseT_Full); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, interfaces); } /* 1000Base-PX or 1000Base-BX10 */ if ((id->base.e_base_px || id->base.e_base_bx10) && - br_min <= 1300 && br_max >= 1200) + br_min <= 1300 && br_max >= 1200) { phylink_set(modes, 1000baseX_Full); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); + } /* 100Base-FX, 100Base-LX, 100Base-PX, 100Base-BX10 */ if (id->base.e100_base_fx || id->base.e100_base_lx) @@ -291,21 +310,30 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, */ if ((id->base.sfp_ct_passive || id->base.sfp_ct_active) && br_nom) { /* This may look odd, but some manufacturers use 12000MBd */ - if (br_min <= 12000 && br_max >= 10300) + if (br_min <= 12000 && br_max >= 10300) { phylink_set(modes, 10000baseCR_Full); - if (br_min <= 3200 && br_max >= 3100) + __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); + } + if (br_min <= 3200 && br_max >= 3100) { phylink_set(modes, 2500baseX_Full); - if (br_min <= 1300 && br_max >= 1200) + __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); + } + if (br_min <= 1300 && br_max >= 1200) { phylink_set(modes, 1000baseX_Full); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); + } } if (id->base.sfp_ct_passive) { - if (id->base.passive.sff8431_app_e) + if (id->base.passive.sff8431_app_e) { phylink_set(modes, 10000baseCR_Full); + __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); + } } if (id->base.sfp_ct_active) { if (id->base.active.sff8431_app_e || id->base.active.sff8431_lim) { phylink_set(modes, 10000baseCR_Full); + __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); } } @@ -330,12 +358,14 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, case SFF8024_ECC_10GBASE_T_SFI: case SFF8024_ECC_10GBASE_T_SR: phylink_set(modes, 10000baseT_Full); + __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); break; case SFF8024_ECC_5GBASE_T: phylink_set(modes, 5000baseT_Full); break; case SFF8024_ECC_2_5GBASE_T: phylink_set(modes, 2500baseT_Full); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); break; default: dev_warn(bus->sfp_dev, @@ -348,10 +378,14 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, if (id->base.fc_speed_100 || id->base.fc_speed_200 || id->base.fc_speed_400) { - if (id->base.br_nominal >= 31) + if (id->base.br_nominal >= 31) { phylink_set(modes, 2500baseX_Full); - if (id->base.br_nominal >= 12) + __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); + } + if (id->base.br_nominal >= 12) { phylink_set(modes, 1000baseX_Full); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); + } } /* If we haven't discovered any modes that this module supports, try @@ -364,14 +398,18 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, * 2500BASE-X, so we allow some slack here. */ if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS) && br_nom) { - if (br_min <= 1300 && br_max >= 1200) + if (br_min <= 1300 && br_max >= 1200) { phylink_set(modes, 1000baseX_Full); - if (br_min <= 3200 && br_max >= 2500) + __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); + } + if (br_min <= 3200 && br_max >= 2500) { phylink_set(modes, 2500baseX_Full); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); + } } if (bus->sfp_quirk) - bus->sfp_quirk->modes(id, modes); + bus->sfp_quirk->modes(id, modes, interfaces); bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS); diff --git a/include/linux/sfp.h b/include/linux/sfp.h index 38893e4dd0f0..2da1a5181779 100644 --- a/include/linux/sfp.h +++ b/include/linux/sfp.h @@ -535,7 +535,7 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, unsigned long *support); bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id); void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, - unsigned long *support); + unsigned long *support, unsigned long *interfaces); phy_interface_t sfp_select_interface(struct sfp_bus *bus, unsigned long *link_modes); @@ -565,7 +565,8 @@ static inline bool sfp_may_have_phy(struct sfp_bus *bus, static inline void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, - unsigned long *support) + unsigned long *support, + unsigned long *interfaces) { } -- cgit From 4bd23792e88485c8fc2fee0988c5739c3fd53fbb Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Mar 2020 12:12:43 +0000 Subject: net: phylink: add mac PHY interface bitmap Add a PHY interface bitmap for the MAC driver to specify which PHY interfaces are supported to phylink. Signed-off-by: Russell King --- include/linux/phylink.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/phylink.h b/include/linux/phylink.h index d81a714cfbbd..d2874f815a47 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -74,6 +74,7 @@ struct phylink_config { bool poll_fixed_state; void (*get_fixed_state)(struct phylink_config *config, struct phylink_link_state *state); + DECLARE_PHY_INTERFACE_MASK(supported_interfaces); }; /** -- cgit From c78989ad44be2a0b28e65fc420a3ca0bee9a4a18 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Mar 2020 12:12:43 +0000 Subject: net: phylink: use phy interface mode bitmaps for optical modules Where a MAC provides the PHY interface mode capabilities, use the PHY interface mode bitmaps to select the operating interface mode for optical SFP modules, rather than using the linkmode bitmaps. Signed-off-by: Russell King --- drivers/net/phy/phylink.c | 56 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 84d70d1812d7..0244fdf843f5 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -2022,6 +2022,41 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) pl->netdev->sfp_bus = NULL; } +static const phy_interface_t phylink_sfp_interface_preference[] = { + PHY_INTERFACE_MODE_USXGMII, + PHY_INTERFACE_MODE_10GBASER, + PHY_INTERFACE_MODE_10GKR, + PHY_INTERFACE_MODE_2500BASEX, + PHY_INTERFACE_MODE_SGMII, + PHY_INTERFACE_MODE_1000BASEX, +}; + +static phy_interface_t phylink_select_interface(struct phylink *pl, + const unsigned long *intf, + const char *intf_name) +{ + DECLARE_PHY_INTERFACE_MASK(u); + phy_interface_t interface; + size_t i; + + phy_interface_and(u, intf, pl->config->supported_interfaces); + + interface = PHY_INTERFACE_MODE_NA; + for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++) + if (test_bit(phylink_sfp_interface_preference[i], u)) { + interface = phylink_sfp_interface_preference[i]; + break; + } + + phylink_info(pl, "interfaces=[mac=%*pbl %s=%*pbl] selected %d (%s)\n", + (int)PHY_INTERFACE_MODE_MAX, + pl->config->supported_interfaces, + intf_name, (int)PHY_INTERFACE_MODE_MAX, intf, + interface, phy_modes(interface)); + + return interface; +} + static int phylink_sfp_config(struct phylink *pl, u8 mode, const unsigned long *supported, const unsigned long *advertising) @@ -2104,25 +2139,33 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode, return ret; } +static int phylink_sfp_config_nophy(struct phylink *pl) +{ + if (!phy_interface_empty(pl->config->supported_interfaces)) + phylink_select_interface(pl, pl->sfp_interfaces, "sfp"); + + return phylink_sfp_config(pl, MLO_AN_INBAND, + pl->sfp_support, pl->sfp_support); +} + static int phylink_sfp_module_insert(void *upstream, const struct sfp_eeprom_id *id) { struct phylink *pl = upstream; - unsigned long *support = pl->sfp_support; ASSERT_RTNL(); - linkmode_zero(support); + linkmode_zero(pl->sfp_support); phy_interface_zero(pl->sfp_interfaces); - sfp_parse_support(pl->sfp_bus, id, support, pl->sfp_interfaces); - pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support); + sfp_parse_support(pl->sfp_bus, id, pl->sfp_support, pl->sfp_interfaces); + pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, pl->sfp_support); /* If this module may have a PHY connecting later, defer until later */ pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id); if (pl->sfp_may_have_phy) return 0; - return phylink_sfp_config(pl, MLO_AN_INBAND, support, support); + return phylink_sfp_config_nophy(pl); } static int phylink_sfp_module_start(void *upstream) @@ -2141,8 +2184,7 @@ static int phylink_sfp_module_start(void *upstream) if (!pl->sfp_may_have_phy) return 0; - return phylink_sfp_config(pl, MLO_AN_INBAND, - pl->sfp_support, pl->sfp_support); + return phylink_sfp_config_nophy(pl); } static void phylink_sfp_module_stop(void *upstream) -- cgit From d197d6477b7a8a64606715b0617b8bef7ccf4efc Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Mar 2020 12:15:38 +0000 Subject: net: mvneta: fill in phy interface mode bitmap Fill in the phy interface mode bitmap for the Marvell mvneta driver, so phylink can know which interfaces are supported by the MAC. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvneta.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index a635cf84608a..b1ffa6f4f03b 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -5178,6 +5178,22 @@ static int mvneta_probe(struct platform_device *pdev) pp->phylink_config.dev = &dev->dev; pp->phylink_config.type = PHYLINK_NETDEV; + __set_bit(PHY_INTERFACE_MODE_SGMII, + pp->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII, + pp->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII_ID, + pp->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, + pp->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, + pp->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_QSGMII, + pp->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + pp->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + pp->phylink_config.supported_interfaces); phylink = phylink_create(&pp->phylink_config, pdev->dev.fwnode, phy_mode, &mvneta_phylink_ops); -- cgit From 5841242c08016430214846ab47162c9b2a415040 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Mar 2020 12:15:38 +0000 Subject: net: mvpp2: fill in phy interface mode bitmap Fill in the phy interface mode bitmap for the Marvell mvpp2 driver, so phylink can know which interfaces are supported by the MAC. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 1767c60056c5..342fd8506001 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -6887,6 +6887,29 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->phylink_config.dev = &dev->dev; port->phylink_config.type = PHYLINK_NETDEV; + if (mvpp2_port_supports_xlg(port)) { + __set_bit(PHY_INTERFACE_MODE_10GBASER, + port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_XAUI, + port->phylink_config.supported_interfaces); + } + if (mvpp2_port_supports_rgmii(port)) { + __set_bit(PHY_INTERFACE_MODE_RGMII, + port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII_ID, + port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, + port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, + port->phylink_config.supported_interfaces); + } + __set_bit(PHY_INTERFACE_MODE_SGMII, + port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + port->phylink_config.supported_interfaces); + phylink = phylink_create(&port->phylink_config, port_fwnode, phy_mode, &mvpp2_phylink_ops); if (IS_ERR(phylink)) { -- cgit From 7aaeb289a0dc5138ecfa86982db03263f0fdb012 Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Tue, 20 Oct 2020 19:09:11 +0200 Subject: net: dsa: fill phylink's config supported_interfaces member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new DSA switch operation, phylink_get_interfaces, which should fill in which PHY_INTERFACE_MODE_* are supported by given port. Use this before phylink_create to fill phylink's config supported_interfaces member. This allows for phylink to determine which PHY_INTERFACE_MODE to use with SFP modules. Signed-off-by: Marek Behún Signed-off-by: Russell King --- include/net/dsa.h | 2 ++ net/dsa/slave.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/include/net/dsa.h b/include/net/dsa.h index 83a933e563fe..35a9e072fb87 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -529,6 +529,8 @@ struct dsa_switch_ops { /* * PHYLINK integration */ + void (*phylink_get_interfaces)(struct dsa_switch *ds, int port, + unsigned long *supported_interfaces); void (*phylink_validate)(struct dsa_switch *ds, int port, unsigned long *supported, struct phylink_link_state *state); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 992fcab4b552..d2377be80591 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1746,6 +1746,10 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev) dp->pl_config.poll_fixed_state = true; } + if (ds->ops->phylink_get_interfaces) + ds->ops->phylink_get_interfaces(ds, dp->index, + dp->pl_config.supported_interfaces); + dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), mode, &dsa_port_phylink_mac_ops); if (IS_ERR(dp->pl)) { -- cgit From fa950e0c8239b653250365f8d7c37fae74052e4f Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Tue, 20 Oct 2020 19:09:12 +0200 Subject: net: dsa: mv88e6xxx: implement .phylink_get_interfaces operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the .phylink_get_interfaces method for mv88e6xxx driver. We are currently only interested in SGMII, 1000base-x and 2500base-x modes (for the SFP code). USXGMII and 10gbase-r can be added later for Amethyst. XAUI and RXAUI are irrelevant for SFP (but maybe not for QSFP?). Signed-off-by: Marek Behún Signed-off-by: Russell King --- drivers/net/dsa/mv88e6xxx/chip.c | 57 ++++++++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/chip.h | 2 ++ 2 files changed, 59 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index e08bf9377140..5f4e39240902 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -660,6 +660,50 @@ static void mv88e6xxx_validate(struct dsa_switch *ds, int port, phylink_helper_basex_speed(state); } +static void mv88e6352_phylink_get_interfaces(struct mv88e6xxx_chip *chip, + int port, + unsigned long *supported) +{ + if (mv88e6xxx_serdes_get_lane(chip, port)) { + /* FIXME: does code for 6352 family support changing between + * SGMII and 1000base-x? + */ + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + } +} + +static void mv88e6341_phylink_get_interfaces(struct mv88e6xxx_chip *chip, + int port, + unsigned long *supported) +{ + if (port == 5) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); + } +} + +static void mv88e6390_phylink_get_interfaces(struct mv88e6xxx_chip *chip, + int port, + unsigned long *supported) +{ + if (port == 9 || port == 10) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); + } +} + +static void mv88e6xxx_get_interfaces(struct dsa_switch *ds, int port, + unsigned long *supported) +{ + struct mv88e6xxx_chip *chip = ds->priv; + + if (chip->info->ops->phylink_get_interfaces) + chip->info->ops->phylink_get_interfaces(chip, port, supported); +} + static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, unsigned int mode, const struct phylink_link_state *state) @@ -3483,6 +3527,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_get_interfaces = mv88e6341_phylink_get_interfaces, .phylink_validate = mv88e6341_phylink_validate, }; @@ -3658,6 +3703,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_get_interfaces = mv88e6352_phylink_get_interfaces, .phylink_validate = mv88e6352_phylink_validate, }; @@ -3758,6 +3804,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_get_interfaces = mv88e6352_phylink_get_interfaces, .phylink_validate = mv88e6352_phylink_validate, }; @@ -3859,6 +3906,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_get_interfaces = mv88e6390_phylink_get_interfaces, .phylink_validate = mv88e6390_phylink_validate, }; @@ -3920,6 +3968,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_get_interfaces = mv88e6390_phylink_get_interfaces, .phylink_validate = mv88e6390x_phylink_validate, }; @@ -3980,6 +4029,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .serdes_get_regs = mv88e6390_serdes_get_regs, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_interfaces = mv88e6390_phylink_get_interfaces, .phylink_validate = mv88e6390_phylink_validate, }; @@ -4040,6 +4090,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_interfaces = mv88e6352_phylink_get_interfaces, .phylink_validate = mv88e6352_phylink_validate, }; @@ -4142,6 +4193,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_interfaces = mv88e6390_phylink_get_interfaces, .phylink_validate = mv88e6390_phylink_validate, }; @@ -4285,6 +4337,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_interfaces = mv88e6341_phylink_get_interfaces, .phylink_validate = mv88e6341_phylink_validate, }; @@ -4434,6 +4487,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .serdes_get_stats = mv88e6352_serdes_get_stats, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, + .phylink_get_interfaces = mv88e6352_phylink_get_interfaces, .phylink_validate = mv88e6352_phylink_validate, }; @@ -4499,6 +4553,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, + .phylink_get_interfaces = mv88e6390_phylink_get_interfaces, .phylink_validate = mv88e6390_phylink_validate, }; @@ -4563,6 +4618,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_interfaces = mv88e6390_phylink_get_interfaces, .phylink_validate = mv88e6390x_phylink_validate, }; @@ -5740,6 +5796,7 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .get_tag_protocol = mv88e6xxx_get_tag_protocol, .setup = mv88e6xxx_setup, .teardown = mv88e6xxx_teardown, + .phylink_get_interfaces = mv88e6xxx_get_interfaces, .phylink_validate = mv88e6xxx_validate, .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state, .phylink_mac_config = mv88e6xxx_mac_config, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index a57c8886f3ac..fe8322be4192 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -573,6 +573,8 @@ struct mv88e6xxx_ops { const struct mv88e6xxx_ptp_ops *ptp_ops; /* Phylink */ + void (*phylink_get_interfaces)(struct mv88e6xxx_chip *chip, int port, + unsigned long *supported); void (*phylink_validate)(struct mv88e6xxx_chip *chip, int port, unsigned long *mask, struct phylink_link_state *state); -- cgit From 5a7f14327bab317c8b744d7f675f859882fd8eea Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 13 Mar 2020 10:48:32 +0000 Subject: net: mtk_eth_soc: use resolved link config for PCS PHY The SGMII PCS PHY needs to be updated with the link configuration in the mac_link_up() call rather than in mac_config(). However, mtk_sgmii_setup_mode_force() programs the SGMII block during mac_config() when using 802.3z interface modes with the link configuration. Split that functionality from mtk_sgmii_setup_mode_force(), moving it to a new mtk_sgmii_link_up() function, and call it from mac_link_up(). This does not look correct to me: 802.3z modes operate at a fixed speed. The contents of mtk_sgmii_link_up() look more appropriate for SGMII mode, but the original code definitely did not call mtk_sgmii_setup_mode_force() for SGMII mode but only 802.3z mode. Signed-off-by: Russell King --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 11 ++++++++- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 3 ++- drivers/net/ethernet/mediatek/mtk_sgmii.c | 37 ++++++++++++++++++++--------- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 01d3ee4b5829..b32875512ee8 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -335,7 +335,7 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode, /* Setup SGMIISYS with the determined property */ if (state->interface != PHY_INTERFACE_MODE_SGMII) err = mtk_sgmii_setup_mode_force(eth->sgmii, sid, - state); + state->interface); else if (phylink_autoneg_inband(mode)) err = mtk_sgmii_setup_mode_an(eth->sgmii, sid); @@ -432,6 +432,15 @@ static void mtk_mac_link_up(struct phylink_config *config, phylink_config); u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + if (phy_interface_mode_is_8023z(interface)) { + struct mtk_eth *eth = mac->hw; + + /* Decide how GMAC and SGMIISYS be mapped */ + int sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? + 0 : mac->id; + mtk_sgmii_link_up(eth->sgmii, sid, speed, duplex); + } + mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | MAC_MCR_FORCE_RX_FC); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index fd3cec8f06ba..646029b17bcd 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -938,7 +938,8 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np, u32 ana_rgc3); int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id); int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, - const struct phylink_link_state *state); + phy_interface_t interface); +void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex); void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id); int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id); diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c index 32d83421226a..372c85c830b5 100644 --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c @@ -60,7 +60,7 @@ int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id) } int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, - const struct phylink_link_state *state) + phy_interface_t interface) { unsigned int val; @@ -69,7 +69,7 @@ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, regmap_read(ss->regmap[id], ss->ana_rgc3, &val); val &= ~RG_PHY_SPEED_MASK; - if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + if (interface == PHY_INTERFACE_MODE_2500BASEX) val |= RG_PHY_SPEED_3_125G; regmap_write(ss->regmap[id], ss->ana_rgc3, val); @@ -78,11 +78,33 @@ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, val &= ~SGMII_AN_ENABLE; regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val); + if (interface == PHY_INTERFACE_MODE_1000BASEX || + interface == PHY_INTERFACE_MODE_2500BASEX) { + /* SGMII force mode setting */ + regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val); + val &= ~SGMII_IF_MODE_MASK; + val |= SGMII_SPEED_1000; + val |= SGMII_DUPLEX_FULL; + regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val); + } + + /* Release PHYA power down state */ + regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val); + val &= ~SGMII_PHYA_PWD; + regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val); + + return 0; +} + +void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex) +{ + unsigned int val; + /* SGMII force mode setting */ regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val); val &= ~SGMII_IF_MODE_MASK; - switch (state->speed) { + switch (speed) { case SPEED_10: val |= SGMII_SPEED_10; break; @@ -95,17 +117,10 @@ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, break; } - if (state->duplex == DUPLEX_FULL) + if (duplex == DUPLEX_FULL) val |= SGMII_DUPLEX_FULL; regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val); - - /* Release PHYA power down state */ - regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val); - val &= ~SGMII_PHYA_PWD; - regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val); - - return 0; } void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id) -- cgit From ce6a281bc49df8f708428d7613296d857878dda6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 21 Jun 2020 14:30:20 +0100 Subject: net: phy: at803x: simplify custom phy id matching The at803x driver contains a function, at803x_match_phy_id(), which tests whether the PHY ID matches the value passed, comparing phy_id with phydev->phy_id and testing all bits that have a "one" in the mask, phydev->drv->phy_id_mask. This is the same test that is used to match the driver, with phy_id replaced with the driver specified ID, phydev->drv->phy_id. Hence, we already know the value of the bits being tested if we look at phydev->drv->phy_id directly, and we do not require a complicated test to check them. Test directly against phydev->drv->phy_id instead. Signed-off-by: Russell King --- drivers/net/phy/at803x.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index c2aa4c92edde..51a2540ae101 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -409,12 +409,6 @@ static int at8031_register_regulators(struct phy_device *phydev) return 0; } -static bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id) -{ - return (phydev->phy_id & phydev->drv->phy_id_mask) - == (phy_id & phydev->drv->phy_id_mask); -} - static int at803x_parse_dt(struct phy_device *phydev) { struct device_node *node = phydev->mdio.dev.of_node; @@ -479,8 +473,8 @@ static int at803x_parse_dt(struct phy_device *phydev) * to the AR8030 so there might be a good chance it works on * the AR8030 too. */ - if (at803x_match_phy_id(phydev, ATH8030_PHY_ID) || - at803x_match_phy_id(phydev, ATH8035_PHY_ID)) { + if (phydev->drv->phy_id == ATH8030_PHY_ID || + phydev->drv->phy_id == ATH8035_PHY_ID) { priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; } @@ -508,7 +502,7 @@ static int at803x_parse_dt(struct phy_device *phydev) /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping * options. */ - if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) { + if (phydev->drv->phy_id == ATH8031_PHY_ID) { if (of_property_read_bool(node, "qca,keep-pll-enabled")) priv->flags |= AT803X_KEEP_PLL_ENABLED; @@ -644,7 +638,7 @@ static int at803x_config_init(struct phy_device *phydev) if (ret < 0) return ret; - if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) { + if (phydev->drv->phy_id == ATH8031_PHY_ID) { ret = at8031_pll_config(phydev); if (ret < 0) return ret; -- cgit From 42e1e52dd64efa7b1b9a4e91da226016e46e2b25 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 29 Nov 2019 00:31:03 +0000 Subject: net: phy: add helpers for comparing phy IDs There are several places which open code comparing PHY IDs. Provide a couple of helpers to assist with this, using a slightly simpler test than the original: - phy_id_compare() compares two arbitary PHY IDs and a mask of the significant bits in the ID. - phydev_id_compare() compares the bound phydev with the specified PHY ID, using the bound driver's mask. Signed-off-by: Russell King --- drivers/net/phy/phy_device.c | 16 +++++++--------- drivers/net/phy/phylink.c | 4 ++-- include/linux/phy.h | 28 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index cc38e326405a..d1780dc54846 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -381,8 +381,7 @@ int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask) fixup = list_entry(pos, struct phy_fixup, list); if ((!strcmp(fixup->bus_id, bus_id)) && - ((fixup->phy_uid & phy_uid_mask) == - (phy_uid & phy_uid_mask))) { + phy_id_compare(fixup->phy_uid, phy_uid, phy_uid_mask)) { list_del(&fixup->list); kfree(fixup); ret = 0; @@ -418,8 +417,8 @@ static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup) if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0) return 0; - if ((fixup->phy_uid & fixup->phy_uid_mask) != - (phydev->phy_id & fixup->phy_uid_mask)) + if (!phy_id_compare(phydev->phy_id, fixup->phy_uid, + fixup->phy_uid_mask)) if (fixup->phy_uid != PHY_ANY_UID) return 0; @@ -466,15 +465,14 @@ static int phy_bus_match(struct device *dev, struct device_driver *drv) if (phydev->c45_ids.device_ids[i] == 0xffffffff) continue; - if ((phydrv->phy_id & phydrv->phy_id_mask) == - (phydev->c45_ids.device_ids[i] & - phydrv->phy_id_mask)) + if (phy_id_compare(phydev->c45_ids.device_ids[i], + phydrv->phy_id, phydrv->phy_id_mask)) return 1; } return 0; } else { - return (phydrv->phy_id & phydrv->phy_id_mask) == - (phydev->phy_id & phydrv->phy_id_mask); + return phy_id_compare(phydev->phy_id, phydrv->phy_id, + phydrv->phy_id_mask); } } diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 0244fdf843f5..18541d9c9ed3 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -2220,8 +2220,8 @@ static void phylink_sfp_link_up(void *upstream) */ static bool phylink_phy_no_inband(struct phy_device *phy) { - return phy->is_c45 && - (phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150; + return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1], + 0xae025150, 0xfffffff0); } static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) diff --git a/include/linux/phy.h b/include/linux/phy.h index 1a12e4436b5b..67a71f714bad 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -893,6 +893,34 @@ struct phy_driver { #define PHY_ID_MATCH_MODEL(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 4) #define PHY_ID_MATCH_VENDOR(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 10) +/** + * phy_id_compare - compare @id1 with @id2 taking account of @mask + * @id1: first PHY ID + * @id2: second PHY ID + * @mask: the PHY ID mask, set bits are significant in matching + * + * Return true if the bits from @id1 and @id2 specified by @mask match. + * This uses an equivalent test to (@id & @mask) == (@phy_id & @mask). + */ +static inline bool phy_id_compare(u32 id1, u32 id2, u32 mask) +{ + return !((id1 ^ id2) & mask); +} + +/** + * phydev_id_compare - compare @id with the PHY's Clause 22 ID + * @phydev: the PHY device + * @id: the PHY ID to be matched + * + * Compare the @phydev clause 22 ID with the provided @id and return true or + * false depending whether it matches, using the bound driver mask. The + * @phydev must be bound to a driver. + */ +static inline bool phydev_id_compare(struct phy_device *phydev, u32 id) +{ + return phy_id_compare(id, phydev->phy_id, phydev->drv->phy_id_mask); +} + /* A Structure for boards to register fixups with the PHY Lib */ struct phy_fixup { struct list_head list; -- cgit From 129ee1f05d28ef0733504aeb55454eb5c22185af Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 29 Nov 2019 21:41:56 +0000 Subject: net: phy: marvell10g: add downshift tunable support Add support for the downshift tunable for the Marvell 88x3310 PHY. Downshift is only usable with firmware 0.3.5.0 and later. Signed-off-by: Russell King --- drivers/net/phy/marvell10g.c | 87 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 37c766517f9c..0a0705116759 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -22,6 +22,7 @@ * If both the fiber and copper ports are connected, the first to gain * link takes priority and the other port is completely locked out. */ +#include #include #include #include @@ -32,6 +33,8 @@ #define MV_PHY_ALASKA_NBT_QUIRK_MASK 0xfffffffe #define MV_PHY_ALASKA_NBT_QUIRK_REV (MARVELL_PHY_ID_88X3310 | 0xa) +#define MV_VERSION(a,b,c,d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) + enum { MV_PMA_FW_VER0 = 0xc011, MV_PMA_FW_VER1 = 0xc012, @@ -52,6 +55,15 @@ enum { MV_PCS_CSCR1_MDIX_MDIX = 0x0020, MV_PCS_CSCR1_MDIX_AUTO = 0x0060, + MV_PCS_DSC1 = 0x8003, + MV_PCS_DSC1_ENABLE = BIT(9), + MV_PCS_DSC1_10GBT = 0x01c0, + MV_PCS_DSC1_1GBR = 0x0038, + MV_PCS_DSC1_100BTX = 0x0007, + MV_PCS_DSC2 = 0x8004, + MV_PCS_DSC2_2P5G = 0xf000, + MV_PCS_DSC2_5G = 0x0f00, + MV_PCS_CSSR1 = 0x8008, MV_PCS_CSSR1_SPD1_MASK = 0xc000, MV_PCS_CSSR1_SPD1_SPD2 = 0xc000, @@ -286,6 +298,66 @@ static int mv3310_reset(struct phy_device *phydev, u32 unit) 5000, 100000, true); } +static int mv3310_get_downshift(struct phy_device *phydev, u8 *ds) +{ + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + int val; + + if (priv->firmware_ver < MV_VERSION(0,3,5,0)) + return -EOPNOTSUPP; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1); + if (val < 0) + return val; + + if (val & MV_PCS_DSC1_ENABLE) + /* assume that all fields are the same */ + *ds = 1 + FIELD_GET(MV_PCS_DSC1_10GBT, (u16)val); + else + *ds = DOWNSHIFT_DEV_DISABLE; + + return 0; +} + +static int mv3310_set_downshift(struct phy_device *phydev, u8 ds) +{ + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + u16 val; + int err; + + /* Fails to downshift with v0.3.5.0 and earlier */ + if (priv->firmware_ver < MV_VERSION(0,3,5,0)) + return -EOPNOTSUPP; + + if (ds == DOWNSHIFT_DEV_DISABLE) + return phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1, + MV_PCS_DSC1_ENABLE); + + /* FIXME: The default is disabled, so should we disable? */ + if (ds == DOWNSHIFT_DEV_DEFAULT_COUNT) + ds = 2; + + if (ds > 8) + return -E2BIG; + + ds -= 1; + val = FIELD_PREP(MV_PCS_DSC2_2P5G, ds); + val |= FIELD_PREP(MV_PCS_DSC2_5G, ds); + err = phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC2, + MV_PCS_DSC2_2P5G | MV_PCS_DSC2_5G, val); + if (err < 0) + return err; + + val = MV_PCS_DSC1_ENABLE; + val |= FIELD_PREP(MV_PCS_DSC1_10GBT, ds); + val |= FIELD_PREP(MV_PCS_DSC1_1GBR, ds); + val |= FIELD_PREP(MV_PCS_DSC1_100BTX, ds); + + return phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1, + MV_PCS_DSC1_ENABLE | MV_PCS_DSC1_10GBT | + MV_PCS_DSC1_1GBR | MV_PCS_DSC1_100BTX, val); +} + static int mv3310_get_edpd(struct phy_device *phydev, u16 *edpd) { int val; @@ -482,7 +554,16 @@ static int mv3310_config_init(struct phy_device *phydev) MV_V2_PORT_MAC_TYPE_RATE_MATCH); /* Enable EDPD mode - saving 600mW */ - return mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS); + err = mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS); + if (err) + return err; + + /* Allow downshift */ + err = mv3310_set_downshift(phydev, DOWNSHIFT_DEV_DEFAULT_COUNT); + if (err && err != -EOPNOTSUPP) + return err; + + return 0; } static int mv3310_get_features(struct phy_device *phydev) @@ -748,6 +829,8 @@ static int mv3310_get_tunable(struct phy_device *phydev, struct ethtool_tunable *tuna, void *data) { switch (tuna->id) { + case ETHTOOL_PHY_DOWNSHIFT: + return mv3310_get_downshift(phydev, data); case ETHTOOL_PHY_EDPD: return mv3310_get_edpd(phydev, data); default: @@ -759,6 +842,8 @@ static int mv3310_set_tunable(struct phy_device *phydev, struct ethtool_tunable *tuna, const void *data) { switch (tuna->id) { + case ETHTOOL_PHY_DOWNSHIFT: + return mv3310_set_downshift(phydev, *(u8 *)data); case ETHTOOL_PHY_EDPD: return mv3310_set_edpd(phydev, *(u16 *)data); default: -- cgit From 93fa5860ea09b12385fb90f8dc3271abd712a600 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 15:21:50 +0000 Subject: dt-bindings: net: add dt bindings for marvell10g driver Add a DT bindings document for the Marvell 10G driver, which will augment the generic ethernet PHY binding by having LED mode configuration. Signed-off-by: Russell King --- .../devicetree/bindings/net/marvell,10g.yaml | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/marvell,10g.yaml diff --git a/Documentation/devicetree/bindings/net/marvell,10g.yaml b/Documentation/devicetree/bindings/net/marvell,10g.yaml new file mode 100644 index 000000000000..93d2c8d7e759 --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell,10g.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/marvell,10g.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Alaska X family Ethernet PHYs + +maintainers: + - Russell King + +allOf: + - $ref: ethernet-phy.yaml# + +properties: + marvell,led-mode: + description: | + An array of one to four 16-bit integers to write to the PHY LED + configuration registers. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint16-array + - minItems: 1 + maxItems: 4 + +examples: + - | + mdio { + #address-cells = <1>; + #size-cells = <0>; + ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>; + }; + }; -- cgit From d05f2ffba853f7bdf404c54a50356ff749102422 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 1 Dec 2019 15:33:39 +0000 Subject: net: phy: marvell10g: add support for configuring LEDs Add support for configuring the LEDs. Macchiatobin has an oddity in that the left LED goes out when the cable is connected, and flashes when there's link activity. This is because the reset default for the LED outputs assume that the LED is connected to supply, not to ground. Add support for configuring the LED modes and polarities. Signed-off-by: Russell King --- drivers/net/phy/marvell10g.c | 62 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 0a0705116759..384962cd8df1 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -109,6 +110,8 @@ struct mv3310_priv { struct device *hwmon_dev; char *hwmon_name; + u8 num_leds; + u16 led_mode[4]; }; #ifdef CONFIG_HWMON @@ -434,6 +437,43 @@ static const struct sfp_upstream_ops mv3310_sfp_ops = { .module_insert = mv3310_sfp_insert, }; +static int mv3310_leds_write(struct phy_device *phydev) +{ + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + int i, ret; + + for (i = 0; i < priv->num_leds; i++) { + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xf020 + i, + priv->led_mode[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int mv3310_fw_config(struct phy_device *phydev) +{ + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + struct device_node *node; + int ret; + + node = phydev->mdio.dev.of_node; + if (!node) + return 0; + + ret = of_property_read_variable_u16_array(node, "marvell,led-mode", + priv->led_mode, 1, ARRAY_SIZE(priv->led_mode)); + if (ret == -EINVAL) + ret = 0; + if (ret < 0) + return ret; + + priv->num_leds = ret; + + return 0; +} + static int mv3310_probe(struct phy_device *phydev) { struct mv3310_priv *priv; @@ -444,6 +484,20 @@ static int mv3310_probe(struct phy_device *phydev) (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) return -ENODEV; + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(&phydev->mdio.dev, priv); + + ret = mv3310_fw_config(phydev); + if (ret < 0) + return ret; + + ret = mv3310_leds_write(phydev); + if (ret < 0) + return ret; + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_BOOT); if (ret < 0) return ret; @@ -454,12 +508,6 @@ static int mv3310_probe(struct phy_device *phydev) return -ENODEV; } - priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - dev_set_drvdata(&phydev->mdio.dev, priv); - ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0); if (ret < 0) return ret; @@ -563,7 +611,7 @@ static int mv3310_config_init(struct phy_device *phydev) if (err && err != -EOPNOTSUPP) return err; - return 0; + return mv3310_leds_write(phydev); } static int mv3310_get_features(struct phy_device *phydev) -- cgit From 5a27b8e45db6af26dfb47e149ef0598d38211f30 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 1 Dec 2019 15:35:08 +0000 Subject: arm64: dts: configure Macchiatobin 10G PHY LED modes Configure the Macchiatobin 10G PHY LED modes to correct their polarity. We keep the existing LED behaviours, but switch their polarity to reflect how they are connected. Tweak the LED modes as well to be: left: off = no link solid green = RJ45 link up (not SFP+ cage) flash green = traffic right: off = no link solid green = 10G solid yellow = 1G flash green = 100M flash yellow = 10M Signed-off-by: Russell King --- arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts index 1766cf58101b..68c27f22ff57 100644 --- a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts +++ b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts @@ -21,12 +21,14 @@ compatible = "ethernet-phy-ieee802.3-c45"; reg = <0>; sfp = <&sfp_eth0>; + marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>; }; phy8: ethernet-phy@8 { compatible = "ethernet-phy-ieee802.3-c45"; reg = <8>; sfp = <&sfp_eth1>; + marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>; }; }; -- cgit From b169cb408813b15e20a6db7b4060ee68b45db729 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 3 Jan 2020 23:13:28 +0000 Subject: net: phy: add resolved pause support Allow phylib drivers to pass the hardware-resolved pause state to MAC drivers, rather than using the software-based pause resolution code. Signed-off-by: Russell King --- drivers/net/phy/phy_device.c | 6 ++++++ include/linux/phy.h | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index d1780dc54846..95edb2e68c29 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -2708,6 +2708,12 @@ void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause) return; } + if (phydev->resolved_pause_valid) { + *tx_pause = phydev->resolved_tx_pause; + *rx_pause = phydev->resolved_rx_pause; + return; + } + return linkmode_resolve_pause(phydev->advertising, phydev->lp_advertising, tx_pause, rx_pause); diff --git a/include/linux/phy.h b/include/linux/phy.h index 67a71f714bad..0e868d9d9fb5 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -595,6 +595,15 @@ struct phy_device { u8 master_slave_set; u8 master_slave_state; + /* + * private to phylib: the resolved pause state - only valid if + * resolved_pause_valid is true. only phy drivers and phylib + * should touch this. + */ + bool resolved_pause_valid; + bool resolved_tx_pause; + bool resolved_rx_pause; + /* Union of PHY and Attached devices' supported link modes */ /* See ethtool.h for more info */ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); -- cgit From 75da6b555f415ba92e3df85509b4b5e788155383 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 4 Jan 2020 00:41:35 +0000 Subject: net: phy: marvell*: add support for hw resolved pause modes Support reporting the hardware resolved pause enablement states via phylib, overriding our software implementation. Signed-off-by: Russell King --- drivers/net/phy/marvell.c | 41 +++++++++++++++++++++++++++++++++++++++-- drivers/net/phy/marvell10g.c | 6 ++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 8018ddf7f316..5701b2f855ec 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -158,6 +158,10 @@ #define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000 #define MII_M1011_PHY_STATUS_RESOLVED 0x0800 #define MII_M1011_PHY_STATUS_LINK 0x0400 +#define MII_M1111_PHY_STATUS_TX_PAUSE 0x0008 +#define MII_M1111_PHY_STATUS_RX_PAUSE 0x0004 +#define MII_88E151X_PHY_STATUS_TX_PAUSE 0x0200 +#define MII_88E151X_PHY_STATUS_RX_PAUSE 0x0100 #define MII_88E3016_PHY_SPEC_CTRL 0x10 #define MII_88E3016_DISABLE_SCRAMBLER 0x0200 @@ -283,6 +287,8 @@ struct marvell_priv { u32 last; u32 step; s8 pair; + u16 tx_pause_mask; + u16 rx_pause_mask; }; static int marvell_read_page(struct phy_device *phydev) @@ -1458,6 +1464,7 @@ static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa) static int marvell_read_status_page_an(struct phy_device *phydev, int fiber, int status) { + struct marvell_priv *priv = phydev->priv; int lpa; int err; @@ -1513,6 +1520,11 @@ static int marvell_read_status_page_an(struct phy_device *phydev, } } + phydev->resolved_tx_pause = !!(status & priv->tx_pause_mask); + phydev->resolved_rx_pause = !!(status & priv->rx_pause_mask); + phydev->resolved_pause_valid = !fiber && priv->tx_pause_mask && + priv->rx_pause_mask; + return 0; } @@ -1556,6 +1568,7 @@ static int marvell_read_status_page(struct phy_device *phydev, int page) phydev->speed = SPEED_UNKNOWN; phydev->duplex = DUPLEX_UNKNOWN; phydev->port = fiber ? PORT_FIBRE : PORT_TP; + phydev->resolved_pause_valid = false; if (phydev->autoneg == AUTONEG_ENABLE) err = marvell_read_status_page_an(phydev, fiber, status); @@ -2674,6 +2687,23 @@ static int marvell_probe(struct phy_device *phydev) return 0; } +static int marvell_probe_pause(struct phy_device *phydev, u16 tx_pause_mask, + u16 rx_pause_mask) +{ + struct marvell_priv *priv; + int err; + + err = marvell_probe(phydev); + if (err) + return err; + + priv = phydev->priv; + priv->tx_pause_mask = tx_pause_mask; + priv->rx_pause_mask = rx_pause_mask; + + return 0; +} + static int m88e1121_probe(struct phy_device *phydev) { int err; @@ -2685,11 +2715,18 @@ static int m88e1121_probe(struct phy_device *phydev) return m88e1121_hwmon_probe(phydev); } +static int m88e1111_probe(struct phy_device *phydev) +{ + return marvell_probe_pause(phydev, MII_M1111_PHY_STATUS_TX_PAUSE, + MII_M1111_PHY_STATUS_RX_PAUSE); +} + static int m88e1510_probe(struct phy_device *phydev) { int err; - err = marvell_probe(phydev); + err = marvell_probe_pause(phydev, MII_88E151X_PHY_STATUS_TX_PAUSE, + MII_88E151X_PHY_STATUS_RX_PAUSE); if (err) return err; @@ -2751,7 +2788,7 @@ static struct phy_driver marvell_drivers[] = { .phy_id_mask = MARVELL_PHY_ID_MASK, .name = "Marvell 88E1111", /* PHY_GBIT_FEATURES */ - .probe = marvell_probe, + .probe = m88e1111_probe, .config_init = m88e1111_config_init, .config_aneg = m88e1111_config_aneg, .read_status = marvell_read_status, diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 384962cd8df1..528367c71621 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -73,6 +73,8 @@ enum { MV_PCS_CSSR1_SPD1_10 = 0x0000, MV_PCS_CSSR1_DUPLEX_FULL= BIT(13), MV_PCS_CSSR1_RESOLVED = BIT(11), + MV_PCS_CSSR1_TX_PAUSE = BIT(9), + MV_PCS_CSSR1_RX_PAUSE = BIT(8), MV_PCS_CSSR1_MDIX = BIT(6), MV_PCS_CSSR1_SPD2_MASK = 0x000c, MV_PCS_CSSR1_SPD2_5000 = 0x0008, @@ -825,6 +827,10 @@ static int mv3310_read_status_copper(struct phy_device *phydev) phydev->mdix = cssr1 & MV_PCS_CSSR1_MDIX ? ETH_TP_MDI_X : ETH_TP_MDI; + phydev->resolved_tx_pause = !!(cssr1 & MV_PCS_CSSR1_TX_PAUSE); + phydev->resolved_rx_pause = !!(cssr1 & MV_PCS_CSSR1_RX_PAUSE); + phydev->resolved_pause_valid = true; + if (val & MDIO_AN_STAT1_COMPLETE) { val = genphy_c45_read_lpa(phydev); if (val < 0) -- cgit From 5de4efa81288d2b1ea05cdbbf20e3c2fe5ff5c0e Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 26 Aug 2019 12:19:35 +0100 Subject: net: phy: provide phy driver start/stop hooks Provide phy driver start/stop hooks so that the PHY driver knows when the network driver is starting or stopping. This will be used for the Marvell 10G driver so that we can sanely refuse to start if the PHYs firmware is not present, and also so that we can sanely support SFPs behind the PHY. Signed-off-by: Russell King --- drivers/net/phy/phy.c | 5 +++++ include/linux/phy.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index fc2e7cb5b2e5..a25e77a4ba52 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1024,6 +1024,8 @@ void phy_stop(struct phy_device *phydev) sfp_upstream_stop(phydev->sfp_bus); phydev->state = PHY_HALTED; + if (phydev->drv->stop) + phydev->drv->stop(phydev); mutex_unlock(&phydev->lock); @@ -1057,6 +1059,9 @@ void phy_start(struct phy_device *phydev) goto out; } + if (phydev->drv->start && phydev->drv->start(phydev)) + goto out; + if (phydev->sfp_bus) sfp_upstream_start(phydev->sfp_bus); diff --git a/include/linux/phy.h b/include/linux/phy.h index 0e868d9d9fb5..26a263dd060a 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -753,6 +753,9 @@ struct phy_driver { /** @resume: Resume the hardware, restoring state if needed */ int (*resume)(struct phy_device *phydev); + int (*start)(struct phy_device *phydev); + void (*stop)(struct phy_device *phydev); + /** * @config_aneg: Configures the advertisement and resets * autonegotiation if phydev->autoneg is on, -- cgit From 94537d389c345512d5071d87b8a0a3b45f8dff65 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 5 Jun 2019 11:44:55 +0100 Subject: net: phy: marvell10g: allow PHY to probe without firmware Allow the PHY to probe when there is no firmware, but do not allow the link to come up by forcing the PHY state to PHY_HALTED in a similar way to phy_error(). Signed-off-by: Russell King --- drivers/net/phy/marvell10g.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 528367c71621..c3c8488c7c77 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -109,6 +109,7 @@ enum { struct mv3310_priv { u32 firmware_ver; bool rate_match; + bool firmware_failed; struct device *hwmon_dev; char *hwmon_name; @@ -507,7 +508,7 @@ static int mv3310_probe(struct phy_device *phydev) if (ret & MV_PMA_BOOT_FATAL) { dev_warn(&phydev->mdio.dev, "PHY failed to boot firmware, status=%04x\n", ret); - return -ENODEV; + priv->firmware_failed = true; } ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0); @@ -559,6 +560,19 @@ static int mv3310_resume(struct phy_device *phydev) return mv3310_hwmon_config(phydev, true); } +static int mv3310_start(struct phy_device *phydev) +{ + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + + if (priv->firmware_failed) { + dev_warn(&phydev->mdio.dev, + "PHY firmware failure: PHY not starting"); + return -EINVAL; + } + + return 0; +} + /* Some PHYs in the Alaska family such as the 88X3310 and the 88E2010 * don't set bit 14 in PMA Extended Abilities (1.11), although they do * support 2.5GBASET and 5GBASET. For these models, we can still read their @@ -915,6 +929,7 @@ static struct phy_driver mv3310_drivers[] = { .probe = mv3310_probe, .suspend = mv3310_suspend, .resume = mv3310_resume, + .start = mv3310_start, .config_aneg = mv3310_config_aneg, .aneg_done = mv3310_aneg_done, .read_status = mv3310_read_status, @@ -929,6 +944,7 @@ static struct phy_driver mv3310_drivers[] = { .probe = mv3310_probe, .suspend = mv3310_suspend, .resume = mv3310_resume, + .start = mv3310_start, .config_init = mv3310_config_init, .config_aneg = mv3310_config_aneg, .aneg_done = mv3310_aneg_done, -- cgit From a2e5ec2559d663c0dc5d51847cbd0cfc7ea1d7e2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 5 Nov 2019 10:34:39 +0000 Subject: net: phy: make phy_error() report which PHY has failed phy_error() is called from phy_interrupt() or phy_state_machine(), and uses WARN_ON() to print a backtrace. The backtrace is not useful when reporting a PHY error. However, a system may contain multiple ethernet PHYs, and phy_error() gives no clue which one caused the problem. Replace WARN_ON() with a call to phydev_err() so that we can see which PHY had an error, and also inform the user that we are halting the PHY. Signed-off-by: Russell King --- drivers/net/phy/phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index a25e77a4ba52..369243a8c399 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -912,7 +912,7 @@ void phy_stop_machine(struct phy_device *phydev) */ void phy_error(struct phy_device *phydev) { - WARN_ON(1); + phydev_err(phydev, "Error detected, halting PHY\n"); mutex_lock(&phydev->lock); phydev->state = PHY_HALTED; -- cgit From 847424f94050f9a84a5d885501cd03d1fa239265 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 18 Oct 2019 10:37:44 +0100 Subject: net: sfp: add support for cooled SFP+ transceivers Cooled SFP+ transceivers need a longer initialisation (startup) time. Select the initialisation time depending on the cooled option bit. Signed-off-by: Russell King --- drivers/net/phy/sfp.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 7998acc689b7..3e016946cd6c 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -168,6 +168,7 @@ static const enum gpiod_flags gpio_flags[] = { #define T_WAIT msecs_to_jiffies(50) #define T_START_UP msecs_to_jiffies(300) #define T_START_UP_BAD_GPON msecs_to_jiffies(60000) +#define T_START_UP_COOLED msecs_to_jiffies(90000) /* t_reset is the time required to assert the TX_DISABLE signal to reset * an indicated TX_FAULT. @@ -1925,6 +1926,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) && !memcmp(id.base.vendor_pn, "3FE46541AA ", 16)) sfp->module_t_start_up = T_START_UP_BAD_GPON; + else if (id.ext.options & cpu_to_be16(SFP_OPTIONS_COOLED_XCVR)) + sfp->module_t_start_up = T_START_UP_COOLED; else sfp->module_t_start_up = T_START_UP; @@ -2131,10 +2134,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) break; if (sfp->state & SFP_F_TX_FAULT) { - /* Wait up to t_init (SFF-8472) or t_start_up (SFF-8431) - * from the TX_DISABLE deassertion for the module to - * initialise, which is indicated by TX_FAULT - * deasserting. + /* Wait up to t_init (SFF-8472), t_start_up (SFF-8431), + * or t_start_up_cooled (SFF-8431) from the TX_DISABLE + * deassertion for the module to initialise, which is + * indicated by TX_FAULT deasserting. */ timeout = sfp->module_t_start_up; if (timeout > T_WAIT) @@ -2153,8 +2156,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) case SFP_S_INIT: if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) { - /* TX_FAULT is still asserted after t_init or - * or t_start_up, so assume there is a fault. + /* TX_FAULT is still asserted after t_init, t_start_up + * or t_start_up_cooled, so assume there is a fault. */ sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT, sfp->sm_fault_retries == N_FAULT_INIT); -- cgit From f01e9844e5e736d4c47114d11e2907b9630378e2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 14 Apr 2017 20:17:13 +0100 Subject: net: sfp: add sfp+ compatible [*not for mainline*] Add a compatible for SFP+ cages. SFP+ cages are backwards compatible, but the ethernet device behind them may not support the slower speeds of SFP modules. Signed-off-by: Russell King --- drivers/net/phy/sfp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 3e016946cd6c..6e582077181c 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -304,6 +304,7 @@ static const struct sff_data sfp_data = { static const struct of_device_id sfp_of_match[] = { { .compatible = "sff,sff", .data = &sff_data, }, { .compatible = "sff,sfp", .data = &sfp_data, }, + { .compatible = "sff,sfp+", .data = &sfp_data, }, { }, }; MODULE_DEVICE_TABLE(of, sfp_of_match); -- cgit From 70c8ff56460967a4a814fc2dae17cf43e32170af Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Sep 2015 01:06:31 +0100 Subject: net: sfp: display SFP module information [*not for mainline*] Display SFP module information verbosely, splitting the generic parts into a separate file. Signed-off-by: Russell King --- drivers/net/phy/Makefile | 2 +- drivers/net/phy/sff.c | 114 ++++++++++++++++++++++++ drivers/net/phy/sff.h | 16 ++++ drivers/net/phy/sfp.c | 228 ++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 349 insertions(+), 11 deletions(-) create mode 100644 drivers/net/phy/sff.c create mode 100644 drivers/net/phy/sff.h diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index a13e402074cf..78439b199723 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -26,7 +26,7 @@ obj-$(CONFIG_PHYLIB) += libphy.o obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o -obj-$(CONFIG_SFP) += sfp.o +obj-$(CONFIG_SFP) += sff.o sfp.o sfp-obj-$(CONFIG_SFP) += sfp-bus.o obj-y += $(sfp-obj-y) $(sfp-obj-m) diff --git a/drivers/net/phy/sff.c b/drivers/net/phy/sff.c new file mode 100644 index 000000000000..a2eb56118dd4 --- /dev/null +++ b/drivers/net/phy/sff.c @@ -0,0 +1,114 @@ +#include +#include +#include "sff.h" + +const char *sff_link_len(char *buf, size_t size, unsigned int length, + unsigned int multiplier) +{ + if (length == 0) + return "unsupported/unspecified"; + + if (length == 255) { + *buf++ = '>'; + size -= 1; + length -= 1; + } + + length *= multiplier; + + if (length >= 1000) + snprintf(buf, size, "%u.%0*ukm", + length / 1000, + multiplier > 100 ? 1 : + multiplier > 10 ? 2 : 3, + length % 1000); + else + snprintf(buf, size, "%um", length); + + return buf; +} +EXPORT_SYMBOL_GPL(sff_link_len); + +const char *sff_bitfield(char *buf, size_t size, + const struct sff_bitfield *bits, unsigned int val) +{ + char *p = buf; + int n; + + *p = '\0'; + while (bits->mask) { + if ((val & bits->mask) == bits->val) { + n = snprintf(p, size, "%s%s", + buf != p ? ", " : "", + bits->str); + if (n == size) + break; + p += n; + size -= n; + } + bits++; + } + + return buf; +} +EXPORT_SYMBOL_GPL(sff_bitfield); + +const char *sff_connector(unsigned int connector) +{ + switch (connector) { + case SFF8024_CONNECTOR_UNSPEC: + return "unknown/unspecified"; + case SFF8024_CONNECTOR_SC: + return "SC"; + case SFF8024_CONNECTOR_FIBERJACK: + return "Fiberjack"; + case SFF8024_CONNECTOR_LC: + return "LC"; + case SFF8024_CONNECTOR_MT_RJ: + return "MT-RJ"; + case SFF8024_CONNECTOR_MU: + return "MU"; + case SFF8024_CONNECTOR_SG: + return "SG"; + case SFF8024_CONNECTOR_OPTICAL_PIGTAIL: + return "Optical pigtail"; + case SFF8024_CONNECTOR_MPO_1X12: + return "MPO 1X12"; + case SFF8024_CONNECTOR_MPO_2X16: + return "MPO 2X16"; + case SFF8024_CONNECTOR_HSSDC_II: + return "HSSDC II"; + case SFF8024_CONNECTOR_COPPER_PIGTAIL: + return "Copper pigtail"; + case SFF8024_CONNECTOR_RJ45: + return "RJ45"; + case SFF8024_CONNECTOR_MXC_2X16: + return "MXC 2X16"; + default: + return "unknown"; + } +} +EXPORT_SYMBOL_GPL(sff_connector); + +const char *sff_encoding(unsigned int encoding) +{ + switch (encoding) { + case SFF8024_ENCODING_UNSPEC: + return "unspecified"; + case SFF8024_ENCODING_8472_64B66B: + return "64b66b"; + case SFF8024_ENCODING_8B10B: + return "8b10b"; + case SFF8024_ENCODING_4B5B: + return "4b5b"; + case SFF8024_ENCODING_NRZ: + return "NRZ"; + case SFF8024_ENCODING_8472_MANCHESTER: + return "MANCHESTER"; + default: + return "unknown"; + } +} +EXPORT_SYMBOL_GPL(sff_encoding); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/sff.h b/drivers/net/phy/sff.h new file mode 100644 index 000000000000..cd7bb7c7ae4a --- /dev/null +++ b/drivers/net/phy/sff.h @@ -0,0 +1,16 @@ +#ifndef SFF_H +#define SFF_H + +struct sff_bitfield { + unsigned int mask; + unsigned int val; + const char *str; +}; + +const char *sff_link_len(char *buf, size_t size, unsigned int length, + unsigned int multiplier); +const char *sff_bitfield(char *buf, size_t size, + const struct sff_bitfield *bits, unsigned int val); +const char *sff_connector(unsigned int connector); +const char *sff_encoding(unsigned int encoding); +#endif diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 6e582077181c..e2ad93bc5467 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -18,6 +18,7 @@ #include #include +#include "sff.h" #include "sfp.h" #include "swphy.h" @@ -1398,6 +1399,114 @@ static void sfp_hwmon_exit(struct sfp *sfp) } #endif +static const struct sff_bitfield sfp_options[] = { + { + .mask = SFP_OPTIONS_HIGH_POWER_LEVEL, + .val = SFP_OPTIONS_HIGH_POWER_LEVEL, + .str = "hpl", + }, { + .mask = SFP_OPTIONS_PAGING_A2, + .val = SFP_OPTIONS_PAGING_A2, + .str = "paginga2", + }, { + .mask = SFP_OPTIONS_RETIMER, + .val = SFP_OPTIONS_RETIMER, + .str = "retimer", + }, { + .mask = SFP_OPTIONS_COOLED_XCVR, + .val = SFP_OPTIONS_COOLED_XCVR, + .str = "cooled", + }, { + .mask = SFP_OPTIONS_POWER_DECL, + .val = SFP_OPTIONS_POWER_DECL, + .str = "powerdecl", + }, { + .mask = SFP_OPTIONS_RX_LINEAR_OUT, + .val = SFP_OPTIONS_RX_LINEAR_OUT, + .str = "rxlinear", + }, { + .mask = SFP_OPTIONS_RX_DECISION_THRESH, + .val = SFP_OPTIONS_RX_DECISION_THRESH, + .str = "rxthresh", + }, { + .mask = SFP_OPTIONS_TUNABLE_TX, + .val = SFP_OPTIONS_TUNABLE_TX, + .str = "tunabletx", + }, { + .mask = SFP_OPTIONS_RATE_SELECT, + .val = SFP_OPTIONS_RATE_SELECT, + .str = "ratesel", + }, { + .mask = SFP_OPTIONS_TX_DISABLE, + .val = SFP_OPTIONS_TX_DISABLE, + .str = "txdisable", + }, { + .mask = SFP_OPTIONS_TX_FAULT, + .val = SFP_OPTIONS_TX_FAULT, + .str = "txfault", + }, { + .mask = SFP_OPTIONS_LOS_INVERTED, + .val = SFP_OPTIONS_LOS_INVERTED, + .str = "los-", + }, { + .mask = SFP_OPTIONS_LOS_NORMAL, + .val = SFP_OPTIONS_LOS_NORMAL, + .str = "los+", + }, { } +}; + +static const struct sff_bitfield diagmon[] = { + { + .mask = SFP_DIAGMON_DDM, + .val = SFP_DIAGMON_DDM, + .str = "ddm", + }, { + .mask = SFP_DIAGMON_INT_CAL, + .val = SFP_DIAGMON_INT_CAL, + .str = "intcal", + }, { + .mask = SFP_DIAGMON_EXT_CAL, + .val = SFP_DIAGMON_EXT_CAL, + .str = "extcal", + }, { + .mask = SFP_DIAGMON_RXPWR_AVG, + .val = SFP_DIAGMON_RXPWR_AVG, + .str = "rxpwravg", + }, { } +}; + +static const struct sff_bitfield sfp_enhopts[] = { + { + .mask = SFP_ENHOPTS_ALARMWARN, + .val = SFP_ENHOPTS_ALARMWARN, + .str = "alarmwarn", + }, { + .mask = SFP_ENHOPTS_SOFT_TX_DISABLE, + .val = SFP_ENHOPTS_SOFT_TX_DISABLE, + .str = "soft_tx_dis", + }, { + .mask = SFP_ENHOPTS_SOFT_TX_FAULT, + .val = SFP_ENHOPTS_SOFT_TX_FAULT, + .str = "soft_tx_fault", + }, { + .mask = SFP_ENHOPTS_SOFT_RX_LOS, + .val = SFP_ENHOPTS_SOFT_RX_LOS, + .str = "soft_rx_los", + }, { + .mask = SFP_ENHOPTS_SOFT_RATE_SELECT, + .val = SFP_ENHOPTS_SOFT_RATE_SELECT, + .str = "soft_rs", + }, { + .mask = SFP_ENHOPTS_APP_SELECT_SFF8079, + .val = SFP_ENHOPTS_APP_SELECT_SFF8079, + .str = "app_sel", + }, { + .mask = SFP_ENHOPTS_SOFT_RATE_SFF8431, + .val = SFP_ENHOPTS_SOFT_RATE_SFF8431, + .str = "soft_r8431", + }, { } +}; + /* Helpers */ static void sfp_module_tx_disable(struct sfp *sfp) { @@ -1781,6 +1890,110 @@ static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id) return 0; } +static void sfp_print_module_info(struct sfp *sfp, const struct sfp_eeprom_id *id, bool cotsworks) +{ + unsigned int br_nom, br_min, br_max; + char date[9]; + char options[80]; + + /* Cotsworks also gets the date code wrong. */ + date[0] = id->ext.datecode[4 - 2 * cotsworks]; + date[1] = id->ext.datecode[5 - 2 * cotsworks]; + date[2] = '-'; + date[3] = id->ext.datecode[2 + 2 * cotsworks]; + date[4] = id->ext.datecode[3 + 2 * cotsworks]; + date[5] = '-'; + date[6] = id->ext.datecode[0]; + date[7] = id->ext.datecode[1]; + date[8] = '\0'; + + if (id->base.br_nominal == 0) { + br_min = br_nom = br_max = 0; + } else if (id->base.br_nominal == 255) { + br_nom = 250 * id->ext.br_max; + br_max = br_nom + br_nom * id->ext.br_min / 100; + br_min = br_nom - br_nom * id->ext.br_min / 100; + } else { + br_nom = id->base.br_nominal * 100; + br_min = br_nom - id->base.br_nominal * id->ext.br_min; + br_max = br_nom + id->base.br_nominal * id->ext.br_max; + } + + dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %s\n", + (int)sizeof(id->base.vendor_name), id->base.vendor_name, + (int)sizeof(id->base.vendor_pn), id->base.vendor_pn, + (int)sizeof(id->base.vendor_rev), id->base.vendor_rev, + (int)sizeof(id->ext.vendor_sn), id->ext.vendor_sn, date); + dev_info(sfp->dev, " %s connector, encoding %s, bitrate %u.%03u (%u.%03u-%u.%03u) Gbps\n", + sff_connector(id->base.connector), + sff_encoding(id->base.encoding), + br_nom / 1000, br_nom % 1000, + br_min / 1000, br_min % 1000, br_max / 1000, br_max % 1000); + dev_info(sfp->dev, " 1000BaseSX%c 1000BaseLX%c 1000BaseCX%c 1000BaseT%c 100BaseLX%c 100BaseFX%c BaseBX10%c BasePX%c\n", + id->base.e1000_base_sx ? '+' : '-', + id->base.e1000_base_lx ? '+' : '-', + id->base.e1000_base_cx ? '+' : '-', + id->base.e1000_base_t ? '+' : '-', + id->base.e100_base_lx ? '+' : '-', + id->base.e100_base_fx ? '+' : '-', + id->base.e_base_bx10 ? '+' : '-', + id->base.e_base_px ? '+' : '-'); + dev_info(sfp->dev, " 10GBaseSR%c 10GBaseLR%c 10GBaseLRM%c 10GBaseER%c\n", + id->base.e10g_base_sr ? '+' : '-', + id->base.e10g_base_lr ? '+' : '-', + id->base.e10g_base_lrm ? '+' : '-', + id->base.e10g_base_er ? '+' : '-'); + + if (!id->base.sfp_ct_passive && !id->base.sfp_ct_active && + !id->base.e1000_base_t) { + char len_9um[16], len_om[16]; + + dev_info(sfp->dev, " Wavelength %unm, fiber lengths:\n", + be16_to_cpup(&id->base.optical_wavelength)); + + if (id->base.link_len[0] == 255) + strcpy(len_9um, ">254km"); + else if (id->base.link_len[1] && id->base.link_len[1] != 255) + sprintf(len_9um, "%um", + id->base.link_len[1] * 100); + else if (id->base.link_len[0]) + sprintf(len_9um, "%ukm", id->base.link_len[0]); + else if (id->base.link_len[1] == 255) + strcpy(len_9um, ">25.4km"); + else + strcpy(len_9um, "unsupported"); + + dev_info(sfp->dev, " 9µm SM : %s\n", len_9um); + dev_info(sfp->dev, " 62.5µm MM OM1: %s\n", + sff_link_len(len_om, sizeof(len_om), + id->base.link_len[3], 10)); + dev_info(sfp->dev, " 50µm MM OM2: %s\n", + sff_link_len(len_om, sizeof(len_om), + id->base.link_len[2], 10)); + dev_info(sfp->dev, " 50µm MM OM3: %s\n", + sff_link_len(len_om, sizeof(len_om), + id->base.link_len[5], 10)); + dev_info(sfp->dev, " 50µm MM OM4: %s\n", + sff_link_len(len_om, sizeof(len_om), + id->base.link_len[4], 10)); + } else { + char len[16]; + dev_info(sfp->dev, " Copper length: %s\n", + sff_link_len(len, sizeof(len), + id->base.link_len[4], 1)); + } + + dev_info(sfp->dev, " Options: %s\n", + sff_bitfield(options, sizeof(options), sfp_options, + be16_to_cpu(id->ext.options))); + dev_info(sfp->dev, " Diagnostics: %s\n", + sff_bitfield(options, sizeof(options), diagmon, + id->ext.diagmon)); + dev_info(sfp->dev, " EnhOpts: %s\n", + sff_bitfield(options, sizeof(options), sfp_enhopts, + id->ext.enhopts)); +} + static int sfp_sm_mod_probe(struct sfp *sfp, bool report) { /* SFP module inserted - read I2C data */ @@ -1835,9 +2048,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) } } - /* Cotsworks do not seem to update the checksums when they - * do the final programming with the final module part number, - * serial number and date code. + /* Cotsworks do not seem to update the checksums when they update the + * module part number, serial number and date code. They also format + * the date code incorrectly. */ cotsworks = !memcmp(id.base.vendor_name, "COTSWORKS ", 16); cotsworks_sfbg = !memcmp(id.base.vendor_pn, "SFBG", 4); @@ -1897,14 +2110,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) } } - sfp->id = id; + sfp_print_module_info(sfp, &id, cotsworks); - dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %.*s\n", - (int)sizeof(id.base.vendor_name), id.base.vendor_name, - (int)sizeof(id.base.vendor_pn), id.base.vendor_pn, - (int)sizeof(id.base.vendor_rev), id.base.vendor_rev, - (int)sizeof(id.ext.vendor_sn), id.ext.vendor_sn, - (int)sizeof(id.ext.datecode), id.ext.datecode); + sfp->id = id; /* Check whether we support this module */ if (!sfp->type->module_supported(&id)) { -- cgit From 9aac19b6c38f3813d6f22ad21764afff3a60651f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Aug 2020 09:32:18 +0100 Subject: net: phy: pass supported PHY interface types to phylib Pass the supported PHY interface types to phylib so that PHY drivers can select an appropriate host configuration mode for their interface according to the host capabilities. This is only done for SFP modules presently. Signed-off-by: Russell King --- drivers/net/phy/phylink.c | 18 ++++++++++++++++++ include/linux/phy.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 18541d9c9ed3..d6b877df4730 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -2022,6 +2022,8 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) pl->netdev->sfp_bus = NULL; } +static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces); + static const phy_interface_t phylink_sfp_interface_preference[] = { PHY_INTERFACE_MODE_USXGMII, PHY_INTERFACE_MODE_10GBASER, @@ -2245,6 +2247,10 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) else mode = MLO_AN_INBAND; + /* Set the PHY's host supported interfaces */ + phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces, + pl->config->supported_interfaces); + /* Do the initial configuration */ ret = phylink_sfp_config(pl, mode, phy->supported, phy->advertising); if (ret < 0) @@ -2628,4 +2634,16 @@ void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs, } EXPORT_SYMBOL_GPL(phylink_mii_c45_pcs_get_state); +static int __init phylink_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++) + set_bit(phylink_sfp_interface_preference[i], + phylink_sfp_interfaces); + + return 0; +} +module_init(phylink_init); + MODULE_LICENSE("GPL v2"); diff --git a/include/linux/phy.h b/include/linux/phy.h index 26a263dd060a..19bdcf8bdce3 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -612,6 +612,9 @@ struct phy_device { /* used with phy_speed_down */ __ETHTOOL_DECLARE_LINK_MODE_MASK(adv_old); + /* supported PHY interface types */ + DECLARE_PHY_INTERFACE_MASK(host_interfaces); + /* Energy efficient ethernet modes which should be prohibited */ u32 eee_broken_modes; -- cgit From b9c67fea389a3b27534ce03a820f43ccd04184c8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Aug 2020 09:32:18 +0100 Subject: net: phy: marvell10g: select host interface configuration Select the host interface configuration according to the capabilities of the host; this allows the kernel to support SFP modules using the 88x3310. Signed-off-by: Russell King --- drivers/net/phy/marvell10g.c | 62 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index c3c8488c7c77..8a81623d82c9 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -95,8 +95,8 @@ enum { MV_V2_PORT_CTRL = 0xf001, MV_V2_PORT_CTRL_SWRST = BIT(15), MV_V2_PORT_CTRL_PWRDOWN = BIT(11), - MV_V2_PORT_MAC_TYPE_MASK = 0x7, - MV_V2_PORT_MAC_TYPE_RATE_MATCH = 0x6, + MV_V2_PORT_CTRL_MACTYPE_MASK = 0x7, + MV_V2_PORT_CTRL_MACTYPE_RATE_MATCH = 0x6, /* Temperature control/read registers (88X3310 only) */ MV_V2_TEMP_CTRL = 0xf08a, MV_V2_TEMP_CTRL_MASK = 0xc000, @@ -590,19 +590,44 @@ static bool mv3310_has_pma_ngbaset_quirk(struct phy_device *phydev) MV_PHY_ALASKA_NBT_QUIRK_MASK) == MV_PHY_ALASKA_NBT_QUIRK_REV; } +static int mv3310_select_mode(struct phy_device *phydev, + unsigned long *host_interfaces) +{ + int mac_type = -1; + + if (test_bit(PHY_INTERFACE_MODE_USXGMII, host_interfaces)) + mac_type = 7; + else if (test_bit(PHY_INTERFACE_MODE_SGMII, host_interfaces) && + test_bit(PHY_INTERFACE_MODE_10GBASER, host_interfaces)) + mac_type = 4; + else if (test_bit(PHY_INTERFACE_MODE_SGMII, host_interfaces) && + test_bit(PHY_INTERFACE_MODE_RXAUI, host_interfaces)) + mac_type = 0; + else if (test_bit(PHY_INTERFACE_MODE_10GBASER, host_interfaces)) + mac_type = 6; + else if (test_bit(PHY_INTERFACE_MODE_RXAUI, host_interfaces)) + mac_type = 2; + else if (test_bit(PHY_INTERFACE_MODE_SGMII, host_interfaces)) + mac_type = 4; + + return mac_type; +} + static int mv3310_config_init(struct phy_device *phydev) { struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); - int err; - int val; + int val, ret, err, mac_type = -1; /* Check that the PHY interface type is compatible */ - if (phydev->interface != PHY_INTERFACE_MODE_SGMII && - phydev->interface != PHY_INTERFACE_MODE_2500BASEX && - phydev->interface != PHY_INTERFACE_MODE_XAUI && - phydev->interface != PHY_INTERFACE_MODE_RXAUI && - phydev->interface != PHY_INTERFACE_MODE_10GBASER) + if (!phy_interface_empty(phydev->host_interfaces)) { + mac_type = mv3310_select_mode(phydev, phydev->host_interfaces); + } else if (phydev->interface != PHY_INTERFACE_MODE_SGMII && + phydev->interface != PHY_INTERFACE_MODE_2500BASEX && + phydev->interface != PHY_INTERFACE_MODE_XAUI && + phydev->interface != PHY_INTERFACE_MODE_RXAUI && + phydev->interface != PHY_INTERFACE_MODE_10GBASER) { return -ENODEV; + } phydev->mdix_ctrl = ETH_TP_MDI_AUTO; @@ -611,11 +636,26 @@ static int mv3310_config_init(struct phy_device *phydev) if (err) return err; + if (mac_type != -1) { + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, + MV_V2_PORT_CTRL, + MV_V2_PORT_CTRL_MACTYPE_MASK, + mac_type); + if (ret > 0) + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, + MV_V2_PORT_CTRL, + MV_V2_PORT_CTRL_SWRST, + MV_V2_PORT_CTRL_SWRST); + + if (ret < 0) + return ret; + } + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL); if (val < 0) return val; - priv->rate_match = ((val & MV_V2_PORT_MAC_TYPE_MASK) == - MV_V2_PORT_MAC_TYPE_RATE_MATCH); + priv->rate_match = ((val & MV_V2_PORT_CTRL_MACTYPE_MASK) == + MV_V2_PORT_CTRL_MACTYPE_RATE_MATCH); /* Enable EDPD mode - saving 600mW */ err = mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS); -- cgit From e683a056030e8a6ae40e2e8956ec5ab390b78c98 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 23 Dec 2019 23:24:17 +0000 Subject: net: phy: add supported_interfaces to phylib Add a supported_interfaces member to phylib so we know which interfaces a PHY supports. Currently, set any unconverted driver to indicate all interfaces are supported. Signed-off-by: Russell King --- include/linux/phy.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/include/linux/phy.h b/include/linux/phy.h index 19bdcf8bdce3..2db2fa58e93a 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -151,6 +151,26 @@ typedef enum { PHY_INTERFACE_MODE_MAX, } phy_interface_t; +/* PHY interface mode bitmap handling */ +#define DECLARE_PHY_INTERFACE_MASK(name) \ + DECLARE_BITMAP(name, PHY_INTERFACE_MODE_MAX) + +static inline void phy_interface_zero(unsigned long *intf) +{ + bitmap_zero(intf, PHY_INTERFACE_MODE_MAX); +} + +static inline bool phy_interface_empty(const unsigned long *intf) +{ + return bitmap_empty(intf, PHY_INTERFACE_MODE_MAX); +} + +static inline void phy_interface_and(unsigned long *dst, const unsigned long *a, + const unsigned long *b) +{ + bitmap_and(dst, a, b, PHY_INTERFACE_MODE_MAX); +} + /* * phy_supported_speeds - return all speeds currently supported by a PHY device */ @@ -614,6 +634,7 @@ struct phy_device { /* supported PHY interface types */ DECLARE_PHY_INTERFACE_MASK(host_interfaces); + DECLARE_PHY_INTERFACE_MASK(supported_interfaces); /* Energy efficient ethernet modes which should be prohibited */ u32 eee_broken_modes; -- cgit From f3f830a3a27b4ccc4ee0fc2d17a59b9cc9d165a1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 23 Dec 2019 23:25:35 +0000 Subject: net: phy: add supported_interfaces to bcm84881 Signed-off-by: Russell King --- drivers/net/phy/bcm84881.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c index 9717a1626f3f..cba3ffc0708f 100644 --- a/drivers/net/phy/bcm84881.c +++ b/drivers/net/phy/bcm84881.c @@ -51,6 +51,10 @@ static int bcm84881_probe(struct phy_device *phydev) (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) return -ENODEV; + __set_bit(PHY_INTERFACE_MODE_SGMII, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GBASER, phydev->supported_interfaces); + return 0; } -- cgit From 6d515b6b17804264e8f2b156aa5f54ea12c5f06f Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 23 Dec 2019 23:25:49 +0000 Subject: net: phy: add supported_interfaces to marvell PHYs Signed-off-by: Russell King --- drivers/net/phy/marvell.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 5701b2f855ec..042790a675d2 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -2684,6 +2684,15 @@ static int marvell_probe(struct phy_device *phydev) phydev->priv = priv; + __set_bit(PHY_INTERFACE_MODE_GMII, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_TBI, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII_ID, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RTBI, phydev->supported_interfaces); + return 0; } -- cgit From 89a48236ceea7748befb5e0434e1d84ad4835a6c Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 23 Dec 2019 23:26:04 +0000 Subject: net: phy: add supported_interfaces to marvell10g PHYs Signed-off-by: Russell King --- drivers/net/phy/marvell10g.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 8a81623d82c9..0c17be11a855 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -487,6 +487,10 @@ static int mv3310_probe(struct phy_device *phydev) (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) return -ENODEV; + __set_bit(PHY_INTERFACE_MODE_SGMII, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GBASER, phydev->supported_interfaces); + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; -- cgit From 14daaffeb42b98915591deb6ad4cf36f687332ab Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Mar 2020 12:12:43 +0000 Subject: net: phylink: use phy interface mode bitmaps Use the phy interface mode bitmaps for SFP modules and PHYs to select the operating interface for SFPs and PHYs with SFPs. Signed-off-by: Russell King --- drivers/net/phy/phylink.c | 57 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index d6b877df4730..2ed9b582558b 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -2251,19 +2251,54 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces, pl->config->supported_interfaces); - /* Do the initial configuration */ - ret = phylink_sfp_config(pl, mode, phy->supported, phy->advertising); - if (ret < 0) - return ret; + if (!phy_interface_empty(phy->supported_interfaces) && + !phy_interface_empty(pl->config->supported_interfaces)) { + interface = phylink_select_interface(pl, + phy->supported_interfaces, + "phy"); + if (interface == PHY_INTERFACE_MODE_NA) { + phylink_err(pl, + "selection of interface for PHY failed\n"); + return -EINVAL; + } - interface = pl->link_config.interface; - ret = phylink_attach_phy(pl, phy, interface); - if (ret < 0) - return ret; + if (pl->cur_link_an_mode != mode || + pl->link_config.interface != interface) { + pl->link_config.interface = interface; + pl->cur_link_an_mode = mode; - ret = phylink_bringup_phy(pl, phy, interface); - if (ret) - phy_detach(phy); + phylink_info(pl, "switched to %s/%s link mode\n", + phylink_an_mode_str(mode), + phy_modes(interface)); + } + + ret = phylink_attach_phy(pl, phy, interface); + if (ret < 0) + return ret; + + ret = phylink_bringup_phy(pl, phy, interface); + if (ret) + phy_detach(phy); + + if (!test_bit(PHYLINK_DISABLE_STOPPED, + &pl->phylink_disable_state)) + phylink_mac_initial_config(pl, false); + } else { + /* Do the initial configuration */ + ret = phylink_sfp_config(pl, mode, phy->supported, + phy->advertising); + if (ret < 0) + return ret; + + interface = pl->link_config.interface; + ret = phylink_attach_phy(pl, phy, interface); + if (ret < 0) + return ret; + + ret = phylink_bringup_phy(pl, phy, interface); + if (ret) + phy_detach(phy); + } return ret; } -- cgit From 79769cc1efff52e27d0abceb55b82e7c75f55927 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 22 Jun 2020 01:05:59 +0100 Subject: net: dsa/bcm_sf2: fix pause mode validation The implementation appears not to support pause modes on anything but RGMII, RGMII_TXID, MII and REVMII interface modes. Let phylink know that detail. (This may not be correct; particularly see the FIXMEs in this patch.) Signed-off-by: Russell King --- drivers/net/dsa/bcm_sf2.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index ba5d546d06aa..25357cc3e12b 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -601,6 +601,7 @@ static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port, struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + /* FIXME: Are RGMII_RXID and RGMII_ID actually supported? */ if (!phy_interface_mode_is_rgmii(state->interface) && state->interface != PHY_INTERFACE_MODE_MII && state->interface != PHY_INTERFACE_MODE_REVMII && @@ -618,8 +619,13 @@ static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port, /* Allow all the expected bits */ phylink_set(mask, Autoneg); phylink_set_port_modes(mask); - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); + if (state->interface == PHY_INTERFACE_MODE_RGMII || + state->interface == PHY_INTERFACE_MODE_RGMII_TXID || + state->interface == PHY_INTERFACE_MODE_MII || + state->interface == PHY_INTERFACE_MODE_REVMII) { + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + } /* With the exclusion of MII and Reverse MII, we support Gigabit, * including Half duplex @@ -653,6 +659,7 @@ static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port, return; switch (state->interface) { + /* FIXME: Are RGMII_RXID and RGMII_ID actually supported? */ case PHY_INTERFACE_MODE_RGMII: id_mode_dis = 1; fallthrough; @@ -746,6 +753,7 @@ static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port, else offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); + /* FIXME: Are RGMII_RXID and RGMII_ID actually supported? */ if (interface == PHY_INTERFACE_MODE_RGMII || interface == PHY_INTERFACE_MODE_RGMII_TXID || interface == PHY_INTERFACE_MODE_MII || -- cgit From 4fb00423b25af8326d04058ed648a3f6e42e84c5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 29 Jun 2020 23:16:21 +0100 Subject: phy: armada-38x: further augmentation of setup Further augmentation of the comphy setup. Signed-off-by: Russell King --- arch/arm/boot/dts/armada-38x.dtsi | 5 +- drivers/phy/marvell/phy-armada38x-comphy.c | 147 +++++++++++++++++++++++++---- 2 files changed, 132 insertions(+), 20 deletions(-) diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi index 9b1a24cc5e91..404943b5cdac 100644 --- a/arch/arm/boot/dts/armada-38x.dtsi +++ b/arch/arm/boot/dts/armada-38x.dtsi @@ -342,8 +342,9 @@ comphy: phy@18300 { compatible = "marvell,armada-380-comphy"; - reg-names = "comphy", "conf"; - reg = <0x18300 0x100>, <0x18460 4>; + reg-names = "comphy", "pipe", "conf"; + reg = <0x18300 0x100>, <0xa0000 0x3000>, + <0x18460 4>; #address-cells = <1>; #size-cells = <0>; diff --git a/drivers/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c index 0fe408964334..0e86f484269c 100644 --- a/drivers/phy/marvell/phy-armada38x-comphy.c +++ b/drivers/phy/marvell/phy-armada38x-comphy.c @@ -15,32 +15,49 @@ #define MAX_A38X_COMPHY 6 #define MAX_A38X_PORTS 3 +/* Common PHY registers */ +#define COMPHY_REG_SIZE 0x28 #define COMPHY_CFG1 0x00 +#define COMPHY_CFG1_RX_INIT BIT(30) #define COMPHY_CFG1_GEN_TX(x) ((x) << 26) #define COMPHY_CFG1_GEN_TX_MSK COMPHY_CFG1_GEN_TX(15) #define COMPHY_CFG1_GEN_RX(x) ((x) << 22) #define COMPHY_CFG1_GEN_RX_MSK COMPHY_CFG1_GEN_RX(15) +#define COMPHY_CFG1_POWER_UP_TX BIT(18) +#define COMPHY_CFG1_POWER_UP_RX BIT(17) +#define COMPHY_CFG1_POWER_UP_PLL BIT(16) #define GEN_SGMII_1_25GBPS 6 #define GEN_SGMII_3_125GBPS 8 #define COMPHY_STAT1 0x18 #define COMPHY_STAT1_PLL_RDY_TX BIT(3) #define COMPHY_STAT1_PLL_RDY_RX BIT(2) +#define COMPHY_STAT1_RX_INIT_DONE BIT(0) #define COMPHY_SELECTOR 0xfc +/* Common PHY and Pipe Registers */ +#define PIPE_REG_SIZE 0x800 +#define PIPE_POWER_CTRL 0x148 +#define PIPE_POWER_CTRL_SFT_RST_NO_REG BIT(10) +/* u-boot sets bit 0 during comphy initialisation, but it is undocumented */ +#define PIPE_POWER_CTRL_BIT0 BIT(0) + struct a38x_comphy; struct a38x_comphy_lane { void __iomem *base; + void __iomem *pipe; struct a38x_comphy *priv; unsigned int n; int port; + u8 gen; }; struct a38x_comphy { void __iomem *base; + void __iomem *pipe; void __iomem *conf; struct device *dev; struct a38x_comphy_lane lane[MAX_A38X_COMPHY]; @@ -88,6 +105,7 @@ static void a38x_comphy_set_speed(struct a38x_comphy_lane *lane, COMPHY_CFG1_GEN_RX(gen_rx)); } +/* Poll every 1ms for 150ms for a status */ static int a38x_comphy_poll(struct a38x_comphy_lane *lane, unsigned int offset, u32 mask, u32 value) { @@ -105,6 +123,97 @@ static int a38x_comphy_poll(struct a38x_comphy_lane *lane, return ret; } +static int a38x_comphy_power_up(struct a38x_comphy_lane *lane) +{ + /* Power up TX, RX and PLL */ + a38x_comphy_set_reg(lane, COMPHY_CFG1, 0, + COMPHY_CFG1_POWER_UP_TX | COMPHY_CFG1_POWER_UP_RX | + COMPHY_CFG1_POWER_UP_PLL); + + /* Wait for power up */ + return a38x_comphy_poll(lane, COMPHY_STAT1, + COMPHY_STAT1_PLL_RDY_TX | + COMPHY_STAT1_PLL_RDY_RX, + COMPHY_STAT1_PLL_RDY_TX | + COMPHY_STAT1_PLL_RDY_RX); +} + +static int a38x_comphy_rx_init(struct a38x_comphy_lane *lane) +{ + int ret; + + /* Perform RX init */ + a38x_comphy_set_reg(lane, COMPHY_CFG1, 0, COMPHY_CFG1_RX_INIT); + + /* Wait for RX init done */ + ret = a38x_comphy_poll(lane, COMPHY_STAT1, COMPHY_STAT1_RX_INIT_DONE, + COMPHY_STAT1_RX_INIT_DONE); + + /* Clear RX init */ + a38x_comphy_set_reg(lane, COMPHY_CFG1, COMPHY_CFG1_RX_INIT, 0); + + return ret; +} + +static int a38x_comphy_power_off(struct phy *phy) +{ + struct a38x_comphy_lane *lane = phy_get_drvdata(phy); + + a38x_set_conf(lane, false); + + /* Soft reset */ + if (lane->pipe) { + u32 rst = PIPE_POWER_CTRL_SFT_RST_NO_REG | PIPE_POWER_CTRL_BIT0; + u32 val; + + val = readl_relaxed(lane->pipe + PIPE_POWER_CTRL); + writel(val | rst, lane->pipe + PIPE_POWER_CTRL); + } + + a38x_comphy_set_reg(lane, COMPHY_CFG1, + COMPHY_CFG1_POWER_UP_TX | COMPHY_CFG1_POWER_UP_RX | + COMPHY_CFG1_POWER_UP_PLL, 0); + + return 0; +} + +static int a38x_comphy_power_on(struct phy *phy) +{ + struct a38x_comphy_lane *lane = phy_get_drvdata(phy); + int ret; + + /* Program the GEN settings */ + a38x_comphy_set_speed(lane, lane->gen, lane->gen); + + /* Soft reset */ + if (lane->pipe) { + u32 rst = PIPE_POWER_CTRL_SFT_RST_NO_REG | PIPE_POWER_CTRL_BIT0; + u32 val; + + val = readl_relaxed(lane->pipe + PIPE_POWER_CTRL); + writel(val | rst, lane->pipe + PIPE_POWER_CTRL); + writel(val & ~rst, lane->pipe + PIPE_POWER_CTRL); + } + + /* Power up TX, RX and PLL and wait */ + ret = a38x_comphy_power_up(lane); + if (ret) + goto fail; + + /* Perform RX init */ + ret = a38x_comphy_rx_init(lane); + if (ret) + goto fail; + + a38x_set_conf(lane, true); + + return 0; + +fail: + a38x_comphy_power_off(phy); + return ret; +} + /* * We only support changing the speed for comphys configured for GBE. * Since that is all we do, we only poll for PLL ready status. @@ -113,7 +222,6 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) { struct a38x_comphy_lane *lane = phy_get_drvdata(phy); unsigned int gen; - int ret; if (mode != PHY_MODE_ETHERNET) return -EINVAL; @@ -132,23 +240,14 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) return -EINVAL; } - a38x_set_conf(lane, false); - - a38x_comphy_set_speed(lane, gen, gen); - - ret = a38x_comphy_poll(lane, COMPHY_STAT1, - COMPHY_STAT1_PLL_RDY_TX | - COMPHY_STAT1_PLL_RDY_RX, - COMPHY_STAT1_PLL_RDY_TX | - COMPHY_STAT1_PLL_RDY_RX); - - if (ret == 0) - a38x_set_conf(lane, true); + lane->gen = gen; - return ret; + return 0; } static const struct phy_ops a38x_comphy_ops = { + .power_on = a38x_comphy_power_on, + .power_off = a38x_comphy_power_off, .set_mode = a38x_comphy_set_mode, .owner = THIS_MODULE, }; @@ -193,6 +292,7 @@ static int a38x_comphy_probe(struct platform_device *pdev) struct a38x_comphy *priv; struct resource *res; void __iomem *base; + void __iomem *pipe = NULL; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -202,8 +302,16 @@ static int a38x_comphy_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pipe"); + if (res) { + pipe = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + } + priv->dev = &pdev->dev; priv->base = base; + priv->pipe = pipe; /* Optional */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "conf"); @@ -230,16 +338,19 @@ static int a38x_comphy_probe(struct platform_device *pdev) continue; } + priv->lane[val].base = base + COMPHY_REG_SIZE * val; + if (pipe) + priv->lane[val].pipe = pipe + PIPE_REG_SIZE * val; + priv->lane[val].priv = priv; + priv->lane[val].n = val; + priv->lane[val].port = -1; + phy = devm_phy_create(&pdev->dev, child, &a38x_comphy_ops); if (IS_ERR(phy)) { of_node_put(child); return PTR_ERR(phy); } - priv->lane[val].base = base + 0x28 * val; - priv->lane[val].priv = priv; - priv->lane[val].n = val; - priv->lane[val].port = -1; phy_set_drvdata(phy, &priv->lane[val]); } -- cgit From 86e44bf1ee6220702dbcf306c2917aa8d24b9c8c Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 8 Jul 2020 11:37:25 +0100 Subject: net: mvneta: program 1ms autonegotiation clock divisor Program the 1ms autonegotiation clock divisor according to the clocking rate of neta - without this, the 1ms clock ticks at about 660us on Armada 38x configured for 250MHz. Bring this into correct specification. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvneta.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index b1ffa6f4f03b..b0d4b1405298 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3967,7 +3967,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { /* SGMII mode receives the state from the PHY */ new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; - new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; + new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | MVNETA_GMAC_FORCE_LINK_PASS | MVNETA_GMAC_CONFIG_MII_SPEED | @@ -3979,7 +3979,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, } else { /* 802.3z negotiation - only 1000base-X */ new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; - new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; + new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | MVNETA_GMAC_FORCE_LINK_PASS | MVNETA_GMAC_CONFIG_MII_SPEED)) | @@ -3992,6 +3992,10 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; } + /* Set the 1ms clock divisor */ + if (new_clk == MVNETA_GMAC_1MS_CLOCK_ENABLE) + new_clk |= clk_get_rate(pp->clk) / 1000; + /* Armada 370 documentation says we can only change the port mode * and in-band enable when the link is down, so force it down * while making these changes. We also do this for GMAC_CTRL2 */ -- cgit From 1c9e9482b4d7346be9e37ec3a5d6cf97a07be920 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 28 Jun 2020 13:03:47 +0100 Subject: net: mvneta: convert to use mac_prepare()/mac_finish() Convert mvneta to use the mac_prepare() and mac_finish() methods in preparation to converting mvneta to split-PCS support. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvneta.c | 77 ++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index b0d4b1405298..01ad998dc328 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3924,6 +3924,32 @@ static void mvneta_mac_an_restart(struct phylink_config *config) gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); } +static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct mvneta_port *pp = netdev_priv(ndev); + u32 val; + + if (pp->phy_interface != interface || + phylink_autoneg_inband(mode)) { + /* Force the link down when changing the interface or if in + * in-band mode. According to Armada 370 documentation, we + * can only change the port mode and in-band enable when the + * link is down. + */ + val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + val &= ~MVNETA_GMAC_FORCE_LINK_PASS; + val |= MVNETA_GMAC_FORCE_LINK_DOWN; + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + } + + if (pp->phy_interface != interface) + WARN_ON(phy_power_off(pp->comphy)); + + return 0; +} + static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { @@ -3968,9 +3994,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, /* SGMII mode receives the state from the PHY */ new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; - new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | - MVNETA_GMAC_FORCE_LINK_PASS | - MVNETA_GMAC_CONFIG_MII_SPEED | + new_an = (new_an & ~(MVNETA_GMAC_CONFIG_MII_SPEED | MVNETA_GMAC_CONFIG_GMII_SPEED | MVNETA_GMAC_CONFIG_FULL_DUPLEX)) | MVNETA_GMAC_INBAND_AN_ENABLE | @@ -3980,9 +4004,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, /* 802.3z negotiation - only 1000base-X */ new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; - new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | - MVNETA_GMAC_FORCE_LINK_PASS | - MVNETA_GMAC_CONFIG_MII_SPEED)) | + new_an = (new_an & ~MVNETA_GMAC_CONFIG_MII_SPEED) | MVNETA_GMAC_INBAND_AN_ENABLE | MVNETA_GMAC_CONFIG_GMII_SPEED | /* The MAC only supports FD mode */ @@ -3996,30 +4018,12 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, if (new_clk == MVNETA_GMAC_1MS_CLOCK_ENABLE) new_clk |= clk_get_rate(pp->clk) / 1000; - /* Armada 370 documentation says we can only change the port mode - * and in-band enable when the link is down, so force it down - * while making these changes. We also do this for GMAC_CTRL2 */ - if ((new_ctrl0 ^ gmac_ctrl0) & MVNETA_GMAC0_PORT_1000BASE_X || - (new_ctrl2 ^ gmac_ctrl2) & MVNETA_GMAC2_INBAND_AN_ENABLE || - (new_an ^ gmac_an) & MVNETA_GMAC_INBAND_AN_ENABLE) { - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, - (gmac_an & ~MVNETA_GMAC_FORCE_LINK_PASS) | - MVNETA_GMAC_FORCE_LINK_DOWN); - } - - /* When at 2.5G, the link partner can send frames with shortened * preambles. */ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) new_ctrl4 |= MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE; - if (pp->phy_interface != state->interface) { - if (pp->comphy) - WARN_ON(phy_power_off(pp->comphy)); - WARN_ON(mvneta_config_interface(pp, state->interface)); - } - if (new_ctrl0 != gmac_ctrl0) mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0); if (new_ctrl2 != gmac_ctrl2) @@ -4038,6 +4042,29 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, } } +static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct mvneta_port *pp = netdev_priv(ndev); + u32 val; + + if (pp->phy_interface != interface) + /* Enable the Serdes PHY */ + WARN_ON(mvneta_config_interface(pp, interface)); + + /* Allow the link to come up if in in-band mode, otherwise the + * link is forced via mac_link_down()/mac_link_up() + */ + if (phylink_autoneg_inband(mode)) { + val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + } + + return 0; +} + static void mvneta_set_eee(struct mvneta_port *pp, bool enable) { u32 lpi_ctl1; @@ -4127,7 +4154,9 @@ static const struct phylink_mac_ops mvneta_phylink_ops = { .validate = mvneta_validate, .mac_pcs_get_state = mvneta_mac_pcs_get_state, .mac_an_restart = mvneta_mac_an_restart, + .mac_prepare = mvneta_mac_prepare, .mac_config = mvneta_mac_config, + .mac_finish = mvneta_mac_finish, .mac_link_down = mvneta_mac_link_down, .mac_link_up = mvneta_mac_link_up, }; -- cgit From 5afc5afde551afe8ccfee51b771e4e1a10c81242 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 8 Jul 2020 11:44:36 +0100 Subject: net: mvneta: move 1ms clock control into mac_prepare/mac_finish Move the 1ms clock control out of mac_config() into mac_prepare() and mac_finish(), which simplifies the mac_config() code. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvneta.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 01ad998dc328..e8ce5abdcea5 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3947,6 +3947,14 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, if (pp->phy_interface != interface) WARN_ON(phy_power_off(pp->comphy)); + /* Enable the 1ms clock */ + if (phylink_autoneg_inband(mode)) { + unsigned long rate = clk_get_rate(pp->clk); + + mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, + MVNETA_GMAC_1MS_CLOCK_ENABLE | (rate / 1000)); + } + return 0; } @@ -3958,14 +3966,12 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0); u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2); u32 new_ctrl4, gmac_ctrl4 = mvreg_read(pp, MVNETA_GMAC_CTRL_4); - u32 new_clk, gmac_clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X; new_ctrl2 = gmac_ctrl2 & ~(MVNETA_GMAC2_INBAND_AN_ENABLE | MVNETA_GMAC2_PORT_RESET); new_ctrl4 = gmac_ctrl4 & ~(MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE); - new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE; new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE | MVNETA_GMAC_INBAND_RESTART_AN | MVNETA_GMAC_AN_SPEED_EN | @@ -3993,7 +3999,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { /* SGMII mode receives the state from the PHY */ new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; - new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; new_an = (new_an & ~(MVNETA_GMAC_CONFIG_MII_SPEED | MVNETA_GMAC_CONFIG_GMII_SPEED | MVNETA_GMAC_CONFIG_FULL_DUPLEX)) | @@ -4003,7 +4008,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, } else { /* 802.3z negotiation - only 1000base-X */ new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; - new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; new_an = (new_an & ~MVNETA_GMAC_CONFIG_MII_SPEED) | MVNETA_GMAC_INBAND_AN_ENABLE | MVNETA_GMAC_CONFIG_GMII_SPEED | @@ -4014,10 +4018,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; } - /* Set the 1ms clock divisor */ - if (new_clk == MVNETA_GMAC_1MS_CLOCK_ENABLE) - new_clk |= clk_get_rate(pp->clk) / 1000; - /* When at 2.5G, the link partner can send frames with shortened * preambles. */ @@ -4030,8 +4030,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2); if (new_ctrl4 != gmac_ctrl4) mvreg_write(pp, MVNETA_GMAC_CTRL_4, new_ctrl4); - if (new_clk != gmac_clk) - mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, new_clk); if (new_an != gmac_an) mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an); @@ -4047,7 +4045,14 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val; + u32 val, clk; + + /* Disable 1ms clock if not in in-band mode */ + if (!phylink_autoneg_inband(mode)) { + clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); + clk &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, clk); + } if (pp->phy_interface != interface) /* Enable the Serdes PHY */ -- cgit From 26a50edb318fee23881564f889d278c7277995dc Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 22 Jun 2020 17:21:43 +0100 Subject: net: mvneta: convert to phylink pcs operations An initial stab at converting mvneta to PCS operations. There's a few FIXMEs to be solved. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvneta.c | 178 +++++++++++++++++++++------------- 1 file changed, 109 insertions(+), 69 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index e8ce5abdcea5..127daf2c06a4 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -499,6 +499,7 @@ struct mvneta_port { unsigned int tx_csum_limit; struct phylink *phylink; struct phylink_config phylink_config; + struct phylink_pcs phylink_pcs; struct phy *comphy; struct mvneta_bm *bm_priv; @@ -3829,6 +3830,111 @@ static int mvneta_set_mac_addr(struct net_device *dev, void *addr) return 0; } +static struct mvneta_port *mvneta_pcs_to_port(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct mvneta_port, phylink_pcs); +} + +static void mvneta_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct mvneta_port *pp = mvneta_pcs_to_port(pcs); + u32 gmac_stat; + + gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); + + if (gmac_stat & MVNETA_GMAC_SPEED_1000) + state->speed = + state->interface == PHY_INTERFACE_MODE_2500BASEX ? + SPEED_2500 : SPEED_1000; + else if (gmac_stat & MVNETA_GMAC_SPEED_100) + state->speed = SPEED_100; + else + state->speed = SPEED_10; + + state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); + state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); + state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); + + if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE) + state->pause |= MLO_PAUSE_RX; + if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE) + state->pause |= MLO_PAUSE_TX; +} + +static int mvneta_pcs_config(struct phylink_pcs *pcs, + unsigned int mode, phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct mvneta_port *pp = mvneta_pcs_to_port(pcs); + u32 mask, val, an, old_an, changed; + + mask = MVNETA_GMAC_INBAND_AN_ENABLE | + MVNETA_GMAC_INBAND_RESTART_AN | + MVNETA_GMAC_AN_SPEED_EN | + MVNETA_GMAC_AN_FLOW_CTRL_EN | + MVNETA_GMAC_AN_DUPLEX_EN; + + if (phylink_autoneg_inband(mode)) { + mask |= MVNETA_GMAC_CONFIG_MII_SPEED | + MVNETA_GMAC_CONFIG_GMII_SPEED | + MVNETA_GMAC_CONFIG_FULL_DUPLEX; + val = MVNETA_GMAC_INBAND_AN_ENABLE; + + if (state->interface == PHY_INTERFACE_MODE_SGMII) { + /* SGMII mode receives the speed and duplex from PHY */ + val |= MVNETA_GMAC_AN_SPEED_EN | + MVNETA_GMAC_AN_DUPLEX_EN; + } else { + /* 802.3z mode has fixed speed and duplex */ + val |= MVNETA_GMAC_CONFIG_GMII_SPEED | + MVNETA_GMAC_CONFIG_FULL_DUPLEX; + + /* The FLOW_CTRL_EN bit selects either the hardware + * automatically or the CONFIG_FLOW_CTRL manually + * controls the GMAC pause mode. + */ + if (permit_pause_to_mac) + val |= MVNETA_GMAC_AN_FLOW_CTRL_EN; + + /* Update the advertisement bits */ + mask |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; + if (phylink_test(advertising, Pause)) + val |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; + } + } else { + /* Phy or fixed speed - disable in-band AN modes */ + val = 0; + } + + old_an = an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + an = (an & ~mask) | val; + changed = old_an ^ an; + if (changed) + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, an); + + /* We are only interested in the advertisement bits changing */ + return !!(changed & MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL); +} + +static void mvneta_pcs_an_restart(struct phylink_pcs *pcs) +{ + struct mvneta_port *pp = mvneta_pcs_to_port(pcs); + u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, + gmac_an | MVNETA_GMAC_INBAND_RESTART_AN); + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, + gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); +} + +static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = { + .pcs_get_state = mvneta_pcs_get_state, + .pcs_config = mvneta_pcs_config, + .pcs_an_restart = mvneta_pcs_an_restart, +}; + static void mvneta_validate(struct phylink_config *config, unsigned long *supported, struct phylink_link_state *state) @@ -3883,47 +3989,6 @@ static void mvneta_validate(struct phylink_config *config, phylink_helper_basex_speed(state); } -static void mvneta_mac_pcs_get_state(struct phylink_config *config, - struct phylink_link_state *state) -{ - struct net_device *ndev = to_net_dev(config->dev); - struct mvneta_port *pp = netdev_priv(ndev); - u32 gmac_stat; - - gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); - - if (gmac_stat & MVNETA_GMAC_SPEED_1000) - state->speed = - state->interface == PHY_INTERFACE_MODE_2500BASEX ? - SPEED_2500 : SPEED_1000; - else if (gmac_stat & MVNETA_GMAC_SPEED_100) - state->speed = SPEED_100; - else - state->speed = SPEED_10; - - state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); - state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); - state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); - - state->pause = 0; - if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE) - state->pause |= MLO_PAUSE_RX; - if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE) - state->pause |= MLO_PAUSE_TX; -} - -static void mvneta_mac_an_restart(struct phylink_config *config) -{ - struct net_device *ndev = to_net_dev(config->dev); - struct mvneta_port *pp = netdev_priv(ndev); - u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, - gmac_an | MVNETA_GMAC_INBAND_RESTART_AN); - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, - gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); -} - static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, phy_interface_t interface) { @@ -3966,18 +4031,11 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0); u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2); u32 new_ctrl4, gmac_ctrl4 = mvreg_read(pp, MVNETA_GMAC_CTRL_4); - u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X; new_ctrl2 = gmac_ctrl2 & ~(MVNETA_GMAC2_INBAND_AN_ENABLE | MVNETA_GMAC2_PORT_RESET); new_ctrl4 = gmac_ctrl4 & ~(MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE); - new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE | - MVNETA_GMAC_INBAND_RESTART_AN | - MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL | - MVNETA_GMAC_AN_FLOW_CTRL_EN | - MVNETA_GMAC_AN_DUPLEX_EN); /* Even though it might look weird, when we're configured in * SGMII or QSGMII mode, the RGMII bit needs to be set. @@ -3989,9 +4047,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, phy_interface_mode_is_8023z(state->interface)) new_ctrl2 |= MVNETA_GMAC2_PCS_ENABLE; - if (phylink_test(state->advertising, Pause)) - new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; - if (!phylink_autoneg_inband(mode)) { /* Phy or fixed speed - nothing to do, leave the * configured speed, duplex and flow control as-is. @@ -3999,23 +4054,9 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { /* SGMII mode receives the state from the PHY */ new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; - new_an = (new_an & ~(MVNETA_GMAC_CONFIG_MII_SPEED | - MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FULL_DUPLEX)) | - MVNETA_GMAC_INBAND_AN_ENABLE | - MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_DUPLEX_EN; } else { /* 802.3z negotiation - only 1000base-X */ new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; - new_an = (new_an & ~MVNETA_GMAC_CONFIG_MII_SPEED) | - MVNETA_GMAC_INBAND_AN_ENABLE | - MVNETA_GMAC_CONFIG_GMII_SPEED | - /* The MAC only supports FD mode */ - MVNETA_GMAC_CONFIG_FULL_DUPLEX; - - if (state->pause & MLO_PAUSE_AN && state->an_enabled) - new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; } /* When at 2.5G, the link partner can send frames with shortened @@ -4030,8 +4071,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2); if (new_ctrl4 != gmac_ctrl4) mvreg_write(pp, MVNETA_GMAC_CTRL_4, new_ctrl4); - if (new_an != gmac_an) - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an); if (gmac_ctrl2 & MVNETA_GMAC2_PORT_RESET) { while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) & @@ -4157,8 +4196,6 @@ static void mvneta_mac_link_up(struct phylink_config *config, static const struct phylink_mac_ops mvneta_phylink_ops = { .validate = mvneta_validate, - .mac_pcs_get_state = mvneta_mac_pcs_get_state, - .mac_an_restart = mvneta_mac_an_restart, .mac_prepare = mvneta_mac_prepare, .mac_config = mvneta_mac_config, .mac_finish = mvneta_mac_finish, @@ -5240,6 +5277,9 @@ static int mvneta_probe(struct platform_device *pdev) goto err_free_irq; } + pp->phylink_pcs.ops = &mvneta_phylink_pcs_ops; + phylink_set_pcs(phylink, &pp->phylink_pcs); + dev->tx_queue_len = MVNETA_MAX_TXD; dev->watchdog_timeo = 5 * HZ; dev->netdev_ops = &mvneta_netdev_ops; -- cgit From 685f6193c184ea0593beebd661527dd2d5752173 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 22 Dec 2016 16:03:15 +0000 Subject: net: mvneta: split out GMAC Split out the code handling the GMAC from the rest of the driver. This block appears to be shared amongst several revisions of the IP. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/Kconfig | 4 + drivers/net/ethernet/marvell/Makefile | 1 + drivers/net/ethernet/marvell/mvgmac.c | 356 ++++++++++++++++++++++++++++++++++ drivers/net/ethernet/marvell/mvgmac.h | 43 ++++ drivers/net/ethernet/marvell/mvneta.c | 310 ++++------------------------- 5 files changed, 438 insertions(+), 276 deletions(-) create mode 100644 drivers/net/ethernet/marvell/mvgmac.c create mode 100644 drivers/net/ethernet/marvell/mvgmac.h diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index fe0989c0fc25..e9149e6fab10 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -59,6 +59,7 @@ config MVNETA_BM_ENABLE config MVNETA tristate "Marvell Armada 370/38x/XP/37xx network interface support" depends on ARCH_MVEBU || COMPILE_TEST + select MVGMAC select MVMDIO select PHYLINK select PAGE_POOL @@ -71,6 +72,9 @@ config MVNETA driver, which should be used for the older Marvell SoCs (Dove, Orion, Discovery, Kirkwood). +config MVGMAC + tristate + config MVNETA_BM tristate depends on !64BIT diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile index 9f88fe822555..b1442ff46abf 100644 --- a/drivers/net/ethernet/marvell/Makefile +++ b/drivers/net/ethernet/marvell/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_MVMDIO) += mvmdio.o obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o obj-$(CONFIG_MVNETA_BM) += mvneta_bm.o obj-$(CONFIG_MVNETA) += mvneta.o +obj-$(CONFIG_MVGMAC) += mvgmac.o obj-$(CONFIG_MVPP2) += mvpp2/ obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o obj-$(CONFIG_SKGE) += skge.o diff --git a/drivers/net/ethernet/marvell/mvgmac.c b/drivers/net/ethernet/marvell/mvgmac.c new file mode 100644 index 000000000000..6c0b7c5c0b24 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvgmac.c @@ -0,0 +1,356 @@ +/* + * GMAC driver for Marvell network interfaces on Armada SoCs. + * + * Copyright (C) 2012 Marvell + * + * Rami Rosen + * Thomas Petazzoni + * + * Split from mvneta and mvpp2 by Russell King. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include + +#include "mvgmac.h" + +enum { + GMAC_CTRL0_REG = 0x00, + GMAC_CTRL0_PORT_ENABLE = BIT(0), + GMAC_CTRL0_PORT_1000BASE_X = BIT(1), + GMAC_CTRL0_MAX_RX_SIZE_SHIFT = 2, + GMAC_CTRL0_MAX_RX_SIZE_MASK = 0x1fff << GMAC_CTRL0_MAX_RX_SIZE_SHIFT, + GMAC_CTRL0_MIB_CNTR_ENABLE = BIT(15), + + GMAC_CTRL2_REG = 0x08, + GMAC_CTRL2_INBAND_AN_SGMII = BIT(0), + GMAC_CTRL2_PCS_ENABLE = BIT(3), + GMAC_CTRL2_PORT_RGMII = BIT(4), + GMAC_CTRL2_PORT_RESET = BIT(6), + + GMAC_ANEG_REG = 0x0c, + GMAC_ANEG_FORCE_LINK_DOWN = BIT(0), + GMAC_ANEG_FORCE_LINK_PASS = BIT(1), + GMAC_ANEG_INBAND_AN_ENABLE = BIT(2), + GMAC_ANEG_AN_BYPASS_ENABLE = BIT(3), + GMAC_ANEG_INBAND_RESTART_AN = BIT(4), + GMAC_ANEG_MII_SPEED = BIT(5), + GMAC_ANEG_GMII_SPEED = BIT(6), + GMAC_ANEG_AN_SPEED_ENABLE = BIT(7), + GMAC_ANEG_CONFIG_FLOW_CTRL = BIT(8), + GMAC_ANEG_ADVERT_SYM_FLOW_CTRL = BIT(9), + GMAC_ANEG_AN_FLOW_CTRL_ENABLE = BIT(11), + GMAC_ANEG_FULL_DUPLEX = BIT(12), + GMAC_ANEG_AN_DUPLEX_ENABLE = BIT(13), + + GMAC_STATUS_REG = 0x10, + MVGMAC_LINK_UP = BIT(0), + MVGMAC_SPEED_1000 = BIT(1), + MVGMAC_SPEED_100 = BIT(2), + MVGMAC_FULL_DUPLEX = BIT(3), + MVGMAC_RX_FLOW_CTRL_ENABLE = BIT(4), + MVGMAC_TX_FLOW_CTRL_ENABLE = BIT(5), + MVGMAC_RX_FLOW_CTRL_ACTIVE = BIT(6), + MVGMAC_TX_FLOW_CTRL_ACTIVE = BIT(7), + MVGMAC_AN_COMPLETE = BIT(11), + MVGMAC_SYNC_OK = BIT(14), + + GMAC_CTRL4_REG = 0x90, + GMAC_CTRL4_SHORT_PREAMBLE_ENABLE = BIT(1), + + GMAC_LPI_CTRL0_REG = 0xc0, + GMAC_LPI_CTRL0_TS = 0xff << 8, + GMAC_LPI_CTRL1_REG = 0xc4, + GMAC_LPI_CTRL1_REQ_EN = BIT(0), + GMAC_LPI_CTRL2_REG = 0xc8, + GMAC_LPI_STATUS_REG = 0xcc, + GMAC_LPI_CNTR_REG = 0xd0, +}; + +#define insert(var, mask, val) ({ \ + u32 __mask = mask; \ + ((var) & ~(__mask)) | (((val) << __ffs(__mask)) & (__mask)); \ +}) + +/* Change maximum receive size of the port. */ +void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size) +{ + int size = (max_rx_size - MARVELL_HEADER_SIZE) / 2; + u32 val; + + val = readl_relaxed(gmac->base + GMAC_CTRL0_REG); + val = insert(val, GMAC_CTRL0_MAX_RX_SIZE_MASK, size); + writel_relaxed(val, gmac->base + GMAC_CTRL0_REG); +} +EXPORT_SYMBOL_GPL(mvgmac_set_max_rx_size); + +/* Enable the port by setting the port enable bit of the MAC control register */ +void mvgmac_enable(struct mvgmac *gmac) +{ + u32 val; + + val = readl_relaxed(gmac->base + GMAC_CTRL0_REG); + val |= GMAC_CTRL0_PORT_ENABLE; + val |= GMAC_CTRL0_MIB_CNTR_ENABLE; + writel_relaxed(val, gmac->base + GMAC_CTRL0_REG); +} +EXPORT_SYMBOL_GPL(mvgmac_enable); + +/* Disable the port */ +void mvgmac_disable(struct mvgmac *gmac) +{ + u32 val; + + val = readl_relaxed(gmac->base + GMAC_CTRL0_REG); + val &= ~GMAC_CTRL0_PORT_ENABLE; + writel_relaxed(val, gmac->base + GMAC_CTRL0_REG); +} +EXPORT_SYMBOL_GPL(mvgmac_disable); + +void mvgmac_link_unforce(struct mvgmac *gmac) +{ + u32 val; + + val = readl_relaxed(gmac->base + GMAC_ANEG_REG); + val &= ~(GMAC_ANEG_FORCE_LINK_PASS | GMAC_ANEG_FORCE_LINK_DOWN); + writel_relaxed(val, gmac->base + GMAC_ANEG_REG); +} +EXPORT_SYMBOL_GPL(mvgmac_link_unforce); + +void mvgmac_link_force_down(struct mvgmac *gmac) +{ + u32 val; + + val = readl_relaxed(gmac->base + GMAC_ANEG_REG); + val &= ~GMAC_ANEG_FORCE_LINK_PASS; + val |= GMAC_ANEG_FORCE_LINK_DOWN; + writel_relaxed(val, gmac->base + GMAC_ANEG_REG); +} +EXPORT_SYMBOL_GPL(mvgmac_link_force_down); + +void mvgmac_link_down(struct mvgmac *gmac, int mode) +{ + if (!phylink_autoneg_inband(mode)) + mvgmac_link_force_down(gmac); +} +EXPORT_SYMBOL_GPL(mvgmac_link_down); + +void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + u32 val; + + val = readl_relaxed(gmac->base + GMAC_ANEG_REG); + val &= ~GMAC_ANEG_CONFIG_FLOW_CTRL; + + if (!phylink_autoneg_inband(mode)) { + val &= ~(GMAC_ANEG_FORCE_LINK_DOWN | + GMAC_ANEG_MII_SPEED | + GMAC_ANEG_GMII_SPEED | + GMAC_ANEG_FULL_DUPLEX); + val |= GMAC_ANEG_FORCE_LINK_PASS; + + if (speed == SPEED_1000 || speed == SPEED_2500) + val |= GMAC_ANEG_GMII_SPEED; + else if (speed == SPEED_100) + val |= GMAC_ANEG_MII_SPEED; + + if (duplex == DUPLEX_FULL) + val |= GMAC_ANEG_FULL_DUPLEX; + } + + if (tx_pause || rx_pause) + val |= GMAC_ANEG_CONFIG_FLOW_CTRL; + + writel_relaxed(val, gmac->base + GMAC_ANEG_REG); +} +EXPORT_SYMBOL_GPL(mvgmac_link_up); + +bool mvgmac_link_is_up(struct mvgmac *gmac) +{ + u32 gmac_stat = readl_relaxed(gmac->base + GMAC_STATUS_REG); + + return !!(gmac_stat & MVGMAC_LINK_UP); +} +EXPORT_SYMBOL_GPL(mvgmac_link_is_up); + +void mvgmac_pcs_get_state(struct mvgmac *gmac, struct phylink_link_state *state) +{ + u32 gmac_stat = readl_relaxed(gmac->base + GMAC_STATUS_REG); + + if (gmac_stat & MVGMAC_SPEED_1000) + state->speed = + state->interface == PHY_INTERFACE_MODE_2500BASEX ? + SPEED_2500 : SPEED_1000; + else if (gmac_stat & MVGMAC_SPEED_100) + state->speed = SPEED_100; + else + state->speed = SPEED_10; + + state->an_complete = !!(gmac_stat & MVGMAC_AN_COMPLETE); + state->link = !!(gmac_stat & MVGMAC_LINK_UP); + state->duplex = !!(gmac_stat & MVGMAC_FULL_DUPLEX); + + if (gmac_stat & MVGMAC_RX_FLOW_CTRL_ENABLE) + state->pause |= MLO_PAUSE_RX; + if (gmac_stat & MVGMAC_TX_FLOW_CTRL_ENABLE) + state->pause |= MLO_PAUSE_TX; +} +EXPORT_SYMBOL_GPL(mvgmac_pcs_get_state); + +void mvgmac_pcs_an_restart(struct mvgmac *gmac) +{ + u32 gmac_an = readl_relaxed(gmac->base + GMAC_ANEG_REG); + + writel_relaxed(gmac_an | GMAC_ANEG_INBAND_RESTART_AN, + gmac->base + GMAC_ANEG_REG); + writel_relaxed(gmac_an & ~GMAC_ANEG_INBAND_RESTART_AN, + gmac->base + GMAC_ANEG_REG); +} +EXPORT_SYMBOL_GPL(mvgmac_pcs_an_restart); + +int mvgmac_pcs_config(struct mvgmac *gmac, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + u32 mask, val, an, old_an, changed; + + mask = GMAC_ANEG_INBAND_AN_ENABLE | + GMAC_ANEG_INBAND_RESTART_AN | + GMAC_ANEG_AN_SPEED_ENABLE | + GMAC_ANEG_AN_FLOW_CTRL_ENABLE | + GMAC_ANEG_AN_DUPLEX_ENABLE; + + if (phylink_autoneg_inband(mode)) { + mask |= GMAC_ANEG_MII_SPEED | + GMAC_ANEG_GMII_SPEED | + GMAC_ANEG_FULL_DUPLEX; + val = GMAC_ANEG_INBAND_AN_ENABLE; + + if (interface == PHY_INTERFACE_MODE_SGMII) { + /* SGMII mode receives the speed and duplex from PHY */ + val |= GMAC_ANEG_AN_SPEED_ENABLE | + GMAC_ANEG_AN_DUPLEX_ENABLE; + } else { + /* 802.3z mode has fixed speed and duplex */ + val |= GMAC_ANEG_GMII_SPEED | + GMAC_ANEG_FULL_DUPLEX; + + /* The FLOW_CTRL_ENABLE bit selects either the hardware + * automatically or the GMAC_ANEG_FLOW_CTRL manually + * controls the GMAC pause mode. + */ + if (permit_pause_to_mac) + val |= GMAC_ANEG_AN_FLOW_CTRL_ENABLE; + + /* Update the advertisement bits */ + mask |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL; + if (phylink_test(advertising, Pause)) + val |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL; + } + } else { + /* Phy or fixed speed - disable in-band AN modes */ + val = 0; + } + + old_an = an = readl_relaxed(gmac->base + GMAC_ANEG_REG); + an = (an & ~mask) | val; + changed = old_an ^ an; + if (changed) + writel_relaxed(an, gmac->base + GMAC_ANEG_REG); + + /* We are only interested in the advertisement bits changing */ + return !!(changed & GMAC_ANEG_ADVERT_SYM_FLOW_CTRL); +} +EXPORT_SYMBOL_GPL(mvgmac_pcs_config); + +void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, + const struct phylink_link_state *state) +{ + u32 new_ctrl0, gmac_ctrl0 = readl_relaxed(gmac->base + GMAC_CTRL0_REG); + u32 new_ctrl2, gmac_ctrl2 = readl_relaxed(gmac->base + GMAC_CTRL2_REG); + u32 new_ctrl4, gmac_ctrl4 = readl_relaxed(gmac->base + GMAC_CTRL4_REG); + + new_ctrl0 = gmac_ctrl0 & ~GMAC_CTRL0_PORT_1000BASE_X; + new_ctrl2 = gmac_ctrl2 & ~(GMAC_CTRL2_INBAND_AN_SGMII | + GMAC_CTRL2_PORT_RESET); + new_ctrl4 = gmac_ctrl4 & ~GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + + /* Even though it might look weird, when we're configured in + * SGMII or QSGMII mode, the RGMII bit needs to be set. + */ + new_ctrl2 |= GMAC_CTRL2_PORT_RGMII; + + if (state->interface == PHY_INTERFACE_MODE_QSGMII || + state->interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(state->interface)) + new_ctrl2 |= GMAC_CTRL2_PCS_ENABLE; + + if (!phylink_autoneg_inband(mode)) { + /* Phy or fixed speed - nothing to do, leave the + * configured speed, duplex and flow control as-is. + */ + } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { + /* SGMII mode receives the state from the PHY */ + new_ctrl2 |= GMAC_CTRL2_INBAND_AN_SGMII; + } else { + /* 802.3z negotiation - 1000BaseX */ + new_ctrl0 |= GMAC_CTRL0_PORT_1000BASE_X; + } + + /* When at 2.5G, the link partner can send frames with shortened + * preambles. + */ + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + new_ctrl4 |= GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + + if (new_ctrl0 != gmac_ctrl0) + writel_relaxed(new_ctrl0, gmac->base + GMAC_CTRL0_REG); + if (new_ctrl2 != gmac_ctrl2) + writel_relaxed(new_ctrl2, gmac->base + GMAC_CTRL2_REG); + if (new_ctrl4 != gmac_ctrl4) + writel_relaxed(new_ctrl4, gmac->base + GMAC_CTRL4_REG); + + if (gmac_ctrl2 & GMAC_CTRL2_PORT_RESET) { + while ((readl_relaxed(gmac->base + GMAC_CTRL2_REG) & + GMAC_CTRL2_PORT_RESET) != 0) + continue; + } +} +EXPORT_SYMBOL_GPL(mvgmac_config_mac); + +int mvgmac_set_lpi_ts(struct mvgmac *gmac, unsigned int ts) +{ + u32 val; + + if (!(readl_relaxed(gmac->base + GMAC_STATUS_REG) & MVGMAC_SPEED_1000)) + ts = DIV_ROUND_UP(ts, 10); + + if (ts > 255) + ts = 255; + + val = readl_relaxed(gmac->base + GMAC_LPI_CTRL0_REG); + val = insert(val, GMAC_LPI_CTRL0_TS, ts); + writel_relaxed(val, gmac->base + GMAC_LPI_CTRL0_REG); + + return 0; +} +EXPORT_SYMBOL_GPL(mvgmac_set_lpi_ts); + +void mvgmac_set_eee(struct mvgmac *gmac, bool enable) +{ + u32 val; + + val = readl_relaxed(gmac->base + GMAC_LPI_CTRL1_REG); + val = insert(val, GMAC_LPI_CTRL1_REQ_EN, enable); + writel_relaxed(val, gmac->base + GMAC_LPI_CTRL1_REG); +} +EXPORT_SYMBOL_GPL(mvgmac_set_eee); + +MODULE_DESCRIPTION("Marvell GMAC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/marvell/mvgmac.h b/drivers/net/ethernet/marvell/mvgmac.h new file mode 100644 index 000000000000..ceccc6777237 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvgmac.h @@ -0,0 +1,43 @@ +#ifndef MVGMAC_H +#define MVGMAC_H + +#include + +struct phylink_link_state; + +/* The two bytes Marvell header. Either contains a special value used by + * Marvell switches when a specific hardware mode is enabled (not supported + * by this driver) or is filled automatically by zeroes on the RX side. + * Those two bytes being at the front of the Ethernet header, they allow + * to have the IP header aligned on a 4 bytes boundary automatically: the + * hardware skips those two bytes on its own. + */ +#define MARVELL_HEADER_SIZE 2 + +struct mvgmac { + void __iomem *base; +}; + +void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size); +void mvgmac_enable(struct mvgmac *gmac); +void mvgmac_disable(struct mvgmac *gmac); +void mvgmac_link_unforce(struct mvgmac *gmac); +void mvgmac_link_force_down(struct mvgmac *gmac); +void mvgmac_link_down(struct mvgmac *gmac, int mode); +void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, + bool tx_pause, bool rx_pause); +bool mvgmac_link_is_up(struct mvgmac *gmac); +void mvgmac_pcs_get_state(struct mvgmac *gmac, + struct phylink_link_state *state); +void mvgmac_pcs_an_restart(struct mvgmac *gmac); +int mvgmac_pcs_config(struct mvgmac *gmac, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac); +void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, + const struct phylink_link_state *state); + +int mvgmac_set_lpi_ts(struct mvgmac *gmac, unsigned int ts); +void mvgmac_set_eee(struct mvgmac *gmac, bool enable); + +#endif diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 127daf2c06a4..5331f5a72ab5 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -34,6 +34,7 @@ #include #include #include "mvneta_bm.h" +#include "mvgmac.h" #include #include #include @@ -193,43 +194,7 @@ #define MVNETA_RXQ_ENABLE_MASK 0x000000ff #define MVETH_TXQ_TOKEN_COUNT_REG(q) (0x2700 + ((q) << 4)) #define MVETH_TXQ_TOKEN_CFG_REG(q) (0x2704 + ((q) << 4)) -#define MVNETA_GMAC_CTRL_0 0x2c00 -#define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2 -#define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc -#define MVNETA_GMAC0_PORT_1000BASE_X BIT(1) -#define MVNETA_GMAC0_PORT_ENABLE BIT(0) -#define MVNETA_GMAC_CTRL_2 0x2c08 -#define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0) -#define MVNETA_GMAC2_PCS_ENABLE BIT(3) -#define MVNETA_GMAC2_PORT_RGMII BIT(4) -#define MVNETA_GMAC2_PORT_RESET BIT(6) -#define MVNETA_GMAC_STATUS 0x2c10 -#define MVNETA_GMAC_LINK_UP BIT(0) -#define MVNETA_GMAC_SPEED_1000 BIT(1) -#define MVNETA_GMAC_SPEED_100 BIT(2) -#define MVNETA_GMAC_FULL_DUPLEX BIT(3) -#define MVNETA_GMAC_RX_FLOW_CTRL_ENABLE BIT(4) -#define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5) -#define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6) -#define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7) -#define MVNETA_GMAC_AN_COMPLETE BIT(11) -#define MVNETA_GMAC_SYNC_OK BIT(14) -#define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c -#define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0) -#define MVNETA_GMAC_FORCE_LINK_PASS BIT(1) -#define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2) -#define MVNETA_GMAC_AN_BYPASS_ENABLE BIT(3) -#define MVNETA_GMAC_INBAND_RESTART_AN BIT(4) -#define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5) -#define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6) -#define MVNETA_GMAC_AN_SPEED_EN BIT(7) -#define MVNETA_GMAC_CONFIG_FLOW_CTRL BIT(8) -#define MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL BIT(9) -#define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11) -#define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12) -#define MVNETA_GMAC_AN_DUPLEX_EN BIT(13) -#define MVNETA_GMAC_CTRL_4 0x2c90 -#define MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE BIT(1) +#define MVNETA_GMAC_BASE 0x2c00 #define MVNETA_MIB_COUNTERS_BASE 0x3000 #define MVNETA_MIB_LATE_COLLISION 0x7c #define MVNETA_DA_FILT_SPEC_MCAST 0x3400 @@ -253,12 +218,6 @@ #define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2)) #define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff -#define MVNETA_LPI_CTRL_0 0x2cc0 -#define MVNETA_LPI_CTRL_1 0x2cc4 -#define MVNETA_LPI_REQUEST_ENABLE BIT(0) -#define MVNETA_LPI_CTRL_2 0x2cc8 -#define MVNETA_LPI_STATUS 0x2ccc - #define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff /* Descriptor ring Macros */ @@ -280,7 +239,7 @@ * boundary automatically: the hardware skips those two bytes on its * own. */ -#define MVNETA_MH_SIZE 2 +#define MVNETA_MH_SIZE MARVELL_HEADER_SIZE #define MVNETA_VLAN_TAG_LEN 4 @@ -502,6 +461,7 @@ struct mvneta_port { struct phylink_pcs phylink_pcs; struct phy *comphy; + struct mvgmac gmac; struct mvneta_bm *bm_priv; struct mvneta_bm_pool *pool_long; struct mvneta_bm_pool *pool_short; @@ -510,6 +470,7 @@ struct mvneta_port { bool eee_enabled; bool eee_active; bool tx_lpi_enabled; + u32 tx_lpi_timer; u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)]; @@ -892,19 +853,6 @@ mvneta_rxq_next_desc_get(struct mvneta_rx_queue *rxq) return rxq->descs + rx_desc; } -/* Change maximum receive size of the port. */ -static void mvneta_max_rx_size_set(struct mvneta_port *pp, int max_rx_size) -{ - u32 val; - - val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); - val &= ~MVNETA_GMAC_MAX_RX_SIZE_MASK; - val |= ((max_rx_size - MVNETA_MH_SIZE) / 2) << - MVNETA_GMAC_MAX_RX_SIZE_SHIFT; - mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); -} - - /* Set rx queue offset */ static void mvneta_rxq_offset_set(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, @@ -1312,23 +1260,13 @@ static void mvneta_port_down(struct mvneta_port *pp) /* Enable the port by setting the port enable bit of the MAC control register */ static void mvneta_port_enable(struct mvneta_port *pp) { - u32 val; - - /* Enable port */ - val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); - val |= MVNETA_GMAC0_PORT_ENABLE; - mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); + mvgmac_enable(&pp->gmac); } /* Disable the port and wait for about 200 usec before retuning */ static void mvneta_port_disable(struct mvneta_port *pp) { - u32 val; - - /* Reset the Enable bit in the Serial Control Register */ - val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); - val &= ~MVNETA_GMAC0_PORT_ENABLE; - mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); + mvgmac_disable(&pp->gmac); udelay(200); } @@ -3134,9 +3072,9 @@ static irqreturn_t mvneta_percpu_isr(int irq, void *dev_id) static void mvneta_link_change(struct mvneta_port *pp) { - u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); + bool link_is_up = mvgmac_link_is_up(&pp->gmac); - phylink_mac_change(pp->phylink, !!(gmac_stat & MVNETA_GMAC_LINK_UP)); + phylink_mac_change(pp->phylink, link_is_up); } /* NAPI handler @@ -3628,7 +3566,7 @@ static void mvneta_start_dev(struct mvneta_port *pp) WARN_ON(mvneta_config_interface(pp, pp->phy_interface)); - mvneta_max_rx_size_set(pp, pp->pkt_size); + mvgmac_set_max_rx_size(&pp->gmac, pp->pkt_size); mvneta_txq_max_tx_size_set(pp, pp->pkt_size); /* start the Rx/Tx activity */ @@ -3839,27 +3777,8 @@ static void mvneta_pcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state) { struct mvneta_port *pp = mvneta_pcs_to_port(pcs); - u32 gmac_stat; - - gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); - if (gmac_stat & MVNETA_GMAC_SPEED_1000) - state->speed = - state->interface == PHY_INTERFACE_MODE_2500BASEX ? - SPEED_2500 : SPEED_1000; - else if (gmac_stat & MVNETA_GMAC_SPEED_100) - state->speed = SPEED_100; - else - state->speed = SPEED_10; - - state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); - state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); - state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); - - if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE) - state->pause |= MLO_PAUSE_RX; - if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE) - state->pause |= MLO_PAUSE_TX; + mvgmac_pcs_get_state(&pp->gmac, state); } static int mvneta_pcs_config(struct phylink_pcs *pcs, @@ -3868,65 +3787,19 @@ static int mvneta_pcs_config(struct phylink_pcs *pcs, bool permit_pause_to_mac) { struct mvneta_port *pp = mvneta_pcs_to_port(pcs); - u32 mask, val, an, old_an, changed; - - mask = MVNETA_GMAC_INBAND_AN_ENABLE | - MVNETA_GMAC_INBAND_RESTART_AN | - MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_FLOW_CTRL_EN | - MVNETA_GMAC_AN_DUPLEX_EN; - if (phylink_autoneg_inband(mode)) { - mask |= MVNETA_GMAC_CONFIG_MII_SPEED | - MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FULL_DUPLEX; - val = MVNETA_GMAC_INBAND_AN_ENABLE; - - if (state->interface == PHY_INTERFACE_MODE_SGMII) { - /* SGMII mode receives the speed and duplex from PHY */ - val |= MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_DUPLEX_EN; - } else { - /* 802.3z mode has fixed speed and duplex */ - val |= MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FULL_DUPLEX; - - /* The FLOW_CTRL_EN bit selects either the hardware - * automatically or the CONFIG_FLOW_CTRL manually - * controls the GMAC pause mode. - */ - if (permit_pause_to_mac) - val |= MVNETA_GMAC_AN_FLOW_CTRL_EN; - - /* Update the advertisement bits */ - mask |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; - if (phylink_test(advertising, Pause)) - val |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; - } - } else { - /* Phy or fixed speed - disable in-band AN modes */ - val = 0; - } - - old_an = an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - an = (an & ~mask) | val; - changed = old_an ^ an; - if (changed) - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, an); + /* We should never see Asym_Pause set */ + WARN_ON(phylink_test(advertising, Asym_Pause)); - /* We are only interested in the advertisement bits changing */ - return !!(changed & MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL); + return mvgmac_pcs_config(&pp->gmac, mode, interface, advertising, + permit_pause_to_mac); } static void mvneta_pcs_an_restart(struct phylink_pcs *pcs) { struct mvneta_port *pp = mvneta_pcs_to_port(pcs); - u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, - gmac_an | MVNETA_GMAC_INBAND_RESTART_AN); - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, - gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); + mvgmac_pcs_an_restart(&pp->gmac); } static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = { @@ -3994,7 +3867,6 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val; if (pp->phy_interface != interface || phylink_autoneg_inband(mode)) { @@ -4003,10 +3875,7 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, * can only change the port mode and in-band enable when the * link is down. */ - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_FORCE_LINK_PASS; - val |= MVNETA_GMAC_FORCE_LINK_DOWN; - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + mvgmac_link_force_down(&pp->gmac); } if (pp->phy_interface != interface) @@ -4028,55 +3897,8 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0); - u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2); - u32 new_ctrl4, gmac_ctrl4 = mvreg_read(pp, MVNETA_GMAC_CTRL_4); - - new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X; - new_ctrl2 = gmac_ctrl2 & ~(MVNETA_GMAC2_INBAND_AN_ENABLE | - MVNETA_GMAC2_PORT_RESET); - new_ctrl4 = gmac_ctrl4 & ~(MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE); - /* Even though it might look weird, when we're configured in - * SGMII or QSGMII mode, the RGMII bit needs to be set. - */ - new_ctrl2 |= MVNETA_GMAC2_PORT_RGMII; - - if (state->interface == PHY_INTERFACE_MODE_QSGMII || - state->interface == PHY_INTERFACE_MODE_SGMII || - phy_interface_mode_is_8023z(state->interface)) - new_ctrl2 |= MVNETA_GMAC2_PCS_ENABLE; - - if (!phylink_autoneg_inband(mode)) { - /* Phy or fixed speed - nothing to do, leave the - * configured speed, duplex and flow control as-is. - */ - } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { - /* SGMII mode receives the state from the PHY */ - new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; - } else { - /* 802.3z negotiation - only 1000base-X */ - new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; - } - - /* When at 2.5G, the link partner can send frames with shortened - * preambles. - */ - if (state->interface == PHY_INTERFACE_MODE_2500BASEX) - new_ctrl4 |= MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE; - - if (new_ctrl0 != gmac_ctrl0) - mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0); - if (new_ctrl2 != gmac_ctrl2) - mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2); - if (new_ctrl4 != gmac_ctrl4) - mvreg_write(pp, MVNETA_GMAC_CTRL_4, new_ctrl4); - - if (gmac_ctrl2 & MVNETA_GMAC2_PORT_RESET) { - while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) & - MVNETA_GMAC2_PORT_RESET) != 0) - continue; - } + mvgmac_config_mac(&pp->gmac, mode, state); } static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, @@ -4084,7 +3906,7 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val, clk; + u32 clk; /* Disable 1ms clock if not in in-band mode */ if (!phylink_autoneg_inband(mode)) { @@ -4100,45 +3922,23 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, /* Allow the link to come up if in in-band mode, otherwise the * link is forced via mac_link_down()/mac_link_up() */ - if (phylink_autoneg_inband(mode)) { - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } + if (phylink_autoneg_inband(mode)) + mvgmac_link_unforce(&pp->gmac); return 0; } -static void mvneta_set_eee(struct mvneta_port *pp, bool enable) -{ - u32 lpi_ctl1; - - lpi_ctl1 = mvreg_read(pp, MVNETA_LPI_CTRL_1); - if (enable) - lpi_ctl1 |= MVNETA_LPI_REQUEST_ENABLE; - else - lpi_ctl1 &= ~MVNETA_LPI_REQUEST_ENABLE; - mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1); -} - static void mvneta_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface) { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val; mvneta_port_down(pp); - - if (!phylink_autoneg_inband(mode)) { - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_FORCE_LINK_PASS; - val |= MVNETA_GMAC_FORCE_LINK_DOWN; - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } + mvgmac_link_down(&pp->gmac, mode); pp->eee_active = false; - mvneta_set_eee(pp, false); + mvgmac_set_eee(&pp->gmac, false); } static void mvneta_mac_link_up(struct phylink_config *config, @@ -4149,48 +3949,14 @@ static void mvneta_mac_link_up(struct phylink_config *config, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val; - - if (!phylink_autoneg_inband(mode)) { - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~(MVNETA_GMAC_FORCE_LINK_DOWN | - MVNETA_GMAC_CONFIG_MII_SPEED | - MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FLOW_CTRL | - MVNETA_GMAC_CONFIG_FULL_DUPLEX); - val |= MVNETA_GMAC_FORCE_LINK_PASS; - - if (speed == SPEED_1000 || speed == SPEED_2500) - val |= MVNETA_GMAC_CONFIG_GMII_SPEED; - else if (speed == SPEED_100) - val |= MVNETA_GMAC_CONFIG_MII_SPEED; - - if (duplex == DUPLEX_FULL) - val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; - - if (tx_pause || rx_pause) - val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; - - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } else { - /* When inband doesn't cover flow control or flow control is - * disabled, we need to manually configure it. This bit will - * only have effect if MVNETA_GMAC_AN_FLOW_CTRL_EN is unset. - */ - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_CONFIG_FLOW_CTRL; - - if (tx_pause || rx_pause) - val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; - - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } + mvgmac_link_up(&pp->gmac, mode, speed, duplex, tx_pause, rx_pause); mvneta_port_up(pp); if (phy && pp->eee_enabled) { pp->eee_active = phy_init_eee(phy, 0) >= 0; - mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled); + mvgmac_set_lpi_ts(&pp->gmac, pp->tx_lpi_timer); + mvgmac_set_eee(&pp->gmac, pp->eee_active && pp->tx_lpi_enabled); } } @@ -4964,14 +4730,11 @@ static int mvneta_ethtool_get_eee(struct net_device *dev, struct ethtool_eee *eee) { struct mvneta_port *pp = netdev_priv(dev); - u32 lpi_ctl0; - - lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0); eee->eee_enabled = pp->eee_enabled; eee->eee_active = pp->eee_active; eee->tx_lpi_enabled = pp->tx_lpi_enabled; - eee->tx_lpi_timer = (lpi_ctl0) >> 8; // * scale; + eee->tx_lpi_timer = pp->tx_lpi_timer; return phylink_ethtool_get_eee(pp->phylink, eee); } @@ -4980,22 +4743,14 @@ static int mvneta_ethtool_set_eee(struct net_device *dev, struct ethtool_eee *eee) { struct mvneta_port *pp = netdev_priv(dev); - u32 lpi_ctl0; - - /* The Armada 37x documents do not give limits for this other than - * it being an 8-bit register. */ - if (eee->tx_lpi_enabled && eee->tx_lpi_timer > 255) - return -EINVAL; - - lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0); - lpi_ctl0 &= ~(0xff << 8); - lpi_ctl0 |= eee->tx_lpi_timer << 8; - mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi_ctl0); pp->eee_enabled = eee->eee_enabled; pp->tx_lpi_enabled = eee->tx_lpi_enabled; + pp->tx_lpi_timer = eee->tx_lpi_timer; - mvneta_set_eee(pp, eee->tx_lpi_enabled && eee->eee_enabled); + mvgmac_set_eee(&pp->gmac, false); + mvgmac_set_lpi_ts(&pp->gmac, eee->tx_lpi_timer); + mvgmac_set_eee(&pp->gmac, pp->eee_active && pp->tx_lpi_enabled); return phylink_ethtool_set_eee(pp->phylink, eee); } @@ -5318,6 +5073,9 @@ static int mvneta_probe(struct platform_device *pdev) goto err_clk; } + pp->gmac.base = pp->base + MVNETA_GMAC_BASE; + pp->tx_lpi_timer = 16; + /* Alloc per-cpu port structure */ pp->ports = alloc_percpu(struct mvneta_pcpu_port); if (!pp->ports) { -- cgit From 8098317b4fdffa8b82a7e89a8fb76b142c22497b Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 23 Dec 2016 01:09:44 +0000 Subject: net: mvgmac: support different hw versions Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvgmac.c | 108 ++++++++++++++++++++++++++++++---- drivers/net/ethernet/marvell/mvgmac.h | 9 +++ drivers/net/ethernet/marvell/mvneta.c | 1 + 3 files changed, 108 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvgmac.c b/drivers/net/ethernet/marvell/mvgmac.c index 6c0b7c5c0b24..e776d4e85d3f 100644 --- a/drivers/net/ethernet/marvell/mvgmac.c +++ b/drivers/net/ethernet/marvell/mvgmac.c @@ -19,6 +19,8 @@ #include "mvgmac.h" enum { + /* N = Neta, 21 = PPV2.1, 22 = PPV2.2 */ + /* N: 0-14 21: 0,2-15 22: 0-14 */ GMAC_CTRL0_REG = 0x00, GMAC_CTRL0_PORT_ENABLE = BIT(0), GMAC_CTRL0_PORT_1000BASE_X = BIT(1), @@ -26,12 +28,21 @@ enum { GMAC_CTRL0_MAX_RX_SIZE_MASK = 0x1fff << GMAC_CTRL0_MAX_RX_SIZE_SHIFT, GMAC_CTRL0_MIB_CNTR_ENABLE = BIT(15), + /* N: 21: 1,5,6 22: */ + GMAC_CTRL1_REG = 0x04, + GMAC_CTRL1_PERIODIC_XON_ENABLE = BIT(1), + GMAC_CTRL1_GMII_LB_ENABLE = BIT(5), + GMAC_CTRL1_PCS_LB_ENABLE = BIT(6), + + /* ALL: 0,3,4,6 */ GMAC_CTRL2_REG = 0x08, GMAC_CTRL2_INBAND_AN_SGMII = BIT(0), GMAC_CTRL2_PCS_ENABLE = BIT(3), GMAC_CTRL2_PORT_RGMII = BIT(4), GMAC_CTRL2_PORT_RESET = BIT(6), + /* N:0-9,11-13 21:0,1,5-7,9,12,13 22:0-7,9-15 */ + /* 22 bit 2 - EN_PCS_AN */ GMAC_ANEG_REG = 0x0c, GMAC_ANEG_FORCE_LINK_DOWN = BIT(0), GMAC_ANEG_FORCE_LINK_PASS = BIT(1), @@ -43,9 +54,12 @@ enum { GMAC_ANEG_AN_SPEED_ENABLE = BIT(7), GMAC_ANEG_CONFIG_FLOW_CTRL = BIT(8), GMAC_ANEG_ADVERT_SYM_FLOW_CTRL = BIT(9), + GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL = BIT(10), GMAC_ANEG_AN_FLOW_CTRL_ENABLE = BIT(11), GMAC_ANEG_FULL_DUPLEX = BIT(12), GMAC_ANEG_AN_DUPLEX_ENABLE = BIT(13), + /* pp22: bit 14 - phy mode */ + /* pp22: bit 15 - choose sample tx config */ GMAC_STATUS_REG = 0x10, MVGMAC_LINK_UP = BIT(0), @@ -59,8 +73,21 @@ enum { MVGMAC_AN_COMPLETE = BIT(11), MVGMAC_SYNC_OK = BIT(14), + /* N: 21:6-13 22: */ + GMAC_FIFO_CFG1_REG = 0x1c, + GMAC_FIFO_CFG1_TX_MIN_TH_SHIFT = 6, + GMAC_FIFO_CFG1_TX_MIN_TH_MASK = 0x7f << + GMAC_FIFO_CFG1_TX_MIN_TH_SHIFT, + + /* N:1 21: 22:0,3-7 */ GMAC_CTRL4_REG = 0x90, + GMAC_CTRL4_EXT_PIN_GMII_SEL = BIT(0), GMAC_CTRL4_SHORT_PREAMBLE_ENABLE = BIT(1), + GMAC_CTRL4_FC_RX_ENABLE = BIT(3), + GMAC_CTRL4_FC_TX_ENABLE = BIT(4), + GMAC_CTRL4_DP_CLK_SEL = BIT(5), + GMAC_CTRL4_SYNC_BYPASS = BIT(6), + GMAC_CTRL4_QSGMII_BYPASS = BIT(7), GMAC_LPI_CTRL0_REG = 0xc0, GMAC_LPI_CTRL0_TS = 0xff << 8, @@ -111,6 +138,47 @@ void mvgmac_disable(struct mvgmac *gmac) } EXPORT_SYMBOL_GPL(mvgmac_disable); +int mvgmac_configure(struct mvgmac *gmac, phy_interface_t phy_mode) +{ + bool ext_pin_gmii; + u32 val; + + switch (phy_mode) { + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_SGMII: + ext_pin_gmii = false; + break; + + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + ext_pin_gmii = true; + break; + + default: + return -EINVAL; + } + + if (gmac->version == MVGMAC_PP21) { + /* Min. TX threshold must be less than minimum packet length */ + val = readl_relaxed(gmac->base + GMAC_FIFO_CFG1_REG); + val = insert(val, GMAC_FIFO_CFG1_TX_MIN_TH_MASK, 64 - 4 - 2); + writel_relaxed(val, gmac->base + GMAC_FIFO_CFG1_REG); + } else if (gmac->version == MVGMAC_PP22) { + val = readl_relaxed(gmac->base + GMAC_CTRL4_REG); + val &= ~GMAC_CTRL4_DP_CLK_SEL; + val |= GMAC_CTRL4_SYNC_BYPASS; + val = insert(val, GMAC_CTRL4_QSGMII_BYPASS, + phy_mode != PHY_INTERFACE_MODE_QSGMII); + val = insert(val, GMAC_CTRL4_EXT_PIN_GMII_SEL, ext_pin_gmii); + writel_relaxed(val, gmac->base + GMAC_CTRL4_REG); + } + + return 0; +} +EXPORT_SYMBOL_GPL(mvgmac_configure); + void mvgmac_link_unforce(struct mvgmac *gmac) { u32 val; @@ -142,7 +210,7 @@ EXPORT_SYMBOL_GPL(mvgmac_link_down); void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, bool tx_pause, bool rx_pause) { - u32 val; + u32 val, ctrl4; val = readl_relaxed(gmac->base + GMAC_ANEG_REG); val &= ~GMAC_ANEG_CONFIG_FLOW_CTRL; @@ -163,8 +231,19 @@ void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, val |= GMAC_ANEG_FULL_DUPLEX; } - if (tx_pause || rx_pause) - val |= GMAC_ANEG_CONFIG_FLOW_CTRL; + switch (gmac->version) { + case MVGMAC_NETA: + val = insert(val, GMAC_ANEG_CONFIG_FLOW_CTRL, + tx_pause || rx_pause); + break; + + case MVGMAC_PP22: + ctrl4 = readl_relaxed(gmac->base + GMAC_CTRL4_REG); + ctrl4 = insert(ctrl4, GMAC_CTRL4_FC_TX_ENABLE, tx_pause); + ctrl4 = insert(ctrl4, GMAC_CTRL4_FC_RX_ENABLE, rx_pause); + writel_relaxed(ctrl4, gmac->base + GMAC_CTRL4_REG); + break; + } writel_relaxed(val, gmac->base + GMAC_ANEG_REG); } @@ -252,6 +331,11 @@ int mvgmac_pcs_config(struct mvgmac *gmac, unsigned int mode, mask |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL; if (phylink_test(advertising, Pause)) val |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL; + if (gmac->version == MVGMAC_PP22) { + mask |= GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL; + if (phylink_test(advertising, Asym_Pause)) + val |= GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL; + } } } else { /* Phy or fixed speed - disable in-band AN modes */ @@ -265,7 +349,8 @@ int mvgmac_pcs_config(struct mvgmac *gmac, unsigned int mode, writel_relaxed(an, gmac->base + GMAC_ANEG_REG); /* We are only interested in the advertisement bits changing */ - return !!(changed & GMAC_ANEG_ADVERT_SYM_FLOW_CTRL); + return !!(changed & (GMAC_ANEG_ADVERT_SYM_FLOW_CTRL | + GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL)); } EXPORT_SYMBOL_GPL(mvgmac_pcs_config); @@ -279,7 +364,7 @@ void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, new_ctrl0 = gmac_ctrl0 & ~GMAC_CTRL0_PORT_1000BASE_X; new_ctrl2 = gmac_ctrl2 & ~(GMAC_CTRL2_INBAND_AN_SGMII | GMAC_CTRL2_PORT_RESET); - new_ctrl4 = gmac_ctrl4 & ~GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + new_ctrl4 = gmac_ctrl4; /* Even though it might look weird, when we're configured in * SGMII or QSGMII mode, the RGMII bit needs to be set. @@ -303,11 +388,14 @@ void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, new_ctrl0 |= GMAC_CTRL0_PORT_1000BASE_X; } - /* When at 2.5G, the link partner can send frames with shortened - * preambles. - */ - if (state->interface == PHY_INTERFACE_MODE_2500BASEX) - new_ctrl4 |= GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + if (gmac->version == MVGMAC_NETA) { + /* When at 2.5G, the link partner can send frames with + * shortened preambles. + */ + new_ctrl4 &= ~GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + new_ctrl4 |= GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + } if (new_ctrl0 != gmac_ctrl0) writel_relaxed(new_ctrl0, gmac->base + GMAC_CTRL0_REG); diff --git a/drivers/net/ethernet/marvell/mvgmac.h b/drivers/net/ethernet/marvell/mvgmac.h index ceccc6777237..f4111fba7258 100644 --- a/drivers/net/ethernet/marvell/mvgmac.h +++ b/drivers/net/ethernet/marvell/mvgmac.h @@ -14,13 +14,22 @@ struct phylink_link_state; */ #define MARVELL_HEADER_SIZE 2 +enum { + /* GMAC version */ + MVGMAC_NETA, + MVGMAC_PP21, + MVGMAC_PP22, +}; + struct mvgmac { void __iomem *base; + unsigned int version; }; void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size); void mvgmac_enable(struct mvgmac *gmac); void mvgmac_disable(struct mvgmac *gmac); +int mvgmac_configure(struct mvgmac *gmac, phy_interface_t phy_mode); void mvgmac_link_unforce(struct mvgmac *gmac); void mvgmac_link_force_down(struct mvgmac *gmac); void mvgmac_link_down(struct mvgmac *gmac, int mode); diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 5331f5a72ab5..b2225166f288 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -5074,6 +5074,7 @@ static int mvneta_probe(struct platform_device *pdev) } pp->gmac.base = pp->base + MVNETA_GMAC_BASE; + pp->gmac.version = MVGMAC_NETA; pp->tx_lpi_timer = 16; /* Alloc per-cpu port structure */ -- cgit