* [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328
@ 2025-01-15 1:26 Peter Geis
2025-01-15 1:26 ` [RFC PATCH v1 2/6] dt-bindings: phy: rockchip: add rk3328 usb3 phy Peter Geis
` (4 more replies)
0 siblings, 5 replies; 24+ messages in thread
From: Peter Geis @ 2025-01-15 1:26 UTC (permalink / raw)
To: Heiko Stuebner
Cc: zyw, kever.yang, frank.wang, william.wu, wulf, linux-rockchip,
Peter Geis, Alex Bee, Algea Cao, Arnd Bergmann, Conor Dooley,
Cristian Ciocaltea, Diederik de Haas, Dragan Simic, Elaine Zhang,
FUKAUMI Naoki, Johan Jonker, Jonas Karlman,
Kishon Vijay Abraham I, Krzysztof Kozlowski, Michael Turquette,
Philipp Zabel, Rob Herring, Sebastian Reichel, Stephen Boyd,
Trevor Woerner, Vinod Koul, Zhang Yubing, devicetree,
linux-arm-kernel, linux-clk, linux-kernel, linux-phy
This is my newly reworked phy driver for the rk3328 usb3 phy. It is
based loosely on my original version, but as of now almost nothing of
the original driver remains. The main fix here is the discovery of
BIT(6) in the interrupt enable grf register fixes the usb3 disconnection
detection (mostly). On occasion an unpopulated usb3 hub will take
several seconds to disconnect. However this means all of the hack around
work to reset the usb core manually is no longer required.
I did my best to document all registers I could find. A lot was taken
from emails between myself and Rockchip's engineers, much thanks to
William Wu <wulf@rock-chips.com> for their assistance here. The rest of
the config bits were taken from the rk3328 and rk3228h TRMs and the
downstream driver. Everything that I couldn't find a definition for is
prefixed UNK_ or UNKNOWN_. There's a lot of obviously used configuration
registers with the pipe interface that are also undocumented.
The only major bug I have so far is my AX88179 usb3 gigabit ethernet
adapter (Pluggable brand) crashes out when large amounts of data are
transmitted. I suspect this is related to the RX and TX tuning, as
leaving it at defaults makes things worse. As I am not a USB3 engineer
and I do not have the specialized knowledge and hardware to determine
what is going wrong, I am hoping the mailing list will have an answer
here.
Please test and review.
Very Respectfully,
Peter Geis
Peter Geis (6):
clk: rockchip: fix wrong clk_ref_usb3otg parent for rk3328
dt-bindings: phy: rockchip: add rk3328 usb3 phy
phy: rockchip: add driver for rk3328 usb3 phy
arm64: dts: rockchip: add rk3328 usb3 phy node
arm64: dts: rockchip: enable the usb3 phy on rk3328-roc boards
arm64: dts: rockchip: enable the usb3 phy on remaining rk3328 boards
.../bindings/phy/rockchip,inno-usb3phy.yaml | 166 ++++
.../boot/dts/rockchip/rk3328-nanopi-r2.dtsi | 12 +
.../dts/rockchip/rk3328-orangepi-r1-plus.dtsi | 12 +
arch/arm64/boot/dts/rockchip/rk3328-roc.dtsi | 12 +
.../boot/dts/rockchip/rk3328-rock-pi-e.dts | 12 +
.../arm64/boot/dts/rockchip/rk3328-rock64.dts | 12 +
arch/arm64/boot/dts/rockchip/rk3328.dtsi | 39 +
drivers/clk/rockchip/clk-rk3328.c | 2 +-
drivers/phy/rockchip/Kconfig | 10 +
drivers/phy/rockchip/Makefile | 1 +
drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 869 ++++++++++++++++++
11 files changed, 1146 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml
create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c
--
2.39.5
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 24+ messages in thread* [RFC PATCH v1 2/6] dt-bindings: phy: rockchip: add rk3328 usb3 phy 2025-01-15 1:26 [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 Peter Geis @ 2025-01-15 1:26 ` Peter Geis 2025-01-16 13:08 ` Krzysztof Kozlowski 2025-01-15 1:26 ` [RFC PATCH v1 3/6] phy: rockchip: add driver for " Peter Geis ` (3 subsequent siblings) 4 siblings, 1 reply; 24+ messages in thread From: Peter Geis @ 2025-01-15 1:26 UTC (permalink / raw) To: Heiko Stuebner Cc: zyw, kever.yang, frank.wang, william.wu, wulf, linux-rockchip, Peter Geis, Conor Dooley, Kishon Vijay Abraham I, Krzysztof Kozlowski, Rob Herring, Vinod Koul, devicetree, linux-arm-kernel, linux-kernel, linux-phy Add documentation for the usb3 phy as implemented on the rk3328 SoC. Signed-off-by: Peter Geis <pgwipeout@gmail.com> --- .../bindings/phy/rockchip,inno-usb3phy.yaml | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml diff --git a/Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml new file mode 100644 index 000000000000..cde489ca87ab --- /dev/null +++ b/Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml @@ -0,0 +1,166 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/rockchip,inno-usb3phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip USB 3.0 phy with Innosilicon IP block + +maintainers: + - Heiko Stuebner <heiko@sntech.de> + +properties: + compatible: + enum: + - rockchip,rk3328-usb3phy + + reg: + maxItems: 1 + + clocks: + minItems: 3 + maxItems: 3 + + clock-names: + items: + - const: refclk-usb3otg + - const: usb3phy-otg + - const: usb3phy-pipe + + interrupts: + minItems: 4 + + interrupt-names: + items: + - const: bvalid + - const: id + - const: linestate + - const: rxdet + + resets: + minItems: 6 + + reset-names: + items: + - const: usb3phy-u2-por + - const: usb3phy-u3-por + - const: usb3phy-pipe-mac + - const: usb3phy-utmi-mac + - const: usb3phy-utmi-apb + - const: usb3phy-pipe-apb + + "#address-cells": + const: 2 + + "#size-cells": + const: 2 + + ranges: true + +patternProperties: + + utmi-port@[0-9a-f]+$: + type: object + additionalProperties: false + + properties: + compatible: + enum: + - rockchip,rk3328-usb3phy-utmi + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + phy-supply: + description: + Phandle to a regulator that provides power to VBUS. + See ./phy-bindings.txt for details. + + required: + - compatible + - reg + - "#phy-cells" + + pipe-port@[0-9a-f]+$: + type: object + additionalProperties: false + + properties: + compatible: + enum: + - rockchip,rk3328-usb3phy-pipe + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + phy-supply: + description: + Phandle to a regulator that provides power to VBUS. + See ./phy-bindings.txt for details. + + required: + - compatible + - reg + - "#phy-cells" + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - interrupt-names + - resets + - reset-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/rk3328-cru.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + soc { + #address-cells = <2>; + #size-cells = <2>; + + usb3phy: usb3-phy@ff460000 { + compatible = "rockchip,rk3328-usb3phy"; + reg = <0x0 0xff460000 0x0 0x10000>; + clocks = <&cru SCLK_REF_USB3OTG>, <&cru PCLK_USB3PHY_OTG>, <&cru PCLK_USB3PHY_PIPE>; + clock-names = "refclk-usb3otg", "usb3phy-otg", "usb3phy-pipe"; + interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "bvalid", "id", "linestate", "rxdet"; + resets = <&cru SRST_USB3PHY_U2>, + <&cru SRST_USB3PHY_U3>, + <&cru SRST_USB3PHY_PIPE>, + <&cru SRST_USB3OTG_UTMI>, + <&cru SRST_USB3PHY_OTG_P>, + <&cru SRST_USB3PHY_PIPE_P>; + reset-names = "usb3phy-u2-por", "usb3phy-u3-por", + "usb3phy-pipe-mac", "usb3phy-utmi-mac", + "usb3phy-utmi-apb", "usb3phy-pipe-apb"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + usb3phy_utmi: utmi-port@ff470000 { + compatible = "rockchip,rk3328-usb3phy-utmi"; + reg = <0x0 0xff470000 0x0 0x8000>; + #phy-cells = <0>; + }; + + usb3phy_pipe: pipe-port@ff478000 { + compatible = "rockchip,rk3328-usb3phy-pipe"; + reg = <0x0 0xff478000 0x0 0x8000>; + #phy-cells = <0>; + }; + }; + }; -- 2.39.5 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 2/6] dt-bindings: phy: rockchip: add rk3328 usb3 phy 2025-01-15 1:26 ` [RFC PATCH v1 2/6] dt-bindings: phy: rockchip: add rk3328 usb3 phy Peter Geis @ 2025-01-16 13:08 ` Krzysztof Kozlowski 2025-01-16 13:32 ` Peter Geis 0 siblings, 1 reply; 24+ messages in thread From: Krzysztof Kozlowski @ 2025-01-16 13:08 UTC (permalink / raw) To: Peter Geis, Heiko Stuebner Cc: zyw, kever.yang, frank.wang, william.wu, wulf, linux-rockchip, Conor Dooley, Kishon Vijay Abraham I, Krzysztof Kozlowski, Rob Herring, Vinod Koul, devicetree, linux-arm-kernel, linux-kernel, linux-phy On 15/01/2025 02:26, Peter Geis wrote: > Add documentation for the usb3 phy as implemented on the rk3328 SoC. > > Signed-off-by: Peter Geis <pgwipeout@gmail.com> > --- > > .../bindings/phy/rockchip,inno-usb3phy.yaml | 166 ++++++++++++++++++ > 1 file changed, 166 insertions(+) > create mode 100644 Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml > > diff --git a/Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml > new file mode 100644 > index 000000000000..cde489ca87ab > --- /dev/null > +++ b/Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml > @@ -0,0 +1,166 @@ > +# SPDX-License-Identifier: GPL-2.0-only Wrong license. Please run scripts/checkpatch.pl and fix reported warnings. After that, run also `scripts/checkpatch.pl --strict` and (probably) fix more warnings. Some warnings can be ignored, especially from --strict run, but the code here looks like it needs a fix. Feel free to get in touch if the warning is not clear. > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/phy/rockchip,inno-usb3phy.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: Rockchip USB 3.0 phy with Innosilicon IP block > + > +maintainers: > + - Heiko Stuebner <heiko@sntech.de> > + > +properties: > + compatible: > + enum: > + - rockchip,rk3328-usb3phy Why is this binding entirely different than existing usb2 phy? Nothing in common? This looks like made for driver and both - driver and binding - duplicating a lot. > + > + reg: > + maxItems: 1 > + > + clocks: > + minItems: 3 Drop > + maxItems: 3 > + > + clock-names: > + items: > + - const: refclk-usb3otg ref > + - const: usb3phy-otg otg > + - const: usb3phy-pipe pipe > + > + interrupts: > + minItems: 4 You code here randomly. reg has only maxItems, clocks have both and this have only minItems. Does not make sense. If you get it wrong, I would assume you repeat the same mistake but here it is like randomly chosen pieces. And this is making me wonder whether this was not sent too fast. Anyway: only maxItems. > + > + interrupt-names: > + items: > + - const: bvalid > + - const: id > + - const: linestate > + - const: rxdet > + > + resets: > + minItems: 6 maxItems instead > + > + reset-names: > + items: > + - const: usb3phy-u2-por > + - const: usb3phy-u3-por > + - const: usb3phy-pipe-mac > + - const: usb3phy-utmi-mac > + - const: usb3phy-utmi-apb > + - const: usb3phy-pipe-apb > + > + "#address-cells": > + const: 2 > + > + "#size-cells": > + const: 2 > + > + ranges: true > + > +patternProperties: > + Drop blank line > + utmi-port@[0-9a-f]+$: This wasn't tested. Missing quotes, missing starting anchor. > + type: object Explain what are the children here - description. > + additionalProperties: false > + > + properties: > + compatible: > + enum: > + - rockchip,rk3328-usb3phy-utmi > + > + reg: > + maxItems: 1 > + > + "#phy-cells": > + const: 0 Does not look correct. Your parent device is the phy, not child. Why do you create children per each type of phy? > + > + phy-supply: > + description: > + Phandle to a regulator that provides power to VBUS. > + See ./phy-bindings.txt for details. > + > + required: > + - compatible > + - reg > + - "#phy-cells" > + > + pipe-port@[0-9a-f]+$: > + type: object > + additionalProperties: false > + > + properties: > + compatible: > + enum: > + - rockchip,rk3328-usb3phy-pipe > + > + reg: > + maxItems: 1 > + > + "#phy-cells": > + const: 0 > + > + phy-supply: > + description: > + Phandle to a regulator that provides power to VBUS. > + See ./phy-bindings.txt for details. Drop "see ....". > + > + required: > + - compatible > + - reg > + - "#phy-cells" > + > +required: > + - compatible > + - reg > + - clocks > + - clock-names > + - interrupts > + - interrupt-names > + - resets > + - reset-names > + > +additionalProperties: false > + > +examples: > + - | > + #include <dt-bindings/clock/rk3328-cru.h> > + #include <dt-bindings/interrupt-controller/arm-gic.h> > + #include <dt-bindings/interrupt-controller/irq.h> > + soc { > + #address-cells = <2>; > + #size-cells = <2>; > + > + usb3phy: usb3-phy@ff460000 { > + compatible = "rockchip,rk3328-usb3phy"; > + reg = <0x0 0xff460000 0x0 0x10000>; > + clocks = <&cru SCLK_REF_USB3OTG>, <&cru PCLK_USB3PHY_OTG>, <&cru PCLK_USB3PHY_PIPE>; That's way over the limit. Wrapping is at 80. > + clock-names = "refclk-usb3otg", "usb3phy-otg", "usb3phy-pipe"; > + interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>, > + <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>; > + interrupt-names = "bvalid", "id", "linestate", "rxdet"; Best regards, Krzysztof -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 2/6] dt-bindings: phy: rockchip: add rk3328 usb3 phy 2025-01-16 13:08 ` Krzysztof Kozlowski @ 2025-01-16 13:32 ` Peter Geis 2025-01-16 13:59 ` Peter Geis 2025-01-18 9:06 ` Krzysztof Kozlowski 0 siblings, 2 replies; 24+ messages in thread From: Peter Geis @ 2025-01-16 13:32 UTC (permalink / raw) To: Krzysztof Kozlowski Cc: Heiko Stuebner, zyw, kever.yang, frank.wang, william.wu, wulf, linux-rockchip, Conor Dooley, Kishon Vijay Abraham I, Krzysztof Kozlowski, Rob Herring, Vinod Koul, devicetree, linux-arm-kernel, linux-kernel, linux-phy On Thu, Jan 16, 2025 at 8:08 AM Krzysztof Kozlowski <krzk@kernel.org> wrote: > > On 15/01/2025 02:26, Peter Geis wrote: > > Add documentation for the usb3 phy as implemented on the rk3328 SoC. > > > > Signed-off-by: Peter Geis <pgwipeout@gmail.com> > > --- > > > > .../bindings/phy/rockchip,inno-usb3phy.yaml | 166 ++++++++++++++++++ > > 1 file changed, 166 insertions(+) > > create mode 100644 Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml > > > > diff --git a/Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml > > new file mode 100644 > > index 000000000000..cde489ca87ab > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml > > @@ -0,0 +1,166 @@ > > +# SPDX-License-Identifier: GPL-2.0-only > > Wrong license. > > Please run scripts/checkpatch.pl and fix reported warnings. After that, > run also `scripts/checkpatch.pl --strict` and (probably) fix more > warnings. Some warnings can be ignored, especially from --strict run, > but the code here looks like it needs a fix. Feel free to get in touch > if the warning is not clear. Checkpatch literally told me to change this. Ran against my original dev binding: ./scripts/checkpatch.pl --strict Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml CHECK: DT binding documents should be licensed (GPL-2.0-only OR BSD-2-Clause) #1: FILE: Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml:1: +# SPDX-License-Identifier: GPL-2.0 > > > +%YAML 1.2 > > +--- > > +$id: http://devicetree.org/schemas/phy/rockchip,inno-usb3phy.yaml# > > +$schema: http://devicetree.org/meta-schemas/core.yaml# > > + > > +title: Rockchip USB 3.0 phy with Innosilicon IP block > > > + > > +maintainers: > > + - Heiko Stuebner <heiko@sntech.de> > > + > > +properties: > > + compatible: > > + enum: > > + - rockchip,rk3328-usb3phy > > Why is this binding entirely different than existing usb2 phy? Nothing > in common? This looks like made for driver and both - driver and binding > - duplicating a lot. Hmm, I hadn't considered merging it into the usb2 binding as it is a unique (and uniquely broken) device. I'd like Heiko's thoughts on this. > > > + > > + reg: > > + maxItems: 1 > > + > > + clocks: > > + minItems: 3 > > Drop > > > + maxItems: 3 > > + > > + clock-names: > > + items: > > + - const: refclk-usb3otg > > ref > > > + - const: usb3phy-otg > > otg > > > + - const: usb3phy-pipe > > pipe > > > + > > + interrupts: > > + minItems: 4 > > You code here randomly. reg has only maxItems, clocks have both and this > have only minItems. Does not make sense. If you get it wrong, I would > assume you repeat the same mistake but here it is like randomly chosen > pieces. And this is making me wonder whether this was not sent too fast. I admit, I dread writing bindings as even now I'm weak in YAML and it seems to pick and choose what rules it wants to follow. > > Anyway: only maxItems. > > > > + > > + interrupt-names: > > + items: > > + - const: bvalid > > + - const: id > > + - const: linestate > > + - const: rxdet > > + > > + resets: > > + minItems: 6 > > maxItems instead > > > + > > + reset-names: > > + items: > > + - const: usb3phy-u2-por > > + - const: usb3phy-u3-por > > + - const: usb3phy-pipe-mac > > + - const: usb3phy-utmi-mac > > + - const: usb3phy-utmi-apb > > + - const: usb3phy-pipe-apb > > + > > + "#address-cells": > > + const: 2 > > + > > + "#size-cells": > > + const: 2 > > + > > + ranges: true > > + > > +patternProperties: > > + > > Drop blank line > > > + utmi-port@[0-9a-f]+$: > > This wasn't tested. Missing quotes, missing starting anchor. make W=1 dt_binding_check didn't complain about it, using the dt-schema from pip3 from about a week ago. > > > + type: object > > Explain what are the children here - description. Fair, will do. > > > > + additionalProperties: false > > + > > + properties: > > + compatible: > > + enum: > > + - rockchip,rk3328-usb3phy-utmi > > + > > + reg: > > + maxItems: 1 > > + > > + "#phy-cells": > > + const: 0 > > Does not look correct. Your parent device is the phy, not child. Why do > you create children per each type of phy? Because that's how it's done elsewhere in Rockchip's phys [1]. How should it be done? > > > + > > + phy-supply: > > + description: > > + Phandle to a regulator that provides power to VBUS. > > + See ./phy-bindings.txt for details. > > + > > + required: > > + - compatible > > + - reg > > + - "#phy-cells" > > + > > + pipe-port@[0-9a-f]+$: > > + type: object > > + additionalProperties: false > > + > > + properties: > > + compatible: > > + enum: > > + - rockchip,rk3328-usb3phy-pipe > > + > > + reg: > > + maxItems: 1 > > + > > + "#phy-cells": > > + const: 0 > > + > > + phy-supply: > > + description: > > + Phandle to a regulator that provides power to VBUS. > > + See ./phy-bindings.txt for details. > > Drop "see ....". I was tempted to convert phy-bindings.txt over but as above I dread writing bindings. Will drop. > > > + > > + required: > > + - compatible > > + - reg > > + - "#phy-cells" > > + > > +required: > > + - compatible > > + - reg > > + - clocks > > + - clock-names > > + - interrupts > > + - interrupt-names > > + - resets > > + - reset-names > > + > > +additionalProperties: false > > + > > +examples: > > + - | > > + #include <dt-bindings/clock/rk3328-cru.h> > > + #include <dt-bindings/interrupt-controller/arm-gic.h> > > + #include <dt-bindings/interrupt-controller/irq.h> > > + soc { > > + #address-cells = <2>; > > + #size-cells = <2>; > > + > > + usb3phy: usb3-phy@ff460000 { > > + compatible = "rockchip,rk3328-usb3phy"; > > + reg = <0x0 0xff460000 0x0 0x10000>; > > + clocks = <&cru SCLK_REF_USB3OTG>, <&cru PCLK_USB3PHY_OTG>, <&cru PCLK_USB3PHY_PIPE>; > > That's way over the limit. Wrapping is at 80. Will correct. > > > + clock-names = "refclk-usb3otg", "usb3phy-otg", "usb3phy-pipe"; > > + interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>, > > + <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>; > > + interrupt-names = "bvalid", "id", "linestate", "rxdet"; > > > I appreciate all the feedback, I'll incorporate the changes you've recommended. Very Respectfully, Peter Geis > > Best regards, > Krzysztof [1] https://elixir.bootlin.com/linux/v6.13-rc3/source/arch/arm64/boot/dts/rockchip/rk3328.dtsi#L887 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 2/6] dt-bindings: phy: rockchip: add rk3328 usb3 phy 2025-01-16 13:32 ` Peter Geis @ 2025-01-16 13:59 ` Peter Geis 2025-01-18 9:06 ` Krzysztof Kozlowski 1 sibling, 0 replies; 24+ messages in thread From: Peter Geis @ 2025-01-16 13:59 UTC (permalink / raw) To: Krzysztof Kozlowski Cc: Heiko Stuebner, zyw, kever.yang, frank.wang, william.wu, wulf, linux-rockchip, Conor Dooley, Kishon Vijay Abraham I, Krzysztof Kozlowski, Rob Herring, Vinod Koul, devicetree, linux-arm-kernel, linux-kernel, linux-phy On Thu, Jan 16, 2025 at 8:32 AM Peter Geis <pgwipeout@gmail.com> wrote: > > On Thu, Jan 16, 2025 at 8:08 AM Krzysztof Kozlowski <krzk@kernel.org> wrote: > > > > On 15/01/2025 02:26, Peter Geis wrote: > > > Add documentation for the usb3 phy as implemented on the rk3328 SoC. > > > > > > Signed-off-by: Peter Geis <pgwipeout@gmail.com> > > > --- > > > > > > .../bindings/phy/rockchip,inno-usb3phy.yaml | 166 ++++++++++++++++++ > > > 1 file changed, 166 insertions(+) > > > create mode 100644 Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml > > > > > > diff --git a/Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml > > > new file mode 100644 > > > index 000000000000..cde489ca87ab > > > --- /dev/null > > > +++ b/Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml > > > @@ -0,0 +1,166 @@ > > > +# SPDX-License-Identifier: GPL-2.0-only > > > > Wrong license. > > > > Please run scripts/checkpatch.pl and fix reported warnings. After that, > > run also `scripts/checkpatch.pl --strict` and (probably) fix more > > warnings. Some warnings can be ignored, especially from --strict run, > > but the code here looks like it needs a fix. Feel free to get in touch > > if the warning is not clear. > > Checkpatch literally told me to change this. Ran against my original > dev binding: > ./scripts/checkpatch.pl --strict > Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml > CHECK: DT binding documents should be licensed (GPL-2.0-only OR BSD-2-Clause) > #1: FILE: Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml:1: > +# SPDX-License-Identifier: GPL-2.0 I understand now, thank you. Perhaps checkpatch could put that in quotes, instead saying ("GPL-2.0-only OR BSD-2-Clause") to make it clear it's one thing. > > > > > > +%YAML 1.2 > > > +--- > > > +$id: http://devicetree.org/schemas/phy/rockchip,inno-usb3phy.yaml# > > > +$schema: http://devicetree.org/meta-schemas/core.yaml# > > > + > > > +title: Rockchip USB 3.0 phy with Innosilicon IP block > > > > > + > > > +maintainers: > > > + - Heiko Stuebner <heiko@sntech.de> > > > + > > > +properties: > > > + compatible: > > > + enum: > > > + - rockchip,rk3328-usb3phy > > > > Why is this binding entirely different than existing usb2 phy? Nothing > > in common? This looks like made for driver and both - driver and binding > > - duplicating a lot. > > Hmm, I hadn't considered merging it into the usb2 binding as it is a > unique (and uniquely broken) device. I'd like Heiko's thoughts on > this. > > > > > > + > > > + reg: > > > + maxItems: 1 > > > + > > > + clocks: > > > + minItems: 3 > > > > Drop > > > > > + maxItems: 3 > > > + > > > + clock-names: > > > + items: > > > + - const: refclk-usb3otg > > > > ref > > > > > + - const: usb3phy-otg > > > > otg > > > > > + - const: usb3phy-pipe > > > > pipe > > > > > + > > > + interrupts: > > > + minItems: 4 > > > > You code here randomly. reg has only maxItems, clocks have both and this > > have only minItems. Does not make sense. If you get it wrong, I would > > assume you repeat the same mistake but here it is like randomly chosen > > pieces. And this is making me wonder whether this was not sent too fast. > > I admit, I dread writing bindings as even now I'm weak in YAML and it > seems to pick and choose what rules it wants to follow. > > > > > Anyway: only maxItems. > > > > > > > + > > > + interrupt-names: > > > + items: > > > + - const: bvalid > > > + - const: id > > > + - const: linestate > > > + - const: rxdet > > > + > > > + resets: > > > + minItems: 6 > > > > maxItems instead > > > > > + > > > + reset-names: > > > + items: > > > + - const: usb3phy-u2-por > > > + - const: usb3phy-u3-por > > > + - const: usb3phy-pipe-mac > > > + - const: usb3phy-utmi-mac > > > + - const: usb3phy-utmi-apb > > > + - const: usb3phy-pipe-apb > > > + > > > + "#address-cells": > > > + const: 2 > > > + > > > + "#size-cells": > > > + const: 2 > > > + > > > + ranges: true > > > + > > > +patternProperties: > > > + > > > > Drop blank line > > > > > + utmi-port@[0-9a-f]+$: > > > > This wasn't tested. Missing quotes, missing starting anchor. > > make W=1 dt_binding_check didn't complain about it, using the > dt-schema from pip3 from about a week ago. > > > > > > + type: object > > > > Explain what are the children here - description. > > Fair, will do. > > > > > > > > + additionalProperties: false > > > + > > > + properties: > > > + compatible: > > > + enum: > > > + - rockchip,rk3328-usb3phy-utmi > > > + > > > + reg: > > > + maxItems: 1 > > > + > > > + "#phy-cells": > > > + const: 0 > > > > Does not look correct. Your parent device is the phy, not child. Why do > > you create children per each type of phy? > > Because that's how it's done elsewhere in Rockchip's phys [1]. How > should it be done? > > > > > > + > > > + phy-supply: > > > + description: > > > + Phandle to a regulator that provides power to VBUS. > > > + See ./phy-bindings.txt for details. > > > + > > > + required: > > > + - compatible > > > + - reg > > > + - "#phy-cells" > > > + > > > + pipe-port@[0-9a-f]+$: > > > + type: object > > > + additionalProperties: false > > > + > > > + properties: > > > + compatible: > > > + enum: > > > + - rockchip,rk3328-usb3phy-pipe > > > + > > > + reg: > > > + maxItems: 1 > > > + > > > + "#phy-cells": > > > + const: 0 > > > + > > > + phy-supply: > > > + description: > > > + Phandle to a regulator that provides power to VBUS. > > > + See ./phy-bindings.txt for details. > > > > Drop "see ....". > > I was tempted to convert phy-bindings.txt over but as above I dread > writing bindings. Will drop. > > > > > > + > > > + required: > > > + - compatible > > > + - reg > > > + - "#phy-cells" > > > + > > > +required: > > > + - compatible > > > + - reg > > > + - clocks > > > + - clock-names > > > + - interrupts > > > + - interrupt-names > > > + - resets > > > + - reset-names > > > + > > > +additionalProperties: false > > > + > > > +examples: > > > + - | > > > + #include <dt-bindings/clock/rk3328-cru.h> > > > + #include <dt-bindings/interrupt-controller/arm-gic.h> > > > + #include <dt-bindings/interrupt-controller/irq.h> > > > + soc { > > > + #address-cells = <2>; > > > + #size-cells = <2>; > > > + > > > + usb3phy: usb3-phy@ff460000 { > > > + compatible = "rockchip,rk3328-usb3phy"; > > > + reg = <0x0 0xff460000 0x0 0x10000>; > > > + clocks = <&cru SCLK_REF_USB3OTG>, <&cru PCLK_USB3PHY_OTG>, <&cru PCLK_USB3PHY_PIPE>; > > > > That's way over the limit. Wrapping is at 80. > > Will correct. > > > > > > + clock-names = "refclk-usb3otg", "usb3phy-otg", "usb3phy-pipe"; > > > + interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>, > > > + <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>; > > > + interrupt-names = "bvalid", "id", "linestate", "rxdet"; > > > > > > > > I appreciate all the feedback, I'll incorporate the changes you've recommended. > > Very Respectfully, > Peter Geis > > > > > Best regards, > > Krzysztof > > [1] https://elixir.bootlin.com/linux/v6.13-rc3/source/arch/arm64/boot/dts/rockchip/rk3328.dtsi#L887 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 2/6] dt-bindings: phy: rockchip: add rk3328 usb3 phy 2025-01-16 13:32 ` Peter Geis 2025-01-16 13:59 ` Peter Geis @ 2025-01-18 9:06 ` Krzysztof Kozlowski 1 sibling, 0 replies; 24+ messages in thread From: Krzysztof Kozlowski @ 2025-01-18 9:06 UTC (permalink / raw) To: Peter Geis Cc: Heiko Stuebner, zyw, kever.yang, frank.wang, william.wu, wulf, linux-rockchip, Conor Dooley, Kishon Vijay Abraham I, Krzysztof Kozlowski, Rob Herring, Vinod Koul, devicetree, linux-arm-kernel, linux-kernel, linux-phy On 16/01/2025 14:32, Peter Geis wrote: >> >> >>> + additionalProperties: false >>> + >>> + properties: >>> + compatible: >>> + enum: >>> + - rockchip,rk3328-usb3phy-utmi >>> + >>> + reg: >>> + maxItems: 1 >>> + >>> + "#phy-cells": >>> + const: 0 >> >> Does not look correct. Your parent device is the phy, not child. Why do >> you create children per each type of phy? > > Because that's how it's done elsewhere in Rockchip's phys [1]. How > should it be done? The phys have separate supplies and IO addresses? Then it is reasonable to keep them separate and as children. But then more questions appear: why resets - which are also per utmi or port - are in top-level node? This should be represented in coherent way: either you define the properties/nodes per PHY or just everything in one/entire PHY controller. Not mixed. Same concerns about clocks in top-level. It also might be that everything is a bit mixed, so you have entire phy controller handling common resources and still separate phy for USB2 and USB3 as children, but that should be conscious choice coming from actual hardware. You have entire "description:" in binding to explain the hardware and any questions I asked now. Best regards, Krzysztof -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* [RFC PATCH v1 3/6] phy: rockchip: add driver for rk3328 usb3 phy 2025-01-15 1:26 [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 Peter Geis 2025-01-15 1:26 ` [RFC PATCH v1 2/6] dt-bindings: phy: rockchip: add rk3328 usb3 phy Peter Geis @ 2025-01-15 1:26 ` Peter Geis 2025-01-15 11:24 ` Piotr Oniszczuk ` (2 more replies) 2025-01-15 11:22 ` [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 Piotr Oniszczuk ` (2 subsequent siblings) 4 siblings, 3 replies; 24+ messages in thread From: Peter Geis @ 2025-01-15 1:26 UTC (permalink / raw) To: Heiko Stuebner Cc: zyw, kever.yang, frank.wang, william.wu, wulf, linux-rockchip, Peter Geis, Algea Cao, Arnd Bergmann, Cristian Ciocaltea, Kishon Vijay Abraham I, Philipp Zabel, Sebastian Reichel, Vinod Koul, Zhang Yubing, linux-arm-kernel, linux-kernel, linux-phy The rk3328 has a combined usb2 and usb3 phy block for the usb3 dwc interface. The implementation up until now has been bugged, with multiple issues ranging from disconnect detection failures to low performance. This driver fixes the majority of the original issues and allows better performance for the rk3328 usb3 port. This driver sources data from multiple sources, including the rk3328 trm, the rk3228h trm, emails from Rockchip, and both the 4.4 and 5.10 downstream drivers. The current implementation allows for basic bring up of the phy block and fixes the detection issues. Interrupts are enabled, but currently only used for debugging. Features missing currently are power management, OTG handling, board specific tuning, regulator control, Currently the only known bugs are a AX88179 usb3 gigabit adapter crashes when operating at usb3 speeds and transmitting large amounts of data and full disconnection detections may be slower than expected (~5-10 seconds). Signed-off-by: Peter Geis <pgwipeout@gmail.com> --- drivers/phy/rockchip/Kconfig | 10 + drivers/phy/rockchip/Makefile | 1 + drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 869 ++++++++++++++++++ 3 files changed, 880 insertions(+) create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index 2f7a05f21dc5..aac623e84f96 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -48,6 +48,16 @@ config PHY_ROCKCHIP_INNO_USB2 help Support for Rockchip USB2.0 PHY with Innosilicon IP block. +config PHY_ROCKCHIP_INNO_USB3 + tristate "Rockchip INNO USB3PHY Driver" + depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF + depends on COMMON_CLK + depends on USB_SUPPORT + select GENERIC_PHY + select USB_COMMON + help + Support for Rockchip USB3.0 PHY with Innosilicon IP block. + config PHY_ROCKCHIP_INNO_CSIDPHY tristate "Rockchip Innosilicon MIPI CSI PHY driver" depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile index 010a824e32ce..ec15dcf37fba 100644 --- a/drivers/phy/rockchip/Makefile +++ b/drivers/phy/rockchip/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY) += phy-rockchip-inno-csidphy.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o +obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB3) += phy-rockchip-inno-usb3.o obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX) += phy-rockchip-samsung-hdptx.o diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb3.c b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c new file mode 100644 index 000000000000..51b9f3b7fbfa --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c @@ -0,0 +1,869 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * phy-rockchip-inno-usb3.c - USB3 PHY based on Innosilicon IP as + * implemented on Rockchip rk3328. Tuning data magic bits are taken as is + * from the downstream driver. Downstream driver is located at: + * https://github.com/rockchip-linux/kernel/blob/240a5660d7c23841ccf7b7cc489078bf521b9802/drivers/phy/rockchip/phy-rockchip-inno-usb3.c + * + * Author: Peter Geis <pgwipeout@gmail.com> + * TODO: + * - Find the rest of the register names / definitions. + * - Implement pm functions. + * - Implement board specific tuning from dts. + * - Implement regulator control. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/mfd/syscon.h> +#include <linux/usb/of.h> + +#define REG_WRITE_MASK GENMASK(31, 16) +#define REG_WRITE_SHIFT 16 +#define DISABLE_BITS 0x0 + +/* USB3PHY GRF Registers */ +#define USB3PHY_WAKEUP_CON_REG 0x40 +#define USB3PHY_WAKEUP_STAT_REG 0x44 +#define USB3_LINESTATE_IRQ_EN BIT(0) +#define USB3_RXDET_IRQ_EN BIT(1) +#define USB3_BVALID_RISE_IRQ_EN BIT(2) +#define USB3_BVALID_FALL_IRQ_EN BIT(3) +#define USB3_BVALID_CLEAR_MASK GENMASK(3, 2) +#define USB3_ID_RISE_IRQ_EN BIT(4) +#define USB3_ID_FALL_IRQ_EN BIT(5) +#define USB3_ID_CLEAR_MASK GENMASK(5, 4) +#define USB3_RXDET_EN BIT(6) + +/* PIPE registers */ +/* 0x08 for SSC, default 0x0e */ +#define UNKNOWN_PIPE_REG_000 0x000 +#define UNKNOWN_SSC_000_MASK GENMASK(2, 1) +#define UNKNOWN_SSC_000_ENABLE (0x00 << 1) + +/* 0x83 for 24m, 0x01 for 25m, default 0x86 */ +#define PIPE_REG_020 0x020 +/* RX CDR multiplier high bits [7:6], as P, default 0x2, RX data rate = (2*refclk*P)/Q */ +#define PIPE_RX_CDR_MULT_HIGH_MASK GENMASK(7, 6) +/* TX PLL divider bits [4:0], as N, default 0x6, TX data rate = (2*refclk*M)/N */ +#define PIPE_TX_PLL_DIV_MASK GENMASK(4, 0) + +/* 0x71 for 24m, 0x64 for 25m, default 0x71 */ +#define PIPE_REG_028 0x028 +/* RX CDR multiplier low bits [7:0], as P, default 0x71, RX data rate = (2*refclk*P)/Q */ +#define PIPE_RX_CDR_MULT_LOW_MASK GENMASK(7, 0) + +/* 0x26 for 24m?, 0x21 for 25m, default 0x26 */ +#define PIPE_REG_030 0x030 +/* RX CDR divider bits [4:0], as Q, default 0x6, RX data rate = (2*refclk*P)/Q */ +#define PIPE_RX_CDR_DIV_MASK GENMASK(4, 0) + +/* 1'b1 Disable bandgap power, default 0x00 */ +#define PIPE_REG_044 0x044 +#define BANDGAP_POWER_DISABLE BIT(4) + +/* 0xe0 for rx tune?, default 0xe1 */ +#define PIPE_REG_060 0x060 +#define PIPE_TX_DETECT_BYPASS_DEBUG BIT(4) /* enable to always force detection */ +/* RX CTLE frequency bandwidth response tuning bits [1:0], default 0x1 */ +#define PIPE_RX_CTLE_FREQ_BW_MASK GENMASK(1, 0) +#define PIPE_RX_CTLE_FREQ_BW_TUNE 0x0 + +/* default 0x49 */ +#define PIPE_REG_064 0x064 +/* RX equalizer tail current control bits [6:4], default 0x4 */ +#define PIPE_RX_EQ_TAIL_CURR_MASK GENMASK(6, 4) + +/* 0x08 for rx tune?, default 0x07 */ +#define PIPE_REG_068 0x068 +/* RX equalizer low frequency gain control bits [7:4], default 0x0 */ +#define PIPE_RX_EQ_LOW_GAIN_MASK GENMASK(7, 4) +#define PIPE_RX_EQ_LOW_GAIN_TUNE (0x1 << 4) +/* RX CTLE gain tuning bits [3:0], higher = more gain default 0x7 */ +#define PIPE_RX_CTLE_GAIN_MASK GENMASK(3, 0) +#define PIPE_RX_CTLE_GAIN_TUNE 0x7 /* 0x5 lowest functional value, 0xf highest */ + +/* RX ODT manual resistance config, higher = less resistance, depends on REG_1C4 BIT(5) set */ +#define PIPE_REG_06C 0x06c +/* RX ODT manual resistance high bits [3:0], default 0x0 */ +#define PIPE_RX_ODT_RES_HIGH_MASK GENMASK(3, 0) +#define PIPE_RX_ODT_RES_HIGH_TUNE 0xf + +#define PIPE_REG_070 0x070 +/* RX ODT manual resistance mid bits [7:0], default 0x03 */ +#define PIPE_RX_ODT_RES_MID_MASK GENMASK(7, 0) +#define PIPE_RX_ODT_RES_MID_TUNE 0xff + +#define PIPE_REG_074 0x074 +/* RX ODT manual resistance low bits [7:0], default 0xff */ +#define PIPE_RX_ODT_RES_LOW_MASK GENMASK(7, 0) +#define PIPE_RX_ODT_RES_LOW_TUNE 0xff + +/* default 0x08 */ +#define PIPE_REG_080 0x080 +#define PIPE_TX_COMMON_MODE_DIS BIT(2) /* 1'b1 disable TX common type */ + +/* default 0x33 */ +#define PIPE_REG_088 0x088 +#define PIPE_TX_DRIVER_PREEMP_EN BIT(4) /* 1'b1 enable pre-emphasis */ + +/* default 0x18 */ +#define PIPE_REG_0C0 0x0c0 +#define PIPE_RX_CM_EN BIT(3) /* 1'b1 enable RX CM */ +#define PIPE_TX_OBS_EN BIT(4) /* 1'b1 enable TX OBS */ + +/* 0x12 for rx tune?, default 0x14 */ +#define PIPE_REG_0C8 0x0c8 +/* RX CDR charge pump current bits [3:1], default 0x2 */ +#define PIPE_RX_CDR_CHG_PUMP_MASK GENMASK(3, 1) +#define PIPE_RX_CDR_CHG_PUMP_TUNE (0x2 << 1) + +/* 0x02 for 24m, 0x06 for 25m, default 0x06 */ +#define UNKNOWN_PIPE_REG_108 0x108 +#define UNKNOWN_REFCLK_108_24M 0x02 + +/* 0x80 for 24m, default 0x00 */ +#define UNKNOWN_PIPE_REG_10C 0x10c +#define UNKNOWN_REFCLK_10C_24M BIT(7) + +/* 0x01 for 24m, 0x00 for 25m, default 0x02 */ +#define PIPE_REG_118 0x118 +/* TX PLL multiplier high bits [3:0], as M, default 0x2, TX data rate = (2*refclk*M)/N */ +#define PIPE_TX_PLL_MUL_HIGH_MASK GENMASK(3, 0) + +/* 0x38 for 24m, 0x64 for 25m, default 0x71 */ +#define PIPE_REG_11C 0x11c +/* TX PLL multiplier low bits [7:0], as M, default 0x71, TX data rate = (2*refclk*M)/N */ +#define PIPE_TX_PLL_MUL_LOW_MASK GENMASK(7, 0) + +/* 0x0c for SSC, default 0x1c */ +#define UNKNOWN_PIPE_REG_120 0x120 +#define UNKNOWN_SSC_120_MASK BIT(4) +#define UNKNOWN_SSC_120_ENABLE (0x0 << 4) + +/* default 0x40 */ +#define PIPE_REG_12C 0x12c +#define PIPE_TX_PLL_ALWAYS_ON BIT(0) /* disable for PLL control by pipe_pd */ + +/* 0x05 for rx tune, default 0x01 */ +#define PIPE_REG_148 0x148 +#define PIPE_RX_CHG_PUMP_DIV_2 BIT(2) /* RX CDR charge pump div/2, default 0 */ + +/* 0x70 for rx tune, default 0x72 */ +#define PIPE_REG_150 0x150 +#define PIPE_TX_BIAS_EN BIT(6) /* 1'b1 Enable TX Bias */ +/* RX CDR phase tracking speed bits [3:0], default 0x2 */ +#define PIPE_RX_CDR_SPEED_MASK GENMASK(3, 0) +#define PIPE_RX_CDR_SPEED_TUNE 0x00 + +/* default 0xd4 */ +#define PIPE_REG_160 +/* RX common mode voltage strength bits [5:4], default 0x1 */ +#define PIPE_RX_CDR_COM_VOLT_MASK GENMASK(5, 4) +#define PIPE_RX_CDR_COM_VOLT_TUNE (0x1 << 4) + +/* default 0x00 */ +#define PIPE_REG_180 0x180 +/* TX driver bias reference voltage bits [3:2], in mv */ +#define PIPE_TX_BIAS_REF_VOLT_MASK GENMASK(3, 2) +#define PIPE_TX_BIAS_REF_VOLT_200 (0x0 << 2) +#define PIPE_TX_BIAS_REF_VOLT_175 (0x1 << 2) +#define PIPE_TX_BIAS_REF_VOLT_225 (0x2 << 2) /* downstream 5.10 driver setting */ +#define PIPE_TX_BIAS_REF_VOLT_250 (0x3 << 2) + +/* default 0x01 */ +#define PIPE_REG_1A8 0x1a8 +#define PIPE_LDO_POWER_DIS BIT(4) /* 1'b1 Disable LDO Power */ + +/* default 0x07 */ +#define PIPE_REG_1AC 0x1ac +/* TX driver output common voltage bits [5:4], in mv */ +#define PIPE_TX_COMMON_VOLT_MASK GENMASK(5, 4) +#define PIPE_TX_COMMON_VOLT_800 (0x0 << 4) +#define PIPE_TX_COMMON_VOLT_750 (0x1 << 4) +#define PIPE_TX_COMMON_VOLT_950 (0x2 << 4) +#define PIPE_TX_COMMON_VOLT_1100 (0x3 << 4) + +/* default 0xfb */ +#define PIPE_REG_1B8 0x1b8 +/* TX driver swing strength bits [7:4], range 0x0 to 0xf */ +#define PIPE_TX_DRIVER_SWING_MASK GENMASK(7, 4) /* 0x2 lowest functional value */ +/* TX driver pre-emphasis strength bits [1:0], default 0x3, enabled by REG 088 */ +#define PIPE_TX_DRIVER_PREEMP_STR_MASK GENMASK(1, 0) + +/* set to 0xf0 for rx tune?, default 0xd0 */ +#define PIPE_REG_1C4 0x1c4 +#define PIPE_RX_ODT_AUTO_DIS BIT(5) /* Disable RX ODT auto compensation */ +#define PIPE_TX_ODT_AUTO_DIS BIT(3) /* Disable TX ODT auto compensation */ + +/* UTMI registers */ +/* 0x0f for utmi tune, default 0x09*/ +#define UTMI_REG_030 0x030 +/* {bits[2:0]=111}: always enable pre-emphasis */ +#define UTMI_ENABLE_PRE_EMPH_MASK GENMASK(2, 0) +#define UTMI_ENABLE_PRE_EMPH 0x07 + +/* 0x41 for utmi tune, default 0x49 */ +#define UTMI_REG_040 0x040 +/* TX HS pre-emphasis strength bits [5:3], default 0x1*/ +#define UTMI_TX_PRE_EMPH_STR_MASK GENMASK(5, 3) +#define UTMI_TX_PRE_EMPH_WEAKEST (0x0 << 3) + +/* set to 0xb5 for utmi tune, default 0xb5 */ +#define UTMI_REG_11C 0x11c +/* {bits[4:0]=10101}: odt 45ohm tuning */ +#define UTMI_ODT_45_OHM_MASK GENMASK(4, 0) +#define UTMI_ODT_45_OHM_TUNE 0x15 + +enum rockchip_usb3phy_type { + USB3PHY_TYPE_USB2, + USB3PHY_TYPE_USB3, + USB3PHY_TYPE_MAX, +}; + +/** + * struct rockchip_usb3phy_port - usb-phy port data. + * @phy: port usb phy struct. + * @regmap: port regmap. + * @type: port usb phy type. + */ +struct rockchip_usb3phy_port { + struct phy *phy; + struct regmap *regmap; + enum rockchip_usb3phy_type type; +}; + +struct rockchip_usb3phy { + struct device *dev; + struct regmap *regmap; + struct clk *clk_pipe; + struct clk *clk_otg; + struct clk *clk_ref; + struct reset_control *u3por_rst; + struct reset_control *u2por_rst; + struct reset_control *pipe_rst; + struct reset_control *utmi_rst; + struct reset_control *pipe_apb_rst; + struct reset_control *utmi_apb_rst; + struct rockchip_usb3phy_port ports[USB3PHY_TYPE_MAX]; + int bvalid_irq; + int id_irq; + int ls_irq; + int rxdet_irq; +}; + +struct usb3phy_config { + unsigned int reg; + unsigned int mask; + u8 def; +}; + +static const struct usb3phy_config rk3328_rx_config[] = { + { PIPE_REG_150, PIPE_RX_CDR_SPEED_MASK, PIPE_RX_CDR_SPEED_TUNE }, + { PIPE_REG_0C8, PIPE_RX_CDR_CHG_PUMP_MASK, PIPE_RX_CDR_CHG_PUMP_TUNE }, + { PIPE_REG_148, PIPE_RX_CHG_PUMP_DIV_2, PIPE_RX_CHG_PUMP_DIV_2 }, + { PIPE_REG_068, PIPE_RX_CTLE_GAIN_MASK, PIPE_RX_CTLE_GAIN_TUNE }, +// { PIPE_REG_1C4, PIPE_RX_ODT_AUTO_DIS, PIPE_RX_ODT_AUTO_DIS }, + { PIPE_REG_070, PIPE_RX_ODT_RES_MID_MASK, PIPE_RX_ODT_RES_MID_TUNE }, + { PIPE_REG_06C, PIPE_RX_ODT_RES_HIGH_MASK, PIPE_RX_ODT_RES_HIGH_TUNE }, + { PIPE_REG_060, PIPE_RX_CTLE_FREQ_BW_MASK, PIPE_RX_CTLE_FREQ_BW_TUNE }, + { UNKNOWN_PIPE_REG_10C, UNKNOWN_REFCLK_10C_24M, UNKNOWN_REFCLK_10C_24M }, + { PIPE_REG_060, PIPE_RX_CTLE_FREQ_BW_MASK, PIPE_RX_CTLE_FREQ_BW_TUNE }, + { PIPE_REG_068, PIPE_RX_EQ_LOW_GAIN_MASK, PIPE_RX_EQ_LOW_GAIN_TUNE }, +}; + +static const struct usb3phy_config rk3328_tx_config[] = { + { PIPE_REG_180, PIPE_TX_BIAS_REF_VOLT_MASK, PIPE_TX_BIAS_REF_VOLT_250 }, +}; + +static const struct usb3phy_config rk3328_ssc_config[] = { + { UNKNOWN_PIPE_REG_000, UNKNOWN_SSC_000_MASK, UNKNOWN_SSC_000_ENABLE }, + { UNKNOWN_PIPE_REG_120, UNKNOWN_SSC_120_MASK, UNKNOWN_SSC_120_ENABLE }, +}; + +static const struct usb3phy_config rk3328_utmi_config[] = { + { UTMI_REG_030, UTMI_ENABLE_PRE_EMPH_MASK, UTMI_ENABLE_PRE_EMPH }, + { UTMI_REG_040, UTMI_TX_PRE_EMPH_STR_MASK, UTMI_TX_PRE_EMPH_WEAKEST }, + { UTMI_REG_11C, UTMI_ODT_45_OHM_MASK, UTMI_ODT_45_OHM_TUNE }, +}; + +static const struct usb3phy_config rk3328_pipe_pwr_en_config[] = { + { PIPE_REG_1A8, PIPE_LDO_POWER_DIS, DISABLE_BITS }, + { PIPE_REG_044, BANDGAP_POWER_DISABLE, DISABLE_BITS }, + { PIPE_REG_150, PIPE_TX_BIAS_EN, PIPE_TX_BIAS_EN }, + { PIPE_REG_080, PIPE_TX_COMMON_MODE_DIS, DISABLE_BITS }, + { PIPE_REG_0C0, PIPE_TX_OBS_EN, PIPE_TX_OBS_EN }, + { PIPE_REG_0C0, PIPE_RX_CM_EN, PIPE_RX_CM_EN }, +}; + +static int rockchip_usb3phy_reset(struct rockchip_usb3phy *usb3phy, + bool reset, enum rockchip_usb3phy_type type) +{ + if (reset) { + if (type == USB3PHY_TYPE_USB2) { + clk_disable_unprepare(usb3phy->clk_otg); + reset_control_assert(usb3phy->utmi_rst); + reset_control_assert(usb3phy->u2por_rst); + } + if (type == USB3PHY_TYPE_USB3) { + clk_disable_unprepare(usb3phy->clk_pipe); + reset_control_assert(usb3phy->pipe_rst); + reset_control_assert(usb3phy->u3por_rst); + } + } else { + if (type == USB3PHY_TYPE_USB2) { + reset_control_deassert(usb3phy->u2por_rst); + fsleep(1000); + clk_prepare_enable(usb3phy->clk_otg); + fsleep(500); + reset_control_deassert(usb3phy->utmi_rst); + fsleep(100); + } + if (type == USB3PHY_TYPE_USB3) { + reset_control_deassert(usb3phy->u3por_rst); + fsleep(500); + clk_prepare_enable(usb3phy->clk_pipe); + fsleep(1000); + reset_control_deassert(usb3phy->pipe_rst); + fsleep(100); + } + } + return 0; +} + +static irqreturn_t rockchip_usb3phy_linestate_irq(int irq, void *data) +{ + struct rockchip_usb3phy *usb3phy = data; + int tmp; + + /* check if the interrupt is enabled */ + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, &tmp); + if (!(tmp & USB3_LINESTATE_IRQ_EN)) { + dev_warn(usb3phy->dev, "invalid linestate irq received\n"); + return IRQ_NONE; + } + + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, &tmp); + if (tmp & USB3_LINESTATE_IRQ_EN) + dev_dbg_ratelimited(usb3phy->dev, "linestate irq received\n"); + + /* clear the interrupt */ + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, USB3_LINESTATE_IRQ_EN); + + return IRQ_HANDLED; +} + +static irqreturn_t rockchip_usb3phy_bvalid_irq(int irq, void *data) +{ + struct rockchip_usb3phy *usb3phy = data; + int tmp; + + /* check if the interrupt is enabled */ + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, &tmp); + if (!((tmp & USB3_BVALID_FALL_IRQ_EN) | (tmp & USB3_BVALID_RISE_IRQ_EN))) { + dev_warn_ratelimited(usb3phy->dev, "invalid bvalid irq received\n"); + return IRQ_NONE; + } + + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, &tmp); + if (tmp & USB3_BVALID_FALL_IRQ_EN) + dev_dbg(usb3phy->dev, "bvalid falling irq received\n"); + if (tmp & USB3_BVALID_RISE_IRQ_EN) + dev_dbg(usb3phy->dev, "bvalid rising irq received\n"); + + /* clear the interrupt */ + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, USB3_BVALID_CLEAR_MASK); + + return IRQ_HANDLED; +} + +static irqreturn_t rockchip_usb3phy_id_irq(int irq, void *data) +{ + struct rockchip_usb3phy *usb3phy = data; + int tmp; + + /* check if the interrupt is enabled */ + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, &tmp); + if (!((tmp & USB3_ID_FALL_IRQ_EN) | (tmp & USB3_ID_RISE_IRQ_EN))) { + dev_warn(usb3phy->dev, "invalid id irq received\n"); + return IRQ_NONE; + } + + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, &tmp); + if (tmp & USB3_ID_FALL_IRQ_EN) + dev_dbg(usb3phy->dev, "id falling irq received\n"); + if (tmp & USB3_ID_RISE_IRQ_EN) + dev_dbg(usb3phy->dev, "id rising irq received\n"); + + /* clear the interrupt */ + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, USB3_ID_CLEAR_MASK); + + return IRQ_HANDLED; +} + +static irqreturn_t rockchip_usb3phy_rxdet_irq(int irq, void *data) +{ + struct rockchip_usb3phy *usb3phy = data; + int tmp; + + /* check if the interrupt is enabled */ + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, &tmp); + if (!(tmp & USB3_RXDET_IRQ_EN)) { + dev_warn(usb3phy->dev, "invalid rxdet irq received\n"); + return IRQ_NONE; + } + + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, &tmp); + if (tmp & USB3_RXDET_IRQ_EN) + dev_dbg_ratelimited(usb3phy->dev, "rxdet irq received\n"); + + /* clear the interrupt */ + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, USB3_RXDET_IRQ_EN); + + return IRQ_HANDLED; +} + +static int rockchip_usb3phy_bulk_update(struct rockchip_usb3phy *usb3phy, struct regmap *regmap, + const struct usb3phy_config *config, unsigned int size) +{ + unsigned int i, val, tmp; + int ret = 0; + + for (i = 0; i < size; i++) { + ret = regmap_read(regmap, config[i].reg, &val); + if (ret < 0) { + dev_err(usb3phy->dev, "failed to read addr: 0x%02x\n", config[i].reg); + return ret; + } + tmp = val & ~config[i].mask; + tmp |= config[i].def; + dev_dbg(usb3phy->dev, "write: 0x%03x old: 0x%02x new: 0x%02x\n", + config[i].reg, val, tmp); + ret = regmap_write(regmap, config[i].reg, tmp); + if (ret < 0) { + dev_err(usb3phy->dev, "failed to write addr: 0x%02x\n", config[i].reg); + return ret; + } + } + + return ret; +} + +static int rockchip_usb3phy_calc_rate(struct rockchip_usb3phy *usb3phy, struct regmap *regmap) +{ + long rate; + unsigned int mul, div, target = (5000000000 / 100000); + + rate = clk_get_rate(usb3phy->clk_ref) / 100000; + if (rate < 0) { + dev_err(usb3phy->dev, "failed to get refclk, %ld\n", rate); + return rate; + /* lowest possible supported clock is 4.8MHZ, highest rk3328 can do is 1.6GHZ */ + } else if ((rate < 48) | (rate > 16000)) { + goto error; + } + + for (div = 1; div < 32; div++) { + for (mul = 1; mul < 1024; mul++) { + if (((2 * rate * mul) / div) == target) + goto done; + if (((2 * rate * mul) / div) > target) + break; + } + } + +error: + dev_err(usb3phy->dev, "invalid refclock rate, %ld\n", rate * 100000); + return -EINVAL; + +done: + dev_dbg(usb3phy->dev, "refclk rate mul: %x div: %x rate: %ld\n", mul, div, (rate * 100000)); + + regmap_write(regmap, PIPE_REG_020, (mul >> 2) & PIPE_RX_CDR_MULT_HIGH_MASK); + regmap_write(regmap, PIPE_REG_020, div & PIPE_TX_PLL_DIV_MASK); + regmap_write(regmap, PIPE_REG_028, mul & PIPE_RX_CDR_MULT_LOW_MASK); + regmap_write(regmap, PIPE_REG_030, div & PIPE_RX_CDR_DIV_MASK); + regmap_write(regmap, PIPE_REG_118, (mul >> 8) & PIPE_TX_PLL_MUL_HIGH_MASK); + regmap_write(regmap, PIPE_REG_11C, mul & PIPE_TX_PLL_MUL_LOW_MASK); + + return 0; +} + +static int rockchip_usb3phy_init(struct phy *phy) +{ + struct rockchip_usb3phy_port *port = phy_get_drvdata(phy); + struct rockchip_usb3phy *usb3phy = dev_get_drvdata(phy->dev.parent); + int tmp, ret; + + dev_warn(usb3phy->dev, "usb3phy_init %s\n", dev_name(&phy->dev)); + clk_prepare_enable(usb3phy->clk_ref); + rockchip_usb3phy_reset(usb3phy, false, port->type); + + if (port->type == USB3PHY_TYPE_USB2) { + /* + * "For RK3328 SoC, pre-emphasis and pre-emphasis strength must be + * written as one fixed value. The ODT 45ohm value should be tuned + * for different boards to adjust HS eye height." + */ + dev_dbg(usb3phy->dev, "tuning UTMI\n"); + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, rk3328_utmi_config, + ARRAY_SIZE(rk3328_utmi_config)); + } + + if (port->type == USB3PHY_TYPE_USB3) { + /* Enable interrupts */ + tmp = (USB3_LINESTATE_IRQ_EN | USB3_ID_FALL_IRQ_EN | USB3_ID_RISE_IRQ_EN | + USB3_RXDET_IRQ_EN | USB3_BVALID_RISE_IRQ_EN | USB3_BVALID_FALL_IRQ_EN); + tmp |= (tmp << REG_WRITE_SHIFT); + + ret = regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, tmp); + if (ret < 0) { + /* interrupt write determines if we have write access */ + dev_err(usb3phy->dev, "failed to write interrupts\n"); + return ret; + } + + /* Configure for 24M ref clk */ + dev_dbg(usb3phy->dev, "setting pipe for 24M refclk\n"); + if (rockchip_usb3phy_calc_rate(usb3phy, usb3phy->regmap)) + return -EINVAL; + + /* Enable SSC */ + udelay(3); + dev_dbg(usb3phy->dev, "setting pipe for SSC\n"); + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, rk3328_ssc_config, + ARRAY_SIZE(rk3328_ssc_config)); + + /* "Tuning RX for compliance RJTL test" */ + dev_dbg(usb3phy->dev, "setting pipe for RX tuning\n"); + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, rk3328_rx_config, + ARRAY_SIZE(rk3328_rx_config)); + if (ret) + return ret; + + /* + * "Tuning TX to increase the bias current used in TX driver and RX EQ, + * it can also increase the voltage of LFPS." + */ + dev_dbg(usb3phy->dev, "setting pipe for TX tuning\n"); + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, + rk3328_tx_config, ARRAY_SIZE(rk3328_tx_config)); + + /* Power up the pipe */ + dev_dbg(usb3phy->dev, "setting pipe power on\n"); + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, rk3328_pipe_pwr_en_config, + ARRAY_SIZE(rk3328_pipe_pwr_en_config)); + } + + return 0; +} + +static int rockchip_usb3phy_parse_dt(struct rockchip_usb3phy *usb3phy, struct device *dev) +{ + int ret; + + usb3phy->clk_pipe = devm_clk_get(dev, "usb3phy-pipe"); + if (IS_ERR(usb3phy->clk_pipe)) { + dev_err(dev, "could not get usb3phy pipe clock\n"); + return PTR_ERR(usb3phy->clk_pipe); + } + + usb3phy->clk_otg = devm_clk_get(dev, "usb3phy-otg"); + if (IS_ERR(usb3phy->clk_otg)) { + dev_err(dev, "could not get usb3phy otg clock\n"); + return PTR_ERR(usb3phy->clk_otg); + } + + usb3phy->clk_ref = devm_clk_get(dev, "refclk-usb3otg"); + if (IS_ERR(usb3phy->clk_ref)) { + dev_err(dev, "could not get usb3phy ref clock\n"); + return PTR_ERR(usb3phy->clk_ref); + } + + usb3phy->u2por_rst = devm_reset_control_get(dev, "usb3phy-u2-por"); + if (IS_ERR(usb3phy->u2por_rst)) { + dev_err(dev, "no usb3phy-u2-por reset control found\n"); + return PTR_ERR(usb3phy->u2por_rst); + } + + usb3phy->u3por_rst = devm_reset_control_get(dev, "usb3phy-u3-por"); + if (IS_ERR(usb3phy->u3por_rst)) { + dev_err(dev, "no usb3phy-u3-por reset control found\n"); + return PTR_ERR(usb3phy->u3por_rst); + } + + usb3phy->pipe_rst = devm_reset_control_get(dev, "usb3phy-pipe-mac"); + if (IS_ERR(usb3phy->pipe_rst)) { + dev_err(dev, "no usb3phy_pipe_mac reset control found\n"); + return PTR_ERR(usb3phy->pipe_rst); + } + + usb3phy->utmi_rst = devm_reset_control_get(dev, "usb3phy-utmi-mac"); + if (IS_ERR(usb3phy->utmi_rst)) { + dev_err(dev, "no usb3phy-utmi-mac reset control found\n"); + return PTR_ERR(usb3phy->utmi_rst); + } + + usb3phy->pipe_apb_rst = devm_reset_control_get(dev, "usb3phy-pipe-apb"); + if (IS_ERR(usb3phy->pipe_apb_rst)) { + dev_err(dev, "no usb3phy-pipe-apb reset control found\n"); + return PTR_ERR(usb3phy->pipe_apb_rst); + } + + usb3phy->utmi_apb_rst = devm_reset_control_get(dev, "usb3phy-utmi-apb"); + if (IS_ERR(usb3phy->utmi_apb_rst)) { + dev_err(dev, "no usb3phy-utmi-apb reset control found\n"); + return PTR_ERR(usb3phy->utmi_apb_rst); + } + + usb3phy->ls_irq = of_irq_get_byname(dev->of_node, "linestate"); + if (usb3phy->ls_irq < 0) { + dev_err(dev, "no linestate irq provided\n"); + return usb3phy->ls_irq; + } + + ret = devm_request_threaded_irq(dev, usb3phy->ls_irq, NULL, rockchip_usb3phy_linestate_irq, + IRQF_ONESHOT, "rockchip_usb3phy_ls", usb3phy); + if (ret) { + dev_err(dev, "failed to request linestate irq handle\n"); + return ret; + } + + usb3phy->bvalid_irq = of_irq_get_byname(dev->of_node, "bvalid"); + if (usb3phy->bvalid_irq < 0) { + dev_err(dev, "no bvalid irq provided\n"); + return usb3phy->bvalid_irq; + } + + ret = devm_request_threaded_irq(dev, usb3phy->bvalid_irq, NULL, rockchip_usb3phy_bvalid_irq, + IRQF_ONESHOT, "rockchip_usb3phy_bvalid", usb3phy); + if (ret) { + dev_err(dev, "failed to request bvalid irq handle\n"); + return ret; + } + + usb3phy->id_irq = of_irq_get_byname(dev->of_node, "id"); + if (usb3phy->id_irq < 0) { + dev_err(dev, "no id irq provided\n"); + return usb3phy->id_irq; + } + + ret = devm_request_threaded_irq(dev, usb3phy->id_irq, NULL, rockchip_usb3phy_id_irq, + IRQF_ONESHOT, "rockchip_usb3phy-id", usb3phy); + if (ret) { + dev_err(dev, "failed to request id irq handle\n"); + return ret; + } + + usb3phy->rxdet_irq = of_irq_get_byname(dev->of_node, "rxdet"); + if (usb3phy->rxdet_irq < 0) { + dev_err(dev, "no rxdet irq provided\n"); + return usb3phy->rxdet_irq; + } + + ret = devm_request_threaded_irq(dev, usb3phy->rxdet_irq, NULL, rockchip_usb3phy_rxdet_irq, + IRQF_ONESHOT, "rockchip_usb3phy-rxdet", usb3phy); + if (ret) { + dev_err(dev, "failed to request rxdet irq handle\n"); + return ret; + } + + return 0; +} + +static int rockchip_usb3phy_exit(struct phy *phy) +{ + struct rockchip_usb3phy_port *port = phy_get_drvdata(phy); + struct rockchip_usb3phy *usb3phy = dev_get_drvdata(phy->dev.parent); + + dev_dbg(usb3phy->dev, "usb3phy_shutdown\n"); + rockchip_usb3phy_reset(usb3phy, true, port->type); + + return 0; +} + +static const struct phy_ops rockchip_usb3phy_ops = { + .init = rockchip_usb3phy_init, + .exit = rockchip_usb3phy_exit, + .owner = THIS_MODULE, +}; + +static const struct regmap_config rockchip_usb3phy_utmi_port_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x400, + .cache_type = REGCACHE_NONE, + .fast_io = true, + .name = "utmi-port", +}; + +static const struct regmap_config rockchip_usb3phy_pipe_port_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x400, + .cache_type = REGCACHE_NONE, + .fast_io = true, + .name = "pipe-port", +}; + +static const struct regmap_config rockchip_usb3phy_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x400, + .write_flag_mask = REG_WRITE_MASK, + .cache_type = REGCACHE_NONE, + .fast_io = true, +}; + +static int rockchip_usb3phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child_np; + struct phy_provider *provider; + struct rockchip_usb3phy *usb3phy; + const struct regmap_config regmap_config = rockchip_usb3phy_regmap_config; + void __iomem *base; + int i, ret; + + dev_dbg(dev, "Probe usb3phy main block\n"); + usb3phy = devm_kzalloc(dev, sizeof(*usb3phy), GFP_KERNEL); + if (!usb3phy) + return -ENOMEM; + + ret = rockchip_usb3phy_parse_dt(usb3phy, dev); + if (ret) { + dev_err(dev, "parse dt failed %i\n", ret); + return ret; + } + + base = devm_of_iomap(dev, np, 0, NULL); + if (IS_ERR(base)) { + dev_err(dev, "failed port ioremap\n"); + return PTR_ERR(base); + } + + usb3phy->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(usb3phy->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(usb3phy->regmap); + } + + usb3phy->dev = dev; + platform_set_drvdata(pdev, usb3phy); + + /* place block in reset */ + reset_control_assert(usb3phy->pipe_rst); + reset_control_assert(usb3phy->utmi_rst); + reset_control_assert(usb3phy->u3por_rst); + reset_control_assert(usb3phy->u2por_rst); + reset_control_assert(usb3phy->pipe_apb_rst); + reset_control_assert(usb3phy->utmi_apb_rst); + + fsleep(20); + + /* take apb interface out of reset */ + reset_control_deassert(usb3phy->utmi_apb_rst); + reset_control_deassert(usb3phy->pipe_apb_rst); + + /* enable usb3phy rx detection to fix disconnection issues */ + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, + (USB3_RXDET_EN | (USB3_RXDET_EN << REG_WRITE_SHIFT))); + + dev_dbg(dev, "Completed usb3phy core probe\n"); + + /* probe the actual ports */ + i = 0; + for_each_available_child_of_node(np, child_np) { + const struct regmap_config *regmap_port_config; + struct rockchip_usb3phy_port *port = &usb3phy->ports[i]; + struct phy *phy; + + if (of_node_name_eq(child_np, "utmi-port")) { + port->type = USB3PHY_TYPE_USB2; + regmap_port_config = &rockchip_usb3phy_utmi_port_regmap_config; + } else if (of_node_name_eq(child_np, "pipe-port")) { + port->type = USB3PHY_TYPE_USB3; + regmap_port_config = &rockchip_usb3phy_pipe_port_regmap_config; + } else { + dev_err(dev, "unknown child node port type %s\n", child_np->name); + goto err_port; + } + + base = devm_of_iomap(dev, child_np, 0, NULL); + if (IS_ERR(base)) { + dev_err(dev, "failed port ioremap\n"); + ret = PTR_ERR(base); + goto err_port; + } + + port->regmap = devm_regmap_init_mmio(dev, base, regmap_port_config); + if (IS_ERR(port->regmap)) { + dev_err(dev, "regmap init failed\n"); + ret = PTR_ERR(port->regmap); + goto err_port; + } + + phy = devm_phy_create(dev, child_np, &rockchip_usb3phy_ops); + if (IS_ERR(phy)) { + dev_err_probe(dev, PTR_ERR(phy), "failed to create phy\n"); + ret = PTR_ERR(phy); + goto err_port; + } + + port->phy = phy; + phy_set_drvdata(port->phy, port); + + /* to prevent out of boundary */ + if (++i >= USB3PHY_TYPE_MAX) { + of_node_put(child_np); + break; + } + + dev_info(dev, "Completed usb3phy %s port init\n", child_np->name); + } + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(provider); + +err_port: + of_node_put(child_np); + return ret; +} + +static const struct of_device_id rockchip_usb3phy_dt_ids[] = { + { .compatible = "rockchip,rk3328-usb3phy", }, +}; + +MODULE_DEVICE_TABLE(of, rockchip_usb3phy_dt_ids); + +static struct platform_driver rockchip_usb3phy_driver = { + .probe = rockchip_usb3phy_probe, + .driver = { + .name = "rockchip-usb3-phy", + .of_match_table = rockchip_usb3phy_dt_ids, + }, +}; + +module_platform_driver(rockchip_usb3phy_driver); + +MODULE_AUTHOR("Peter Geis <pgwipeout@gmail.com>"); +MODULE_DESCRIPTION("Rockchip Innosilicon USB3PHY driver"); +MODULE_LICENSE("GPL"); -- 2.39.5 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 3/6] phy: rockchip: add driver for rk3328 usb3 phy 2025-01-15 1:26 ` [RFC PATCH v1 3/6] phy: rockchip: add driver for " Peter Geis @ 2025-01-15 11:24 ` Piotr Oniszczuk 2025-01-16 14:09 ` Peter Geis 2025-01-16 12:59 ` Krzysztof Kozlowski 2025-01-16 15:26 ` Diederik de Haas 2 siblings, 1 reply; 24+ messages in thread From: Piotr Oniszczuk @ 2025-01-15 11:24 UTC (permalink / raw) To: Peter Geis Cc: Heiko Stuebner, Kishon Vijay Abraham I, Algea Cao, linux-kernel, Philipp Zabel, Arnd Bergmann, frank.wang, william.wu, kever.yang, linux-phy, Sebastian Reichel, linux-rockchip, Vinod Koul, Zhang Yubing, zyw, wulf, linux-arm-kernel > Wiadomość napisana przez Peter Geis <pgwipeout@gmail.com> w dniu 15 sty 2025, o godz. 02:26: > > The rk3328 has a combined usb2 and usb3 phy block for the usb3 dwc > interface. The implementation up until now has been bugged, with > multiple issues ranging from disconnect detection failures to low > performance. This driver fixes the majority of the original issues and > allows better performance for the rk3328 usb3 port. > > This driver sources data from multiple sources, including the rk3328 > trm, the rk3228h trm, emails from Rockchip, and both the 4.4 and 5.10 > downstream drivers. The current implementation allows for basic bring up > of the phy block and fixes the detection issues. Interrupts are enabled, > but currently only used for debugging. Features missing currently are > power management, OTG handling, board specific tuning, regulator control, > > Currently the only known bugs are a AX88179 usb3 gigabit adapter crashes > when operating at usb3 speeds and transmitting large amounts of data and > full disconnection detections may be slower than expected (~5-10 seconds). > > Signed-off-by: Peter Geis <pgwipeout@gmail.com> > --- > > drivers/phy/rockchip/Kconfig | 10 + > drivers/phy/rockchip/Makefile | 1 + > drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 869 ++++++++++++++++++ > 3 files changed, 880 insertions(+) > create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c > > diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig > index 2f7a05f21dc5..aac623e84f96 100644 > --- a/drivers/phy/rockchip/Kconfig > +++ b/drivers/phy/rockchip/Kconfig > @@ -48,6 +48,16 @@ config PHY_ROCKCHIP_INNO_USB2 > help > Support for Rockchip USB2.0 PHY with Innosilicon IP block. > > +config PHY_ROCKCHIP_INNO_USB3 > + tristate "Rockchip INNO USB3PHY Driver" > + depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF > + depends on COMMON_CLK > + depends on USB_SUPPORT > + select GENERIC_PHY > + select USB_COMMON > + help > + Support for Rockchip USB3.0 PHY with Innosilicon IP block. > + > config PHY_ROCKCHIP_INNO_CSIDPHY > tristate "Rockchip Innosilicon MIPI CSI PHY driver" > depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF > diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile > index 010a824e32ce..ec15dcf37fba 100644 > --- a/drivers/phy/rockchip/Makefile > +++ b/drivers/phy/rockchip/Makefile > @@ -6,6 +6,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY) += phy-rockchip-inno-csidphy.o > obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o > obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o > obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o > +obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB3) += phy-rockchip-inno-usb3.o > obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o > obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o > obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX) += phy-rockchip-samsung-hdptx.o > diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb3.c b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c > new file mode 100644 > index 000000000000..51b9f3b7fbfa > --- /dev/null > +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c > @@ -0,0 +1,869 @@ > +// SPDX-License-Identifier: GPL-2.0+ > + > +/* > + * phy-rockchip-inno-usb3.c - USB3 PHY based on Innosilicon IP as > + * implemented on Rockchip rk3328. Tuning data magic bits are taken as is > + * from the downstream driver. Downstream driver is located at: > + * https://github.com/rockchip-linux/kernel/blob/240a5660d7c23841ccf7b7cc489078bf521b9802/drivers/phy/rockchip/phy-rockchip-inno-usb3.c > + * > + * Author: Peter Geis <pgwipeout@gmail.com> > + * TODO: > + * - Find the rest of the register names / definitions. > + * - Implement pm functions. > + * - Implement board specific tuning from dts. > + * - Implement regulator control. > + */ > + > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/interrupt.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/of_address.h> > +#include <linux/of_device.h> > +#include <linux/of_irq.h> > +#include <linux/of_platform.h> > +#include <linux/phy/phy.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > +#include <linux/reset.h> > +#include <linux/mfd/syscon.h> > +#include <linux/usb/of.h> > + > +#define REG_WRITE_MASK GENMASK(31, 16) > +#define REG_WRITE_SHIFT 16 > +#define DISABLE_BITS 0x0 > + > +/* USB3PHY GRF Registers */ > +#define USB3PHY_WAKEUP_CON_REG 0x40 > +#define USB3PHY_WAKEUP_STAT_REG 0x44 > +#define USB3_LINESTATE_IRQ_EN BIT(0) > +#define USB3_RXDET_IRQ_EN BIT(1) > +#define USB3_BVALID_RISE_IRQ_EN BIT(2) > +#define USB3_BVALID_FALL_IRQ_EN BIT(3) > +#define USB3_BVALID_CLEAR_MASK GENMASK(3, 2) > +#define USB3_ID_RISE_IRQ_EN BIT(4) > +#define USB3_ID_FALL_IRQ_EN BIT(5) > +#define USB3_ID_CLEAR_MASK GENMASK(5, 4) > +#define USB3_RXDET_EN BIT(6) > + > +/* PIPE registers */ > +/* 0x08 for SSC, default 0x0e */ > +#define UNKNOWN_PIPE_REG_000 0x000 > +#define UNKNOWN_SSC_000_MASK GENMASK(2, 1) > +#define UNKNOWN_SSC_000_ENABLE (0x00 << 1) > + > +/* 0x83 for 24m, 0x01 for 25m, default 0x86 */ > +#define PIPE_REG_020 0x020 > +/* RX CDR multiplier high bits [7:6], as P, default 0x2, RX data rate = (2*refclk*P)/Q */ > +#define PIPE_RX_CDR_MULT_HIGH_MASK GENMASK(7, 6) > +/* TX PLL divider bits [4:0], as N, default 0x6, TX data rate = (2*refclk*M)/N */ > +#define PIPE_TX_PLL_DIV_MASK GENMASK(4, 0) > + > +/* 0x71 for 24m, 0x64 for 25m, default 0x71 */ > +#define PIPE_REG_028 0x028 > +/* RX CDR multiplier low bits [7:0], as P, default 0x71, RX data rate = (2*refclk*P)/Q */ > +#define PIPE_RX_CDR_MULT_LOW_MASK GENMASK(7, 0) > + > +/* 0x26 for 24m?, 0x21 for 25m, default 0x26 */ > +#define PIPE_REG_030 0x030 > +/* RX CDR divider bits [4:0], as Q, default 0x6, RX data rate = (2*refclk*P)/Q */ > +#define PIPE_RX_CDR_DIV_MASK GENMASK(4, 0) > + > +/* 1'b1 Disable bandgap power, default 0x00 */ > +#define PIPE_REG_044 0x044 > +#define BANDGAP_POWER_DISABLE BIT(4) > + > +/* 0xe0 for rx tune?, default 0xe1 */ > +#define PIPE_REG_060 0x060 > +#define PIPE_TX_DETECT_BYPASS_DEBUG BIT(4) /* enable to always force detection */ > +/* RX CTLE frequency bandwidth response tuning bits [1:0], default 0x1 */ > +#define PIPE_RX_CTLE_FREQ_BW_MASK GENMASK(1, 0) > +#define PIPE_RX_CTLE_FREQ_BW_TUNE 0x0 > + > +/* default 0x49 */ > +#define PIPE_REG_064 0x064 > +/* RX equalizer tail current control bits [6:4], default 0x4 */ > +#define PIPE_RX_EQ_TAIL_CURR_MASK GENMASK(6, 4) > + > +/* 0x08 for rx tune?, default 0x07 */ > +#define PIPE_REG_068 0x068 > +/* RX equalizer low frequency gain control bits [7:4], default 0x0 */ > +#define PIPE_RX_EQ_LOW_GAIN_MASK GENMASK(7, 4) > +#define PIPE_RX_EQ_LOW_GAIN_TUNE (0x1 << 4) > +/* RX CTLE gain tuning bits [3:0], higher = more gain default 0x7 */ > +#define PIPE_RX_CTLE_GAIN_MASK GENMASK(3, 0) > +#define PIPE_RX_CTLE_GAIN_TUNE 0x7 /* 0x5 lowest functional value, 0xf highest */ > + > +/* RX ODT manual resistance config, higher = less resistance, depends on REG_1C4 BIT(5) set */ > +#define PIPE_REG_06C 0x06c > +/* RX ODT manual resistance high bits [3:0], default 0x0 */ > +#define PIPE_RX_ODT_RES_HIGH_MASK GENMASK(3, 0) > +#define PIPE_RX_ODT_RES_HIGH_TUNE 0xf > + > +#define PIPE_REG_070 0x070 > +/* RX ODT manual resistance mid bits [7:0], default 0x03 */ > +#define PIPE_RX_ODT_RES_MID_MASK GENMASK(7, 0) > +#define PIPE_RX_ODT_RES_MID_TUNE 0xff > + > +#define PIPE_REG_074 0x074 > +/* RX ODT manual resistance low bits [7:0], default 0xff */ > +#define PIPE_RX_ODT_RES_LOW_MASK GENMASK(7, 0) > +#define PIPE_RX_ODT_RES_LOW_TUNE 0xff > + > +/* default 0x08 */ > +#define PIPE_REG_080 0x080 > +#define PIPE_TX_COMMON_MODE_DIS BIT(2) /* 1'b1 disable TX common type */ > + > +/* default 0x33 */ > +#define PIPE_REG_088 0x088 > +#define PIPE_TX_DRIVER_PREEMP_EN BIT(4) /* 1'b1 enable pre-emphasis */ > + > +/* default 0x18 */ > +#define PIPE_REG_0C0 0x0c0 > +#define PIPE_RX_CM_EN BIT(3) /* 1'b1 enable RX CM */ > +#define PIPE_TX_OBS_EN BIT(4) /* 1'b1 enable TX OBS */ > + > +/* 0x12 for rx tune?, default 0x14 */ > +#define PIPE_REG_0C8 0x0c8 > +/* RX CDR charge pump current bits [3:1], default 0x2 */ > +#define PIPE_RX_CDR_CHG_PUMP_MASK GENMASK(3, 1) > +#define PIPE_RX_CDR_CHG_PUMP_TUNE (0x2 << 1) > + > +/* 0x02 for 24m, 0x06 for 25m, default 0x06 */ > +#define UNKNOWN_PIPE_REG_108 0x108 > +#define UNKNOWN_REFCLK_108_24M 0x02 > + > +/* 0x80 for 24m, default 0x00 */ > +#define UNKNOWN_PIPE_REG_10C 0x10c > +#define UNKNOWN_REFCLK_10C_24M BIT(7) > + > +/* 0x01 for 24m, 0x00 for 25m, default 0x02 */ > +#define PIPE_REG_118 0x118 > +/* TX PLL multiplier high bits [3:0], as M, default 0x2, TX data rate = (2*refclk*M)/N */ > +#define PIPE_TX_PLL_MUL_HIGH_MASK GENMASK(3, 0) > + > +/* 0x38 for 24m, 0x64 for 25m, default 0x71 */ > +#define PIPE_REG_11C 0x11c > +/* TX PLL multiplier low bits [7:0], as M, default 0x71, TX data rate = (2*refclk*M)/N */ > +#define PIPE_TX_PLL_MUL_LOW_MASK GENMASK(7, 0) > + > +/* 0x0c for SSC, default 0x1c */ > +#define UNKNOWN_PIPE_REG_120 0x120 > +#define UNKNOWN_SSC_120_MASK BIT(4) > +#define UNKNOWN_SSC_120_ENABLE (0x0 << 4) > + > +/* default 0x40 */ > +#define PIPE_REG_12C 0x12c > +#define PIPE_TX_PLL_ALWAYS_ON BIT(0) /* disable for PLL control by pipe_pd */ > + > +/* 0x05 for rx tune, default 0x01 */ > +#define PIPE_REG_148 0x148 > +#define PIPE_RX_CHG_PUMP_DIV_2 BIT(2) /* RX CDR charge pump div/2, default 0 */ > + > +/* 0x70 for rx tune, default 0x72 */ > +#define PIPE_REG_150 0x150 > +#define PIPE_TX_BIAS_EN BIT(6) /* 1'b1 Enable TX Bias */ > +/* RX CDR phase tracking speed bits [3:0], default 0x2 */ > +#define PIPE_RX_CDR_SPEED_MASK GENMASK(3, 0) > +#define PIPE_RX_CDR_SPEED_TUNE 0x00 > + > +/* default 0xd4 */ > +#define PIPE_REG_160 > +/* RX common mode voltage strength bits [5:4], default 0x1 */ > +#define PIPE_RX_CDR_COM_VOLT_MASK GENMASK(5, 4) > +#define PIPE_RX_CDR_COM_VOLT_TUNE (0x1 << 4) > + > +/* default 0x00 */ > +#define PIPE_REG_180 0x180 > +/* TX driver bias reference voltage bits [3:2], in mv */ > +#define PIPE_TX_BIAS_REF_VOLT_MASK GENMASK(3, 2) > +#define PIPE_TX_BIAS_REF_VOLT_200 (0x0 << 2) > +#define PIPE_TX_BIAS_REF_VOLT_175 (0x1 << 2) > +#define PIPE_TX_BIAS_REF_VOLT_225 (0x2 << 2) /* downstream 5.10 driver setting */ > +#define PIPE_TX_BIAS_REF_VOLT_250 (0x3 << 2) > + > +/* default 0x01 */ > +#define PIPE_REG_1A8 0x1a8 > +#define PIPE_LDO_POWER_DIS BIT(4) /* 1'b1 Disable LDO Power */ > + > +/* default 0x07 */ > +#define PIPE_REG_1AC 0x1ac > +/* TX driver output common voltage bits [5:4], in mv */ > +#define PIPE_TX_COMMON_VOLT_MASK GENMASK(5, 4) > +#define PIPE_TX_COMMON_VOLT_800 (0x0 << 4) > +#define PIPE_TX_COMMON_VOLT_750 (0x1 << 4) > +#define PIPE_TX_COMMON_VOLT_950 (0x2 << 4) > +#define PIPE_TX_COMMON_VOLT_1100 (0x3 << 4) > + > +/* default 0xfb */ > +#define PIPE_REG_1B8 0x1b8 > +/* TX driver swing strength bits [7:4], range 0x0 to 0xf */ > +#define PIPE_TX_DRIVER_SWING_MASK GENMASK(7, 4) /* 0x2 lowest functional value */ > +/* TX driver pre-emphasis strength bits [1:0], default 0x3, enabled by REG 088 */ > +#define PIPE_TX_DRIVER_PREEMP_STR_MASK GENMASK(1, 0) > + > +/* set to 0xf0 for rx tune?, default 0xd0 */ > +#define PIPE_REG_1C4 0x1c4 > +#define PIPE_RX_ODT_AUTO_DIS BIT(5) /* Disable RX ODT auto compensation */ > +#define PIPE_TX_ODT_AUTO_DIS BIT(3) /* Disable TX ODT auto compensation */ > + > +/* UTMI registers */ > +/* 0x0f for utmi tune, default 0x09*/ > +#define UTMI_REG_030 0x030 > +/* {bits[2:0]=111}: always enable pre-emphasis */ > +#define UTMI_ENABLE_PRE_EMPH_MASK GENMASK(2, 0) > +#define UTMI_ENABLE_PRE_EMPH 0x07 > + > +/* 0x41 for utmi tune, default 0x49 */ > +#define UTMI_REG_040 0x040 > +/* TX HS pre-emphasis strength bits [5:3], default 0x1*/ > +#define UTMI_TX_PRE_EMPH_STR_MASK GENMASK(5, 3) > +#define UTMI_TX_PRE_EMPH_WEAKEST (0x0 << 3) > + > +/* set to 0xb5 for utmi tune, default 0xb5 */ > +#define UTMI_REG_11C 0x11c > +/* {bits[4:0]=10101}: odt 45ohm tuning */ > +#define UTMI_ODT_45_OHM_MASK GENMASK(4, 0) > +#define UTMI_ODT_45_OHM_TUNE 0x15 > + > +enum rockchip_usb3phy_type { > + USB3PHY_TYPE_USB2, > + USB3PHY_TYPE_USB3, > + USB3PHY_TYPE_MAX, > +}; > + > +/** > + * struct rockchip_usb3phy_port - usb-phy port data. > + * @phy: port usb phy struct. > + * @regmap: port regmap. > + * @type: port usb phy type. > + */ > +struct rockchip_usb3phy_port { > + struct phy *phy; > + struct regmap *regmap; > + enum rockchip_usb3phy_type type; > +}; > + > +struct rockchip_usb3phy { > + struct device *dev; > + struct regmap *regmap; > + struct clk *clk_pipe; > + struct clk *clk_otg; > + struct clk *clk_ref; > + struct reset_control *u3por_rst; > + struct reset_control *u2por_rst; > + struct reset_control *pipe_rst; > + struct reset_control *utmi_rst; > + struct reset_control *pipe_apb_rst; > + struct reset_control *utmi_apb_rst; > + struct rockchip_usb3phy_port ports[USB3PHY_TYPE_MAX]; > + int bvalid_irq; > + int id_irq; > + int ls_irq; > + int rxdet_irq; > +}; > + > +struct usb3phy_config { > + unsigned int reg; > + unsigned int mask; > + u8 def; > +}; > + > +static const struct usb3phy_config rk3328_rx_config[] = { > + { PIPE_REG_150, PIPE_RX_CDR_SPEED_MASK, PIPE_RX_CDR_SPEED_TUNE }, > + { PIPE_REG_0C8, PIPE_RX_CDR_CHG_PUMP_MASK, PIPE_RX_CDR_CHG_PUMP_TUNE }, > + { PIPE_REG_148, PIPE_RX_CHG_PUMP_DIV_2, PIPE_RX_CHG_PUMP_DIV_2 }, > + { PIPE_REG_068, PIPE_RX_CTLE_GAIN_MASK, PIPE_RX_CTLE_GAIN_TUNE }, > +// { PIPE_REG_1C4, PIPE_RX_ODT_AUTO_DIS, PIPE_RX_ODT_AUTO_DIS }, > + { PIPE_REG_070, PIPE_RX_ODT_RES_MID_MASK, PIPE_RX_ODT_RES_MID_TUNE }, > + { PIPE_REG_06C, PIPE_RX_ODT_RES_HIGH_MASK, PIPE_RX_ODT_RES_HIGH_TUNE }, > + { PIPE_REG_060, PIPE_RX_CTLE_FREQ_BW_MASK, PIPE_RX_CTLE_FREQ_BW_TUNE }, > + { UNKNOWN_PIPE_REG_10C, UNKNOWN_REFCLK_10C_24M, UNKNOWN_REFCLK_10C_24M }, > + { PIPE_REG_060, PIPE_RX_CTLE_FREQ_BW_MASK, PIPE_RX_CTLE_FREQ_BW_TUNE }, > + { PIPE_REG_068, PIPE_RX_EQ_LOW_GAIN_MASK, PIPE_RX_EQ_LOW_GAIN_TUNE }, > +}; > + > +static const struct usb3phy_config rk3328_tx_config[] = { > + { PIPE_REG_180, PIPE_TX_BIAS_REF_VOLT_MASK, PIPE_TX_BIAS_REF_VOLT_250 }, > +}; > + > +static const struct usb3phy_config rk3328_ssc_config[] = { > + { UNKNOWN_PIPE_REG_000, UNKNOWN_SSC_000_MASK, UNKNOWN_SSC_000_ENABLE }, > + { UNKNOWN_PIPE_REG_120, UNKNOWN_SSC_120_MASK, UNKNOWN_SSC_120_ENABLE }, > +}; > + > +static const struct usb3phy_config rk3328_utmi_config[] = { > + { UTMI_REG_030, UTMI_ENABLE_PRE_EMPH_MASK, UTMI_ENABLE_PRE_EMPH }, > + { UTMI_REG_040, UTMI_TX_PRE_EMPH_STR_MASK, UTMI_TX_PRE_EMPH_WEAKEST }, > + { UTMI_REG_11C, UTMI_ODT_45_OHM_MASK, UTMI_ODT_45_OHM_TUNE }, > +}; > + > +static const struct usb3phy_config rk3328_pipe_pwr_en_config[] = { > + { PIPE_REG_1A8, PIPE_LDO_POWER_DIS, DISABLE_BITS }, > + { PIPE_REG_044, BANDGAP_POWER_DISABLE, DISABLE_BITS }, > + { PIPE_REG_150, PIPE_TX_BIAS_EN, PIPE_TX_BIAS_EN }, > + { PIPE_REG_080, PIPE_TX_COMMON_MODE_DIS, DISABLE_BITS }, > + { PIPE_REG_0C0, PIPE_TX_OBS_EN, PIPE_TX_OBS_EN }, > + { PIPE_REG_0C0, PIPE_RX_CM_EN, PIPE_RX_CM_EN }, > +}; > + > +static int rockchip_usb3phy_reset(struct rockchip_usb3phy *usb3phy, > + bool reset, enum rockchip_usb3phy_type type) > +{ > + if (reset) { > + if (type == USB3PHY_TYPE_USB2) { > + clk_disable_unprepare(usb3phy->clk_otg); > + reset_control_assert(usb3phy->utmi_rst); > + reset_control_assert(usb3phy->u2por_rst); > + } > + if (type == USB3PHY_TYPE_USB3) { > + clk_disable_unprepare(usb3phy->clk_pipe); > + reset_control_assert(usb3phy->pipe_rst); > + reset_control_assert(usb3phy->u3por_rst); > + } > + } else { > + if (type == USB3PHY_TYPE_USB2) { > + reset_control_deassert(usb3phy->u2por_rst); > + fsleep(1000); > + clk_prepare_enable(usb3phy->clk_otg); > + fsleep(500); > + reset_control_deassert(usb3phy->utmi_rst); > + fsleep(100); > + } > + if (type == USB3PHY_TYPE_USB3) { > + reset_control_deassert(usb3phy->u3por_rst); > + fsleep(500); > + clk_prepare_enable(usb3phy->clk_pipe); > + fsleep(1000); > + reset_control_deassert(usb3phy->pipe_rst); > + fsleep(100); > + } > + } > + return 0; > +} > + > +static irqreturn_t rockchip_usb3phy_linestate_irq(int irq, void *data) > +{ > + struct rockchip_usb3phy *usb3phy = data; > + int tmp; > + > + /* check if the interrupt is enabled */ > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, &tmp); > + if (!(tmp & USB3_LINESTATE_IRQ_EN)) { > + dev_warn(usb3phy->dev, "invalid linestate irq received\n"); > + return IRQ_NONE; > + } > + > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, &tmp); > + if (tmp & USB3_LINESTATE_IRQ_EN) > + dev_dbg_ratelimited(usb3phy->dev, "linestate irq received\n"); > + > + /* clear the interrupt */ > + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, USB3_LINESTATE_IRQ_EN); > + > + return IRQ_HANDLED; > +} > + > +static irqreturn_t rockchip_usb3phy_bvalid_irq(int irq, void *data) > +{ > + struct rockchip_usb3phy *usb3phy = data; > + int tmp; > + > + /* check if the interrupt is enabled */ > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, &tmp); > + if (!((tmp & USB3_BVALID_FALL_IRQ_EN) | (tmp & USB3_BVALID_RISE_IRQ_EN))) { > + dev_warn_ratelimited(usb3phy->dev, "invalid bvalid irq received\n"); > + return IRQ_NONE; > + } > + > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, &tmp); > + if (tmp & USB3_BVALID_FALL_IRQ_EN) > + dev_dbg(usb3phy->dev, "bvalid falling irq received\n"); > + if (tmp & USB3_BVALID_RISE_IRQ_EN) > + dev_dbg(usb3phy->dev, "bvalid rising irq received\n"); > + > + /* clear the interrupt */ > + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, USB3_BVALID_CLEAR_MASK); > + > + return IRQ_HANDLED; > +} > + > +static irqreturn_t rockchip_usb3phy_id_irq(int irq, void *data) > +{ > + struct rockchip_usb3phy *usb3phy = data; > + int tmp; > + > + /* check if the interrupt is enabled */ > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, &tmp); > + if (!((tmp & USB3_ID_FALL_IRQ_EN) | (tmp & USB3_ID_RISE_IRQ_EN))) { > + dev_warn(usb3phy->dev, "invalid id irq received\n"); > + return IRQ_NONE; > + } > + > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, &tmp); > + if (tmp & USB3_ID_FALL_IRQ_EN) > + dev_dbg(usb3phy->dev, "id falling irq received\n"); > + if (tmp & USB3_ID_RISE_IRQ_EN) > + dev_dbg(usb3phy->dev, "id rising irq received\n"); > + > + /* clear the interrupt */ > + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, USB3_ID_CLEAR_MASK); > + > + return IRQ_HANDLED; > +} > + > +static irqreturn_t rockchip_usb3phy_rxdet_irq(int irq, void *data) > +{ > + struct rockchip_usb3phy *usb3phy = data; > + int tmp; > + > + /* check if the interrupt is enabled */ > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, &tmp); > + if (!(tmp & USB3_RXDET_IRQ_EN)) { > + dev_warn(usb3phy->dev, "invalid rxdet irq received\n"); > + return IRQ_NONE; > + } > + > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, &tmp); > + if (tmp & USB3_RXDET_IRQ_EN) > + dev_dbg_ratelimited(usb3phy->dev, "rxdet irq received\n"); > + > + /* clear the interrupt */ > + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, USB3_RXDET_IRQ_EN); > + > + return IRQ_HANDLED; > +} > + > +static int rockchip_usb3phy_bulk_update(struct rockchip_usb3phy *usb3phy, struct regmap *regmap, > + const struct usb3phy_config *config, unsigned int size) > +{ > + unsigned int i, val, tmp; > + int ret = 0; > + > + for (i = 0; i < size; i++) { > + ret = regmap_read(regmap, config[i].reg, &val); > + if (ret < 0) { > + dev_err(usb3phy->dev, "failed to read addr: 0x%02x\n", config[i].reg); > + return ret; > + } > + tmp = val & ~config[i].mask; > + tmp |= config[i].def; > + dev_dbg(usb3phy->dev, "write: 0x%03x old: 0x%02x new: 0x%02x\n", > + config[i].reg, val, tmp); > + ret = regmap_write(regmap, config[i].reg, tmp); > + if (ret < 0) { > + dev_err(usb3phy->dev, "failed to write addr: 0x%02x\n", config[i].reg); > + return ret; > + } > + } > + > + return ret; > +} > + > +static int rockchip_usb3phy_calc_rate(struct rockchip_usb3phy *usb3phy, struct regmap *regmap) > +{ > + long rate; > + unsigned int mul, div, target = (5000000000 / 100000); > + > + rate = clk_get_rate(usb3phy->clk_ref) / 100000; > + if (rate < 0) { > + dev_err(usb3phy->dev, "failed to get refclk, %ld\n", rate); > + return rate; > + /* lowest possible supported clock is 4.8MHZ, highest rk3328 can do is 1.6GHZ */ > + } else if ((rate < 48) | (rate > 16000)) { > + goto error; > + } > + > + for (div = 1; div < 32; div++) { > + for (mul = 1; mul < 1024; mul++) { > + if (((2 * rate * mul) / div) == target) > + goto done; > + if (((2 * rate * mul) / div) > target) > + break; > + } > + } > + > +error: > + dev_err(usb3phy->dev, "invalid refclock rate, %ld\n", rate * 100000); > + return -EINVAL; > + > +done: > + dev_dbg(usb3phy->dev, "refclk rate mul: %x div: %x rate: %ld\n", mul, div, (rate * 100000)); > + > + regmap_write(regmap, PIPE_REG_020, (mul >> 2) & PIPE_RX_CDR_MULT_HIGH_MASK); > + regmap_write(regmap, PIPE_REG_020, div & PIPE_TX_PLL_DIV_MASK); > + regmap_write(regmap, PIPE_REG_028, mul & PIPE_RX_CDR_MULT_LOW_MASK); > + regmap_write(regmap, PIPE_REG_030, div & PIPE_RX_CDR_DIV_MASK); > + regmap_write(regmap, PIPE_REG_118, (mul >> 8) & PIPE_TX_PLL_MUL_HIGH_MASK); > + regmap_write(regmap, PIPE_REG_11C, mul & PIPE_TX_PLL_MUL_LOW_MASK); > + > + return 0; > +} > + > +static int rockchip_usb3phy_init(struct phy *phy) > +{ > + struct rockchip_usb3phy_port *port = phy_get_drvdata(phy); > + struct rockchip_usb3phy *usb3phy = dev_get_drvdata(phy->dev.parent); > + int tmp, ret; > + > + dev_warn(usb3phy->dev, "usb3phy_init %s\n", dev_name(&phy->dev)); > + clk_prepare_enable(usb3phy->clk_ref); > + rockchip_usb3phy_reset(usb3phy, false, port->type); > + > + if (port->type == USB3PHY_TYPE_USB2) { > + /* > + * "For RK3328 SoC, pre-emphasis and pre-emphasis strength must be > + * written as one fixed value. The ODT 45ohm value should be tuned > + * for different boards to adjust HS eye height." > + */ > + dev_dbg(usb3phy->dev, "tuning UTMI\n"); > + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, rk3328_utmi_config, > + ARRAY_SIZE(rk3328_utmi_config)); > + } > + > + if (port->type == USB3PHY_TYPE_USB3) { > + /* Enable interrupts */ > + tmp = (USB3_LINESTATE_IRQ_EN | USB3_ID_FALL_IRQ_EN | USB3_ID_RISE_IRQ_EN | > + USB3_RXDET_IRQ_EN | USB3_BVALID_RISE_IRQ_EN | USB3_BVALID_FALL_IRQ_EN); > + tmp |= (tmp << REG_WRITE_SHIFT); > + > + ret = regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, tmp); > + if (ret < 0) { > + /* interrupt write determines if we have write access */ > + dev_err(usb3phy->dev, "failed to write interrupts\n"); > + return ret; > + } > + > + /* Configure for 24M ref clk */ > + dev_dbg(usb3phy->dev, "setting pipe for 24M refclk\n"); > + if (rockchip_usb3phy_calc_rate(usb3phy, usb3phy->regmap)) > + return -EINVAL; > + > + /* Enable SSC */ > + udelay(3); > + dev_dbg(usb3phy->dev, "setting pipe for SSC\n"); > + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, rk3328_ssc_config, > + ARRAY_SIZE(rk3328_ssc_config)); > + > + /* "Tuning RX for compliance RJTL test" */ > + dev_dbg(usb3phy->dev, "setting pipe for RX tuning\n"); > + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, rk3328_rx_config, > + ARRAY_SIZE(rk3328_rx_config)); > + if (ret) > + return ret; > + > + /* > + * "Tuning TX to increase the bias current used in TX driver and RX EQ, > + * it can also increase the voltage of LFPS." > + */ > + dev_dbg(usb3phy->dev, "setting pipe for TX tuning\n"); > + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, > + rk3328_tx_config, ARRAY_SIZE(rk3328_tx_config)); > + > + /* Power up the pipe */ > + dev_dbg(usb3phy->dev, "setting pipe power on\n"); > + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, rk3328_pipe_pwr_en_config, > + ARRAY_SIZE(rk3328_pipe_pwr_en_config)); > + } > + > + return 0; > +} > + > +static int rockchip_usb3phy_parse_dt(struct rockchip_usb3phy *usb3phy, struct device *dev) > +{ > + int ret; > + > + usb3phy->clk_pipe = devm_clk_get(dev, "usb3phy-pipe"); > + if (IS_ERR(usb3phy->clk_pipe)) { > + dev_err(dev, "could not get usb3phy pipe clock\n"); > + return PTR_ERR(usb3phy->clk_pipe); > + } > + > + usb3phy->clk_otg = devm_clk_get(dev, "usb3phy-otg"); > + if (IS_ERR(usb3phy->clk_otg)) { > + dev_err(dev, "could not get usb3phy otg clock\n"); > + return PTR_ERR(usb3phy->clk_otg); > + } > + > + usb3phy->clk_ref = devm_clk_get(dev, "refclk-usb3otg"); > + if (IS_ERR(usb3phy->clk_ref)) { > + dev_err(dev, "could not get usb3phy ref clock\n"); > + return PTR_ERR(usb3phy->clk_ref); > + } > + > + usb3phy->u2por_rst = devm_reset_control_get(dev, "usb3phy-u2-por"); > + if (IS_ERR(usb3phy->u2por_rst)) { > + dev_err(dev, "no usb3phy-u2-por reset control found\n"); > + return PTR_ERR(usb3phy->u2por_rst); > + } > + > + usb3phy->u3por_rst = devm_reset_control_get(dev, "usb3phy-u3-por"); > + if (IS_ERR(usb3phy->u3por_rst)) { > + dev_err(dev, "no usb3phy-u3-por reset control found\n"); > + return PTR_ERR(usb3phy->u3por_rst); > + } > + > + usb3phy->pipe_rst = devm_reset_control_get(dev, "usb3phy-pipe-mac"); > + if (IS_ERR(usb3phy->pipe_rst)) { > + dev_err(dev, "no usb3phy_pipe_mac reset control found\n"); > + return PTR_ERR(usb3phy->pipe_rst); > + } > + > + usb3phy->utmi_rst = devm_reset_control_get(dev, "usb3phy-utmi-mac"); > + if (IS_ERR(usb3phy->utmi_rst)) { > + dev_err(dev, "no usb3phy-utmi-mac reset control found\n"); > + return PTR_ERR(usb3phy->utmi_rst); > + } > + > + usb3phy->pipe_apb_rst = devm_reset_control_get(dev, "usb3phy-pipe-apb"); > + if (IS_ERR(usb3phy->pipe_apb_rst)) { > + dev_err(dev, "no usb3phy-pipe-apb reset control found\n"); > + return PTR_ERR(usb3phy->pipe_apb_rst); > + } > + > + usb3phy->utmi_apb_rst = devm_reset_control_get(dev, "usb3phy-utmi-apb"); > + if (IS_ERR(usb3phy->utmi_apb_rst)) { > + dev_err(dev, "no usb3phy-utmi-apb reset control found\n"); > + return PTR_ERR(usb3phy->utmi_apb_rst); > + } > + > + usb3phy->ls_irq = of_irq_get_byname(dev->of_node, "linestate"); > + if (usb3phy->ls_irq < 0) { > + dev_err(dev, "no linestate irq provided\n"); > + return usb3phy->ls_irq; > + } > + > + ret = devm_request_threaded_irq(dev, usb3phy->ls_irq, NULL, rockchip_usb3phy_linestate_irq, > + IRQF_ONESHOT, "rockchip_usb3phy_ls", usb3phy); > + if (ret) { > + dev_err(dev, "failed to request linestate irq handle\n"); > + return ret; > + } > + > + usb3phy->bvalid_irq = of_irq_get_byname(dev->of_node, "bvalid"); > + if (usb3phy->bvalid_irq < 0) { > + dev_err(dev, "no bvalid irq provided\n"); > + return usb3phy->bvalid_irq; > + } > + > + ret = devm_request_threaded_irq(dev, usb3phy->bvalid_irq, NULL, rockchip_usb3phy_bvalid_irq, > + IRQF_ONESHOT, "rockchip_usb3phy_bvalid", usb3phy); > + if (ret) { > + dev_err(dev, "failed to request bvalid irq handle\n"); > + return ret; > + } > + > + usb3phy->id_irq = of_irq_get_byname(dev->of_node, "id"); > + if (usb3phy->id_irq < 0) { > + dev_err(dev, "no id irq provided\n"); > + return usb3phy->id_irq; > + } > + > + ret = devm_request_threaded_irq(dev, usb3phy->id_irq, NULL, rockchip_usb3phy_id_irq, > + IRQF_ONESHOT, "rockchip_usb3phy-id", usb3phy); > + if (ret) { > + dev_err(dev, "failed to request id irq handle\n"); > + return ret; > + } > + > + usb3phy->rxdet_irq = of_irq_get_byname(dev->of_node, "rxdet"); > + if (usb3phy->rxdet_irq < 0) { > + dev_err(dev, "no rxdet irq provided\n"); > + return usb3phy->rxdet_irq; > + } > + > + ret = devm_request_threaded_irq(dev, usb3phy->rxdet_irq, NULL, rockchip_usb3phy_rxdet_irq, > + IRQF_ONESHOT, "rockchip_usb3phy-rxdet", usb3phy); > + if (ret) { > + dev_err(dev, "failed to request rxdet irq handle\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int rockchip_usb3phy_exit(struct phy *phy) > +{ > + struct rockchip_usb3phy_port *port = phy_get_drvdata(phy); > + struct rockchip_usb3phy *usb3phy = dev_get_drvdata(phy->dev.parent); > + > + dev_dbg(usb3phy->dev, "usb3phy_shutdown\n"); > + rockchip_usb3phy_reset(usb3phy, true, port->type); > + > + return 0; > +} > + > +static const struct phy_ops rockchip_usb3phy_ops = { > + .init = rockchip_usb3phy_init, > + .exit = rockchip_usb3phy_exit, > + .owner = THIS_MODULE, > +}; > + > +static const struct regmap_config rockchip_usb3phy_utmi_port_regmap_config = { > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > + .max_register = 0x400, > + .cache_type = REGCACHE_NONE, > + .fast_io = true, > + .name = "utmi-port", > +}; > + > +static const struct regmap_config rockchip_usb3phy_pipe_port_regmap_config = { > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > + .max_register = 0x400, > + .cache_type = REGCACHE_NONE, > + .fast_io = true, > + .name = "pipe-port", > +}; > + > +static const struct regmap_config rockchip_usb3phy_regmap_config = { > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > + .max_register = 0x400, > + .write_flag_mask = REG_WRITE_MASK, > + .cache_type = REGCACHE_NONE, > + .fast_io = true, > +}; > + > +static int rockchip_usb3phy_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct device_node *np = dev->of_node; > + struct device_node *child_np; > + struct phy_provider *provider; > + struct rockchip_usb3phy *usb3phy; > + const struct regmap_config regmap_config = rockchip_usb3phy_regmap_config; > + void __iomem *base; > + int i, ret; > + > + dev_dbg(dev, "Probe usb3phy main block\n"); > + usb3phy = devm_kzalloc(dev, sizeof(*usb3phy), GFP_KERNEL); > + if (!usb3phy) > + return -ENOMEM; > + > + ret = rockchip_usb3phy_parse_dt(usb3phy, dev); > + if (ret) { > + dev_err(dev, "parse dt failed %i\n", ret); > + return ret; > + } > + > + base = devm_of_iomap(dev, np, 0, NULL); > + if (IS_ERR(base)) { > + dev_err(dev, "failed port ioremap\n"); > + return PTR_ERR(base); > + } > + > + usb3phy->regmap = devm_regmap_init_mmio(dev, base, ®map_config); > + if (IS_ERR(usb3phy->regmap)) { > + dev_err(dev, "regmap init failed\n"); > + return PTR_ERR(usb3phy->regmap); > + } > + > + usb3phy->dev = dev; > + platform_set_drvdata(pdev, usb3phy); > + > + /* place block in reset */ > + reset_control_assert(usb3phy->pipe_rst); > + reset_control_assert(usb3phy->utmi_rst); > + reset_control_assert(usb3phy->u3por_rst); > + reset_control_assert(usb3phy->u2por_rst); > + reset_control_assert(usb3phy->pipe_apb_rst); > + reset_control_assert(usb3phy->utmi_apb_rst); > + > + fsleep(20); > + > + /* take apb interface out of reset */ > + reset_control_deassert(usb3phy->utmi_apb_rst); > + reset_control_deassert(usb3phy->pipe_apb_rst); > + > + /* enable usb3phy rx detection to fix disconnection issues */ > + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, > + (USB3_RXDET_EN | (USB3_RXDET_EN << REG_WRITE_SHIFT))); > + > + dev_dbg(dev, "Completed usb3phy core probe\n"); > + > + /* probe the actual ports */ > + i = 0; > + for_each_available_child_of_node(np, child_np) { > + const struct regmap_config *regmap_port_config; > + struct rockchip_usb3phy_port *port = &usb3phy->ports[i]; > + struct phy *phy; > + > + if (of_node_name_eq(child_np, "utmi-port")) { > + port->type = USB3PHY_TYPE_USB2; > + regmap_port_config = &rockchip_usb3phy_utmi_port_regmap_config; > + } else if (of_node_name_eq(child_np, "pipe-port")) { > + port->type = USB3PHY_TYPE_USB3; > + regmap_port_config = &rockchip_usb3phy_pipe_port_regmap_config; > + } else { > + dev_err(dev, "unknown child node port type %s\n", child_np->name); > + goto err_port; > + } > + > + base = devm_of_iomap(dev, child_np, 0, NULL); > + if (IS_ERR(base)) { > + dev_err(dev, "failed port ioremap\n"); > + ret = PTR_ERR(base); > + goto err_port; > + } > + > + port->regmap = devm_regmap_init_mmio(dev, base, regmap_port_config); > + if (IS_ERR(port->regmap)) { > + dev_err(dev, "regmap init failed\n"); > + ret = PTR_ERR(port->regmap); > + goto err_port; > + } > + > + phy = devm_phy_create(dev, child_np, &rockchip_usb3phy_ops); > + if (IS_ERR(phy)) { > + dev_err_probe(dev, PTR_ERR(phy), "failed to create phy\n"); > + ret = PTR_ERR(phy); > + goto err_port; > + } > + > + port->phy = phy; > + phy_set_drvdata(port->phy, port); > + > + /* to prevent out of boundary */ > + if (++i >= USB3PHY_TYPE_MAX) { > + of_node_put(child_np); > + break; > + } > + > + dev_info(dev, "Completed usb3phy %s port init\n", child_np->name); > + } > + > + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > + return PTR_ERR_OR_ZERO(provider); > + > +err_port: > + of_node_put(child_np); > + return ret; > +} > + > +static const struct of_device_id rockchip_usb3phy_dt_ids[] = { > + { .compatible = "rockchip,rk3328-usb3phy", }, > +}; isn’t { /* sentinel */ } missing here? > + > +MODULE_DEVICE_TABLE(of, rockchip_usb3phy_dt_ids); > + > +static struct platform_driver rockchip_usb3phy_driver = { > + .probe = rockchip_usb3phy_probe, > + .driver = { > + .name = "rockchip-usb3-phy", > + .of_match_table = rockchip_usb3phy_dt_ids, > + }, > +}; > + > +module_platform_driver(rockchip_usb3phy_driver); > + > +MODULE_AUTHOR("Peter Geis <pgwipeout@gmail.com>"); > +MODULE_DESCRIPTION("Rockchip Innosilicon USB3PHY driver"); > +MODULE_LICENSE("GPL"); > -- > 2.39.5 > > > _______________________________________________ > Linux-rockchip mailing list > Linux-rockchip@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-rockchip -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 3/6] phy: rockchip: add driver for rk3328 usb3 phy 2025-01-15 11:24 ` Piotr Oniszczuk @ 2025-01-16 14:09 ` Peter Geis 0 siblings, 0 replies; 24+ messages in thread From: Peter Geis @ 2025-01-16 14:09 UTC (permalink / raw) To: Piotr Oniszczuk Cc: Heiko Stuebner, Kishon Vijay Abraham I, Algea Cao, linux-kernel, Philipp Zabel, Arnd Bergmann, frank.wang, william.wu, kever.yang, linux-phy, Sebastian Reichel, linux-rockchip, Vinod Koul, Zhang Yubing, zyw, wulf, linux-arm-kernel On Wed, Jan 15, 2025 at 6:24 AM Piotr Oniszczuk <piotr.oniszczuk@gmail.com> wrote: > > > > > Wiadomość napisana przez Peter Geis <pgwipeout@gmail.com> w dniu 15 sty 2025, o godz. 02:26: > > > > The rk3328 has a combined usb2 and usb3 phy block for the usb3 dwc > > interface. The implementation up until now has been bugged, with > > multiple issues ranging from disconnect detection failures to low > > performance. This driver fixes the majority of the original issues and > > allows better performance for the rk3328 usb3 port. > > > > This driver sources data from multiple sources, including the rk3328 > > trm, the rk3228h trm, emails from Rockchip, and both the 4.4 and 5.10 > > downstream drivers. The current implementation allows for basic bring up > > of the phy block and fixes the detection issues. Interrupts are enabled, > > but currently only used for debugging. Features missing currently are > > power management, OTG handling, board specific tuning, regulator control, > > > > Currently the only known bugs are a AX88179 usb3 gigabit adapter crashes > > when operating at usb3 speeds and transmitting large amounts of data and > > full disconnection detections may be slower than expected (~5-10 seconds). > > > > Signed-off-by: Peter Geis <pgwipeout@gmail.com> > > --- > > > > drivers/phy/rockchip/Kconfig | 10 + > > drivers/phy/rockchip/Makefile | 1 + > > drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 869 ++++++++++++++++++ > > 3 files changed, 880 insertions(+) > > create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c > > > > diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig > > index 2f7a05f21dc5..aac623e84f96 100644 > > --- a/drivers/phy/rockchip/Kconfig > > +++ b/drivers/phy/rockchip/Kconfig > > @@ -48,6 +48,16 @@ config PHY_ROCKCHIP_INNO_USB2 > > help > > Support for Rockchip USB2.0 PHY with Innosilicon IP block. > > > > +config PHY_ROCKCHIP_INNO_USB3 > > + tristate "Rockchip INNO USB3PHY Driver" > > + depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF > > + depends on COMMON_CLK > > + depends on USB_SUPPORT > > + select GENERIC_PHY > > + select USB_COMMON > > + help > > + Support for Rockchip USB3.0 PHY with Innosilicon IP block. > > + > > config PHY_ROCKCHIP_INNO_CSIDPHY > > tristate "Rockchip Innosilicon MIPI CSI PHY driver" > > depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF > > diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile > > index 010a824e32ce..ec15dcf37fba 100644 > > --- a/drivers/phy/rockchip/Makefile > > +++ b/drivers/phy/rockchip/Makefile > > @@ -6,6 +6,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY) += phy-rockchip-inno-csidphy.o > > obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o > > obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o > > obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o > > +obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB3) += phy-rockchip-inno-usb3.o > > obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o > > obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o > > obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX) += phy-rockchip-samsung-hdptx.o > > diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb3.c b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c > > new file mode 100644 > > index 000000000000..51b9f3b7fbfa > > --- /dev/null > > +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c > > @@ -0,0 +1,869 @@ > > +// SPDX-License-Identifier: GPL-2.0+ > > + > > +/* > > + * phy-rockchip-inno-usb3.c - USB3 PHY based on Innosilicon IP as > > + * implemented on Rockchip rk3328. Tuning data magic bits are taken as is > > + * from the downstream driver. Downstream driver is located at: > > + * https://github.com/rockchip-linux/kernel/blob/240a5660d7c23841ccf7b7cc489078bf521b9802/drivers/phy/rockchip/phy-rockchip-inno-usb3.c > > + * > > + * Author: Peter Geis <pgwipeout@gmail.com> > > + * TODO: > > + * - Find the rest of the register names / definitions. > > + * - Implement pm functions. > > + * - Implement board specific tuning from dts. > > + * - Implement regulator control. > > + */ > > + > > +#include <linux/clk.h> > > +#include <linux/delay.h> > > +#include <linux/interrupt.h> > > +#include <linux/io.h> > > +#include <linux/kernel.h> > > +#include <linux/module.h> > > +#include <linux/of.h> > > +#include <linux/of_address.h> > > +#include <linux/of_device.h> > > +#include <linux/of_irq.h> > > +#include <linux/of_platform.h> > > +#include <linux/phy/phy.h> > > +#include <linux/platform_device.h> > > +#include <linux/regmap.h> > > +#include <linux/reset.h> > > +#include <linux/mfd/syscon.h> > > +#include <linux/usb/of.h> > > + > > +#define REG_WRITE_MASK GENMASK(31, 16) > > +#define REG_WRITE_SHIFT 16 > > +#define DISABLE_BITS 0x0 > > + > > +/* USB3PHY GRF Registers */ > > +#define USB3PHY_WAKEUP_CON_REG 0x40 > > +#define USB3PHY_WAKEUP_STAT_REG 0x44 > > +#define USB3_LINESTATE_IRQ_EN BIT(0) > > +#define USB3_RXDET_IRQ_EN BIT(1) > > +#define USB3_BVALID_RISE_IRQ_EN BIT(2) > > +#define USB3_BVALID_FALL_IRQ_EN BIT(3) > > +#define USB3_BVALID_CLEAR_MASK GENMASK(3, 2) > > +#define USB3_ID_RISE_IRQ_EN BIT(4) > > +#define USB3_ID_FALL_IRQ_EN BIT(5) > > +#define USB3_ID_CLEAR_MASK GENMASK(5, 4) > > +#define USB3_RXDET_EN BIT(6) > > + > > +/* PIPE registers */ > > +/* 0x08 for SSC, default 0x0e */ > > +#define UNKNOWN_PIPE_REG_000 0x000 > > +#define UNKNOWN_SSC_000_MASK GENMASK(2, 1) > > +#define UNKNOWN_SSC_000_ENABLE (0x00 << 1) > > + > > +/* 0x83 for 24m, 0x01 for 25m, default 0x86 */ > > +#define PIPE_REG_020 0x020 > > +/* RX CDR multiplier high bits [7:6], as P, default 0x2, RX data rate = (2*refclk*P)/Q */ > > +#define PIPE_RX_CDR_MULT_HIGH_MASK GENMASK(7, 6) > > +/* TX PLL divider bits [4:0], as N, default 0x6, TX data rate = (2*refclk*M)/N */ > > +#define PIPE_TX_PLL_DIV_MASK GENMASK(4, 0) > > + > > +/* 0x71 for 24m, 0x64 for 25m, default 0x71 */ > > +#define PIPE_REG_028 0x028 > > +/* RX CDR multiplier low bits [7:0], as P, default 0x71, RX data rate = (2*refclk*P)/Q */ > > +#define PIPE_RX_CDR_MULT_LOW_MASK GENMASK(7, 0) > > + > > +/* 0x26 for 24m?, 0x21 for 25m, default 0x26 */ > > +#define PIPE_REG_030 0x030 > > +/* RX CDR divider bits [4:0], as Q, default 0x6, RX data rate = (2*refclk*P)/Q */ > > +#define PIPE_RX_CDR_DIV_MASK GENMASK(4, 0) > > + > > +/* 1'b1 Disable bandgap power, default 0x00 */ > > +#define PIPE_REG_044 0x044 > > +#define BANDGAP_POWER_DISABLE BIT(4) > > + > > +/* 0xe0 for rx tune?, default 0xe1 */ > > +#define PIPE_REG_060 0x060 > > +#define PIPE_TX_DETECT_BYPASS_DEBUG BIT(4) /* enable to always force detection */ > > +/* RX CTLE frequency bandwidth response tuning bits [1:0], default 0x1 */ > > +#define PIPE_RX_CTLE_FREQ_BW_MASK GENMASK(1, 0) > > +#define PIPE_RX_CTLE_FREQ_BW_TUNE 0x0 > > + > > +/* default 0x49 */ > > +#define PIPE_REG_064 0x064 > > +/* RX equalizer tail current control bits [6:4], default 0x4 */ > > +#define PIPE_RX_EQ_TAIL_CURR_MASK GENMASK(6, 4) > > + > > +/* 0x08 for rx tune?, default 0x07 */ > > +#define PIPE_REG_068 0x068 > > +/* RX equalizer low frequency gain control bits [7:4], default 0x0 */ > > +#define PIPE_RX_EQ_LOW_GAIN_MASK GENMASK(7, 4) > > +#define PIPE_RX_EQ_LOW_GAIN_TUNE (0x1 << 4) > > +/* RX CTLE gain tuning bits [3:0], higher = more gain default 0x7 */ > > +#define PIPE_RX_CTLE_GAIN_MASK GENMASK(3, 0) > > +#define PIPE_RX_CTLE_GAIN_TUNE 0x7 /* 0x5 lowest functional value, 0xf highest */ > > + > > +/* RX ODT manual resistance config, higher = less resistance, depends on REG_1C4 BIT(5) set */ > > +#define PIPE_REG_06C 0x06c > > +/* RX ODT manual resistance high bits [3:0], default 0x0 */ > > +#define PIPE_RX_ODT_RES_HIGH_MASK GENMASK(3, 0) > > +#define PIPE_RX_ODT_RES_HIGH_TUNE 0xf > > + > > +#define PIPE_REG_070 0x070 > > +/* RX ODT manual resistance mid bits [7:0], default 0x03 */ > > +#define PIPE_RX_ODT_RES_MID_MASK GENMASK(7, 0) > > +#define PIPE_RX_ODT_RES_MID_TUNE 0xff > > + > > +#define PIPE_REG_074 0x074 > > +/* RX ODT manual resistance low bits [7:0], default 0xff */ > > +#define PIPE_RX_ODT_RES_LOW_MASK GENMASK(7, 0) > > +#define PIPE_RX_ODT_RES_LOW_TUNE 0xff > > + > > +/* default 0x08 */ > > +#define PIPE_REG_080 0x080 > > +#define PIPE_TX_COMMON_MODE_DIS BIT(2) /* 1'b1 disable TX common type */ > > + > > +/* default 0x33 */ > > +#define PIPE_REG_088 0x088 > > +#define PIPE_TX_DRIVER_PREEMP_EN BIT(4) /* 1'b1 enable pre-emphasis */ > > + > > +/* default 0x18 */ > > +#define PIPE_REG_0C0 0x0c0 > > +#define PIPE_RX_CM_EN BIT(3) /* 1'b1 enable RX CM */ > > +#define PIPE_TX_OBS_EN BIT(4) /* 1'b1 enable TX OBS */ > > + > > +/* 0x12 for rx tune?, default 0x14 */ > > +#define PIPE_REG_0C8 0x0c8 > > +/* RX CDR charge pump current bits [3:1], default 0x2 */ > > +#define PIPE_RX_CDR_CHG_PUMP_MASK GENMASK(3, 1) > > +#define PIPE_RX_CDR_CHG_PUMP_TUNE (0x2 << 1) > > + > > +/* 0x02 for 24m, 0x06 for 25m, default 0x06 */ > > +#define UNKNOWN_PIPE_REG_108 0x108 > > +#define UNKNOWN_REFCLK_108_24M 0x02 > > + > > +/* 0x80 for 24m, default 0x00 */ > > +#define UNKNOWN_PIPE_REG_10C 0x10c > > +#define UNKNOWN_REFCLK_10C_24M BIT(7) > > + > > +/* 0x01 for 24m, 0x00 for 25m, default 0x02 */ > > +#define PIPE_REG_118 0x118 > > +/* TX PLL multiplier high bits [3:0], as M, default 0x2, TX data rate = (2*refclk*M)/N */ > > +#define PIPE_TX_PLL_MUL_HIGH_MASK GENMASK(3, 0) > > + > > +/* 0x38 for 24m, 0x64 for 25m, default 0x71 */ > > +#define PIPE_REG_11C 0x11c > > +/* TX PLL multiplier low bits [7:0], as M, default 0x71, TX data rate = (2*refclk*M)/N */ > > +#define PIPE_TX_PLL_MUL_LOW_MASK GENMASK(7, 0) > > + > > +/* 0x0c for SSC, default 0x1c */ > > +#define UNKNOWN_PIPE_REG_120 0x120 > > +#define UNKNOWN_SSC_120_MASK BIT(4) > > +#define UNKNOWN_SSC_120_ENABLE (0x0 << 4) > > + > > +/* default 0x40 */ > > +#define PIPE_REG_12C 0x12c > > +#define PIPE_TX_PLL_ALWAYS_ON BIT(0) /* disable for PLL control by pipe_pd */ > > + > > +/* 0x05 for rx tune, default 0x01 */ > > +#define PIPE_REG_148 0x148 > > +#define PIPE_RX_CHG_PUMP_DIV_2 BIT(2) /* RX CDR charge pump div/2, default 0 */ > > + > > +/* 0x70 for rx tune, default 0x72 */ > > +#define PIPE_REG_150 0x150 > > +#define PIPE_TX_BIAS_EN BIT(6) /* 1'b1 Enable TX Bias */ > > +/* RX CDR phase tracking speed bits [3:0], default 0x2 */ > > +#define PIPE_RX_CDR_SPEED_MASK GENMASK(3, 0) > > +#define PIPE_RX_CDR_SPEED_TUNE 0x00 > > + > > +/* default 0xd4 */ > > +#define PIPE_REG_160 > > +/* RX common mode voltage strength bits [5:4], default 0x1 */ > > +#define PIPE_RX_CDR_COM_VOLT_MASK GENMASK(5, 4) > > +#define PIPE_RX_CDR_COM_VOLT_TUNE (0x1 << 4) > > + > > +/* default 0x00 */ > > +#define PIPE_REG_180 0x180 > > +/* TX driver bias reference voltage bits [3:2], in mv */ > > +#define PIPE_TX_BIAS_REF_VOLT_MASK GENMASK(3, 2) > > +#define PIPE_TX_BIAS_REF_VOLT_200 (0x0 << 2) > > +#define PIPE_TX_BIAS_REF_VOLT_175 (0x1 << 2) > > +#define PIPE_TX_BIAS_REF_VOLT_225 (0x2 << 2) /* downstream 5.10 driver setting */ > > +#define PIPE_TX_BIAS_REF_VOLT_250 (0x3 << 2) > > + > > +/* default 0x01 */ > > +#define PIPE_REG_1A8 0x1a8 > > +#define PIPE_LDO_POWER_DIS BIT(4) /* 1'b1 Disable LDO Power */ > > + > > +/* default 0x07 */ > > +#define PIPE_REG_1AC 0x1ac > > +/* TX driver output common voltage bits [5:4], in mv */ > > +#define PIPE_TX_COMMON_VOLT_MASK GENMASK(5, 4) > > +#define PIPE_TX_COMMON_VOLT_800 (0x0 << 4) > > +#define PIPE_TX_COMMON_VOLT_750 (0x1 << 4) > > +#define PIPE_TX_COMMON_VOLT_950 (0x2 << 4) > > +#define PIPE_TX_COMMON_VOLT_1100 (0x3 << 4) > > + > > +/* default 0xfb */ > > +#define PIPE_REG_1B8 0x1b8 > > +/* TX driver swing strength bits [7:4], range 0x0 to 0xf */ > > +#define PIPE_TX_DRIVER_SWING_MASK GENMASK(7, 4) /* 0x2 lowest functional value */ > > +/* TX driver pre-emphasis strength bits [1:0], default 0x3, enabled by REG 088 */ > > +#define PIPE_TX_DRIVER_PREEMP_STR_MASK GENMASK(1, 0) > > + > > +/* set to 0xf0 for rx tune?, default 0xd0 */ > > +#define PIPE_REG_1C4 0x1c4 > > +#define PIPE_RX_ODT_AUTO_DIS BIT(5) /* Disable RX ODT auto compensation */ > > +#define PIPE_TX_ODT_AUTO_DIS BIT(3) /* Disable TX ODT auto compensation */ > > + > > +/* UTMI registers */ > > +/* 0x0f for utmi tune, default 0x09*/ > > +#define UTMI_REG_030 0x030 > > +/* {bits[2:0]=111}: always enable pre-emphasis */ > > +#define UTMI_ENABLE_PRE_EMPH_MASK GENMASK(2, 0) > > +#define UTMI_ENABLE_PRE_EMPH 0x07 > > + > > +/* 0x41 for utmi tune, default 0x49 */ > > +#define UTMI_REG_040 0x040 > > +/* TX HS pre-emphasis strength bits [5:3], default 0x1*/ > > +#define UTMI_TX_PRE_EMPH_STR_MASK GENMASK(5, 3) > > +#define UTMI_TX_PRE_EMPH_WEAKEST (0x0 << 3) > > + > > +/* set to 0xb5 for utmi tune, default 0xb5 */ > > +#define UTMI_REG_11C 0x11c > > +/* {bits[4:0]=10101}: odt 45ohm tuning */ > > +#define UTMI_ODT_45_OHM_MASK GENMASK(4, 0) > > +#define UTMI_ODT_45_OHM_TUNE 0x15 > > + > > +enum rockchip_usb3phy_type { > > + USB3PHY_TYPE_USB2, > > + USB3PHY_TYPE_USB3, > > + USB3PHY_TYPE_MAX, > > +}; > > + > > +/** > > + * struct rockchip_usb3phy_port - usb-phy port data. > > + * @phy: port usb phy struct. > > + * @regmap: port regmap. > > + * @type: port usb phy type. > > + */ > > +struct rockchip_usb3phy_port { > > + struct phy *phy; > > + struct regmap *regmap; > > + enum rockchip_usb3phy_type type; > > +}; > > + > > +struct rockchip_usb3phy { > > + struct device *dev; > > + struct regmap *regmap; > > + struct clk *clk_pipe; > > + struct clk *clk_otg; > > + struct clk *clk_ref; > > + struct reset_control *u3por_rst; > > + struct reset_control *u2por_rst; > > + struct reset_control *pipe_rst; > > + struct reset_control *utmi_rst; > > + struct reset_control *pipe_apb_rst; > > + struct reset_control *utmi_apb_rst; > > + struct rockchip_usb3phy_port ports[USB3PHY_TYPE_MAX]; > > + int bvalid_irq; > > + int id_irq; > > + int ls_irq; > > + int rxdet_irq; > > +}; > > + > > +struct usb3phy_config { > > + unsigned int reg; > > + unsigned int mask; > > + u8 def; > > +}; > > + > > +static const struct usb3phy_config rk3328_rx_config[] = { > > + { PIPE_REG_150, PIPE_RX_CDR_SPEED_MASK, PIPE_RX_CDR_SPEED_TUNE }, > > + { PIPE_REG_0C8, PIPE_RX_CDR_CHG_PUMP_MASK, PIPE_RX_CDR_CHG_PUMP_TUNE }, > > + { PIPE_REG_148, PIPE_RX_CHG_PUMP_DIV_2, PIPE_RX_CHG_PUMP_DIV_2 }, > > + { PIPE_REG_068, PIPE_RX_CTLE_GAIN_MASK, PIPE_RX_CTLE_GAIN_TUNE }, > > +// { PIPE_REG_1C4, PIPE_RX_ODT_AUTO_DIS, PIPE_RX_ODT_AUTO_DIS }, > > + { PIPE_REG_070, PIPE_RX_ODT_RES_MID_MASK, PIPE_RX_ODT_RES_MID_TUNE }, > > + { PIPE_REG_06C, PIPE_RX_ODT_RES_HIGH_MASK, PIPE_RX_ODT_RES_HIGH_TUNE }, > > + { PIPE_REG_060, PIPE_RX_CTLE_FREQ_BW_MASK, PIPE_RX_CTLE_FREQ_BW_TUNE }, > > + { UNKNOWN_PIPE_REG_10C, UNKNOWN_REFCLK_10C_24M, UNKNOWN_REFCLK_10C_24M }, > > + { PIPE_REG_060, PIPE_RX_CTLE_FREQ_BW_MASK, PIPE_RX_CTLE_FREQ_BW_TUNE }, > > + { PIPE_REG_068, PIPE_RX_EQ_LOW_GAIN_MASK, PIPE_RX_EQ_LOW_GAIN_TUNE }, > > +}; > > + > > +static const struct usb3phy_config rk3328_tx_config[] = { > > + { PIPE_REG_180, PIPE_TX_BIAS_REF_VOLT_MASK, PIPE_TX_BIAS_REF_VOLT_250 }, > > +}; > > + > > +static const struct usb3phy_config rk3328_ssc_config[] = { > > + { UNKNOWN_PIPE_REG_000, UNKNOWN_SSC_000_MASK, UNKNOWN_SSC_000_ENABLE }, > > + { UNKNOWN_PIPE_REG_120, UNKNOWN_SSC_120_MASK, UNKNOWN_SSC_120_ENABLE }, > > +}; > > + > > +static const struct usb3phy_config rk3328_utmi_config[] = { > > + { UTMI_REG_030, UTMI_ENABLE_PRE_EMPH_MASK, UTMI_ENABLE_PRE_EMPH }, > > + { UTMI_REG_040, UTMI_TX_PRE_EMPH_STR_MASK, UTMI_TX_PRE_EMPH_WEAKEST }, > > + { UTMI_REG_11C, UTMI_ODT_45_OHM_MASK, UTMI_ODT_45_OHM_TUNE }, > > +}; > > + > > +static const struct usb3phy_config rk3328_pipe_pwr_en_config[] = { > > + { PIPE_REG_1A8, PIPE_LDO_POWER_DIS, DISABLE_BITS }, > > + { PIPE_REG_044, BANDGAP_POWER_DISABLE, DISABLE_BITS }, > > + { PIPE_REG_150, PIPE_TX_BIAS_EN, PIPE_TX_BIAS_EN }, > > + { PIPE_REG_080, PIPE_TX_COMMON_MODE_DIS, DISABLE_BITS }, > > + { PIPE_REG_0C0, PIPE_TX_OBS_EN, PIPE_TX_OBS_EN }, > > + { PIPE_REG_0C0, PIPE_RX_CM_EN, PIPE_RX_CM_EN }, > > +}; > > + > > +static int rockchip_usb3phy_reset(struct rockchip_usb3phy *usb3phy, > > + bool reset, enum rockchip_usb3phy_type type) > > +{ > > + if (reset) { > > + if (type == USB3PHY_TYPE_USB2) { > > + clk_disable_unprepare(usb3phy->clk_otg); > > + reset_control_assert(usb3phy->utmi_rst); > > + reset_control_assert(usb3phy->u2por_rst); > > + } > > + if (type == USB3PHY_TYPE_USB3) { > > + clk_disable_unprepare(usb3phy->clk_pipe); > > + reset_control_assert(usb3phy->pipe_rst); > > + reset_control_assert(usb3phy->u3por_rst); > > + } > > + } else { > > + if (type == USB3PHY_TYPE_USB2) { > > + reset_control_deassert(usb3phy->u2por_rst); > > + fsleep(1000); > > + clk_prepare_enable(usb3phy->clk_otg); > > + fsleep(500); > > + reset_control_deassert(usb3phy->utmi_rst); > > + fsleep(100); > > + } > > + if (type == USB3PHY_TYPE_USB3) { > > + reset_control_deassert(usb3phy->u3por_rst); > > + fsleep(500); > > + clk_prepare_enable(usb3phy->clk_pipe); > > + fsleep(1000); > > + reset_control_deassert(usb3phy->pipe_rst); > > + fsleep(100); > > + } > > + } > > + return 0; > > +} > > + > > +static irqreturn_t rockchip_usb3phy_linestate_irq(int irq, void *data) > > +{ > > + struct rockchip_usb3phy *usb3phy = data; > > + int tmp; > > + > > + /* check if the interrupt is enabled */ > > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, &tmp); > > + if (!(tmp & USB3_LINESTATE_IRQ_EN)) { > > + dev_warn(usb3phy->dev, "invalid linestate irq received\n"); > > + return IRQ_NONE; > > + } > > + > > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, &tmp); > > + if (tmp & USB3_LINESTATE_IRQ_EN) > > + dev_dbg_ratelimited(usb3phy->dev, "linestate irq received\n"); > > + > > + /* clear the interrupt */ > > + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, USB3_LINESTATE_IRQ_EN); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static irqreturn_t rockchip_usb3phy_bvalid_irq(int irq, void *data) > > +{ > > + struct rockchip_usb3phy *usb3phy = data; > > + int tmp; > > + > > + /* check if the interrupt is enabled */ > > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, &tmp); > > + if (!((tmp & USB3_BVALID_FALL_IRQ_EN) | (tmp & USB3_BVALID_RISE_IRQ_EN))) { > > + dev_warn_ratelimited(usb3phy->dev, "invalid bvalid irq received\n"); > > + return IRQ_NONE; > > + } > > + > > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, &tmp); > > + if (tmp & USB3_BVALID_FALL_IRQ_EN) > > + dev_dbg(usb3phy->dev, "bvalid falling irq received\n"); > > + if (tmp & USB3_BVALID_RISE_IRQ_EN) > > + dev_dbg(usb3phy->dev, "bvalid rising irq received\n"); > > + > > + /* clear the interrupt */ > > + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, USB3_BVALID_CLEAR_MASK); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static irqreturn_t rockchip_usb3phy_id_irq(int irq, void *data) > > +{ > > + struct rockchip_usb3phy *usb3phy = data; > > + int tmp; > > + > > + /* check if the interrupt is enabled */ > > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, &tmp); > > + if (!((tmp & USB3_ID_FALL_IRQ_EN) | (tmp & USB3_ID_RISE_IRQ_EN))) { > > + dev_warn(usb3phy->dev, "invalid id irq received\n"); > > + return IRQ_NONE; > > + } > > + > > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, &tmp); > > + if (tmp & USB3_ID_FALL_IRQ_EN) > > + dev_dbg(usb3phy->dev, "id falling irq received\n"); > > + if (tmp & USB3_ID_RISE_IRQ_EN) > > + dev_dbg(usb3phy->dev, "id rising irq received\n"); > > + > > + /* clear the interrupt */ > > + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, USB3_ID_CLEAR_MASK); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static irqreturn_t rockchip_usb3phy_rxdet_irq(int irq, void *data) > > +{ > > + struct rockchip_usb3phy *usb3phy = data; > > + int tmp; > > + > > + /* check if the interrupt is enabled */ > > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, &tmp); > > + if (!(tmp & USB3_RXDET_IRQ_EN)) { > > + dev_warn(usb3phy->dev, "invalid rxdet irq received\n"); > > + return IRQ_NONE; > > + } > > + > > + regmap_read(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, &tmp); > > + if (tmp & USB3_RXDET_IRQ_EN) > > + dev_dbg_ratelimited(usb3phy->dev, "rxdet irq received\n"); > > + > > + /* clear the interrupt */ > > + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_STAT_REG, USB3_RXDET_IRQ_EN); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static int rockchip_usb3phy_bulk_update(struct rockchip_usb3phy *usb3phy, struct regmap *regmap, > > + const struct usb3phy_config *config, unsigned int size) > > +{ > > + unsigned int i, val, tmp; > > + int ret = 0; > > + > > + for (i = 0; i < size; i++) { > > + ret = regmap_read(regmap, config[i].reg, &val); > > + if (ret < 0) { > > + dev_err(usb3phy->dev, "failed to read addr: 0x%02x\n", config[i].reg); > > + return ret; > > + } > > + tmp = val & ~config[i].mask; > > + tmp |= config[i].def; > > + dev_dbg(usb3phy->dev, "write: 0x%03x old: 0x%02x new: 0x%02x\n", > > + config[i].reg, val, tmp); > > + ret = regmap_write(regmap, config[i].reg, tmp); > > + if (ret < 0) { > > + dev_err(usb3phy->dev, "failed to write addr: 0x%02x\n", config[i].reg); > > + return ret; > > + } > > + } > > + > > + return ret; > > +} > > + > > +static int rockchip_usb3phy_calc_rate(struct rockchip_usb3phy *usb3phy, struct regmap *regmap) > > +{ > > + long rate; > > + unsigned int mul, div, target = (5000000000 / 100000); > > + > > + rate = clk_get_rate(usb3phy->clk_ref) / 100000; > > + if (rate < 0) { > > + dev_err(usb3phy->dev, "failed to get refclk, %ld\n", rate); > > + return rate; > > + /* lowest possible supported clock is 4.8MHZ, highest rk3328 can do is 1.6GHZ */ > > + } else if ((rate < 48) | (rate > 16000)) { > > + goto error; > > + } > > + > > + for (div = 1; div < 32; div++) { > > + for (mul = 1; mul < 1024; mul++) { > > + if (((2 * rate * mul) / div) == target) > > + goto done; > > + if (((2 * rate * mul) / div) > target) > > + break; > > + } > > + } > > + > > +error: > > + dev_err(usb3phy->dev, "invalid refclock rate, %ld\n", rate * 100000); > > + return -EINVAL; > > + > > +done: > > + dev_dbg(usb3phy->dev, "refclk rate mul: %x div: %x rate: %ld\n", mul, div, (rate * 100000)); > > + > > + regmap_write(regmap, PIPE_REG_020, (mul >> 2) & PIPE_RX_CDR_MULT_HIGH_MASK); > > + regmap_write(regmap, PIPE_REG_020, div & PIPE_TX_PLL_DIV_MASK); > > + regmap_write(regmap, PIPE_REG_028, mul & PIPE_RX_CDR_MULT_LOW_MASK); > > + regmap_write(regmap, PIPE_REG_030, div & PIPE_RX_CDR_DIV_MASK); > > + regmap_write(regmap, PIPE_REG_118, (mul >> 8) & PIPE_TX_PLL_MUL_HIGH_MASK); > > + regmap_write(regmap, PIPE_REG_11C, mul & PIPE_TX_PLL_MUL_LOW_MASK); > > + > > + return 0; > > +} > > + > > +static int rockchip_usb3phy_init(struct phy *phy) > > +{ > > + struct rockchip_usb3phy_port *port = phy_get_drvdata(phy); > > + struct rockchip_usb3phy *usb3phy = dev_get_drvdata(phy->dev.parent); > > + int tmp, ret; > > + > > + dev_warn(usb3phy->dev, "usb3phy_init %s\n", dev_name(&phy->dev)); > > + clk_prepare_enable(usb3phy->clk_ref); > > + rockchip_usb3phy_reset(usb3phy, false, port->type); > > + > > + if (port->type == USB3PHY_TYPE_USB2) { > > + /* > > + * "For RK3328 SoC, pre-emphasis and pre-emphasis strength must be > > + * written as one fixed value. The ODT 45ohm value should be tuned > > + * for different boards to adjust HS eye height." > > + */ > > + dev_dbg(usb3phy->dev, "tuning UTMI\n"); > > + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, rk3328_utmi_config, > > + ARRAY_SIZE(rk3328_utmi_config)); > > + } > > + > > + if (port->type == USB3PHY_TYPE_USB3) { > > + /* Enable interrupts */ > > + tmp = (USB3_LINESTATE_IRQ_EN | USB3_ID_FALL_IRQ_EN | USB3_ID_RISE_IRQ_EN | > > + USB3_RXDET_IRQ_EN | USB3_BVALID_RISE_IRQ_EN | USB3_BVALID_FALL_IRQ_EN); > > + tmp |= (tmp << REG_WRITE_SHIFT); > > + > > + ret = regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, tmp); > > + if (ret < 0) { > > + /* interrupt write determines if we have write access */ > > + dev_err(usb3phy->dev, "failed to write interrupts\n"); > > + return ret; > > + } > > + > > + /* Configure for 24M ref clk */ > > + dev_dbg(usb3phy->dev, "setting pipe for 24M refclk\n"); > > + if (rockchip_usb3phy_calc_rate(usb3phy, usb3phy->regmap)) > > + return -EINVAL; > > + > > + /* Enable SSC */ > > + udelay(3); > > + dev_dbg(usb3phy->dev, "setting pipe for SSC\n"); > > + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, rk3328_ssc_config, > > + ARRAY_SIZE(rk3328_ssc_config)); > > + > > + /* "Tuning RX for compliance RJTL test" */ > > + dev_dbg(usb3phy->dev, "setting pipe for RX tuning\n"); > > + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, rk3328_rx_config, > > + ARRAY_SIZE(rk3328_rx_config)); > > + if (ret) > > + return ret; > > + > > + /* > > + * "Tuning TX to increase the bias current used in TX driver and RX EQ, > > + * it can also increase the voltage of LFPS." > > + */ > > + dev_dbg(usb3phy->dev, "setting pipe for TX tuning\n"); > > + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, > > + rk3328_tx_config, ARRAY_SIZE(rk3328_tx_config)); > > + > > + /* Power up the pipe */ > > + dev_dbg(usb3phy->dev, "setting pipe power on\n"); > > + ret = rockchip_usb3phy_bulk_update(usb3phy, port->regmap, rk3328_pipe_pwr_en_config, > > + ARRAY_SIZE(rk3328_pipe_pwr_en_config)); > > + } > > + > > + return 0; > > +} > > + > > +static int rockchip_usb3phy_parse_dt(struct rockchip_usb3phy *usb3phy, struct device *dev) > > +{ > > + int ret; > > + > > + usb3phy->clk_pipe = devm_clk_get(dev, "usb3phy-pipe"); > > + if (IS_ERR(usb3phy->clk_pipe)) { > > + dev_err(dev, "could not get usb3phy pipe clock\n"); > > + return PTR_ERR(usb3phy->clk_pipe); > > + } > > + > > + usb3phy->clk_otg = devm_clk_get(dev, "usb3phy-otg"); > > + if (IS_ERR(usb3phy->clk_otg)) { > > + dev_err(dev, "could not get usb3phy otg clock\n"); > > + return PTR_ERR(usb3phy->clk_otg); > > + } > > + > > + usb3phy->clk_ref = devm_clk_get(dev, "refclk-usb3otg"); > > + if (IS_ERR(usb3phy->clk_ref)) { > > + dev_err(dev, "could not get usb3phy ref clock\n"); > > + return PTR_ERR(usb3phy->clk_ref); > > + } > > + > > + usb3phy->u2por_rst = devm_reset_control_get(dev, "usb3phy-u2-por"); > > + if (IS_ERR(usb3phy->u2por_rst)) { > > + dev_err(dev, "no usb3phy-u2-por reset control found\n"); > > + return PTR_ERR(usb3phy->u2por_rst); > > + } > > + > > + usb3phy->u3por_rst = devm_reset_control_get(dev, "usb3phy-u3-por"); > > + if (IS_ERR(usb3phy->u3por_rst)) { > > + dev_err(dev, "no usb3phy-u3-por reset control found\n"); > > + return PTR_ERR(usb3phy->u3por_rst); > > + } > > + > > + usb3phy->pipe_rst = devm_reset_control_get(dev, "usb3phy-pipe-mac"); > > + if (IS_ERR(usb3phy->pipe_rst)) { > > + dev_err(dev, "no usb3phy_pipe_mac reset control found\n"); > > + return PTR_ERR(usb3phy->pipe_rst); > > + } > > + > > + usb3phy->utmi_rst = devm_reset_control_get(dev, "usb3phy-utmi-mac"); > > + if (IS_ERR(usb3phy->utmi_rst)) { > > + dev_err(dev, "no usb3phy-utmi-mac reset control found\n"); > > + return PTR_ERR(usb3phy->utmi_rst); > > + } > > + > > + usb3phy->pipe_apb_rst = devm_reset_control_get(dev, "usb3phy-pipe-apb"); > > + if (IS_ERR(usb3phy->pipe_apb_rst)) { > > + dev_err(dev, "no usb3phy-pipe-apb reset control found\n"); > > + return PTR_ERR(usb3phy->pipe_apb_rst); > > + } > > + > > + usb3phy->utmi_apb_rst = devm_reset_control_get(dev, "usb3phy-utmi-apb"); > > + if (IS_ERR(usb3phy->utmi_apb_rst)) { > > + dev_err(dev, "no usb3phy-utmi-apb reset control found\n"); > > + return PTR_ERR(usb3phy->utmi_apb_rst); > > + } > > + > > + usb3phy->ls_irq = of_irq_get_byname(dev->of_node, "linestate"); > > + if (usb3phy->ls_irq < 0) { > > + dev_err(dev, "no linestate irq provided\n"); > > + return usb3phy->ls_irq; > > + } > > + > > + ret = devm_request_threaded_irq(dev, usb3phy->ls_irq, NULL, rockchip_usb3phy_linestate_irq, > > + IRQF_ONESHOT, "rockchip_usb3phy_ls", usb3phy); > > + if (ret) { > > + dev_err(dev, "failed to request linestate irq handle\n"); > > + return ret; > > + } > > + > > + usb3phy->bvalid_irq = of_irq_get_byname(dev->of_node, "bvalid"); > > + if (usb3phy->bvalid_irq < 0) { > > + dev_err(dev, "no bvalid irq provided\n"); > > + return usb3phy->bvalid_irq; > > + } > > + > > + ret = devm_request_threaded_irq(dev, usb3phy->bvalid_irq, NULL, rockchip_usb3phy_bvalid_irq, > > + IRQF_ONESHOT, "rockchip_usb3phy_bvalid", usb3phy); > > + if (ret) { > > + dev_err(dev, "failed to request bvalid irq handle\n"); > > + return ret; > > + } > > + > > + usb3phy->id_irq = of_irq_get_byname(dev->of_node, "id"); > > + if (usb3phy->id_irq < 0) { > > + dev_err(dev, "no id irq provided\n"); > > + return usb3phy->id_irq; > > + } > > + > > + ret = devm_request_threaded_irq(dev, usb3phy->id_irq, NULL, rockchip_usb3phy_id_irq, > > + IRQF_ONESHOT, "rockchip_usb3phy-id", usb3phy); > > + if (ret) { > > + dev_err(dev, "failed to request id irq handle\n"); > > + return ret; > > + } > > + > > + usb3phy->rxdet_irq = of_irq_get_byname(dev->of_node, "rxdet"); > > + if (usb3phy->rxdet_irq < 0) { > > + dev_err(dev, "no rxdet irq provided\n"); > > + return usb3phy->rxdet_irq; > > + } > > + > > + ret = devm_request_threaded_irq(dev, usb3phy->rxdet_irq, NULL, rockchip_usb3phy_rxdet_irq, > > + IRQF_ONESHOT, "rockchip_usb3phy-rxdet", usb3phy); > > + if (ret) { > > + dev_err(dev, "failed to request rxdet irq handle\n"); > > + return ret; > > + } > > + > > + return 0; > > +} > > + > > +static int rockchip_usb3phy_exit(struct phy *phy) > > +{ > > + struct rockchip_usb3phy_port *port = phy_get_drvdata(phy); > > + struct rockchip_usb3phy *usb3phy = dev_get_drvdata(phy->dev.parent); > > + > > + dev_dbg(usb3phy->dev, "usb3phy_shutdown\n"); > > + rockchip_usb3phy_reset(usb3phy, true, port->type); > > + > > + return 0; > > +} > > + > > +static const struct phy_ops rockchip_usb3phy_ops = { > > + .init = rockchip_usb3phy_init, > > + .exit = rockchip_usb3phy_exit, > > + .owner = THIS_MODULE, > > +}; > > + > > +static const struct regmap_config rockchip_usb3phy_utmi_port_regmap_config = { > > + .reg_bits = 32, > > + .val_bits = 32, > > + .reg_stride = 4, > > + .max_register = 0x400, > > + .cache_type = REGCACHE_NONE, > > + .fast_io = true, > > + .name = "utmi-port", > > +}; > > + > > +static const struct regmap_config rockchip_usb3phy_pipe_port_regmap_config = { > > + .reg_bits = 32, > > + .val_bits = 32, > > + .reg_stride = 4, > > + .max_register = 0x400, > > + .cache_type = REGCACHE_NONE, > > + .fast_io = true, > > + .name = "pipe-port", > > +}; > > + > > +static const struct regmap_config rockchip_usb3phy_regmap_config = { > > + .reg_bits = 32, > > + .val_bits = 32, > > + .reg_stride = 4, > > + .max_register = 0x400, > > + .write_flag_mask = REG_WRITE_MASK, > > + .cache_type = REGCACHE_NONE, > > + .fast_io = true, > > +}; > > + > > +static int rockchip_usb3phy_probe(struct platform_device *pdev) > > +{ > > + struct device *dev = &pdev->dev; > > + struct device_node *np = dev->of_node; > > + struct device_node *child_np; > > + struct phy_provider *provider; > > + struct rockchip_usb3phy *usb3phy; > > + const struct regmap_config regmap_config = rockchip_usb3phy_regmap_config; > > + void __iomem *base; > > + int i, ret; > > + > > + dev_dbg(dev, "Probe usb3phy main block\n"); > > + usb3phy = devm_kzalloc(dev, sizeof(*usb3phy), GFP_KERNEL); > > + if (!usb3phy) > > + return -ENOMEM; > > + > > + ret = rockchip_usb3phy_parse_dt(usb3phy, dev); > > + if (ret) { > > + dev_err(dev, "parse dt failed %i\n", ret); > > + return ret; > > + } > > + > > + base = devm_of_iomap(dev, np, 0, NULL); > > + if (IS_ERR(base)) { > > + dev_err(dev, "failed port ioremap\n"); > > + return PTR_ERR(base); > > + } > > + > > + usb3phy->regmap = devm_regmap_init_mmio(dev, base, ®map_config); > > + if (IS_ERR(usb3phy->regmap)) { > > + dev_err(dev, "regmap init failed\n"); > > + return PTR_ERR(usb3phy->regmap); > > + } > > + > > + usb3phy->dev = dev; > > + platform_set_drvdata(pdev, usb3phy); > > + > > + /* place block in reset */ > > + reset_control_assert(usb3phy->pipe_rst); > > + reset_control_assert(usb3phy->utmi_rst); > > + reset_control_assert(usb3phy->u3por_rst); > > + reset_control_assert(usb3phy->u2por_rst); > > + reset_control_assert(usb3phy->pipe_apb_rst); > > + reset_control_assert(usb3phy->utmi_apb_rst); > > + > > + fsleep(20); > > + > > + /* take apb interface out of reset */ > > + reset_control_deassert(usb3phy->utmi_apb_rst); > > + reset_control_deassert(usb3phy->pipe_apb_rst); > > + > > + /* enable usb3phy rx detection to fix disconnection issues */ > > + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, > > + (USB3_RXDET_EN | (USB3_RXDET_EN << REG_WRITE_SHIFT))); > > + > > + dev_dbg(dev, "Completed usb3phy core probe\n"); > > + > > + /* probe the actual ports */ > > + i = 0; > > + for_each_available_child_of_node(np, child_np) { > > + const struct regmap_config *regmap_port_config; > > + struct rockchip_usb3phy_port *port = &usb3phy->ports[i]; > > + struct phy *phy; > > + > > + if (of_node_name_eq(child_np, "utmi-port")) { > > + port->type = USB3PHY_TYPE_USB2; > > + regmap_port_config = &rockchip_usb3phy_utmi_port_regmap_config; > > + } else if (of_node_name_eq(child_np, "pipe-port")) { > > + port->type = USB3PHY_TYPE_USB3; > > + regmap_port_config = &rockchip_usb3phy_pipe_port_regmap_config; > > + } else { > > + dev_err(dev, "unknown child node port type %s\n", child_np->name); > > + goto err_port; > > + } > > + > > + base = devm_of_iomap(dev, child_np, 0, NULL); > > + if (IS_ERR(base)) { > > + dev_err(dev, "failed port ioremap\n"); > > + ret = PTR_ERR(base); > > + goto err_port; > > + } > > + > > + port->regmap = devm_regmap_init_mmio(dev, base, regmap_port_config); > > + if (IS_ERR(port->regmap)) { > > + dev_err(dev, "regmap init failed\n"); > > + ret = PTR_ERR(port->regmap); > > + goto err_port; > > + } > > + > > + phy = devm_phy_create(dev, child_np, &rockchip_usb3phy_ops); > > + if (IS_ERR(phy)) { > > + dev_err_probe(dev, PTR_ERR(phy), "failed to create phy\n"); > > + ret = PTR_ERR(phy); > > + goto err_port; > > + } > > + > > + port->phy = phy; > > + phy_set_drvdata(port->phy, port); > > + > > + /* to prevent out of boundary */ > > + if (++i >= USB3PHY_TYPE_MAX) { > > + of_node_put(child_np); > > + break; > > + } > > + > > + dev_info(dev, "Completed usb3phy %s port init\n", child_np->name); > > + } > > + > > + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > > + return PTR_ERR_OR_ZERO(provider); > > + > > +err_port: > > + of_node_put(child_np); > > + return ret; > > +} > > + > > +static const struct of_device_id rockchip_usb3phy_dt_ids[] = { > > + { .compatible = "rockchip,rk3328-usb3phy", }, > > +}; > > isn’t { /* sentinel */ } missing here? Yes, there should be. Thank you. > > > > + > > +MODULE_DEVICE_TABLE(of, rockchip_usb3phy_dt_ids); > > + > > +static struct platform_driver rockchip_usb3phy_driver = { > > + .probe = rockchip_usb3phy_probe, > > + .driver = { > > + .name = "rockchip-usb3-phy", > > + .of_match_table = rockchip_usb3phy_dt_ids, > > + }, > > +}; > > + > > +module_platform_driver(rockchip_usb3phy_driver); > > + > > +MODULE_AUTHOR("Peter Geis <pgwipeout@gmail.com>"); > > +MODULE_DESCRIPTION("Rockchip Innosilicon USB3PHY driver"); > > +MODULE_LICENSE("GPL"); > > -- > > 2.39.5 > > > > > > _______________________________________________ > > Linux-rockchip mailing list > > Linux-rockchip@lists.infradead.org > > http://lists.infradead.org/mailman/listinfo/linux-rockchip > -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 3/6] phy: rockchip: add driver for rk3328 usb3 phy 2025-01-15 1:26 ` [RFC PATCH v1 3/6] phy: rockchip: add driver for " Peter Geis 2025-01-15 11:24 ` Piotr Oniszczuk @ 2025-01-16 12:59 ` Krzysztof Kozlowski 2025-01-16 13:14 ` Peter Geis 2025-01-16 15:26 ` Diederik de Haas 2 siblings, 1 reply; 24+ messages in thread From: Krzysztof Kozlowski @ 2025-01-16 12:59 UTC (permalink / raw) To: Peter Geis, Heiko Stuebner Cc: zyw, kever.yang, frank.wang, william.wu, wulf, linux-rockchip, Algea Cao, Arnd Bergmann, Cristian Ciocaltea, Kishon Vijay Abraham I, Philipp Zabel, Sebastian Reichel, Vinod Koul, Zhang Yubing, linux-arm-kernel, linux-kernel, linux-phy On 15/01/2025 02:26, Peter Geis wrote: > The rk3328 has a combined usb2 and usb3 phy block for the usb3 dwc > interface. The implementation up until now has been bugged, with > multiple issues ranging from disconnect detection failures to low > performance. This driver fixes the majority of the original issues and > allows better performance for the rk3328 usb3 port. > > This driver sources data from multiple sources, including the rk3328 > trm, the rk3228h trm, emails from Rockchip, and both the 4.4 and 5.10 > downstream drivers. The current implementation allows for basic bring up > of the phy block and fixes the detection issues. Interrupts are enabled, > but currently only used for debugging. Features missing currently are > power management, OTG handling, board specific tuning, regulator control, > > Currently the only known bugs are a AX88179 usb3 gigabit adapter crashes > when operating at usb3 speeds and transmitting large amounts of data and > full disconnection detections may be slower than expected (~5-10 seconds). > > Signed-off-by: Peter Geis <pgwipeout@gmail.com> > --- ... > +} > + > +static int rockchip_usb3phy_parse_dt(struct rockchip_usb3phy *usb3phy, struct device *dev) > +{ > + int ret; > + > + usb3phy->clk_pipe = devm_clk_get(dev, "usb3phy-pipe"); > + if (IS_ERR(usb3phy->clk_pipe)) { > + dev_err(dev, "could not get usb3phy pipe clock\n"); Syntax is always: return dev_err_probe > + return PTR_ERR(usb3phy->clk_pipe); > + } > + > + usb3phy->clk_otg = devm_clk_get(dev, "usb3phy-otg"); > + if (IS_ERR(usb3phy->clk_otg)) { > + dev_err(dev, "could not get usb3phy otg clock\n"); > + return PTR_ERR(usb3phy->clk_otg); > + } > + > + usb3phy->clk_ref = devm_clk_get(dev, "refclk-usb3otg"); > + if (IS_ERR(usb3phy->clk_ref)) { > + dev_err(dev, "could not get usb3phy ref clock\n"); > + return PTR_ERR(usb3phy->clk_ref); > + } > + > + usb3phy->u2por_rst = devm_reset_control_get(dev, "usb3phy-u2-por"); > + if (IS_ERR(usb3phy->u2por_rst)) { > + dev_err(dev, "no usb3phy-u2-por reset control found\n"); > + return PTR_ERR(usb3phy->u2por_rst); > + } > + > + usb3phy->u3por_rst = devm_reset_control_get(dev, "usb3phy-u3-por"); > + if (IS_ERR(usb3phy->u3por_rst)) { > + dev_err(dev, "no usb3phy-u3-por reset control found\n"); > + return PTR_ERR(usb3phy->u3por_rst); > + } > + > + usb3phy->pipe_rst = devm_reset_control_get(dev, "usb3phy-pipe-mac"); > + if (IS_ERR(usb3phy->pipe_rst)) { > + dev_err(dev, "no usb3phy_pipe_mac reset control found\n"); > + return PTR_ERR(usb3phy->pipe_rst); > + } > + > + usb3phy->utmi_rst = devm_reset_control_get(dev, "usb3phy-utmi-mac"); > + if (IS_ERR(usb3phy->utmi_rst)) { > + dev_err(dev, "no usb3phy-utmi-mac reset control found\n"); > + return PTR_ERR(usb3phy->utmi_rst); > + } > + > + usb3phy->pipe_apb_rst = devm_reset_control_get(dev, "usb3phy-pipe-apb"); > + if (IS_ERR(usb3phy->pipe_apb_rst)) { > + dev_err(dev, "no usb3phy-pipe-apb reset control found\n"); > + return PTR_ERR(usb3phy->pipe_apb_rst); > + } > + > + usb3phy->utmi_apb_rst = devm_reset_control_get(dev, "usb3phy-utmi-apb"); > + if (IS_ERR(usb3phy->utmi_apb_rst)) { > + dev_err(dev, "no usb3phy-utmi-apb reset control found\n"); > + return PTR_ERR(usb3phy->utmi_apb_rst); > + } > + > + usb3phy->ls_irq = of_irq_get_byname(dev->of_node, "linestate"); > + if (usb3phy->ls_irq < 0) { > + dev_err(dev, "no linestate irq provided\n"); > + return usb3phy->ls_irq; > + } > + > + ret = devm_request_threaded_irq(dev, usb3phy->ls_irq, NULL, rockchip_usb3phy_linestate_irq, > + IRQF_ONESHOT, "rockchip_usb3phy_ls", usb3phy); > + if (ret) { > + dev_err(dev, "failed to request linestate irq handle\n"); > + return ret; > + } > + > + usb3phy->bvalid_irq = of_irq_get_byname(dev->of_node, "bvalid"); > + if (usb3phy->bvalid_irq < 0) { > + dev_err(dev, "no bvalid irq provided\n"); > + return usb3phy->bvalid_irq; > + } > + > + ret = devm_request_threaded_irq(dev, usb3phy->bvalid_irq, NULL, rockchip_usb3phy_bvalid_irq, > + IRQF_ONESHOT, "rockchip_usb3phy_bvalid", usb3phy); > + if (ret) { > + dev_err(dev, "failed to request bvalid irq handle\n"); > + return ret; > + } > + > + usb3phy->id_irq = of_irq_get_byname(dev->of_node, "id"); > + if (usb3phy->id_irq < 0) { > + dev_err(dev, "no id irq provided\n"); > + return usb3phy->id_irq; > + } > + > + ret = devm_request_threaded_irq(dev, usb3phy->id_irq, NULL, rockchip_usb3phy_id_irq, > + IRQF_ONESHOT, "rockchip_usb3phy-id", usb3phy); > + if (ret) { > + dev_err(dev, "failed to request id irq handle\n"); > + return ret; > + } > + > + usb3phy->rxdet_irq = of_irq_get_byname(dev->of_node, "rxdet"); > + if (usb3phy->rxdet_irq < 0) { > + dev_err(dev, "no rxdet irq provided\n"); > + return usb3phy->rxdet_irq; > + } > + > + ret = devm_request_threaded_irq(dev, usb3phy->rxdet_irq, NULL, rockchip_usb3phy_rxdet_irq, > + IRQF_ONESHOT, "rockchip_usb3phy-rxdet", usb3phy); > + if (ret) { > + dev_err(dev, "failed to request rxdet irq handle\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int rockchip_usb3phy_exit(struct phy *phy) > +{ > + struct rockchip_usb3phy_port *port = phy_get_drvdata(phy); > + struct rockchip_usb3phy *usb3phy = dev_get_drvdata(phy->dev.parent); > + > + dev_dbg(usb3phy->dev, "usb3phy_shutdown\n"); > + rockchip_usb3phy_reset(usb3phy, true, port->type); > + > + return 0; > +} > + > +static const struct phy_ops rockchip_usb3phy_ops = { > + .init = rockchip_usb3phy_init, > + .exit = rockchip_usb3phy_exit, > + .owner = THIS_MODULE, > +}; > + > +static const struct regmap_config rockchip_usb3phy_utmi_port_regmap_config = { > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > + .max_register = 0x400, > + .cache_type = REGCACHE_NONE, > + .fast_io = true, > + .name = "utmi-port", > +}; > + > +static const struct regmap_config rockchip_usb3phy_pipe_port_regmap_config = { > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > + .max_register = 0x400, > + .cache_type = REGCACHE_NONE, > + .fast_io = true, > + .name = "pipe-port", > +}; > + > +static const struct regmap_config rockchip_usb3phy_regmap_config = { > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > + .max_register = 0x400, > + .write_flag_mask = REG_WRITE_MASK, > + .cache_type = REGCACHE_NONE, > + .fast_io = true, > +}; > + > +static int rockchip_usb3phy_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct device_node *np = dev->of_node; > + struct device_node *child_np; > + struct phy_provider *provider; > + struct rockchip_usb3phy *usb3phy; > + const struct regmap_config regmap_config = rockchip_usb3phy_regmap_config; > + void __iomem *base; > + int i, ret; > + > + dev_dbg(dev, "Probe usb3phy main block\n"); > + usb3phy = devm_kzalloc(dev, sizeof(*usb3phy), GFP_KERNEL); > + if (!usb3phy) > + return -ENOMEM; > + > + ret = rockchip_usb3phy_parse_dt(usb3phy, dev); > + if (ret) { > + dev_err(dev, "parse dt failed %i\n", ret); return dev_err_probe() And do not print errors twice. > + return ret; > + > + /* take apb interface out of reset */ > + reset_control_deassert(usb3phy->utmi_apb_rst); > + reset_control_deassert(usb3phy->pipe_apb_rst); > + > + /* enable usb3phy rx detection to fix disconnection issues */ > + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, > + (USB3_RXDET_EN | (USB3_RXDET_EN << REG_WRITE_SHIFT))); > + > + dev_dbg(dev, "Completed usb3phy core probe\n"); Drop. > + > + /* probe the actual ports */ > + i = 0; > + for_each_available_child_of_node(np, child_np) { > + const struct regmap_config *regmap_port_config; > + struct rockchip_usb3phy_port *port = &usb3phy->ports[i]; > + struct phy *phy; > + > + if (of_node_name_eq(child_np, "utmi-port")) { Why are you comparing node names, if you decided to put there compatibles? Decide on one method, but wait for full DT review. > + port->type = USB3PHY_TYPE_USB2; > + regmap_port_config = &rockchip_usb3phy_utmi_port_regmap_config; > + } else if (of_node_name_eq(child_np, "pipe-port")) { > + port->type = USB3PHY_TYPE_USB3; > + regmap_port_config = &rockchip_usb3phy_pipe_port_regmap_config; > + } else { > + dev_err(dev, "unknown child node port type %s\n", child_np->name); > + goto err_port; > + } > + > + base = devm_of_iomap(dev, child_np, 0, NULL); > + if (IS_ERR(base)) { > + dev_err(dev, "failed port ioremap\n"); > + ret = PTR_ERR(base); > + goto err_port; > + } > + > + port->regmap = devm_regmap_init_mmio(dev, base, regmap_port_config); > + if (IS_ERR(port->regmap)) { > + dev_err(dev, "regmap init failed\n"); > + ret = PTR_ERR(port->regmap); > + goto err_port; > + } > + > + phy = devm_phy_create(dev, child_np, &rockchip_usb3phy_ops); > + if (IS_ERR(phy)) { > + dev_err_probe(dev, PTR_ERR(phy), "failed to create phy\n"); > + ret = PTR_ERR(phy); Just read how dev_err_probe() works. The syntax is: ret = dev_err_probe. > + goto err_port; > + } > + > + port->phy = phy; > + phy_set_drvdata(port->phy, port); > + > + /* to prevent out of boundary */ > + if (++i >= USB3PHY_TYPE_MAX) { > + of_node_put(child_np); > + break; > + } > + > + dev_info(dev, "Completed usb3phy %s port init\n", child_np->name); Drop, not really helping. Best regards, Krzysztof -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 3/6] phy: rockchip: add driver for rk3328 usb3 phy 2025-01-16 12:59 ` Krzysztof Kozlowski @ 2025-01-16 13:14 ` Peter Geis 0 siblings, 0 replies; 24+ messages in thread From: Peter Geis @ 2025-01-16 13:14 UTC (permalink / raw) To: Krzysztof Kozlowski Cc: Heiko Stuebner, zyw, kever.yang, frank.wang, william.wu, wulf, linux-rockchip, Algea Cao, Arnd Bergmann, Cristian Ciocaltea, Kishon Vijay Abraham I, Philipp Zabel, Sebastian Reichel, Vinod Koul, Zhang Yubing, linux-arm-kernel, linux-kernel, linux-phy On Thu, Jan 16, 2025 at 7:59 AM Krzysztof Kozlowski <krzk@kernel.org> wrote: > > On 15/01/2025 02:26, Peter Geis wrote: > > The rk3328 has a combined usb2 and usb3 phy block for the usb3 dwc > > interface. The implementation up until now has been bugged, with > > multiple issues ranging from disconnect detection failures to low > > performance. This driver fixes the majority of the original issues and > > allows better performance for the rk3328 usb3 port. > > > > This driver sources data from multiple sources, including the rk3328 > > trm, the rk3228h trm, emails from Rockchip, and both the 4.4 and 5.10 > > downstream drivers. The current implementation allows for basic bring up > > of the phy block and fixes the detection issues. Interrupts are enabled, > > but currently only used for debugging. Features missing currently are > > power management, OTG handling, board specific tuning, regulator control, > > > > Currently the only known bugs are a AX88179 usb3 gigabit adapter crashes > > when operating at usb3 speeds and transmitting large amounts of data and > > full disconnection detections may be slower than expected (~5-10 seconds). > > > > Signed-off-by: Peter Geis <pgwipeout@gmail.com> > > --- > > > ... > > > +} > > + > > +static int rockchip_usb3phy_parse_dt(struct rockchip_usb3phy *usb3phy, struct device *dev) > > +{ > > + int ret; > > + > > + usb3phy->clk_pipe = devm_clk_get(dev, "usb3phy-pipe"); > > + if (IS_ERR(usb3phy->clk_pipe)) { > > + dev_err(dev, "could not get usb3phy pipe clock\n"); > > Syntax is always: > return dev_err_probe > > > + return PTR_ERR(usb3phy->clk_pipe); > > + } > > + > > + usb3phy->clk_otg = devm_clk_get(dev, "usb3phy-otg"); > > + if (IS_ERR(usb3phy->clk_otg)) { > > + dev_err(dev, "could not get usb3phy otg clock\n"); > > + return PTR_ERR(usb3phy->clk_otg); > > + } > > + > > + usb3phy->clk_ref = devm_clk_get(dev, "refclk-usb3otg"); > > + if (IS_ERR(usb3phy->clk_ref)) { > > + dev_err(dev, "could not get usb3phy ref clock\n"); > > + return PTR_ERR(usb3phy->clk_ref); > > + } > > + > > + usb3phy->u2por_rst = devm_reset_control_get(dev, "usb3phy-u2-por"); > > + if (IS_ERR(usb3phy->u2por_rst)) { > > + dev_err(dev, "no usb3phy-u2-por reset control found\n"); > > + return PTR_ERR(usb3phy->u2por_rst); > > + } > > + > > + usb3phy->u3por_rst = devm_reset_control_get(dev, "usb3phy-u3-por"); > > + if (IS_ERR(usb3phy->u3por_rst)) { > > + dev_err(dev, "no usb3phy-u3-por reset control found\n"); > > + return PTR_ERR(usb3phy->u3por_rst); > > + } > > + > > + usb3phy->pipe_rst = devm_reset_control_get(dev, "usb3phy-pipe-mac"); > > + if (IS_ERR(usb3phy->pipe_rst)) { > > + dev_err(dev, "no usb3phy_pipe_mac reset control found\n"); > > + return PTR_ERR(usb3phy->pipe_rst); > > + } > > + > > + usb3phy->utmi_rst = devm_reset_control_get(dev, "usb3phy-utmi-mac"); > > + if (IS_ERR(usb3phy->utmi_rst)) { > > + dev_err(dev, "no usb3phy-utmi-mac reset control found\n"); > > + return PTR_ERR(usb3phy->utmi_rst); > > + } > > + > > + usb3phy->pipe_apb_rst = devm_reset_control_get(dev, "usb3phy-pipe-apb"); > > + if (IS_ERR(usb3phy->pipe_apb_rst)) { > > + dev_err(dev, "no usb3phy-pipe-apb reset control found\n"); > > + return PTR_ERR(usb3phy->pipe_apb_rst); > > + } > > + > > + usb3phy->utmi_apb_rst = devm_reset_control_get(dev, "usb3phy-utmi-apb"); > > + if (IS_ERR(usb3phy->utmi_apb_rst)) { > > + dev_err(dev, "no usb3phy-utmi-apb reset control found\n"); > > + return PTR_ERR(usb3phy->utmi_apb_rst); > > + } > > + > > + usb3phy->ls_irq = of_irq_get_byname(dev->of_node, "linestate"); > > + if (usb3phy->ls_irq < 0) { > > + dev_err(dev, "no linestate irq provided\n"); > > + return usb3phy->ls_irq; > > + } > > + > > + ret = devm_request_threaded_irq(dev, usb3phy->ls_irq, NULL, rockchip_usb3phy_linestate_irq, > > + IRQF_ONESHOT, "rockchip_usb3phy_ls", usb3phy); > > + if (ret) { > > + dev_err(dev, "failed to request linestate irq handle\n"); > > + return ret; > > + } > > + > > + usb3phy->bvalid_irq = of_irq_get_byname(dev->of_node, "bvalid"); > > + if (usb3phy->bvalid_irq < 0) { > > + dev_err(dev, "no bvalid irq provided\n"); > > + return usb3phy->bvalid_irq; > > + } > > + > > + ret = devm_request_threaded_irq(dev, usb3phy->bvalid_irq, NULL, rockchip_usb3phy_bvalid_irq, > > + IRQF_ONESHOT, "rockchip_usb3phy_bvalid", usb3phy); > > + if (ret) { > > + dev_err(dev, "failed to request bvalid irq handle\n"); > > + return ret; > > + } > > + > > + usb3phy->id_irq = of_irq_get_byname(dev->of_node, "id"); > > + if (usb3phy->id_irq < 0) { > > + dev_err(dev, "no id irq provided\n"); > > + return usb3phy->id_irq; > > + } > > + > > + ret = devm_request_threaded_irq(dev, usb3phy->id_irq, NULL, rockchip_usb3phy_id_irq, > > + IRQF_ONESHOT, "rockchip_usb3phy-id", usb3phy); > > + if (ret) { > > + dev_err(dev, "failed to request id irq handle\n"); > > + return ret; > > + } > > + > > + usb3phy->rxdet_irq = of_irq_get_byname(dev->of_node, "rxdet"); > > + if (usb3phy->rxdet_irq < 0) { > > + dev_err(dev, "no rxdet irq provided\n"); > > + return usb3phy->rxdet_irq; > > + } > > + > > + ret = devm_request_threaded_irq(dev, usb3phy->rxdet_irq, NULL, rockchip_usb3phy_rxdet_irq, > > + IRQF_ONESHOT, "rockchip_usb3phy-rxdet", usb3phy); > > + if (ret) { > > + dev_err(dev, "failed to request rxdet irq handle\n"); > > + return ret; > > + } > > + > > + return 0; > > +} > > + > > +static int rockchip_usb3phy_exit(struct phy *phy) > > +{ > > + struct rockchip_usb3phy_port *port = phy_get_drvdata(phy); > > + struct rockchip_usb3phy *usb3phy = dev_get_drvdata(phy->dev.parent); > > + > > + dev_dbg(usb3phy->dev, "usb3phy_shutdown\n"); > > + rockchip_usb3phy_reset(usb3phy, true, port->type); > > + > > + return 0; > > +} > > + > > +static const struct phy_ops rockchip_usb3phy_ops = { > > + .init = rockchip_usb3phy_init, > > + .exit = rockchip_usb3phy_exit, > > + .owner = THIS_MODULE, > > +}; > > + > > +static const struct regmap_config rockchip_usb3phy_utmi_port_regmap_config = { > > + .reg_bits = 32, > > + .val_bits = 32, > > + .reg_stride = 4, > > + .max_register = 0x400, > > + .cache_type = REGCACHE_NONE, > > + .fast_io = true, > > + .name = "utmi-port", > > +}; > > + > > +static const struct regmap_config rockchip_usb3phy_pipe_port_regmap_config = { > > + .reg_bits = 32, > > + .val_bits = 32, > > + .reg_stride = 4, > > + .max_register = 0x400, > > + .cache_type = REGCACHE_NONE, > > + .fast_io = true, > > + .name = "pipe-port", > > +}; > > + > > +static const struct regmap_config rockchip_usb3phy_regmap_config = { > > + .reg_bits = 32, > > + .val_bits = 32, > > + .reg_stride = 4, > > + .max_register = 0x400, > > + .write_flag_mask = REG_WRITE_MASK, > > + .cache_type = REGCACHE_NONE, > > + .fast_io = true, > > +}; > > + > > +static int rockchip_usb3phy_probe(struct platform_device *pdev) > > +{ > > + struct device *dev = &pdev->dev; > > + struct device_node *np = dev->of_node; > > + struct device_node *child_np; > > + struct phy_provider *provider; > > + struct rockchip_usb3phy *usb3phy; > > + const struct regmap_config regmap_config = rockchip_usb3phy_regmap_config; > > + void __iomem *base; > > + int i, ret; > > + > > + dev_dbg(dev, "Probe usb3phy main block\n"); > > + usb3phy = devm_kzalloc(dev, sizeof(*usb3phy), GFP_KERNEL); > > + if (!usb3phy) > > + return -ENOMEM; > > + > > + ret = rockchip_usb3phy_parse_dt(usb3phy, dev); > > + if (ret) { > > + dev_err(dev, "parse dt failed %i\n", ret); > > return dev_err_probe() > > And do not print errors twice. This is a remnant of the debugging era, thank you for catching it. > > > + return ret; > > > > > > + > > + /* take apb interface out of reset */ > > + reset_control_deassert(usb3phy->utmi_apb_rst); > > + reset_control_deassert(usb3phy->pipe_apb_rst); > > + > > + /* enable usb3phy rx detection to fix disconnection issues */ > > + regmap_write(usb3phy->regmap, USB3PHY_WAKEUP_CON_REG, > > + (USB3_RXDET_EN | (USB3_RXDET_EN << REG_WRITE_SHIFT))); > > + > > + dev_dbg(dev, "Completed usb3phy core probe\n"); > > Drop. > > > + > > + /* probe the actual ports */ > > + i = 0; > > + for_each_available_child_of_node(np, child_np) { > > + const struct regmap_config *regmap_port_config; > > + struct rockchip_usb3phy_port *port = &usb3phy->ports[i]; > > + struct phy *phy; > > + > > + if (of_node_name_eq(child_np, "utmi-port")) { > > Why are you comparing node names, if you decided to put there compatibles? > > Decide on one method, but wait for full DT review. The compatibles are remnants from the old method where the driver bound each device individually. Now that I've switched to the generic phy system from the usb-phy system, everything is bound at once, they are no longer necessary. > > > + port->type = USB3PHY_TYPE_USB2; > > + regmap_port_config = &rockchip_usb3phy_utmi_port_regmap_config; > > + } else if (of_node_name_eq(child_np, "pipe-port")) { > > + port->type = USB3PHY_TYPE_USB3; > > + regmap_port_config = &rockchip_usb3phy_pipe_port_regmap_config; > > + } else { > > + dev_err(dev, "unknown child node port type %s\n", child_np->name); > > + goto err_port; > > + } > > + > > + base = devm_of_iomap(dev, child_np, 0, NULL); > > + if (IS_ERR(base)) { > > + dev_err(dev, "failed port ioremap\n"); > > + ret = PTR_ERR(base); > > + goto err_port; > > + } > > + > > + port->regmap = devm_regmap_init_mmio(dev, base, regmap_port_config); > > + if (IS_ERR(port->regmap)) { > > + dev_err(dev, "regmap init failed\n"); > > + ret = PTR_ERR(port->regmap); > > + goto err_port; > > + } > > + > > + phy = devm_phy_create(dev, child_np, &rockchip_usb3phy_ops); > > + if (IS_ERR(phy)) { > > + dev_err_probe(dev, PTR_ERR(phy), "failed to create phy\n"); > > + ret = PTR_ERR(phy); > > Just read how dev_err_probe() works. The syntax is: > ret = dev_err_probe. Thank you for reminding me of this. The error handling portion is about the only part that remains from the original driver, back before dev_err_probe became a big thing. > > > + goto err_port; > > + } > > + > > + port->phy = phy; > > + phy_set_drvdata(port->phy, port); > > + > > + /* to prevent out of boundary */ > > + if (++i >= USB3PHY_TYPE_MAX) { > > + of_node_put(child_np); > > + break; > > + } > > + > > + dev_info(dev, "Completed usb3phy %s port init\n", child_np->name); > Drop, not really helping. Thank you for catching the remaining debugging stuff I missed stripping out. > > Best regards, > Krzysztof I appreciate your review. Very Respectfully, Peter Geis -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 3/6] phy: rockchip: add driver for rk3328 usb3 phy 2025-01-15 1:26 ` [RFC PATCH v1 3/6] phy: rockchip: add driver for " Peter Geis 2025-01-15 11:24 ` Piotr Oniszczuk 2025-01-16 12:59 ` Krzysztof Kozlowski @ 2025-01-16 15:26 ` Diederik de Haas 2025-01-16 15:57 ` Peter Geis 2 siblings, 1 reply; 24+ messages in thread From: Diederik de Haas @ 2025-01-16 15:26 UTC (permalink / raw) To: Peter Geis, Heiko Stuebner Cc: Kishon Vijay Abraham I, Algea Cao, linux-kernel, Philipp Zabel, Arnd Bergmann, frank.wang, william.wu, kever.yang, linux-phy, Sebastian Reichel, linux-rockchip, Vinod Koul, Zhang Yubing, zyw, wulf, linux-arm-kernel [-- Attachment #1.1: Type: text/plain, Size: 11596 bytes --] Hi Peter, Thanks for working on this :-) On Wed Jan 15, 2025 at 2:26 AM CET, Peter Geis wrote: > The rk3328 has a combined usb2 and usb3 phy block for the usb3 dwc > interface. The implementation up until now has been bugged, with > multiple issues ranging from disconnect detection failures to low > performance. This driver fixes the majority of the original issues and > allows better performance for the rk3328 usb3 port. > > This driver sources data from multiple sources, including the rk3328 > trm, the rk3228h trm, emails from Rockchip, and both the 4.4 and 5.10 > downstream drivers. The current implementation allows for basic bring up > of the phy block and fixes the detection issues. Interrupts are enabled, > but currently only used for debugging. Features missing currently are > power management, OTG handling, board specific tuning, regulator control, > > Currently the only known bugs are a AX88179 usb3 gigabit adapter crashes > when operating at usb3 speeds and transmitting large amounts of data and > full disconnection detections may be slower than expected (~5-10 seconds). > > Signed-off-by: Peter Geis <pgwipeout@gmail.com> > --- > > drivers/phy/rockchip/Kconfig | 10 + > drivers/phy/rockchip/Makefile | 1 + > drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 869 ++++++++++++++++++ > 3 files changed, 880 insertions(+) > create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c > > diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig > index 2f7a05f21dc5..aac623e84f96 100644 > --- a/drivers/phy/rockchip/Kconfig > +++ b/drivers/phy/rockchip/Kconfig > @@ -48,6 +48,16 @@ config PHY_ROCKCHIP_INNO_USB2 > help > Support for Rockchip USB2.0 PHY with Innosilicon IP block. > > +config PHY_ROCKCHIP_INNO_USB3 > + tristate "Rockchip INNO USB3PHY Driver" > + depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF > + depends on COMMON_CLK > + depends on USB_SUPPORT > + select GENERIC_PHY > + select USB_COMMON > + help > + Support for Rockchip USB3.0 PHY with Innosilicon IP block. > + > config PHY_ROCKCHIP_INNO_CSIDPHY > tristate "Rockchip Innosilicon MIPI CSI PHY driver" > depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF > diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile > index 010a824e32ce..ec15dcf37fba 100644 > --- a/drivers/phy/rockchip/Makefile > +++ b/drivers/phy/rockchip/Makefile > @@ -6,6 +6,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY) += phy-rockchip-inno-csidphy.o > obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o > obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o > obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o > +obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB3) += phy-rockchip-inno-usb3.o > obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o > obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o > obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX) += phy-rockchip-samsung-hdptx.o > diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb3.c b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c > new file mode 100644 > index 000000000000..51b9f3b7fbfa > --- /dev/null > +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c > @@ -0,0 +1,869 @@ > +// SPDX-License-Identifier: GPL-2.0+ > + > +/* > + * phy-rockchip-inno-usb3.c - USB3 PHY based on Innosilicon IP as > + * implemented on Rockchip rk3328. Tuning data magic bits are taken as is > + * from the downstream driver. Downstream driver is located at: > + * https://github.com/rockchip-linux/kernel/blob/240a5660d7c23841ccf7b7cc489078bf521b9802/drivers/phy/rockchip/phy-rockchip-inno-usb3.c > + * > + * Author: Peter Geis <pgwipeout@gmail.com> > + * TODO: > + * - Find the rest of the register names / definitions. > + * - Implement pm functions. > + * - Implement board specific tuning from dts. > + * - Implement regulator control. > + */ > + > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/interrupt.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/of_address.h> > +#include <linux/of_device.h> > +#include <linux/of_irq.h> > +#include <linux/of_platform.h> > +#include <linux/phy/phy.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > +#include <linux/reset.h> > +#include <linux/mfd/syscon.h> > +#include <linux/usb/of.h> > + > +#define REG_WRITE_MASK GENMASK(31, 16) > +#define REG_WRITE_SHIFT 16 > +#define DISABLE_BITS 0x0 > + > +/* USB3PHY GRF Registers */ > +#define USB3PHY_WAKEUP_CON_REG 0x40 > +#define USB3PHY_WAKEUP_STAT_REG 0x44 > +#define USB3_LINESTATE_IRQ_EN BIT(0) > +#define USB3_RXDET_IRQ_EN BIT(1) > +#define USB3_BVALID_RISE_IRQ_EN BIT(2) > +#define USB3_BVALID_FALL_IRQ_EN BIT(3) > +#define USB3_BVALID_CLEAR_MASK GENMASK(3, 2) > +#define USB3_ID_RISE_IRQ_EN BIT(4) > +#define USB3_ID_FALL_IRQ_EN BIT(5) > +#define USB3_ID_CLEAR_MASK GENMASK(5, 4) > +#define USB3_RXDET_EN BIT(6) > + > +/* PIPE registers */ > +/* 0x08 for SSC, default 0x0e */ > +#define UNKNOWN_PIPE_REG_000 0x000 > +#define UNKNOWN_SSC_000_MASK GENMASK(2, 1) > +#define UNKNOWN_SSC_000_ENABLE (0x00 << 1) > + > +/* 0x83 for 24m, 0x01 for 25m, default 0x86 */ > +#define PIPE_REG_020 0x020 > +/* RX CDR multiplier high bits [7:6], as P, default 0x2, RX data rate = (2*refclk*P)/Q */ > +#define PIPE_RX_CDR_MULT_HIGH_MASK GENMASK(7, 6) > +/* TX PLL divider bits [4:0], as N, default 0x6, TX data rate = (2*refclk*M)/N */ > +#define PIPE_TX_PLL_DIV_MASK GENMASK(4, 0) > + > +/* 0x71 for 24m, 0x64 for 25m, default 0x71 */ > +#define PIPE_REG_028 0x028 > +/* RX CDR multiplier low bits [7:0], as P, default 0x71, RX data rate = (2*refclk*P)/Q */ > +#define PIPE_RX_CDR_MULT_LOW_MASK GENMASK(7, 0) > + > +/* 0x26 for 24m?, 0x21 for 25m, default 0x26 */ > +#define PIPE_REG_030 0x030 > +/* RX CDR divider bits [4:0], as Q, default 0x6, RX data rate = (2*refclk*P)/Q */ > +#define PIPE_RX_CDR_DIV_MASK GENMASK(4, 0) > + > +/* 1'b1 Disable bandgap power, default 0x00 */ > +#define PIPE_REG_044 0x044 > +#define BANDGAP_POWER_DISABLE BIT(4) > + > +/* 0xe0 for rx tune?, default 0xe1 */ > +#define PIPE_REG_060 0x060 > +#define PIPE_TX_DETECT_BYPASS_DEBUG BIT(4) /* enable to always force detection */ > +/* RX CTLE frequency bandwidth response tuning bits [1:0], default 0x1 */ > +#define PIPE_RX_CTLE_FREQ_BW_MASK GENMASK(1, 0) > +#define PIPE_RX_CTLE_FREQ_BW_TUNE 0x0 > + > +/* default 0x49 */ > +#define PIPE_REG_064 0x064 > +/* RX equalizer tail current control bits [6:4], default 0x4 */ > +#define PIPE_RX_EQ_TAIL_CURR_MASK GENMASK(6, 4) > + > +/* 0x08 for rx tune?, default 0x07 */ > +#define PIPE_REG_068 0x068 > +/* RX equalizer low frequency gain control bits [7:4], default 0x0 */ > +#define PIPE_RX_EQ_LOW_GAIN_MASK GENMASK(7, 4) > +#define PIPE_RX_EQ_LOW_GAIN_TUNE (0x1 << 4) > +/* RX CTLE gain tuning bits [3:0], higher = more gain default 0x7 */ > +#define PIPE_RX_CTLE_GAIN_MASK GENMASK(3, 0) > +#define PIPE_RX_CTLE_GAIN_TUNE 0x7 /* 0x5 lowest functional value, 0xf highest */ > + > +/* RX ODT manual resistance config, higher = less resistance, depends on REG_1C4 BIT(5) set */ > +#define PIPE_REG_06C 0x06c > +/* RX ODT manual resistance high bits [3:0], default 0x0 */ > +#define PIPE_RX_ODT_RES_HIGH_MASK GENMASK(3, 0) > +#define PIPE_RX_ODT_RES_HIGH_TUNE 0xf > + > +#define PIPE_REG_070 0x070 > +/* RX ODT manual resistance mid bits [7:0], default 0x03 */ > +#define PIPE_RX_ODT_RES_MID_MASK GENMASK(7, 0) > +#define PIPE_RX_ODT_RES_MID_TUNE 0xff > + > +#define PIPE_REG_074 0x074 > +/* RX ODT manual resistance low bits [7:0], default 0xff */ > +#define PIPE_RX_ODT_RES_LOW_MASK GENMASK(7, 0) > +#define PIPE_RX_ODT_RES_LOW_TUNE 0xff > + > +/* default 0x08 */ > +#define PIPE_REG_080 0x080 > +#define PIPE_TX_COMMON_MODE_DIS BIT(2) /* 1'b1 disable TX common type */ > + > +/* default 0x33 */ > +#define PIPE_REG_088 0x088 > +#define PIPE_TX_DRIVER_PREEMP_EN BIT(4) /* 1'b1 enable pre-emphasis */ > + > +/* default 0x18 */ > +#define PIPE_REG_0C0 0x0c0 > +#define PIPE_RX_CM_EN BIT(3) /* 1'b1 enable RX CM */ > +#define PIPE_TX_OBS_EN BIT(4) /* 1'b1 enable TX OBS */ > + > +/* 0x12 for rx tune?, default 0x14 */ > +#define PIPE_REG_0C8 0x0c8 > +/* RX CDR charge pump current bits [3:1], default 0x2 */ > +#define PIPE_RX_CDR_CHG_PUMP_MASK GENMASK(3, 1) > +#define PIPE_RX_CDR_CHG_PUMP_TUNE (0x2 << 1) > + > +/* 0x02 for 24m, 0x06 for 25m, default 0x06 */ > +#define UNKNOWN_PIPE_REG_108 0x108 > +#define UNKNOWN_REFCLK_108_24M 0x02 > + > +/* 0x80 for 24m, default 0x00 */ > +#define UNKNOWN_PIPE_REG_10C 0x10c > +#define UNKNOWN_REFCLK_10C_24M BIT(7) > + > +/* 0x01 for 24m, 0x00 for 25m, default 0x02 */ > +#define PIPE_REG_118 0x118 > +/* TX PLL multiplier high bits [3:0], as M, default 0x2, TX data rate = (2*refclk*M)/N */ > +#define PIPE_TX_PLL_MUL_HIGH_MASK GENMASK(3, 0) > + > +/* 0x38 for 24m, 0x64 for 25m, default 0x71 */ > +#define PIPE_REG_11C 0x11c > +/* TX PLL multiplier low bits [7:0], as M, default 0x71, TX data rate = (2*refclk*M)/N */ > +#define PIPE_TX_PLL_MUL_LOW_MASK GENMASK(7, 0) > + > +/* 0x0c for SSC, default 0x1c */ > +#define UNKNOWN_PIPE_REG_120 0x120 > +#define UNKNOWN_SSC_120_MASK BIT(4) > +#define UNKNOWN_SSC_120_ENABLE (0x0 << 4) > + > +/* default 0x40 */ > +#define PIPE_REG_12C 0x12c > +#define PIPE_TX_PLL_ALWAYS_ON BIT(0) /* disable for PLL control by pipe_pd */ > + > +/* 0x05 for rx tune, default 0x01 */ > +#define PIPE_REG_148 0x148 > +#define PIPE_RX_CHG_PUMP_DIV_2 BIT(2) /* RX CDR charge pump div/2, default 0 */ > + > +/* 0x70 for rx tune, default 0x72 */ > +#define PIPE_REG_150 0x150 > +#define PIPE_TX_BIAS_EN BIT(6) /* 1'b1 Enable TX Bias */ > +/* RX CDR phase tracking speed bits [3:0], default 0x2 */ > +#define PIPE_RX_CDR_SPEED_MASK GENMASK(3, 0) > +#define PIPE_RX_CDR_SPEED_TUNE 0x00 > + > +/* default 0xd4 */ > +#define PIPE_REG_160 > +/* RX common mode voltage strength bits [5:4], default 0x1 */ > +#define PIPE_RX_CDR_COM_VOLT_MASK GENMASK(5, 4) > +#define PIPE_RX_CDR_COM_VOLT_TUNE (0x1 << 4) > + > +/* default 0x00 */ > +#define PIPE_REG_180 0x180 > +/* TX driver bias reference voltage bits [3:2], in mv */ > +#define PIPE_TX_BIAS_REF_VOLT_MASK GENMASK(3, 2) > +#define PIPE_TX_BIAS_REF_VOLT_200 (0x0 << 2) > +#define PIPE_TX_BIAS_REF_VOLT_175 (0x1 << 2) > +#define PIPE_TX_BIAS_REF_VOLT_225 (0x2 << 2) /* downstream 5.10 driver setting */ > +#define PIPE_TX_BIAS_REF_VOLT_250 (0x3 << 2) > + > +/* default 0x01 */ > +#define PIPE_REG_1A8 0x1a8 > +#define PIPE_LDO_POWER_DIS BIT(4) /* 1'b1 Disable LDO Power */ > + > +/* default 0x07 */ > +#define PIPE_REG_1AC 0x1ac > +/* TX driver output common voltage bits [5:4], in mv */ > +#define PIPE_TX_COMMON_VOLT_MASK GENMASK(5, 4) > +#define PIPE_TX_COMMON_VOLT_800 (0x0 << 4) > +#define PIPE_TX_COMMON_VOLT_750 (0x1 << 4) > +#define PIPE_TX_COMMON_VOLT_950 (0x2 << 4) > +#define PIPE_TX_COMMON_VOLT_1100 (0x3 << 4) Just a question: In this list as well as the one above it (PIPE_TX_BIAS_xyz) I noticed the values are not sequential (800, 750, 950, 1100) and (200, 175, 225, 250). Is that correct? Cheers, Diederik [-- Attachment #1.2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] [-- Attachment #2: Type: text/plain, Size: 112 bytes --] -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 3/6] phy: rockchip: add driver for rk3328 usb3 phy 2025-01-16 15:26 ` Diederik de Haas @ 2025-01-16 15:57 ` Peter Geis 0 siblings, 0 replies; 24+ messages in thread From: Peter Geis @ 2025-01-16 15:57 UTC (permalink / raw) To: Diederik de Haas Cc: Heiko Stuebner, Kishon Vijay Abraham I, Algea Cao, linux-kernel, Philipp Zabel, Arnd Bergmann, frank.wang, william.wu, kever.yang, linux-phy, Sebastian Reichel, linux-rockchip, Vinod Koul, Zhang Yubing, zyw, wulf, linux-arm-kernel On Thu, Jan 16, 2025 at 10:27 AM Diederik de Haas <didi.debian@cknow.org> wrote: > > Hi Peter, > > Thanks for working on this :-) > > On Wed Jan 15, 2025 at 2:26 AM CET, Peter Geis wrote: > > The rk3328 has a combined usb2 and usb3 phy block for the usb3 dwc > > interface. The implementation up until now has been bugged, with > > multiple issues ranging from disconnect detection failures to low > > performance. This driver fixes the majority of the original issues and > > allows better performance for the rk3328 usb3 port. > > > > This driver sources data from multiple sources, including the rk3328 > > trm, the rk3228h trm, emails from Rockchip, and both the 4.4 and 5.10 > > downstream drivers. The current implementation allows for basic bring up > > of the phy block and fixes the detection issues. Interrupts are enabled, > > but currently only used for debugging. Features missing currently are > > power management, OTG handling, board specific tuning, regulator control, > > > > Currently the only known bugs are a AX88179 usb3 gigabit adapter crashes > > when operating at usb3 speeds and transmitting large amounts of data and > > full disconnection detections may be slower than expected (~5-10 seconds). > > > > Signed-off-by: Peter Geis <pgwipeout@gmail.com> > > --- > > > > drivers/phy/rockchip/Kconfig | 10 + > > drivers/phy/rockchip/Makefile | 1 + > > drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 869 ++++++++++++++++++ > > 3 files changed, 880 insertions(+) > > create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c > > > > diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig > > index 2f7a05f21dc5..aac623e84f96 100644 > > --- a/drivers/phy/rockchip/Kconfig > > +++ b/drivers/phy/rockchip/Kconfig > > @@ -48,6 +48,16 @@ config PHY_ROCKCHIP_INNO_USB2 > > help > > Support for Rockchip USB2.0 PHY with Innosilicon IP block. > > > > +config PHY_ROCKCHIP_INNO_USB3 > > + tristate "Rockchip INNO USB3PHY Driver" > > + depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF > > + depends on COMMON_CLK > > + depends on USB_SUPPORT > > + select GENERIC_PHY > > + select USB_COMMON > > + help > > + Support for Rockchip USB3.0 PHY with Innosilicon IP block. > > + > > config PHY_ROCKCHIP_INNO_CSIDPHY > > tristate "Rockchip Innosilicon MIPI CSI PHY driver" > > depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF > > diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile > > index 010a824e32ce..ec15dcf37fba 100644 > > --- a/drivers/phy/rockchip/Makefile > > +++ b/drivers/phy/rockchip/Makefile > > @@ -6,6 +6,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY) += phy-rockchip-inno-csidphy.o > > obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o > > obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o > > obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o > > +obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB3) += phy-rockchip-inno-usb3.o > > obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o > > obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o > > obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX) += phy-rockchip-samsung-hdptx.o > > diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb3.c b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c > > new file mode 100644 > > index 000000000000..51b9f3b7fbfa > > --- /dev/null > > +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c > > @@ -0,0 +1,869 @@ > > +// SPDX-License-Identifier: GPL-2.0+ > > + > > +/* > > + * phy-rockchip-inno-usb3.c - USB3 PHY based on Innosilicon IP as > > + * implemented on Rockchip rk3328. Tuning data magic bits are taken as is > > + * from the downstream driver. Downstream driver is located at: > > + * https://github.com/rockchip-linux/kernel/blob/240a5660d7c23841ccf7b7cc489078bf521b9802/drivers/phy/rockchip/phy-rockchip-inno-usb3.c > > + * > > + * Author: Peter Geis <pgwipeout@gmail.com> > > + * TODO: > > + * - Find the rest of the register names / definitions. > > + * - Implement pm functions. > > + * - Implement board specific tuning from dts. > > + * - Implement regulator control. > > + */ > > + > > +#include <linux/clk.h> > > +#include <linux/delay.h> > > +#include <linux/interrupt.h> > > +#include <linux/io.h> > > +#include <linux/kernel.h> > > +#include <linux/module.h> > > +#include <linux/of.h> > > +#include <linux/of_address.h> > > +#include <linux/of_device.h> > > +#include <linux/of_irq.h> > > +#include <linux/of_platform.h> > > +#include <linux/phy/phy.h> > > +#include <linux/platform_device.h> > > +#include <linux/regmap.h> > > +#include <linux/reset.h> > > +#include <linux/mfd/syscon.h> > > +#include <linux/usb/of.h> > > + > > +#define REG_WRITE_MASK GENMASK(31, 16) > > +#define REG_WRITE_SHIFT 16 > > +#define DISABLE_BITS 0x0 > > + > > +/* USB3PHY GRF Registers */ > > +#define USB3PHY_WAKEUP_CON_REG 0x40 > > +#define USB3PHY_WAKEUP_STAT_REG 0x44 > > +#define USB3_LINESTATE_IRQ_EN BIT(0) > > +#define USB3_RXDET_IRQ_EN BIT(1) > > +#define USB3_BVALID_RISE_IRQ_EN BIT(2) > > +#define USB3_BVALID_FALL_IRQ_EN BIT(3) > > +#define USB3_BVALID_CLEAR_MASK GENMASK(3, 2) > > +#define USB3_ID_RISE_IRQ_EN BIT(4) > > +#define USB3_ID_FALL_IRQ_EN BIT(5) > > +#define USB3_ID_CLEAR_MASK GENMASK(5, 4) > > +#define USB3_RXDET_EN BIT(6) > > + > > +/* PIPE registers */ > > +/* 0x08 for SSC, default 0x0e */ > > +#define UNKNOWN_PIPE_REG_000 0x000 > > +#define UNKNOWN_SSC_000_MASK GENMASK(2, 1) > > +#define UNKNOWN_SSC_000_ENABLE (0x00 << 1) > > + > > +/* 0x83 for 24m, 0x01 for 25m, default 0x86 */ > > +#define PIPE_REG_020 0x020 > > +/* RX CDR multiplier high bits [7:6], as P, default 0x2, RX data rate = (2*refclk*P)/Q */ > > +#define PIPE_RX_CDR_MULT_HIGH_MASK GENMASK(7, 6) > > +/* TX PLL divider bits [4:0], as N, default 0x6, TX data rate = (2*refclk*M)/N */ > > +#define PIPE_TX_PLL_DIV_MASK GENMASK(4, 0) > > + > > +/* 0x71 for 24m, 0x64 for 25m, default 0x71 */ > > +#define PIPE_REG_028 0x028 > > +/* RX CDR multiplier low bits [7:0], as P, default 0x71, RX data rate = (2*refclk*P)/Q */ > > +#define PIPE_RX_CDR_MULT_LOW_MASK GENMASK(7, 0) > > + > > +/* 0x26 for 24m?, 0x21 for 25m, default 0x26 */ > > +#define PIPE_REG_030 0x030 > > +/* RX CDR divider bits [4:0], as Q, default 0x6, RX data rate = (2*refclk*P)/Q */ > > +#define PIPE_RX_CDR_DIV_MASK GENMASK(4, 0) > > + > > +/* 1'b1 Disable bandgap power, default 0x00 */ > > +#define PIPE_REG_044 0x044 > > +#define BANDGAP_POWER_DISABLE BIT(4) > > + > > +/* 0xe0 for rx tune?, default 0xe1 */ > > +#define PIPE_REG_060 0x060 > > +#define PIPE_TX_DETECT_BYPASS_DEBUG BIT(4) /* enable to always force detection */ > > +/* RX CTLE frequency bandwidth response tuning bits [1:0], default 0x1 */ > > +#define PIPE_RX_CTLE_FREQ_BW_MASK GENMASK(1, 0) > > +#define PIPE_RX_CTLE_FREQ_BW_TUNE 0x0 > > + > > +/* default 0x49 */ > > +#define PIPE_REG_064 0x064 > > +/* RX equalizer tail current control bits [6:4], default 0x4 */ > > +#define PIPE_RX_EQ_TAIL_CURR_MASK GENMASK(6, 4) > > + > > +/* 0x08 for rx tune?, default 0x07 */ > > +#define PIPE_REG_068 0x068 > > +/* RX equalizer low frequency gain control bits [7:4], default 0x0 */ > > +#define PIPE_RX_EQ_LOW_GAIN_MASK GENMASK(7, 4) > > +#define PIPE_RX_EQ_LOW_GAIN_TUNE (0x1 << 4) > > +/* RX CTLE gain tuning bits [3:0], higher = more gain default 0x7 */ > > +#define PIPE_RX_CTLE_GAIN_MASK GENMASK(3, 0) > > +#define PIPE_RX_CTLE_GAIN_TUNE 0x7 /* 0x5 lowest functional value, 0xf highest */ > > + > > +/* RX ODT manual resistance config, higher = less resistance, depends on REG_1C4 BIT(5) set */ > > +#define PIPE_REG_06C 0x06c > > +/* RX ODT manual resistance high bits [3:0], default 0x0 */ > > +#define PIPE_RX_ODT_RES_HIGH_MASK GENMASK(3, 0) > > +#define PIPE_RX_ODT_RES_HIGH_TUNE 0xf > > + > > +#define PIPE_REG_070 0x070 > > +/* RX ODT manual resistance mid bits [7:0], default 0x03 */ > > +#define PIPE_RX_ODT_RES_MID_MASK GENMASK(7, 0) > > +#define PIPE_RX_ODT_RES_MID_TUNE 0xff > > + > > +#define PIPE_REG_074 0x074 > > +/* RX ODT manual resistance low bits [7:0], default 0xff */ > > +#define PIPE_RX_ODT_RES_LOW_MASK GENMASK(7, 0) > > +#define PIPE_RX_ODT_RES_LOW_TUNE 0xff > > + > > +/* default 0x08 */ > > +#define PIPE_REG_080 0x080 > > +#define PIPE_TX_COMMON_MODE_DIS BIT(2) /* 1'b1 disable TX common type */ > > + > > +/* default 0x33 */ > > +#define PIPE_REG_088 0x088 > > +#define PIPE_TX_DRIVER_PREEMP_EN BIT(4) /* 1'b1 enable pre-emphasis */ > > + > > +/* default 0x18 */ > > +#define PIPE_REG_0C0 0x0c0 > > +#define PIPE_RX_CM_EN BIT(3) /* 1'b1 enable RX CM */ > > +#define PIPE_TX_OBS_EN BIT(4) /* 1'b1 enable TX OBS */ > > + > > +/* 0x12 for rx tune?, default 0x14 */ > > +#define PIPE_REG_0C8 0x0c8 > > +/* RX CDR charge pump current bits [3:1], default 0x2 */ > > +#define PIPE_RX_CDR_CHG_PUMP_MASK GENMASK(3, 1) > > +#define PIPE_RX_CDR_CHG_PUMP_TUNE (0x2 << 1) > > + > > +/* 0x02 for 24m, 0x06 for 25m, default 0x06 */ > > +#define UNKNOWN_PIPE_REG_108 0x108 > > +#define UNKNOWN_REFCLK_108_24M 0x02 > > + > > +/* 0x80 for 24m, default 0x00 */ > > +#define UNKNOWN_PIPE_REG_10C 0x10c > > +#define UNKNOWN_REFCLK_10C_24M BIT(7) > > + > > +/* 0x01 for 24m, 0x00 for 25m, default 0x02 */ > > +#define PIPE_REG_118 0x118 > > +/* TX PLL multiplier high bits [3:0], as M, default 0x2, TX data rate = (2*refclk*M)/N */ > > +#define PIPE_TX_PLL_MUL_HIGH_MASK GENMASK(3, 0) > > + > > +/* 0x38 for 24m, 0x64 for 25m, default 0x71 */ > > +#define PIPE_REG_11C 0x11c > > +/* TX PLL multiplier low bits [7:0], as M, default 0x71, TX data rate = (2*refclk*M)/N */ > > +#define PIPE_TX_PLL_MUL_LOW_MASK GENMASK(7, 0) > > + > > +/* 0x0c for SSC, default 0x1c */ > > +#define UNKNOWN_PIPE_REG_120 0x120 > > +#define UNKNOWN_SSC_120_MASK BIT(4) > > +#define UNKNOWN_SSC_120_ENABLE (0x0 << 4) > > + > > +/* default 0x40 */ > > +#define PIPE_REG_12C 0x12c > > +#define PIPE_TX_PLL_ALWAYS_ON BIT(0) /* disable for PLL control by pipe_pd */ > > + > > +/* 0x05 for rx tune, default 0x01 */ > > +#define PIPE_REG_148 0x148 > > +#define PIPE_RX_CHG_PUMP_DIV_2 BIT(2) /* RX CDR charge pump div/2, default 0 */ > > + > > +/* 0x70 for rx tune, default 0x72 */ > > +#define PIPE_REG_150 0x150 > > +#define PIPE_TX_BIAS_EN BIT(6) /* 1'b1 Enable TX Bias */ > > +/* RX CDR phase tracking speed bits [3:0], default 0x2 */ > > +#define PIPE_RX_CDR_SPEED_MASK GENMASK(3, 0) > > +#define PIPE_RX_CDR_SPEED_TUNE 0x00 > > + > > +/* default 0xd4 */ > > +#define PIPE_REG_160 > > +/* RX common mode voltage strength bits [5:4], default 0x1 */ > > +#define PIPE_RX_CDR_COM_VOLT_MASK GENMASK(5, 4) > > +#define PIPE_RX_CDR_COM_VOLT_TUNE (0x1 << 4) > > + > > +/* default 0x00 */ > > +#define PIPE_REG_180 0x180 > > +/* TX driver bias reference voltage bits [3:2], in mv */ > > +#define PIPE_TX_BIAS_REF_VOLT_MASK GENMASK(3, 2) > > +#define PIPE_TX_BIAS_REF_VOLT_200 (0x0 << 2) > > +#define PIPE_TX_BIAS_REF_VOLT_175 (0x1 << 2) > > +#define PIPE_TX_BIAS_REF_VOLT_225 (0x2 << 2) /* downstream 5.10 driver setting */ > > +#define PIPE_TX_BIAS_REF_VOLT_250 (0x3 << 2) > > + > > +/* default 0x01 */ > > +#define PIPE_REG_1A8 0x1a8 > > +#define PIPE_LDO_POWER_DIS BIT(4) /* 1'b1 Disable LDO Power */ > > + > > +/* default 0x07 */ > > +#define PIPE_REG_1AC 0x1ac > > +/* TX driver output common voltage bits [5:4], in mv */ > > +#define PIPE_TX_COMMON_VOLT_MASK GENMASK(5, 4) > > +#define PIPE_TX_COMMON_VOLT_800 (0x0 << 4) > > +#define PIPE_TX_COMMON_VOLT_750 (0x1 << 4) > > +#define PIPE_TX_COMMON_VOLT_950 (0x2 << 4) > > +#define PIPE_TX_COMMON_VOLT_1100 (0x3 << 4) > > Just a question: > In this list as well as the one above it (PIPE_TX_BIAS_xyz) I noticed > the values are not sequential (800, 750, 950, 1100) and (200, 175, 225, > 250). Is that correct? According to the rk3228h trm, that's accurate. I'm hoping someone with access to Innosilicon's IP can confirm the registers I've found and provide insight to the rest. > > Cheers, > Diederik -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 2025-01-15 1:26 [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 Peter Geis 2025-01-15 1:26 ` [RFC PATCH v1 2/6] dt-bindings: phy: rockchip: add rk3328 usb3 phy Peter Geis 2025-01-15 1:26 ` [RFC PATCH v1 3/6] phy: rockchip: add driver for " Peter Geis @ 2025-01-15 11:22 ` Piotr Oniszczuk 2025-01-15 12:25 ` Peter Geis 2025-01-18 9:08 ` Krzysztof Kozlowski 2025-02-26 19:49 ` (subset) " Heiko Stuebner 4 siblings, 1 reply; 24+ messages in thread From: Piotr Oniszczuk @ 2025-01-15 11:22 UTC (permalink / raw) To: Peter Geis Cc: Heiko Stuebner, Algea Cao, Michael Turquette, kever.yang, linux-phy, wulf, zyw, Dragan Simic, Kishon Vijay Abraham I, Rob Herring, Sebastian Reichel, linux-clk, linux-rockchip, devicetree, Conor Dooley, Philipp Zabel, Arnd Bergmann, Jonas Karlman, frank.wang, Elaine Zhang, Alex Bee, william.wu, Zhang Yubing, Johan Jonker, linux-arm-kernel, Trevor Woerner, Stephen Boyd, linux-kernel, Vinod Koul, FUKAUMI Naoki, Diederik de Haas, Krzysztof Kozlowski > Wiadomość napisana przez Peter Geis <pgwipeout@gmail.com> w dniu 15 sty 2025, o godz. 02:26: > > > This is my newly reworked phy driver for the rk3328 usb3 phy. It is > based loosely on my original version, but as of now almost nothing of > the original driver remains. The main fix here is the discovery of > BIT(6) in the interrupt enable grf register fixes the usb3 disconnection > detection (mostly). On occasion an unpopulated usb3 hub will take > several seconds to disconnect. However this means all of the hack around > work to reset the usb core manually is no longer required. > > I did my best to document all registers I could find. A lot was taken > from emails between myself and Rockchip's engineers, much thanks to > William Wu <wulf@rock-chips.com> for their assistance here. The rest of > the config bits were taken from the rk3328 and rk3228h TRMs and the > downstream driver. Everything that I couldn't find a definition for is > prefixed UNK_ or UNKNOWN_. There's a lot of obviously used configuration > registers with the pipe interface that are also undocumented. > > The only major bug I have so far is my AX88179 usb3 gigabit ethernet > adapter (Pluggable brand) crashes out when large amounts of data are > transmitted. I suspect this is related to the RX and TX tuning, as > leaving it at defaults makes things worse. As I am not a USB3 engineer > and I do not have the specialized knowledge and hardware to determine > what is going wrong, I am hoping the mailing list will have an answer > here. > > Please test and review. > > Very Respectfully, > Peter Geis > > > > Peter Geis (6): > clk: rockchip: fix wrong clk_ref_usb3otg parent for rk3328 > dt-bindings: phy: rockchip: add rk3328 usb3 phy > phy: rockchip: add driver for rk3328 usb3 phy > arm64: dts: rockchip: add rk3328 usb3 phy node > arm64: dts: rockchip: enable the usb3 phy on rk3328-roc boards > arm64: dts: rockchip: enable the usb3 phy on remaining rk3328 boards > > .../bindings/phy/rockchip,inno-usb3phy.yaml | 166 ++++ > .../boot/dts/rockchip/rk3328-nanopi-r2.dtsi | 12 + > .../dts/rockchip/rk3328-orangepi-r1-plus.dtsi | 12 + > arch/arm64/boot/dts/rockchip/rk3328-roc.dtsi | 12 + > .../boot/dts/rockchip/rk3328-rock-pi-e.dts | 12 + > .../arm64/boot/dts/rockchip/rk3328-rock64.dts | 12 + > arch/arm64/boot/dts/rockchip/rk3328.dtsi | 39 + > drivers/clk/rockchip/clk-rk3328.c | 2 +- > drivers/phy/rockchip/Kconfig | 10 + > drivers/phy/rockchip/Makefile | 1 + > drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 869 ++++++++++++++++++ > 11 files changed, 1146 insertions(+), 1 deletion(-) > create mode 100644 Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml > create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c > > -- > 2.39.5 > > Peter, I applied this as test run on 6.12.9 and dmesg says: [ 16.368514] rockchip-usb3-phy ff460000.usb3-phy: could not get usb3phy ref clock [ 16.368534] rockchip-usb3-phy ff460000.usb3-phy: parse dt failed -2 [ 16.368543] rockchip-usb3-phy ff460000.usb3-phy: probe with driver rockchip-usb3-phy failed with error -2 This is on beelink a1 tvbox. Do I miss something? -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 2025-01-15 11:22 ` [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 Piotr Oniszczuk @ 2025-01-15 12:25 ` Peter Geis 2025-01-15 12:35 ` Piotr Oniszczuk 0 siblings, 1 reply; 24+ messages in thread From: Peter Geis @ 2025-01-15 12:25 UTC (permalink / raw) To: Piotr Oniszczuk Cc: Heiko Stuebner, Algea Cao, Michael Turquette, kever.yang, linux-phy, wulf, zyw, Dragan Simic, Kishon Vijay Abraham I, Rob Herring, Sebastian Reichel, linux-clk, linux-rockchip, devicetree, Conor Dooley, Philipp Zabel, Arnd Bergmann, Jonas Karlman, frank.wang, Elaine Zhang, Alex Bee, william.wu, Zhang Yubing, Johan Jonker, linux-arm-kernel, Trevor Woerner, Stephen Boyd, linux-kernel, Vinod Koul, FUKAUMI Naoki, Diederik de Haas, Krzysztof Kozlowski On Wed, Jan 15, 2025 at 6:22 AM Piotr Oniszczuk <piotr.oniszczuk@gmail.com> wrote: > > > > > Wiadomość napisana przez Peter Geis <pgwipeout@gmail.com> w dniu 15 sty 2025, o godz. 02:26: > > > > > > This is my newly reworked phy driver for the rk3328 usb3 phy. It is > > based loosely on my original version, but as of now almost nothing of > > the original driver remains. The main fix here is the discovery of > > BIT(6) in the interrupt enable grf register fixes the usb3 disconnection > > detection (mostly). On occasion an unpopulated usb3 hub will take > > several seconds to disconnect. However this means all of the hack around > > work to reset the usb core manually is no longer required. > > > > I did my best to document all registers I could find. A lot was taken > > from emails between myself and Rockchip's engineers, much thanks to > > William Wu <wulf@rock-chips.com> for their assistance here. The rest of > > the config bits were taken from the rk3328 and rk3228h TRMs and the > > downstream driver. Everything that I couldn't find a definition for is > > prefixed UNK_ or UNKNOWN_. There's a lot of obviously used configuration > > registers with the pipe interface that are also undocumented. > > > > The only major bug I have so far is my AX88179 usb3 gigabit ethernet > > adapter (Pluggable brand) crashes out when large amounts of data are > > transmitted. I suspect this is related to the RX and TX tuning, as > > leaving it at defaults makes things worse. As I am not a USB3 engineer > > and I do not have the specialized knowledge and hardware to determine > > what is going wrong, I am hoping the mailing list will have an answer > > here. > > > > Please test and review. > > > > Very Respectfully, > > Peter Geis > > > > > > > > Peter Geis (6): > > clk: rockchip: fix wrong clk_ref_usb3otg parent for rk3328 > > dt-bindings: phy: rockchip: add rk3328 usb3 phy > > phy: rockchip: add driver for rk3328 usb3 phy > > arm64: dts: rockchip: add rk3328 usb3 phy node > > arm64: dts: rockchip: enable the usb3 phy on rk3328-roc boards > > arm64: dts: rockchip: enable the usb3 phy on remaining rk3328 boards > > > > .../bindings/phy/rockchip,inno-usb3phy.yaml | 166 ++++ > > .../boot/dts/rockchip/rk3328-nanopi-r2.dtsi | 12 + > > .../dts/rockchip/rk3328-orangepi-r1-plus.dtsi | 12 + > > arch/arm64/boot/dts/rockchip/rk3328-roc.dtsi | 12 + > > .../boot/dts/rockchip/rk3328-rock-pi-e.dts | 12 + > > .../arm64/boot/dts/rockchip/rk3328-rock64.dts | 12 + > > arch/arm64/boot/dts/rockchip/rk3328.dtsi | 39 + > > drivers/clk/rockchip/clk-rk3328.c | 2 +- > > drivers/phy/rockchip/Kconfig | 10 + > > drivers/phy/rockchip/Makefile | 1 + > > drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 869 ++++++++++++++++++ > > 11 files changed, 1146 insertions(+), 1 deletion(-) > > create mode 100644 Documentation/devicetree/bindings/phy/rockchip,inno-usb3phy.yaml > > create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c > > > > -- > > 2.39.5 > > > > > > Peter, > I applied this as test run on 6.12.9 and dmesg says: > > [ 16.368514] rockchip-usb3-phy ff460000.usb3-phy: could not get usb3phy ref clock > [ 16.368534] rockchip-usb3-phy ff460000.usb3-phy: parse dt failed -2 > [ 16.368543] rockchip-usb3-phy ff460000.usb3-phy: probe with driver rockchip-usb3-phy failed with error -2 > > This is on beelink a1 tvbox. > > Do I miss something? > Good Morning, That is an interesting failure. The simplest answer is the `clock-names = "refclk-usb3otg", "usb3phy-otg", "usb3phy-pipe";` line was corrupted. Please check that when applied it matches the patch exactly. If you are still having problems, you can send me the compiled DTB and I'll take a look. Very Respectfully, Peter Geis > -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 2025-01-15 12:25 ` Peter Geis @ 2025-01-15 12:35 ` Piotr Oniszczuk 2025-01-15 13:15 ` Peter Geis 0 siblings, 1 reply; 24+ messages in thread From: Piotr Oniszczuk @ 2025-01-15 12:35 UTC (permalink / raw) To: Peter Geis Cc: Heiko Stuebner, Algea Cao, Michael Turquette, kever.yang, linux-phy, wulf, zyw, Dragan Simic, Kishon Vijay Abraham I, Rob Herring, Sebastian Reichel, linux-clk, linux-rockchip, devicetree, Conor Dooley, Philipp Zabel, Arnd Bergmann, Jonas Karlman, frank.wang, Elaine Zhang, Alex Bee, william.wu, Zhang Yubing, Johan Jonker, linux-arm-kernel, Trevor Woerner, Stephen Boyd, linux-kernel, Vinod Koul, FUKAUMI Naoki, Diederik de Haas, Krzysztof Kozlowski [-- Attachment #1: Type: text/plain, Size: 724 bytes --] > Wiadomość napisana przez Peter Geis <pgwipeout@gmail.com> w dniu 15 sty 2025, o godz. 13:25: > >> >> >> Do I miss something? >> > > Good Morning, > > That is an interesting failure. The simplest answer is the > `clock-names = "refclk-usb3otg", "usb3phy-otg", "usb3phy-pipe";` line > was corrupted. Please check that when applied it matches the patch > exactly. If you are still having problems, you can send me the > compiled DTB and I'll take a look. > oh - this check i done as first thing to look on :-) pls find: dtsi: https://gist.github.com/warpme/6af9e2778fb06bfb47b64f98fe79d678 and dt: https://gist.github.com/warpme/79340c54c87b2b1e52f2a146461d8c9f and compiled .dtb [-- Attachment #2: rk3328-a1.dtb --] [-- Type: application/octet-stream, Size: 40319 bytes --] [-- Attachment #3: Type: text/plain, Size: 112 bytes --] -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 2025-01-15 12:35 ` Piotr Oniszczuk @ 2025-01-15 13:15 ` Peter Geis 2025-01-15 13:25 ` Piotr Oniszczuk 0 siblings, 1 reply; 24+ messages in thread From: Peter Geis @ 2025-01-15 13:15 UTC (permalink / raw) To: Piotr Oniszczuk Cc: Heiko Stuebner, Algea Cao, Michael Turquette, kever.yang, linux-phy, wulf, zyw, Dragan Simic, Kishon Vijay Abraham I, Rob Herring, Sebastian Reichel, linux-clk, linux-rockchip, devicetree, Conor Dooley, Philipp Zabel, Arnd Bergmann, Jonas Karlman, frank.wang, Elaine Zhang, Alex Bee, william.wu, Zhang Yubing, Johan Jonker, linux-arm-kernel, Trevor Woerner, Stephen Boyd, linux-kernel, Vinod Koul, FUKAUMI Naoki, Diederik de Haas, Krzysztof Kozlowski On Wed, Jan 15, 2025 at 7:35 AM Piotr Oniszczuk <piotr.oniszczuk@gmail.com> wrote: > > > > > Wiadomość napisana przez Peter Geis <pgwipeout@gmail.com> w dniu 15 sty 2025, o godz. 13:25: > > > >> > >> > >> Do I miss something? > >> > > > > Good Morning, > > > > That is an interesting failure. The simplest answer is the > > `clock-names = "refclk-usb3otg", "usb3phy-otg", "usb3phy-pipe";` line > > was corrupted. Please check that when applied it matches the patch > > exactly. If you are still having problems, you can send me the > > compiled DTB and I'll take a look. > > > > oh - this check i done as first thing to look on :-) > > pls find: > > dtsi: https://gist.github.com/warpme/6af9e2778fb06bfb47b64f98fe79d678 > and dt: https://gist.github.com/warpme/79340c54c87b2b1e52f2a146461d8c9f > > > and compiled .dtb It all looks good. Do you have any CRU errors in the boot log? Can you check for the presence of clk_ref_usb3otg in /sys/kernel/debug/clk/clk_summary? > -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 2025-01-15 13:15 ` Peter Geis @ 2025-01-15 13:25 ` Piotr Oniszczuk 2025-01-16 14:02 ` Peter Geis 0 siblings, 1 reply; 24+ messages in thread From: Piotr Oniszczuk @ 2025-01-15 13:25 UTC (permalink / raw) To: Peter Geis Cc: Heiko Stuebner, Algea Cao, Michael Turquette, kever.yang, linux-phy, wulf, zyw, Dragan Simic, Kishon Vijay Abraham I, Rob Herring, Sebastian Reichel, linux-clk, linux-rockchip, devicetree, Conor Dooley, Philipp Zabel, Arnd Bergmann, Jonas Karlman, frank.wang, Elaine Zhang, Alex Bee, william.wu, Zhang Yubing, Johan Jonker, linux-arm-kernel, Trevor Woerner, Stephen Boyd, linux-kernel, Vinod Koul, FUKAUMI Naoki, Diederik de Haas, Krzysztof Kozlowski > Wiadomość napisana przez Peter Geis <pgwipeout@gmail.com> w dniu 15 sty 2025, o godz. 14:15: > >> >> >> pls find: >> >> dtsi: https://gist.github.com/warpme/6af9e2778fb06bfb47b64f98fe79d678 >> and dt: https://gist.github.com/warpme/79340c54c87b2b1e52f2a146461d8c9f >> >> >> and compiled .dtb > > It all looks good. > Do you have any CRU errors in the boot log? > Can you check for the presence of clk_ref_usb3otg in > /sys/kernel/debug/clk/clk_summary? > >> cru errors in dmesg - seems no any. here is my dmesg: https://gist.github.com/warpme/ecf38482cc88fb68471355d011ecf974 For clk_ref_usb3otg i’m getting following: root@myth-frontend-e67a8de4c815:~ # cat /sys/kernel/debug/clk/clk_summary | grep usb3 clk_usb3otg_suspend 0 0 0 30770 0 0 50000 N deviceless no_connection_id clk_usb3otg_ref 0 0 0 24000000 0 0 50000 N deviceless no_connection_id clk_ref_usb3otg 0 0 0 24000000 0 0 50000 Y deviceless no_connection_id pclk_usb3_grf 0 0 0 75000000 0 0 50000 Y deviceless no_connection_id pclk_usb3phy_pipe 0 0 0 75000000 0 0 50000 N deviceless no_connection_id pclk_usb3phy_otg 0 0 0 75000000 0 0 50000 N deviceless no_connection_id clk_ref_usb3otg_src 0 0 0 37500000 0 0 50000 N deviceless no_connection_id aclk_usb3otg 0 0 0 150000000 0 0 50000 N deviceless no_connection_id -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 2025-01-15 13:25 ` Piotr Oniszczuk @ 2025-01-16 14:02 ` Peter Geis 2025-01-16 14:35 ` Piotr Oniszczuk 0 siblings, 1 reply; 24+ messages in thread From: Peter Geis @ 2025-01-16 14:02 UTC (permalink / raw) To: Piotr Oniszczuk Cc: Heiko Stuebner, Algea Cao, Michael Turquette, kever.yang, linux-phy, wulf, zyw, Dragan Simic, Kishon Vijay Abraham I, Rob Herring, Sebastian Reichel, linux-clk, linux-rockchip, devicetree, Conor Dooley, Philipp Zabel, Arnd Bergmann, Jonas Karlman, frank.wang, Elaine Zhang, Alex Bee, william.wu, Zhang Yubing, Johan Jonker, linux-arm-kernel, Trevor Woerner, Stephen Boyd, linux-kernel, Vinod Koul, FUKAUMI Naoki, Diederik de Haas, Krzysztof Kozlowski On Wed, Jan 15, 2025 at 8:25 AM Piotr Oniszczuk <piotr.oniszczuk@gmail.com> wrote: > > > > > Wiadomość napisana przez Peter Geis <pgwipeout@gmail.com> w dniu 15 sty 2025, o godz. 14:15: > > > >> > >> > >> pls find: > >> > >> dtsi: https://gist.github.com/warpme/6af9e2778fb06bfb47b64f98fe79d678 > >> and dt: https://gist.github.com/warpme/79340c54c87b2b1e52f2a146461d8c9f > >> > >> > >> and compiled .dtb > > > > It all looks good. > > Do you have any CRU errors in the boot log? > > Can you check for the presence of clk_ref_usb3otg in > > /sys/kernel/debug/clk/clk_summary? > > > >> > > cru errors in dmesg - seems no any. > here is my dmesg: https://gist.github.com/warpme/ecf38482cc88fb68471355d011ecf974 > > For clk_ref_usb3otg i’m getting following: > > root@myth-frontend-e67a8de4c815:~ # cat /sys/kernel/debug/clk/clk_summary | grep usb3 > clk_usb3otg_suspend 0 0 0 30770 0 0 50000 N deviceless no_connection_id > clk_usb3otg_ref 0 0 0 24000000 0 0 50000 N deviceless no_connection_id > clk_ref_usb3otg 0 0 0 24000000 0 0 50000 Y deviceless no_connection_id > pclk_usb3_grf 0 0 0 75000000 0 0 50000 Y deviceless no_connection_id > pclk_usb3phy_pipe 0 0 0 75000000 0 0 50000 N deviceless no_connection_id > pclk_usb3phy_otg 0 0 0 75000000 0 0 50000 N deviceless no_connection_id > clk_ref_usb3otg_src 0 0 0 37500000 0 0 50000 N deviceless no_connection_id > aclk_usb3otg 0 0 0 150000000 0 0 50000 N deviceless no_connection_id > I'm at a loss here, I applied the patches to a clean 6.9 tree and even built it as a module (thank you for the sentinel). I have no issues. I'm wondering if it's your .config or something about your bootloader. > -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 2025-01-16 14:02 ` Peter Geis @ 2025-01-16 14:35 ` Piotr Oniszczuk 2025-01-16 16:00 ` Peter Geis 0 siblings, 1 reply; 24+ messages in thread From: Piotr Oniszczuk @ 2025-01-16 14:35 UTC (permalink / raw) To: Peter Geis Cc: Heiko Stuebner, Algea Cao, Michael Turquette, kever.yang, linux-phy, wulf, zyw, Dragan Simic, Kishon Vijay Abraham I, Rob Herring, Sebastian Reichel, linux-clk, linux-rockchip, devicetree, Conor Dooley, Philipp Zabel, Arnd Bergmann, Jonas Karlman, frank.wang, Elaine Zhang, Alex Bee, william.wu, Zhang Yubing, Johan Jonker, linux-arm-kernel, Trevor Woerner, Stephen Boyd, linux-kernel, Vinod Koul, FUKAUMI Naoki, Diederik de Haas, Krzysztof Kozlowski > Wiadomość napisana przez Peter Geis <pgwipeout@gmail.com> w dniu 16 sty 2025, o godz. 15:02: > >> > > I'm at a loss here, I applied the patches to a clean 6.9 tree and even >> oh maybe issue is in kernel age? 6.9 seems a bit quite old. i’m on 6.12.9…. -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 2025-01-16 14:35 ` Piotr Oniszczuk @ 2025-01-16 16:00 ` Peter Geis 0 siblings, 0 replies; 24+ messages in thread From: Peter Geis @ 2025-01-16 16:00 UTC (permalink / raw) To: Piotr Oniszczuk Cc: Heiko Stuebner, Algea Cao, Michael Turquette, kever.yang, linux-phy, wulf, zyw, Dragan Simic, Kishon Vijay Abraham I, Rob Herring, Sebastian Reichel, linux-clk, linux-rockchip, devicetree, Conor Dooley, Philipp Zabel, Arnd Bergmann, Jonas Karlman, frank.wang, Elaine Zhang, Alex Bee, william.wu, Zhang Yubing, Johan Jonker, linux-arm-kernel, Trevor Woerner, Stephen Boyd, linux-kernel, Vinod Koul, FUKAUMI Naoki, Diederik de Haas, Krzysztof Kozlowski On Thu, Jan 16, 2025 at 9:35 AM Piotr Oniszczuk <piotr.oniszczuk@gmail.com> wrote: > > > > > Wiadomość napisana przez Peter Geis <pgwipeout@gmail.com> w dniu 16 sty 2025, o godz. 15:02: > > > >> > > > > I'm at a loss here, I applied the patches to a clean 6.9 tree and even > >> > > oh maybe issue is in kernel age? > 6.9 seems a bit quite old. > i’m on 6.12.9…. The patches were prepared and tested against 6.13-rc1, but nothing has changed significantly in the kernel in regards to rk3328 clock handling in several years. I jumped back to 6.9 due to dyslexia. -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 2025-01-15 1:26 [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 Peter Geis ` (2 preceding siblings ...) 2025-01-15 11:22 ` [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 Piotr Oniszczuk @ 2025-01-18 9:08 ` Krzysztof Kozlowski 2025-01-18 14:35 ` Peter Geis 2025-02-26 19:49 ` (subset) " Heiko Stuebner 4 siblings, 1 reply; 24+ messages in thread From: Krzysztof Kozlowski @ 2025-01-18 9:08 UTC (permalink / raw) To: Peter Geis, Heiko Stuebner Cc: zyw, kever.yang, frank.wang, william.wu, wulf, linux-rockchip, Alex Bee, Algea Cao, Arnd Bergmann, Conor Dooley, Cristian Ciocaltea, Diederik de Haas, Dragan Simic, Elaine Zhang, FUKAUMI Naoki, Johan Jonker, Jonas Karlman, Kishon Vijay Abraham I, Krzysztof Kozlowski, Michael Turquette, Philipp Zabel, Rob Herring, Sebastian Reichel, Stephen Boyd, Trevor Woerner, Vinod Koul, Zhang Yubing, devicetree, linux-arm-kernel, linux-clk, linux-kernel, linux-phy On 15/01/2025 02:26, Peter Geis wrote: > > This is my newly reworked phy driver for the rk3328 usb3 phy. It is > based loosely on my original version, but as of now almost nothing of > the original driver remains. The main fix here is the discovery of > BIT(6) in the interrupt enable grf register fixes the usb3 disconnection > detection (mostly). On occasion an unpopulated usb3 hub will take > several seconds to disconnect. However this means all of the hack around > work to reset the usb core manually is no longer required. > BTW, RFC for some maintainers means "do not review, work-in-progress". For some means "review, but low priority" or "review, but for sure I have bugs here". I usually review and then someone responds: "it is not for review, it is just RFC", so to avoid my wasted time please always mention in cover letter why this is RFC. What do you expect here or why this is not ready for review as normal patch. Best regards, Krzysztof -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 2025-01-18 9:08 ` Krzysztof Kozlowski @ 2025-01-18 14:35 ` Peter Geis 0 siblings, 0 replies; 24+ messages in thread From: Peter Geis @ 2025-01-18 14:35 UTC (permalink / raw) To: Krzysztof Kozlowski Cc: Heiko Stuebner, zyw, kever.yang, frank.wang, william.wu, wulf, linux-rockchip, Alex Bee, Algea Cao, Arnd Bergmann, Conor Dooley, Cristian Ciocaltea, Diederik de Haas, Dragan Simic, Elaine Zhang, FUKAUMI Naoki, Johan Jonker, Jonas Karlman, Kishon Vijay Abraham I, Krzysztof Kozlowski, Michael Turquette, Philipp Zabel, Rob Herring, Sebastian Reichel, Stephen Boyd, Trevor Woerner, Vinod Koul, Zhang Yubing, devicetree, linux-arm-kernel, linux-clk, linux-kernel, linux-phy On Sat, Jan 18, 2025 at 4:08 AM Krzysztof Kozlowski <krzk@kernel.org> wrote: > > On 15/01/2025 02:26, Peter Geis wrote: > > > > This is my newly reworked phy driver for the rk3328 usb3 phy. It is > > based loosely on my original version, but as of now almost nothing of > > the original driver remains. The main fix here is the discovery of > > BIT(6) in the interrupt enable grf register fixes the usb3 disconnection > > detection (mostly). On occasion an unpopulated usb3 hub will take > > several seconds to disconnect. However this means all of the hack around > > work to reset the usb core manually is no longer required. > > > BTW, RFC for some maintainers means "do not review, work-in-progress". > For some means "review, but low priority" or "review, but for sure I > have bugs here". I usually review and then someone responds: "it is not > for review, it is just RFC", so to avoid my wasted time please always > mention in cover letter why this is RFC. What do you expect here or why > this is not ready for review as normal patch. Thank you, that makes sense. I marked it as RFC as I'm aware it isn't a perfect solution and there's a lot of undefined values. What I was looking for here was: - Review for code quality, so if I'm completely off track I can get to work fixing it. (Thank you so far) - Sanity testing from others struggling with the issues it fixes. - Feedback from USB engineers about the issues remaining. - Hopefully someone with access to the IP can provide insight into the magic registers. > > Best regards, > Krzysztof -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: (subset) [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 2025-01-15 1:26 [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 Peter Geis ` (3 preceding siblings ...) 2025-01-18 9:08 ` Krzysztof Kozlowski @ 2025-02-26 19:49 ` Heiko Stuebner 4 siblings, 0 replies; 24+ messages in thread From: Heiko Stuebner @ 2025-02-26 19:49 UTC (permalink / raw) To: Peter Geis Cc: Heiko Stuebner, zyw, kever.yang, frank.wang, william.wu, wulf, linux-rockchip, Alex Bee, Algea Cao, Arnd Bergmann, Conor Dooley, Cristian Ciocaltea, Diederik de Haas, Dragan Simic, Elaine Zhang, FUKAUMI Naoki, Johan Jonker, Jonas Karlman, Kishon Vijay Abraham I, Krzysztof Kozlowski, Michael Turquette, Philipp Zabel, Rob Herring, Sebastian Reichel, Stephen Boyd, Trevor Woerner, Vinod Koul, Zhang Yubing, devicetree, linux-arm-kernel, linux-clk, linux-kernel, linux-phy On Wed, 15 Jan 2025 01:26:21 +0000, Peter Geis wrote: > This is my newly reworked phy driver for the rk3328 usb3 phy. It is > based loosely on my original version, but as of now almost nothing of > the original driver remains. The main fix here is the discovery of > BIT(6) in the interrupt enable grf register fixes the usb3 disconnection > detection (mostly). On occasion an unpopulated usb3 hub will take > several seconds to disconnect. However this means all of the hack around > work to reset the usb core manually is no longer required. > > [...] Applied, thanks! [1/6] clk: rockchip: fix wrong clk_ref_usb3otg parent for rk3328 commit: a9e60f1ffe1ca57d6af6a2573e2f950e76efbf5b Best regards, -- Heiko Stuebner <heiko@sntech.de> -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2025-02-26 19:54 UTC | newest] Thread overview: 24+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-01-15 1:26 [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 Peter Geis 2025-01-15 1:26 ` [RFC PATCH v1 2/6] dt-bindings: phy: rockchip: add rk3328 usb3 phy Peter Geis 2025-01-16 13:08 ` Krzysztof Kozlowski 2025-01-16 13:32 ` Peter Geis 2025-01-16 13:59 ` Peter Geis 2025-01-18 9:06 ` Krzysztof Kozlowski 2025-01-15 1:26 ` [RFC PATCH v1 3/6] phy: rockchip: add driver for " Peter Geis 2025-01-15 11:24 ` Piotr Oniszczuk 2025-01-16 14:09 ` Peter Geis 2025-01-16 12:59 ` Krzysztof Kozlowski 2025-01-16 13:14 ` Peter Geis 2025-01-16 15:26 ` Diederik de Haas 2025-01-16 15:57 ` Peter Geis 2025-01-15 11:22 ` [RFC PATCH v1 0/6] rockchip: add a functional usb3 phy driver for rk3328 Piotr Oniszczuk 2025-01-15 12:25 ` Peter Geis 2025-01-15 12:35 ` Piotr Oniszczuk 2025-01-15 13:15 ` Peter Geis 2025-01-15 13:25 ` Piotr Oniszczuk 2025-01-16 14:02 ` Peter Geis 2025-01-16 14:35 ` Piotr Oniszczuk 2025-01-16 16:00 ` Peter Geis 2025-01-18 9:08 ` Krzysztof Kozlowski 2025-01-18 14:35 ` Peter Geis 2025-02-26 19:49 ` (subset) " Heiko Stuebner
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox