Commit 6143eca3 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-sfp-improve-high-power-module-implementation'

Russell King says:

====================
net: sfp: improve high power module implementation

This series aims to improve the power level switching between standard
level 1 and the higher power levels.

The first patch updates the DT binding documentation to include the
minimum and default of 1W, which is the base level that every SFP cage
must support. Hence, it makes sense to document this in the binding.

The second patch enforces a minimum of 1W when parsing the firmware
description, and optimises the code for that case; there's no need to
check for SFF8472 compliance since we will not need to touch the
A2h registers.

Patch 3 validates that the module supports SFF-8472 rev 10.2 before
checking for power level 2 - rev 10.2 is where support for power
levels was introduced, so if the module doesn't support this revision,
it doesn't support power levels. Setting the power level 2 declaration
bit is likely to be spurious.

Patch 4 does the same for power level 3, except this was introduced in
SFF-8472 rev 11.9. The revision code was never updated, so we use the
rev 11.4 to signify this.

Patch 5 cleans up the code - rather than using BIT(0), we now use a
properly named value for the power level select bit.

Patch 6 introduces a read-modify-write helper.

Patch 7 gets rid of the DM7052 hack (which sets a power level
declaration bit but is not compatible with SFF-8472 rev 10.2, and
the module does not implement the A2h I2C address.)

Series tested with my DM7052.

v2: update sff.sfp.yaml with Rob's feedback
====================

Andrew's review tags from v1.

Link: https://lore.kernel.org/r/Y0%2F7dAB8OU3jrbz6@shell.armlinux.org.uk
Link: https://lore.kernel.org/r/Y1K17UtfFopACIi2@shell.armlinux.org.ukSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 1b3d6ecd bd1432f6
......@@ -22,7 +22,8 @@ properties:
phandle of an I2C bus controller for the SFP two wire serial
maximum-power-milliwatt:
maxItems: 1
minimum: 1000
default: 1000
description:
Maximum module power consumption Specifies the maximum power consumption
allowable by a module in the slot, in milli-Watts. Presently, modules can
......
......@@ -608,6 +608,22 @@ static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
return sfp->write(sfp, a2, addr, buf, len);
}
static int sfp_modify_u8(struct sfp *sfp, bool a2, u8 addr, u8 mask, u8 val)
{
int ret;
u8 old, v;
ret = sfp_read(sfp, a2, addr, &old, sizeof(old));
if (ret != sizeof(old))
return ret;
v = (old & ~mask) | (val & mask);
if (v == old)
return sizeof(v);
return sfp_write(sfp, a2, addr, &v, sizeof(v));
}
static unsigned int sfp_soft_get_state(struct sfp *sfp)
{
unsigned int state = 0;
......@@ -633,17 +649,14 @@ static unsigned int sfp_soft_get_state(struct sfp *sfp)
static void sfp_soft_set_state(struct sfp *sfp, unsigned int state)
{
u8 status;
u8 mask = SFP_STATUS_TX_DISABLE_FORCE;
u8 val = 0;
if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) ==
sizeof(status)) {
if (state & SFP_F_TX_DISABLE)
status |= SFP_STATUS_TX_DISABLE_FORCE;
else
status &= ~SFP_STATUS_TX_DISABLE_FORCE;
if (state & SFP_F_TX_DISABLE)
val |= SFP_STATUS_TX_DISABLE_FORCE;
sfp_write(sfp, true, SFP_STATUS, &status, sizeof(status));
}
sfp_modify_u8(sfp, true, SFP_STATUS, mask, val);
}
static void sfp_soft_start_poll(struct sfp *sfp)
......@@ -1761,11 +1774,20 @@ static int sfp_module_parse_power(struct sfp *sfp)
u32 power_mW = 1000;
bool supports_a2;
if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL))
if (sfp->id.ext.sff8472_compliance >= SFP_SFF8472_COMPLIANCE_REV10_2 &&
sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL))
power_mW = 1500;
if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL))
/* Added in Rev 11.9, but there is no compliance code for this */
if (sfp->id.ext.sff8472_compliance >= SFP_SFF8472_COMPLIANCE_REV11_4 &&
sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL))
power_mW = 2000;
/* Power level 1 modules (max. 1W) are always supported. */
if (power_mW <= 1000) {
sfp->module_power_mW = power_mW;
return 0;
}
supports_a2 = sfp->id.ext.sff8472_compliance !=
SFP_SFF8472_COMPLIANCE_NONE ||
sfp->id.ext.diagmon & SFP_DIAGMON_DDM;
......@@ -1789,12 +1811,6 @@ static int sfp_module_parse_power(struct sfp *sfp)
}
}
if (power_mW <= 1000) {
/* Modules below 1W do not require a power change sequence */
sfp->module_power_mW = power_mW;
return 0;
}
if (!supports_a2) {
/* The module power level is below the host maximum and the
* module appears not to implement bus address 0xa2, so assume
......@@ -1821,31 +1837,14 @@ static int sfp_module_parse_power(struct sfp *sfp)
static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable)
{
u8 val;
int err;
err = sfp_read(sfp, true, SFP_EXT_STATUS, &val, sizeof(val));
if (err != sizeof(val)) {
dev_err(sfp->dev, "Failed to read EEPROM: %pe\n", ERR_PTR(err));
return -EAGAIN;
}
/* DM7052 reports as a high power module, responds to reads (with
* all bytes 0xff) at 0x51 but does not accept writes. In any case,
* if the bit is already set, we're already in high power mode.
*/
if (!!(val & BIT(0)) == enable)
return 0;
if (enable)
val |= BIT(0);
else
val &= ~BIT(0);
err = sfp_write(sfp, true, SFP_EXT_STATUS, &val, sizeof(val));
if (err != sizeof(val)) {
dev_err(sfp->dev, "Failed to write EEPROM: %pe\n",
ERR_PTR(err));
err = sfp_modify_u8(sfp, true, SFP_EXT_STATUS,
SFP_EXT_STATUS_PWRLVL_SELECT,
enable ? SFP_EXT_STATUS_PWRLVL_SELECT : 0);
if (err != sizeof(u8)) {
dev_err(sfp->dev, "failed to %sable high power: %pe\n",
enable ? "en" : "dis", ERR_PTR(err));
return -EAGAIN;
}
......@@ -2729,8 +2728,12 @@ static int sfp_probe(struct platform_device *pdev)
device_property_read_u32(&pdev->dev, "maximum-power-milliwatt",
&sfp->max_power_mW);
if (!sfp->max_power_mW)
if (sfp->max_power_mW < 1000) {
if (sfp->max_power_mW)
dev_warn(sfp->dev,
"Firmware bug: host maximum power should be at least 1W\n");
sfp->max_power_mW = 1000;
}
dev_info(sfp->dev, "Host maximum power %u.%uW\n",
sfp->max_power_mW / 1000, (sfp->max_power_mW / 100) % 10);
......
......@@ -489,6 +489,8 @@ enum {
SFP_WARN1_RXPWR_LOW = BIT(6),
SFP_EXT_STATUS = 0x76,
SFP_EXT_STATUS_PWRLVL_SELECT = BIT(0),
SFP_VSL = 0x78,
SFP_PAGE = 0x7f,
};
......
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