Skip to content

Commit

Permalink
Merge branch 'sfp-quirks'
Browse files Browse the repository at this point in the history
Russell King says:

====================
Add rudimentary SFP module quirk support

The SFP module EEPROM describes the capabilities of the module, but
doesn't describe the host interface.  We have a certain amount of
guess-work to work out how to configure the host - which works most
of the time.

However, there are some (such as GPON) modules which are able to
support different host interfaces, such as 1000BASE-X and 2500BASE-X.
The module will switch between each mode until it achieves link with
the host.

There is no defined way to describe this in the SFP EEPROM, so we can
only recognise the module and handle it appropriately.  This series
adds the necessary recognition of the modules using a quirk system,
and tweaks the support mask to allow them to link with the host at
2500BASE-X, thereby allowing the user to achieve full line rate.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Nov 21, 2019
2 parents 9bb59a2 + b0eae33 commit 9ce3335
Showing 1 changed file with 79 additions and 0 deletions.
79 changes: 79 additions & 0 deletions drivers/net/phy/sfp-bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@

#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
*/
Expand All @@ -22,6 +28,7 @@ struct sfp_bus {
const struct sfp_socket_ops *socket_ops;
struct device *sfp_dev;
struct sfp *sfp;
const struct sfp_quirk *sfp_quirk;

const struct sfp_upstream_ops *upstream_ops;
void *upstream;
Expand All @@ -31,6 +38,71 @@ 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 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,
},
};

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
Expand Down Expand Up @@ -234,6 +306,9 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
phylink_set(modes, 1000baseX_Full);
}

if (bus->sfp_quirk)
bus->sfp_quirk->modes(id, modes);

bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS);

phylink_set(support, Autoneg);
Expand Down Expand Up @@ -610,6 +685,8 @@ int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id)
const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
int ret = 0;

bus->sfp_quirk = sfp_lookup_quirk(id);

if (ops && ops->module_insert)
ret = ops->module_insert(bus->upstream, id);

Expand All @@ -623,6 +700,8 @@ void sfp_module_remove(struct sfp_bus *bus)

if (ops && ops->module_remove)
ops->module_remove(bus->upstream);

bus->sfp_quirk = NULL;
}
EXPORT_SYMBOL_GPL(sfp_module_remove);

Expand Down

0 comments on commit 9ce3335

Please sign in to comment.