Skip to content

Commit 39718c3

Browse files
dangowrtfrank-w
authored andcommitted
net: dsa: mxl862xx: wait for firmware boot-time configuration to finish
The switch firmware answers MDIO API commands long before its application thread has finished applying the built-in boot-time default configuration. The management interface is brought up as part of the firmware's device initialization, while the application thread afterwards reconfigures bridge ports 0..16, meters, WRED thresholds, PCE rules and VLAN resources, and only then enables ingress SDMA on all ports as the very last step of its default configuration. mxl862xx_wait_ready() declares the switch ready as soon as FW_VERSION and CFGGET succeed, typically ~2.4s after reset. All configuration issued by the driver between that point and the firmware's final SDMA enable races the firmware's own writes and may be silently overwritten. Depending on boot timing this can leave the switch in a state where every frame received from the conduit is dropped by the ingress filter (RxFilteredPkts on the CPU port increments in lockstep with conduit TX) while the reverse direction keeps working. The state persists until the race is re-run with better luck. Observed on a MxL86252C in roughly half of the boots of a BananaPi BPI-R4 Pro 8X board [1]. Gate readiness on the firmware's final configuration step instead: poll the SDMA port control register of logical port 0 via the RegisterGet API until the port enable bit is set. The reset issued right before waiting clears that bit on all ports, and the driver does not touch any port state until after mxl862xx_wait_ready() has returned, so within this window the bit can only have been set by the firmware application completing its boot-time configuration. Logical port 0 is the switch-internal CPU port which the driver itself never enables. [1] openwrt/openwrt#24642 (comment) Fixes: 23794be ("net: dsa: add basic initial driver for MxL862xx switches") Signed-off-by: Daniel Golle <daniel@makrotopia.org>
1 parent 156ccf7 commit 39718c3

4 files changed

Lines changed: 42 additions & 1 deletion

File tree

drivers/net/dsa/mxl862xx/mxl862xx-api.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@ struct mdio_relay_data {
2020
__le16 reg;
2121
} __packed;
2222

23+
/**
24+
* struct mxl862xx_register - Register access parameter to directly read
25+
* internal registers
26+
* @addr: Register address offset for read access
27+
* @data: Value read from the register address
28+
*
29+
* Used for direct register read operations.
30+
*/
31+
struct mxl862xx_register {
32+
__le16 addr;
33+
__le16 data;
34+
} __packed;
35+
2336
/**
2437
* struct mxl862xx_register_mod - Register access parameter to directly
2538
* modify internal registers

drivers/net/dsa/mxl862xx/mxl862xx-cmd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#define SYS_MISC_MAGIC 0x1900
2828
#define MXL862XX_XPCS_MAGIC 0x1a00
2929

30+
#define MXL862XX_COMMON_REGISTERGET (MXL862XX_COMMON_MAGIC + 0x1)
3031
#define MXL862XX_COMMON_PORTLINKCFGGET (MXL862XX_COMMON_MAGIC + 0x5)
3132
#define MXL862XX_COMMON_PORTCFGGET (MXL862XX_COMMON_MAGIC + 0x7)
3233
#define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
diff a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h (rejected hunks)
2+
@@ -26,6 +26,7 @@
3+
#define SYS_MISC_MAGIC 0x1900
4+
#define MXL862XX_XPCS_MAGIC 0x1a00
5+
6+
+#define MXL862XX_COMMON_REGISTERGET (MXL862XX_COMMON_MAGIC + 0x1)
7+
#define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9)
8+
#define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xa)
9+
#define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11)

drivers/net/dsa/mxl862xx/mxl862xx.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ int mxl862xx_wait_ready(struct dsa_switch *ds)
365365
struct mxl862xx_sys_fw_image_version ver = {};
366366
unsigned long start = jiffies, timeout;
367367
struct mxl862xx_priv *priv = ds->priv;
368+
struct mxl862xx_register reg = {};
368369
struct mxl862xx_cfg cfg = {};
369370
int ret;
370371

@@ -376,14 +377,31 @@ int mxl862xx_wait_ready(struct dsa_switch *ds)
376377
goto not_ready_yet;
377378

378379
/* being able to perform CFGGET indicates that
379-
* the firmware is ready
380+
* the firmware is servicing API requests
380381
*/
381382
ret = MXL862XX_API_READ_QUIET(priv,
382383
MXL862XX_COMMON_CFGGET,
383384
cfg);
384385
if (ret)
385386
goto not_ready_yet;
386387

388+
/* The firmware application enables ingress SDMA on all
389+
* ports as the very last step of its boot-time default
390+
* configuration; driver configuration issued before that
391+
* point may be silently overwritten. The reset cleared
392+
* this bit on all ports and the driver has not touched
393+
* any port state yet at this point, so the bit of
394+
* logical port 0 (switch-internal port, never enabled by
395+
* the driver) being set means the firmware boot-time
396+
* configuration has completed.
397+
*/
398+
reg.addr = cpu_to_le16(MXL862XX_SDMA_PCTRLP(0));
399+
ret = MXL862XX_API_READ_QUIET(priv,
400+
MXL862XX_COMMON_REGISTERGET,
401+
reg);
402+
if (ret || !(le16_to_cpu(reg.data) & MXL862XX_SDMA_PCTRL_EN))
403+
goto not_ready_yet;
404+
387405
dev_info(ds->dev, "switch ready after %ums, firmware %u.%u.%u (build %u)\n",
388406
jiffies_to_msecs(jiffies - start),
389407
ver.iv_major, ver.iv_minor,

0 commit comments

Comments
 (0)