diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
index 86194d5..96b15dc 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -98,8 +98,10 @@
#include <linux/mii.h>
#include <linux/mutex.h>
#include <linux/of_irq.h>
+#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/if_bridge.h>
+#include <linux/workqueue.h>
#include <linux/if_vlan.h>
#include <linux/mdio.h>
#include <linux/phylink.h>
@@ -301,6 +303,29 @@
#define RTL8365MB_SDS_MISC_SGMII_SPD_MASK GENMASK(8, 7)
#define RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK BIT(6)
+/* Re-latch retry interval and attempt budget. Each attempt parks the
+ * SerDes for 20 ms, so a far end that never comes up must not be retried
+ * at this rate forever, and the interval must not be so tight that
+ * back-to-back parks starve a link that is about to latch. The count is
+ * reset to 1 whenever an episode starts and to 0 whenever the receiver
+ * latches. Episodes start when the conduit comes up, so with the
+ * interval below the budget is roughly RELATCH_MAX_TRIES seconds, which
+ * is generous for a far end that needs a moment to bring up its half of
+ * the link.
+ */
+#define RTL8365MB_D_SDS_RELATCH_INTERVAL (HZ)
+#define RTL8365MB_D_SDS_RELATCH_MAX_TRIES 15
+
+/* The SerDes stays parked until the restore write succeeds, so the write is
+ * attempted up to this many times before giving up until the next run.
+ */
+#define RTL8365MB_D_SDS_RESTORE_TRIES 3
+
+/* How often a latched (or out-of-budget) SerDes is checked, and re-latched
+ * if still down. Much slower than the re-latch interval.
+ */
+#define RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL (10 * HZ)
+
/* SerDes internal registers, accessed via the SDS_INDACS registers. The BMCR
* data path reset holds BMCR_ANENABLE | BMCR_ISOLATE while toggling the
* vendor-specific low bits from phase 1 to phase 2, which triggers a data path
@@ -934,11 +959,24 @@ struct rtl8365mb_port {
* @chip_info: chip-specific info about the attached switch
* @cpu: CPU tagging and CPU port configuration for this chip
* @mib_lock: prevent concurrent reads of MIB counters
+ * @sds_cfg_lock: serializes pcs_config(), pcs_disable(), teardown() and
+ * conduit_state_change(), which arm, start and stop the
+ * re-latch work
+ * @sds_lock: serializes access to the shared SDS_INDACS ADR/CMD/DATA window,
+ * reachable both from phylink's PCS callbacks and from the
+ * family D SerDes re-latch work
* @ports: per-port data
* @pcs: PCS for the SerDes external interface
* @sds_supported: SerDes interface modes can be advertised: the tuning
* parameters match the chip option (family C), or the chip
* is family D, which does not use the option
+ * @sds_reboot_nb: disables @sds_relatch before the devices are shut down
+ * @sds_relatch: re-latch edges and link health check (family D)
+ * @sds_relatch_count: next attempt of the re-latch episode, 0 once latched
+ * @sds_misc_target_val: SDS_MISC_CFG_MASK fields the re-latch work restores
+ * @sds_relatch_armed: pcs_config() set up 2500base-x; the re-latch work may run
+ * @sds_conduit_up: the conduit of the CPU port that carries the SerDes (port 6)
+ * is admin and oper up
*
* Private data for this driver.
*/@@ -948,9 +986,17 @@ struct rtl8365mb {
const struct rtl8365mb_chip_info *chip_info;
struct rtl8365mb_cpu cpu;
struct mutex mib_lock;
+ struct mutex sds_cfg_lock;
+ struct mutex sds_lock;
struct rtl8365mb_port ports[RTL8365MB_MAX_NUM_PORTS];
struct phylink_pcs pcs;
bool sds_supported;
+ struct notifier_block sds_reboot_nb;
+ struct delayed_work sds_relatch;
+ unsigned int sds_relatch_count;
+ u32 sds_misc_target_val;
+ bool sds_relatch_armed;
+ bool sds_conduit_up;
};
#define pcs_to_rtl8365mb(_pcs) container_of((_pcs), struct rtl8365mb, pcs)@@ -1354,43 +1400,53 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port,
static int rtl8365mb_sds_write(struct realtek_priv *priv, u8 index,
u16 addr, u16 data)
{
+ struct rtl8365mb *mb = priv->chip_data;
int ret;
+ mutex_lock(&mb->sds_lock);
+
ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_DATA_REG, data);
if (ret)
- return ret;
+ goto out_unlock;
ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_ADR_REG, addr);
if (ret)
- return ret;
+ goto out_unlock;
/* The SerDes indirect access engine completes the command within the
* register write transaction, so there is no need to wait or poll for
* completion before the next access, matching the vendor driver.
*/
- return regmap_write(priv->map, RTL8365MB_SDS_INDACS_CMD_REG,
- RTL8365MB_SDS_INDACS_CMD_RUN_MASK |
- RTL8365MB_SDS_INDACS_CMD_WR_MASK |
- FIELD_PREP(RTL8365MB_SDS_INDACS_CMD_INDEX_MASK,
- index));
+ ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_CMD_REG,
+ RTL8365MB_SDS_INDACS_CMD_RUN_MASK |
+ RTL8365MB_SDS_INDACS_CMD_WR_MASK |
+ FIELD_PREP(RTL8365MB_SDS_INDACS_CMD_INDEX_MASK,
+ index));
+
+out_unlock:
+ mutex_unlock(&mb->sds_lock);
+ return ret;
}
static int rtl8365mb_sds_read(struct realtek_priv *priv, u8 index,
u16 addr, u16 *data)
{
+ struct rtl8365mb *mb = priv->chip_data;
u32 val;
int ret;
+ mutex_lock(&mb->sds_lock);
+
ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_ADR_REG, addr);
if (ret)
- return ret;
+ goto out_unlock;
ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_CMD_REG,
RTL8365MB_SDS_INDACS_CMD_RUN_MASK |
FIELD_PREP(RTL8365MB_SDS_INDACS_CMD_INDEX_MASK,
index));
if (ret)
- return ret;
+ goto out_unlock;
/* Wait for the indirect read to complete: the engine clears the BUSY
* bit once the data register holds the result.@@ -1400,15 +1456,19 @@ static int rtl8365mb_sds_read(struct realtek_priv *priv, u8 index,
!(val & RTL8365MB_SDS_INDACS_CMD_BUSY_MASK),
10, 1000);
if (ret)
- return ret;
+ goto out_unlock;
ret = regmap_read(priv->map, RTL8365MB_SDS_INDACS_DATA_REG, &val);
if (ret)
- return ret;
+ goto out_unlock;
*data = val;
- return 0;
+ ret = 0;
+
+out_unlock:
+ mutex_unlock(&mb->sds_lock);
+ return ret;
}
/* The vendor driver selects between two sets of SerDes tuning parameters based
@@ -1509,6 +1569,37 @@ static int rtl8365mb_sds_raise_rate_limits(struct realtek_priv *priv)
RTL8365MB_PORT6_EGRESSBW_CTRL1_MASK);
}
+static void rtl8365mb_sds_relatch_start(struct rtl8365mb *mb)
+{
+ WRITE_ONCE(mb->sds_relatch_count, 1);
+ schedule_delayed_work(&mb->sds_relatch, 0);
+}
+
+/* Put SDS_MISC back to the target mode. The lane stays parked (mode field
+ * DISABLE) until this succeeds, so retry a few times before giving up until
+ * the next run.
+ */
+static int rtl8365mb_sds_relatch_restore(struct rtl8365mb *mb)
+{
+ struct realtek_priv *priv = mb->priv;
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < RTL8365MB_D_SDS_RESTORE_TRIES; i++) {
+ ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
+ RTL8365MB_D_SDS_MISC_CFG_MASK,
+ READ_ONCE(mb->sds_misc_target_val));
+ if (!ret)
+ return 0;
+ if (i < RTL8365MB_D_SDS_RESTORE_TRIES - 1)
+ usleep_range(1000, 2000);
+ }
+
+ dev_err_ratelimited(priv->dev, "failed to restore SDS_MISC: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+}
+
static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface,
const unsigned long *advertising,@@ -1530,6 +1621,17 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
is_d = rtl8365mb_is_d(priv);
+ mutex_lock(&mb->sds_cfg_lock);
+
+ /* Cancel any in-flight re-latch edge before touching SDS_MISC: the
+ * work parks SDS_MISC and restores the target value after a sleep, so
+ * it could otherwise write a stale target over the new configuration.
+ */
+ if (is_d) {
+ WRITE_ONCE(mb->sds_relatch_armed, false);
+ cancel_delayed_work_sync(&mb->sds_relatch);
+ }
+
/* Select the appropriate tuning table and SDS mode */
if (interface == PHY_INTERFACE_MODE_2500BASEX) {
if (is_d) {@@ -1566,12 +1668,12 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
RTL8365MB_CHIP_RESET_DW8051_MASK,
RTL8365MB_CHIP_RESET_DW8051_MASK);
if (ret)
- return ret;
+ goto out_unlock;
ret = regmap_update_bits(priv->map, RTL8365MB_MISC_CFG0_REG,
RTL8365MB_MISC_CFG0_DW8051_EN_MASK, 0);
if (ret)
- return ret;
+ goto out_unlock;
/* The vendor driver clears the line rate bypass for all interface
* modes except TMII.
@@ -1579,14 +1681,14 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
ret = regmap_update_bits(priv->map, RTL8365MB_BYPASS_LINE_RATE_REG,
RTL8365MB_SDS_BYPASS_LINE_RATE_MASK, 0);
if (ret)
- return ret;
+ goto out_unlock;
/* Tune the SerDes with vendor-prescribed parameters */
for (i = 0; i < sds_jam_size; i++) {
ret = rtl8365mb_sds_write(priv, sds_index,
sds_jam[i].reg, sds_jam[i].val);
if (ret)
- return ret;
+ goto out_unlock;
}
/* Family-specific post-tuning configuration */@@ -1600,7 +1702,7 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
RTL8365MB_D_FIBER_CFG2_RX_DISABLE_MASK,
RTL8365MB_D_FIBER_CFG2_RX_DISABLE_SDS0);
if (ret)
- return ret;
+ goto out_unlock;
misc_mask = RTL8365MB_D_SDS_MISC_CFG_MASK;
misc_val = RTL8365MB_D_SDS_MISC_PA33PC_EN |
@@ -1618,16 +1720,18 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
misc_mask, misc_val);
if (ret)
- return ret;
+ goto out_unlock;
- if (!is_d) {
+ if (is_d) {
+ WRITE_ONCE(mb->sds_misc_target_val, misc_val);
+ } else {
val = sds_mode << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(id);
ret = regmap_update_bits(priv->map,
RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(id),
RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(id),
val);
if (ret)
- return ret;
+ goto out_unlock;
}
/* Take the SerDes out of reset. The vendor driver does this only@@ -1637,7 +1741,7 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
RTL8365MB_SDS_REG_RESET,
RTL8365MB_SDS_RESET_DEASSERT);
if (ret)
- return ret;
+ goto out_unlock;
/* Reset the SerDes data path and resync its PLL, mirroring what the
* vendor firmware does right after deasserting the SerDes reset.
@@ -1648,13 +1752,13 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
RTL8365MB_SDS_REG_BMCR,
RTL8365MB_SDS_BMCR_DPRST_PHASE1);
if (ret)
- return ret;
+ goto out_unlock;
ret = rtl8365mb_sds_write(priv, sds_index,
RTL8365MB_SDS_REG_BMCR,
RTL8365MB_SDS_BMCR_DPRST_PHASE2);
if (ret)
- return ret;
+ goto out_unlock;
/* Keep SGMII in-band autonegotiation disabled: the link parameters are
* forced from rtl8365mb_pcs_link_up() instead (family C), or from
@@ -1663,13 +1767,207 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
ret = rtl8365mb_sds_read(priv, sds_index,
RTL8365MB_SDS_REG_NWAY, &val);
if (ret)
- return ret;
+ goto out_unlock;
val &= ~RTL8365MB_SDS_NWAY_EN_MASK;
val |= RTL8365MB_SDS_NWAY_RESTART_MASK;
- return rtl8365mb_sds_write(priv, sds_index,
- RTL8365MB_SDS_REG_NWAY, val);
+ ret = rtl8365mb_sds_write(priv, sds_index,
+ RTL8365MB_SDS_REG_NWAY, val);
+ if (ret)
+ goto out_unlock;
+
+ /* The re-latch edge has only been observed on a CPU trunk with
+ * HSGMII, so the work is only armed for 2500base-x on a CPU port.
+ * It starts once the conduit is up.
+ */
+ if (is_d) {
+ WRITE_ONCE(mb->sds_relatch_armed,
+ interface == PHY_INTERFACE_MODE_2500BASEX &&
+ dsa_is_cpu_port(&priv->ds,
+ RTL8365MB_SDS_EXT_INTERFACE_PORT));
+ /* With the conduit already up no conduit_state_change() will
+ * follow, so start the episode here.
+ */
+ if (READ_ONCE(mb->sds_relatch_armed) &&
+ READ_ONCE(mb->sds_conduit_up))
+ rtl8365mb_sds_relatch_start(mb);
+ }
+
+ ret = 0;
+
+out_unlock:
+ mutex_unlock(&mb->sds_cfg_lock);
+ return ret;
+}
+
+static void rtl8365mb_conduit_state_change(struct dsa_switch *ds,
+ const struct net_device *conduit,
+ bool operational)
+{
+ struct dsa_port *cpu_dp = conduit->dsa_ptr;
+ struct realtek_priv *priv = ds->priv;
+ struct rtl8365mb *mb = priv->chip_data;
+
+ if (!rtl8365mb_is_d(priv))
+ return;
+
+ /* DSA reports the conduits of every switch in the tree, and this
+ * chip can have two CPU ports (SerDes on port 6, RGMII on port 7).
+ * Only the conduit of this switch's SerDes CPU port matters here.
+ */
+ if (cpu_dp->ds != ds ||
+ cpu_dp->index != RTL8365MB_SDS_EXT_INTERFACE_PORT)
+ return;
+
+ mutex_lock(&mb->sds_cfg_lock);
+
+ WRITE_ONCE(mb->sds_conduit_up, operational);
+ cancel_delayed_work_sync(&mb->sds_relatch);
+ if (operational && READ_ONCE(mb->sds_relatch_armed))
+ rtl8365mb_sds_relatch_start(mb);
+
+ mutex_unlock(&mb->sds_cfg_lock);
+}
+
+/* The family D receiver latches on a DISABLE -> mode edge in SDS_MISC
+ * rather than on the value, and only once the far-end MAC has brought up
+ * its half of the link. No local signal predicts that (bit 8 of the SDS
+ * link-status word does not change ahead of it), so the work cannot wait
+ * for it, only check afterwards whether an edge took. Nor does phylink
+ * call rtl8365mb_pcs_get_state() for a link without in-band signalling
+ * such as the fixed-link CPU trunk (mb->pcs.poll only arms phylink's own
+ * poll for in-band links), so this cannot rely on being polled at all.
+ * Instead, an episode starts when the conduit comes up (see
+ * rtl8365mb_conduit_state_change(), or pcs_config() if it is already up)
+ * and this work re-arms itself.
+ *
+ * An episode is capped at RTL8365MB_D_SDS_RELATCH_MAX_TRIES edges, spaced
+ * RTL8365MB_D_SDS_RELATCH_INTERVAL apart, so that a far end which never
+ * comes up costs a bounded burst of 20 ms parks. The run after the last
+ * edge only checks whether it took and reports failure; from then on one
+ * edge is driven per RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL, so a far end
+ * that comes up late is recovered without a reconfiguration. Once the
+ * receiver has latched the work just polls the link at the same slow
+ * interval; finding it down then starts a new episode, e.g. after the far
+ * end reset its PLL without a reconfiguration.
+ *
+ * sds_relatch_count is the number of the next attempt of the episode: 0
+ * while latched, 1..MAX_TRIES in budget, MAX_TRIES + 1 for the run that
+ * only reports, and above that once out of budget.
+ */
+static void rtl8365mb_sds_relatch_work(struct work_struct *work)
+{
+ struct rtl8365mb *mb = container_of(to_delayed_work(work),
+ struct rtl8365mb, sds_relatch);
+ struct realtek_priv *priv = mb->priv;
+ u32 park = (READ_ONCE(mb->sds_misc_target_val) &
+ ~RTL8365MB_D_SDS_MISC_MODE_FIELD_MASK) |
+ RTL8365MB_D_PORT_SDS_MODE_DISABLE;
+ unsigned long delay = RTL8365MB_D_SDS_RELATCH_INTERVAL;
+ unsigned int attempts;
+ u16 status;
+ u32 misc;
+ int ret;
+
+ /* An earlier run may have failed to restore the target mode, which
+ * leaves the lane parked. The mode field in SDS_MISC says so: put it
+ * back before anything else.
+ */
+ ret = regmap_read(priv->map, RTL8365MB_SDS_MISC_REG, &misc);
+ if (ret) {
+ dev_err_ratelimited(priv->dev,
+ "failed to read SDS_MISC: %pe\n",
+ ERR_PTR(ret));
+ goto rearm;
+ }
+ if ((misc & RTL8365MB_D_SDS_MISC_MODE_FIELD_MASK) ==
+ RTL8365MB_D_PORT_SDS_MODE_DISABLE) {
+ rtl8365mb_sds_relatch_restore(mb);
+ goto rearm;
+ }
+
+ /* The condition that queued this work may already be stale by the
+ * time it runs (e.g. an earlier edge from a prior attempt just
+ * latched). Parking an already-live link would drop it for no
+ * reason.
+ */
+ ret = rtl8365mb_sds_read(priv, RTL8365MB_D_SDS_EXT0_INDEX,
+ RTL8365MB_SDS_REG_LINK_STATUS, &status);
+ if (ret) {
+ dev_err_ratelimited(priv->dev,
+ "failed to read SerDes link status: %pe\n",
+ ERR_PTR(ret));
+ goto rearm;
+ }
+ if (status & RTL8365MB_SDS_LINK_STATUS_LINK_MASK) {
+ /* Latched: end the episode, keep watching for a later loss. */
+ WRITE_ONCE(mb->sds_relatch_count, 0);
+ delay = RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL;
+ goto rearm;
+ }
+
+ attempts = READ_ONCE(mb->sds_relatch_count);
+ if (!attempts) {
+ dev_warn(priv->dev,
+ "SerDes link lost outside a reconfiguration; re-latching\n");
+ attempts = 1;
+ } else if (attempts == RTL8365MB_D_SDS_RELATCH_MAX_TRIES + 1) {
+ /* This run only checked whether the last in-budget edge
+ * took. The first out-of-budget edge comes one healthcheck
+ * interval from now, not on this pass.
+ */
+ WRITE_ONCE(mb->sds_relatch_count, attempts + 1);
+ dev_warn(priv->dev,
+ "SerDes did not latch after %u re-latch attempts; still retrying every %d s\n",
+ RTL8365MB_D_SDS_RELATCH_MAX_TRIES,
+ RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL / HZ);
+ delay = RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL;
+ goto rearm;
+ }
+
+ if (attempts <= RTL8365MB_D_SDS_RELATCH_MAX_TRIES)
+ WRITE_ONCE(mb->sds_relatch_count, attempts + 1);
+ else
+ /* Past the fast-retry budget. The receiver latches only on a
+ * DISABLE -> mode edge and nothing re-runs pcs_config() for a
+ * link without in-band signalling, so keep driving one edge
+ * per interval.
+ */
+ delay = RTL8365MB_D_SDS_HEALTHCHECK_INTERVAL;
+
+ ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
+ RTL8365MB_D_SDS_MISC_CFG_MASK, park);
+ if (ret) {
+ dev_err_ratelimited(priv->dev,
+ "failed to park SDS_MISC: %pe\n",
+ ERR_PTR(ret));
+ goto rearm;
+ }
+
+ usleep_range(20000, 21000);
+
+ if (rtl8365mb_sds_relatch_restore(mb)) {
+ /* The SerDes is left parked: come back soon rather than
+ * at the (possibly 10 s) pace chosen above.
+ */
+ delay = RTL8365MB_D_SDS_RELATCH_INTERVAL;
+ goto rearm;
+ }
+
+ dev_dbg(priv->dev, "SerDes re-latch edge driven (attempt %u)\n",
+ attempts);
+
+ /* This edge may not have taken either; the next run checks. */
+rearm:
+ schedule_delayed_work(&mb->sds_relatch, delay);
+}
+
+static void rtl8365mb_sds_relatch_disable(void *data)
+{
+ struct rtl8365mb *mb = data;
+
+ disable_delayed_work_sync(&mb->sds_relatch);
}
static bool rtl8365mb_interface_is_serdes(phy_interface_t interface)@@ -1823,11 +2121,27 @@ static void rtl8365mb_pcs_link_up(struct phylink_pcs *pcs,
}
}
+static void rtl8365mb_pcs_disable(struct phylink_pcs *pcs)
+{
+ struct rtl8365mb *mb = pcs_to_rtl8365mb(pcs);
+
+ if (!rtl8365mb_is_d(mb->priv))
+ return;
+
+ mutex_lock(&mb->sds_cfg_lock);
+
+ WRITE_ONCE(mb->sds_relatch_armed, false);
+ cancel_delayed_work_sync(&mb->sds_relatch);
+
+ mutex_unlock(&mb->sds_cfg_lock);
+}
+
static const struct phylink_pcs_ops rtl8365mb_pcs_ops = {
.pcs_inband_caps = rtl8365mb_pcs_inband_caps,
.pcs_config = rtl8365mb_pcs_config,
.pcs_get_state = rtl8365mb_pcs_get_state,
.pcs_link_up = rtl8365mb_pcs_link_up,
+ .pcs_disable = rtl8365mb_pcs_disable,
};
static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,@@ -3560,6 +3874,17 @@ out_error:
static void rtl8365mb_teardown(struct dsa_switch *ds)
{
struct realtek_priv *priv = ds->priv;
+ struct rtl8365mb *mb = priv->chip_data;
+
+ if (rtl8365mb_is_d(priv)) {
+ mutex_lock(&mb->sds_cfg_lock);
+
+ WRITE_ONCE(mb->sds_relatch_armed, false);
+ WRITE_ONCE(mb->sds_conduit_up, false);
+ cancel_delayed_work_sync(&mb->sds_relatch);
+
+ mutex_unlock(&mb->sds_cfg_lock);
+ }
rtl8365mb_stats_teardown(priv);
rtl8365mb_irq_teardown(priv);@@ -3592,6 +3917,23 @@ static int rtl8365mb_get_chip_id_and_ver(struct regmap *map, u32 *id, u32 *ver)
return 0;
}
+static int rtl8365mb_sds_reboot_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct rtl8365mb *mb = container_of(nb, struct rtl8365mb,
+ sds_reboot_nb);
+
+ /* Runs before device_shutdown() on reboot, halt, power off and kexec.
+ * disable_delayed_work_sync() waits for a running pass and keeps
+ * pcs_config() from queueing the work again, so the bus is not
+ * touched once it is being shut down. If the last restore failed the
+ * lane stays parked until the next pcs_config().
+ */
+ disable_delayed_work_sync(&mb->sds_relatch);
+
+ return NOTIFY_DONE;
+}
+
static int rtl8365mb_detect(struct realtek_priv *priv)
{
struct rtl8365mb *mb = priv->chip_data;@@ -3627,6 +3969,34 @@ static int rtl8365mb_detect(struct realtek_priv *priv)
priv->num_ports = mb->chip_info->family_info->num_ports;
mb->priv = priv;
+
+ ret = devm_mutex_init(priv->dev, &mb->sds_lock);
+ if (ret)
+ return ret;
+
+ ret = devm_mutex_init(priv->dev, &mb->sds_cfg_lock);
+ if (ret)
+ return ret;
+
+ if (rtl8365mb_is_d(priv)) {
+ /* Registered after sds_lock, so on release the work is
+ * disabled before the mutex is destroyed.
+ */
+ INIT_DELAYED_WORK(&mb->sds_relatch,
+ rtl8365mb_sds_relatch_work);
+ ret = devm_add_action_or_reset(priv->dev,
+ rtl8365mb_sds_relatch_disable,
+ mb);
+ if (ret)
+ return ret;
+
+ mb->sds_reboot_nb.notifier_call = rtl8365mb_sds_reboot_notify;
+ ret = devm_register_reboot_notifier(priv->dev,
+ &mb->sds_reboot_nb);
+ if (ret)
+ return ret;
+ }
+
mb->cpu.trap_port = RTL8365MB_MAX_NUM_PORTS;
mb->cpu.insert = RTL8365MB_CPU_INSERT_TO_ALL;
mb->cpu.position = RTL8365MB_CPU_POS_AFTER_SA;@@ -3711,6 +4081,7 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = {
.port_vlan_add = rtl8365mb_port_vlan_add,
.port_vlan_del = rtl8365mb_port_vlan_del,
.port_vlan_filtering = rtl8365mb_port_vlan_filtering,
+ .conduit_state_change = rtl8365mb_conduit_state_change,
.get_strings = rtl8365mb_get_strings,
.get_ethtool_stats = rtl8365mb_get_ethtool_stats,
.get_sset_count = rtl8365mb_get_sset_count,--
2.53.0