From: Michael Dege michael.dege@renesas.com
[ Upstream commit e4a8db93b5ec9bca1cc66b295544899e3afd5e86 ]
R-Car S4-8 datasheet Rev.1.20 describes some additional register settings at the end of the initialization.
Signed-off-by: Michael Dege michael.dege@renesas.com Link: https://lore.kernel.org/r/20250703-renesas-serdes-update-v4-2-1db5629cac2b@r... Signed-off-by: Vinod Koul vkoul@kernel.org Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
YES
- What changed and where - Adds a safe read helper for banked registers: `r8a779f0_eth_serdes_read32()` to mirror the existing writer (drivers/phy/renesas/r8a779f0-ether-serdes.c:52). - Extends the late init sequence to perform two datasheet‑mandated strobes with explicit completion waits: - Pulse BIT(8) in `0x00c0` (bank `0x180`), then wait for status `0x0100` BIT(0) to assert and deassert (drivers/phy/renesas/r8a779f0-ether-serdes.c:343, drivers/phy/renesas/r8a779f0-ether-serdes.c:345, drivers/phy/renesas/r8a779f0-ether-serdes.c:349). - Pulse BIT(4) in `0x0144` (bank `0x180`), then wait for status `0x0180` BIT(0) to assert and deassert (drivers/phy/renesas/r8a779f0-ether-serdes.c:353, drivers/phy/renesas/r8a779f0-ether-serdes.c:355, drivers/phy/renesas/r8a779f0-ether-serdes.c:359). - These additions are contained to `r8a779f0_eth_serdes_hw_init_late()` which is invoked by `.power_on` (drivers/phy/renesas/r8a779f0-ether-serdes.c:366, drivers/phy/renesas/r8a779f0-ether-serdes.c:370).
- Why this is a bug fix - The commit implements “additional register settings at the end of the initialization” per R‑Car S4‑8 datasheet Rev.1.20. Omitting datasheet‑required init steps is a correctness issue that can manifest as unreliable bring‑up, failed calibration/training, or intermittent link. - The second strobe uses register `0x0144`, already used by the driver as a link “restart” control (drivers/phy/renesas/r8a779f0-ether- serdes.c:253 to drivers/phy/renesas/r8a779f0-ether-serdes.c:255), reinforcing that this affects required control sequencing rather than adding a feature.
- Risk and containment - Scope is limited to the Renesas R‑Car S4‑8 Ethernet SERDES PHY driver; no core or ABI changes; no DT changes. - Waits use `readl_poll_timeout_atomic()` with a bounded timeout (`R8A779F0_ETH_SERDES_TIMEOUT_US` = 100ms) preventing hangs (drivers/phy/renesas/r8a779f0-ether-serdes.c:20, drivers/phy/renesas/r8a779f0-ether-serdes.c:59 to drivers/phy/renesas/r8a779f0-ether-serdes.c:77). - The registers being toggled are already part of this IP’s register space; `0x0144` is pre‑existing in the code path. Worst case is a small increase in init time; best case fixes real bring‑up issues.
- Stable policy alignment - Fixes a hardware initialization deficiency per the vendor datasheet; small, self‑contained change; minimal regression risk; confined to a single driver file of a specific SoC family. - No architectural changes, no new features, no API surface modifications. This matches stable backport guidelines for important bug fixes with low risk.
- Recommendation - Backport to stable trees that include this driver (i.e., where `drivers/phy/renesas/r8a779f0-ether-serdes.c` exists). It improves reliability of SERDES initialization for R‑Car S4‑8 platforms without broader impact.
drivers/phy/renesas/r8a779f0-ether-serdes.c | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+)
diff --git a/drivers/phy/renesas/r8a779f0-ether-serdes.c b/drivers/phy/renesas/r8a779f0-ether-serdes.c index 3b2d8cef75e52..4d12d091b0ab0 100644 --- a/drivers/phy/renesas/r8a779f0-ether-serdes.c +++ b/drivers/phy/renesas/r8a779f0-ether-serdes.c @@ -49,6 +49,13 @@ static void r8a779f0_eth_serdes_write32(void __iomem *addr, u32 offs, u32 bank, iowrite32(data, addr + offs); }
+static u32 r8a779f0_eth_serdes_read32(void __iomem *addr, u32 offs, u32 bank) +{ + iowrite32(bank, addr + R8A779F0_ETH_SERDES_BANK_SELECT); + + return ioread32(addr + offs); +} + static int r8a779f0_eth_serdes_reg_wait(struct r8a779f0_eth_serdes_channel *channel, u32 offs, u32 bank, u32 mask, u32 expected) @@ -274,6 +281,7 @@ static int r8a779f0_eth_serdes_hw_init_late(struct r8a779f0_eth_serdes_channel *channel) { int ret; + u32 val;
ret = r8a779f0_eth_serdes_chan_setting(channel); if (ret) @@ -287,6 +295,26 @@ static int r8a779f0_eth_serdes_hw_init_late(struct r8a779f0_eth_serdes_channel
r8a779f0_eth_serdes_write32(channel->addr, 0x03d0, 0x380, 0x0000);
+ val = r8a779f0_eth_serdes_read32(channel->addr, 0x00c0, 0x180); + r8a779f0_eth_serdes_write32(channel->addr, 0x00c0, 0x180, val | BIT(8)); + ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0100, 0x180, BIT(0), 1); + if (ret) + return ret; + r8a779f0_eth_serdes_write32(channel->addr, 0x00c0, 0x180, val & ~BIT(8)); + ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0100, 0x180, BIT(0), 0); + if (ret) + return ret; + + val = r8a779f0_eth_serdes_read32(channel->addr, 0x0144, 0x180); + r8a779f0_eth_serdes_write32(channel->addr, 0x0144, 0x180, val | BIT(4)); + ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0180, 0x180, BIT(0), 1); + if (ret) + return ret; + r8a779f0_eth_serdes_write32(channel->addr, 0x0144, 0x180, val & ~BIT(4)); + ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0180, 0x180, BIT(0), 0); + if (ret) + return ret; + return r8a779f0_eth_serdes_monitor_linkup(channel); }