Commit bea0f4a5 authored by David S. Miller's avatar David S. Miller

Merge branch 'sfp-slow-to-probe-copper'

Russell King says:

====================
Add support for slow-to-probe-PHY copper SFP modules

This series, following on from the previous adding SFP+ copper support,
adds support for a range of Copper SFP modules, made by a variety of
companies, all of which have a Marvell 88E1111 PHY on them, but take
far longer than the Marvell spec'd 15ms to start communicating on the
I2C bus.

Researching the Champion One 1000SFPT module reveals that TX_DISABLE is
routed through a MAX1971 switching regulator and reset IC which adds a
175ms delay to releasing the 88E1111 reset.

It is not known whether other modules use a similar setup, but there
are a range of modules that are slow for the Marvell PHY to appear.

This patch series adds support for these modules by repeatedly trying
to probe the PHY for up to 600ms.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b58e9fd4 1cb89a14
......@@ -62,6 +62,7 @@ enum {
SFP_S_FAIL,
SFP_S_WAIT,
SFP_S_INIT,
SFP_S_INIT_PHY,
SFP_S_INIT_TX_FAULT,
SFP_S_WAIT_LOS,
SFP_S_LINK_UP,
......@@ -126,6 +127,7 @@ static const char * const sm_state_strings[] = {
[SFP_S_FAIL] = "fail",
[SFP_S_WAIT] = "wait",
[SFP_S_INIT] = "init",
[SFP_S_INIT_PHY] = "init_phy",
[SFP_S_INIT_TX_FAULT] = "init_tx_fault",
[SFP_S_WAIT_LOS] = "wait_los",
[SFP_S_LINK_UP] = "link_up",
......@@ -172,6 +174,20 @@ static const enum gpiod_flags gpio_flags[] = {
#define T_RESET_US 10
#define T_FAULT_RECOVER msecs_to_jiffies(1000)
/* N_FAULT_INIT is the number of recovery attempts at module initialisation
* time. If the TX_FAULT signal is not deasserted after this number of
* attempts at clearing it, we decide that the module is faulty.
* N_FAULT is the same but after the module has initialised.
*/
#define N_FAULT_INIT 5
#define N_FAULT 5
/* T_PHY_RETRY is the time interval between attempts to probe the PHY.
* R_PHY_RETRY is the number of attempts.
*/
#define T_PHY_RETRY msecs_to_jiffies(50)
#define R_PHY_RETRY 12
/* SFP module presence detection is poor: the three MOD DEF signals are
* the same length on the PCB, which means it's possible for MOD DEF 0 to
* connect before the I2C bus on MOD DEF 1/2.
......@@ -226,7 +242,8 @@ struct sfp {
unsigned char sm_mod_tries;
unsigned char sm_dev_state;
unsigned short sm_state;
unsigned int sm_retries;
unsigned char sm_fault_retries;
unsigned char sm_phy_retries;
struct sfp_eeprom_id id;
unsigned int module_power_mW;
......@@ -1402,26 +1419,24 @@ static void sfp_sm_phy_detach(struct sfp *sfp)
sfp->mod_phy = NULL;
}
static void sfp_sm_probe_phy(struct sfp *sfp, bool is_c45)
static int sfp_sm_probe_phy(struct sfp *sfp, bool is_c45)
{
struct phy_device *phy;
int err;
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;
}
if (phy == ERR_PTR(-ENODEV))
return PTR_ERR(phy);
if (IS_ERR(phy)) {
dev_err(sfp->dev, "mdiobus scan returned %ld\n", PTR_ERR(phy));
return;
return PTR_ERR(phy);
}
err = phy_device_register(phy);
if (err) {
phy_device_free(phy);
dev_err(sfp->dev, "phy_device_register failed: %d\n", err);
return;
return err;
}
err = sfp_add_phy(sfp->sfp_bus, phy);
......@@ -1429,10 +1444,12 @@ static void sfp_sm_probe_phy(struct sfp *sfp, bool is_c45)
phy_device_remove(phy);
phy_device_free(phy);
dev_err(sfp->dev, "sfp_add_phy failed: %d\n", err);
return;
return err;
}
sfp->mod_phy = phy;
return 0;
}
static void sfp_sm_link_up(struct sfp *sfp)
......@@ -1482,7 +1499,7 @@ static bool sfp_los_event_inactive(struct sfp *sfp, unsigned int event)
static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
{
if (sfp->sm_retries && !--sfp->sm_retries) {
if (sfp->sm_fault_retries && !--sfp->sm_fault_retries) {
dev_err(sfp->dev,
"module persistently indicates fault, disabling\n");
sfp_sm_next(sfp, SFP_S_TX_DISABLE, 0);
......@@ -1505,21 +1522,24 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
* 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)
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:
sfp_sm_probe_phy(sfp, true);
err = sfp_sm_probe_phy(sfp, true);
break;
default:
if (sfp->id.base.e1000_base_t)
sfp_sm_probe_phy(sfp, false);
err = sfp_sm_probe_phy(sfp, false);
break;
}
return err;
}
static int sfp_module_parse_power(struct sfp *sfp)
......@@ -1854,6 +1874,7 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event)
static void sfp_sm_main(struct sfp *sfp, unsigned int event)
{
unsigned long timeout;
int ret;
/* Some events are global */
if (sfp->sm_state != SFP_S_DOWN &&
......@@ -1885,7 +1906,7 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
sfp_module_tx_enable(sfp);
/* Initialise the fault clearance retries */
sfp->sm_retries = 5;
sfp->sm_fault_retries = N_FAULT_INIT;
/* We need to check the TX_FAULT state, which is not defined
* while TX_DISABLE is asserted. The earliest we want to do
......@@ -1925,21 +1946,41 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
* or t_start_up, so assume there is a fault.
*/
sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT,
sfp->sm_retries == 5);
sfp->sm_fault_retries == N_FAULT_INIT);
} else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
init_done: /* TX_FAULT deasserted or we timed out with TX_FAULT
* 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);
init_done:
sfp->sm_phy_retries = R_PHY_RETRY;
goto phy_probe;
}
break;
case SFP_S_INIT_PHY:
if (event != SFP_E_TIMEOUT)
break;
phy_probe:
/* TX_FAULT deasserted or we timed out with TX_FAULT
* clear. Probe for the PHY and check the LOS state.
*/
ret = sfp_sm_probe_for_phy(sfp);
if (ret == -ENODEV) {
if (--sfp->sm_phy_retries) {
sfp_sm_next(sfp, SFP_S_INIT_PHY, T_PHY_RETRY);
break;
} else {
dev_info(sfp->dev, "no PHY detected\n");
}
sfp_sm_link_check_los(sfp);
/* Reset the fault retry count */
sfp->sm_retries = 5;
} else if (ret) {
sfp_sm_next(sfp, SFP_S_FAIL, 0);
break;
}
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 */
sfp->sm_fault_retries = N_FAULT;
break;
case SFP_S_INIT_TX_FAULT:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment