Merge branch 'sfp-add-support-for-halny-gpon-module'
Russell King says: ==================== sfp: add support for HALNy GPON module This series adds support for the HALNy GPON SFP module. In order to do this sensibly, we need a more flexible quirk system, since we need to change the behaviour of the SFP cage driver to ignore the LOS and TX_FAULT signals after module detection. Since we move the SFP quirks into the SFP cage driver, we can use it for the MA5671A and 3FE46541AA modules as well. ==================== Link: https://lore.kernel.org/r/YyDUnvM1b0dZPmmd@shell.armlinux.org.uk Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
c3188dbafa
@ -10,12 +10,6 @@
|
||||
|
||||
#include "sfp.h"
|
||||
|
||||
struct sfp_quirk {
|
||||
const char *vendor;
|
||||
const char *part;
|
||||
void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sfp_bus - internal representation of a sfp bus
|
||||
*/
|
||||
@ -38,93 +32,6 @@ struct sfp_bus {
|
||||
bool started;
|
||||
};
|
||||
|
||||
static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
|
||||
unsigned long *modes)
|
||||
{
|
||||
phylink_set(modes, 2500baseX_Full);
|
||||
}
|
||||
|
||||
static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
|
||||
unsigned long *modes)
|
||||
{
|
||||
/* Ubiquiti U-Fiber Instant module claims that support all transceiver
|
||||
* types including 10G Ethernet which is not truth. So clear all claimed
|
||||
* modes and set only one mode which module supports: 1000baseX_Full.
|
||||
*/
|
||||
phylink_zero(modes);
|
||||
phylink_set(modes, 1000baseX_Full);
|
||||
}
|
||||
|
||||
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,
|
||||
}, {
|
||||
// Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
|
||||
// NRZ in their EEPROM
|
||||
.vendor = "HUAWEI",
|
||||
.part = "MA5671A",
|
||||
.modes = sfp_quirk_2500basex,
|
||||
}, {
|
||||
// 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,
|
||||
},
|
||||
};
|
||||
|
||||
static size_t sfp_strlen(const char *str, size_t maxlen)
|
||||
{
|
||||
size_t size, i;
|
||||
|
||||
/* Trailing characters should be filled with space chars */
|
||||
for (i = 0, size = 0; i < maxlen; i++)
|
||||
if (str[i] != ' ')
|
||||
size = i + 1;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool sfp_match(const char *qs, const char *str, size_t len)
|
||||
{
|
||||
if (!qs)
|
||||
return true;
|
||||
if (strlen(qs) != len)
|
||||
return false;
|
||||
return !strncmp(qs, str, len);
|
||||
}
|
||||
|
||||
static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id)
|
||||
{
|
||||
const struct sfp_quirk *q;
|
||||
unsigned int i;
|
||||
size_t vs, ps;
|
||||
|
||||
vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name));
|
||||
ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn));
|
||||
|
||||
for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++)
|
||||
if (sfp_match(q->vendor, id->base.vendor_name, vs) &&
|
||||
sfp_match(q->part, id->base.vendor_pn, ps))
|
||||
return q;
|
||||
|
||||
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
|
||||
@ -376,7 +283,7 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
|
||||
phylink_set(modes, 2500baseX_Full);
|
||||
}
|
||||
|
||||
if (bus->sfp_quirk)
|
||||
if (bus->sfp_quirk && bus->sfp_quirk->modes)
|
||||
bus->sfp_quirk->modes(id, modes);
|
||||
|
||||
linkmode_or(support, support, modes);
|
||||
@ -786,12 +693,13 @@ void sfp_link_down(struct sfp_bus *bus)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sfp_link_down);
|
||||
|
||||
int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id)
|
||||
int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
|
||||
const struct sfp_quirk *quirk)
|
||||
{
|
||||
const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
|
||||
int ret = 0;
|
||||
|
||||
bus->sfp_quirk = sfp_lookup_quirk(id);
|
||||
bus->sfp_quirk = quirk;
|
||||
|
||||
if (ops && ops->module_insert)
|
||||
ret = ops->module_insert(bus->upstream, id);
|
||||
|
@ -234,6 +234,7 @@ struct sfp {
|
||||
bool need_poll;
|
||||
|
||||
struct mutex st_mutex; /* Protects state */
|
||||
unsigned int state_hw_mask;
|
||||
unsigned int state_soft_mask;
|
||||
unsigned int state;
|
||||
struct delayed_work poll;
|
||||
@ -252,6 +253,8 @@ struct sfp {
|
||||
unsigned int module_t_start_up;
|
||||
bool tx_fault_ignore;
|
||||
|
||||
const struct sfp_quirk *quirk;
|
||||
|
||||
#if IS_ENABLED(CONFIG_HWMON)
|
||||
struct sfp_diag diag;
|
||||
struct delayed_work hwmon_probe;
|
||||
@ -308,6 +311,120 @@ static const struct of_device_id sfp_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sfp_of_match);
|
||||
|
||||
static void sfp_fixup_long_startup(struct sfp *sfp)
|
||||
{
|
||||
sfp->module_t_start_up = T_START_UP_BAD_GPON;
|
||||
}
|
||||
|
||||
static void sfp_fixup_ignore_tx_fault(struct sfp *sfp)
|
||||
{
|
||||
sfp->tx_fault_ignore = true;
|
||||
}
|
||||
|
||||
static void sfp_fixup_halny_gsfp(struct sfp *sfp)
|
||||
{
|
||||
/* Ignore the TX_FAULT and LOS signals on this module.
|
||||
* these are possibly used for other purposes on this
|
||||
* module, e.g. a serial port.
|
||||
*/
|
||||
sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS);
|
||||
}
|
||||
|
||||
static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
|
||||
unsigned long *modes)
|
||||
{
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, modes);
|
||||
}
|
||||
|
||||
static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
|
||||
unsigned long *modes)
|
||||
{
|
||||
/* Ubiquiti U-Fiber Instant module claims that support all transceiver
|
||||
* types including 10G Ethernet which is not truth. So clear all claimed
|
||||
* modes and set only one mode which module supports: 1000baseX_Full.
|
||||
*/
|
||||
linkmode_zero(modes);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, modes);
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
};
|
||||
|
||||
static size_t sfp_strlen(const char *str, size_t maxlen)
|
||||
{
|
||||
size_t size, i;
|
||||
|
||||
/* Trailing characters should be filled with space chars, but
|
||||
* some manufacturers can't read SFF-8472 and use NUL.
|
||||
*/
|
||||
for (i = 0, size = 0; i < maxlen; i++)
|
||||
if (str[i] != ' ' && str[i] != '\0')
|
||||
size = i + 1;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool sfp_match(const char *qs, const char *str, size_t len)
|
||||
{
|
||||
if (!qs)
|
||||
return true;
|
||||
if (strlen(qs) != len)
|
||||
return false;
|
||||
return !strncmp(qs, str, len);
|
||||
}
|
||||
|
||||
static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id)
|
||||
{
|
||||
const struct sfp_quirk *q;
|
||||
unsigned int i;
|
||||
size_t vs, ps;
|
||||
|
||||
vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name));
|
||||
ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn));
|
||||
|
||||
for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++)
|
||||
if (sfp_match(q->vendor, id->base.vendor_name, vs) &&
|
||||
sfp_match(q->part, id->base.vendor_pn, ps))
|
||||
return q;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned long poll_jiffies;
|
||||
|
||||
static unsigned int sfp_gpio_get_state(struct sfp *sfp)
|
||||
@ -499,17 +616,18 @@ static void sfp_soft_set_state(struct sfp *sfp, unsigned int state)
|
||||
static void sfp_soft_start_poll(struct sfp *sfp)
|
||||
{
|
||||
const struct sfp_eeprom_id *id = &sfp->id;
|
||||
unsigned int mask = 0;
|
||||
|
||||
sfp->state_soft_mask = 0;
|
||||
if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE &&
|
||||
!sfp->gpio[GPIO_TX_DISABLE])
|
||||
sfp->state_soft_mask |= SFP_F_TX_DISABLE;
|
||||
if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT &&
|
||||
!sfp->gpio[GPIO_TX_FAULT])
|
||||
sfp->state_soft_mask |= SFP_F_TX_FAULT;
|
||||
if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS &&
|
||||
!sfp->gpio[GPIO_LOS])
|
||||
sfp->state_soft_mask |= SFP_F_LOS;
|
||||
if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE)
|
||||
mask |= SFP_F_TX_DISABLE;
|
||||
if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT)
|
||||
mask |= SFP_F_TX_FAULT;
|
||||
if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS)
|
||||
mask |= SFP_F_LOS;
|
||||
|
||||
// Poll the soft state for hardware pins we want to ignore
|
||||
sfp->state_soft_mask = ~sfp->state_hw_mask & mask;
|
||||
|
||||
if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) &&
|
||||
!sfp->need_poll)
|
||||
@ -523,10 +641,11 @@ static void sfp_soft_stop_poll(struct sfp *sfp)
|
||||
|
||||
static unsigned int sfp_get_state(struct sfp *sfp)
|
||||
{
|
||||
unsigned int state = sfp->get_state(sfp);
|
||||
unsigned int soft = sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT);
|
||||
unsigned int state;
|
||||
|
||||
if (state & SFP_F_PRESENT &&
|
||||
sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT))
|
||||
state = sfp->get_state(sfp) & sfp->state_hw_mask;
|
||||
if (state & SFP_F_PRESENT && soft)
|
||||
state |= sfp_soft_get_state(sfp);
|
||||
|
||||
return state;
|
||||
@ -1902,17 +2021,22 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) &&
|
||||
!memcmp(id.base.vendor_pn, "3FE46541AA ", 16))
|
||||
sfp->module_t_start_up = T_START_UP_BAD_GPON;
|
||||
else
|
||||
sfp->module_t_start_up = T_START_UP;
|
||||
/* Initialise state bits to use from hardware */
|
||||
sfp->state_hw_mask = SFP_F_PRESENT;
|
||||
if (sfp->gpio[GPIO_TX_DISABLE])
|
||||
sfp->state_hw_mask |= SFP_F_TX_DISABLE;
|
||||
if (sfp->gpio[GPIO_TX_FAULT])
|
||||
sfp->state_hw_mask |= SFP_F_TX_FAULT;
|
||||
if (sfp->gpio[GPIO_LOS])
|
||||
sfp->state_hw_mask |= SFP_F_LOS;
|
||||
|
||||
if (!memcmp(id.base.vendor_name, "HUAWEI ", 16) &&
|
||||
!memcmp(id.base.vendor_pn, "MA5671A ", 16))
|
||||
sfp->tx_fault_ignore = true;
|
||||
else
|
||||
sfp->tx_fault_ignore = false;
|
||||
sfp->module_t_start_up = T_START_UP;
|
||||
|
||||
sfp->tx_fault_ignore = false;
|
||||
|
||||
sfp->quirk = sfp_lookup_quirk(&id);
|
||||
if (sfp->quirk && sfp->quirk->fixup)
|
||||
sfp->quirk->fixup(sfp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2026,7 +2150,8 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event)
|
||||
break;
|
||||
|
||||
/* Report the module insertion to the upstream device */
|
||||
err = sfp_module_insert(sfp->sfp_bus, &sfp->id);
|
||||
err = sfp_module_insert(sfp->sfp_bus, &sfp->id,
|
||||
sfp->quirk);
|
||||
if (err < 0) {
|
||||
sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
|
||||
break;
|
||||
@ -2528,6 +2653,8 @@ static int sfp_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(sfp->gpio[i]);
|
||||
}
|
||||
|
||||
sfp->state_hw_mask = SFP_F_PRESENT;
|
||||
|
||||
sfp->get_state = sfp_gpio_get_state;
|
||||
sfp->set_state = sfp_gpio_set_state;
|
||||
|
||||
|
@ -6,6 +6,13 @@
|
||||
|
||||
struct sfp;
|
||||
|
||||
struct sfp_quirk {
|
||||
const char *vendor;
|
||||
const char *part;
|
||||
void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
|
||||
void (*fixup)(struct sfp *sfp);
|
||||
};
|
||||
|
||||
struct sfp_socket_ops {
|
||||
void (*attach)(struct sfp *sfp);
|
||||
void (*detach)(struct sfp *sfp);
|
||||
@ -23,7 +30,8 @@ int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev);
|
||||
void sfp_remove_phy(struct sfp_bus *bus);
|
||||
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);
|
||||
int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
|
||||
const struct sfp_quirk *quirk);
|
||||
void sfp_module_remove(struct sfp_bus *bus);
|
||||
int sfp_module_start(struct sfp_bus *bus);
|
||||
void sfp_module_stop(struct sfp_bus *bus);
|
||||
|
Loading…
Reference in New Issue
Block a user