diff --git a/Documentation/devicetree/bindings/phy/allwinner,sun8i-r40-usb-phy.yaml b/Documentation/devicetree/bindings/phy/allwinner,sun8i-r40-usb-phy.yaml index 7e578e9f3b75..0555de23383f 100644 --- a/Documentation/devicetree/bindings/phy/allwinner,sun8i-r40-usb-phy.yaml +++ b/Documentation/devicetree/bindings/phy/allwinner,sun8i-r40-usb-phy.yaml @@ -98,6 +98,25 @@ properties: wirenboard,mux-on-id-pin: description: On Wiren Board 7 USB0 is muxed between USB-A connector for host mode and USB-C connector for peripheral mode. The mux is controller by voltage on perihperal connector. The same signal is used as USB ID. +patternProperties: + "^allwinner,usb[0-2]-tx-amplitude$": + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + default: 0 + description: + TX amplitude tune value for the given PHY. The default (0, the + minimum) may result in a high-speed TX signal too weak for some + link partners to receive. + + "^allwinner,usb[0-2]-tx-slew-rate$": + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + default: 5 + description: + TX slew rate tune value for the given PHY. + required: - "#phy-cells" - compatible diff --git a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi index 05531e205128..3743271d8a26 100644 --- a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi +++ b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi @@ -706,6 +706,12 @@ dr_mode = "otg"; wirenboard,mux-on-id-pin; + /* Maximum TX amplitude and slew rate: with the default TX tuning + * (minimum amplitude) some hosts fail to receive high-speed data + * from us: gadget enumeration fails and bulk data gets corrupted */ + allwinner,usb0-tx-amplitude = <3>; + allwinner,usb0-tx-slew-rate = <7>; + status = "okay"; }; @@ -980,7 +986,7 @@ &usb_otg { dr_mode = "peripheral"; status = "okay"; - maximum-speed = "full-speed"; + maximum-speed = "high-speed"; }; /* CPU RTC is useless since it doesn't have separate power supply. However we must keep it on to get proper LOSC for the rest of SoC.*/ diff --git a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi index 508fc00c10a4..17235ce9ecbe 100644 --- a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi +++ b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi @@ -837,6 +837,12 @@ dr_mode = "otg"; wirenboard,mux-on-id-pin; + /* Maximum TX amplitude and slew rate: with the default TX tuning + * (minimum amplitude) some hosts fail to receive high-speed data + * from us: gadget enumeration fails and bulk data gets corrupted */ + allwinner,usb0-tx-amplitude = <3>; + allwinner,usb0-tx-slew-rate = <7>; + status = "okay"; }; diff --git a/debian/changelog b/debian/changelog index f22248623dde..8c3ab3f4ef35 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +linux-wb (6.18.22-wb7) stable; urgency=medium + + * phy-sun4i-usb: allow tuning PHY TX amplitude/slew rate via DT; + set both to maximum on Wiren Board 7 USB0: fixes high-speed gadget + enumeration failures and bulk data corruption on some hosts + + -- Evgeny Boger Fri, 10 Jul 2026 03:24:30 +0300 + linux-wb (6.18.22-wb6) stable; urgency=medium * scripts: fix wb6x-bootlet build diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 3e9fd7badfce..1002c956a902 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -12,6 +12,7 @@ * Author: Sylwester Nawrocki */ +#include #include #include #include @@ -78,6 +79,17 @@ #define PHY_DISCON_TH_SEL 0x2a #define PHY_SQUELCH_DETECT 0x3c +/* + * TX tune field layout, written as 5 bits starting at + * PHY_TX_AMPLITUDE_TUNE. The default is inherited from the + * Allwinner BSP: amplitude 0 (minimum), slew rate 5. + */ +#define PHY_TX_AMPLITUDE_MASK GENMASK(1, 0) +#define PHY_TX_AMPLITUDE_MAX 3 +#define PHY_TX_SLEWRATE_MASK GENMASK(4, 2) +#define PHY_TX_SLEWRATE_MAX 7 +#define PHY_TX_TUNE_DEFAULT 0x14 + /* A83T specific control bits for PHY0 */ #define PHY_CTL_VBUSVLDEXT BIT(5) #define PHY_CTL_SIDDQ BIT(3) @@ -127,6 +139,7 @@ struct sun4i_usb_phy_data { struct clk *clk2; bool regulator_on; int index; + u32 tx_tune; } phys[MAX_PHYS]; /* phy0 / otg related variables */ struct extcon_dev *extcon; @@ -394,7 +407,7 @@ static int sun4i_usb_phy_init(struct phy *_phy) sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); /* Adjust PHY's magnitude and rate */ - sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); + sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, phy->tx_tune, 5); /* Disconnect threshold adjustment */ sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, @@ -915,6 +928,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) for (i = 0; i < MAX_PHYS; i++) { struct sun4i_usb_phy *phy = data->phys + i; char name[32]; + u32 val; if (data->cfg->missing_phys & BIT(i)) continue; @@ -928,6 +942,30 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) return PTR_ERR(phy->reset); } + phy->tx_tune = PHY_TX_TUNE_DEFAULT; + + snprintf(name, sizeof(name), "allwinner,usb%d-tx-amplitude", i); + if (!of_property_read_u32(np, name, &val)) { + if (val > PHY_TX_AMPLITUDE_MAX) { + dev_warn(dev, "%s out of range, clamping to %d\n", + name, PHY_TX_AMPLITUDE_MAX); + val = PHY_TX_AMPLITUDE_MAX; + } + phy->tx_tune &= ~PHY_TX_AMPLITUDE_MASK; + phy->tx_tune |= FIELD_PREP(PHY_TX_AMPLITUDE_MASK, val); + } + + snprintf(name, sizeof(name), "allwinner,usb%d-tx-slew-rate", i); + if (!of_property_read_u32(np, name, &val)) { + if (val > PHY_TX_SLEWRATE_MAX) { + dev_warn(dev, "%s out of range, clamping to %d\n", + name, PHY_TX_SLEWRATE_MAX); + val = PHY_TX_SLEWRATE_MAX; + } + phy->tx_tune &= ~PHY_TX_SLEWRATE_MASK; + phy->tx_tune |= FIELD_PREP(PHY_TX_SLEWRATE_MASK, val); + } + snprintf(name, sizeof(name), "usb%d_vbus", i); phy->vbus = devm_regulator_get_optional(dev, name); if (IS_ERR(phy->vbus)) {