phy-for-5.13

- Updates:
 	- Yaml conversion for mvebu-utmi binding, bcm-ns-usb2 and
 	  bcm-ns-usb3 bindings
 	- Mediatek dsi and hdmi phy updates
 	- TI j721e-wiz updates for AM64
 	- Cadence-torrent phy updates for SGMII/QSGMII
 
   - New support:
 	- usb3-dp phy for Qualcomm SM8250
 	- UTMI phy for Armada CP110
 	- USB phy for Qualcomm SC7280
 	- Binding and driver for Sparx5 ethernet serdes
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmBtqI0ACgkQfBQHDyUj
 g0f09hAAn1C0rmyPFjycPTWwnbom52AFMvG95jZJvUK8QDxBDOuhnv0qcZa7pdgt
 ulSMLK2AhEExyqtUFf6TSZDZJXHioriGJh54D3qqBGtFHnc/60eTQfVHbp6H8ulr
 G/NmyXeB4nUdco8Lw38Fs6RQufSwQTWLLaPHPCEGTFZr3g0gh7RfaHyHF8CpOGNI
 Dd+IiPs+TMfB3MKaY3im45WP4e1sYQiPkMs8XQpu04Fvz2CELUxk1qK7GDviISIt
 tkU5G87Syu5oCNaKm1sus817yIbLTIU0pPYq6c/JDpPL3l9vavbsZ3HrOkMD7XOQ
 tpDcOkepU3srDkv8GIClfdJdzu8lOhmYbE4ktvlBtKLSf6XFC17whamHu9eYcrqH
 6x9PFDRm7hloAjxLuiEQInsNndW7mF0BUwCfEW7iOZ+Mzh06HqkHSSLF0gpFoYFj
 YUC0fKubqP/IHV32G1iQ+61ftjcAx7WOcZn2VJXHxiomjPJrRr5plIlTkOT8pcIW
 R7nDcxN8f0LRpuXx+pFjqWpTK/lvG0cO3DxNpaeTF7kfy5hYyvOtw6MconDR7qJa
 f1PQGhO49IYBjSwDdxgu6MtevhOt62NVRXuNs5mjqPEzLVrjvHshAOdo2Xwd8mjz
 47wWR9LUAXc1/S+H+gHpn1Chl7p+8HbgE1VdbrJ7limuF6E6fFQ=
 =T8Jv
 -----END PGP SIGNATURE-----

Merge tag 'phy-for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy into char-misc-next

Vinod writes:

phy-for-5.13

  - Updates:
	- Yaml conversion for mvebu-utmi binding, bcm-ns-usb2 and
	  bcm-ns-usb3 bindings
	- Mediatek dsi and hdmi phy updates
	- TI j721e-wiz updates for AM64
	- Cadence-torrent phy updates for SGMII/QSGMII

  - New support:
	- usb3-dp phy for Qualcomm SM8250
	- UTMI phy for Armada CP110
	- USB phy for Qualcomm SC7280
	- Binding and driver for Sparx5 ethernet serdes

* tag 'phy-for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy: (75 commits)
  phy: fix resource_size.cocci warnings
  phy: Sparx5 Eth SerDes: Use direct register operations
  phy: hisilicon: Use the correct HiSilicon copyright
  phy: marvell: phy-mvebu-cp11i-utmi needs USB_COMMON
  phy: qcom-qmp: add support for sm8250-usb3-dp phy
  phy: qcom-qmp: rename common registers
  phy: qcom-qmp: move DP functions to callbacks
  dt-bindings: phy: qcom,qmp-usb3-dp: Add support for SM8250
  dt-bindings: phy: qcom,qmp-usb3-dp-phy: move usb3 compatibles back to qcom,qmp-phy.yaml
  phy: ti: j721e-wiz: Configure 'p_standard_mode' only for DP/QSGMII
  dt-bindings: phy: fix dt_binding_check warning in mediatek, ufs-phy.yaml
  phy: zynqmp: Handle the clock enable/disable properly
  dt-bindings: phy: bcm-ns-usb3-phy: convert to yaml
  dt-bindings: phy: bcm-ns-usb2-phy: convert to yaml
  phy: microchip: PHY_SPARX5_SERDES should depend on ARCH_SPARX5
  phy: cadence-torrent: Add delay for PIPE clock to be stable
  phy: cadence-torrent: Explicitly request exclusive reset control
  phy: cadence-torrent: Do not configure SERDES if it's already configured
  phy: cadence-torrent: Group reset APIs and clock APIs
  phy: ti: j721e-wiz: Do not configure wiz if its already configured
  ...
This commit is contained in:
Greg Kroah-Hartman 2021-04-07 16:52:40 +02:00
commit fbb0ad4229
55 changed files with 8150 additions and 439 deletions

View file

@ -1,21 +0,0 @@
Driver for Broadcom Northstar USB 2.0 PHY
Required properties:
- compatible: brcm,ns-usb2-phy
- reg: iomem address range of DMU (Device Management Unit)
- reg-names: "dmu", the only needed & supported reg right now
- clocks: USB PHY reference clock
- clock-names: "phy-ref-clk", the only needed & supported clock right now
To initialize USB 2.0 PHY driver needs to setup PLL correctly. To do this it
requires passing phandle to the USB PHY reference clock.
Example:
usb2-phy {
compatible = "brcm,ns-usb2-phy";
reg = <0x1800c000 0x1000>;
reg-names = "dmu";
#phy-cells = <0>;
clocks = <&genpll BCM_NSP_GENPLL_USB_PHY_REF_CLK>;
clock-names = "phy-ref-clk";
};

View file

@ -0,0 +1,59 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/bcm-ns-usb2-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom Northstar USB 2.0 PHY
description: >
To initialize USB 2.0 PHY driver needs to setup PLL correctly.
To do this it requires passing phandle to the USB PHY reference clock.
maintainers:
- Rafał Miłecki <rafal@milecki.pl>
properties:
compatible:
const: brcm,ns-usb2-phy
reg:
items:
- description: iomem address range of DMU (Device Management Unit)
reg-names:
items:
- const: dmu
clocks:
items:
- description: USB PHY reference clock
clock-names:
items:
- const: phy-ref-clk
"#phy-cells":
const: 0
required:
- compatible
- reg
- reg-names
- clocks
- clock-names
- "#phy-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/bcm-nsp.h>
phy@1800c000 {
compatible = "brcm,ns-usb2-phy";
reg = <0x1800c000 0x1000>;
reg-names = "dmu";
clocks = <&genpll BCM_NSP_GENPLL_USB_PHY_REF_CLK>;
clock-names = "phy-ref-clk";
#phy-cells = <0>;
};

View file

@ -1,34 +0,0 @@
Driver for Broadcom Northstar USB 3.0 PHY
Required properties:
- compatible: one of: "brcm,ns-ax-usb3-phy", "brcm,ns-bx-usb3-phy".
- reg: address of MDIO bus device
- usb3-dmp-syscon: phandle to syscon with DMP (Device Management Plugin)
registers
- #phy-cells: must be 0
Initialization of USB 3.0 PHY depends on Northstar version. There are currently
three known series: Ax, Bx and Cx.
Known A0: BCM4707 rev 0
Known B0: BCM4707 rev 4, BCM53573 rev 2
Known B1: BCM4707 rev 6
Known C0: BCM47094 rev 0
Example:
mdio: mdio@0 {
reg = <0x0>;
#size-cells = <1>;
#address-cells = <0>;
usb3-phy@10 {
compatible = "brcm,ns-ax-usb3-phy";
reg = <0x10>;
usb3-dmp-syscon = <&usb3_dmp>;
#phy-cells = <0>;
};
};
usb3_dmp: syscon@18105000 {
reg = <0x18105000 0x1000>;
};

View file

@ -0,0 +1,62 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/bcm-ns-usb3-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom Northstar USB 3.0 PHY
description: |
Initialization of USB 3.0 PHY depends on Northstar version. There are currently
three known series: Ax, Bx and Cx.
Known A0: BCM4707 rev 0
Known B0: BCM4707 rev 4, BCM53573 rev 2
Known B1: BCM4707 rev 6
Known C0: BCM47094 rev 0
maintainers:
- Rafał Miłecki <rafal@milecki.pl>
properties:
compatible:
enum:
- brcm,ns-ax-usb3-phy
- brcm,ns-bx-usb3-phy
reg:
description: address of MDIO bus device
maxItems: 1
usb3-dmp-syscon:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to the DMP (Device Management Plugin) syscon
"#phy-cells":
const: 0
required:
- compatible
- reg
- usb3-dmp-syscon
- "#phy-cells"
additionalProperties: false
examples:
- |
mdio {
#address-cells = <1>;
#size-cells = <0>;
usb3-phy@10 {
compatible = "brcm,ns-ax-usb3-phy";
reg = <0x10>;
usb3-dmp-syscon = <&usb3_dmp>;
#phy-cells = <0>;
};
};
usb3_dmp: syscon@18105000 {
reg = <0x18105000 0x1000>;
};

View file

@ -42,6 +42,9 @@ properties:
- const: usb_mdio
- const: bdc_ec
power-domains:
maxItems: 1
clocks:
minItems: 1
maxItems: 2

View file

@ -0,0 +1,57 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/phy/marvell,armada-3700-utmi-phy.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Marvell Armada UTMI/UTMI+ PHY
maintainers:
- Miquel Raynal <miquel.raynal@bootlin.com>
description:
On Armada 3700, there are two USB controllers, one is compatible with
the USB2 and USB3 specifications and supports OTG. The other one is USB2
compliant and only supports host mode. Both of these controllers come with
a slightly different UTMI PHY.
properties:
compatible:
enum:
- marvell,a3700-utmi-host-phy
- marvell,a3700-utmi-otg-phy
reg:
maxItems: 1
"#phy-cells":
const: 0
marvell,usb-misc-reg:
description:
Phandle on the "USB miscellaneous registers" shared region
covering registers related to both the host controller and
the PHY.
$ref: /schemas/types.yaml#/definitions/phandle
required:
- compatible
- reg
- "#phy-cells"
- marvell,usb-misc-reg
additionalProperties: false
examples:
- |
usb2_utmi_host_phy: phy@5f000 {
compatible = "marvell,armada-3700-utmi-host-phy";
reg = <0x5f000 0x800>;
marvell,usb-misc-reg = <&usb2_syscon>;
#phy-cells = <0>;
};
usb2_syscon: system-controller@5f800 {
compatible = "marvell,armada-3700-usb2-host-misc", "syscon";
reg = <0x5f800 0x800>;
};

View file

@ -0,0 +1,109 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/phy/marvell,armada-cp110-utmi-phy.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Marvell Armada CP110/CP115 UTMI PHY
maintainers:
- Konstantin Porotchkin <kostap@marvell.com>
description:
On Armada 7k/8k and CN913x, there are two host and one device USB controllers.
Each of two exiting UTMI PHYs could be connected to either USB host or USB device
controller.
The USB device controller can only be connected to a single UTMI PHY port
0.H----- USB HOST0
UTMI PHY0 --------/
0.D-----0
\------ USB DEVICE
1.D-----1
UTMI PHY1 --------\
1.H----- USB HOST1
properties:
compatible:
const: marvell,cp110-utmi-phy
reg:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
marvell,system-controller:
description:
Phandle to the system controller node
$ref: /schemas/types.yaml#/definitions/phandle
#Required child nodes:
patternProperties:
"^usb-phy@[0|1]$":
type: object
description:
Each UTMI PHY port must be represented as a sub-node.
properties:
reg:
description: phy port index.
maxItems: 1
"#phy-cells":
const: 0
required:
- reg
- "#phy-cells"
additionalProperties: false
required:
- compatible
- reg
- "#address-cells"
- "#size-cells"
- marvell,system-controller
additionalProperties: false
examples:
- |
cp0_utmi: utmi@580000 {
compatible = "marvell,cp110-utmi-phy";
reg = <0x580000 0x2000>;
marvell,system-controller = <&cp0_syscon0>;
#address-cells = <1>;
#size-cells = <0>;
cp0_utmi0: usb-phy@0 {
reg = <0>;
#phy-cells = <0>;
};
cp0_utmi1: usb-phy@1 {
reg = <1>;
#phy-cells = <0>;
};
};
cp0_usb3_0 {
usb-phy = <&cp0_usb3_0_phy0>;
phys = <&cp0_utmi0>;
phy-names = "utmi";
/* UTMI0 is connected to USB host controller (default mode) */
dr_mode = "host";
};
cp0_usb3_1 {
usb-phy = <&cp0_usb3_0_phy1>;
phys = <&cp0_utmi1>;
phy-names = "utmi";
/* UTMI1 is connected to USB device controller */
dr_mode = "peripheral";
};

View file

@ -19,11 +19,14 @@ properties:
pattern: "^dsi-phy@[0-9a-f]+$"
compatible:
enum:
- mediatek,mt2701-mipi-tx
- mediatek,mt7623-mipi-tx
- mediatek,mt8173-mipi-tx
- mediatek,mt8183-mipi-tx
oneOf:
- items:
- enum:
- mediatek,mt7623-mipi-tx
- const: mediatek,mt2701-mipi-tx
- const: mediatek,mt2701-mipi-tx
- const: mediatek,mt8173-mipi-tx
- const: mediatek,mt8183-mipi-tx
reg:
maxItems: 1

View file

@ -21,10 +21,13 @@ properties:
pattern: "^hdmi-phy@[0-9a-f]+$"
compatible:
enum:
- mediatek,mt2701-hdmi-phy
- mediatek,mt7623-hdmi-phy
- mediatek,mt8173-hdmi-phy
oneOf:
- items:
- enum:
- mediatek,mt7623-hdmi-phy
- const: mediatek,mt2701-hdmi-phy
- const: mediatek,mt2701-hdmi-phy
- const: mediatek,mt8173-hdmi-phy
reg:
maxItems: 1

View file

@ -79,6 +79,7 @@ properties:
- mediatek,mt2712-tphy
- mediatek,mt7629-tphy
- mediatek,mt8183-tphy
- mediatek,mt8195-tphy
- const: mediatek,generic-tphy-v2
- const: mediatek,mt2701-u3phy
deprecated: true
@ -117,7 +118,7 @@ properties:
# Required child node:
patternProperties:
"^usb-phy@[0-9a-f]+$":
"^(usb|pcie|sata)-phy@[0-9a-f]+$":
type: object
description:
A sub-node is required for each port the controller provides.

View file

@ -22,7 +22,12 @@ properties:
pattern: "^ufs-phy@[0-9a-f]+$"
compatible:
const: mediatek,mt8183-ufsphy
oneOf:
- items:
- enum:
- mediatek,mt8195-ufsphy
- const: mediatek,mt8183-ufsphy
- const: mediatek,mt8183-ufsphy
reg:
maxItems: 1

View file

@ -0,0 +1,100 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/microchip,sparx5-serdes.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip Sparx5 Serdes controller
maintainers:
- Steen Hegelund <steen.hegelund@microchip.com>
description: |
The Sparx5 SERDES interfaces share the same basic functionality, but
support different operating modes and line rates.
The following list lists the SERDES features:
* RX Adaptive Decision Feedback Equalizer (DFE)
* Programmable continuous time linear equalizer (CTLE)
* Rx variable gain control
* Rx built-in fault detector (loss-of-lock/loss-of-signal)
* Adjustable tx de-emphasis (FFE)
* Tx output amplitude control
* Supports rx eye monitor
* Multiple loopback modes
* Prbs generator and checker
* Polarity inversion control
SERDES6G:
The SERDES6G is a high-speed SERDES interface, which can operate at
the following data rates:
* 100 Mbps (100BASE-FX)
* 1.25 Gbps (SGMII/1000BASE-X/1000BASE-KX)
* 3.125 Gbps (2.5GBASE-X/2.5GBASE-KX)
* 5.15625 Gbps (5GBASE-KR/5G-USXGMII)
SERDES10G
The SERDES10G is a high-speed SERDES interface, which can operate at
the following data rates:
* 100 Mbps (100BASE-FX)
* 1.25 Gbps (SGMII/1000BASE-X/1000BASE-KX)
* 3.125 Gbps (2.5GBASE-X/2.5GBASE-KX)
* 5 Gbps (QSGMII/USGMII)
* 5.15625 Gbps (5GBASE-KR/5G-USXGMII)
* 10 Gbps (10G-USGMII)
* 10.3125 Gbps (10GBASE-R/10GBASE-KR/USXGMII)
SERDES25G
The SERDES25G is a high-speed SERDES interface, which can operate at
the following data rates:
* 1.25 Gbps (SGMII/1000BASE-X/1000BASE-KX)
* 3.125 Gbps (2.5GBASE-X/2.5GBASE-KX)
* 5 Gbps (QSGMII/USGMII)
* 5.15625 Gbps (5GBASE-KR/5G-USXGMII)
* 10 Gbps (10G-USGMII)
* 10.3125 Gbps (10GBASE-R/10GBASE-KR/USXGMII)
* 25.78125 Gbps (25GBASE-KR/25GBASE-CR/25GBASE-SR/25GBASE-LR/25GBASE-ER)
properties:
$nodename:
pattern: "^serdes@[0-9a-f]+$"
compatible:
const: microchip,sparx5-serdes
reg:
minItems: 1
'#phy-cells':
const: 1
description: |
- The main serdes input port
clocks:
maxItems: 1
required:
- compatible
- reg
- '#phy-cells'
- clocks
additionalProperties: false
examples:
- |
serdes: serdes@10808000 {
compatible = "microchip,sparx5-serdes";
#phy-cells = <1>;
clocks = <&sys_clk>;
reg = <0x10808000 0x5d0000>;
};
...

View file

@ -26,6 +26,9 @@ properties:
'#size-cells':
const: 0
'#clock-cells':
const: 1
resets:
minItems: 1
maxItems: 2
@ -49,12 +52,24 @@ properties:
const: serdes
clocks:
maxItems: 2
minItems: 2
maxItems: 4
clock-names:
minItems: 2
items:
- const: cmn_refclk_dig_div
- const: cmn_refclk1_dig_div
- const: pll0_refclk
- const: pll1_refclk
assigned-clocks:
minItems: 1
maxItems: 2
assigned-clock-parents:
minItems: 1
maxItems: 2
cdns,autoconf:
type: boolean

View file

@ -28,13 +28,27 @@ properties:
'#size-cells':
const: 0
'#clock-cells':
const: 1
clocks:
maxItems: 1
minItems: 1
maxItems: 2
description:
PHY reference clock. Must contain an entry in clock-names.
PHY reference clock for 1 item. Must contain an entry in clock-names.
Optional Parent to enable output reference clock.
clock-names:
const: refclk
minItems: 1
items:
- const: refclk
- const: phy_en_refclk
assigned-clocks:
maxItems: 3
assigned-clock-parents:
maxItems: 3
reg:
minItems: 1
@ -170,7 +184,7 @@ examples:
};
- |
#include <dt-bindings/phy/phy.h>
#include <dt-bindings/phy/phy-cadence-torrent.h>
#include <dt-bindings/phy/phy-cadence.h>
bus {
#address-cells = <2>;

View file

@ -1,38 +0,0 @@
MVEBU A3700 UTMI PHY
--------------------
USB2 UTMI+ PHY controllers can be found on the following Marvell MVEBU SoCs:
* Armada 3700
On Armada 3700, there are two USB controllers, one is compatible with the USB2
and USB3 specifications and supports OTG. The other one is USB2 compliant and
only supports host mode. Both of these controllers come with a slightly
different UTMI PHY.
Required Properties:
- compatible: Should be one of:
* "marvell,a3700-utmi-host-phy" for the PHY connected to
the USB2 host-only controller.
* "marvell,a3700-utmi-otg-phy" for the PHY connected to
the USB3 and USB2 OTG capable controller.
- reg: PHY IP register range.
- marvell,usb-misc-reg: handle on the "USB miscellaneous registers" shared
region covering registers related to both the host
controller and the PHY.
- #phy-cells: Standard property (Documentation: phy-bindings.txt) Should be 0.
Example:
usb2_utmi_host_phy: phy@5f000 {
compatible = "marvell,armada-3700-utmi-host-phy";
reg = <0x5f000 0x800>;
marvell,usb-misc-reg = <&usb2_syscon>;
#phy-cells = <0>;
};
usb2_syscon: system-controller@5f800 {
compatible = "marvell,armada-3700-usb2-host-misc", "syscon";
reg = <0x5f800 0x800>;
};

View file

@ -51,6 +51,10 @@ properties:
vdda1v8-supply:
description: regulator providing 1V8 power supply to the PLL block
'#clock-cells':
description: number of clock cells for ck_usbo_48m consumer
const: 0
#Required child nodes:
patternProperties:
@ -120,6 +124,7 @@ examples:
vdda1v8-supply = <&reg18>;
#address-cells = <1>;
#size-cells = <0>;
#clock-cells = <0>;
usbphyc_port0: usb-phy@0 {
reg = <0>;

View file

@ -25,11 +25,13 @@ properties:
- qcom,msm8998-qmp-pcie-phy
- qcom,msm8998-qmp-ufs-phy
- qcom,msm8998-qmp-usb3-phy
- qcom,sc7180-qmp-usb3-phy
- qcom,sc8180x-qmp-ufs-phy
- qcom,sc8180x-qmp-usb3-phy
- qcom,sdm845-qhp-pcie-phy
- qcom,sdm845-qmp-pcie-phy
- qcom,sdm845-qmp-ufs-phy
- qcom,sdm845-qmp-usb3-phy
- qcom,sdm845-qmp-usb3-uni-phy
- qcom,sm8150-qmp-ufs-phy
- qcom,sm8150-qmp-usb3-phy

View file

@ -14,9 +14,8 @@ properties:
compatible:
enum:
- qcom,sc7180-qmp-usb3-dp-phy
- qcom,sc7180-qmp-usb3-phy
- qcom,sdm845-qmp-usb3-dp-phy
- qcom,sdm845-qmp-usb3-phy
- qcom,sm8250-qmp-usb3-dp-phy
reg:
items:
- description: Address and length of PHY's USB serdes block.

View file

@ -16,6 +16,7 @@ properties:
compatible:
enum:
- qcom,usb-snps-hs-7nm-phy
- qcom,sc7280-usb-hs-phy
- qcom,sm8150-usb-hs-phy
- qcom,sm8250-usb-hs-phy
- qcom,sm8350-usb-hs-phy

View file

@ -15,6 +15,7 @@ properties:
enum:
- ti,j721e-wiz-16g
- ti,j721e-wiz-10g
- ti,am64-wiz-10g
power-domains:
maxItems: 1
@ -42,6 +43,9 @@ properties:
"#reset-cells":
const: 1
"#clock-cells":
const: 1
ranges: true
assigned-clocks:

View file

@ -71,6 +71,7 @@ source "drivers/phy/ingenic/Kconfig"
source "drivers/phy/lantiq/Kconfig"
source "drivers/phy/marvell/Kconfig"
source "drivers/phy/mediatek/Kconfig"
source "drivers/phy/microchip/Kconfig"
source "drivers/phy/motorola/Kconfig"
source "drivers/phy/mscc/Kconfig"
source "drivers/phy/qualcomm/Kconfig"

View file

@ -20,6 +20,7 @@ obj-y += allwinner/ \
lantiq/ \
marvell/ \
mediatek/ \
microchip/ \
motorola/ \
mscc/ \
qualcomm/ \

View file

@ -94,7 +94,7 @@ config PHY_BRCM_USB
depends on ARCH_BCM4908 || ARCH_BRCMSTB || COMPILE_TEST
depends on OF
select GENERIC_PHY
select SOC_BRCMSTB
select SOC_BRCMSTB if ARCH_BRCMSTB
default ARCH_BCM4908
default ARCH_BRCMSTB
help

View file

@ -7,6 +7,7 @@ config PHY_CADENCE_TORRENT
tristate "Cadence Torrent PHY driver"
depends on OF
depends on HAS_IOMEM
depends on COMMON_CLK
select GENERIC_PHY
help
Support for Cadence Torrent PHY.
@ -24,6 +25,7 @@ config PHY_CADENCE_DPHY
config PHY_CADENCE_SIERRA
tristate "Cadence Sierra PHY Driver"
depends on OF && HAS_IOMEM && RESET_CONTROLLER
depends on COMMON_CLK
select GENERIC_PHY
help
Enable this to support the Cadence Sierra PHY driver

View file

@ -7,6 +7,7 @@
*
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
@ -20,10 +21,12 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <dt-bindings/phy/phy.h>
#include <dt-bindings/phy/phy-cadence.h>
/* PHY register offsets */
#define SIERRA_COMMON_CDB_OFFSET 0x0
#define SIERRA_MACRO_ID_REG 0x0
#define SIERRA_CMN_PLLLC_GEN_PREG 0x42
#define SIERRA_CMN_PLLLC_MODE_PREG 0x48
#define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG 0x49
#define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG 0x4A
@ -31,6 +34,9 @@
#define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F
#define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50
#define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG 0x62
#define SIERRA_CMN_REFRCV_PREG 0x98
#define SIERRA_CMN_REFRCV1_PREG 0xB8
#define SIERRA_CMN_PLLLC1_GEN_PREG 0xC2
#define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
((0x4000 << (block_offset)) + \
@ -144,6 +150,19 @@
#define SIERRA_MAX_LANES 16
#define PLL_LOCK_TIME 100000
#define CDNS_SIERRA_OUTPUT_CLOCKS 2
#define CDNS_SIERRA_INPUT_CLOCKS 5
enum cdns_sierra_clock_input {
PHY_CLK,
CMN_REFCLK_DIG_DIV,
CMN_REFCLK1_DIG_DIV,
PLL0_REFCLK,
PLL1_REFCLK,
};
#define SIERRA_NUM_CMN_PLLC 2
#define SIERRA_NUM_CMN_PLLC_PARENTS 2
static const struct reg_field macro_id_type =
REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15);
static const struct reg_field phy_pll_cfg_1 =
@ -151,6 +170,53 @@ static const struct reg_field phy_pll_cfg_1 =
static const struct reg_field pllctrl_lock =
REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0, 0);
static const char * const clk_names[] = {
[CDNS_SIERRA_PLL_CMNLC] = "pll_cmnlc",
[CDNS_SIERRA_PLL_CMNLC1] = "pll_cmnlc1",
};
enum cdns_sierra_cmn_plllc {
CMN_PLLLC,
CMN_PLLLC1,
};
struct cdns_sierra_pll_mux_reg_fields {
struct reg_field pfdclk_sel_preg;
struct reg_field plllc1en_field;
struct reg_field termen_field;
};
static const struct cdns_sierra_pll_mux_reg_fields cmn_plllc_pfdclk1_sel_preg[] = {
[CMN_PLLLC] = {
.pfdclk_sel_preg = REG_FIELD(SIERRA_CMN_PLLLC_GEN_PREG, 1, 1),
.plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 8, 8),
.termen_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 0, 0),
},
[CMN_PLLLC1] = {
.pfdclk_sel_preg = REG_FIELD(SIERRA_CMN_PLLLC1_GEN_PREG, 1, 1),
.plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 8, 8),
.termen_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 0, 0),
},
};
struct cdns_sierra_pll_mux {
struct clk_hw hw;
struct regmap_field *pfdclk_sel_preg;
struct regmap_field *plllc1en_field;
struct regmap_field *termen_field;
struct clk_init_data clk_data;
};
#define to_cdns_sierra_pll_mux(_hw) \
container_of(_hw, struct cdns_sierra_pll_mux, hw)
static const int pll_mux_parent_index[][SIERRA_NUM_CMN_PLLC_PARENTS] = {
[CMN_PLLLC] = { PLL0_REFCLK, PLL1_REFCLK },
[CMN_PLLLC1] = { PLL1_REFCLK, PLL0_REFCLK },
};
static u32 cdns_sierra_pll_mux_table[] = { 0, 1 };
struct cdns_sierra_inst {
struct phy *phy;
u32 phy_type;
@ -197,12 +263,15 @@ struct cdns_sierra_phy {
struct regmap_field *macro_id_type;
struct regmap_field *phy_pll_cfg_1;
struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES];
struct clk *clk;
struct clk *cmn_refclk_dig_div;
struct clk *cmn_refclk1_dig_div;
struct regmap_field *cmn_refrcv_refclk_plllc1en_preg[SIERRA_NUM_CMN_PLLC];
struct regmap_field *cmn_refrcv_refclk_termen_preg[SIERRA_NUM_CMN_PLLC];
struct regmap_field *cmn_plllc_pfdclk1_sel_preg[SIERRA_NUM_CMN_PLLC];
struct clk *input_clks[CDNS_SIERRA_INPUT_CLOCKS];
int nsubnodes;
u32 num_lanes;
bool autoconf;
struct clk_onecell_data clk_data;
struct clk *output_clks[CDNS_SIERRA_OUTPUT_CLOCKS];
};
static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val)
@ -281,8 +350,8 @@ static int cdns_sierra_phy_init(struct phy *gphy)
if (phy->autoconf)
return 0;
clk_set_rate(phy->cmn_refclk_dig_div, 25000000);
clk_set_rate(phy->cmn_refclk1_dig_div, 25000000);
clk_set_rate(phy->input_clks[CMN_REFCLK_DIG_DIV], 25000000);
clk_set_rate(phy->input_clks[CMN_REFCLK1_DIG_DIV], 25000000);
if (ins->phy_type == PHY_TYPE_PCIE) {
num_cmn_regs = phy->init_data->pcie_cmn_regs;
num_ln_regs = phy->init_data->pcie_ln_regs;
@ -319,6 +388,12 @@ static int cdns_sierra_phy_on(struct phy *gphy)
u32 val;
int ret;
ret = reset_control_deassert(sp->phy_rst);
if (ret) {
dev_err(dev, "Failed to take the PHY out of reset\n");
return ret;
}
/* Take the PHY lane group out of reset */
ret = reset_control_deassert(ins->lnk_rst);
if (ret) {
@ -358,6 +433,153 @@ static const struct phy_ops ops = {
.owner = THIS_MODULE,
};
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 *field = mux->pfdclk_sel_preg;
unsigned int val;
regmap_field_read(field, &val);
return clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table, 0, val);
}
static int cdns_sierra_pll_mux_set_parent(struct clk_hw *hw, u8 index)
{
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;
int val, ret;
ret = regmap_field_write(plllc1en_field, 0);
ret |= regmap_field_write(termen_field, 0);
if (index == 1) {
ret |= regmap_field_write(plllc1en_field, 1);
ret |= regmap_field_write(termen_field, 1);
}
val = cdns_sierra_pll_mux_table[index];
ret |= regmap_field_write(field, val);
return ret;
}
static const struct clk_ops cdns_sierra_pll_mux_ops = {
.set_parent = cdns_sierra_pll_mux_set_parent,
.get_parent = cdns_sierra_pll_mux_get_parent,
};
static int cdns_sierra_pll_mux_register(struct cdns_sierra_phy *sp,
struct regmap_field *pfdclk1_sel_field,
struct regmap_field *plllc1en_field,
struct regmap_field *termen_field,
int clk_index)
{
struct cdns_sierra_pll_mux *mux;
struct device *dev = sp->dev;
struct clk_init_data *init;
const char **parent_names;
unsigned int num_parents;
char clk_name[100];
struct clk *clk;
int i;
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return -ENOMEM;
num_parents = SIERRA_NUM_CMN_PLLC_PARENTS;
parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), GFP_KERNEL);
if (!parent_names)
return -ENOMEM;
for (i = 0; i < num_parents; i++) {
clk = sp->input_clks[pll_mux_parent_index[clk_index][i]];
if (IS_ERR_OR_NULL(clk)) {
dev_err(dev, "No parent clock for derived_refclk\n");
return PTR_ERR(clk);
}
parent_names[i] = __clk_get_name(clk);
}
snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), clk_names[clk_index]);
init = &mux->clk_data;
init->ops = &cdns_sierra_pll_mux_ops;
init->flags = CLK_SET_RATE_NO_REPARENT;
init->parent_names = parent_names;
init->num_parents = num_parents;
init->name = clk_name;
mux->pfdclk_sel_preg = pfdclk1_sel_field;
mux->plllc1en_field = plllc1en_field;
mux->termen_field = termen_field;
mux->hw.init = init;
clk = devm_clk_register(dev, &mux->hw);
if (IS_ERR(clk))
return PTR_ERR(clk);
sp->output_clks[clk_index] = clk;
return 0;
}
static int cdns_sierra_phy_register_pll_mux(struct cdns_sierra_phy *sp)
{
struct regmap_field *pfdclk1_sel_field;
struct regmap_field *plllc1en_field;
struct regmap_field *termen_field;
struct device *dev = sp->dev;
int ret = 0, i, clk_index;
clk_index = CDNS_SIERRA_PLL_CMNLC;
for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++, clk_index++) {
pfdclk1_sel_field = sp->cmn_plllc_pfdclk1_sel_preg[i];
plllc1en_field = sp->cmn_refrcv_refclk_plllc1en_preg[i];
termen_field = sp->cmn_refrcv_refclk_termen_preg[i];
ret = cdns_sierra_pll_mux_register(sp, pfdclk1_sel_field, plllc1en_field,
termen_field, clk_index);
if (ret) {
dev_err(dev, "Fail to register cmn plllc mux\n");
return ret;
}
}
return 0;
}
static void cdns_sierra_clk_unregister(struct cdns_sierra_phy *sp)
{
struct device *dev = sp->dev;
struct device_node *node = dev->of_node;
of_clk_del_provider(node);
}
static int cdns_sierra_clk_register(struct cdns_sierra_phy *sp)
{
struct device *dev = sp->dev;
struct device_node *node = dev->of_node;
int ret;
ret = cdns_sierra_phy_register_pll_mux(sp);
if (ret) {
dev_err(dev, "Failed to pll mux clocks\n");
return ret;
}
sp->clk_data.clks = sp->output_clks;
sp->clk_data.clk_num = CDNS_SIERRA_OUTPUT_CLOCKS;
ret = of_clk_add_provider(node, of_clk_src_onecell_get, &sp->clk_data);
if (ret)
dev_err(dev, "Failed to add clock provider: %s\n", node->name);
return ret;
}
static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst,
struct device_node *child)
{
@ -396,6 +618,7 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp)
{
struct device *dev = sp->dev;
struct regmap_field *field;
struct reg_field reg_field;
struct regmap *regmap;
int i;
@ -407,6 +630,32 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp)
}
sp->macro_id_type = field;
for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++) {
reg_field = cmn_plllc_pfdclk1_sel_preg[i].pfdclk_sel_preg;
field = devm_regmap_field_alloc(dev, regmap, reg_field);
if (IS_ERR(field)) {
dev_err(dev, "PLLLC%d_PFDCLK1_SEL failed\n", i);
return PTR_ERR(field);
}
sp->cmn_plllc_pfdclk1_sel_preg[i] = field;
reg_field = cmn_plllc_pfdclk1_sel_preg[i].plllc1en_field;
field = devm_regmap_field_alloc(dev, regmap, reg_field);
if (IS_ERR(field)) {
dev_err(dev, "REFRCV%d_REFCLK_PLLLC1EN failed\n", i);
return PTR_ERR(field);
}
sp->cmn_refrcv_refclk_plllc1en_preg[i] = field;
reg_field = cmn_plllc_pfdclk1_sel_preg[i].termen_field;
field = devm_regmap_field_alloc(dev, regmap, reg_field);
if (IS_ERR(field)) {
dev_err(dev, "REFRCV%d_REFCLK_TERMEN failed\n", i);
return PTR_ERR(field);
}
sp->cmn_refrcv_refclk_termen_preg[i] = field;
}
regmap = sp->regmap_phy_config_ctrl;
field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1);
if (IS_ERR(field)) {
@ -471,6 +720,110 @@ static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp,
return 0;
}
static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp,
struct device *dev)
{
struct clk *clk;
int ret;
clk = devm_clk_get_optional(dev, "phy_clk");
if (IS_ERR(clk)) {
dev_err(dev, "failed to get clock phy_clk\n");
return PTR_ERR(clk);
}
sp->input_clks[PHY_CLK] = clk;
clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div");
if (IS_ERR(clk)) {
dev_err(dev, "cmn_refclk_dig_div clock not found\n");
ret = PTR_ERR(clk);
return ret;
}
sp->input_clks[CMN_REFCLK_DIG_DIV] = clk;
clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div");
if (IS_ERR(clk)) {
dev_err(dev, "cmn_refclk1_dig_div clock not found\n");
ret = PTR_ERR(clk);
return ret;
}
sp->input_clks[CMN_REFCLK1_DIG_DIV] = clk;
clk = devm_clk_get_optional(dev, "pll0_refclk");
if (IS_ERR(clk)) {
dev_err(dev, "pll0_refclk clock not found\n");
ret = PTR_ERR(clk);
return ret;
}
sp->input_clks[PLL0_REFCLK] = clk;
clk = devm_clk_get_optional(dev, "pll1_refclk");
if (IS_ERR(clk)) {
dev_err(dev, "pll1_refclk clock not found\n");
ret = PTR_ERR(clk);
return ret;
}
sp->input_clks[PLL1_REFCLK] = clk;
return 0;
}
static int cdns_sierra_phy_enable_clocks(struct cdns_sierra_phy *sp)
{
int ret;
ret = clk_prepare_enable(sp->input_clks[PHY_CLK]);
if (ret)
return ret;
ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
if (ret)
goto err_pll_cmnlc;
ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]);
if (ret)
goto err_pll_cmnlc1;
return 0;
err_pll_cmnlc1:
clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
err_pll_cmnlc:
clk_disable_unprepare(sp->input_clks[PHY_CLK]);
return ret;
}
static void cdns_sierra_phy_disable_clocks(struct cdns_sierra_phy *sp)
{
clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]);
clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
clk_disable_unprepare(sp->input_clks[PHY_CLK]);
}
static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp,
struct device *dev)
{
struct reset_control *rst;
rst = devm_reset_control_get_exclusive(dev, "sierra_reset");
if (IS_ERR(rst)) {
dev_err(dev, "failed to get reset\n");
return PTR_ERR(rst);
}
sp->phy_rst = rst;
rst = devm_reset_control_get_optional_exclusive(dev, "sierra_apb");
if (IS_ERR(rst)) {
dev_err(dev, "failed to get apb reset\n");
return PTR_ERR(rst);
}
sp->apb_rst = rst;
return 0;
}
static int cdns_sierra_phy_probe(struct platform_device *pdev)
{
struct cdns_sierra_phy *sp;
@ -481,7 +834,6 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
unsigned int id_value;
int i, ret, node = 0;
void __iomem *base;
struct clk *clk;
struct device_node *dn = dev->of_node, *child;
if (of_get_child_count(dn) == 0)
@ -518,44 +870,22 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, sp);
sp->clk = devm_clk_get_optional(dev, "phy_clk");
if (IS_ERR(sp->clk)) {
dev_err(dev, "failed to get clock phy_clk\n");
return PTR_ERR(sp->clk);
}
sp->phy_rst = devm_reset_control_get(dev, "sierra_reset");
if (IS_ERR(sp->phy_rst)) {
dev_err(dev, "failed to get reset\n");
return PTR_ERR(sp->phy_rst);
}
sp->apb_rst = devm_reset_control_get_optional(dev, "sierra_apb");
if (IS_ERR(sp->apb_rst)) {
dev_err(dev, "failed to get apb reset\n");
return PTR_ERR(sp->apb_rst);
}
clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div");
if (IS_ERR(clk)) {
dev_err(dev, "cmn_refclk_dig_div clock not found\n");
ret = PTR_ERR(clk);
return ret;
}
sp->cmn_refclk_dig_div = clk;
clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div");
if (IS_ERR(clk)) {
dev_err(dev, "cmn_refclk1_dig_div clock not found\n");
ret = PTR_ERR(clk);
return ret;
}
sp->cmn_refclk1_dig_div = clk;
ret = clk_prepare_enable(sp->clk);
ret = cdns_sierra_phy_get_clocks(sp, dev);
if (ret)
return ret;
ret = cdns_sierra_clk_register(sp);
if (ret)
return ret;
ret = cdns_sierra_phy_get_resets(sp, dev);
if (ret)
goto unregister_clk;
ret = cdns_sierra_phy_enable_clocks(sp);
if (ret)
goto unregister_clk;
/* Enable APB */
reset_control_deassert(sp->apb_rst);
@ -571,6 +901,10 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
for_each_available_child_of_node(dn, child) {
struct phy *gphy;
if (!(of_node_name_eq(child, "phy") ||
of_node_name_eq(child, "link")))
continue;
sp->phys[node].lnk_rst =
of_reset_control_array_get_exclusive(child);
@ -616,7 +950,6 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
reset_control_deassert(sp->phy_rst);
return PTR_ERR_OR_ZERO(phy_provider);
put_child:
@ -626,8 +959,10 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
reset_control_put(sp->phys[i].lnk_rst);
of_node_put(child);
clk_disable:
clk_disable_unprepare(sp->clk);
cdns_sierra_phy_disable_clocks(sp);
reset_control_assert(sp->apb_rst);
unregister_clk:
cdns_sierra_clk_unregister(sp);
return ret;
}
@ -640,6 +975,7 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev)
reset_control_assert(phy->apb_rst);
pm_runtime_disable(&pdev->dev);
cdns_sierra_phy_disable_clocks(phy);
/*
* The device level resets will be put automatically.
* Need to put the subnode resets here though.
@ -648,6 +984,9 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev)
reset_control_assert(phy->phys[i].lnk_rst);
reset_control_put(phy->phys[i].lnk_rst);
}
cdns_sierra_clk_unregister(phy);
return 0;
}

View file

@ -7,7 +7,9 @@
*/
#include <dt-bindings/phy/phy.h>
#include <dt-bindings/phy/phy-cadence.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
@ -84,6 +86,8 @@
#define CMN_PLLSM1_PLLLOCK_TMR 0x0034U
#define CMN_CDIAG_CDB_PWRI_OVRD 0x0041U
#define CMN_CDIAG_XCVRC_PWRI_OVRD 0x0047U
#define CMN_CDIAG_REFCLK_OVRD 0x004CU
#define CMN_CDIAG_REFCLK_DRV0_CTRL 0x0050U
#define CMN_BGCAL_INIT_TMR 0x0064U
#define CMN_BGCAL_ITER_TMR 0x0065U
#define CMN_IBCAL_INIT_TMR 0x0074U
@ -122,6 +126,8 @@
#define CMN_PLL1_FRACDIVH_M0 0x00D2U
#define CMN_PLL1_HIGH_THR_M0 0x00D3U
#define CMN_PLL1_DSM_DIAG_M0 0x00D4U
#define CMN_PLL1_DSM_FBH_OVRD_M0 0x00D5U
#define CMN_PLL1_DSM_FBL_OVRD_M0 0x00D6U
#define CMN_PLL1_SS_CTRL1_M0 0x00D8U
#define CMN_PLL1_SS_CTRL2_M0 0x00D9U
#define CMN_PLL1_SS_CTRL3_M0 0x00DAU
@ -163,10 +169,12 @@
#define TX_TXCC_CPOST_MULT_00 0x004CU
#define TX_TXCC_CPOST_MULT_01 0x004DU
#define TX_TXCC_MGNFS_MULT_000 0x0050U
#define TX_TXCC_MGNFS_MULT_100 0x0054U
#define DRV_DIAG_TX_DRV 0x00C6U
#define XCVR_DIAG_PLLDRC_CTRL 0x00E5U
#define XCVR_DIAG_HSCLK_SEL 0x00E6U
#define XCVR_DIAG_HSCLK_DIV 0x00E7U
#define XCVR_DIAG_RXCLK_CTRL 0x00E9U
#define XCVR_DIAG_BIDI_CTRL 0x00EAU
#define XCVR_DIAG_PSC_OVRD 0x00EBU
#define TX_PSC_A0 0x0100U
@ -206,6 +214,7 @@
#define RX_DIAG_ACYA 0x01FFU
/* PHY PCS common registers */
#define PHY_PIPE_CMN_CTRL1 0x0000U
#define PHY_PLL_CFG 0x000EU
#define PHY_PIPE_USB3_GEN2_PRE_CFG0 0x0020U
#define PHY_PIPE_USB3_GEN2_POST_CFG0 0x0022U
@ -216,6 +225,10 @@
#define PHY_PMA_CMN_CTRL2 0x0001U
#define PHY_PMA_PLL_RAW_CTRL 0x0003U
static const char * const clk_names[] = {
[CDNS_TORRENT_REFCLK_DRIVER] = "refclk-driver",
};
static const struct reg_field phy_pll_cfg =
REG_FIELD(PHY_PLL_CFG, 0, 1);
@ -231,6 +244,26 @@ static const struct reg_field phy_pma_pll_raw_ctrl =
static const struct reg_field phy_reset_ctrl =
REG_FIELD(PHY_RESET, 8, 8);
static const struct reg_field phy_pipe_cmn_ctrl1_0 = REG_FIELD(PHY_PIPE_CMN_CTRL1, 0, 0);
#define REFCLK_OUT_NUM_CMN_CONFIG 5
enum cdns_torrent_refclk_out_cmn {
CMN_CDIAG_REFCLK_OVRD_4,
CMN_CDIAG_REFCLK_DRV0_CTRL_1,
CMN_CDIAG_REFCLK_DRV0_CTRL_4,
CMN_CDIAG_REFCLK_DRV0_CTRL_5,
CMN_CDIAG_REFCLK_DRV0_CTRL_6,
};
static const struct reg_field refclk_out_cmn_cfg[] = {
[CMN_CDIAG_REFCLK_OVRD_4] = REG_FIELD(CMN_CDIAG_REFCLK_OVRD, 4, 4),
[CMN_CDIAG_REFCLK_DRV0_CTRL_1] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 1, 1),
[CMN_CDIAG_REFCLK_DRV0_CTRL_4] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 4, 4),
[CMN_CDIAG_REFCLK_DRV0_CTRL_5] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 5, 5),
[CMN_CDIAG_REFCLK_DRV0_CTRL_6] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 6, 6),
};
enum cdns_torrent_phy_type {
TYPE_NONE,
TYPE_DP,
@ -279,6 +312,8 @@ struct cdns_torrent_phy {
struct regmap_field *phy_pma_cmn_ctrl_2;
struct regmap_field *phy_pma_pll_raw_ctrl;
struct regmap_field *phy_reset_ctrl;
struct clk *clks[CDNS_TORRENT_REFCLK_DRIVER + 1];
struct clk_onecell_data clk_data;
};
enum phy_powerstate {
@ -288,6 +323,16 @@ enum phy_powerstate {
POWERSTATE_A3 = 3,
};
struct cdns_torrent_derived_refclk {
struct clk_hw hw;
struct regmap_field *phy_pipe_cmn_ctrl1_0;
struct regmap_field *cmn_fields[REFCLK_OUT_NUM_CMN_CONFIG];
struct clk_init_data clk_data;
};
#define to_cdns_torrent_derived_refclk(_hw) \
container_of(_hw, struct cdns_torrent_derived_refclk, hw)
static int cdns_torrent_phy_init(struct phy *phy);
static int cdns_torrent_dp_init(struct phy *phy);
static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy,
@ -326,6 +371,19 @@ static const struct phy_ops cdns_torrent_phy_ops = {
.owner = THIS_MODULE,
};
static int cdns_torrent_noop_phy_on(struct phy *phy)
{
/* Give 5ms to 10ms delay for the PIPE clock to be stable */
usleep_range(5000, 10000);
return 0;
}
static const struct phy_ops noop_ops = {
.power_on = cdns_torrent_noop_phy_on,
.owner = THIS_MODULE,
};
struct cdns_reg_pairs {
u32 val;
u32 off;
@ -1604,6 +1662,108 @@ static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, u32 num_lanes)
return ret;
}
static int cdns_torrent_derived_refclk_enable(struct clk_hw *hw)
{
struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw);
regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_6], 0);
regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1);
regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_5], 1);
regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 0);
regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_OVRD_4], 1);
regmap_field_write(derived_refclk->phy_pipe_cmn_ctrl1_0, 1);
return 0;
}
static void cdns_torrent_derived_refclk_disable(struct clk_hw *hw)
{
struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw);
regmap_field_write(derived_refclk->phy_pipe_cmn_ctrl1_0, 0);
}
static int cdns_torrent_derived_refclk_is_enabled(struct clk_hw *hw)
{
struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw);
int val;
regmap_field_read(derived_refclk->phy_pipe_cmn_ctrl1_0, &val);
return !!val;
}
static const struct clk_ops cdns_torrent_derived_refclk_ops = {
.enable = cdns_torrent_derived_refclk_enable,
.disable = cdns_torrent_derived_refclk_disable,
.is_enabled = cdns_torrent_derived_refclk_is_enabled,
};
static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_phy)
{
struct cdns_torrent_derived_refclk *derived_refclk;
struct device *dev = cdns_phy->dev;
struct regmap_field *field;
struct clk_init_data *init;
const char *parent_name;
struct regmap *regmap;
char clk_name[100];
struct clk *clk;
int i;
derived_refclk = devm_kzalloc(dev, sizeof(*derived_refclk), GFP_KERNEL);
if (!derived_refclk)
return -ENOMEM;
snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
clk_names[CDNS_TORRENT_REFCLK_DRIVER]);
clk = devm_clk_get_optional(dev, "phy_en_refclk");
if (IS_ERR(clk)) {
dev_err(dev, "No parent clock for derived_refclk\n");
return PTR_ERR(clk);
}
init = &derived_refclk->clk_data;
if (clk) {
parent_name = __clk_get_name(clk);
init->parent_names = &parent_name;
init->num_parents = 1;
}
init->ops = &cdns_torrent_derived_refclk_ops;
init->flags = 0;
init->name = clk_name;
regmap = cdns_phy->regmap_phy_pcs_common_cdb;
field = devm_regmap_field_alloc(dev, regmap, phy_pipe_cmn_ctrl1_0);
if (IS_ERR(field)) {
dev_err(dev, "phy_pipe_cmn_ctrl1_0 reg field init failed\n");
return PTR_ERR(field);
}
derived_refclk->phy_pipe_cmn_ctrl1_0 = field;
regmap = cdns_phy->regmap_common_cdb;
for (i = 0; i < REFCLK_OUT_NUM_CMN_CONFIG; i++) {
field = devm_regmap_field_alloc(dev, regmap, refclk_out_cmn_cfg[i]);
if (IS_ERR(field)) {
dev_err(dev, "CMN reg field init failed\n");
return PTR_ERR(field);
}
derived_refclk->cmn_fields[i] = field;
}
derived_refclk->hw.init = init;
clk = devm_clk_register(dev, &derived_refclk->hw);
if (IS_ERR(clk))
return PTR_ERR(clk);
cdns_phy->clks[CDNS_TORRENT_REFCLK_DRIVER] = clk;
return 0;
}
static int cdns_torrent_phy_on(struct phy *phy)
{
struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
@ -2071,6 +2231,85 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy)
return 0;
}
static void cdns_torrent_clk_cleanup(struct cdns_torrent_phy *cdns_phy)
{
struct device *dev = cdns_phy->dev;
of_clk_del_provider(dev->of_node);
}
static int cdns_torrent_clk_register(struct cdns_torrent_phy *cdns_phy)
{
struct device *dev = cdns_phy->dev;
struct device_node *node = dev->of_node;
int ret;
ret = cdns_torrent_derived_refclk_register(cdns_phy);
if (ret) {
dev_err(dev, "failed to register derived refclk\n");
return ret;
}
cdns_phy->clk_data.clks = cdns_phy->clks;
cdns_phy->clk_data.clk_num = CDNS_TORRENT_REFCLK_DRIVER + 1;
ret = of_clk_add_provider(node, of_clk_src_onecell_get, &cdns_phy->clk_data);
if (ret) {
dev_err(dev, "Failed to add clock provider: %s\n", node->name);
return ret;
}
return 0;
}
static int cdns_torrent_reset(struct cdns_torrent_phy *cdns_phy)
{
struct device *dev = cdns_phy->dev;
cdns_phy->phy_rst = devm_reset_control_get_exclusive_by_index(dev, 0);
if (IS_ERR(cdns_phy->phy_rst)) {
dev_err(dev, "%s: failed to get reset\n",
dev->of_node->full_name);
return PTR_ERR(cdns_phy->phy_rst);
}
cdns_phy->apb_rst = devm_reset_control_get_optional_exclusive(dev, "torrent_apb");
if (IS_ERR(cdns_phy->apb_rst)) {
dev_err(dev, "%s: failed to get apb reset\n",
dev->of_node->full_name);
return PTR_ERR(cdns_phy->apb_rst);
}
return 0;
}
static int cdns_torrent_clk(struct cdns_torrent_phy *cdns_phy)
{
struct device *dev = cdns_phy->dev;
int ret;
cdns_phy->clk = devm_clk_get(dev, "refclk");
if (IS_ERR(cdns_phy->clk)) {
dev_err(dev, "phy ref clock not found\n");
return PTR_ERR(cdns_phy->clk);
}
ret = clk_prepare_enable(cdns_phy->clk);
if (ret) {
dev_err(cdns_phy->dev, "Failed to prepare ref clock\n");
return ret;
}
cdns_phy->ref_clk_rate = clk_get_rate(cdns_phy->clk);
if (!(cdns_phy->ref_clk_rate)) {
dev_err(cdns_phy->dev, "Failed to get ref clock rate\n");
clk_disable_unprepare(cdns_phy->clk);
return -EINVAL;
}
return 0;
}
static int cdns_torrent_phy_probe(struct platform_device *pdev)
{
struct cdns_torrent_phy *cdns_phy;
@ -2080,6 +2319,7 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
struct device_node *child;
int ret, subnodes, node = 0, i;
u32 total_num_lanes = 0;
int already_configured;
u8 init_dp_regmap = 0;
u32 phy_type;
@ -2096,26 +2336,6 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
cdns_phy->dev = dev;
cdns_phy->init_data = data;
cdns_phy->phy_rst = devm_reset_control_get_exclusive_by_index(dev, 0);
if (IS_ERR(cdns_phy->phy_rst)) {
dev_err(dev, "%s: failed to get reset\n",
dev->of_node->full_name);
return PTR_ERR(cdns_phy->phy_rst);
}
cdns_phy->apb_rst = devm_reset_control_get_optional(dev, "torrent_apb");
if (IS_ERR(cdns_phy->apb_rst)) {
dev_err(dev, "%s: failed to get apb reset\n",
dev->of_node->full_name);
return PTR_ERR(cdns_phy->apb_rst);
}
cdns_phy->clk = devm_clk_get(dev, "refclk");
if (IS_ERR(cdns_phy->clk)) {
dev_err(dev, "phy ref clock not found\n");
return PTR_ERR(cdns_phy->clk);
}
cdns_phy->sd_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(cdns_phy->sd_base))
return PTR_ERR(cdns_phy->sd_base);
@ -2134,21 +2354,24 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
if (ret)
return ret;
ret = clk_prepare_enable(cdns_phy->clk);
if (ret) {
dev_err(cdns_phy->dev, "Failed to prepare ref clock\n");
ret = cdns_torrent_clk_register(cdns_phy);
if (ret)
return ret;
}
cdns_phy->ref_clk_rate = clk_get_rate(cdns_phy->clk);
if (!(cdns_phy->ref_clk_rate)) {
dev_err(cdns_phy->dev, "Failed to get ref clock rate\n");
clk_disable_unprepare(cdns_phy->clk);
return -EINVAL;
}
regmap_field_read(cdns_phy->phy_pma_cmn_ctrl_1, &already_configured);
/* Enable APB */
reset_control_deassert(cdns_phy->apb_rst);
if (!already_configured) {
ret = cdns_torrent_reset(cdns_phy);
if (ret)
goto clk_cleanup;
ret = cdns_torrent_clk(cdns_phy);
if (ret)
goto clk_cleanup;
/* Enable APB */
reset_control_deassert(cdns_phy->apb_rst);
}
for_each_available_child_of_node(dev->of_node, child) {
struct phy *gphy;
@ -2218,7 +2441,10 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
of_property_read_u32(child, "cdns,ssc-mode",
&cdns_phy->phys[node].ssc_mode);
gphy = devm_phy_create(dev, child, &cdns_torrent_phy_ops);
if (!already_configured)
gphy = devm_phy_create(dev, child, &cdns_torrent_phy_ops);
else
gphy = devm_phy_create(dev, child, &noop_ops);
if (IS_ERR(gphy)) {
ret = PTR_ERR(gphy);
goto put_child;
@ -2302,7 +2528,7 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
goto put_lnk_rst;
}
if (cdns_phy->nsubnodes > 1) {
if (cdns_phy->nsubnodes > 1 && !already_configured) {
ret = cdns_torrent_phy_configure_multilink(cdns_phy);
if (ret)
goto put_lnk_rst;
@ -2324,6 +2550,8 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
of_node_put(child);
reset_control_assert(cdns_phy->apb_rst);
clk_disable_unprepare(cdns_phy->clk);
clk_cleanup:
cdns_torrent_clk_cleanup(cdns_phy);
return ret;
}
@ -2340,6 +2568,7 @@ static int cdns_torrent_phy_remove(struct platform_device *pdev)
}
clk_disable_unprepare(cdns_phy->clk);
cdns_torrent_clk_cleanup(cdns_phy);
return 0;
}
@ -2455,8 +2684,6 @@ static struct cdns_reg_pairs usb_100_int_ssc_cmn_regs[] = {
{0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
{0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
{0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x00C7, CMN_PLL0_LOCK_REFCNT_START},
{0x00C7, CMN_PLL1_LOCK_REFCNT_START},
{0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
@ -2464,7 +2691,9 @@ static struct cdns_reg_pairs usb_100_int_ssc_cmn_regs[] = {
{0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
{0x0005, CMN_PLL1_LOCK_PLLCNT_THR},
{0x8200, CMN_CDIAG_CDB_PWRI_OVRD},
{0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD}
{0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD},
{0x007F, CMN_TXPUCAL_TUNE},
{0x007F, CMN_TXPDCAL_TUNE}
};
static struct cdns_torrent_vals usb_100_int_ssc_cmn_vals = {
@ -2507,13 +2736,28 @@ static struct cdns_torrent_vals usb_phy_pcs_cmn_vals = {
};
/* USB 100 MHz Ref clk, no SSC */
static struct cdns_reg_pairs usb_100_no_ssc_cmn_regs[] = {
static struct cdns_reg_pairs sl_usb_100_no_ssc_cmn_regs[] = {
{0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0},
{0x001E, CMN_PLL1_DSM_FBH_OVRD_M0},
{0x000C, CMN_PLL1_DSM_FBL_OVRD_M0},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x8200, CMN_CDIAG_CDB_PWRI_OVRD},
{0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD}
};
static struct cdns_torrent_vals sl_usb_100_no_ssc_cmn_vals = {
.reg_pairs = sl_usb_100_no_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(sl_usb_100_no_ssc_cmn_regs),
};
static struct cdns_reg_pairs usb_100_no_ssc_cmn_regs[] = {
{0x8200, CMN_CDIAG_CDB_PWRI_OVRD},
{0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD},
{0x007F, CMN_TXPUCAL_TUNE},
{0x007F, CMN_TXPDCAL_TUNE}
};
static struct cdns_reg_pairs usb_100_no_ssc_tx_ln_regs[] = {
{0x02FF, TX_PSC_A0},
{0x06AF, TX_PSC_A1},
@ -2645,12 +2889,22 @@ static struct cdns_torrent_vals sgmii_pcie_xcvr_diag_ln_vals = {
};
/* SGMII 100 MHz Ref clk, no SSC */
static struct cdns_reg_pairs sgmii_100_no_ssc_cmn_regs[] = {
static struct cdns_reg_pairs sl_sgmii_100_no_ssc_cmn_regs[] = {
{0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0},
{0x001E, CMN_PLL1_DSM_FBH_OVRD_M0},
{0x000C, CMN_PLL1_DSM_FBL_OVRD_M0},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x3700, CMN_DIAG_BIAS_OVRD1},
{0x0008, CMN_TXPUCAL_TUNE},
{0x0008, CMN_TXPDCAL_TUNE}
{0x0003, CMN_PLL1_VCOCAL_TCTRL}
};
static struct cdns_torrent_vals sl_sgmii_100_no_ssc_cmn_vals = {
.reg_pairs = sl_sgmii_100_no_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(sl_sgmii_100_no_ssc_cmn_regs),
};
static struct cdns_reg_pairs sgmii_100_no_ssc_cmn_regs[] = {
{0x007F, CMN_TXPUCAL_TUNE},
{0x007F, CMN_TXPDCAL_TUNE}
};
static struct cdns_reg_pairs sgmii_100_no_ssc_tx_ln_regs[] = {
@ -2661,6 +2915,15 @@ static struct cdns_reg_pairs sgmii_100_no_ssc_tx_ln_regs[] = {
{0x00B3, DRV_DIAG_TX_DRV}
};
static struct cdns_reg_pairs ti_sgmii_100_no_ssc_tx_ln_regs[] = {
{0x00F3, TX_PSC_A0},
{0x04A2, TX_PSC_A2},
{0x04A2, TX_PSC_A3},
{0x0000, TX_TXCC_CPOST_MULT_00},
{0x00B3, DRV_DIAG_TX_DRV},
{0x4000, XCVR_DIAG_RXCLK_CTRL},
};
static struct cdns_reg_pairs sgmii_100_no_ssc_rx_ln_regs[] = {
{0x091D, RX_PSC_A0},
{0x0900, RX_PSC_A2},
@ -2689,6 +2952,11 @@ static struct cdns_torrent_vals sgmii_100_no_ssc_tx_ln_vals = {
.num_regs = ARRAY_SIZE(sgmii_100_no_ssc_tx_ln_regs),
};
static struct cdns_torrent_vals ti_sgmii_100_no_ssc_tx_ln_vals = {
.reg_pairs = ti_sgmii_100_no_ssc_tx_ln_regs,
.num_regs = ARRAY_SIZE(ti_sgmii_100_no_ssc_tx_ln_regs),
};
static struct cdns_torrent_vals sgmii_100_no_ssc_rx_ln_vals = {
.reg_pairs = sgmii_100_no_ssc_rx_ln_regs,
.num_regs = ARRAY_SIZE(sgmii_100_no_ssc_rx_ln_regs),
@ -2736,17 +3004,14 @@ static struct cdns_reg_pairs sgmii_100_int_ssc_cmn_regs[] = {
{0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
{0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
{0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x00C7, CMN_PLL0_LOCK_REFCNT_START},
{0x00C7, CMN_PLL1_LOCK_REFCNT_START},
{0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
{0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
{0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
{0x0005, CMN_PLL1_LOCK_PLLCNT_THR},
{0x3700, CMN_DIAG_BIAS_OVRD1},
{0x0008, CMN_TXPUCAL_TUNE},
{0x0008, CMN_TXPDCAL_TUNE}
{0x007F, CMN_TXPUCAL_TUNE},
{0x007F, CMN_TXPDCAL_TUNE}
};
static struct cdns_torrent_vals sgmii_100_int_ssc_cmn_vals = {
@ -2755,19 +3020,43 @@ static struct cdns_torrent_vals sgmii_100_int_ssc_cmn_vals = {
};
/* QSGMII 100 MHz Ref clk, no SSC */
static struct cdns_reg_pairs qsgmii_100_no_ssc_cmn_regs[] = {
static struct cdns_reg_pairs sl_qsgmii_100_no_ssc_cmn_regs[] = {
{0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0},
{0x001E, CMN_PLL1_DSM_FBH_OVRD_M0},
{0x000C, CMN_PLL1_DSM_FBL_OVRD_M0},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL}
};
static struct cdns_torrent_vals sl_qsgmii_100_no_ssc_cmn_vals = {
.reg_pairs = sl_qsgmii_100_no_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(sl_qsgmii_100_no_ssc_cmn_regs),
};
static struct cdns_reg_pairs qsgmii_100_no_ssc_cmn_regs[] = {
{0x007F, CMN_TXPUCAL_TUNE},
{0x007F, CMN_TXPDCAL_TUNE}
};
static struct cdns_reg_pairs qsgmii_100_no_ssc_tx_ln_regs[] = {
{0x00F3, TX_PSC_A0},
{0x04A2, TX_PSC_A2},
{0x04A2, TX_PSC_A3},
{0x0000, TX_TXCC_CPOST_MULT_00},
{0x0011, TX_TXCC_MGNFS_MULT_100},
{0x0003, DRV_DIAG_TX_DRV}
};
static struct cdns_reg_pairs ti_qsgmii_100_no_ssc_tx_ln_regs[] = {
{0x00F3, TX_PSC_A0},
{0x04A2, TX_PSC_A2},
{0x04A2, TX_PSC_A3},
{0x0000, TX_TXCC_CPOST_MULT_00},
{0x0011, TX_TXCC_MGNFS_MULT_100},
{0x0003, DRV_DIAG_TX_DRV},
{0x4000, XCVR_DIAG_RXCLK_CTRL},
};
static struct cdns_reg_pairs qsgmii_100_no_ssc_rx_ln_regs[] = {
{0x091D, RX_PSC_A0},
{0x0900, RX_PSC_A2},
@ -2796,6 +3085,11 @@ static struct cdns_torrent_vals qsgmii_100_no_ssc_tx_ln_vals = {
.num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_tx_ln_regs),
};
static struct cdns_torrent_vals ti_qsgmii_100_no_ssc_tx_ln_vals = {
.reg_pairs = ti_qsgmii_100_no_ssc_tx_ln_regs,
.num_regs = ARRAY_SIZE(ti_qsgmii_100_no_ssc_tx_ln_regs),
};
static struct cdns_torrent_vals qsgmii_100_no_ssc_rx_ln_vals = {
.reg_pairs = qsgmii_100_no_ssc_rx_ln_regs,
.num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_rx_ln_regs),
@ -2843,14 +3137,14 @@ static struct cdns_reg_pairs qsgmii_100_int_ssc_cmn_regs[] = {
{0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
{0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
{0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x00C7, CMN_PLL0_LOCK_REFCNT_START},
{0x00C7, CMN_PLL1_LOCK_REFCNT_START},
{0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
{0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
{0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
{0x0005, CMN_PLL1_LOCK_PLLCNT_THR}
{0x0005, CMN_PLL1_LOCK_PLLCNT_THR},
{0x007F, CMN_TXPUCAL_TUNE},
{0x007F, CMN_TXPDCAL_TUNE}
};
static struct cdns_torrent_vals qsgmii_100_int_ssc_cmn_vals = {
@ -2922,8 +3216,6 @@ static struct cdns_reg_pairs pcie_100_int_ssc_cmn_regs[] = {
{0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
{0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
{0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x00C7, CMN_PLL0_LOCK_REFCNT_START},
{0x00C7, CMN_PLL1_LOCK_REFCNT_START},
{0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
@ -2979,8 +3271,6 @@ static struct cdns_reg_pairs sl_pcie_100_int_ssc_cmn_regs[] = {
{0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
{0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
{0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x00C7, CMN_PLL0_LOCK_REFCNT_START},
{0x00C7, CMN_PLL1_LOCK_REFCNT_START},
{0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
@ -2996,8 +3286,9 @@ static struct cdns_torrent_vals sl_pcie_100_int_ssc_cmn_vals = {
/* PCIe, 100 MHz Ref clk, no SSC & external SSC */
static struct cdns_reg_pairs pcie_100_ext_no_ssc_cmn_regs[] = {
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL}
{0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0},
{0x001E, CMN_PLL1_DSM_FBH_OVRD_M0},
{0x000C, CMN_PLL1_DSM_FBL_OVRD_M0}
};
static struct cdns_reg_pairs pcie_100_ext_no_ssc_rx_ln_regs[] = {
@ -3198,8 +3489,8 @@ static const struct cdns_torrent_data cdns_map_torrent = {
.cmn_vals = {
[TYPE_PCIE] = {
[TYPE_NONE] = {
[NO_SSC] = &pcie_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = &sl_pcie_100_int_ssc_cmn_vals,
},
[TYPE_SGMII] = {
@ -3220,7 +3511,7 @@ static const struct cdns_torrent_data cdns_map_torrent = {
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
[NO_SSC] = &sl_sgmii_100_no_ssc_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
@ -3235,7 +3526,7 @@ static const struct cdns_torrent_data cdns_map_torrent = {
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
[NO_SSC] = &sl_qsgmii_100_no_ssc_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
@ -3250,8 +3541,8 @@ static const struct cdns_torrent_data cdns_map_torrent = {
},
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[NO_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
[TYPE_PCIE] = {
@ -3260,13 +3551,13 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[INTERNAL_SSC] = &usb_100_int_ssc_cmn_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[NO_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[NO_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
},
@ -3607,8 +3898,8 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
.cmn_vals = {
[TYPE_PCIE] = {
[TYPE_NONE] = {
[NO_SSC] = &pcie_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = &sl_pcie_100_int_ssc_cmn_vals,
},
[TYPE_SGMII] = {
@ -3629,7 +3920,7 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
[NO_SSC] = &sl_sgmii_100_no_ssc_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
@ -3644,7 +3935,7 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
[NO_SSC] = &sl_qsgmii_100_no_ssc_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
@ -3659,8 +3950,8 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
},
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[NO_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
[TYPE_PCIE] = {
@ -3669,13 +3960,13 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[INTERNAL_SSC] = &usb_100_int_ssc_cmn_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[NO_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[NO_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
},
@ -3705,32 +3996,32 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
},
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
},
},
[TYPE_USB] = {

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2015 Linaro Ltd.
* Copyright (c) 2015 Hisilicon Limited.
* Copyright (c) 2015 HiSilicon Limited.
*/
#include <linux/mfd/syscon.h>

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2014 Linaro Ltd.
* Copyright (c) 2014 Hisilicon Limited.
* Copyright (c) 2014 HiSilicon Limited.
*/
#include <linux/delay.h>

View file

@ -352,8 +352,8 @@ static int ingenic_usb_phy_probe(struct platform_device *pdev)
}
priv->phy = devm_phy_create(dev, NULL, &ingenic_usb_phy_ops);
if (IS_ERR(priv))
return PTR_ERR(priv);
if (IS_ERR(priv->phy))
return PTR_ERR(priv->phy);
phy_set_drvdata(priv->phy, priv);

View file

@ -462,7 +462,7 @@ static int intel_cbphy_fwnode_parse(struct intel_combo_phy *cbphy)
/*
* syscfg and hsiocfg variables stores the handle of the registers set
* in which ComboPhy subsytem specific registers are subset. Using
* in which ComboPhy subsystem specific registers are subset. Using
* Register map framework to access the registers set.
*/
ret = fwnode_property_get_reference_args(fwnode, "intel,syscfg", NULL,

View file

@ -3,8 +3,8 @@
# Phy drivers for Marvell platforms
#
config ARMADA375_USBCLUSTER_PHY
def_bool y
depends on MACH_ARMADA_375 || COMPILE_TEST
bool "Armada 375 USB cluster PHY support" if COMPILE_TEST
default y if MACH_ARMADA_375
depends on OF && HAS_IOMEM
select GENERIC_PHY
@ -67,6 +67,14 @@ config PHY_MVEBU_CP110_COMPHY
lanes can be used by various controllers (Ethernet, sata, usb,
PCIe...).
config PHY_MVEBU_CP110_UTMI
tristate "Marvell CP110 UTMI driver"
depends on ARCH_MVEBU || COMPILE_TEST
depends on OF && USB_COMMON
select GENERIC_PHY
help
Enable this to support Marvell CP110 UTMI PHY driver.
config PHY_MVEBU_SATA
def_bool y
depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD

View file

@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o
obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o
obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY) += phy-armada38x-comphy.o
obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o
obj-$(CONFIG_PHY_MVEBU_CP110_UTMI) += phy-mvebu-cp110-utmi.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o
obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o

View file

@ -0,0 +1,384 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Marvell
*
* Authors:
* Konstantin Porotchkin <kostap@marvell.com>
*
* Marvell CP110 UTMI PHY driver
*/
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/usb/of.h>
#include <linux/usb/otg.h>
#define UTMI_PHY_PORTS 2
/* CP110 UTMI register macro definetions */
#define SYSCON_USB_CFG_REG 0x420
#define USB_CFG_DEVICE_EN_MASK BIT(0)
#define USB_CFG_DEVICE_MUX_OFFSET 1
#define USB_CFG_DEVICE_MUX_MASK BIT(1)
#define USB_CFG_PLL_MASK BIT(25)
#define SYSCON_UTMI_CFG_REG(id) (0x440 + (id) * 4)
#define UTMI_PHY_CFG_PU_MASK BIT(5)
#define UTMI_PLL_CTRL_REG 0x0
#define PLL_REFDIV_OFFSET 0
#define PLL_REFDIV_MASK GENMASK(6, 0)
#define PLL_REFDIV_VAL 0x5
#define PLL_FBDIV_OFFSET 16
#define PLL_FBDIV_MASK GENMASK(24, 16)
#define PLL_FBDIV_VAL 0x60
#define PLL_SEL_LPFR_MASK GENMASK(29, 28)
#define PLL_RDY BIT(31)
#define UTMI_CAL_CTRL_REG 0x8
#define IMPCAL_VTH_OFFSET 8
#define IMPCAL_VTH_MASK GENMASK(10, 8)
#define IMPCAL_VTH_VAL 0x7
#define IMPCAL_DONE BIT(23)
#define PLLCAL_DONE BIT(31)
#define UTMI_TX_CH_CTRL_REG 0xC
#define DRV_EN_LS_OFFSET 12
#define DRV_EN_LS_MASK GENMASK(15, 12)
#define IMP_SEL_LS_OFFSET 16
#define IMP_SEL_LS_MASK GENMASK(19, 16)
#define TX_AMP_OFFSET 20
#define TX_AMP_MASK GENMASK(22, 20)
#define TX_AMP_VAL 0x4
#define UTMI_RX_CH_CTRL0_REG 0x14
#define SQ_DET_EN BIT(15)
#define SQ_ANA_DTC_SEL BIT(28)
#define UTMI_RX_CH_CTRL1_REG 0x18
#define SQ_AMP_CAL_OFFSET 0
#define SQ_AMP_CAL_MASK GENMASK(2, 0)
#define SQ_AMP_CAL_VAL 1
#define SQ_AMP_CAL_EN BIT(3)
#define UTMI_CTRL_STATUS0_REG 0x24
#define SUSPENDM BIT(22)
#define TEST_SEL BIT(25)
#define UTMI_CHGDTC_CTRL_REG 0x38
#define VDAT_OFFSET 8
#define VDAT_MASK GENMASK(9, 8)
#define VDAT_VAL 1
#define VSRC_OFFSET 10
#define VSRC_MASK GENMASK(11, 10)
#define VSRC_VAL 1
#define PLL_LOCK_DELAY_US 10000
#define PLL_LOCK_TIMEOUT_US 1000000
#define PORT_REGS(p) ((p)->priv->regs + (p)->id * 0x1000)
/**
* struct mvebu_cp110_utmi - PHY driver data
*
* @regs: PHY registers
* @syscom: Regmap with system controller registers
* @dev: device driver handle
* @caps: PHY capabilities
*/
struct mvebu_cp110_utmi {
void __iomem *regs;
struct regmap *syscon;
struct device *dev;
const struct phy_ops *ops;
};
/**
* struct mvebu_cp110_utmi_port - PHY port data
*
* @priv: PHY driver data
* @id: PHY port ID
* @dr_mode: PHY connection: USB_DR_MODE_HOST or USB_DR_MODE_PERIPHERAL
*/
struct mvebu_cp110_utmi_port {
struct mvebu_cp110_utmi *priv;
u32 id;
enum usb_dr_mode dr_mode;
};
static void mvebu_cp110_utmi_port_setup(struct mvebu_cp110_utmi_port *port)
{
u32 reg;
/*
* Setup PLL.
* The reference clock is the frequency of quartz resonator
* connected to pins REFCLK_XIN and REFCLK_XOUT of the SoC.
* Register init values are matching the 40MHz default clock.
* The crystal used for all platform boards is now 25MHz.
* See the functional specification for details.
*/
reg = readl(PORT_REGS(port) + UTMI_PLL_CTRL_REG);
reg &= ~(PLL_REFDIV_MASK | PLL_FBDIV_MASK | PLL_SEL_LPFR_MASK);
reg |= (PLL_REFDIV_VAL << PLL_REFDIV_OFFSET) |
(PLL_FBDIV_VAL << PLL_FBDIV_OFFSET);
writel(reg, PORT_REGS(port) + UTMI_PLL_CTRL_REG);
/* Impedance Calibration Threshold Setting */
reg = readl(PORT_REGS(port) + UTMI_CAL_CTRL_REG);
reg &= ~IMPCAL_VTH_MASK;
reg |= IMPCAL_VTH_VAL << IMPCAL_VTH_OFFSET;
writel(reg, PORT_REGS(port) + UTMI_CAL_CTRL_REG);
/* Set LS TX driver strength coarse control */
reg = readl(PORT_REGS(port) + UTMI_TX_CH_CTRL_REG);
reg &= ~TX_AMP_MASK;
reg |= TX_AMP_VAL << TX_AMP_OFFSET;
writel(reg, PORT_REGS(port) + UTMI_TX_CH_CTRL_REG);
/* Disable SQ and enable analog squelch detect */
reg = readl(PORT_REGS(port) + UTMI_RX_CH_CTRL0_REG);
reg &= ~SQ_DET_EN;
reg |= SQ_ANA_DTC_SEL;
writel(reg, PORT_REGS(port) + UTMI_RX_CH_CTRL0_REG);
/*
* Set External squelch calibration number and
* enable the External squelch calibration
*/
reg = readl(PORT_REGS(port) + UTMI_RX_CH_CTRL1_REG);
reg &= ~SQ_AMP_CAL_MASK;
reg |= (SQ_AMP_CAL_VAL << SQ_AMP_CAL_OFFSET) | SQ_AMP_CAL_EN;
writel(reg, PORT_REGS(port) + UTMI_RX_CH_CTRL1_REG);
/*
* Set Control VDAT Reference Voltage - 0.325V and
* Control VSRC Reference Voltage - 0.6V
*/
reg = readl(PORT_REGS(port) + UTMI_CHGDTC_CTRL_REG);
reg &= ~(VDAT_MASK | VSRC_MASK);
reg |= (VDAT_VAL << VDAT_OFFSET) | (VSRC_VAL << VSRC_OFFSET);
writel(reg, PORT_REGS(port) + UTMI_CHGDTC_CTRL_REG);
}
static int mvebu_cp110_utmi_phy_power_off(struct phy *phy)
{
struct mvebu_cp110_utmi_port *port = phy_get_drvdata(phy);
struct mvebu_cp110_utmi *utmi = port->priv;
int i;
/* Power down UTMI PHY port */
regmap_clear_bits(utmi->syscon, SYSCON_UTMI_CFG_REG(port->id),
UTMI_PHY_CFG_PU_MASK);
for (i = 0; i < UTMI_PHY_PORTS; i++) {
int test = regmap_test_bits(utmi->syscon,
SYSCON_UTMI_CFG_REG(i),
UTMI_PHY_CFG_PU_MASK);
/* skip PLL shutdown if there are active UTMI PHY ports */
if (test != 0)
return 0;
}
/* PLL Power down if all UTMI PHYs are down */
regmap_clear_bits(utmi->syscon, SYSCON_USB_CFG_REG, USB_CFG_PLL_MASK);
return 0;
}
static int mvebu_cp110_utmi_phy_power_on(struct phy *phy)
{
struct mvebu_cp110_utmi_port *port = phy_get_drvdata(phy);
struct mvebu_cp110_utmi *utmi = port->priv;
struct device *dev = &phy->dev;
int ret;
u32 reg;
/* It is necessary to power off UTMI before configuration */
ret = mvebu_cp110_utmi_phy_power_off(phy);
if (ret) {
dev_err(dev, "UTMI power OFF before power ON failed\n");
return ret;
}
/*
* If UTMI port is connected to USB Device controller,
* configure the USB MUX prior to UTMI PHY initialization.
* The single USB device controller can be connected
* to UTMI0 or to UTMI1 PHY port, but not to both.
*/
if (port->dr_mode == USB_DR_MODE_PERIPHERAL) {
regmap_update_bits(utmi->syscon, SYSCON_USB_CFG_REG,
USB_CFG_DEVICE_EN_MASK | USB_CFG_DEVICE_MUX_MASK,
USB_CFG_DEVICE_EN_MASK |
(port->id << USB_CFG_DEVICE_MUX_OFFSET));
}
/* Set Test suspendm mode and enable Test UTMI select */
reg = readl(PORT_REGS(port) + UTMI_CTRL_STATUS0_REG);
reg |= SUSPENDM | TEST_SEL;
writel(reg, PORT_REGS(port) + UTMI_CTRL_STATUS0_REG);
/* Wait for UTMI power down */
mdelay(1);
/* PHY port setup first */
mvebu_cp110_utmi_port_setup(port);
/* Power UP UTMI PHY */
regmap_set_bits(utmi->syscon, SYSCON_UTMI_CFG_REG(port->id),
UTMI_PHY_CFG_PU_MASK);
/* Disable Test UTMI select */
reg = readl(PORT_REGS(port) + UTMI_CTRL_STATUS0_REG);
reg &= ~TEST_SEL;
writel(reg, PORT_REGS(port) + UTMI_CTRL_STATUS0_REG);
/* Wait for impedance calibration */
ret = readl_poll_timeout(PORT_REGS(port) + UTMI_CAL_CTRL_REG, reg,
reg & IMPCAL_DONE,
PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
if (ret) {
dev_err(dev, "Failed to end UTMI impedance calibration\n");
return ret;
}
/* Wait for PLL calibration */
ret = readl_poll_timeout(PORT_REGS(port) + UTMI_CAL_CTRL_REG, reg,
reg & PLLCAL_DONE,
PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
if (ret) {
dev_err(dev, "Failed to end UTMI PLL calibration\n");
return ret;
}
/* Wait for PLL ready */
ret = readl_poll_timeout(PORT_REGS(port) + UTMI_PLL_CTRL_REG, reg,
reg & PLL_RDY,
PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
if (ret) {
dev_err(dev, "PLL is not ready\n");
return ret;
}
/* PLL Power up */
regmap_set_bits(utmi->syscon, SYSCON_USB_CFG_REG, USB_CFG_PLL_MASK);
return 0;
}
static const struct phy_ops mvebu_cp110_utmi_phy_ops = {
.power_on = mvebu_cp110_utmi_phy_power_on,
.power_off = mvebu_cp110_utmi_phy_power_off,
.owner = THIS_MODULE,
};
static const struct of_device_id mvebu_cp110_utmi_of_match[] = {
{ .compatible = "marvell,cp110-utmi-phy" },
{},
};
MODULE_DEVICE_TABLE(of, mvebu_cp110_utmi_of_match);
static int mvebu_cp110_utmi_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mvebu_cp110_utmi *utmi;
struct phy_provider *provider;
struct device_node *child;
u32 usb_devices = 0;
utmi = devm_kzalloc(dev, sizeof(*utmi), GFP_KERNEL);
if (!utmi)
return -ENOMEM;
utmi->dev = dev;
/* Get system controller region */
utmi->syscon = syscon_regmap_lookup_by_phandle(dev->of_node,
"marvell,system-controller");
if (IS_ERR(utmi->syscon)) {
dev_err(dev, "Missing UTMI system controller\n");
return PTR_ERR(utmi->syscon);
}
/* Get UTMI memory region */
utmi->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(utmi->regs))
return PTR_ERR(utmi->regs);
for_each_available_child_of_node(dev->of_node, child) {
struct mvebu_cp110_utmi_port *port;
struct phy *phy;
int ret;
u32 port_id;
ret = of_property_read_u32(child, "reg", &port_id);
if ((ret < 0) || (port_id >= UTMI_PHY_PORTS)) {
dev_err(dev,
"invalid 'reg' property on child %pOF\n",
child);
continue;
}
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
if (!port) {
of_node_put(child);
return -ENOMEM;
}
port->dr_mode = of_usb_get_dr_mode_by_phy(child, -1);
if ((port->dr_mode != USB_DR_MODE_HOST) &&
(port->dr_mode != USB_DR_MODE_PERIPHERAL)) {
dev_err(&pdev->dev,
"Missing dual role setting of the port%d, will use HOST mode\n",
port_id);
port->dr_mode = USB_DR_MODE_HOST;
}
if (port->dr_mode == USB_DR_MODE_PERIPHERAL) {
usb_devices++;
if (usb_devices > 1) {
dev_err(dev,
"Single USB device allowed! Port%d will use HOST mode\n",
port_id);
port->dr_mode = USB_DR_MODE_HOST;
}
}
/* Retrieve PHY capabilities */
utmi->ops = &mvebu_cp110_utmi_phy_ops;
/* Instantiate the PHY */
phy = devm_phy_create(dev, child, utmi->ops);
if (IS_ERR(phy)) {
dev_err(dev, "Failed to create the UTMI PHY\n");
of_node_put(child);
return PTR_ERR(phy);
}
port->priv = utmi;
port->id = port_id;
phy_set_drvdata(phy, port);
/* Ensure the PHY is powered off */
mvebu_cp110_utmi_phy_power_off(phy);
}
dev_set_drvdata(dev, utmi);
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(provider);
}
static struct platform_driver mvebu_cp110_utmi_driver = {
.probe = mvebu_cp110_utmi_phy_probe,
.driver = {
.name = "mvebu-cp110-utmi-phy",
.of_match_table = mvebu_cp110_utmi_of_match,
},
};
module_platform_driver(mvebu_cp110_utmi_driver);
MODULE_AUTHOR("Konstatin Porotchkin <kostap@marvell.com>");
MODULE_DESCRIPTION("Marvell Armada CP110 UTMI PHY driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Phy drivers for Microchip devices
#
config PHY_SPARX5_SERDES
tristate "Microchip Sparx5 SerDes PHY driver"
select GENERIC_PHY
depends on ARCH_SPARX5 || COMPILE_TEST
depends on OF
depends on HAS_IOMEM
help
Enable this for support of the 10G/25G SerDes on Microchip Sparx5.

View file

@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the Microchip phy drivers.
#
obj-$(CONFIG_PHY_SPARX5_SERDES) := sparx5_serdes.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,136 @@
/* SPDX-License-Identifier: GPL-2.0+
* Microchip Sparx5 SerDes driver
*
* Copyright (c) 2020 Microchip Technology Inc.
*/
#ifndef _SPARX5_SERDES_H_
#define _SPARX5_SERDES_H_
#include "sparx5_serdes_regs.h"
#define SPX5_SERDES_MAX 33
enum sparx5_serdes_type {
SPX5_SDT_6G = 6,
SPX5_SDT_10G = 10,
SPX5_SDT_25G = 25,
};
enum sparx5_serdes_mode {
SPX5_SD_MODE_NONE,
SPX5_SD_MODE_2G5,
SPX5_SD_MODE_QSGMII,
SPX5_SD_MODE_100FX,
SPX5_SD_MODE_1000BASEX,
SPX5_SD_MODE_SFI,
};
struct sparx5_serdes_private {
struct device *dev;
void __iomem *regs[NUM_TARGETS];
struct phy *phys[SPX5_SERDES_MAX];
bool cmu_enabled;
unsigned long coreclock;
};
struct sparx5_serdes_macro {
struct sparx5_serdes_private *priv;
u32 sidx;
u32 stpidx;
enum sparx5_serdes_type serdestype;
enum sparx5_serdes_mode serdesmode;
phy_interface_t portmode;
int speed;
enum phy_media media;
};
/* Read, Write and modify registers content.
* The register definition macros start at the id
*/
static inline void __iomem *sdx5_addr(void __iomem *base[],
int id, int tinst, int tcnt,
int gbase, int ginst,
int gcnt, int gwidth,
int raddr, int rinst,
int rcnt, int rwidth)
{
WARN_ON((tinst) >= tcnt);
WARN_ON((ginst) >= gcnt);
WARN_ON((rinst) >= rcnt);
return base[id + (tinst)] +
gbase + ((ginst) * gwidth) +
raddr + ((rinst) * rwidth);
}
static inline void __iomem *sdx5_inst_baseaddr(void __iomem *base,
int gbase, int ginst,
int gcnt, int gwidth,
int raddr, int rinst,
int rcnt, int rwidth)
{
WARN_ON((ginst) >= gcnt);
WARN_ON((rinst) >= rcnt);
return base +
gbase + ((ginst) * gwidth) +
raddr + ((rinst) * rwidth);
}
static inline void sdx5_rmw(u32 val, u32 mask, struct sparx5_serdes_private *priv,
int id, int tinst, int tcnt,
int gbase, int ginst, int gcnt, int gwidth,
int raddr, int rinst, int rcnt, int rwidth)
{
u32 nval;
void __iomem *addr =
sdx5_addr(priv->regs, id, tinst, tcnt,
gbase, ginst, gcnt, gwidth,
raddr, rinst, rcnt, rwidth);
nval = readl(addr);
nval = (nval & ~mask) | (val & mask);
writel(nval, addr);
}
static inline void sdx5_inst_rmw(u32 val, u32 mask, void __iomem *iomem,
int id, int tinst, int tcnt,
int gbase, int ginst, int gcnt, int gwidth,
int raddr, int rinst, int rcnt, int rwidth)
{
u32 nval;
void __iomem *addr =
sdx5_inst_baseaddr(iomem,
gbase, ginst, gcnt, gwidth,
raddr, rinst, rcnt, rwidth);
nval = readl(addr);
nval = (nval & ~mask) | (val & mask);
writel(nval, addr);
}
static inline void sdx5_rmw_addr(u32 val, u32 mask, void __iomem *addr)
{
u32 nval;
nval = readl(addr);
nval = (nval & ~mask) | (val & mask);
writel(nval, addr);
}
static inline void __iomem *sdx5_inst_get(struct sparx5_serdes_private *priv,
int id, int tinst)
{
return priv->regs[id + tinst];
}
static inline void __iomem *sdx5_inst_addr(void __iomem *iomem,
int id, int tinst, int tcnt,
int gbase,
int ginst, int gcnt, int gwidth,
int raddr,
int rinst, int rcnt, int rwidth)
{
return sdx5_inst_baseaddr(iomem, gbase, ginst, gcnt, gwidth,
raddr, rinst, rcnt, rwidth);
}
#endif /* _SPARX5_SERDES_REGS_H_ */

File diff suppressed because it is too large Load diff

View file

@ -373,6 +373,36 @@ int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode)
}
EXPORT_SYMBOL_GPL(phy_set_mode_ext);
int phy_set_media(struct phy *phy, enum phy_media media)
{
int ret;
if (!phy || !phy->ops->set_media)
return 0;
mutex_lock(&phy->mutex);
ret = phy->ops->set_media(phy, media);
mutex_unlock(&phy->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(phy_set_media);
int phy_set_speed(struct phy *phy, int speed)
{
int ret;
if (!phy || !phy->ops->set_speed)
return 0;
mutex_lock(&phy->mutex);
ret = phy->ops->set_speed(phy, speed);
mutex_unlock(&phy->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(phy_set_speed);
int phy_reset(struct phy *phy)
{
int ret;

View file

@ -276,8 +276,8 @@ static int qcom_ipq806x_usb_hs_phy_init(struct phy *phy)
val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP |
HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN |
HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP |
HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID |
HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70;
HSUSB_CTRL_UTMI_OTG_VBUS_VALID | HSUSB_CTRL_UTMI_CLK_EN |
HSUSB_CTRL_CLAMP_EN | 0x70;
/* use core clock if external reference is not present */
if (!phy_dwc3->xo_clk)

View file

@ -1840,6 +1840,86 @@ static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
};
static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_COM_SVS_MODE_CLK_SEL, 0x05),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x3b),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYS_CLK_CTRL, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_ENABLE1, 0x0c),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x30),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_CONFIG, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_BG_TIMER, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE0, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_CTRL, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN, 0x17),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORE_CLK_EN, 0x1f),
};
static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_rbr[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x05),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x69),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x6f),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04),
};
static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_hbr[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x03),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x69),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x0e),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x08),
};
static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_hbr2[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x8c),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x1c),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x08),
};
static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_hbr3[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x69),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x2f),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x2a),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x08),
};
static const struct qmp_phy_init_tbl qmp_v4_dp_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_TX_VMODE_CTRL1, 0x40),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_PRE_STALL_LDO_BOOST_EN, 0x30),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_INTERFACE_SELECT, 0x3b),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_CLKBUF_ENABLE, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_RESET_TSYNC_EN, 0x03),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_PARRATE_REC_DETECT_IDLE_EN, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_INTERFACE_MODE, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x11),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_BAND, 0x4),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_POL_INV, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_DRV_LVL, 0x2a),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_EMP_POST1_LVL, 0x20),
};
static const struct qmp_phy_init_tbl sm8250_qmp_pcie_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x34),
@ -2268,6 +2348,8 @@ static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
};
struct qmp_phy;
/* struct qmp_phy_cfg - per-PHY initialization config */
struct qmp_phy_cfg {
/* phy-type - PCIE/UFS/USB */
@ -2307,6 +2389,12 @@ struct qmp_phy_cfg {
const struct qmp_phy_init_tbl *serdes_tbl_hbr3;
int serdes_tbl_hbr3_num;
/* DP PHY callbacks */
int (*configure_dp_phy)(struct qmp_phy *qphy);
void (*configure_dp_tx)(struct qmp_phy *qphy);
int (*calibrate_dp_phy)(struct qmp_phy *qphy);
void (*dp_aux_init)(struct qmp_phy *qphy);
/* clock ids to be requested */
const char * const *clk_list;
int num_clks;
@ -2423,6 +2511,16 @@ struct qcom_qmp {
struct reset_control *ufs_reset;
};
static void qcom_qmp_v3_phy_dp_aux_init(struct qmp_phy *qphy);
static void qcom_qmp_v3_phy_configure_dp_tx(struct qmp_phy *qphy);
static int qcom_qmp_v3_phy_configure_dp_phy(struct qmp_phy *qphy);
static int qcom_qmp_v3_dp_phy_calibrate(struct qmp_phy *qphy);
static void qcom_qmp_v4_phy_dp_aux_init(struct qmp_phy *qphy);
static void qcom_qmp_v4_phy_configure_dp_tx(struct qmp_phy *qphy);
static int qcom_qmp_v4_phy_configure_dp_phy(struct qmp_phy *qphy);
static int qcom_qmp_v4_dp_phy_calibrate(struct qmp_phy *qphy);
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
@ -2871,6 +2969,11 @@ static const struct qmp_phy_cfg sc7180_dpphy_cfg = {
.has_phy_dp_com_ctrl = true,
.is_dual_lane_phy = true,
.dp_aux_init = qcom_qmp_v3_phy_dp_aux_init,
.configure_dp_tx = qcom_qmp_v3_phy_configure_dp_tx,
.configure_dp_phy = qcom_qmp_v3_phy_configure_dp_phy,
.calibrate_dp_phy = qcom_qmp_v3_dp_phy_calibrate,
};
static const struct qmp_phy_combo_cfg sc7180_usb3dpphy_cfg = {
@ -3123,6 +3226,46 @@ static const struct qmp_phy_cfg sm8250_usb3_uniphy_cfg = {
.pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
};
static const struct qmp_phy_cfg sm8250_dpphy_cfg = {
.type = PHY_TYPE_DP,
.nlanes = 1,
.serdes_tbl = qmp_v4_dp_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl),
.tx_tbl = qmp_v4_dp_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(qmp_v4_dp_tx_tbl),
.serdes_tbl_rbr = qmp_v4_dp_serdes_tbl_rbr,
.serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_rbr),
.serdes_tbl_hbr = qmp_v4_dp_serdes_tbl_hbr,
.serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr),
.serdes_tbl_hbr2 = qmp_v4_dp_serdes_tbl_hbr2,
.serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr2),
.serdes_tbl_hbr3 = qmp_v4_dp_serdes_tbl_hbr3,
.serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr3),
.clk_list = qmp_v4_phy_clk_l,
.num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l),
.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),
.regs = qmp_v4_usb3phy_regs_layout,
.has_phy_dp_com_ctrl = true,
.is_dual_lane_phy = true,
.dp_aux_init = qcom_qmp_v4_phy_dp_aux_init,
.configure_dp_tx = qcom_qmp_v4_phy_configure_dp_tx,
.configure_dp_phy = qcom_qmp_v4_phy_configure_dp_phy,
.calibrate_dp_phy = qcom_qmp_v4_dp_phy_calibrate,
};
static const struct qmp_phy_combo_cfg sm8250_usb3dpphy_cfg = {
.usb_cfg = &sm8250_usb3phy_cfg,
.dp_cfg = &sm8250_dpphy_cfg,
};
static const struct qmp_phy_cfg sdx55_usb3_uniphy_cfg = {
.type = PHY_TYPE_USB3,
.nlanes = 1,
@ -3332,24 +3475,24 @@ static int qcom_qmp_phy_serdes_init(struct qmp_phy *qphy)
return 0;
}
static void qcom_qmp_phy_dp_aux_init(struct qmp_phy *qphy)
static void qcom_qmp_v3_phy_dp_aux_init(struct qmp_phy *qphy)
{
writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL);
qphy->pcs + QSERDES_DP_PHY_PD_CTL);
/* Turn on BIAS current for PHY/PLL */
writel(QSERDES_V3_COM_BIAS_EN | QSERDES_V3_COM_BIAS_EN_MUX |
QSERDES_V3_COM_CLKBUF_L_EN | QSERDES_V3_COM_EN_SYSCLK_TX_SEL,
qphy->serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN);
writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL);
writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_DP_PHY_PD_CTL);
writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_LANE_0_1_PWRDN |
DP_PHY_PD_CTL_LANE_2_3_PWRDN | DP_PHY_PD_CTL_PLL_PWRDN |
DP_PHY_PD_CTL_DP_CLAMP_EN,
qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL);
qphy->pcs + QSERDES_DP_PHY_PD_CTL);
writel(QSERDES_V3_COM_BIAS_EN |
QSERDES_V3_COM_BIAS_EN_MUX | QSERDES_V3_COM_CLKBUF_R_EN |
@ -3357,16 +3500,16 @@ static void qcom_qmp_phy_dp_aux_init(struct qmp_phy *qphy)
QSERDES_V3_COM_CLKBUF_RX_DRIVE_L,
qphy->serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN);
writel(0x00, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG0);
writel(0x13, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG1);
writel(0x24, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG2);
writel(0x00, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG3);
writel(0x0a, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG4);
writel(0x26, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG5);
writel(0x0a, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG6);
writel(0x03, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG7);
writel(0xbb, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG8);
writel(0x03, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG9);
writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG0);
writel(0x13, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1);
writel(0x24, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2);
writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG3);
writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG4);
writel(0x26, qphy->pcs + QSERDES_DP_PHY_AUX_CFG5);
writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG6);
writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG7);
writel(0xbb, qphy->pcs + QSERDES_DP_PHY_AUX_CFG8);
writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG9);
qphy->dp_aux_cfg = 0;
writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK |
@ -3375,6 +3518,20 @@ static void qcom_qmp_phy_dp_aux_init(struct qmp_phy *qphy)
qphy->pcs + QSERDES_V3_DP_PHY_AUX_INTERRUPT_MASK);
}
static const u8 qmp_dp_v3_pre_emphasis_hbr3_hbr2[4][4] = {
{ 0x00, 0x0c, 0x15, 0x1a },
{ 0x02, 0x0e, 0x16, 0xff },
{ 0x02, 0x11, 0xff, 0xff },
{ 0x04, 0xff, 0xff, 0xff }
};
static const u8 qmp_dp_v3_voltage_swing_hbr3_hbr2[4][4] = {
{ 0x02, 0x12, 0x16, 0x1a },
{ 0x09, 0x19, 0x1f, 0xff },
{ 0x10, 0x1f, 0xff, 0xff },
{ 0x1f, 0xff, 0xff, 0xff }
};
static const u8 qmp_dp_v3_pre_emphasis_hbr_rbr[4][4] = {
{ 0x00, 0x0c, 0x14, 0x19 },
{ 0x00, 0x0b, 0x12, 0xff },
@ -3389,11 +3546,11 @@ static const u8 qmp_dp_v3_voltage_swing_hbr_rbr[4][4] = {
{ 0x1f, 0xff, 0xff, 0xff }
};
static void qcom_qmp_phy_configure_dp_tx(struct qmp_phy *qphy)
static int qcom_qmp_phy_configure_dp_swing(struct qmp_phy *qphy,
unsigned int drv_lvl_reg, unsigned int emp_post_reg)
{
const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
unsigned int v_level = 0, p_level = 0;
u32 bias_en, drvr_en;
u8 voltage_swing_cfg, pre_emphasis_cfg;
int i;
@ -3402,6 +3559,40 @@ static void qcom_qmp_phy_configure_dp_tx(struct qmp_phy *qphy)
p_level = max(p_level, dp_opts->pre[i]);
}
if (dp_opts->link_rate <= 2700) {
voltage_swing_cfg = qmp_dp_v3_voltage_swing_hbr_rbr[v_level][p_level];
pre_emphasis_cfg = qmp_dp_v3_pre_emphasis_hbr_rbr[v_level][p_level];
} else {
voltage_swing_cfg = qmp_dp_v3_voltage_swing_hbr3_hbr2[v_level][p_level];
pre_emphasis_cfg = qmp_dp_v3_pre_emphasis_hbr3_hbr2[v_level][p_level];
}
/* TODO: Move check to config check */
if (voltage_swing_cfg == 0xFF && pre_emphasis_cfg == 0xFF)
return -EINVAL;
/* Enable MUX to use Cursor values from these registers */
voltage_swing_cfg |= DP_PHY_TXn_TX_DRV_LVL_MUX_EN;
pre_emphasis_cfg |= DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN;
writel(voltage_swing_cfg, qphy->tx + drv_lvl_reg);
writel(pre_emphasis_cfg, qphy->tx + emp_post_reg);
writel(voltage_swing_cfg, qphy->tx2 + drv_lvl_reg);
writel(pre_emphasis_cfg, qphy->tx2 + emp_post_reg);
return 0;
}
static void qcom_qmp_v3_phy_configure_dp_tx(struct qmp_phy *qphy)
{
const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
u32 bias_en, drvr_en;
if (qcom_qmp_phy_configure_dp_swing(qphy,
QSERDES_V3_TX_TX_DRV_LVL,
QSERDES_V3_TX_TX_EMP_POST1_LVL) < 0)
return;
if (dp_opts->lanes == 1) {
bias_en = 0x3e;
drvr_en = 0x13;
@ -3410,48 +3601,16 @@ static void qcom_qmp_phy_configure_dp_tx(struct qmp_phy *qphy)
drvr_en = 0x10;
}
voltage_swing_cfg = qmp_dp_v3_voltage_swing_hbr_rbr[v_level][p_level];
pre_emphasis_cfg = qmp_dp_v3_pre_emphasis_hbr_rbr[v_level][p_level];
/* TODO: Move check to config check */
if (voltage_swing_cfg == 0xFF && pre_emphasis_cfg == 0xFF)
return;
/* Enable MUX to use Cursor values from these registers */
voltage_swing_cfg |= DP_PHY_TXn_TX_DRV_LVL_MUX_EN;
pre_emphasis_cfg |= DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN;
writel(voltage_swing_cfg, qphy->tx + QSERDES_V3_TX_TX_DRV_LVL);
writel(pre_emphasis_cfg, qphy->tx + QSERDES_V3_TX_TX_EMP_POST1_LVL);
writel(voltage_swing_cfg, qphy->tx2 + QSERDES_V3_TX_TX_DRV_LVL);
writel(pre_emphasis_cfg, qphy->tx2 + QSERDES_V3_TX_TX_EMP_POST1_LVL);
writel(drvr_en, qphy->tx + QSERDES_V3_TX_HIGHZ_DRVR_EN);
writel(bias_en, qphy->tx + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN);
writel(drvr_en, qphy->tx2 + QSERDES_V3_TX_HIGHZ_DRVR_EN);
writel(bias_en, qphy->tx2 + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN);
}
static int qcom_qmp_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
static bool qcom_qmp_phy_configure_dp_mode(struct qmp_phy *qphy)
{
const struct phy_configure_opts_dp *dp_opts = &opts->dp;
struct qmp_phy *qphy = phy_get_drvdata(phy);
memcpy(&qphy->dp_opts, dp_opts, sizeof(*dp_opts));
if (qphy->dp_opts.set_voltages) {
qcom_qmp_phy_configure_dp_tx(qphy);
qphy->dp_opts.set_voltages = 0;
}
return 0;
}
static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy)
{
const struct qmp_phy_dp_clks *dp_clks = qphy->dp_clks;
const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
u32 val, phy_vco_div, status;
unsigned long pixel_freq;
u32 val;
bool reverse = false;
val = DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN;
@ -3471,9 +3630,22 @@ static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy)
* writel(0x4c, qphy->pcs + QSERDES_V3_DP_PHY_MODE);
*/
val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN;
writel(val, qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL);
writel(val, qphy->pcs + QSERDES_DP_PHY_PD_CTL);
writel(0x5c, qphy->pcs + QSERDES_DP_PHY_MODE);
return reverse;
}
static int qcom_qmp_v3_phy_configure_dp_phy(struct qmp_phy *qphy)
{
const struct qmp_phy_dp_clks *dp_clks = qphy->dp_clks;
const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
u32 phy_vco_div, status;
unsigned long pixel_freq;
qcom_qmp_phy_configure_dp_mode(qphy);
writel(0x5c, qphy->pcs + QSERDES_V3_DP_PHY_MODE);
writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_TX0_TX1_LANE_CTL);
writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_TX2_TX3_LANE_CTL);
@ -3503,11 +3675,11 @@ static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy)
clk_set_rate(dp_clks->dp_link_hw.clk, dp_opts->link_rate * 100000);
clk_set_rate(dp_clks->dp_pixel_hw.clk, pixel_freq);
writel(0x04, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG2);
writel(0x01, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
writel(0x01, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
writel(0x09, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
writel(0x04, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2);
writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG);
writel(0x05, qphy->pcs + QSERDES_DP_PHY_CFG);
writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG);
writel(0x09, qphy->pcs + QSERDES_DP_PHY_CFG);
writel(0x20, qphy->serdes + QSERDES_V3_COM_RESETSM_CNTRL);
@ -3518,7 +3690,7 @@ static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy)
10000))
return -ETIMEDOUT;
writel(0x19, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG);
if (readl_poll_timeout(qphy->pcs + QSERDES_V3_DP_PHY_STATUS,
status,
@ -3527,9 +3699,9 @@ static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy)
10000))
return -ETIMEDOUT;
writel(0x18, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
writel(0x18, qphy->pcs + QSERDES_DP_PHY_CFG);
udelay(2000);
writel(0x19, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG);
return readl_poll_timeout(qphy->pcs + QSERDES_V3_DP_PHY_STATUS,
status,
@ -3542,9 +3714,8 @@ static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy)
* We need to calibrate the aux setting here as many times
* as the caller tries
*/
static int qcom_qmp_dp_phy_calibrate(struct phy *phy)
static int qcom_qmp_v3_dp_phy_calibrate(struct qmp_phy *qphy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
static const u8 cfg1_settings[] = { 0x13, 0x23, 0x1d };
u8 val;
@ -3552,7 +3723,231 @@ static int qcom_qmp_dp_phy_calibrate(struct phy *phy)
qphy->dp_aux_cfg %= ARRAY_SIZE(cfg1_settings);
val = cfg1_settings[qphy->dp_aux_cfg];
writel(val, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG1);
writel(val, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1);
return 0;
}
static void qcom_qmp_v4_phy_dp_aux_init(struct qmp_phy *qphy)
{
writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_PSR_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
qphy->pcs + QSERDES_DP_PHY_PD_CTL);
/* Turn on BIAS current for PHY/PLL */
writel(0x17, qphy->serdes + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN);
writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG0);
writel(0x13, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1);
writel(0xa4, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2);
writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG3);
writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG4);
writel(0x26, qphy->pcs + QSERDES_DP_PHY_AUX_CFG5);
writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG6);
writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG7);
writel(0xb7, qphy->pcs + QSERDES_DP_PHY_AUX_CFG8);
writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG9);
qphy->dp_aux_cfg = 0;
writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK |
PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK |
PHY_AUX_REQ_ERR_MASK,
qphy->pcs + QSERDES_V4_DP_PHY_AUX_INTERRUPT_MASK);
}
static void qcom_qmp_v4_phy_configure_dp_tx(struct qmp_phy *qphy)
{
/* Program default values before writing proper values */
writel(0x27, qphy->tx + QSERDES_V4_TX_TX_DRV_LVL);
writel(0x27, qphy->tx2 + QSERDES_V4_TX_TX_DRV_LVL);
writel(0x20, qphy->tx + QSERDES_V4_TX_TX_EMP_POST1_LVL);
writel(0x20, qphy->tx2 + QSERDES_V4_TX_TX_EMP_POST1_LVL);
qcom_qmp_phy_configure_dp_swing(qphy,
QSERDES_V4_TX_TX_DRV_LVL,
QSERDES_V4_TX_TX_EMP_POST1_LVL);
}
static int qcom_qmp_v4_phy_configure_dp_phy(struct qmp_phy *qphy)
{
const struct qmp_phy_dp_clks *dp_clks = qphy->dp_clks;
const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
u32 phy_vco_div, status;
unsigned long pixel_freq;
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
bool reverse;
writel(0x0f, qphy->pcs + QSERDES_V4_DP_PHY_CFG_1);
reverse = qcom_qmp_phy_configure_dp_mode(qphy);
writel(0x13, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1);
writel(0xa4, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2);
writel(0x05, qphy->pcs + QSERDES_V4_DP_PHY_TX0_TX1_LANE_CTL);
writel(0x05, qphy->pcs + QSERDES_V4_DP_PHY_TX2_TX3_LANE_CTL);
switch (dp_opts->link_rate) {
case 1620:
phy_vco_div = 0x1;
pixel_freq = 1620000000UL / 2;
break;
case 2700:
phy_vco_div = 0x1;
pixel_freq = 2700000000UL / 2;
break;
case 5400:
phy_vco_div = 0x2;
pixel_freq = 5400000000UL / 4;
break;
case 8100:
phy_vco_div = 0x0;
pixel_freq = 8100000000UL / 6;
break;
default:
/* Other link rates aren't supported */
return -EINVAL;
}
writel(phy_vco_div, qphy->pcs + QSERDES_V4_DP_PHY_VCO_DIV);
clk_set_rate(dp_clks->dp_link_hw.clk, dp_opts->link_rate * 100000);
clk_set_rate(dp_clks->dp_pixel_hw.clk, pixel_freq);
writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG);
writel(0x05, qphy->pcs + QSERDES_DP_PHY_CFG);
writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG);
writel(0x09, qphy->pcs + QSERDES_DP_PHY_CFG);
writel(0x20, qphy->serdes + QSERDES_V4_COM_RESETSM_CNTRL);
if (readl_poll_timeout(qphy->serdes + QSERDES_V4_COM_C_READY_STATUS,
status,
((status & BIT(0)) > 0),
500,
10000))
return -ETIMEDOUT;
if (readl_poll_timeout(qphy->serdes + QSERDES_V4_COM_CMN_STATUS,
status,
((status & BIT(0)) > 0),
500,
10000))
return -ETIMEDOUT;
if (readl_poll_timeout(qphy->serdes + QSERDES_V4_COM_CMN_STATUS,
status,
((status & BIT(1)) > 0),
500,
10000))
return -ETIMEDOUT;
writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG);
if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS,
status,
((status & BIT(0)) > 0),
500,
10000))
return -ETIMEDOUT;
if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS,
status,
((status & BIT(1)) > 0),
500,
10000))
return -ETIMEDOUT;
/*
* At least for 7nm DP PHY this has to be done after enabling link
* clock.
*/
if (dp_opts->lanes == 1) {
bias0_en = reverse ? 0x3e : 0x15;
bias1_en = reverse ? 0x15 : 0x3e;
drvr0_en = reverse ? 0x13 : 0x10;
drvr1_en = reverse ? 0x10 : 0x13;
} else if (dp_opts->lanes == 2) {
bias0_en = reverse ? 0x3f : 0x15;
bias1_en = reverse ? 0x15 : 0x3f;
drvr0_en = 0x10;
drvr1_en = 0x10;
} else {
bias0_en = 0x3f;
bias1_en = 0x3f;
drvr0_en = 0x10;
drvr1_en = 0x10;
}
writel(drvr0_en, qphy->tx + QSERDES_V4_TX_HIGHZ_DRVR_EN);
writel(bias0_en, qphy->tx + QSERDES_V4_TX_TRANSCEIVER_BIAS_EN);
writel(drvr1_en, qphy->tx2 + QSERDES_V4_TX_HIGHZ_DRVR_EN);
writel(bias1_en, qphy->tx2 + QSERDES_V4_TX_TRANSCEIVER_BIAS_EN);
writel(0x18, qphy->pcs + QSERDES_DP_PHY_CFG);
udelay(2000);
writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG);
if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS,
status,
((status & BIT(1)) > 0),
500,
10000))
return -ETIMEDOUT;
writel(0x0a, qphy->tx + QSERDES_V4_TX_TX_POL_INV);
writel(0x0a, qphy->tx2 + QSERDES_V4_TX_TX_POL_INV);
writel(0x27, qphy->tx + QSERDES_V4_TX_TX_DRV_LVL);
writel(0x27, qphy->tx2 + QSERDES_V4_TX_TX_DRV_LVL);
writel(0x20, qphy->tx + QSERDES_V4_TX_TX_EMP_POST1_LVL);
writel(0x20, qphy->tx2 + QSERDES_V4_TX_TX_EMP_POST1_LVL);
return 0;
}
/*
* We need to calibrate the aux setting here as many times
* as the caller tries
*/
static int qcom_qmp_v4_dp_phy_calibrate(struct qmp_phy *qphy)
{
static const u8 cfg1_settings[] = { 0x20, 0x13, 0x23, 0x1d };
u8 val;
qphy->dp_aux_cfg++;
qphy->dp_aux_cfg %= ARRAY_SIZE(cfg1_settings);
val = cfg1_settings[qphy->dp_aux_cfg];
writel(val, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1);
return 0;
}
static int qcom_qmp_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
{
const struct phy_configure_opts_dp *dp_opts = &opts->dp;
struct qmp_phy *qphy = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qphy->cfg;
memcpy(&qphy->dp_opts, dp_opts, sizeof(*dp_opts));
if (qphy->dp_opts.set_voltages) {
cfg->configure_dp_tx(qphy);
qphy->dp_opts.set_voltages = 0;
}
return 0;
}
static int qcom_qmp_dp_phy_calibrate(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qphy->cfg;
if (cfg->calibrate_dp_phy)
return cfg->calibrate_dp_phy(qphy);
return 0;
}
@ -3729,7 +4124,7 @@ static int qcom_qmp_phy_init(struct phy *phy)
return ret;
if (cfg->type == PHY_TYPE_DP)
qcom_qmp_phy_dp_aux_init(qphy);
cfg->dp_aux_init(qphy);
return 0;
}
@ -3783,7 +4178,7 @@ static int qcom_qmp_phy_power_on(struct phy *phy)
/* Configure special DP tx tunings */
if (cfg->type == PHY_TYPE_DP)
qcom_qmp_phy_configure_dp_tx(qphy);
cfg->configure_dp_tx(qphy);
qcom_qmp_phy_configure_lane(rx, cfg->regs,
cfg->rx_tbl, cfg->rx_tbl_num, 1);
@ -3802,7 +4197,7 @@ static int qcom_qmp_phy_power_on(struct phy *phy)
/* Configure link rate, swing, etc. */
if (cfg->type == PHY_TYPE_DP) {
qcom_qmp_phy_configure_dp_phy(qphy);
cfg->configure_dp_phy(qphy);
} else {
qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
if (cfg->pcs_tbl_sec)
@ -3874,7 +4269,7 @@ static int qcom_qmp_phy_power_off(struct phy *phy)
if (cfg->type == PHY_TYPE_DP) {
/* Assert DP PHY power down */
writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL);
writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_DP_PHY_PD_CTL);
} else {
/* PHY reset */
if (!cfg->no_pcs_sw_reset)
@ -4577,6 +4972,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
}, {
.compatible = "qcom,sm8250-qmp-usb3-phy",
.data = &sm8250_usb3phy_cfg,
}, {
.compatible = "qcom,sm8250-qmp-usb3-dp-phy",
/* It's a combo phy */
}, {
.compatible = "qcom,sm8250-qmp-usb3-uni-phy",
.data = &sm8250_usb3_uniphy_cfg,
@ -4611,6 +5009,10 @@ static const struct of_device_id qcom_qmp_combo_phy_of_match_table[] = {
.compatible = "qcom,sc7180-qmp-usb3-dp-phy",
.data = &sc7180_usb3dpphy_cfg,
},
{
.compatible = "qcom,sm8250-qmp-usb3-dp-phy",
.data = &sm8250_usb3dpphy_cfg,
},
{ }
};

View file

@ -349,13 +349,13 @@
#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4 0x5c
#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5 0x60
/* Only for QMP V3 PHY - DP PHY registers */
#define QSERDES_V3_DP_PHY_REVISION_ID0 0x000
#define QSERDES_V3_DP_PHY_REVISION_ID1 0x004
#define QSERDES_V3_DP_PHY_REVISION_ID2 0x008
#define QSERDES_V3_DP_PHY_REVISION_ID3 0x00c
#define QSERDES_V3_DP_PHY_CFG 0x010
#define QSERDES_V3_DP_PHY_PD_CTL 0x018
/* QMP PHY - DP PHY registers */
#define QSERDES_DP_PHY_REVISION_ID0 0x000
#define QSERDES_DP_PHY_REVISION_ID1 0x004
#define QSERDES_DP_PHY_REVISION_ID2 0x008
#define QSERDES_DP_PHY_REVISION_ID3 0x00c
#define QSERDES_DP_PHY_CFG 0x010
#define QSERDES_DP_PHY_PD_CTL 0x018
# define DP_PHY_PD_CTL_PWRDN 0x001
# define DP_PHY_PD_CTL_PSR_PWRDN 0x002
# define DP_PHY_PD_CTL_AUX_PWRDN 0x004
@ -363,18 +363,19 @@
# define DP_PHY_PD_CTL_LANE_2_3_PWRDN 0x010
# define DP_PHY_PD_CTL_PLL_PWRDN 0x020
# define DP_PHY_PD_CTL_DP_CLAMP_EN 0x040
#define QSERDES_V3_DP_PHY_MODE 0x01c
#define QSERDES_V3_DP_PHY_AUX_CFG0 0x020
#define QSERDES_V3_DP_PHY_AUX_CFG1 0x024
#define QSERDES_V3_DP_PHY_AUX_CFG2 0x028
#define QSERDES_V3_DP_PHY_AUX_CFG3 0x02c
#define QSERDES_V3_DP_PHY_AUX_CFG4 0x030
#define QSERDES_V3_DP_PHY_AUX_CFG5 0x034
#define QSERDES_V3_DP_PHY_AUX_CFG6 0x038
#define QSERDES_V3_DP_PHY_AUX_CFG7 0x03c
#define QSERDES_V3_DP_PHY_AUX_CFG8 0x040
#define QSERDES_V3_DP_PHY_AUX_CFG9 0x044
#define QSERDES_DP_PHY_MODE 0x01c
#define QSERDES_DP_PHY_AUX_CFG0 0x020
#define QSERDES_DP_PHY_AUX_CFG1 0x024
#define QSERDES_DP_PHY_AUX_CFG2 0x028
#define QSERDES_DP_PHY_AUX_CFG3 0x02c
#define QSERDES_DP_PHY_AUX_CFG4 0x030
#define QSERDES_DP_PHY_AUX_CFG5 0x034
#define QSERDES_DP_PHY_AUX_CFG6 0x038
#define QSERDES_DP_PHY_AUX_CFG7 0x03c
#define QSERDES_DP_PHY_AUX_CFG8 0x040
#define QSERDES_DP_PHY_AUX_CFG9 0x044
/* Only for QMP V3 PHY - DP PHY registers */
#define QSERDES_V3_DP_PHY_AUX_INTERRUPT_MASK 0x048
# define PHY_AUX_STOP_ERR_MASK 0x01
# define PHY_AUX_DEC_ERR_MASK 0x02
@ -396,6 +397,7 @@
#define QSERDES_V3_DP_PHY_STATUS 0x0c0
/* Only for QMP V4 PHY - QSERDES COM registers */
#define QSERDES_V4_COM_BG_TIMER 0x00c
#define QSERDES_V4_COM_SSC_EN_CENTER 0x010
#define QSERDES_V4_COM_SSC_PER1 0x01c
#define QSERDES_V4_COM_SSC_PER2 0x020
@ -403,7 +405,9 @@
#define QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0 0x028
#define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1 0x030
#define QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1 0x034
#define QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN 0x044
#define QSERDES_V4_COM_CLK_ENABLE1 0x048
#define QSERDES_V4_COM_SYS_CLK_CTRL 0x04c
#define QSERDES_V4_COM_SYSCLK_BUF_ENABLE 0x050
#define QSERDES_V4_COM_PLL_IVCO 0x058
#define QSERDES_V4_COM_CMN_IPTRIM 0x060
@ -414,6 +418,7 @@
#define QSERDES_V4_COM_PLL_CCTRL_MODE0 0x084
#define QSERDES_V4_COM_PLL_CCTRL_MODE1 0x088
#define QSERDES_V4_COM_SYSCLK_EN_SEL 0x094
#define QSERDES_V4_COM_RESETSM_CNTRL 0x09c
#define QSERDES_V4_COM_LOCK_CMP_EN 0x0a4
#define QSERDES_V4_COM_LOCK_CMP1_MODE0 0x0ac
#define QSERDES_V4_COM_LOCK_CMP2_MODE0 0x0b0
@ -427,16 +432,24 @@
#define QSERDES_V4_COM_DIV_FRAC_START1_MODE1 0x0d8
#define QSERDES_V4_COM_DIV_FRAC_START2_MODE1 0x0dc
#define QSERDES_V4_COM_DIV_FRAC_START3_MODE1 0x0e0
#define QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0 0x0ec
#define QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0 0x0f0
#define QSERDES_V4_COM_VCO_TUNE_CTRL 0x108
#define QSERDES_V4_COM_VCO_TUNE_MAP 0x10c
#define QSERDES_V4_COM_VCO_TUNE1_MODE0 0x110
#define QSERDES_V4_COM_VCO_TUNE2_MODE0 0x114
#define QSERDES_V4_COM_VCO_TUNE1_MODE1 0x118
#define QSERDES_V4_COM_VCO_TUNE2_MODE1 0x11c
#define QSERDES_V4_COM_VCO_TUNE_INITVAL2 0x124
#define QSERDES_V4_COM_CMN_STATUS 0x140
#define QSERDES_V4_COM_CLK_SELECT 0x154
#define QSERDES_V4_COM_HSCLK_SEL 0x158
#define QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL 0x15c
#define QSERDES_V4_COM_CORECLK_DIV_MODE0 0x168
#define QSERDES_V4_COM_CORECLK_DIV_MODE1 0x16c
#define QSERDES_V4_COM_CORE_CLK_EN 0x174
#define QSERDES_V4_COM_C_READY_STATUS 0x178
#define QSERDES_V4_COM_CMN_CONFIG 0x17c
#define QSERDES_V4_COM_SVS_MODE_CLK_SEL 0x184
#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x1ac
#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1b0
@ -445,19 +458,32 @@
#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x1b8
/* Only for QMP V4 PHY - TX registers */
#define QSERDES_V4_TX_CLKBUF_ENABLE 0x08
#define QSERDES_V4_TX_TX_EMP_POST1_LVL 0x0c
#define QSERDES_V4_TX_TX_DRV_LVL 0x14
#define QSERDES_V4_TX_RESET_TSYNC_EN 0x1c
#define QSERDES_V4_TX_PRE_STALL_LDO_BOOST_EN 0x20
#define QSERDES_V4_TX_TX_BAND 0x24
#define QSERDES_V4_TX_INTERFACE_SELECT 0x2c
#define QSERDES_V4_TX_RES_CODE_LANE_TX 0x34
#define QSERDES_V4_TX_RES_CODE_LANE_RX 0x38
#define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX 0x3c
#define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX 0x40
#define QSERDES_V4_TX_TRANSCEIVER_BIAS_EN 0x54
#define QSERDES_V4_TX_HIGHZ_DRVR_EN 0x58
#define QSERDES_V4_TX_TX_POL_INV 0x5c
#define QSERDES_V4_TX_PARRATE_REC_DETECT_IDLE_EN 0x60
#define QSERDES_V4_TX_LANE_MODE_1 0x84
#define QSERDES_V4_TX_LANE_MODE_2 0x88
#define QSERDES_V4_TX_RCV_DETECT_LVL_2 0x9c
#define QSERDES_V4_TX_TRAN_DRVR_EMP_EN 0xb8
#define QSERDES_V4_TX_TX_INTERFACE_MODE 0xbc
#define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0xd8
#define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0xdC
#define QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0xe0
#define QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0xe4
#define QSERDES_V4_TX_TRAN_DRVR_EMP_EN 0xb8
#define QSERDES_V4_TX_PI_QEC_CTRL 0x104
#define QSERDES_V4_TX_VMODE_CTRL1 0xe8
#define QSERDES_V4_TX_PI_QEC_CTRL 0x104
/* Only for QMP V4 PHY - RX registers */
#define QSERDES_V4_RX_UCDR_FO_GAIN 0x008
@ -514,6 +540,17 @@
#define QSERDES_V4_RX_DCC_CTRL1 0x1bc
#define QSERDES_V4_RX_VTH_CODE 0x1c4
/* Only for QMP V4 PHY - DP PHY registers */
#define QSERDES_V4_DP_PHY_CFG_1 0x014
#define QSERDES_V4_DP_PHY_AUX_INTERRUPT_MASK 0x054
#define QSERDES_V4_DP_PHY_AUX_INTERRUPT_CLEAR 0x058
#define QSERDES_V4_DP_PHY_VCO_DIV 0x070
#define QSERDES_V4_DP_PHY_TX0_TX1_LANE_CTL 0x078
#define QSERDES_V4_DP_PHY_TX2_TX3_LANE_CTL 0x09c
#define QSERDES_V4_DP_PHY_SPARE0 0x0c8
#define QSERDES_V4_DP_PHY_AUX_INTERRUPT_STATUS 0x0d8
#define QSERDES_V4_DP_PHY_STATUS 0x0dc
/* Only for QMP V4 PHY - UFS PCS registers */
#define QPHY_V4_PCS_UFS_PHY_START 0x000
#define QPHY_V4_PCS_UFS_POWER_DOWN_CONTROL 0x004

View file

@ -56,6 +56,7 @@ static int qcom_usb_hs_phy_set_mode(struct phy *phy,
fallthrough;
case PHY_MODE_USB_DEVICE:
val |= ULPI_INT_SESS_VALID;
break;
default:
break;
}

View file

@ -62,7 +62,7 @@
#define RG_PE1_FRC_MSTCKDIV BIT(5)
#define XTAL_MASK GENMASK(7, 6)
#define XTAL_MASK GENMASK(8, 6)
#define MAX_PHYS 2
@ -319,9 +319,9 @@ static int mt7621_pci_phy_probe(struct platform_device *pdev)
return PTR_ERR(phy->regmap);
phy->phy = devm_phy_create(dev, dev->of_node, &mt7621_pci_phy_ops);
if (IS_ERR(phy)) {
if (IS_ERR(phy->phy)) {
dev_err(dev, "failed to create phy\n");
return PTR_ERR(phy);
return PTR_ERR(phy->phy);
}
phy_set_drvdata(phy->phy, phy);

View file

@ -1180,6 +1180,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
dev_err(dev, "failed to create phy: %pOFn\n",
child_np);
pm_runtime_disable(dev);
of_node_put(child_np);
return PTR_ERR(phy);
}

View file

@ -36,6 +36,7 @@ config PHY_STIH407_USB
config PHY_STM32_USBPHYC
tristate "STMicroelectronics STM32 USB HS PHY Controller driver"
depends on ARCH_STM32 || COMPILE_TEST
depends on COMMON_CLK
select GENERIC_PHY
help
Enable this to support the High-Speed USB transceivers that are part

View file

@ -7,6 +7,7 @@
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@ -70,6 +71,7 @@ struct stm32_usbphyc {
struct regulator *vdda1v1;
struct regulator *vdda1v8;
atomic_t n_pll_cons;
struct clk_hw clk48_hw;
int switch_setup;
};
@ -295,6 +297,61 @@ static const struct phy_ops stm32_usbphyc_phy_ops = {
.owner = THIS_MODULE,
};
static int stm32_usbphyc_clk48_prepare(struct clk_hw *hw)
{
struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, clk48_hw);
return stm32_usbphyc_pll_enable(usbphyc);
}
static void stm32_usbphyc_clk48_unprepare(struct clk_hw *hw)
{
struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, clk48_hw);
stm32_usbphyc_pll_disable(usbphyc);
}
static unsigned long stm32_usbphyc_clk48_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
return 48000000;
}
static const struct clk_ops usbphyc_clk48_ops = {
.prepare = stm32_usbphyc_clk48_prepare,
.unprepare = stm32_usbphyc_clk48_unprepare,
.recalc_rate = stm32_usbphyc_clk48_recalc_rate,
};
static void stm32_usbphyc_clk48_unregister(void *data)
{
struct stm32_usbphyc *usbphyc = data;
of_clk_del_provider(usbphyc->dev->of_node);
clk_hw_unregister(&usbphyc->clk48_hw);
}
static int stm32_usbphyc_clk48_register(struct stm32_usbphyc *usbphyc)
{
struct device_node *node = usbphyc->dev->of_node;
struct clk_init_data init = { };
int ret = 0;
init.name = "ck_usbo_48m";
init.ops = &usbphyc_clk48_ops;
usbphyc->clk48_hw.init = &init;
ret = clk_hw_register(usbphyc->dev, &usbphyc->clk48_hw);
if (ret)
return ret;
ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &usbphyc->clk48_hw);
if (ret)
clk_hw_unregister(&usbphyc->clk48_hw);
return ret;
}
static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc,
u32 utmi_switch)
{
@ -473,6 +530,12 @@ static int stm32_usbphyc_probe(struct platform_device *pdev)
goto clk_disable;
}
ret = stm32_usbphyc_clk48_register(usbphyc);
if (ret) {
dev_err(dev, "failed to register ck_usbo_48m clock: %d\n", ret);
goto clk_disable;
}
version = readl_relaxed(usbphyc->base + STM32_USBPHYC_VERSION);
dev_info(dev, "registered rev:%lu.%lu\n",
FIELD_GET(MAJREV, version), FIELD_GET(MINREV, version));
@ -497,6 +560,8 @@ static int stm32_usbphyc_remove(struct platform_device *pdev)
if (usbphyc->phys[port]->active)
stm32_usbphyc_phy_exit(usbphyc->phys[port]->phy);
stm32_usbphyc_clk48_unregister(usbphyc);
clk_disable_unprepare(usbphyc->clk);
return 0;

View file

@ -7,6 +7,7 @@
*/
#include <dt-bindings/phy/phy.h>
#include <dt-bindings/phy/phy-ti.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/gpio.h>
@ -26,6 +27,11 @@
#define WIZ_SERDES_RST 0x40c
#define WIZ_SERDES_TYPEC 0x410
#define WIZ_LANECTL(n) (0x480 + (0x40 * (n)))
#define WIZ_LANEDIV(n) (0x484 + (0x40 * (n)))
#define WIZ_MAX_INPUT_CLOCKS 4
/* To include mux clocks, divider clocks and gate clocks */
#define WIZ_MAX_OUTPUT_CLOCKS 32
#define WIZ_MAX_LANES 4
#define WIZ_MUX_NUM_CLOCKS 3
@ -52,8 +58,16 @@ enum wiz_refclk_div_sel {
CMN_REFCLK1_DIG_DIV,
};
enum wiz_clock_input {
WIZ_CORE_REFCLK,
WIZ_EXT_REFCLK,
WIZ_CORE_REFCLK1,
WIZ_EXT_REFCLK1,
};
static const struct reg_field por_en = REG_FIELD(WIZ_SERDES_CTRL, 31, 31);
static const struct reg_field phy_reset_n = REG_FIELD(WIZ_SERDES_RST, 31, 31);
static const struct reg_field phy_en_refclk = REG_FIELD(WIZ_SERDES_RST, 30, 30);
static const struct reg_field pll1_refclk_mux_sel =
REG_FIELD(WIZ_SERDES_RST, 29, 29);
static const struct reg_field pll0_refclk_mux_sel =
@ -70,6 +84,12 @@ static const struct reg_field pma_cmn_refclk_dig_div =
REG_FIELD(WIZ_SERDES_TOP_CTRL, 26, 27);
static const struct reg_field pma_cmn_refclk1_dig_div =
REG_FIELD(WIZ_SERDES_TOP_CTRL, 24, 25);
static const char * const output_clk_names[] = {
[TI_WIZ_PLL0_REFCLK] = "pll0-refclk",
[TI_WIZ_PLL1_REFCLK] = "pll1-refclk",
[TI_WIZ_REFCLK_DIG] = "refclk-dig",
[TI_WIZ_PHY_EN_REFCLK] = "phy-en-refclk",
};
static const struct reg_field p_enable[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(0), 30, 31),
@ -101,13 +121,34 @@ static const struct reg_field p_standard_mode[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(3), 24, 25),
};
static const struct reg_field p0_fullrt_div[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(0), 22, 23),
REG_FIELD(WIZ_LANECTL(1), 22, 23),
REG_FIELD(WIZ_LANECTL(2), 22, 23),
REG_FIELD(WIZ_LANECTL(3), 22, 23),
};
static const struct reg_field p_mac_div_sel0[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANEDIV(0), 16, 22),
REG_FIELD(WIZ_LANEDIV(1), 16, 22),
REG_FIELD(WIZ_LANEDIV(2), 16, 22),
REG_FIELD(WIZ_LANEDIV(3), 16, 22),
};
static const struct reg_field p_mac_div_sel1[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANEDIV(0), 0, 8),
REG_FIELD(WIZ_LANEDIV(1), 0, 8),
REG_FIELD(WIZ_LANEDIV(2), 0, 8),
REG_FIELD(WIZ_LANEDIV(3), 0, 8),
};
static const struct reg_field typec_ln10_swap =
REG_FIELD(WIZ_SERDES_TYPEC, 30, 30);
struct wiz_clk_mux {
struct clk_hw hw;
struct regmap_field *field;
u32 *table;
const u32 *table;
struct clk_init_data clk_data;
};
@ -123,18 +164,26 @@ struct wiz_clk_divider {
#define to_wiz_clk_div(_hw) container_of(_hw, struct wiz_clk_divider, hw)
struct wiz_clk_mux_sel {
struct regmap_field *field;
u32 table[4];
u32 table[WIZ_MAX_INPUT_CLOCKS];
const char *node_name;
u32 num_parents;
u32 parents[WIZ_MAX_INPUT_CLOCKS];
};
struct wiz_clk_div_sel {
struct regmap_field *field;
const struct clk_div_table *table;
const struct clk_div_table *table;
const char *node_name;
};
static struct wiz_clk_mux_sel clk_mux_sel_16g[] = {
struct wiz_phy_en_refclk {
struct clk_hw hw;
struct regmap_field *phy_en_refclk;
struct clk_init_data clk_data;
};
#define to_wiz_phy_en_refclk(_hw) container_of(_hw, struct wiz_phy_en_refclk, hw)
static const struct wiz_clk_mux_sel clk_mux_sel_16g[] = {
{
/*
* Mux value to be configured for each of the input clocks
@ -153,20 +202,26 @@ static struct wiz_clk_mux_sel clk_mux_sel_16g[] = {
},
};
static struct wiz_clk_mux_sel clk_mux_sel_10g[] = {
static const struct wiz_clk_mux_sel clk_mux_sel_10g[] = {
{
/*
* Mux value to be configured for each of the input clocks
* in the order populated in device tree
*/
.num_parents = 2,
.parents = { WIZ_CORE_REFCLK, WIZ_EXT_REFCLK },
.table = { 1, 0 },
.node_name = "pll0-refclk",
},
{
.num_parents = 2,
.parents = { WIZ_CORE_REFCLK, WIZ_EXT_REFCLK },
.table = { 1, 0 },
.node_name = "pll1-refclk",
},
{
.num_parents = 2,
.parents = { WIZ_CORE_REFCLK, WIZ_EXT_REFCLK },
.table = { 1, 0 },
.node_name = "refclk-dig",
},
@ -179,7 +234,7 @@ static const struct clk_div_table clk_div_table[] = {
{ .val = 3, .div = 8, },
};
static struct wiz_clk_div_sel clk_div_sel[] = {
static const struct wiz_clk_div_sel clk_div_sel[] = {
{
.table = clk_div_table,
.node_name = "cmn-refclk-dig-div",
@ -193,6 +248,7 @@ static struct wiz_clk_div_sel clk_div_sel[] = {
enum wiz_type {
J721E_WIZ_16G,
J721E_WIZ_10G,
AM64_WIZ_10G,
};
#define WIZ_TYPEC_DIR_DEBOUNCE_MIN 100 /* ms */
@ -201,19 +257,25 @@ enum wiz_type {
struct wiz {
struct regmap *regmap;
enum wiz_type type;
struct wiz_clk_mux_sel *clk_mux_sel;
struct wiz_clk_div_sel *clk_div_sel;
const struct wiz_clk_mux_sel *clk_mux_sel;
const struct wiz_clk_div_sel *clk_div_sel;
unsigned int clk_div_sel_num;
struct regmap_field *por_en;
struct regmap_field *phy_reset_n;
struct regmap_field *phy_en_refclk;
struct regmap_field *p_enable[WIZ_MAX_LANES];
struct regmap_field *p_align[WIZ_MAX_LANES];
struct regmap_field *p_raw_auto_start[WIZ_MAX_LANES];
struct regmap_field *p_standard_mode[WIZ_MAX_LANES];
struct regmap_field *p_mac_div_sel0[WIZ_MAX_LANES];
struct regmap_field *p_mac_div_sel1[WIZ_MAX_LANES];
struct regmap_field *p0_fullrt_div[WIZ_MAX_LANES];
struct regmap_field *pma_cmn_refclk_int_mode;
struct regmap_field *pma_cmn_refclk_mode;
struct regmap_field *pma_cmn_refclk_dig_div;
struct regmap_field *pma_cmn_refclk1_dig_div;
struct regmap_field *mux_sel_field[WIZ_MUX_NUM_CLOCKS];
struct regmap_field *div_sel_field[WIZ_DIV_NUM_CLOCKS_16G];
struct regmap_field *typec_ln10_swap;
struct device *dev;
@ -223,6 +285,9 @@ struct wiz {
struct gpio_desc *gpio_typec_dir;
int typec_dir_delay;
u32 lane_phy_type[WIZ_MAX_LANES];
struct clk *input_clks[WIZ_MAX_INPUT_CLOCKS];
struct clk *output_clks[WIZ_MAX_OUTPUT_CLOCKS];
struct clk_onecell_data clk_data;
};
static int wiz_reset(struct wiz *wiz)
@ -242,6 +307,27 @@ static int wiz_reset(struct wiz *wiz)
return 0;
}
static int wiz_p_mac_div_sel(struct wiz *wiz)
{
u32 num_lanes = wiz->num_lanes;
int ret;
int i;
for (i = 0; i < num_lanes; i++) {
if (wiz->lane_phy_type[i] == PHY_TYPE_QSGMII) {
ret = regmap_field_write(wiz->p_mac_div_sel0[i], 1);
if (ret)
return ret;
ret = regmap_field_write(wiz->p_mac_div_sel1[i], 2);
if (ret)
return ret;
}
}
return 0;
}
static int wiz_mode_select(struct wiz *wiz)
{
u32 num_lanes = wiz->num_lanes;
@ -252,8 +338,10 @@ static int wiz_mode_select(struct wiz *wiz)
for (i = 0; i < num_lanes; i++) {
if (wiz->lane_phy_type[i] == PHY_TYPE_DP)
mode = LANE_MODE_GEN1;
else if (wiz->lane_phy_type[i] == PHY_TYPE_QSGMII)
mode = LANE_MODE_GEN2;
else
mode = LANE_MODE_GEN4;
continue;
ret = regmap_field_write(wiz->p_standard_mode[i], mode);
if (ret)
@ -299,6 +387,12 @@ static int wiz_init(struct wiz *wiz)
return ret;
}
ret = wiz_p_mac_div_sel(wiz);
if (ret) {
dev_err(dev, "Configuring P0 MAC DIV SEL failed\n");
return ret;
}
ret = wiz_init_raw_interface(wiz, true);
if (ret) {
dev_err(dev, "WIZ interface initialization failed\n");
@ -310,8 +404,6 @@ static int wiz_init(struct wiz *wiz)
static int wiz_regfield_init(struct wiz *wiz)
{
struct wiz_clk_mux_sel *clk_mux_sel;
struct wiz_clk_div_sel *clk_div_sel;
struct regmap *regmap = wiz->regmap;
int num_lanes = wiz->num_lanes;
struct device *dev = wiz->dev;
@ -344,54 +436,49 @@ static int wiz_regfield_init(struct wiz *wiz)
return PTR_ERR(wiz->pma_cmn_refclk_mode);
}
clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK_DIG_DIV];
clk_div_sel->field = devm_regmap_field_alloc(dev, regmap,
pma_cmn_refclk_dig_div);
if (IS_ERR(clk_div_sel->field)) {
wiz->div_sel_field[CMN_REFCLK_DIG_DIV] =
devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk_dig_div);
if (IS_ERR(wiz->div_sel_field[CMN_REFCLK_DIG_DIV])) {
dev_err(dev, "PMA_CMN_REFCLK_DIG_DIV reg field init failed\n");
return PTR_ERR(clk_div_sel->field);
return PTR_ERR(wiz->div_sel_field[CMN_REFCLK_DIG_DIV]);
}
if (wiz->type == J721E_WIZ_16G) {
clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK1_DIG_DIV];
clk_div_sel->field =
wiz->div_sel_field[CMN_REFCLK1_DIG_DIV] =
devm_regmap_field_alloc(dev, regmap,
pma_cmn_refclk1_dig_div);
if (IS_ERR(clk_div_sel->field)) {
if (IS_ERR(wiz->div_sel_field[CMN_REFCLK1_DIG_DIV])) {
dev_err(dev, "PMA_CMN_REFCLK1_DIG_DIV reg field init failed\n");
return PTR_ERR(clk_div_sel->field);
return PTR_ERR(wiz->div_sel_field[CMN_REFCLK1_DIG_DIV]);
}
}
clk_mux_sel = &wiz->clk_mux_sel[PLL0_REFCLK];
clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap,
pll0_refclk_mux_sel);
if (IS_ERR(clk_mux_sel->field)) {
wiz->mux_sel_field[PLL0_REFCLK] =
devm_regmap_field_alloc(dev, regmap, pll0_refclk_mux_sel);
if (IS_ERR(wiz->mux_sel_field[PLL0_REFCLK])) {
dev_err(dev, "PLL0_REFCLK_SEL reg field init failed\n");
return PTR_ERR(clk_mux_sel->field);
return PTR_ERR(wiz->mux_sel_field[PLL0_REFCLK]);
}
clk_mux_sel = &wiz->clk_mux_sel[PLL1_REFCLK];
clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap,
pll1_refclk_mux_sel);
if (IS_ERR(clk_mux_sel->field)) {
wiz->mux_sel_field[PLL1_REFCLK] =
devm_regmap_field_alloc(dev, regmap, pll1_refclk_mux_sel);
if (IS_ERR(wiz->mux_sel_field[PLL1_REFCLK])) {
dev_err(dev, "PLL1_REFCLK_SEL reg field init failed\n");
return PTR_ERR(clk_mux_sel->field);
return PTR_ERR(wiz->mux_sel_field[PLL1_REFCLK]);
}
clk_mux_sel = &wiz->clk_mux_sel[REFCLK_DIG];
if (wiz->type == J721E_WIZ_10G)
clk_mux_sel->field =
if (wiz->type == J721E_WIZ_10G || wiz->type == AM64_WIZ_10G)
wiz->mux_sel_field[REFCLK_DIG] =
devm_regmap_field_alloc(dev, regmap,
refclk_dig_sel_10g);
else
clk_mux_sel->field =
wiz->mux_sel_field[REFCLK_DIG] =
devm_regmap_field_alloc(dev, regmap,
refclk_dig_sel_16g);
if (IS_ERR(clk_mux_sel->field)) {
if (IS_ERR(wiz->mux_sel_field[REFCLK_DIG])) {
dev_err(dev, "REFCLK_DIG_SEL reg field init failed\n");
return PTR_ERR(clk_mux_sel->field);
return PTR_ERR(wiz->mux_sel_field[REFCLK_DIG]);
}
for (i = 0; i < num_lanes; i++) {
@ -424,6 +511,28 @@ static int wiz_regfield_init(struct wiz *wiz)
i);
return PTR_ERR(wiz->p_standard_mode[i]);
}
wiz->p0_fullrt_div[i] = devm_regmap_field_alloc(dev, regmap, p0_fullrt_div[i]);
if (IS_ERR(wiz->p0_fullrt_div[i])) {
dev_err(dev, "P%d_FULLRT_DIV reg field init failed\n", i);
return PTR_ERR(wiz->p0_fullrt_div[i]);
}
wiz->p_mac_div_sel0[i] =
devm_regmap_field_alloc(dev, regmap, p_mac_div_sel0[i]);
if (IS_ERR(wiz->p_mac_div_sel0[i])) {
dev_err(dev, "P%d_MAC_DIV_SEL0 reg field init fail\n",
i);
return PTR_ERR(wiz->p_mac_div_sel0[i]);
}
wiz->p_mac_div_sel1[i] =
devm_regmap_field_alloc(dev, regmap, p_mac_div_sel1[i]);
if (IS_ERR(wiz->p_mac_div_sel1[i])) {
dev_err(dev, "P%d_MAC_DIV_SEL1 reg field init fail\n",
i);
return PTR_ERR(wiz->p_mac_div_sel1[i]);
}
}
wiz->typec_ln10_swap = devm_regmap_field_alloc(dev, regmap,
@ -433,6 +542,76 @@ static int wiz_regfield_init(struct wiz *wiz)
return PTR_ERR(wiz->typec_ln10_swap);
}
wiz->phy_en_refclk = devm_regmap_field_alloc(dev, regmap, phy_en_refclk);
if (IS_ERR(wiz->phy_en_refclk)) {
dev_err(dev, "PHY_EN_REFCLK reg field init failed\n");
return PTR_ERR(wiz->phy_en_refclk);
}
return 0;
}
static int wiz_phy_en_refclk_enable(struct clk_hw *hw)
{
struct wiz_phy_en_refclk *wiz_phy_en_refclk = to_wiz_phy_en_refclk(hw);
struct regmap_field *phy_en_refclk = wiz_phy_en_refclk->phy_en_refclk;
regmap_field_write(phy_en_refclk, 1);
return 0;
}
static void wiz_phy_en_refclk_disable(struct clk_hw *hw)
{
struct wiz_phy_en_refclk *wiz_phy_en_refclk = to_wiz_phy_en_refclk(hw);
struct regmap_field *phy_en_refclk = wiz_phy_en_refclk->phy_en_refclk;
regmap_field_write(phy_en_refclk, 0);
}
static int wiz_phy_en_refclk_is_enabled(struct clk_hw *hw)
{
struct wiz_phy_en_refclk *wiz_phy_en_refclk = to_wiz_phy_en_refclk(hw);
struct regmap_field *phy_en_refclk = wiz_phy_en_refclk->phy_en_refclk;
int val;
regmap_field_read(phy_en_refclk, &val);
return !!val;
}
static const struct clk_ops wiz_phy_en_refclk_ops = {
.enable = wiz_phy_en_refclk_enable,
.disable = wiz_phy_en_refclk_disable,
.is_enabled = wiz_phy_en_refclk_is_enabled,
};
static int wiz_phy_en_refclk_register(struct wiz *wiz)
{
struct wiz_phy_en_refclk *wiz_phy_en_refclk;
struct device *dev = wiz->dev;
struct clk_init_data *init;
struct clk *clk;
wiz_phy_en_refclk = devm_kzalloc(dev, sizeof(*wiz_phy_en_refclk), GFP_KERNEL);
if (!wiz_phy_en_refclk)
return -ENOMEM;
init = &wiz_phy_en_refclk->clk_data;
init->ops = &wiz_phy_en_refclk_ops;
init->flags = 0;
init->name = output_clk_names[TI_WIZ_PHY_EN_REFCLK];
wiz_phy_en_refclk->phy_en_refclk = wiz->phy_en_refclk;
wiz_phy_en_refclk->hw.init = init;
clk = devm_clk_register(dev, &wiz_phy_en_refclk->hw);
if (IS_ERR(clk))
return PTR_ERR(clk);
wiz->output_clks[TI_WIZ_PHY_EN_REFCLK] = clk;
return 0;
}
@ -443,7 +622,7 @@ static u8 wiz_clk_mux_get_parent(struct clk_hw *hw)
unsigned int val;
regmap_field_read(field, &val);
return clk_mux_val_to_index(hw, mux->table, 0, val);
return clk_mux_val_to_index(hw, (u32 *)mux->table, 0, val);
}
static int wiz_clk_mux_set_parent(struct clk_hw *hw, u8 index)
@ -461,8 +640,69 @@ static const struct clk_ops wiz_clk_mux_ops = {
.get_parent = wiz_clk_mux_get_parent,
};
static int wiz_mux_clk_register(struct wiz *wiz, struct device_node *node,
struct regmap_field *field, u32 *table)
static int wiz_mux_clk_register(struct wiz *wiz, struct regmap_field *field,
const struct wiz_clk_mux_sel *mux_sel, int clk_index)
{
struct device *dev = wiz->dev;
struct clk_init_data *init;
const char **parent_names;
unsigned int num_parents;
struct wiz_clk_mux *mux;
char clk_name[100];
struct clk *clk;
int ret = 0, i;
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return -ENOMEM;
num_parents = mux_sel->num_parents;
parent_names = kzalloc((sizeof(char *) * num_parents), GFP_KERNEL);
if (!parent_names)
return -ENOMEM;
for (i = 0; i < num_parents; i++) {
clk = wiz->input_clks[mux_sel->parents[i]];
if (IS_ERR_OR_NULL(clk)) {
dev_err(dev, "Failed to get parent clk for %s\n",
output_clk_names[clk_index]);
ret = -EINVAL;
goto err;
}
parent_names[i] = __clk_get_name(clk);
}
snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), output_clk_names[clk_index]);
init = &mux->clk_data;
init->ops = &wiz_clk_mux_ops;
init->flags = CLK_SET_RATE_NO_REPARENT;
init->parent_names = parent_names;
init->num_parents = num_parents;
init->name = clk_name;
mux->field = field;
mux->table = mux_sel->table;
mux->hw.init = init;
clk = devm_clk_register(dev, &mux->hw);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto err;
}
wiz->output_clks[clk_index] = clk;
err:
kfree(parent_names);
return ret;
}
static int wiz_mux_of_clk_register(struct wiz *wiz, struct device_node *node,
struct regmap_field *field, const u32 *table)
{
struct device *dev = wiz->dev;
struct clk_init_data *init;
@ -606,20 +846,70 @@ static int wiz_div_clk_register(struct wiz *wiz, struct device_node *node,
static void wiz_clock_cleanup(struct wiz *wiz, struct device_node *node)
{
struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
const struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
struct device *dev = wiz->dev;
struct device_node *clk_node;
int i;
if (wiz->type == AM64_WIZ_10G) {
of_clk_del_provider(dev->of_node);
return;
}
for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) {
clk_node = of_get_child_by_name(node, clk_mux_sel[i].node_name);
of_clk_del_provider(clk_node);
of_node_put(clk_node);
}
for (i = 0; i < wiz->clk_div_sel_num; i++) {
clk_node = of_get_child_by_name(node, clk_div_sel[i].node_name);
of_clk_del_provider(clk_node);
of_node_put(clk_node);
}
of_clk_del_provider(wiz->dev->of_node);
}
static int wiz_clock_register(struct wiz *wiz)
{
const struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
struct device *dev = wiz->dev;
struct device_node *node = dev->of_node;
int clk_index;
int ret;
int i;
if (wiz->type != AM64_WIZ_10G)
return 0;
clk_index = TI_WIZ_PLL0_REFCLK;
for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++, clk_index++) {
ret = wiz_mux_clk_register(wiz, wiz->mux_sel_field[i], &clk_mux_sel[i], clk_index);
if (ret) {
dev_err(dev, "Failed to register clk: %s\n", output_clk_names[clk_index]);
return ret;
}
}
ret = wiz_phy_en_refclk_register(wiz);
if (ret) {
dev_err(dev, "Failed to add phy-en-refclk\n");
return ret;
}
wiz->clk_data.clks = wiz->output_clks;
wiz->clk_data.clk_num = WIZ_MAX_OUTPUT_CLOCKS;
ret = of_clk_add_provider(node, of_clk_src_onecell_get, &wiz->clk_data);
if (ret)
dev_err(dev, "Failed to add clock provider: %s\n", node->name);
return ret;
}
static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
{
struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
const struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
struct device *dev = wiz->dev;
struct device_node *clk_node;
const char *node_name;
@ -634,6 +924,7 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
ret = PTR_ERR(clk);
return ret;
}
wiz->input_clks[WIZ_CORE_REFCLK] = clk;
rate = clk_get_rate(clk);
if (rate >= 100000000)
@ -647,6 +938,7 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
ret = PTR_ERR(clk);
return ret;
}
wiz->input_clks[WIZ_EXT_REFCLK] = clk;
rate = clk_get_rate(clk);
if (rate >= 100000000)
@ -654,6 +946,13 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
else
regmap_field_write(wiz->pma_cmn_refclk_mode, 0x2);
if (wiz->type == AM64_WIZ_10G) {
ret = wiz_clock_register(wiz);
if (ret)
dev_err(dev, "Failed to register wiz clocks\n");
return ret;
}
for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) {
node_name = clk_mux_sel[i].node_name;
clk_node = of_get_child_by_name(node, node_name);
@ -663,8 +962,8 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
goto err;
}
ret = wiz_mux_clk_register(wiz, clk_node, clk_mux_sel[i].field,
clk_mux_sel[i].table);
ret = wiz_mux_of_clk_register(wiz, clk_node, wiz->mux_sel_field[i],
clk_mux_sel[i].table);
if (ret) {
dev_err(dev, "Failed to register %s clock\n",
node_name);
@ -684,7 +983,7 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
goto err;
}
ret = wiz_div_clk_register(wiz, clk_node, clk_div_sel[i].field,
ret = wiz_div_clk_register(wiz, clk_node, wiz->div_sel_field[i],
clk_div_sel[i].table);
if (ret) {
dev_err(dev, "Failed to register %s clock\n",
@ -719,6 +1018,17 @@ static int wiz_phy_reset_assert(struct reset_controller_dev *rcdev,
return ret;
}
static int wiz_phy_fullrt_div(struct wiz *wiz, int lane)
{
if (wiz->type != AM64_WIZ_10G)
return 0;
if (wiz->lane_phy_type[lane] == PHY_TYPE_PCIE)
return regmap_field_write(wiz->p0_fullrt_div[lane], 0x1);
return 0;
}
static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
@ -742,6 +1052,10 @@ static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev,
return ret;
}
ret = wiz_phy_fullrt_div(wiz, id - 1);
if (ret)
return ret;
if (wiz->lane_phy_type[id - 1] == PHY_TYPE_DP)
ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE);
else
@ -769,6 +1083,9 @@ static const struct of_device_id wiz_id_table[] = {
{
.compatible = "ti,j721e-wiz-10g", .data = (void *)J721E_WIZ_10G
},
{
.compatible = "ti,am64-wiz-10g", .data = (void *)AM64_WIZ_10G
},
{}
};
MODULE_DEVICE_TABLE(of, wiz_id_table);
@ -787,11 +1104,17 @@ static int wiz_get_lane_phy_types(struct device *dev, struct wiz *wiz)
u32 reg, num_lanes = 1, phy_type = PHY_NONE;
int ret, i;
if (!(of_node_name_eq(subnode, "phy") ||
of_node_name_eq(subnode, "link")))
continue;
ret = of_property_read_u32(subnode, "reg", &reg);
if (ret) {
of_node_put(subnode);
dev_err(dev,
"%s: Reading \"reg\" from \"%s\" failed: %d\n",
__func__, subnode->name, ret);
of_node_put(subnode);
return ret;
}
of_property_read_u32(subnode, "cdns,num-lanes", &num_lanes);
@ -813,13 +1136,14 @@ static int wiz_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct platform_device *serdes_pdev;
bool already_configured = false;
struct device_node *child_node;
struct regmap *regmap;
struct resource res;
void __iomem *base;
struct wiz *wiz;
int ret, val, i;
u32 num_lanes;
int ret;
wiz = devm_kzalloc(dev, sizeof(*wiz), GFP_KERNEL);
if (!wiz)
@ -900,14 +1224,14 @@ static int wiz_probe(struct platform_device *pdev)
wiz->dev = dev;
wiz->regmap = regmap;
wiz->num_lanes = num_lanes;
if (wiz->type == J721E_WIZ_10G)
if (wiz->type == J721E_WIZ_10G || wiz->type == AM64_WIZ_10G)
wiz->clk_mux_sel = clk_mux_sel_10g;
else
wiz->clk_mux_sel = clk_mux_sel_16g;
wiz->clk_div_sel = clk_div_sel;
if (wiz->type == J721E_WIZ_10G)
if (wiz->type == J721E_WIZ_10G || wiz->type == AM64_WIZ_10G)
wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G;
else
wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_16G;
@ -947,27 +1271,34 @@ static int wiz_probe(struct platform_device *pdev)
goto err_get_sync;
}
for (i = 0; i < wiz->num_lanes; i++) {
regmap_field_read(wiz->p_enable[i], &val);
if (val & (P_ENABLE | P_ENABLE_FORCE)) {
already_configured = true;
break;
}
}
if (!already_configured) {
ret = wiz_init(wiz);
if (ret) {
dev_err(dev, "WIZ initialization failed\n");
goto err_wiz_init;
}
}
serdes_pdev = of_platform_device_create(child_node, NULL, dev);
if (!serdes_pdev) {
dev_WARN(dev, "Unable to create SERDES platform device\n");
ret = -ENOMEM;
goto err_pdev_create;
}
wiz->serdes_pdev = serdes_pdev;
ret = wiz_init(wiz);
if (ret) {
dev_err(dev, "WIZ initialization failed\n");
goto err_wiz_init;
}
wiz->serdes_pdev = serdes_pdev;
of_node_put(child_node);
return 0;
err_wiz_init:
of_platform_device_destroy(&serdes_pdev->dev, NULL);
err_pdev_create:
wiz_clock_cleanup(wiz, node);
err_get_sync:

View file

@ -7,15 +7,16 @@
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
*/
#include <linux/module.h>
#include <linux/bitfield.h>
#include <linux/ulpi/driver.h>
#include <linux/ulpi/regs.h>
#include <linux/gpio/consumer.h>
#include <linux/phy/ulpi_phy.h>
#define TUSB1210_VENDOR_SPECIFIC2 0x80
#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0
#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4
#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6
#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK GENMASK(3, 0)
#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK GENMASK(5, 4)
#define TUSB1210_VENDOR_SPECIFIC2_DP_MASK BIT(6)
struct tusb1210 {
struct ulpi *ulpi;
@ -118,22 +119,22 @@ static int tusb1210_probe(struct ulpi *ulpi)
* diagram optimization and DP/DM swap.
*/
reg = ulpi_read(ulpi, TUSB1210_VENDOR_SPECIFIC2);
/* High speed output drive strength configuration */
device_property_read_u8(&ulpi->dev, "ihstx", &val);
reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT;
if (!device_property_read_u8(&ulpi->dev, "ihstx", &val))
u8p_replace_bits(&reg, val, (u8)TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK);
/* High speed output impedance configuration */
device_property_read_u8(&ulpi->dev, "zhsdrv", &val);
reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT;
if (!device_property_read_u8(&ulpi->dev, "zhsdrv", &val))
u8p_replace_bits(&reg, val, (u8)TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK);
/* DP/DM swap control */
device_property_read_u8(&ulpi->dev, "datapolarity", &val);
reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT;
if (!device_property_read_u8(&ulpi->dev, "datapolarity", &val))
u8p_replace_bits(&reg, val, (u8)TUSB1210_VENDOR_SPECIFIC2_DP_MASK);
if (reg) {
ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg);
tusb->vendor_specific2 = reg;
}
ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg);
tusb->vendor_specific2 = reg;
tusb->phy = ulpi_phy_create(ulpi, &phy_ops);
if (IS_ERR(tusb->phy))

View file

@ -208,6 +208,7 @@ struct xpsgtr_phy {
* @gtr_mutex: mutex for locking
* @phys: PHY lanes
* @refclk_sscs: spread spectrum settings for the reference clocks
* @clk: reference clocks
* @tx_term_fix: fix for GT issue
* @saved_icm_cfg0: stored value of ICM CFG0 register
* @saved_icm_cfg1: stored value of ICM CFG1 register
@ -219,6 +220,7 @@ struct xpsgtr_dev {
struct mutex gtr_mutex; /* mutex for locking */
struct xpsgtr_phy phys[NUM_LANES];
const struct xpsgtr_ssc *refclk_sscs[NUM_LANES];
struct clk *clk[NUM_LANES];
bool tx_term_fix;
unsigned int saved_icm_cfg0;
unsigned int saved_icm_cfg1;
@ -818,11 +820,15 @@ static struct phy *xpsgtr_xlate(struct device *dev,
static int __maybe_unused xpsgtr_suspend(struct device *dev)
{
struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev);
unsigned int i;
/* Save the snapshot ICM_CFG registers. */
gtr_dev->saved_icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0);
gtr_dev->saved_icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1);
for (i = 0; i < ARRAY_SIZE(gtr_dev->clk); i++)
clk_disable_unprepare(gtr_dev->clk[i]);
return 0;
}
@ -832,6 +838,13 @@ static int __maybe_unused xpsgtr_resume(struct device *dev)
unsigned int icm_cfg0, icm_cfg1;
unsigned int i;
bool skip_phy_init;
int err;
for (i = 0; i < ARRAY_SIZE(gtr_dev->clk); i++) {
err = clk_prepare_enable(gtr_dev->clk[i]);
if (err)
goto err_clk_put;
}
icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0);
icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1);
@ -852,6 +865,12 @@ static int __maybe_unused xpsgtr_resume(struct device *dev)
gtr_dev->phys[i].skip_phy_init = skip_phy_init;
return 0;
err_clk_put:
while (i--)
clk_disable_unprepare(gtr_dev->clk[i]);
return err;
}
static const struct dev_pm_ops xpsgtr_pm_ops = {
@ -865,6 +884,7 @@ static const struct dev_pm_ops xpsgtr_pm_ops = {
static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
{
unsigned int refclk;
int ret;
for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) {
unsigned long rate;
@ -874,14 +894,22 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
snprintf(name, sizeof(name), "ref%u", refclk);
clk = devm_clk_get_optional(gtr_dev->dev, name);
if (IS_ERR(clk))
return dev_err_probe(gtr_dev->dev, PTR_ERR(clk),
"Failed to get reference clock %u\n",
refclk);
if (IS_ERR(clk)) {
ret = dev_err_probe(gtr_dev->dev, PTR_ERR(clk),
"Failed to get reference clock %u\n",
refclk);
goto err_clk_put;
}
if (!clk)
continue;
ret = clk_prepare_enable(clk);
if (ret)
goto err_clk_put;
gtr_dev->clk[refclk] = clk;
/*
* Get the spread spectrum (SSC) settings for the reference
* clock rate.
@ -899,11 +927,18 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
dev_err(gtr_dev->dev,
"Invalid rate %lu for reference clock %u\n",
rate, refclk);
return -EINVAL;
ret = -EINVAL;
goto err_clk_put;
}
}
return 0;
err_clk_put:
while (refclk--)
clk_disable_unprepare(gtr_dev->clk[refclk]);
return ret;
}
static int xpsgtr_probe(struct platform_device *pdev)
@ -912,6 +947,7 @@ static int xpsgtr_probe(struct platform_device *pdev)
struct xpsgtr_dev *gtr_dev;
struct phy_provider *provider;
unsigned int port;
unsigned int i;
int ret;
gtr_dev = devm_kzalloc(&pdev->dev, sizeof(*gtr_dev), GFP_KERNEL);
@ -951,7 +987,8 @@ static int xpsgtr_probe(struct platform_device *pdev)
phy = devm_phy_create(&pdev->dev, np, &xpsgtr_phyops);
if (IS_ERR(phy)) {
dev_err(&pdev->dev, "failed to create PHY\n");
return PTR_ERR(phy);
ret = PTR_ERR(phy);
goto err_clk_put;
}
gtr_phy->phy = phy;
@ -962,9 +999,16 @@ static int xpsgtr_probe(struct platform_device *pdev)
provider = devm_of_phy_provider_register(&pdev->dev, xpsgtr_xlate);
if (IS_ERR(provider)) {
dev_err(&pdev->dev, "registering provider failed\n");
return PTR_ERR(provider);
ret = PTR_ERR(provider);
goto err_clk_put;
}
return 0;
err_clk_put:
for (i = 0; i < ARRAY_SIZE(gtr_dev->clk); i++)
clk_disable_unprepare(gtr_dev->clk[i]);
return ret;
}
static const struct of_device_id xpsgtr_of_match[] = {

View file

@ -90,4 +90,9 @@
#define J7200_SERDES0_LANE3_USB 0x2
#define J7200_SERDES0_LANE3_IP4_UNUSED 0x3
/* AM64 */
#define AM64_SERDES0_LANE0_PCIE0 0x0
#define AM64_SERDES0_LANE0_USB 0x1
#endif /* _DT_BINDINGS_MUX_TI_SERDES */

View file

@ -1,13 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This header provides constants for Cadence Torrent SERDES.
*/
#ifndef _DT_BINDINGS_TORRENT_SERDES_H
#define _DT_BINDINGS_TORRENT_SERDES_H
#define TORRENT_SERDES_NO_SSC 0
#define TORRENT_SERDES_EXTERNAL_SSC 1
#define TORRENT_SERDES_INTERNAL_SSC 2
#endif /* _DT_BINDINGS_TORRENT_SERDES_H */

View file

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This header provides constants for Cadence SERDES.
*/
#ifndef _DT_BINDINGS_CADENCE_SERDES_H
#define _DT_BINDINGS_CADENCE_SERDES_H
/* Torrent */
#define TORRENT_SERDES_NO_SSC 0
#define TORRENT_SERDES_EXTERNAL_SSC 1
#define TORRENT_SERDES_INTERNAL_SSC 2
#define CDNS_TORRENT_REFCLK_DRIVER 0
/* Sierra */
#define CDNS_SIERRA_PLL_CMNLC 0
#define CDNS_SIERRA_PLL_CMNLC1 1
#endif /* _DT_BINDINGS_CADENCE_SERDES_H */

View file

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This header provides constants for TI SERDES.
*/
#ifndef _DT_BINDINGS_TI_SERDES
#define _DT_BINDINGS_TI_SERDES
/* Clock index for output clocks from WIZ */
/* MUX Clocks */
#define TI_WIZ_PLL0_REFCLK 0
#define TI_WIZ_PLL1_REFCLK 1
#define TI_WIZ_REFCLK_DIG 2
/* Reserve index here for future additions */
/* MISC Clocks */
#define TI_WIZ_PHY_EN_REFCLK 16
#endif /* _DT_BINDINGS_TI_SERDES */

View file

@ -44,6 +44,12 @@ enum phy_mode {
PHY_MODE_DP
};
enum phy_media {
PHY_MEDIA_DEFAULT,
PHY_MEDIA_SR,
PHY_MEDIA_DAC,
};
/**
* union phy_configure_opts - Opaque generic phy configuration
*
@ -64,6 +70,8 @@ union phy_configure_opts {
* @power_on: powering on the phy
* @power_off: powering off the phy
* @set_mode: set the mode of the phy
* @set_media: set the media type of the phy (optional)
* @set_speed: set the speed of the phy (optional)
* @reset: resetting the phy
* @calibrate: calibrate the phy
* @release: ops to be performed while the consumer relinquishes the PHY
@ -75,6 +83,8 @@ struct phy_ops {
int (*power_on)(struct phy *phy);
int (*power_off)(struct phy *phy);
int (*set_mode)(struct phy *phy, enum phy_mode mode, int submode);
int (*set_media)(struct phy *phy, enum phy_media media);
int (*set_speed)(struct phy *phy, int speed);
/**
* @configure:
@ -215,6 +225,8 @@ int phy_power_off(struct phy *phy);
int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode);
#define phy_set_mode(phy, mode) \
phy_set_mode_ext(phy, mode, 0)
int phy_set_media(struct phy *phy, enum phy_media media);
int phy_set_speed(struct phy *phy, int speed);
int phy_configure(struct phy *phy, union phy_configure_opts *opts);
int phy_validate(struct phy *phy, enum phy_mode mode, int submode,
union phy_configure_opts *opts);
@ -344,6 +356,20 @@ static inline int phy_set_mode_ext(struct phy *phy, enum phy_mode mode,
#define phy_set_mode(phy, mode) \
phy_set_mode_ext(phy, mode, 0)
static inline int phy_set_media(struct phy *phy, enum phy_media media)
{
if (!phy)
return 0;
return -ENODEV;
}
static inline int phy_set_speed(struct phy *phy, int speed)
{
if (!phy)
return 0;
return -ENODEV;
}
static inline enum phy_mode phy_get_mode(struct phy *phy)
{
return PHY_MODE_INVALID;