* [PATCH 1/5] dt-bindings: phy: qcom,qmp-usb3-dp: Extend X1E description for USB4
2026-05-18 10:29 [PATCH 0/5] USB4 mode programming for QMMPHY on X1E Konrad Dybcio
@ 2026-05-18 10:29 ` Konrad Dybcio
2026-05-18 10:47 ` sashiko-bot
2026-05-18 10:29 ` [PATCH 2/5] phy: core: Define TBT phy_mode Konrad Dybcio
` (3 subsequent siblings)
4 siblings, 1 reply; 17+ messages in thread
From: Konrad Dybcio @ 2026-05-18 10:29 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: linux-kernel, linux-phy, linux-arm-msm, devicetree, usb4-upstream,
Raghavendra Thoorpu, Mika Westerberg, Sven Peter, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Some instances of the QMP combo PHY (called USB43DP) feature a third
functional sub-block, responsible for USB4/Thunderbolt 3 communication.
Compared to the today's state of the binding, one more clock (P2RR2P -
PHY-to-Router, Router-to-PHY) needs to be enabled for the PHY to be
able to switch to USB4 mode. Allow that for X1E.
Also, add a bindings define to let consumers access it.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
.../devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml | 3 ++-
include/dt-bindings/phy/phy-qcom-qmp.h | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml
index 3d537b7f9985..eba4bee474fb 100644
--- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml
@@ -52,7 +52,7 @@ properties:
- const: ref
- const: com_aux
- const: usb3_pipe
- - const: cfg_ahb
+ - enum: [ p2rr2p_pipe, cfg_ahb ]
power-domains:
maxItems: 1
@@ -186,6 +186,7 @@ allOf:
enum:
- qcom,sc7180-qmp-usb3-dp-phy
- qcom,sdm845-qmp-usb3-dp-phy
+ - qcom,x1e80100-qmp-usb3-dp-phy
then:
properties:
clocks:
diff --git a/include/dt-bindings/phy/phy-qcom-qmp.h b/include/dt-bindings/phy/phy-qcom-qmp.h
index 6b43ea9e0051..1c3ce0c02b0c 100644
--- a/include/dt-bindings/phy/phy-qcom-qmp.h
+++ b/include/dt-bindings/phy/phy-qcom-qmp.h
@@ -16,6 +16,7 @@
/* QMP USB4-USB3-DP PHYs */
#define QMP_USB43DP_USB3_PHY 0
#define QMP_USB43DP_DP_PHY 1
+#define QMP_USB43DP_USB4_PHY 2
/* QMP PCIE PHYs */
#define QMP_PCIE_PIPE_CLK 0
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH 1/5] dt-bindings: phy: qcom,qmp-usb3-dp: Extend X1E description for USB4
2026-05-18 10:29 ` [PATCH 1/5] dt-bindings: phy: qcom,qmp-usb3-dp: Extend X1E description for USB4 Konrad Dybcio
@ 2026-05-18 10:47 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-05-18 10:47 UTC (permalink / raw)
To: Konrad Dybcio
Cc: conor+dt, neil.armstrong, devicetree, olteanv, vkoul, linux-phy,
robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The binding incorrectly relaxes clock constraints by grouping 'x1e80100' with 'sc7180' and 'sdm845' and globally changing the 5th 'clock-names' item to an enum.
--
commit 54d1f0fd2346d0dbf486ca5212767ad33f59fde0
Author: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
dt-bindings: phy: qcom,qmp-usb3-dp: Extend X1E description for USB4
This commit extends the QMP combo PHY binding for X1E80100 to support USB4
communication. It updates the schema to allow a new clock, P2RR2P, which is
required for switching to USB4 mode, and adds a corresponding definition.
> diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml
> --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml
> +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml
> @@ -52,7 +52,7 @@ properties:
> - const: ref
> - const: com_aux
> - const: usb3_pipe
> - - const: cfg_ahb
> + - enum: [ p2rr2p_pipe, cfg_ahb ]
Does this global enum change inadvertently relax the constraint for all
variants?
It appears that older variants which previously strictly required cfg_ahb
could now pass schema validation using p2rr2p_pipe instead, and vice versa.
[ ... ]
> @@ -186,6 +186,7 @@ allOf:
> enum:
> - qcom,sc7180-qmp-usb3-dp-phy
> - qcom,sdm845-qmp-usb3-dp-phy
> + - qcom,x1e80100-qmp-usb3-dp-phy
> then:
> properties:
> clocks:
Should x1e80100 be placed in its own if/then block?
Grouping x1e80100 with sc7180 and sdm845 means the schema only enforces the
maximum number of items, but doesn't strictly tie the 5th clock to the
specific hardware generation. A separate block would ensure x1e80100 is
restricted to p2rr2p_pipe, while the older variants remain restricted to
cfg_ahb.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260518-topic-usb4phy-v1-0-71d827c49dca@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 2/5] phy: core: Define TBT phy_mode
2026-05-18 10:29 [PATCH 0/5] USB4 mode programming for QMMPHY on X1E Konrad Dybcio
2026-05-18 10:29 ` [PATCH 1/5] dt-bindings: phy: qcom,qmp-usb3-dp: Extend X1E description for USB4 Konrad Dybcio
@ 2026-05-18 10:29 ` Konrad Dybcio
2026-05-18 11:03 ` sashiko-bot
2026-05-18 12:25 ` Dmitry Baryshkov
2026-05-18 10:29 ` [PATCH 3/5] phy: qualcomm: qmp-combo: Add preliminary USB4 support Konrad Dybcio
` (2 subsequent siblings)
4 siblings, 2 replies; 17+ messages in thread
From: Konrad Dybcio @ 2026-05-18 10:29 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: linux-kernel, linux-phy, linux-arm-msm, devicetree, usb4-upstream,
Raghavendra Thoorpu, Mika Westerberg, Sven Peter, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
There exist OS-accessible USB4 and Thunderbolt PHYs that need specific
configuration. Define a new phy_mode for them.
Currently, USB4 and TBT3 are defined as submodes, because they're quite
distinct, most visibly in the electrical/analog aspects (slightly
different frequencies, timings, etc.). This results in a need to make
the PHY aware of the actual mode needed (at least in the Qualcomm
implementation, but I believe that'll be a general need).
Newer versions of TBT are basically supersets of USB4 with higher
host-side requirements, so these are not defined. This can always be
changed as necessary.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
include/linux/phy/phy-tbt.h | 14 ++++++++++++++
include/linux/phy/phy.h | 2 ++
2 files changed, 16 insertions(+)
diff --git a/include/linux/phy/phy-tbt.h b/include/linux/phy/phy-tbt.h
new file mode 100644
index 000000000000..5f48059814e1
--- /dev/null
+++ b/include/linux/phy/phy-tbt.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __PHY_TBT_H_
+#define __PHY_TBT_H_
+
+enum tbt_phy_submode {
+ PHY_SUBMODE_TBT3,
+ PHY_SUBMODE_USB4,
+};
+
+#endif
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index ea47975e288a..26a91c070f8c 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -20,6 +20,7 @@
#include <linux/phy/phy-hdmi.h>
#include <linux/phy/phy-lvds.h>
#include <linux/phy/phy-mipi-dphy.h>
+#include <linux/phy/phy-tbt.h>
struct phy;
@@ -45,6 +46,7 @@ enum phy_mode {
PHY_MODE_LVDS,
PHY_MODE_DP,
PHY_MODE_HDMI,
+ PHY_MODE_TBT,
};
enum phy_media {
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH 2/5] phy: core: Define TBT phy_mode
2026-05-18 10:29 ` [PATCH 2/5] phy: core: Define TBT phy_mode Konrad Dybcio
@ 2026-05-18 11:03 ` sashiko-bot
2026-05-18 12:25 ` Dmitry Baryshkov
1 sibling, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-05-18 11:03 UTC (permalink / raw)
To: Konrad Dybcio
Cc: olteanv, conor+dt, devicetree, vkoul, linux-phy, robh,
neil.armstrong
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The tbt_phy_submode enum implicitly sets PHY_SUBMODE_TBT3 to 0, making it the hidden default submode for phy_set_mode().
- [Low] Using PHY_MODE_TBT as the generic mode with USB4 as a submode conceptually inverts the industry standard hierarchy.
--
commit f1ad33ca1ed62aa8d4ab806ced09908d4e9f3625
Author: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
phy: core: Define TBT phy_mode
This commit introduces a new PHY mode for Thunderbolt (TBT) and USB4 PHYs
that require specific electrical and analog configurations. It defines
PHY_MODE_TBT along with PHY_SUBMODE_TBT3 and PHY_SUBMODE_USB4 submodes.
> diff --git a/include/linux/phy/phy-tbt.h b/include/linux/phy/phy-tbt.h
> new file mode 100644
> index 0000000000000..5f48059814e19
> --- /dev/null
> +++ b/include/linux/phy/phy-tbt.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#ifndef __PHY_TBT_H_
> +#define __PHY_TBT_H_
> +
> +enum tbt_phy_submode {
> + PHY_SUBMODE_TBT3,
Since PHY_SUBMODE_TBT3 is the first element, it implicitly evaluates to 0.
The generic phy_set_mode(phy, mode) macro in include/linux/phy/phy.h wraps
phy_set_mode_ext(phy, mode, 0).
Does this mean any driver calling phy_set_mode(phy, PHY_MODE_TBT) will
silently default to requesting the legacy TBT3 electrical/timing
configuration instead of USB4?
Would it be safer to reserve 0 for an unspecified state (e.g.,
PHY_SUBMODE_UNSPECIFIED = 0) to force explicit submode selection, or to
make the modern standard (USB4) the default?
> + PHY_SUBMODE_USB4,
> +};
> +
> +#endif
> diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
> index ea47975e288ae..26a91c070f8ce 100644
> --- a/include/linux/phy/phy.h
> +++ b/include/linux/phy/phy.h
[ ... ]
> @@ -45,6 +46,7 @@ enum phy_mode {
> PHY_MODE_LVDS,
> PHY_MODE_DP,
> PHY_MODE_HDMI,
> + PHY_MODE_TBT,
Is using PHY_MODE_TBT as the generic mode with USB4 as a submode the best
hierarchy here?
USB4 is the foundational open standard established by the USB-IF, and the
Linux kernel USB and Type-C subsystems natively treat USB4 as the primary
mode (e.g., TYPEC_MODE_USB4).
Could grouping USB4 under a Thunderbolt PHY mode cause semantic confusion
and contradict existing kernel USB nomenclature? Might it be more
consistent to use PHY_MODE_USB4 as the primary mode, with TBT3 as a
legacy fallback submode?
> };
>
> enum phy_media {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260518-topic-usb4phy-v1-0-71d827c49dca@oss.qualcomm.com?part=2
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 2/5] phy: core: Define TBT phy_mode
2026-05-18 10:29 ` [PATCH 2/5] phy: core: Define TBT phy_mode Konrad Dybcio
2026-05-18 11:03 ` sashiko-bot
@ 2026-05-18 12:25 ` Dmitry Baryshkov
2026-05-18 12:29 ` Konrad Dybcio
1 sibling, 1 reply; 17+ messages in thread
From: Dmitry Baryshkov @ 2026-05-18 12:25 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, linux-kernel, linux-phy,
linux-arm-msm, devicetree, usb4-upstream, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
On Mon, May 18, 2026 at 12:29:49PM +0200, Konrad Dybcio wrote:
> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>
> There exist OS-accessible USB4 and Thunderbolt PHYs that need specific
> configuration. Define a new phy_mode for them.
>
> Currently, USB4 and TBT3 are defined as submodes, because they're quite
> distinct, most visibly in the electrical/analog aspects (slightly
> different frequencies, timings, etc.). This results in a need to make
> the PHY aware of the actual mode needed (at least in the Qualcomm
> implementation, but I believe that'll be a general need).
>
> Newer versions of TBT are basically supersets of USB4 with higher
> host-side requirements, so these are not defined. This can always be
> changed as necessary.
>
> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---
> include/linux/phy/phy-tbt.h | 14 ++++++++++++++
> include/linux/phy/phy.h | 2 ++
> 2 files changed, 16 insertions(+)
>
> diff --git a/include/linux/phy/phy-tbt.h b/include/linux/phy/phy-tbt.h
> new file mode 100644
> index 000000000000..5f48059814e1
> --- /dev/null
> +++ b/include/linux/phy/phy-tbt.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#ifndef __PHY_TBT_H_
> +#define __PHY_TBT_H_
> +
> +enum tbt_phy_submode {
> + PHY_SUBMODE_TBT3,
> + PHY_SUBMODE_USB4,
Do we need TBT2 as a separate submode?
Other than that:
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> +};
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 2/5] phy: core: Define TBT phy_mode
2026-05-18 12:25 ` Dmitry Baryshkov
@ 2026-05-18 12:29 ` Konrad Dybcio
2026-05-18 15:19 ` Dmitry Baryshkov
0 siblings, 1 reply; 17+ messages in thread
From: Konrad Dybcio @ 2026-05-18 12:29 UTC (permalink / raw)
To: Dmitry Baryshkov, Konrad Dybcio
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, linux-kernel, linux-phy,
linux-arm-msm, devicetree, usb4-upstream, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter
On 5/18/26 2:25 PM, Dmitry Baryshkov wrote:
> On Mon, May 18, 2026 at 12:29:49PM +0200, Konrad Dybcio wrote:
>> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>>
>> There exist OS-accessible USB4 and Thunderbolt PHYs that need specific
>> configuration. Define a new phy_mode for them.
>>
>> Currently, USB4 and TBT3 are defined as submodes, because they're quite
>> distinct, most visibly in the electrical/analog aspects (slightly
>> different frequencies, timings, etc.). This results in a need to make
>> the PHY aware of the actual mode needed (at least in the Qualcomm
>> implementation, but I believe that'll be a general need).
>>
>> Newer versions of TBT are basically supersets of USB4 with higher
>> host-side requirements, so these are not defined. This can always be
>> changed as necessary.
>>
>> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>> ---
>> include/linux/phy/phy-tbt.h | 14 ++++++++++++++
>> include/linux/phy/phy.h | 2 ++
>> 2 files changed, 16 insertions(+)
>>
>> diff --git a/include/linux/phy/phy-tbt.h b/include/linux/phy/phy-tbt.h
>> new file mode 100644
>> index 000000000000..5f48059814e1
>> --- /dev/null
>> +++ b/include/linux/phy/phy-tbt.h
>> @@ -0,0 +1,14 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
>> + */
>> +
>> +#ifndef __PHY_TBT_H_
>> +#define __PHY_TBT_H_
>> +
>> +enum tbt_phy_submode {
>> + PHY_SUBMODE_TBT3,
>> + PHY_SUBMODE_USB4,
>
> Do we need TBT2 as a separate submode?
I would like to avoid extending that unless there's a user for it
Konrad
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 2/5] phy: core: Define TBT phy_mode
2026-05-18 12:29 ` Konrad Dybcio
@ 2026-05-18 15:19 ` Dmitry Baryshkov
0 siblings, 0 replies; 17+ messages in thread
From: Dmitry Baryshkov @ 2026-05-18 15:19 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Konrad Dybcio, Vinod Koul, Neil Armstrong, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, linux-kernel,
linux-phy, linux-arm-msm, devicetree, usb4-upstream,
Raghavendra Thoorpu, Mika Westerberg, Sven Peter
On Mon, May 18, 2026 at 02:29:26PM +0200, Konrad Dybcio wrote:
> On 5/18/26 2:25 PM, Dmitry Baryshkov wrote:
> > On Mon, May 18, 2026 at 12:29:49PM +0200, Konrad Dybcio wrote:
> >> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> >>
> >> There exist OS-accessible USB4 and Thunderbolt PHYs that need specific
> >> configuration. Define a new phy_mode for them.
> >>
> >> Currently, USB4 and TBT3 are defined as submodes, because they're quite
> >> distinct, most visibly in the electrical/analog aspects (slightly
> >> different frequencies, timings, etc.). This results in a need to make
> >> the PHY aware of the actual mode needed (at least in the Qualcomm
> >> implementation, but I believe that'll be a general need).
> >>
> >> Newer versions of TBT are basically supersets of USB4 with higher
> >> host-side requirements, so these are not defined. This can always be
> >> changed as necessary.
> >>
> >> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> >> ---
> >> include/linux/phy/phy-tbt.h | 14 ++++++++++++++
> >> include/linux/phy/phy.h | 2 ++
> >> 2 files changed, 16 insertions(+)
> >>
> >> diff --git a/include/linux/phy/phy-tbt.h b/include/linux/phy/phy-tbt.h
> >> new file mode 100644
> >> index 000000000000..5f48059814e1
> >> --- /dev/null
> >> +++ b/include/linux/phy/phy-tbt.h
> >> @@ -0,0 +1,14 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> >> + */
> >> +
> >> +#ifndef __PHY_TBT_H_
> >> +#define __PHY_TBT_H_
> >> +
> >> +enum tbt_phy_submode {
> >> + PHY_SUBMODE_TBT3,
> >> + PHY_SUBMODE_USB4,
> >
> > Do we need TBT2 as a separate submode?
>
> I would like to avoid extending that unless there's a user for it
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 3/5] phy: qualcomm: qmp-combo: Add preliminary USB4 support
2026-05-18 10:29 [PATCH 0/5] USB4 mode programming for QMMPHY on X1E Konrad Dybcio
2026-05-18 10:29 ` [PATCH 1/5] dt-bindings: phy: qcom,qmp-usb3-dp: Extend X1E description for USB4 Konrad Dybcio
2026-05-18 10:29 ` [PATCH 2/5] phy: core: Define TBT phy_mode Konrad Dybcio
@ 2026-05-18 10:29 ` Konrad Dybcio
2026-05-18 11:32 ` sashiko-bot
2026-05-18 13:57 ` Dmitry Baryshkov
2026-05-18 10:29 ` [PATCH 4/5] phy: qualcomm: qmp-combo: Add USB4/TBT3 configuration data for Hamoa Konrad Dybcio
2026-05-18 10:29 ` [PATCH 5/5] arm64: dts: qcom: hamoa: Extend QMPPHY description for USB4 Konrad Dybcio
4 siblings, 2 replies; 17+ messages in thread
From: Konrad Dybcio @ 2026-05-18 10:29 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: linux-kernel, linux-phy, linux-arm-msm, devicetree, usb4-upstream,
Raghavendra Thoorpu, Mika Westerberg, Sven Peter, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Some Combo PHYs (so far only on SC8280XP, X1E80100 and Glymur), come in
a flavor called USB43DP, which as the name implies, features USB4, USB3
and DP signal processing capabilities. In that architecture, USB3 and
USB4 PHYs share the same USB_PLL while featuring separate logic spaces.
The DP part is roughly the same as on the instances without USB4.
The USB4 and USB3/DP operation modes of the PHY are mutually exclusive.
Only one USB protocol (and flavor of pipe clock) can be active at a
given moment (not to be confused with USB3 not being able to be
tunneled as USB4 packets - that of course remains possible).
The DP PLL is still used for clocking tunneled DP links. It may be
turned off to save power when no tunnels are active, but that's left as
a TODO item for now.
Due to the nature of USB4, the Type-C handling happens entirely inside
the Host Router, and as such the QMPPHY's mux_set() function is
nullified for the period when USB4 PHY remains active. This is strictly
necessary, as the Host Router driver is going to excercise manual
control over the USB4 PHY's power state, which is needed by the suspend
and resume flows. Failure to control that synchronously with other
parts of the code results in a SoC crash by unlocked access.
Because of that, a new struct phy is spawned to expose the USB4 mode,
along with a .set_mode callback to allow toggling between USB4 and TBT3
submodes.
Thunderbolt 3, having a number of differences vs USB4, requires a
couple specific overrides, pertaining to electrical characteristics,
which are easily accommodated for.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 392 ++++++++++++++++++++++++------
1 file changed, 322 insertions(+), 70 deletions(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
index 93f1aa10d400..898f42de4a08 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
@@ -22,6 +22,7 @@
#include <linux/usb/typec.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_tbt.h>
#include <drm/bridge/aux-bridge.h>
@@ -61,10 +62,14 @@
#define SW_USB3PHY_RESET BIT(2)
/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */
#define SW_USB3PHY_RESET_MUX BIT(3)
+#define SW_USB4PHY_RESET BIT(4)
+#define SW_USB4PHY_RESET_MUX BIT(5)
/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */
#define USB3_MODE BIT(0) /* enables USB3 mode */
#define DP_MODE BIT(1) /* enables DP mode */
+#define USB4_MODE BIT(2) /* mutually exclusive with the above */
+#define DP_TUNNELING_CLOCK_GEN_EN BIT(3)
/* QPHY_V3_DP_COM_TYPEC_CTRL register bits */
#define SW_PORTSELECT_VAL BIT(0)
@@ -77,6 +82,8 @@ enum qmpphy_mode {
QMPPHY_MODE_USB3DP = 0,
QMPPHY_MODE_DP_ONLY,
QMPPHY_MODE_USB3_ONLY,
+ /* USB4 QMPPHY mode refers to both USB4 and TBT3 */
+ QMPPHY_MODE_USB4,
};
/* set of registers with offsets different per-PHY */
@@ -89,6 +96,7 @@ enum qphy_reg_layout {
QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR,
QPHY_PCS_POWER_DOWN_CONTROL,
QPHY_PCS_CLAMP_ENABLE,
+ QPHY_PCS_USB4_CLAMP_ENABLE,
QPHY_COM_RESETSM_CNTRL,
QPHY_COM_C_READY_STATUS,
@@ -2198,6 +2206,8 @@ struct qmp_combo_offsets {
u16 dp_txa;
u16 dp_txb;
u16 dp_dp_phy;
+ u16 usb4_serdes;
+ u16 usb4_pcs;
};
struct qmp_phy_cfg {
@@ -2245,6 +2255,18 @@ struct qmp_phy_cfg {
int (*calibrate_dp_phy)(struct qmp_combo *qmp);
void (*dp_aux_init)(struct qmp_combo *qmp);
+ /* USB4 specifics */
+ const struct qmp_phy_init_tbl *usb4_serdes_tbl;
+ int usb4_serdes_tbl_num;
+ const struct qmp_phy_init_tbl *usb4_serdes_tb3_ovrd_tbl;
+ int usb4_serdes_tb3_ovrd_num;
+ const struct qmp_phy_init_tbl *usb4_tx_tbl;
+ int usb4_tx_tbl_num;
+ const struct qmp_phy_init_tbl *usb4_rx_tbl;
+ int usb4_rx_tbl_num;
+ const struct qmp_phy_init_tbl *usb4_pcs_tbl;
+ int usb4_pcs_tbl_num;
+
/* resets to be requested */
const char * const *reset_list;
int num_resets;
@@ -2286,8 +2308,12 @@ struct qmp_combo {
void __iomem *dp_tx2;
void __iomem *dp_dp_phy;
+ void __iomem *usb4_serdes;
+ void __iomem *usb4_pcs;
+
struct clk *pipe_clk;
struct clk_bulk_data *clks;
+ struct clk *p2rr2p_pipe_clk;
int num_clks;
struct reset_control_bulk_data *resets;
struct regulator_bulk_data *vregs;
@@ -2306,6 +2332,10 @@ struct qmp_combo {
unsigned int dp_init_count;
bool dp_powered_on;
+ struct phy *usb4_phy;
+ enum tbt_phy_submode usb4_phy_submode;
+ unsigned int usb4phy_init_count;
+
struct clk_fixed_rate pipe_clk_fixed;
struct clk_hw dp_link_hw;
struct clk_hw dp_pixel_hw;
@@ -3661,14 +3691,26 @@ static int qmp_combo_dp_calibrate(struct phy *phy)
static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
+ u32 dp_reset_val = SW_DPPHY_RESET_MUX | SW_DPPHY_RESET;
void __iomem *com = qmp->com;
void __iomem *pcs_aon = qmp->pcs_aon;
+ void __iomem *pcs = qmp->pcs;
int ret;
u32 val;
if (!force && qmp->init_count++)
return 0;
+ if (qmp->qmpphy_mode == QMPPHY_MODE_USB4) {
+ pcs = qmp->usb4_pcs;
+
+ qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_USB4_CLAMP_ENABLE], CLAMP_EN);
+
+ /* Do not disturb the DP PLL in case there's an active DP tunnel */
+ if (readl(com + QPHY_V3_DP_COM_RESET_OVRD_CTRL) & DP_TUNNELING_CLOCK_GEN_EN)
+ dp_reset_val &= ~SW_DPPHY_RESET_MUX;
+ }
+
ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
if (ret) {
dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
@@ -3695,8 +3737,9 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
/* override hardware control for reset of qmp phy */
qphy_setbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
- SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
- SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
+ dp_reset_val |
+ SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET |
+ SW_USB4PHY_RESET_MUX | SW_USB4PHY_RESET);
/* override hardware control for reset of qmp phy */
if (pcs_aon && cfg->regs[QPHY_AON_TOGGLE_ENABLE]) {
@@ -3712,6 +3755,10 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
if (cfg->invert_cc_polarity)
val |= INVERT_CC_POLARITY;
+ /*
+ * Note that in USB4 mode, the router controls pin assignments instead
+ * and the values of the PORTSELECT registers are ignored.
+ */
writel(val, com + QPHY_V3_DP_COM_TYPEC_CTRL);
switch (qmp->qmpphy_mode) {
@@ -3739,12 +3786,21 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
break;
+ case QMPPHY_MODE_USB4:
+ writel(USB4_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL);
+
+ /* bring both QMP USB and QMP DP PHYs PCS block out of reset */
+ /* TODO: disable DP PLL if there are no active tunnels after router setup */
+ qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
+ SW_USB4PHY_RESET_MUX | SW_USB4PHY_RESET |
+ SW_DPPHY_RESET_MUX | SW_DPPHY_RESET);
+ break;
}
qphy_clrbits(com, QPHY_V3_DP_COM_SWI_CTRL, 0x03);
qphy_clrbits(com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
- qphy_setbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
SW_PWRDN);
return 0;
@@ -3859,6 +3915,10 @@ static int qmp_combo_usb_power_on(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *serdes = qmp->serdes;
+ const struct qmp_phy_init_tbl *pcs_tbl;
+ const struct qmp_phy_init_tbl *rx_tbl;
+ const struct qmp_phy_init_tbl *serdes_tbl;
+ const struct qmp_phy_init_tbl *tx_tbl;
void __iomem *tx = qmp->tx;
void __iomem *rx = qmp->rx;
void __iomem *tx2 = qmp->tx2;
@@ -3866,10 +3926,39 @@ static int qmp_combo_usb_power_on(struct phy *phy)
void __iomem *pcs = qmp->pcs;
void __iomem *pcs_usb = qmp->pcs_usb;
void __iomem *status;
+ int pcs_tbl_num, rx_tbl_num, serdes_tbl_num, tx_tbl_num;
unsigned int val;
int ret;
- qmp_configure(qmp->dev, serdes, cfg->serdes_tbl, cfg->serdes_tbl_num);
+ if (qmp->qmpphy_mode == QMPPHY_MODE_USB4) {
+ pcs = qmp->usb4_pcs;
+ serdes = qmp->usb4_serdes;
+ pcs_tbl = cfg->usb4_pcs_tbl;
+ pcs_tbl_num = cfg->usb4_pcs_tbl_num;
+
+ serdes_tbl = cfg->usb4_serdes_tbl;
+ serdes_tbl_num = cfg->usb4_serdes_tbl_num;
+
+ rx_tbl = cfg->usb4_rx_tbl;
+ rx_tbl_num = cfg->usb4_rx_tbl_num;
+
+ tx_tbl = cfg->usb4_tx_tbl;
+ tx_tbl_num = cfg->usb4_tx_tbl_num;
+ } else {
+ pcs_tbl = cfg->pcs_tbl;
+ pcs_tbl_num = cfg->pcs_tbl_num;
+
+ serdes_tbl = cfg->serdes_tbl;
+ serdes_tbl_num = cfg->serdes_tbl_num;
+
+ rx_tbl = cfg->rx_tbl;
+ rx_tbl_num = cfg->rx_tbl_num;
+
+ tx_tbl = cfg->tx_tbl;
+ tx_tbl_num = cfg->tx_tbl_num;
+ }
+
+ qmp_configure(qmp->dev, serdes, serdes_tbl, serdes_tbl_num);
ret = clk_prepare_enable(qmp->pipe_clk);
if (ret) {
@@ -3878,17 +3967,19 @@ static int qmp_combo_usb_power_on(struct phy *phy)
}
/* Tx, Rx, and PCS configurations */
- qmp_configure_lane(qmp->dev, tx, cfg->tx_tbl, cfg->tx_tbl_num, 1);
- qmp_configure_lane(qmp->dev, tx2, cfg->tx_tbl, cfg->tx_tbl_num, 2);
+ qmp_configure_lane(qmp->dev, tx, tx_tbl, tx_tbl_num, 1);
+ qmp_configure_lane(qmp->dev, tx2, tx_tbl, tx_tbl_num, 2);
- qmp_configure_lane(qmp->dev, rx, cfg->rx_tbl, cfg->rx_tbl_num, 1);
- qmp_configure_lane(qmp->dev, rx2, cfg->rx_tbl, cfg->rx_tbl_num, 2);
+ qmp_configure_lane(qmp->dev, rx, rx_tbl, rx_tbl_num, 1);
+ qmp_configure_lane(qmp->dev, rx2, rx_tbl, rx_tbl_num, 2);
- qmp_configure(qmp->dev, pcs, cfg->pcs_tbl, cfg->pcs_tbl_num);
+ qmp_configure(qmp->dev, pcs, pcs_tbl, pcs_tbl_num);
qmp_configure(qmp->dev, qmp->pcs_misc, cfg->pcs_misc_tbl, cfg->pcs_misc_tbl_num);
-
- if (pcs_usb)
+ if (qmp->qmpphy_mode == QMPPHY_MODE_USB4 && qmp->usb4_phy_submode == PHY_SUBMODE_TBT3)
+ qmp_configure(qmp->dev, serdes, cfg->usb4_serdes_tb3_ovrd_tbl,
+ cfg->usb4_serdes_tb3_ovrd_num);
+ else if (qmp->qmpphy_mode != QMPPHY_MODE_USB4 && pcs_usb)
qmp_configure(qmp->dev, pcs_usb, cfg->pcs_usb_tbl,
cfg->pcs_usb_tbl_num);
@@ -3921,18 +4012,22 @@ static int qmp_combo_usb_power_off(struct phy *phy)
{
struct qmp_combo *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
+ void __iomem *pcs = qmp->pcs;
+
+ if (qmp->usb4phy_init_count)
+ pcs = qmp->usb4_pcs;
clk_disable_unprepare(qmp->pipe_clk);
/* PHY reset */
- qphy_setbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+ qphy_setbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
/* stop SerDes and Phy-Coding-Sublayer */
- qphy_clrbits(qmp->pcs, cfg->regs[QPHY_START_CTRL],
+ qphy_clrbits(pcs, cfg->regs[QPHY_START_CTRL],
SERDES_START | PCS_START);
/* Put PHY into POWER DOWN state: active low */
- qphy_clrbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ qphy_clrbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
SW_PWRDN);
return 0;
@@ -3943,21 +4038,27 @@ static int qmp_combo_usb_init(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
int ret;
- mutex_lock(&qmp->phy_mutex);
+ guard(mutex)(&qmp->phy_mutex);
+
+ /* USB4 mode takes precedence, do not reprogram the PHY in that case */
+ if (qmp->usb4phy_init_count) {
+ qmp->init_count++;
+ qmp->usb_init_count++;
+ return 0;
+ }
+
ret = qmp_combo_com_init(qmp, false);
if (ret)
- goto out_unlock;
+ return ret;
ret = qmp_combo_usb_power_on(phy);
if (ret) {
qmp_combo_com_exit(qmp, false);
- goto out_unlock;
+ return ret;
}
qmp->usb_init_count++;
-out_unlock:
- mutex_unlock(&qmp->phy_mutex);
return ret;
}
@@ -3966,20 +4067,26 @@ static int qmp_combo_usb_exit(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
int ret;
- mutex_lock(&qmp->phy_mutex);
+ guard(mutex)(&qmp->phy_mutex);
+
+ /* USB4 mode takes precedence, do not reprogram the PHY in that case */
+ if (qmp->usb4phy_init_count) {
+ qmp->init_count--;
+ qmp->usb_init_count--;
+ return 0;
+ }
+
ret = qmp_combo_usb_power_off(phy);
if (ret)
- goto out_unlock;
+ return ret;
ret = qmp_combo_com_exit(qmp, false);
if (ret)
- goto out_unlock;
+ return ret;
qmp->usb_init_count--;
-out_unlock:
- mutex_unlock(&qmp->phy_mutex);
- return ret;
+ return 0;
}
static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submode)
@@ -3991,6 +4098,119 @@ static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submo
return 0;
}
+static int qmp_combo_reconfigure_phy(struct qmp_combo *qmp, enum qmpphy_mode new_mode)
+{
+ dev_dbg(qmp->dev, "qmp_combo_reconfigure_phy: switching from qmpphy mode %d to %d\n",
+ qmp->qmpphy_mode, new_mode);
+
+ if (qmp->usb_init_count || qmp->usb4phy_init_count)
+ qmp_combo_usb_power_off(qmp->usb_phy);
+
+ if (qmp->dp_init_count)
+ writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);
+
+ qmp_combo_com_exit(qmp, true);
+
+ qmp->qmpphy_mode = new_mode;
+
+ /* Now everything's powered down, power up the right PHYs */
+ qmp_combo_com_init(qmp, true);
+
+ if ((qmp->usb_init_count || qmp->usb4phy_init_count) &&
+ new_mode != QMPPHY_MODE_DP_ONLY)
+ qmp_combo_usb_power_on(qmp->usb_phy);
+
+ if ((new_mode == QMPPHY_MODE_USB3DP || new_mode == QMPPHY_MODE_DP_ONLY) &&
+ qmp->dp_init_count)
+ qmp->cfg->dp_aux_init(qmp);
+
+ return 0;
+}
+
+static int qmp_combo_usb4_init(struct phy *phy)
+{
+ struct qmp_combo *qmp = phy_get_drvdata(phy);
+ int ret;
+
+ guard(mutex)(&qmp->phy_mutex);
+
+ if (!qmp->p2rr2p_pipe_clk) {
+ dev_err(qmp->dev, "missing p2rr2p_pipe clock handle. Update your Device Tree.\n");
+ return -EINVAL;
+ }
+
+ ret = clk_prepare_enable(qmp->p2rr2p_pipe_clk);
+ if (ret) {
+ dev_err(qmp->dev, "p2rr2p_pipe enable failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = qmp_combo_com_init(qmp, false);
+ if (ret)
+ return ret;
+
+ /* USB4 mode takes precedence to USB3(+DP), force reconfigure the PHY */
+ ret = qmp_combo_reconfigure_phy(qmp, QMPPHY_MODE_USB4);
+ if (ret)
+ return ret;
+
+ ret = qmp_combo_usb_power_on(phy);
+ if (ret) {
+ qmp_combo_com_exit(qmp, false);
+ return ret;
+ }
+
+ /*
+ * Due to the SoC design, the PHY only has a single valid consumer and
+ * preventing it from having sole ownership of the PHY's power state
+ * makes suspending/resuming the router impossible.
+ */
+ WARN_ON(qmp->usb4phy_init_count++);
+
+ return 0;
+}
+
+static int qmp_combo_usb4_exit(struct phy *phy)
+{
+ struct qmp_combo *qmp = phy_get_drvdata(phy);
+ int ret;
+
+ guard(mutex)(&qmp->phy_mutex);
+
+ ret = qmp_combo_usb_power_off(qmp->usb_phy);
+ if (ret)
+ return ret;
+
+ ret = qmp_combo_com_exit(qmp, false);
+ if (ret)
+ return ret;
+
+ /*
+ * Mark the USB4 PHY uninitialized and wait for a mux_set event to determine the correct
+ * setting. This will always be possible because USB4 requires Type-C.
+ */
+ qmp->usb4phy_init_count--;
+
+ clk_disable_unprepare(qmp->p2rr2p_pipe_clk);
+
+ return 0;
+}
+
+static int qmp_combo_usb4_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct qmp_combo *qmp = phy_get_drvdata(phy);
+
+ if (mode != PHY_MODE_TBT)
+ return -EINVAL;
+
+ if (submode == PHY_SUBMODE_USB4 || submode == PHY_SUBMODE_TBT3) {
+ qmp->usb4_phy_submode = submode;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
static const struct phy_ops qmp_combo_usb_phy_ops = {
.init = qmp_combo_usb_init,
.exit = qmp_combo_usb_exit,
@@ -3998,6 +4218,13 @@ static const struct phy_ops qmp_combo_usb_phy_ops = {
.owner = THIS_MODULE,
};
+static const struct phy_ops qmp_combo_usb4_phy_ops = {
+ .init = qmp_combo_usb4_init,
+ .exit = qmp_combo_usb4_exit,
+ .set_mode = qmp_combo_usb4_set_mode,
+ .owner = THIS_MODULE,
+};
+
static const struct phy_ops qmp_combo_dp_phy_ops = {
.init = qmp_combo_dp_init,
.configure = qmp_combo_dp_configure,
@@ -4037,9 +4264,12 @@ static void qmp_combo_enable_autonomous_mode(struct qmp_combo *qmp)
* Enable i/o clamp_n for autonomous mode
* V6 and later versions use pcs aon clamp register
*/
- if (pcs_aon)
- qphy_clrbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
- else if (pcs_misc)
+ if (pcs_aon) {
+ if (qmp->qmpphy_mode == QMPPHY_MODE_USB4)
+ qphy_clrbits(pcs_aon, cfg->regs[QPHY_PCS_USB4_CLAMP_ENABLE], CLAMP_EN);
+ else
+ qphy_clrbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
+ } else if (pcs_misc)
qphy_clrbits(pcs_misc, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
}
@@ -4051,9 +4281,12 @@ static void qmp_combo_disable_autonomous_mode(struct qmp_combo *qmp)
void __iomem *pcs_aon = qmp->pcs_aon;
/* Disable i/o clamp_n on resume for normal mode */
- if (pcs_aon)
- qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
- else if (pcs_misc)
+ if (pcs_aon) {
+ if (qmp->qmpphy_mode == QMPPHY_MODE_USB4)
+ qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_USB4_CLAMP_ENABLE], CLAMP_EN);
+ else
+ qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
+ } else if (pcs_misc)
qphy_setbits(pcs_misc, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
@@ -4427,24 +4660,29 @@ static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw,
struct qmp_combo *qmp = typec_switch_get_drvdata(sw);
const struct qmp_phy_cfg *cfg = qmp->cfg;
+ if (qmp->qmpphy_mode == QMPPHY_MODE_USB4) {
+ /* QMPPHY has no orientation handling in USB4 mode, don't cache the setting */
+ qmp->orientation = TYPEC_ORIENTATION_NONE;
+ return 0;
+ }
+
if (orientation == qmp->orientation || orientation == TYPEC_ORIENTATION_NONE)
return 0;
- mutex_lock(&qmp->phy_mutex);
+ guard(mutex)(&qmp->phy_mutex);
qmp->orientation = orientation;
if (qmp->init_count) {
- if (qmp->usb_init_count)
+ if (qmp->usb_init_count || qmp->usb4phy_init_count)
qmp_combo_usb_power_off(qmp->usb_phy);
qmp_combo_com_exit(qmp, true);
qmp_combo_com_init(qmp, true);
- if (qmp->usb_init_count)
+ if (qmp->usb_init_count || qmp->usb4phy_init_count)
qmp_combo_usb_power_on(qmp->usb_phy);
if (qmp->dp_init_count)
cfg->dp_aux_init(qmp);
}
- mutex_unlock(&qmp->phy_mutex);
return 0;
}
@@ -4452,7 +4690,6 @@ static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw,
static int qmp_combo_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
{
struct qmp_combo *qmp = typec_mux_get_drvdata(mux);
- const struct qmp_phy_cfg *cfg = qmp->cfg;
enum qmpphy_mode new_mode;
unsigned int svid;
@@ -4463,6 +4700,29 @@ static int qmp_combo_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_s
else
svid = 0;
+ /*
+ * The USB4 router driver must excercise fine-grained control over the timing of
+ * USB4 QMPPHY mode entry/exit, which is difficult to otherwise ensure within Linux's
+ * Type-C framework if the PHY acts as a self-decisive mode mux in parallel.
+ *
+ * Keep the hardware in QMPPHY_MODE_USB4 at all times between .init and .exit of the
+ * (Linux) USB4 PHY and wait for the router driver to turn it off at its discretion.
+ * Once that happens, fall back to the usual USB3/DP/Combo mode logic.
+ *
+ * After the QMPPHY has been turned off through phy_exit(usb4_phy), the next mux_set
+ * will initialize it in the right mode.
+ */
+ if (qmp->usb4phy_init_count)
+ return 0;
+
+ /*
+ * Explicitly ignore TBT/USB4 mode requests that may come if the USB4 PHY hasn't been
+ * initialized, either due to the USB4 drivers being disabled or due to this PHY instance
+ * lacking USB4 support.
+ */
+ if (svid == USB_TYPEC_TBT_SID || (!state->alt && state->mode == TYPEC_MODE_USB4))
+ return 0;
+
if (svid == USB_TYPEC_DP_SID) {
switch (state->mode) {
/* DP Only */
@@ -4495,41 +4755,11 @@ static int qmp_combo_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_s
return 0;
}
- dev_dbg(qmp->dev, "typec_mux_set: switching from qmpphy mode %d to %d\n",
- qmp->qmpphy_mode, new_mode);
+ /* The mux still receives Type-C events, even if all PHYs are uninitialized */
+ if (!qmp->init_count)
+ return 0;
- qmp->qmpphy_mode = new_mode;
-
- if (qmp->init_count) {
- if (qmp->usb_init_count)
- qmp_combo_usb_power_off(qmp->usb_phy);
-
- if (qmp->dp_init_count)
- writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);
-
- qmp_combo_com_exit(qmp, true);
-
- /* Now everything's powered down, power up the right PHYs */
- qmp_combo_com_init(qmp, true);
-
- if (new_mode == QMPPHY_MODE_DP_ONLY) {
- if (qmp->usb_init_count)
- qmp->usb_init_count--;
- }
-
- if (new_mode == QMPPHY_MODE_USB3DP || new_mode == QMPPHY_MODE_USB3_ONLY) {
- qmp_combo_usb_power_on(qmp->usb_phy);
- if (!qmp->usb_init_count)
- qmp->usb_init_count++;
- }
-
- if (new_mode == QMPPHY_MODE_DP_ONLY || new_mode == QMPPHY_MODE_USB3DP) {
- if (qmp->dp_init_count)
- cfg->dp_aux_init(qmp);
- }
- }
-
- return 0;
+ return qmp_combo_reconfigure_phy(qmp, new_mode);
}
static void qmp_combo_typec_switch_unregister(void *data)
@@ -4733,6 +4963,9 @@ static int qmp_combo_parse_dt(struct qmp_combo *qmp)
}
qmp->dp_dp_phy = base + offs->dp_dp_phy;
+ qmp->usb4_serdes = base + offs->usb4_serdes;
+ qmp->usb4_pcs = base + offs->usb4_pcs;
+
ret = qmp_combo_clk_init(qmp);
if (ret)
return ret;
@@ -4743,6 +4976,12 @@ static int qmp_combo_parse_dt(struct qmp_combo *qmp)
"failed to get usb3_pipe clock\n");
}
+ qmp->p2rr2p_pipe_clk = devm_clk_get_optional(dev, "p2rr2p_pipe");
+ if (IS_ERR(qmp->p2rr2p_pipe_clk)) {
+ return dev_err_probe(dev, PTR_ERR(qmp->p2rr2p_pipe_clk),
+ "failed to get p2rr2p_pipe clock\n");
+ }
+
return 0;
}
@@ -4758,6 +4997,8 @@ static struct phy *qmp_combo_phy_xlate(struct device *dev, const struct of_phand
return qmp->usb_phy;
case QMP_USB43DP_DP_PHY:
return qmp->dp_phy;
+ case QMP_USB43DP_USB4_PHY:
+ return qmp->usb4_phy ?: ERR_PTR(-EINVAL);
}
return ERR_PTR(-EINVAL);
@@ -4965,6 +5206,17 @@ static int qmp_combo_probe(struct platform_device *pdev)
phy_set_drvdata(qmp->dp_phy, qmp);
+ if (qmp->cfg->usb4_serdes_tbl) {
+ qmp->usb4_phy = devm_phy_create(dev, dev->of_node, &qmp_combo_usb4_phy_ops);
+ if (IS_ERR(qmp->usb4_phy)) {
+ ret = PTR_ERR(qmp->usb4_phy);
+ dev_err(dev, "failed to create USB4 PHY: %d\n", ret);
+ goto err_node_put;
+ }
+
+ phy_set_drvdata(qmp->usb4_phy, qmp);
+ }
+
if (usb_np == dev->of_node)
phy_provider = devm_of_phy_provider_register(dev, qmp_combo_phy_xlate);
else
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH 3/5] phy: qualcomm: qmp-combo: Add preliminary USB4 support
2026-05-18 10:29 ` [PATCH 3/5] phy: qualcomm: qmp-combo: Add preliminary USB4 support Konrad Dybcio
@ 2026-05-18 11:32 ` sashiko-bot
2026-05-18 13:57 ` Dmitry Baryshkov
1 sibling, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-05-18 11:32 UTC (permalink / raw)
To: Konrad Dybcio
Cc: conor+dt, neil.armstrong, olteanv, linux-phy, vkoul, robh,
devicetree
Thank you for your contribution! Sashiko AI review found 12 potential issue(s) to consider:
- [Critical] Accessing hardware registers before enabling regulators and clocks causes a system crash.
- [High] Unconditional dereference of the `pcs_aon` pointer in `qmp_combo_com_init()`.
- [High] Register mismatch when checking for an active DP tunnel breaks the DP PLL.
- [High] Incomplete masking of DP reset bits in `qmp_combo_com_init()` actively forces the DP PHY into reset.
- [High] Type-C mux events are permanently dropped before the PHY is initialized, breaking alt modes.
- [High] Double hardware initialization and `pipe_clk` reference leak in `qmp_combo_usb4_init()`.
- [High] Type-C cable orientation state is discarded for USB3 connections following a USB4 disconnect.
- [High] Error paths in `qmp_combo_usb4_init()` leak the `p2rr2p_pipe_clk` reference.
- [High] TOCTOU data race on `qmpphy_mode` and `orientation` in `qmp_combo_typec_switch_set()`.
- [High] Autonomous mode functions access unclocked `pcs_usb` registers, causing a hardware hang.
- [High] Missing lock in `qmp_combo_typec_mux_set()` leads to concurrent PHY reconfiguration races.
- [High] `init_count` underflows on error paths in `qmp_combo_com_init()` when `force` is true.
--
commit f92b7738f7f78a59373363a1b4f2191edf48f3e5
Author: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
phy: qualcomm: qmp-combo: Add preliminary USB4 support
This commit adds preliminary USB4 support for Qualcomm QMP combo PHYs,
introducing a new USB4 submode and handling mutually exclusive USB4 and
USB3/DP operation modes.
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
> index 93f1aa10d4008..898f42de4a086 100644
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
[ ... ]
> @@ -3691,14 +3691,26 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
> {
> const struct qmp_phy_cfg *cfg = qmp->cfg;
> + u32 dp_reset_val = SW_DPPHY_RESET_MUX | SW_DPPHY_RESET;
> void __iomem *com = qmp->com;
> void __iomem *pcs_aon = qmp->pcs_aon;
> + void __iomem *pcs = qmp->pcs;
> int ret;
> u32 val;
>
> if (!force && qmp->init_count++)
> return 0;
If force is true, this bypasses the init_count increment. However, if a
failure occurs later in the function (e.g., regulator_bulk_enable fails),
the error path jumps to err_decrement_count and executes qmp->init_count--.
Could this decrement a count that was never incremented, corrupting the
driver state?
>
> + if (qmp->qmpphy_mode == QMPPHY_MODE_USB4) {
> + pcs = qmp->usb4_pcs;
> +
> + qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_USB4_CLAMP_ENABLE], CLAMP_EN);
Could pcs_aon be NULL here? Other functions like
qmp_combo_enable_autonomous_mode() check if pcs_aon is valid before using it.
If a platform lacks this memory region in the device tree, will this cause a
kernel panic?
Can accessing these hardware registers before enabling regulators and clocks
cause a system crash? The readl() and qphy_setbits() are called before
regulator_bulk_enable() and clk_bulk_prepare_enable(). Accessing unpowered
or unclocked hardware domains often triggers a synchronous external abort.
> +
> + /* Do not disturb the DP PLL in case there's an active DP tunnel */
> + if (readl(com + QPHY_V3_DP_COM_RESET_OVRD_CTRL) & DP_TUNNELING_CLOCK_GEN_EN)
Does this read the wrong register? DP_TUNNELING_CLOCK_GEN_EN is defined as a
bit in QPHY_V3_DP_COM_PHY_MODE_CTRL, rather than QPHY_V3_DP_COM_RESET_OVRD_CTRL.
This might evaluate SW_USB3PHY_RESET_MUX instead, leading to incorrect DP reset
logic.
> + dp_reset_val &= ~SW_DPPHY_RESET_MUX;
> + }
Does this incomplete mask force the DP PHY into reset? dp_reset_val is
initially SW_DPPHY_RESET_MUX | SW_DPPHY_RESET, so this clears the mux bit
but leaves SW_DPPHY_RESET set to 1.
> +
> ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
[ ... ]
> @@ -3695,8 +3737,9 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
>
> /* override hardware control for reset of qmp phy */
> qphy_setbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
> - SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
> - SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
> + dp_reset_val |
> + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET |
> + SW_USB4PHY_RESET_MUX | SW_USB4PHY_RESET);
When this is applied, since qphy_setbits() performs a bitwise OR, it cannot
clear SW_DPPHY_RESET_MUX in the hardware if it was already 1. Combined with
SW_DPPHY_RESET still being 1 in dp_reset_val, won't this actively put the
DP PHY into reset and destroy the state it was intended to protect?
[ ... ]
> @@ -3991,6 +4098,119 @@ static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submo
[ ... ]
> +static int qmp_combo_usb4_init(struct phy *phy)
> +{
> + struct qmp_combo *qmp = phy_get_drvdata(phy);
> + int ret;
> +
> + guard(mutex)(&qmp->phy_mutex);
> +
> + if (!qmp->p2rr2p_pipe_clk) {
> + dev_err(qmp->dev, "missing p2rr2p_pipe clock handle. Update your Device Tree.\n");
> + return -EINVAL;
> + }
> +
> + ret = clk_prepare_enable(qmp->p2rr2p_pipe_clk);
> + if (ret) {
> + dev_err(qmp->dev, "p2rr2p_pipe enable failed: %d\n", ret);
> + return ret;
> + }
> +
> + ret = qmp_combo_com_init(qmp, false);
> + if (ret)
> + return ret;
Do the error paths in this function leak the p2rr2p_pipe_clk reference?
If qmp_combo_com_init(), qmp_combo_reconfigure_phy(), or
qmp_combo_usb_power_on() fail, the function returns directly without calling
clk_disable_unprepare() for p2rr2p_pipe_clk.
> +
> + /* USB4 mode takes precedence to USB3(+DP), force reconfigure the PHY */
> + ret = qmp_combo_reconfigure_phy(qmp, QMPPHY_MODE_USB4);
> + if (ret)
> + return ret;
> +
> + ret = qmp_combo_usb_power_on(phy);
Can this cause a double hardware initialization and leak the pipe_clk reference?
If the USB3 PHY is already active, qmp_combo_reconfigure_phy() internally
calls qmp_combo_usb_power_on(qmp->usb_phy). Immediately after, this function
unconditionally calls qmp_combo_usb_power_on(phy) a second time. This would
enable the hardware twice and increment the pipe_clk reference count twice,
but usb4_exit() only calls power_off once.
[ ... ]
> @@ -4037,9 +4264,12 @@ static void qmp_combo_enable_autonomous_mode(struct qmp_combo *qmp)
> * Enable i/o clamp_n for autonomous mode
> * V6 and later versions use pcs aon clamp register
> */
> - if (pcs_aon)
> - qphy_clrbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
> - else if (pcs_misc)
> + if (pcs_aon) {
> + if (qmp->qmpphy_mode == QMPPHY_MODE_USB4)
> + qphy_clrbits(pcs_aon, cfg->regs[QPHY_PCS_USB4_CLAMP_ENABLE], CLAMP_EN);
> + else
> + qphy_clrbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
> + } else if (pcs_misc)
> qphy_clrbits(pcs_misc, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
> }
In USB4 mode, qmp_combo_com_init() does not clear SW_USB3PHY_RESET, leaving
the USB3 PCS block in hardware reset. However,
qmp_combo_enable_autonomous_mode() unconditionally accesses pcs_usb registers
earlier in the function (e.g., QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR). Will
accessing an IP block held in reset cause an external abort?
[ ... ]
> @@ -4427,24 +4660,29 @@ static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw,
> struct qmp_combo *qmp = typec_switch_get_drvdata(sw);
> const struct qmp_phy_cfg *cfg = qmp->cfg;
>
> + if (qmp->qmpphy_mode == QMPPHY_MODE_USB4) {
> + /* QMPPHY has no orientation handling in USB4 mode, don't cache the setting */
> + qmp->orientation = TYPEC_ORIENTATION_NONE;
> + return 0;
> + }
> +
> if (orientation == qmp->orientation || orientation == TYPEC_ORIENTATION_NONE)
> return 0;
>
> - mutex_lock(&qmp->phy_mutex);
> + guard(mutex)(&qmp->phy_mutex);
Is there a data race here? The check for qmp->qmpphy_mode and the
modification of qmp->orientation are performed before acquiring
qmp->phy_mutex. If an orientation switch event races with
qmp_combo_usb4_init() modifying qmpphy_mode, switch_set could read a stale
mode and enter the critical section to execute a full PHY reset while the
USB4 router is actively using it.
Also, if a USB4 device is unplugged, usb4_exit is called but qmpphy_mode
remains QMPPHY_MODE_USB4. If a USB3 device is then plugged in, switch_set()
is typically called before mux_set(). Because it sees the stale USB4 mode,
it drops the valid orientation and forces TYPEC_ORIENTATION_NONE.
When mux_set() is subsequently called, it configures USB3 with no orientation.
Will this cause enumeration to fail if the Type-C cable is flipped?
[ ... ]
> @@ -4495,41 +4755,11 @@ static int qmp_combo_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_s
[ ... ]
> + /* The mux still receives Type-C events, even if all PHYs are uninitialized */
> + if (!qmp->init_count)
> + return 0;
>
> - return 0;
> + return qmp_combo_reconfigure_phy(qmp, new_mode);
> }
Does this early return break alt modes by dropping Type-C events? It
bypasses saving the requested mode (qmp->qmpphy_mode = new_mode). Since
Type-C events often arrive before the USB controller initializes the PHY,
the requested mode is lost. When phy_init() is later called, it will
initialize with the stale default mode, breaking USB3 or DP routing.
Additionally, qmp_combo_reconfigure_phy() is called here without acquiring
qmp->phy_mutex. Concurrently, qmp_combo_usb4_init() uses the mutex and also
calls qmp_combo_reconfigure_phy(). Doesn't this lack of synchronization
permit concurrent execution of PHY hardware power down/up sequences,
potentially corrupting register states?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260518-topic-usb4phy-v1-0-71d827c49dca@oss.qualcomm.com?part=3
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 3/5] phy: qualcomm: qmp-combo: Add preliminary USB4 support
2026-05-18 10:29 ` [PATCH 3/5] phy: qualcomm: qmp-combo: Add preliminary USB4 support Konrad Dybcio
2026-05-18 11:32 ` sashiko-bot
@ 2026-05-18 13:57 ` Dmitry Baryshkov
2026-05-18 14:15 ` Konrad Dybcio
1 sibling, 1 reply; 17+ messages in thread
From: Dmitry Baryshkov @ 2026-05-18 13:57 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, linux-kernel, linux-phy,
linux-arm-msm, devicetree, usb4-upstream, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
On Mon, May 18, 2026 at 12:29:50PM +0200, Konrad Dybcio wrote:
> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>
> Some Combo PHYs (so far only on SC8280XP, X1E80100 and Glymur), come in
> a flavor called USB43DP, which as the name implies, features USB4, USB3
> and DP signal processing capabilities. In that architecture, USB3 and
> USB4 PHYs share the same USB_PLL while featuring separate logic spaces.
> The DP part is roughly the same as on the instances without USB4.
>
> The USB4 and USB3/DP operation modes of the PHY are mutually exclusive.
> Only one USB protocol (and flavor of pipe clock) can be active at a
> given moment (not to be confused with USB3 not being able to be
> tunneled as USB4 packets - that of course remains possible).
> The DP PLL is still used for clocking tunneled DP links. It may be
> turned off to save power when no tunnels are active, but that's left as
> a TODO item for now.
>
> Due to the nature of USB4, the Type-C handling happens entirely inside
> the Host Router, and as such the QMPPHY's mux_set() function is
> nullified for the period when USB4 PHY remains active. This is strictly
> necessary, as the Host Router driver is going to excercise manual
> control over the USB4 PHY's power state, which is needed by the suspend
> and resume flows. Failure to control that synchronously with other
> parts of the code results in a SoC crash by unlocked access.
>
> Because of that, a new struct phy is spawned to expose the USB4 mode,
> along with a .set_mode callback to allow toggling between USB4 and TBT3
> submodes.
>
> Thunderbolt 3, having a number of differences vs USB4, requires a
> couple specific overrides, pertaining to electrical characteristics,
> which are easily accommodated for.
>
> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---
> drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 392 ++++++++++++++++++++++++------
> 1 file changed, 322 insertions(+), 70 deletions(-)
>
Overall it looks good. The major question (after looking at TODOs), do
we need a separate submode for USB+DP / TBT+DP?
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 3/5] phy: qualcomm: qmp-combo: Add preliminary USB4 support
2026-05-18 13:57 ` Dmitry Baryshkov
@ 2026-05-18 14:15 ` Konrad Dybcio
2026-05-18 15:38 ` Dmitry Baryshkov
0 siblings, 1 reply; 17+ messages in thread
From: Konrad Dybcio @ 2026-05-18 14:15 UTC (permalink / raw)
To: Dmitry Baryshkov, Konrad Dybcio
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, linux-kernel, linux-phy,
linux-arm-msm, devicetree, usb4-upstream, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter
On 5/18/26 3:57 PM, Dmitry Baryshkov wrote:
> On Mon, May 18, 2026 at 12:29:50PM +0200, Konrad Dybcio wrote:
>> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>>
>> Some Combo PHYs (so far only on SC8280XP, X1E80100 and Glymur), come in
>> a flavor called USB43DP, which as the name implies, features USB4, USB3
>> and DP signal processing capabilities. In that architecture, USB3 and
>> USB4 PHYs share the same USB_PLL while featuring separate logic spaces.
>> The DP part is roughly the same as on the instances without USB4.
>>
>> The USB4 and USB3/DP operation modes of the PHY are mutually exclusive.
>> Only one USB protocol (and flavor of pipe clock) can be active at a
>> given moment (not to be confused with USB3 not being able to be
>> tunneled as USB4 packets - that of course remains possible).
>> The DP PLL is still used for clocking tunneled DP links. It may be
>> turned off to save power when no tunnels are active, but that's left as
>> a TODO item for now.
>>
>> Due to the nature of USB4, the Type-C handling happens entirely inside
>> the Host Router, and as such the QMPPHY's mux_set() function is
>> nullified for the period when USB4 PHY remains active. This is strictly
>> necessary, as the Host Router driver is going to excercise manual
>> control over the USB4 PHY's power state, which is needed by the suspend
>> and resume flows. Failure to control that synchronously with other
>> parts of the code results in a SoC crash by unlocked access.
>>
>> Because of that, a new struct phy is spawned to expose the USB4 mode,
>> along with a .set_mode callback to allow toggling between USB4 and TBT3
>> submodes.
>>
>> Thunderbolt 3, having a number of differences vs USB4, requires a
>> couple specific overrides, pertaining to electrical characteristics,
>> which are easily accommodated for.
>>
>> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>> ---
>> drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 392 ++++++++++++++++++++++++------
>> 1 file changed, 322 insertions(+), 70 deletions(-)
>>
>
> Overall it looks good. The major question (after looking at TODOs), do
> we need a separate submode for USB+DP / TBT+DP?
The problem space is as follows:
After a TBT (collectively TBT3+ and USB4) link has been established and
we have a link partner, we may (based on the HW capabilities and user
config, such as kernel params but not only) start or stop a DP tunnel at
runtime. On Qualcomm hardware, the PHY is kept in USB4 mode and its DP
AUX lines are not used (instead, the encapsulated DP AUX packets are r/w
entirely within the USB4 subsystem via a pair of FIFOs that Linux sees
as a separate DP AUX host)
Then, on hamoa/glymur specifically, any of the 3 USB4-capable DP hosts
can be muxed to either of the 2 DPIN ports on any of the 3 USB4 routers
(and each of these routers is hardwired to one of the PHYs).
To underline, we have 3 DP producers and 6 consumers. If there's e.g. a
super high-res display at one of the physical ports, or a long
daisy-chain, we may need to use 2 DPTXes to service 1 receptacle. Then,
we would only need one of the PHYs (associated with the router that's
wired to that port) to provide a DP clock.
This, along with the normal (logical or physical) present/absent status
can change at runtime. My plan is to use phy_set_opts(dp_tunelling=true)
or something along those lines to toggle that bit as necessary
Konrad
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 3/5] phy: qualcomm: qmp-combo: Add preliminary USB4 support
2026-05-18 14:15 ` Konrad Dybcio
@ 2026-05-18 15:38 ` Dmitry Baryshkov
2026-05-19 8:12 ` Konrad Dybcio
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry Baryshkov @ 2026-05-18 15:38 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Konrad Dybcio, Vinod Koul, Neil Armstrong, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, linux-kernel,
linux-phy, linux-arm-msm, devicetree, usb4-upstream,
Raghavendra Thoorpu, Mika Westerberg, Sven Peter
On Mon, May 18, 2026 at 04:15:16PM +0200, Konrad Dybcio wrote:
> On 5/18/26 3:57 PM, Dmitry Baryshkov wrote:
> > On Mon, May 18, 2026 at 12:29:50PM +0200, Konrad Dybcio wrote:
> >> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> >>
> >> Some Combo PHYs (so far only on SC8280XP, X1E80100 and Glymur), come in
> >> a flavor called USB43DP, which as the name implies, features USB4, USB3
> >> and DP signal processing capabilities. In that architecture, USB3 and
> >> USB4 PHYs share the same USB_PLL while featuring separate logic spaces.
> >> The DP part is roughly the same as on the instances without USB4.
> >>
> >> The USB4 and USB3/DP operation modes of the PHY are mutually exclusive.
> >> Only one USB protocol (and flavor of pipe clock) can be active at a
> >> given moment (not to be confused with USB3 not being able to be
> >> tunneled as USB4 packets - that of course remains possible).
> >> The DP PLL is still used for clocking tunneled DP links. It may be
> >> turned off to save power when no tunnels are active, but that's left as
> >> a TODO item for now.
> >>
> >> Due to the nature of USB4, the Type-C handling happens entirely inside
> >> the Host Router, and as such the QMPPHY's mux_set() function is
> >> nullified for the period when USB4 PHY remains active. This is strictly
> >> necessary, as the Host Router driver is going to excercise manual
> >> control over the USB4 PHY's power state, which is needed by the suspend
> >> and resume flows. Failure to control that synchronously with other
> >> parts of the code results in a SoC crash by unlocked access.
> >>
> >> Because of that, a new struct phy is spawned to expose the USB4 mode,
> >> along with a .set_mode callback to allow toggling between USB4 and TBT3
> >> submodes.
> >>
> >> Thunderbolt 3, having a number of differences vs USB4, requires a
> >> couple specific overrides, pertaining to electrical characteristics,
> >> which are easily accommodated for.
> >>
> >> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> >> ---
> >> drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 392 ++++++++++++++++++++++++------
> >> 1 file changed, 322 insertions(+), 70 deletions(-)
> >>
> >
> > Overall it looks good. The major question (after looking at TODOs), do
> > we need a separate submode for USB+DP / TBT+DP?
>
> The problem space is as follows:
>
> After a TBT (collectively TBT3+ and USB4) link has been established and
> we have a link partner, we may (based on the HW capabilities and user
> config, such as kernel params but not only) start or stop a DP tunnel at
> runtime. On Qualcomm hardware, the PHY is kept in USB4 mode and its DP
> AUX lines are not used (instead, the encapsulated DP AUX packets are r/w
> entirely within the USB4 subsystem via a pair of FIFOs that Linux sees
> as a separate DP AUX host)
So far so good. But I still don't grok if having a DP-over-USB4 is a
separate submode or not. I.e. I see code (and TODOs) to detect and
handle DP going on and off. Would it be better if we specify that
explicitly?
> Then, on hamoa/glymur specifically, any of the 3 USB4-capable DP hosts
> can be muxed to either of the 2 DPIN ports on any of the 3 USB4 routers
> (and each of these routers is hardwired to one of the PHYs).
>
> To underline, we have 3 DP producers and 6 consumers. If there's e.g. a
> super high-res display at one of the physical ports, or a long
> daisy-chain, we may need to use 2 DPTXes to service 1 receptacle. Then,
> we would only need one of the PHYs (associated with the router that's
> wired to that port) to provide a DP clock.
>
> This, along with the normal (logical or physical) present/absent status
> can change at runtime. My plan is to use phy_set_opts(dp_tunelling=true)
> or something along those lines to toggle that bit as necessary
I don't see phy_set_opts(). So maybe a submode then...
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 3/5] phy: qualcomm: qmp-combo: Add preliminary USB4 support
2026-05-18 15:38 ` Dmitry Baryshkov
@ 2026-05-19 8:12 ` Konrad Dybcio
0 siblings, 0 replies; 17+ messages in thread
From: Konrad Dybcio @ 2026-05-19 8:12 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Konrad Dybcio, Vinod Koul, Neil Armstrong, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, linux-kernel,
linux-phy, linux-arm-msm, devicetree, usb4-upstream,
Raghavendra Thoorpu, Mika Westerberg, Sven Peter
On 5/18/26 5:38 PM, Dmitry Baryshkov wrote:
> On Mon, May 18, 2026 at 04:15:16PM +0200, Konrad Dybcio wrote:
>> On 5/18/26 3:57 PM, Dmitry Baryshkov wrote:
>>> On Mon, May 18, 2026 at 12:29:50PM +0200, Konrad Dybcio wrote:
>>>> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>>>>
>>>> Some Combo PHYs (so far only on SC8280XP, X1E80100 and Glymur), come in
>>>> a flavor called USB43DP, which as the name implies, features USB4, USB3
>>>> and DP signal processing capabilities. In that architecture, USB3 and
>>>> USB4 PHYs share the same USB_PLL while featuring separate logic spaces.
>>>> The DP part is roughly the same as on the instances without USB4.
>>>>
>>>> The USB4 and USB3/DP operation modes of the PHY are mutually exclusive.
>>>> Only one USB protocol (and flavor of pipe clock) can be active at a
>>>> given moment (not to be confused with USB3 not being able to be
>>>> tunneled as USB4 packets - that of course remains possible).
>>>> The DP PLL is still used for clocking tunneled DP links. It may be
>>>> turned off to save power when no tunnels are active, but that's left as
>>>> a TODO item for now.
>>>>
>>>> Due to the nature of USB4, the Type-C handling happens entirely inside
>>>> the Host Router, and as such the QMPPHY's mux_set() function is
>>>> nullified for the period when USB4 PHY remains active. This is strictly
>>>> necessary, as the Host Router driver is going to excercise manual
>>>> control over the USB4 PHY's power state, which is needed by the suspend
>>>> and resume flows. Failure to control that synchronously with other
>>>> parts of the code results in a SoC crash by unlocked access.
>>>>
>>>> Because of that, a new struct phy is spawned to expose the USB4 mode,
>>>> along with a .set_mode callback to allow toggling between USB4 and TBT3
>>>> submodes.
>>>>
>>>> Thunderbolt 3, having a number of differences vs USB4, requires a
>>>> couple specific overrides, pertaining to electrical characteristics,
>>>> which are easily accommodated for.
>>>>
>>>> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>>>> ---
>>>> drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 392 ++++++++++++++++++++++++------
>>>> 1 file changed, 322 insertions(+), 70 deletions(-)
>>>>
>>>
>>> Overall it looks good. The major question (after looking at TODOs), do
>>> we need a separate submode for USB+DP / TBT+DP?
>>
>> The problem space is as follows:
>>
>> After a TBT (collectively TBT3+ and USB4) link has been established and
>> we have a link partner, we may (based on the HW capabilities and user
>> config, such as kernel params but not only) start or stop a DP tunnel at
>> runtime. On Qualcomm hardware, the PHY is kept in USB4 mode and its DP
>> AUX lines are not used (instead, the encapsulated DP AUX packets are r/w
>> entirely within the USB4 subsystem via a pair of FIFOs that Linux sees
>> as a separate DP AUX host)
>
> So far so good. But I still don't grok if having a DP-over-USB4 is a
> separate submode or not. I.e. I see code (and TODOs) to detect and
> handle DP going on and off. Would it be better if we specify that
> explicitly?
I really don't want to end up in a situation like we have with:
$ rg _USB include/linux/phy/phy.h
29: PHY_MODE_USB_HOST,
30: PHY_MODE_USB_HOST_LS,
31: PHY_MODE_USB_HOST_FS,
32: PHY_MODE_USB_HOST_HS,
33: PHY_MODE_USB_HOST_SS,
34: PHY_MODE_USB_DEVICE,
35: PHY_MODE_USB_DEVICE_LS,
36: PHY_MODE_USB_DEVICE_FS,
37: PHY_MODE_USB_DEVICE_HS,
38: PHY_MODE_USB_DEVICE_SS,
39: PHY_MODE_USB_OTG,
>> Then, on hamoa/glymur specifically, any of the 3 USB4-capable DP hosts
>> can be muxed to either of the 2 DPIN ports on any of the 3 USB4 routers
>> (and each of these routers is hardwired to one of the PHYs).
>>
>> To underline, we have 3 DP producers and 6 consumers. If there's e.g. a
>> super high-res display at one of the physical ports, or a long
>> daisy-chain, we may need to use 2 DPTXes to service 1 receptacle. Then,
>> we would only need one of the PHYs (associated with the router that's
>> wired to that port) to provide a DP clock.
>>
>> This, along with the normal (logical or physical) present/absent status
>> can change at runtime. My plan is to use phy_set_opts(dp_tunelling=true)
>> or something along those lines to toggle that bit as necessary
>
> I don't see phy_set_opts(). So maybe a submode then...
Sorry, I misremembered the name. The function is phy_configure(), and it
takes a union phy_configure_opts, hence the confusion
Konrad
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 4/5] phy: qualcomm: qmp-combo: Add USB4/TBT3 configuration data for Hamoa
2026-05-18 10:29 [PATCH 0/5] USB4 mode programming for QMMPHY on X1E Konrad Dybcio
` (2 preceding siblings ...)
2026-05-18 10:29 ` [PATCH 3/5] phy: qualcomm: qmp-combo: Add preliminary USB4 support Konrad Dybcio
@ 2026-05-18 10:29 ` Konrad Dybcio
2026-05-18 11:49 ` sashiko-bot
2026-05-18 10:29 ` [PATCH 5/5] arm64: dts: qcom: hamoa: Extend QMPPHY description for USB4 Konrad Dybcio
4 siblings, 1 reply; 17+ messages in thread
From: Konrad Dybcio @ 2026-05-18 10:29 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: linux-kernel, linux-phy, linux-arm-msm, devicetree, usb4-upstream,
Raghavendra Thoorpu, Mika Westerberg, Sven Peter, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Add the offsets and configuration tables to support USB4 and
Thunderbolt 3 operation on the USB4-capable PHYs found on Hamoa chips.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 200 ++++++++++++++++++++-
drivers/phy/qualcomm/phy-qcom-qmp-pcs-aon-v6.h | 2 +
drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v6.h | 15 ++
.../phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h | 45 +++++
4 files changed, 256 insertions(+), 6 deletions(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
index 898f42de4a08..0916d9a4599e 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
@@ -235,6 +235,7 @@ static const unsigned int qmp_v6_n4_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = {
[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V6_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR,
[QPHY_PCS_CLAMP_ENABLE] = QPHY_V6_PCS_AON_CLAMP_ENABLE,
+ [QPHY_PCS_USB4_CLAMP_ENABLE] = QPHY_V6_PCS_AON_USB4_CLAMP_ENABLE,
[QPHY_COM_RESETSM_CNTRL] = QSERDES_V6_COM_RESETSM_CNTRL,
[QPHY_COM_C_READY_STATUS] = QSERDES_V6_COM_C_READY_STATUS,
@@ -1812,7 +1813,85 @@ static const struct qmp_phy_init_tbl sc8280xp_usb43dp_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
};
-static const struct qmp_phy_init_tbl x1e80100_usb43dp_serdes_tbl[] = {
+static const struct qmp_phy_init_tbl x1e80100_usb4_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER1, 0x62),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER2, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE0, 0xd9),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE1, 0xb2),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_BUF_ENABLE, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_CFG, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x41),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x43),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MSB_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x86),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MSB_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE1, 0xa7),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE1_MODE0, 0xba),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE2_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE1_MODE1, 0x3c),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE2_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CORE_CLK_DIV_MODE0, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORECLK_DIV_MODE1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x76),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORE_CLK_EN, 0xb0),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO_MODE1, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_INTEGLOOP_GAIN0_MODE0, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_INTEGLOOP_GAIN0_MODE1, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IPTRIM, 0x17),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SVS_MODE_CLK_SEL, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_MODE, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_VCO_DC_LEVEL_CTRL, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_ADDITIONAL_MISC_2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_INITVAL2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAXVAL2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_POST_DIV_MUX, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_BG_TIMER, 0x0a),
+};
+
+static const struct qmp_phy_init_tbl x1e80100_usb4_tb3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER1, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x54),
+};
+
+static const struct qmp_phy_init_tbl x1e80100_usb4_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_LANE_MODE_1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_LANE_MODE_2, 0x50),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_LANE_MODE_3, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_RES_CODE_LANE_OFFSET_TX, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_RES_CODE_LANE_OFFSET_RX, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_RX_MARG_COARSE_THRESH1_RATE210, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_RX_MARG_COARSE_THRESH1_RATE3, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_RX_MARG_COARSE_THRESH2_RATE210, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_RX_MARG_COARSE_THRESH2_RATE3, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_RX_MARG_COARSE_THRESH3_RATE210, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_RX_MARG_COARSE_THRESH3_RATE3, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_TX_LDO_TIMER_CTRL, 0x03),
+};
+
+static const struct qmp_phy_init_tbl x1e80100_usb3dp_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_EN_CENTER, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER1, 0x62),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER2, 0x02),
@@ -1873,6 +1952,68 @@ static const struct qmp_phy_init_tbl x1e80100_usb43dp_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_RES_CODE_LANE_OFFSET_RX, 0x0a),
};
+static const struct qmp_phy_init_tbl x1e80100_usb4_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_SIGDET_ENABLES, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE2_B0, 0xd3),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE2_B1, 0xee),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE2_B2, 0x58),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE2_B4, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE3_B0, 0xbf),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE3_B1, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE3_B2, 0xa2),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE3_B3, 0xdd),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE3_B4, 0x6a),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE3_B5, 0xdb),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE3_B6, 0xe3),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_LOW_RATE2, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_HIGH_RATE2, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_LOW_RATE3, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_HIGH_RATE3, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_PI_CTRL1, 0xd0),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_PI_CTRL2, 0x48),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_SB2_THRESH1_RATE3, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_SB2_GAIN2_RATE2, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_SB2_GAIN1_RATE3, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_SB2_GAIN2_RATE3, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_IVCM_CAL_CODE_OVERRIDE, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_RX_IVCM_CAL_CTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_RX_SUMMER_CAL_SPD_MODE, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DCC_CMUX_POSTCAL_OFFSET, 0x1d),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_FO_GAIN_RATE2, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_FO_GAIN_RATE3, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_SO_GAIN_RATE2, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_SO_GAIN_RATE3, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_PI_CONTROLS, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_FASTLOCK_FO_GAIN_RATE3, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_FASTLOCK_SO_GAIN_RATE3, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_SO_SATURATION, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_RX_IVCM_POSTCAL_OFFSET, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_VGA_CAL_CNTRL1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_VGA_CAL_MAN_VAL, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_DAC_ENABLE1, 0x88),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_DAC_ENABLE2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_2, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_3, 0x45),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_TAP3_CTRL, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_TAP4_CTRL, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_TAP5_CTRL, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_GM_CAL, 0x0d),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_RX_OFFSET_ADAPTOR_CNTRL3, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_EOM_CTRL1, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_EOM_CTRL2, 0x21),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_TAP3_MANVAL_KTAP, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_TAP4_MANVAL_KTAP, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_TAP5_MANVAL_KTAP, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_VTHRESH_CAL_MAN_VAL_RATE3, 0x78),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_Q_PI_INTRINSIC_BIAS_RATE32, 0x27),
+ QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_RX_BKUP_CTRL1, 0x14),
+};
+
static const struct qmp_phy_init_tbl x1e80100_usb43dp_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_SIGDET_CNTRL, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
@@ -1911,6 +2052,23 @@ static const struct qmp_phy_init_tbl x1e80100_usb43dp_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_RX_BKUP_CTRL1, 0x14),
};
+static const struct qmp_phy_init_tbl x1e80100_usb4_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_POWER_STATE_CONFIG3, 0x4f),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_LOCK_DETECT_CONFIG1, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_LOCK_DETECT_CONFIG2, 0xc0),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_RX_SIGDET_LVL, 0x55),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_PCS_PCS_TX_RX_CONFIG1, 0x51),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_EQ_CONFIG1, 0x2e),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_G3_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_G3_EQ_CONFIG5, 0x1e),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_G2_FOM_EQ_CONFIG1, 0x83),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_G2_FOM_EQ_CONFIG3, 0x2e),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_G2_FOM_EQ_CONFIG5, 0x10),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_G3_FOM_EQ_CONFIG3, 0x25),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_G3_FOM_EQ_CONFIG5, 0x22),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB4_LPFS_TX_ECSTART, 0x0f),
+};
+
static const struct qmp_phy_init_tbl x1e80100_usb43dp_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V6_N4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
QMP_PHY_INIT_CFG(QPHY_V6_N4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
@@ -2429,6 +2587,23 @@ static const struct qmp_combo_offsets qmp_combo_offsets_v5 = {
.dp_dp_phy = 0x2200,
};
+static const struct qmp_combo_offsets qmp_combo_offsets_v6_n4 = {
+ .com = 0x0000,
+ .usb3_pcs_aon = 0x0100,
+ .txa = 0x0400,
+ .rxa = 0x0600,
+ .txb = 0x0a00,
+ .rxb = 0x0c00,
+ .usb3_serdes = 0x1000,
+ .usb3_pcs_misc = 0x1200,
+ .usb3_pcs = 0x1400,
+ .usb3_pcs_usb = 0x1700,
+ .dp_serdes = 0x2000,
+ .dp_dp_phy = 0x2200,
+ .usb4_serdes = 0x3000,
+ .usb4_pcs = 0x3400,
+};
+
static const struct qmp_combo_offsets qmp_combo_offsets_v8 = {
.com = 0x0000,
.txa = 0x1400,
@@ -2689,11 +2864,11 @@ static const struct qmp_phy_cfg sc8280xp_usb43dpphy_cfg = {
.regs = qmp_v5_5nm_usb3phy_regs_layout,
};
-static const struct qmp_phy_cfg x1e80100_usb3dpphy_cfg = {
- .offsets = &qmp_combo_offsets_v5,
+static const struct qmp_phy_cfg x1e80100_usb43dpphy_cfg = {
+ .offsets = &qmp_combo_offsets_v6_n4,
- .serdes_tbl = x1e80100_usb43dp_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(x1e80100_usb43dp_serdes_tbl),
+ .serdes_tbl = x1e80100_usb3dp_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(x1e80100_usb3dp_serdes_tbl),
.tx_tbl = x1e80100_usb43dp_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(x1e80100_usb43dp_tx_tbl),
.rx_tbl = x1e80100_usb43dp_rx_tbl,
@@ -2728,6 +2903,19 @@ static const struct qmp_phy_cfg x1e80100_usb3dpphy_cfg = {
.configure_dp_phy = qmp_v4_configure_dp_phy,
.calibrate_dp_phy = qmp_v4_calibrate_dp_phy,
+ .usb4_serdes_tbl = x1e80100_usb4_serdes_tbl,
+ .usb4_serdes_tbl_num = ARRAY_SIZE(x1e80100_usb4_serdes_tbl),
+ .usb4_serdes_tb3_ovrd_tbl = x1e80100_usb4_tb3_serdes_tbl,
+ .usb4_serdes_tb3_ovrd_num = ARRAY_SIZE(x1e80100_usb4_tb3_serdes_tbl),
+
+ .usb4_tx_tbl = x1e80100_usb4_tx_tbl,
+ .usb4_tx_tbl_num = ARRAY_SIZE(x1e80100_usb4_tx_tbl),
+ .usb4_rx_tbl = x1e80100_usb4_rx_tbl,
+ .usb4_rx_tbl_num = ARRAY_SIZE(x1e80100_usb4_rx_tbl),
+
+ .usb4_pcs_tbl = x1e80100_usb4_pcs_tbl,
+ .usb4_pcs_tbl_num = ARRAY_SIZE(x1e80100_usb4_pcs_tbl),
+
.reset_list = msm8996_usb3phy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
.vreg_list = qmp_phy_vreg_l,
@@ -5296,7 +5484,7 @@ static const struct of_device_id qmp_combo_of_match_table[] = {
},
{
.compatible = "qcom,x1e80100-qmp-usb3-dp-phy",
- .data = &x1e80100_usb3dpphy_cfg,
+ .data = &x1e80100_usb43dpphy_cfg,
},
{ }
};
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-aon-v6.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-aon-v6.h
index 52db31a7cf22..f19f461297b6 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-aon-v6.h
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-aon-v6.h
@@ -8,5 +8,7 @@
/* Only for QMP V6 PHY - PCS_AON registers */
#define QPHY_V6_PCS_AON_CLAMP_ENABLE 0x00
+/* Valid only for USB43DP variants */
+#define QPHY_V6_PCS_AON_USB4_CLAMP_ENABLE 0x04
#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v6.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v6.h
index df670143feb1..30fdb437146c 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v6.h
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v6.h
@@ -14,4 +14,19 @@
#define QPHY_V6_PCS_USB3_RCVR_DTCT_DLY_U3_L 0x40
#define QPHY_V6_PCS_USB3_RCVR_DTCT_DLY_U3_H 0x44
+#define QPHY_V6_PCS_USB4_POWER_STATE_CONFIG3 0x98
+#define QPHY_V6_PCS_USB4_LOCK_DETECT_CONFIG1 0xd0
+#define QPHY_V6_PCS_USB4_LOCK_DETECT_CONFIG2 0xd4
+#define QPHY_V6_PCS_USB4_RX_SIGDET_LVL 0x17c
+#define QPHY_V6_PCS_USB4_PCS_PCS_TX_RX_CONFIG1 0x1a8
+#define QPHY_V6_PCS_USB4_EQ_CONFIG1 0x1b8
+#define QPHY_V6_PCS_USB4_G3_EQ_CONFIG1 0x1d8
+#define QPHY_V6_PCS_USB4_G3_EQ_CONFIG5 0x1e8
+#define QPHY_V6_PCS_USB4_G2_FOM_EQ_CONFIG1 0x1f4
+#define QPHY_V6_PCS_USB4_G2_FOM_EQ_CONFIG3 0x1fc
+#define QPHY_V6_PCS_USB4_G2_FOM_EQ_CONFIG5 0x204
+#define QPHY_V6_PCS_USB4_G3_FOM_EQ_CONFIG3 0x210
+#define QPHY_V6_PCS_USB4_G3_FOM_EQ_CONFIG5 0x218
+#define QPHY_V6_PCS_USB4_LPFS_TX_ECSTART 0x220
+
#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h
index d37cc0d4fd36..e72ae2cb460a 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h
@@ -23,27 +23,63 @@
#define QSERDES_V6_N4_TX_TRAN_DRVR_EMP_EN 0xac
#define QSERDES_V6_N4_TX_TX_BAND 0xd8
#define QSERDES_V6_N4_TX_INTERFACE_SELECT 0xe4
+#define QSERDES_V6_N4_TX_RX_MARG_COARSE_THRESH1_RATE210 0xe8
+#define QSERDES_V6_N4_TX_RX_MARG_COARSE_THRESH1_RATE3 0xec
+#define QSERDES_V6_N4_TX_RX_MARG_COARSE_THRESH2_RATE210 0xf0
+#define QSERDES_V6_N4_TX_RX_MARG_COARSE_THRESH2_RATE3 0xf4
+#define QSERDES_V6_N4_TX_RX_MARG_COARSE_THRESH3_RATE210 0xf8
+#define QSERDES_V6_N4_TX_RX_MARG_COARSE_THRESH3_RATE3 0xfc
#define QSERDES_V6_N4_TX_VMODE_CTRL1 0xb0
+#define QSERDES_V6_TX_LDO_TIMER_CTRL 0x120
#define QSERDES_V6_N4_RX_UCDR_FO_GAIN_RATE2 0x8
+#define QSERDES_V6_N4_RX_UCDR_FO_GAIN_RATE3 0xc
+#define QSERDES_V6_N4_RX_UCDR_SO_GAIN_RATE3 0x1c
#define QSERDES_V6_N4_RX_UCDR_SO_GAIN_RATE2 0x18
#define QSERDES_V6_N4_RX_UCDR_PI_CONTROLS 0x20
+#define QSERDES_V6_N4_RX_EOM_CTRL1 0x44
+#define QSERDES_V6_N4_RX_EOM_CTRL2 0x48
#define QSERDES_V6_N4_RX_IVCM_CAL_CODE_OVERRIDE 0x94
#define QSERDES_V6_N4_RX_RX_IVCM_CAL_CTRL2 0x9c
#define QSERDES_V6_N4_RX_RX_IVCM_POSTCAL_OFFSET 0xa0
+#define QSERDES_V6_N4_RX_DFE_1 0xac
+#define QSERDES_V6_N4_RX_DFE_2 0xb0
#define QSERDES_V6_N4_RX_DFE_3 0xb4
+#define QSERDES_V6_N4_RX_DFE_TAP3_CTRL 0xbc
+#define QSERDES_V6_N4_RX_DFE_TAP3_MANVAL_KTAP 0xc0
+#define QSERDES_V6_N4_RX_DFE_TAP4_CTRL 0xc4
+#define QSERDES_V6_N4_RX_DFE_TAP4_MANVAL_KTAP 0xc8
+#define QSERDES_V6_N4_RX_DFE_TAP5_CTRL 0xcc
+#define QSERDES_V6_N4_RX_DFE_TAP5_MANVAL_KTAP 0xd0
#define QSERDES_V6_N4_RX_VGA_CAL_CNTRL1 0xe0
#define QSERDES_V6_N4_RX_VGA_CAL_MAN_VAL 0xe8
+#define QSERDES_V6_N4_RX_VTHRESH_CAL_MAN_VAL_RATE3 0x108
#define QSERDES_V6_N4_RX_GM_CAL 0x10c
+#define QSERDES_V6_N4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x13c
+#define QSERDES_V6_N4_RX_RX_OFFSET_ADAPTOR_CNTRL3 0x144
#define QSERDES_V6_N4_RX_SIGDET_ENABLES 0x148
#define QSERDES_V6_N4_RX_SIGDET_CNTRL 0x14c
#define QSERDES_V6_N4_RX_SIGDET_DEGLITCH_CNTRL 0x154
#define QSERDES_V6_N4_RX_DFE_CTLE_POST_CAL_OFFSET 0x194
+#define QSERDES_V6_N4_RX_DCC_CMUX_POSTCAL_OFFSET 0x1a8
#define QSERDES_V6_N4_RX_Q_PI_INTRINSIC_BIAS_RATE32 0x1dc
+#define QSERDES_V6_N4_RX_UCDR_FASTLOCK_FO_GAIN_RATE3 0x200
+#define QSERDES_V6_N4_RX_UCDR_FASTLOCK_SO_GAIN_RATE3 0x210
+#define QSERDES_V6_N4_RX_UCDR_SO_SATURATION 0x214
+#define QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_LOW_RATE1 0x224
+#define QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_HIGH_RATE1 0x228
+#define QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_LOW_RATE2 0x22c
+#define QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_HIGH_RATE2 0x230
+#define QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_LOW_RATE3 0x234
+#define QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_HIGH_RATE3 0x238
#define QSERDES_V6_N4_RX_UCDR_PI_CTRL1 0x23c
#define QSERDES_V6_N4_RX_UCDR_PI_CTRL2 0x240
+#define QSERDES_V6_N4_RX_UCDR_SB2_THRESH1_RATE3 0x250
+#define QSERDES_V6_N4_RX_UCDR_SB2_GAIN1_RATE3 0x270
#define QSERDES_V6_N4_RX_UCDR_SB2_GAIN2_RATE2 0x27c
+#define QSERDES_V6_N4_RX_UCDR_SB2_GAIN2_RATE3 0x280
#define QSERDES_V6_N4_RX_DFE_DAC_ENABLE1 0x298
+#define QSERDES_V6_N4_RX_DFE_DAC_ENABLE2 0x29c
#define QSERDES_V6_N4_RX_MODE_RATE_0_1_B0 0x2b8
#define QSERDES_V6_N4_RX_MODE_RATE_0_1_B1 0x2bc
#define QSERDES_V6_N4_RX_MODE_RATE_0_1_B2 0x2c0
@@ -58,6 +94,15 @@
#define QSERDES_V6_N4_RX_MODE_RATE2_B4 0x2e4
#define QSERDES_V6_N4_RX_MODE_RATE2_B5 0x2e8
#define QSERDES_V6_N4_RX_MODE_RATE2_B6 0x2ec
+
+#define QSERDES_V6_N4_RX_MODE_RATE3_B0 0x2f0
+#define QSERDES_V6_N4_RX_MODE_RATE3_B1 0x2f4
+#define QSERDES_V6_N4_RX_MODE_RATE3_B2 0x2f8
+#define QSERDES_V6_N4_RX_MODE_RATE3_B3 0x2fc
+#define QSERDES_V6_N4_RX_MODE_RATE3_B4 0x300
+#define QSERDES_V6_N4_RX_MODE_RATE3_B5 0x304
+#define QSERDES_V6_N4_RX_MODE_RATE3_B6 0x308
+
#define QSERDES_V6_N4_RX_RX_SUMMER_CAL_SPD_MODE 0x30c
#define QSERDES_V6_N4_RX_RX_BKUP_CTRL1 0x310
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH 4/5] phy: qualcomm: qmp-combo: Add USB4/TBT3 configuration data for Hamoa
2026-05-18 10:29 ` [PATCH 4/5] phy: qualcomm: qmp-combo: Add USB4/TBT3 configuration data for Hamoa Konrad Dybcio
@ 2026-05-18 11:49 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-05-18 11:49 UTC (permalink / raw)
To: Konrad Dybcio
Cc: olteanv, linux-phy, devicetree, conor+dt, robh, neil.armstrong,
vkoul
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Low] Incorrect `TX_` prefix for RX UCDR fastlock registers.
- [Low] Typo in register macro name: `LPFS` instead of `LFPS`.
- [Low] Redundant `PCS_PCS` prefix in register macro name.
- [Low] Missing `_N4_` infix in the `QSERDES_V6_TX_LDO_TIMER_CTRL` macro.
--
commit 13a3731f3b7c12c298d2f498011a7ef852fd4a42
Author: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
phy: qualcomm: qmp-combo: Add USB4/TBT3 configuration data for Hamoa
This commit adds the offsets and configuration tables to support USB4
and Thunderbolt 3 operation on the USB4-capable PHYs found on Hamoa chips.
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
> index 898f42de4a086..0916d9a4599ed 100644
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
[ ... ]
> @@ -1873,6 +1952,68 @@ static const struct qmp_phy_init_tbl x1e80100_usb43dp_tx_tbl[] = {
> [ ... ]
> +static const struct qmp_phy_init_tbl x1e80100_usb4_rx_tbl[] = {
> [ ... ]
> + QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_LOW_RATE2, 0x30),
> + QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_HIGH_RATE2, 0x14),
> + QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_LOW_RATE3, 0xff),
> + QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_UCDR_FASTLOCK_COUNTER_HIGH_RATE3, 0x3f),
This isn't a bug, but should these use the RX_ prefix instead of TX_?
These registers appear to map to the RX block address space and are used
here to configure the RX hardware.
[ ... ]
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v6.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v6.h
> index df670143feb1e..30fdb437146c4 100644
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v6.h
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v6.h
> @@ -14,4 +14,19 @@
> #define QPHY_V6_PCS_USB3_RCVR_DTCT_DLY_U3_L 0x40
> #define QPHY_V6_PCS_USB3_RCVR_DTCT_DLY_U3_H 0x44
>
> +#define QPHY_V6_PCS_USB4_POWER_STATE_CONFIG3 0x98
> [ ... ]
> +#define QPHY_V6_PCS_USB4_PCS_PCS_TX_RX_CONFIG1 0x1a8
This isn't a bug, but is there a duplicate PCS_ prefix in this macro name?
> +#define QPHY_V6_PCS_USB4_EQ_CONFIG1 0x1b8
> [ ... ]
> +#define QPHY_V6_PCS_USB4_LPFS_TX_ECSTART 0x220
This isn't a bug, but is this a typo for LFPS (Low Frequency Periodic
Signaling)?
[ ... ]
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h
> index d37cc0d4fd365..e72ae2cb460ac 100644
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h
> @@ -23,27 +23,63 @@
> [ ... ]
> #define QSERDES_V6_N4_TX_VMODE_CTRL1 0xb0
> +#define QSERDES_V6_TX_LDO_TIMER_CTRL 0x120
This isn't a bug, but should this macro include the _N4_ infix for
consistency with the rest of the V6 N4 block definitions?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260518-topic-usb4phy-v1-0-71d827c49dca@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 5/5] arm64: dts: qcom: hamoa: Extend QMPPHY description for USB4
2026-05-18 10:29 [PATCH 0/5] USB4 mode programming for QMMPHY on X1E Konrad Dybcio
` (3 preceding siblings ...)
2026-05-18 10:29 ` [PATCH 4/5] phy: qualcomm: qmp-combo: Add USB4/TBT3 configuration data for Hamoa Konrad Dybcio
@ 2026-05-18 10:29 ` Konrad Dybcio
4 siblings, 0 replies; 17+ messages in thread
From: Konrad Dybcio @ 2026-05-18 10:29 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: linux-kernel, linux-phy, linux-arm-msm, devicetree, usb4-upstream,
Raghavendra Thoorpu, Mika Westerberg, Sven Peter, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
The USB4 part of the QMPPHY requires that one more GCC clock (P2RR2P -
PHY-to-Router, Router-to-PHY) is enabled for the PHY to initialize
successfully. Describe that.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/hamoa.dtsi | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/hamoa.dtsi b/arch/arm64/boot/dts/qcom/hamoa.dtsi
index 4ba751a65142..63430f49ba2a 100644
--- a/arch/arm64/boot/dts/qcom/hamoa.dtsi
+++ b/arch/arm64/boot/dts/qcom/hamoa.dtsi
@@ -2883,11 +2883,13 @@ usb_1_ss0_qmpphy: phy@fd5000 {
clocks = <&gcc GCC_USB3_PRIM_PHY_AUX_CLK>,
<&rpmhcc RPMH_CXO_CLK>,
<&gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>,
- <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>;
+ <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
+ <&gcc GCC_USB4_0_PHY_P2RR2P_PIPE_CLK>;
clock-names = "aux",
"ref",
"com_aux",
- "usb3_pipe";
+ "usb3_pipe",
+ "p2rr2p_pipe";
power-domains = <&gcc GCC_USB_0_PHY_GDSC>;
@@ -2954,11 +2956,13 @@ usb_1_ss1_qmpphy: phy@fda000 {
clocks = <&gcc GCC_USB3_SEC_PHY_AUX_CLK>,
<&tcsr TCSR_USB4_1_CLKREF_EN>,
<&gcc GCC_USB3_SEC_PHY_COM_AUX_CLK>,
- <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>;
+ <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>,
+ <&gcc GCC_USB4_1_PHY_P2RR2P_PIPE_CLK>;
clock-names = "aux",
"ref",
"com_aux",
- "usb3_pipe";
+ "usb3_pipe",
+ "p2rr2p_pipe";
power-domains = <&gcc GCC_USB_1_PHY_GDSC>;
@@ -3025,11 +3029,13 @@ usb_1_ss2_qmpphy: phy@fdf000 {
clocks = <&gcc GCC_USB3_TERT_PHY_AUX_CLK>,
<&tcsr TCSR_USB4_2_CLKREF_EN>,
<&gcc GCC_USB3_TERT_PHY_COM_AUX_CLK>,
- <&gcc GCC_USB3_TERT_PHY_PIPE_CLK>;
+ <&gcc GCC_USB3_TERT_PHY_PIPE_CLK>,
+ <&gcc GCC_USB4_2_PHY_P2RR2P_PIPE_CLK>;
clock-names = "aux",
"ref",
"com_aux",
- "usb3_pipe";
+ "usb3_pipe",
+ "p2rr2p_pipe";
power-domains = <&gcc GCC_USB_2_PHY_GDSC>;
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread