phy updates for 7.4

As usual bunch of new device and driver support and updates to existing
 drivers
 
  - New Support
   - Rockchip rk3576 csi dphy support,
   - Mediatek MT8195, MT8196 DisplayPort PHYs. MT8196 PCI-Express Gen4 S-PHY,
     MT8189 tphy support
   - Qualcomm Nord eUSB2, QMP USB PHY, QMP PCIe and Gen5x16 PHY support, M31
     eUSB2 PHY, Maili QMP UFS & PCIe PHY, Shikra PCIe phy.
   - New driver for Qualcomm CSI2 C-PHY/DPHY support and new APIs in core to
     parse phy nodes.
   - Nvidia Tegra238 XUSB pad controller support, Tegra264 MPHY driver,
     Tegra264 xusb support.
   - Thunderbolt phy support in core and Qualcomm driver support for USB4 for
     X1E platforms
   - Google Tensor SoC USB PHY driver
   - ASPEED AST2700 USB3 PHY driver
   - Microchip MCP2542 can phy support
 
 - Updates
   - FRL TxFFE config support and Samsumg driver support and runtime PM for
     dcphy driver
   - Mediatek optional repeater support for USB2 ports
   - Refactoring and improvements on rockchip usbdp driver
   - Apple USB4 pipehandler state support
   - Rockchip single-lane DP support
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmriAEsACgkQfBQHDyUj
 g0fWaRAArUBXzGFRwELsxSj3hUy9P09lmZiO55h65hfIOxqXSxX6+4v4/4iq9YOP
 jOvaiJPY7G5uJl26rv99rak61cwuE1l8upZpPezOkDHCa3YoD699bPSvwTgO8L/+
 pJmzu3O6Wi9OaVgjlEq4qevd+j4f1/W376UlbB1tCCtIH2c8bQyIkcFN5JoIZoHN
 W1tefkRl9rpgADCM20b01LyQDd4nEsSyZSW0ApIBc52Flc2307F8rdvF6ZWMvSSU
 R2XT2mQmdRhsnM4xPAqVfs8XmNNGtllevORruqXr43Ee85CiOdNUj5TLJgMTx8wz
 DG0kObHfyhCQ5AVASeaNfgcWVjUHM/b+pz3d3WW+pgzTnGXMaNHNJlO7nEECnmgS
 l0Qgpb1srN6wCyVJvsrbY5MIU76INZxyhyIOb3uYW6WJd7VYjjEAalOFT4dbxWOt
 IlnmbWbGHwbeNFK72mKbc7FynMeql8gaISGbAHXC6Dq9G4iGfi7BIfZK3zsCZspQ
 G8eAzJW2neyk9xIuFuKYF8UnsTANRnf2WoAUgMrtxRRpfV+ULjB1lfEh7Fp7WmT8
 JGvsHyvJ0fRUEnUiTCghLMv5JiIDfi51UKJuQfp/Z+P187LW7iOF4Al1kFmUmNqZ
 y3dhXcs5RvO/ZZOUPl2UIcicGtxn2cTedqvmCnYPwYEtgQ+Q0aM=
 =0bxB
 -----END PGP SIGNATURE-----

Merge tag 'phy-for-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy

Pull phy updates from Vinod Koul:
 "As usual bunch of new device and driver support and updates to
  existing drivers:

  New Support:
   - Rockchip rk3576 csi dphy support
   - Mediatek MT8195, MT8196 DisplayPort PHYs. MT8196 PCI-Express Gen4
     S-PHY, MT8189 tphy support
   - Qualcomm Nord eUSB2, QMP USB PHY, QMP PCIe and Gen5x16 PHY support,
     M31 eUSB2 PHY, Maili QMP UFS & PCIe PHY, Shikra PCIe phy
   - New driver for Qualcomm CSI2 C-PHY/DPHY support and new APIs in
     core to parse phy nodes
   - Nvidia Tegra238 XUSB pad controller support, Tegra264 MPHY driver,
     Tegra264 xusb support
   - Thunderbolt phy support in core and Qualcomm driver support for
     USB4 for X1E platforms
   - Google Tensor SoC USB PHY driver
   - ASPEED AST2700 USB3 PHY driver
   - Microchip MCP2542 can phy support

  Updates:
   - FRL TxFFE config support and Samsumg driver support and runtime PM
     for dcphy driver
   - Mediatek optional repeater support for USB2 ports
   - Refactoring and improvements on rockchip usbdp driver
   - Apple USB4 pipehandler state support
   - Rockchip single-lane DP support"

* tag 'phy-for-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy: (114 commits)
  phy: tegra: Add support for Tegra238 XUSB pad controller
  phy: tegra: xusb: Use dev_err_probe()
  dt-bindings: phy: tegra194: Add compatible for Tegra238
  MAINTAINERS: Add ASPEED USB3 PHY driver
  phy: aspeed: Add AST2700 USB3.2 PHY driver
  dt-bindings: phy: aspeed: Document AST2700 USB3.2 PHY
  phy: rockchip: phy-rockchip-inno-csidphy: add support for rk3576 variant
  dt-bindings: phy: rockchip-inno-csi-dphy: add rk3576 variant
  phy: mediatek: Add support for PCI-Express Gen4 S-PHY
  dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY
  phy: tegra: Add Tegra264 MPHY driver
  dt-bindings: phy: Add nvidia,tegra264-mphy
  phy: rockchip: samsung-hdptx: Add support for FRL TxFFE level control
  phy: rockchip: samsung-hdptx: Handle PHY config after module reload
  phy: hdmi: Add optional FRL TxFFE config options
  phy: tegra: xusb: Add Tegra264 support
  phy: tegra: xusb: Increase timeout for USB2_TRK_COMPLETED polling
  phy: tegra: xusb: Use devm_clk_get_optional to fetch USB2 tracking clock
  dt-bindings: phy: tegra-xusb: Add support for Tegra264
  phy: freescale: fsl-samsung-hdmi: initialize default rate
  ...
This commit is contained in:
Linus Torvalds 2026-10-28 07:21:13 -10:00
commit e191b05f32
76 changed files with 5663 additions and 875 deletions

View File

@ -0,0 +1,48 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/aspeed,ast2700-usb3-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ASPEED AST2700 USB 3.2 PHY
maintainers:
- Ryan Chen <ryan_chen@aspeedtech.com>
properties:
compatible:
const: aspeed,ast2700-usb3-phy
reg:
maxItems: 1
clocks:
maxItems: 1
resets:
maxItems: 1
'#phy-cells':
const: 0
required:
- compatible
- reg
- clocks
- resets
- '#phy-cells'
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/aspeed,ast2700-scu.h>
#include <dt-bindings/reset/aspeed,ast2700-scu.h>
usb-phy@12010000 {
compatible = "aspeed,ast2700-usb3-phy";
reg = <0x12010000 0xc0>;
clocks = <&syscon0 SCU0_CLK_GATE_PORTAUSB2CLK>;
resets = <&syscon0 SCU0_RESET_PORTA_PHY3>;
#phy-cells = <0>;
};

View File

@ -0,0 +1,77 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/mediatek,mt8195-dp-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek SoC DisplayPort Transmitter PHY
maintainers:
- AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
properties:
compatible:
enum:
- mediatek,mt8195-dp-phy
- mediatek,mt8196-edp-phy
reg:
maxItems: 1
"#phy-cells":
const: 0
nvmem-cells:
description: PHY calibrations from eFuse for optimal EYE signal pattern
items:
- description: PHY-Global Reference Bias trim
- description: PHY-Global AUX Transmitter clock impedance adjustment
- description: Lane 0 Transmitter impedance selection (P-MOSFET)
- description: Lane 0 Transmitter impedance selection (N-MOSFET)
- description: Lane 1 Transmitter impedance selection (P-MOSFET)
- description: Lane 1 Transmitter impedance selection (N-MOSFET)
- description: Lane 2 Transmitter impedance selection (P-MOSFET)
- description: Lane 2 Transmitter impedance selection (N-MOSFET)
- description: Lane 3 Transmitter impedance selection (P-MOSFET)
- description: Lane 3 Transmitter impedance selection (N-MOSFET)
nvmem-cell-names:
items:
- const: rbias-trim
- const: impedance-txclk
- const: impedance-lane0p
- const: impedance-lane0n
- const: impedance-lane1p
- const: impedance-lane1n
- const: impedance-lane2p
- const: impedance-lane2n
- const: impedance-lane3p
- const: impedance-lane3n
power-domains:
maxItems: 1
required:
- compatible
- reg
- "#phy-cells"
additionalProperties: false
examples:
- |
phy@1c500000 {
compatible = "mediatek,mt8195-dp-phy";
reg = <0x1c500000 0x2000>;
#phy-cells = <0>;
nvmem-cells = <&edp_glb_bias_trim>, <&edp_clktx_impsel>,
<&edp_imp_ln0_pmos>, <&edp_imp_ln0_nmos>,
<&edp_imp_ln1_pmos>, <&edp_imp_ln1_nmos>,
<&edp_imp_ln2_pmos>, <&edp_imp_ln2_nmos>,
<&edp_imp_ln3_pmos>, <&edp_imp_ln3_nmos>;
nvmem-cell-names = "rbias-trim", "impedance-txclk",
"impedance-lane0p", "impedance-lane0n",
"impedance-lane1p", "impedance-lane1n",
"impedance-lane2p", "impedance-lane2n",
"impedance-lane3p", "impedance-lane3n";
};

View File

@ -0,0 +1,93 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/mediatek,mt8196-pcie-sphy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek PCI-Express Gen4 S-PHY
maintainers:
- AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
description:
The MediaTek PCI-Express Gen4 S-PHY is capable of data serialization
and de-serialization with 8, 10, 128 or 130 bits (16GT/s) encoding
and decoding, has analog buffers, elastic buffers and mechanisms for
receiver detection.
This PHY supports link rates of 2.5, 5.0, 8.0 and 16.0 GT/s per-lane
in both Root Complex (RC) and End Point (EP) modes, depending on the
chosen port.
The Digital PHY (PHYD) part adheres to the Intel PIPE (PHY Interface
for the PCIe) specification.
properties:
compatible:
const: mediatek,mt8196-pcie-sphy
reg:
items:
- description: System Interface (SIF) registers
- description: Clock Management (CKM) registers
reg-names:
items:
- const: sif
- const: ckm
"#phy-cells":
const: 0
nvmem-cells:
items:
- description: Internal resistor selection of TX Bias Current
- description: XTP Bias V2V voltage calibration
- description: S-PLL CKTX Impedance Selection (P-MOSFET side)
- description: S-PLL CKTX Impedance Selection (N-MOSFET side)
- description: S-PLL CKTX Intermediate Transition Impedance
- description: RX Front-End Return Loss Continuous Time Linear EQ
- description: RX Front-End Return Loss Variable Gain Amplifier
- description: RX Front-End Return Loss Decision Feedback EQ
- description: RX Impedance Selection
- description: TX Impedance Selection (P-MOSFET side)
- description: TX Impedance Selection (N-MOSFET side)
- description: TX Lane 0 Switch Resistance (RSWn)
- description: TX Lane 1 Switch Resistance (RSWn)
- description: eFuse calibration table version
nvmem-cell-names:
items:
- const: int-r
- const: xtp-vtrim
- const: cktx-pmos
- const: cktx-nmos
- const: cktx-r-mid
- const: rxfe-lanes-rl-ctle
- const: rxfe-lanes-rl-vga
- const: rxfe-lanes-rl-dfe
- const: rx-lanes-imp
- const: tx-lanes-pmos
- const: tx-lanes-nmos
- const: tx-ln0-rswn
- const: tx-ln1-rswn
- const: cal-version
power-domains:
maxItems: 1
required:
- compatible
- reg
- reg-names
- "#phy-cells"
additionalProperties: false
examples:
- |
phy@16900000 {
compatible = "mediatek,mt8196-pcie-sphy";
reg = <0x16900000 0x10000>, <0x16920000 0x10000>;
reg-names = "sif", "ckm";
power-domains = <&spm 10>;
#phy-cells = <0>;
};

View File

@ -90,6 +90,7 @@ properties:
- items:
- enum:
- mediatek,mt8188-tphy
- mediatek,mt8189-tphy
- mediatek,mt8195-tphy
- const: mediatek,generic-tphy-v3
- const: mediatek,mt2701-u3phy

View File

@ -108,6 +108,11 @@ patternProperties:
- PHY_TYPE_USB2
- PHY_TYPE_USB3
phys:
description:
eUSB2 repeater PHY connected between this port and the USB connector.
maxItems: 1
# The following optional vendor properties are only for debug or HQA test
mediatek,eye-src:
description:

View File

@ -45,6 +45,8 @@ properties:
enum:
- nvidia,tegra194-xusb-padctl
- nvidia,tegra234-xusb-padctl
- nvidia,tegra238-xusb-padctl
- nvidia,tegra264-xusb-padctl
reg:
items:
@ -541,6 +543,43 @@ properties:
description: connection to a USB Type C controller
$ref: /schemas/graph.yaml#/properties/port
allOf:
- if:
properties:
compatible:
contains:
const: nvidia,tegra238-xusb-padctl
then:
properties:
pads:
properties:
usb2:
properties:
lanes:
properties:
usb2-3: false
usb3:
properties:
lanes:
properties:
usb3-3: false
ports:
properties:
usb2-3: false
usb3-0:
properties:
nvidia,usb2-companion:
enum: [ 0, 1, 2 ]
usb3-1:
properties:
nvidia,usb2-companion:
enum: [ 0, 1, 2 ]
usb3-2:
properties:
nvidia,usb2-companion:
enum: [ 0, 1, 2 ]
usb3-3: false
additionalProperties: false
required:

View File

@ -0,0 +1,108 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/nvidia,tegra264-mphy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra264 MIPI M-PHY
maintainers:
- Kartik Rajput <kkartik@nvidia.com>
- Thierry Reding <treding@nvidia.com>
properties:
compatible:
const: nvidia,tegra264-mphy
reg:
items:
- description: Lane 0 register window
- description: Lane 1 register window
reg-names:
items:
- const: l0
- const: l1
clocks:
maxItems: 11
clock-names:
items:
- const: pll
- const: tx-ref
- const: l0-tx-sym
- const: l0-tx-ls
- const: l0-tx-hs
- const: l0-tx-fifo
- const: l0-rx-sym
- const: l0-rx-ana
- const: l0-rx-ls
- const: l0-rx-hs
- const: l1-rx-ana
resets:
maxItems: 5
reset-names:
items:
- const: clk-ctl
- const: l0-rx
- const: l0-tx
- const: l1-rx
- const: l1-tx
'#phy-cells':
description: |
The cell selects one M-PHY lane direction. Valid values are defined in
<dt-bindings/phy/nvidia,tegra264-mphy.h>: TEGRA_MPHY_L0_TX, TEGRA_MPHY_L0_RX,
TEGRA_MPHY_L1_TX and TEGRA_MPHY_L1_RX.
const: 1
required:
- compatible
- reg
- reg-names
- clocks
- clock-names
- resets
- reset-names
- '#phy-cells'
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/nvidia,tegra264.h>
#include <dt-bindings/reset/nvidia,tegra264.h>
#include <dt-bindings/phy/nvidia,tegra264-mphy.h>
mphy: phy@b910000 {
compatible = "nvidia,tegra264-mphy";
reg = <0xb910000 0x10000>,
<0xb920000 0x10000>;
reg-names = "l0", "l1";
clocks = <&bpmp TEGRA264_CLK_MPHY_CORE_PLL_FIXED>,
<&bpmp TEGRA264_CLK_MPHY_TX_1MHZ_REF>,
<&bpmp TEGRA264_CLK_MPHY_L0_TX_SYMB>,
<&bpmp TEGRA264_CLK_MPHY_L0_TX_LS_3XBIT>,
<&bpmp TEGRA264_CLK_MPHY_L0_TX_HS_SYMB_DIV>,
<&bpmp TEGRA264_CLK_MPHY_L0_UPHY_TX_FIFO>,
<&bpmp TEGRA264_CLK_MPHY_L0_RX_SYMB>,
<&bpmp TEGRA264_CLK_MPHY_L0_RX_ANA>,
<&bpmp TEGRA264_CLK_MPHY_L0_RX_LS_BIT>,
<&bpmp TEGRA264_CLK_MPHY_L0_RX_HS_SYMB_DIV>,
<&bpmp TEGRA264_CLK_MPHY_L1_RX_ANA>;
clock-names = "pll", "tx-ref",
"l0-tx-sym", "l0-tx-ls", "l0-tx-hs", "l0-tx-fifo",
"l0-rx-sym", "l0-rx-ana", "l0-rx-ls", "l0-rx-hs",
"l1-rx-ana";
resets = <&bpmp TEGRA264_RESET_MPHY_CLK_CTL>,
<&bpmp TEGRA264_RESET_MPHY_L0_RX>,
<&bpmp TEGRA264_RESET_MPHY_L0_TX>,
<&bpmp TEGRA264_RESET_MPHY_L1_RX>,
<&bpmp TEGRA264_RESET_MPHY_L1_TX>;
reset-names = "clk-ctl", "l0-rx", "l0-tx", "l1-rx", "l1-tx";
#phy-cells = <1>;
};
...

View File

@ -110,10 +110,34 @@ properties:
port:
$ref: /schemas/graph.yaml#/properties/port
deprecated: true
description:
A port node to link the PHY to a TypeC controller for the purpose of
handling orientation switching.
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description:
Output endpoint of the PHY for USB (or DP when configured into 4 lane
mode), which should point to the superspeed port of a USB connector.
port@1:
$ref: /schemas/graph.yaml#/properties/port
description: Incoming endpoint from the USB controller
port@2:
$ref: /schemas/graph.yaml#/properties/port
description: Incoming endpoint from the DisplayPort controller
port@3:
$ref: /schemas/graph.yaml#/properties/port
description:
Output endpoint of the PHY for DP Auxiliary, which should either point to
the SBU port of a USB-C connector or a DisplayPort connector input port.
required:
- compatible
- reg

View File

@ -21,6 +21,7 @@ properties:
- qcom,glymur-m31-eusb2-phy
- qcom,hawi-m31-eusb2-phy
- qcom,kaanapali-m31-eusb2-phy
- qcom,maili-m31-eusb2-phy
- const: qcom,sm8750-m31-eusb2-phy
- const: qcom,sm8750-m31-eusb2-phy

View File

@ -0,0 +1,267 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/qcom,nord-qmp-gen5x16-pcie-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Nord QMP Quadfurcated PCIe multiple link-mode PHY
maintainers:
- Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
description:
The Qualcomm Nord PCIe PHY is a Gen5-capable QMP PCIe PHY composed
of four PHY ports (A/B/C/D). The ports can be combined to support
multiple lane topologies, including x16, x8+x8, x8+x4+x4 and
x8+x4+x2+x2 configurations.
The selected topology is determined by the PCIe link configuration mode
programmed in TCSR registers. Depending on the selected mode, PHY ports
may operate independently or be grouped together to form wider PCIe links.
Each port provides dedicated register space, clocks, resets and power
resources required for PHY operation.
properties:
compatible:
enum:
- qcom,nord-qmp-gen5x16-pcie-phy
reg:
maxItems: 4
reg-names:
items:
- const: port_a
- const: port_b
- const: port_c
- const: port_d
clocks:
maxItems: 17
clock-names:
items:
- const: aux_a
- const: cfg_ahb_a
- const: ref
- const: rchng_a
- const: pipe_a
- const: aux_b
- const: cfg_ahb_b
- const: rchng_b
- const: pipe_b
- const: aux_c
- const: cfg_ahb_c
- const: rchng_c
- const: pipe_c
- const: aux_d
- const: cfg_ahb_d
- const: rchng_d
- const: pipe_d
power-domains:
maxItems: 4
power-domain-names:
items:
- const: port_a
- const: port_b
- const: port_c
- const: port_d
resets:
maxItems: 8
reset-names:
items:
- const: port_a
- const: port_a_nocsr
- const: port_b
- const: port_b_nocsr
- const: port_c
- const: port_c_nocsr
- const: port_d
- const: port_d_nocsr
vdda-phy-a-supply: true
vdda-phy-b-supply: true
vdda-phy-c-supply: true
vdda-phy-d-supply: true
vdda-pll-a-supply: true
vdda-pll-b-supply: true
vdda-pll-c-supply: true
vdda-pll-d-supply: true
qcom,link-mode:
description:
Reference to the TCSR syscon register containing the PCIe link mode
selected by firmware. The property consists of the TCSR syscon
phandle followed by the offset of the TCSR_PCIE_LINK_CONFIG_MODE
register.
The register value selects one of these topologies.
0 (QMP_PCIE_NORD_MODE_X16) — all ports combined as one x16 PHY
1 (QMP_PCIE_NORD_MODE_X8_X8) — port A as x8 and ports B+C+D as x8
2 (QMP_PCIE_NORD_MODE_X8_X4_X4) — port A as x8, port B as x4, and ports C+D as x4
3 (QMP_PCIE_NORD_MODE_X8_X4_X2_X2) — port A as x8, port B as x4, and ports C and D as x2
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- items:
- description: Phandle of the TCSR syscon
- description: Offset of TCSR_PCIE_LINK_CONFIG_MODE register
"#clock-cells":
const: 1
description:
Selects the pipe clock output by port index (0=A, 1=B, 2=C, 3=D).
GCC pipe-clock mux parents reference this provider.
clock-output-names:
items:
- description: Name of the PHY A pipe clock output.
- description: Name of the PHY B pipe clock output.
- description: Name of the PHY C pipe clock output.
- description: Name of the PHY D pipe clock output.
"#phy-cells":
const: 1
description:
The cell is the index of the sub-PHY within the active link
mode, e.g. on nord, 0 for "port a" and 1 for "port b" etc.
required:
- compatible
- reg
- reg-names
- clocks
- clock-names
- power-domains
- power-domain-names
- resets
- reset-names
- vdda-phy-a-supply
- vdda-phy-b-supply
- vdda-phy-c-supply
- vdda-phy-d-supply
- vdda-pll-a-supply
- vdda-pll-b-supply
- vdda-pll-c-supply
- vdda-pll-d-supply
- qcom,link-mode
- "#clock-cells"
- clock-output-names
- "#phy-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,nord-gcc.h>
#include <dt-bindings/clock/qcom,nord-tcsrcc.h>
phy@1b00000 {
compatible = "qcom,nord-qmp-gen5x16-pcie-phy";
reg = <0x01b00000 0x10000>,
<0x01b10000 0x10000>,
<0x01b20000 0x10000>,
<0x01b30000 0x10000>;
reg-names = "port_a",
"port_b",
"port_c",
"port_d";
clocks = <&gcc GCC_PCIE_A_PHY_AUX_CLK>,
<&gcc GCC_PCIE_A_CFG_AHB_CLK>,
<&tcsrcc TCSR_PCIE_CLKREF_EN>,
<&gcc GCC_PCIE_A_PHY_RCHNG_CLK>,
<&gcc GCC_PCIE_A_PIPE_CLK>,
<&gcc GCC_PCIE_B_PHY_AUX_CLK>,
<&gcc GCC_PCIE_B_CFG_AHB_CLK>,
<&gcc GCC_PCIE_B_PHY_RCHNG_CLK>,
<&gcc GCC_PCIE_B_PIPE_CLK>,
<&gcc GCC_PCIE_C_PHY_AUX_CLK>,
<&gcc GCC_PCIE_C_CFG_AHB_CLK>,
<&gcc GCC_PCIE_C_PHY_RCHNG_CLK>,
<&gcc GCC_PCIE_C_PIPE_CLK>,
<&gcc GCC_PCIE_D_PHY_AUX_CLK>,
<&gcc GCC_PCIE_D_CFG_AHB_CLK>,
<&gcc GCC_PCIE_D_PHY_RCHNG_CLK>,
<&gcc GCC_PCIE_D_PIPE_CLK>;
clock-names = "aux_a",
"cfg_ahb_a",
"ref",
"rchng_a",
"pipe_a",
"aux_b",
"cfg_ahb_b",
"rchng_b",
"pipe_b",
"aux_c",
"cfg_ahb_c",
"rchng_c",
"pipe_c",
"aux_d",
"cfg_ahb_d",
"rchng_d",
"pipe_d";
assigned-clocks = <&gcc GCC_PCIE_A_PHY_RCHNG_CLK>,
<&gcc GCC_PCIE_B_PHY_RCHNG_CLK>,
<&gcc GCC_PCIE_C_PHY_RCHNG_CLK>,
<&gcc GCC_PCIE_D_PHY_RCHNG_CLK>;
assigned-clock-rates = <100000000>, <100000000>,
<100000000>, <100000000>;
power-domains = <&gcc GCC_PCIE_A_PHY_GDSC>,
<&gcc GCC_PCIE_B_PHY_GDSC>,
<&gcc GCC_PCIE_C_PHY_GDSC>,
<&gcc GCC_PCIE_D_PHY_GDSC>;
power-domain-names = "port_a",
"port_b",
"port_c",
"port_d";
resets = <&gcc GCC_PCIE_A_PHY_COM_BCR>,
<&gcc GCC_PCIE_A_NOCSR_COM_PHY_BCR>,
<&gcc GCC_PCIE_B_PHY_COM_BCR>,
<&gcc GCC_PCIE_B_NOCSR_COM_PHY_BCR>,
<&gcc GCC_PCIE_C_PHY_COM_BCR>,
<&gcc GCC_PCIE_C_NOCSR_COM_PHY_BCR>,
<&gcc GCC_PCIE_D_PHY_COM_BCR>,
<&gcc GCC_PCIE_D_NOCSR_COM_PHY_BCR>;
reset-names = "port_a",
"port_a_nocsr",
"port_b",
"port_b_nocsr",
"port_c",
"port_c_nocsr",
"port_d",
"port_d_nocsr";
vdda-phy-a-supply = <&vreg_l6a_1p2>;
vdda-phy-b-supply = <&vreg_l4a_1p2>;
vdda-phy-c-supply = <&vreg_l4a_1p2>;
vdda-phy-d-supply = <&vreg_l4a_1p2>;
vdda-pll-a-supply = <&vreg_l1e_0p9>;
vdda-pll-b-supply = <&vreg_l1e_0p9>;
vdda-pll-c-supply = <&vreg_l1e_0p9>;
vdda-pll-d-supply = <&vreg_l1e_0p9>;
qcom,link-mode = <&tcsr 0x71050>;
#clock-cells = <1>;
clock-output-names = "pcie_a_pipe_clk",
"pcie_b_pipe_clk",
"pcie_c_pipe_clk",
"pcie_d_pipe_clk";
#phy-cells = <1>;
};

View File

@ -15,47 +15,57 @@ description:
properties:
compatible:
enum:
- qcom,eliza-qmp-gen3x1-pcie-phy
- qcom,eliza-qmp-gen3x2-pcie-phy
- qcom,glymur-qmp-gen4x2-pcie-phy
- qcom,glymur-qmp-gen5x4-pcie-phy
- qcom,hawi-qmp-gen3x2-pcie-phy
- qcom,hawi-qmp-gen4x1-pcie-phy
- qcom,kaanapali-qmp-gen3x2-pcie-phy
- qcom,qcs615-qmp-gen3x1-pcie-phy
- qcom,qcs8300-qmp-gen4x2-pcie-phy
- qcom,sa8775p-qmp-gen4x2-pcie-phy
- qcom,sa8775p-qmp-gen4x4-pcie-phy
- qcom,sar2130p-qmp-gen3x2-pcie-phy
- qcom,sc8180x-qmp-pcie-phy
- qcom,sc8280xp-qmp-gen3x1-pcie-phy
- qcom,sc8280xp-qmp-gen3x2-pcie-phy
- qcom,sc8280xp-qmp-gen3x4-pcie-phy
- qcom,sdm845-qhp-pcie-phy
- qcom,sdm845-qmp-pcie-phy
- qcom,sdx55-qmp-pcie-phy
- qcom,sdx65-qmp-gen4x2-pcie-phy
- qcom,sm8150-qmp-gen3x1-pcie-phy
- qcom,sm8150-qmp-gen3x2-pcie-phy
- qcom,sm8250-qmp-gen3x1-pcie-phy
- qcom,sm8250-qmp-gen3x2-pcie-phy
- qcom,sm8250-qmp-modem-pcie-phy
- qcom,sm8350-qmp-gen3x1-pcie-phy
- qcom,sm8350-qmp-gen3x2-pcie-phy
- qcom,sm8450-qmp-gen3x1-pcie-phy
- qcom,sm8450-qmp-gen4x2-pcie-phy
- qcom,sm8475-qmp-gen3x1-pcie-phy
- qcom,sm8550-qmp-gen3x2-pcie-phy
- qcom,sm8550-qmp-gen4x2-pcie-phy
- qcom,sm8650-qmp-gen3x2-pcie-phy
- qcom,sm8650-qmp-gen4x2-pcie-phy
- qcom,sm8750-qmp-gen3x2-pcie-phy
- qcom,x1e80100-qmp-gen3x2-pcie-phy
- qcom,x1e80100-qmp-gen4x2-pcie-phy
- qcom,x1e80100-qmp-gen4x4-pcie-phy
- qcom,x1e80100-qmp-gen4x8-pcie-phy
- qcom,x1p42100-qmp-gen4x4-pcie-phy
oneOf:
- items:
- enum:
- qcom,maili-qmp-gen3x2-pcie-phy
- const: qcom,hawi-qmp-gen3x2-pcie-phy
- items:
- enum:
- qcom,maili-qmp-gen4x1-pcie-phy
- const: qcom,hawi-qmp-gen4x1-pcie-phy
- enum:
- qcom,eliza-qmp-gen3x1-pcie-phy
- qcom,eliza-qmp-gen3x2-pcie-phy
- qcom,glymur-qmp-gen4x2-pcie-phy
- qcom,glymur-qmp-gen5x4-pcie-phy
- qcom,hawi-qmp-gen3x2-pcie-phy
- qcom,hawi-qmp-gen4x1-pcie-phy
- qcom,kaanapali-qmp-gen3x2-pcie-phy
- qcom,qcs615-qmp-gen3x1-pcie-phy
- qcom,qcs8300-qmp-gen4x2-pcie-phy
- qcom,sa8775p-qmp-gen4x2-pcie-phy
- qcom,sa8775p-qmp-gen4x4-pcie-phy
- qcom,sar2130p-qmp-gen3x2-pcie-phy
- qcom,sc8180x-qmp-pcie-phy
- qcom,sc8280xp-qmp-gen3x1-pcie-phy
- qcom,sc8280xp-qmp-gen3x2-pcie-phy
- qcom,sc8280xp-qmp-gen3x4-pcie-phy
- qcom,sdm845-qhp-pcie-phy
- qcom,sdm845-qmp-pcie-phy
- qcom,sdx55-qmp-pcie-phy
- qcom,sdx65-qmp-gen4x2-pcie-phy
- qcom,shikra-qmp-gen2x1-pcie-phy
- qcom,sm8150-qmp-gen3x1-pcie-phy
- qcom,sm8150-qmp-gen3x2-pcie-phy
- qcom,sm8250-qmp-gen3x1-pcie-phy
- qcom,sm8250-qmp-gen3x2-pcie-phy
- qcom,sm8250-qmp-modem-pcie-phy
- qcom,sm8350-qmp-gen3x1-pcie-phy
- qcom,sm8350-qmp-gen3x2-pcie-phy
- qcom,sm8450-qmp-gen3x1-pcie-phy
- qcom,sm8450-qmp-gen4x2-pcie-phy
- qcom,sm8475-qmp-gen3x1-pcie-phy
- qcom,sm8550-qmp-gen3x2-pcie-phy
- qcom,sm8550-qmp-gen4x2-pcie-phy
- qcom,sm8650-qmp-gen3x2-pcie-phy
- qcom,sm8650-qmp-gen4x2-pcie-phy
- qcom,sm8750-qmp-gen3x2-pcie-phy
- qcom,x1e80100-qmp-gen3x2-pcie-phy
- qcom,x1e80100-qmp-gen4x2-pcie-phy
- qcom,x1e80100-qmp-gen4x4-pcie-phy
- qcom,x1e80100-qmp-gen4x8-pcie-phy
- qcom,x1p42100-qmp-gen4x4-pcie-phy
reg:
minItems: 1
@ -169,6 +179,7 @@ allOf:
- qcom,sdm845-qhp-pcie-phy
- qcom,sdm845-qmp-pcie-phy
- qcom,sdx55-qmp-pcie-phy
- qcom,shikra-qmp-gen2x1-pcie-phy
- qcom,sm8150-qmp-gen3x1-pcie-phy
- qcom,sm8150-qmp-gen3x2-pcie-phy
- qcom,sm8250-qmp-gen3x1-pcie-phy

View File

@ -16,6 +16,10 @@ description:
properties:
compatible:
oneOf:
- items:
- enum:
- qcom,maili-qmp-ufs-phy
- const: qcom,hawi-qmp-ufs-phy
- items:
- enum:
- qcom,qcs8300-qmp-ufs-phy

View File

@ -16,6 +16,10 @@ description:
properties:
compatible:
oneOf:
- items:
- enum:
- qcom,maili-qmp-usb3-dp-phy
- const: qcom,hawi-qmp-usb3-dp-phy
- items:
- enum:
- qcom,eliza-qmp-usb3-dp-phy
@ -27,6 +31,7 @@ properties:
- enum:
- qcom,glymur-qmp-usb3-dp-phy
- qcom,hawi-qmp-usb3-dp-phy
- qcom,nord-qmp-usb3-dp-phy
- qcom,sar2130p-qmp-usb3-dp-phy
- qcom,sc7180-qmp-usb3-dp-phy
- qcom,sc7280-qmp-usb3-dp-phy
@ -53,12 +58,7 @@ properties:
clock-names:
minItems: 4
items:
- const: aux
- const: ref
- const: com_aux
- const: usb3_pipe
- const: cfg_ahb
maxItems: 5
power-domains:
maxItems: 1
@ -189,21 +189,59 @@ allOf:
- if:
properties:
compatible:
enum:
- qcom,sc7180-qmp-usb3-dp-phy
- qcom,sdm845-qmp-usb3-dp-phy
contains:
enum:
- qcom,x1e80100-qmp-usb3-dp-phy
then:
properties:
clocks:
maxItems: 5
clock-names:
items:
- const: aux
- const: ref
- const: com_aux
- const: usb3_pipe
- const: p2rr2p_pipe
- if:
properties:
compatible:
contains:
enum:
- qcom,sc7180-qmp-usb3-dp-phy
- qcom,sdm845-qmp-usb3-dp-phy
then:
properties:
clocks:
maxItems: 5
else:
clock-names:
items:
- const: aux
- const: ref
- const: com_aux
- const: usb3_pipe
- const: cfg_ahb
- if:
not:
properties:
compatible:
contains:
enum:
- qcom,x1e80100-qmp-usb3-dp-phy
- qcom,sc7180-qmp-usb3-dp-phy
- qcom,sdm845-qmp-usb3-dp-phy
then:
properties:
clocks:
maxItems: 4
clock-names:
maxItems: 4
items:
- const: aux
- const: ref
- const: com_aux
- const: usb3_pipe
- if:
properties:
@ -212,6 +250,7 @@ allOf:
enum:
- qcom,glymur-qmp-usb3-dp-phy
- qcom,hawi-qmp-usb3-dp-phy
- qcom,nord-qmp-usb3-dp-phy
- qcom,sar2130p-qmp-usb3-dp-phy
- qcom,sc8280xp-qmp-usb43dp-phy
- qcom,sm6350-qmp-usb3-dp-phy

View File

@ -19,6 +19,7 @@ properties:
- enum:
- qcom,eliza-snps-eusb2-phy
- qcom,milos-snps-eusb2-phy
- qcom,nord-snps-eusb2-phy
- qcom,sar2130p-snps-eusb2-phy
- qcom,sdx75-snps-eusb2-phy
- qcom,sm8650-snps-eusb2-phy
@ -64,7 +65,17 @@ required:
- clock-names
- vdd-supply
- vdda12-supply
- resets
allOf:
- if:
not:
properties:
compatible:
contains:
const: qcom,nord-snps-eusb2-phy
then:
required:
- resets
additionalProperties: false

View File

@ -21,6 +21,7 @@ properties:
- rockchip,rk3326-csi-dphy
- rockchip,rk3368-csi-dphy
- rockchip,rk3568-csi-dphy
- rockchip,rk3576-csi-dphy
- rockchip,rk3588-csi-dphy
reg:
@ -99,6 +100,7 @@ allOf:
- rockchip,rk3326-csi-dphy
- rockchip,rk3368-csi-dphy
- rockchip,rk3568-csi-dphy
- rockchip,rk3576-csi-dphy
then:
properties:
resets:

View File

@ -18,6 +18,7 @@ properties:
- items:
- enum:
- microchip,ata6561
- microchip,mcp2542
- ti,tcan1051
- const: ti,tcan1042
- items:
@ -71,10 +72,11 @@ allOf:
- if:
properties:
compatible:
enum:
- nxp,tjr1443
- ti,tcan1042
- ti,tcan1043
contains:
enum:
- nxp,tjr1443
- ti,tcan1042
- ti,tcan1043
then:
properties:
'#phy-cells':

View File

@ -4157,6 +4157,14 @@ S: Maintained
F: Documentation/devicetree/bindings/usb/aspeed,ast2600-udc.yaml
F: drivers/usb/gadget/udc/aspeed_udc.c
ASPEED USB3 PHY DRIVER
M: Ryan Chen <ryan_chen@aspeedtech.com>
L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers)
L: linux-phy@lists.infradead.org
S: Maintained
F: Documentation/devicetree/bindings/phy/aspeed,ast2700-usb3-phy.yaml
F: drivers/phy/aspeed/phy-aspeed-usb3.c
ASPEED VIDEO ENGINE DRIVER
M: Eddie James <eajames@linux.ibm.com>
L: linux-media@vger.kernel.org
@ -11236,7 +11244,7 @@ R: Manivannan Sadhasivam <mani@kernel.org>
L: linux-phy@lists.infradead.org
S: Supported
Q: https://patchwork.kernel.org/project/linux-phy/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy.git next
F: Documentation/devicetree/bindings/phy/
F: drivers/phy/
F: include/dt-bindings/phy/

View File

@ -100,7 +100,7 @@ config PHY_GOOGLE_USB
the G5 generation (Laguna). This driver provides the PHY interfaces
to interact with the SNPS eUSB2 and USB 3.2/DisplayPort Combo PHY,
both of which are integrated with the DWC3 USB DRD controller.
This driver currently supports USB high-speed.
This driver currently supports USB high-speed and SuperSpeed.
config USB_LGM_PHY
tristate "INTEL Lightning Mountain USB PHY Driver"
@ -171,6 +171,7 @@ config PHY_XGENE
source "drivers/phy/allwinner/Kconfig"
source "drivers/phy/amlogic/Kconfig"
source "drivers/phy/apple/Kconfig"
source "drivers/phy/aspeed/Kconfig"
source "drivers/phy/axiado/Kconfig"
source "drivers/phy/broadcom/Kconfig"
source "drivers/phy/cadence/Kconfig"

View File

@ -23,6 +23,7 @@ obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
obj-$(CONFIG_GENERIC_PHY) += allwinner/ \
amlogic/ \
apple/ \
aspeed/ \
axiado/ \
broadcom/ \
cadence/ \

View File

@ -585,8 +585,6 @@ struct atcphy_mode_configuration {
* @phys.dp: DisplayPort PHY instance
* @phy_provider: PHY provider instance
* @rcdev: Reset controller device
* @sw: Type-C switch instance
* @mux: Type-C mux instance
* @lock: Mutex for synchronizing register access across PHY, Type-C switch/mux and reset controller
*/
struct apple_atcphy {
@ -972,6 +970,19 @@ static int atcphy_pipehandler_check(struct apple_atcphy *atcphy)
return 0;
}
static void atcphy_pipehandler_set_mux(struct apple_atcphy *atcphy, u32 data, u32 clk)
{
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_OFF));
udelay(10);
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_DATA,
FIELD_PREP(PIPEHANDLER_MUX_CTRL_DATA, data));
udelay(10);
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, clk));
udelay(10);
}
static int atcphy_configure_pipehandler_usb3(struct apple_atcphy *atcphy, bool host)
{
int ret;
@ -1054,15 +1065,8 @@ static int atcphy_configure_pipehandler_usb3(struct apple_atcphy *atcphy, bool h
}
/* Configure PIPE mux to USB3 PHY */
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_OFF));
udelay(10);
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_DATA,
FIELD_PREP(PIPEHANDLER_MUX_CTRL_DATA, PIPEHANDLER_MUX_CTRL_DATA_USB3));
udelay(10);
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_USB3));
udelay(10);
atcphy_pipehandler_set_mux(atcphy, PIPEHANDLER_MUX_CTRL_DATA_USB3,
PIPEHANDLER_MUX_CTRL_CLK_USB3);
/* Remove link detection override */
clear32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXVALID);
@ -1078,6 +1082,41 @@ static int atcphy_configure_pipehandler_usb3(struct apple_atcphy *atcphy, bool h
return 0;
}
static int atcphy_configure_pipehandler_usb4(struct apple_atcphy *atcphy)
{
int ret;
ret = atcphy_pipehandler_check(atcphy);
if (ret)
return ret;
/* Force disable link detection */
clear32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE_VALUES,
PIPEHANDLER_OVERRIDE_VAL_RXDETECT0 | PIPEHANDLER_OVERRIDE_VAL_RXDETECT1);
set32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXVALID);
set32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXDETECT);
ret = atcphy_pipehandler_lock(atcphy);
if (ret) {
dev_err(atcphy->dev, "Failed to lock pipehandler\n");
return ret;
}
/* Configure PIPE mux to the USB4/Thunderbolt controller */
atcphy_pipehandler_set_mux(atcphy, PIPEHANDLER_MUX_CTRL_DATA_USB4,
PIPEHANDLER_MUX_CTRL_CLK_USB4);
/* Remove link detection override */
clear32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXVALID);
clear32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXDETECT);
ret = atcphy_pipehandler_unlock(atcphy);
if (ret)
dev_warn(atcphy->dev, "Failed to unlock pipehandler\n");
return 0;
}
static int atcphy_configure_pipehandler_dummy(struct apple_atcphy *atcphy)
{
int ret;
@ -1097,15 +1136,8 @@ static int atcphy_configure_pipehandler_dummy(struct apple_atcphy *atcphy)
dev_warn(atcphy->dev, "Failed to lock pipehandler");
/* Switch to dummy PHY */
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_OFF));
udelay(10);
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_DATA,
FIELD_PREP(PIPEHANDLER_MUX_CTRL_DATA, PIPEHANDLER_MUX_CTRL_DATA_DUMMY));
udelay(10);
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_DUMMY));
udelay(10);
atcphy_pipehandler_set_mux(atcphy, PIPEHANDLER_MUX_CTRL_DATA_DUMMY,
PIPEHANDLER_MUX_CTRL_CLK_DUMMY);
ret = atcphy_pipehandler_unlock(atcphy);
if (ret)
@ -1121,7 +1153,7 @@ static int atcphy_configure_pipehandler_dummy(struct apple_atcphy *atcphy)
static int atcphy_configure_pipehandler(struct apple_atcphy *atcphy, bool host)
{
int ret;
int ret = -EINVAL;
lockdep_assert_held(&atcphy->lock);
@ -1131,13 +1163,13 @@ static int atcphy_configure_pipehandler(struct apple_atcphy *atcphy, bool host)
atcphy->pipehandler_up = true;
break;
case ATCPHY_PIPEHANDLER_STATE_USB4:
dev_warn(atcphy->dev,
"ATCPHY_PIPEHANDLER_STATE_USB4 not implemented; falling back to USB2\n");
ret = atcphy_configure_pipehandler_usb4(atcphy);
atcphy->pipehandler_up = true;
break;
case ATCPHY_PIPEHANDLER_STATE_DUMMY:
ret = atcphy_configure_pipehandler_dummy(atcphy);
atcphy->pipehandler_up = false;
break;
default:
ret = -EINVAL;
}
return ret;
@ -1147,15 +1179,8 @@ static void atcphy_setup_pipehandler(struct apple_atcphy *atcphy)
{
lockdep_assert_held(&atcphy->lock);
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_OFF));
udelay(10);
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_DATA,
FIELD_PREP(PIPEHANDLER_MUX_CTRL_DATA, PIPEHANDLER_MUX_CTRL_DATA_DUMMY));
udelay(10);
mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_DUMMY));
udelay(10);
atcphy_pipehandler_set_mux(atcphy, PIPEHANDLER_MUX_CTRL_DATA_DUMMY,
PIPEHANDLER_MUX_CTRL_CLK_DUMMY);
}
static void atcphy_configure_lanes(struct apple_atcphy *atcphy, enum atcphy_mode mode)

View File

@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# PHY drivers for ASPEED
#
config PHY_ASPEED_USB3
tristate "ASPEED USB3 PHY driver"
select GENERIC_PHY
depends on (ARCH_ASPEED || COMPILE_TEST)
help
Enable this to support the USB 3.2 PHY on the Aspeed AST2700 SoC.
It supports SuperSpeedPlus Gen2x1 (10 Gbps), SuperSpeed (5 Gbps),
High Speed (480 Mbps), Full Speed (12 Mbps) and Low Speed
(1.5 Mbps), and is paired with the DWC3 USB controller.

View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_ASPEED_USB3) += phy-aspeed-usb3.o

View File

@ -0,0 +1,236 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2026 Aspeed Technology Inc.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#define PHY3S00 0x00
#define PHY3S00_INIT_DONE BIT(15)
#define PHY3S00_SRAM_BYPASS BIT(7)
#define PHY3S00_SRAM_EXT_LOAD BIT(6)
#define PHY3S04 0x04
#define PHY3C00 0x08
#define PHY3C04 0x0c
#define PHY3P00 0x10
#define PHY3P00_RX_ADAPT_AFE_EN_G1 BIT(0)
#define PHY3P00_RX_ADAPT_AFE_EN_G2 BIT(1)
#define PHY3P00_RX_ADAPT_DFE_EN_G1 BIT(2)
#define PHY3P00_RX_ADAPT_DFE_EN_G2 BIT(3)
#define PHY3P00_RX_CDR_VCO_LOWFREQ_G1 BIT(4)
#define PHY3P00_RX_CDR_VCO_LOWFREQ_G2 BIT(5)
#define PHY3P00_RX_EQ_AFE_GAIN_G1 GENMASK(9, 6)
#define PHY3P00_RX_EQ_AFE_GAIN_G2 GENMASK(13, 10)
#define PHY3P00_RX_EQ_ATT_LVL_G1 GENMASK(16, 14)
#define PHY3P00_RX_EQ_ATT_LVL_G2 GENMASK(19, 17)
#define PHY3P00_RX_EQ_CTLE_BOOST_G1 GENMASK(24, 20)
#define PHY3P00_RX_EQ_CTLE_BOOST_G2 GENMASK(29, 25)
#define PHY3P00_RX_EQ_DELTA_IQ_G1_LO GENMASK(31, 30)
#define PHY3P04 0x14
#define PHY3P04_RX_EQ_DELTA_IQ_G1_HI GENMASK(1, 0)
#define PHY3P04_RX_EQ_DELTA_IQ_G2 GENMASK(5, 2)
#define PHY3P04_RX_EQ_DFE_TAP1_G1 GENMASK(13, 6)
#define PHY3P04_RX_EQ_DFE_TAP1_G2 GENMASK(21, 14)
#define PHY3P04_RX_LOS_LFPS_EN BIT(22)
#define PHY3P04_RX_LOS_THRESHOLD GENMASK(25, 23)
#define PHY3P04_RX_TERM_CTRL GENMASK(28, 26)
#define PHY3P04_TX_EQ_MAIN_G1_LO GENMASK(31, 29)
#define PHY3P08 0x18
#define PHY3P08_TX_EQ_MAIN_G1_HI GENMASK(1, 0)
#define PHY3P08_TX_EQ_MAIN_G2 GENMASK(6, 2)
#define PHY3P08_TX_EQ_OVRD BIT(7)
#define PHY3P08_TX_EQ_POST_G1 GENMASK(12, 9)
#define PHY3P08_TX_EQ_POST_G2 GENMASK(16, 13)
#define PHY3P08_TX_EQ_PRE_G1 GENMASK(20, 17)
#define PHY3P08_TX_EQ_PRE_G2 GENMASK(24, 21)
#define PHY3P08_TX_IBOOST_LVL GENMASK(28, 25)
#define PHY3P08_TX_TERM_CTRL GENMASK(31, 29)
#define PHY3P0C 0x1c
#define PHY3P0C_TX_VBOOST_EN BIT(0)
#define PHY3CMD 0x40
#define PHY3P_RX_EQ_CTLE_BOOST_G1_DEFAULT 0x7
#define PHY3P_RX_EQ_CTLE_BOOST_G2_DEFAULT 0x7
#define PHY3P_RX_EQ_DELTA_IQ_G1_DEFAULT 0x3
#define PHY3P_RX_EQ_DELTA_IQ_G2_DEFAULT 0x5
#define PHY3P_RX_LOS_THRESHOLD_DEFAULT 0x3
#define PHY3P_RX_TERM_CTRL_DEFAULT 0x2
#define PHY3P_TX_EQ_MAIN_G1_DEFAULT 0xa
#define PHY3P_TX_EQ_MAIN_G2_DEFAULT 0x9
#define PHY3P_TX_EQ_POST_G1_DEFAULT 0x4
#define PHY3P_TX_EQ_POST_G2_DEFAULT 0x3
#define PHY3P_TX_EQ_PRE_G2_DEFAULT 0x2
#define PHY3P_TX_IBOOST_LVL_DEFAULT 0xf
#define PHY3P_TX_TERM_CTRL_DEFAULT 0x2
#define PHY3P00_DEFAULT ( \
PHY3P00_RX_ADAPT_AFE_EN_G1 | \
PHY3P00_RX_ADAPT_AFE_EN_G2 | \
PHY3P00_RX_ADAPT_DFE_EN_G1 | \
PHY3P00_RX_ADAPT_DFE_EN_G2 | \
FIELD_PREP(PHY3P00_RX_EQ_CTLE_BOOST_G1, PHY3P_RX_EQ_CTLE_BOOST_G1_DEFAULT) | \
FIELD_PREP(PHY3P00_RX_EQ_CTLE_BOOST_G2, PHY3P_RX_EQ_CTLE_BOOST_G2_DEFAULT) | \
FIELD_PREP(PHY3P00_RX_EQ_DELTA_IQ_G1_LO, \
PHY3P_RX_EQ_DELTA_IQ_G1_DEFAULT & 0x3) \
)
#define PHY3P04_DEFAULT ( \
FIELD_PREP(PHY3P04_RX_EQ_DELTA_IQ_G1_HI, \
PHY3P_RX_EQ_DELTA_IQ_G1_DEFAULT >> 2) | \
FIELD_PREP(PHY3P04_RX_EQ_DELTA_IQ_G2, PHY3P_RX_EQ_DELTA_IQ_G2_DEFAULT) | \
PHY3P04_RX_LOS_LFPS_EN | \
FIELD_PREP(PHY3P04_RX_LOS_THRESHOLD, PHY3P_RX_LOS_THRESHOLD_DEFAULT) | \
FIELD_PREP(PHY3P04_RX_TERM_CTRL, PHY3P_RX_TERM_CTRL_DEFAULT) | \
FIELD_PREP(PHY3P04_TX_EQ_MAIN_G1_LO, \
PHY3P_TX_EQ_MAIN_G1_DEFAULT & 0x7) \
)
#define PHY3P08_DEFAULT ( \
FIELD_PREP(PHY3P08_TX_EQ_MAIN_G1_HI, PHY3P_TX_EQ_MAIN_G1_DEFAULT >> 3) | \
FIELD_PREP(PHY3P08_TX_EQ_MAIN_G2, PHY3P_TX_EQ_MAIN_G2_DEFAULT) | \
FIELD_PREP(PHY3P08_TX_EQ_POST_G1, PHY3P_TX_EQ_POST_G1_DEFAULT) | \
FIELD_PREP(PHY3P08_TX_EQ_POST_G2, PHY3P_TX_EQ_POST_G2_DEFAULT) | \
FIELD_PREP(PHY3P08_TX_EQ_PRE_G2, PHY3P_TX_EQ_PRE_G2_DEFAULT) | \
FIELD_PREP(PHY3P08_TX_IBOOST_LVL, PHY3P_TX_IBOOST_LVL_DEFAULT) | \
FIELD_PREP(PHY3P08_TX_TERM_CTRL, PHY3P_TX_TERM_CTRL_DEFAULT) \
)
#define PHY3P0C_DEFAULT \
PHY3P0C_TX_VBOOST_EN
struct aspeed_usb3_phy {
void __iomem *regs;
struct reset_control *rst;
struct device *dev;
struct clk *clk;
};
static int aspeed_usb3_phy_init(struct phy *phy)
{
struct aspeed_usb3_phy *aspeed_phy = phy_get_drvdata(phy);
u32 val;
int ret;
ret = clk_prepare_enable(aspeed_phy->clk);
if (ret) {
dev_err(aspeed_phy->dev, "Failed to enable clock %d\n", ret);
return ret;
}
ret = reset_control_deassert(aspeed_phy->rst);
if (ret)
goto err_disable_clk;
/* Wait for USB3 PHY internal SRAM initialization done */
ret = readl_poll_timeout(aspeed_phy->regs + PHY3S00, val,
val & PHY3S00_INIT_DONE,
USEC_PER_MSEC, 10 * USEC_PER_MSEC);
if (ret) {
dev_err(aspeed_phy->dev, "SRAM init timeout\n");
goto err_assert_reset;
}
val = readl(aspeed_phy->regs + PHY3S00);
val |= PHY3S00_SRAM_EXT_LOAD;
writel(val, aspeed_phy->regs + PHY3S00);
/* Set protocol1_ext signals as default PHY3 settings based on SNPS documents.
* Including PCFGI[54]: protocol1_ext_rx_los_lfps_en for better compatibility
*/
writel(PHY3P00_DEFAULT, aspeed_phy->regs + PHY3P00);
writel(PHY3P04_DEFAULT, aspeed_phy->regs + PHY3P04);
writel(PHY3P08_DEFAULT, aspeed_phy->regs + PHY3P08);
writel(PHY3P0C_DEFAULT, aspeed_phy->regs + PHY3P0C);
return 0;
err_assert_reset:
reset_control_assert(aspeed_phy->rst);
err_disable_clk:
clk_disable_unprepare(aspeed_phy->clk);
return ret;
}
static int aspeed_usb3_phy_exit(struct phy *phy)
{
struct aspeed_usb3_phy *aspeed_phy = phy_get_drvdata(phy);
reset_control_assert(aspeed_phy->rst);
clk_disable_unprepare(aspeed_phy->clk);
return 0;
}
static const struct phy_ops aspeed_usb3_phy_ops = {
.init = aspeed_usb3_phy_init,
.exit = aspeed_usb3_phy_exit,
.owner = THIS_MODULE,
};
static int aspeed_usb3_phy_probe(struct platform_device *pdev)
{
struct aspeed_usb3_phy *aspeed_phy;
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct phy *phy;
aspeed_phy = devm_kzalloc(dev, sizeof(*aspeed_phy), GFP_KERNEL);
if (!aspeed_phy)
return -ENOMEM;
aspeed_phy->dev = dev;
aspeed_phy->clk = devm_clk_get(dev, NULL);
if (IS_ERR(aspeed_phy->clk))
return PTR_ERR(aspeed_phy->clk);
aspeed_phy->rst = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(aspeed_phy->rst))
return PTR_ERR(aspeed_phy->rst);
aspeed_phy->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(aspeed_phy->regs))
return PTR_ERR(aspeed_phy->regs);
phy = devm_phy_create(dev, NULL, &aspeed_usb3_phy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
phy_set_drvdata(phy, aspeed_phy);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id aspeed_usb3_phy_match_table[] = {
{
.compatible = "aspeed,ast2700-usb3-phy",
},
{ }
};
MODULE_DEVICE_TABLE(of, aspeed_usb3_phy_match_table);
static struct platform_driver aspeed_usb3_phy_driver = {
.probe = aspeed_usb3_phy_probe,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = aspeed_usb3_phy_match_table,
},
};
module_platform_driver(aspeed_usb3_phy_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ASPEED USB3.2 PHY Driver");
MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");

View File

@ -334,7 +334,7 @@ static int brcm_stb_sata_16nm_ssc_init(struct brcm_sata_port *port)
brcm_sata_phy_wr(port, RXPMD_REG_BANK, RXPMD_RX_CDR_CONTROL1,
~RXPMD_RX_PPM_VAL_MASK, value);
/* Set proportional loop bandwith Gen1/2/3 */
/* Set proportional loop bandwidth Gen1/2/3 */
tmp = RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G1_CDR_PROP_BW_SHIFT |
RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G2_CDR_PROP_BW_SHIFT |
RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G3_CDR_PROB_BW_SHIFT;

View File

@ -71,6 +71,8 @@ struct brcm_usb_phy_data {
int wake_irq;
struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX];
struct notifier_block pm_notifier;
bool pm_notifier_registered;
bool sysfs_group_created;
bool pm_active;
};
@ -545,7 +547,11 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
return err;
priv->pm_notifier.notifier_call = brcm_pm_notifier;
register_pm_notifier(&priv->pm_notifier);
err = register_pm_notifier(&priv->pm_notifier);
if (err)
dev_warn(dev, "Error registering PM notifier\n");
else
priv->pm_notifier_registered = true;
mutex_init(&priv->mutex);
@ -561,6 +567,8 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
err = sysfs_create_group(&dev->kobj, &brcm_usb_phy_group);
if (err)
dev_warn(dev, "Error creating sysfs attributes\n");
else
priv->sysfs_group_created = true;
/* Get piarbctl syscon if it exists */
rmap = syscon_regmap_lookup_by_phandle(dev->of_node,
@ -581,16 +589,29 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
clk_disable_unprepare(priv->usb_30_clk);
phy_provider = devm_of_phy_provider_register(dev, brcm_usb_phy_xlate);
err = PTR_ERR_OR_ZERO(phy_provider);
if (err)
goto err_remove_debug;
return PTR_ERR_OR_ZERO(phy_provider);
return 0;
err_remove_debug:
if (priv->sysfs_group_created)
sysfs_remove_group(&dev->kobj, &brcm_usb_phy_group);
if (priv->pm_notifier_registered)
unregister_pm_notifier(&priv->pm_notifier);
return err;
}
static void brcm_usb_phy_remove(struct platform_device *pdev)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(&pdev->dev);
sysfs_remove_group(&pdev->dev.kobj, &brcm_usb_phy_group);
unregister_pm_notifier(&priv->pm_notifier);
if (priv->sysfs_group_created)
sysfs_remove_group(&pdev->dev.kobj, &brcm_usb_phy_group);
if (priv->pm_notifier_registered)
unregister_pm_notifier(&priv->pm_notifier);
}
#ifdef CONFIG_PM_SLEEP

View File

@ -698,23 +698,16 @@ static const struct phy_ops noop_ops = {
static u8 cdns_sierra_pll_mux_get_parent(struct clk_hw *hw)
{
struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw);
struct regmap_field *plllc1en_field = mux->plllc1en_field;
struct regmap_field *termen_field = mux->termen_field;
struct regmap_field *field = mux->pfdclk_sel_preg;
unsigned int val;
int index;
regmap_field_read(field, &val);
if (strstr(clk_hw_get_name(hw), clk_names[CDNS_SIERRA_PLL_CMNLC1])) {
if (strstr(clk_hw_get_name(hw), clk_names[CDNS_SIERRA_PLL_CMNLC1]))
index = clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table[CMN_PLLLC1], 0, val);
if (index == 1) {
regmap_field_write(plllc1en_field, 1);
regmap_field_write(termen_field, 1);
}
} else {
else
index = clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table[CMN_PLLLC], 0, val);
}
return index;
}

View File

@ -44,6 +44,8 @@
#define MHZ (1000UL * 1000UL)
#endif
#define PHY_DEFAULT_RATE 74250000
#define PHY_PLL_DIV_REGS_NUM 7
struct phy_config {
@ -489,17 +491,6 @@ static int fsl_samsung_hdmi_phy_configure(struct fsl_samsung_hdmi_phy *phy,
return ret;
}
static unsigned long phy_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw);
if (!phy->cur_cfg)
return 74250000;
return phy->cur_cfg->pixclk;
}
/* Helper function to lookup the available fractional-divider rate */
static const struct phy_config *fsl_samsung_hdmi_phy_lookup_rate(unsigned long rate)
{
@ -520,6 +511,14 @@ static const struct phy_config *fsl_samsung_hdmi_phy_lookup_rate(unsigned long r
&phy_pll_cfg[i] : &phy_pll_cfg[i+1]);
}
static unsigned long phy_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw);
return phy->cur_cfg->pixclk;
}
static void fsl_samsung_hdmi_calculate_phy(struct phy_config *cal_phy, unsigned long rate,
u8 p, u16 m, u8 s)
{
@ -610,7 +609,7 @@ static int phy_clk_register(struct fsl_samsung_hdmi_phy *phy)
{
struct device *dev = phy->dev;
struct device_node *np = dev->of_node;
struct clk_init_data init;
struct clk_init_data init = {};
const char *parent_name;
struct clk *phyclk;
int ret;
@ -640,6 +639,7 @@ static int phy_clk_register(struct fsl_samsung_hdmi_phy *phy)
static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
{
const struct phy_config *default_cfg;
struct fsl_samsung_hdmi_phy *phy;
int ret;
@ -664,6 +664,23 @@ static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
return dev_err_probe(phy->dev, PTR_ERR(phy->refclk),
"failed to get ref clk\n");
/*
* Establish the rate reported by recalc_rate() before registering the
* clock. Otherwise CCF may skip the first set_rate() when it requests
* the assumed default rate, leaving the PHY unconfigured.
*/
ret = clk_prepare_enable(phy->refclk);
if (ret)
return dev_err_probe(phy->dev, ret,
"failed to enable ref clk\n");
default_cfg = fsl_samsung_hdmi_phy_lookup_rate(PHY_DEFAULT_RATE);
ret = fsl_samsung_hdmi_phy_configure(phy, default_cfg);
clk_disable_unprepare(phy->refclk);
if (ret)
return dev_err_probe(phy->dev, ret,
"failed to configure default rate\n");
pm_runtime_get_noresume(phy->dev);
pm_runtime_set_active(phy->dev);
pm_runtime_enable(phy->dev);
@ -702,7 +719,7 @@ static int __maybe_unused fsl_samsung_hdmi_phy_suspend(struct device *dev)
static int __maybe_unused fsl_samsung_hdmi_phy_resume(struct device *dev)
{
struct fsl_samsung_hdmi_phy *phy = dev_get_drvdata(dev);
int ret = 0;
int ret;
ret = clk_prepare_enable(phy->apbclk);
if (ret) {
@ -710,11 +727,23 @@ static int __maybe_unused fsl_samsung_hdmi_phy_resume(struct device *dev)
return ret;
}
if (phy->cur_cfg)
ret = fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
if (!phy->cur_cfg)
return 0;
ret = clk_prepare_enable(phy->refclk);
if (ret) {
dev_err(phy->dev, "failed to enable ref clk\n");
goto disable_apbclk;
}
ret = fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
clk_disable_unprepare(phy->refclk);
if (!ret)
return 0;
disable_apbclk:
clk_disable_unprepare(phy->apbclk);
return ret;
}
static DEFINE_RUNTIME_DEV_PM_OPS(fsl_samsung_hdmi_phy_pm_ops,

View File

@ -480,7 +480,7 @@ static int hi3670_pcie_allclk_ctrl(struct hi3670_pcie_phy *phy, bool clk_on)
ret = hi3670_pcie_pll_ctrl(phy, true);
if (ret) {
dev_err(dev, "Failed to enable pll\n");
return -EINVAL;
goto close_clocks;
}
hi3670_pcie_hp_debounce_gt(phy, true);
hi3670_pcie_phyref_gt(phy, true);
@ -569,6 +569,7 @@ static int hi3670_pcie_get_resources_from_pcie(struct hi3670_pcie_phy *phy)
}
pcie_dev = bus_find_device_by_of_node(&platform_bus_type, pcie_port);
of_node_put(pcie_port);
if (!pcie_dev) {
dev_err(dev, "Didn't find pcie device\n");
return -ENODEV;
@ -583,6 +584,7 @@ static int hi3670_pcie_get_resources_from_pcie(struct hi3670_pcie_phy *phy)
* right regmap. So, let's use the named version.
*/
phy->apb = dev_get_regmap(pcie_dev, "kirin_pcie_apb");
put_device(pcie_dev);
if (!phy->apb) {
dev_err(dev, "Failed to get APB regmap\n");
return -ENODEV;

View File

@ -13,6 +13,18 @@ config PHY_MTK_PCIE
callback for PCIe GEN3 port, it supports software efuse
initialization.
config PHY_MTK_PCIE_SPHY
tristate "MediaTek PCIe S-PHY Driver"
depends on ARCH_MEDIATEK || COMPILE_TEST
depends on OF
select GENERIC_PHY
help
Enable this to add support for MediaTek PCIe S-PHYs used with the
PCI-Express Gen4 controllers as found in MT6991, MT8196 and others.
To compile this driver as module, say 'M' here: the module will be
called phy-mtk-pcie-sphy.
config PHY_MTK_XFI_TPHY
tristate "MediaTek 10GE SerDes XFI T-PHY driver"
depends on ARCH_MEDIATEK || COMPILE_TEST

View File

@ -5,6 +5,7 @@
obj-$(CONFIG_PHY_MTK_DP) += phy-mtk-dp.o
obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-pcie.o
obj-$(CONFIG_PHY_MTK_PCIE_SPHY) += phy-mtk-pcie-sphy.o
obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o
obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o

View File

@ -4,104 +4,350 @@
*
* Copyright (c) 2022, BayLibre Inc.
* Copyright (c) 2022, MediaTek Inc.
*
* Major refactoring and new SoCs support
* Copyright (c) 2026, Collabora Ltd.
* AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#define PHY_OFFSET 0x1000
#define MTK_DP_PHY_MAX_LANES 4
#define MTK_DP_PHY_DIG_PLL_CTL_1 (PHY_OFFSET + 0x14)
/* DP_PHYA_GLB_BIAS_GEN_0 (PHYA - Analog) */
#define XTP_GLB_BIAS_INT_R_CTRL GENMASK(20, 16)
/* DP_PHYA_GLB_FORCE_CTRL_1 */
#define CKM_CKTX0_EN_FORCE_MODE BIT(10)
/* DP_PHYA_GLB_DPAUX_TX */
#define CKM_PT0_CKTX_IMPSEL GENMASK(23, 20)
/* DP_PHYA_LAN_LANE_TX_0 */
#define XTP_LN_TX_IMPSEL_PMOS GENMASK(15, 12)
#define XTP_LN_TX_IMPSEL_NMOS GENMASK(19, 16)
/* DP_PHYA_GLB_FORCE_CTRL_1 */
#define CKM_CKTX0_EN_FORCE_MODE BIT(10)
/* DP_PHYD_PLL_CTL_1 */
#define TPLL_SSC_EN BIT(3)
#define MTK_DP_PHY_DIG_BIT_RATE (PHY_OFFSET + 0x3C)
#define BIT_RATE_RBR 0
#define BIT_RATE_HBR 1
#define BIT_RATE_HBR2 2
#define BIT_RATE_HBR3 3
/* DP_PHYD_BIT_RATE */
#define PHYD_DIG_RG_BIT_RATE_V2 GENMASK(3, 0)
#define PHYD_DIG_RG_BIT_RATE GENMASK(1, 0)
#define MTK_DP_PHY_DIG_SW_RST (PHY_OFFSET + 0x38)
#define DP_GLB_SW_RST_PHYD BIT(0)
/* DP_PHYD_SW_RST */
#define PHYD_DIG_GLB_SW_RST_B GENMASK(7, 0)
# define DP_GLB_SW_RST_PHYD BIT(0)
# define DP_GLB_SW_RST_TFIFO_ANA BIT(1)
# define DP_GLB_SW_RST_XTAL_CLK BIT(2)
# define DP_GLB_SW_RST_MAIN_LINK BIT(3)
#define MTK_DP_LANE0_DRIVING_PARAM_3 (PHY_OFFSET + 0x138)
#define MTK_DP_LANE1_DRIVING_PARAM_3 (PHY_OFFSET + 0x238)
#define MTK_DP_LANE2_DRIVING_PARAM_3 (PHY_OFFSET + 0x338)
#define MTK_DP_LANE3_DRIVING_PARAM_3 (PHY_OFFSET + 0x438)
#define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT BIT(4)
#define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT (BIT(10) | BIT(12))
#define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT GENMASK(20, 19)
#define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT GENMASK(29, 29)
#define DRIVING_PARAM_3_DEFAULT (XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \
XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT | \
XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT | \
XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT)
/* DP_PHYD_AUX_RX_CTL */
#define PHYD_DIG_DPAUX_RX_EN BIT(0)
#define PHYD_DIG_XTP_GLB_CKDET_EN BIT(1)
#define PHYD_DIG_DPAUX_RX_DEGLITCH_EN BIT(2)
#define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT GENMASK(4, 3)
#define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT GENMASK(12, 9)
#define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT (BIT(18) | BIT(21))
#define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT GENMASK(29, 29)
#define DRIVING_PARAM_4_DEFAULT (XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \
XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT | \
XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT | \
XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT)
#define DRIVING_PARAM_0_DEFAULT 0x0
#define DRIVING_PARAM_1_DEFAULT 0x0
#define DRIVING_PARAM_2_DEFAULT 0x0
#define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT (BIT(3) | BIT(5))
#define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT GENMASK(13, 12)
#define DRIVING_PARAM_5_DEFAULT (XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \
XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT)
/* DP_PHYD_TX_CTL_0 */
#define PHYD_TX_LN_EN GENMASK(7, 4)
#define PHYD_TX_LN_EN_V2 GENMASK(3, 0)
#define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT 0
#define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT GENMASK(10, 10)
#define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT GENMASK(19, 19)
#define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT GENMASK(28, 28)
#define DRIVING_PARAM_6_DEFAULT (XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT)
/* DP_PHYD_DRIVING_FORCE */
#define PHYD_DP_TX_FORCE_VOLT_SWING_EN BIT(0)
#define PHYD_DP_TX_FORCE_VOLT_SWING_VAL GENMASK(2, 1)
#define PHYD_DP_TX_FORCE_PRE_EMPH_VAL GENMASK(4, 3)
#define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT 0
#define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT GENMASK(10, 9)
#define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT GENMASK(19, 18)
#define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT 0
#define DRIVING_PARAM_7_DEFAULT (XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT)
/*
* DRIVING_PARAM_X (PHYD - Digital)
*
* Driving param registers are split in three sets, all containing settings
* for Voltage Swing and Pre-Emphasis for each lane's differential pair.
*
* All three sets share the same layout, but for different physical signals;
* In particular:
* [0-2]: LC TX CM (Minus / Negative Edge)
* [3-5]: LC TX C (Logic State Change Point)
* [6-8]: LC TX CP (Plus / Positive Edge)
*
* And they contain values for:
* [0,3,6]: Swing 0 Pre[0-3]
* [1,4,7]: Swing 1 Pre[0-2] and Swing 2 Pre0
* [2,5,8]: Swing 2 Pre1 and Swing 3 Pre0
*/
#define PHYD_DIG_NUM_DRV_PARA_REGS 9
#define XTP_LN_TX_LCTXC_SW0_PRE0 GENMASK(5, 0)
#define XTP_LN_TX_LCTXC_SW0_PRE1 GENMASK(13, 8)
#define XTP_LN_TX_LCTXC_SW0_PRE2 GENMASK(21, 16)
#define XTP_LN_TX_LCTXC_SW0_PRE3 GENMASK(29, 24)
#define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT GENMASK(3, 3)
#define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT 0
#define DRIVING_PARAM_8_DEFAULT (XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
#define XTP_LN_TX_LCTXC_SW1_PRE0 GENMASK(5, 0)
#define XTP_LN_TX_LCTXC_SW1_PRE1 GENMASK(13, 8)
#define XTP_LN_TX_LCTXC_SW1_PRE2 GENMASK(21, 16)
#define XTP_LN_TX_LCTXC_SW2_PRE0 GENMASK(29, 24)
#define XTP_LN_TX_LCTXC_SW2_PRE1 GENMASK(5, 0)
#define XTP_LN_TX_LCTXC_SW3_PRE0 GENMASK(13, 8)
#define BUILD_DRIVING_PARAM_0(sw0_pre0, sw0_pre1, sw0_pre2, sw0_pre3) ( \
FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE0, sw0_pre0) | \
FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE1, sw0_pre1) | \
FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE2, sw0_pre2) | \
FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE3, sw0_pre3) \
)
#define BUILD_DRIVING_PARAM_12(sw1_pre0, sw1_pre1, sw1_pre2, sw2_pre0) (\
FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW1_PRE0, sw1_pre0) | \
FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW1_PRE1, sw1_pre1) | \
FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW1_PRE2, sw1_pre2) | \
FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW2_PRE0, sw2_pre0) \
)
#define BUILD_DRIVING_PARAM_23(sw2_pre1, sw3_pre0) ( \
FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW2_PRE1, sw2_pre1) | \
FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW3_PRE0, sw3_pre0) \
)
/* MT8195: Logic State Change Point (LC TX C) */
#define MT8195_DRIVING_PARAM_3_DEFAULT BUILD_DRIVING_PARAM_0( 16, 20, 24, 32)
#define MT8195_DRIVING_PARAM_4_DEFAULT BUILD_DRIVING_PARAM_12(24, 30, 36, 32)
#define MT8195_DRIVING_PARAM_5_DEFAULT BUILD_DRIVING_PARAM_23(40, 48)
/* MT8195: Positive Edge (LC TX CP) */
#define MT8195_DRIVING_PARAM_6_DEFAULT BUILD_DRIVING_PARAM_0( 0, 4, 8, 16)
#define MT8195_DRIVING_PARAM_7_DEFAULT BUILD_DRIVING_PARAM_12(0, 6, 12, 0)
#define MT8195_DRIVING_PARAM_8_DEFAULT BUILD_DRIVING_PARAM_23(8, 0)
/* MT8196/MT6991: Logic State Change Point (LC TX C) */
#define MT8196_DRIVING_PARAM_3_DEFAULT BUILD_DRIVING_PARAM_0( 10, 12, 14, 17)
#define MT8196_DRIVING_PARAM_4_DEFAULT BUILD_DRIVING_PARAM_12(14, 17, 18, 18)
#define MT8196_DRIVING_PARAM_5_DEFAULT BUILD_DRIVING_PARAM_23(21, 24)
/* MT8196/MT6991: Positive Edge (LC TX CP) */
#define MT8196_DRIVING_PARAM_6_DEFAULT BUILD_DRIVING_PARAM_0( 0, 2, 4, 7)
#define MT8196_DRIVING_PARAM_7_DEFAULT BUILD_DRIVING_PARAM_12(0, 3, 6, 0)
#define MT8196_DRIVING_PARAM_8_DEFAULT BUILD_DRIVING_PARAM_23(3, 0)
enum mtk_dp_phya_ana_glb_regidx {
DP_PHYA_GLB_BIAS_GEN_0,
DP_PHYA_GLB_BIAS_GEN_1,
DP_PHYA_GLB_DPAUX_TX,
DP_PHYA_GLB_FORCE_CTRL_0,
DP_PHYA_GLB_FORCE_CTRL_1,
DP_PHYA_GLOBAL_MAX
};
enum mtk_dp_phya_ana_lane_regidx {
DP_PHYA_LAN_LANE_TX_0,
DP_PHYA_LAN_MAX
};
enum mtk_dp_phyd_dig_lane_regidx {
DP_PHYD_LAN_DRIVING_FORCE,
DP_PHYD_LAN_DRIVING_PARAM_0,
DP_PHYD_LAN_MAX
};
enum mtk_dp_phyd_dig_glb_regidx {
DP_PHYD_PLL_CTL_0,
DP_PHYD_PLL_CTL_1,
DP_PHYD_SW_RST,
DP_PHYD_BIT_RATE,
DP_PHYD_AUX_RX_CTL,
DP_PHYD_TX_CTL_0,
DP_PHYD_GLOBAL_MAX
};
enum mtk_dp_phyd_bit_rate_regval {
DP_PHYD_BIT_RATE_RBR,
DP_PHYD_BIT_RATE_HBR,
DP_PHYD_BIT_RATE_HBR2,
DP_PHYD_BIT_RATE_HBR3,
DP_PHYD_BIT_RATE_MAX,
};
static const u8 mt8195_phy_ana_glb_regs[DP_PHYA_GLOBAL_MAX] = {
[DP_PHYA_GLB_BIAS_GEN_0] = 0x0,
[DP_PHYA_GLB_BIAS_GEN_1] = 0x4,
[DP_PHYA_GLB_DPAUX_TX] = 0x8,
[DP_PHYA_GLB_FORCE_CTRL_0] = 0x30,
[DP_PHYA_GLB_FORCE_CTRL_1] = 0x34,
};
static const u8 mt8195_phy_ana_lane_regs[DP_PHYA_LAN_MAX] = {
[DP_PHYA_LAN_LANE_TX_0] = 0x4,
};
static const u8 mt8195_phy_dig_lane_regs[DP_PHYD_LAN_MAX] = {
[DP_PHYD_LAN_DRIVING_FORCE] = 0x18,
[DP_PHYD_LAN_DRIVING_PARAM_0] = 0x2c,
};
static const u8 mt8196_phy_dig_lane_regs[DP_PHYD_LAN_MAX] = {
[DP_PHYD_LAN_DRIVING_FORCE] = 0x30,
[DP_PHYD_LAN_DRIVING_PARAM_0] = 0x34,
};
static const u8 mt8195_phy_dig_glb_regs[DP_PHYD_GLOBAL_MAX] = {
[DP_PHYD_PLL_CTL_0] = 0x10,
[DP_PHYD_PLL_CTL_1] = 0x14,
[DP_PHYD_SW_RST] = 0x38,
[DP_PHYD_BIT_RATE] = 0x3c,
[DP_PHYD_AUX_RX_CTL] = 0x40,
[DP_PHYD_TX_CTL_0] = 0x44,
};
static const u8 mt8196_phy_dig_glb_regs[DP_PHYD_GLOBAL_MAX] = {
[DP_PHYD_PLL_CTL_0] = 0x10,
[DP_PHYD_PLL_CTL_1] = 0x14,
[DP_PHYD_SW_RST] = 0x38,
[DP_PHYD_BIT_RATE] = 0x3c,
[DP_PHYD_AUX_RX_CTL] = 0x40,
[DP_PHYD_TX_CTL_0] = 0x74,
};
static const u8 mt8195_phy_dig_bitrate_val[DP_PHYD_BIT_RATE_MAX] = {
[DP_PHYD_BIT_RATE_RBR] = 0,
[DP_PHYD_BIT_RATE_HBR] = 1,
[DP_PHYD_BIT_RATE_HBR2] = 2,
[DP_PHYD_BIT_RATE_HBR3] = 3
};
static const u8 mt8196_edp_phy_dig_bitrate_val[DP_PHYD_BIT_RATE_MAX] = {
[DP_PHYD_BIT_RATE_RBR] = 1,
[DP_PHYD_BIT_RATE_HBR] = 4,
[DP_PHYD_BIT_RATE_HBR2] = 7,
[DP_PHYD_BIT_RATE_HBR3] = 9
};
/**
* struct mtk_dp_phya_imp_sel - Per-Lane Impedance Selection
* @pmos: Impedance selection for P-Channel MOSFET
* @nmos: Impedance selection for N-Channel MOSFET
*/
struct mtk_dp_phya_imp_sel {
u8 pmos : 4;
u8 nmos : 4;
};
/**
* struct mtk_dp_phy_pdata - Platform data and defaults for MediaTek DP/eDP PHY
* @off_ana_glb: Base offset for dptx_phyd_sifslv_ana_glb
* @off_ana_lane: Base offsets for dptx_phyd_sifslv_ana_lan (for each lane)
* @off_dig_glb: Base offset for dptx_phyd_sifslv_dig_glb
* @off_dig_lane: Base offsets for dptx_phyd_sifslv_dig_lan (for each lane)
* @regs_ana_glb: Register (layout) offsets for ana_glb
* @regs_ana_lane: Register (layout) offsets for ana_lan
* @regs_dig_glb: Register (layout) offsets for dig_glb
* @regs_dig_lane: Register (layout) offsets for dig_lan
* @mask_dig_tx_ln: Register mask for PHYD_TX_LN_EN field
* @val_dig_bitrate:IP Version specific register values for Bit Rate setting
* @ana_bias_r: Internal resistance "R" Selection Settings (global)
* @ana_cktx_imp: TX Clock Impedance Selection Settings (global)
* @ana_lanes_imp: TX Impedance Selection Settings (for all lanes)
* @driving_params: Voltage Swing and Pre-Emphasis settings (for all lanes)
*/
struct mtk_dp_phy_pdata {
/* Register offsets */
u16 off_ana_glb;
u16 off_ana_lane[MTK_DP_PHY_MAX_LANES];
u16 off_dig_glb;
u16 off_dig_lane[MTK_DP_PHY_MAX_LANES];
/* Register maps */
const u8 *regs_ana_glb;
const u8 *regs_ana_lane;
const u8 *regs_dig_glb;
const u8 *regs_dig_lane;
/* Register masks */
u32 mask_dig_tx_ln;
/* IP-Version specific register value arrays */
const u8 *val_dig_bitrate;
/* Calibration defaults */
u8 ana_bias_r;
u8 ana_cktx_imp;
struct mtk_dp_phya_imp_sel ana_lanes_imp;
u32 driving_params[PHYD_DIG_NUM_DRV_PARA_REGS];
};
struct mtk_dp_phy {
struct regmap *regs;
struct device *dev;
struct regmap *regmap;
const struct mtk_dp_phy_pdata *pdata;
u8 ana_bias_r;
u8 ana_cktx_imp;
struct mtk_dp_phya_imp_sel ana_impsel[MTK_DP_PHY_MAX_LANES];
};
static void mtk_dp_phy_set_analog_calibration_params(struct mtk_dp_phy *dp_phy)
{
const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
const u8 *regs_ana_glb = pdata->regs_ana_glb;
const u8 *regs_ana_lane = pdata->regs_ana_lane;
int i;
regmap_update_bits(dp_phy->regmap,
pdata->off_ana_glb + regs_ana_glb[DP_PHYA_GLB_BIAS_GEN_0],
XTP_GLB_BIAS_INT_R_CTRL,
FIELD_PREP(XTP_GLB_BIAS_INT_R_CTRL, dp_phy->ana_bias_r));
regmap_update_bits(dp_phy->regmap,
pdata->off_ana_glb + regs_ana_glb[DP_PHYA_GLB_DPAUX_TX],
CKM_PT0_CKTX_IMPSEL,
FIELD_PREP(CKM_PT0_CKTX_IMPSEL, dp_phy->ana_cktx_imp));
for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
struct mtk_dp_phya_imp_sel *ana_imp = &dp_phy->ana_impsel[i];
u32 val = FIELD_PREP(XTP_LN_TX_IMPSEL_PMOS, ana_imp->pmos) |
FIELD_PREP(XTP_LN_TX_IMPSEL_NMOS, ana_imp->nmos);
u32 off_ana_lane = pdata->off_ana_lane[i];
regmap_update_bits(dp_phy->regmap,
off_ana_lane + regs_ana_lane[DP_PHYA_LAN_LANE_TX_0],
XTP_LN_TX_IMPSEL_PMOS | XTP_LN_TX_IMPSEL_NMOS, val);
}
}
static void mtk_dp_phy_set_digital_drv_params(struct mtk_dp_phy *dp_phy)
{
const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
const u32 reg = pdata->regs_dig_lane[DP_PHYD_LAN_DRIVING_PARAM_0];
int i;
/*
* Assume that all lanes need the same driving parameters: this
* will bulk write from DRIVING_PARAM_0 to DRIVING_PARAM_8 on
* all lanes (a grand total of [9 * num_lanes] 32-bit writes)
*/
for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++)
regmap_bulk_write(dp_phy->regmap, pdata->off_dig_lane[i] + reg,
pdata->driving_params,
ARRAY_SIZE(pdata->driving_params));
}
static int mtk_dp_phy_init(struct phy *phy)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
static const u32 driving_params[] = {
DRIVING_PARAM_3_DEFAULT,
DRIVING_PARAM_4_DEFAULT,
DRIVING_PARAM_5_DEFAULT,
DRIVING_PARAM_6_DEFAULT,
DRIVING_PARAM_7_DEFAULT,
DRIVING_PARAM_8_DEFAULT
};
regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
driving_params, ARRAY_SIZE(driving_params));
regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
driving_params, ARRAY_SIZE(driving_params));
regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
driving_params, ARRAY_SIZE(driving_params));
regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
driving_params, ARRAY_SIZE(driving_params));
mtk_dp_phy_set_digital_drv_params(dp_phy);
mtk_dp_phy_set_analog_calibration_params(dp_phy);
return 0;
}
@ -109,9 +355,14 @@ static int mtk_dp_phy_init(struct phy *phy)
static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
u32 val;
int i;
if (opts->dp.set_rate) {
const u32 reg_bit_rate = pdata->regs_dig_glb[DP_PHYD_BIT_RATE];
enum mtk_dp_phyd_bit_rate_regval regval_idx;
switch (opts->dp.link_rate) {
default:
dev_err(&phy->dev,
@ -119,84 +370,469 @@ static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
opts->dp.link_rate);
return -EINVAL;
case 1620:
val = BIT_RATE_RBR;
regval_idx = DP_PHYD_BIT_RATE_RBR;
break;
case 2700:
val = BIT_RATE_HBR;
regval_idx = DP_PHYD_BIT_RATE_HBR;
break;
case 5400:
val = BIT_RATE_HBR2;
regval_idx = DP_PHYD_BIT_RATE_HBR2;
break;
case 8100:
val = BIT_RATE_HBR3;
regval_idx = DP_PHYD_BIT_RATE_HBR3;
break;
}
regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE, val);
regmap_write(dp_phy->regmap, pdata->off_dig_glb + reg_bit_rate,
pdata->val_dig_bitrate[regval_idx]);
}
regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
if (opts->dp.set_lanes) {
const u32 reg_dig_tx_ctl = pdata->regs_dig_glb[DP_PHYD_TX_CTL_0];
val = 0;
for (i = 0; i < opts->dp.lanes; i++)
val |= field_prep(pdata->mask_dig_tx_ln, BIT(i));
regmap_update_bits(dp_phy->regmap, pdata->off_dig_glb + reg_dig_tx_ctl,
pdata->mask_dig_tx_ln, val);
}
if (opts->dp.set_voltages) {
const u32 reg_drv_force = pdata->regs_dig_lane[DP_PHYD_LAN_DRIVING_FORCE];
if (opts->dp.lanes > 4) {
dev_err(&phy->dev, "Wrong lanes config %u\n", opts->dp.lanes);
return -EINVAL;
}
for (i = 0; i < opts->dp.lanes; i++) {
const u32 off_dig_lane = pdata->off_dig_lane[i];
u32 val;
val = FIELD_PREP(PHYD_DP_TX_FORCE_VOLT_SWING_VAL, opts->dp.voltage[i]);
val |= FIELD_PREP(PHYD_DP_TX_FORCE_PRE_EMPH_VAL, opts->dp.pre[i]);
val |= PHYD_DP_TX_FORCE_VOLT_SWING_EN;
regmap_update_bits(dp_phy->regmap, off_dig_lane + reg_drv_force,
PHYD_DP_TX_FORCE_VOLT_SWING_EN |
PHYD_DP_TX_FORCE_VOLT_SWING_VAL |
PHYD_DP_TX_FORCE_PRE_EMPH_VAL,
val);
}
}
regmap_update_bits(dp_phy->regmap,
pdata->off_dig_glb + pdata->regs_dig_glb[DP_PHYD_PLL_CTL_1],
TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0);
return 0;
}
static int mtk_dp_phy_power_on(struct phy *phy)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
const u8 *regs_dig = pdata->regs_dig_glb;
const u8 *regs_ana = pdata->regs_ana_glb;
/* Enable AUX Channel with RX De-Glitch and input clock detection */
regmap_write(dp_phy->regmap,
pdata->off_dig_glb + regs_dig[DP_PHYD_AUX_RX_CTL],
PHYD_DIG_DPAUX_RX_EN |
PHYD_DIG_XTP_GLB_CKDET_EN |
PHYD_DIG_DPAUX_RX_DEGLITCH_EN);
regmap_clear_bits(dp_phy->regmap,
pdata->off_ana_glb + regs_ana[DP_PHYA_GLB_FORCE_CTRL_1],
CKM_CKTX0_EN_FORCE_MODE);
return 0;
}
static int mtk_dp_phy_disable_all_lanes(struct mtk_dp_phy *dp_phy)
{
const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
const u8 *regs = pdata->regs_dig_glb;
u32 val;
/* Get mask of currently enabled lane */
regmap_read(dp_phy->regmap, pdata->off_dig_glb + regs[DP_PHYD_TX_CTL_0], &val);
val = field_get(pdata->mask_dig_tx_ln, val);
if (val == 0)
return 0;
/* Disable all lanes (needs to be done one by one, from last to first) */
do {
u32 lane_num = fls(val) - 1;
val &= ~BIT(lane_num);
regmap_clear_bits(dp_phy->regmap,
pdata->off_dig_glb + regs[DP_PHYD_TX_CTL_0],
field_prep(pdata->mask_dig_tx_ln, BIT(lane_num)));
} while (val);
return 0;
}
static int mtk_dp_phy_power_off(struct phy *phy)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
const u8 *regs_dig = pdata->regs_dig_glb;
const u8 *regs_ana = pdata->regs_ana_glb;
int ret;
regmap_set_bits(dp_phy->regmap,
pdata->off_ana_glb + regs_ana[DP_PHYA_GLB_FORCE_CTRL_1],
CKM_CKTX0_EN_FORCE_MODE);
/* Disable RX unconditionally */
regmap_write(dp_phy->regmap,
pdata->off_dig_glb + regs_dig[DP_PHYD_AUX_RX_CTL], 0);
ret = mtk_dp_phy_disable_all_lanes(dp_phy);
if (ret) {
dev_err(dp_phy->dev, "Could not disable lanes for poweroff!\n");
return ret;
}
return 0;
}
static int mtk_dp_phy_reset(struct phy *phy)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
const u32 reg_rst = pdata->regs_dig_glb[DP_PHYD_SW_RST];
const u32 reg_drv_force = pdata->regs_dig_lane[DP_PHYD_LAN_DRIVING_FORCE];
int i, ret;
regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
DP_GLB_SW_RST_PHYD, 0);
/* Clearing bits sets reset state */
regmap_clear_bits(dp_phy->regmap, pdata->off_dig_glb + reg_rst, DP_GLB_SW_RST_PHYD);
/* PHYD needs 50uS to guarantee reset done */
usleep_range(50, 200);
regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
DP_GLB_SW_RST_PHYD, 1);
/* Setting bits means go out of reset */
regmap_set_bits(dp_phy->regmap, pdata->off_dig_glb + reg_rst, DP_GLB_SW_RST_PHYD);
/* Disable all lanes and continue reset even if this fails, but notify */
ret = mtk_dp_phy_disable_all_lanes(dp_phy);
if (ret)
dev_err(dp_phy->dev, "Could not disable lanes during reset!\n");
/* Reset Voltage Swing and Preemphasis values */
for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
const u32 off_dig_lane = pdata->off_dig_lane[i];
regmap_clear_bits(dp_phy->regmap, off_dig_lane + reg_drv_force,
PHYD_DP_TX_FORCE_VOLT_SWING_EN |
PHYD_DP_TX_FORCE_VOLT_SWING_VAL |
PHYD_DP_TX_FORCE_PRE_EMPH_VAL);
}
return 0;
}
static const struct phy_ops mtk_dp_phy_dev_ops = {
.init = mtk_dp_phy_init,
.power_on = mtk_dp_phy_power_on,
.power_off = mtk_dp_phy_power_off,
.configure = mtk_dp_phy_configure,
.reset = mtk_dp_phy_reset,
.owner = THIS_MODULE,
};
static int mtk_dp_phy_probe(struct platform_device *pdev)
static void mtk_dp_phy_get_default_cal_data(struct mtk_dp_phy *dp_phy)
{
const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
int i;
dp_phy->ana_bias_r = pdata->ana_bias_r;
dp_phy->ana_cktx_imp = pdata->ana_cktx_imp;
/* Copy the default lane impedance settings to all lanes */
for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++)
memcpy(&dp_phy->ana_impsel[i], &pdata->ana_lanes_imp,
sizeof(dp_phy->ana_impsel[0]));
return;
}
static int mtk_dp_phy_get_one_cal_para(struct device *dev, const char *name, u8 max_val)
{
u8 buf_byte;
u16 buf;
int ret;
/*
* All of the calibrations are always max 8 bits long, but some may
* be split between two different 8-bits cells: handle this corner
* case by retrying reading as u16.
*/
ret = nvmem_cell_read_u8(dev, name, &buf_byte);
if (ret)
ret = nvmem_cell_read_u16(dev, name, &buf);
else
buf = buf_byte;
if (ret)
return dev_err_probe(dev, ret, "Cannot get calibration data for %s\n", name);
if (buf == 0) {
dev_warn(dev, "No calibration for %s. Using defaults\n", name);
return -ENOENT;
}
if (buf > max_val)
return dev_err_probe(dev, -ERANGE, "Bad value %u retrieved for %s\n", buf, name);
return buf;
}
static int mtk_dp_phy_get_calibration_data(struct mtk_dp_phy *dp_phy)
{
char mtk_dp_cal_lane_imp_name[] = "impedance-laneXM";
struct device *dev = dp_phy->dev;
int i, ret;
ret = mtk_dp_phy_get_one_cal_para(dev, "rbias-trim", FIELD_MAX(XTP_GLB_BIAS_INT_R_CTRL));
if (ret < 0)
goto end;
dp_phy->ana_bias_r = ret;
ret = mtk_dp_phy_get_one_cal_para(dev, "impedance-txclk", FIELD_MAX(CKM_PT0_CKTX_IMPSEL));
if (ret < 0)
goto end;
dp_phy->ana_cktx_imp = ret;
/* Get impedance params for each lane */
for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
/* P-MOSFET first */
snprintf(mtk_dp_cal_lane_imp_name, ARRAY_SIZE(mtk_dp_cal_lane_imp_name),
"impedance-lane%dp", i);
ret = mtk_dp_phy_get_one_cal_para(dev, mtk_dp_cal_lane_imp_name,
FIELD_MAX(XTP_LN_TX_IMPSEL_PMOS));
if (ret < 0)
goto end;
dp_phy->ana_impsel[i].pmos = ret;
/* ...and then N-MOSFET too */
snprintf(mtk_dp_cal_lane_imp_name, ARRAY_SIZE(mtk_dp_cal_lane_imp_name),
"impedance-lane%dn", i);
ret = mtk_dp_phy_get_one_cal_para(dev, mtk_dp_cal_lane_imp_name,
FIELD_MAX(XTP_LN_TX_IMPSEL_NMOS));
if (ret < 0)
goto end;
dp_phy->ana_impsel[i].nmos = ret;
}
end:
if (ret < 0) {
/*
* If any of the calibration values is missing, or if there
* is no calibration at all in the eFuses, copy the default
* one entirely (as partial values shall not be mixed!)
*/
if (ret == -ENOENT) {
dev_info(dev, "Using calibration default values\n");
mtk_dp_phy_get_default_cal_data(dp_phy);
return 0;
}
return ret;
};
return 0;
}
static void mtk_dp_phy_legacy_remove_lookup(void *data)
{
struct phy *phy = data;
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
phy_remove_lookup(phy, "dp", dev_name(dp_phy->dev));
}
static const struct mtk_dp_phy_pdata mt8195_dp_phy_data;
static int mtk_dp_phy_legacy_probe(struct platform_device *pdev, struct mtk_dp_phy *dp_phy)
{
struct device *dev = &pdev->dev;
struct mtk_dp_phy *dp_phy;
struct phy *phy;
struct regmap *regs;
int ret;
regs = *(struct regmap **)dev->platform_data;
if (!regs)
return dev_err_probe(dev, -EINVAL,
"No data passed, requires struct regmap**\n");
/*
* If legacy platform driver probe, assume this is MT8195 or compatible
* with a devicetree that was not migrated to the new, proper bindings.
*/
dp_phy->pdata = &mt8195_dp_phy_data;
dp_phy->regmap = *(struct regmap **)dev->platform_data;
if (!dp_phy->regmap)
return dev_err_probe(dev, -EINVAL, "No platform data available\n");
dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
if (!dp_phy)
return -ENOMEM;
dp_phy->regs = regs;
phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
if (IS_ERR(phy))
return dev_err_probe(dev, PTR_ERR(phy),
"Failed to create DP PHY\n");
phy_set_drvdata(phy, dp_phy);
if (!dev->of_node)
phy_create_lookup(phy, "dp", dev_name(dev));
/*
* Set default calibration data before exposing the PHY.
* For legacy probe, mtk_dp will set calibrations from eFuse, if found.
*/
mtk_dp_phy_get_default_cal_data(dp_phy);
ret = phy_create_lookup(phy, "dp", dev_name(dev));
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, mtk_dp_phy_legacy_remove_lookup, phy);
if (ret)
return ret;
return 0;
}
static const struct regmap_config mtk_dp_phy_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.disable_locking = true,
};
static int mtk_dp_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *provider;
struct mtk_dp_phy *dp_phy;
void __iomem *base;
struct phy *phy;
int ret;
dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
if (!dp_phy)
return -ENOMEM;
dp_phy->dev = dev;
/* If there's no devicetree, go for legacy pdev probe */
if (!dev->of_node)
return mtk_dp_phy_legacy_probe(pdev, dp_phy);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
dp_phy->regmap = devm_regmap_init_mmio(dev, base, &mtk_dp_phy_regmap_cfg);
if (IS_ERR(dp_phy->regmap))
return PTR_ERR(dp_phy->regmap);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
dp_phy->pdata = device_get_match_data(dev);
if (IS_REACHABLE(CONFIG_NVMEM)) {
ret = mtk_dp_phy_get_calibration_data(dp_phy);
if (ret)
return ret;
} else {
/* Use default calibration data */
mtk_dp_phy_get_default_cal_data(dp_phy);
}
phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
if (IS_ERR(phy))
return dev_err_probe(dev, PTR_ERR(phy),
"Failed to create DP PHY\n");
phy_set_drvdata(phy, dp_phy);
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(provider))
return PTR_ERR(provider);
return 0;
}
static const struct mtk_dp_phy_pdata mt8195_dp_phy_data = {
.off_ana_glb = 0,
.off_ana_lane = { 0x100, 0x200, 0x300, 0x400 },
.off_dig_glb = 0x1000,
.off_dig_lane = { 0x1100, 0x1200, 0x1300, 0x1400 },
.regs_ana_glb = mt8195_phy_ana_glb_regs,
.regs_ana_lane = mt8195_phy_ana_lane_regs,
.regs_dig_glb = mt8195_phy_dig_glb_regs,
.regs_dig_lane = mt8195_phy_dig_lane_regs,
.mask_dig_tx_ln = PHYD_TX_LN_EN,
.val_dig_bitrate = mt8195_phy_dig_bitrate_val,
.ana_bias_r = 15,
.ana_cktx_imp = 8,
.ana_lanes_imp = {
.pmos = 8,
.nmos = 8,
},
.driving_params = {
[0] = 0,
[1] = 0,
[2] = 0,
[3] = MT8195_DRIVING_PARAM_3_DEFAULT,
[4] = MT8195_DRIVING_PARAM_4_DEFAULT,
[5] = MT8195_DRIVING_PARAM_5_DEFAULT,
[6] = MT8195_DRIVING_PARAM_6_DEFAULT,
[7] = MT8195_DRIVING_PARAM_7_DEFAULT,
[8] = MT8195_DRIVING_PARAM_8_DEFAULT
},
};
static const struct mtk_dp_phy_pdata mt8196_edp_phy_data = {
.off_ana_glb = 0x400,
.off_ana_lane = { 0x0, 0x100, 0x200, 0x300 },
.off_dig_glb = 0x1400,
.off_dig_lane = { 0x1000, 0x1100, 0x1200, 0x1300 },
.regs_ana_glb = mt8195_phy_ana_glb_regs,
.regs_ana_lane = mt8195_phy_ana_lane_regs,
.regs_dig_glb = mt8196_phy_dig_glb_regs,
.regs_dig_lane = mt8196_phy_dig_lane_regs,
.mask_dig_tx_ln = PHYD_TX_LN_EN_V2,
.val_dig_bitrate = mt8196_edp_phy_dig_bitrate_val,
.ana_bias_r = 15,
.ana_cktx_imp = 8,
.ana_lanes_imp = {
.pmos = 8,
.nmos = 8,
},
.driving_params = {
[0] = 0,
[1] = 0,
[2] = 0,
[3] = MT8196_DRIVING_PARAM_3_DEFAULT,
[4] = MT8196_DRIVING_PARAM_4_DEFAULT,
[5] = MT8196_DRIVING_PARAM_5_DEFAULT,
[6] = MT8196_DRIVING_PARAM_6_DEFAULT,
[7] = MT8196_DRIVING_PARAM_7_DEFAULT,
[8] = MT8196_DRIVING_PARAM_8_DEFAULT
},
};
static const struct of_device_id mtk_dp_phy_of_match[] = {
{ .compatible = "mediatek,mt8195-dp-phy", .data = &mt8195_dp_phy_data },
{ .compatible = "mediatek,mt8196-edp-phy", .data = &mt8196_edp_phy_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_dp_phy_of_match);
static struct platform_driver mtk_dp_phy_driver = {
.probe = mtk_dp_phy_probe,
.driver = {
.name = "mediatek-dp-phy",
.of_match_table = mtk_dp_phy_of_match,
},
};
module_platform_driver(mtk_dp_phy_driver);
MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
MODULE_DESCRIPTION("MediaTek DP PHY Driver");
MODULE_DESCRIPTION("MediaTek DisplayPort PHY Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,561 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022 MediaTek Inc.
* Copyright (c) 2026 Collabora Ltd.
* Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include "phy-mtk-io.h"
/* PHY System Interface (SIF) registers */
#define PEXTP_DIG_GLB_TOP 0x20
# define RG_XTP_BYPASS_PIPE_RST_RC BIT(17)
#define PEXTP_DIG_GLB_CKBG0 0x30
# define RG_XTP_CKBG_XTAL_STABLE_TIME_SEL GENMASK(25, 16)
#define PEXTP_DIG_GLB_TPLL_CTL0 0x38
# define RG_XTP_TPLL_SET_STABLE_TIME_SEL GENMASK(7, 2)
# define RG_XTP_TPLL_PWE_ON_STABLE_TIME_SEL GENMASK(9, 8)
#define PEXTP_DIG_GLB_CLKREQ_CTL 0x50
# define RG_XTP_CKM_EN_L1S0 BIT(13)
# define RG_XTP_CKM_EN_L1S1 BIT(14)
#define PEXTP_DIG_GLB_TPLL_CTL2 0xf4
# define RG_XTP_TPLL_ISO_EN_STABLE_TIME_SEL GENMASK(13, 12)
/* PHY System Interface Digital registers */
#define PEXTP_DIG_LN_TRX_PIPE_IF_17 0x30e8
# define RG_XTP_LN_RX_LF_CTLE_CSEL_GEN4 GENMASK(14, 12)
#define PEXTP_DIG_LN_RX_F0 0x50f0
# define RG_XTP_LN_RX_GEN1_CTLE1_CSEL GENMASK(3, 0)
# define RG_XTP_LN_RX_GEN2_CTLE1_CSEL GENMASK(7, 4)
# define RG_XTP_LN_RX_GEN3_CTLE1_CSEL GENMASK(11, 8)
# define RG_XTP_LN_RX_GEN4_CTLE1_CSEL GENMASK(15, 12)
#define PEXTP_DIG_LN_RX2_AEQ_EDGE_0 0x6004
# define RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN3 GENMASK(21, 16)
# define RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN4 GENMASK(29, 24)
/* PHY System Interface Analog registers */
#define PEXTP_ANA_GLB_TPLL1_RSVD 0x902c
# define RG_XTP_GLB_TPLL1_P_PATH_GAIN GENMASK(2, 0)
#define PEXTP_ANA_GLB_BIAS_0 0x9060
# define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(5, 0)
#define PEXTP_ANA_GLB_BIAS_1 0x90c0
# define RG_XTP_GLB_BIAS_V2V_VTRIM GENMASK(9, 6)
#define PEXTP_ANA_LN_TRX_0C 0xa00c
# define RG_XTP_LN_TX_RSWN_IMPSEL GENMASK(20, 16)
#define PEXTP_ANA_LN_TRX_34 0xa034
# define RG_XTP_LN_RX_FE BIT(15)
#define PEXTP_ANA_LN_TRX_6C 0xa06c
# define RG_XTP_LN_RX_AEQ_CTLE_ERR_TYPE GENMASK(14, 13)
# define AEQ_CTLE_SEARCH_ERR_TYPE_H1P5 0
# define AEQ_CTLE_SEARCH_ERR_TYPE_H1P5_H2P5 1
# define AEQ_CTLE_SEARCH_ERR_TYPE_P1P5_H2P5_H3P5 2
#define PEXTP_ANA_LN_TRX_A0 0xa0a0
# define RG_XTP_LN_TX_IMPSEL_PMOS GENMASK(4, 0)
# define RG_XTP_LN_TX_IMPSEL_NMOS GENMASK(11, 7)
# define RG_XTP_LN_RX_IMPSEL GENMASK(15, 12)
#define PEXTP_ANA_LN_TRX_A8 0xa0a8
# define RG_XTP_LN_RX_LEQ_RL_CTLE_CAL GENMASK(6, 2)
# define RG_XTP_LN_RX_LEQ_RL_VGA_CAL GENMASK(11, 7)
# define RG_XTP_LN_RX_LEQ_RL_DFE_CAL GENMASK(23, 19)
#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4 0xb004
#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8 0xb008
#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_C 0xb00c
#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10 0xb010
#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14 0xb014
#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18 0xb018
# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1 GENMASK(5, 0)
# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0 GENMASK(13, 8)
# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1 GENMASK(21, 16)
#define PEXTP_REG_LANE(x) ((x) * 0x100)
/* PHY Clock Management (CKM) registers */
#define XTP_CKM_FORCE_6 0x38
# define RG_CKM_BIAS_WAIT_PRD_US GENMASK(21, 16)
#define XTP_CKM_REG_SPLL_FBKDIV_5 0xd4
# define RG_CKM_CKTX_IMPSEL_PMOS GENMASK(19, 16)
# define RG_CKM_CKTX_IMPSEL_NMOS GENMASK(23, 20)
# define RG_CKM_CKTX_IMPSEL_SW GENMASK(27, 24)
/* Calibration data from eFuses */
#define MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES 2
#define MTK_PCIE_SPHY_CALIBRATION_LAST_QUIRK_VER 4
/**
* struct mtk_pcie_sphy_imp_sel - Impedance Selection parameters
* @pmos: Impedance selection for P-Channel MOSFET
* @nmos: Impedance selection for N-Channel MOSFET
*/
struct mtk_pcie_sphy_imp_sel {
u8 pmos;
u8 nmos;
};
/**
* struct mtk_pcie_sphy_efuse - eFuse calibration data for S-PHY
* @cktx_impsel: SPLL CKTX Impedance Selection (P and N MOSFET)
* @tx_impsel: TX Impedance Selection (P and N MOSFET)
* @int_r_ctrl: Internal resistor selection of TX Bias Current
* @xtp_vtrim: XTP Bias V2V voltage calibration
* @cktx_r_mid: SPLL CKTX Intermediate Transition Impedance (Rmid)
* @rx_leq_rl_ctle: RX Front-End Return Loss Continuous Time Linear Equalization value
* @rx_leq_rl_vga: RX Front-End Return Loss Variable Gain Amplifier value
* @rx_leq_rl_dfe: RX Front-End Return Loss Decision Feedback Equalization value
* @rx_impsel: RX Impedance Selection
* @tx_rswn_impsel: TX RSWn (Switch Resistance) impedance selection
* @supported: eFuse calibration data is supported
*/
struct mtk_pcie_sphy_efuse {
struct mtk_pcie_sphy_imp_sel cktx_impsel;
struct mtk_pcie_sphy_imp_sel tx_impsel;
u8 int_r_ctrl;
u8 xtp_vtrim;
u8 cktx_r_mid;
u8 rx_leq_rl_ctle;
u8 rx_leq_rl_vga;
u8 rx_leq_rl_dfe;
u8 rx_impsel;
u8 tx_rswn_impsel[MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES];
bool supported;
};
/**
* struct mtk_pcie_sphy - PCI-Express S-PHY driver main structure
* @dev: Pointer to device structure
* @phy: Pointer to generic phy structure
* @sif_base: IO mapped register base address of system interface
* @ckm_base: IO mapped register base address of clock management interface
* @num_lanes: Number of lanes
* @calibration: eFuse calibration data for S-PHY
*/
struct mtk_pcie_sphy {
struct device *dev;
struct phy *phy;
void __iomem *sif_base;
void __iomem *ckm_base;
u8 num_lanes;
struct mtk_pcie_sphy_efuse calibration;
};
static void mtk_pcie_sphy_apply_calibration(struct mtk_pcie_sphy *pcie_sphy)
{
struct mtk_pcie_sphy_efuse *cal = &pcie_sphy->calibration;
int i;
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_BIAS_0,
RG_XTP_GLB_BIAS_INTR_CTRL, cal->int_r_ctrl);
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_BIAS_1,
RG_XTP_GLB_BIAS_V2V_VTRIM, cal->xtp_vtrim);
mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5,
RG_CKM_CKTX_IMPSEL_PMOS, cal->cktx_impsel.pmos);
mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5,
RG_CKM_CKTX_IMPSEL_NMOS, cal->cktx_impsel.nmos);
mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5,
RG_CKM_CKTX_IMPSEL_SW, cal->cktx_r_mid);
for (i = 0; i < pcie_sphy->num_lanes; i++) {
void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_0C,
RG_XTP_LN_TX_RSWN_IMPSEL, cal->tx_rswn_impsel[i]);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8,
RG_XTP_LN_RX_LEQ_RL_CTLE_CAL, cal->rx_leq_rl_ctle);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8,
RG_XTP_LN_RX_LEQ_RL_VGA_CAL, cal->rx_leq_rl_vga);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8,
RG_XTP_LN_RX_LEQ_RL_DFE_CAL, cal->rx_leq_rl_dfe);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0,
RG_XTP_LN_RX_IMPSEL, cal->rx_impsel);
}
}
/**
* mtk_pcie_sphy_init() - Initialize the PCI-Express S-PHY
* @phy: the phy to be initialized
*
* The hardware settings will be reset during suspend, it should be
* reinitialized when the consumer calls phy_init() again on resume.
*/
static int mtk_pcie_sphy_init(struct phy *phy)
{
struct mtk_pcie_sphy *pcie_sphy = phy_get_drvdata(phy);
struct mtk_pcie_sphy_imp_sel tx_impsel;
int i;
/* Set CKM Bias wait time to 4 microseconds */
mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_FORCE_6,
RG_CKM_BIAS_WAIT_PRD_US, 4);
/* TPLL needs 63 ref_ck ticks to stabilize when setting frequency */
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL0,
RG_XTP_TPLL_SET_STABLE_TIME_SEL, 63);
/* TPLL needs 3 ref_ck ticks to stabilize when powering on... */
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL0,
RG_XTP_TPLL_PWE_ON_STABLE_TIME_SEL, 3);
/* ...and the same goes for setting isolation */
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL2,
RG_XTP_TPLL_ISO_EN_STABLE_TIME_SEL, 3);
/* XTAL doesn't need any stabilization time */
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_CKBG0,
RG_XTP_CKBG_XTAL_STABLE_TIME_SEL, 0);
/* Keep pextp_ckm enabled when in L1SS_L1S1 state */
mtk_phy_clear_bits(pcie_sphy->sif_base + PEXTP_DIG_GLB_CLKREQ_CTL, RG_XTP_CKM_EN_L1S1);
/* Set PIPE to reset TPLL */
mtk_phy_clear_bits(pcie_sphy->sif_base + PEXTP_DIG_GLB_TOP, RG_XTP_BYPASS_PIPE_RST_RC);
/* Set TPLL P-Path gain compensation to 1 */
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_TPLL1_RSVD,
RG_XTP_GLB_TPLL1_P_PATH_GAIN, 1);
for (i = 0; i < pcie_sphy->num_lanes; i++) {
void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i);
/* Set RX Lane AEQ CTRL-E Search Error type to h1.5 + h2.5 */
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_6C,
RG_XTP_LN_RX_AEQ_CTLE_ERR_TYPE,
AEQ_CTLE_SEARCH_ERR_TYPE_H1P5_H2P5);
mtk_phy_set_bits(sif_lane_base + PEXTP_ANA_LN_TRX_34, RG_XTP_LN_RX_FE);
/* TRX: Select CTLE1 for RX Lane AutoEQ CTRL-E Setting on Gen4 */
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TRX_PIPE_IF_17,
RG_XTP_LN_RX_LF_CTLE_CSEL_GEN4, 1);
/* Set RX Lane AutoEQ CTRL-E for PCI-Express Gen1 to Gen 4 */
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0,
RG_XTP_LN_RX_GEN1_CTLE1_CSEL, 13);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0,
RG_XTP_LN_RX_GEN2_CTLE1_CSEL, 13);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0,
RG_XTP_LN_RX_GEN3_CTLE1_CSEL, 13);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0,
RG_XTP_LN_RX_GEN4_CTLE1_CSEL, 0);
/* Set RX Lane AutoEQ's Edge EQ Ratio to 22 * 0.0625 = 1.375 */
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX2_AEQ_EDGE_0,
RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN3, 22);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX2_AEQ_EDGE_0,
RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN4, 22);
/* Setup Digital lane TX Link Characteristics Table */
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 10);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1, 2);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 11);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1, 1);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_C,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 12);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 13);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 1);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 11);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 1);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 10);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 2);
}
if (pcie_sphy->calibration.supported) {
mtk_pcie_sphy_apply_calibration(pcie_sphy);
tx_impsel.pmos = pcie_sphy->calibration.tx_impsel.pmos;
tx_impsel.nmos = pcie_sphy->calibration.tx_impsel.nmos;
} else {
/* Set P=10, N=9 to prevent EMI if no calibration present */
tx_impsel.pmos = 10;
tx_impsel.nmos = 9;
}
/* Select TX Impedance on N and P MOSFETs */
for (i = 0; i < pcie_sphy->num_lanes; i++) {
void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0,
RG_XTP_LN_TX_IMPSEL_PMOS, tx_impsel.pmos);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0,
RG_XTP_LN_TX_IMPSEL_NMOS, tx_impsel.nmos);
}
return 0;
}
static const struct phy_ops mtk_pcie_sphy_ops = {
.init = mtk_pcie_sphy_init,
.owner = THIS_MODULE,
};
static int mtk_pcie_sphy_get_one_cal_para(struct device *dev, const char *name, u8 max_val)
{
u16 buf;
u8 tmp;
int ret;
/*
* All of the calibrations are always max 8 bits long, but some may
* be split between two different 8-bits cells: handle this corner
* case by retrying reading as u16.
*/
ret = nvmem_cell_read_u8(dev, name, &tmp);
if (ret == 0)
buf = tmp;
else
ret = nvmem_cell_read_u16(dev, name, &buf);
if (ret == -ENOENT) {
dev_info(dev, "No calibration for %s. Using defaults\n", name);
return -ENOENT;
} else if (ret)
return dev_err_probe(dev, ret,
"Cannot get calibration data for %s\n", name);
if (buf > max_val)
return dev_err_probe(dev, -ERANGE,
"Bad value %u retrieved for %s.\n", buf, name);
return buf;
}
static int mtk_pcie_sphy_get_calibration_data(struct mtk_pcie_sphy *pcie_sphy)
{
struct mtk_pcie_sphy_efuse *cal = &pcie_sphy->calibration;
struct device *dev = pcie_sphy->dev;
u8 version;
int ret;
if (pcie_sphy->num_lanes > MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES) {
dev_info(dev, "Skipping PHY calibration for more than %u lanes.\n",
MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES);
ret = -EOPNOTSUPP;
goto end;
}
ret = mtk_pcie_sphy_get_one_cal_para(dev, "int-r",
FIELD_MAX(RG_XTP_GLB_BIAS_INTR_CTRL));
if (ret < 0)
goto end;
cal->int_r_ctrl = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "xtp-vtrim",
FIELD_MAX(RG_XTP_GLB_BIAS_V2V_VTRIM));
if (ret < 0)
goto end;
cal->xtp_vtrim = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-pmos",
FIELD_MAX(RG_CKM_CKTX_IMPSEL_PMOS));
if (ret < 0)
goto end;
cal->cktx_impsel.pmos = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-nmos",
FIELD_MAX(RG_CKM_CKTX_IMPSEL_NMOS));
if (ret < 0)
goto end;
cal->cktx_impsel.nmos = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-r-mid",
FIELD_MAX(RG_CKM_CKTX_IMPSEL_SW));
if (ret < 0)
goto end;
cal->cktx_r_mid = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-ctle",
FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_CTLE_CAL));
if (ret < 0)
goto end;
cal->rx_leq_rl_ctle = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-vga",
FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_VGA_CAL));
if (ret < 0)
goto end;
cal->rx_leq_rl_vga = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-dfe",
FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_DFE_CAL));
if (ret < 0)
goto end;
cal->rx_leq_rl_dfe = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "rx-lanes-imp",
FIELD_MAX(RG_XTP_LN_RX_IMPSEL));
if (ret < 0)
goto end;
cal->rx_impsel = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-lanes-pmos",
FIELD_MAX(RG_XTP_LN_TX_IMPSEL_PMOS));
if (ret < 0)
goto end;
cal->tx_impsel.pmos = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-lanes-nmos",
FIELD_MAX(RG_XTP_LN_TX_IMPSEL_NMOS));
if (ret < 0)
goto end;
cal->tx_impsel.nmos = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln0-rswn",
FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL));
if (ret < 0)
goto end;
cal->tx_rswn_impsel[0] = ret;
if (pcie_sphy->num_lanes == 2) {
ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln1-rswn",
FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL));
if (ret < 0)
goto end;
cal->tx_rswn_impsel[1] = ret;
}
ret = mtk_pcie_sphy_get_one_cal_para(dev, "cal-version", 15);
if (ret < 0)
goto end;
version = ret;
/* Quirk for eFuse calibration table versions 0 to 4 */
if ((version <= MTK_PCIE_SPHY_CALIBRATION_LAST_QUIRK_VER) &&
cal->rx_leq_rl_ctle == 10) {
cal->rx_leq_rl_vga = cal->rx_leq_rl_ctle;
cal->rx_leq_rl_dfe = cal->rx_leq_rl_ctle;
}
end:
if (ret < 0) {
/*
* If any of the calibration values is missing, or if there is
* no calibration at all in the eFuses, this is not a problem,
* as the PHY doesn't require one to actually work.
*
* Note that if even just one of the calibration values is
* missing it would not be safe to trust the correctness of
* the others as this may be a different eFuse layout.
*/
if (ret == -ENOENT || ret == -EOPNOTSUPP) {
cal->supported = false;
return 0;
}
return ret;
};
cal->supported = true;
return 0;
}
static int mtk_pcie_sphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *provider;
struct mtk_pcie_sphy *pcie_sphy;
u32 num_lanes;
int ret;
pcie_sphy = devm_kzalloc(dev, sizeof(*pcie_sphy), GFP_KERNEL);
if (!pcie_sphy)
return -ENOMEM;
pcie_sphy->sif_base = devm_platform_ioremap_resource_byname(pdev, "sif");
if (IS_ERR(pcie_sphy->sif_base))
return dev_err_probe(dev, PTR_ERR(pcie_sphy->sif_base),
"Failed to map phy-sif base\n");
pcie_sphy->ckm_base = devm_platform_ioremap_resource_byname(pdev, "ckm");
if (IS_ERR(pcie_sphy->ckm_base))
return dev_err_probe(dev, PTR_ERR(pcie_sphy->ckm_base),
"Failed to map phy-ckm base\n");
pcie_sphy->phy = devm_phy_create(dev, dev->of_node, &mtk_pcie_sphy_ops);
if (IS_ERR(pcie_sphy->phy))
return dev_err_probe(dev, PTR_ERR(pcie_sphy->phy),
"Failed to create PCIe phy\n");
ret = of_property_read_u32(dev->of_node, "num-lanes", &num_lanes);
if (ret)
num_lanes = 1;
else if (num_lanes > 4)
return dev_err_probe(dev, -EINVAL, "Invalid number of lanes.\n");
pcie_sphy->num_lanes = num_lanes;
pcie_sphy->dev = dev;
ret = mtk_pcie_sphy_get_calibration_data(pcie_sphy);
if (ret)
return ret;
phy_set_drvdata(pcie_sphy->phy, pcie_sphy);
pm_runtime_no_callbacks(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(provider))
return dev_err_probe(dev, PTR_ERR(provider),
"Could not register PCI-Express S-PHY\n");
return 0;
}
static const struct of_device_id mtk_pcie_sphy_of_match[] = {
{ .compatible = "mediatek,mt8196-pcie-sphy" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_pcie_sphy_of_match);
static struct platform_driver mtk_pcie_sphy_driver = {
.probe = mtk_pcie_sphy_probe,
.driver = {
.name = "mtk-pcie-sphy",
.of_match_table = mtk_pcie_sphy_of_match,
},
};
module_platform_driver(mtk_pcie_sphy_driver);
MODULE_DESCRIPTION("MediaTek PCIe SPHY driver");
MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_LICENSE("GPL");

View File

@ -62,6 +62,14 @@
#define P2A6_RG_BC11_SW_EN BIT(23)
#define P2A6_RG_OTG_VBUSCMP_EN BIT(20)
#define XSP_U2PHYDTM0 ((SSUSB_SIFSLV_U2PHY_COM) + 0x068)
#define P2D_FORCE_DATAIN BIT(23)
#define P2D_FORCE_DM_PULLDOWN BIT(21)
#define P2D_FORCE_DP_PULLDOWN BIT(20)
#define P2D_FORCE_XCVRSEL BIT(19)
#define P2D_FORCE_SUSPENDM BIT(18)
#define P2D_FORCE_TERMSEL BIT(17)
#define XSP_U2PHYDTM1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x06C)
#define P2D_FORCE_IDDIG BIT(9)
#define P2D_RG_VBUSVALID BIT(5)
@ -94,6 +102,7 @@ struct xsphy_instance {
struct phy *phy;
void __iomem *port_base;
struct clk *ref_clk; /* reference clock of anolog phy */
struct phy *repeater;
u32 index;
u32 type;
struct regmap *type_sw;
@ -181,6 +190,11 @@ static void u2_phy_instance_init(struct mtk_xsphy *xsphy,
{
void __iomem *pbase = inst->port_base;
mtk_phy_clear_bits(pbase + XSP_U2PHYDTM0,
P2D_FORCE_DATAIN | P2D_FORCE_DM_PULLDOWN |
P2D_FORCE_DP_PULLDOWN | P2D_FORCE_XCVRSEL |
P2D_FORCE_SUSPENDM | P2D_FORCE_TERMSEL);
/* DP/DM BC1.1 path Disable */
mtk_phy_clear_bits(pbase + XSP_USBPHYACR6, P2A6_RG_BC11_SW_EN);
@ -391,6 +405,11 @@ static int mtk_phy_init(struct phy *phy)
switch (inst->type) {
case PHY_TYPE_USB2:
ret = phy_init(inst->repeater);
if (ret) {
clk_disable_unprepare(inst->ref_clk);
return ret;
}
u2_phy_instance_init(xsphy, inst);
u2_phy_props_set(xsphy, inst);
break;
@ -438,7 +457,11 @@ static int mtk_phy_exit(struct phy *phy)
{
struct xsphy_instance *inst = phy_get_drvdata(phy);
if (inst->type == PHY_TYPE_USB2)
phy_exit(inst->repeater);
clk_disable_unprepare(inst->ref_clk);
return 0;
}
@ -446,9 +469,16 @@ static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
struct xsphy_instance *inst = phy_get_drvdata(phy);
struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent);
int ret;
if (inst->type == PHY_TYPE_USB2)
u2_phy_instance_set_mode(xsphy, inst, mode);
if (inst->type != PHY_TYPE_USB2)
return 0;
ret = phy_set_mode_ext(inst->repeater, mode, submode);
if (ret)
return ret;
u2_phy_instance_set_mode(xsphy, inst, mode);
return 0;
}
@ -590,6 +620,11 @@ static int mtk_xsphy_probe(struct platform_device *pdev)
retval = phy_type_syscon_get(inst, child_np);
if (retval)
return retval;
inst->repeater = devm_of_phy_optional_get(dev, child_np, NULL);
if (IS_ERR(inst->repeater))
return dev_err_probe(dev, PTR_ERR(inst->repeater),
"failed to get repeater\n");
}
provider = devm_of_phy_provider_register(dev, mtk_phy_xlate);

View File

@ -9,6 +9,7 @@
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
@ -16,10 +17,12 @@
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/usb/typec_mux.h>
/* USB_CFG_CSR */
#define USBCS_USB2PHY_CFG19_OFFSET 0x0
#define USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV GENMASK(19, 8)
@ -28,11 +31,44 @@
#define USBCS_USB2PHY_CFG21_REF_FREQ_SEL GENMASK(15, 13)
#define USBCS_USB2PHY_CFG21_PHY_TX_DIG_BYPASS_SEL BIT(19)
/* USBDP_TOP */
#define USBCS_PHY_CFG1_OFFSET 0x28
#define USBCS_PHY_CFG1_PHY0_MPLLA_SSC_EN BIT(1)
#define USBCS_PHY_CFG1_PHY0_SRAM_BYPASS_MODE GENMASK(11, 10)
#define SRAM_BYPASS_MODE_BYPASS_FIRMWARE BIT(0)
#define SRAM_BYPASS_MODE_BYPASS_CONTEXT BIT(1)
#define USBCS_PHY_CFG1_SYS_VBUSVALID BIT(17)
#define USBDP_TOP_CFG_REG_OFFSET 0x44
#define USBDP_TOP_CFG_REG_PMGT_REF_CLK_REQ_N BIT(0)
#define PHY_POWER_CONFIG_REG1_OFFSET 0x48
#define PHY_POWER_CONFIG_REG1_PG_MODE_EN BIT(1)
#define PHY_POWER_CONFIG_REG1_UPCS_PIPE_CONFIG GENMASK(31, 14)
#define UPCS_PIPE_CONFIG_ISO_CPM BIT(5)
#define UPCS_PIPE_CONFIG_PG_MODE_STATIC BIT(6)
#define UPCS_PIPE_CONFIG_LANE_RESET_NO_PG_EXIT BIT(9)
/* USB3_TCA */
#define TCA_INTR_STS_OFFSET 0x8
#define TCA_INTR_STS_XA_ACT_EVT BIT(0)
#define TCA_TCPC_OFFSET 0x14
#define TCA_TCPC_MUX_CONTROL GENMASK(2, 0)
#define TCA_TCPC_MUX_CONTROL_USB_ONLY 0x1
#define TCA_TCPC_CONNECTOR_ORIENTATION BIT(3)
#define TCA_TCPC_VALID BIT(4)
#define TCA_CTRLSYNCMODE_CFG1_OFFSET 0x24
#define TCA_CTRLSYNCMODE_CFG1_XA_TIMEOUT_VAL GENMASK(19, 0)
#define TCA_CTRLSYNCMODE_CFG1_XA_TIMEOUT_VAL_100MS 0x1e85
#define TCA_PSTATE_0_OFFSET 0x50
#define TCA_PSTATE_0_UPCS_LANE0_PHYSTATUS BIT(8)
#define GPHY_TCA_DELAY_US 10
#define GPHY_TCA_TIMEOUT_US 110000
enum google_usb_phy_id {
GOOGLE_USB2_PHY,
GOOGLE_USB3_PHY,
GOOGLE_USB_PHY_NUM,
};
@ -46,53 +82,205 @@ struct google_usb_phy_instance {
struct reset_control_bulk_data *rsts;
};
struct google_usb_phy_config {
const char * const *clk_names;
unsigned int num_clks;
const char * const *rst_names;
unsigned int num_rsts;
};
static const char * const u2phy_clk_names[] = {
"usb2",
"usb2_apb",
};
static const char * const u3phy_clk_names[] = {
"usb3"
};
static const char * const u2phy_rst_names[] = {
"usb2",
"usb2_apb",
};
static const char * const u3phy_rst_names[] = {
"usb3"
};
static const struct google_usb_phy_config phy_configs[GOOGLE_USB_PHY_NUM] = {
[GOOGLE_USB2_PHY] = {
.clk_names = u2phy_clk_names,
.num_clks = ARRAY_SIZE(u2phy_clk_names),
.rst_names = u2phy_rst_names,
.num_rsts = ARRAY_SIZE(u2phy_rst_names),
},
[GOOGLE_USB3_PHY] = {
.clk_names = u3phy_clk_names,
.num_clks = ARRAY_SIZE(u3phy_clk_names),
.rst_names = u3phy_rst_names,
.num_rsts = ARRAY_SIZE(u3phy_rst_names),
},
};
static inline void google_usb_phy_clk_disable(struct google_usb_phy_instance *inst)
{
clk_bulk_disable_unprepare(inst->num_clks, inst->clks);
}
DEFINE_FREE(inst_clk_disable, struct google_usb_phy_instance *,
if (_T) google_usb_phy_clk_disable(_T))
static inline void google_usb_phy_rst_disable(struct google_usb_phy_instance *inst)
{
reset_control_bulk_assert(inst->num_rsts, inst->rsts);
}
DEFINE_FREE(inst_rst_disable, struct google_usb_phy_instance *,
if (_T) google_usb_phy_rst_disable(_T))
/*
* combo_phy_state
* COMBO_PHY_IDLE: The ComboPHY has been torn down and USB3 has not completed
* bringup
* COMBO_PHY_INIT_DONE: The ComboPHY bringup sequence is complete.
* COMBO_PHY_TCA_READY: The PoR => NC transition is complete, and the TCA can be
* moved into USB.
*/
enum combo_phy_state {
COMBO_PHY_IDLE,
COMBO_PHY_INIT_DONE,
COMBO_PHY_TCA_READY,
};
struct google_usb_phy {
struct device *dev;
struct regmap *usb_cfg_regmap;
unsigned int usb2_cfg_offset;
void __iomem *usbdp_top_base;
void __iomem *usb3_tca_base;
struct google_usb_phy_instance *insts;
/*
* Protect phy registers from concurrent access, specifically via
* google_usb_set_orientation callback.
* google_usb_set_orientation callback. phy_mutex also protects
* concurrent access to phy_state.
*/
struct mutex phy_mutex;
struct typec_switch_dev *sw;
enum typec_orientation orientation;
enum combo_phy_state phy_state;
};
static void set_vbus_valid(struct google_usb_phy *gphy)
{
u32 reg;
if (gphy->orientation == TYPEC_ORIENTATION_NONE) {
reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
if (gphy->orientation == TYPEC_ORIENTATION_NONE)
reg &= ~USBCS_PHY_CFG1_SYS_VBUSVALID;
writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
} else {
reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
else
reg |= USBCS_PHY_CFG1_SYS_VBUSVALID;
writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
}
writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
}
static void set_sram_bypass(struct google_usb_phy *gphy, u32 bypass)
{
u32 reg;
reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
reg &= ~USBCS_PHY_CFG1_PHY0_SRAM_BYPASS_MODE;
reg |= FIELD_PREP(USBCS_PHY_CFG1_PHY0_SRAM_BYPASS_MODE, bypass);
writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
}
static void set_pmgt_ref_clk_req_n(struct google_usb_phy *gphy, bool resume)
{
u32 reg;
reg = readl(gphy->usbdp_top_base + USBDP_TOP_CFG_REG_OFFSET);
if (resume)
reg |= USBDP_TOP_CFG_REG_PMGT_REF_CLK_REQ_N;
else
reg &= ~USBDP_TOP_CFG_REG_PMGT_REF_CLK_REQ_N;
writel(reg, gphy->usbdp_top_base + USBDP_TOP_CFG_REG_OFFSET);
}
static inline void disable_pmgt_ref_clk_req_n(struct google_usb_phy *gphy)
{
set_pmgt_ref_clk_req_n(gphy, false);
}
DEFINE_FREE(pmgt_ref_clk_req_n, struct google_usb_phy *, if (_T) disable_pmgt_ref_clk_req_n(_T))
static int wait_tca_xa_ack(struct google_usb_phy *gphy)
{
int ret;
u32 reg;
ret = readl_poll_timeout(gphy->usb3_tca_base + TCA_INTR_STS_OFFSET,
reg, !!(reg & TCA_INTR_STS_XA_ACT_EVT),
GPHY_TCA_DELAY_US, GPHY_TCA_TIMEOUT_US);
if (ret)
dev_err(gphy->dev, "tca xa_ack timeout, ret=%d\n", ret);
return ret;
}
static int program_tca_locked(struct google_usb_phy *gphy)
__must_hold(&gphy->phy_mutex)
{
int ret;
u32 reg;
reg = readl(gphy->usb3_tca_base + TCA_INTR_STS_OFFSET);
writel(reg, gphy->usb3_tca_base + TCA_INTR_STS_OFFSET);
reg = readl(gphy->usb3_tca_base + TCA_TCPC_OFFSET);
reg &= ~TCA_TCPC_MUX_CONTROL;
reg |= FIELD_PREP(TCA_TCPC_MUX_CONTROL, TCA_TCPC_MUX_CONTROL_USB_ONLY);
if (gphy->orientation == TYPEC_ORIENTATION_REVERSE)
reg |= TCA_TCPC_CONNECTOR_ORIENTATION;
else
reg &= ~TCA_TCPC_CONNECTOR_ORIENTATION;
reg |= TCA_TCPC_VALID;
writel(reg, gphy->usb3_tca_base + TCA_TCPC_OFFSET);
ret = wait_tca_xa_ack(gphy);
dev_dbg(gphy->dev, "TCA switch %s, mux %lu, orientation %s\n",
ret ? "failed" : "success",
FIELD_GET(TCA_TCPC_MUX_CONTROL, reg),
FIELD_GET(TCA_TCPC_CONNECTOR_ORIENTATION, reg) ? "reverse" : "normal");
reg = readl(gphy->usb3_tca_base + TCA_INTR_STS_OFFSET);
writel(reg, gphy->usb3_tca_base + TCA_INTR_STS_OFFSET);
return ret;
}
static int google_usb_set_orientation(struct typec_switch_dev *sw,
enum typec_orientation orientation)
{
struct google_usb_phy *gphy = typec_switch_get_drvdata(sw);
int ret = 0;
dev_dbg(gphy->dev, "set orientation %d\n", orientation);
guard(mutex)(&gphy->phy_mutex);
gphy->orientation = orientation;
if (pm_runtime_suspended(gphy->dev))
return 0;
guard(mutex)(&gphy->phy_mutex);
if (IS_ENABLED(CONFIG_PM)) {
if (pm_runtime_get_if_active(gphy->dev) <= 0)
return 0;
}
set_vbus_valid(gphy);
return 0;
if (gphy->phy_state == COMBO_PHY_TCA_READY && orientation != TYPEC_ORIENTATION_NONE)
ret = program_tca_locked(gphy);
pm_runtime_put(gphy->dev);
return ret;
}
static int google_usb2_phy_init(struct phy *_phy)
@ -122,17 +310,18 @@ static int google_usb2_phy_init(struct phy *_phy)
ret = clk_bulk_prepare_enable(inst->num_clks, inst->clks);
if (ret)
return ret;
struct google_usb_phy_instance *clk_dev __free(inst_clk_disable) = inst;
ret = reset_control_bulk_deassert(inst->num_rsts, inst->rsts);
if (ret) {
clk_bulk_disable_unprepare(inst->num_clks, inst->clks);
if (ret)
return ret;
}
regmap_read(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, &reg);
reg |= USBCS_USB2PHY_CFG21_PHY_ENABLE;
regmap_write(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, reg);
retain_and_null_ptr(clk_dev);
return 0;
}
@ -161,6 +350,128 @@ static const struct phy_ops google_usb2_phy_ops = {
.exit = google_usb2_phy_exit,
};
static int google_usb3_phy_init(struct phy *_phy)
{
struct google_usb_phy_instance *inst = phy_get_drvdata(_phy);
struct google_usb_phy *gphy = inst->parent;
int ret = 0;
u32 reg;
dev_dbg(gphy->dev, "initializing usb3 phy\n");
guard(mutex)(&gphy->phy_mutex);
if (gphy->phy_state != COMBO_PHY_IDLE) {
dev_warn(gphy->dev, "usb3 phy init called when combo phy state is not idle\n");
return 0;
}
reg = readl(gphy->usb3_tca_base + TCA_CTRLSYNCMODE_CFG1_OFFSET);
reg &= ~TCA_CTRLSYNCMODE_CFG1_XA_TIMEOUT_VAL;
reg |= FIELD_PREP(TCA_CTRLSYNCMODE_CFG1_XA_TIMEOUT_VAL,
TCA_CTRLSYNCMODE_CFG1_XA_TIMEOUT_VAL_100MS);
writel(reg, gphy->usb3_tca_base + TCA_CTRLSYNCMODE_CFG1_OFFSET);
reg = readl(gphy->usbdp_top_base + PHY_POWER_CONFIG_REG1_OFFSET);
reg |= PHY_POWER_CONFIG_REG1_PG_MODE_EN;
reg &= ~PHY_POWER_CONFIG_REG1_UPCS_PIPE_CONFIG;
reg |= FIELD_PREP(PHY_POWER_CONFIG_REG1_UPCS_PIPE_CONFIG,
(UPCS_PIPE_CONFIG_ISO_CPM |
UPCS_PIPE_CONFIG_PG_MODE_STATIC |
UPCS_PIPE_CONFIG_LANE_RESET_NO_PG_EXIT));
writel(reg, gphy->usbdp_top_base + PHY_POWER_CONFIG_REG1_OFFSET);
set_vbus_valid(gphy);
reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
reg |= USBCS_PHY_CFG1_PHY0_MPLLA_SSC_EN;
writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
set_sram_bypass(gphy, SRAM_BYPASS_MODE_BYPASS_FIRMWARE |
SRAM_BYPASS_MODE_BYPASS_CONTEXT);
set_pmgt_ref_clk_req_n(gphy, true);
struct google_usb_phy *pmgt_ref_clk_req_dev __free(pmgt_ref_clk_req_n) = gphy;
ret = clk_bulk_prepare_enable(inst->num_clks, inst->clks);
if (ret)
return ret;
struct google_usb_phy_instance *clk_dev __free(inst_clk_disable) = inst;
ret = reset_control_bulk_deassert(inst->num_rsts, inst->rsts);
if (ret)
return ret;
struct google_usb_phy_instance *rst_dev __free(inst_rst_disable) = inst;
ret = readl_poll_timeout(gphy->usb3_tca_base + TCA_PSTATE_0_OFFSET,
reg, !(reg & TCA_PSTATE_0_UPCS_LANE0_PHYSTATUS),
GPHY_TCA_DELAY_US, GPHY_TCA_TIMEOUT_US);
if (ret) {
dev_err(gphy->dev, "wait for lane0 phystatus timed out\n");
return ret;
}
gphy->phy_state = COMBO_PHY_INIT_DONE;
retain_and_null_ptr(rst_dev);
retain_and_null_ptr(clk_dev);
retain_and_null_ptr(pmgt_ref_clk_req_dev);
return 0;
}
static int google_usb3_phy_exit(struct phy *_phy)
{
struct google_usb_phy_instance *inst = phy_get_drvdata(_phy);
struct google_usb_phy *gphy = inst->parent;
dev_dbg(gphy->dev, "exiting usb3 phy\n");
guard(mutex)(&gphy->phy_mutex);
reset_control_bulk_assert(inst->num_rsts, inst->rsts);
clk_bulk_disable_unprepare(inst->num_clks, inst->clks);
set_pmgt_ref_clk_req_n(gphy, false);
gphy->phy_state = COMBO_PHY_IDLE;
return 0;
}
static int google_usb3_phy_power_on(struct phy *_phy)
{
struct google_usb_phy_instance *inst = phy_get_drvdata(_phy);
struct google_usb_phy *gphy = inst->parent;
int ret;
dev_dbg(gphy->dev, "power on usb3 phy\n");
guard(mutex)(&gphy->phy_mutex);
if (gphy->phy_state == COMBO_PHY_IDLE)
return -EINVAL;
if (gphy->phy_state == COMBO_PHY_INIT_DONE) {
/* Wait for PoR -> NC transitions*/
ret = wait_tca_xa_ack(gphy);
if (ret) {
dev_err(gphy->dev, "PoR->NC transition timeout\n");
return ret;
}
gphy->phy_state = COMBO_PHY_TCA_READY;
}
if (gphy->orientation != TYPEC_ORIENTATION_NONE)
return program_tca_locked(gphy);
return 0;
}
static const struct phy_ops google_usb3_phy_ops = {
.init = google_usb3_phy_init,
.exit = google_usb3_phy_exit,
.power_on = google_usb3_phy_power_on,
};
static struct phy *google_usb_phy_xlate(struct device *dev,
const struct of_phandle_args *args)
{
@ -173,14 +484,61 @@ static struct phy *google_usb_phy_xlate(struct device *dev,
return gphy->insts[args->args[0]].phy;
}
static int google_usb_phy_parse_clocks(struct google_usb_phy *gphy)
{
struct device *dev = gphy->dev;
int id, i, ret;
for (id = 0; id < GOOGLE_USB_PHY_NUM; id++) {
const struct google_usb_phy_config *cfg = &phy_configs[id];
struct google_usb_phy_instance *inst = &gphy->insts[id];
inst->num_clks = cfg->num_clks;
inst->clks = devm_kcalloc(dev, inst->num_clks, sizeof(*inst->clks), GFP_KERNEL);
if (!inst->clks)
return -ENOMEM;
for (i = 0; i < inst->num_clks; i++)
inst->clks[i].id = cfg->clk_names[i];
ret = devm_clk_bulk_get(dev, inst->num_clks, inst->clks);
if (ret)
return dev_err_probe(dev, ret, "failed to get phy%d clks\n", id);
}
return 0;
}
static int google_usb_phy_parse_resets(struct google_usb_phy *gphy)
{
struct device *dev = gphy->dev;
int id, i, ret;
for (id = 0; id < GOOGLE_USB_PHY_NUM; id++) {
const struct google_usb_phy_config *cfg = &phy_configs[id];
struct google_usb_phy_instance *inst = &gphy->insts[id];
inst->num_rsts = cfg->num_rsts;
inst->rsts = devm_kcalloc(dev, inst->num_rsts, sizeof(*inst->rsts), GFP_KERNEL);
if (!inst->rsts)
return -ENOMEM;
for (i = 0; i < inst->num_rsts; i++)
inst->rsts[i].id = cfg->rst_names[i];
ret = devm_reset_control_bulk_get_exclusive(dev, inst->num_rsts, inst->rsts);
if (ret)
return dev_err_probe(dev, ret, "failed to get phy%d resets\n", id);
}
return 0;
}
static int google_usb_phy_probe(struct platform_device *pdev)
{
struct typec_switch_desc sw_desc = { };
struct google_usb_phy_instance *inst;
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct google_usb_phy *gphy;
struct phy *phy;
u32 args[1];
int ret;
@ -212,46 +570,48 @@ static int google_usb_phy_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(gphy->usbdp_top_base),
"invalid usbdp top\n");
gphy->usb3_tca_base = devm_platform_ioremap_resource_byname(pdev,
"usb3_tca");
if (IS_ERR(gphy->usb3_tca_base))
return dev_err_probe(dev, PTR_ERR(gphy->usb3_tca_base),
"invalid usb3 tca\n");
gphy->insts = devm_kcalloc(dev, GOOGLE_USB_PHY_NUM, sizeof(*gphy->insts), GFP_KERNEL);
if (!gphy->insts)
return -ENOMEM;
inst = &gphy->insts[GOOGLE_USB2_PHY];
inst->parent = gphy;
inst->index = GOOGLE_USB2_PHY;
phy = devm_phy_create(dev, NULL, &google_usb2_phy_ops);
if (IS_ERR(phy))
return dev_err_probe(dev, PTR_ERR(phy),
gphy->insts[GOOGLE_USB2_PHY].phy = devm_phy_create(dev, NULL, &google_usb2_phy_ops);
gphy->insts[GOOGLE_USB2_PHY].index = GOOGLE_USB2_PHY;
gphy->insts[GOOGLE_USB2_PHY].parent = gphy;
if (IS_ERR(gphy->insts[GOOGLE_USB2_PHY].phy))
return dev_err_probe(dev, PTR_ERR(gphy->insts[GOOGLE_USB2_PHY].phy),
"failed to create usb2 phy instance\n");
inst->phy = phy;
phy_set_drvdata(phy, inst);
phy_set_drvdata(gphy->insts[GOOGLE_USB2_PHY].phy, &gphy->insts[GOOGLE_USB2_PHY]);
inst->num_clks = 2;
inst->clks = devm_kcalloc(dev, inst->num_clks, sizeof(*inst->clks), GFP_KERNEL);
if (!inst->clks)
return -ENOMEM;
inst->clks[0].id = "usb2";
inst->clks[1].id = "usb2_apb";
ret = devm_clk_bulk_get(dev, inst->num_clks, inst->clks);
if (ret)
return dev_err_probe(dev, ret, "failed to get u2 phy clks\n");
gphy->insts[GOOGLE_USB3_PHY].phy = devm_phy_create(dev, NULL, &google_usb3_phy_ops);
gphy->insts[GOOGLE_USB3_PHY].index = GOOGLE_USB3_PHY;
gphy->insts[GOOGLE_USB3_PHY].parent = gphy;
if (IS_ERR(gphy->insts[GOOGLE_USB3_PHY].phy))
return dev_err_probe(dev, PTR_ERR(gphy->insts[GOOGLE_USB3_PHY].phy),
"failed to create usb3 phy instance\n");
phy_set_drvdata(gphy->insts[GOOGLE_USB3_PHY].phy, &gphy->insts[GOOGLE_USB3_PHY]);
inst->num_rsts = 2;
inst->rsts = devm_kcalloc(dev, inst->num_rsts, sizeof(*inst->rsts), GFP_KERNEL);
if (!inst->rsts)
return -ENOMEM;
inst->rsts[0].id = "usb2";
inst->rsts[1].id = "usb2_apb";
ret = devm_reset_control_bulk_get_exclusive(dev, inst->num_rsts, inst->rsts);
ret = google_usb_phy_parse_clocks(gphy);
if (ret)
return dev_err_probe(dev, ret, "failed to get u2 phy resets\n");
return ret;
ret = google_usb_phy_parse_resets(gphy);
if (ret)
return ret;
phy_provider = devm_of_phy_provider_register(dev, google_usb_phy_xlate);
if (IS_ERR(phy_provider))
return dev_err_probe(dev, PTR_ERR(phy_provider),
"failed to register phy provider\n");
pm_runtime_enable(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
sw_desc.fwnode = dev_fwnode(dev);
sw_desc.drvdata = gphy;
@ -271,7 +631,6 @@ static void google_usb_phy_remove(struct platform_device *pdev)
struct google_usb_phy *gphy = dev_get_drvdata(&pdev->dev);
typec_switch_unregister(gphy->sw);
pm_runtime_disable(&pdev->dev);
}
static const struct of_device_id google_usb_phy_of_match[] = {

View File

@ -1669,7 +1669,7 @@ static int xgene_phy_probe(struct platform_device *pdev)
/* Retrieve optional clock */
ctx->clk = clk_get(&pdev->dev, NULL);
/* Load override paramaters */
/* Load override parameters */
xgene_phy_get_param(pdev, "apm,tx-eye-tuning",
ctx->sata_param.txeyetuning, 6, default_txeye_tuning, 1);
xgene_phy_get_param(pdev, "apm,tx-eye-direction",

View File

@ -96,6 +96,7 @@ config PHY_QCOM_QMP_PCIE_MULTIPHY
tristate "Qualcomm QMP PCIe Multi PHY Driver"
depends on PCI || COMPILE_TEST
select GENERIC_PHY
select MFD_SYSCON
help
Enable this to support the QMP PCIe PHY transceiver that is used
with PCIe controllers on Qualcomm chips. This PHY is a single

View File

@ -22,6 +22,7 @@
#include <linux/usb/typec.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
#include <linux/usb/typec_tbt.h>
#include <drm/bridge/aux-bridge.h>
@ -66,10 +67,14 @@
#define SW_USB3PHY_RESET BIT(2)
/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */
#define SW_USB3PHY_RESET_MUX BIT(3)
#define SW_USB4PHY_RESET BIT(4)
#define SW_USB4PHY_RESET_MUX BIT(5)
/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */
#define USB3_MODE BIT(0) /* enables USB3 mode */
#define DP_MODE BIT(1) /* enables DP mode */
#define USB4_MODE BIT(2) /* mutually exclusive with the above */
#define DP_TUNNELING_CLOCK_GEN_EN BIT(3)
/* QPHY_V3_DP_COM_TYPEC_CTRL register bits */
#define SW_PORTSELECT_VAL BIT(0)
@ -82,6 +87,8 @@ enum qmpphy_mode {
QMPPHY_MODE_USB3DP = 0,
QMPPHY_MODE_DP_ONLY,
QMPPHY_MODE_USB3_ONLY,
/* USB4 QMPPHY mode refers to both USB4 and TBT3 */
QMPPHY_MODE_USB4,
};
/* set of registers with offsets different per-PHY */
@ -94,6 +101,7 @@ enum qphy_reg_layout {
QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR,
QPHY_PCS_POWER_DOWN_CONTROL,
QPHY_PCS_CLAMP_ENABLE,
QPHY_PCS_USB4_CLAMP_ENABLE,
QPHY_COM_RESETSM_CNTRL,
QPHY_COM_C_READY_STATUS,
@ -2348,6 +2356,20 @@ static const struct qmp_phy_init_tbl hawi_usb3_pcs_usb_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V10_PCS_USB3_RCVR_DTCT_DLY_U3_H, 0x00),
};
static const struct qmp_phy_init_tbl nord_usb3_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_TX, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_RX, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_OFFSET_TX, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_OFFSET_RX, 0x09),
QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_1, 0xf5),
QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_3, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_4, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_5, 0x5f),
QMP_PHY_INIT_CFG(QSERDES_V6_TX_RCV_DETECT_LVL_2, 0x14),
QMP_PHY_INIT_CFG_LANE(QSERDES_V6_TX_PI_QEC_CTRL, 0x21, 1),
QMP_PHY_INIT_CFG_LANE(QSERDES_V6_TX_PI_QEC_CTRL, 0x05, 2),
};
/* list of regulators */
static struct regulator_bulk_data qmp_phy_vreg_l[] = {
{ .supply = "vdda-phy", .init_load_uA = 21800, },
@ -2489,6 +2511,8 @@ struct qmp_combo_offsets {
u16 dp_txb;
u16 dp_dp_phy;
u16 aon_toggle;
u16 usb4_serdes;
u16 usb4_pcs;
};
struct qmp_phy_cfg {
@ -2536,6 +2560,18 @@ struct qmp_phy_cfg {
int (*calibrate_dp_phy)(struct qmp_combo *qmp);
void (*dp_aux_init)(struct qmp_combo *qmp);
/* USB4 specifics */
const struct qmp_phy_init_tbl *usb4_serdes_tbl;
int usb4_serdes_tbl_num;
const struct qmp_phy_init_tbl *usb4_serdes_tb3_ovrd_tbl;
int usb4_serdes_tb3_ovrd_num;
const struct qmp_phy_init_tbl *usb4_tx_tbl;
int usb4_tx_tbl_num;
const struct qmp_phy_init_tbl *usb4_rx_tbl;
int usb4_rx_tbl_num;
const struct qmp_phy_init_tbl *usb4_pcs_tbl;
int usb4_pcs_tbl_num;
/* resets to be requested */
const char * const *reset_list;
int num_resets;
@ -2578,8 +2614,12 @@ struct qmp_combo {
void __iomem *dp_tx2;
void __iomem *dp_dp_phy;
void __iomem *usb4_serdes;
void __iomem *usb4_pcs;
struct clk *pipe_clk;
struct clk_bulk_data *clks;
struct clk *p2rr2p_pipe_clk;
int num_clks;
struct reset_control_bulk_data *resets;
struct regulator_bulk_data *vregs;
@ -2598,6 +2638,10 @@ struct qmp_combo {
unsigned int dp_init_count;
bool dp_powered_on;
struct phy *usb4_phy;
enum tbt_phy_submode usb4_phy_submode;
unsigned int usb4_init_count;
struct clk_fixed_rate pipe_clk_fixed;
struct clk_hw dp_link_hw;
struct clk_hw dp_pixel_hw;
@ -2622,30 +2666,6 @@ static void qmp_v8_dp_aux_init(struct qmp_combo *qmp);
static int qmp_v8_configure_dp_clocks(struct qmp_combo *qmp);
static int qmp_v8_configure_dp_phy(struct qmp_combo *qmp);
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg |= val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg &= ~val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
/* list of clocks required by phy */
static const char * const qmp_combo_phy_clk_l[] = {
"aux", "cfg_ahb", "ref", "com_aux",
@ -2690,6 +2710,23 @@ static const struct qmp_combo_offsets qmp_combo_offsets_v5 = {
.dp_dp_phy = 0x2200,
};
static const struct qmp_combo_offsets qmp_combo_offsets_v6 = {
.com = 0x0000,
.txa = 0x1200,
.rxa = 0x1400,
.txb = 0x1600,
.rxb = 0x1800,
.usb3_serdes = 0x1000,
.usb3_pcs_misc = 0x1a00,
.usb3_pcs = 0x1c00,
.usb3_pcs_aon = 0x1e00,
.usb3_pcs_usb = 0x1f00,
.dp_serdes = 0x2000,
.dp_txa = 0x2200,
.dp_txb = 0x2600,
.dp_dp_phy = 0x2a00,
};
static const struct qmp_combo_offsets qmp_combo_offsets_v8 = {
.com = 0x0000,
.txa = 0x1400,
@ -3394,6 +3431,51 @@ static const struct qmp_phy_cfg glymur_usb3dpphy_cfg = {
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_refgen),
};
static const struct qmp_phy_cfg nord_usb3dpphy_cfg = {
.offsets = &qmp_combo_offsets_v6,
.serdes_tbl = sm8550_usb3_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(sm8550_usb3_serdes_tbl),
.tx_tbl = nord_usb3_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(nord_usb3_tx_tbl),
.rx_tbl = sm8550_usb3_rx_tbl,
.rx_tbl_num = ARRAY_SIZE(sm8550_usb3_rx_tbl),
.pcs_tbl = sm8550_usb3_pcs_tbl,
.pcs_tbl_num = ARRAY_SIZE(sm8550_usb3_pcs_tbl),
.pcs_usb_tbl = sm8550_usb3_pcs_usb_tbl,
.pcs_usb_tbl_num = ARRAY_SIZE(sm8550_usb3_pcs_usb_tbl),
.dp_serdes_tbl = qmp_v6_dp_serdes_tbl,
.dp_serdes_tbl_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl),
.dp_tx_tbl = qmp_v6_dp_tx_tbl,
.dp_tx_tbl_num = ARRAY_SIZE(qmp_v6_dp_tx_tbl),
.serdes_tbl_rbr = qmp_v6_dp_serdes_tbl_rbr,
.serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_rbr),
.serdes_tbl_hbr = qmp_v6_dp_serdes_tbl_hbr,
.serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr),
.serdes_tbl_hbr2 = qmp_v6_dp_serdes_tbl_hbr2,
.serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr2),
.serdes_tbl_hbr3 = qmp_v6_dp_serdes_tbl_hbr3,
.serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr3),
.swing_hbr_rbr = &qmp_dp_v5_voltage_swing_hbr_rbr,
.pre_emphasis_hbr_rbr = &qmp_dp_v6_pre_emphasis_hbr_rbr,
.swing_hbr3_hbr2 = &qmp_dp_v5_voltage_swing_hbr3_hbr2,
.pre_emphasis_hbr3_hbr2 = &qmp_dp_v5_pre_emphasis_hbr3_hbr2,
.dp_aux_init = qmp_v4_dp_aux_init,
.configure_dp_tx = qmp_v4_configure_dp_tx,
.configure_dp_clocks = qmp_v3_configure_dp_clocks,
.configure_dp_phy = qmp_v4_configure_dp_phy,
.regs = qmp_v6_usb3phy_regs_layout,
.reset_list = msm8996_usb3phy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
};
static int qmp_combo_dp_serdes_init(struct qmp_combo *qmp)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
@ -4006,6 +4088,9 @@ static int qmp_combo_dp_calibrate(struct phy *phy)
static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
u32 dp_reset_val = SW_DPPHY_RESET_MUX | SW_DPPHY_RESET;
void __iomem *pcs_aon = qmp->pcs_aon;
void __iomem *pcs = qmp->pcs;
void __iomem *com = qmp->com;
void __iomem *aon_toggle;
int ret;
@ -4019,6 +4104,16 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
else
aon_toggle = qmp->pcs_aon;
if (qmp->qmpphy_mode == QMPPHY_MODE_USB4) {
pcs = qmp->usb4_pcs;
qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_USB4_CLAMP_ENABLE], CLAMP_EN);
/* Do not disturb the DP PLL in case there's an active DP tunnel */
if (readl(com + QPHY_V3_DP_COM_RESET_OVRD_CTRL) & DP_TUNNELING_CLOCK_GEN_EN)
dp_reset_val &= ~SW_DPPHY_RESET_MUX;
}
ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
if (ret) {
dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
@ -4052,8 +4147,9 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
/* override hardware control for reset of qmp phy */
qphy_setbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
dp_reset_val |
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET |
SW_USB4PHY_RESET_MUX | SW_USB4PHY_RESET);
/* override hardware control for reset of qmp phy */
if (aon_toggle && cfg->regs[QPHY_AON_TOGGLE_ENABLE]) {
@ -4069,6 +4165,10 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
if (cfg->invert_cc_polarity)
val |= INVERT_CC_POLARITY;
/*
* Note that in USB4 mode, the router controls pin assignments instead
* and the values of the PORTSELECT registers are ignored.
*/
writel(val, com + QPHY_V3_DP_COM_TYPEC_CTRL);
switch (qmp->qmpphy_mode) {
@ -4096,12 +4196,24 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
break;
case QMPPHY_MODE_USB4:
writel(USB4_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL);
/* bring both QMP USB and QMP DP PHYs PCS block out of reset */
/*
* TODO: disable the DP PLL to save power if there are no
* active DP tunnels after router setup has concluded
*/
qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
SW_USB4PHY_RESET_MUX | SW_USB4PHY_RESET |
SW_DPPHY_RESET_MUX | SW_DPPHY_RESET);
break;
}
qphy_clrbits(com, QPHY_V3_DP_COM_SWI_CTRL, 0x03);
qphy_clrbits(com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
qphy_setbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
SW_PWRDN);
return 0;
@ -4219,6 +4331,10 @@ static int qmp_combo_usb_power_on(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *serdes = qmp->serdes;
const struct qmp_phy_init_tbl *pcs_tbl;
const struct qmp_phy_init_tbl *rx_tbl;
const struct qmp_phy_init_tbl *serdes_tbl;
const struct qmp_phy_init_tbl *tx_tbl;
void __iomem *tx = qmp->tx;
void __iomem *rx = qmp->rx;
void __iomem *tx2 = qmp->tx2;
@ -4226,23 +4342,54 @@ static int qmp_combo_usb_power_on(struct phy *phy)
void __iomem *pcs = qmp->pcs;
void __iomem *pcs_usb = qmp->pcs_usb;
void __iomem *status;
int pcs_tbl_num, rx_tbl_num, serdes_tbl_num, tx_tbl_num;
unsigned int val;
int ret;
qmp_configure(qmp->dev, serdes, cfg->serdes_tbl, cfg->serdes_tbl_num);
if (qmp->qmpphy_mode == QMPPHY_MODE_USB4) {
pcs = qmp->usb4_pcs;
serdes = qmp->usb4_serdes;
pcs_tbl = cfg->usb4_pcs_tbl;
pcs_tbl_num = cfg->usb4_pcs_tbl_num;
serdes_tbl = cfg->usb4_serdes_tbl;
serdes_tbl_num = cfg->usb4_serdes_tbl_num;
rx_tbl = cfg->usb4_rx_tbl;
rx_tbl_num = cfg->usb4_rx_tbl_num;
tx_tbl = cfg->usb4_tx_tbl;
tx_tbl_num = cfg->usb4_tx_tbl_num;
} else {
pcs_tbl = cfg->pcs_tbl;
pcs_tbl_num = cfg->pcs_tbl_num;
serdes_tbl = cfg->serdes_tbl;
serdes_tbl_num = cfg->serdes_tbl_num;
rx_tbl = cfg->rx_tbl;
rx_tbl_num = cfg->rx_tbl_num;
tx_tbl = cfg->tx_tbl;
tx_tbl_num = cfg->tx_tbl_num;
}
qmp_configure(qmp->dev, serdes, serdes_tbl, serdes_tbl_num);
/* Tx, Rx, and PCS configurations */
qmp_configure_lane(qmp->dev, tx, cfg->tx_tbl, cfg->tx_tbl_num, 1);
qmp_configure_lane(qmp->dev, tx2, cfg->tx_tbl, cfg->tx_tbl_num, 2);
qmp_configure_lane(qmp->dev, tx, tx_tbl, tx_tbl_num, 1);
qmp_configure_lane(qmp->dev, tx2, tx_tbl, tx_tbl_num, 2);
qmp_configure_lane(qmp->dev, rx, cfg->rx_tbl, cfg->rx_tbl_num, 1);
qmp_configure_lane(qmp->dev, rx2, cfg->rx_tbl, cfg->rx_tbl_num, 2);
qmp_configure_lane(qmp->dev, rx, rx_tbl, rx_tbl_num, 1);
qmp_configure_lane(qmp->dev, rx2, rx_tbl, rx_tbl_num, 2);
qmp_configure(qmp->dev, pcs, cfg->pcs_tbl, cfg->pcs_tbl_num);
qmp_configure(qmp->dev, pcs, pcs_tbl, pcs_tbl_num);
qmp_configure(qmp->dev, qmp->pcs_misc, cfg->pcs_misc_tbl, cfg->pcs_misc_tbl_num);
if (pcs_usb)
if (qmp->qmpphy_mode == QMPPHY_MODE_USB4 && qmp->usb4_phy_submode == PHY_SUBMODE_TBT3)
qmp_configure(qmp->dev, serdes, cfg->usb4_serdes_tb3_ovrd_tbl,
cfg->usb4_serdes_tb3_ovrd_num);
else if (qmp->qmpphy_mode != QMPPHY_MODE_USB4 && pcs_usb)
qmp_configure(qmp->dev, pcs_usb, cfg->pcs_usb_tbl,
cfg->pcs_usb_tbl_num);
@ -4275,16 +4422,20 @@ static int qmp_combo_usb_power_off(struct phy *phy)
{
struct qmp_combo *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *pcs = qmp->pcs;
if (qmp->usb4_init_count)
pcs = qmp->usb4_pcs;
/* PHY reset */
qphy_setbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
qphy_setbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
/* stop SerDes and Phy-Coding-Sublayer */
qphy_clrbits(qmp->pcs, cfg->regs[QPHY_START_CTRL],
qphy_clrbits(pcs, cfg->regs[QPHY_START_CTRL],
SERDES_START | PCS_START);
/* Put PHY into POWER DOWN state: active low */
qphy_clrbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
qphy_clrbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
SW_PWRDN);
return 0;
@ -4295,21 +4446,27 @@ static int qmp_combo_usb_init(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
int ret;
mutex_lock(&qmp->phy_mutex);
guard(mutex)(&qmp->phy_mutex);
/* USB4 mode takes precedence, do not reprogram the PHY in that case */
if (qmp->usb4_init_count) {
qmp->init_count++;
qmp->usb_init_count++;
return 0;
}
ret = qmp_combo_com_init(qmp, false);
if (ret)
goto out_unlock;
return ret;
ret = qmp_combo_usb_power_on(phy);
if (ret) {
qmp_combo_com_exit(qmp, false);
goto out_unlock;
return ret;
}
qmp->usb_init_count++;
out_unlock:
mutex_unlock(&qmp->phy_mutex);
return ret;
}
@ -4318,20 +4475,26 @@ static int qmp_combo_usb_exit(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
int ret;
mutex_lock(&qmp->phy_mutex);
guard(mutex)(&qmp->phy_mutex);
/* USB4 mode takes precedence, do not reprogram the PHY in that case */
if (qmp->usb4_init_count) {
qmp->init_count--;
qmp->usb_init_count--;
return 0;
}
ret = qmp_combo_usb_power_off(phy);
if (ret)
goto out_unlock;
return ret;
ret = qmp_combo_com_exit(qmp, false);
if (ret)
goto out_unlock;
return ret;
qmp->usb_init_count--;
out_unlock:
mutex_unlock(&qmp->phy_mutex);
return ret;
return 0;
}
static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submode)
@ -4343,6 +4506,125 @@ static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submo
return 0;
}
static int qmp_combo_reconfigure_phy(struct qmp_combo *qmp, enum qmpphy_mode new_mode)
{
dev_dbg(qmp->dev, "qmp_combo_reconfigure_phy: switching from qmpphy mode %d to %d\n",
qmp->qmpphy_mode, new_mode);
if (qmp->usb_init_count || qmp->usb4_init_count)
qmp_combo_usb_power_off(qmp->usb_phy);
if (qmp->dp_init_count)
writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);
qmp_combo_com_exit(qmp, true);
qmp->qmpphy_mode = new_mode;
/* Now everything's powered down, power up the right PHYs */
qmp_combo_com_init(qmp, true);
if ((qmp->usb_init_count || qmp->usb4_init_count) &&
new_mode != QMPPHY_MODE_DP_ONLY)
qmp_combo_usb_power_on(qmp->usb_phy);
if ((new_mode == QMPPHY_MODE_USB3DP || new_mode == QMPPHY_MODE_DP_ONLY) &&
qmp->dp_init_count)
qmp->cfg->dp_aux_init(qmp);
return 0;
}
static int qmp_combo_usb4_init(struct phy *phy)
{
struct qmp_combo *qmp = phy_get_drvdata(phy);
int ret;
guard(mutex)(&qmp->phy_mutex);
if (!qmp->p2rr2p_pipe_clk) {
dev_err(qmp->dev, "missing p2rr2p_pipe clock handle. Update your Device Tree.\n");
return -EINVAL;
}
ret = clk_prepare_enable(qmp->p2rr2p_pipe_clk);
if (ret) {
dev_err(qmp->dev, "p2rr2p_pipe enable failed: %d\n", ret);
return ret;
}
ret = qmp_combo_com_init(qmp, false);
if (ret) {
clk_disable_unprepare(qmp->p2rr2p_pipe_clk);
return ret;
}
/* USB4 mode takes precedence to USB3(+DP), force reconfigure the PHY */
ret = qmp_combo_reconfigure_phy(qmp, QMPPHY_MODE_USB4);
if (ret) {
clk_disable_unprepare(qmp->p2rr2p_pipe_clk);
qmp_combo_com_exit(qmp, false);
return ret;
}
ret = qmp_combo_usb_power_on(phy);
if (ret) {
clk_disable_unprepare(qmp->p2rr2p_pipe_clk);
qmp_combo_com_exit(qmp, false);
return ret;
}
/*
* Due to the SoC design, the PHY only has a single valid consumer and
* preventing it from having sole ownership of the PHY's power state
* makes suspending/resuming the router impossible.
*/
WARN_ON(qmp->usb4_init_count++);
return 0;
}
static int qmp_combo_usb4_exit(struct phy *phy)
{
struct qmp_combo *qmp = phy_get_drvdata(phy);
int ret;
guard(mutex)(&qmp->phy_mutex);
ret = qmp_combo_usb_power_off(qmp->usb_phy);
if (ret)
return ret;
ret = qmp_combo_com_exit(qmp, false);
if (ret)
return ret;
/*
* Mark the USB4 PHY uninitialized and wait for a mux_set event to determine the correct
* setting. This will always be possible because USB4 requires Type-C.
*/
qmp->usb4_init_count--;
clk_disable_unprepare(qmp->p2rr2p_pipe_clk);
return 0;
}
static int qmp_combo_usb4_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
struct qmp_combo *qmp = phy_get_drvdata(phy);
if (mode != PHY_MODE_TBT)
return -EINVAL;
if (submode != PHY_SUBMODE_USB4 && submode != PHY_SUBMODE_TBT3)
return -EINVAL;
qmp->usb4_phy_submode = submode;
return 0;
}
static const struct phy_ops qmp_combo_usb_phy_ops = {
.init = qmp_combo_usb_init,
.exit = qmp_combo_usb_exit,
@ -4350,6 +4632,13 @@ static const struct phy_ops qmp_combo_usb_phy_ops = {
.owner = THIS_MODULE,
};
static const struct phy_ops qmp_combo_usb4_phy_ops = {
.init = qmp_combo_usb4_init,
.exit = qmp_combo_usb4_exit,
.set_mode = qmp_combo_usb4_set_mode,
.owner = THIS_MODULE,
};
static const struct phy_ops qmp_combo_dp_phy_ops = {
.init = qmp_combo_dp_init,
.configure = qmp_combo_dp_configure,
@ -4389,9 +4678,12 @@ static void qmp_combo_enable_autonomous_mode(struct qmp_combo *qmp)
* Enable i/o clamp_n for autonomous mode
* V6 and later versions use pcs aon clamp register
*/
if (pcs_aon)
qphy_clrbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
else if (pcs_misc)
if (pcs_aon) {
if (qmp->qmpphy_mode == QMPPHY_MODE_USB4)
qphy_clrbits(pcs_aon, cfg->regs[QPHY_PCS_USB4_CLAMP_ENABLE], CLAMP_EN);
else
qphy_clrbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
} else if (pcs_misc)
qphy_clrbits(pcs_misc, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
}
@ -4403,9 +4695,12 @@ static void qmp_combo_disable_autonomous_mode(struct qmp_combo *qmp)
void __iomem *pcs_aon = qmp->pcs_aon;
/* Disable i/o clamp_n on resume for normal mode */
if (pcs_aon)
qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
else if (pcs_misc)
if (pcs_aon) {
if (qmp->qmpphy_mode == QMPPHY_MODE_USB4)
qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_USB4_CLAMP_ENABLE], CLAMP_EN);
else
qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
} else if (pcs_misc)
qphy_setbits(pcs_misc, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
@ -4779,24 +5074,29 @@ static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw,
struct qmp_combo *qmp = typec_switch_get_drvdata(sw);
const struct qmp_phy_cfg *cfg = qmp->cfg;
if (qmp->qmpphy_mode == QMPPHY_MODE_USB4) {
/* QMPPHY has no orientation handling in USB4 mode, don't cache the setting */
qmp->orientation = TYPEC_ORIENTATION_NONE;
return 0;
}
if (orientation == qmp->orientation || orientation == TYPEC_ORIENTATION_NONE)
return 0;
mutex_lock(&qmp->phy_mutex);
guard(mutex)(&qmp->phy_mutex);
qmp->orientation = orientation;
if (qmp->init_count) {
if (qmp->usb_init_count)
if (qmp->usb_init_count || qmp->usb4_init_count)
qmp_combo_usb_power_off(qmp->usb_phy);
qmp_combo_com_exit(qmp, true);
qmp_combo_com_init(qmp, true);
if (qmp->usb_init_count)
if (qmp->usb_init_count || qmp->usb4_init_count)
qmp_combo_usb_power_on(qmp->usb_phy);
if (qmp->dp_init_count)
cfg->dp_aux_init(qmp);
}
mutex_unlock(&qmp->phy_mutex);
return 0;
}
@ -4804,7 +5104,6 @@ static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw,
static int qmp_combo_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
{
struct qmp_combo *qmp = typec_mux_get_drvdata(mux);
const struct qmp_phy_cfg *cfg = qmp->cfg;
enum qmpphy_mode new_mode;
unsigned int svid;
@ -4815,6 +5114,29 @@ static int qmp_combo_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_s
else
svid = 0;
/*
* The USB4 router driver must excercise fine-grained control over the timing of
* USB4 QMPPHY mode entry/exit, which is difficult to otherwise ensure within Linux's
* Type-C framework if the PHY acts as a self-decisive mode mux in parallel.
*
* Keep the hardware in QMPPHY_MODE_USB4 at all times between .init and .exit of the
* (Linux) USB4 PHY and wait for the router driver to turn it off at its discretion.
* Once that happens, fall back to the usual USB3/DP/Combo mode logic.
*
* After the QMPPHY has been turned off through phy_exit(usb4_phy), the next mux_set
* will initialize it in the right mode.
*/
if (qmp->usb4_init_count)
return 0;
/*
* Explicitly ignore TBT/USB4 mode requests that may come if the USB4 PHY hasn't been
* initialized, either due to the USB4 drivers being disabled or due to this PHY instance
* lacking USB4 support.
*/
if (svid == USB_TYPEC_TBT_SID || (!state->alt && state->mode == TYPEC_MODE_USB4))
return 0;
if (svid == USB_TYPEC_DP_SID) {
switch (state->mode) {
/* DP Only */
@ -4847,41 +5169,11 @@ static int qmp_combo_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_s
return 0;
}
dev_dbg(qmp->dev, "typec_mux_set: switching from qmpphy mode %d to %d\n",
qmp->qmpphy_mode, new_mode);
/* The mux still receives Type-C events, even if all PHYs are uninitialized */
if (!qmp->init_count)
return 0;
qmp->qmpphy_mode = new_mode;
if (qmp->init_count) {
if (qmp->usb_init_count)
qmp_combo_usb_power_off(qmp->usb_phy);
if (qmp->dp_init_count)
writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);
qmp_combo_com_exit(qmp, true);
/* Now everything's powered down, power up the right PHYs */
qmp_combo_com_init(qmp, true);
if (new_mode == QMPPHY_MODE_DP_ONLY) {
if (qmp->usb_init_count)
qmp->usb_init_count--;
}
if (new_mode == QMPPHY_MODE_USB3DP || new_mode == QMPPHY_MODE_USB3_ONLY) {
qmp_combo_usb_power_on(qmp->usb_phy);
if (!qmp->usb_init_count)
qmp->usb_init_count++;
}
if (new_mode == QMPPHY_MODE_DP_ONLY || new_mode == QMPPHY_MODE_USB3DP) {
if (qmp->dp_init_count)
cfg->dp_aux_init(qmp);
}
}
return 0;
return qmp_combo_reconfigure_phy(qmp, new_mode);
}
static void qmp_combo_typec_switch_unregister(void *data)
@ -5088,6 +5380,9 @@ static int qmp_combo_parse_dt(struct qmp_combo *qmp)
if (offs->aon_toggle)
qmp->aon_toggle = base + offs->aon_toggle;
qmp->usb4_serdes = base + offs->usb4_serdes;
qmp->usb4_pcs = base + offs->usb4_pcs;
ret = qmp_combo_clk_init(qmp);
if (ret)
return ret;
@ -5098,6 +5393,12 @@ static int qmp_combo_parse_dt(struct qmp_combo *qmp)
"failed to get usb3_pipe clock\n");
}
qmp->p2rr2p_pipe_clk = devm_clk_get_optional(dev, "p2rr2p_pipe");
if (IS_ERR(qmp->p2rr2p_pipe_clk)) {
return dev_err_probe(dev, PTR_ERR(qmp->p2rr2p_pipe_clk),
"failed to get p2rr2p_pipe clock\n");
}
return 0;
}
@ -5113,6 +5414,8 @@ static struct phy *qmp_combo_phy_xlate(struct device *dev, const struct of_phand
return qmp->usb_phy;
case QMP_USB43DP_DP_PHY:
return qmp->dp_phy;
case QMP_USB43DP_USB4_PHY:
return qmp->usb4_phy ?: ERR_PTR(-EINVAL);
}
return ERR_PTR(-EINVAL);
@ -5325,6 +5628,17 @@ static int qmp_combo_probe(struct platform_device *pdev)
phy_set_drvdata(qmp->dp_phy, qmp);
if (qmp->cfg->usb4_serdes_tbl) {
qmp->usb4_phy = devm_phy_create(dev, dev->of_node, &qmp_combo_usb4_phy_ops);
if (IS_ERR(qmp->usb4_phy)) {
ret = PTR_ERR(qmp->usb4_phy);
dev_err(dev, "failed to create USB4 PHY: %d\n", ret);
goto err_node_put;
}
phy_set_drvdata(qmp->usb4_phy, qmp);
}
if (usb_np == dev->of_node)
phy_provider = devm_of_phy_provider_register(dev, qmp_combo_phy_xlate);
else
@ -5359,6 +5673,10 @@ static const struct of_device_id qmp_combo_of_match_table[] = {
.compatible = "qcom,hawi-qmp-usb3-dp-phy",
.data = &hawi_usb3dpphy_cfg,
},
{
.compatible = "qcom,nord-qmp-usb3-dp-phy",
.data = &nord_usb3dpphy_cfg,
},
{
.compatible = "qcom,sar2130p-qmp-usb3-dp-phy",
.data = &sar2130p_usb3dpphy_cfg,

View File

@ -59,4 +59,36 @@ static inline void qmp_configure(struct device *dev, void __iomem *base,
qmp_configure_lane(dev, base, tbl, num, 0xff);
}
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg |= val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg &= ~val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
static inline bool qphy_checkbits(const void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
return (reg & val) == val;
}
#endif

View File

@ -218,30 +218,6 @@ struct qcom_qmp {
int init_count;
};
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg |= val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg &= ~val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
/* list of clocks required by phy */
static const char * const msm8996_phy_clk_l[] = {
"aux", "cfg_ahb", "ref",

File diff suppressed because it is too large Load Diff

View File

@ -2417,6 +2417,50 @@ static const struct qmp_phy_init_tbl sdx65_qmp_pcie_pcs_lane1_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_LANE1_INSIG_MX_CTRL2, 0x00),
};
static const struct qmp_phy_init_tbl shikra_pcie_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x18),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_ENABLE1, 0x10),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TRIM, 0xf),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_EN, 0x1),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x0),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER1, 0xff),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER2, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x6),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_IVCO, 0xf),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x0),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x1),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORE_CLK_EN, 0x20),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORECLK_DIV, 0xa),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_RESETSM_CNTRL, 0x20),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0x9),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0xa),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x82),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x3),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x55),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x55),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x0),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0xd),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x35),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYS_CLK_CTRL, 0x2),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_BUF_ENABLE, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE0, 0x4),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE0, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE0, 0x30),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN1_MODE0, 0x0),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CTRL_BY_PSM, 0x1),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0xa),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_EN_CENTER, 0x1),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER1, 0x31),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER2, 0x1),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER1, 0x2),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER2, 0x0),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE1, 0x2f),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE2, 0x19),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_EP_DIV, 0x19),
};
static const struct qmp_phy_init_tbl sm8450_qmp_gen3_pcie_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_SELECT, 0x34),
@ -4044,38 +4088,6 @@ struct qmp_pcie {
struct clk_fixed_rate aux_clk_fixed;
};
static bool qphy_checkbits(const void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
return (reg & val) == val;
}
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg |= val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg &= ~val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
/* list of clocks required by phy */
static const char * const qmp_pciephy_clk_l[] = {
"aux", "cfg_ahb", "ref", "refgen", "rchng", "phy_aux",
@ -4643,6 +4655,32 @@ static const struct qmp_phy_cfg sdm845_qhp_pciephy_cfg = {
.phy_status = PHYSTATUS,
};
static const struct qmp_phy_cfg shikra_pciephy_cfg = {
.lanes = 1,
.offsets = &qmp_pcie_offsets_v2,
.tbls = {
.serdes = shikra_pcie_serdes_tbl,
.serdes_num = ARRAY_SIZE(shikra_pcie_serdes_tbl),
.tx = qcs615_pcie_tx_tbl,
.tx_num = ARRAY_SIZE(qcs615_pcie_tx_tbl),
.rx = qcs615_pcie_rx_tbl,
.rx_num = ARRAY_SIZE(qcs615_pcie_rx_tbl),
.pcs = qcs615_pcie_pcs_tbl,
.pcs_num = ARRAY_SIZE(qcs615_pcie_pcs_tbl),
},
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = pciephy_v2_regs_layout,
.pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
.phy_status = PHYSTATUS,
};
static const struct qmp_phy_cfg sm8250_qmp_gen3x1_pciephy_cfg = {
.lanes = 1,
@ -6444,6 +6482,9 @@ static const struct of_device_id qmp_pcie_of_match_table[] = {
}, {
.compatible = "qcom,sdx65-qmp-gen4x2-pcie-phy",
.data = &sdx65_qmp_pciephy_cfg,
}, {
.compatible = "qcom,shikra-qmp-gen2x1-pcie-phy",
.data = &shikra_pciephy_cfg,
}, {
.compatible = "qcom,sm8150-qmp-gen3x1-pcie-phy",
.data = &sm8250_qmp_gen3x1_pciephy_cfg,

View File

@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef QCOM_PHY_QMP_PCS_PCIE_V6_40_H_
#define QCOM_PHY_QMP_PCS_PCIE_V6_40_H_
/* PCS COM — power, start, clocks */
#define QPHY_PCIE_V6_40_PCS_COM_POWER_STATE_CONFIG2 0x06c
#define QPHY_PCIE_V6_40_PCS_COM_POWER_STATE_CONFIG5 0x078
#define QPHY_PCIE_V6_40_PCS_COM_POWER_STATE_CONFIG6 0x07c
#define QPHY_PCIE_V6_40_PCS_COM_POWER_STATE_CONFIG7 0x080
#define QPHY_PCIE_V6_40_PCS_COM_MULTIPHY_CONFIG2 0x088
#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG2 0x0a0
#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG3 0x0a4
#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG5 0x0ac
#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG6 0x0b0
#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG7 0x0b4
#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG8 0x0b8
#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG10 0x0c0
#define QPHY_PCIE_V6_40_PCS_COM_ENDPOINT_REFCLK_DRIVE 0x0c8
#define QPHY_PCIE_V6_40_PCS_COM_RX_SIGDET_LVL 0x0ec
#define QPHY_PCIE_V6_40_PCS_COM_DIVTX_CLK_DCC_CAL_CONFIG2 0x0f8
#define QPHY_PCIE_V6_40_PCS_COM_DIVTX_CLK_DCC_CAL_CONFIG3 0x0fc
#define QPHY_PCIE_V6_40_PCS_COM_DIVTX_CLK_DCC_CAL_CONFIG4 0x100
#define QPHY_PCIE_V6_40_PCS_COM_OSC_DTCT_ACTIONS 0x194
#define QPHY_PCIE_V6_40_PCS_COM_LOCK_DETECT_CONFIG1 0x1bc
#define QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG1 0x1dc
#define QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG2 0x1e0
#define QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG3 0x1e4
#define QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG7 0x1f4
#define QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG8 0x1f8
/* PCS COM — EQ config */
#define QPHY_PCIE_V6_40_PCS_COM_EQ_CONFIG1 0x380
#define QPHY_PCIE_V6_40_PCS_COM_EQ_CONFIG2 0x384
#define QPHY_PCIE_V6_40_PCS_COM_G345_EQ_CONFIG1 0x38c
#define QPHY_PCIE_V6_40_PCS_COM_G345_EQ_CONFIG3 0x394
#define QPHY_PCIE_V6_40_PCS_COM_G3_EQ_CONFIG1 0x3a4
#define QPHY_PCIE_V6_40_PCS_COM_G3_EQ_CONFIG5 0x3b4
#define QPHY_PCIE_V6_40_PCS_COM_G3_EQ_CONFIG7 0x3bc
#define QPHY_PCIE_V6_40_PCS_COM_G4_EQ_CONFIG1 0x3c8
#define QPHY_PCIE_V6_40_PCS_COM_G4_EQ_CONFIG5 0x3d8
#define QPHY_PCIE_V6_40_PCS_COM_G4_EQ_CONFIG7 0x3e0
#define QPHY_PCIE_V6_40_PCS_COM_G5_EQ_CONFIG1 0x3ec
#define QPHY_PCIE_V6_40_PCS_COM_G5_EQ_CONFIG5 0x3fc
#define QPHY_PCIE_V6_40_PCS_COM_G5_EQ_CONFIG7 0x404
#define QPHY_PCIE_V6_40_PCS_COM_TEST_CONTROL1 0x468
/* PCS LANEZ — output signal mux control */
#define QPHY_PCIE_V6_40_PCS_LANEZ_OUTSIG_MX_CTRL2 0x0e0
#define QPHY_PCIE_V6_40_PCS_LANEZ_OUTSIG_MX_CTRL5 0x0f8
#endif /* QCOM_PHY_QMP_PCS_PCIE_V6_40_H_ */

View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef QCOM_PHY_QMP_PCS_V6_40_H_
#define QCOM_PHY_QMP_PCS_V6_40_H_
#define QPHY_V6_40_PCS_STATUS1 0x010
#define QPHY_V6_40_PCS_START_CONTROL 0x05c
#define QPHY_V6_40_PCS_SW_RESET 0x060
#define QPHY_V6_40_PCS_POWER_DOWN_CONTROL 0x064
#endif /* QCOM_PHY_QMP_PCS_V6_40_H_ */

View File

@ -7,7 +7,7 @@
#define QCOM_PHY_QMP_PCS_V8_50_H_
#define QPHY_V8_50_PCS_STATUS1 0x010
#define QPHY_V8_50_PCS_START_CONTROL 0x05c
#define QPHY_V8_50_PCS_POWER_DOWN_CONTROL 0x64
#define QPHY_V8_50_PCS_START_CONTROL 0x05c
#define QPHY_V8_50_PCS_POWER_DOWN_CONTROL 0x64
#endif

View File

@ -0,0 +1,94 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef QCOM_PHY_QMP_QSERDES_COM_PCIE_V6_40_H_
#define QCOM_PHY_QMP_QSERDES_COM_PCIE_V6_40_H_
#define QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE1_MODE1 0x000
#define QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE2_MODE1 0x004
#define QSERDES_PCIE_V6_40_COM_CLK_EP_DIV_MODE1 0x00c
#define QSERDES_PCIE_V6_40_COM_CP_CTRL_MODE1 0x010
#define QSERDES_PCIE_V6_40_COM_PLL_RCTRL_MODE1 0x014
#define QSERDES_PCIE_V6_40_COM_PLL_CCTRL_MODE1 0x018
#define QSERDES_PCIE_V6_40_COM_CORECLK_DIV_MODE1 0x01c
#define QSERDES_PCIE_V6_40_COM_LOCK_CMP1_MODE1 0x020
#define QSERDES_PCIE_V6_40_COM_LOCK_CMP2_MODE1 0x024
#define QSERDES_PCIE_V6_40_COM_DEC_START_MODE1 0x028
#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START1_MODE1 0x030
#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START2_MODE1 0x034
#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START3_MODE1 0x038
#define QSERDES_PCIE_V6_40_COM_HSCLK_SEL_1 0x03c
#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x050
#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x054
#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x058
#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x05c
#define QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE1_MODE0 0x060
#define QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE2_MODE0 0x064
#define QSERDES_PCIE_V6_40_COM_CLK_EP_DIV_MODE0 0x06c
#define QSERDES_PCIE_V6_40_COM_CP_CTRL_MODE0 0x070
#define QSERDES_PCIE_V6_40_COM_PLL_RCTRL_MODE0 0x074
#define QSERDES_PCIE_V6_40_COM_PLL_CCTRL_MODE0 0x078
#define QSERDES_PCIE_V6_40_COM_CORECLK_DIV_MODE0 0x07c
#define QSERDES_PCIE_V6_40_COM_LOCK_CMP1_MODE0 0x080
#define QSERDES_PCIE_V6_40_COM_LOCK_CMP2_MODE0 0x084
#define QSERDES_PCIE_V6_40_COM_DEC_START_MODE0 0x088
#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START1_MODE0 0x090
#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START2_MODE0 0x094
#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START3_MODE0 0x098
#define QSERDES_PCIE_V6_40_COM_HSCLK_HS_SWITCH_SEL_1 0x09c
#define QSERDES_PCIE_V6_40_COM_BG_TIMER 0x0bc
#define QSERDES_PCIE_V6_40_COM_SSC_EN_CENTER 0x0c0
#define QSERDES_PCIE_V6_40_COM_SSC_PER1 0x0cc
#define QSERDES_PCIE_V6_40_COM_SSC_PER2 0x0d0
#define QSERDES_PCIE_V6_40_COM_POST_DIV_MUX 0x0d8
#define QSERDES_PCIE_V6_40_COM_BIAS_EN_CLKBUFLR_EN 0x0dc
#define QSERDES_PCIE_V6_40_COM_CLK_ENABLE1 0x0e0
#define QSERDES_PCIE_V6_40_COM_SYS_CLK_CTRL 0x0e4
#define QSERDES_PCIE_V6_40_COM_PLL_IVCO 0x0f4
#define QSERDES_PCIE_V6_40_COM_CMN_IETRIM 0x0fc
#define QSERDES_PCIE_V6_40_COM_SYSCLK_EN_SEL 0x110
#define QSERDES_PCIE_V6_40_COM_LOCK_CMP_EN 0x120
#define QSERDES_PCIE_V6_40_COM_LOCK_CMP_CFG 0x124
#define QSERDES_PCIE_V6_40_COM_VCO_TUNE_CTRL 0x13c
#define QSERDES_PCIE_V6_40_COM_VCO_TUNE_MAP 0x140
#define QSERDES_PCIE_V6_40_COM_CLK_SELECT 0x164
#define QSERDES_PCIE_V6_40_COM_CORE_CLK_EN 0x170
#define QSERDES_PCIE_V6_40_COM_CMN_CONFIG_1 0x174
#define QSERDES_PCIE_V6_40_COM_CMN_MISC1 0x188
#define QSERDES_PCIE_V6_40_COM_CMN_MODE 0x18c
#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_HSCLK_SEL_1 0x1a0
#define QSERDES_PCIE_V6_40_COM_VCO_DC_LEVEL_CTRL 0x19c
#define QSERDES_PCIE_V6_40_COM_HSCLK_SEL_2 0x21c
#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE1_MODE2 0x220
#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE2_MODE2 0x224
#define QSERDES_PCIE_V6_40_COM_HSCLK_HS_SWITCH_SEL_2 0x228
#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_HSCLK_SEL_2 0x230
#define QSERDES_PCIE_V6_40_COM_CMN_CONFIG_2 0x22c
#define QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE1_MODE2 0x1cc
#define QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE2_MODE2 0x1d0
#define QSERDES_PCIE_V6_40_COM_CP_CTRL_MODE2 0x1dc
#define QSERDES_PCIE_V6_40_COM_PLL_RCTRL_MODE2 0x1e0
#define QSERDES_PCIE_V6_40_COM_PLL_CCTRL_MODE2 0x1e4
#define QSERDES_PCIE_V6_40_COM_CORECLK_DIV_MODE2 0x1e8
#define QSERDES_PCIE_V6_40_COM_LOCK_CMP1_MODE2 0x1ec
#define QSERDES_PCIE_V6_40_COM_LOCK_CMP2_MODE2 0x1f0
#define QSERDES_PCIE_V6_40_COM_DEC_START_MODE2 0x1f4
#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START1_MODE2 0x1fc
#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START2_MODE2 0x200
#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START3_MODE2 0x204
#define QSERDES_PCIE_V6_40_COM_DCC_CAL_1 0x2d8
#define QSERDES_PCIE_V6_40_COM_DCC_CAL_2 0x2dc
#define QSERDES_PCIE_V6_40_COM_DCC_CAL_3 0x2e0
#define QSERDES_PCIE_V6_40_COM_PSM_CAL_EN 0x2f0
#define QSERDES_PCIE_V6_40_COM_CLK_FWD_CONFIG_1 0x2f4
#define QSERDES_PCIE_V6_40_COM_IP_CTRL_AND_DP_SEL 0x2fc
#define QSERDES_PCIE_V6_40_COM_PLL_SPARE_FOR_ECO 0x2b8
#define QSERDES_PCIE_V6_40_COM_PLL_IVCO_MODE1 0x0f8
#define QSERDES_PCIE_V6_40_COM_PLL_IVCO_MODE2 0x218
#define QSERDES_PCIE_V6_40_COM_DCC_CAL_7 0x300
#define QSERDES_PCIE_V6_40_COM_DCC_CAL_8 0x304
#define QSERDES_PCIE_V6_40_COM_DCC_CAL_9 0x308
#endif /* QCOM_PHY_QMP_QSERDES_COM_PCIE_V6_40_H_ */

View File

@ -0,0 +1,194 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef QCOM_PHY_QMP_QSERDES_TXRXZ_PCIE_V6_40_H_
#define QCOM_PHY_QMP_QSERDES_TXRXZ_PCIE_V6_40_H_
/* Lane mode / LDO */
#define QSERDES_PCIE_V6_40_TXRXZ_TOP_LDO_CODE_CTRL1 0x070
#define QSERDES_PCIE_V6_40_TXRXZ_TOP_LDO_CODE_CTRL2 0x074
#define QSERDES_PCIE_V6_40_TXRXZ_TOP_LDO_CODE_CTRL3 0x078
#define QSERDES_PCIE_V6_40_TXRXZ_TOP_LDO_CODE_CTRL4 0x07c
#define QSERDES_PCIE_V6_40_TXRXZ_LANE_MODE_1 0x094
#define QSERDES_PCIE_V6_40_TXRXZ_LANE_MODE_2 0x098
#define QSERDES_PCIE_V6_40_TXRXZ_LANE_MODE_3 0x09c
#define QSERDES_PCIE_V6_40_TXRXZ_LANE_MODE_4 0x0a0
#define QSERDES_PCIE_V6_40_TXRXZ_RESTRIM_VREF_SEL 0x0bc
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B0 0x0f4
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B1 0x0f8
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B2 0x0fc
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B3 0x100
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B4 0x104
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B5 0x108
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B6 0x10c
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B7 0x110
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B0 0x114
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B1 0x118
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B2 0x11c
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B3 0x120
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B4 0x124
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B5 0x128
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B6 0x12c
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B7 0x130
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B0 0x134
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B1 0x138
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B2 0x13c
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B3 0x140
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B4 0x144
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B5 0x148
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B6 0x14c
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B7 0x150
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B0 0x154
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B1 0x158
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B2 0x15c
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B3 0x160
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B4 0x164
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B5 0x168
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B6 0x16c
#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B7 0x170
/* VCO / KVCO calibration */
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE0 0x1b4
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE0 0x1b8
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE1 0x1bc
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE1 0x1c0
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE2 0x1c4
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE2 0x1c8
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE3 0x1cc
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE3 0x1d0
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE4 0x1d4
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE4 0x1d8
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCTRL_RATE_0_1 0x1dc
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCTRL_RATE_2_3 0x1e0
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_INIT_RATE_0_1 0x1e8
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_INIT_RATE_2_3 0x1ec
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_INIT_RATE_4 0x1f0
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_CODE_OVRD_RATE0 0x1f4
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_CODE_OVRD_RATE1 0x1f8
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_CODE_OVRD_RATE2 0x1fc
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE0 0x220
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF2_RATE0 0x224
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE1 0x228
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF2_RATE1 0x22c
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE2 0x230
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF2_RATE2 0x234
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE3 0x238
#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE4 0x240
#define QSERDES_PCIE_V6_40_TXRXZ_KP_CODE_OVRD_RATE_2_3 0x250
#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE0 0x258
#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE1 0x260
#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE2 0x268
#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE3 0x270
#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE4 0x278
#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE0 0x280
#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE1 0x284
#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE2 0x288
#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE3 0x28c
#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE4 0x290
#define QSERDES_PCIE_V6_40_TXRXZ_RX_SUMMER_CAL_SPD_MODE_RATE_0123 0x2a8
#define QSERDES_PCIE_V6_40_TXRXZ_RX_IVCM_CAL_CTRL2 0x2c8
#define QSERDES_PCIE_V6_40_TXRXZ_RX_IVCM_CAL_CTRL3 0x2cc
#define QSERDES_PCIE_V6_40_TXRXZ_RX_IVCM_CAL_CTRL4 0x2d0
/* Signal detect */
#define QSERDES_PCIE_V6_40_TXRXZ_SIGDET_ENABLES 0x31c
#define QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE0 0x33c
#define QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE1 0x340
#define QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE2 0x344
#define QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE3 0x348
#define QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE4 0x34c
/* CDR */
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOCK_CTRL 0x364
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE0 0x368
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE1 0x36c
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE2 0x370
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE3 0x374
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE4 0x378
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE0 0x37c
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE1 0x380
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE2 0x384
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE3 0x388
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE4 0x38c
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_FLL_DIV_RATIO_RATE_0123 0x390
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_CCODE_RATE_01 0x398
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_CCODE_RATE_23 0x39c
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_CCODE_RATE4 0x3a0
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FAST_RATE_0_1 0x3a4
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FAST_RATE_2_3 0x3a8
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FAST_RATE4 0x3ac
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FLL_RATE_0_1 0x3b0
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FLL_RATE_2_3 0x3b4
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FLL_RATE4 0x3b8
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_PLL_RATE_0_1 0x3bc
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_PLL_RATE_2_3 0x3c0
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_PLL_RATE4 0x3c4
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CAP_CODE_RATE_0123 0x3c8
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_TYPE_CONFIG 0x3d0
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_EN_LOWFREQ 0x3d4
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_FUNC_CTRL 0x3dc
/* Rescode / residuals */
#define QSERDES_PCIE_V6_40_TXRXZ_RES_CODE_LANE_OFFSET_RX 0x3f4
/* VGA / GM calibration */
#define QSERDES_PCIE_V6_40_TXRXZ_GM_CAL_RES_RATE0_1 0x404
#define QSERDES_PCIE_V6_40_TXRXZ_GM_CAL_RES_RATE2_3 0x408
#define QSERDES_PCIE_V6_40_TXRXZ_GM_CAL_RES_RATE4 0x40c
/* RX equalizer */
#define QSERDES_PCIE_V6_40_TXRXZ_RX_EQU_ADAPTOR_CNTRL3 0x454
#define QSERDES_PCIE_V6_40_TXRXZ_RX_EQU_ADAPTOR_CNTRL4 0x458
#define QSERDES_PCIE_V6_40_TXRXZ_RX_EQU_ADAPTOR_CNTRL5 0x45c
#define QSERDES_PCIE_V6_40_TXRXZ_CTLE_POST_CAL_OFFSET_RATE_0_1_2 0x478
#define QSERDES_PCIE_V6_40_TXRXZ_VGA_CAL_MAN_VAL_RATE0_1 0x490
#define QSERDES_PCIE_V6_40_TXRXZ_VGA_CAL_MAN_VAL_RATE2_3 0x494
#define QSERDES_PCIE_V6_40_TXRXZ_VGA_CAL_MAN_VAL_RATE4 0x498
#define QSERDES_PCIE_V6_40_TXRXZ_VTHRESH_CAL_MAN_VAL_RATE4 0x4bc
/* DFE */
#define QSERDES_PCIE_V6_40_TXRXZ_DFE_TAP1_DAC_ENABLE 0x50c
#define QSERDES_PCIE_V6_40_TXRXZ_DFE_TAP2_DAC_ENABLE 0x510
#define QSERDES_PCIE_V6_40_TXRXZ_DFE_TAP345_DAC_ENABLE 0x514
#define QSERDES_PCIE_V6_40_TXRXZ_DFE_TAP67_DAC_ENABLE 0x518
#define QSERDES_PCIE_V6_40_TXRXZ_PHPRE_CTRL 0x51c
#define QSERDES_PCIE_V6_40_TXRXZ_TX_ADAPT_POST_THRESH1 0x52c
#define QSERDES_PCIE_V6_40_TXRXZ_TX_ADAPT_POST_THRESH2 0x530
#define QSERDES_PCIE_V6_40_TXRXZ_TX_ADPT_CTRL 0x53c
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_IQTUNE_CTRL 0x548
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_IQTUNE_MAN_INDEX 0x550
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_IQTUNE_ANA_CTRL 0x584
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_IQTUNE_DIV2_CTRL_RATE0123 0x58c
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CAP_CODE_OVRD_MUXES 0x5ac
#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CAP_CODE_RATE4 0x3cc
/* Clock buffer / LDO stall control (used by bifurcated pre/post-pwrup sequences) */
#define QSERDES_PCIE_V6_40_TXRXZ_CLKBUF_ENABLE 0x04c
#define QSERDES_PCIE_V6_40_TXRXZ_PRE_STALL_LDO_BOOST_EN 0x06c
#define QSERDES_PCIE_V6_40_TXRXZ_LDO_TIMER_CTRL 0x0d8
/* Restrim / VGA / signal-detect (v2-only) */
#define QSERDES_PCIE_V6_40_TXRXZ_RESTRIM_CAL_CTRL 0x0b0
#define QSERDES_PCIE_V6_40_TXRXZ_RESTRIM_POST_CAL_OFFSET 0x0b8
#define QSERDES_PCIE_V6_40_TXRXZ_SIGDET_CNTRL 0x320
#define QSERDES_PCIE_V6_40_TXRXZ_SIGDET_LVL 0x324
#define QSERDES_PCIE_V6_40_TXRXZ_SIGDET_CAL_CTRL1 0x32c
#define QSERDES_PCIE_V6_40_TXRXZ_VGA_CAL_CNTRL1 0x488
/* BLW / VCO coarse-tune bands (v2-only) */
#define QSERDES_PCIE_V6_40_TXRXZ_BLW_CTRL 0x594
#define QSERDES_PCIE_V6_40_TXRXZ_BLW_MAN_VAL_RATE3 0x5a4
#define QSERDES_PCIE_V6_40_TXRXZ_BLW_MAN_VAL_RATE4 0x5a8
#define QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE0 0x5c4
#define QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE1 0x5c8
#define QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE2 0x5cc
#define QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE3 0x5d0
#define QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE4 0x5d4
/* Analog backup control (v2-only) */
#define QSERDES_PCIE_V6_40_TXRXZ_DIG_BKUP_CTRL15 0x7e8
#define QSERDES_PCIE_V6_40_TXRXZ_DIG_BKUP_CTRL16 0x7ec
#endif /* QCOM_PHY_QMP_QSERDES_TXRXZ_PCIE_V6_40_H_ */

View File

@ -1211,30 +1211,6 @@ struct qmp_ufs {
u32 submode;
};
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg |= val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg &= ~val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
/* Regulator bulk data with load values for specific configurations */
static const struct regulator_bulk_data milos_ufsphy_vreg_l[] = {
{ .supply = "vdda-phy", .init_load_uA = 140120 },

View File

@ -1441,30 +1441,6 @@ struct qmp_usb {
struct clk_fixed_rate pipe_clk_fixed;
};
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg |= val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg &= ~val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
/* list of clocks required by phy */
static const char * const qmp_usb_phy_clk_l[] = {
"aux", "cfg_ahb", "ref", "com_aux",

View File

@ -474,30 +474,6 @@ struct qmp_usbc {
enum typec_orientation orientation;
};
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg |= val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg &= ~val;
writel(reg, base + offset);
/* ensure that above write is through */
readl(base + offset);
}
/* list of clocks required by phy */
static const char * const qmp_usbc_phy_clk_l[] = {
"aux", "cfg_ahb", "ref", "com_aux",

View File

@ -1022,7 +1022,8 @@ static int rtk_usb2phy_probe(struct platform_device *pdev)
return -ENOMEM;
rtk_phy->dev = &pdev->dev;
rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL);
rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*rtk_phy->phy_cfg),
GFP_KERNEL);
if (!rtk_phy->phy_cfg)
return -ENOMEM;

View File

@ -576,7 +576,8 @@ static int rtk_usb3phy_probe(struct platform_device *pdev)
return -ENOMEM;
rtk_phy->dev = &pdev->dev;
rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL);
rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*rtk_phy->phy_cfg),
GFP_KERNEL);
if (!rtk_phy->phy_cfg)
return -ENOMEM;

View File

@ -136,8 +136,10 @@ config PHY_ROCKCHIP_USBDP
tristate "Rockchip USBDP COMBO PHY Driver"
depends on ARCH_ROCKCHIP && OF
depends on TYPEC
depends on DRM || DRM=n
select GENERIC_PHY
select USB_COMMON
select DRM_AUX_BRIDGE if DRM_BRIDGE
help
Enable this to support the Rockchip USB3.0/DP combo PHY with
Samsung IP block. This is required for USB3 support on RK3588.

View File

@ -415,6 +415,17 @@ static const struct dphy_drv_data rk3568_mipidphy_drv_data = {
.resets_num = ARRAY_SIZE(rk3368_reset_names),
};
static const struct dphy_drv_data rk3576_mipidphy_drv_data = {
.pwrctl_offset = -1,
.ths_settle_offset = RK3568_CSIDPHY_CLK_WR_THS_SETTLE,
.calib_offset = RK3568_CSIDPHY_CLK_CALIB_EN,
.hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges,
.num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges),
.grf_regs = rk3588_grf_dphy_regs,
.resets = rk3368_reset_names,
.resets_num = ARRAY_SIZE(rk3368_reset_names),
};
static const struct dphy_drv_data rk3588_mipidphy_drv_data = {
.pwrctl_offset = -1,
.ths_settle_offset = RK3568_CSIDPHY_CLK_WR_THS_SETTLE,
@ -447,6 +458,10 @@ static const struct of_device_id rockchip_inno_csidphy_match_id[] = {
.compatible = "rockchip,rk3568-csi-dphy",
.data = &rk3568_mipidphy_drv_data,
},
{
.compatible = "rockchip,rk3576-csi-dphy",
.data = &rk3576_mipidphy_drv_data,
},
{
.compatible = "rockchip,rk3588-csi-dphy",
.data = &rk3588_mipidphy_drv_data,

View File

@ -1044,7 +1044,7 @@ static int inno_hdmi_phy_clk_register(struct inno_hdmi_phy *inno)
{
struct device *dev = inno->dev;
struct device_node *np = dev->of_node;
struct clk_init_data init;
struct clk_init_data init = {};
const char *parent_name;
int ret;

View File

@ -400,7 +400,7 @@ static int
rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
{
struct device_node *node = rphy->dev->of_node;
struct clk_init_data init;
struct clk_init_data init = {};
struct clk *refclk = NULL;
const char *clk_name;
int i;

View File

@ -106,6 +106,9 @@
#define RK3568_PHYREG18 0x44
#define RK3568_PHYREG18_PLL_LOOP 0x32
#define RK3568_PHYREG26 0x64
#define RK3568_PHYREG26_FORCE_RTERM_DET_RDY BIT(5)
#define RK3568_PHYREG30 0x74
#define RK3568_PHYREG30_GATE_TX_PCK_SEL BIT(7)
#define RK3568_PHYREG30_GATE_TX_PCK_DLY_PLL_OFF BIT(7)
@ -194,6 +197,7 @@ struct rockchip_combphy_cfg {
unsigned int num_phys;
unsigned int phy_ids[3];
const struct rockchip_combphy_grfcfg *grfcfg;
bool force_rxterm_det_rdy;
int (*combphy_cfg)(struct rockchip_combphy_priv *priv);
};
@ -265,6 +269,17 @@ static int rockchip_combphy_init(struct phy *phy)
switch (priv->type) {
case PHY_TYPE_PCIE:
/*
* Hardware Errata: TX fails to detect peer RX termination.
* Some PHY revisions may fail to detect remote RX's RTERM
* (receiver termination resistor) under certain critical
* temperature conditions. Set force rterm detect ready to
* fix it.
*/
if (priv->cfg->force_rxterm_det_rdy)
rockchip_combphy_updatel(priv, RK3568_PHYREG26_FORCE_RTERM_DET_RDY,
RK3568_PHYREG26_FORCE_RTERM_DET_RDY, RK3568_PHYREG26);
fallthrough;
case PHY_TYPE_USB3:
case PHY_TYPE_SATA:
case PHY_TYPE_SGMII:
@ -801,6 +816,7 @@ static const struct rockchip_combphy_cfg rk3562_combphy_cfgs = {
},
.grfcfg = &rk3562_combphy_grfcfgs,
.combphy_cfg = rk3562_combphy_cfg,
.force_rxterm_det_rdy = true,
};
static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv)
@ -998,6 +1014,7 @@ static const struct rockchip_combphy_cfg rk3568_combphy_cfgs = {
},
.grfcfg = &rk3568_combphy_grfcfgs,
.combphy_cfg = rk3568_combphy_cfg,
.force_rxterm_det_rdy = true,
};
static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv)
@ -1219,6 +1236,7 @@ static const struct rockchip_combphy_cfg rk3576_combphy_cfgs = {
},
.grfcfg = &rk3576_combphy_grfcfgs,
.combphy_cfg = rk3576_combphy_cfg,
.force_rxterm_det_rdy = true,
};
static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv)
@ -1391,6 +1409,7 @@ static const struct rockchip_combphy_cfg rk3588_combphy_cfgs = {
},
.grfcfg = &rk3588_combphy_grfcfgs,
.combphy_cfg = rk3588_combphy_cfg,
.force_rxterm_det_rdy = true,
};
static const struct of_device_id rockchip_combphy_of_match[] = {

View File

@ -1610,16 +1610,16 @@ static int samsung_mipi_dcphy_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(samsung->grf_apb_rst),
"Failed to get system grf_apb_rst control\n");
ret = devm_pm_runtime_enable(dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
samsung->phy = devm_phy_create(dev, NULL, &samsung_mipi_dcphy_ops);
if (IS_ERR(samsung->phy))
return dev_err_probe(dev, PTR_ERR(samsung->phy), "Failed to create MIPI DC-PHY\n");
phy_set_drvdata(samsung->phy, samsung);
ret = devm_pm_runtime_enable(dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
phy_provider = devm_of_phy_provider_register(dev, samsung_mipi_dcphy_xlate);
if (IS_ERR(phy_provider))
return dev_err_probe(dev, PTR_ERR(phy_provider),

View File

@ -333,6 +333,7 @@
#define FRL_3G3L_RATE 900000000
#define FRL_6G3L_RATE 1800000000
#define FRL_8G4L_RATE 3200000000
#define FRL_FFE_MAX_LEVEL 3
enum dp_link_rate {
DP_BW_RBR,
@ -466,6 +467,16 @@ static const struct ropll_config rk_hdptx_tmds_ropll_cfg[] = {
{ 25175000ULL, 84, 84, 1, 1, 15, 1, 168, 1, 16, 4, 1, 1, },
};
static const struct ffe_config {
u8 pre_shoot;
u8 de_emphasis;
} rk_hdptx_frl_ffe_cfg[FRL_FFE_MAX_LEVEL + 1] = {
{ 0x3, 0x4 },
{ 0x3, 0x6 },
{ 0x3, 0x8 },
{ 0x3, 0x9 },
};
static const struct reg_sequence rk_hdptx_common_cmn_init_seq[] = {
REG_SEQ0(CMN_REG(0009), 0x0c),
REG_SEQ0(CMN_REG(000a), 0x83),
@ -1321,6 +1332,45 @@ static int rk_hdptx_tmds_ropll_mode_config(struct rk_hdptx_phy *hdptx)
return rk_hdptx_post_enable_lane(hdptx);
}
static int rk_hdptx_frl_ffe_config(struct rk_hdptx_phy *hdptx, u8 ffe_level)
{
u8 val;
if (ffe_level > FRL_FFE_MAX_LEVEL)
return -EINVAL;
val = rk_hdptx_frl_ffe_cfg[ffe_level].pre_shoot;
regmap_update_bits(hdptx->regmap, LANE_REG(0305),
LN_TX_DRV_PRE_LVL_CTRL_MASK,
FIELD_PREP(LN_TX_DRV_PRE_LVL_CTRL_MASK, val));
regmap_update_bits(hdptx->regmap, LANE_REG(0405),
LN_TX_DRV_PRE_LVL_CTRL_MASK,
FIELD_PREP(LN_TX_DRV_PRE_LVL_CTRL_MASK, val));
regmap_update_bits(hdptx->regmap, LANE_REG(0505),
LN_TX_DRV_PRE_LVL_CTRL_MASK,
FIELD_PREP(LN_TX_DRV_PRE_LVL_CTRL_MASK, val));
regmap_update_bits(hdptx->regmap, LANE_REG(0605),
LN_TX_DRV_PRE_LVL_CTRL_MASK,
FIELD_PREP(LN_TX_DRV_PRE_LVL_CTRL_MASK, val));
val = rk_hdptx_frl_ffe_cfg[ffe_level].de_emphasis;
regmap_update_bits(hdptx->regmap, LANE_REG(0304),
LN_TX_DRV_POST_LVL_CTRL_MASK,
FIELD_PREP(LN_TX_DRV_POST_LVL_CTRL_MASK, val));
regmap_update_bits(hdptx->regmap, LANE_REG(0404),
LN_TX_DRV_POST_LVL_CTRL_MASK,
FIELD_PREP(LN_TX_DRV_POST_LVL_CTRL_MASK, val));
regmap_update_bits(hdptx->regmap, LANE_REG(0504),
LN_TX_DRV_POST_LVL_CTRL_MASK,
FIELD_PREP(LN_TX_DRV_POST_LVL_CTRL_MASK, val));
regmap_update_bits(hdptx->regmap, LANE_REG(0604),
LN_TX_DRV_POST_LVL_CTRL_MASK,
FIELD_PREP(LN_TX_DRV_POST_LVL_CTRL_MASK, val));
return 0;
}
static void rk_hdptx_dp_reset(struct rk_hdptx_phy *hdptx)
{
reset_control_assert(hdptx->rsts[RST_LANE].rstc);
@ -1735,6 +1785,13 @@ static int rk_hdptx_phy_verify_hdmi_config(struct rk_hdptx_phy *hdptx,
unsigned long long frl_rate = 100000000ULL * hdmi_in->frl.lanes *
hdmi_in->frl.rate_per_lane;
if (hdmi_in->frl.set_ffe_level) {
if (hdmi_in->frl.ffe_level > FRL_FFE_MAX_LEVEL)
return -EINVAL;
return 0;
}
switch (hdmi_in->frl.rate_per_lane) {
case 3:
case 6:
@ -2081,11 +2138,18 @@ static int rk_hdptx_phy_configure(struct phy *phy, union phy_configure_opts *opt
if (ret) {
dev_err(hdptx->dev, "invalid hdmi params for phy configure\n");
} else {
hdptx->pll_config_dirty = true;
if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL &&
opts->hdmi.frl.set_ffe_level) {
dev_dbg(hdptx->dev, "%s ffe_level=%u\n", __func__,
opts->hdmi.frl.ffe_level);
ret = rk_hdptx_frl_ffe_config(hdptx, opts->hdmi.frl.ffe_level);
} else {
hdptx->pll_config_dirty = true;
dev_dbg(hdptx->dev, "%s %s rate=%llu bpc=%u\n", __func__,
hdptx->hdmi_cfg.mode ? "FRL" : "TMDS",
hdptx->hdmi_cfg.rate, hdptx->hdmi_cfg.bpc);
dev_dbg(hdptx->dev, "%s %s rate=%llu bpc=%u\n", __func__,
hdptx->hdmi_cfg.mode ? "FRL" : "TMDS",
hdptx->hdmi_cfg.rate, hdptx->hdmi_cfg.bpc);
}
}
return ret;
@ -2346,14 +2410,16 @@ static int rk_hdptx_phy_clk_determine_rate(struct clk_hw *hw,
* to ensure rk_hdptx_phy_clk_set_rate() will be always invoked.
* Otherwise, restrict the rate according to the PHY link setup.
*/
if (hdptx->pll_config_dirty)
req->rate = 0;
else if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
req->rate = hdptx->hdmi_cfg.rate;
else
req->rate = DIV_ROUND_CLOSEST_ULL(hdptx->hdmi_cfg.rate * 8,
hdptx->hdmi_cfg.bpc);
if (hdptx->pll_config_dirty && req->rate == clk_hw_get_rate(hw))
req->rate = 0;
return 0;
}

View File

@ -200,9 +200,9 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
struct device_node *child)
{
struct rockchip_usb_phy *rk_phy;
struct clk_init_data init = {};
unsigned int reg_offset;
const char *clk_name;
struct clk_init_data init;
int err, i;
rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL);

View File

@ -6,12 +6,15 @@
* Copyright (C) 2024 Collabora Ltd
*/
#include <drm/bridge/aux-bridge.h>
#include <dt-bindings/phy/phy.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/hw_bitfield.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/mutex.h>
@ -20,6 +23,7 @@
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/string_choices.h>
#include <linux/usb/ch9.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
@ -74,7 +78,6 @@
#define TRSV_LN2_MON_RX_CDR_DONE_OFFSET 0x1b84 /* trsv_reg06E1 */
#define TRSV_LN2_MON_RX_CDR_LOCK_DONE BIT(0)
#define BIT_WRITEABLE_SHIFT 16
#define PHY_AUX_DP_DATA_POL_NORMAL 0
#define PHY_AUX_DP_DATA_POL_INVERT 1
#define PHY_LANE_MUX_USB 0
@ -103,8 +106,8 @@ struct rk_udphy_grf_reg {
#define _RK_UDPHY_GEN_GRF_REG(offset, mask, disable, enable) \
{\
offset, \
FIELD_PREP_CONST(mask, disable) | (mask << BIT_WRITEABLE_SHIFT), \
FIELD_PREP_CONST(mask, enable) | (mask << BIT_WRITEABLE_SHIFT), \
FIELD_PREP_WM16_CONST(mask, disable), \
FIELD_PREP_WM16_CONST(mask, enable), \
}
#define RK_UDPHY_GEN_GRF_REG(offset, bitend, bitstart, disable, enable) \
@ -126,7 +129,6 @@ struct rk_udphy_grf_cfg {
struct rk_udphy_vogrf_cfg {
/* vo-grf */
struct rk_udphy_grf_reg hpd_trigger;
u32 dp_lane_reg;
};
@ -170,7 +172,7 @@ struct rk_udphy {
/* PHY status management */
bool flip;
bool mode_change;
bool phy_needs_reinit;
u8 mode;
u8 status;
@ -184,14 +186,12 @@ struct rk_udphy {
u32 dp_lane_sel[4];
u32 dp_aux_dout_sel;
u32 dp_aux_din_sel;
bool dp_sink_hpd_sel;
bool dp_sink_hpd_cfg;
unsigned int link_rate;
unsigned int lanes;
u8 bw;
int id;
bool dp_in_use;
int dp_lanes;
/* PHY const config */
const struct rk_udphy_cfg *cfgs;
@ -349,7 +349,8 @@ static const struct reg_sequence rk_udphy_24m_refclk_cfg[] = {
{0x0a64, 0xa8}, {0x1a3c, 0xd0},
{0x1a44, 0xd0}, {0x1a48, 0x01},
{0x1a4c, 0x0d}, {0x1a54, 0xe0},
{0x1a5c, 0xe0}, {0x1a64, 0xa8}
{0x1a5c, 0xe0}, {0x1a64, 0xa8},
{0x00d4, 0x30}
};
static const struct reg_sequence rk_udphy_26m_refclk_cfg[] = {
@ -376,7 +377,7 @@ static const struct reg_sequence rk_udphy_26m_refclk_cfg[] = {
{0x0c30, 0x0e}, {0x0c48, 0x06},
{0x1c30, 0x0e}, {0x1c48, 0x06},
{0x028c, 0x18}, {0x0af0, 0x00},
{0x1af0, 0x00}
{0x1af0, 0x00}, {0x00d4, 0x33}
};
static const struct reg_sequence rk_udphy_init_sequence[] = {
@ -411,8 +412,8 @@ static const struct reg_sequence rk_udphy_init_sequence[] = {
{0x0070, 0x7d}, {0x0074, 0x68},
{0x0af4, 0x1a}, {0x1af4, 0x1a},
{0x0440, 0x3f}, {0x10d4, 0x08},
{0x20d4, 0x08}, {0x00d4, 0x30},
{0x0024, 0x6e},
{0x20d4, 0x08}, {0x0024, 0x6e},
{0x09c0, 0x0a}, {0x19c0, 0x0a}
};
static inline int rk_udphy_grfreg_write(struct regmap *base,
@ -426,11 +427,14 @@ static int rk_udphy_clk_init(struct rk_udphy *udphy, struct device *dev)
int i;
udphy->num_clks = devm_clk_bulk_get_all(dev, &udphy->clks);
if (udphy->num_clks < 1)
return -ENODEV;
if (udphy->num_clks < 0)
return udphy->num_clks;
/* used for configure phy reference clock frequency */
for (i = 0; i < udphy->num_clks; i++) {
if (!udphy->clks[i].id)
continue;
if (!strncmp(udphy->clks[i].id, "refclk", 6)) {
udphy->refclk = udphy->clks[i].clk;
break;
@ -448,11 +452,6 @@ static int rk_udphy_reset_assert_all(struct rk_udphy *udphy)
return reset_control_bulk_assert(udphy->num_rsts, udphy->rsts);
}
static int rk_udphy_reset_deassert_all(struct rk_udphy *udphy)
{
return reset_control_bulk_deassert(udphy->num_rsts, udphy->rsts);
}
static int rk_udphy_reset_deassert(struct rk_udphy *udphy, char *name)
{
struct reset_control_bulk_data *list = udphy->rsts;
@ -463,6 +462,8 @@ static int rk_udphy_reset_deassert(struct rk_udphy *udphy, char *name)
return reset_control_deassert(list[idx].rstc);
}
dev_err(udphy->dev, "failed to de-assert missing reset line: %s\n", name);
return -EINVAL;
}
@ -489,6 +490,8 @@ static void rk_udphy_u3_port_disable(struct rk_udphy *udphy, u8 disable)
const struct rk_udphy_cfg *cfg = udphy->cfgs;
const struct rk_udphy_grf_reg *preg;
dev_dbg(udphy->dev, "USB3 port %s\n", str_on_off(!disable));
preg = udphy->id ? &cfg->grfcfg.usb3otg1_cfg : &cfg->grfcfg.usb3otg0_cfg;
rk_udphy_grfreg_write(udphy->usbgrf, preg, disable);
}
@ -535,60 +538,31 @@ static void rk_udphy_usb_bvalid_enable(struct rk_udphy *udphy, u8 enable)
* <0 1> dpln0 dpln1 usbrx usbtx
* <2 3> usbrx usbtx dpln0 dpln1
* ---------------------------------------------------------------------------
* if 1 lane for dp function, 2 lane for usb function, define rockchip,dp-lane-mux = <x>;
* sample as follow:
* ---------------------------------------------------------------------------
* B11-B10 A2-A3 A11-A10 B2-B3
* rockchip,dp-lane-mux ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx)
* <0> dpln0 \ usbrx usbtx
* ---------------------------------------------------------------------------
*/
static void rk_udphy_dplane_select(struct rk_udphy *udphy)
static void rk_udphy_dp_lane_select(struct rk_udphy *udphy)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
u32 value = 0;
u32 value = FIELD_PREP_WM16(DP_LANE_SEL_ALL, 0);
int i;
switch (udphy->mode) {
case UDPHY_MODE_DP:
value |= 2 << udphy->dp_lane_sel[2] * 2;
value |= 3 << udphy->dp_lane_sel[3] * 2;
fallthrough;
for (i = 0; i < udphy->dp_lanes; i++)
value |= field_prep(DP_LANE_SEL_N(udphy->dp_lane_sel[i]), i);
case UDPHY_MODE_DP_USB:
value |= 0 << udphy->dp_lane_sel[0] * 2;
value |= 1 << udphy->dp_lane_sel[1] * 2;
break;
value |= FIELD_PREP_WM16(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel);
value |= FIELD_PREP_WM16(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel);
case UDPHY_MODE_USB:
break;
default:
break;
}
regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg,
((DP_AUX_DIN_SEL | DP_AUX_DOUT_SEL | DP_LANE_SEL_ALL) << 16) |
FIELD_PREP(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel) |
FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value);
regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg, value);
}
static int rk_udphy_dplane_get(struct rk_udphy *udphy)
{
int dp_lanes;
switch (udphy->mode) {
case UDPHY_MODE_DP:
dp_lanes = 4;
break;
case UDPHY_MODE_DP_USB:
dp_lanes = 2;
break;
case UDPHY_MODE_USB:
default:
dp_lanes = 0;
break;
}
return dp_lanes;
}
static void rk_udphy_dplane_enable(struct rk_udphy *udphy, int dp_lanes)
static void rk_udphy_dp_lane_enable(struct rk_udphy *udphy, int dp_lanes)
{
u32 val = 0;
int i;
@ -604,17 +578,49 @@ static void rk_udphy_dplane_enable(struct rk_udphy *udphy, int dp_lanes)
CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
}
static void rk_udphy_dp_hpd_event_trigger(struct rk_udphy *udphy, bool hpd)
static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
udphy->dp_sink_hpd_sel = true;
udphy->dp_sink_hpd_cfg = hpd;
if (!udphy->dp_in_use)
if (udphy->mode == mode)
return;
rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, hpd);
udphy->phy_needs_reinit = true;
udphy->mode = mode;
}
static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state)
{
u8 mode;
switch (state) {
case TYPEC_DP_STATE_C:
case TYPEC_DP_STATE_E:
udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
mode = UDPHY_MODE_DP;
udphy->dp_lanes = 4;
break;
case TYPEC_DP_STATE_D:
default:
if (udphy->flip) {
udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
} else {
udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
}
mode = UDPHY_MODE_DP_USB;
udphy->dp_lanes = 2;
break;
}
rk_udphy_mode_set(udphy, mode);
}
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
@ -624,10 +630,6 @@ static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
udphy->dp_lane_sel[1] = 1;
udphy->dp_lane_sel[2] = 3;
udphy->dp_lane_sel[3] = 2;
udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_INVERT;
udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_INVERT;
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 1);
@ -637,40 +639,42 @@ static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
udphy->dp_lane_sel[1] = 3;
udphy->dp_lane_sel[2] = 1;
udphy->dp_lane_sel[3] = 0;
udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_NORMAL;
udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_NORMAL;
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 1);
}
udphy->mode = UDPHY_MODE_DP_USB;
/* default to USB3 + DP as 4 lane USB is not supported */
rk_udphy_set_typec_state(udphy, TYPEC_DP_STATE_D);
}
static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
enum typec_orientation orien)
{
struct rk_udphy *udphy = typec_switch_get_drvdata(sw);
bool flipped = orien == TYPEC_ORIENTATION_REVERSE;
mutex_lock(&udphy->mutex);
guard(mutex)(&udphy->mutex);
if (orien == TYPEC_ORIENTATION_NONE) {
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0);
/* unattached */
rk_udphy_usb_bvalid_enable(udphy, false);
goto unlock_ret;
return 0;
}
udphy->flip = orien == TYPEC_ORIENTATION_REVERSE;
if (udphy->flip != flipped) {
dev_dbg(udphy->dev, "cable orientation changed, PHY re-init required.\n");
udphy->phy_needs_reinit = true;
}
udphy->flip = flipped;
rk_udphy_set_typec_default_mapping(udphy);
rk_udphy_usb_bvalid_enable(udphy, true);
unlock_ret:
mutex_unlock(&udphy->mutex);
return 0;
}
@ -783,6 +787,11 @@ static int rk_udphy_init(struct rk_udphy *udphy)
const struct rk_udphy_cfg *cfg = udphy->cfgs;
int ret;
dev_dbg(udphy->dev, "reinit PHY with USB3=%s and DP=%s (%u lanes) flipped=%s\n",
str_on_off(udphy->mode & UDPHY_MODE_USB),
str_on_off(udphy->mode & UDPHY_MODE_DP),
udphy->dp_lanes, str_yes_no(udphy->flip));
rk_udphy_reset_assert_all(udphy);
usleep_range(10000, 11000);
@ -793,8 +802,12 @@ static int rk_udphy_init(struct rk_udphy *udphy)
/* Step 1: power on pma and deassert apb rstn */
rk_udphy_grfreg_write(udphy->udphygrf, &cfg->grfcfg.low_pwrn, true);
rk_udphy_reset_deassert(udphy, "pma_apb");
rk_udphy_reset_deassert(udphy, "pcs_apb");
ret = rk_udphy_reset_deassert(udphy, "pma_apb");
if (ret)
goto assert_resets;
ret = rk_udphy_reset_deassert(udphy, "pcs_apb");
if (ret)
goto assert_resets;
/* Step 2: set init sequence and phy refclk */
ret = regmap_multi_reg_write(udphy->pma_regmap, rk_udphy_init_sequence,
@ -820,8 +833,11 @@ static int rk_udphy_init(struct rk_udphy *udphy)
FIELD_PREP(CMN_DP_LANE_EN_ALL, 0));
/* Step 4: deassert init rstn and wait for 200ns from datasheet */
if (udphy->mode & UDPHY_MODE_USB)
rk_udphy_reset_deassert(udphy, "init");
if (udphy->mode & UDPHY_MODE_USB) {
ret = rk_udphy_reset_deassert(udphy, "init");
if (ret)
goto assert_resets;
}
if (udphy->mode & UDPHY_MODE_DP) {
regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
@ -833,8 +849,14 @@ static int rk_udphy_init(struct rk_udphy *udphy)
/* Step 5: deassert cmn/lane rstn */
if (udphy->mode & UDPHY_MODE_USB) {
rk_udphy_reset_deassert(udphy, "cmn");
rk_udphy_reset_deassert(udphy, "lane");
ret = rk_udphy_reset_deassert(udphy, "cmn");
if (ret)
goto assert_resets;
ret = rk_udphy_reset_deassert(udphy, "lane");
if (ret)
goto assert_resets;
}
/* Step 6: wait for lock done of pll */
@ -853,6 +875,8 @@ static int rk_udphy_setup(struct rk_udphy *udphy)
{
int ret;
dev_dbg(udphy->dev, "enable PHY\n");
ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
if (ret) {
dev_err(udphy->dev, "failed to enable clk\n");
@ -871,6 +895,7 @@ static int rk_udphy_setup(struct rk_udphy *udphy)
static void rk_udphy_disable(struct rk_udphy *udphy)
{
dev_dbg(udphy->dev, "disable PHY\n");
clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks);
rk_udphy_reset_assert_all(udphy);
}
@ -886,7 +911,7 @@ static int rk_udphy_parse_lane_mux_data(struct rk_udphy *udphy)
return 0;
}
if (num_lanes != 2 && num_lanes != 4)
if (num_lanes != 1 && num_lanes != 2 && num_lanes != 4)
return dev_err_probe(udphy->dev, -EINVAL,
"invalid number of lane mux\n");
@ -912,36 +937,16 @@ static int rk_udphy_parse_lane_mux_data(struct rk_udphy *udphy)
}
udphy->mode = UDPHY_MODE_DP;
if (num_lanes == 2) {
udphy->dp_lanes = num_lanes;
if (num_lanes == 1 || num_lanes == 2) {
udphy->mode |= UDPHY_MODE_USB;
udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP);
udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP) ||
(udphy->lane_mux_sel[1] == PHY_LANE_MUX_DP);
}
return 0;
}
static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
{
int ret;
u32 value;
ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
if (ret) {
dev_err(udphy->dev, "failed to enable clk\n");
return ret;
}
rk_udphy_reset_deassert_all(udphy);
regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value);
if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value))
udphy->status = UDPHY_MODE_DP;
else
rk_udphy_disable(udphy);
return 0;
}
static int rk_udphy_parse_dt(struct rk_udphy *udphy)
{
struct device *dev = udphy->dev;
@ -999,23 +1004,24 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
}
if (udphy->status == UDPHY_MODE_NONE) {
udphy->mode_change = false;
ret = rk_udphy_setup(udphy);
if (ret)
return ret;
if (udphy->mode & UDPHY_MODE_USB)
if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
} else if (udphy->mode_change) {
udphy->mode_change = false;
udphy->status = UDPHY_MODE_NONE;
if (udphy->mode == UDPHY_MODE_DP)
rk_udphy_u3_port_disable(udphy, true);
udphy->phy_needs_reinit = false;
} else if (udphy->phy_needs_reinit) {
rk_udphy_u3_port_disable(udphy, true);
udelay(10);
rk_udphy_disable(udphy);
ret = rk_udphy_setup(udphy);
ret = rk_udphy_init(udphy);
if (ret)
return ret;
if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
udphy->phy_needs_reinit = false;
}
udphy->status |= mode;
@ -1039,55 +1045,30 @@ static void rk_udphy_power_off(struct rk_udphy *udphy, u8 mode)
rk_udphy_disable(udphy);
}
static int rk_udphy_dp_phy_init(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
mutex_lock(&udphy->mutex);
udphy->dp_in_use = true;
mutex_unlock(&udphy->mutex);
return 0;
}
static int rk_udphy_dp_phy_exit(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
mutex_lock(&udphy->mutex);
udphy->dp_in_use = false;
mutex_unlock(&udphy->mutex);
return 0;
}
static int rk_udphy_dp_phy_power_on(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
int ret, dp_lanes;
int ret;
mutex_lock(&udphy->mutex);
scoped_guard(mutex, &udphy->mutex) {
phy_set_bus_width(phy, udphy->dp_lanes);
dp_lanes = rk_udphy_dplane_get(udphy);
phy_set_bus_width(phy, dp_lanes);
ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
if (ret)
return ret;
ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
if (ret)
goto unlock;
rk_udphy_dp_lane_enable(udphy, udphy->dp_lanes);
rk_udphy_dplane_enable(udphy, dp_lanes);
rk_udphy_dp_lane_select(udphy);
}
rk_udphy_dplane_select(udphy);
unlock:
mutex_unlock(&udphy->mutex);
/*
* If data send by aux channel too fast after phy power on,
* the aux may be not ready which will cause aux error. Adding
* delay to avoid this issue.
*/
usleep_range(10000, 11000);
return ret;
}
@ -1095,10 +1076,10 @@ static int rk_udphy_dp_phy_power_off(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
mutex_lock(&udphy->mutex);
rk_udphy_dplane_enable(udphy, 0);
guard(mutex)(&udphy->mutex);
rk_udphy_dp_lane_enable(udphy, 0);
rk_udphy_power_off(udphy, UDPHY_MODE_DP);
mutex_unlock(&udphy->mutex);
return 0;
}
@ -1126,6 +1107,9 @@ static int rk_udphy_dp_phy_verify_link_rate(struct rk_udphy *udphy,
static int rk_udphy_dp_phy_verify_lanes(struct rk_udphy *udphy,
struct phy_configure_opts_dp *dp)
{
if (dp->lanes > udphy->dp_lanes)
return -EINVAL;
switch (dp->lanes) {
case 1:
case 2:
@ -1200,6 +1184,8 @@ static int rk_udphy_dp_phy_configure(struct phy *phy,
u32 i, val, lane;
int ret;
guard(mutex)(&udphy->mutex);
if (dp->set_rate) {
ret = rk_udphy_dp_phy_verify_link_rate(udphy, dp);
if (ret)
@ -1291,8 +1277,6 @@ static int rk_udphy_dp_phy_configure(struct phy *phy,
}
static const struct phy_ops rk_udphy_dp_phy_ops = {
.init = rk_udphy_dp_phy_init,
.exit = rk_udphy_dp_phy_exit,
.power_on = rk_udphy_dp_phy_power_on,
.power_off = rk_udphy_dp_phy_power_off,
.configure = rk_udphy_dp_phy_configure,
@ -1302,35 +1286,30 @@ static const struct phy_ops rk_udphy_dp_phy_ops = {
static int rk_udphy_usb3_phy_init(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
int ret = 0;
mutex_lock(&udphy->mutex);
guard(mutex)(&udphy->mutex);
/* DP only or high-speed, disable U3 port */
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
rk_udphy_u3_port_disable(udphy, true);
goto unlock;
return 0;
}
ret = rk_udphy_power_on(udphy, UDPHY_MODE_USB);
unlock:
mutex_unlock(&udphy->mutex);
return ret;
return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
}
static int rk_udphy_usb3_phy_exit(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
mutex_lock(&udphy->mutex);
guard(mutex)(&udphy->mutex);
/* DP only or high-speed */
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
goto unlock;
return 0;
rk_udphy_power_off(udphy, UDPHY_MODE_USB);
unlock:
mutex_unlock(&udphy->mutex);
return 0;
}
@ -1340,62 +1319,36 @@ static const struct phy_ops rk_udphy_usb3_phy_ops = {
.owner = THIS_MODULE,
};
static bool rk_udphy_is_supported_mode(struct typec_mux_state *state)
{
/* Handle Safe State and USB State */
if (state->mode < TYPEC_STATE_MODAL)
return true;
/* Handle DP AltMode */
if (state->alt && state->alt->svid == USB_TYPEC_DP_SID)
return true;
return false;
}
static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
struct typec_mux_state *state)
{
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
u8 mode;
mutex_lock(&udphy->mutex);
switch (state->mode) {
case TYPEC_DP_STATE_C:
case TYPEC_DP_STATE_E:
udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
mode = UDPHY_MODE_DP;
break;
case TYPEC_DP_STATE_D:
default:
if (udphy->flip) {
udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
} else {
udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
}
mode = UDPHY_MODE_DP_USB;
break;
/* Ignore mux events not involving USB or DP */
if (!rk_udphy_is_supported_mode(state)) {
dev_dbg(udphy->dev, "ignore mux event with mode=%lu\n", state->mode);
return 0;
}
if (state->alt && state->alt->svid == USB_TYPEC_DP_SID) {
struct typec_displayport_data *data = state->data;
dev_dbg(udphy->dev, "new mode: %lu\n", state->mode);
if (!data) {
rk_udphy_dp_hpd_event_trigger(udphy, false);
} else if (data->status & DP_STATUS_IRQ_HPD) {
rk_udphy_dp_hpd_event_trigger(udphy, false);
usleep_range(750, 800);
rk_udphy_dp_hpd_event_trigger(udphy, true);
} else if (data->status & DP_STATUS_HPD_STATE) {
if (udphy->mode != mode) {
udphy->mode = mode;
udphy->mode_change = true;
}
rk_udphy_dp_hpd_event_trigger(udphy, true);
} else {
rk_udphy_dp_hpd_event_trigger(udphy, false);
}
}
guard(mutex)(&udphy->mutex);
rk_udphy_set_typec_state(udphy, state->mode);
mutex_unlock(&udphy->mutex);
return 0;
}
@ -1453,6 +1406,7 @@ static int rk_udphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
struct fwnode_handle *dp_aux_ep;
struct resource *res;
struct rk_udphy *udphy;
void __iomem *base;
@ -1492,10 +1446,6 @@ static int rk_udphy_probe(struct platform_device *pdev)
if (ret)
return ret;
ret = rk_udphy_get_initial_status(udphy);
if (ret)
return ret;
mutex_init(&udphy->mutex);
platform_set_drvdata(pdev, udphy);
@ -1511,6 +1461,21 @@ static int rk_udphy_probe(struct platform_device *pdev)
return ret;
}
/*
* Only register the DRM bridge, if the DP aux channel is connected.
* Some boards use the USBDP PHY only for its USB3 capabilities. The
* aux bridge itself will be registered using port 0, endpoint 0, which
* is fine as that is the actual superspeed data connection shared by
* USB3 and DP based on the mux config.
*/
dp_aux_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 3, 0, 0);
if (dp_aux_ep) {
ret = drm_aux_bridge_register(dev);
fwnode_handle_put(dp_aux_ep);
if (ret)
return ret;
}
udphy->phy_u3 = devm_phy_create(dev, dev->of_node, &rk_udphy_usb3_phy_ops);
if (IS_ERR(udphy->phy_u3)) {
ret = PTR_ERR(udphy->phy_u3);
@ -1523,7 +1488,7 @@ static int rk_udphy_probe(struct platform_device *pdev)
ret = PTR_ERR(udphy->phy_dp);
return dev_err_probe(dev, ret, "failed to create DP phy\n");
}
phy_set_bus_width(udphy->phy_dp, rk_udphy_dplane_get(udphy));
phy_set_bus_width(udphy->phy_dp, udphy->dp_lanes);
udphy->phy_dp->attrs.max_link_rate = 8100;
phy_set_drvdata(udphy->phy_dp, udphy);
@ -1536,20 +1501,6 @@ static int rk_udphy_probe(struct platform_device *pdev)
return 0;
}
static int __maybe_unused rk_udphy_resume(struct device *dev)
{
struct rk_udphy *udphy = dev_get_drvdata(dev);
if (udphy->dp_sink_hpd_sel)
rk_udphy_dp_hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg);
return 0;
}
static const struct dev_pm_ops rk_udphy_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, rk_udphy_resume)
};
static const char * const rk_udphy_rst_list[] = {
"init", "cmn", "lane", "pcs_apb", "pma_apb"
};
@ -1573,7 +1524,6 @@ static const struct rk_udphy_cfg rk3576_udphy_cfgs = {
},
.vogrfcfg = {
{
.hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3),
.dp_lane_reg = 0x0000,
},
},
@ -1614,11 +1564,9 @@ static const struct rk_udphy_cfg rk3588_udphy_cfgs = {
},
.vogrfcfg = {
{
.hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3),
.dp_lane_reg = 0x0000,
},
{
.hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0008, 11, 10, 1, 3),
.dp_lane_reg = 0x0008,
},
},
@ -1654,7 +1602,6 @@ static struct platform_driver rk_udphy_driver = {
.driver = {
.name = "rockchip-usbdp-phy",
.of_match_table = rk_udphy_dt_match,
.pm = &rk_udphy_pm_ops,
},
};
module_platform_driver(rk_udphy_driver);

View File

@ -471,7 +471,7 @@ struct exynos5_usbdrd_phy;
struct exynos5_usbdrd_phy_config {
u32 id;
void (*phy_isol)(struct phy_usb_instance *inst, bool isolate);
void (*phy_init)(struct exynos5_usbdrd_phy *phy_drd);
int (*phy_init)(struct exynos5_usbdrd_phy *phy_drd);
unsigned int (*set_refclk)(struct phy_usb_instance *inst);
};
@ -708,7 +708,7 @@ exynos5_usbdrd_apply_phy_tunes(struct exynos5_usbdrd_phy *phy_drd,
}
}
static void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
static int exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
{
u32 reg;
@ -721,6 +721,8 @@ static void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
reg &= ~PHYTEST_POWERDOWN_SSP;
writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
return 0;
}
static void
@ -831,7 +833,7 @@ exynos5_usbdrd_usbdp_g2_v4_pma_check_pll_lock(struct exynos5_usbdrd_phy *phy_drd
return err;
}
static void
static int
exynos5_usbdrd_usbdp_g2_v4_pma_check_cdr_lock(struct exynos5_usbdrd_phy *phy_drd)
{
static const unsigned int timeout_us = 40000;
@ -857,9 +859,11 @@ exynos5_usbdrd_usbdp_g2_v4_pma_check_cdr_lock(struct exynos5_usbdrd_phy *phy_drd
((phy_drd->orientation == TYPEC_ORIENTATION_NORMAL)
? 0
: 2), reg);
return err;
}
static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
static int exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
{
u32 reg;
@ -881,6 +885,8 @@ static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
reg &= ~PHYTEST_POWERDOWN_HSP;
writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
return 0;
}
static int exynos5_usbdrd_phy_init(struct phy *phy)
@ -917,7 +923,9 @@ static int exynos5_usbdrd_phy_init(struct phy *phy)
writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL);
/* UTMI or PIPE3 specific init */
inst->phy_cfg->phy_init(phy_drd);
ret = inst->phy_cfg->phy_init(phy_drd);
if (ret)
goto disable_clks;
/* reference clock settings */
reg = inst->phy_cfg->set_refclk(inst);
@ -940,9 +948,10 @@ static int exynos5_usbdrd_phy_init(struct phy *phy)
reg &= ~PHYCLKRST_PORTRESET;
writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
disable_clks:
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
return 0;
return ret;
}
static int exynos5_usbdrd_phy_exit(struct phy *phy)
@ -1203,7 +1212,7 @@ static void exynos7870_usbdrd_phy_isol(struct phy_usb_instance *inst,
EXYNOS7870_USB2PHY_ENABLE, val);
}
static void exynos7870_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
static int exynos7870_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
{
u32 reg;
@ -1291,6 +1300,8 @@ static void exynos7870_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
if (phy_drd->drv_data->phy_tunes)
exynos5_usbdrd_apply_phy_tunes(phy_drd,
PTS_UTMI_POSTINIT);
return 0;
}
static int exynos7870_usbdrd_phy_init(struct phy *phy)
@ -1304,11 +1315,11 @@ static int exynos7870_usbdrd_phy_init(struct phy *phy)
return ret;
/* UTMI or PIPE3 specific init */
inst->phy_cfg->phy_init(phy_drd);
ret = inst->phy_cfg->phy_init(phy_drd);
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
return 0;
return ret;
}
static int exynos7870_usbdrd_phy_exit(struct phy *phy)
@ -1355,10 +1366,12 @@ static const struct phy_ops exynos7870_usbdrd_phy_ops = {
.owner = THIS_MODULE,
};
static void exynos2200_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
static int exynos2200_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
{
/* Configure non-Samsung IP PHY, responsible for UTMI */
phy_init(phy_drd->hs_phy);
return 0;
}
static void exynos2200_usbdrd_link_init(struct exynos5_usbdrd_phy *phy_drd)
@ -1458,11 +1471,11 @@ static int exynos2200_usbdrd_phy_init(struct phy *phy)
exynos2200_usbdrd_link_attach_detach_pipe3_phy(inst);
/* UTMI or PIPE3 specific init */
inst->phy_cfg->phy_init(phy_drd);
ret = inst->phy_cfg->phy_init(phy_drd);
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
return 0;
return ret;
}
static int exynos2200_usbdrd_phy_exit(struct phy *phy)
@ -1516,7 +1529,7 @@ exynos5_usbdrd_usb_v3p1_pipe_override(struct exynos5_usbdrd_phy *phy_drd)
writel(reg, regs_base + EXYNOS850_DRD_SECPMACTL);
}
static void exynos850_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
static int exynos850_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
{
void __iomem *regs_base = phy_drd->reg_phy;
u32 reg;
@ -1622,6 +1635,8 @@ static void exynos850_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
if (ss_ports)
exynos5_usbdrd_usb_v3p1_pipe_override(phy_drd);
return 0;
}
static int exynos850_usbdrd_phy_init(struct phy *phy)
@ -1635,12 +1650,13 @@ static int exynos850_usbdrd_phy_init(struct phy *phy)
return ret;
/* UTMI or PIPE3 specific init */
ret = 0;
scoped_guard(mutex, &phy_drd->phy_mutex)
inst->phy_cfg->phy_init(phy_drd);
ret = inst->phy_cfg->phy_init(phy_drd);
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
return 0;
return ret;
}
static int exynos850_usbdrd_phy_exit(struct phy *phy)
@ -1689,11 +1705,12 @@ static const struct phy_ops exynos850_usbdrd_phy_ops = {
.owner = THIS_MODULE,
};
static void exynos5_usbdrd_gs101_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
static int exynos5_usbdrd_gs101_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
{
void __iomem *regs_pma = phy_drd->reg_pma;
void __iomem *regs_phy = phy_drd->reg_phy;
u32 reg;
int ret;
exynos5_usbdrd_usbdp_g2_v4_ctrl_pma_ready(phy_drd);
@ -1715,8 +1732,11 @@ static void exynos5_usbdrd_gs101_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
SECPMACTL_PMA_INIT_SW_RST);
writel(reg, regs_phy + EXYNOS850_DRD_SECPMACTL);
if (!exynos5_usbdrd_usbdp_g2_v4_pma_check_pll_lock(phy_drd))
exynos5_usbdrd_usbdp_g2_v4_pma_check_cdr_lock(phy_drd);
ret = exynos5_usbdrd_usbdp_g2_v4_pma_check_pll_lock(phy_drd);
if (ret)
return ret;
return exynos5_usbdrd_usbdp_g2_v4_pma_check_cdr_lock(phy_drd);
}
static int exynos5_usbdrd_gs101_phy_init(struct phy *phy)
@ -1741,7 +1761,15 @@ static int exynos5_usbdrd_gs101_phy_init(struct phy *phy)
*/
exynos5_usbdrd_phy_isol(inst, false);
return exynos850_usbdrd_phy_init(phy);
ret = exynos850_usbdrd_phy_init(phy);
if (ret) {
exynos5_usbdrd_phy_isol(inst, true);
if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI)
regulator_bulk_disable(phy_drd->drv_data->n_regulators,
phy_drd->regulators);
}
return ret;
}
static int exynos5_usbdrd_gs101_phy_exit(struct phy *phy)
@ -2277,7 +2305,7 @@ exynosautov920_usb31drd_lane0_reset(struct exynos5_usbdrd_phy *phy_drd, int val)
writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_RST_CTRL);
}
static void
static int
exynosautov920_usb31drd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
{
void __iomem *reg_phy = phy_drd->reg_phy;
@ -2335,6 +2363,8 @@ exynosautov920_usb31drd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
/* override values for level settings */
exynosautov920_usb31drd_cr_write(phy_drd, 0x22, 0x00F5);
return 0;
}
static void
@ -2353,7 +2383,7 @@ exynosautov920_usb31drd_ssphy_disable(struct exynos5_usbdrd_phy *phy_drd)
writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CONFIG7);
}
static void
static int
exynosautov920_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
{
void __iomem *reg_phy = phy_drd->reg_phy;
@ -2457,6 +2487,8 @@ exynosautov920_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
reg = readl(reg_phy + EXYNOS2200_DRD_CLKRST);
reg |= EXYNOS2200_CLKRST_LINK_PCLK_SEL;
writel(reg, reg_phy + EXYNOS2200_DRD_CLKRST);
return 0;
}
static void
@ -2504,11 +2536,11 @@ static int exynosautov920_usbdrd_phy_init(struct phy *phy)
inst->phy_cfg->phy_isol(inst, false);
/* UTMI or PIPE3 specific init */
inst->phy_cfg->phy_init(phy_drd);
ret = inst->phy_cfg->phy_init(phy_drd);
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
return 0;
return ret;
}
static int exynosautov920_usbdrd_phy_exit(struct phy *phy)

View File

@ -104,11 +104,12 @@ static int uniphier_u3hsphy_get_nvparam(struct uniphier_u3hsphy_priv *priv,
struct nvmem_cell *cell;
u8 *buf;
cell = devm_nvmem_cell_get(priv->dev, name);
cell = nvmem_cell_get(priv->dev, name);
if (IS_ERR(cell))
return PTR_ERR(cell);
buf = nvmem_cell_read(cell, NULL);
nvmem_cell_put(cell);
if (IS_ERR(buf))
return PTR_ERR(buf);

View File

@ -18,8 +18,11 @@ config PHY_SPACEMIT_K1_USB2
tristate "SpacemiT K1 USB 2.0 PHY support"
depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF
depends on COMMON_CLK
depends on HAS_IOMEM
depends on USB_COMMON
select GENERIC_PHY
select REGMAP_MMIO
default ARCH_SPACEMIT
help
Enable this to support K1 USB 2.0 PHY driver. This driver takes care of
enabling and clock setup and will be used by K1 udc/ehci/otg/xhci driver.

View File

@ -209,7 +209,7 @@ static u32 stf_dphy_get_config_index(u32 bitrate)
return 0;
}
static void stf_dphy_hw_reset(struct stf_dphy *dphy, int assert)
static int stf_dphy_hw_reset(struct stf_dphy *dphy, int assert)
{
int rc;
u32 status = 0;
@ -224,8 +224,12 @@ static void stf_dphy_hw_reset(struct stf_dphy *dphy, int assert)
!(FIELD_GET(STF_DPHY_RGS_CDTX_PLL_UNLOCK, status)),
STF_DPHY_HW_DELAY_US, STF_DPHY_HW_TIMEOUT_US);
if (rc)
dev_err(dphy->dev, "MIPI dphy-tx # PLL Locked\n");
dev_err(dphy->dev, "MIPI dphy-tx PLL failed to lock\n");
return rc;
}
return 0;
}
static int stf_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
@ -313,7 +317,9 @@ static int stf_dphy_init(struct phy *phy)
struct stf_dphy *dphy = phy_get_drvdata(phy);
int ret;
stf_dphy_hw_reset(dphy, 1);
ret = stf_dphy_hw_reset(dphy, 1);
if (ret)
return ret;
writel(FIELD_PREP(STF_DPHY_SCFG_PPI_C_READY_SEL, 0) |
FIELD_PREP(STF_DPHY_SCFG_DSI_TXREADY_ESC_SEL, 0),
@ -350,7 +356,9 @@ static int stf_dphy_exit(struct phy *phy)
clk_disable_unprepare(dphy->txesc_clk);
stf_dphy_hw_reset(dphy, 0);
ret = stf_dphy_hw_reset(dphy, 0);
if (ret)
return ret;
return 0;
}

View File

@ -18,3 +18,14 @@ config PHY_TEGRA194_P2U
help
Enable this to support the P2U (PIPE to UPHY) that is part of Tegra 19x
and 234 SOCs.
config PHY_TEGRA_MPHY
tristate "NVIDIA Tegra264 MPHY driver"
depends on ARCH_TEGRA_264_SOC || COMPILE_TEST
select GENERIC_PHY
help
Enable this to support the MIPI M-PHY on NVIDIA Tegra264 SoCs.
The M-PHY provides the physical layer for the on-die UFS host
controller. The driver manages the shared clock-control block
and per-lane resets, exposing four sub-phys (L0-TX, L0-RX,
L1-TX, L1-RX) consumed by the UFS host driver.

View File

@ -8,4 +8,7 @@ phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_210_SOC) += xusb-tegra210.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_186_SOC) += xusb-tegra186.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_194_SOC) += xusb-tegra186.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_234_SOC) += xusb-tegra186.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_238_SOC) += xusb-tegra186.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_264_SOC) += xusb-tegra186.o
obj-$(CONFIG_PHY_TEGRA194_P2U) += phy-tegra194-p2u.o
obj-$(CONFIG_PHY_TEGRA_MPHY) += phy-tegra-mphy.o

View File

@ -0,0 +1,459 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// NVIDIA Tegra264 MPHY driver.
#include <dt-bindings/phy/nvidia,tegra264-mphy.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#define MPHY_GO_BIT BIT(0)
#define MPHY_RX_APB_CAPABILITY_88_8B 0x88
#define RX_HS_G1_SYNC_LENGTH_CAPABILITY(x) (((x) & 0x3f) << 24)
#define MPHY_RX_APB_CAPABILITY_94_97 0x94
#define RX_HS_G3_SYNC_LENGTH_CAPABILITY(x) (((x) & 0x3f) << 8)
#define RX_HS_G2_SYNC_LENGTH_CAPABILITY(x) (((x) & 0x3f) << 0)
#define MPHY_TX_APB_VENDOR0 0x100
#define MPHY_TX_APB_VENDOR2 0x108
#define TX_CAL_DONE BIT(19)
#define TX_CAL_EN BIT(15)
#define MPHY_RX_APB_VENDOR2 0x184
#define RX_CAL_DONE BIT(19)
#define RX_CAL_EN BIT(15)
#define MPHY_RX_APB_VENDOR3 0x188
#define RX_MPHY2UPHY_IF_OVR_CTRL BIT(26)
#define MPHY_RX_APB_VENDOR3B 0x220
#define MPHY_RX_APB_VENDOR49 0x254
#define MPHY_EQ_TIMEOUT 0xffffffff
#define MPHY_PWR_CHANGE_CLK_BOOST 0x0017
#define MPHY_TX_OFFSET 0x1000
#define MPHY_RX_OFFSET 0x2000
struct tegra_mphy_lane {
void __iomem *regs;
void __iomem *tx_regs;
void __iomem *rx_regs;
struct reset_control *rst_rx;
struct reset_control *rst_tx;
struct phy *tx;
struct phy *rx;
};
struct tegra_mphy {
struct device *dev;
struct tegra_mphy_lane l0;
struct tegra_mphy_lane l1;
unsigned int num_clks;
struct clk_bulk_data *clks;
struct reset_control *rst_clk_ctl;
unsigned int power_count;
};
static int tegra_mphy_get_clocks(struct tegra_mphy *mphy)
{
struct device *dev = mphy->dev;
int num_clks;
num_clks = devm_clk_bulk_get_all(dev, &mphy->clks);
if (num_clks < 0)
return dev_err_probe(dev, num_clks, "failed to get clocks\n");
mphy->num_clks = num_clks;
return 0;
}
static int tegra_mphy_get_resets(struct tegra_mphy *mphy)
{
struct device *dev = mphy->dev;
mphy->rst_clk_ctl = devm_reset_control_get_exclusive(dev, "clk-ctl");
if (IS_ERR(mphy->rst_clk_ctl))
return dev_err_probe(dev, PTR_ERR(mphy->rst_clk_ctl),
"failed to get clk-ctl reset\n");
mphy->l0.rst_rx = devm_reset_control_get_exclusive(dev, "l0-rx");
if (IS_ERR(mphy->l0.rst_rx))
return dev_err_probe(dev, PTR_ERR(mphy->l0.rst_rx), "failed to get l0-rx reset\n");
mphy->l0.rst_tx = devm_reset_control_get_exclusive(dev, "l0-tx");
if (IS_ERR(mphy->l0.rst_tx))
return dev_err_probe(dev, PTR_ERR(mphy->l0.rst_tx), "failed to get l0-tx reset\n");
mphy->l1.rst_rx = devm_reset_control_get_exclusive(dev, "l1-rx");
if (IS_ERR(mphy->l1.rst_rx))
return dev_err_probe(dev, PTR_ERR(mphy->l1.rst_rx), "failed to get l1-rx reset\n");
mphy->l1.rst_tx = devm_reset_control_get_exclusive(dev, "l1-tx");
if (IS_ERR(mphy->l1.rst_tx))
return dev_err_probe(dev, PTR_ERR(mphy->l1.rst_tx), "failed to get l1-tx reset\n");
return 0;
}
static int tegra_mphy_rx_write_kick_go(struct tegra_mphy_lane *lane, u32 offset, u32 value)
{
u32 v;
writel(value, lane->rx_regs + offset);
v = readl(lane->rx_regs + MPHY_RX_APB_VENDOR2);
v |= MPHY_GO_BIT;
writel(v, lane->rx_regs + MPHY_RX_APB_VENDOR2);
return readl_poll_timeout(lane->rx_regs + MPHY_RX_APB_VENDOR2, v,
(v & MPHY_GO_BIT) == 0, 25, 5000);
}
static int tegra_mphy_rx_power_on(struct phy *phy)
{
struct tegra_mphy *mphy = dev_get_drvdata(phy->dev.parent);
struct tegra_mphy_lane *lane = phy_get_drvdata(phy);
u32 value;
int err;
if (mphy->power_count++ == 0) {
err = clk_bulk_prepare_enable(mphy->num_clks, mphy->clks);
if (err) {
mphy->power_count--;
dev_err(&phy->dev, "failed to enable clocks: %d\n", err);
return err;
}
reset_control_deassert(mphy->rst_clk_ctl);
}
reset_control_deassert(lane->rst_rx);
reset_control_deassert(lane->rst_tx);
err = tegra_mphy_rx_write_kick_go(lane, MPHY_RX_APB_VENDOR3B, MPHY_EQ_TIMEOUT);
if (err) {
dev_err(&phy->dev, "eq_timeout programming failed: %d\n", err);
goto err_reset;
}
err = tegra_mphy_rx_write_kick_go(lane, MPHY_RX_APB_VENDOR49, MPHY_PWR_CHANGE_CLK_BOOST);
if (err) {
dev_err(&phy->dev, "pwr_change_clk_boost programming failed: %d\n", err);
goto err_reset;
}
value = readl(lane->regs + MPHY_RX_APB_CAPABILITY_88_8B);
value &= ~RX_HS_G1_SYNC_LENGTH_CAPABILITY(~0);
value |= RX_HS_G1_SYNC_LENGTH_CAPABILITY(0xf);
writel(value, lane->regs + MPHY_RX_APB_CAPABILITY_88_8B);
value = readl(lane->regs + MPHY_RX_APB_CAPABILITY_94_97);
value &= ~RX_HS_G3_SYNC_LENGTH_CAPABILITY(~0);
value |= RX_HS_G3_SYNC_LENGTH_CAPABILITY(0xf);
value &= ~RX_HS_G2_SYNC_LENGTH_CAPABILITY(~0);
value |= RX_HS_G2_SYNC_LENGTH_CAPABILITY(0xf);
writel(value, lane->regs + MPHY_RX_APB_CAPABILITY_94_97);
value = readl(lane->rx_regs + MPHY_RX_APB_VENDOR3);
value |= RX_MPHY2UPHY_IF_OVR_CTRL;
writel(value, lane->rx_regs + MPHY_RX_APB_VENDOR3);
value = readl(lane->rx_regs + MPHY_RX_APB_VENDOR2);
value |= MPHY_GO_BIT;
writel(value, lane->rx_regs + MPHY_RX_APB_VENDOR2);
err = readl_poll_timeout(lane->rx_regs + MPHY_RX_APB_VENDOR2, value,
(value & MPHY_GO_BIT) == 0, 25, 5000);
if (err) {
dev_err(&phy->dev, "RX cap update failed: %d\n", err);
goto err_reset;
}
return 0;
err_reset:
reset_control_assert(lane->rst_rx);
reset_control_assert(lane->rst_tx);
if (--mphy->power_count == 0) {
reset_control_assert(mphy->rst_clk_ctl);
clk_bulk_disable_unprepare(mphy->num_clks, mphy->clks);
}
return err;
}
static int tegra_mphy_rx_power_off(struct phy *phy)
{
struct tegra_mphy *mphy = dev_get_drvdata(phy->dev.parent);
struct tegra_mphy_lane *lane = phy_get_drvdata(phy);
if (WARN_ON(mphy->power_count == 0))
return -EINVAL;
reset_control_assert(lane->rst_rx);
reset_control_assert(lane->rst_tx);
if (--mphy->power_count == 0)
clk_bulk_disable_unprepare(mphy->num_clks, mphy->clks);
return 0;
}
static int tegra_mphy_rx_configure(struct phy *phy, union phy_configure_opts *opts)
{
struct tegra_mphy_lane *lane = phy_get_drvdata(phy);
u32 value;
int err;
value = readl(lane->rx_regs + MPHY_RX_APB_VENDOR2);
value |= RX_CAL_EN;
writel(value, lane->rx_regs + MPHY_RX_APB_VENDOR2);
value = readl(lane->rx_regs + MPHY_RX_APB_VENDOR2);
value |= MPHY_GO_BIT;
writel(value, lane->rx_regs + MPHY_RX_APB_VENDOR2);
err = readl_poll_timeout(lane->rx_regs + MPHY_RX_APB_VENDOR2, value,
(value & MPHY_GO_BIT) == 0, 25, 5000);
if (err)
dev_err(&phy->dev, "failed to arm RX calibration: %d\n", err);
return err;
}
static int tegra_mphy_rx_calibrate(struct phy *phy)
{
struct tegra_mphy_lane *lane = phy_get_drvdata(phy);
u32 value;
int err;
err = readl_poll_timeout(lane->rx_regs + MPHY_RX_APB_VENDOR2, value,
(value & RX_CAL_DONE) != 0, 25, 100000);
if (err) {
dev_err(&phy->dev, "RX calibration timed out: %d\n", err);
return err;
}
value = readl(lane->rx_regs + MPHY_RX_APB_VENDOR2);
value &= ~RX_CAL_EN;
writel(value, lane->rx_regs + MPHY_RX_APB_VENDOR2);
value = readl(lane->rx_regs + MPHY_RX_APB_VENDOR2);
value |= MPHY_GO_BIT;
writel(value, lane->rx_regs + MPHY_RX_APB_VENDOR2);
err = readl_poll_timeout(lane->rx_regs + MPHY_RX_APB_VENDOR2, value,
(value & MPHY_GO_BIT) == 0, 25, 5000);
if (err) {
dev_err(&phy->dev, "failed to clear RX calibration: %d\n", err);
return err;
}
err = readl_poll_timeout(lane->rx_regs + MPHY_RX_APB_VENDOR2, value,
(value & RX_CAL_DONE) == 0, 25, 100000);
if (err)
dev_err(&phy->dev, "RX calibration failed to clear: %d\n", err);
return err;
}
static const struct phy_ops tegra_mphy_rx_ops = {
.power_on = tegra_mphy_rx_power_on,
.power_off = tegra_mphy_rx_power_off,
.configure = tegra_mphy_rx_configure,
.calibrate = tegra_mphy_rx_calibrate,
};
static int tegra_mphy_tx_configure(struct phy *phy, union phy_configure_opts *opts)
{
struct tegra_mphy_lane *lane = phy_get_drvdata(phy);
u32 value;
int err;
value = readl(lane->tx_regs + MPHY_TX_APB_VENDOR2);
value |= TX_CAL_EN;
writel(value, lane->tx_regs + MPHY_TX_APB_VENDOR2);
value = readl(lane->tx_regs + MPHY_TX_APB_VENDOR0);
value |= MPHY_GO_BIT;
writel(value, lane->tx_regs + MPHY_TX_APB_VENDOR0);
err = readl_poll_timeout(lane->tx_regs + MPHY_TX_APB_VENDOR0, value,
(value & MPHY_GO_BIT) == 0, 25, 5000);
if (err)
dev_err(&phy->dev, "failed to arm TX calibration: %d\n", err);
return err;
}
static int tegra_mphy_tx_calibrate(struct phy *phy)
{
struct tegra_mphy_lane *lane = phy_get_drvdata(phy);
u32 value;
int err;
err = readl_poll_timeout(lane->tx_regs + MPHY_TX_APB_VENDOR2, value,
(value & TX_CAL_DONE) != 0, 25, 50000);
if (err) {
dev_err(&phy->dev, "TX calibration timed out: %d\n", err);
return err;
}
value = readl(lane->tx_regs + MPHY_TX_APB_VENDOR2);
value &= ~TX_CAL_EN;
writel(value, lane->tx_regs + MPHY_TX_APB_VENDOR2);
value = readl(lane->tx_regs + MPHY_TX_APB_VENDOR0);
value |= MPHY_GO_BIT;
writel(value, lane->tx_regs + MPHY_TX_APB_VENDOR0);
err = readl_poll_timeout(lane->tx_regs + MPHY_TX_APB_VENDOR0, value,
(value & MPHY_GO_BIT) == 0, 25, 5000);
if (err) {
dev_err(&phy->dev, "GO bit failed to clear: %d\n", err);
return err;
}
err = readl_poll_timeout(lane->tx_regs + MPHY_TX_APB_VENDOR2, value,
(value & TX_CAL_DONE) == 0, 25, 50000);
if (err)
dev_err(&phy->dev, "TX calibration failed: %d\n", err);
return err;
}
static const struct phy_ops tegra_mphy_tx_ops = {
.configure = tegra_mphy_tx_configure,
.calibrate = tegra_mphy_tx_calibrate,
};
static struct phy *tegra_mphy_xlate(struct device *dev, const struct of_phandle_args *args)
{
struct tegra_mphy *mphy = dev_get_drvdata(dev);
if (args->args_count != 1)
return ERR_PTR(-EINVAL);
switch (args->args[0]) {
case TEGRA_MPHY_L0_TX:
return mphy->l0.tx;
case TEGRA_MPHY_L0_RX:
return mphy->l0.rx;
case TEGRA_MPHY_L1_TX:
return mphy->l1.tx;
case TEGRA_MPHY_L1_RX:
return mphy->l1.rx;
default:
return ERR_PTR(-EINVAL);
}
}
static int tegra_mphy_create_phys(struct tegra_mphy *mphy)
{
struct device *dev = mphy->dev;
mphy->l0.tx = devm_phy_create(dev, dev->of_node, &tegra_mphy_tx_ops);
if (IS_ERR(mphy->l0.tx))
return dev_err_probe(dev, PTR_ERR(mphy->l0.tx), "failed to create l0-tx PHY\n");
phy_set_drvdata(mphy->l0.tx, &mphy->l0);
mphy->l0.rx = devm_phy_create(dev, dev->of_node, &tegra_mphy_rx_ops);
if (IS_ERR(mphy->l0.rx))
return dev_err_probe(dev, PTR_ERR(mphy->l0.rx), "failed to create l0-rx PHY\n");
phy_set_drvdata(mphy->l0.rx, &mphy->l0);
mphy->l1.tx = devm_phy_create(dev, dev->of_node, &tegra_mphy_tx_ops);
if (IS_ERR(mphy->l1.tx))
return dev_err_probe(dev, PTR_ERR(mphy->l1.tx), "failed to create l1-tx PHY\n");
phy_set_drvdata(mphy->l1.tx, &mphy->l1);
mphy->l1.rx = devm_phy_create(dev, dev->of_node, &tegra_mphy_rx_ops);
if (IS_ERR(mphy->l1.rx))
return dev_err_probe(dev, PTR_ERR(mphy->l1.rx), "failed to create l1-rx PHY\n");
phy_set_drvdata(mphy->l1.rx, &mphy->l1);
return 0;
}
static int tegra_mphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *provider;
struct tegra_mphy *mphy;
int err;
mphy = devm_kzalloc(dev, sizeof(*mphy), GFP_KERNEL);
if (!mphy)
return -ENOMEM;
mphy->dev = dev;
mphy->l0.regs = devm_platform_ioremap_resource_byname(pdev, "l0");
if (IS_ERR(mphy->l0.regs))
return PTR_ERR(mphy->l0.regs);
mphy->l1.regs = devm_platform_ioremap_resource_byname(pdev, "l1");
if (IS_ERR(mphy->l1.regs))
return PTR_ERR(mphy->l1.regs);
mphy->l0.tx_regs = mphy->l0.regs + MPHY_TX_OFFSET;
mphy->l0.rx_regs = mphy->l0.regs + MPHY_RX_OFFSET;
mphy->l1.tx_regs = mphy->l1.regs + MPHY_TX_OFFSET;
mphy->l1.rx_regs = mphy->l1.regs + MPHY_RX_OFFSET;
err = tegra_mphy_get_clocks(mphy);
if (err)
return err;
err = tegra_mphy_get_resets(mphy);
if (err)
return err;
err = tegra_mphy_create_phys(mphy);
if (err)
return err;
platform_set_drvdata(pdev, mphy);
provider = devm_of_phy_provider_register(dev, tegra_mphy_xlate);
if (IS_ERR(provider))
return dev_err_probe(dev, PTR_ERR(provider),
"failed to register PHY\n");
return 0;
}
static const struct of_device_id tegra_mphy_of_table[] = {
{ .compatible = "nvidia,tegra264-mphy" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tegra_mphy_of_table);
static struct platform_driver tegra_mphy_driver = {
.driver = {
.name = "tegra-mphy",
.of_match_table = tegra_mphy_of_table,
},
.probe = tegra_mphy_probe,
};
module_platform_driver(tegra_mphy_driver);
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_AUTHOR("Kartik Rajput <kkartik@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra MPHY driver");
MODULE_LICENSE("GPL");

View File

@ -636,7 +636,7 @@ static void tegra186_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl)
if (padctl->soc->poll_trk_completed) {
err = padctl_readl_poll(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1,
USB2_TRK_COMPLETED, USB2_TRK_COMPLETED, 100);
USB2_TRK_COMPLETED, USB2_TRK_COMPLETED, 200);
if (err) {
/* The failure with polling on trk complete will not
* cause the failure of powering on the bias pad.
@ -1045,7 +1045,7 @@ tegra186_usb2_pad_probe(struct tegra_xusb_padctl *padctl,
goto out;
}
priv->usb2_trk_clk = devm_clk_get(&pad->dev, "trk");
priv->usb2_trk_clk = devm_clk_get_optional(&pad->dev, "trk");
if (IS_ERR(priv->usb2_trk_clk)) {
err = PTR_ERR(priv->usb2_trk_clk);
dev_dbg(&pad->dev, "failed to get usb2 trk clock: %d\n", err);
@ -1599,7 +1599,6 @@ static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = {
.utmi_pad_power_down = tegra186_utmi_pad_power_down,
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
static const char * const tegra186_xusb_padctl_supply_names[] = {
"avdd-pll-erefeut",
"avdd-usb",
@ -1665,10 +1664,7 @@ const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = {
.num_supplies = ARRAY_SIZE(tegra186_xusb_padctl_supply_names),
};
EXPORT_SYMBOL_GPL(tegra186_xusb_padctl_soc);
#endif
#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \
IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
static const char * const tegra194_xusb_padctl_supply_names[] = {
"avdd-usb",
"vclamp-usb",
@ -1753,7 +1749,53 @@ const struct tegra_xusb_padctl_soc tegra234_xusb_padctl_soc = {
.has_per_pad_term = true,
};
EXPORT_SYMBOL_GPL(tegra234_xusb_padctl_soc);
#endif
const struct tegra_xusb_padctl_soc tegra238_xusb_padctl_soc = {
.num_pads = ARRAY_SIZE(tegra186_pads),
.pads = tegra186_pads,
.ports = {
.usb2 = {
.ops = &tegra186_usb2_port_ops,
.count = 3,
},
.usb3 = {
.ops = &tegra186_usb3_port_ops,
.count = 3,
},
},
.ops = &tegra186_xusb_padctl_ops,
.supply_names = tegra194_xusb_padctl_supply_names,
.num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
.supports_gen2 = true,
.poll_trk_completed = true,
.trk_hw_mode = false,
.supports_lp_cfg_en = true,
};
EXPORT_SYMBOL_GPL(tegra238_xusb_padctl_soc);
const struct tegra_xusb_padctl_soc tegra264_xusb_padctl_soc = {
.num_pads = ARRAY_SIZE(tegra194_pads),
.pads = tegra194_pads,
.ports = {
.usb2 = {
.ops = &tegra186_usb2_port_ops,
.count = 4,
},
.usb3 = {
.ops = &tegra186_usb3_port_ops,
.count = 4,
},
},
.ops = &tegra186_xusb_padctl_ops,
.supply_names = tegra194_xusb_padctl_supply_names,
.num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
.supports_gen2 = true,
.poll_trk_completed = true,
.trk_hw_mode = true,
.supports_lp_cfg_en = true,
.has_per_pad_term = true,
};
EXPORT_SYMBOL_GPL(tegra264_xusb_padctl_soc);
MODULE_AUTHOR("JC Kuo <jckuo@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra186 XUSB Pad Controller driver");

View File

@ -77,6 +77,18 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
.compatible = "nvidia,tegra234-xusb-padctl",
.data = &tegra234_xusb_padctl_soc,
},
#endif
#if defined(CONFIG_ARCH_TEGRA_238_SOC)
{
.compatible = "nvidia,tegra238-xusb-padctl",
.data = &tegra238_xusb_padctl_soc,
},
#endif
#if defined(CONFIG_ARCH_TEGRA_264_SOC)
{
.compatible = "nvidia,tegra264-xusb-padctl",
.data = &tegra264_xusb_padctl_soc,
},
#endif
{ }
};
@ -1251,13 +1263,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
err = tegra_xusb_setup_ports(padctl);
if (err) {
const char *level = KERN_ERR;
if (err == -EPROBE_DEFER)
level = KERN_DEBUG;
dev_printk(level, &pdev->dev,
dev_fmt("failed to setup XUSB ports: %d\n"), err);
dev_err_probe(&pdev->dev, err, "failed to setup XUSB ports\n");
goto remove_pads;
}

View File

@ -514,5 +514,11 @@ extern const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc;
#if defined(CONFIG_ARCH_TEGRA_234_SOC)
extern const struct tegra_xusb_padctl_soc tegra234_xusb_padctl_soc;
#endif
#if defined(CONFIG_ARCH_TEGRA_238_SOC)
extern const struct tegra_xusb_padctl_soc tegra238_xusb_padctl_soc;
#endif
#if defined(CONFIG_ARCH_TEGRA_264_SOC)
extern const struct tegra_xusb_padctl_soc tegra264_xusb_padctl_soc;
#endif
#endif /* __PHY_TEGRA_XUSB_H */

View File

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
/* Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
#ifndef DT_BINDINGS_PHY_NVIDIA_TEGRA264_MPHY_H
#define DT_BINDINGS_PHY_NVIDIA_TEGRA264_MPHY_H
#define TEGRA_MPHY_L0_TX 0
#define TEGRA_MPHY_L0_RX 1
#define TEGRA_MPHY_L1_TX 2
#define TEGRA_MPHY_L1_RX 3
#endif /* DT_BINDINGS_PHY_NVIDIA_TEGRA264_MPHY_H */

View File

@ -16,6 +16,7 @@
/* QMP USB4-USB3-DP PHYs */
#define QMP_USB43DP_USB3_PHY 0
#define QMP_USB43DP_DP_PHY 1
#define QMP_USB43DP_USB4_PHY 2
/* QMP PCIE PHYs */
#define QMP_PCIE_PIPE_CLK 0

View File

@ -12,7 +12,7 @@
enum omap_control_phy_type {
OMAP_CTRL_TYPE_OTGHS = 1, /* Mailbox OTGHS_CONTROL */
OMAP_CTRL_TYPE_USB2, /* USB2_PHY, power down in CONTROL_DEV_CONF */
OMAP_CTRL_TYPE_PIPE3, /* PIPE3 PHY, DPLL & seperate Rx/Tx power */
OMAP_CTRL_TYPE_PIPE3, /* PIPE3 PHY, DPLL & separate Rx/Tx power */
OMAP_CTRL_TYPE_PCIE, /* RX TX control of ACSPCIE */
OMAP_CTRL_TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */
OMAP_CTRL_TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */

View File

@ -19,6 +19,10 @@ enum phy_hdmi_mode {
* @tmds_char_rate: HDMI TMDS Character Rate in Hertz.
* @frl.rate_per_lane: HDMI FRL Rate per Lane in Gbps.
* @frl.lanes: HDMI FRL lanes count.
* @frl.ffe_level: Transmitter Feed Forward Equalizer Level.
* Optional, only meaningful when set_ffe_level flag is on.
* @frl.set_ffe_level: Flag indicating whether or not to reconfigure ffe_level.
* All the other struct fields must be ignored when this is used.
*
* This structure is used to represent the configuration state of a HDMI phy.
*/
@ -29,6 +33,8 @@ struct phy_configure_opts_hdmi {
struct {
u8 rate_per_lane;
u8 lanes;
u8 ffe_level;
u8 set_ffe_level : 1;
} frl;
};
};

View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef __PHY_TBT_H_
#define __PHY_TBT_H_
enum tbt_phy_submode {
PHY_SUBMODE_TBT3,
PHY_SUBMODE_USB4,
};
#endif

View File

@ -20,6 +20,7 @@
#include <linux/phy/phy-hdmi.h>
#include <linux/phy/phy-lvds.h>
#include <linux/phy/phy-mipi-dphy.h>
#include <linux/phy/phy-thunderbolt.h>
struct phy;
@ -45,6 +46,7 @@ enum phy_mode {
PHY_MODE_LVDS,
PHY_MODE_DP,
PHY_MODE_HDMI,
PHY_MODE_TBT,
};
enum phy_media {