diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -247,6 +247,7 @@ struct device *dev; struct regmap *grf; struct regmap *usbgrf; + void __iomem *phy_base; struct clk_bulk_data *clks; struct clk *clk480m; struct clk_hw clk480m_hw; @@ -1417,6 +1418,19 @@ return -EINVAL; } + /* + * RV1106 tunes signal quality through the PHY's own register block, which + * is this node's "reg" resource and is distinct from the GRF regmap above. + * Map it only where a tuning callback needs it; every other SoC here tunes + * through the GRF and must keep working with phy_base left NULL. + */ + if (of_device_is_compatible(np, "rockchip,rv1106-usb2phy")) { + rphy->phy_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rphy->phy_base)) + return dev_err_probe(dev, PTR_ERR(rphy->phy_base), + "failed to map phy registers\n"); + } + rphy->phy_reset = devm_reset_control_get_optional(dev, "phy"); if (IS_ERR(rphy->phy_reset)) return PTR_ERR(rphy->phy_reset); @@ -1506,6 +1520,30 @@ return ret; } +/* + * Direct PHY-register accessors. Most SoCs here tune through the GRF regmap, + * but the RV1106's signal-quality registers live in the PHY's own block, which + * is mapped separately into rphy->phy_base. + */ +static inline void phy_update_bits(void __iomem *reg, u32 mask, u32 val) +{ + u32 tmp = readl(reg); + + tmp &= ~mask; + tmp |= val & mask; + writel(tmp, reg); +} + +static inline void phy_set_bits(void __iomem *reg, u32 bits) +{ + writel(readl(reg) | bits, reg); +} + +static inline void phy_clear_bits(void __iomem *reg, u32 bits) +{ + writel(readl(reg) & ~bits, reg); +} + static int rk3128_usb2phy_tuning(struct rockchip_usb2phy *rphy) { /* Turn off differential receiver in suspend mode */ @@ -2291,10 +2329,65 @@ * iddig_output/iddig_en/bvalid_grf_sel are dropped). Signal-quality phy_tuning is * left off for now (guarded, so NULL is safe) — the phy is functional without it. */ +/* + * RV1106 USB2 PHY signal-quality tuning, ported from the vendor 5.10 driver + * (drivers/phy/rockchip/phy-rockchip-inno-usb2.c, rv1106_usb2phy_tuning). + * + * The 6.18 forward-port originally omitted this ("the phy is functional without + * it"). It is functional, but not reliable: with these registers left at reset + * defaults the HS link is electrically marginal under sustained traffic. On a + * bench panel a USB-Ethernet dongle would stall a bulk transfer after a few + * minutes; the r8152 Tx-timeout recovery then issues an xHCI Stop Endpoint + * command which the controller never answers, and xhci-hcd declares the + * controller dead and tears the bus down (flare-edge issue #160). Measured + * carrier-on to death across four runs: 14s, 519s, 521s, 584s -- a stochastic + * spread, not a timer, which is what marginal signalling looks like. + * + * Pre-emphasis strength is silicon-revision dependent. The vendor driver picks + * it from rockchip_get_cpu_version(), which does not exist in mainline; it + * derives from RV1106_OS_REG1 (0xff020204) & 0x7. Our panels read 1, i.e. the + * non-zero branch, so 3'b001 is pinned here. If a board ever reports version 0 + * it wants 3'b011 instead -- see the vendor driver. + */ +static int rv1106_usb2phy_tuning(struct rockchip_usb2phy *rphy) +{ + /* Mapped only for this compatible; refuse to poke a NULL base. */ + if (!rphy->phy_base) + return -ENODEV; + + /* Always enable pre-emphasis in SOF & EOP & chirp & non-chirp state */ + phy_update_bits(rphy->phy_base + 0x30, GENMASK(2, 0), 0x07); + + /* Set Tx HS pre_emphasize strength to 3'b001 (cpu_version != 0) */ + phy_update_bits(rphy->phy_base + 0x40, GENMASK(5, 3), (0x01 << 3)); + + /* Set RX Squelch trigger point configure to 4'b0000(112.5 mV) */ + phy_update_bits(rphy->phy_base + 0x64, GENMASK(6, 3), (0x00 << 3)); + + /* Turn off differential receiver by default to save power */ + phy_clear_bits(rphy->phy_base + 0x100, BIT(6)); + + /* Set 45ohm HS ODT value to 5'b10111 to increase driver strength */ + phy_update_bits(rphy->phy_base + 0x11c, GENMASK(4, 0), 0x17); + + /* Set Tx HS eye height tuning to 3'b011(462 mV) */ + phy_update_bits(rphy->phy_base + 0x124, GENMASK(4, 2), (0x03 << 2)); + + /* Bypass Squelch detector calibration */ + phy_update_bits(rphy->phy_base + 0x1a4, GENMASK(7, 4), (0x01 << 4)); + phy_update_bits(rphy->phy_base + 0x1b4, GENMASK(7, 4), (0x01 << 4)); + + /* Set HS disconnect detect mode to single ended detect mode */ + phy_set_bits(rphy->phy_base + 0x70, BIT(2)); + + return 0; +} + static const struct rockchip_usb2phy_cfg rv1106_phy_cfgs[] = { { .reg = 0xff3e0000, .num_ports = 1, + .phy_tuning = rv1106_usb2phy_tuning, .clkout_ctl = { 0x0058, 4, 4, 1, 0 }, .port_cfgs = { [USB2PHY_PORT_OTG] = {