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:
Linus Torvalds 2022-12-19 08:40:58 -06:00
commit e79041113b
48 changed files with 3973 additions and 2588 deletions

View File

@ -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>;

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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>;

View File

@ -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:

View File

@ -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>

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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

View File

@ -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

View File

@ -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
================================

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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));
}
}

View File

@ -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 */

View File

@ -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);
/*

View File

@ -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 = {

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View 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");

View File

@ -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,
};

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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);

View File

@ -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)) {

View File

@ -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);

View 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 */