diff --git a/kernel/rv1106-enablement/usb/rv1106_usb2phy_cfg.c.frag b/kernel/rv1106-enablement/usb/rv1106_usb2phy_cfg.c.frag index 216a46d..912a570 100644 --- a/kernel/rv1106-enablement/usb/rv1106_usb2phy_cfg.c.frag +++ b/kernel/rv1106-enablement/usb/rv1106_usb2phy_cfg.c.frag @@ -1,12 +1,14 @@ * RV1106 USB2 PHY: single OTG port at 0xff3e0000 (ported from the vendor driver; * fields map 1:1 to 6.18 except utmi_iddig -> utmi_id, and the 5.10-only - * 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. + * iddig_output/iddig_en/bvalid_grf_sel are dropped). Signal-quality phy_tuning + * was originally left off here; it is now implemented (patches/27-usb-phy-tuning.patch) + * because "functional without it" turned out not to mean reliable -- see #160. */ 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] = { diff --git a/patches/27-usb-phy-tuning.patch b/patches/27-usb-phy-tuning.patch new file mode 100644 index 0000000..d806c5c --- /dev/null +++ b/patches/27-usb-phy-tuning.patch @@ -0,0 +1,128 @@ +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] = {