* [PATCH v3 1/2] dt-bindings: phy: qcom,usb-hs-phy: add qcom,hs-drv-slope
2026-06-16 13:26 [PATCH v3 0/2] phy: qcom: usb-hs: MSM8x60 vendor ULPI init Herman van Hazendonk
@ 2026-06-16 13:26 ` Herman van Hazendonk
2026-06-22 9:21 ` Krzysztof Kozlowski
2026-06-16 13:26 ` [PATCH v3 2/2] phy: qcom: usb-hs: program MSM8x60 vendor ULPI registers on power-on Herman van Hazendonk
1 sibling, 1 reply; 4+ messages in thread
From: Herman van Hazendonk @ 2026-06-16 13:26 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Philipp Zabel, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel, llvm,
Herman van Hazendonk, konrad.dybcio, dmitry.baryshkov
The MSM8x60 / APQ8060 PHY needs three vendor ULPI register tweaks for
stable USB operation: pre-emphasis level, CDR auto-reset and SE1
gating in registers 0x32 and 0x36. A survey of MSM8x60-class
downstream board files (Qualcomm SURF/FFA/Fluid/Dragon, Samsung
Galaxy S2 family, Sony Xperia, HTC and HP TouchPad) shows that those
three values are identical across every reference board and can be
hardcoded in the driver behind the existing
qcom,usb-hs-phy-msm8660 compatible.
The only board-specific value is the 4-bit HS driver slope in bits
[3:0] of register 0x32:
HP TouchPad 5
HTC MSM8660 ports 1
Qualcomm / Samsung / Sony reference boards 0 (silicon default)
Add a qcom,hs-drv-slope property carrying that 4-bit value, valid
only on the qcom,usb-hs-phy-msm8660 variant. When the property is
absent the driver leaves the silicon default in place, matching the
behaviour of the Qualcomm reference platform.
No public Qualcomm documentation describes how the 4-bit value maps
to an actual slew rate, V/ns or %; the bits are an opaque hardware
control whose meaning only Qualcomm knows. The legal range (0..15)
comes from the field width in the downstream
arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
(ULPI_HSDRVSLOPE_MASK == 0x0F). Boards must therefore copy the
value from their downstream/vendor kernel; this is a measured /
tuned-per-layout knob, not a derived one.
Assisted-by: Claude:claude-opus-4-7 dt_binding_check checkpatch
Assisted-by: Sashiko:claude-opus-4-7
Signed-off-by: Herman van Hazendonk <github.com@herrie.org>
---
.../devicetree/bindings/phy/qcom,usb-hs-phy.yaml | 89 +++++++++++++++-------
1 file changed, 63 insertions(+), 26 deletions(-)
diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.yaml
index e03b516c698c..e605f5683f7d 100644
--- a/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.yaml
@@ -9,32 +9,43 @@ title: Qualcomm's USB HS PHY
maintainers:
- Bjorn Andersson <bjorn.andersson@linaro.org>
-if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,usb-hs-phy-apq8064
- - qcom,usb-hs-phy-msm8660
- - qcom,usb-hs-phy-msm8960
-then:
- properties:
- resets:
- maxItems: 1
-
- reset-names:
- const: por
-
-else:
- properties:
- resets:
- minItems: 2
- maxItems: 2
-
- reset-names:
- items:
- - const: phy
- - const: por
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,usb-hs-phy-apq8064
+ - qcom,usb-hs-phy-msm8660
+ - qcom,usb-hs-phy-msm8960
+ then:
+ properties:
+ resets:
+ maxItems: 1
+
+ reset-names:
+ const: por
+
+ else:
+ properties:
+ resets:
+ minItems: 2
+ maxItems: 2
+
+ reset-names:
+ items:
+ - const: phy
+ - const: por
+
+ - if:
+ not:
+ properties:
+ compatible:
+ contains:
+ const: qcom,usb-hs-phy-msm8660
+ then:
+ properties:
+ qcom,hs-drv-slope: false
properties:
compatible:
@@ -85,6 +96,15 @@ properties:
the address is offset from the ULPI_EXT_VENDOR_SPECIFIC address
- description: value
+ qcom,hs-drv-slope:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: >
+ 4-bit HS driver slope written to bits [3:0] of ULPI vendor
+ register 0x32. Board-specific tuning value; absent means
+ leave silicon default. Only valid on qcom,usb-hs-phy-msm8660.
+ minimum: 0
+ maximum: 15
+
required:
- clocks
- clock-names
@@ -114,3 +134,20 @@ examples:
};
};
};
+
+ - |
+ usb-controller {
+ #reset-cells = <1>;
+
+ ulpi {
+ phy {
+ compatible = "qcom,usb-hs-phy-msm8660", "qcom,usb-hs-phy";
+ #phy-cells = <0>;
+ clocks = <&clk 0>, <&clk 1>;
+ clock-names = "ref", "sleep";
+ resets = <&otg 0>;
+ reset-names = "por";
+ qcom,hs-drv-slope = <5>;
+ };
+ };
+ };
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH v3 2/2] phy: qcom: usb-hs: program MSM8x60 vendor ULPI registers on power-on
2026-06-16 13:26 [PATCH v3 0/2] phy: qcom: usb-hs: MSM8x60 vendor ULPI init Herman van Hazendonk
2026-06-16 13:26 ` [PATCH v3 1/2] dt-bindings: phy: qcom,usb-hs-phy: add qcom,hs-drv-slope Herman van Hazendonk
@ 2026-06-16 13:26 ` Herman van Hazendonk
1 sibling, 0 replies; 4+ messages in thread
From: Herman van Hazendonk @ 2026-06-16 13:26 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Philipp Zabel, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel, llvm,
Herman van Hazendonk, konrad.dybcio, dmitry.baryshkov
The MSM8x60-class PHY needs three vendor-register tweaks for stable
USB operation, which the legacy msm_otg driver used to drive from
board platform data. A survey of every MSM8x60-class downstream tree
(Qualcomm SURF/FFA/Fluid/Dragon, Samsung Galaxy S2 family, Sony
Xperia, HTC MSM8660 ports and HP TouchPad) shows that two of the
three settings are identical across every board:
- reg 0x32 [5:4] = 11b: pre-emphasis level set to 20%
- reg 0x36 bit 1 = 1, bit 2 = 1: CDR auto-reset and SE1 gating
disabled (the legacy driver inverts these bits, so setting them
disables the function)
Hardcode those two unconditionally behind the existing
qcom,usb-hs-phy-msm8660 compatible. The bit-level documentation
comes from the Code Aurora downstream header
arch/arm/mach-msm/include/mach/msm_hsusb_hw.h, which Samsung and HP
both shipped byte-for-byte identical.
The third setting -- reg 0x32 [3:0] HS driver slope -- is genuinely
board-specific (HP TouchPad uses 5, HTC MSM8660 ports use 1, every
Qualcomm/Samsung/Sony reference board leaves the silicon default of
0) and is consumed from the new qcom,hs-drv-slope DT property. When
the property is absent the silicon default is preserved.
No public Qualcomm documentation describes how the 4-bit slope value
maps to an actual slew rate, V/ns or %; the field is an opaque
hardware control whose semantics only Qualcomm knows. Boards must
copy the value from their vendor / downstream kernel -- this is a
measured / tuned-per-layout knob, not a derived one. We program the
4 bits verbatim and trust the silicon to do the right thing.
The writes live behind a runtime flag that only matches
"qcom,usb-hs-phy-msm8660" so the existing MSM8226/8916/8960/8974
consumers are untouched. They are issued *after*
reset_control_reset() so the values survive the register restore the
reset performs.
Note: HTC MSM8660 vendor kernels additionally write 0x0C to reg 0x31.
The HP TouchPad webOS kernel does not touch that register and USB is
stable without it, so those bits are omitted here until documentation
is available to explain what they control.
Assisted-by: Claude:claude-opus-4-7 sparse smatch clang-analyzer coccinelle checkpatch
Assisted-by: Sashiko:claude-opus-4-7
Signed-off-by: Herman van Hazendonk <github.com@herrie.org>
---
drivers/phy/qualcomm/phy-qcom-usb-hs.c | 68 ++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs.c b/drivers/phy/qualcomm/phy-qcom-usb-hs.c
index 98a18987f1be..a7649a09e82c 100644
--- a/drivers/phy/qualcomm/phy-qcom-usb-hs.c
+++ b/drivers/phy/qualcomm/phy-qcom-usb-hs.c
@@ -20,6 +20,14 @@
# define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1)
# define ULPI_MISC_A_VBUSVLDEXT BIT(0)
+/* MSM8x60 vendor ULPI registers (raw addresses, not ULPI_EXT_VENDOR_SPECIFIC). */
+#define ULPI_MSM_CONFIG_REG3 0x32
+# define ULPI_MSM_HSDRVSLOPE_MASK GENMASK(3, 0)
+# define ULPI_MSM_PRE_EMPHASIS_MASK GENMASK(5, 4)
+# define ULPI_MSM_PRE_EMPHASIS_20PCT (3 << 4)
+#define ULPI_MSM_DIGOUT_CTRL 0x36
+# define ULPI_MSM_CDR_AUTORESET BIT(1)
+# define ULPI_MSM_SE1_GATE BIT(2)
struct ulpi_seq {
u8 addr;
@@ -37,6 +45,9 @@ struct qcom_usb_hs_phy {
struct ulpi_seq *init_seq;
struct extcon_dev *vbus_edev;
struct notifier_block vbus_notify;
+ bool msm8x60_init;
+ bool hs_drv_slope_present;
+ u8 hs_drv_slope;
};
static int qcom_usb_hs_phy_set_mode(struct phy *phy,
@@ -105,6 +116,41 @@ qcom_usb_hs_phy_vbus_notifier(struct notifier_block *nb, unsigned long event,
return ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXT);
}
+/*
+ * RMW the vendor registers to preserve silicon reserved bits.
+ * In reg 0x36 the legacy semantics are inverted: setting
+ * CDR_AUTORESET / SE1_GATE *disables* those functions.
+ */
+static int qcom_usb_hs_phy_msm8x60_init(struct qcom_usb_hs_phy *uphy)
+{
+ struct ulpi *ulpi = uphy->ulpi;
+ int reg32, reg36, ret;
+
+ reg32 = ulpi_read(ulpi, ULPI_MSM_CONFIG_REG3);
+ if (reg32 < 0)
+ return reg32;
+
+ reg32 &= ~ULPI_MSM_PRE_EMPHASIS_MASK;
+ reg32 |= ULPI_MSM_PRE_EMPHASIS_20PCT;
+
+ if (uphy->hs_drv_slope_present) {
+ reg32 &= ~ULPI_MSM_HSDRVSLOPE_MASK;
+ reg32 |= uphy->hs_drv_slope & ULPI_MSM_HSDRVSLOPE_MASK;
+ }
+
+ ret = ulpi_write(ulpi, ULPI_MSM_CONFIG_REG3, reg32);
+ if (ret)
+ return ret;
+
+ reg36 = ulpi_read(ulpi, ULPI_MSM_DIGOUT_CTRL);
+ if (reg36 < 0)
+ return reg36;
+
+ reg36 |= ULPI_MSM_CDR_AUTORESET | ULPI_MSM_SE1_GATE;
+
+ return ulpi_write(ulpi, ULPI_MSM_DIGOUT_CTRL, reg36);
+}
+
static int qcom_usb_hs_phy_power_on(struct phy *phy)
{
struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
@@ -154,6 +200,12 @@ static int qcom_usb_hs_phy_power_on(struct phy *phy)
goto err_ulpi;
}
+ if (uphy->msm8x60_init) {
+ ret = qcom_usb_hs_phy_msm8x60_init(uphy);
+ if (ret)
+ goto err_ulpi;
+ }
+
if (uphy->vbus_edev) {
state = extcon_get_state(uphy->vbus_edev, EXTCON_USB);
/* setup initial state */
@@ -214,6 +266,22 @@ static int qcom_usb_hs_phy_probe(struct ulpi *ulpi)
return -ENOMEM;
ulpi_set_drvdata(ulpi, uphy);
uphy->ulpi = ulpi;
+ uphy->msm8x60_init = of_device_is_compatible(ulpi->dev.of_node,
+ "qcom,usb-hs-phy-msm8660");
+
+ if (uphy->msm8x60_init) {
+ u32 slope;
+
+ if (!of_property_read_u32(ulpi->dev.of_node,
+ "qcom,hs-drv-slope", &slope)) {
+ if (slope > ULPI_MSM_HSDRVSLOPE_MASK)
+ return dev_err_probe(&ulpi->dev, -EINVAL,
+ "qcom,hs-drv-slope out of range (max %lu)\n",
+ ULPI_MSM_HSDRVSLOPE_MASK);
+ uphy->hs_drv_slope = slope;
+ uphy->hs_drv_slope_present = true;
+ }
+ }
size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq");
if (size < 0)
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 4+ messages in thread