mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
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:
commit
e191b05f32
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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";
|
||||
};
|
||||
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
108
Documentation/devicetree/bindings/phy/nvidia,tegra264-mphy.yaml
Normal file
108
Documentation/devicetree/bindings/phy/nvidia,tegra264-mphy.yaml
Normal 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>;
|
||||
};
|
||||
...
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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':
|
||||
|
|
|
|||
10
MAINTAINERS
10
MAINTAINERS
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
|
|||
obj-$(CONFIG_GENERIC_PHY) += allwinner/ \
|
||||
amlogic/ \
|
||||
apple/ \
|
||||
aspeed/ \
|
||||
axiado/ \
|
||||
broadcom/ \
|
||||
cadence/ \
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
15
drivers/phy/aspeed/Kconfig
Normal file
15
drivers/phy/aspeed/Kconfig
Normal 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.
|
||||
2
drivers/phy/aspeed/Makefile
Normal file
2
drivers/phy/aspeed/Makefile
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_PHY_ASPEED_USB3) += phy-aspeed-usb3.o
|
||||
236
drivers/phy/aspeed/phy-aspeed-usb3.c
Normal file
236
drivers/phy/aspeed/phy-aspeed-usb3.c
Normal 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>");
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
561
drivers/phy/mediatek/phy-mtk-pcie-sphy.c
Normal file
561
drivers/phy/mediatek/phy-mtk-pcie-sphy.c
Normal 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");
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 |= 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[] = {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
|||
55
drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6_40.h
Normal file
55
drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6_40.h
Normal 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_ */
|
||||
14
drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6_40.h
Normal file
14
drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6_40.h
Normal 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_ */
|
||||
|
|
@ -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
|
||||
|
|
|
|||
94
drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-pcie-v6_40.h
Normal file
94
drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-pcie-v6_40.h
Normal 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_ */
|
||||
194
drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrxz-pcie-v6_40.h
Normal file
194
drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrxz-pcie-v6_40.h
Normal 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_ */
|
||||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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[] = {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
459
drivers/phy/tegra/phy-tegra-mphy.c
Normal file
459
drivers/phy/tegra/phy-tegra-mphy.c
Normal 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");
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
12
include/dt-bindings/phy/nvidia,tegra264-mphy.h
Normal file
12
include/dt-bindings/phy/nvidia,tegra264-mphy.h
Normal 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 */
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
14
include/linux/phy/phy-thunderbolt.h
Normal file
14
include/linux/phy/phy-thunderbolt.h
Normal 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
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user