Merge branch 'sfp-copper-modules'
Russell King says: ==================== Add support for SFP+ copper modules This series adds support for Copper SFP+ modules with Clause 45 PHYs. Specifically the patches: 1. drop support for the probably never tested 100BASE-*X modules. 2. drop EEPROM ID from sfp_select_interface() 3. add more compliance code definitions from SFF-8024, renaming the existing definitions. 4. add module start/stop methods so phylink knows when a module is about to become active. The module start method is called after we have probed for a PHY on the module. 5. move start/stop of module PHY down into phylink using the new module start/stop methods. 6. add support for Clause 45 I2C accesses, tested with Methode DM7052. Other modules appear to use the same protocol, but slight differences, but I do not have those modules to test with. (if someone does, please holler!) 7. rearrange how we attach to PHYs so that we can support Clause 45 PHYs with indeterminant interface modes. (Clause 45 PHYs appear to like to change their PHY interface mode depending on the negotiated speed.) 8. add support for phylink to connect to a clause 45 PHY on a SFP module. 9. split the link_an_mode between the configured value and the currently selected mode value; some clause 45 PHYs have no capability to provide in-band negotiation. 10. split the link configuration on SFP module insertion in phylink so we can use it in other code paths. 11. delay MAC configuration for copper modules without a PHY to the module start method - after any module PHY has been probed. If the module has a PHY, then we setup the MAC when the PHY is detected. 12. the Broadcom 84881 PHY does not support in-band negotiation even though it uses SGMII and 2500BASE-X. Having the MAC operating with in-band negotiation enabled, even with AN bypass enabled, results in no link - Broadcom say that the host MAC must always be forced. 13. add support for the Broadcom 84881 PHY found on the Methode DM7052 module. 14. add support to SFP to probe for a Clause 45 PHY on copper SFP+ modules. v3: now bisectable! ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
b58e9fd483
@ -324,6 +324,12 @@ config BROADCOM_PHY
|
||||
Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464,
|
||||
BCM5481, BCM54810 and BCM5482 PHYs.
|
||||
|
||||
config BCM84881_PHY
|
||||
bool "Broadcom BCM84881 PHY"
|
||||
depends on PHYLIB=y
|
||||
---help---
|
||||
Support the Broadcom BCM84881 PHY.
|
||||
|
||||
config CICADA_PHY
|
||||
tristate "Cicada PHYs"
|
||||
---help---
|
||||
|
@ -62,6 +62,7 @@ obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o
|
||||
obj-$(CONFIG_BCM_CYGNUS_PHY) += bcm-cygnus.o
|
||||
obj-$(CONFIG_BCM_NET_PHYLIB) += bcm-phy-lib.o
|
||||
obj-$(CONFIG_BROADCOM_PHY) += broadcom.o
|
||||
obj-$(CONFIG_BCM84881_PHY) += bcm84881.o
|
||||
obj-$(CONFIG_CICADA_PHY) += cicada.o
|
||||
obj-$(CONFIG_CORTINA_PHY) += cortina.o
|
||||
obj-$(CONFIG_DAVICOM_PHY) += davicom.o
|
||||
|
269
drivers/net/phy/bcm84881.c
Normal file
269
drivers/net/phy/bcm84881.c
Normal file
@ -0,0 +1,269 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Broadcom BCM84881 NBASE-T PHY driver, as found on a SFP+ module.
|
||||
// Copyright (C) 2019 Russell King, Deep Blue Solutions Ltd.
|
||||
//
|
||||
// Like the Marvell 88x3310, the Broadcom 84881 changes its host-side
|
||||
// interface according to the operating speed between 10GBASE-R,
|
||||
// 2500BASE-X and SGMII (but unlike the 88x3310, without the control
|
||||
// word).
|
||||
//
|
||||
// This driver only supports those aspects of the PHY that I'm able to
|
||||
// observe and test with the SFP+ module, which is an incomplete subset
|
||||
// of what this PHY is able to support. For example, I only assume it
|
||||
// supports a single lane Serdes connection, but it may be that the PHY
|
||||
// is able to support more than that.
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
enum {
|
||||
MDIO_AN_C22 = 0xffe0,
|
||||
};
|
||||
|
||||
static int bcm84881_wait_init(struct phy_device *phydev)
|
||||
{
|
||||
unsigned int tries = 20;
|
||||
int ret, val;
|
||||
|
||||
do {
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1);
|
||||
if (val < 0) {
|
||||
ret = val;
|
||||
break;
|
||||
}
|
||||
if (!(val & MDIO_CTRL1_RESET)) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
if (!--tries) {
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
msleep(100);
|
||||
} while (1);
|
||||
|
||||
if (ret)
|
||||
phydev_err(phydev, "%s failed: %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcm84881_config_init(struct phy_device *phydev)
|
||||
{
|
||||
switch (phydev->interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
case PHY_INTERFACE_MODE_10GKR:
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm84881_probe(struct phy_device *phydev)
|
||||
{
|
||||
/* This driver requires PMAPMD and AN blocks */
|
||||
const u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
|
||||
|
||||
if (!phydev->is_c45 ||
|
||||
(phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm84881_get_features(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = genphy_c45_pma_read_abilities(phydev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Although the PHY sets bit 1.11.8, it does not support 10M modes */
|
||||
linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
|
||||
phydev->supported);
|
||||
linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
|
||||
phydev->supported);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm84881_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
bool changed = false;
|
||||
u32 adv;
|
||||
int ret;
|
||||
|
||||
/* Wait for the PHY to finish initialising, otherwise our
|
||||
* advertisement may be overwritten.
|
||||
*/
|
||||
ret = bcm84881_wait_init(phydev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* We don't support manual MDI control */
|
||||
phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
|
||||
|
||||
/* disabled autoneg doesn't seem to work with this PHY */
|
||||
if (phydev->autoneg == AUTONEG_DISABLE)
|
||||
return -EINVAL;
|
||||
|
||||
ret = genphy_c45_an_config_aneg(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret > 0)
|
||||
changed = true;
|
||||
|
||||
adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
|
||||
ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN,
|
||||
MDIO_AN_C22 + MII_CTRL1000,
|
||||
ADVERTISE_1000FULL | ADVERTISE_1000HALF,
|
||||
adv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret > 0)
|
||||
changed = true;
|
||||
|
||||
return genphy_c45_check_and_restart_aneg(phydev, changed);
|
||||
}
|
||||
|
||||
static int bcm84881_aneg_done(struct phy_device *phydev)
|
||||
{
|
||||
int bmsr, val;
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
bmsr = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_C22 + MII_BMSR);
|
||||
if (bmsr < 0)
|
||||
return val;
|
||||
|
||||
return !!(val & MDIO_AN_STAT1_COMPLETE) &&
|
||||
!!(bmsr & BMSR_ANEGCOMPLETE);
|
||||
}
|
||||
|
||||
static int bcm84881_read_status(struct phy_device *phydev)
|
||||
{
|
||||
unsigned int mode;
|
||||
int bmsr, val;
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val & MDIO_AN_CTRL1_RESTART) {
|
||||
phydev->link = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
bmsr = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_C22 + MII_BMSR);
|
||||
if (bmsr < 0)
|
||||
return val;
|
||||
|
||||
phydev->autoneg_complete = !!(val & MDIO_AN_STAT1_COMPLETE) &&
|
||||
!!(bmsr & BMSR_ANEGCOMPLETE);
|
||||
phydev->link = !!(val & MDIO_STAT1_LSTATUS) &&
|
||||
!!(bmsr & BMSR_LSTATUS);
|
||||
if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete)
|
||||
phydev->link = false;
|
||||
|
||||
if (!phydev->link)
|
||||
return 0;
|
||||
|
||||
linkmode_zero(phydev->lp_advertising);
|
||||
phydev->speed = SPEED_UNKNOWN;
|
||||
phydev->duplex = DUPLEX_UNKNOWN;
|
||||
phydev->pause = 0;
|
||||
phydev->asym_pause = 0;
|
||||
phydev->mdix = 0;
|
||||
|
||||
if (phydev->autoneg_complete) {
|
||||
val = genphy_c45_read_lpa(phydev);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_AN,
|
||||
MDIO_AN_C22 + MII_STAT1000);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
|
||||
|
||||
if (phydev->autoneg == AUTONEG_ENABLE)
|
||||
phy_resolve_aneg_linkmode(phydev);
|
||||
}
|
||||
|
||||
if (phydev->autoneg == AUTONEG_DISABLE) {
|
||||
/* disabled autoneg doesn't seem to work, so force the link
|
||||
* down.
|
||||
*/
|
||||
phydev->link = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the host link mode - we set the phy interface mode and
|
||||
* the speed according to this register so that downshift works.
|
||||
* We leave the duplex setting as per the resolution from the
|
||||
* above.
|
||||
*/
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x4011);
|
||||
mode = (val & 0x1e) >> 1;
|
||||
if (mode == 1 || mode == 2)
|
||||
phydev->interface = PHY_INTERFACE_MODE_SGMII;
|
||||
else if (mode == 3)
|
||||
phydev->interface = PHY_INTERFACE_MODE_10GKR;
|
||||
else if (mode == 4)
|
||||
phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
|
||||
switch (mode & 7) {
|
||||
case 1:
|
||||
phydev->speed = SPEED_100;
|
||||
break;
|
||||
case 2:
|
||||
phydev->speed = SPEED_1000;
|
||||
break;
|
||||
case 3:
|
||||
phydev->speed = SPEED_10000;
|
||||
break;
|
||||
case 4:
|
||||
phydev->speed = SPEED_2500;
|
||||
break;
|
||||
case 5:
|
||||
phydev->speed = SPEED_5000;
|
||||
break;
|
||||
}
|
||||
|
||||
return genphy_c45_read_mdix(phydev);
|
||||
}
|
||||
|
||||
static struct phy_driver bcm84881_drivers[] = {
|
||||
{
|
||||
.phy_id = 0xae025150,
|
||||
.phy_id_mask = 0xfffffff0,
|
||||
.name = "Broadcom BCM84881",
|
||||
.config_init = bcm84881_config_init,
|
||||
.probe = bcm84881_probe,
|
||||
.get_features = bcm84881_get_features,
|
||||
.config_aneg = bcm84881_config_aneg,
|
||||
.aneg_done = bcm84881_aneg_done,
|
||||
.read_status = bcm84881_read_status,
|
||||
},
|
||||
};
|
||||
|
||||
module_phy_driver(bcm84881_drivers);
|
||||
|
||||
/* FIXME: module auto-loading for Clause 45 PHYs seems non-functional */
|
||||
static struct mdio_device_id __maybe_unused bcm84881_tbl[] = {
|
||||
{ 0xae025150, 0xfffffff0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_AUTHOR("Russell King");
|
||||
MODULE_DESCRIPTION("Broadcom BCM84881 PHY driver");
|
||||
MODULE_DEVICE_TABLE(mdio, bcm84881_tbl);
|
||||
MODULE_LICENSE("GPL");
|
@ -214,7 +214,7 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
phy_interface_t iface;
|
||||
|
||||
sfp_parse_support(phydev->sfp_bus, id, support);
|
||||
iface = sfp_select_interface(phydev->sfp_bus, id, support);
|
||||
iface = sfp_select_interface(phydev->sfp_bus, support);
|
||||
|
||||
if (iface != PHY_INTERFACE_MODE_10GKR) {
|
||||
dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
|
||||
|
@ -33,17 +33,24 @@ static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg)
|
||||
{
|
||||
struct i2c_adapter *i2c = bus->priv;
|
||||
struct i2c_msg msgs[2];
|
||||
u8 data[2], dev_addr = reg;
|
||||
u8 addr[3], data[2], *p;
|
||||
int bus_addr, ret;
|
||||
|
||||
if (!i2c_mii_valid_phy_id(phy_id))
|
||||
return 0xffff;
|
||||
|
||||
p = addr;
|
||||
if (reg & MII_ADDR_C45) {
|
||||
*p++ = 0x20 | ((reg >> 16) & 31);
|
||||
*p++ = reg >> 8;
|
||||
}
|
||||
*p++ = reg;
|
||||
|
||||
bus_addr = i2c_mii_phy_addr(phy_id);
|
||||
msgs[0].addr = bus_addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = 1;
|
||||
msgs[0].buf = &dev_addr;
|
||||
msgs[0].len = p - addr;
|
||||
msgs[0].buf = addr;
|
||||
msgs[1].addr = bus_addr;
|
||||
msgs[1].flags = I2C_M_RD;
|
||||
msgs[1].len = sizeof(data);
|
||||
@ -61,18 +68,23 @@ static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
|
||||
struct i2c_adapter *i2c = bus->priv;
|
||||
struct i2c_msg msg;
|
||||
int ret;
|
||||
u8 data[3];
|
||||
u8 data[5], *p;
|
||||
|
||||
if (!i2c_mii_valid_phy_id(phy_id))
|
||||
return 0;
|
||||
|
||||
data[0] = reg;
|
||||
data[1] = val >> 8;
|
||||
data[2] = val;
|
||||
p = data;
|
||||
if (reg & MII_ADDR_C45) {
|
||||
*p++ = (reg >> 16) & 31;
|
||||
*p++ = reg >> 8;
|
||||
}
|
||||
*p++ = reg;
|
||||
*p++ = val >> 8;
|
||||
*p++ = val;
|
||||
|
||||
msg.addr = i2c_mii_phy_addr(phy_id);
|
||||
msg.flags = 0;
|
||||
msg.len = 3;
|
||||
msg.len = p - data;
|
||||
msg.buf = data;
|
||||
|
||||
ret = i2c_transfer(i2c, &msg, 1);
|
||||
|
@ -48,7 +48,8 @@ struct phylink {
|
||||
unsigned long phylink_disable_state; /* bitmask of disables */
|
||||
struct phy_device *phydev;
|
||||
phy_interface_t link_interface; /* PHY_INTERFACE_xxx */
|
||||
u8 link_an_mode; /* MLO_AN_xxx */
|
||||
u8 cfg_link_an_mode; /* MLO_AN_xxx */
|
||||
u8 cur_link_an_mode;
|
||||
u8 link_port; /* The current non-phy ethtool port */
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
|
||||
|
||||
@ -71,6 +72,9 @@ struct phylink {
|
||||
bool mac_link_dropped;
|
||||
|
||||
struct sfp_bus *sfp_bus;
|
||||
bool sfp_may_have_phy;
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
|
||||
u8 sfp_port;
|
||||
};
|
||||
|
||||
#define phylink_printk(level, pl, fmt, ...) \
|
||||
@ -256,12 +260,12 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
|
||||
|
||||
dn = fwnode_get_named_child_node(fwnode, "fixed-link");
|
||||
if (dn || fwnode_property_present(fwnode, "fixed-link"))
|
||||
pl->link_an_mode = MLO_AN_FIXED;
|
||||
pl->cfg_link_an_mode = MLO_AN_FIXED;
|
||||
fwnode_handle_put(dn);
|
||||
|
||||
if (fwnode_property_read_string(fwnode, "managed", &managed) == 0 &&
|
||||
strcmp(managed, "in-band-status") == 0) {
|
||||
if (pl->link_an_mode == MLO_AN_FIXED) {
|
||||
if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
|
||||
phylink_err(pl,
|
||||
"can't use both fixed-link and in-band-status\n");
|
||||
return -EINVAL;
|
||||
@ -273,7 +277,7 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
|
||||
phylink_set(pl->supported, Asym_Pause);
|
||||
phylink_set(pl->supported, Pause);
|
||||
pl->link_config.an_enabled = true;
|
||||
pl->link_an_mode = MLO_AN_INBAND;
|
||||
pl->cfg_link_an_mode = MLO_AN_INBAND;
|
||||
|
||||
switch (pl->link_config.interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
@ -333,14 +337,14 @@ static void phylink_mac_config(struct phylink *pl,
|
||||
{
|
||||
phylink_dbg(pl,
|
||||
"%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
|
||||
__func__, phylink_an_mode_str(pl->link_an_mode),
|
||||
__func__, phylink_an_mode_str(pl->cur_link_an_mode),
|
||||
phy_modes(state->interface),
|
||||
phy_speed_to_str(state->speed),
|
||||
phy_duplex_to_str(state->duplex),
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
|
||||
state->pause, state->link, state->an_enabled);
|
||||
|
||||
pl->ops->mac_config(pl->config, pl->link_an_mode, state);
|
||||
pl->ops->mac_config(pl->config, pl->cur_link_an_mode, state);
|
||||
}
|
||||
|
||||
static void phylink_mac_config_up(struct phylink *pl,
|
||||
@ -441,7 +445,7 @@ static void phylink_mac_link_up(struct phylink *pl,
|
||||
struct net_device *ndev = pl->netdev;
|
||||
|
||||
pl->cur_interface = link_state.interface;
|
||||
pl->ops->mac_link_up(pl->config, pl->link_an_mode,
|
||||
pl->ops->mac_link_up(pl->config, pl->cur_link_an_mode,
|
||||
pl->phy_state.interface,
|
||||
pl->phydev);
|
||||
|
||||
@ -461,7 +465,7 @@ static void phylink_mac_link_down(struct phylink *pl)
|
||||
|
||||
if (ndev)
|
||||
netif_carrier_off(ndev);
|
||||
pl->ops->mac_link_down(pl->config, pl->link_an_mode,
|
||||
pl->ops->mac_link_down(pl->config, pl->cur_link_an_mode,
|
||||
pl->cur_interface);
|
||||
phylink_info(pl, "Link is Down\n");
|
||||
}
|
||||
@ -480,7 +484,7 @@ static void phylink_resolve(struct work_struct *w)
|
||||
} else if (pl->mac_link_dropped) {
|
||||
link_state.link = false;
|
||||
} else {
|
||||
switch (pl->link_an_mode) {
|
||||
switch (pl->cur_link_an_mode) {
|
||||
case MLO_AN_PHY:
|
||||
link_state = pl->phy_state;
|
||||
phylink_resolve_flow(pl, &link_state);
|
||||
@ -648,7 +652,7 @@ struct phylink *phylink_create(struct phylink_config *config,
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (pl->link_an_mode == MLO_AN_FIXED) {
|
||||
if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
|
||||
ret = phylink_parse_fixedlink(pl, fwnode);
|
||||
if (ret < 0) {
|
||||
kfree(pl);
|
||||
@ -656,6 +660,8 @@ struct phylink *phylink_create(struct phylink_config *config,
|
||||
}
|
||||
}
|
||||
|
||||
pl->cur_link_an_mode = pl->cfg_link_an_mode;
|
||||
|
||||
ret = phylink_register_sfp(pl, fwnode);
|
||||
if (ret < 0) {
|
||||
kfree(pl);
|
||||
@ -711,7 +717,8 @@ static void phylink_phy_change(struct phy_device *phydev, bool up,
|
||||
phy_duplex_to_str(phydev->duplex));
|
||||
}
|
||||
|
||||
static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy)
|
||||
static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
struct phylink_link_state config;
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
|
||||
@ -729,7 +736,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy)
|
||||
memset(&config, 0, sizeof(config));
|
||||
linkmode_copy(supported, phy->supported);
|
||||
linkmode_copy(config.advertising, phy->advertising);
|
||||
config.interface = pl->link_config.interface;
|
||||
config.interface = interface;
|
||||
|
||||
ret = phylink_validate(pl, supported, &config);
|
||||
if (ret)
|
||||
@ -745,6 +752,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy)
|
||||
mutex_lock(&phy->lock);
|
||||
mutex_lock(&pl->state_mutex);
|
||||
pl->phydev = phy;
|
||||
pl->phy_state.interface = interface;
|
||||
linkmode_copy(pl->supported, supported);
|
||||
linkmode_copy(pl->link_config.advertising, config.advertising);
|
||||
|
||||
@ -764,28 +772,18 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __phylink_connect_phy(struct phylink *pl, struct phy_device *phy,
|
||||
phy_interface_t interface)
|
||||
static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(pl->link_an_mode == MLO_AN_FIXED ||
|
||||
(pl->link_an_mode == MLO_AN_INBAND &&
|
||||
if (WARN_ON(pl->cfg_link_an_mode == MLO_AN_FIXED ||
|
||||
(pl->cfg_link_an_mode == MLO_AN_INBAND &&
|
||||
phy_interface_mode_is_8023z(interface))))
|
||||
return -EINVAL;
|
||||
|
||||
if (pl->phydev)
|
||||
return -EBUSY;
|
||||
|
||||
ret = phy_attach_direct(pl->netdev, phy, 0, interface);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phylink_bringup_phy(pl, phy);
|
||||
if (ret)
|
||||
phy_detach(phy);
|
||||
|
||||
return ret;
|
||||
return phy_attach_direct(pl->netdev, phy, 0, interface);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -805,13 +803,23 @@ static int __phylink_connect_phy(struct phylink *pl, struct phy_device *phy,
|
||||
*/
|
||||
int phylink_connect_phy(struct phylink *pl, struct phy_device *phy)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Use PHY device/driver interface */
|
||||
if (pl->link_interface == PHY_INTERFACE_MODE_NA) {
|
||||
pl->link_interface = phy->interface;
|
||||
pl->link_config.interface = pl->link_interface;
|
||||
}
|
||||
|
||||
return __phylink_connect_phy(pl, phy, pl->link_interface);
|
||||
ret = phylink_attach_phy(pl, phy, pl->link_interface);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = phylink_bringup_phy(pl, phy, pl->link_config.interface);
|
||||
if (ret)
|
||||
phy_detach(phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phylink_connect_phy);
|
||||
|
||||
@ -835,8 +843,8 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn,
|
||||
int ret;
|
||||
|
||||
/* Fixed links and 802.3z are handled without needing a PHY */
|
||||
if (pl->link_an_mode == MLO_AN_FIXED ||
|
||||
(pl->link_an_mode == MLO_AN_INBAND &&
|
||||
if (pl->cfg_link_an_mode == MLO_AN_FIXED ||
|
||||
(pl->cfg_link_an_mode == MLO_AN_INBAND &&
|
||||
phy_interface_mode_is_8023z(pl->link_interface)))
|
||||
return 0;
|
||||
|
||||
@ -847,7 +855,7 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn,
|
||||
phy_node = of_parse_phandle(dn, "phy-device", 0);
|
||||
|
||||
if (!phy_node) {
|
||||
if (pl->link_an_mode == MLO_AN_PHY)
|
||||
if (pl->cfg_link_an_mode == MLO_AN_PHY)
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
@ -860,7 +868,7 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn,
|
||||
if (!phy_dev)
|
||||
return -ENODEV;
|
||||
|
||||
ret = phylink_bringup_phy(pl, phy_dev);
|
||||
ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface);
|
||||
if (ret)
|
||||
phy_detach(phy_dev);
|
||||
|
||||
@ -910,7 +918,7 @@ int phylink_fixed_state_cb(struct phylink *pl,
|
||||
/* It does not make sense to let the link be overriden unless we use
|
||||
* MLO_AN_FIXED
|
||||
*/
|
||||
if (pl->link_an_mode != MLO_AN_FIXED)
|
||||
if (pl->cfg_link_an_mode != MLO_AN_FIXED)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pl->state_mutex);
|
||||
@ -960,7 +968,7 @@ void phylink_start(struct phylink *pl)
|
||||
ASSERT_RTNL();
|
||||
|
||||
phylink_info(pl, "configuring for %s/%s link mode\n",
|
||||
phylink_an_mode_str(pl->link_an_mode),
|
||||
phylink_an_mode_str(pl->cur_link_an_mode),
|
||||
phy_modes(pl->link_config.interface));
|
||||
|
||||
/* Always set the carrier off */
|
||||
@ -983,7 +991,7 @@ void phylink_start(struct phylink *pl)
|
||||
clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
|
||||
phylink_run_resolve(pl);
|
||||
|
||||
if (pl->link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
|
||||
if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
|
||||
int irq = gpiod_to_irq(pl->link_gpio);
|
||||
|
||||
if (irq > 0) {
|
||||
@ -998,7 +1006,7 @@ void phylink_start(struct phylink *pl)
|
||||
if (irq <= 0)
|
||||
mod_timer(&pl->link_poll, jiffies + HZ);
|
||||
}
|
||||
if (pl->link_an_mode == MLO_AN_FIXED && pl->get_fixed_state)
|
||||
if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->get_fixed_state)
|
||||
mod_timer(&pl->link_poll, jiffies + HZ);
|
||||
if (pl->phydev)
|
||||
phy_start(pl->phydev);
|
||||
@ -1125,7 +1133,7 @@ int phylink_ethtool_ksettings_get(struct phylink *pl,
|
||||
|
||||
linkmode_copy(kset->link_modes.supported, pl->supported);
|
||||
|
||||
switch (pl->link_an_mode) {
|
||||
switch (pl->cur_link_an_mode) {
|
||||
case MLO_AN_FIXED:
|
||||
/* We are using fixed settings. Report these as the
|
||||
* current link settings - and note that these also
|
||||
@ -1197,7 +1205,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
|
||||
/* If we have a fixed link (as specified by firmware), refuse
|
||||
* to change link parameters.
|
||||
*/
|
||||
if (pl->link_an_mode == MLO_AN_FIXED &&
|
||||
if (pl->cur_link_an_mode == MLO_AN_FIXED &&
|
||||
(s->speed != pl->link_config.speed ||
|
||||
s->duplex != pl->link_config.duplex))
|
||||
return -EINVAL;
|
||||
@ -1209,7 +1217,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
|
||||
__clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising);
|
||||
} else {
|
||||
/* If we have a fixed link, refuse to enable autonegotiation */
|
||||
if (pl->link_an_mode == MLO_AN_FIXED)
|
||||
if (pl->cur_link_an_mode == MLO_AN_FIXED)
|
||||
return -EINVAL;
|
||||
|
||||
config.speed = SPEED_UNKNOWN;
|
||||
@ -1251,7 +1259,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
|
||||
* configuration. For a fixed link, this isn't able to change any
|
||||
* parameters, which just leaves inband mode.
|
||||
*/
|
||||
if (pl->link_an_mode == MLO_AN_INBAND &&
|
||||
if (pl->cur_link_an_mode == MLO_AN_INBAND &&
|
||||
!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) {
|
||||
phylink_mac_config(pl, &pl->link_config);
|
||||
phylink_mac_an_restart(pl);
|
||||
@ -1341,7 +1349,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl,
|
||||
pause->tx_pause);
|
||||
} else if (!test_bit(PHYLINK_DISABLE_STOPPED,
|
||||
&pl->phylink_disable_state)) {
|
||||
switch (pl->link_an_mode) {
|
||||
switch (pl->cur_link_an_mode) {
|
||||
case MLO_AN_FIXED:
|
||||
/* Should we allow fixed links to change against the config? */
|
||||
phylink_resolve_flow(pl, config);
|
||||
@ -1548,7 +1556,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id,
|
||||
struct phylink_link_state state;
|
||||
int val = 0xffff;
|
||||
|
||||
switch (pl->link_an_mode) {
|
||||
switch (pl->cur_link_an_mode) {
|
||||
case MLO_AN_FIXED:
|
||||
if (phy_id == 0) {
|
||||
phylink_get_fixed_state(pl, &state);
|
||||
@ -1573,7 +1581,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id,
|
||||
static int phylink_mii_write(struct phylink *pl, unsigned int phy_id,
|
||||
unsigned int reg, unsigned int val)
|
||||
{
|
||||
switch (pl->link_an_mode) {
|
||||
switch (pl->cur_link_an_mode) {
|
||||
case MLO_AN_FIXED:
|
||||
break;
|
||||
|
||||
@ -1679,25 +1687,21 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus)
|
||||
pl->netdev->sfp_bus = NULL;
|
||||
}
|
||||
|
||||
static int phylink_sfp_module_insert(void *upstream,
|
||||
const struct sfp_eeprom_id *id)
|
||||
static int phylink_sfp_config(struct phylink *pl, u8 mode,
|
||||
const unsigned long *supported,
|
||||
const unsigned long *advertising)
|
||||
{
|
||||
struct phylink *pl = upstream;
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(support1);
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
|
||||
struct phylink_link_state config;
|
||||
phy_interface_t iface;
|
||||
int ret = 0;
|
||||
bool changed;
|
||||
u8 port;
|
||||
int ret;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
sfp_parse_support(pl->sfp_bus, id, support);
|
||||
port = sfp_parse_port(pl->sfp_bus, id, support);
|
||||
linkmode_copy(support, supported);
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
linkmode_copy(config.advertising, support);
|
||||
linkmode_copy(config.advertising, advertising);
|
||||
config.interface = PHY_INTERFACE_MODE_NA;
|
||||
config.speed = SPEED_UNKNOWN;
|
||||
config.duplex = DUPLEX_UNKNOWN;
|
||||
@ -1712,9 +1716,7 @@ static int phylink_sfp_module_insert(void *upstream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
linkmode_copy(support1, support);
|
||||
|
||||
iface = sfp_select_interface(pl->sfp_bus, id, config.advertising);
|
||||
iface = sfp_select_interface(pl->sfp_bus, config.advertising);
|
||||
if (iface == PHY_INTERFACE_MODE_NA) {
|
||||
phylink_err(pl,
|
||||
"selection of interface failed, advertisement %*pb\n",
|
||||
@ -1723,18 +1725,18 @@ static int phylink_sfp_module_insert(void *upstream,
|
||||
}
|
||||
|
||||
config.interface = iface;
|
||||
linkmode_copy(support1, support);
|
||||
ret = phylink_validate(pl, support1, &config);
|
||||
if (ret) {
|
||||
phylink_err(pl, "validation of %s/%s with support %*pb failed: %d\n",
|
||||
phylink_an_mode_str(MLO_AN_INBAND),
|
||||
phylink_an_mode_str(mode),
|
||||
phy_modes(config.interface),
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, support, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n",
|
||||
phylink_an_mode_str(MLO_AN_INBAND),
|
||||
phy_modes(config.interface),
|
||||
phylink_an_mode_str(mode), phy_modes(config.interface),
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, support);
|
||||
|
||||
if (phy_interface_mode_is_8023z(iface) && pl->phydev)
|
||||
@ -1746,19 +1748,19 @@ static int phylink_sfp_module_insert(void *upstream,
|
||||
linkmode_copy(pl->link_config.advertising, config.advertising);
|
||||
}
|
||||
|
||||
if (pl->link_an_mode != MLO_AN_INBAND ||
|
||||
if (pl->cur_link_an_mode != mode ||
|
||||
pl->link_config.interface != config.interface) {
|
||||
pl->link_config.interface = config.interface;
|
||||
pl->link_an_mode = MLO_AN_INBAND;
|
||||
pl->cur_link_an_mode = mode;
|
||||
|
||||
changed = true;
|
||||
|
||||
phylink_info(pl, "switched to %s/%s link mode\n",
|
||||
phylink_an_mode_str(MLO_AN_INBAND),
|
||||
phylink_an_mode_str(mode),
|
||||
phy_modes(config.interface));
|
||||
}
|
||||
|
||||
pl->link_port = port;
|
||||
pl->link_port = pl->sfp_port;
|
||||
|
||||
if (changed && !test_bit(PHYLINK_DISABLE_STOPPED,
|
||||
&pl->phylink_disable_state))
|
||||
@ -1767,6 +1769,55 @@ static int phylink_sfp_module_insert(void *upstream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
sfp_parse_support(pl->sfp_bus, id, support);
|
||||
pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, 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);
|
||||
}
|
||||
|
||||
static int phylink_sfp_module_start(void *upstream)
|
||||
{
|
||||
struct phylink *pl = upstream;
|
||||
|
||||
/* If this SFP module has a PHY, start the PHY now. */
|
||||
if (pl->phydev) {
|
||||
phy_start(pl->phydev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the module may have a PHY but we didn't detect one we
|
||||
* need to configure the MAC here.
|
||||
*/
|
||||
if (!pl->sfp_may_have_phy)
|
||||
return 0;
|
||||
|
||||
return phylink_sfp_config(pl, MLO_AN_INBAND,
|
||||
pl->sfp_support, pl->sfp_support);
|
||||
}
|
||||
|
||||
static void phylink_sfp_module_stop(void *upstream)
|
||||
{
|
||||
struct phylink *pl = upstream;
|
||||
|
||||
/* If this SFP module has a PHY, stop it. */
|
||||
if (pl->phydev)
|
||||
phy_stop(pl->phydev);
|
||||
}
|
||||
|
||||
static void phylink_sfp_link_down(void *upstream)
|
||||
{
|
||||
struct phylink *pl = upstream;
|
||||
@ -1786,11 +1837,59 @@ static void phylink_sfp_link_up(void *upstream)
|
||||
phylink_run_resolve(pl);
|
||||
}
|
||||
|
||||
/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII
|
||||
* or 802.3z control word, so inband will not work.
|
||||
*/
|
||||
static bool phylink_phy_no_inband(struct phy_device *phy)
|
||||
{
|
||||
return phy->is_c45 &&
|
||||
(phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150;
|
||||
}
|
||||
|
||||
static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
|
||||
{
|
||||
struct phylink *pl = upstream;
|
||||
phy_interface_t interface;
|
||||
u8 mode;
|
||||
int ret;
|
||||
|
||||
return __phylink_connect_phy(upstream, phy, pl->link_config.interface);
|
||||
/*
|
||||
* This is the new way of dealing with flow control for PHYs,
|
||||
* as described by Timur Tabi in commit 529ed1275263 ("net: phy:
|
||||
* phy drivers should not set SUPPORTED_[Asym_]Pause") except
|
||||
* using our validate call to the MAC, we rely upon the MAC
|
||||
* clearing the bits from both supported and advertising fields.
|
||||
*/
|
||||
phy_support_asym_pause(phy);
|
||||
|
||||
if (phylink_phy_no_inband(phy))
|
||||
mode = MLO_AN_PHY;
|
||||
else
|
||||
mode = MLO_AN_INBAND;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Clause 45 PHYs switch their Serdes lane between several different
|
||||
* modes, normally 10GBASE-R, SGMII. Some use 2500BASE-X for 2.5G
|
||||
* speeds. We really need to know which interface modes the PHY and
|
||||
* MAC supports to properly work out which linkmodes can be supported.
|
||||
*/
|
||||
if (phy->is_c45)
|
||||
interface = PHY_INTERFACE_MODE_NA;
|
||||
|
||||
ret = phylink_bringup_phy(pl, phy, interface);
|
||||
if (ret)
|
||||
phy_detach(phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void phylink_sfp_disconnect_phy(void *upstream)
|
||||
@ -1802,6 +1901,8 @@ static const struct sfp_upstream_ops sfp_phylink_ops = {
|
||||
.attach = phylink_sfp_attach,
|
||||
.detach = phylink_sfp_detach,
|
||||
.module_insert = phylink_sfp_module_insert,
|
||||
.module_start = phylink_sfp_module_start,
|
||||
.module_stop = phylink_sfp_module_stop,
|
||||
.link_up = phylink_sfp_link_up,
|
||||
.link_down = phylink_sfp_link_down,
|
||||
.connect_phy = phylink_sfp_connect_phy,
|
||||
|
@ -103,6 +103,7 @@ static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* sfp_parse_port() - Parse the EEPROM base ID, setting the port type
|
||||
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
|
||||
@ -124,35 +125,35 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
|
||||
|
||||
/* port is the physical connector, set this from the connector field. */
|
||||
switch (id->base.connector) {
|
||||
case SFP_CONNECTOR_SC:
|
||||
case SFP_CONNECTOR_FIBERJACK:
|
||||
case SFP_CONNECTOR_LC:
|
||||
case SFP_CONNECTOR_MT_RJ:
|
||||
case SFP_CONNECTOR_MU:
|
||||
case SFP_CONNECTOR_OPTICAL_PIGTAIL:
|
||||
case SFF8024_CONNECTOR_SC:
|
||||
case SFF8024_CONNECTOR_FIBERJACK:
|
||||
case SFF8024_CONNECTOR_LC:
|
||||
case SFF8024_CONNECTOR_MT_RJ:
|
||||
case SFF8024_CONNECTOR_MU:
|
||||
case SFF8024_CONNECTOR_OPTICAL_PIGTAIL:
|
||||
case SFF8024_CONNECTOR_MPO_1X12:
|
||||
case SFF8024_CONNECTOR_MPO_2X16:
|
||||
port = PORT_FIBRE;
|
||||
break;
|
||||
|
||||
case SFP_CONNECTOR_RJ45:
|
||||
case SFF8024_CONNECTOR_RJ45:
|
||||
port = PORT_TP;
|
||||
break;
|
||||
|
||||
case SFP_CONNECTOR_COPPER_PIGTAIL:
|
||||
case SFF8024_CONNECTOR_COPPER_PIGTAIL:
|
||||
port = PORT_DA;
|
||||
break;
|
||||
|
||||
case SFP_CONNECTOR_UNSPEC:
|
||||
case SFF8024_CONNECTOR_UNSPEC:
|
||||
if (id->base.e1000_base_t) {
|
||||
port = PORT_TP;
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
case SFP_CONNECTOR_SG: /* guess */
|
||||
case SFP_CONNECTOR_MPO_1X12:
|
||||
case SFP_CONNECTOR_MPO_2X16:
|
||||
case SFP_CONNECTOR_HSSDC_II:
|
||||
case SFP_CONNECTOR_NOSEPARATE:
|
||||
case SFP_CONNECTOR_MXC_2X16:
|
||||
case SFF8024_CONNECTOR_SG: /* guess */
|
||||
case SFF8024_CONNECTOR_HSSDC_II:
|
||||
case SFF8024_CONNECTOR_NOSEPARATE:
|
||||
case SFF8024_CONNECTOR_MXC_2X16:
|
||||
port = PORT_OTHER;
|
||||
break;
|
||||
default:
|
||||
@ -178,6 +179,33 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sfp_parse_port);
|
||||
|
||||
/**
|
||||
* sfp_may_have_phy() - indicate whether the module may have a 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
|
||||
*
|
||||
* Parse the EEPROM identification given in @id, and return whether
|
||||
* this module may have a PHY.
|
||||
*/
|
||||
bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id)
|
||||
{
|
||||
if (id->base.e1000_base_t)
|
||||
return true;
|
||||
|
||||
if (id->base.phys_id != SFF8024_ID_DWDM_SFP) {
|
||||
switch (id->base.extended_cc) {
|
||||
case SFF8024_ECC_10GBASE_T_SFI:
|
||||
case SFF8024_ECC_10GBASE_T_SR:
|
||||
case SFF8024_ECC_5GBASE_T:
|
||||
case SFF8024_ECC_2_5GBASE_T:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sfp_may_have_phy);
|
||||
|
||||
/**
|
||||
* sfp_parse_support() - Parse the eeprom id for supported link modes
|
||||
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
|
||||
@ -261,22 +289,33 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
|
||||
}
|
||||
|
||||
switch (id->base.extended_cc) {
|
||||
case 0x00: /* Unspecified */
|
||||
case SFF8024_ECC_UNSPEC:
|
||||
break;
|
||||
case 0x02: /* 100Gbase-SR4 or 25Gbase-SR */
|
||||
case SFF8024_ECC_100GBASE_SR4_25GBASE_SR:
|
||||
phylink_set(modes, 100000baseSR4_Full);
|
||||
phylink_set(modes, 25000baseSR_Full);
|
||||
break;
|
||||
case 0x03: /* 100Gbase-LR4 or 25Gbase-LR */
|
||||
case 0x04: /* 100Gbase-ER4 or 25Gbase-ER */
|
||||
case SFF8024_ECC_100GBASE_LR4_25GBASE_LR:
|
||||
case SFF8024_ECC_100GBASE_ER4_25GBASE_ER:
|
||||
phylink_set(modes, 100000baseLR4_ER4_Full);
|
||||
break;
|
||||
case 0x0b: /* 100Gbase-CR4 or 25Gbase-CR CA-L */
|
||||
case 0x0c: /* 25Gbase-CR CA-S */
|
||||
case 0x0d: /* 25Gbase-CR CA-N */
|
||||
case SFF8024_ECC_100GBASE_CR4:
|
||||
phylink_set(modes, 100000baseCR4_Full);
|
||||
/* fallthrough */
|
||||
case SFF8024_ECC_25GBASE_CR_S:
|
||||
case SFF8024_ECC_25GBASE_CR_N:
|
||||
phylink_set(modes, 25000baseCR_Full);
|
||||
break;
|
||||
case SFF8024_ECC_10GBASE_T_SFI:
|
||||
case SFF8024_ECC_10GBASE_T_SR:
|
||||
phylink_set(modes, 10000baseT_Full);
|
||||
break;
|
||||
case SFF8024_ECC_5GBASE_T:
|
||||
phylink_set(modes, 5000baseT_Full);
|
||||
break;
|
||||
case SFF8024_ECC_2_5GBASE_T:
|
||||
phylink_set(modes, 2500baseT_Full);
|
||||
break;
|
||||
default:
|
||||
dev_warn(bus->sfp_dev,
|
||||
"Unknown/unsupported extended compliance code: 0x%02x\n",
|
||||
@ -301,7 +340,7 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
|
||||
*/
|
||||
if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS)) {
|
||||
/* If the encoding and bit rate allows 1000baseX */
|
||||
if (id->base.encoding == SFP_ENCODING_8B10B && br_nom &&
|
||||
if (id->base.encoding == SFF8024_ENCODING_8B10B && br_nom &&
|
||||
br_min <= 1300 && br_max >= 1200)
|
||||
phylink_set(modes, 1000baseX_Full);
|
||||
}
|
||||
@ -320,31 +359,27 @@ EXPORT_SYMBOL_GPL(sfp_parse_support);
|
||||
/**
|
||||
* sfp_select_interface() - Select appropriate phy_interface_t mode
|
||||
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
|
||||
* @id: a pointer to the module's &struct sfp_eeprom_id
|
||||
* @link_modes: ethtool link modes mask
|
||||
*
|
||||
* Derive the phy_interface_t mode for the information found in the
|
||||
* module's identifying EEPROM and the link modes mask. There is no
|
||||
* standard or defined way to derive this information, so we decide
|
||||
* based upon the link mode mask.
|
||||
* Derive the phy_interface_t mode for the SFP module from the link
|
||||
* modes mask.
|
||||
*/
|
||||
phy_interface_t sfp_select_interface(struct sfp_bus *bus,
|
||||
const struct sfp_eeprom_id *id,
|
||||
unsigned long *link_modes)
|
||||
{
|
||||
if (phylink_test(link_modes, 10000baseCR_Full) ||
|
||||
phylink_test(link_modes, 10000baseSR_Full) ||
|
||||
phylink_test(link_modes, 10000baseLR_Full) ||
|
||||
phylink_test(link_modes, 10000baseLRM_Full) ||
|
||||
phylink_test(link_modes, 10000baseER_Full))
|
||||
phylink_test(link_modes, 10000baseER_Full) ||
|
||||
phylink_test(link_modes, 10000baseT_Full))
|
||||
return PHY_INTERFACE_MODE_10GKR;
|
||||
|
||||
if (phylink_test(link_modes, 2500baseX_Full))
|
||||
return PHY_INTERFACE_MODE_2500BASEX;
|
||||
|
||||
if (id->base.e1000_base_t ||
|
||||
id->base.e100_base_lx ||
|
||||
id->base.e100_base_fx)
|
||||
if (phylink_test(link_modes, 1000baseT_Half) ||
|
||||
phylink_test(link_modes, 1000baseT_Full))
|
||||
return PHY_INTERFACE_MODE_SGMII;
|
||||
|
||||
if (phylink_test(link_modes, 1000baseX_Full))
|
||||
@ -705,6 +740,27 @@ void sfp_module_remove(struct sfp_bus *bus)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sfp_module_remove);
|
||||
|
||||
int sfp_module_start(struct sfp_bus *bus)
|
||||
{
|
||||
const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
|
||||
int ret = 0;
|
||||
|
||||
if (ops && ops->module_start)
|
||||
ret = ops->module_start(bus->upstream);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sfp_module_start);
|
||||
|
||||
void sfp_module_stop(struct sfp_bus *bus)
|
||||
{
|
||||
const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
|
||||
|
||||
if (ops && ops->module_stop)
|
||||
ops->module_stop(bus->upstream);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sfp_module_stop);
|
||||
|
||||
static void sfp_socket_clear(struct sfp_bus *bus)
|
||||
{
|
||||
bus->sfp_dev = NULL;
|
||||
|
@ -59,6 +59,7 @@ enum {
|
||||
SFP_DEV_UP,
|
||||
|
||||
SFP_S_DOWN = 0,
|
||||
SFP_S_FAIL,
|
||||
SFP_S_WAIT,
|
||||
SFP_S_INIT,
|
||||
SFP_S_INIT_TX_FAULT,
|
||||
@ -122,6 +123,7 @@ static const char *event_to_str(unsigned short event)
|
||||
|
||||
static const char * const sm_state_strings[] = {
|
||||
[SFP_S_DOWN] = "down",
|
||||
[SFP_S_FAIL] = "fail",
|
||||
[SFP_S_WAIT] = "wait",
|
||||
[SFP_S_INIT] = "init",
|
||||
[SFP_S_INIT_TX_FAULT] = "init_tx_fault",
|
||||
@ -242,7 +244,7 @@ struct sfp {
|
||||
|
||||
static bool sff_module_supported(const struct sfp_eeprom_id *id)
|
||||
{
|
||||
return id->base.phys_id == SFP_PHYS_ID_SFF &&
|
||||
return id->base.phys_id == SFF8024_ID_SFF_8472 &&
|
||||
id->base.phys_ext_id == SFP_PHYS_EXT_ID_SFP;
|
||||
}
|
||||
|
||||
@ -253,7 +255,7 @@ static const struct sff_data sff_data = {
|
||||
|
||||
static bool sfp_module_supported(const struct sfp_eeprom_id *id)
|
||||
{
|
||||
return id->base.phys_id == SFP_PHYS_ID_SFP &&
|
||||
return id->base.phys_id == SFF8024_ID_SFP &&
|
||||
id->base.phys_ext_id == SFP_PHYS_EXT_ID_SFP;
|
||||
}
|
||||
|
||||
@ -1394,19 +1396,18 @@ static void sfp_sm_mod_next(struct sfp *sfp, unsigned int state,
|
||||
|
||||
static void sfp_sm_phy_detach(struct sfp *sfp)
|
||||
{
|
||||
phy_stop(sfp->mod_phy);
|
||||
sfp_remove_phy(sfp->sfp_bus);
|
||||
phy_device_remove(sfp->mod_phy);
|
||||
phy_device_free(sfp->mod_phy);
|
||||
sfp->mod_phy = NULL;
|
||||
}
|
||||
|
||||
static void sfp_sm_probe_phy(struct sfp *sfp)
|
||||
static void sfp_sm_probe_phy(struct sfp *sfp, bool is_c45)
|
||||
{
|
||||
struct phy_device *phy;
|
||||
int err;
|
||||
|
||||
phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR);
|
||||
phy = get_phy_device(sfp->i2c_mii, SFP_PHY_ADDR, is_c45);
|
||||
if (phy == ERR_PTR(-ENODEV)) {
|
||||
dev_info(sfp->dev, "no PHY detected\n");
|
||||
return;
|
||||
@ -1416,6 +1417,13 @@ static void sfp_sm_probe_phy(struct sfp *sfp)
|
||||
return;
|
||||
}
|
||||
|
||||
err = phy_device_register(phy);
|
||||
if (err) {
|
||||
phy_device_free(phy);
|
||||
dev_err(sfp->dev, "phy_device_register failed: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
err = sfp_add_phy(sfp->sfp_bus, phy);
|
||||
if (err) {
|
||||
phy_device_remove(phy);
|
||||
@ -1425,7 +1433,6 @@ static void sfp_sm_probe_phy(struct sfp *sfp)
|
||||
}
|
||||
|
||||
sfp->mod_phy = phy;
|
||||
phy_start(phy);
|
||||
}
|
||||
|
||||
static void sfp_sm_link_up(struct sfp *sfp)
|
||||
@ -1487,21 +1494,32 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
|
||||
}
|
||||
}
|
||||
|
||||
/* Probe a SFP for a PHY device if the module supports copper - the PHY
|
||||
* normally sits at I2C bus address 0x56, and may either be a clause 22
|
||||
* or clause 45 PHY.
|
||||
*
|
||||
* Clause 22 copper SFP modules normally operate in Cisco SGMII mode with
|
||||
* negotiation enabled, but some may be in 1000base-X - which is for the
|
||||
* PHY driver to determine.
|
||||
*
|
||||
* Clause 45 copper SFP+ modules (10G) appear to switch their interface
|
||||
* mode according to the negotiated line speed.
|
||||
*/
|
||||
static void sfp_sm_probe_for_phy(struct sfp *sfp)
|
||||
{
|
||||
/* Setting the serdes link mode is guesswork: there's no
|
||||
* field in the EEPROM which indicates what mode should
|
||||
* be used.
|
||||
*
|
||||
* If it's a gigabit-only fiber module, it probably does
|
||||
* not have a PHY, so switch to 802.3z negotiation mode.
|
||||
* Otherwise, switch to SGMII mode (which is required to
|
||||
* support non-gigabit speeds) and probe for a PHY.
|
||||
*/
|
||||
if (sfp->id.base.e1000_base_t ||
|
||||
sfp->id.base.e100_base_lx ||
|
||||
sfp->id.base.e100_base_fx)
|
||||
sfp_sm_probe_phy(sfp);
|
||||
switch (sfp->id.base.extended_cc) {
|
||||
case SFF8024_ECC_10GBASE_T_SFI:
|
||||
case SFF8024_ECC_10GBASE_T_SR:
|
||||
case SFF8024_ECC_5GBASE_T:
|
||||
case SFF8024_ECC_2_5GBASE_T:
|
||||
sfp_sm_probe_phy(sfp, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (sfp->id.base.e1000_base_t)
|
||||
sfp_sm_probe_phy(sfp, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int sfp_module_parse_power(struct sfp *sfp)
|
||||
@ -1561,6 +1579,13 @@ static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable)
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* DM7052 reports as a high power module, responds to reads (with
|
||||
* all bytes 0xff) at 0x51 but does not accept writes. In any case,
|
||||
* if the bit is already set, we're already in high power mode.
|
||||
*/
|
||||
if (!!(val & BIT(0)) == enable)
|
||||
return 0;
|
||||
|
||||
if (enable)
|
||||
val |= BIT(0);
|
||||
else
|
||||
@ -1837,6 +1862,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
|
||||
if (sfp->sm_state == SFP_S_LINK_UP &&
|
||||
sfp->sm_dev_state == SFP_DEV_UP)
|
||||
sfp_sm_link_down(sfp);
|
||||
if (sfp->sm_state > SFP_S_INIT)
|
||||
sfp_module_stop(sfp->sfp_bus);
|
||||
if (sfp->mod_phy)
|
||||
sfp_sm_phy_detach(sfp);
|
||||
sfp_module_tx_disable(sfp);
|
||||
@ -1904,6 +1931,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
|
||||
* clear. Probe for the PHY and check the LOS state.
|
||||
*/
|
||||
sfp_sm_probe_for_phy(sfp);
|
||||
if (sfp_module_start(sfp->sfp_bus)) {
|
||||
sfp_sm_next(sfp, SFP_S_FAIL, 0);
|
||||
break;
|
||||
}
|
||||
sfp_sm_link_check_los(sfp);
|
||||
|
||||
/* Reset the fault retry count */
|
||||
|
@ -22,6 +22,8 @@ void sfp_link_up(struct sfp_bus *bus);
|
||||
void sfp_link_down(struct sfp_bus *bus);
|
||||
int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id);
|
||||
void sfp_module_remove(struct sfp_bus *bus);
|
||||
int sfp_module_start(struct sfp_bus *bus);
|
||||
void sfp_module_stop(struct sfp_bus *bus);
|
||||
int sfp_link_configure(struct sfp_bus *bus, const struct sfp_eeprom_id *id);
|
||||
struct sfp_bus *sfp_register_socket(struct device *dev, struct sfp *sfp,
|
||||
const struct sfp_socket_ops *ops);
|
||||
|
@ -275,6 +275,61 @@ struct sfp_diag {
|
||||
__be16 cal_v_offset;
|
||||
} __packed;
|
||||
|
||||
/* SFF8024 defined constants */
|
||||
enum {
|
||||
SFF8024_ID_UNK = 0x00,
|
||||
SFF8024_ID_SFF_8472 = 0x02,
|
||||
SFF8024_ID_SFP = 0x03,
|
||||
SFF8024_ID_DWDM_SFP = 0x0b,
|
||||
SFF8024_ID_QSFP_8438 = 0x0c,
|
||||
SFF8024_ID_QSFP_8436_8636 = 0x0d,
|
||||
SFF8024_ID_QSFP28_8636 = 0x11,
|
||||
|
||||
SFF8024_ENCODING_UNSPEC = 0x00,
|
||||
SFF8024_ENCODING_8B10B = 0x01,
|
||||
SFF8024_ENCODING_4B5B = 0x02,
|
||||
SFF8024_ENCODING_NRZ = 0x03,
|
||||
SFF8024_ENCODING_8472_MANCHESTER= 0x04,
|
||||
SFF8024_ENCODING_8472_SONET = 0x05,
|
||||
SFF8024_ENCODING_8472_64B66B = 0x06,
|
||||
SFF8024_ENCODING_8436_MANCHESTER= 0x06,
|
||||
SFF8024_ENCODING_8436_SONET = 0x04,
|
||||
SFF8024_ENCODING_8436_64B66B = 0x05,
|
||||
SFF8024_ENCODING_256B257B = 0x07,
|
||||
SFF8024_ENCODING_PAM4 = 0x08,
|
||||
|
||||
SFF8024_CONNECTOR_UNSPEC = 0x00,
|
||||
/* codes 01-05 not supportable on SFP, but some modules have single SC */
|
||||
SFF8024_CONNECTOR_SC = 0x01,
|
||||
SFF8024_CONNECTOR_FIBERJACK = 0x06,
|
||||
SFF8024_CONNECTOR_LC = 0x07,
|
||||
SFF8024_CONNECTOR_MT_RJ = 0x08,
|
||||
SFF8024_CONNECTOR_MU = 0x09,
|
||||
SFF8024_CONNECTOR_SG = 0x0a,
|
||||
SFF8024_CONNECTOR_OPTICAL_PIGTAIL= 0x0b,
|
||||
SFF8024_CONNECTOR_MPO_1X12 = 0x0c,
|
||||
SFF8024_CONNECTOR_MPO_2X16 = 0x0d,
|
||||
SFF8024_CONNECTOR_HSSDC_II = 0x20,
|
||||
SFF8024_CONNECTOR_COPPER_PIGTAIL= 0x21,
|
||||
SFF8024_CONNECTOR_RJ45 = 0x22,
|
||||
SFF8024_CONNECTOR_NOSEPARATE = 0x23,
|
||||
SFF8024_CONNECTOR_MXC_2X16 = 0x24,
|
||||
|
||||
SFF8024_ECC_UNSPEC = 0x00,
|
||||
SFF8024_ECC_100G_25GAUI_C2M_AOC = 0x01,
|
||||
SFF8024_ECC_100GBASE_SR4_25GBASE_SR = 0x02,
|
||||
SFF8024_ECC_100GBASE_LR4_25GBASE_LR = 0x03,
|
||||
SFF8024_ECC_100GBASE_ER4_25GBASE_ER = 0x04,
|
||||
SFF8024_ECC_100GBASE_SR10 = 0x05,
|
||||
SFF8024_ECC_100GBASE_CR4 = 0x0b,
|
||||
SFF8024_ECC_25GBASE_CR_S = 0x0c,
|
||||
SFF8024_ECC_25GBASE_CR_N = 0x0d,
|
||||
SFF8024_ECC_10GBASE_T_SFI = 0x16,
|
||||
SFF8024_ECC_10GBASE_T_SR = 0x1c,
|
||||
SFF8024_ECC_5GBASE_T = 0x1d,
|
||||
SFF8024_ECC_2_5GBASE_T = 0x1e,
|
||||
};
|
||||
|
||||
/* SFP EEPROM registers */
|
||||
enum {
|
||||
SFP_PHYS_ID = 0x00,
|
||||
@ -309,34 +364,7 @@ enum {
|
||||
SFP_SFF8472_COMPLIANCE = 0x5e,
|
||||
SFP_CC_EXT = 0x5f,
|
||||
|
||||
SFP_PHYS_ID_SFF = 0x02,
|
||||
SFP_PHYS_ID_SFP = 0x03,
|
||||
SFP_PHYS_EXT_ID_SFP = 0x04,
|
||||
SFP_CONNECTOR_UNSPEC = 0x00,
|
||||
/* codes 01-05 not supportable on SFP, but some modules have single SC */
|
||||
SFP_CONNECTOR_SC = 0x01,
|
||||
SFP_CONNECTOR_FIBERJACK = 0x06,
|
||||
SFP_CONNECTOR_LC = 0x07,
|
||||
SFP_CONNECTOR_MT_RJ = 0x08,
|
||||
SFP_CONNECTOR_MU = 0x09,
|
||||
SFP_CONNECTOR_SG = 0x0a,
|
||||
SFP_CONNECTOR_OPTICAL_PIGTAIL = 0x0b,
|
||||
SFP_CONNECTOR_MPO_1X12 = 0x0c,
|
||||
SFP_CONNECTOR_MPO_2X16 = 0x0d,
|
||||
SFP_CONNECTOR_HSSDC_II = 0x20,
|
||||
SFP_CONNECTOR_COPPER_PIGTAIL = 0x21,
|
||||
SFP_CONNECTOR_RJ45 = 0x22,
|
||||
SFP_CONNECTOR_NOSEPARATE = 0x23,
|
||||
SFP_CONNECTOR_MXC_2X16 = 0x24,
|
||||
SFP_ENCODING_UNSPEC = 0x00,
|
||||
SFP_ENCODING_8B10B = 0x01,
|
||||
SFP_ENCODING_4B5B = 0x02,
|
||||
SFP_ENCODING_NRZ = 0x03,
|
||||
SFP_ENCODING_8472_MANCHESTER = 0x04,
|
||||
SFP_ENCODING_8472_SONET = 0x05,
|
||||
SFP_ENCODING_8472_64B66B = 0x06,
|
||||
SFP_ENCODING_256B257B = 0x07,
|
||||
SFP_ENCODING_PAM4 = 0x08,
|
||||
SFP_OPTIONS_HIGH_POWER_LEVEL = BIT(13),
|
||||
SFP_OPTIONS_PAGING_A2 = BIT(12),
|
||||
SFP_OPTIONS_RETIMER = BIT(11),
|
||||
@ -479,6 +507,8 @@ struct sfp_bus;
|
||||
* @module_insert: called after a module has been detected to determine
|
||||
* whether the module is supported for the upstream device.
|
||||
* @module_remove: called after the module has been removed.
|
||||
* @module_start: called after the PHY probe step
|
||||
* @module_stop: called before the PHY is removed
|
||||
* @link_down: called when the link is non-operational for whatever
|
||||
* reason.
|
||||
* @link_up: called when the link is operational.
|
||||
@ -492,6 +522,8 @@ struct sfp_upstream_ops {
|
||||
void (*detach)(void *priv, struct sfp_bus *bus);
|
||||
int (*module_insert)(void *priv, const struct sfp_eeprom_id *id);
|
||||
void (*module_remove)(void *priv);
|
||||
int (*module_start)(void *priv);
|
||||
void (*module_stop)(void *priv);
|
||||
void (*link_down)(void *priv);
|
||||
void (*link_up)(void *priv);
|
||||
int (*connect_phy)(void *priv, struct phy_device *);
|
||||
@ -501,10 +533,10 @@ struct sfp_upstream_ops {
|
||||
#if IS_ENABLED(CONFIG_SFP)
|
||||
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);
|
||||
phy_interface_t sfp_select_interface(struct sfp_bus *bus,
|
||||
const struct sfp_eeprom_id *id,
|
||||
unsigned long *link_modes);
|
||||
|
||||
int sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo);
|
||||
@ -525,6 +557,12 @@ static inline int sfp_parse_port(struct sfp_bus *bus,
|
||||
return PORT_OTHER;
|
||||
}
|
||||
|
||||
static inline bool sfp_may_have_phy(struct sfp_bus *bus,
|
||||
const struct sfp_eeprom_id *id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void sfp_parse_support(struct sfp_bus *bus,
|
||||
const struct sfp_eeprom_id *id,
|
||||
unsigned long *support)
|
||||
@ -532,7 +570,6 @@ static inline void sfp_parse_support(struct sfp_bus *bus,
|
||||
}
|
||||
|
||||
static inline phy_interface_t sfp_select_interface(struct sfp_bus *bus,
|
||||
const struct sfp_eeprom_id *id,
|
||||
unsigned long *link_modes)
|
||||
{
|
||||
return PHY_INTERFACE_MODE_NA;
|
||||
|
Loading…
x
Reference in New Issue
Block a user