mirror of
https://github.com/torvalds/linux.git
synced 2026-05-24 23:22:31 +02:00
Merge branch 'RollBall-Hilink-Turris-10G-copper-SFP-support'
Marek Behún says: ==================== RollBall / Hilink / Turris 10G copper SFP support I am resurrecting my attempt to add support for RollBall / Hilink / Turris 10G copper SFPs modules. The modules contain Marvell 88X3310 PHY, which can communicate with the system via sgmii, 2500base-x, 5gbase-r, 10gbase-r or usxgmii mode. Some of the patches I've taken from Russell King's net-queue [1] (with some rebasing). The important change from my previous attempts are: - I am including the changes needed to phylink and marvell10g driver, so that the 88X3310 PHY is configured to use PHY modes supported by the host (the PHY defaults to use 10gbase-r only on host's side) - I have changed the patch that informs phylib about the interfaces supported by the host (patch 5 of this series): it now fills in the phydev->host_interfaces member only when connecting a PHY that is inside a SFP module. This may change in the future. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
7171e8a1a4
|
|
@ -3,6 +3,7 @@
|
|||
* MDIO I2C bridge
|
||||
*
|
||||
* Copyright (C) 2015-2016 Russell King
|
||||
* Copyright (C) 2021 Marek Behun
|
||||
*
|
||||
* Network PHYs can appear on I2C buses when they are part of SFP module.
|
||||
* This driver exposes these PHYs to the networking PHY code, allowing
|
||||
|
|
@ -12,6 +13,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/mdio/mdio-i2c.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/sfp.h>
|
||||
|
||||
/*
|
||||
* I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is
|
||||
|
|
@ -28,7 +30,7 @@ static unsigned int i2c_mii_phy_addr(int phy_id)
|
|||
return phy_id + 0x40;
|
||||
}
|
||||
|
||||
static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg)
|
||||
static int i2c_mii_read_default(struct mii_bus *bus, int phy_id, int reg)
|
||||
{
|
||||
struct i2c_adapter *i2c = bus->priv;
|
||||
struct i2c_msg msgs[2];
|
||||
|
|
@ -62,7 +64,8 @@ static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg)
|
|||
return data[0] << 8 | data[1];
|
||||
}
|
||||
|
||||
static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
|
||||
static int i2c_mii_write_default(struct mii_bus *bus, int phy_id, int reg,
|
||||
u16 val)
|
||||
{
|
||||
struct i2c_adapter *i2c = bus->priv;
|
||||
struct i2c_msg msg;
|
||||
|
|
@ -91,9 +94,288 @@ static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
|
|||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c)
|
||||
/* RollBall SFPs do not access internal PHY via I2C address 0x56, but
|
||||
* instead via address 0x51, when SFP page is set to 0x03 and password to
|
||||
* 0xffffffff.
|
||||
*
|
||||
* address size contents description
|
||||
* ------- ---- -------- -----------
|
||||
* 0x80 1 CMD 0x01/0x02/0x04 for write/read/done
|
||||
* 0x81 1 DEV Clause 45 device
|
||||
* 0x82 2 REG Clause 45 register
|
||||
* 0x84 2 VAL Register value
|
||||
*/
|
||||
#define ROLLBALL_PHY_I2C_ADDR 0x51
|
||||
|
||||
#define ROLLBALL_PASSWORD (SFP_VSL + 3)
|
||||
|
||||
#define ROLLBALL_CMD_ADDR 0x80
|
||||
#define ROLLBALL_DATA_ADDR 0x81
|
||||
|
||||
#define ROLLBALL_CMD_WRITE 0x01
|
||||
#define ROLLBALL_CMD_READ 0x02
|
||||
#define ROLLBALL_CMD_DONE 0x04
|
||||
|
||||
#define SFP_PAGE_ROLLBALL_MDIO 3
|
||||
|
||||
static int __i2c_transfer_err(struct i2c_adapter *i2c, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __i2c_transfer(i2c, msgs, num);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != num)
|
||||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __i2c_rollball_get_page(struct i2c_adapter *i2c, int bus_addr,
|
||||
u8 *page)
|
||||
{
|
||||
struct i2c_msg msgs[2];
|
||||
u8 addr = SFP_PAGE;
|
||||
|
||||
msgs[0].addr = bus_addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = 1;
|
||||
msgs[0].buf = &addr;
|
||||
|
||||
msgs[1].addr = bus_addr;
|
||||
msgs[1].flags = I2C_M_RD;
|
||||
msgs[1].len = 1;
|
||||
msgs[1].buf = page;
|
||||
|
||||
return __i2c_transfer_err(i2c, msgs, 2);
|
||||
}
|
||||
|
||||
static int __i2c_rollball_set_page(struct i2c_adapter *i2c, int bus_addr,
|
||||
u8 page)
|
||||
{
|
||||
struct i2c_msg msg;
|
||||
u8 buf[2];
|
||||
|
||||
buf[0] = SFP_PAGE;
|
||||
buf[1] = page;
|
||||
|
||||
msg.addr = bus_addr;
|
||||
msg.flags = 0;
|
||||
msg.len = 2;
|
||||
msg.buf = buf;
|
||||
|
||||
return __i2c_transfer_err(i2c, &msg, 1);
|
||||
}
|
||||
|
||||
/* In order to not interfere with other SFP code (which possibly may manipulate
|
||||
* SFP_PAGE), for every transfer we do this:
|
||||
* 1. lock the bus
|
||||
* 2. save content of SFP_PAGE
|
||||
* 3. set SFP_PAGE to 3
|
||||
* 4. do the transfer
|
||||
* 5. restore original SFP_PAGE
|
||||
* 6. unlock the bus
|
||||
* Note that one might think that steps 2 to 5 could be theoretically done all
|
||||
* in one call to i2c_transfer (by constructing msgs array in such a way), but
|
||||
* unfortunately tests show that this does not work :-( Changed SFP_PAGE does
|
||||
* not take into account until i2c_transfer() is done.
|
||||
*/
|
||||
static int i2c_transfer_rollball(struct i2c_adapter *i2c,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int ret, main_err = 0;
|
||||
u8 saved_page;
|
||||
|
||||
i2c_lock_bus(i2c, I2C_LOCK_SEGMENT);
|
||||
|
||||
/* save original page */
|
||||
ret = __i2c_rollball_get_page(i2c, msgs->addr, &saved_page);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
/* change to RollBall MDIO page */
|
||||
ret = __i2c_rollball_set_page(i2c, msgs->addr, SFP_PAGE_ROLLBALL_MDIO);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
/* do the transfer; we try to restore original page if this fails */
|
||||
ret = __i2c_transfer_err(i2c, msgs, num);
|
||||
if (ret)
|
||||
main_err = ret;
|
||||
|
||||
/* restore original page */
|
||||
ret = __i2c_rollball_set_page(i2c, msgs->addr, saved_page);
|
||||
|
||||
unlock:
|
||||
i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT);
|
||||
|
||||
return main_err ? : ret;
|
||||
}
|
||||
|
||||
static int i2c_rollball_mii_poll(struct mii_bus *bus, int bus_addr, u8 *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct i2c_adapter *i2c = bus->priv;
|
||||
struct i2c_msg msgs[2];
|
||||
u8 cmd_addr, tmp, *res;
|
||||
int i, ret;
|
||||
|
||||
cmd_addr = ROLLBALL_CMD_ADDR;
|
||||
|
||||
res = buf ? buf : &tmp;
|
||||
len = buf ? len : 1;
|
||||
|
||||
msgs[0].addr = bus_addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = 1;
|
||||
msgs[0].buf = &cmd_addr;
|
||||
|
||||
msgs[1].addr = bus_addr;
|
||||
msgs[1].flags = I2C_M_RD;
|
||||
msgs[1].len = len;
|
||||
msgs[1].buf = res;
|
||||
|
||||
/* By experiment it takes up to 70 ms to access a register for these
|
||||
* SFPs. Sleep 20ms between iterations and try 10 times.
|
||||
*/
|
||||
i = 10;
|
||||
do {
|
||||
msleep(20);
|
||||
|
||||
ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (*res == ROLLBALL_CMD_DONE)
|
||||
return 0;
|
||||
} while (i-- > 0);
|
||||
|
||||
dev_dbg(&bus->dev, "poll timed out\n");
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int i2c_rollball_mii_cmd(struct mii_bus *bus, int bus_addr, u8 cmd,
|
||||
u8 *data, size_t len)
|
||||
{
|
||||
struct i2c_adapter *i2c = bus->priv;
|
||||
struct i2c_msg msgs[2];
|
||||
u8 cmdbuf[2];
|
||||
|
||||
cmdbuf[0] = ROLLBALL_CMD_ADDR;
|
||||
cmdbuf[1] = cmd;
|
||||
|
||||
msgs[0].addr = bus_addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = len;
|
||||
msgs[0].buf = data;
|
||||
|
||||
msgs[1].addr = bus_addr;
|
||||
msgs[1].flags = 0;
|
||||
msgs[1].len = sizeof(cmdbuf);
|
||||
msgs[1].buf = cmdbuf;
|
||||
|
||||
return i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs));
|
||||
}
|
||||
|
||||
static int i2c_mii_read_rollball(struct mii_bus *bus, int phy_id, int reg)
|
||||
{
|
||||
u8 buf[4], res[6];
|
||||
int bus_addr, ret;
|
||||
u16 val;
|
||||
|
||||
if (!(reg & MII_ADDR_C45))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
bus_addr = i2c_mii_phy_addr(phy_id);
|
||||
if (bus_addr != ROLLBALL_PHY_I2C_ADDR)
|
||||
return 0xffff;
|
||||
|
||||
buf[0] = ROLLBALL_DATA_ADDR;
|
||||
buf[1] = (reg >> 16) & 0x1f;
|
||||
buf[2] = (reg >> 8) & 0xff;
|
||||
buf[3] = reg & 0xff;
|
||||
|
||||
ret = i2c_rollball_mii_cmd(bus, bus_addr, ROLLBALL_CMD_READ, buf,
|
||||
sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_rollball_mii_poll(bus, bus_addr, res, sizeof(res));
|
||||
if (ret == -ETIMEDOUT)
|
||||
return 0xffff;
|
||||
else if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = res[4] << 8 | res[5];
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int i2c_mii_write_rollball(struct mii_bus *bus, int phy_id, int reg,
|
||||
u16 val)
|
||||
{
|
||||
int bus_addr, ret;
|
||||
u8 buf[6];
|
||||
|
||||
if (!(reg & MII_ADDR_C45))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
bus_addr = i2c_mii_phy_addr(phy_id);
|
||||
if (bus_addr != ROLLBALL_PHY_I2C_ADDR)
|
||||
return 0;
|
||||
|
||||
buf[0] = ROLLBALL_DATA_ADDR;
|
||||
buf[1] = (reg >> 16) & 0x1f;
|
||||
buf[2] = (reg >> 8) & 0xff;
|
||||
buf[3] = reg & 0xff;
|
||||
buf[4] = val >> 8;
|
||||
buf[5] = val & 0xff;
|
||||
|
||||
ret = i2c_rollball_mii_cmd(bus, bus_addr, ROLLBALL_CMD_WRITE, buf,
|
||||
sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_rollball_mii_poll(bus, bus_addr, NULL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_mii_init_rollball(struct i2c_adapter *i2c)
|
||||
{
|
||||
struct i2c_msg msg;
|
||||
u8 pw[5];
|
||||
int ret;
|
||||
|
||||
pw[0] = ROLLBALL_PASSWORD;
|
||||
pw[1] = 0xff;
|
||||
pw[2] = 0xff;
|
||||
pw[3] = 0xff;
|
||||
pw[4] = 0xff;
|
||||
|
||||
msg.addr = ROLLBALL_PHY_I2C_ADDR;
|
||||
msg.flags = 0;
|
||||
msg.len = sizeof(pw);
|
||||
msg.buf = pw;
|
||||
|
||||
ret = i2c_transfer(i2c, &msg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 1)
|
||||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
|
||||
enum mdio_i2c_proto protocol)
|
||||
{
|
||||
struct mii_bus *mii;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
|
@ -104,10 +386,28 @@ struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c)
|
|||
|
||||
snprintf(mii->id, MII_BUS_ID_SIZE, "i2c:%s", dev_name(parent));
|
||||
mii->parent = parent;
|
||||
mii->read = i2c_mii_read;
|
||||
mii->write = i2c_mii_write;
|
||||
mii->priv = i2c;
|
||||
|
||||
switch (protocol) {
|
||||
case MDIO_I2C_ROLLBALL:
|
||||
ret = i2c_mii_init_rollball(i2c);
|
||||
if (ret < 0) {
|
||||
dev_err(parent,
|
||||
"Cannot initialize RollBall MDIO I2C protocol: %d\n",
|
||||
ret);
|
||||
mdiobus_free(mii);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
mii->read = i2c_mii_read_rollball;
|
||||
mii->write = i2c_mii_write_rollball;
|
||||
break;
|
||||
default:
|
||||
mii->read = i2c_mii_read_default;
|
||||
mii->write = i2c_mii_write_default;
|
||||
break;
|
||||
}
|
||||
|
||||
return mii;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mdio_i2c_alloc);
|
||||
|
|
|
|||
|
|
@ -676,6 +676,7 @@ static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
|||
struct phy_device *phydev = upstream;
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
|
||||
DECLARE_PHY_INTERFACE_MASK(interfaces);
|
||||
phy_interface_t iface;
|
||||
|
||||
linkmode_zero(phy_support);
|
||||
|
|
@ -686,7 +687,7 @@ static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
|||
phylink_set(phy_support, Asym_Pause);
|
||||
|
||||
linkmode_zero(sfp_support);
|
||||
sfp_parse_support(phydev->sfp_bus, id, sfp_support);
|
||||
sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
|
||||
/* Some modules support 10G modes as well as others we support.
|
||||
* Mask out non-supported modes so the correct interface is picked.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -478,6 +478,7 @@ static int mv2222_config_init(struct phy_device *phydev)
|
|||
|
||||
static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
{
|
||||
DECLARE_PHY_INTERFACE_MASK(interfaces);
|
||||
struct phy_device *phydev = upstream;
|
||||
phy_interface_t sfp_interface;
|
||||
struct mv2222_data *priv;
|
||||
|
|
@ -489,7 +490,7 @@ static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
|||
priv = (struct mv2222_data *)phydev->priv;
|
||||
dev = &phydev->mdio.dev;
|
||||
|
||||
sfp_parse_support(phydev->sfp_bus, id, sfp_supported);
|
||||
sfp_parse_support(phydev->sfp_bus, id, sfp_supported, interfaces);
|
||||
phydev->port = sfp_parse_port(phydev->sfp_bus, id, sfp_supported);
|
||||
sfp_interface = sfp_select_interface(phydev->sfp_bus, sfp_supported);
|
||||
|
||||
|
|
|
|||
|
|
@ -2845,6 +2845,7 @@ static int marvell_probe(struct phy_device *phydev)
|
|||
|
||||
static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
{
|
||||
DECLARE_PHY_INTERFACE_MASK(interfaces);
|
||||
struct phy_device *phydev = upstream;
|
||||
phy_interface_t interface;
|
||||
struct device *dev;
|
||||
|
|
@ -2856,7 +2857,7 @@ static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
|||
|
||||
dev = &phydev->mdio.dev;
|
||||
|
||||
sfp_parse_support(phydev->sfp_bus, id, supported);
|
||||
sfp_parse_support(phydev->sfp_bus, id, supported, interfaces);
|
||||
interface = sfp_select_interface(phydev->sfp_bus, supported);
|
||||
|
||||
dev_info(dev, "%s SFP module inserted\n", phy_modes(interface));
|
||||
|
|
|
|||
|
|
@ -96,6 +96,11 @@ enum {
|
|||
MV_PCS_PORT_INFO_NPORTS_MASK = 0x0380,
|
||||
MV_PCS_PORT_INFO_NPORTS_SHIFT = 7,
|
||||
|
||||
/* SerDes reinitialization 88E21X0 */
|
||||
MV_AN_21X0_SERDES_CTRL2 = 0x800f,
|
||||
MV_AN_21X0_SERDES_CTRL2_AUTO_INIT_DIS = BIT(13),
|
||||
MV_AN_21X0_SERDES_CTRL2_RUN_INIT = BIT(15),
|
||||
|
||||
/* These registers appear at 0x800X and 0xa00X - the 0xa00X control
|
||||
* registers appear to set themselves to the 0x800X when AN is
|
||||
* restarted, but status registers appear readable from either.
|
||||
|
|
@ -117,16 +122,16 @@ enum {
|
|||
MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN = 0x5,
|
||||
MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH = 0x6,
|
||||
MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII = 0x7,
|
||||
MV_V2_PORT_INTR_STS = 0xf040,
|
||||
MV_V2_PORT_INTR_MASK = 0xf043,
|
||||
MV_V2_PORT_INTR_STS_WOL_EN = BIT(8),
|
||||
MV_V2_MAGIC_PKT_WORD0 = 0xf06b,
|
||||
MV_V2_MAGIC_PKT_WORD1 = 0xf06c,
|
||||
MV_V2_MAGIC_PKT_WORD2 = 0xf06d,
|
||||
MV_V2_PORT_INTR_STS = 0xf040,
|
||||
MV_V2_PORT_INTR_MASK = 0xf043,
|
||||
MV_V2_PORT_INTR_STS_WOL_EN = BIT(8),
|
||||
MV_V2_MAGIC_PKT_WORD0 = 0xf06b,
|
||||
MV_V2_MAGIC_PKT_WORD1 = 0xf06c,
|
||||
MV_V2_MAGIC_PKT_WORD2 = 0xf06d,
|
||||
/* Wake on LAN registers */
|
||||
MV_V2_WOL_CTRL = 0xf06e,
|
||||
MV_V2_WOL_CTRL_CLEAR_STS = BIT(15),
|
||||
MV_V2_WOL_CTRL_MAGIC_PKT_EN = BIT(0),
|
||||
MV_V2_WOL_CTRL = 0xf06e,
|
||||
MV_V2_WOL_CTRL_CLEAR_STS = BIT(15),
|
||||
MV_V2_WOL_CTRL_MAGIC_PKT_EN = BIT(0),
|
||||
/* Temperature control/read registers (88X3310 only) */
|
||||
MV_V2_TEMP_CTRL = 0xf08a,
|
||||
MV_V2_TEMP_CTRL_MASK = 0xc000,
|
||||
|
|
@ -140,6 +145,8 @@ struct mv3310_chip {
|
|||
bool (*has_downshift)(struct phy_device *phydev);
|
||||
void (*init_supported_interfaces)(unsigned long *mask);
|
||||
int (*get_mactype)(struct phy_device *phydev);
|
||||
int (*set_mactype)(struct phy_device *phydev, int mactype);
|
||||
int (*select_mactype)(unsigned long *interfaces);
|
||||
int (*init_interface)(struct phy_device *phydev, int mactype);
|
||||
|
||||
#ifdef CONFIG_HWMON
|
||||
|
|
@ -466,9 +473,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) {
|
||||
|
|
@ -593,6 +601,49 @@ static int mv2110_get_mactype(struct phy_device *phydev)
|
|||
return mactype & MV_PMA_21X0_PORT_CTRL_MACTYPE_MASK;
|
||||
}
|
||||
|
||||
static int mv2110_set_mactype(struct phy_device *phydev, int mactype)
|
||||
{
|
||||
int err, val;
|
||||
|
||||
mactype &= MV_PMA_21X0_PORT_CTRL_MACTYPE_MASK;
|
||||
err = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_21X0_PORT_CTRL,
|
||||
MV_PMA_21X0_PORT_CTRL_SWRST |
|
||||
MV_PMA_21X0_PORT_CTRL_MACTYPE_MASK,
|
||||
MV_PMA_21X0_PORT_CTRL_SWRST | mactype);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = phy_set_bits_mmd(phydev, MDIO_MMD_AN, MV_AN_21X0_SERDES_CTRL2,
|
||||
MV_AN_21X0_SERDES_CTRL2_AUTO_INIT_DIS |
|
||||
MV_AN_21X0_SERDES_CTRL2_RUN_INIT);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_AN,
|
||||
MV_AN_21X0_SERDES_CTRL2, val,
|
||||
!(val &
|
||||
MV_AN_21X0_SERDES_CTRL2_RUN_INIT),
|
||||
5000, 100000, true);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, MV_AN_21X0_SERDES_CTRL2,
|
||||
MV_AN_21X0_SERDES_CTRL2_AUTO_INIT_DIS);
|
||||
}
|
||||
|
||||
static int mv2110_select_mactype(unsigned long *interfaces)
|
||||
{
|
||||
if (test_bit(PHY_INTERFACE_MODE_USXGMII, interfaces))
|
||||
return MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII;
|
||||
else if (test_bit(PHY_INTERFACE_MODE_SGMII, interfaces) &&
|
||||
!test_bit(PHY_INTERFACE_MODE_10GBASER, interfaces))
|
||||
return MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER;
|
||||
else if (test_bit(PHY_INTERFACE_MODE_10GBASER, interfaces))
|
||||
return MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int mv3310_get_mactype(struct phy_device *phydev)
|
||||
{
|
||||
int mactype;
|
||||
|
|
@ -604,6 +655,46 @@ static int mv3310_get_mactype(struct phy_device *phydev)
|
|||
return mactype & MV_V2_33X0_PORT_CTRL_MACTYPE_MASK;
|
||||
}
|
||||
|
||||
static int mv3310_set_mactype(struct phy_device *phydev, int mactype)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mactype &= MV_V2_33X0_PORT_CTRL_MACTYPE_MASK;
|
||||
ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL,
|
||||
MV_V2_33X0_PORT_CTRL_MACTYPE_MASK,
|
||||
mactype);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
|
||||
return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL,
|
||||
MV_V2_33X0_PORT_CTRL_SWRST);
|
||||
}
|
||||
|
||||
static int mv3310_select_mactype(unsigned long *interfaces)
|
||||
{
|
||||
if (test_bit(PHY_INTERFACE_MODE_USXGMII, interfaces))
|
||||
return MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII;
|
||||
else if (test_bit(PHY_INTERFACE_MODE_SGMII, interfaces) &&
|
||||
test_bit(PHY_INTERFACE_MODE_10GBASER, interfaces))
|
||||
return MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER;
|
||||
else if (test_bit(PHY_INTERFACE_MODE_SGMII, interfaces) &&
|
||||
test_bit(PHY_INTERFACE_MODE_RXAUI, interfaces))
|
||||
return MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI;
|
||||
else if (test_bit(PHY_INTERFACE_MODE_SGMII, interfaces) &&
|
||||
test_bit(PHY_INTERFACE_MODE_XAUI, interfaces))
|
||||
return MV_V2_3310_PORT_CTRL_MACTYPE_XAUI;
|
||||
else if (test_bit(PHY_INTERFACE_MODE_10GBASER, interfaces))
|
||||
return MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH;
|
||||
else if (test_bit(PHY_INTERFACE_MODE_RXAUI, interfaces))
|
||||
return MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH;
|
||||
else if (test_bit(PHY_INTERFACE_MODE_XAUI, interfaces))
|
||||
return MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH;
|
||||
else if (test_bit(PHY_INTERFACE_MODE_SGMII, interfaces))
|
||||
return MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int mv2110_init_interface(struct phy_device *phydev, int mactype)
|
||||
{
|
||||
struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
|
||||
|
|
@ -687,6 +778,20 @@ static int mv3310_config_init(struct phy_device *phydev)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
/* If host provided host supported interface modes, try to select the
|
||||
* best one
|
||||
*/
|
||||
if (!phy_interface_empty(phydev->host_interfaces)) {
|
||||
mactype = chip->select_mactype(phydev->host_interfaces);
|
||||
if (mactype >= 0) {
|
||||
phydev_info(phydev, "Changing MACTYPE to %i\n",
|
||||
mactype);
|
||||
err = chip->set_mactype(phydev, mactype);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
mactype = chip->get_mactype(phydev);
|
||||
if (mactype < 0)
|
||||
return mactype;
|
||||
|
|
@ -1049,6 +1154,8 @@ static const struct mv3310_chip mv3310_type = {
|
|||
.has_downshift = mv3310_has_downshift,
|
||||
.init_supported_interfaces = mv3310_init_supported_interfaces,
|
||||
.get_mactype = mv3310_get_mactype,
|
||||
.set_mactype = mv3310_set_mactype,
|
||||
.select_mactype = mv3310_select_mactype,
|
||||
.init_interface = mv3310_init_interface,
|
||||
|
||||
#ifdef CONFIG_HWMON
|
||||
|
|
@ -1060,6 +1167,8 @@ static const struct mv3310_chip mv3340_type = {
|
|||
.has_downshift = mv3310_has_downshift,
|
||||
.init_supported_interfaces = mv3340_init_supported_interfaces,
|
||||
.get_mactype = mv3310_get_mactype,
|
||||
.set_mactype = mv3310_set_mactype,
|
||||
.select_mactype = mv3310_select_mactype,
|
||||
.init_interface = mv3340_init_interface,
|
||||
|
||||
#ifdef CONFIG_HWMON
|
||||
|
|
@ -1070,6 +1179,8 @@ static const struct mv3310_chip mv3340_type = {
|
|||
static const struct mv3310_chip mv2110_type = {
|
||||
.init_supported_interfaces = mv2110_init_supported_interfaces,
|
||||
.get_mactype = mv2110_get_mactype,
|
||||
.set_mactype = mv2110_set_mactype,
|
||||
.select_mactype = mv2110_select_mactype,
|
||||
.init_interface = mv2110_init_interface,
|
||||
|
||||
#ifdef CONFIG_HWMON
|
||||
|
|
@ -1080,6 +1191,8 @@ static const struct mv3310_chip mv2110_type = {
|
|||
static const struct mv3310_chip mv2111_type = {
|
||||
.init_supported_interfaces = mv2111_init_supported_interfaces,
|
||||
.get_mactype = mv2110_get_mactype,
|
||||
.set_mactype = mv2110_set_mactype,
|
||||
.select_mactype = mv2110_select_mactype,
|
||||
.init_interface = mv2110_init_interface,
|
||||
|
||||
#ifdef CONFIG_HWMON
|
||||
|
|
|
|||
|
|
@ -77,6 +77,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;
|
||||
};
|
||||
|
|
@ -637,8 +638,9 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl,
|
|||
return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
|
||||
struct phylink_link_state *state)
|
||||
static int phylink_validate_mask(struct phylink *pl, unsigned long *supported,
|
||||
struct phylink_link_state *state,
|
||||
const unsigned long *interfaces)
|
||||
{
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, };
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, };
|
||||
|
|
@ -647,7 +649,7 @@ static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
|
|||
int intf;
|
||||
|
||||
for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) {
|
||||
if (test_bit(intf, pl->config->supported_interfaces)) {
|
||||
if (test_bit(intf, interfaces)) {
|
||||
linkmode_copy(s, supported);
|
||||
|
||||
t = *state;
|
||||
|
|
@ -668,12 +670,14 @@ static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
|
|||
static int phylink_validate(struct phylink *pl, unsigned long *supported,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
if (!phy_interface_empty(pl->config->supported_interfaces)) {
|
||||
if (state->interface == PHY_INTERFACE_MODE_NA)
|
||||
return phylink_validate_any(pl, supported, state);
|
||||
const unsigned long *interfaces = pl->config->supported_interfaces;
|
||||
|
||||
if (!test_bit(state->interface,
|
||||
pl->config->supported_interfaces))
|
||||
if (!phy_interface_empty(interfaces)) {
|
||||
if (state->interface == PHY_INTERFACE_MODE_NA)
|
||||
return phylink_validate_mask(pl, supported, state,
|
||||
interfaces);
|
||||
|
||||
if (!test_bit(state->interface, interfaces))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -1665,7 +1669,7 @@ static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,
|
|||
{
|
||||
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))))
|
||||
phy_interface_mode_is_8023z(interface) && !pl->sfp_bus)))
|
||||
return -EINVAL;
|
||||
|
||||
if (pl->phydev)
|
||||
|
|
@ -2799,21 +2803,85 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus)
|
|||
pl->netdev->sfp_bus = NULL;
|
||||
}
|
||||
|
||||
static int phylink_sfp_config(struct phylink *pl, u8 mode,
|
||||
const unsigned long *supported,
|
||||
const unsigned long *advertising)
|
||||
static const phy_interface_t phylink_sfp_interface_preference[] = {
|
||||
PHY_INTERFACE_MODE_25GBASER,
|
||||
PHY_INTERFACE_MODE_USXGMII,
|
||||
PHY_INTERFACE_MODE_10GBASER,
|
||||
PHY_INTERFACE_MODE_5GBASER,
|
||||
PHY_INTERFACE_MODE_2500BASEX,
|
||||
PHY_INTERFACE_MODE_SGMII,
|
||||
PHY_INTERFACE_MODE_1000BASEX,
|
||||
PHY_INTERFACE_MODE_100BASEX,
|
||||
};
|
||||
|
||||
static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
|
||||
|
||||
static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl,
|
||||
const unsigned long *intf)
|
||||
{
|
||||
phy_interface_t interface;
|
||||
size_t i;
|
||||
|
||||
interface = PHY_INTERFACE_MODE_NA;
|
||||
for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++)
|
||||
if (test_bit(phylink_sfp_interface_preference[i], intf)) {
|
||||
interface = phylink_sfp_interface_preference[i];
|
||||
break;
|
||||
}
|
||||
|
||||
return interface;
|
||||
}
|
||||
|
||||
static void phylink_sfp_set_config(struct phylink *pl, u8 mode,
|
||||
unsigned long *supported,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n",
|
||||
phylink_an_mode_str(mode), phy_modes(state->interface),
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, supported);
|
||||
|
||||
if (!linkmode_equal(pl->supported, supported)) {
|
||||
linkmode_copy(pl->supported, supported);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!linkmode_equal(pl->link_config.advertising, state->advertising)) {
|
||||
linkmode_copy(pl->link_config.advertising, state->advertising);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (pl->cur_link_an_mode != mode ||
|
||||
pl->link_config.interface != state->interface) {
|
||||
pl->cur_link_an_mode = mode;
|
||||
pl->link_config.interface = state->interface;
|
||||
|
||||
changed = true;
|
||||
|
||||
phylink_info(pl, "switched to %s/%s link mode\n",
|
||||
phylink_an_mode_str(mode),
|
||||
phy_modes(state->interface));
|
||||
}
|
||||
|
||||
if (changed && !test_bit(PHYLINK_DISABLE_STOPPED,
|
||||
&pl->phylink_disable_state))
|
||||
phylink_mac_initial_config(pl, false);
|
||||
}
|
||||
|
||||
static int phylink_sfp_config_phy(struct phylink *pl, u8 mode,
|
||||
struct phy_device *phy)
|
||||
{
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(support1);
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
|
||||
struct phylink_link_state config;
|
||||
phy_interface_t iface;
|
||||
bool changed;
|
||||
int ret;
|
||||
|
||||
linkmode_copy(support, supported);
|
||||
linkmode_copy(support, phy->supported);
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
linkmode_copy(config.advertising, advertising);
|
||||
linkmode_copy(config.advertising, phy->advertising);
|
||||
config.interface = PHY_INTERFACE_MODE_NA;
|
||||
config.speed = SPEED_UNKNOWN;
|
||||
config.duplex = DUPLEX_UNKNOWN;
|
||||
|
|
@ -2850,60 +2918,100 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode,
|
|||
return ret;
|
||||
}
|
||||
|
||||
phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n",
|
||||
phylink_an_mode_str(mode), phy_modes(config.interface),
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, support);
|
||||
pl->link_port = pl->sfp_port;
|
||||
|
||||
if (phy_interface_mode_is_8023z(iface) && pl->phydev)
|
||||
phylink_sfp_set_config(pl, mode, support, &config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phylink_sfp_config_optical(struct phylink *pl)
|
||||
{
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
|
||||
DECLARE_PHY_INTERFACE_MASK(interfaces);
|
||||
struct phylink_link_state config;
|
||||
phy_interface_t interface;
|
||||
int ret;
|
||||
|
||||
phylink_dbg(pl, "optical SFP: interfaces=[mac=%*pbl, sfp=%*pbl]\n",
|
||||
(int)PHY_INTERFACE_MODE_MAX,
|
||||
pl->config->supported_interfaces,
|
||||
(int)PHY_INTERFACE_MODE_MAX,
|
||||
pl->sfp_interfaces);
|
||||
|
||||
/* Find the union of the supported interfaces by the PCS/MAC and
|
||||
* the SFP module.
|
||||
*/
|
||||
phy_interface_and(interfaces, pl->config->supported_interfaces,
|
||||
pl->sfp_interfaces);
|
||||
if (phy_interface_empty(interfaces)) {
|
||||
phylink_err(pl, "unsupported SFP module: no common interface modes\n");
|
||||
return -EINVAL;
|
||||
|
||||
changed = !linkmode_equal(pl->supported, support) ||
|
||||
!linkmode_equal(pl->link_config.advertising,
|
||||
config.advertising);
|
||||
if (changed) {
|
||||
linkmode_copy(pl->supported, support);
|
||||
linkmode_copy(pl->link_config.advertising, config.advertising);
|
||||
}
|
||||
|
||||
if (pl->cur_link_an_mode != mode ||
|
||||
pl->link_config.interface != config.interface) {
|
||||
pl->link_config.interface = config.interface;
|
||||
pl->cur_link_an_mode = mode;
|
||||
memset(&config, 0, sizeof(config));
|
||||
linkmode_copy(support, pl->sfp_support);
|
||||
linkmode_copy(config.advertising, pl->sfp_support);
|
||||
config.speed = SPEED_UNKNOWN;
|
||||
config.duplex = DUPLEX_UNKNOWN;
|
||||
config.pause = MLO_PAUSE_AN;
|
||||
config.an_enabled = true;
|
||||
|
||||
changed = true;
|
||||
/* For all the interfaces that are supported, reduce the sfp_support
|
||||
* mask to only those link modes that can be supported.
|
||||
*/
|
||||
ret = phylink_validate_mask(pl, pl->sfp_support, &config, interfaces);
|
||||
if (ret) {
|
||||
phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n",
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, support);
|
||||
return ret;
|
||||
}
|
||||
|
||||
phylink_info(pl, "switched to %s/%s link mode\n",
|
||||
phylink_an_mode_str(mode),
|
||||
phy_modes(config.interface));
|
||||
interface = phylink_choose_sfp_interface(pl, interfaces);
|
||||
if (interface == PHY_INTERFACE_MODE_NA) {
|
||||
phylink_err(pl, "failed to select SFP interface\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phylink_dbg(pl, "optical SFP: chosen %s interface\n",
|
||||
phy_modes(interface));
|
||||
|
||||
config.interface = interface;
|
||||
|
||||
/* Ignore errors if we're expecting a PHY to attach later */
|
||||
ret = phylink_validate(pl, support, &config);
|
||||
if (ret) {
|
||||
phylink_err(pl, "validation with support %*pb failed: %pe\n",
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS, support,
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
pl->link_port = pl->sfp_port;
|
||||
|
||||
if (changed && !test_bit(PHYLINK_DISABLE_STOPPED,
|
||||
&pl->phylink_disable_state))
|
||||
phylink_mac_initial_config(pl, false);
|
||||
phylink_sfp_set_config(pl, MLO_AN_INBAND, pl->sfp_support, &config);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
linkmode_zero(pl->sfp_support);
|
||||
phy_interface_zero(pl->sfp_interfaces);
|
||||
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_optical(pl);
|
||||
}
|
||||
|
||||
static int phylink_sfp_module_start(void *upstream)
|
||||
|
|
@ -2922,8 +3030,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_optical(pl);
|
||||
}
|
||||
|
||||
static void phylink_sfp_module_stop(void *upstream)
|
||||
|
|
@ -2983,8 +3090,12 @@ 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);
|
||||
ret = phylink_sfp_config_phy(pl, mode, phy);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -3336,4 +3447,15 @@ 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)
|
||||
{
|
||||
for (int 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");
|
||||
|
|
|
|||
|
|
@ -139,12 +139,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, };
|
||||
|
|
@ -171,54 +173,81 @@ 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)
|
||||
if (id->base.e100_base_fx || id->base.e100_base_lx) {
|
||||
phylink_set(modes, 100baseFX_Full);
|
||||
if ((id->base.e_base_px || id->base.e_base_bx10) && br_nom == 100)
|
||||
__set_bit(PHY_INTERFACE_MODE_100BASEX, interfaces);
|
||||
}
|
||||
if ((id->base.e_base_px || id->base.e_base_bx10) && br_nom == 100) {
|
||||
phylink_set(modes, 100baseFX_Full);
|
||||
__set_bit(PHY_INTERFACE_MODE_100BASEX, interfaces);
|
||||
}
|
||||
|
||||
/* For active or passive cables, select the link modes
|
||||
* based on the bit rates and the cable compliance bytes.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -243,12 +272,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,
|
||||
|
|
@ -261,10 +292,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
|
||||
|
|
@ -277,14 +312,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)
|
||||
bus->sfp_quirk->modes(id, modes);
|
||||
bus->sfp_quirk->modes(id, modes, interfaces);
|
||||
|
||||
linkmode_or(support, support, modes);
|
||||
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ static const enum gpiod_flags gpio_flags[] = {
|
|||
* on board (for a copper SFP) time to initialise.
|
||||
*/
|
||||
#define T_WAIT msecs_to_jiffies(50)
|
||||
#define T_WAIT_ROLLBALL msecs_to_jiffies(25000)
|
||||
#define T_START_UP msecs_to_jiffies(300)
|
||||
#define T_START_UP_BAD_GPON msecs_to_jiffies(60000)
|
||||
|
||||
|
|
@ -205,8 +206,11 @@ static const enum gpiod_flags gpio_flags[] = {
|
|||
|
||||
/* SFP modules appear to always have their PHY configured for bus address
|
||||
* 0x56 (which with mdio-i2c, translates to a PHY address of 22).
|
||||
* RollBall SFPs access phy via SFP Enhanced Digital Diagnostic Interface
|
||||
* via address 0x51 (mdio-i2c will use RollBall protocol on this address).
|
||||
*/
|
||||
#define SFP_PHY_ADDR 22
|
||||
#define SFP_PHY_ADDR 22
|
||||
#define SFP_PHY_ADDR_ROLLBALL 17
|
||||
|
||||
struct sff_data {
|
||||
unsigned int gpios;
|
||||
|
|
@ -218,6 +222,7 @@ struct sfp {
|
|||
struct i2c_adapter *i2c;
|
||||
struct mii_bus *i2c_mii;
|
||||
struct sfp_bus *sfp_bus;
|
||||
enum mdio_i2c_proto mdio_protocol;
|
||||
struct phy_device *mod_phy;
|
||||
const struct sff_data *type;
|
||||
size_t i2c_block_size;
|
||||
|
|
@ -251,6 +256,7 @@ struct sfp {
|
|||
struct sfp_eeprom_id id;
|
||||
unsigned int module_power_mW;
|
||||
unsigned int module_t_start_up;
|
||||
unsigned int module_t_wait;
|
||||
bool tx_fault_ignore;
|
||||
|
||||
const struct sfp_quirk *quirk;
|
||||
|
|
@ -330,14 +336,33 @@ static void sfp_fixup_halny_gsfp(struct sfp *sfp)
|
|||
sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS);
|
||||
}
|
||||
|
||||
static void sfp_fixup_rollball(struct sfp *sfp)
|
||||
{
|
||||
sfp->mdio_protocol = MDIO_I2C_ROLLBALL;
|
||||
sfp->module_t_wait = T_WAIT_ROLLBALL;
|
||||
}
|
||||
|
||||
static void sfp_fixup_rollball_cc(struct sfp *sfp)
|
||||
{
|
||||
sfp_fixup_rollball(sfp);
|
||||
|
||||
/* Some RollBall SFPs may have wrong (zero) extended compliance code
|
||||
* burned in EEPROM. For PHY probing we need the correct one.
|
||||
*/
|
||||
sfp->id.base.extended_cc = SFF8024_ECC_10GBASE_T_SFI;
|
||||
}
|
||||
|
||||
static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
|
||||
unsigned long *modes)
|
||||
unsigned long *modes,
|
||||
unsigned long *interfaces)
|
||||
{
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, modes);
|
||||
__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
|
||||
|
|
@ -347,42 +372,39 @@ static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
|
|||
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, modes);
|
||||
}
|
||||
|
||||
#define SFP_QUIRK(_v, _p, _m, _f) \
|
||||
{ .vendor = _v, .part = _p, .modes = _m, .fixup = _f, }
|
||||
#define SFP_QUIRK_M(_v, _p, _m) SFP_QUIRK(_v, _p, _m, NULL)
|
||||
#define SFP_QUIRK_F(_v, _p, _f) SFP_QUIRK(_v, _p, NULL, _f)
|
||||
|
||||
static const struct sfp_quirk sfp_quirks[] = {
|
||||
{
|
||||
// Alcatel Lucent G-010S-P can operate at 2500base-X, but
|
||||
// incorrectly report 2500MBd NRZ in their EEPROM
|
||||
.vendor = "ALCATELLUCENT",
|
||||
.part = "G010SP",
|
||||
.modes = sfp_quirk_2500basex,
|
||||
}, {
|
||||
// Alcatel Lucent G-010S-A can operate at 2500base-X, but
|
||||
// report 3.2GBd NRZ in their EEPROM
|
||||
.vendor = "ALCATELLUCENT",
|
||||
.part = "3FE46541AA",
|
||||
.modes = sfp_quirk_2500basex,
|
||||
.fixup = sfp_fixup_long_startup,
|
||||
}, {
|
||||
.vendor = "HALNy",
|
||||
.part = "HL-GSFP",
|
||||
.fixup = sfp_fixup_halny_gsfp,
|
||||
}, {
|
||||
// Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
|
||||
// NRZ in their EEPROM
|
||||
.vendor = "HUAWEI",
|
||||
.part = "MA5671A",
|
||||
.modes = sfp_quirk_2500basex,
|
||||
.fixup = sfp_fixup_ignore_tx_fault,
|
||||
}, {
|
||||
// Lantech 8330-262D-E can operate at 2500base-X, but
|
||||
// incorrectly report 2500MBd NRZ in their EEPROM
|
||||
.vendor = "Lantech",
|
||||
.part = "8330-262D-E",
|
||||
.modes = sfp_quirk_2500basex,
|
||||
}, {
|
||||
.vendor = "UBNT",
|
||||
.part = "UF-INSTANT",
|
||||
.modes = sfp_quirk_ubnt_uf_instant,
|
||||
}
|
||||
// Alcatel Lucent G-010S-P can operate at 2500base-X, but incorrectly
|
||||
// report 2500MBd NRZ in their EEPROM
|
||||
SFP_QUIRK_M("ALCATELLUCENT", "G010SP", sfp_quirk_2500basex),
|
||||
|
||||
// Alcatel Lucent G-010S-A can operate at 2500base-X, but report 3.2GBd
|
||||
// NRZ in their EEPROM
|
||||
SFP_QUIRK("ALCATELLUCENT", "3FE46541AA", sfp_quirk_2500basex,
|
||||
sfp_fixup_long_startup),
|
||||
|
||||
SFP_QUIRK_F("HALNy", "HL-GSFP", sfp_fixup_halny_gsfp),
|
||||
|
||||
// Huawei MA5671A can operate at 2500base-X, but report 1.2GBd NRZ in
|
||||
// their EEPROM
|
||||
SFP_QUIRK("HUAWEI", "MA5671A", sfp_quirk_2500basex,
|
||||
sfp_fixup_ignore_tx_fault),
|
||||
|
||||
// Lantech 8330-262D-E can operate at 2500base-X, but incorrectly report
|
||||
// 2500MBd NRZ in their EEPROM
|
||||
SFP_QUIRK_M("Lantech", "8330-262D-E", sfp_quirk_2500basex),
|
||||
|
||||
SFP_QUIRK_M("UBNT", "UF-INSTANT", sfp_quirk_ubnt_uf_instant),
|
||||
|
||||
SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc),
|
||||
SFP_QUIRK_F("OEM", "RTSFP-10", sfp_fixup_rollball_cc),
|
||||
SFP_QUIRK_F("OEM", "RTSFP-10G", sfp_fixup_rollball_cc),
|
||||
SFP_QUIRK_F("Turris", "RTSFP-10", sfp_fixup_rollball),
|
||||
SFP_QUIRK_F("Turris", "RTSFP-10G", sfp_fixup_rollball),
|
||||
};
|
||||
|
||||
static size_t sfp_strlen(const char *str, size_t maxlen)
|
||||
|
|
@ -536,9 +558,6 @@ static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
|
|||
|
||||
static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
|
||||
{
|
||||
struct mii_bus *i2c_mii;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -546,7 +565,15 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
|
|||
sfp->read = sfp_i2c_read;
|
||||
sfp->write = sfp_i2c_write;
|
||||
|
||||
i2c_mii = mdio_i2c_alloc(sfp->dev, i2c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sfp_i2c_mdiobus_create(struct sfp *sfp)
|
||||
{
|
||||
struct mii_bus *i2c_mii;
|
||||
int ret;
|
||||
|
||||
i2c_mii = mdio_i2c_alloc(sfp->dev, sfp->i2c, sfp->mdio_protocol);
|
||||
if (IS_ERR(i2c_mii))
|
||||
return PTR_ERR(i2c_mii);
|
||||
|
||||
|
|
@ -564,6 +591,12 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void sfp_i2c_mdiobus_destroy(struct sfp *sfp)
|
||||
{
|
||||
mdiobus_unregister(sfp->i2c_mii);
|
||||
sfp->i2c_mii = NULL;
|
||||
}
|
||||
|
||||
/* Interface */
|
||||
static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
|
||||
{
|
||||
|
|
@ -1579,12 +1612,12 @@ static void sfp_sm_phy_detach(struct sfp *sfp)
|
|||
sfp->mod_phy = NULL;
|
||||
}
|
||||
|
||||
static int sfp_sm_probe_phy(struct sfp *sfp, bool is_c45)
|
||||
static int sfp_sm_probe_phy(struct sfp *sfp, int addr, bool is_c45)
|
||||
{
|
||||
struct phy_device *phy;
|
||||
int err;
|
||||
|
||||
phy = get_phy_device(sfp->i2c_mii, SFP_PHY_ADDR, is_c45);
|
||||
phy = get_phy_device(sfp->i2c_mii, addr, is_c45);
|
||||
if (phy == ERR_PTR(-ENODEV))
|
||||
return PTR_ERR(phy);
|
||||
if (IS_ERR(phy)) {
|
||||
|
|
@ -1680,6 +1713,14 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
|
|||
}
|
||||
}
|
||||
|
||||
static int sfp_sm_add_mdio_bus(struct sfp *sfp)
|
||||
{
|
||||
if (sfp->mdio_protocol != MDIO_I2C_NONE)
|
||||
return sfp_i2c_mdiobus_create(sfp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
|
|
@ -1695,19 +1736,23 @@ static int sfp_sm_probe_for_phy(struct sfp *sfp)
|
|||
{
|
||||
int err = 0;
|
||||
|
||||
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:
|
||||
err = sfp_sm_probe_phy(sfp, true);
|
||||
switch (sfp->mdio_protocol) {
|
||||
case MDIO_I2C_NONE:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (sfp->id.base.e1000_base_t)
|
||||
err = sfp_sm_probe_phy(sfp, false);
|
||||
case MDIO_I2C_MARVELL_C22:
|
||||
err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR, false);
|
||||
break;
|
||||
|
||||
case MDIO_I2C_C45:
|
||||
err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR, true);
|
||||
break;
|
||||
|
||||
case MDIO_I2C_ROLLBALL:
|
||||
err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR_ROLLBALL, true);
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -2031,9 +2076,20 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
|
|||
sfp->state_hw_mask |= SFP_F_LOS;
|
||||
|
||||
sfp->module_t_start_up = T_START_UP;
|
||||
sfp->module_t_wait = T_WAIT;
|
||||
|
||||
sfp->tx_fault_ignore = false;
|
||||
|
||||
if (sfp->id.base.extended_cc == SFF8024_ECC_10GBASE_T_SFI ||
|
||||
sfp->id.base.extended_cc == SFF8024_ECC_10GBASE_T_SR ||
|
||||
sfp->id.base.extended_cc == SFF8024_ECC_5GBASE_T ||
|
||||
sfp->id.base.extended_cc == SFF8024_ECC_2_5GBASE_T)
|
||||
sfp->mdio_protocol = MDIO_I2C_C45;
|
||||
else if (sfp->id.base.e1000_base_t)
|
||||
sfp->mdio_protocol = MDIO_I2C_MARVELL_C22;
|
||||
else
|
||||
sfp->mdio_protocol = MDIO_I2C_NONE;
|
||||
|
||||
sfp->quirk = sfp_lookup_quirk(&id);
|
||||
if (sfp->quirk && sfp->quirk->fixup)
|
||||
sfp->quirk->fixup(sfp);
|
||||
|
|
@ -2210,6 +2266,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
|
|||
sfp_module_stop(sfp->sfp_bus);
|
||||
if (sfp->mod_phy)
|
||||
sfp_sm_phy_detach(sfp);
|
||||
if (sfp->i2c_mii)
|
||||
sfp_i2c_mdiobus_destroy(sfp);
|
||||
sfp_module_tx_disable(sfp);
|
||||
sfp_soft_stop_poll(sfp);
|
||||
sfp_sm_next(sfp, SFP_S_DOWN, 0);
|
||||
|
|
@ -2233,9 +2291,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
|
|||
|
||||
/* We need to check the TX_FAULT state, which is not defined
|
||||
* while TX_DISABLE is asserted. The earliest we want to do
|
||||
* anything (such as probe for a PHY) is 50ms.
|
||||
* anything (such as probe for a PHY) is 50ms (or more on
|
||||
* specific modules).
|
||||
*/
|
||||
sfp_sm_next(sfp, SFP_S_WAIT, T_WAIT);
|
||||
sfp_sm_next(sfp, SFP_S_WAIT, sfp->module_t_wait);
|
||||
break;
|
||||
|
||||
case SFP_S_WAIT:
|
||||
|
|
@ -2249,8 +2308,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
|
|||
* deasserting.
|
||||
*/
|
||||
timeout = sfp->module_t_start_up;
|
||||
if (timeout > T_WAIT)
|
||||
timeout -= T_WAIT;
|
||||
if (timeout > sfp->module_t_wait)
|
||||
timeout -= sfp->module_t_wait;
|
||||
else
|
||||
timeout = 1;
|
||||
|
||||
|
|
@ -2272,6 +2331,12 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
|
|||
sfp->sm_fault_retries == N_FAULT_INIT);
|
||||
} else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
|
||||
init_done:
|
||||
/* Create mdiobus and start trying for PHY */
|
||||
ret = sfp_sm_add_mdio_bus(sfp);
|
||||
if (ret < 0) {
|
||||
sfp_sm_next(sfp, SFP_S_FAIL, 0);
|
||||
break;
|
||||
}
|
||||
sfp->sm_phy_retries = R_PHY_RETRY;
|
||||
goto phy_probe;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ struct sfp;
|
|||
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);
|
||||
void (*fixup)(struct sfp *sfp);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,14 @@ struct device;
|
|||
struct i2c_adapter;
|
||||
struct mii_bus;
|
||||
|
||||
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c);
|
||||
enum mdio_i2c_proto {
|
||||
MDIO_I2C_NONE,
|
||||
MDIO_I2C_MARVELL_C22,
|
||||
MDIO_I2C_C45,
|
||||
MDIO_I2C_ROLLBALL,
|
||||
};
|
||||
|
||||
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
|
||||
enum mdio_i2c_proto protocol);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -571,6 +571,7 @@ struct macsec_ops;
|
|||
* @advertising: Currently advertised linkmodes
|
||||
* @adv_old: Saved advertised while power saving for WoL
|
||||
* @lp_advertising: Current link partner advertised linkmodes
|
||||
* @host_interfaces: PHY interface modes supported by host
|
||||
* @eee_broken_modes: Energy efficient ethernet modes which should be prohibited
|
||||
* @autoneg: Flag autoneg being used
|
||||
* @rate_matching: Current rate matching mode
|
||||
|
|
@ -670,6 +671,9 @@ struct phy_device {
|
|||
/* used with phy_speed_down */
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(adv_old);
|
||||
|
||||
/* Host supported PHY interface types. Should be ignored if empty. */
|
||||
DECLARE_PHY_INTERFACE_MASK(host_interfaces);
|
||||
|
||||
/* Energy efficient ethernet modes which should be prohibited */
|
||||
u32 eee_broken_modes;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
@ -568,7 +568,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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user