Commit 139d3a21 authored by Russell King's avatar Russell King Committed by David S. Miller

net: sfp: allow modules with slow diagnostics to probe

When a module is inserted, we attempt to read read the ID from address
0x50.  Once we are able to read the ID, we immediately attempt to
initialise the hwmon support by reading from address 0x51.  If this
fails, then we fall into error state, and assume that the module is
not usable.

Modules such as the ALCATELLUCENT 3FE46541AA use a real EEPROM for
I2C address 0x50, which responds immediately.  However, address 0x51
is an emulated, which only becomes available once the on-board firmware
has booted.  This prompts us to fall into the error state.

Since the module may be usable without diagnostics, arrange for the
hwmon probe independent of the rest of the SFP itself, retrying every
5s for up to about 60s for the monitoring to become available, and
print an error message if it doesn't become available.
Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e117be74
...@@ -218,6 +218,8 @@ struct sfp { ...@@ -218,6 +218,8 @@ struct sfp {
#if IS_ENABLED(CONFIG_HWMON) #if IS_ENABLED(CONFIG_HWMON)
struct sfp_diag diag; struct sfp_diag diag;
struct delayed_work hwmon_probe;
unsigned int hwmon_tries;
struct device *hwmon_dev; struct device *hwmon_dev;
char *hwmon_name; char *hwmon_name;
#endif #endif
...@@ -1159,29 +1161,27 @@ static const struct hwmon_chip_info sfp_hwmon_chip_info = { ...@@ -1159,29 +1161,27 @@ static const struct hwmon_chip_info sfp_hwmon_chip_info = {
.info = sfp_hwmon_info, .info = sfp_hwmon_info,
}; };
static int sfp_hwmon_insert(struct sfp *sfp) static void sfp_hwmon_probe(struct work_struct *work)
{ {
struct sfp *sfp = container_of(work, struct sfp, hwmon_probe.work);
int err, i; int err, i;
if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE)
return 0;
if (!(sfp->id.ext.diagmon & SFP_DIAGMON_DDM))
return 0;
if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE)
/* This driver in general does not support address
* change.
*/
return 0;
err = sfp_read(sfp, true, 0, &sfp->diag, sizeof(sfp->diag)); err = sfp_read(sfp, true, 0, &sfp->diag, sizeof(sfp->diag));
if (err < 0) if (err < 0) {
return err; if (sfp->hwmon_tries--) {
mod_delayed_work(system_wq, &sfp->hwmon_probe,
T_PROBE_RETRY_SLOW);
} else {
dev_warn(sfp->dev, "hwmon probe failed: %d\n", err);
}
return;
}
sfp->hwmon_name = kstrdup(dev_name(sfp->dev), GFP_KERNEL); sfp->hwmon_name = kstrdup(dev_name(sfp->dev), GFP_KERNEL);
if (!sfp->hwmon_name) if (!sfp->hwmon_name) {
return -ENODEV; dev_err(sfp->dev, "out of memory for hwmon name\n");
return;
}
for (i = 0; sfp->hwmon_name[i]; i++) for (i = 0; sfp->hwmon_name[i]; i++)
if (hwmon_is_bad_char(sfp->hwmon_name[i])) if (hwmon_is_bad_char(sfp->hwmon_name[i]))
...@@ -1191,18 +1191,52 @@ static int sfp_hwmon_insert(struct sfp *sfp) ...@@ -1191,18 +1191,52 @@ static int sfp_hwmon_insert(struct sfp *sfp)
sfp->hwmon_name, sfp, sfp->hwmon_name, sfp,
&sfp_hwmon_chip_info, &sfp_hwmon_chip_info,
NULL); NULL);
if (IS_ERR(sfp->hwmon_dev))
dev_err(sfp->dev, "failed to register hwmon device: %ld\n",
PTR_ERR(sfp->hwmon_dev));
}
static int sfp_hwmon_insert(struct sfp *sfp)
{
if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE)
return 0;
return PTR_ERR_OR_ZERO(sfp->hwmon_dev); if (!(sfp->id.ext.diagmon & SFP_DIAGMON_DDM))
return 0;
if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE)
/* This driver in general does not support address
* change.
*/
return 0;
mod_delayed_work(system_wq, &sfp->hwmon_probe, 1);
sfp->hwmon_tries = R_PROBE_RETRY_SLOW;
return 0;
} }
static void sfp_hwmon_remove(struct sfp *sfp) static void sfp_hwmon_remove(struct sfp *sfp)
{ {
cancel_delayed_work_sync(&sfp->hwmon_probe);
if (!IS_ERR_OR_NULL(sfp->hwmon_dev)) { if (!IS_ERR_OR_NULL(sfp->hwmon_dev)) {
hwmon_device_unregister(sfp->hwmon_dev); hwmon_device_unregister(sfp->hwmon_dev);
sfp->hwmon_dev = NULL; sfp->hwmon_dev = NULL;
kfree(sfp->hwmon_name); kfree(sfp->hwmon_name);
} }
} }
static int sfp_hwmon_init(struct sfp *sfp)
{
INIT_DELAYED_WORK(&sfp->hwmon_probe, sfp_hwmon_probe);
return 0;
}
static void sfp_hwmon_exit(struct sfp *sfp)
{
cancel_delayed_work_sync(&sfp->hwmon_probe);
}
#else #else
static int sfp_hwmon_insert(struct sfp *sfp) static int sfp_hwmon_insert(struct sfp *sfp)
{ {
...@@ -1212,6 +1246,15 @@ static int sfp_hwmon_insert(struct sfp *sfp) ...@@ -1212,6 +1246,15 @@ static int sfp_hwmon_insert(struct sfp *sfp)
static void sfp_hwmon_remove(struct sfp *sfp) static void sfp_hwmon_remove(struct sfp *sfp)
{ {
} }
static int sfp_hwmon_init(struct sfp *sfp)
{
return 0;
}
static void sfp_hwmon_exit(struct sfp *sfp)
{
}
#endif #endif
/* Helpers */ /* Helpers */
...@@ -1548,10 +1591,6 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) ...@@ -1548,10 +1591,6 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = sfp_hwmon_insert(sfp);
if (ret < 0)
return ret;
return 0; return 0;
} }
...@@ -1700,6 +1739,15 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event) ...@@ -1700,6 +1739,15 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event)
case SFP_MOD_ERROR: case SFP_MOD_ERROR:
break; break;
} }
#if IS_ENABLED(CONFIG_HWMON)
if (sfp->sm_mod_state >= SFP_MOD_WAITDEV &&
IS_ERR_OR_NULL(sfp->hwmon_dev)) {
err = sfp_hwmon_insert(sfp);
if (err)
dev_warn(sfp->dev, "hwmon probe failed: %d\n", err);
}
#endif
} }
static void sfp_sm_main(struct sfp *sfp, unsigned int event) static void sfp_sm_main(struct sfp *sfp, unsigned int event)
...@@ -2001,6 +2049,8 @@ static struct sfp *sfp_alloc(struct device *dev) ...@@ -2001,6 +2049,8 @@ static struct sfp *sfp_alloc(struct device *dev)
INIT_DELAYED_WORK(&sfp->poll, sfp_poll); INIT_DELAYED_WORK(&sfp->poll, sfp_poll);
INIT_DELAYED_WORK(&sfp->timeout, sfp_timeout); INIT_DELAYED_WORK(&sfp->timeout, sfp_timeout);
sfp_hwmon_init(sfp);
return sfp; return sfp;
} }
...@@ -2008,6 +2058,8 @@ static void sfp_cleanup(void *data) ...@@ -2008,6 +2058,8 @@ static void sfp_cleanup(void *data)
{ {
struct sfp *sfp = data; struct sfp *sfp = data;
sfp_hwmon_exit(sfp);
cancel_delayed_work_sync(&sfp->poll); cancel_delayed_work_sync(&sfp->poll);
cancel_delayed_work_sync(&sfp->timeout); cancel_delayed_work_sync(&sfp->timeout);
if (sfp->i2c_mii) { if (sfp->i2c_mii) {
......
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