mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-15 16:53:54 +08:00
phy-for-6.2
- New support: - Allwinner H616 USB PHY and A100 DPHY support - TI J721s2, J784s4 and J721e support - Freescale i.MX8MP PCIe PHY support - New driver for Renesas Ethernet SERDES supporting R-Car S4-8 - Qualcomm SM8450 PCIe1 PHY support in EP mode - Updates: - again a big pile of updates on qcom-qmp-* drivers following the driver split and reorganization merged earlier - Phy order of API calls documentation update -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmOfIbYACgkQfBQHDyUj g0fSbw//Rgfk+owGLWyJ3PxRXiDhZaJdBUQNuZEe46TjGKKHvWLJ4+ig6vrXlPgr 8mVte7jEMZubO7YE/1Vifv9xiFmjo+5R4//WlfkIwy/0SFR8+N+DPQiGU7i7ecov uzkFN26qsi4aQrKmxyadGJQzHipaLViBkr6fqfuFcmyDiFII0FoVa/mV7ZQlFtl3 cDv3leFnp3HQ9mr/mKhOSmbyWCEQHqQvjDwB50R915WfH9PLV2jYddfO4Cbwpr4r 7m7wX2EiFlQ1o2gwcFQdLiDkA8YL9Kw3wOChpbcCu4gOapJ+GWqCk0AqS9m8MMWF HnyAyHw3NxDagwV6sN19Xxa7XgkPJZPn6/92BfGYeD6H5gxmYwdROeU2/x6Qt1+z scTl1m6z8X9WWwjnWK1cqVqBPUXoJJ2smym6VBHh3f4AJAVmwZy+yyk1Oar5qa2M yDWV7nIRJQmXnuQ+XsG5rmXmmMwOuBgng4NsNX9PjhdVy6/1FUOJuMCr8ldPLAkG Lpg+GN8w6tn2G0bxrHzWeAOytxjK5XuXch99BHmXDl+NgIpp/6DuyddXmvG4nrvk R6eDv86UOQgGP2h7SujUm9f6RIWb3nJrYN27r+IHK/z5LjSMfylSSu13GvMjZkt4 Et5Q4Wk9MomHFQkhiTGTd9WlSvb497RgzKhBhMg/lJoSyTi9Eew= =4HRP -----END PGP SIGNATURE----- Merge tag 'phy-for-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy Pull phy updates from Vinod Koul: "This tme we have again a big pile of qcom-qmp-* changes, one new driver and bunch of new hardware support. New hardware support: - Allwinner H616 USB PHY and A100 DPHY support - TI J721s2, J784s4 and J721e support - Freescale i.MX8MP PCIe PHY support - New driver for Renesas Ethernet SERDES supporting R-Car S4-8 - Qualcomm SM8450 PCIe1 PHY support in EP mode - Qualcomm SC8280XP PCIe PHY support (including x4 mode) - Fixed Qualcomm SC8280XP USB4-USB3-DP PHY DT bindings Updates: - A big pile of updates on qcom-qmp-* drivers following the driver split and reorganization merged earlier - Phy order of API calls documentation update" * tag 'phy-for-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy: (174 commits) phy: ti: phy-j721e-wiz: add j721s2-wiz-10g module support dt-bindings: phy-j721e-wiz: add j721s2 compatible string phy: use devm_platform_get_and_ioremap_resource() phy: allwinner: phy-sun6i-mipi-dphy: Add the A100 DPHY variant phy: allwinner: phy-sun6i-mipi-dphy: Add a variant power-on hook phy: allwinner: phy-sun6i-mipi-dphy: Set the enable bit last phy: allwinner: phy-sun6i-mipi-dphy: Make RX support optional dt-bindings: sun6i-a31-mipi-dphy: Add the A100 DPHY variant dt-bindings: sun6i-a31-mipi-dphy: Add the interrupts property phy: qcom-qmp-pcie: drop redundant clock allocation phy: qcom-qmp-usb: drop redundant clock allocation phy: qcom-qmp: drop unused type header phy: qcom-qmp-usb: drop sc8280xp reference-clock source dt-bindings: phy: qcom,sc8280xp-qmp-usb3-uni: drop reference-clock source phy: qcom-qmp-combo: add support for updated sc8280xp binding phy: qcom-qmp-combo: rename DP_PHY register pointer phy: qcom-qmp-combo: rename common-register pointers phy: qcom-qmp-combo: clean up DP clock callbacks phy: qcom-qmp-combo: separate clock and provider registration phy: qcom-qmp-combo: add clock registration helper ...
This commit is contained in:
commit
e79041113b
@ -17,13 +17,20 @@ properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: allwinner,sun6i-a31-mipi-dphy
|
||||
- const: allwinner,sun50i-a100-mipi-dphy
|
||||
- items:
|
||||
- const: allwinner,sun50i-a64-mipi-dphy
|
||||
- const: allwinner,sun6i-a31-mipi-dphy
|
||||
- items:
|
||||
- const: allwinner,sun20i-d1-mipi-dphy
|
||||
- const: allwinner,sun50i-a100-mipi-dphy
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Bus Clock
|
||||
@ -53,6 +60,7 @@ required:
|
||||
- "#phy-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
@ -61,9 +69,12 @@ additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
dphy0: d-phy@1ca1000 {
|
||||
compatible = "allwinner,sun6i-a31-mipi-dphy";
|
||||
reg = <0x01ca1000 0x1000>;
|
||||
interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&ccu 23>, <&ccu 97>;
|
||||
clock-names = "bus", "mod";
|
||||
resets = <&ccu 4>;
|
||||
|
@ -36,18 +36,22 @@ properties:
|
||||
- const: pmu3
|
||||
|
||||
clocks:
|
||||
minItems: 4
|
||||
items:
|
||||
- description: USB OTG PHY bus clock
|
||||
- description: USB Host 0 PHY bus clock
|
||||
- description: USB Host 1 PHY bus clock
|
||||
- description: USB Host 2 PHY bus clock
|
||||
- description: PMU clock for host port 2
|
||||
|
||||
clock-names:
|
||||
minItems: 4
|
||||
items:
|
||||
- const: usb0_phy
|
||||
- const: usb1_phy
|
||||
- const: usb2_phy
|
||||
- const: usb3_phy
|
||||
- const: pmu2_clk
|
||||
|
||||
resets:
|
||||
items:
|
||||
@ -96,6 +100,28 @@ required:
|
||||
- resets
|
||||
- reset-names
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- allwinner,sun50i-h616-usb-phy
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 5
|
||||
|
||||
clock-names:
|
||||
minItems: 5
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 4
|
||||
|
||||
clock-names:
|
||||
maxItems: 4
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -16,6 +16,7 @@ properties:
|
||||
compatible:
|
||||
enum:
|
||||
- fsl,imx8mm-pcie-phy
|
||||
- fsl,imx8mp-pcie-phy
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -28,11 +29,16 @@ properties:
|
||||
- const: ref
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: pciephy
|
||||
oneOf:
|
||||
- items: # for iMX8MM
|
||||
- const: pciephy
|
||||
- items: # for IMX8MP
|
||||
- const: pciephy
|
||||
- const: perst
|
||||
|
||||
fsl,refclk-pad-mode:
|
||||
description: |
|
||||
@ -60,6 +66,10 @@ properties:
|
||||
description: A boolean property indicating the CLKREQ# signal is
|
||||
not supported in the board design (optional)
|
||||
|
||||
power-domains:
|
||||
description: PCIe PHY power domain (optional).
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- "#phy-cells"
|
||||
- compatible
|
||||
|
@ -1,10 +1,10 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/qcom,qmp-pcie-phy.yaml#
|
||||
$id: http://devicetree.org/schemas/phy/qcom,ipq8074-qmp-pcie-phy.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm QMP PHY controller (PCIe)
|
||||
title: Qualcomm QMP PHY controller (PCIe, IPQ8074)
|
||||
|
||||
maintainers:
|
||||
- Vinod Koul <vkoul@kernel.org>
|
||||
@ -13,6 +13,9 @@ description:
|
||||
QMP PHY controller supports physical layer functionality for a number of
|
||||
controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
|
||||
|
||||
Note that these bindings are for SoCs up to SC8180X. For newer SoCs, see
|
||||
qcom,sc8280xp-qmp-pcie-phy.yaml.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
@ -1,10 +1,10 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/qcom,qmp-ufs-phy.yaml#
|
||||
$id: http://devicetree.org/schemas/phy/qcom,msm8996-qmp-ufs-phy.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm QMP PHY controller (UFS)
|
||||
title: Qualcomm QMP PHY controller (UFS, MSM8996)
|
||||
|
||||
maintainers:
|
||||
- Vinod Koul <vkoul@kernel.org>
|
||||
@ -13,13 +13,15 @@ description:
|
||||
QMP PHY controller supports physical layer functionality for a number of
|
||||
controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
|
||||
|
||||
Note that these bindings are for SoCs up to SC8180X. For newer SoCs, see
|
||||
qcom,sc8280xp-qmp-ufs-phy.yaml.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,msm8996-qmp-ufs-phy
|
||||
- qcom,msm8998-qmp-ufs-phy
|
||||
- qcom,sc8180x-qmp-ufs-phy
|
||||
- qcom,sc8280xp-qmp-ufs-phy
|
||||
- qcom,sdm845-qmp-ufs-phy
|
||||
- qcom,sm6115-qmp-ufs-phy
|
||||
- qcom,sm6350-qmp-ufs-phy
|
||||
@ -119,7 +121,6 @@ allOf:
|
||||
enum:
|
||||
- qcom,msm8998-qmp-ufs-phy
|
||||
- qcom,sc8180x-qmp-ufs-phy
|
||||
- qcom,sc8280xp-qmp-ufs-phy
|
||||
- qcom,sdm845-qmp-ufs-phy
|
||||
- qcom,sm6115-qmp-ufs-phy
|
||||
- qcom,sm6350-qmp-ufs-phy
|
||||
@ -156,7 +157,6 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,msm8998-qmp-ufs-phy
|
||||
- qcom,sc8280xp-qmp-ufs-phy
|
||||
- qcom,sdm845-qmp-ufs-phy
|
||||
- qcom,sm6350-qmp-ufs-phy
|
||||
- qcom,sm8150-qmp-ufs-phy
|
||||
@ -211,11 +211,12 @@ allOf:
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-sc8280xp.h>
|
||||
#include <dt-bindings/clock/qcom,gcc-sm8250.h>
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
|
||||
phy-wrapper@1d87000 {
|
||||
compatible = "qcom,sc8280xp-qmp-ufs-phy";
|
||||
reg = <0x01d87000 0xe10>;
|
||||
compatible = "qcom,sm8250-qmp-ufs-phy";
|
||||
reg = <0x01d87000 0x1c0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0x0 0x01d87000 0x1000>;
|
@ -1,10 +1,10 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/qcom,qmp-usb-phy.yaml#
|
||||
$id: http://devicetree.org/schemas/phy/qcom,msm8996-qmp-usb3-phy.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm QMP PHY controller (USB)
|
||||
title: Qualcomm QMP PHY controller (USB, MSM8996)
|
||||
|
||||
maintainers:
|
||||
- Vinod Koul <vkoul@kernel.org>
|
||||
@ -13,6 +13,9 @@ description:
|
||||
QMP PHY controller supports physical layer functionality for a number of
|
||||
controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
|
||||
|
||||
Note that these bindings are for SoCs up to SC8180X. For newer SoCs, see
|
||||
qcom,sc8280xp-qmp-usb3-uni-phy.yaml.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
@ -23,7 +26,6 @@ properties:
|
||||
- qcom,qcm2290-qmp-usb3-phy
|
||||
- qcom,sc7180-qmp-usb3-phy
|
||||
- qcom,sc8180x-qmp-usb3-phy
|
||||
- qcom,sc8280xp-qmp-usb3-uni-phy
|
||||
- qcom,sdm845-qmp-usb3-phy
|
||||
- qcom,sdm845-qmp-usb3-uni-phy
|
||||
- qcom,sdx55-qmp-usb3-uni-phy
|
||||
@ -201,7 +203,6 @@ allOf:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-qmp-usb3-uni-phy
|
||||
- qcom,sm8150-qmp-usb3-phy
|
||||
- qcom,sm8150-qmp-usb3-uni-phy
|
||||
- qcom,sm8250-qmp-usb3-uni-phy
|
||||
@ -268,16 +269,6 @@ allOf:
|
||||
- const: phy_phy
|
||||
- const: phy
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-qmp-usb3-uni-phy
|
||||
then:
|
||||
required:
|
||||
- power-domains
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@ -349,7 +340,6 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,msm8996-qmp-usb3-phy
|
||||
- qcom,sc8280xp-qmp-usb3-uni-phy
|
||||
- qcom,sm8250-qmp-usb3-uni-phy
|
||||
- qcom,sm8350-qmp-usb3-uni-phy
|
||||
then:
|
@ -2,10 +2,17 @@
|
||||
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/phy/qcom,qmp-usb3-dp-phy.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
$id: http://devicetree.org/schemas/phy/qcom,sc7180-qmp-usb3-dp-phy.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm QMP USB3 DP PHY controller
|
||||
title: Qualcomm QMP USB3 DP PHY controller (SC7180)
|
||||
|
||||
description:
|
||||
The QMP PHY controller supports physical layer functionality for a number of
|
||||
controllers on Qualcomm chipsets, such as, PCIe, UFS and USB.
|
||||
|
||||
Note that these bindings are for SoCs up to SC8180X. For newer SoCs, see
|
||||
qcom,sc8280xp-qmp-usb43dp-phy.yaml.
|
||||
|
||||
maintainers:
|
||||
- Wesley Cheng <quic_wcheng@quicinc.com>
|
||||
@ -16,7 +23,6 @@ properties:
|
||||
- qcom,sc7180-qmp-usb3-dp-phy
|
||||
- qcom,sc7280-qmp-usb3-dp-phy
|
||||
- qcom,sc8180x-qmp-usb3-dp-phy
|
||||
- qcom,sc8280xp-qmp-usb43dp-phy
|
||||
- qcom,sdm845-qmp-usb3-dp-phy
|
||||
- qcom,sm8250-qmp-usb3-dp-phy
|
||||
reg:
|
||||
@ -162,17 +168,6 @@ required:
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-qmp-usb43dp-phy
|
||||
then:
|
||||
required:
|
||||
- power-domains
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-sdm845.h>
|
@ -0,0 +1,165 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/qcom,sc8280xp-qmp-pcie-phy.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm QMP PHY controller (PCIe, SC8280XP)
|
||||
|
||||
maintainers:
|
||||
- Vinod Koul <vkoul@kernel.org>
|
||||
|
||||
description:
|
||||
The QMP PHY controller supports physical layer functionality for a number of
|
||||
controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sc8280xp-qmp-gen3x1-pcie-phy
|
||||
- qcom,sc8280xp-qmp-gen3x2-pcie-phy
|
||||
- qcom,sc8280xp-qmp-gen3x4-pcie-phy
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clocks:
|
||||
maxItems: 6
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: aux
|
||||
- const: cfg_ahb
|
||||
- const: ref
|
||||
- const: rchng
|
||||
- const: pipe
|
||||
- const: pipediv2
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: phy
|
||||
|
||||
vdda-phy-supply: true
|
||||
|
||||
vdda-pll-supply: true
|
||||
|
||||
qcom,4ln-config-sel:
|
||||
description: PCIe 4-lane configuration
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
- items:
|
||||
- description: phandle of TCSR syscon
|
||||
- description: offset of PCIe 4-lane configuration register
|
||||
- description: offset of configuration bit for this PHY
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
clock-output-names:
|
||||
maxItems: 1
|
||||
|
||||
"#phy-cells":
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- resets
|
||||
- reset-names
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- "#clock-cells"
|
||||
- clock-output-names
|
||||
- "#phy-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-qmp-gen3x4-pcie-phy
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: port a
|
||||
- description: port b
|
||||
required:
|
||||
- qcom,4ln-config-sel
|
||||
else:
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-sc8280xp.h>
|
||||
|
||||
pcie2b_phy: phy@1c18000 {
|
||||
compatible = "qcom,sc8280xp-qmp-gen3x2-pcie-phy";
|
||||
reg = <0x01c18000 0x2000>;
|
||||
|
||||
clocks = <&gcc GCC_PCIE_2B_AUX_CLK>,
|
||||
<&gcc GCC_PCIE_2B_CFG_AHB_CLK>,
|
||||
<&gcc GCC_PCIE_2A2B_CLKREF_CLK>,
|
||||
<&gcc GCC_PCIE2B_PHY_RCHNG_CLK>,
|
||||
<&gcc GCC_PCIE_2B_PIPE_CLK>,
|
||||
<&gcc GCC_PCIE_2B_PIPEDIV2_CLK>;
|
||||
clock-names = "aux", "cfg_ahb", "ref", "rchng",
|
||||
"pipe", "pipediv2";
|
||||
|
||||
power-domains = <&gcc PCIE_2B_GDSC>;
|
||||
|
||||
resets = <&gcc GCC_PCIE_2B_PHY_BCR>;
|
||||
reset-names = "phy";
|
||||
|
||||
vdda-phy-supply = <&vreg_l6d>;
|
||||
vdda-pll-supply = <&vreg_l4d>;
|
||||
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "pcie_2b_pipe_clk";
|
||||
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
pcie2a_phy: phy@1c24000 {
|
||||
compatible = "qcom,sc8280xp-qmp-gen3x4-pcie-phy";
|
||||
reg = <0x01c24000 0x2000>, <0x01c26000 0x2000>;
|
||||
|
||||
clocks = <&gcc GCC_PCIE_2A_AUX_CLK>,
|
||||
<&gcc GCC_PCIE_2A_CFG_AHB_CLK>,
|
||||
<&gcc GCC_PCIE_2A2B_CLKREF_CLK>,
|
||||
<&gcc GCC_PCIE2A_PHY_RCHNG_CLK>,
|
||||
<&gcc GCC_PCIE_2A_PIPE_CLK>,
|
||||
<&gcc GCC_PCIE_2A_PIPEDIV2_CLK>;
|
||||
clock-names = "aux", "cfg_ahb", "ref", "rchng",
|
||||
"pipe", "pipediv2";
|
||||
|
||||
power-domains = <&gcc PCIE_2A_GDSC>;
|
||||
|
||||
resets = <&gcc GCC_PCIE_2A_PHY_BCR>;
|
||||
reset-names = "phy";
|
||||
|
||||
vdda-phy-supply = <&vreg_l6d>;
|
||||
vdda-pll-supply = <&vreg_l4d>;
|
||||
|
||||
qcom,4ln-config-sel = <&tcsr 0xa044 0>;
|
||||
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "pcie_2a_pipe_clk";
|
||||
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -0,0 +1,83 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/qcom,sc8280xp-qmp-ufs-phy.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm QMP PHY controller (UFS, SC8280XP)
|
||||
|
||||
maintainers:
|
||||
- Vinod Koul <vkoul@kernel.org>
|
||||
|
||||
description:
|
||||
The QMP PHY controller supports physical layer functionality for a number of
|
||||
controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sc8280xp-qmp-ufs-phy
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ref
|
||||
- const: ref_aux
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: ufsphy
|
||||
|
||||
vdda-phy-supply: true
|
||||
|
||||
vdda-pll-supply: true
|
||||
|
||||
"#phy-cells":
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- resets
|
||||
- reset-names
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- "#phy-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-sc8280xp.h>
|
||||
|
||||
ufs_mem_phy: phy@1d87000 {
|
||||
compatible = "qcom,sc8280xp-qmp-ufs-phy";
|
||||
reg = <0x01d87000 0x1000>;
|
||||
|
||||
clocks = <&gcc GCC_UFS_REF_CLKREF_CLK>, <&gcc GCC_UFS_PHY_PHY_AUX_CLK>;
|
||||
clock-names = "ref", "ref_aux";
|
||||
|
||||
power-domains = <&gcc UFS_PHY_GDSC>;
|
||||
|
||||
resets = <&ufs_mem_hc 0>;
|
||||
reset-names = "ufsphy";
|
||||
|
||||
vdda-phy-supply = <&vreg_l6b>;
|
||||
vdda-pll-supply = <&vreg_l3b>;
|
||||
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -0,0 +1,102 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm QMP PHY controller (USB, SC8280XP)
|
||||
|
||||
maintainers:
|
||||
- Vinod Koul <vkoul@kernel.org>
|
||||
|
||||
description:
|
||||
The QMP PHY controller supports physical layer functionality for a number of
|
||||
controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sc8280xp-qmp-usb3-uni-phy
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 4
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: aux
|
||||
- const: ref
|
||||
- const: com_aux
|
||||
- const: pipe
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: phy
|
||||
- const: phy_phy
|
||||
|
||||
vdda-phy-supply: true
|
||||
|
||||
vdda-pll-supply: true
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
clock-output-names:
|
||||
maxItems: 1
|
||||
|
||||
"#phy-cells":
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- resets
|
||||
- reset-names
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- "#clock-cells"
|
||||
- clock-output-names
|
||||
- "#phy-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-sc8280xp.h>
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
|
||||
phy@88ef000 {
|
||||
compatible = "qcom,sc8280xp-qmp-usb3-uni-phy";
|
||||
reg = <0x088ef000 0x2000>;
|
||||
|
||||
clocks = <&gcc GCC_USB3_MP_PHY_AUX_CLK>,
|
||||
<&gcc GCC_USB3_MP0_CLKREF_CLK>,
|
||||
<&gcc GCC_USB3_MP_PHY_COM_AUX_CLK>,
|
||||
<&gcc GCC_USB3_MP_PHY_PIPE_0_CLK>;
|
||||
clock-names = "aux", "ref", "com_aux", "pipe";
|
||||
|
||||
power-domains = <&gcc USB30_MP_GDSC>;
|
||||
|
||||
resets = <&gcc GCC_USB3_UNIPHY_MP0_BCR>,
|
||||
<&gcc GCC_USB3UNIPHY_PHY_MP0_BCR>;
|
||||
reset-names = "phy", "phy_phy";
|
||||
|
||||
vdda-phy-supply = <&vreg_l3a>;
|
||||
vdda-pll-supply = <&vreg_l5a>;
|
||||
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "usb2_phy0_pipe_clk";
|
||||
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -0,0 +1,99 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm QMP USB4-USB3-DP PHY controller (SC8280XP)
|
||||
|
||||
maintainers:
|
||||
- Vinod Koul <vkoul@kernel.org>
|
||||
|
||||
description:
|
||||
The QMP PHY controller supports physical layer functionality for a number of
|
||||
controllers on Qualcomm chipsets, such as, PCIe, UFS and USB.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sc8280xp-qmp-usb43dp-phy
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 4
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: aux
|
||||
- const: ref
|
||||
- const: com_aux
|
||||
- const: usb3_pipe
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: phy
|
||||
- const: common
|
||||
|
||||
vdda-phy-supply: true
|
||||
|
||||
vdda-pll-supply: true
|
||||
|
||||
"#clock-cells":
|
||||
const: 1
|
||||
description:
|
||||
See include/dt-bindings/dt-bindings/phy/phy-qcom-qmp.h
|
||||
|
||||
"#phy-cells":
|
||||
const: 1
|
||||
description:
|
||||
See include/dt-bindings/dt-bindings/phy/phy-qcom-qmp.h
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- resets
|
||||
- reset-names
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- "#clock-cells"
|
||||
- "#phy-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-sc8280xp.h>
|
||||
|
||||
phy@88eb000 {
|
||||
compatible = "qcom,sc8280xp-qmp-usb43dp-phy";
|
||||
reg = <0x088eb000 0x4000>;
|
||||
|
||||
clocks = <&gcc GCC_USB3_PRIM_PHY_AUX_CLK>,
|
||||
<&gcc GCC_USB4_EUD_CLKREF_CLK>,
|
||||
<&gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>,
|
||||
<&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>;
|
||||
clock-names = "aux", "ref", "com_aux", "usb3_pipe";
|
||||
|
||||
power-domains = <&gcc USB30_PRIM_GDSC>;
|
||||
|
||||
resets = <&gcc GCC_USB3_PHY_PRIM_BCR>,
|
||||
<&gcc GCC_USB4_DP_PHY_PRIM_BCR>;
|
||||
reset-names = "phy", "common";
|
||||
|
||||
vdda-phy-supply = <&vreg_l9d>;
|
||||
vdda-pll-supply = <&vreg_l4d>;
|
||||
|
||||
#clock-cells = <1>;
|
||||
#phy-cells = <1>;
|
||||
};
|
@ -0,0 +1,54 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/renesas,r8a779f0-ether-serdes.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas Ethernet SERDES
|
||||
|
||||
maintainers:
|
||||
- Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: renesas,r8a779f0-ether-serdes
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
'#phy-cells':
|
||||
description: Port number of SERDES.
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- resets
|
||||
- power-domains
|
||||
- '#phy-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/r8a779f0-cpg-mssr.h>
|
||||
#include <dt-bindings/power/r8a779f0-sysc.h>
|
||||
|
||||
phy@e6444000 {
|
||||
compatible = "renesas,r8a779f0-ether-serdes";
|
||||
reg = <0xe6444000 0xc00>;
|
||||
clocks = <&cpg CPG_MOD 1506>;
|
||||
power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 1506>;
|
||||
#phy-cells = <1>;
|
||||
};
|
@ -54,6 +54,7 @@ properties:
|
||||
- ti,dm814-phy-gmii-sel
|
||||
- ti,am654-phy-gmii-sel
|
||||
- ti,j7200-cpsw5g-phy-gmii-sel
|
||||
- ti,j721e-cpsw9g-phy-gmii-sel
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -63,14 +64,17 @@ properties:
|
||||
ti,qsgmii-main-ports:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description: |
|
||||
Required only for QSGMII mode. Array to select the port for
|
||||
QSGMII main mode. Rest of the ports are selected as QSGMII_SUB
|
||||
ports automatically. Any one of the 4 CPSW5G ports can act as the
|
||||
main port with the rest of them being the QSGMII_SUB ports.
|
||||
maxItems: 1
|
||||
Required only for QSGMII mode. Array to select the port/s for QSGMII
|
||||
main mode. The size of the array corresponds to the number of QSGMII
|
||||
interfaces and thus, the number of distinct QSGMII main ports,
|
||||
supported by the device. If the device supports two QSGMII interfaces
|
||||
but only one QSGMII interface is desired, repeat the QSGMII main port
|
||||
value corresponding to the QSGMII interface in the array.
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
minimum: 1
|
||||
maximum: 4
|
||||
maximum: 8
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
@ -81,12 +85,43 @@ allOf:
|
||||
- ti,dra7xx-phy-gmii-sel
|
||||
- ti,dm814-phy-gmii-sel
|
||||
- ti,am654-phy-gmii-sel
|
||||
- ti,j7200-cpsw5g-phy-gmii-sel
|
||||
- ti,j721e-cpsw9g-phy-gmii-sel
|
||||
then:
|
||||
properties:
|
||||
'#phy-cells':
|
||||
const: 1
|
||||
description: CPSW port number (starting from 1)
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- ti,j7200-cpsw5g-phy-gmii-sel
|
||||
then:
|
||||
properties:
|
||||
ti,qsgmii-main-ports:
|
||||
maxItems: 1
|
||||
items:
|
||||
minimum: 1
|
||||
maximum: 4
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- ti,j721e-cpsw9g-phy-gmii-sel
|
||||
then:
|
||||
properties:
|
||||
ti,qsgmii-main-ports:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
items:
|
||||
minimum: 1
|
||||
maximum: 8
|
||||
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
@ -94,6 +129,7 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- ti,j7200-cpsw5g-phy-gmii-sel
|
||||
- ti,j721e-cpsw9g-phy-gmii-sel
|
||||
then:
|
||||
properties:
|
||||
ti,qsgmii-main-ports: false
|
||||
|
@ -15,8 +15,10 @@ properties:
|
||||
enum:
|
||||
- ti,j721e-wiz-16g
|
||||
- ti,j721e-wiz-10g
|
||||
- ti,j721s2-wiz-10g
|
||||
- ti,am64-wiz-10g
|
||||
- ti,j7200-wiz-10g
|
||||
- ti,j784s4-wiz-10g
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
@ -94,7 +94,8 @@ Inorder to dereference the private data (in phy_ops), the phy provider driver
|
||||
can use phy_set_drvdata() after creating the PHY and use phy_get_drvdata() in
|
||||
phy_ops to get back the private data.
|
||||
|
||||
4. Getting a reference to the PHY
|
||||
Getting a reference to the PHY
|
||||
==============================
|
||||
|
||||
Before the controller can make use of the PHY, it has to get a reference to
|
||||
it. This framework provides the following APIs to get a reference to the PHY.
|
||||
@ -130,6 +131,28 @@ the phy_init() and phy_exit() calls, and phy_power_on() and
|
||||
phy_power_off() calls are all NOP when applied to a NULL phy. The NULL
|
||||
phy is useful in devices for handling optional phy devices.
|
||||
|
||||
Order of API calls
|
||||
==================
|
||||
|
||||
The general order of calls should be::
|
||||
|
||||
[devm_][of_]phy_get()
|
||||
phy_init()
|
||||
phy_power_on()
|
||||
[phy_set_mode[_ext]()]
|
||||
...
|
||||
phy_power_off()
|
||||
phy_exit()
|
||||
[[of_]phy_put()]
|
||||
|
||||
Some PHY drivers may not implement :c:func:`phy_init` or :c:func:`phy_power_on`,
|
||||
but controllers should always call these functions to be compatible with other
|
||||
PHYs. Some PHYs may require :c:func:`phy_set_mode <phy_set_mode_ext>`, while
|
||||
others may use a default mode (typically configured via devicetree or other
|
||||
firmware). For compatibility, you should always call this function if you know
|
||||
what mode you will be using. Generally, this function should be called after
|
||||
:c:func:`phy_power_on`, although some PHY drivers may allow it at any time.
|
||||
|
||||
Releasing a reference to the PHY
|
||||
================================
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/phy/pcie.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
@ -268,6 +269,10 @@ static int qcom_pcie_enable_resources(struct qcom_pcie_ep *pcie_ep)
|
||||
if (ret)
|
||||
goto err_disable_clk;
|
||||
|
||||
ret = phy_set_mode_ext(pcie_ep->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_EP);
|
||||
if (ret)
|
||||
goto err_phy_exit;
|
||||
|
||||
ret = phy_power_on(pcie_ep->phy);
|
||||
if (ret)
|
||||
goto err_phy_exit;
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/pcie.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
@ -1499,6 +1500,10 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_set_mode_ext(pcie->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
|
||||
if (ret)
|
||||
goto err_deinit;
|
||||
|
||||
ret = phy_power_on(pcie->phy);
|
||||
if (ret)
|
||||
goto err_deinit;
|
||||
|
@ -120,6 +120,7 @@ struct sun4i_usb_phy_cfg {
|
||||
u8 phyctl_offset;
|
||||
bool dedicated_clocks;
|
||||
bool phy0_dual_route;
|
||||
bool needs_phy2_siddq;
|
||||
int missing_phys;
|
||||
};
|
||||
|
||||
@ -289,6 +290,50 @@ static int sun4i_usb_phy_init(struct phy *_phy)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Some PHYs on some SoCs need the help of PHY2 to work. */
|
||||
if (data->cfg->needs_phy2_siddq && phy->index != 2) {
|
||||
struct sun4i_usb_phy *phy2 = &data->phys[2];
|
||||
|
||||
ret = clk_prepare_enable(phy2->clk);
|
||||
if (ret) {
|
||||
reset_control_assert(phy->reset);
|
||||
clk_disable_unprepare(phy->clk2);
|
||||
clk_disable_unprepare(phy->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(phy2->reset);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(phy2->clk);
|
||||
reset_control_assert(phy->reset);
|
||||
clk_disable_unprepare(phy->clk2);
|
||||
clk_disable_unprepare(phy->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This extra clock is just needed to access the
|
||||
* REG_HCI_PHY_CTL PMU register for PHY2.
|
||||
*/
|
||||
ret = clk_prepare_enable(phy2->clk2);
|
||||
if (ret) {
|
||||
reset_control_assert(phy2->reset);
|
||||
clk_disable_unprepare(phy2->clk);
|
||||
reset_control_assert(phy->reset);
|
||||
clk_disable_unprepare(phy->clk2);
|
||||
clk_disable_unprepare(phy->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (phy2->pmu && data->cfg->hci_phy_ctl_clear) {
|
||||
val = readl(phy2->pmu + REG_HCI_PHY_CTL);
|
||||
val &= ~data->cfg->hci_phy_ctl_clear;
|
||||
writel(val, phy2->pmu + REG_HCI_PHY_CTL);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(phy->clk2);
|
||||
}
|
||||
|
||||
if (phy->pmu && data->cfg->hci_phy_ctl_clear) {
|
||||
val = readl(phy->pmu + REG_HCI_PHY_CTL);
|
||||
val &= ~data->cfg->hci_phy_ctl_clear;
|
||||
@ -354,6 +399,13 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
|
||||
data->phy0_init = false;
|
||||
}
|
||||
|
||||
if (data->cfg->needs_phy2_siddq && phy->index != 2) {
|
||||
struct sun4i_usb_phy *phy2 = &data->phys[2];
|
||||
|
||||
clk_disable_unprepare(phy2->clk);
|
||||
reset_control_assert(phy2->reset);
|
||||
}
|
||||
|
||||
sun4i_usb_phy_passby(phy, 0);
|
||||
reset_control_assert(phy->reset);
|
||||
clk_disable_unprepare(phy->clk2);
|
||||
@ -785,6 +837,13 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
dev_err(dev, "failed to get clock %s\n", name);
|
||||
return PTR_ERR(phy->clk2);
|
||||
}
|
||||
} else {
|
||||
snprintf(name, sizeof(name), "pmu%d_clk", i);
|
||||
phy->clk2 = devm_clk_get_optional(dev, name);
|
||||
if (IS_ERR(phy->clk2)) {
|
||||
dev_err(dev, "failed to get clock %s\n", name);
|
||||
return PTR_ERR(phy->clk2);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(name, sizeof(name), "usb%d_reset", i);
|
||||
@ -973,6 +1032,17 @@ static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = {
|
||||
.missing_phys = BIT(1) | BIT(2),
|
||||
};
|
||||
|
||||
static const struct sun4i_usb_phy_cfg sun50i_h616_cfg = {
|
||||
.num_phys = 4,
|
||||
.type = sun50i_h6_phy,
|
||||
.disc_thresh = 3,
|
||||
.phyctl_offset = REG_PHYCTL_A33,
|
||||
.dedicated_clocks = true,
|
||||
.phy0_dual_route = true,
|
||||
.hci_phy_ctl_clear = PHY_CTL_SIDDQ,
|
||||
.needs_phy2_siddq = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id sun4i_usb_phy_of_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg },
|
||||
{ .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg },
|
||||
@ -988,6 +1058,7 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = {
|
||||
{ .compatible = "allwinner,sun50i-a64-usb-phy",
|
||||
.data = &sun50i_a64_cfg},
|
||||
{ .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg },
|
||||
{ .compatible = "allwinner,sun50i-h616-usb-phy", .data = &sun50i_h616_cfg },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
|
||||
|
@ -70,11 +70,19 @@
|
||||
|
||||
#define SUN6I_DPHY_ANA0_REG 0x4c
|
||||
#define SUN6I_DPHY_ANA0_REG_PWS BIT(31)
|
||||
#define SUN6I_DPHY_ANA0_REG_PWEND BIT(30)
|
||||
#define SUN6I_DPHY_ANA0_REG_PWENC BIT(29)
|
||||
#define SUN6I_DPHY_ANA0_REG_DMPC BIT(28)
|
||||
#define SUN6I_DPHY_ANA0_REG_DMPD(n) (((n) & 0xf) << 24)
|
||||
#define SUN6I_DPHY_ANA0_REG_SRXDT(n) (((n) & 0xf) << 20)
|
||||
#define SUN6I_DPHY_ANA0_REG_SRXCK(n) (((n) & 0xf) << 16)
|
||||
#define SUN6I_DPHY_ANA0_REG_SDIV2 BIT(15)
|
||||
#define SUN6I_DPHY_ANA0_REG_SLV(n) (((n) & 7) << 12)
|
||||
#define SUN6I_DPHY_ANA0_REG_DEN(n) (((n) & 0xf) << 8)
|
||||
#define SUN6I_DPHY_ANA0_REG_PLR(n) (((n) & 0xf) << 4)
|
||||
#define SUN6I_DPHY_ANA0_REG_SFB(n) (((n) & 3) << 2)
|
||||
#define SUN6I_DPHY_ANA0_REG_RSD BIT(1)
|
||||
#define SUN6I_DPHY_ANA0_REG_SELSCK BIT(0)
|
||||
|
||||
#define SUN6I_DPHY_ANA1_REG 0x50
|
||||
#define SUN6I_DPHY_ANA1_REG_VTTMODE BIT(31)
|
||||
@ -97,8 +105,13 @@
|
||||
#define SUN6I_DPHY_ANA3_EN_LDOR BIT(18)
|
||||
|
||||
#define SUN6I_DPHY_ANA4_REG 0x5c
|
||||
#define SUN6I_DPHY_ANA4_REG_EN_MIPI BIT(31)
|
||||
#define SUN6I_DPHY_ANA4_REG_EN_COMTEST BIT(30)
|
||||
#define SUN6I_DPHY_ANA4_REG_COMTEST(n) (((n) & 3) << 28)
|
||||
#define SUN6I_DPHY_ANA4_REG_IB(n) (((n) & 3) << 25)
|
||||
#define SUN6I_DPHY_ANA4_REG_DMPLVC BIT(24)
|
||||
#define SUN6I_DPHY_ANA4_REG_DMPLVD(n) (((n) & 0xf) << 20)
|
||||
#define SUN6I_DPHY_ANA4_REG_VTT_SET(n) (((n) & 0x7) << 17)
|
||||
#define SUN6I_DPHY_ANA4_REG_CKDV(n) (((n) & 0x1f) << 12)
|
||||
#define SUN6I_DPHY_ANA4_REG_TMSC(n) (((n) & 3) << 10)
|
||||
#define SUN6I_DPHY_ANA4_REG_TMSD(n) (((n) & 3) << 8)
|
||||
@ -109,11 +122,68 @@
|
||||
|
||||
#define SUN6I_DPHY_DBG5_REG 0xf4
|
||||
|
||||
#define SUN50I_DPHY_TX_SLEW_REG0 0xf8
|
||||
#define SUN50I_DPHY_TX_SLEW_REG1 0xfc
|
||||
#define SUN50I_DPHY_TX_SLEW_REG2 0x100
|
||||
|
||||
#define SUN50I_DPHY_PLL_REG0 0x104
|
||||
#define SUN50I_DPHY_PLL_REG0_CP36_EN BIT(23)
|
||||
#define SUN50I_DPHY_PLL_REG0_LDO_EN BIT(22)
|
||||
#define SUN50I_DPHY_PLL_REG0_EN_LVS BIT(21)
|
||||
#define SUN50I_DPHY_PLL_REG0_PLL_EN BIT(20)
|
||||
#define SUN50I_DPHY_PLL_REG0_P(n) (((n) & 0xf) << 16)
|
||||
#define SUN50I_DPHY_PLL_REG0_N(n) (((n) & 0xff) << 8)
|
||||
#define SUN50I_DPHY_PLL_REG0_NDET BIT(7)
|
||||
#define SUN50I_DPHY_PLL_REG0_TDIV BIT(6)
|
||||
#define SUN50I_DPHY_PLL_REG0_M0(n) (((n) & 3) << 4)
|
||||
#define SUN50I_DPHY_PLL_REG0_M1(n) ((n) & 0xf)
|
||||
|
||||
#define SUN50I_DPHY_PLL_REG1 0x108
|
||||
#define SUN50I_DPHY_PLL_REG1_UNLOCK_MDSEL(n) (((n) & 3) << 14)
|
||||
#define SUN50I_DPHY_PLL_REG1_LOCKMDSEL BIT(13)
|
||||
#define SUN50I_DPHY_PLL_REG1_LOCKDET_EN BIT(12)
|
||||
#define SUN50I_DPHY_PLL_REG1_VSETA(n) (((n) & 0x7) << 9)
|
||||
#define SUN50I_DPHY_PLL_REG1_VSETD(n) (((n) & 0x7) << 6)
|
||||
#define SUN50I_DPHY_PLL_REG1_LPF_SW BIT(5)
|
||||
#define SUN50I_DPHY_PLL_REG1_ICP_SEL(n) (((n) & 3) << 3)
|
||||
#define SUN50I_DPHY_PLL_REG1_ATEST_SEL(n) (((n) & 3) << 1)
|
||||
#define SUN50I_DPHY_PLL_REG1_TEST_EN BIT(0)
|
||||
|
||||
#define SUN50I_DPHY_PLL_REG2 0x10c
|
||||
#define SUN50I_DPHY_PLL_REG2_SDM_EN BIT(31)
|
||||
#define SUN50I_DPHY_PLL_REG2_FF_EN BIT(30)
|
||||
#define SUN50I_DPHY_PLL_REG2_SS_EN BIT(29)
|
||||
#define SUN50I_DPHY_PLL_REG2_SS_FRAC(n) (((n) & 0x1ff) << 20)
|
||||
#define SUN50I_DPHY_PLL_REG2_SS_INT(n) (((n) & 0xff) << 12)
|
||||
#define SUN50I_DPHY_PLL_REG2_FRAC(n) ((n) & 0xfff)
|
||||
|
||||
#define SUN50I_COMBO_PHY_REG0 0x110
|
||||
#define SUN50I_COMBO_PHY_REG0_EN_TEST_COMBOLDO BIT(5)
|
||||
#define SUN50I_COMBO_PHY_REG0_EN_TEST_0P8 BIT(4)
|
||||
#define SUN50I_COMBO_PHY_REG0_EN_MIPI BIT(3)
|
||||
#define SUN50I_COMBO_PHY_REG0_EN_LVDS BIT(2)
|
||||
#define SUN50I_COMBO_PHY_REG0_EN_COMBOLDO BIT(1)
|
||||
#define SUN50I_COMBO_PHY_REG0_EN_CP BIT(0)
|
||||
|
||||
#define SUN50I_COMBO_PHY_REG1 0x114
|
||||
#define SUN50I_COMBO_PHY_REG2_REG_VREF1P6(n) (((n) & 0x7) << 4)
|
||||
#define SUN50I_COMBO_PHY_REG2_REG_VREF0P8(n) ((n) & 0x7)
|
||||
|
||||
#define SUN50I_COMBO_PHY_REG2 0x118
|
||||
#define SUN50I_COMBO_PHY_REG2_HS_STOP_DLY(n) ((n) & 0xff)
|
||||
|
||||
enum sun6i_dphy_direction {
|
||||
SUN6I_DPHY_DIRECTION_TX,
|
||||
SUN6I_DPHY_DIRECTION_RX,
|
||||
};
|
||||
|
||||
struct sun6i_dphy;
|
||||
|
||||
struct sun6i_dphy_variant {
|
||||
void (*tx_power_on)(struct sun6i_dphy *dphy);
|
||||
bool rx_supported;
|
||||
};
|
||||
|
||||
struct sun6i_dphy {
|
||||
struct clk *bus_clk;
|
||||
struct clk *mod_clk;
|
||||
@ -123,6 +193,7 @@ struct sun6i_dphy {
|
||||
struct phy *phy;
|
||||
struct phy_configure_opts_mipi_dphy config;
|
||||
|
||||
const struct sun6i_dphy_variant *variant;
|
||||
enum sun6i_dphy_direction direction;
|
||||
};
|
||||
|
||||
@ -151,37 +222,10 @@ static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun6i_dphy_tx_power_on(struct sun6i_dphy *dphy)
|
||||
static void sun6i_a31_mipi_dphy_tx_power_on(struct sun6i_dphy *dphy)
|
||||
{
|
||||
u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
|
||||
SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME0_REG,
|
||||
SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) |
|
||||
SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) |
|
||||
SUN6I_DPHY_TX_TIME0_HS_TRAIL(10));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME1_REG,
|
||||
SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) |
|
||||
SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) |
|
||||
SUN6I_DPHY_TX_TIME1_CLK_PRE(3) |
|
||||
SUN6I_DPHY_TX_TIME1_CLK_POST(10));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME2_REG,
|
||||
SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME3_REG, 0);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME4_REG,
|
||||
SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) |
|
||||
SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
|
||||
SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
|
||||
SUN6I_DPHY_GCTL_EN);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
|
||||
SUN6I_DPHY_ANA0_REG_PWS |
|
||||
SUN6I_DPHY_ANA0_REG_DMPC |
|
||||
@ -213,6 +257,106 @@ static int sun6i_dphy_tx_power_on(struct sun6i_dphy *dphy)
|
||||
SUN6I_DPHY_ANA3_EN_LDOC |
|
||||
SUN6I_DPHY_ANA3_EN_LDOD);
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
static void sun50i_a100_mipi_dphy_tx_power_on(struct sun6i_dphy *dphy)
|
||||
{
|
||||
unsigned long mipi_symbol_rate = dphy->config.hs_clk_rate;
|
||||
unsigned int div, n;
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
|
||||
SUN6I_DPHY_ANA4_REG_IB(2) |
|
||||
SUN6I_DPHY_ANA4_REG_DMPLVD(4) |
|
||||
SUN6I_DPHY_ANA4_REG_VTT_SET(3) |
|
||||
SUN6I_DPHY_ANA4_REG_CKDV(3) |
|
||||
SUN6I_DPHY_ANA4_REG_TMSD(1) |
|
||||
SUN6I_DPHY_ANA4_REG_TMSC(1) |
|
||||
SUN6I_DPHY_ANA4_REG_TXPUSD(2) |
|
||||
SUN6I_DPHY_ANA4_REG_TXPUSC(3) |
|
||||
SUN6I_DPHY_ANA4_REG_TXDNSD(2) |
|
||||
SUN6I_DPHY_ANA4_REG_TXDNSC(3));
|
||||
|
||||
regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
|
||||
SUN6I_DPHY_ANA2_EN_CK_CPU,
|
||||
SUN6I_DPHY_ANA2_EN_CK_CPU);
|
||||
|
||||
regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
|
||||
SUN6I_DPHY_ANA2_REG_ENIB,
|
||||
SUN6I_DPHY_ANA2_REG_ENIB);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
|
||||
SUN6I_DPHY_ANA3_EN_LDOR |
|
||||
SUN6I_DPHY_ANA3_EN_LDOC |
|
||||
SUN6I_DPHY_ANA3_EN_LDOD);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
|
||||
SUN6I_DPHY_ANA0_REG_PLR(4) |
|
||||
SUN6I_DPHY_ANA0_REG_SFB(1));
|
||||
|
||||
regmap_write(dphy->regs, SUN50I_COMBO_PHY_REG0,
|
||||
SUN50I_COMBO_PHY_REG0_EN_CP);
|
||||
|
||||
/* Choose a divider to limit the VCO frequency to around 2 GHz. */
|
||||
div = 16 >> order_base_2(DIV_ROUND_UP(mipi_symbol_rate, 264000000));
|
||||
n = mipi_symbol_rate * div / 24000000;
|
||||
|
||||
regmap_write(dphy->regs, SUN50I_DPHY_PLL_REG0,
|
||||
SUN50I_DPHY_PLL_REG0_CP36_EN |
|
||||
SUN50I_DPHY_PLL_REG0_LDO_EN |
|
||||
SUN50I_DPHY_PLL_REG0_EN_LVS |
|
||||
SUN50I_DPHY_PLL_REG0_PLL_EN |
|
||||
SUN50I_DPHY_PLL_REG0_NDET |
|
||||
SUN50I_DPHY_PLL_REG0_P((div - 1) % 8) |
|
||||
SUN50I_DPHY_PLL_REG0_N(n) |
|
||||
SUN50I_DPHY_PLL_REG0_M0((div - 1) / 8) |
|
||||
SUN50I_DPHY_PLL_REG0_M1(2));
|
||||
|
||||
/* Disable sigma-delta modulation. */
|
||||
regmap_write(dphy->regs, SUN50I_DPHY_PLL_REG2, 0);
|
||||
|
||||
regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA4_REG,
|
||||
SUN6I_DPHY_ANA4_REG_EN_MIPI,
|
||||
SUN6I_DPHY_ANA4_REG_EN_MIPI);
|
||||
|
||||
regmap_update_bits(dphy->regs, SUN50I_COMBO_PHY_REG0,
|
||||
SUN50I_COMBO_PHY_REG0_EN_MIPI |
|
||||
SUN50I_COMBO_PHY_REG0_EN_COMBOLDO,
|
||||
SUN50I_COMBO_PHY_REG0_EN_MIPI |
|
||||
SUN50I_COMBO_PHY_REG0_EN_COMBOLDO);
|
||||
|
||||
regmap_write(dphy->regs, SUN50I_COMBO_PHY_REG2,
|
||||
SUN50I_COMBO_PHY_REG2_HS_STOP_DLY(20));
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
static int sun6i_dphy_tx_power_on(struct sun6i_dphy *dphy)
|
||||
{
|
||||
u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
|
||||
SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME0_REG,
|
||||
SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) |
|
||||
SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) |
|
||||
SUN6I_DPHY_TX_TIME0_HS_TRAIL(10));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME1_REG,
|
||||
SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) |
|
||||
SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) |
|
||||
SUN6I_DPHY_TX_TIME1_CLK_PRE(3) |
|
||||
SUN6I_DPHY_TX_TIME1_CLK_POST(10));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME2_REG,
|
||||
SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME3_REG, 0);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME4_REG,
|
||||
SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) |
|
||||
SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
|
||||
|
||||
dphy->variant->tx_power_on(dphy);
|
||||
|
||||
regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
|
||||
SUN6I_DPHY_ANA3_EN_VTTC |
|
||||
@ -239,6 +383,10 @@ static int sun6i_dphy_tx_power_on(struct sun6i_dphy *dphy)
|
||||
SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK,
|
||||
SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
|
||||
SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
|
||||
SUN6I_DPHY_GCTL_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -393,7 +541,7 @@ static const struct regmap_config sun6i_dphy_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = SUN6I_DPHY_DBG5_REG,
|
||||
.max_register = SUN50I_COMBO_PHY_REG2,
|
||||
.name = "mipi-dphy",
|
||||
};
|
||||
|
||||
@ -409,6 +557,10 @@ static int sun6i_dphy_probe(struct platform_device *pdev)
|
||||
if (!dphy)
|
||||
return -ENOMEM;
|
||||
|
||||
dphy->variant = device_get_match_data(&pdev->dev);
|
||||
if (!dphy->variant)
|
||||
return -EINVAL;
|
||||
|
||||
regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(regs)) {
|
||||
dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n");
|
||||
@ -445,8 +597,14 @@ static int sun6i_dphy_probe(struct platform_device *pdev)
|
||||
ret = of_property_read_string(pdev->dev.of_node, "allwinner,direction",
|
||||
&direction);
|
||||
|
||||
if (!ret && !strncmp(direction, "rx", 2))
|
||||
if (!ret && !strncmp(direction, "rx", 2)) {
|
||||
if (!dphy->variant->rx_supported) {
|
||||
dev_err(&pdev->dev, "RX not supported on this variant\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
dphy->direction = SUN6I_DPHY_DIRECTION_RX;
|
||||
}
|
||||
|
||||
phy_set_drvdata(dphy->phy, dphy);
|
||||
phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
|
||||
@ -454,8 +612,24 @@ static int sun6i_dphy_probe(struct platform_device *pdev)
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct sun6i_dphy_variant sun6i_a31_mipi_dphy_variant = {
|
||||
.tx_power_on = sun6i_a31_mipi_dphy_tx_power_on,
|
||||
.rx_supported = true,
|
||||
};
|
||||
|
||||
static const struct sun6i_dphy_variant sun50i_a100_mipi_dphy_variant = {
|
||||
.tx_power_on = sun50i_a100_mipi_dphy_tx_power_on,
|
||||
};
|
||||
|
||||
static const struct of_device_id sun6i_dphy_of_table[] = {
|
||||
{ .compatible = "allwinner,sun6i-a31-mipi-dphy" },
|
||||
{
|
||||
.compatible = "allwinner,sun6i-a31-mipi-dphy",
|
||||
.data = &sun6i_a31_mipi_dphy_variant,
|
||||
},
|
||||
{
|
||||
.compatible = "allwinner,sun50i-a100-mipi-dphy",
|
||||
.data = &sun50i_a100_mipi_dphy_variant,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table);
|
||||
|
@ -18,14 +18,14 @@
|
||||
#define PIARBCTL_CAM 0x00
|
||||
#define PIARBCTL_SPLITTER 0x04
|
||||
#define PIARBCTL_MISC 0x08
|
||||
#define PIARBCTL_MISC_SECURE_MASK 0x80000000
|
||||
#define PIARBCTL_MISC_USB_SELECT_MASK 0x40000000
|
||||
#define PIARBCTL_MISC_USB_4G_SDRAM_MASK 0x20000000
|
||||
#define PIARBCTL_MISC_USB_PRIORITY_MASK 0x000f0000
|
||||
#define PIARBCTL_MISC_USB_MEM_PAGE_MASK 0x0000f000
|
||||
#define PIARBCTL_MISC_CAM1_MEM_PAGE_MASK 0x00000f00
|
||||
#define PIARBCTL_MISC_CAM0_MEM_PAGE_MASK 0x000000f0
|
||||
#define PIARBCTL_MISC_SATA_PRIORITY_MASK 0x0000000f
|
||||
#define PIARBCTL_MISC_SATA_PRIORITY_MASK GENMASK(3, 0)
|
||||
#define PIARBCTL_MISC_CAM0_MEM_PAGE_MASK GENMASK(7, 4)
|
||||
#define PIARBCTL_MISC_CAM1_MEM_PAGE_MASK GENMASK(11, 8)
|
||||
#define PIARBCTL_MISC_USB_MEM_PAGE_MASK GENMASK(15, 12)
|
||||
#define PIARBCTL_MISC_USB_PRIORITY_MASK GENMASK(19, 16)
|
||||
#define PIARBCTL_MISC_USB_4G_SDRAM_MASK BIT(29)
|
||||
#define PIARBCTL_MISC_USB_SELECT_MASK BIT(30)
|
||||
#define PIARBCTL_MISC_SECURE_MASK BIT(31)
|
||||
|
||||
#define PIARBCTL_MISC_USB_ONLY_MASK \
|
||||
(PIARBCTL_MISC_USB_SELECT_MASK | \
|
||||
@ -35,46 +35,47 @@
|
||||
|
||||
/* Register definitions for the USB CTRL block */
|
||||
#define USB_CTRL_SETUP 0x00
|
||||
#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK 0x02000000
|
||||
#define USB_CTRL_SETUP_SCB2_EN_MASK 0x00008000
|
||||
#define USB_CTRL_SETUP_tca_drv_sel_MASK 0x01000000
|
||||
#define USB_CTRL_SETUP_SCB1_EN_MASK 0x00004000
|
||||
#define USB_CTRL_SETUP_SOFT_SHUTDOWN_MASK 0x00000200
|
||||
#define USB_CTRL_SETUP_IPP_MASK 0x00000020
|
||||
#define USB_CTRL_SETUP_IOC_MASK 0x00000010
|
||||
#define USB_CTRL_SETUP_IOC_MASK BIT(4)
|
||||
#define USB_CTRL_SETUP_IPP_MASK BIT(5)
|
||||
#define USB_CTRL_SETUP_SOFT_SHUTDOWN_MASK BIT(9)
|
||||
#define USB_CTRL_SETUP_SCB1_EN_MASK BIT(14)
|
||||
#define USB_CTRL_SETUP_SCB2_EN_MASK BIT(15)
|
||||
#define USB_CTRL_SETUP_tca_drv_sel_MASK BIT(24)
|
||||
#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK BIT(25)
|
||||
#define USB_CTRL_USB_PM 0x04
|
||||
#define USB_CTRL_USB_PM_USB_PWRDN_MASK 0x80000000
|
||||
#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000
|
||||
#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000
|
||||
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000
|
||||
#define USB_CTRL_USB_PM_XHC_PME_EN_MASK 0x00000010
|
||||
#define USB_CTRL_USB_PM_XHC_S2_CLK_SWITCH_EN_MASK 0x00000008
|
||||
#define USB_CTRL_USB_PM_XHC_S2_CLK_SWITCH_EN_MASK BIT(3)
|
||||
#define USB_CTRL_USB_PM_XHC_PME_EN_MASK BIT(4)
|
||||
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK BIT(22)
|
||||
#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK BIT(23)
|
||||
#define USB_CTRL_USB_PM_SOFT_RESET_MASK BIT(30)
|
||||
#define USB_CTRL_USB_PM_USB_PWRDN_MASK BIT(31)
|
||||
#define USB_CTRL_USB_PM_STATUS 0x08
|
||||
#define USB_CTRL_USB_DEVICE_CTL1 0x10
|
||||
#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003
|
||||
#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK GENMASK(1, 0)
|
||||
#define USB_CTRL_TEST_PORT_CTL 0x30
|
||||
#define USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_MASK 0x000000ff
|
||||
#define USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_MASK GENMASK(7, 0)
|
||||
#define USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_PME_GEN_MASK 0x0000002e
|
||||
#define USB_CTRL_TP_DIAG1 0x34
|
||||
#define USB_CTLR_TP_DIAG1_wake_MASK 0x00000002
|
||||
#define USB_CTLR_TP_DIAG1_wake_MASK BIT(1)
|
||||
#define USB_CTRL_CTLR_CSHCR 0x50
|
||||
#define USB_CTRL_CTLR_CSHCR_ctl_pme_en_MASK 0x00040000
|
||||
#define USB_CTRL_CTLR_CSHCR_ctl_pme_en_MASK BIT(18)
|
||||
|
||||
/* Register definitions for the USB_PHY block in 7211b0 */
|
||||
#define USB_PHY_PLL_CTL 0x00
|
||||
#define USB_PHY_PLL_CTL_PLL_RESETB_MASK 0x40000000
|
||||
#define USB_PHY_PLL_CTL_PLL_SUSPEND_MASK BIT(27)
|
||||
#define USB_PHY_PLL_CTL_PLL_RESETB_MASK BIT(30)
|
||||
#define USB_PHY_PLL_LDO_CTL 0x08
|
||||
#define USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK 0x00000004
|
||||
#define USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK 0x00000002
|
||||
#define USB_PHY_PLL_LDO_CTL_AFE_BG_PWRDWNB_MASK 0x00000001
|
||||
#define USB_PHY_PLL_LDO_CTL_AFE_BG_PWRDWNB_MASK BIT(0)
|
||||
#define USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK BIT(1)
|
||||
#define USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK BIT(2)
|
||||
#define USB_PHY_UTMI_CTL_1 0x04
|
||||
#define USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK 0x00000800
|
||||
#define USB_PHY_UTMI_CTL_1_PHY_MODE_MASK 0x0000000c
|
||||
#define USB_PHY_UTMI_CTL_1_PHY_MODE_MASK GENMASK(3, 2)
|
||||
#define USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT 2
|
||||
#define USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK BIT(11)
|
||||
#define USB_PHY_IDDQ 0x1c
|
||||
#define USB_PHY_IDDQ_phy_iddq_MASK 0x00000001
|
||||
#define USB_PHY_IDDQ_phy_iddq_MASK BIT(0)
|
||||
#define USB_PHY_STATUS 0x20
|
||||
#define USB_PHY_STATUS_pll_lock_MASK 0x00000001
|
||||
#define USB_PHY_STATUS_pll_lock_MASK BIT(0)
|
||||
|
||||
/* Register definitions for the MDIO registers in the DWC2 block of
|
||||
* the 7211b0.
|
||||
@ -86,7 +87,7 @@
|
||||
|
||||
/* Register definitions for the BDC EC block in 7211b0 */
|
||||
#define BDC_EC_AXIRDA 0x0c
|
||||
#define BDC_EC_AXIRDA_RTS_MASK 0xf0000000
|
||||
#define BDC_EC_AXIRDA_RTS_MASK GENMASK(31, 28)
|
||||
#define BDC_EC_AXIRDA_RTS_SHIFT 28
|
||||
|
||||
|
||||
@ -195,10 +196,10 @@ static void usb_init_common(struct brcm_usb_init_params *params)
|
||||
if (USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE)) {
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
|
||||
reg |= params->mode;
|
||||
reg |= params->port_mode;
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
}
|
||||
switch (params->mode) {
|
||||
switch (params->supported_port_modes) {
|
||||
case USB_CTLR_MODE_HOST:
|
||||
USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB);
|
||||
break;
|
||||
@ -259,6 +260,11 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params)
|
||||
brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1);
|
||||
}
|
||||
|
||||
/* Disable PLL auto suspend */
|
||||
reg = brcm_usb_readl(usb_phy + USB_PHY_PLL_CTL);
|
||||
reg |= USB_PHY_PLL_CTL_PLL_SUSPEND_MASK;
|
||||
brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_CTL);
|
||||
|
||||
/* Init the PHY */
|
||||
reg = USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK |
|
||||
USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK |
|
||||
@ -276,7 +282,7 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params)
|
||||
/* Set the PHY_MODE */
|
||||
reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1);
|
||||
reg &= ~USB_PHY_UTMI_CTL_1_PHY_MODE_MASK;
|
||||
reg |= params->mode << USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT;
|
||||
reg |= params->supported_port_modes << USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT;
|
||||
brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1);
|
||||
|
||||
usb_init_common(params);
|
||||
@ -286,7 +292,7 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params)
|
||||
* the default "Read Transaction Size" of 6 (1024 bytes).
|
||||
* Set it to 4 (256 bytes).
|
||||
*/
|
||||
if ((params->mode != USB_CTLR_MODE_HOST) && bdc_ec) {
|
||||
if ((params->supported_port_modes != USB_CTLR_MODE_HOST) && bdc_ec) {
|
||||
reg = brcm_usb_readl(bdc_ec + BDC_EC_AXIRDA);
|
||||
reg &= ~BDC_EC_AXIRDA_RTS_MASK;
|
||||
reg |= (0x4 << BDC_EC_AXIRDA_RTS_SHIFT);
|
||||
@ -331,13 +337,12 @@ static void usb_uninit_common_7216(struct brcm_usb_init_params *params)
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (!params->wake_enabled) {
|
||||
USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
|
||||
|
||||
if (params->wake_enabled) {
|
||||
/* Switch to using slower clock during suspend to save power */
|
||||
USB_CTRL_SET(ctrl, USB_PM, XHC_S2_CLK_SWITCH_EN);
|
||||
} else {
|
||||
usb_wake_enable_7216(params, true);
|
||||
} else {
|
||||
USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,7 +390,7 @@ static int usb_get_dual_select(struct brcm_usb_init_params *params)
|
||||
return reg;
|
||||
}
|
||||
|
||||
static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
|
||||
static void usb_set_dual_select(struct brcm_usb_init_params *params)
|
||||
{
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
u32 reg;
|
||||
@ -394,7 +399,7 @@ static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
|
||||
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
|
||||
reg |= mode;
|
||||
reg |= params->port_mode;
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
}
|
||||
|
||||
@ -425,7 +430,6 @@ void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params)
|
||||
|
||||
params->family_name = "7216";
|
||||
params->ops = &bcm7216_ops;
|
||||
params->suspend_with_clocks = true;
|
||||
}
|
||||
|
||||
void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params)
|
||||
@ -435,5 +439,4 @@ void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params)
|
||||
|
||||
params->family_name = "7211";
|
||||
params->ops = &bcm7211b0_ops;
|
||||
params->suspend_with_clocks = true;
|
||||
}
|
||||
|
@ -21,57 +21,57 @@
|
||||
|
||||
/* Register definitions for the USB CTRL block */
|
||||
#define USB_CTRL_SETUP 0x00
|
||||
#define USB_CTRL_SETUP_IOC_MASK 0x00000010
|
||||
#define USB_CTRL_SETUP_IPP_MASK 0x00000020
|
||||
#define USB_CTRL_SETUP_BABO_MASK 0x00000001
|
||||
#define USB_CTRL_SETUP_FNHW_MASK 0x00000002
|
||||
#define USB_CTRL_SETUP_FNBO_MASK 0x00000004
|
||||
#define USB_CTRL_SETUP_WABO_MASK 0x00000008
|
||||
#define USB_CTRL_SETUP_SCB_CLIENT_SWAP_MASK 0x00002000 /* option */
|
||||
#define USB_CTRL_SETUP_SCB1_EN_MASK 0x00004000 /* option */
|
||||
#define USB_CTRL_SETUP_SCB2_EN_MASK 0x00008000 /* option */
|
||||
#define USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK 0X00020000 /* option */
|
||||
#define USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK 0x00010000 /* option */
|
||||
#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK 0x02000000 /* option */
|
||||
#define USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK 0x04000000 /* option */
|
||||
#define USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK 0x08000000 /* opt */
|
||||
#define USB_CTRL_SETUP_OC3_DISABLE_MASK 0xc0000000 /* option */
|
||||
#define USB_CTRL_SETUP_BABO_MASK BIT(0)
|
||||
#define USB_CTRL_SETUP_FNHW_MASK BIT(1)
|
||||
#define USB_CTRL_SETUP_FNBO_MASK BIT(2)
|
||||
#define USB_CTRL_SETUP_WABO_MASK BIT(3)
|
||||
#define USB_CTRL_SETUP_IOC_MASK BIT(4)
|
||||
#define USB_CTRL_SETUP_IPP_MASK BIT(5)
|
||||
#define USB_CTRL_SETUP_SCB_CLIENT_SWAP_MASK BIT(13) /* option */
|
||||
#define USB_CTRL_SETUP_SCB1_EN_MASK BIT(14) /* option */
|
||||
#define USB_CTRL_SETUP_SCB2_EN_MASK BIT(15) /* option */
|
||||
#define USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK BIT(17) /* option */
|
||||
#define USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK BIT(16) /* option */
|
||||
#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK BIT(25) /* option */
|
||||
#define USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK BIT(26) /* option */
|
||||
#define USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK BIT(27) /* opt */
|
||||
#define USB_CTRL_SETUP_OC3_DISABLE_MASK GENMASK(31, 30) /* option */
|
||||
#define USB_CTRL_PLL_CTL 0x04
|
||||
#define USB_CTRL_PLL_CTL_PLL_SUSPEND_EN_MASK 0x08000000
|
||||
#define USB_CTRL_PLL_CTL_PLL_RESETB_MASK 0x40000000
|
||||
#define USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK 0x80000000 /* option */
|
||||
#define USB_CTRL_PLL_CTL_PLL_SUSPEND_EN_MASK BIT(27)
|
||||
#define USB_CTRL_PLL_CTL_PLL_RESETB_MASK BIT(30)
|
||||
#define USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK BIT(31) /* option */
|
||||
#define USB_CTRL_EBRIDGE 0x0c
|
||||
#define USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK 0x00020000 /* option */
|
||||
#define USB_CTRL_EBRIDGE_EBR_SCB_SIZE_MASK 0x00000f80 /* option */
|
||||
#define USB_CTRL_EBRIDGE_EBR_SCB_SIZE_MASK GENMASK(11, 7) /* option */
|
||||
#define USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK BIT(17) /* option */
|
||||
#define USB_CTRL_OBRIDGE 0x10
|
||||
#define USB_CTRL_OBRIDGE_LS_KEEP_ALIVE_MASK 0x08000000
|
||||
#define USB_CTRL_OBRIDGE_LS_KEEP_ALIVE_MASK BIT(27)
|
||||
#define USB_CTRL_MDIO 0x14
|
||||
#define USB_CTRL_MDIO2 0x18
|
||||
#define USB_CTRL_UTMI_CTL_1 0x2c
|
||||
#define USB_CTRL_UTMI_CTL_1_POWER_UP_FSM_EN_MASK 0x00000800
|
||||
#define USB_CTRL_UTMI_CTL_1_POWER_UP_FSM_EN_P1_MASK 0x08000000
|
||||
#define USB_CTRL_UTMI_CTL_1_POWER_UP_FSM_EN_MASK BIT(11)
|
||||
#define USB_CTRL_UTMI_CTL_1_POWER_UP_FSM_EN_P1_MASK BIT(27)
|
||||
#define USB_CTRL_USB_PM 0x34
|
||||
#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000 /* option */
|
||||
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000 /* option */
|
||||
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK 0x40000000 /* option */
|
||||
#define USB_CTRL_USB_PM_USB_PWRDN_MASK 0x80000000 /* option */
|
||||
#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 /* option */
|
||||
#define USB_CTRL_USB_PM_USB20_HC_RESETB_MASK 0x30000000 /* option */
|
||||
#define USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK 0x00300000 /* option */
|
||||
#define USB_CTRL_USB_PM_RMTWKUP_EN_MASK 0x00000001
|
||||
#define USB_CTRL_USB_PM_RMTWKUP_EN_MASK BIT(0)
|
||||
#define USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK GENMASK(21, 20) /* option */
|
||||
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK BIT(22) /* option */
|
||||
#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK BIT(23) /* option */
|
||||
#define USB_CTRL_USB_PM_USB20_HC_RESETB_MASK GENMASK(29, 28) /* option */
|
||||
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK BIT(30) /* option */
|
||||
#define USB_CTRL_USB_PM_SOFT_RESET_MASK BIT(30) /* option */
|
||||
#define USB_CTRL_USB_PM_USB_PWRDN_MASK BIT(31) /* option */
|
||||
#define USB_CTRL_USB_PM_STATUS 0x38
|
||||
#define USB_CTRL_USB30_CTL1 0x60
|
||||
#define USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK 0x00000010
|
||||
#define USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK 0x00010000
|
||||
#define USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK 0x00020000 /* option */
|
||||
#define USB_CTRL_USB30_CTL1_USB3_IOC_MASK 0x10000000 /* option */
|
||||
#define USB_CTRL_USB30_CTL1_USB3_IPP_MASK 0x20000000 /* option */
|
||||
#define USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK BIT(4)
|
||||
#define USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK BIT(16)
|
||||
#define USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK BIT(17) /* option */
|
||||
#define USB_CTRL_USB30_CTL1_USB3_IOC_MASK BIT(28) /* option */
|
||||
#define USB_CTRL_USB30_CTL1_USB3_IPP_MASK BIT(29) /* option */
|
||||
#define USB_CTRL_USB30_PCTL 0x70
|
||||
#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_MASK 0x00000002
|
||||
#define USB_CTRL_USB30_PCTL_PHY3_IDDQ_OVERRIDE_MASK 0x00008000
|
||||
#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_P1_MASK 0x00020000
|
||||
#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_MASK BIT(1)
|
||||
#define USB_CTRL_USB30_PCTL_PHY3_IDDQ_OVERRIDE_MASK BIT(15)
|
||||
#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_P1_MASK BIT(17)
|
||||
#define USB_CTRL_USB_DEVICE_CTL1 0x90
|
||||
#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003 /* option */
|
||||
#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK GENMASK(1, 0) /* option */
|
||||
|
||||
/* Register definitions for the XHCI EC block */
|
||||
#define USB_XHCI_EC_IRAADR 0x658
|
||||
@ -876,11 +876,11 @@ static void usb_init_common(struct brcm_usb_init_params *params)
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
|
||||
PORT_MODE);
|
||||
reg |= params->mode;
|
||||
reg |= params->port_mode;
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
}
|
||||
if (USB_CTRL_MASK_FAMILY(params, USB_PM, BDC_SOFT_RESETB)) {
|
||||
switch (params->mode) {
|
||||
switch (params->supported_port_modes) {
|
||||
case USB_CTLR_MODE_HOST:
|
||||
USB_CTRL_UNSET_FAMILY(params, USB_PM, BDC_SOFT_RESETB);
|
||||
break;
|
||||
@ -891,7 +891,7 @@ static void usb_init_common(struct brcm_usb_init_params *params)
|
||||
}
|
||||
}
|
||||
if (USB_CTRL_MASK_FAMILY(params, SETUP, CC_DRD_MODE_ENABLE)) {
|
||||
if (params->mode == USB_CTLR_MODE_TYPEC_PD)
|
||||
if (params->supported_port_modes == USB_CTLR_MODE_TYPEC_PD)
|
||||
USB_CTRL_SET_FAMILY(params, SETUP, CC_DRD_MODE_ENABLE);
|
||||
else
|
||||
USB_CTRL_UNSET_FAMILY(params, SETUP,
|
||||
@ -1000,7 +1000,7 @@ static int usb_get_dual_select(struct brcm_usb_init_params *params)
|
||||
return reg;
|
||||
}
|
||||
|
||||
static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
|
||||
static void usb_set_dual_select(struct brcm_usb_init_params *params)
|
||||
{
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
u32 reg;
|
||||
@ -1011,7 +1011,7 @@ static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
|
||||
PORT_MODE);
|
||||
reg |= mode;
|
||||
reg |= params->port_mode;
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
}
|
||||
}
|
||||
|
@ -45,14 +45,15 @@ struct brcm_usb_init_ops {
|
||||
void (*uninit_eohci)(struct brcm_usb_init_params *params);
|
||||
void (*uninit_xhci)(struct brcm_usb_init_params *params);
|
||||
int (*get_dual_select)(struct brcm_usb_init_params *params);
|
||||
void (*set_dual_select)(struct brcm_usb_init_params *params, int mode);
|
||||
void (*set_dual_select)(struct brcm_usb_init_params *params);
|
||||
};
|
||||
|
||||
struct brcm_usb_init_params {
|
||||
void __iomem *regs[BRCM_REGS_MAX];
|
||||
int ioc;
|
||||
int ipp;
|
||||
int mode;
|
||||
int supported_port_modes;
|
||||
int port_mode;
|
||||
u32 family_id;
|
||||
u32 product_id;
|
||||
int selected_family;
|
||||
@ -61,7 +62,6 @@ struct brcm_usb_init_params {
|
||||
const struct brcm_usb_init_ops *ops;
|
||||
struct regmap *syscon_piarbctl;
|
||||
bool wake_enabled;
|
||||
bool suspend_with_clocks;
|
||||
};
|
||||
|
||||
void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params);
|
||||
@ -153,11 +153,10 @@ static inline int brcm_usb_get_dual_select(struct brcm_usb_init_params *ini)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void brcm_usb_set_dual_select(struct brcm_usb_init_params *ini,
|
||||
int mode)
|
||||
static inline void brcm_usb_set_dual_select(struct brcm_usb_init_params *ini)
|
||||
{
|
||||
if (ini->ops->set_dual_select)
|
||||
ini->ops->set_dual_select(ini, mode);
|
||||
ini->ops->set_dual_select(ini);
|
||||
}
|
||||
|
||||
#endif /* _USB_BRCM_COMMON_INIT_H */
|
||||
|
@ -102,9 +102,9 @@ static int brcm_pm_notifier(struct notifier_block *notifier,
|
||||
|
||||
static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct phy *gphy = dev_id;
|
||||
struct device *dev = dev_id;
|
||||
|
||||
pm_wakeup_event(&gphy->dev, 0);
|
||||
pm_wakeup_event(dev, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -233,7 +233,7 @@ static ssize_t dr_mode_show(struct device *dev,
|
||||
return sprintf(buf, "%s\n",
|
||||
value_to_name(&brcm_dr_mode_to_name[0],
|
||||
ARRAY_SIZE(brcm_dr_mode_to_name),
|
||||
priv->ini.mode));
|
||||
priv->ini.supported_port_modes));
|
||||
}
|
||||
static DEVICE_ATTR_RO(dr_mode);
|
||||
|
||||
@ -249,7 +249,8 @@ static ssize_t dual_select_store(struct device *dev,
|
||||
res = name_to_value(&brcm_dual_mode_to_name[0],
|
||||
ARRAY_SIZE(brcm_dual_mode_to_name), buf, &value);
|
||||
if (!res) {
|
||||
brcm_usb_set_dual_select(&priv->ini, value);
|
||||
priv->ini.port_mode = value;
|
||||
brcm_usb_set_dual_select(&priv->ini);
|
||||
res = len;
|
||||
}
|
||||
mutex_unlock(&sysfs_lock);
|
||||
@ -445,13 +446,13 @@ static int brcm_usb_phy_dvr_init(struct platform_device *pdev,
|
||||
priv->suspend_clk = NULL;
|
||||
}
|
||||
|
||||
priv->wake_irq = platform_get_irq_byname(pdev, "wake");
|
||||
priv->wake_irq = platform_get_irq_byname_optional(pdev, "wake");
|
||||
if (priv->wake_irq < 0)
|
||||
priv->wake_irq = platform_get_irq_byname(pdev, "wakeup");
|
||||
priv->wake_irq = platform_get_irq_byname_optional(pdev, "wakeup");
|
||||
if (priv->wake_irq >= 0) {
|
||||
err = devm_request_irq(dev, priv->wake_irq,
|
||||
brcm_usb_phy_wake_isr, 0,
|
||||
dev_name(dev), gphy);
|
||||
dev_name(dev), dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
device_set_wakeup_capable(dev, 1);
|
||||
@ -495,13 +496,16 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
|
||||
of_property_read_u32(dn, "brcm,ipp", &priv->ini.ipp);
|
||||
of_property_read_u32(dn, "brcm,ioc", &priv->ini.ioc);
|
||||
|
||||
priv->ini.mode = USB_CTLR_MODE_HOST;
|
||||
priv->ini.supported_port_modes = USB_CTLR_MODE_HOST;
|
||||
err = of_property_read_string(dn, "dr_mode", &mode);
|
||||
if (err == 0) {
|
||||
name_to_value(&brcm_dr_mode_to_name[0],
|
||||
ARRAY_SIZE(brcm_dr_mode_to_name),
|
||||
mode, &priv->ini.mode);
|
||||
mode, &priv->ini.supported_port_modes);
|
||||
}
|
||||
/* Default port_mode to supported port_modes */
|
||||
priv->ini.port_mode = priv->ini.supported_port_modes;
|
||||
|
||||
if (of_property_read_bool(dn, "brcm,has-xhci"))
|
||||
priv->has_xhci = true;
|
||||
if (of_property_read_bool(dn, "brcm,has-eohci"))
|
||||
@ -539,7 +543,7 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
|
||||
* Create sysfs entries for mode.
|
||||
* Remove "dual_select" attribute if not in dual mode
|
||||
*/
|
||||
if (priv->ini.mode != USB_CTLR_MODE_DRD)
|
||||
if (priv->ini.supported_port_modes != USB_CTLR_MODE_DRD)
|
||||
brcm_usb_phy_attrs[1] = NULL;
|
||||
err = sysfs_create_group(&dev->kobj, &brcm_usb_phy_group);
|
||||
if (err)
|
||||
@ -598,7 +602,7 @@ static int brcm_usb_phy_suspend(struct device *dev)
|
||||
* and newer XHCI->2.0-clks/3.0-clks.
|
||||
*/
|
||||
|
||||
if (!priv->ini.suspend_with_clocks) {
|
||||
if (!priv->ini.wake_enabled) {
|
||||
if (priv->phys[BRCM_USB_PHY_3_0].inited)
|
||||
clk_disable_unprepare(priv->usb_30_clk);
|
||||
if (priv->phys[BRCM_USB_PHY_2_0].inited ||
|
||||
@ -615,8 +619,10 @@ static int brcm_usb_phy_resume(struct device *dev)
|
||||
{
|
||||
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
|
||||
|
||||
clk_prepare_enable(priv->usb_20_clk);
|
||||
clk_prepare_enable(priv->usb_30_clk);
|
||||
if (!priv->ini.wake_enabled) {
|
||||
clk_prepare_enable(priv->usb_20_clk);
|
||||
clk_prepare_enable(priv->usb_30_clk);
|
||||
}
|
||||
brcm_usb_init_ipp(&priv->ini);
|
||||
|
||||
/*
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mfd/syscon/imx7-iomuxc-gpr.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
@ -31,12 +32,10 @@
|
||||
#define IMX8MM_PCIE_PHY_CMN_REG065 0x194
|
||||
#define ANA_AUX_RX_TERM (BIT(7) | BIT(4))
|
||||
#define ANA_AUX_TX_LVL GENMASK(3, 0)
|
||||
#define IMX8MM_PCIE_PHY_CMN_REG75 0x1D4
|
||||
#define PCIE_PHY_CMN_REG75_PLL_DONE 0x3
|
||||
#define IMX8MM_PCIE_PHY_CMN_REG075 0x1D4
|
||||
#define ANA_PLL_DONE 0x3
|
||||
#define PCIE_PHY_TRSV_REG5 0x414
|
||||
#define PCIE_PHY_TRSV_REG5_GEN1_DEEMP 0x2D
|
||||
#define PCIE_PHY_TRSV_REG6 0x418
|
||||
#define PCIE_PHY_TRSV_REG6_GEN2_DEEMP 0xF
|
||||
|
||||
#define IMX8MM_GPR_PCIE_REF_CLK_SEL GENMASK(25, 24)
|
||||
#define IMX8MM_GPR_PCIE_REF_CLK_PLL FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x3)
|
||||
@ -47,16 +46,28 @@
|
||||
#define IMX8MM_GPR_PCIE_SSC_EN BIT(16)
|
||||
#define IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE BIT(9)
|
||||
|
||||
enum imx8_pcie_phy_type {
|
||||
IMX8MM,
|
||||
IMX8MP,
|
||||
};
|
||||
|
||||
struct imx8_pcie_phy_drvdata {
|
||||
const char *gpr;
|
||||
enum imx8_pcie_phy_type variant;
|
||||
};
|
||||
|
||||
struct imx8_pcie_phy {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct phy *phy;
|
||||
struct regmap *iomuxc_gpr;
|
||||
struct reset_control *perst;
|
||||
struct reset_control *reset;
|
||||
u32 refclk_pad_mode;
|
||||
u32 tx_deemph_gen1;
|
||||
u32 tx_deemph_gen2;
|
||||
bool clkreq_unused;
|
||||
const struct imx8_pcie_phy_drvdata *drvdata;
|
||||
};
|
||||
|
||||
static int imx8_pcie_phy_power_on(struct phy *phy)
|
||||
@ -65,34 +76,22 @@ static int imx8_pcie_phy_power_on(struct phy *phy)
|
||||
u32 val, pad_mode;
|
||||
struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
|
||||
|
||||
reset_control_assert(imx8_phy->reset);
|
||||
|
||||
pad_mode = imx8_phy->refclk_pad_mode;
|
||||
/* Set AUX_EN_OVERRIDE 1'b0, when the CLKREQ# isn't hooked */
|
||||
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
|
||||
IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE,
|
||||
imx8_phy->clkreq_unused ?
|
||||
0 : IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE);
|
||||
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
|
||||
IMX8MM_GPR_PCIE_AUX_EN,
|
||||
IMX8MM_GPR_PCIE_AUX_EN);
|
||||
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
|
||||
IMX8MM_GPR_PCIE_POWER_OFF, 0);
|
||||
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
|
||||
IMX8MM_GPR_PCIE_SSC_EN, 0);
|
||||
switch (imx8_phy->drvdata->variant) {
|
||||
case IMX8MM:
|
||||
reset_control_assert(imx8_phy->reset);
|
||||
|
||||
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
|
||||
IMX8MM_GPR_PCIE_REF_CLK_SEL,
|
||||
pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ?
|
||||
IMX8MM_GPR_PCIE_REF_CLK_EXT :
|
||||
IMX8MM_GPR_PCIE_REF_CLK_PLL);
|
||||
usleep_range(100, 200);
|
||||
|
||||
/* Do the PHY common block reset */
|
||||
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
|
||||
IMX8MM_GPR_PCIE_CMN_RST,
|
||||
IMX8MM_GPR_PCIE_CMN_RST);
|
||||
usleep_range(200, 500);
|
||||
/* Tune PHY de-emphasis setting to pass PCIe compliance. */
|
||||
if (imx8_phy->tx_deemph_gen1)
|
||||
writel(imx8_phy->tx_deemph_gen1,
|
||||
imx8_phy->base + PCIE_PHY_TRSV_REG5);
|
||||
if (imx8_phy->tx_deemph_gen2)
|
||||
writel(imx8_phy->tx_deemph_gen2,
|
||||
imx8_phy->base + PCIE_PHY_TRSV_REG6);
|
||||
break;
|
||||
case IMX8MP: /* Do nothing. */
|
||||
break;
|
||||
}
|
||||
|
||||
if (pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ||
|
||||
pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) {
|
||||
@ -120,20 +119,44 @@ static int imx8_pcie_phy_power_on(struct phy *phy)
|
||||
imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG065);
|
||||
}
|
||||
|
||||
/* Tune PHY de-emphasis setting to pass PCIe compliance. */
|
||||
if (imx8_phy->tx_deemph_gen1)
|
||||
writel(imx8_phy->tx_deemph_gen1,
|
||||
imx8_phy->base + PCIE_PHY_TRSV_REG5);
|
||||
if (imx8_phy->tx_deemph_gen2)
|
||||
writel(imx8_phy->tx_deemph_gen2,
|
||||
imx8_phy->base + PCIE_PHY_TRSV_REG6);
|
||||
/* Set AUX_EN_OVERRIDE 1'b0, when the CLKREQ# isn't hooked */
|
||||
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
|
||||
IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE,
|
||||
imx8_phy->clkreq_unused ?
|
||||
0 : IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE);
|
||||
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
|
||||
IMX8MM_GPR_PCIE_AUX_EN,
|
||||
IMX8MM_GPR_PCIE_AUX_EN);
|
||||
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
|
||||
IMX8MM_GPR_PCIE_POWER_OFF, 0);
|
||||
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
|
||||
IMX8MM_GPR_PCIE_SSC_EN, 0);
|
||||
|
||||
reset_control_deassert(imx8_phy->reset);
|
||||
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
|
||||
IMX8MM_GPR_PCIE_REF_CLK_SEL,
|
||||
pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ?
|
||||
IMX8MM_GPR_PCIE_REF_CLK_EXT :
|
||||
IMX8MM_GPR_PCIE_REF_CLK_PLL);
|
||||
usleep_range(100, 200);
|
||||
|
||||
/* Do the PHY common block reset */
|
||||
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
|
||||
IMX8MM_GPR_PCIE_CMN_RST,
|
||||
IMX8MM_GPR_PCIE_CMN_RST);
|
||||
|
||||
switch (imx8_phy->drvdata->variant) {
|
||||
case IMX8MP:
|
||||
reset_control_deassert(imx8_phy->perst);
|
||||
fallthrough;
|
||||
case IMX8MM:
|
||||
reset_control_deassert(imx8_phy->reset);
|
||||
usleep_range(200, 500);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Polling to check the phy is ready or not. */
|
||||
ret = readl_poll_timeout(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG75,
|
||||
val, val == PCIE_PHY_CMN_REG75_PLL_DONE,
|
||||
10, 20000);
|
||||
ret = readl_poll_timeout(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG075,
|
||||
val, val == ANA_PLL_DONE, 10, 20000);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -160,6 +183,23 @@ static const struct phy_ops imx8_pcie_phy_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct imx8_pcie_phy_drvdata imx8mm_drvdata = {
|
||||
.gpr = "fsl,imx8mm-iomuxc-gpr",
|
||||
.variant = IMX8MM,
|
||||
};
|
||||
|
||||
static const struct imx8_pcie_phy_drvdata imx8mp_drvdata = {
|
||||
.gpr = "fsl,imx8mp-iomuxc-gpr",
|
||||
.variant = IMX8MP,
|
||||
};
|
||||
|
||||
static const struct of_device_id imx8_pcie_phy_of_match[] = {
|
||||
{.compatible = "fsl,imx8mm-pcie-phy", .data = &imx8mm_drvdata, },
|
||||
{.compatible = "fsl,imx8mp-pcie-phy", .data = &imx8mp_drvdata, },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx8_pcie_phy_of_match);
|
||||
|
||||
static int imx8_pcie_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
@ -172,6 +212,8 @@ static int imx8_pcie_phy_probe(struct platform_device *pdev)
|
||||
if (!imx8_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
imx8_phy->drvdata = of_device_get_match_data(dev);
|
||||
|
||||
/* get PHY refclk pad mode */
|
||||
of_property_read_u32(np, "fsl,refclk-pad-mode",
|
||||
&imx8_phy->refclk_pad_mode);
|
||||
@ -197,7 +239,7 @@ static int imx8_pcie_phy_probe(struct platform_device *pdev)
|
||||
|
||||
/* Grab GPR config register range */
|
||||
imx8_phy->iomuxc_gpr =
|
||||
syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
|
||||
syscon_regmap_lookup_by_compatible(imx8_phy->drvdata->gpr);
|
||||
if (IS_ERR(imx8_phy->iomuxc_gpr)) {
|
||||
dev_err(dev, "unable to find iomuxc registers\n");
|
||||
return PTR_ERR(imx8_phy->iomuxc_gpr);
|
||||
@ -209,6 +251,14 @@ static int imx8_pcie_phy_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(imx8_phy->reset);
|
||||
}
|
||||
|
||||
if (imx8_phy->drvdata->variant == IMX8MP) {
|
||||
imx8_phy->perst =
|
||||
devm_reset_control_get_exclusive(dev, "perst");
|
||||
if (IS_ERR(imx8_phy->perst))
|
||||
dev_err_probe(dev, PTR_ERR(imx8_phy->perst),
|
||||
"Failed to get PCIE PHY PERST control\n");
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
imx8_phy->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(imx8_phy->base))
|
||||
@ -225,12 +275,6 @@ static int imx8_pcie_phy_probe(struct platform_device *pdev)
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id imx8_pcie_phy_of_match[] = {
|
||||
{.compatible = "fsl,imx8mm-pcie-phy",},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx8_pcie_phy_of_match);
|
||||
|
||||
static struct platform_driver imx8_pcie_phy_driver = {
|
||||
.probe = imx8_pcie_phy_probe,
|
||||
.driver = {
|
||||
|
@ -41,12 +41,10 @@ static int mmp3_hsic_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy_provider *provider;
|
||||
struct resource *resource;
|
||||
void __iomem *base;
|
||||
struct phy *phy;
|
||||
|
||||
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, resource);
|
||||
base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
|
@ -826,6 +826,9 @@ mvebu_a3700_comphy_usb3_power_on(struct mvebu_a3700_comphy_lane *lane)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* COMPHY register reset (cleared automatically) */
|
||||
comphy_lane_reg_set(lane, COMPHY_SFT_RESET, SFT_RST, SFT_RST);
|
||||
|
||||
/*
|
||||
* 0. Set PHY OTG Control(0x5d034), bit 4, Power up OTG module The
|
||||
* register belong to UTMI module, so it is set in UTMI phy driver.
|
||||
|
@ -54,6 +54,7 @@ config PHY_QCOM_QMP
|
||||
tristate "Qualcomm QMP PHY Driver"
|
||||
depends on OF && COMMON_CLK && (ARCH_QCOM || COMPILE_TEST)
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Enable this to support the QMP PHY transceiver that is used
|
||||
with controllers such as PCIe, UFS, and USB on Qualcomm chips.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -20,8 +20,6 @@
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
|
||||
#include "phy-qcom-qmp.h"
|
||||
|
||||
/* QPHY_SW_RESET bit */
|
||||
@ -35,22 +33,16 @@
|
||||
#define PLL_READY_GATE_EN BIT(3)
|
||||
/* QPHY_PCS_STATUS bit */
|
||||
#define PHYSTATUS BIT(6)
|
||||
#define PHYSTATUS_4_20 BIT(7)
|
||||
/* QPHY_COM_PCS_READY_STATUS bit */
|
||||
#define PCS_READY BIT(0)
|
||||
|
||||
#define PHY_INIT_COMPLETE_TIMEOUT 10000
|
||||
#define POWER_DOWN_DELAY_US_MIN 10
|
||||
#define POWER_DOWN_DELAY_US_MAX 11
|
||||
#define POWER_DOWN_DELAY_US_MAX 20
|
||||
|
||||
struct qmp_phy_init_tbl {
|
||||
unsigned int offset;
|
||||
unsigned int val;
|
||||
/*
|
||||
* register part of layout ?
|
||||
* if yes, then offset gives index in the reg-layout
|
||||
*/
|
||||
bool in_layout;
|
||||
/*
|
||||
* mask of lanes for which this register is written
|
||||
* for cases when second lane needs different values
|
||||
@ -65,14 +57,6 @@ struct qmp_phy_init_tbl {
|
||||
.lane_mask = 0xff, \
|
||||
}
|
||||
|
||||
#define QMP_PHY_INIT_CFG_L(o, v) \
|
||||
{ \
|
||||
.offset = o, \
|
||||
.val = v, \
|
||||
.in_layout = true, \
|
||||
.lane_mask = 0xff, \
|
||||
}
|
||||
|
||||
#define QMP_PHY_INIT_CFG_LANE(o, v, l) \
|
||||
{ \
|
||||
.offset = o, \
|
||||
@ -91,7 +75,6 @@ enum qphy_reg_layout {
|
||||
QPHY_SW_RESET,
|
||||
QPHY_START_CTRL,
|
||||
QPHY_PCS_STATUS,
|
||||
QPHY_PCS_POWER_DOWN_CONTROL,
|
||||
/* Keep last to ensure regs_layout arrays are properly initialized */
|
||||
QPHY_LAYOUT_SIZE
|
||||
};
|
||||
@ -211,18 +194,6 @@ struct qmp_phy_cfg {
|
||||
|
||||
/* array of registers with different offsets */
|
||||
const unsigned int *regs;
|
||||
|
||||
unsigned int start_ctrl;
|
||||
unsigned int pwrdn_ctrl;
|
||||
unsigned int mask_com_pcs_ready;
|
||||
/* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */
|
||||
unsigned int phy_status;
|
||||
|
||||
/* true, if PHY needs delay after POWER_DOWN */
|
||||
bool has_pwrdn_delay;
|
||||
/* power_down delay in usec */
|
||||
int pwrdn_delay_min;
|
||||
int pwrdn_delay_max;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -335,19 +306,9 @@ static const struct qmp_phy_cfg msm8996_pciephy_cfg = {
|
||||
.vreg_list = qmp_phy_vreg_l,
|
||||
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
|
||||
.regs = pciephy_regs_layout,
|
||||
|
||||
.start_ctrl = PCS_START | PLL_READY_GATE_EN,
|
||||
.pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
|
||||
.mask_com_pcs_ready = PCS_READY,
|
||||
.phy_status = PHYSTATUS,
|
||||
|
||||
.has_pwrdn_delay = true,
|
||||
.pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
|
||||
.pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
|
||||
};
|
||||
|
||||
static void qmp_pcie_msm8996_configure_lane(void __iomem *base,
|
||||
const unsigned int *regs,
|
||||
const struct qmp_phy_init_tbl tbl[],
|
||||
int num,
|
||||
u8 lane_mask)
|
||||
@ -362,19 +323,15 @@ static void qmp_pcie_msm8996_configure_lane(void __iomem *base,
|
||||
if (!(t->lane_mask & lane_mask))
|
||||
continue;
|
||||
|
||||
if (t->in_layout)
|
||||
writel(t->val, base + regs[t->offset]);
|
||||
else
|
||||
writel(t->val, base + t->offset);
|
||||
writel(t->val, base + t->offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void qmp_pcie_msm8996_configure(void __iomem *base,
|
||||
const unsigned int *regs,
|
||||
const struct qmp_phy_init_tbl tbl[],
|
||||
int num)
|
||||
{
|
||||
qmp_pcie_msm8996_configure_lane(base, regs, tbl, num, 0xff);
|
||||
qmp_pcie_msm8996_configure_lane(base, tbl, num, 0xff);
|
||||
}
|
||||
|
||||
static int qmp_pcie_msm8996_serdes_init(struct qmp_phy *qphy)
|
||||
@ -385,19 +342,17 @@ static int qmp_pcie_msm8996_serdes_init(struct qmp_phy *qphy)
|
||||
const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl;
|
||||
int serdes_tbl_num = cfg->serdes_tbl_num;
|
||||
void __iomem *status;
|
||||
unsigned int mask, val;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
qmp_pcie_msm8996_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num);
|
||||
qmp_pcie_msm8996_configure(serdes, serdes_tbl, serdes_tbl_num);
|
||||
|
||||
qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET);
|
||||
qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
|
||||
SERDES_START | PCS_START);
|
||||
|
||||
status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS];
|
||||
mask = cfg->mask_com_pcs_ready;
|
||||
|
||||
ret = readl_poll_timeout(status, val, (val & mask), 10,
|
||||
ret = readl_poll_timeout(status, val, (val & PCS_READY), 200,
|
||||
PHY_INIT_COMPLETE_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_err(qmp->dev,
|
||||
@ -421,7 +376,6 @@ static int qmp_pcie_msm8996_com_init(struct qmp_phy *qphy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* turn on regulator supplies */
|
||||
ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
|
||||
if (ret) {
|
||||
dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
|
||||
@ -514,7 +468,7 @@ static int qmp_pcie_msm8996_power_on(struct phy *phy)
|
||||
void __iomem *rx = qphy->rx;
|
||||
void __iomem *pcs = qphy->pcs;
|
||||
void __iomem *status;
|
||||
unsigned int mask, val, ready;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
qmp_pcie_msm8996_serdes_init(qphy);
|
||||
@ -533,34 +487,28 @@ static int qmp_pcie_msm8996_power_on(struct phy *phy)
|
||||
}
|
||||
|
||||
/* Tx, Rx, and PCS configurations */
|
||||
qmp_pcie_msm8996_configure_lane(tx, cfg->regs, cfg->tx_tbl,
|
||||
cfg->tx_tbl_num, 1);
|
||||
|
||||
qmp_pcie_msm8996_configure_lane(rx, cfg->regs, cfg->rx_tbl,
|
||||
cfg->rx_tbl_num, 1);
|
||||
|
||||
qmp_pcie_msm8996_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
|
||||
qmp_pcie_msm8996_configure_lane(tx, cfg->tx_tbl, cfg->tx_tbl_num, 1);
|
||||
qmp_pcie_msm8996_configure_lane(rx, cfg->rx_tbl, cfg->rx_tbl_num, 1);
|
||||
qmp_pcie_msm8996_configure(pcs, cfg->pcs_tbl, cfg->pcs_tbl_num);
|
||||
|
||||
/*
|
||||
* Pull out PHY from POWER DOWN state.
|
||||
* This is active low enable signal to power-down PHY.
|
||||
*/
|
||||
qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
|
||||
qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
|
||||
SW_PWRDN | REFCLK_DRV_DSBL);
|
||||
|
||||
if (cfg->has_pwrdn_delay)
|
||||
usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
|
||||
usleep_range(POWER_DOWN_DELAY_US_MIN, POWER_DOWN_DELAY_US_MAX);
|
||||
|
||||
/* Pull PHY out of reset state */
|
||||
qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
|
||||
|
||||
/* start SerDes and Phy-Coding-Sublayer */
|
||||
qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
|
||||
qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL],
|
||||
PCS_START | PLL_READY_GATE_EN);
|
||||
|
||||
status = pcs + cfg->regs[QPHY_PCS_STATUS];
|
||||
mask = cfg->phy_status;
|
||||
ready = 0;
|
||||
|
||||
ret = readl_poll_timeout(status, val, (val & mask) == ready, 10,
|
||||
ret = readl_poll_timeout(status, val, !(val & PHYSTATUS), 200,
|
||||
PHY_INIT_COMPLETE_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_err(qmp->dev, "phy initialization timed-out\n");
|
||||
@ -588,16 +536,12 @@ static int qmp_pcie_msm8996_power_off(struct phy *phy)
|
||||
qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
|
||||
|
||||
/* stop SerDes and Phy-Coding-Sublayer */
|
||||
qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
|
||||
qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL],
|
||||
SERDES_START | PCS_START);
|
||||
|
||||
/* Put PHY into POWER DOWN state: active low */
|
||||
if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) {
|
||||
qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
|
||||
cfg->pwrdn_ctrl);
|
||||
} else {
|
||||
qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
|
||||
cfg->pwrdn_ctrl);
|
||||
}
|
||||
qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
|
||||
SW_PWRDN | REFCLK_DRV_DSBL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -777,7 +721,7 @@ static int qmp_pcie_msm8996_create(struct device *dev, struct device_node *np, i
|
||||
qphy->cfg = cfg;
|
||||
qphy->serdes = serdes;
|
||||
/*
|
||||
* Get memory resources for each phy lane:
|
||||
* Get memory resources for each PHY:
|
||||
* Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
|
||||
*/
|
||||
qphy->tx = devm_of_iomap(dev, np, 0, NULL);
|
||||
@ -851,12 +795,10 @@ static int qmp_pcie_msm8996_probe(struct platform_device *pdev)
|
||||
qmp->dev = dev;
|
||||
dev_set_drvdata(dev, qmp);
|
||||
|
||||
/* Get the specific init parameters of QMP phy */
|
||||
cfg = of_device_get_match_data(dev);
|
||||
if (!cfg)
|
||||
return -EINVAL;
|
||||
|
||||
/* per PHY serdes; usually located at base address */
|
||||
serdes = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(serdes))
|
||||
return PTR_ERR(serdes);
|
||||
@ -875,8 +817,7 @@ static int qmp_pcie_msm8996_probe(struct platform_device *pdev)
|
||||
|
||||
ret = qmp_pcie_msm8996_vreg_init(dev, cfg);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to get regulator supplies\n");
|
||||
return ret;
|
||||
|
||||
num = of_get_available_child_count(dev->of_node);
|
||||
/* do we have a rogue child node ? */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,6 +8,8 @@
|
||||
#define QCOM_PHY_QMP_PCS_PCIE_V5_H_
|
||||
|
||||
/* Only for QMP V5 PHY - PCS_PCIE registers */
|
||||
#define QPHY_V5_PCS_PCIE_POWER_STATE_CONFIG2 0x0c
|
||||
#define QPHY_V5_PCS_PCIE_POWER_STATE_CONFIG4 0x14
|
||||
#define QPHY_V5_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x20
|
||||
#define QPHY_V5_PCS_PCIE_INT_AUX_CLK_CONFIG1 0x54
|
||||
#define QPHY_V5_PCS_PCIE_OSC_DTCT_ACTIONS 0x94
|
||||
|
@ -8,8 +8,10 @@
|
||||
|
||||
/* Only for QMP V5_20 PHY - PCIe PCS registers */
|
||||
#define QPHY_V5_20_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x01c
|
||||
#define QPHY_V5_20_PCS_PCIE_OSC_DTCT_MODE2_CONFIG5 0x084
|
||||
#define QPHY_V5_20_PCS_PCIE_OSC_DTCT_ACTIONS 0x090
|
||||
#define QPHY_V5_20_PCS_PCIE_EQ_CONFIG1 0x0a0
|
||||
#define QPHY_V5_20_PCS_PCIE_PRESET_P10_POST 0x0e0
|
||||
#define QPHY_V5_20_PCS_PCIE_G4_EQ_CONFIG5 0x108
|
||||
#define QPHY_V5_20_PCS_PCIE_G4_PRE_GAIN 0x15c
|
||||
#define QPHY_V5_20_PCS_PCIE_RX_MARGINING_CONFIG3 0x184
|
||||
|
14
drivers/phy/qualcomm/phy-qcom-qmp-pcs-v5_20.h
Normal file
14
drivers/phy/qualcomm/phy-qcom-qmp-pcs-v5_20.h
Normal file
@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2022, Linaro Ltd.
|
||||
*/
|
||||
|
||||
#ifndef QCOM_PHY_QMP_PCS_V5_20_H_
|
||||
#define QCOM_PHY_QMP_PCS_V5_20_H_
|
||||
|
||||
#define QPHY_V5_20_PCS_G3S2_PRE_GAIN 0x170
|
||||
#define QPHY_V5_20_PCS_RX_SIGDET_LVL 0x188
|
||||
#define QPHY_V5_20_PCS_EQ_CONFIG4 0x1e0
|
||||
#define QPHY_V5_20_PCS_EQ_CONFIG5 0x1e4
|
||||
|
||||
#endif
|
@ -20,8 +20,6 @@
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
|
||||
#include "phy-qcom-qmp.h"
|
||||
|
||||
/* QPHY_SW_RESET bit */
|
||||
@ -31,8 +29,6 @@
|
||||
/* QPHY_START_CONTROL bits */
|
||||
#define SERDES_START BIT(0)
|
||||
#define PCS_START BIT(1)
|
||||
/* QPHY_PCS_STATUS bit */
|
||||
#define PHYSTATUS BIT(6)
|
||||
/* QPHY_PCS_READY_STATUS bit */
|
||||
#define PCS_READY BIT(0)
|
||||
|
||||
@ -41,11 +37,6 @@
|
||||
struct qmp_phy_init_tbl {
|
||||
unsigned int offset;
|
||||
unsigned int val;
|
||||
/*
|
||||
* register part of layout ?
|
||||
* if yes, then offset gives index in the reg-layout
|
||||
*/
|
||||
bool in_layout;
|
||||
/*
|
||||
* mask of lanes for which this register is written
|
||||
* for cases when second lane needs different values
|
||||
@ -60,14 +51,6 @@ struct qmp_phy_init_tbl {
|
||||
.lane_mask = 0xff, \
|
||||
}
|
||||
|
||||
#define QMP_PHY_INIT_CFG_L(o, v) \
|
||||
{ \
|
||||
.offset = o, \
|
||||
.val = v, \
|
||||
.in_layout = true, \
|
||||
.lane_mask = 0xff, \
|
||||
}
|
||||
|
||||
#define QMP_PHY_INIT_CFG_LANE(o, v, l) \
|
||||
{ \
|
||||
.offset = o, \
|
||||
@ -89,22 +72,26 @@ enum qphy_reg_layout {
|
||||
static const unsigned int msm8996_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
|
||||
[QPHY_START_CTRL] = 0x00,
|
||||
[QPHY_PCS_READY_STATUS] = 0x168,
|
||||
[QPHY_PCS_POWER_DOWN_CONTROL] = 0x04,
|
||||
};
|
||||
|
||||
static const unsigned int sdm845_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
|
||||
[QPHY_START_CTRL] = 0x00,
|
||||
[QPHY_PCS_READY_STATUS] = 0x160,
|
||||
[QPHY_PCS_POWER_DOWN_CONTROL] = 0x04,
|
||||
};
|
||||
|
||||
static const unsigned int sm6115_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
|
||||
[QPHY_START_CTRL] = 0x00,
|
||||
[QPHY_PCS_READY_STATUS] = 0x168,
|
||||
[QPHY_PCS_POWER_DOWN_CONTROL] = 0x04,
|
||||
};
|
||||
|
||||
static const unsigned int sm8150_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
|
||||
[QPHY_START_CTRL] = QPHY_V4_PCS_UFS_PHY_START,
|
||||
[QPHY_PCS_READY_STATUS] = QPHY_V4_PCS_UFS_READY_STATUS,
|
||||
[QPHY_SW_RESET] = QPHY_V4_PCS_UFS_SW_RESET,
|
||||
[QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V4_PCS_UFS_POWER_DOWN_CONTROL,
|
||||
};
|
||||
|
||||
static const struct qmp_phy_init_tbl msm8996_ufs_serdes_tbl[] = {
|
||||
@ -531,10 +518,21 @@ static const struct qmp_phy_init_tbl sm8350_ufsphy_pcs_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
|
||||
};
|
||||
|
||||
struct qmp_ufs_offsets {
|
||||
u16 serdes;
|
||||
u16 pcs;
|
||||
u16 tx;
|
||||
u16 rx;
|
||||
u16 tx2;
|
||||
u16 rx2;
|
||||
};
|
||||
|
||||
/* struct qmp_phy_cfg - per-PHY initialization config */
|
||||
struct qmp_phy_cfg {
|
||||
int lanes;
|
||||
|
||||
const struct qmp_ufs_offsets *offsets;
|
||||
|
||||
/* Init sequence for PHY blocks - serdes, tx, rx, pcs */
|
||||
const struct qmp_phy_init_tbl *serdes_tbl;
|
||||
int serdes_tbl_num;
|
||||
@ -555,63 +553,28 @@ struct qmp_phy_cfg {
|
||||
/* array of registers with different offsets */
|
||||
const unsigned int *regs;
|
||||
|
||||
unsigned int start_ctrl;
|
||||
unsigned int pwrdn_ctrl;
|
||||
/* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */
|
||||
unsigned int phy_status;
|
||||
|
||||
/* true, if PCS block has no separate SW_RESET register */
|
||||
bool no_pcs_sw_reset;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct qmp_phy - per-lane phy descriptor
|
||||
*
|
||||
* @phy: generic phy
|
||||
* @cfg: phy specific configuration
|
||||
* @serdes: iomapped memory space for phy's serdes (i.e. PLL)
|
||||
* @tx: iomapped memory space for lane's tx
|
||||
* @rx: iomapped memory space for lane's rx
|
||||
* @pcs: iomapped memory space for lane's pcs
|
||||
* @tx2: iomapped memory space for second lane's tx (in dual lane PHYs)
|
||||
* @rx2: iomapped memory space for second lane's rx (in dual lane PHYs)
|
||||
* @pcs_misc: iomapped memory space for lane's pcs_misc
|
||||
* @qmp: QMP phy to which this lane belongs
|
||||
*/
|
||||
struct qmp_phy {
|
||||
struct phy *phy;
|
||||
struct qmp_ufs {
|
||||
struct device *dev;
|
||||
|
||||
const struct qmp_phy_cfg *cfg;
|
||||
|
||||
void __iomem *serdes;
|
||||
void __iomem *pcs;
|
||||
void __iomem *pcs_misc;
|
||||
void __iomem *tx;
|
||||
void __iomem *rx;
|
||||
void __iomem *pcs;
|
||||
void __iomem *tx2;
|
||||
void __iomem *rx2;
|
||||
void __iomem *pcs_misc;
|
||||
struct qcom_qmp *qmp;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct qcom_qmp - structure holding QMP phy block attributes
|
||||
*
|
||||
* @dev: device
|
||||
*
|
||||
* @clks: array of clocks required by phy
|
||||
* @resets: array of resets required by phy
|
||||
* @vregs: regulator supplies bulk data
|
||||
*
|
||||
* @phys: array of per-lane phy descriptors
|
||||
* @ufs_reset: optional UFS PHY reset handle
|
||||
*/
|
||||
struct qcom_qmp {
|
||||
struct device *dev;
|
||||
|
||||
struct clk_bulk_data *clks;
|
||||
struct regulator_bulk_data *vregs;
|
||||
|
||||
struct qmp_phy **phys;
|
||||
|
||||
struct reset_control *ufs_reset;
|
||||
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
|
||||
@ -657,6 +620,15 @@ static const char * const qmp_phy_vreg_l[] = {
|
||||
"vdda-phy", "vdda-pll",
|
||||
};
|
||||
|
||||
static const struct qmp_ufs_offsets qmp_ufs_offsets_v5 = {
|
||||
.serdes = 0,
|
||||
.pcs = 0xc00,
|
||||
.tx = 0x400,
|
||||
.rx = 0x600,
|
||||
.tx2 = 0x800,
|
||||
.rx2 = 0xa00,
|
||||
};
|
||||
|
||||
static const struct qmp_phy_cfg msm8996_ufs_cfg = {
|
||||
.lanes = 1,
|
||||
|
||||
@ -675,13 +647,29 @@ static const struct qmp_phy_cfg msm8996_ufs_cfg = {
|
||||
|
||||
.regs = msm8996_ufsphy_regs_layout,
|
||||
|
||||
.start_ctrl = SERDES_START,
|
||||
.pwrdn_ctrl = SW_PWRDN,
|
||||
.phy_status = PHYSTATUS,
|
||||
|
||||
.no_pcs_sw_reset = true,
|
||||
};
|
||||
|
||||
static const struct qmp_phy_cfg sc8280xp_ufsphy_cfg = {
|
||||
.lanes = 2,
|
||||
|
||||
.offsets = &qmp_ufs_offsets_v5,
|
||||
|
||||
.serdes_tbl = sm8350_ufsphy_serdes_tbl,
|
||||
.serdes_tbl_num = ARRAY_SIZE(sm8350_ufsphy_serdes_tbl),
|
||||
.tx_tbl = sm8350_ufsphy_tx_tbl,
|
||||
.tx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_tx_tbl),
|
||||
.rx_tbl = sm8350_ufsphy_rx_tbl,
|
||||
.rx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_rx_tbl),
|
||||
.pcs_tbl = sm8350_ufsphy_pcs_tbl,
|
||||
.pcs_tbl_num = ARRAY_SIZE(sm8350_ufsphy_pcs_tbl),
|
||||
.clk_list = sdm845_ufs_phy_clk_l,
|
||||
.num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
|
||||
.vreg_list = qmp_phy_vreg_l,
|
||||
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
|
||||
.regs = sm8150_ufsphy_regs_layout,
|
||||
};
|
||||
|
||||
static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
|
||||
.lanes = 2,
|
||||
|
||||
@ -699,10 +687,6 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
|
||||
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
|
||||
.regs = sdm845_ufsphy_regs_layout,
|
||||
|
||||
.start_ctrl = SERDES_START,
|
||||
.pwrdn_ctrl = SW_PWRDN,
|
||||
.phy_status = PHYSTATUS,
|
||||
|
||||
.no_pcs_sw_reset = true,
|
||||
};
|
||||
|
||||
@ -723,9 +707,6 @@ static const struct qmp_phy_cfg sm6115_ufsphy_cfg = {
|
||||
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
|
||||
.regs = sm6115_ufsphy_regs_layout,
|
||||
|
||||
.start_ctrl = SERDES_START,
|
||||
.pwrdn_ctrl = SW_PWRDN,
|
||||
|
||||
.no_pcs_sw_reset = true,
|
||||
};
|
||||
|
||||
@ -745,10 +726,6 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = {
|
||||
.vreg_list = qmp_phy_vreg_l,
|
||||
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
|
||||
.regs = sm8150_ufsphy_regs_layout,
|
||||
|
||||
.start_ctrl = SERDES_START,
|
||||
.pwrdn_ctrl = SW_PWRDN,
|
||||
.phy_status = PHYSTATUS,
|
||||
};
|
||||
|
||||
static const struct qmp_phy_cfg sm8350_ufsphy_cfg = {
|
||||
@ -767,10 +744,6 @@ static const struct qmp_phy_cfg sm8350_ufsphy_cfg = {
|
||||
.vreg_list = qmp_phy_vreg_l,
|
||||
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
|
||||
.regs = sm8150_ufsphy_regs_layout,
|
||||
|
||||
.start_ctrl = SERDES_START,
|
||||
.pwrdn_ctrl = SW_PWRDN,
|
||||
.phy_status = PHYSTATUS,
|
||||
};
|
||||
|
||||
static const struct qmp_phy_cfg sm8450_ufsphy_cfg = {
|
||||
@ -789,14 +762,9 @@ static const struct qmp_phy_cfg sm8450_ufsphy_cfg = {
|
||||
.vreg_list = qmp_phy_vreg_l,
|
||||
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
|
||||
.regs = sm8150_ufsphy_regs_layout,
|
||||
|
||||
.start_ctrl = SERDES_START,
|
||||
.pwrdn_ctrl = SW_PWRDN,
|
||||
.phy_status = PHYSTATUS,
|
||||
};
|
||||
|
||||
static void qmp_ufs_configure_lane(void __iomem *base,
|
||||
const unsigned int *regs,
|
||||
const struct qmp_phy_init_tbl tbl[],
|
||||
int num,
|
||||
u8 lane_mask)
|
||||
@ -811,41 +779,35 @@ static void qmp_ufs_configure_lane(void __iomem *base,
|
||||
if (!(t->lane_mask & lane_mask))
|
||||
continue;
|
||||
|
||||
if (t->in_layout)
|
||||
writel(t->val, base + regs[t->offset]);
|
||||
else
|
||||
writel(t->val, base + t->offset);
|
||||
writel(t->val, base + t->offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void qmp_ufs_configure(void __iomem *base,
|
||||
const unsigned int *regs,
|
||||
const struct qmp_phy_init_tbl tbl[],
|
||||
int num)
|
||||
{
|
||||
qmp_ufs_configure_lane(base, regs, tbl, num, 0xff);
|
||||
qmp_ufs_configure_lane(base, tbl, num, 0xff);
|
||||
}
|
||||
|
||||
static int qmp_ufs_serdes_init(struct qmp_phy *qphy)
|
||||
static int qmp_ufs_serdes_init(struct qmp_ufs *qmp)
|
||||
{
|
||||
const struct qmp_phy_cfg *cfg = qphy->cfg;
|
||||
void __iomem *serdes = qphy->serdes;
|
||||
const struct qmp_phy_cfg *cfg = qmp->cfg;
|
||||
void __iomem *serdes = qmp->serdes;
|
||||
const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl;
|
||||
int serdes_tbl_num = cfg->serdes_tbl_num;
|
||||
|
||||
qmp_ufs_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num);
|
||||
qmp_ufs_configure(serdes, serdes_tbl, serdes_tbl_num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qmp_ufs_com_init(struct qmp_phy *qphy)
|
||||
static int qmp_ufs_com_init(struct qmp_ufs *qmp)
|
||||
{
|
||||
struct qcom_qmp *qmp = qphy->qmp;
|
||||
const struct qmp_phy_cfg *cfg = qphy->cfg;
|
||||
void __iomem *pcs = qphy->pcs;
|
||||
const struct qmp_phy_cfg *cfg = qmp->cfg;
|
||||
void __iomem *pcs = qmp->pcs;
|
||||
int ret;
|
||||
|
||||
/* turn on regulator supplies */
|
||||
ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
|
||||
if (ret) {
|
||||
dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
|
||||
@ -856,13 +818,7 @@ static int qmp_ufs_com_init(struct qmp_phy *qphy)
|
||||
if (ret)
|
||||
goto err_disable_regulators;
|
||||
|
||||
if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL])
|
||||
qphy_setbits(pcs,
|
||||
cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
|
||||
cfg->pwrdn_ctrl);
|
||||
else
|
||||
qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
|
||||
cfg->pwrdn_ctrl);
|
||||
qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], SW_PWRDN);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -872,10 +828,9 @@ err_disable_regulators:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmp_ufs_com_exit(struct qmp_phy *qphy)
|
||||
static int qmp_ufs_com_exit(struct qmp_ufs *qmp)
|
||||
{
|
||||
struct qcom_qmp *qmp = qphy->qmp;
|
||||
const struct qmp_phy_cfg *cfg = qphy->cfg;
|
||||
const struct qmp_phy_cfg *cfg = qmp->cfg;
|
||||
|
||||
reset_control_assert(qmp->ufs_reset);
|
||||
|
||||
@ -888,9 +843,8 @@ static int qmp_ufs_com_exit(struct qmp_phy *qphy)
|
||||
|
||||
static int qmp_ufs_init(struct phy *phy)
|
||||
{
|
||||
struct qmp_phy *qphy = phy_get_drvdata(phy);
|
||||
struct qcom_qmp *qmp = qphy->qmp;
|
||||
const struct qmp_phy_cfg *cfg = qphy->cfg;
|
||||
struct qmp_ufs *qmp = phy_get_drvdata(phy);
|
||||
const struct qmp_phy_cfg *cfg = qmp->cfg;
|
||||
int ret;
|
||||
dev_vdbg(qmp->dev, "Initializing QMP phy\n");
|
||||
|
||||
@ -921,7 +875,7 @@ static int qmp_ufs_init(struct phy *phy)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = qmp_ufs_com_init(qphy);
|
||||
ret = qmp_ufs_com_init(qmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -930,34 +884,27 @@ static int qmp_ufs_init(struct phy *phy)
|
||||
|
||||
static int qmp_ufs_power_on(struct phy *phy)
|
||||
{
|
||||
struct qmp_phy *qphy = phy_get_drvdata(phy);
|
||||
struct qcom_qmp *qmp = qphy->qmp;
|
||||
const struct qmp_phy_cfg *cfg = qphy->cfg;
|
||||
void __iomem *tx = qphy->tx;
|
||||
void __iomem *rx = qphy->rx;
|
||||
void __iomem *pcs = qphy->pcs;
|
||||
struct qmp_ufs *qmp = phy_get_drvdata(phy);
|
||||
const struct qmp_phy_cfg *cfg = qmp->cfg;
|
||||
void __iomem *tx = qmp->tx;
|
||||
void __iomem *rx = qmp->rx;
|
||||
void __iomem *pcs = qmp->pcs;
|
||||
void __iomem *status;
|
||||
unsigned int mask, val, ready;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
qmp_ufs_serdes_init(qphy);
|
||||
qmp_ufs_serdes_init(qmp);
|
||||
|
||||
/* Tx, Rx, and PCS configurations */
|
||||
qmp_ufs_configure_lane(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num, 1);
|
||||
qmp_ufs_configure_lane(tx, cfg->tx_tbl, cfg->tx_tbl_num, 1);
|
||||
qmp_ufs_configure_lane(rx, cfg->rx_tbl, cfg->rx_tbl_num, 1);
|
||||
|
||||
if (cfg->lanes >= 2) {
|
||||
qmp_ufs_configure_lane(qphy->tx2, cfg->regs,
|
||||
cfg->tx_tbl, cfg->tx_tbl_num, 2);
|
||||
qmp_ufs_configure_lane(qmp->tx2, cfg->tx_tbl, cfg->tx_tbl_num, 2);
|
||||
qmp_ufs_configure_lane(qmp->rx2, cfg->rx_tbl, cfg->rx_tbl_num, 2);
|
||||
}
|
||||
|
||||
qmp_ufs_configure_lane(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 1);
|
||||
|
||||
if (cfg->lanes >= 2) {
|
||||
qmp_ufs_configure_lane(qphy->rx2, cfg->regs,
|
||||
cfg->rx_tbl, cfg->rx_tbl_num, 2);
|
||||
}
|
||||
|
||||
qmp_ufs_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
|
||||
qmp_ufs_configure(pcs, cfg->pcs_tbl, cfg->pcs_tbl_num);
|
||||
|
||||
ret = reset_control_deassert(qmp->ufs_reset);
|
||||
if (ret)
|
||||
@ -966,14 +913,12 @@ static int qmp_ufs_power_on(struct phy *phy)
|
||||
/* Pull PHY out of reset state */
|
||||
if (!cfg->no_pcs_sw_reset)
|
||||
qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
|
||||
/* start SerDes and Phy-Coding-Sublayer */
|
||||
qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
|
||||
|
||||
/* start SerDes */
|
||||
qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], SERDES_START);
|
||||
|
||||
status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
|
||||
mask = PCS_READY;
|
||||
ready = PCS_READY;
|
||||
|
||||
ret = readl_poll_timeout(status, val, (val & mask) == ready, 10,
|
||||
ret = readl_poll_timeout(status, val, (val & PCS_READY), 200,
|
||||
PHY_INIT_COMPLETE_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_err(qmp->dev, "phy initialization timed-out\n");
|
||||
@ -985,33 +930,28 @@ static int qmp_ufs_power_on(struct phy *phy)
|
||||
|
||||
static int qmp_ufs_power_off(struct phy *phy)
|
||||
{
|
||||
struct qmp_phy *qphy = phy_get_drvdata(phy);
|
||||
const struct qmp_phy_cfg *cfg = qphy->cfg;
|
||||
struct qmp_ufs *qmp = phy_get_drvdata(phy);
|
||||
const struct qmp_phy_cfg *cfg = qmp->cfg;
|
||||
|
||||
/* PHY reset */
|
||||
if (!cfg->no_pcs_sw_reset)
|
||||
qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
|
||||
qphy_setbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
|
||||
|
||||
/* stop SerDes and Phy-Coding-Sublayer */
|
||||
qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
|
||||
/* stop SerDes */
|
||||
qphy_clrbits(qmp->pcs, cfg->regs[QPHY_START_CTRL], SERDES_START);
|
||||
|
||||
/* Put PHY into POWER DOWN state: active low */
|
||||
if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) {
|
||||
qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
|
||||
cfg->pwrdn_ctrl);
|
||||
} else {
|
||||
qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
|
||||
cfg->pwrdn_ctrl);
|
||||
}
|
||||
qphy_clrbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
|
||||
SW_PWRDN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qmp_ufs_exit(struct phy *phy)
|
||||
{
|
||||
struct qmp_phy *qphy = phy_get_drvdata(phy);
|
||||
struct qmp_ufs *qmp = phy_get_drvdata(phy);
|
||||
|
||||
qmp_ufs_com_exit(qphy);
|
||||
qmp_ufs_com_exit(qmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1041,9 +981,16 @@ static int qmp_ufs_disable(struct phy *phy)
|
||||
return qmp_ufs_exit(phy);
|
||||
}
|
||||
|
||||
static int qmp_ufs_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
|
||||
static const struct phy_ops qcom_qmp_ufs_phy_ops = {
|
||||
.power_on = qmp_ufs_enable,
|
||||
.power_off = qmp_ufs_disable,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int qmp_ufs_vreg_init(struct qmp_ufs *qmp)
|
||||
{
|
||||
struct qcom_qmp *qmp = dev_get_drvdata(dev);
|
||||
const struct qmp_phy_cfg *cfg = qmp->cfg;
|
||||
struct device *dev = qmp->dev;
|
||||
int num = cfg->num_vregs;
|
||||
int i;
|
||||
|
||||
@ -1057,9 +1004,10 @@ static int qmp_ufs_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
|
||||
return devm_regulator_bulk_get(dev, num, qmp->vregs);
|
||||
}
|
||||
|
||||
static int qmp_ufs_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg)
|
||||
static int qmp_ufs_clk_init(struct qmp_ufs *qmp)
|
||||
{
|
||||
struct qcom_qmp *qmp = dev_get_drvdata(dev);
|
||||
const struct qmp_phy_cfg *cfg = qmp->cfg;
|
||||
struct device *dev = qmp->dev;
|
||||
int num = cfg->num_clks;
|
||||
int i;
|
||||
|
||||
@ -1073,76 +1021,138 @@ static int qmp_ufs_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg)
|
||||
return devm_clk_bulk_get(dev, num, qmp->clks);
|
||||
}
|
||||
|
||||
static const struct phy_ops qcom_qmp_ufs_ops = {
|
||||
.power_on = qmp_ufs_enable,
|
||||
.power_off = qmp_ufs_disable,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int qmp_ufs_create(struct device *dev, struct device_node *np, int id,
|
||||
void __iomem *serdes, const struct qmp_phy_cfg *cfg)
|
||||
static int qmp_ufs_parse_dt_legacy(struct qmp_ufs *qmp, struct device_node *np)
|
||||
{
|
||||
struct qcom_qmp *qmp = dev_get_drvdata(dev);
|
||||
struct phy *generic_phy;
|
||||
struct qmp_phy *qphy;
|
||||
int ret;
|
||||
struct platform_device *pdev = to_platform_device(qmp->dev);
|
||||
const struct qmp_phy_cfg *cfg = qmp->cfg;
|
||||
struct device *dev = qmp->dev;
|
||||
|
||||
qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
|
||||
if (!qphy)
|
||||
return -ENOMEM;
|
||||
qmp->serdes = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(qmp->serdes))
|
||||
return PTR_ERR(qmp->serdes);
|
||||
|
||||
qphy->cfg = cfg;
|
||||
qphy->serdes = serdes;
|
||||
/*
|
||||
* Get memory resources for each phy lane:
|
||||
* Get memory resources for the PHY:
|
||||
* Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
|
||||
* For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5
|
||||
* For single lane PHYs: pcs_misc (optional) -> 3.
|
||||
*/
|
||||
qphy->tx = devm_of_iomap(dev, np, 0, NULL);
|
||||
if (IS_ERR(qphy->tx))
|
||||
return PTR_ERR(qphy->tx);
|
||||
qmp->tx = devm_of_iomap(dev, np, 0, NULL);
|
||||
if (IS_ERR(qmp->tx))
|
||||
return PTR_ERR(qmp->tx);
|
||||
|
||||
qphy->rx = devm_of_iomap(dev, np, 1, NULL);
|
||||
if (IS_ERR(qphy->rx))
|
||||
return PTR_ERR(qphy->rx);
|
||||
qmp->rx = devm_of_iomap(dev, np, 1, NULL);
|
||||
if (IS_ERR(qmp->rx))
|
||||
return PTR_ERR(qmp->rx);
|
||||
|
||||
qphy->pcs = devm_of_iomap(dev, np, 2, NULL);
|
||||
if (IS_ERR(qphy->pcs))
|
||||
return PTR_ERR(qphy->pcs);
|
||||
qmp->pcs = devm_of_iomap(dev, np, 2, NULL);
|
||||
if (IS_ERR(qmp->pcs))
|
||||
return PTR_ERR(qmp->pcs);
|
||||
|
||||
if (cfg->lanes >= 2) {
|
||||
qphy->tx2 = devm_of_iomap(dev, np, 3, NULL);
|
||||
if (IS_ERR(qphy->tx2))
|
||||
return PTR_ERR(qphy->tx2);
|
||||
qmp->tx2 = devm_of_iomap(dev, np, 3, NULL);
|
||||
if (IS_ERR(qmp->tx2))
|
||||
return PTR_ERR(qmp->tx2);
|
||||
|
||||
qphy->rx2 = devm_of_iomap(dev, np, 4, NULL);
|
||||
if (IS_ERR(qphy->rx2))
|
||||
return PTR_ERR(qphy->rx2);
|
||||
qmp->rx2 = devm_of_iomap(dev, np, 4, NULL);
|
||||
if (IS_ERR(qmp->rx2))
|
||||
return PTR_ERR(qmp->rx2);
|
||||
|
||||
qphy->pcs_misc = devm_of_iomap(dev, np, 5, NULL);
|
||||
qmp->pcs_misc = devm_of_iomap(dev, np, 5, NULL);
|
||||
} else {
|
||||
qphy->pcs_misc = devm_of_iomap(dev, np, 3, NULL);
|
||||
qmp->pcs_misc = devm_of_iomap(dev, np, 3, NULL);
|
||||
}
|
||||
|
||||
if (IS_ERR(qphy->pcs_misc))
|
||||
if (IS_ERR(qmp->pcs_misc))
|
||||
dev_vdbg(dev, "PHY pcs_misc-reg not used\n");
|
||||
|
||||
generic_phy = devm_phy_create(dev, np, &qcom_qmp_ufs_ops);
|
||||
if (IS_ERR(generic_phy)) {
|
||||
ret = PTR_ERR(generic_phy);
|
||||
dev_err(dev, "failed to create qphy %d\n", ret);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qmp_ufs_parse_dt(struct qmp_ufs *qmp)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(qmp->dev);
|
||||
const struct qmp_phy_cfg *cfg = qmp->cfg;
|
||||
const struct qmp_ufs_offsets *offs = cfg->offsets;
|
||||
void __iomem *base;
|
||||
|
||||
if (!offs)
|
||||
return -EINVAL;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
qmp->serdes = base + offs->serdes;
|
||||
qmp->pcs = base + offs->pcs;
|
||||
qmp->tx = base + offs->tx;
|
||||
qmp->rx = base + offs->rx;
|
||||
|
||||
if (cfg->lanes >= 2) {
|
||||
qmp->tx2 = base + offs->tx2;
|
||||
qmp->rx2 = base + offs->rx2;
|
||||
}
|
||||
|
||||
qphy->phy = generic_phy;
|
||||
qphy->qmp = qmp;
|
||||
qmp->phys[id] = qphy;
|
||||
phy_set_drvdata(generic_phy, qphy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qmp_ufs_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy_provider *phy_provider;
|
||||
struct device_node *np;
|
||||
struct qmp_ufs *qmp;
|
||||
int ret;
|
||||
|
||||
qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL);
|
||||
if (!qmp)
|
||||
return -ENOMEM;
|
||||
|
||||
qmp->dev = dev;
|
||||
|
||||
qmp->cfg = of_device_get_match_data(dev);
|
||||
if (!qmp->cfg)
|
||||
return -EINVAL;
|
||||
|
||||
ret = qmp_ufs_clk_init(qmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qmp_ufs_vreg_init(qmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Check for legacy binding with child node. */
|
||||
np = of_get_next_available_child(dev->of_node, NULL);
|
||||
if (np) {
|
||||
ret = qmp_ufs_parse_dt_legacy(qmp, np);
|
||||
} else {
|
||||
np = of_node_get(dev->of_node);
|
||||
ret = qmp_ufs_parse_dt(qmp);
|
||||
}
|
||||
if (ret)
|
||||
goto err_node_put;
|
||||
|
||||
qmp->phy = devm_phy_create(dev, np, &qcom_qmp_ufs_phy_ops);
|
||||
if (IS_ERR(qmp->phy)) {
|
||||
ret = PTR_ERR(qmp->phy);
|
||||
dev_err(dev, "failed to create PHY: %d\n", ret);
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
phy_set_drvdata(qmp->phy, qmp);
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
|
||||
err_node_put:
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id qmp_ufs_of_match_table[] = {
|
||||
{
|
||||
.compatible = "qcom,msm8996-qmp-ufs-phy",
|
||||
@ -1155,7 +1165,7 @@ static const struct of_device_id qmp_ufs_of_match_table[] = {
|
||||
.data = &sm8150_ufsphy_cfg,
|
||||
}, {
|
||||
.compatible = "qcom,sc8280xp-qmp-ufs-phy",
|
||||
.data = &sm8350_ufsphy_cfg,
|
||||
.data = &sc8280xp_ufsphy_cfg,
|
||||
}, {
|
||||
.compatible = "qcom,sdm845-qmp-ufs-phy",
|
||||
.data = &sdm845_ufsphy_cfg,
|
||||
@ -1182,74 +1192,6 @@ static const struct of_device_id qmp_ufs_of_match_table[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qmp_ufs_of_match_table);
|
||||
|
||||
static int qmp_ufs_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_qmp *qmp;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *child;
|
||||
struct phy_provider *phy_provider;
|
||||
void __iomem *serdes;
|
||||
const struct qmp_phy_cfg *cfg = NULL;
|
||||
int num, id;
|
||||
int ret;
|
||||
|
||||
qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL);
|
||||
if (!qmp)
|
||||
return -ENOMEM;
|
||||
|
||||
qmp->dev = dev;
|
||||
dev_set_drvdata(dev, qmp);
|
||||
|
||||
/* Get the specific init parameters of QMP phy */
|
||||
cfg = of_device_get_match_data(dev);
|
||||
if (!cfg)
|
||||
return -EINVAL;
|
||||
|
||||
/* per PHY serdes; usually located at base address */
|
||||
serdes = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(serdes))
|
||||
return PTR_ERR(serdes);
|
||||
|
||||
ret = qmp_ufs_clk_init(dev, cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qmp_ufs_vreg_init(dev, cfg);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to get regulator supplies\n");
|
||||
|
||||
num = of_get_available_child_count(dev->of_node);
|
||||
/* do we have a rogue child node ? */
|
||||
if (num > 1)
|
||||
return -EINVAL;
|
||||
|
||||
qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL);
|
||||
if (!qmp->phys)
|
||||
return -ENOMEM;
|
||||
|
||||
id = 0;
|
||||
for_each_available_child_of_node(dev->of_node, child) {
|
||||
/* Create per-lane phy */
|
||||
ret = qmp_ufs_create(dev, child, id, serdes, cfg);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to create lane%d phy, %d\n",
|
||||
id, ret);
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
id++;
|
||||
}
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
|
||||
err_node_put:
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver qmp_ufs_driver = {
|
||||
.probe = qmp_ufs_probe,
|
||||
.driver = {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -38,6 +38,7 @@
|
||||
#include "phy-qcom-qmp-pcs-pcie-v4_20.h"
|
||||
|
||||
#include "phy-qcom-qmp-pcs-v5.h"
|
||||
#include "phy-qcom-qmp-pcs-v5_20.h"
|
||||
#include "phy-qcom-qmp-pcs-pcie-v5.h"
|
||||
#include "phy-qcom-qmp-pcs-usb-v5.h"
|
||||
#include "phy-qcom-qmp-pcs-ufs-v5.h"
|
||||
|
@ -2,6 +2,14 @@
|
||||
#
|
||||
# Phy drivers for Renesas platforms
|
||||
#
|
||||
# NOTE: Please sorted config names alphabetically.
|
||||
config PHY_R8A779F0_ETHERNET_SERDES
|
||||
tristate "Renesas R-Car S4-8 Ethernet SERDES driver"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Support for Ethernet SERDES found on Renesas R-Car S4-8 SoCs.
|
||||
|
||||
config PHY_RCAR_GEN2
|
||||
tristate "Renesas R-Car generation 2 USB PHY driver"
|
||||
depends on ARCH_RENESAS
|
||||
|
@ -1,4 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_PHY_R8A779F0_ETHERNET_SERDES) += r8a779f0-ether-serdes.o
|
||||
obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
|
||||
obj-$(CONFIG_PHY_RCAR_GEN3_PCIE) += phy-rcar-gen3-pcie.o
|
||||
obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o
|
||||
|
417
drivers/phy/renesas/r8a779f0-ether-serdes.c
Normal file
417
drivers/phy/renesas/r8a779f0-ether-serdes.c
Normal file
@ -0,0 +1,417 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Renesas Ethernet SERDES device driver
|
||||
*
|
||||
* Copyright (C) 2022 Renesas Electronics Corporation
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#define R8A779F0_ETH_SERDES_NUM 3
|
||||
#define R8A779F0_ETH_SERDES_OFFSET 0x0400
|
||||
#define R8A779F0_ETH_SERDES_BANK_SELECT 0x03fc
|
||||
#define R8A779F0_ETH_SERDES_TIMEOUT_US 100000
|
||||
#define R8A779F0_ETH_SERDES_NUM_RETRY_LINKUP 3
|
||||
#define R8A779F0_ETH_SERDES_NUM_RETRY_INIT 3
|
||||
|
||||
struct r8a779f0_eth_serdes_drv_data;
|
||||
struct r8a779f0_eth_serdes_channel {
|
||||
struct r8a779f0_eth_serdes_drv_data *dd;
|
||||
struct phy *phy;
|
||||
void __iomem *addr;
|
||||
phy_interface_t phy_interface;
|
||||
int speed;
|
||||
int index;
|
||||
};
|
||||
|
||||
struct r8a779f0_eth_serdes_drv_data {
|
||||
void __iomem *addr;
|
||||
struct platform_device *pdev;
|
||||
struct reset_control *reset;
|
||||
struct r8a779f0_eth_serdes_channel channel[R8A779F0_ETH_SERDES_NUM];
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
/*
|
||||
* The datasheet describes initialization procedure without any information
|
||||
* about registers' name/bits. So, this is all black magic to initialize
|
||||
* the hardware.
|
||||
*/
|
||||
static void r8a779f0_eth_serdes_write32(void __iomem *addr, u32 offs, u32 bank, u32 data)
|
||||
{
|
||||
iowrite32(bank, addr + R8A779F0_ETH_SERDES_BANK_SELECT);
|
||||
iowrite32(data, addr + offs);
|
||||
}
|
||||
|
||||
static int
|
||||
r8a779f0_eth_serdes_reg_wait(struct r8a779f0_eth_serdes_channel *channel,
|
||||
u32 offs, u32 bank, u32 mask, u32 expected)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
iowrite32(bank, channel->addr + R8A779F0_ETH_SERDES_BANK_SELECT);
|
||||
|
||||
ret = readl_poll_timeout_atomic(channel->addr + offs, val,
|
||||
(val & mask) == expected,
|
||||
1, R8A779F0_ETH_SERDES_TIMEOUT_US);
|
||||
if (ret)
|
||||
dev_dbg(&channel->phy->dev,
|
||||
"%s: index %d, offs %x, bank %x, mask %x, expected %x\n",
|
||||
__func__, channel->index, offs, bank, mask, expected);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
r8a779f0_eth_serdes_common_init_ram(struct r8a779f0_eth_serdes_drv_data *dd)
|
||||
{
|
||||
struct r8a779f0_eth_serdes_channel *channel;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++) {
|
||||
channel = &dd->channel[i];
|
||||
ret = r8a779f0_eth_serdes_reg_wait(channel, 0x026c, 0x180, BIT(0), 0x01);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
r8a779f0_eth_serdes_write32(dd->addr, 0x026c, 0x180, 0x03);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
r8a779f0_eth_serdes_common_setting(struct r8a779f0_eth_serdes_channel *channel)
|
||||
{
|
||||
struct r8a779f0_eth_serdes_drv_data *dd = channel->dd;
|
||||
|
||||
switch (channel->phy_interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
r8a779f0_eth_serdes_write32(dd->addr, 0x0244, 0x180, 0x0097);
|
||||
r8a779f0_eth_serdes_write32(dd->addr, 0x01d0, 0x180, 0x0060);
|
||||
r8a779f0_eth_serdes_write32(dd->addr, 0x01d8, 0x180, 0x2200);
|
||||
r8a779f0_eth_serdes_write32(dd->addr, 0x01d4, 0x180, 0x0000);
|
||||
r8a779f0_eth_serdes_write32(dd->addr, 0x01e0, 0x180, 0x003d);
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
r8a779f0_eth_serdes_chan_setting(struct r8a779f0_eth_serdes_channel *channel)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (channel->phy_interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0000, 0x380, 0x2000);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x01c0, 0x180, 0x0011);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0248, 0x180, 0x0540);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0258, 0x180, 0x0015);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0144, 0x180, 0x0100);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x01a0, 0x180, 0x0000);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x00d0, 0x180, 0x0002);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0150, 0x180, 0x0003);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x00c8, 0x180, 0x0100);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0148, 0x180, 0x0100);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0174, 0x180, 0x0000);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0160, 0x180, 0x0007);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x01ac, 0x180, 0x0000);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x00c4, 0x180, 0x0310);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x00c8, 0x380, 0x0101);
|
||||
ret = r8a779f0_eth_serdes_reg_wait(channel, 0x00c8, 0x0180, BIT(0), 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0148, 0x180, 0x0101);
|
||||
ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0148, 0x0180, BIT(0), 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x00c4, 0x180, 0x1310);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x00d8, 0x180, 0x1800);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x00dc, 0x180, 0x0000);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x001c, 0x300, 0x0001);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0000, 0x380, 0x2100);
|
||||
ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0000, 0x0380, BIT(8), 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (channel->speed == 1000)
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0000, 0x1f00, 0x0140);
|
||||
else if (channel->speed == 100)
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0000, 0x1f00, 0x2100);
|
||||
|
||||
/* For AN_ON */
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0004, 0x1f80, 0x0005);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0028, 0x1f80, 0x07a1);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0000, 0x1f80, 0x0208);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
r8a779f0_eth_serdes_chan_speed(struct r8a779f0_eth_serdes_channel *channel)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (channel->phy_interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
/* For AN_ON */
|
||||
if (channel->speed == 1000)
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0000, 0x1f00, 0x1140);
|
||||
else if (channel->speed == 100)
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0000, 0x1f00, 0x3100);
|
||||
ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0008, 0x1f80, BIT(0), 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0008, 0x1f80, 0x0000);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int r8a779f0_eth_serdes_monitor_linkup(struct r8a779f0_eth_serdes_channel *channel)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < R8A779F0_ETH_SERDES_NUM_RETRY_LINKUP; i++) {
|
||||
ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0004, 0x300,
|
||||
BIT(2), BIT(2));
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
/* restart */
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0144, 0x180, 0x0100);
|
||||
udelay(1);
|
||||
r8a779f0_eth_serdes_write32(channel->addr, 0x0144, 0x180, 0x0000);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int r8a779f0_eth_serdes_hw_init(struct r8a779f0_eth_serdes_channel *channel)
|
||||
{
|
||||
struct r8a779f0_eth_serdes_drv_data *dd = channel->dd;
|
||||
int i, ret;
|
||||
|
||||
if (dd->initialized)
|
||||
return 0;
|
||||
|
||||
ret = r8a779f0_eth_serdes_common_init_ram(dd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++) {
|
||||
ret = r8a779f0_eth_serdes_reg_wait(&dd->channel[i], 0x0000,
|
||||
0x300, BIT(15), 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++)
|
||||
r8a779f0_eth_serdes_write32(dd->channel[i].addr, 0x03d4, 0x380, 0x0443);
|
||||
|
||||
ret = r8a779f0_eth_serdes_common_setting(channel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++)
|
||||
r8a779f0_eth_serdes_write32(dd->channel[i].addr, 0x03d0, 0x380, 0x0001);
|
||||
|
||||
|
||||
r8a779f0_eth_serdes_write32(dd->addr, 0x0000, 0x380, 0x8000);
|
||||
|
||||
ret = r8a779f0_eth_serdes_common_init_ram(dd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = r8a779f0_eth_serdes_reg_wait(&dd->channel[0], 0x0000, 0x380, BIT(15), 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++) {
|
||||
ret = r8a779f0_eth_serdes_chan_setting(&dd->channel[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++) {
|
||||
ret = r8a779f0_eth_serdes_chan_speed(&dd->channel[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++)
|
||||
r8a779f0_eth_serdes_write32(dd->channel[i].addr, 0x03c0, 0x380, 0x0000);
|
||||
for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++)
|
||||
r8a779f0_eth_serdes_write32(dd->channel[i].addr, 0x03d0, 0x380, 0x0000);
|
||||
|
||||
for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++) {
|
||||
ret = r8a779f0_eth_serdes_monitor_linkup(&dd->channel[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int r8a779f0_eth_serdes_init(struct phy *p)
|
||||
{
|
||||
struct r8a779f0_eth_serdes_channel *channel = phy_get_drvdata(p);
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < R8A779F0_ETH_SERDES_NUM_RETRY_INIT; i++) {
|
||||
ret = r8a779f0_eth_serdes_hw_init(channel);
|
||||
if (!ret) {
|
||||
channel->dd->initialized = true;
|
||||
break;
|
||||
}
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int r8a779f0_eth_serdes_set_mode(struct phy *p, enum phy_mode mode,
|
||||
int submode)
|
||||
{
|
||||
struct r8a779f0_eth_serdes_channel *channel = phy_get_drvdata(p);
|
||||
|
||||
if (mode != PHY_MODE_ETHERNET)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (submode) {
|
||||
case PHY_INTERFACE_MODE_GMII:
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_USXGMII:
|
||||
channel->phy_interface = submode;
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int r8a779f0_eth_serdes_set_speed(struct phy *p, int speed)
|
||||
{
|
||||
struct r8a779f0_eth_serdes_channel *channel = phy_get_drvdata(p);
|
||||
|
||||
channel->speed = speed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops r8a779f0_eth_serdes_ops = {
|
||||
.init = r8a779f0_eth_serdes_init,
|
||||
.set_mode = r8a779f0_eth_serdes_set_mode,
|
||||
.set_speed = r8a779f0_eth_serdes_set_speed,
|
||||
};
|
||||
|
||||
static struct phy *r8a779f0_eth_serdes_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct r8a779f0_eth_serdes_drv_data *dd = dev_get_drvdata(dev);
|
||||
|
||||
if (args->args[0] >= R8A779F0_ETH_SERDES_NUM)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return dd->channel[args->args[0]].phy;
|
||||
}
|
||||
|
||||
static const struct of_device_id r8a779f0_eth_serdes_of_table[] = {
|
||||
{ .compatible = "renesas,r8a779f0-ether-serdes", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, r8a779f0_eth_serdes_of_table);
|
||||
|
||||
static int r8a779f0_eth_serdes_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct r8a779f0_eth_serdes_drv_data *dd;
|
||||
struct phy_provider *provider;
|
||||
struct resource *res;
|
||||
int i;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "invalid resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dd = devm_kzalloc(&pdev->dev, sizeof(*dd), GFP_KERNEL);
|
||||
if (!dd)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, dd);
|
||||
dd->pdev = pdev;
|
||||
dd->addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(dd->addr))
|
||||
return PTR_ERR(dd->addr);
|
||||
|
||||
dd->reset = devm_reset_control_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(dd->reset))
|
||||
return PTR_ERR(dd->reset);
|
||||
|
||||
reset_control_reset(dd->reset);
|
||||
|
||||
for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++) {
|
||||
struct r8a779f0_eth_serdes_channel *channel = &dd->channel[i];
|
||||
|
||||
channel->phy = devm_phy_create(&pdev->dev, NULL,
|
||||
&r8a779f0_eth_serdes_ops);
|
||||
if (IS_ERR(channel->phy))
|
||||
return PTR_ERR(channel->phy);
|
||||
channel->addr = dd->addr + R8A779F0_ETH_SERDES_OFFSET * i;
|
||||
channel->dd = dd;
|
||||
channel->index = i;
|
||||
phy_set_drvdata(channel->phy, channel);
|
||||
}
|
||||
|
||||
provider = devm_of_phy_provider_register(&pdev->dev,
|
||||
r8a779f0_eth_serdes_xlate);
|
||||
if (IS_ERR(provider))
|
||||
return PTR_ERR(provider);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int r8a779f0_eth_serdes_remove(struct platform_device *pdev)
|
||||
{
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver r8a779f0_eth_serdes_driver_platform = {
|
||||
.probe = r8a779f0_eth_serdes_probe,
|
||||
.remove = r8a779f0_eth_serdes_remove,
|
||||
.driver = {
|
||||
.name = "r8a779f0_eth_serdes",
|
||||
.of_match_table = r8a779f0_eth_serdes_of_table,
|
||||
}
|
||||
};
|
||||
module_platform_driver(r8a779f0_eth_serdes_driver_platform);
|
||||
MODULE_AUTHOR("Yoshihiro Shimoda");
|
||||
MODULE_DESCRIPTION("Renesas Ethernet SERDES device driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -15,6 +15,7 @@
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
#define P2U_CONTROL_CMN 0x74
|
||||
#define P2U_CONTROL_CMN_ENABLE_L2_EXIT_RATE_CHANGE BIT(13)
|
||||
#define P2U_CONTROL_CMN_SKP_SIZE_PROTECTION_EN BIT(20)
|
||||
|
||||
#define P2U_PERIODIC_EQ_CTRL_GEN3 0xc0
|
||||
@ -85,8 +86,21 @@ static int tegra_p2u_power_on(struct phy *x)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_p2u_calibrate(struct phy *x)
|
||||
{
|
||||
struct tegra_p2u *phy = phy_get_drvdata(x);
|
||||
u32 val;
|
||||
|
||||
val = p2u_readl(phy, P2U_CONTROL_CMN);
|
||||
val |= P2U_CONTROL_CMN_ENABLE_L2_EXIT_RATE_CHANGE;
|
||||
p2u_writel(phy, val, P2U_CONTROL_CMN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops ops = {
|
||||
.power_on = tegra_p2u_power_on,
|
||||
.calibrate = tegra_p2u_calibrate,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
@ -1652,7 +1652,6 @@ tegra124_usb3_port_map(struct tegra_xusb_port *port)
|
||||
|
||||
static const struct tegra_xusb_port_ops tegra124_usb3_port_ops = {
|
||||
.release = tegra_xusb_usb3_port_release,
|
||||
.remove = tegra_xusb_usb3_port_remove,
|
||||
.enable = tegra124_usb3_port_enable,
|
||||
.disable = tegra124_usb3_port_disable,
|
||||
.map = tegra124_usb3_port_map,
|
||||
|
@ -1185,7 +1185,6 @@ tegra186_usb3_port_map(struct tegra_xusb_port *port)
|
||||
|
||||
static const struct tegra_xusb_port_ops tegra186_usb3_port_ops = {
|
||||
.release = tegra_xusb_usb3_port_release,
|
||||
.remove = tegra_xusb_usb3_port_remove,
|
||||
.enable = tegra186_usb3_port_enable,
|
||||
.disable = tegra186_usb3_port_disable,
|
||||
.map = tegra186_usb3_port_map,
|
||||
|
@ -3078,7 +3078,6 @@ tegra210_usb3_port_map(struct tegra_xusb_port *port)
|
||||
|
||||
static const struct tegra_xusb_port_ops tegra210_usb3_port_ops = {
|
||||
.release = tegra_xusb_usb3_port_release,
|
||||
.remove = tegra_xusb_usb3_port_remove,
|
||||
.enable = tegra210_usb3_port_enable,
|
||||
.disable = tegra210_usb3_port_disable,
|
||||
.map = tegra210_usb3_port_map,
|
||||
|
@ -954,8 +954,7 @@ static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
usb3->supply = regulator_get(&port->dev, "vbus");
|
||||
return PTR_ERR_OR_ZERO(usb3->supply);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_xusb_add_usb3_port(struct tegra_xusb_padctl *padctl,
|
||||
@ -1012,13 +1011,6 @@ void tegra_xusb_usb3_port_release(struct tegra_xusb_port *port)
|
||||
kfree(usb3);
|
||||
}
|
||||
|
||||
void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port)
|
||||
{
|
||||
struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
|
||||
|
||||
regulator_put(usb3->supply);
|
||||
}
|
||||
|
||||
static void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl)
|
||||
{
|
||||
struct tegra_xusb_port *port, *tmp;
|
||||
|
@ -359,7 +359,6 @@ void tegra_xusb_hsic_port_release(struct tegra_xusb_port *port);
|
||||
|
||||
struct tegra_xusb_usb3_port {
|
||||
struct tegra_xusb_port base;
|
||||
struct regulator *supply;
|
||||
bool context_saved;
|
||||
unsigned int port;
|
||||
bool internal;
|
||||
@ -381,7 +380,6 @@ struct tegra_xusb_usb3_port *
|
||||
tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl,
|
||||
unsigned int index);
|
||||
void tegra_xusb_usb3_port_release(struct tegra_xusb_port *port);
|
||||
void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port);
|
||||
|
||||
struct tegra_xusb_port_ops {
|
||||
void (*release)(struct tegra_xusb_port *port);
|
||||
|
@ -50,6 +50,7 @@ struct phy_gmii_sel_soc_data {
|
||||
const struct reg_field (*regfields)[PHY_GMII_SEL_LAST];
|
||||
bool use_of_data;
|
||||
u64 extra_modes;
|
||||
u32 num_qsgmii_main_ports;
|
||||
};
|
||||
|
||||
struct phy_gmii_sel_priv {
|
||||
@ -213,6 +214,17 @@ struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw5g_soc_j7200 = {
|
||||
.use_of_data = true,
|
||||
.regfields = phy_gmii_sel_fields_am654,
|
||||
.extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII),
|
||||
.num_ports = 4,
|
||||
.num_qsgmii_main_ports = 1,
|
||||
};
|
||||
|
||||
static const
|
||||
struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw9g_soc_j721e = {
|
||||
.use_of_data = true,
|
||||
.regfields = phy_gmii_sel_fields_am654,
|
||||
.extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII),
|
||||
.num_ports = 8,
|
||||
.num_qsgmii_main_ports = 2,
|
||||
};
|
||||
|
||||
static const struct of_device_id phy_gmii_sel_id_table[] = {
|
||||
@ -240,6 +252,10 @@ static const struct of_device_id phy_gmii_sel_id_table[] = {
|
||||
.compatible = "ti,j7200-cpsw5g-phy-gmii-sel",
|
||||
.data = &phy_gmii_sel_cpsw5g_soc_j7200,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,j721e-cpsw9g-phy-gmii-sel",
|
||||
.data = &phy_gmii_sel_cpsw9g_soc_j721e,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, phy_gmii_sel_id_table);
|
||||
@ -378,11 +394,13 @@ static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv)
|
||||
static int phy_gmii_sel_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct phy_gmii_sel_soc_data *soc_data;
|
||||
struct device_node *node = dev->of_node;
|
||||
const struct of_device_id *of_id;
|
||||
struct phy_gmii_sel_priv *priv;
|
||||
u32 main_ports = 1;
|
||||
int ret;
|
||||
u32 i;
|
||||
|
||||
of_id = of_match_node(phy_gmii_sel_id_table, pdev->dev.of_node);
|
||||
if (!of_id)
|
||||
@ -394,16 +412,26 @@ static int phy_gmii_sel_probe(struct platform_device *pdev)
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
priv->soc_data = of_id->data;
|
||||
soc_data = priv->soc_data;
|
||||
priv->num_ports = priv->soc_data->num_ports;
|
||||
of_property_read_u32(node, "ti,qsgmii-main-ports", &main_ports);
|
||||
priv->qsgmii_main_ports = 0;
|
||||
|
||||
/*
|
||||
* Ensure that main_ports is within bounds. If the property
|
||||
* ti,qsgmii-main-ports is not mentioned, or the value mentioned
|
||||
* is out of bounds, default to 1.
|
||||
* Based on the compatible, try to read the appropriate number of
|
||||
* QSGMII main ports from the "ti,qsgmii-main-ports" property from
|
||||
* the device-tree node.
|
||||
*/
|
||||
if (main_ports < 1 || main_ports > 4)
|
||||
main_ports = 1;
|
||||
priv->qsgmii_main_ports = PHY_GMII_PORT(main_ports);
|
||||
for (i = 0; i < soc_data->num_qsgmii_main_ports; i++) {
|
||||
of_property_read_u32_index(node, "ti,qsgmii-main-ports", i, &main_ports);
|
||||
/*
|
||||
* Ensure that main_ports is within bounds.
|
||||
*/
|
||||
if (main_ports < 1 || main_ports > soc_data->num_ports) {
|
||||
dev_err(dev, "Invalid qsgmii main port provided\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->qsgmii_main_ports |= PHY_GMII_PORT(main_ports);
|
||||
}
|
||||
|
||||
priv->regmap = syscon_node_to_regmap(node->parent);
|
||||
if (IS_ERR(priv->regmap)) {
|
||||
|
@ -81,14 +81,20 @@ static const struct reg_field phy_reset_n = REG_FIELD(WIZ_SERDES_RST, 31, 31);
|
||||
static const struct reg_field phy_en_refclk = REG_FIELD(WIZ_SERDES_RST, 30, 30);
|
||||
static const struct reg_field pll1_refclk_mux_sel =
|
||||
REG_FIELD(WIZ_SERDES_RST, 29, 29);
|
||||
static const struct reg_field pll1_refclk_mux_sel_2 =
|
||||
REG_FIELD(WIZ_SERDES_RST, 22, 23);
|
||||
static const struct reg_field pll0_refclk_mux_sel =
|
||||
REG_FIELD(WIZ_SERDES_RST, 28, 28);
|
||||
static const struct reg_field pll0_refclk_mux_sel_2 =
|
||||
REG_FIELD(WIZ_SERDES_RST, 28, 29);
|
||||
static const struct reg_field refclk_dig_sel_16g =
|
||||
REG_FIELD(WIZ_SERDES_RST, 24, 25);
|
||||
static const struct reg_field refclk_dig_sel_10g =
|
||||
REG_FIELD(WIZ_SERDES_RST, 24, 24);
|
||||
static const struct reg_field pma_cmn_refclk_int_mode =
|
||||
REG_FIELD(WIZ_SERDES_TOP_CTRL, 28, 29);
|
||||
static const struct reg_field pma_cmn_refclk1_int_mode =
|
||||
REG_FIELD(WIZ_SERDES_TOP_CTRL, 20, 21);
|
||||
static const struct reg_field pma_cmn_refclk_mode =
|
||||
REG_FIELD(WIZ_SERDES_TOP_CTRL, 30, 31);
|
||||
static const struct reg_field pma_cmn_refclk_dig_div =
|
||||
@ -315,6 +321,8 @@ enum wiz_type {
|
||||
J721E_WIZ_10G, /* Also for J7200 SR1.0 */
|
||||
AM64_WIZ_10G,
|
||||
J7200_WIZ_10G, /* J7200 SR2.0 */
|
||||
J784S4_WIZ_10G,
|
||||
J721S2_WIZ_10G,
|
||||
};
|
||||
|
||||
struct wiz_data {
|
||||
@ -992,6 +1000,8 @@ static void wiz_clock_cleanup(struct wiz *wiz, struct device_node *node)
|
||||
switch (wiz->type) {
|
||||
case AM64_WIZ_10G:
|
||||
case J7200_WIZ_10G:
|
||||
case J784S4_WIZ_10G:
|
||||
case J721S2_WIZ_10G:
|
||||
of_clk_del_provider(dev->of_node);
|
||||
return;
|
||||
default:
|
||||
@ -1123,6 +1133,8 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
|
||||
switch (wiz->type) {
|
||||
case AM64_WIZ_10G:
|
||||
case J7200_WIZ_10G:
|
||||
case J784S4_WIZ_10G:
|
||||
case J721S2_WIZ_10G:
|
||||
ret = wiz_clock_register(wiz);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to register wiz clocks\n");
|
||||
@ -1205,6 +1217,7 @@ static int wiz_phy_fullrt_div(struct wiz *wiz, int lane)
|
||||
break;
|
||||
case J721E_WIZ_10G:
|
||||
case J7200_WIZ_10G:
|
||||
case J721S2_WIZ_10G:
|
||||
if (wiz->lane_phy_type[lane] == PHY_TYPE_SGMII)
|
||||
return regmap_field_write(wiz->p0_fullrt_div[lane], 0x2);
|
||||
break;
|
||||
@ -1299,6 +1312,25 @@ static struct wiz_data j7200_pg2_10g_data = {
|
||||
.clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G,
|
||||
};
|
||||
|
||||
static struct wiz_data j784s4_10g_data = {
|
||||
.type = J784S4_WIZ_10G,
|
||||
.pll0_refclk_mux_sel = &pll0_refclk_mux_sel_2,
|
||||
.pll1_refclk_mux_sel = &pll1_refclk_mux_sel_2,
|
||||
.refclk_dig_sel = &refclk_dig_sel_16g,
|
||||
.pma_cmn_refclk1_int_mode = &pma_cmn_refclk1_int_mode,
|
||||
.clk_mux_sel = clk_mux_sel_10g_2_refclk,
|
||||
.clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G,
|
||||
};
|
||||
|
||||
static struct wiz_data j721s2_10g_data = {
|
||||
.type = J721S2_WIZ_10G,
|
||||
.pll0_refclk_mux_sel = &pll0_refclk_mux_sel,
|
||||
.pll1_refclk_mux_sel = &pll1_refclk_mux_sel,
|
||||
.refclk_dig_sel = &refclk_dig_sel_10g,
|
||||
.clk_mux_sel = clk_mux_sel_10g,
|
||||
.clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G,
|
||||
};
|
||||
|
||||
static const struct of_device_id wiz_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,j721e-wiz-16g", .data = &j721e_16g_data,
|
||||
@ -1312,6 +1344,12 @@ static const struct of_device_id wiz_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,j7200-wiz-10g", .data = &j7200_pg2_10g_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,j784s4-wiz-10g", .data = &j784s4_10g_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,j721s2-wiz-10g", .data = &j721s2_10g_data,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wiz_id_table);
|
||||
|
20
include/dt-bindings/phy/phy-qcom-qmp.h
Normal file
20
include/dt-bindings/phy/phy-qcom-qmp.h
Normal file
@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/*
|
||||
* Qualcomm QMP PHY constants
|
||||
*
|
||||
* Copyright (C) 2022 Linaro Limited
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_PHY_QMP
|
||||
#define _DT_BINDINGS_PHY_QMP
|
||||
|
||||
/* QMP USB4-USB3-DP clocks */
|
||||
#define QMP_USB43DP_USB3_PIPE_CLK 0
|
||||
#define QMP_USB43DP_DP_LINK_CLK 1
|
||||
#define QMP_USB43DP_DP_VCO_DIV_CLK 2
|
||||
|
||||
/* QMP USB4-USB3-DP PHYs */
|
||||
#define QMP_USB43DP_USB3_PHY 0
|
||||
#define QMP_USB43DP_DP_PHY 1
|
||||
|
||||
#endif /* _DT_BINDINGS_PHY_QMP */
|
Loading…
Reference in New Issue
Block a user