phy-for-5.17

- New support:
         - Qualcomm eDP PHY driver
 	- Qualcomm SM8450 UFS, USB2, USB3, PCIe0 and PCIe1 phy support
 	- Lan966x ethernet serdes PHY driver
 	- Support for uniphier NXI & Pro4 SoC
         - Qualcomm SM6350 USB2 support
 	- Amlogic Meson8 HDMI TX PHY driver
 	- Rockchip rk3568 usb2 support
 	- Intel Thunder Bay eMMC PHY driver
 	- Freescale IMX8 PCIe phy driver
 
   - Updates:
 	- Cadence Sierra driver updates for multilink configurations
         - Bcm usb2 updates for Phy reg space
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmHNmqQACgkQfBQHDyUj
 g0fhew/9H14cOScEaekVHvEtyxWJwAuNFlquEiQPLqnjL67aSDy4Xku43Z8xXvvY
 8XwSmDhlZV/UmQMte1+XKyEEPpfUScN7elVP5/Vq07oHM/jaDG1dPPPZyf0pIM4y
 bpD2dameXpHThC3Mb8QpERsZA0d4zvm6+8PN0MtkZGTPNKTwMl7sBIA/W76Ic+my
 6+HUqANa5OXo0iEZSDK8TkygpblIdgYthYsTaSTuiAkxYSse47U0xUWuex3UVQpZ
 DSSCZUtjxTXTMSxJBNi8ry8ZJIkUhiVL4fY3Oh9bYRi9e7UGiEMwCb/yF979kPYA
 ZcI8bR/w0/f4oIQdOUjcxIA6n9avKrnAEIQFp18dWTBjUZTEZdYz2zS0DMuXq9t2
 4yLXSNqO2FvOo3/AK5B/K6tf2j3zJTEdVpiq+rKOKxCAZWY2EDVtdDJRYYynCPK9
 xBiut4PrGoG8Fs1RiL768kzy5a21fbDK5CFS8QYbbno/YhznwNFKKRX4VdgY/b1b
 ltI/cD6G70M+TOYCJ0jNIwRoA2dZCUClhdulpltSrTx9tR4M6oH+pXxHBD66WVNK
 ouJRaqtvi48ILwXZ9oAEKFQfu8hQt1OMWTcGbJ0ntPPVBPHv7hRQAxAyJZcADfDP
 RjO7CX48Il3/33w4kIH9VFO4DE/asYJ6QGOVo9SA0iRh5M9fI88=
 =29yF
 -----END PGP SIGNATURE-----

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

Vinod writes:

phy-for-5.17

  - New support:
        - Qualcomm eDP PHY driver
	- Qualcomm SM8450 UFS, USB2, USB3, PCIe0 and PCIe1 phy support
	- Lan966x ethernet serdes PHY driver
	- Support for uniphier NXI & Pro4 SoC
        - Qualcomm SM6350 USB2 support
	- Amlogic Meson8 HDMI TX PHY driver
	- Rockchip rk3568 usb2 support
	- Intel Thunder Bay eMMC PHY driver
	- Freescale IMX8 PCIe phy driver

  - Updates:
	- Cadence Sierra driver updates for multilink configurations
        - Bcm usb2 updates for Phy reg space

* tag 'phy-for-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy: (72 commits)
  phy: cadence: Sierra: Add support for derived reference clock output
  dt-bindings: phy: cadence-sierra: Add clock ID for derived reference clock
  phy: cadence: Sierra: Add PCIe + QSGMII PHY multilink configuration
  phy: cadence: Sierra: Add support for PHY multilink configurations
  phy: cadence: Sierra: Fix to get correct parent for mux clocks
  phy: cadence: Sierra: Update single link PCIe register configuration
  phy: cadence: Sierra: Check PIPE mode PHY status to be ready for operation
  phy: cadence: Sierra: Check cmn_ready assertion during PHY power on
  phy: cadence: Sierra: Add PHY PCS common register configurations
  phy: cadence: Sierra: Rename some regmap variables to be in sync with Sierra documentation
  phy: cadence: Sierra: Add support to get SSC type from device tree
  dt-bindings: phy: cadence-sierra: Add binding to specify SSC mode
  dt-bindings: phy: cadence-torrent: Rename SSC macros to use generic names
  phy: cadence: Sierra: Prepare driver to add support for multilink configurations
  phy: cadence: Sierra: Use of_device_get_match_data() to get driver data
  phy: mediatek: Fix missing check in mtk_mipi_tx_probe
  phy: uniphier-usb3ss: fix unintended writing zeros to PHY register
  phy: phy-mtk-tphy: use new io helpers to access register
  phy: phy-mtk-xsphy: use new io helpers to access register
  phy: mediatek: add helpers to update bits of registers
  ...
This commit is contained in:
Greg Kroah-Hartman 2021-12-30 14:02:16 +01:00
commit e75a58db41
58 changed files with 5446 additions and 606 deletions

View File

@ -0,0 +1,65 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/phy/amlogic,meson8-hdmi-tx-phy.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Amlogic Meson8, Meson8b and Meson8m2 HDMI TX PHY
maintainers:
- Martin Blumenstingl <martin.blumenstingl@googlemail.com>
description: |+
The HDMI TX PHY node should be the child of a syscon node with the
required property:
compatible = "amlogic,meson-hhi-sysctrl", "simple-mfd", "syscon"
Refer to the bindings described in
Documentation/devicetree/bindings/mfd/syscon.yaml
properties:
$nodename:
pattern: "^hdmi-phy@[0-9a-f]+$"
compatible:
oneOf:
- items:
- enum:
- amlogic,meson8b-hdmi-tx-phy
- amlogic,meson8m2-hdmi-tx-phy
- const: amlogic,meson8-hdmi-tx-phy
- const: amlogic,meson8-hdmi-tx-phy
reg:
maxItems: 1
clocks:
minItems: 1
description:
HDMI TMDS clock
"#phy-cells":
const: 0
required:
- compatible
- "#phy-cells"
additionalProperties: false
examples:
- |
hdmi-phy@3a0 {
compatible = "amlogic,meson8-hdmi-tx-phy";
reg = <0x3a0 0xc>;
clocks = <&tmds_clock>;
#phy-cells = <0>;
};
- |
hdmi-phy@3a0 {
compatible = "amlogic,meson8b-hdmi-tx-phy", "amlogic,meson8-hdmi-tx-phy";
reg = <0x3a0 0xc>;
clocks = <&tmds_clock>;
#phy-cells = <0>;
};

View File

@ -0,0 +1,92 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/fsl,imx8-pcie-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Freescale i.MX8 SoC series PCIe PHY Device Tree Bindings
maintainers:
- Richard Zhu <hongxing.zhu@nxp.com>
properties:
"#phy-cells":
const: 0
compatible:
enum:
- fsl,imx8mm-pcie-phy
reg:
maxItems: 1
clocks:
maxItems: 1
clock-names:
items:
- const: ref
resets:
maxItems: 1
reset-names:
items:
- const: pciephy
fsl,refclk-pad-mode:
description: |
Specifies the mode of the refclk pad used. It can be UNUSED(PHY
refclock is derived from SoC internal source), INPUT(PHY refclock
is provided externally via the refclk pad) or OUTPUT(PHY refclock
is derived from SoC internal source and provided on the refclk pad).
Refer include/dt-bindings/phy/phy-imx8-pcie.h for the constants
to be used.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [ 0, 1, 2 ]
fsl,tx-deemph-gen1:
description: Gen1 De-emphasis value (optional).
$ref: /schemas/types.yaml#/definitions/uint32
default: 0
fsl,tx-deemph-gen2:
description: Gen2 De-emphasis value (optional).
$ref: /schemas/types.yaml#/definitions/uint32
default: 0
fsl,clkreq-unsupported:
type: boolean
description: A boolean property indicating the CLKREQ# signal is
not supported in the board design (optional)
required:
- "#phy-cells"
- compatible
- reg
- clocks
- clock-names
- fsl,refclk-pad-mode
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/imx8mm-clock.h>
#include <dt-bindings/phy/phy-imx8-pcie.h>
#include <dt-bindings/reset/imx8mq-reset.h>
pcie_phy: pcie-phy@32f00000 {
compatible = "fsl,imx8mm-pcie-phy";
reg = <0x32f00000 0x10000>;
clocks = <&clk IMX8MM_CLK_PCIE1_PHY>;
clock-names = "ref";
assigned-clocks = <&clk IMX8MM_CLK_PCIE1_PHY>;
assigned-clock-rates = <100000000>;
assigned-clock-parents = <&clk IMX8MM_SYS_PLL2_100M>;
resets = <&src IMX8MQ_RESET_PCIEPHY>;
reset-names = "pciephy";
fsl,refclk-pad-mode = <IMX8_PCIE_REFCLK_PAD_INPUT>;
#phy-cells = <0>;
};
...

View File

@ -0,0 +1,46 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/intel,phy-thunderbay-emmc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Intel Thunder Bay eMMC PHY bindings
maintainers:
- Srikandan Nandhini <nandhini.srikandan@intel.com>
properties:
compatible:
const: intel,thunderbay-emmc-phy
"#phy-cells":
const: 0
reg:
maxItems: 1
clocks:
maxItems: 1
clock-names:
items:
- const: emmcclk
required:
- "#phy-cells"
- compatible
- reg
- clocks
additionalProperties: false
examples:
- |
mmc_phy@80440800 {
#phy-cells = <0x0>;
compatible = "intel,thunderbay-emmc-phy";
status = "okay";
reg = <0x80440800 0x100>;
clocks = <&emmc>;
clock-names = "emmcclk";
};

View File

@ -160,6 +160,24 @@ patternProperties:
- PHY_TYPE_PCIE
- PHY_TYPE_SATA
nvmem-cells:
items:
- description: internal R efuse for U2 PHY or U3/PCIe PHY
- description: rx_imp_sel efuse for U3/PCIe PHY
- description: tx_imp_sel efuse for U3/PCIe PHY
description: |
Phandles to nvmem cell that contains the efuse data;
Available only for U2 PHY or U3/PCIe PHY of version 2/3, these
three items should be provided at the same time for U3/PCIe PHY,
when use software to load efuse;
If unspecified, will use hardware auto-load efuse.
nvmem-cell-names:
items:
- const: intr
- const: rx_imp
- const: tx_imp
# The following optional vendor properties are only for debug or HQA test
mediatek,eye-src:
description:

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/microchip,lan966x-serdes.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip Lan966x Serdes controller
maintainers:
- Horatiu Vultur <horatiu.vultur@microchip.com>
description: |
Lan966x has 7 interfaces, consisting of 2 copper transceivers(CU),
3 SERDES6G and 2 RGMII interfaces. Two of the SERDES6G support QSGMII.
Also it has 8 logical Ethernet ports which can be connected to these
interfaces. The Serdes controller will allow to configure these interfaces
and allows to "mux" the interfaces to different ports.
For simple selection of the interface that is used with a port, the
following macros are defined CU(X), SERDES6G(X), RGMII(X). Where X is a
number that represents the index of that interface type. For example
CU(1) means use interface copper transceivers 1. SERDES6G(2) means use
interface SerDes 2.
properties:
$nodename:
pattern: "^serdes@[0-9a-f]+$"
compatible:
const: microchip,lan966x-serdes
reg:
items:
- description: HSIO registers
- description: HW_STAT register
'#phy-cells':
const: 2
description: |
- Input port to use for a given macro.
- The macro to be used. The macros are defined in
dt-bindings/phy/phy-lan966x-serdes.
required:
- compatible
- reg
- '#phy-cells'
additionalProperties: false
examples:
- |
serdes: serdes@e2004010 {
compatible = "microchip,lan966x-serdes";
reg = <0xe202c000 0x9c>, <0xe2004010 0x4>;
#phy-cells = <2>;
};
...

View File

@ -113,6 +113,15 @@ patternProperties:
minimum: 1
maximum: 16
cdns,ssc-mode:
description:
Specifies the Spread Spectrum Clocking mode used. It can be NO_SSC,
EXTERNAL_SSC or INTERNAL_SSC.
Refer include/dt-bindings/phy/phy-cadence.h for the constants to be used.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2]
default: 1
required:
- reg
- resets

View File

@ -202,7 +202,7 @@ examples:
#phy-cells = <0>;
cdns,phy-type = <PHY_TYPE_PCIE>;
cdns,num-lanes = <2>;
cdns,ssc-mode = <TORRENT_SERDES_NO_SSC>;
cdns,ssc-mode = <CDNS_SERDES_NO_SSC>;
};
phy@2 {
@ -211,7 +211,7 @@ examples:
#phy-cells = <0>;
cdns,phy-type = <PHY_TYPE_SGMII>;
cdns,num-lanes = <1>;
cdns,ssc-mode = <TORRENT_SERDES_NO_SSC>;
cdns,ssc-mode = <CDNS_SERDES_NO_SSC>;
};
};
};

View File

@ -18,6 +18,7 @@ properties:
- rockchip,rk3328-usb2phy
- rockchip,rk3366-usb2phy
- rockchip,rk3399-usb2phy
- rockchip,rk3568-usb2phy
- rockchip,rv1108-usb2phy
reg:
@ -50,6 +51,10 @@ properties:
description:
Phandle to the extcon device providing the cable state for the otg phy.
interrupts:
description: Muxed interrupt for both ports
maxItems: 1
rockchip,usbgrf:
$ref: /schemas/types.yaml#/definitions/phandle
description:
@ -67,6 +72,7 @@ properties:
interrupts:
description: host linestate interrupt
maxItems: 1
interrupt-names:
const: linestate
@ -78,8 +84,6 @@ properties:
required:
- "#phy-cells"
- interrupts
- interrupt-names
otg-port:
type: object
@ -109,8 +113,6 @@ properties:
required:
- "#phy-cells"
- interrupts
- interrupt-names
required:
- compatible
@ -120,6 +122,40 @@ required:
- host-port
- otg-port
allOf:
- if:
properties:
compatible:
contains:
const: rockchip,rk3568-usb2phy
then:
properties:
host-port:
properties:
interrupts: false
otg-port:
properties:
interrupts: false
required:
- interrupts
else:
properties:
interrupts: false
host-port:
required:
- interrupts
- interrupt-names
otg-port:
required:
- interrupts
- interrupt-names
additionalProperties: false
examples:

View File

@ -1,28 +0,0 @@
NVIDIA Tegra194 P2U binding
Tegra194 has two PHY bricks namely HSIO (High Speed IO) and NVHS (NVIDIA High
Speed) each interfacing with 12 and 8 P2U instances respectively.
A P2U instance is a glue logic between Synopsys DesignWare Core PCIe IP's PIPE
interface and PHY of HSIO/NVHS bricks. Each P2U instance represents one PCIe
lane.
Required properties:
- compatible: For Tegra19x, must contain "nvidia,tegra194-p2u".
- reg: Should be the physical address space and length of respective each P2U
instance.
- reg-names: Must include the entry "ctl".
Required properties for PHY port node:
- #phy-cells: Defined by generic PHY bindings. Must be 0.
Refer to phy/phy-bindings.txt for the generic PHY binding properties.
Example:
p2u_hsio_0: phy@3e10000 {
compatible = "nvidia,tegra194-p2u";
reg = <0x03e10000 0x10000>;
reg-names = "ctl";
#phy-cells = <0>;
};

View File

@ -0,0 +1,44 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/phy/phy-tegra194-p2u.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: NVIDIA Tegra194 P2U binding
maintainers:
- Thierry Reding <treding@nvidia.com>
description: >
Tegra194 has two PHY bricks namely HSIO (High Speed IO) and NVHS (NVIDIA High
Speed) each interfacing with 12 and 8 P2U instances respectively.
A P2U instance is a glue logic between Synopsys DesignWare Core PCIe IP's PIPE
interface and PHY of HSIO/NVHS bricks. Each P2U instance represents one PCIe
lane.
properties:
compatible:
const: nvidia,tegra194-p2u
reg:
maxItems: 1
description: Should be the physical address space and length of respective each P2U instance.
reg-names:
items:
- const: ctl
'#phy-cells':
const: 0
additionalProperties: false
examples:
- |
p2u_hsio_0: phy@3e10000 {
compatible = "nvidia,tegra194-p2u";
reg = <0x03e10000 0x10000>;
reg-names = "ctl";
#phy-cells = <0>;
};

View File

@ -0,0 +1,67 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/phy/qcom,edp-phy.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Qualcomm eDP PHY
maintainers:
- Bjorn Andersson <bjorn.andersson@linaro.org>
description:
The Qualcomm eDP PHY is found in a number of Qualcomm platform and provides
the physical interface for Embedded Display Port.
properties:
compatible:
const: qcom,sc8180x-edp-phy
reg:
items:
- description: PHY base register block
- description: tx0 register block
- description: tx1 register block
- description: PLL register block
clocks:
maxItems: 2
clock-names:
items:
- const: aux
- const: cfg_ahb
"#clock-cells":
const: 1
"#phy-cells":
const: 0
required:
- compatible
- reg
- clocks
- clock-names
- "#clock-cells"
- "#phy-cells"
additionalProperties: false
examples:
- |
phy@aec2a00 {
compatible = "qcom,sc8180x-edp-phy";
reg = <0x0aec2a00 0x1c0>,
<0x0aec2200 0xa0>,
<0x0aec2600 0xa0>,
<0x0aec2000 0x19c>;
clocks = <&dispcc 0>, <&dispcc 1>;
clock-names = "aux", "cfg_ahb";
#clock-cells = <1>;
#phy-cells = <0>;
};
...

View File

@ -50,6 +50,10 @@ properties:
- qcom,sm8350-qmp-ufs-phy
- qcom,sm8350-qmp-usb3-phy
- qcom,sm8350-qmp-usb3-uni-phy
- qcom,sm8450-qmp-gen3x1-pcie-phy
- qcom,sm8450-qmp-gen4x2-pcie-phy
- qcom,sm8450-qmp-ufs-phy
- qcom,sm8450-qmp-usb3-phy
- qcom,sdx55-qmp-pcie-phy
- qcom,sdx55-qmp-usb3-uni-phy
@ -332,6 +336,8 @@ allOf:
- qcom,sm8250-qmp-gen3x1-pcie-phy
- qcom,sm8250-qmp-gen3x2-pcie-phy
- qcom,sm8250-qmp-modem-pcie-phy
- qcom,sm8450-qmp-gen3x1-pcie-phy
- qcom,sm8450-qmp-gen4x2-pcie-phy
then:
properties:
clocks:

View File

@ -30,6 +30,7 @@ properties:
- enum:
- qcom,sc7180-qusb2-phy
- qcom,sdm845-qusb2-phy
- qcom,sm6350-qusb2-phy
- const: qcom,qusb2-v2-phy
reg:
maxItems: 1

View File

@ -20,6 +20,7 @@ properties:
- qcom,sm8150-usb-hs-phy
- qcom,sm8250-usb-hs-phy
- qcom,sm8350-usb-hs-phy
- qcom,sm8450-usb-hs-phy
- qcom,usb-snps-femto-v2-phy
reg:

View File

@ -16,6 +16,7 @@ maintainers:
properties:
compatible:
enum:
- socionext,uniphier-pro4-ahci-phy
- socionext,uniphier-pxs2-ahci-phy
- socionext,uniphier-pxs3-ahci-phy
@ -26,23 +27,35 @@ properties:
const: 0
clocks:
minItems: 1
maxItems: 2
clock-names:
oneOf:
- items: # for PXs2
- const: link
- items: # for Pro4
- const: link
- const: gio
- items: # for others
- const: link
- const: phy
resets:
maxItems: 2
minItems: 2
maxItems: 5
reset-names:
items:
- const: link
- const: phy
oneOf:
- items: # for Pro4
- const: link
- const: gio
- const: pm
- const: tx
- const: rx
- items: # for others
- const: link
- const: phy
required:
- compatible

View File

@ -19,6 +19,7 @@ properties:
- socionext,uniphier-pro5-pcie-phy
- socionext,uniphier-ld20-pcie-phy
- socionext,uniphier-pxs3-pcie-phy
- socionext,uniphier-nx1-pcie-phy
reg:
maxItems: 1

View File

@ -22,6 +22,7 @@ properties:
- socionext,uniphier-pxs2-usb3-hsphy
- socionext,uniphier-ld20-usb3-hsphy
- socionext,uniphier-pxs3-usb3-hsphy
- socionext,uniphier-nx1-usb3-hsphy
reg:
maxItems: 1

View File

@ -23,6 +23,7 @@ properties:
- socionext,uniphier-pxs2-usb3-ssphy
- socionext,uniphier-ld20-usb3-ssphy
- socionext,uniphier-pxs3-usb3-ssphy
- socionext,uniphier-nx1-usb3-ssphy
reg:
maxItems: 1

View File

@ -16,6 +16,7 @@ Required properties:
"qcom,sm8150-ufshc", "qcom,ufshc", "jedec,ufs-2.0"
"qcom,sm8250-ufshc", "qcom,ufshc", "jedec,ufs-2.0"
"qcom,sm8350-ufshc", "qcom,ufshc", "jedec,ufs-2.0"
"qcom,sm8450-ufshc", "qcom,ufshc", "jedec,ufs-2.0"
- interrupts : <interrupt mapping for UFS host controller IRQ>
- reg : <registers mapping>

View File

@ -9708,6 +9708,13 @@ F: drivers/crypto/keembay/keembay-ocs-hcu-core.c
F: drivers/crypto/keembay/ocs-hcu.c
F: drivers/crypto/keembay/ocs-hcu.h
INTEL THUNDER BAY EMMC PHY DRIVER
M: Nandhini Srikandan <nandhini.srikandan@intel.com>
M: Rashmi A <rashmi.a@intel.com>
S: Maintained
F: Documentation/devicetree/bindings/phy/intel,phy-thunderbay-emmc.yaml
F: drivers/phy/intel/phy-intel-thunderbay-emmc.c
INTEL MANAGEMENT ENGINE (mei)
M: Tomas Winkler <tomas.winkler@intel.com>
L: linux-kernel@vger.kernel.org

View File

@ -2,6 +2,16 @@
#
# Phy drivers for Amlogic platforms
#
config PHY_MESON8_HDMI_TX
tristate "Meson8, Meson8b and Meson8m2 HDMI TX PHY driver"
depends on (ARCH_MESON && ARM) || COMPILE_TEST
depends on OF
select MFD_SYSCON
help
Enable this to support the HDMI TX PHYs found in Meson8,
Meson8b and Meson8m2 SoCs.
If unsure, say N.
config PHY_MESON8B_USB2
tristate "Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY driver"
default ARCH_MESON

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PHY_MESON8_HDMI_TX) += phy-meson8-hdmi-tx.o
obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o
obj-$(CONFIG_PHY_MESON_GXL_USB2) += phy-meson-gxl-usb2.o
obj-$(CONFIG_PHY_MESON_G12A_USB2) += phy-meson-g12a-usb2.o

View File

@ -0,0 +1,160 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Meson8, Meson8b and Meson8m2 HDMI TX PHY.
*
* Copyright (C) 2021 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.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/property.h>
#include <linux/regmap.h>
/*
* Unfortunately there is no detailed documentation available for the
* HHI_HDMI_PHY_CNTL0 register. CTL0 and CTL1 is all we know about.
* Magic register values in the driver below are taken from the vendor
* BSP / kernel.
*/
#define HHI_HDMI_PHY_CNTL0 0x3a0
#define HHI_HDMI_PHY_CNTL0_HDMI_CTL1 GENMASK(31, 16)
#define HHI_HDMI_PHY_CNTL0_HDMI_CTL0 GENMASK(15, 0)
#define HHI_HDMI_PHY_CNTL1 0x3a4
#define HHI_HDMI_PHY_CNTL1_CLOCK_ENABLE BIT(1)
#define HHI_HDMI_PHY_CNTL1_SOFT_RESET BIT(0)
#define HHI_HDMI_PHY_CNTL2 0x3a8
struct phy_meson8_hdmi_tx_priv {
struct regmap *hhi;
struct clk *tmds_clk;
};
static int phy_meson8_hdmi_tx_init(struct phy *phy)
{
struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy);
return clk_prepare_enable(priv->tmds_clk);
}
static int phy_meson8_hdmi_tx_exit(struct phy *phy)
{
struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy);
clk_disable_unprepare(priv->tmds_clk);
return 0;
}
static int phy_meson8_hdmi_tx_power_on(struct phy *phy)
{
struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy);
unsigned int i;
u16 hdmi_ctl0;
if (clk_get_rate(priv->tmds_clk) >= 2970UL * 1000 * 1000)
hdmi_ctl0 = 0x1e8b;
else
hdmi_ctl0 = 0x4d0b;
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0,
FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL1, 0x08c3) |
FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL0, hdmi_ctl0));
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, 0x0);
/* Reset three times, just like the vendor driver does */
for (i = 0; i < 3; i++) {
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1,
HHI_HDMI_PHY_CNTL1_CLOCK_ENABLE |
HHI_HDMI_PHY_CNTL1_SOFT_RESET);
usleep_range(1000, 2000);
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1,
HHI_HDMI_PHY_CNTL1_CLOCK_ENABLE);
usleep_range(1000, 2000);
}
return 0;
}
static int phy_meson8_hdmi_tx_power_off(struct phy *phy)
{
struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy);
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0,
FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL1, 0x0841) |
FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL0, 0x8d00));
return 0;
}
static const struct phy_ops phy_meson8_hdmi_tx_ops = {
.init = phy_meson8_hdmi_tx_init,
.exit = phy_meson8_hdmi_tx_exit,
.power_on = phy_meson8_hdmi_tx_power_on,
.power_off = phy_meson8_hdmi_tx_power_off,
.owner = THIS_MODULE,
};
static int phy_meson8_hdmi_tx_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct phy_meson8_hdmi_tx_priv *priv;
struct phy_provider *phy_provider;
struct resource *res;
struct phy *phy;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->hhi = syscon_node_to_regmap(np->parent);
if (IS_ERR(priv->hhi))
return PTR_ERR(priv->hhi);
priv->tmds_clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(priv->tmds_clk))
return PTR_ERR(priv->tmds_clk);
phy = devm_phy_create(&pdev->dev, np, &phy_meson8_hdmi_tx_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(&pdev->dev,
of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id phy_meson8_hdmi_tx_of_match[] = {
{ .compatible = "amlogic,meson8-hdmi-tx-phy" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, phy_meson8_hdmi_tx_of_match);
static struct platform_driver phy_meson8_hdmi_tx_driver = {
.probe = phy_meson8_hdmi_tx_probe,
.driver = {
.name = "phy-meson8-hdmi-tx",
.of_match_table = phy_meson8_hdmi_tx_of_match,
},
};
module_platform_driver(phy_meson8_hdmi_tx_driver);
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
MODULE_DESCRIPTION("Meson8, Meson8b and Meson8m2 HDMI TX PHY driver");
MODULE_LICENSE("GPL v2");

View File

@ -9,17 +9,23 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
struct bcm_ns_usb2 {
struct device *dev;
struct clk *ref_clk;
struct phy *phy;
struct regmap *clkset;
void __iomem *base;
/* Deprecated binding */
void __iomem *dmu;
};
@ -27,7 +33,6 @@ static int bcm_ns_usb2_phy_init(struct phy *phy)
{
struct bcm_ns_usb2 *usb2 = phy_get_drvdata(phy);
struct device *dev = usb2->dev;
void __iomem *dmu = usb2->dmu;
u32 ref_clk_rate, usb2ctl, usb_pll_ndiv, usb_pll_pdiv;
int err = 0;
@ -44,7 +49,10 @@ static int bcm_ns_usb2_phy_init(struct phy *phy)
goto err_clk_off;
}
usb2ctl = readl(dmu + BCMA_DMU_CRU_USB2_CONTROL);
if (usb2->base)
usb2ctl = readl(usb2->base);
else
usb2ctl = readl(usb2->dmu + BCMA_DMU_CRU_USB2_CONTROL);
if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) {
usb_pll_pdiv = usb2ctl;
@ -58,15 +66,24 @@ static int bcm_ns_usb2_phy_init(struct phy *phy)
usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate;
/* Unlock DMU PLL settings with some magic value */
writel(0x0000ea68, dmu + BCMA_DMU_CRU_CLKSET_KEY);
if (usb2->clkset)
regmap_write(usb2->clkset, 0, 0x0000ea68);
else
writel(0x0000ea68, usb2->dmu + BCMA_DMU_CRU_CLKSET_KEY);
/* Write USB 2.0 PLL control setting */
usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK;
usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT;
writel(usb2ctl, dmu + BCMA_DMU_CRU_USB2_CONTROL);
if (usb2->base)
writel(usb2ctl, usb2->base);
else
writel(usb2ctl, usb2->dmu + BCMA_DMU_CRU_USB2_CONTROL);
/* Lock DMU PLL settings */
writel(0x00000000, dmu + BCMA_DMU_CRU_CLKSET_KEY);
if (usb2->clkset)
regmap_write(usb2->clkset, 0, 0x00000000);
else
writel(0x00000000, usb2->dmu + BCMA_DMU_CRU_CLKSET_KEY);
err_clk_off:
clk_disable_unprepare(usb2->ref_clk);
@ -90,15 +107,32 @@ static int bcm_ns_usb2_probe(struct platform_device *pdev)
return -ENOMEM;
usb2->dev = dev;
usb2->dmu = devm_platform_ioremap_resource_byname(pdev, "dmu");
if (IS_ERR(usb2->dmu)) {
dev_err(dev, "Failed to map DMU regs\n");
return PTR_ERR(usb2->dmu);
if (of_find_property(dev->of_node, "brcm,syscon-clkset", NULL)) {
usb2->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(usb2->base)) {
dev_err(dev, "Failed to map control reg\n");
return PTR_ERR(usb2->base);
}
usb2->clkset = syscon_regmap_lookup_by_phandle(dev->of_node,
"brcm,syscon-clkset");
if (IS_ERR(usb2->clkset)) {
dev_err(dev, "Failed to lookup clkset regmap\n");
return PTR_ERR(usb2->clkset);
}
} else {
usb2->dmu = devm_platform_ioremap_resource_byname(pdev, "dmu");
if (IS_ERR(usb2->dmu)) {
dev_err(dev, "Failed to map DMU regs\n");
return PTR_ERR(usb2->dmu);
}
dev_warn(dev, "using deprecated DT binding\n");
}
usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk");
if (IS_ERR(usb2->ref_clk)) {
dev_err(dev, "Clock not defined\n");
dev_err_probe(dev, PTR_ERR(usb2->ref_clk), "failed to get ref clk\n");
return PTR_ERR(usb2->ref_clk);
}

File diff suppressed because it is too large Load Diff

View File

@ -2278,7 +2278,7 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy)
struct cdns_torrent_vals *cmn_vals, *tx_ln_vals, *rx_ln_vals;
enum cdns_torrent_ref_clk ref_clk = cdns_phy->ref_clk_rate;
struct cdns_torrent_vals *link_cmn_vals, *xcvr_diag_vals;
enum cdns_torrent_phy_type phy_t1, phy_t2, tmp_phy_type;
enum cdns_torrent_phy_type phy_t1, phy_t2;
struct cdns_torrent_vals *pcs_cmn_vals;
int i, j, node, mlane, num_lanes, ret;
struct cdns_reg_pairs *reg_pairs;
@ -2304,9 +2304,7 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy)
* configure the PHY for second link with phy_t2.
* Get the array values as [phy_t2][phy_t1][ssc].
*/
tmp_phy_type = phy_t1;
phy_t1 = phy_t2;
phy_t2 = tmp_phy_type;
swap(phy_t1, phy_t2);
}
mlane = cdns_phy->phys[node].mlane;

View File

@ -14,3 +14,11 @@ config PHY_MIXEL_MIPI_DPHY
help
Enable this to add support for the Mixel DSI PHY as found
on NXP's i.MX8 family of SOCs.
config PHY_FSL_IMX8M_PCIE
tristate "Freescale i.MX8M PCIE PHY"
depends on OF && HAS_IOMEM
select GENERIC_PHY
help
Enable this to add support for the PCIE PHY as found on
i.MX8M family of SOCs.

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o
obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o
obj-$(CONFIG_PHY_FSL_IMX8M_PCIE) += phy-fsl-imx8m-pcie.o

View File

@ -0,0 +1,237 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2021 NXP
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx7-iomuxc-gpr.h>
#include <linux/module.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <dt-bindings/phy/phy-imx8-pcie.h>
#define IMX8MM_PCIE_PHY_CMN_REG061 0x184
#define ANA_PLL_CLK_OUT_TO_EXT_IO_EN BIT(0)
#define IMX8MM_PCIE_PHY_CMN_REG062 0x188
#define ANA_PLL_CLK_OUT_TO_EXT_IO_SEL BIT(3)
#define IMX8MM_PCIE_PHY_CMN_REG063 0x18C
#define AUX_PLL_REFCLK_SEL_SYS_PLL GENMASK(7, 6)
#define IMX8MM_PCIE_PHY_CMN_REG064 0x190
#define ANA_AUX_RX_TX_SEL_TX BIT(7)
#define ANA_AUX_RX_TERM_GND_EN BIT(3)
#define ANA_AUX_TX_TERM BIT(2)
#define IMX8MM_PCIE_PHY_CMN_REG065 0x194
#define ANA_AUX_RX_TERM (BIT(7) | BIT(4))
#define ANA_AUX_TX_LVL GENMASK(3, 0)
#define IMX8MM_PCIE_PHY_CMN_REG75 0x1D4
#define PCIE_PHY_CMN_REG75_PLL_DONE 0x3
#define PCIE_PHY_TRSV_REG5 0x414
#define PCIE_PHY_TRSV_REG5_GEN1_DEEMP 0x2D
#define PCIE_PHY_TRSV_REG6 0x418
#define PCIE_PHY_TRSV_REG6_GEN2_DEEMP 0xF
#define IMX8MM_GPR_PCIE_REF_CLK_SEL GENMASK(25, 24)
#define IMX8MM_GPR_PCIE_REF_CLK_PLL FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x3)
#define IMX8MM_GPR_PCIE_REF_CLK_EXT FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x2)
#define IMX8MM_GPR_PCIE_AUX_EN BIT(19)
#define IMX8MM_GPR_PCIE_CMN_RST BIT(18)
#define IMX8MM_GPR_PCIE_POWER_OFF BIT(17)
#define IMX8MM_GPR_PCIE_SSC_EN BIT(16)
#define IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE BIT(9)
struct imx8_pcie_phy {
void __iomem *base;
struct clk *clk;
struct phy *phy;
struct regmap *iomuxc_gpr;
struct reset_control *reset;
u32 refclk_pad_mode;
u32 tx_deemph_gen1;
u32 tx_deemph_gen2;
bool clkreq_unused;
};
static int imx8_pcie_phy_init(struct phy *phy)
{
int ret;
u32 val, pad_mode;
struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
reset_control_assert(imx8_phy->reset);
pad_mode = imx8_phy->refclk_pad_mode;
/* Set AUX_EN_OVERRIDE 1'b0, when the CLKREQ# isn't hooked */
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE,
imx8_phy->clkreq_unused ?
0 : IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE);
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
IMX8MM_GPR_PCIE_AUX_EN,
IMX8MM_GPR_PCIE_AUX_EN);
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
IMX8MM_GPR_PCIE_POWER_OFF, 0);
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
IMX8MM_GPR_PCIE_SSC_EN, 0);
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
IMX8MM_GPR_PCIE_REF_CLK_SEL,
pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ?
IMX8MM_GPR_PCIE_REF_CLK_EXT :
IMX8MM_GPR_PCIE_REF_CLK_PLL);
usleep_range(100, 200);
/* Do the PHY common block reset */
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
IMX8MM_GPR_PCIE_CMN_RST,
IMX8MM_GPR_PCIE_CMN_RST);
usleep_range(200, 500);
if (pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT) {
/* Configure the pad as input */
val = readl(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061);
writel(val & ~ANA_PLL_CLK_OUT_TO_EXT_IO_EN,
imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061);
} else if (pad_mode == IMX8_PCIE_REFCLK_PAD_OUTPUT) {
/* Configure the PHY to output the refclock via pad */
writel(ANA_PLL_CLK_OUT_TO_EXT_IO_EN,
imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061);
writel(ANA_PLL_CLK_OUT_TO_EXT_IO_SEL,
imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG062);
writel(AUX_PLL_REFCLK_SEL_SYS_PLL,
imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG063);
val = ANA_AUX_RX_TX_SEL_TX | ANA_AUX_TX_TERM;
writel(val | ANA_AUX_RX_TERM_GND_EN,
imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG064);
writel(ANA_AUX_RX_TERM | ANA_AUX_TX_LVL,
imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG065);
}
/* Tune PHY de-emphasis setting to pass PCIe compliance. */
if (imx8_phy->tx_deemph_gen1)
writel(imx8_phy->tx_deemph_gen1,
imx8_phy->base + PCIE_PHY_TRSV_REG5);
if (imx8_phy->tx_deemph_gen2)
writel(imx8_phy->tx_deemph_gen2,
imx8_phy->base + PCIE_PHY_TRSV_REG6);
reset_control_deassert(imx8_phy->reset);
/* Polling to check the phy is ready or not. */
ret = readl_poll_timeout(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG75,
val, val == PCIE_PHY_CMN_REG75_PLL_DONE,
10, 20000);
return ret;
}
static int imx8_pcie_phy_power_on(struct phy *phy)
{
struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
return clk_prepare_enable(imx8_phy->clk);
}
static int imx8_pcie_phy_power_off(struct phy *phy)
{
struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
clk_disable_unprepare(imx8_phy->clk);
return 0;
}
static const struct phy_ops imx8_pcie_phy_ops = {
.init = imx8_pcie_phy_init,
.power_on = imx8_pcie_phy_power_on,
.power_off = imx8_pcie_phy_power_off,
.owner = THIS_MODULE,
};
static int imx8_pcie_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct imx8_pcie_phy *imx8_phy;
struct resource *res;
imx8_phy = devm_kzalloc(dev, sizeof(*imx8_phy), GFP_KERNEL);
if (!imx8_phy)
return -ENOMEM;
/* get PHY refclk pad mode */
of_property_read_u32(np, "fsl,refclk-pad-mode",
&imx8_phy->refclk_pad_mode);
if (of_property_read_u32(np, "fsl,tx-deemph-gen1",
&imx8_phy->tx_deemph_gen1))
imx8_phy->tx_deemph_gen1 = 0;
if (of_property_read_u32(np, "fsl,tx-deemph-gen2",
&imx8_phy->tx_deemph_gen2))
imx8_phy->tx_deemph_gen2 = 0;
if (of_property_read_bool(np, "fsl,clkreq-unsupported"))
imx8_phy->clkreq_unused = true;
else
imx8_phy->clkreq_unused = false;
imx8_phy->clk = devm_clk_get(dev, "ref");
if (IS_ERR(imx8_phy->clk)) {
dev_err(dev, "failed to get imx pcie phy clock\n");
return PTR_ERR(imx8_phy->clk);
}
/* Grab GPR config register range */
imx8_phy->iomuxc_gpr =
syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
if (IS_ERR(imx8_phy->iomuxc_gpr)) {
dev_err(dev, "unable to find iomuxc registers\n");
return PTR_ERR(imx8_phy->iomuxc_gpr);
}
imx8_phy->reset = devm_reset_control_get_exclusive(dev, "pciephy");
if (IS_ERR(imx8_phy->reset)) {
dev_err(dev, "Failed to get PCIEPHY reset control\n");
return PTR_ERR(imx8_phy->reset);
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
imx8_phy->base = devm_ioremap_resource(dev, res);
if (IS_ERR(imx8_phy->base))
return PTR_ERR(imx8_phy->base);
imx8_phy->phy = devm_phy_create(dev, NULL, &imx8_pcie_phy_ops);
if (IS_ERR(imx8_phy->phy))
return PTR_ERR(imx8_phy->phy);
phy_set_drvdata(imx8_phy->phy, imx8_phy);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id imx8_pcie_phy_of_match[] = {
{.compatible = "fsl,imx8mm-pcie-phy",},
{ },
};
MODULE_DEVICE_TABLE(of, imx8_pcie_phy_of_match);
static struct platform_driver imx8_pcie_phy_driver = {
.probe = imx8_pcie_phy_probe,
.driver = {
.name = "imx8-pcie-phy",
.of_match_table = imx8_pcie_phy_of_match,
}
};
module_platform_driver(imx8_pcie_phy_driver);
MODULE_DESCRIPTION("FSL IMX8 PCIE PHY driver");
MODULE_LICENSE("GPL v2");

View File

@ -46,3 +46,13 @@ config PHY_INTEL_LGM_EMMC
select GENERIC_PHY
help
Enable this to support the Intel EMMC PHY
config PHY_INTEL_THUNDERBAY_EMMC
tristate "Intel Thunder Bay eMMC PHY driver"
depends on OF && (ARCH_THUNDERBAY || COMPILE_TEST)
select GENERIC_PHY
help
This option enables support for Intel Thunder Bay SoC eMMC PHY.
To compile this driver as a module, choose M here: the module
will be called phy-intel-thunderbay-emmc.ko.

View File

@ -3,3 +3,4 @@ obj-$(CONFIG_PHY_INTEL_KEEMBAY_EMMC) += phy-intel-keembay-emmc.o
obj-$(CONFIG_PHY_INTEL_KEEMBAY_USB) += phy-intel-keembay-usb.o
obj-$(CONFIG_PHY_INTEL_LGM_COMBO) += phy-intel-lgm-combo.o
obj-$(CONFIG_PHY_INTEL_LGM_EMMC) += phy-intel-lgm-emmc.o
obj-$(CONFIG_PHY_INTEL_THUNDERBAY_EMMC) += phy-intel-thunderbay-emmc.o

View File

@ -0,0 +1,509 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel ThunderBay eMMC PHY driver
*
* Copyright (C) 2021 Intel Corporation
*
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
/* eMMC/SD/SDIO core/phy configuration registers */
#define CTRL_CFG_0 0x00
#define CTRL_CFG_1 0x04
#define CTRL_PRESET_0 0x08
#define CTRL_PRESET_1 0x0c
#define CTRL_PRESET_2 0x10
#define CTRL_PRESET_3 0x14
#define CTRL_PRESET_4 0x18
#define CTRL_CFG_2 0x1c
#define CTRL_CFG_3 0x20
#define PHY_CFG_0 0x24
#define PHY_CFG_1 0x28
#define PHY_CFG_2 0x2c
#define PHYBIST_CTRL 0x30
#define SDHC_STAT3 0x34
#define PHY_STAT 0x38
#define PHYBIST_STAT_0 0x3c
#define PHYBIST_STAT_1 0x40
#define EMMC_AXI 0x44
/* CTRL_PRESET_3 */
#define CTRL_PRESET3_MASK GENMASK(31, 0)
#define CTRL_PRESET3_SHIFT 0
/* CTRL_CFG_0 bit fields */
#define SUPPORT_HS_MASK BIT(26)
#define SUPPORT_HS_SHIFT 26
#define SUPPORT_8B_MASK BIT(24)
#define SUPPORT_8B_SHIFT 24
/* CTRL_CFG_1 bit fields */
#define SUPPORT_SDR50_MASK BIT(28)
#define SUPPORT_SDR50_SHIFT 28
#define SLOT_TYPE_MASK GENMASK(27, 26)
#define SLOT_TYPE_OFFSET 26
#define SUPPORT_64B_MASK BIT(24)
#define SUPPORT_64B_SHIFT 24
#define SUPPORT_HS400_MASK BIT(2)
#define SUPPORT_HS400_SHIFT 2
#define SUPPORT_DDR50_MASK BIT(1)
#define SUPPORT_DDR50_SHIFT 1
#define SUPPORT_SDR104_MASK BIT(0)
#define SUPPORT_SDR104_SHIFT 0
/* PHY_CFG_0 bit fields */
#define SEL_DLY_TXCLK_MASK BIT(29)
#define SEL_DLY_TXCLK_SHIFT 29
#define SEL_DLY_RXCLK_MASK BIT(28)
#define SEL_DLY_RXCLK_SHIFT 28
#define OTAP_DLY_ENA_MASK BIT(27)
#define OTAP_DLY_ENA_SHIFT 27
#define OTAP_DLY_SEL_MASK GENMASK(26, 23)
#define OTAP_DLY_SEL_SHIFT 23
#define ITAP_CHG_WIN_MASK BIT(22)
#define ITAP_CHG_WIN_SHIFT 22
#define ITAP_DLY_ENA_MASK BIT(21)
#define ITAP_DLY_ENA_SHIFT 21
#define ITAP_DLY_SEL_MASK GENMASK(20, 16)
#define ITAP_DLY_SEL_SHIFT 16
#define RET_ENB_MASK BIT(15)
#define RET_ENB_SHIFT 15
#define RET_EN_MASK BIT(14)
#define RET_EN_SHIFT 14
#define DLL_IFF_MASK GENMASK(13, 11)
#define DLL_IFF_SHIFT 11
#define DLL_EN_MASK BIT(10)
#define DLL_EN_SHIFT 10
#define DLL_TRIM_ICP_MASK GENMASK(9, 6)
#define DLL_TRIM_ICP_SHIFT 6
#define RETRIM_EN_MASK BIT(5)
#define RETRIM_EN_SHIFT 5
#define RETRIM_MASK BIT(4)
#define RETRIM_SHIFT 4
#define DR_TY_MASK GENMASK(3, 1)
#define DR_TY_SHIFT 1
#define PWR_DOWN_MASK BIT(0)
#define PWR_DOWN_SHIFT 0
/* PHY_CFG_1 bit fields */
#define REN_DAT_MASK GENMASK(19, 12)
#define REN_DAT_SHIFT 12
#define REN_CMD_MASK BIT(11)
#define REN_CMD_SHIFT 11
#define REN_STRB_MASK BIT(10)
#define REN_STRB_SHIFT 10
#define PU_STRB_MASK BIT(20)
#define PU_STRB_SHIFT 20
/* PHY_CFG_2 bit fields */
#define CLKBUF_MASK GENMASK(24, 21)
#define CLKBUF_SHIFT 21
#define SEL_STRB_MASK GENMASK(20, 13)
#define SEL_STRB_SHIFT 13
#define SEL_FREQ_MASK GENMASK(12, 10)
#define SEL_FREQ_SHIFT 10
/* PHY_STAT bit fields */
#define CAL_DONE BIT(6)
#define DLL_RDY BIT(5)
#define OTAP_DLY 0x0
#define ITAP_DLY 0x0
#define STRB 0x33
/* From ACS_eMMC51_16nFFC_RO1100_Userguide_v1p0.pdf p17 */
#define FREQSEL_200M_170M 0x0
#define FREQSEL_170M_140M 0x1
#define FREQSEL_140M_110M 0x2
#define FREQSEL_110M_80M 0x3
#define FREQSEL_80M_50M 0x4
#define FREQSEL_275M_250M 0x5
#define FREQSEL_250M_225M 0x6
#define FREQSEL_225M_200M 0x7
/* Phy power status */
#define PHY_UNINITIALIZED 0
#define PHY_INITIALIZED 1
/*
* During init(400KHz) phy_settings will be called with 200MHZ clock
* To avoid incorrectly setting the phy for init(400KHZ) "phy_power_sts" is used.
* When actual clock is set always phy is powered off once and then powered on.
* (sdhci_arasan_set_clock). That feature will be used to identify whether the
* settings are for init phy_power_on or actual clock phy_power_on
* 0 --> init settings
* 1 --> actual settings
*/
struct thunderbay_emmc_phy {
void __iomem *reg_base;
struct clk *emmcclk;
int phy_power_sts;
};
static inline void update_reg(struct thunderbay_emmc_phy *tbh_phy, u32 offset,
u32 mask, u32 shift, u32 val)
{
u32 tmp;
tmp = readl(tbh_phy->reg_base + offset);
tmp &= ~mask;
tmp |= val << shift;
writel(tmp, tbh_phy->reg_base + offset);
}
static int thunderbay_emmc_phy_power(struct phy *phy, bool power_on)
{
struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy);
unsigned int freqsel = FREQSEL_200M_170M;
unsigned long rate;
static int lock;
u32 val;
int ret;
/* Disable DLL */
rate = clk_get_rate(tbh_phy->emmcclk);
switch (rate) {
case 200000000:
/* lock dll only when it is used, i.e only if SEL_DLY_TXCLK/RXCLK are 0 */
update_reg(tbh_phy, PHY_CFG_0, DLL_EN_MASK, DLL_EN_SHIFT, 0x0);
break;
/* dll lock not required for other frequencies */
case 50000000 ... 52000000:
case 400000:
default:
break;
}
if (!power_on)
return 0;
rate = clk_get_rate(tbh_phy->emmcclk);
switch (rate) {
case 170000001 ... 200000000:
freqsel = FREQSEL_200M_170M;
break;
case 140000001 ... 170000000:
freqsel = FREQSEL_170M_140M;
break;
case 110000001 ... 140000000:
freqsel = FREQSEL_140M_110M;
break;
case 80000001 ... 110000000:
freqsel = FREQSEL_110M_80M;
break;
case 50000000 ... 80000000:
freqsel = FREQSEL_80M_50M;
break;
case 250000001 ... 275000000:
freqsel = FREQSEL_275M_250M;
break;
case 225000001 ... 250000000:
freqsel = FREQSEL_250M_225M;
break;
case 200000001 ... 225000000:
freqsel = FREQSEL_225M_200M;
break;
default:
break;
}
/* Clock rate is checked against upper limit. It may fall low during init */
if (rate > 200000000)
dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate);
udelay(5);
if (lock == 0) {
/* PDB will be done only once per boot */
update_reg(tbh_phy, PHY_CFG_0, PWR_DOWN_MASK,
PWR_DOWN_SHIFT, 0x1);
lock = 1;
/*
* According to the user manual, it asks driver to wait 5us for
* calpad busy trimming. However it is documented that this value is
* PVT(A.K.A. process, voltage and temperature) relevant, so some
* failure cases are found which indicates we should be more tolerant
* to calpad busy trimming.
*/
ret = readl_poll_timeout(tbh_phy->reg_base + PHY_STAT,
val, (val & CAL_DONE), 10, 50);
if (ret) {
dev_err(&phy->dev, "caldone failed, ret=%d\n", ret);
return ret;
}
}
rate = clk_get_rate(tbh_phy->emmcclk);
switch (rate) {
case 200000000:
/* Set frequency of the DLL operation */
update_reg(tbh_phy, PHY_CFG_2, SEL_FREQ_MASK, SEL_FREQ_SHIFT, freqsel);
/* Enable DLL */
update_reg(tbh_phy, PHY_CFG_0, DLL_EN_MASK, DLL_EN_SHIFT, 0x1);
/*
* After enabling analog DLL circuits docs say that we need 10.2 us if
* our source clock is at 50 MHz and that lock time scales linearly
* with clock speed. If we are powering on the PHY and the card clock
* is super slow (like 100kHz) this could take as long as 5.1 ms as
* per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms
* hopefully we won't be running at 100 kHz, but we should still make
* sure we wait long enough.
*
* NOTE: There appear to be corner cases where the DLL seems to take
* extra long to lock for reasons that aren't understood. In some
* extreme cases we've seen it take up to over 10ms (!). We'll be
* generous and give it 50ms.
*/
ret = readl_poll_timeout(tbh_phy->reg_base + PHY_STAT,
val, (val & DLL_RDY), 10, 50 * USEC_PER_MSEC);
if (ret) {
dev_err(&phy->dev, "dllrdy failed, ret=%d\n", ret);
return ret;
}
break;
default:
break;
}
return 0;
}
static int thunderbay_emmc_phy_init(struct phy *phy)
{
struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy);
tbh_phy->emmcclk = clk_get(&phy->dev, "emmcclk");
return PTR_ERR_OR_ZERO(tbh_phy->emmcclk);
}
static int thunderbay_emmc_phy_exit(struct phy *phy)
{
struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy);
clk_put(tbh_phy->emmcclk);
return 0;
}
static int thunderbay_emmc_phy_power_on(struct phy *phy)
{
struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy);
unsigned long rate;
/* Overwrite capability bits configurable in bootloader */
update_reg(tbh_phy, CTRL_CFG_0,
SUPPORT_HS_MASK, SUPPORT_HS_SHIFT, 0x1);
update_reg(tbh_phy, CTRL_CFG_0,
SUPPORT_8B_MASK, SUPPORT_8B_SHIFT, 0x1);
update_reg(tbh_phy, CTRL_CFG_1,
SUPPORT_SDR50_MASK, SUPPORT_SDR50_SHIFT, 0x1);
update_reg(tbh_phy, CTRL_CFG_1,
SUPPORT_DDR50_MASK, SUPPORT_DDR50_SHIFT, 0x1);
update_reg(tbh_phy, CTRL_CFG_1,
SUPPORT_SDR104_MASK, SUPPORT_SDR104_SHIFT, 0x1);
update_reg(tbh_phy, CTRL_CFG_1,
SUPPORT_HS400_MASK, SUPPORT_HS400_SHIFT, 0x1);
update_reg(tbh_phy, CTRL_CFG_1,
SUPPORT_64B_MASK, SUPPORT_64B_SHIFT, 0x1);
if (tbh_phy->phy_power_sts == PHY_UNINITIALIZED) {
/* Indicates initialization, settings for init, same as 400KHZ setting */
update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK, SEL_DLY_TXCLK_SHIFT, 0x1);
update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK, SEL_DLY_RXCLK_SHIFT, 0x1);
update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK, ITAP_DLY_ENA_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK, ITAP_DLY_SEL_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK, OTAP_DLY_ENA_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK, OTAP_DLY_SEL_SHIFT, 0);
update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK, DLL_TRIM_ICP_SHIFT, 0);
update_reg(tbh_phy, PHY_CFG_0, DR_TY_MASK, DR_TY_SHIFT, 0x1);
} else if (tbh_phy->phy_power_sts == PHY_INITIALIZED) {
/* Indicates actual clock setting */
rate = clk_get_rate(tbh_phy->emmcclk);
switch (rate) {
case 200000000:
update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK,
SEL_DLY_TXCLK_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK,
SEL_DLY_RXCLK_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK,
ITAP_DLY_ENA_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK,
ITAP_DLY_SEL_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK,
OTAP_DLY_ENA_SHIFT, 0x1);
update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK,
OTAP_DLY_SEL_SHIFT, 2);
update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK,
DLL_TRIM_ICP_SHIFT, 0x8);
update_reg(tbh_phy, PHY_CFG_0, DR_TY_MASK,
DR_TY_SHIFT, 0x1);
/* For HS400 only */
update_reg(tbh_phy, PHY_CFG_2, SEL_STRB_MASK,
SEL_STRB_SHIFT, STRB);
break;
case 50000000 ... 52000000:
/* For both HS and DDR52 this setting works */
update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK,
SEL_DLY_TXCLK_SHIFT, 0x1);
update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK,
SEL_DLY_RXCLK_SHIFT, 0x1);
update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK,
ITAP_DLY_ENA_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK,
ITAP_DLY_SEL_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK,
OTAP_DLY_ENA_SHIFT, 0x1);
update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK,
OTAP_DLY_SEL_SHIFT, 4);
update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK,
DLL_TRIM_ICP_SHIFT, 0x8);
update_reg(tbh_phy, PHY_CFG_0,
DR_TY_MASK, DR_TY_SHIFT, 0x1);
break;
case 400000:
update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK,
SEL_DLY_TXCLK_SHIFT, 0x1);
update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK,
SEL_DLY_RXCLK_SHIFT, 0x1);
update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK,
ITAP_DLY_ENA_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK,
ITAP_DLY_SEL_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK,
OTAP_DLY_ENA_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK,
OTAP_DLY_SEL_SHIFT, 0);
update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK,
DLL_TRIM_ICP_SHIFT, 0);
update_reg(tbh_phy, PHY_CFG_0, DR_TY_MASK, DR_TY_SHIFT, 0x1);
break;
default:
update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK,
SEL_DLY_TXCLK_SHIFT, 0x1);
update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK,
SEL_DLY_RXCLK_SHIFT, 0x1);
update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK,
ITAP_DLY_ENA_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK,
ITAP_DLY_SEL_SHIFT, 0x0);
update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK,
OTAP_DLY_ENA_SHIFT, 0x1);
update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK,
OTAP_DLY_SEL_SHIFT, 2);
update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK,
DLL_TRIM_ICP_SHIFT, 0x8);
update_reg(tbh_phy, PHY_CFG_0, DR_TY_MASK,
DR_TY_SHIFT, 0x1);
break;
}
/* Reset, init seq called without phy_power_off, this indicates init seq */
tbh_phy->phy_power_sts = PHY_UNINITIALIZED;
}
update_reg(tbh_phy, PHY_CFG_0, RETRIM_EN_MASK, RETRIM_EN_SHIFT, 0x1);
update_reg(tbh_phy, PHY_CFG_0, RETRIM_MASK, RETRIM_SHIFT, 0x0);
return thunderbay_emmc_phy_power(phy, 1);
}
static int thunderbay_emmc_phy_power_off(struct phy *phy)
{
struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy);
tbh_phy->phy_power_sts = PHY_INITIALIZED;
return thunderbay_emmc_phy_power(phy, 0);
}
static const struct phy_ops thunderbay_emmc_phy_ops = {
.init = thunderbay_emmc_phy_init,
.exit = thunderbay_emmc_phy_exit,
.power_on = thunderbay_emmc_phy_power_on,
.power_off = thunderbay_emmc_phy_power_off,
.owner = THIS_MODULE,
};
static const struct of_device_id thunderbay_emmc_phy_of_match[] = {
{ .compatible = "intel,thunderbay-emmc-phy",
(void *)&thunderbay_emmc_phy_ops },
{}
};
MODULE_DEVICE_TABLE(of, thunderbay_emmc_phy_of_match);
static int thunderbay_emmc_phy_probe(struct platform_device *pdev)
{
struct thunderbay_emmc_phy *tbh_phy;
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
const struct of_device_id *id;
struct phy *generic_phy;
struct resource *res;
if (!dev->of_node)
return -ENODEV;
tbh_phy = devm_kzalloc(dev, sizeof(*tbh_phy), GFP_KERNEL);
if (!tbh_phy)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tbh_phy->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tbh_phy->reg_base))
return PTR_ERR(tbh_phy->reg_base);
tbh_phy->phy_power_sts = PHY_UNINITIALIZED;
id = of_match_node(thunderbay_emmc_phy_of_match, pdev->dev.of_node);
if (!id) {
dev_err(dev, "failed to get match_node\n");
return -EINVAL;
}
generic_phy = devm_phy_create(dev, dev->of_node, id->data);
if (IS_ERR(generic_phy)) {
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(generic_phy);
}
phy_set_drvdata(generic_phy, tbh_phy);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static struct platform_driver thunderbay_emmc_phy_driver = {
.probe = thunderbay_emmc_phy_probe,
.driver = {
.name = "thunderbay-emmc-phy",
.of_match_table = thunderbay_emmc_phy_of_match,
},
};
module_platform_driver(thunderbay_emmc_phy_driver);
MODULE_AUTHOR("Nandhini S <nandhini.srikandan@intel.com>");
MODULE_AUTHOR("Rashmi A <rashmi.a@intel.com>");
MODULE_DESCRIPTION("Intel Thunder Bay eMMC PHY driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021 MediaTek Inc.
*
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
#ifndef __PHY_MTK_H__
#define __PHY_MTK_H__
#include <linux/io.h>
static inline void mtk_phy_clear_bits(void __iomem *reg, u32 bits)
{
u32 tmp = readl(reg);
tmp &= ~bits;
writel(tmp, reg);
}
static inline void mtk_phy_set_bits(void __iomem *reg, u32 bits)
{
u32 tmp = readl(reg);
tmp |= bits;
writel(tmp, reg);
}
static inline void mtk_phy_update_bits(void __iomem *reg, u32 mask, u32 val)
{
u32 tmp = readl(reg);
tmp &= ~mask;
tmp |= val & mask;
writel(tmp, reg);
}
#endif

View File

@ -146,6 +146,8 @@ static int mtk_mipi_tx_probe(struct platform_device *pdev)
return -ENOMEM;
mipi_tx->driver_data = of_device_get_match_data(dev);
if (!mipi_tx->driver_data)
return -ENODEV;
mipi_tx->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mipi_tx->regs))

View File

@ -8,16 +8,18 @@
#include <dt-bindings/phy/phy.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include "phy-mtk-io.h"
/* version V1 sub-banks offset base address */
/* banks shared by multiple phys */
#define SSUSB_SIFSLV_V1_SPLLC 0x000 /* shared by u3 phys */
@ -41,6 +43,9 @@
#define SSUSB_SIFSLV_V2_U3PHYD 0x200
#define SSUSB_SIFSLV_V2_U3PHYA 0x400
#define U3P_MISC_REG1 0x04
#define MR1_EFUSE_AUTO_LOAD_DIS BIT(6)
#define U3P_USBPHYACR0 0x000
#define PA0_RG_U2PLL_FORCE_ON BIT(15)
#define PA0_USB20_PLL_PREDIV GENMASK(7, 6)
@ -133,6 +138,8 @@
#define P3C_RG_SWRST_U3_PHYD_FORCE_EN BIT(24)
#define U3P_U3_PHYA_REG0 0x000
#define P3A_RG_IEXT_INTR GENMASK(15, 10)
#define P3A_RG_IEXT_INTR_VAL(x) ((0x3f & (x)) << 10)
#define P3A_RG_CLKDRV_OFF GENMASK(3, 2)
#define P3A_RG_CLKDRV_OFF_VAL(x) ((0x3 & (x)) << 2)
@ -187,6 +194,19 @@
#define P3D_RG_FWAKE_TH GENMASK(21, 16)
#define P3D_RG_FWAKE_TH_VAL(x) ((0x3f & (x)) << 16)
#define U3P_U3_PHYD_IMPCAL0 0x010
#define P3D_RG_FORCE_TX_IMPEL BIT(31)
#define P3D_RG_TX_IMPEL GENMASK(28, 24)
#define P3D_RG_TX_IMPEL_VAL(x) ((0x1f & (x)) << 24)
#define U3P_U3_PHYD_IMPCAL1 0x014
#define P3D_RG_FORCE_RX_IMPEL BIT(31)
#define P3D_RG_RX_IMPEL GENMASK(28, 24)
#define P3D_RG_RX_IMPEL_VAL(x) ((0x1f & (x)) << 24)
#define U3P_U3_PHYD_RSV 0x054
#define P3D_RG_EFUSE_AUTO_LOAD_DIS BIT(12)
#define U3P_U3_PHYD_CDR1 0x05c
#define P3D_RG_CDR_BIR_LTD1 GENMASK(28, 24)
#define P3D_RG_CDR_BIR_LTD1_VAL(x) ((0x1f & (x)) << 24)
@ -307,6 +327,11 @@ struct mtk_phy_pdata {
* 48M PLL, fix it by switching PLL to 26M from default 48M
*/
bool sw_pll_48m_to_26m;
/*
* Some SoCs (e.g. mt8195) drop a bit when use auto load efuse,
* support sw way, also support it for v2/v3 optionally.
*/
bool sw_efuse_supported;
enum mtk_phy_version version;
};
@ -336,6 +361,10 @@ struct mtk_phy_instance {
struct regmap *type_sw;
u32 type_sw_reg;
u32 type_sw_index;
u32 efuse_sw_en;
u32 efuse_intr;
u32 efuse_tx_imp;
u32 efuse_rx_imp;
int eye_src;
int eye_vrt;
int eye_term;
@ -373,15 +402,11 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
return;
/* enable USB ring oscillator */
tmp = readl(com + U3P_USBPHYACR5);
tmp |= PA5_RG_U2_HSTX_SRCAL_EN;
writel(tmp, com + U3P_USBPHYACR5);
mtk_phy_set_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCAL_EN);
udelay(1);
/*enable free run clock */
tmp = readl(fmreg + U3P_U2FREQ_FMMONR1);
tmp |= P2F_RG_FRCK_EN;
writel(tmp, fmreg + U3P_U2FREQ_FMMONR1);
mtk_phy_set_bits(fmreg + U3P_U2FREQ_FMMONR1, P2F_RG_FRCK_EN);
/* set cycle count as 1024, and select u2 channel */
tmp = readl(fmreg + U3P_U2FREQ_FMCR0);
@ -393,9 +418,7 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
writel(tmp, fmreg + U3P_U2FREQ_FMCR0);
/* enable frequency meter */
tmp = readl(fmreg + U3P_U2FREQ_FMCR0);
tmp |= P2F_RG_FREQDET_EN;
writel(tmp, fmreg + U3P_U2FREQ_FMCR0);
mtk_phy_set_bits(fmreg + U3P_U2FREQ_FMCR0, P2F_RG_FREQDET_EN);
/* ignore return value */
readl_poll_timeout(fmreg + U3P_U2FREQ_FMMONR1, tmp,
@ -404,14 +427,10 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
fm_out = readl(fmreg + U3P_U2FREQ_VALUE);
/* disable frequency meter */
tmp = readl(fmreg + U3P_U2FREQ_FMCR0);
tmp &= ~P2F_RG_FREQDET_EN;
writel(tmp, fmreg + U3P_U2FREQ_FMCR0);
mtk_phy_clear_bits(fmreg + U3P_U2FREQ_FMCR0, P2F_RG_FREQDET_EN);
/*disable free run clock */
tmp = readl(fmreg + U3P_U2FREQ_FMMONR1);
tmp &= ~P2F_RG_FRCK_EN;
writel(tmp, fmreg + U3P_U2FREQ_FMMONR1);
mtk_phy_clear_bits(fmreg + U3P_U2FREQ_FMMONR1, P2F_RG_FRCK_EN);
if (fm_out) {
/* ( 1024 / FM_OUT ) x reference clock frequency x coef */
@ -427,63 +446,44 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
tphy->src_ref_clk, tphy->src_coef);
/* set HS slew rate */
tmp = readl(com + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val);
writel(tmp, com + U3P_USBPHYACR5);
mtk_phy_update_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCTRL,
PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val));
/* disable USB ring oscillator */
tmp = readl(com + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HSTX_SRCAL_EN;
writel(tmp, com + U3P_USBPHYACR5);
mtk_phy_clear_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCAL_EN);
}
static void u3_phy_instance_init(struct mtk_tphy *tphy,
struct mtk_phy_instance *instance)
{
struct u3phy_banks *u3_banks = &instance->u3_banks;
u32 tmp;
/* gating PCIe Analog XTAL clock */
tmp = readl(u3_banks->spllc + U3P_SPLLC_XTALCTL3);
tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD;
writel(tmp, u3_banks->spllc + U3P_SPLLC_XTALCTL3);
mtk_phy_set_bits(u3_banks->spllc + U3P_SPLLC_XTALCTL3,
XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD);
/* gating XSQ */
tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG0);
tmp &= ~P3A_RG_XTAL_EXT_EN_U3;
tmp |= P3A_RG_XTAL_EXT_EN_U3_VAL(2);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG0);
mtk_phy_update_bits(u3_banks->phya + U3P_U3_PHYA_DA_REG0,
P3A_RG_XTAL_EXT_EN_U3, P3A_RG_XTAL_EXT_EN_U3_VAL(2));
tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG9);
tmp &= ~P3A_RG_RX_DAC_MUX;
tmp |= P3A_RG_RX_DAC_MUX_VAL(4);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG9);
mtk_phy_update_bits(u3_banks->phya + U3P_U3_PHYA_REG9,
P3A_RG_RX_DAC_MUX, P3A_RG_RX_DAC_MUX_VAL(4));
tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG6);
tmp &= ~P3A_RG_TX_EIDLE_CM;
tmp |= P3A_RG_TX_EIDLE_CM_VAL(0xe);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG6);
mtk_phy_update_bits(u3_banks->phya + U3P_U3_PHYA_REG6,
P3A_RG_TX_EIDLE_CM, P3A_RG_TX_EIDLE_CM_VAL(0xe));
tmp = readl(u3_banks->phyd + U3P_U3_PHYD_CDR1);
tmp &= ~(P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1);
tmp |= P3D_RG_CDR_BIR_LTD0_VAL(0xc) | P3D_RG_CDR_BIR_LTD1_VAL(0x3);
writel(tmp, u3_banks->phyd + U3P_U3_PHYD_CDR1);
mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_CDR1,
P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1,
P3D_RG_CDR_BIR_LTD0_VAL(0xc) | P3D_RG_CDR_BIR_LTD1_VAL(0x3));
tmp = readl(u3_banks->phyd + U3P_U3_PHYD_LFPS1);
tmp &= ~P3D_RG_FWAKE_TH;
tmp |= P3D_RG_FWAKE_TH_VAL(0x34);
writel(tmp, u3_banks->phyd + U3P_U3_PHYD_LFPS1);
mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_LFPS1,
P3D_RG_FWAKE_TH, P3D_RG_FWAKE_TH_VAL(0x34));
tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET1);
tmp &= ~P3D_RG_RXDET_STB2_SET;
tmp |= P3D_RG_RXDET_STB2_SET_VAL(0x10);
writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET1);
mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_RXDET1,
P3D_RG_RXDET_STB2_SET, P3D_RG_RXDET_STB2_SET_VAL(0x10));
tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET2);
tmp &= ~P3D_RG_RXDET_STB2_SET_P3;
tmp |= P3D_RG_RXDET_STB2_SET_P3_VAL(0x10);
writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET2);
mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_RXDET2,
P3D_RG_RXDET_STB2_SET_P3, P3D_RG_RXDET_STB2_SET_P3_VAL(0x10));
dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index);
}
@ -493,26 +493,20 @@ static void u2_phy_pll_26m_set(struct mtk_tphy *tphy,
{
struct u2phy_banks *u2_banks = &instance->u2_banks;
void __iomem *com = u2_banks->com;
u32 tmp;
if (!tphy->pdata->sw_pll_48m_to_26m)
return;
tmp = readl(com + U3P_USBPHYACR0);
tmp &= ~PA0_USB20_PLL_PREDIV;
tmp |= PA0_USB20_PLL_PREDIV_VAL(0);
writel(tmp, com + U3P_USBPHYACR0);
mtk_phy_update_bits(com + U3P_USBPHYACR0, PA0_USB20_PLL_PREDIV,
PA0_USB20_PLL_PREDIV_VAL(0));
tmp = readl(com + U3P_USBPHYACR2);
tmp &= ~PA2_RG_U2PLL_BW;
tmp |= PA2_RG_U2PLL_BW_VAL(3);
writel(tmp, com + U3P_USBPHYACR2);
mtk_phy_update_bits(com + U3P_USBPHYACR2, PA2_RG_U2PLL_BW,
PA2_RG_U2PLL_BW_VAL(3));
writel(P2R_RG_U2PLL_FBDIV_26M, com + U3P_U2PHYA_RESV);
tmp = readl(com + U3P_U2PHYA_RESV1);
tmp |= P2R_RG_U2PLL_FRA_EN | P2R_RG_U2PLL_REFCLK_SEL;
writel(tmp, com + U3P_U2PHYA_RESV1);
mtk_phy_set_bits(com + U3P_U2PHYA_RESV1,
P2R_RG_U2PLL_FRA_EN | P2R_RG_U2PLL_REFCLK_SEL);
}
static void u2_phy_instance_init(struct mtk_tphy *tphy,
@ -521,58 +515,40 @@ static void u2_phy_instance_init(struct mtk_tphy *tphy,
struct u2phy_banks *u2_banks = &instance->u2_banks;
void __iomem *com = u2_banks->com;
u32 index = instance->index;
u32 tmp;
/* switch to USB function, and enable usb pll */
tmp = readl(com + U3P_U2PHYDTM0);
tmp &= ~(P2C_FORCE_UART_EN | P2C_FORCE_SUSPENDM);
tmp |= P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0);
writel(tmp, com + U3P_U2PHYDTM0);
mtk_phy_clear_bits(com + U3P_U2PHYDTM0, P2C_FORCE_UART_EN | P2C_FORCE_SUSPENDM);
tmp = readl(com + U3P_U2PHYDTM1);
tmp &= ~P2C_RG_UART_EN;
writel(tmp, com + U3P_U2PHYDTM1);
mtk_phy_update_bits(com + U3P_U2PHYDTM0, P2C_RG_XCVRSEL | P2C_RG_DATAIN,
P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0));
tmp = readl(com + U3P_USBPHYACR0);
tmp |= PA0_RG_USB20_INTR_EN;
writel(tmp, com + U3P_USBPHYACR0);
mtk_phy_clear_bits(com + U3P_U2PHYDTM1, P2C_RG_UART_EN);
mtk_phy_set_bits(com + U3P_USBPHYACR0, PA0_RG_USB20_INTR_EN);
/* disable switch 100uA current to SSUSB */
tmp = readl(com + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
writel(tmp, com + U3P_USBPHYACR5);
mtk_phy_clear_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HS_100U_U3_EN);
if (!index) {
tmp = readl(com + U3P_U2PHYACR4);
tmp &= ~P2C_U2_GPIO_CTR_MSK;
writel(tmp, com + U3P_U2PHYACR4);
}
if (!index)
mtk_phy_clear_bits(com + U3P_U2PHYACR4, P2C_U2_GPIO_CTR_MSK);
if (tphy->pdata->avoid_rx_sen_degradation) {
if (!index) {
tmp = readl(com + U3P_USBPHYACR2);
tmp |= PA2_RG_SIF_U2PLL_FORCE_EN;
writel(tmp, com + U3P_USBPHYACR2);
mtk_phy_set_bits(com + U3P_USBPHYACR2, PA2_RG_SIF_U2PLL_FORCE_EN);
tmp = readl(com + U3D_U2PHYDCR0);
tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
writel(tmp, com + U3D_U2PHYDCR0);
mtk_phy_clear_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON);
} else {
tmp = readl(com + U3D_U2PHYDCR0);
tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
writel(tmp, com + U3D_U2PHYDCR0);
mtk_phy_set_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON);
tmp = readl(com + U3P_U2PHYDTM0);
tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
writel(tmp, com + U3P_U2PHYDTM0);
mtk_phy_set_bits(com + U3P_U2PHYDTM0,
P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM);
}
}
tmp = readl(com + U3P_USBPHYACR6);
tmp &= ~PA6_RG_U2_BC11_SW_EN; /* DP/DM BC1.1 path Disable */
tmp &= ~PA6_RG_U2_SQTH;
tmp |= PA6_RG_U2_SQTH_VAL(2);
writel(tmp, com + U3P_USBPHYACR6);
/* DP/DM BC1.1 path Disable */
mtk_phy_clear_bits(com + U3P_USBPHYACR6, PA6_RG_U2_BC11_SW_EN);
mtk_phy_update_bits(com + U3P_USBPHYACR6, PA6_RG_U2_SQTH, PA6_RG_U2_SQTH_VAL(2));
/* Workaround only for mt8195, HW fix it for others (V3) */
u2_phy_pll_26m_set(tphy, instance);
@ -586,30 +562,21 @@ static void u2_phy_instance_power_on(struct mtk_tphy *tphy,
struct u2phy_banks *u2_banks = &instance->u2_banks;
void __iomem *com = u2_banks->com;
u32 index = instance->index;
u32 tmp;
tmp = readl(com + U3P_U2PHYDTM0);
tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN | P2C_DTM0_PART_MASK);
writel(tmp, com + U3P_U2PHYDTM0);
mtk_phy_clear_bits(com + U3P_U2PHYDTM0,
P2C_RG_XCVRSEL | P2C_RG_DATAIN | P2C_DTM0_PART_MASK);
/* OTG Enable */
tmp = readl(com + U3P_USBPHYACR6);
tmp |= PA6_RG_U2_OTG_VBUSCMP_EN;
writel(tmp, com + U3P_USBPHYACR6);
mtk_phy_set_bits(com + U3P_USBPHYACR6, PA6_RG_U2_OTG_VBUSCMP_EN);
tmp = readl(com + U3P_U2PHYDTM1);
tmp |= P2C_RG_VBUSVALID | P2C_RG_AVALID;
tmp &= ~P2C_RG_SESSEND;
writel(tmp, com + U3P_U2PHYDTM1);
mtk_phy_set_bits(com + U3P_U2PHYDTM1, P2C_RG_VBUSVALID | P2C_RG_AVALID);
mtk_phy_clear_bits(com + U3P_U2PHYDTM1, P2C_RG_SESSEND);
if (tphy->pdata->avoid_rx_sen_degradation && index) {
tmp = readl(com + U3D_U2PHYDCR0);
tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
writel(tmp, com + U3D_U2PHYDCR0);
mtk_phy_set_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON);
tmp = readl(com + U3P_U2PHYDTM0);
tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
writel(tmp, com + U3P_U2PHYDTM0);
mtk_phy_set_bits(com + U3P_U2PHYDTM0, P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM);
}
dev_dbg(tphy->dev, "%s(%d)\n", __func__, index);
}
@ -620,30 +587,20 @@ static void u2_phy_instance_power_off(struct mtk_tphy *tphy,
struct u2phy_banks *u2_banks = &instance->u2_banks;
void __iomem *com = u2_banks->com;
u32 index = instance->index;
u32 tmp;
tmp = readl(com + U3P_U2PHYDTM0);
tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN);
writel(tmp, com + U3P_U2PHYDTM0);
mtk_phy_clear_bits(com + U3P_U2PHYDTM0, P2C_RG_XCVRSEL | P2C_RG_DATAIN);
/* OTG Disable */
tmp = readl(com + U3P_USBPHYACR6);
tmp &= ~PA6_RG_U2_OTG_VBUSCMP_EN;
writel(tmp, com + U3P_USBPHYACR6);
mtk_phy_clear_bits(com + U3P_USBPHYACR6, PA6_RG_U2_OTG_VBUSCMP_EN);
tmp = readl(com + U3P_U2PHYDTM1);
tmp &= ~(P2C_RG_VBUSVALID | P2C_RG_AVALID);
tmp |= P2C_RG_SESSEND;
writel(tmp, com + U3P_U2PHYDTM1);
mtk_phy_clear_bits(com + U3P_U2PHYDTM1, P2C_RG_VBUSVALID | P2C_RG_AVALID);
mtk_phy_set_bits(com + U3P_U2PHYDTM1, P2C_RG_SESSEND);
if (tphy->pdata->avoid_rx_sen_degradation && index) {
tmp = readl(com + U3P_U2PHYDTM0);
tmp &= ~(P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM);
writel(tmp, com + U3P_U2PHYDTM0);
mtk_phy_clear_bits(com + U3P_U2PHYDTM0, P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM);
tmp = readl(com + U3D_U2PHYDCR0);
tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
writel(tmp, com + U3D_U2PHYDCR0);
mtk_phy_clear_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON);
}
dev_dbg(tphy->dev, "%s(%d)\n", __func__, index);
@ -655,16 +612,11 @@ static void u2_phy_instance_exit(struct mtk_tphy *tphy,
struct u2phy_banks *u2_banks = &instance->u2_banks;
void __iomem *com = u2_banks->com;
u32 index = instance->index;
u32 tmp;
if (tphy->pdata->avoid_rx_sen_degradation && index) {
tmp = readl(com + U3D_U2PHYDCR0);
tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
writel(tmp, com + U3D_U2PHYDCR0);
mtk_phy_clear_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON);
tmp = readl(com + U3P_U2PHYDTM0);
tmp &= ~P2C_FORCE_SUSPENDM;
writel(tmp, com + U3P_U2PHYDTM0);
mtk_phy_clear_bits(com + U3P_U2PHYDTM0, P2C_FORCE_SUSPENDM);
}
}
@ -697,69 +649,50 @@ static void pcie_phy_instance_init(struct mtk_tphy *tphy,
struct mtk_phy_instance *instance)
{
struct u3phy_banks *u3_banks = &instance->u3_banks;
u32 tmp;
void __iomem *phya = u3_banks->phya;
if (tphy->pdata->version != MTK_PHY_V1)
return;
tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG0);
tmp &= ~(P3A_RG_XTAL_EXT_PE1H | P3A_RG_XTAL_EXT_PE2H);
tmp |= P3A_RG_XTAL_EXT_PE1H_VAL(0x2) | P3A_RG_XTAL_EXT_PE2H_VAL(0x2);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG0);
mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG0,
P3A_RG_XTAL_EXT_PE1H | P3A_RG_XTAL_EXT_PE2H,
P3A_RG_XTAL_EXT_PE1H_VAL(0x2) | P3A_RG_XTAL_EXT_PE2H_VAL(0x2));
/* ref clk drive */
tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG1);
tmp &= ~P3A_RG_CLKDRV_AMP;
tmp |= P3A_RG_CLKDRV_AMP_VAL(0x4);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG1);
mtk_phy_update_bits(phya + U3P_U3_PHYA_REG1, P3A_RG_CLKDRV_AMP,
P3A_RG_CLKDRV_AMP_VAL(0x4));
tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0);
tmp &= ~P3A_RG_CLKDRV_OFF;
tmp |= P3A_RG_CLKDRV_OFF_VAL(0x1);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG0);
mtk_phy_update_bits(phya + U3P_U3_PHYA_REG0, P3A_RG_CLKDRV_OFF,
P3A_RG_CLKDRV_OFF_VAL(0x1));
/* SSC delta -5000ppm */
tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG20);
tmp &= ~P3A_RG_PLL_DELTA1_PE2H;
tmp |= P3A_RG_PLL_DELTA1_PE2H_VAL(0x3c);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG20);
mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG20, P3A_RG_PLL_DELTA1_PE2H,
P3A_RG_PLL_DELTA1_PE2H_VAL(0x3c));
tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG25);
tmp &= ~P3A_RG_PLL_DELTA_PE2H;
tmp |= P3A_RG_PLL_DELTA_PE2H_VAL(0x36);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG25);
mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG25, P3A_RG_PLL_DELTA_PE2H,
P3A_RG_PLL_DELTA_PE2H_VAL(0x36));
/* change pll BW 0.6M */
tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG5);
tmp &= ~(P3A_RG_PLL_BR_PE2H | P3A_RG_PLL_IC_PE2H);
tmp |= P3A_RG_PLL_BR_PE2H_VAL(0x1) | P3A_RG_PLL_IC_PE2H_VAL(0x1);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG5);
mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG5,
P3A_RG_PLL_BR_PE2H | P3A_RG_PLL_IC_PE2H,
P3A_RG_PLL_BR_PE2H_VAL(0x1) | P3A_RG_PLL_IC_PE2H_VAL(0x1));
tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG4);
tmp &= ~(P3A_RG_PLL_DIVEN_PE2H | P3A_RG_PLL_BC_PE2H);
tmp |= P3A_RG_PLL_BC_PE2H_VAL(0x3);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG4);
mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG4,
P3A_RG_PLL_DIVEN_PE2H | P3A_RG_PLL_BC_PE2H,
P3A_RG_PLL_BC_PE2H_VAL(0x3));
tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG6);
tmp &= ~P3A_RG_PLL_IR_PE2H;
tmp |= P3A_RG_PLL_IR_PE2H_VAL(0x2);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG6);
mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG6, P3A_RG_PLL_IR_PE2H,
P3A_RG_PLL_IR_PE2H_VAL(0x2));
tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG7);
tmp &= ~P3A_RG_PLL_BP_PE2H;
tmp |= P3A_RG_PLL_BP_PE2H_VAL(0xa);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG7);
mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG7, P3A_RG_PLL_BP_PE2H,
P3A_RG_PLL_BP_PE2H_VAL(0xa));
/* Tx Detect Rx Timing: 10us -> 5us */
tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET1);
tmp &= ~P3D_RG_RXDET_STB2_SET;
tmp |= P3D_RG_RXDET_STB2_SET_VAL(0x10);
writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET1);
mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_RXDET1,
P3D_RG_RXDET_STB2_SET, P3D_RG_RXDET_STB2_SET_VAL(0x10));
tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET2);
tmp &= ~P3D_RG_RXDET_STB2_SET_P3;
tmp |= P3D_RG_RXDET_STB2_SET_P3_VAL(0x10);
writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET2);
mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_RXDET2,
P3D_RG_RXDET_STB2_SET_P3, P3D_RG_RXDET_STB2_SET_P3_VAL(0x10));
/* wait for PCIe subsys register to active */
usleep_range(2500, 3000);
@ -770,15 +703,12 @@ static void pcie_phy_instance_power_on(struct mtk_tphy *tphy,
struct mtk_phy_instance *instance)
{
struct u3phy_banks *bank = &instance->u3_banks;
u32 tmp;
tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD);
tmp &= ~(P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST);
writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD);
mtk_phy_clear_bits(bank->chip + U3P_U3_CHIP_GPIO_CTLD,
P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST);
tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE);
tmp &= ~(P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD);
writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLE);
mtk_phy_clear_bits(bank->chip + U3P_U3_CHIP_GPIO_CTLE,
P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD);
}
static void pcie_phy_instance_power_off(struct mtk_tphy *tphy,
@ -786,15 +716,12 @@ static void pcie_phy_instance_power_off(struct mtk_tphy *tphy,
{
struct u3phy_banks *bank = &instance->u3_banks;
u32 tmp;
tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD);
tmp |= P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST;
writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD);
mtk_phy_set_bits(bank->chip + U3P_U3_CHIP_GPIO_CTLD,
P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST);
tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE);
tmp |= P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD;
writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLE);
mtk_phy_set_bits(bank->chip + U3P_U3_CHIP_GPIO_CTLE,
P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD);
}
static void sata_phy_instance_init(struct mtk_tphy *tphy,
@ -802,55 +729,42 @@ static void sata_phy_instance_init(struct mtk_tphy *tphy,
{
struct u3phy_banks *u3_banks = &instance->u3_banks;
void __iomem *phyd = u3_banks->phyd;
u32 tmp;
/* charge current adjustment */
tmp = readl(phyd + ANA_RG_CTRL_SIGNAL6);
tmp &= ~(RG_CDR_BIRLTR_GEN1_MSK | RG_CDR_BC_GEN1_MSK);
tmp |= RG_CDR_BIRLTR_GEN1_VAL(0x6) | RG_CDR_BC_GEN1_VAL(0x1a);
writel(tmp, phyd + ANA_RG_CTRL_SIGNAL6);
mtk_phy_update_bits(phyd + ANA_RG_CTRL_SIGNAL6,
RG_CDR_BIRLTR_GEN1_MSK | RG_CDR_BC_GEN1_MSK,
RG_CDR_BIRLTR_GEN1_VAL(0x6) | RG_CDR_BC_GEN1_VAL(0x1a));
tmp = readl(phyd + ANA_EQ_EYE_CTRL_SIGNAL4);
tmp &= ~RG_CDR_BIRLTD0_GEN1_MSK;
tmp |= RG_CDR_BIRLTD0_GEN1_VAL(0x18);
writel(tmp, phyd + ANA_EQ_EYE_CTRL_SIGNAL4);
mtk_phy_update_bits(phyd + ANA_EQ_EYE_CTRL_SIGNAL4, RG_CDR_BIRLTD0_GEN1_MSK,
RG_CDR_BIRLTD0_GEN1_VAL(0x18));
tmp = readl(phyd + ANA_EQ_EYE_CTRL_SIGNAL5);
tmp &= ~RG_CDR_BIRLTD0_GEN3_MSK;
tmp |= RG_CDR_BIRLTD0_GEN3_VAL(0x06);
writel(tmp, phyd + ANA_EQ_EYE_CTRL_SIGNAL5);
mtk_phy_update_bits(phyd + ANA_EQ_EYE_CTRL_SIGNAL5, RG_CDR_BIRLTD0_GEN3_MSK,
RG_CDR_BIRLTD0_GEN3_VAL(0x06));
tmp = readl(phyd + ANA_RG_CTRL_SIGNAL4);
tmp &= ~(RG_CDR_BICLTR_GEN1_MSK | RG_CDR_BR_GEN2_MSK);
tmp |= RG_CDR_BICLTR_GEN1_VAL(0x0c) | RG_CDR_BR_GEN2_VAL(0x07);
writel(tmp, phyd + ANA_RG_CTRL_SIGNAL4);
mtk_phy_update_bits(phyd + ANA_RG_CTRL_SIGNAL4,
RG_CDR_BICLTR_GEN1_MSK | RG_CDR_BR_GEN2_MSK,
RG_CDR_BICLTR_GEN1_VAL(0x0c) | RG_CDR_BR_GEN2_VAL(0x07));
tmp = readl(phyd + PHYD_CTRL_SIGNAL_MODE4);
tmp &= ~(RG_CDR_BICLTD0_GEN1_MSK | RG_CDR_BICLTD1_GEN1_MSK);
tmp |= RG_CDR_BICLTD0_GEN1_VAL(0x08) | RG_CDR_BICLTD1_GEN1_VAL(0x02);
writel(tmp, phyd + PHYD_CTRL_SIGNAL_MODE4);
mtk_phy_update_bits(phyd + PHYD_CTRL_SIGNAL_MODE4,
RG_CDR_BICLTD0_GEN1_MSK | RG_CDR_BICLTD1_GEN1_MSK,
RG_CDR_BICLTD0_GEN1_VAL(0x08) | RG_CDR_BICLTD1_GEN1_VAL(0x02));
tmp = readl(phyd + PHYD_DESIGN_OPTION2);
tmp &= ~RG_LOCK_CNT_SEL_MSK;
tmp |= RG_LOCK_CNT_SEL_VAL(0x02);
writel(tmp, phyd + PHYD_DESIGN_OPTION2);
mtk_phy_update_bits(phyd + PHYD_DESIGN_OPTION2, RG_LOCK_CNT_SEL_MSK,
RG_LOCK_CNT_SEL_VAL(0x02));
tmp = readl(phyd + PHYD_DESIGN_OPTION9);
tmp &= ~(RG_T2_MIN_MSK | RG_TG_MIN_MSK |
RG_T2_MAX_MSK | RG_TG_MAX_MSK);
tmp |= RG_T2_MIN_VAL(0x12) | RG_TG_MIN_VAL(0x04) |
RG_T2_MAX_VAL(0x31) | RG_TG_MAX_VAL(0x0e);
writel(tmp, phyd + PHYD_DESIGN_OPTION9);
mtk_phy_update_bits(phyd + PHYD_DESIGN_OPTION9,
RG_T2_MIN_MSK | RG_TG_MIN_MSK,
RG_T2_MIN_VAL(0x12) | RG_TG_MIN_VAL(0x04));
tmp = readl(phyd + ANA_RG_CTRL_SIGNAL1);
tmp &= ~RG_IDRV_0DB_GEN1_MSK;
tmp |= RG_IDRV_0DB_GEN1_VAL(0x20);
writel(tmp, phyd + ANA_RG_CTRL_SIGNAL1);
mtk_phy_update_bits(phyd + PHYD_DESIGN_OPTION9,
RG_T2_MAX_MSK | RG_TG_MAX_MSK,
RG_T2_MAX_VAL(0x31) | RG_TG_MAX_VAL(0x0e));
tmp = readl(phyd + ANA_EQ_EYE_CTRL_SIGNAL1);
tmp &= ~RG_EQ_DLEQ_LFI_GEN1_MSK;
tmp |= RG_EQ_DLEQ_LFI_GEN1_VAL(0x03);
writel(tmp, phyd + ANA_EQ_EYE_CTRL_SIGNAL1);
mtk_phy_update_bits(phyd + ANA_RG_CTRL_SIGNAL1, RG_IDRV_0DB_GEN1_MSK,
RG_IDRV_0DB_GEN1_VAL(0x20));
mtk_phy_update_bits(phyd + ANA_EQ_EYE_CTRL_SIGNAL1, RG_EQ_DLEQ_LFI_GEN1_MSK,
RG_EQ_DLEQ_LFI_GEN1_VAL(0x03));
dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index);
}
@ -938,48 +852,29 @@ static void u2_phy_props_set(struct mtk_tphy *tphy,
{
struct u2phy_banks *u2_banks = &instance->u2_banks;
void __iomem *com = u2_banks->com;
u32 tmp;
if (instance->bc12_en) {
tmp = readl(com + U3P_U2PHYBC12C);
tmp |= P2C_RG_CHGDT_EN; /* BC1.2 path Enable */
writel(tmp, com + U3P_U2PHYBC12C);
}
if (instance->bc12_en) /* BC1.2 path Enable */
mtk_phy_set_bits(com + U3P_U2PHYBC12C, P2C_RG_CHGDT_EN);
if (tphy->pdata->version < MTK_PHY_V3 && instance->eye_src) {
tmp = readl(com + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(instance->eye_src);
writel(tmp, com + U3P_USBPHYACR5);
}
if (tphy->pdata->version < MTK_PHY_V3 && instance->eye_src)
mtk_phy_update_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCTRL,
PA5_RG_U2_HSTX_SRCTRL_VAL(instance->eye_src));
if (instance->eye_vrt) {
tmp = readl(com + U3P_USBPHYACR1);
tmp &= ~PA1_RG_VRT_SEL;
tmp |= PA1_RG_VRT_SEL_VAL(instance->eye_vrt);
writel(tmp, com + U3P_USBPHYACR1);
}
if (instance->eye_vrt)
mtk_phy_update_bits(com + U3P_USBPHYACR1, PA1_RG_VRT_SEL,
PA1_RG_VRT_SEL_VAL(instance->eye_vrt));
if (instance->eye_term) {
tmp = readl(com + U3P_USBPHYACR1);
tmp &= ~PA1_RG_TERM_SEL;
tmp |= PA1_RG_TERM_SEL_VAL(instance->eye_term);
writel(tmp, com + U3P_USBPHYACR1);
}
if (instance->eye_term)
mtk_phy_update_bits(com + U3P_USBPHYACR1, PA1_RG_TERM_SEL,
PA1_RG_TERM_SEL_VAL(instance->eye_term));
if (instance->intr) {
tmp = readl(com + U3P_USBPHYACR1);
tmp &= ~PA1_RG_INTR_CAL;
tmp |= PA1_RG_INTR_CAL_VAL(instance->intr);
writel(tmp, com + U3P_USBPHYACR1);
}
if (instance->intr)
mtk_phy_update_bits(com + U3P_USBPHYACR1, PA1_RG_INTR_CAL,
PA1_RG_INTR_CAL_VAL(instance->intr));
if (instance->discth) {
tmp = readl(com + U3P_USBPHYACR6);
tmp &= ~PA6_RG_U2_DISCTH;
tmp |= PA6_RG_U2_DISCTH_VAL(instance->discth);
writel(tmp, com + U3P_USBPHYACR6);
}
if (instance->discth)
mtk_phy_update_bits(com + U3P_USBPHYACR6, PA6_RG_U2_DISCTH,
PA6_RG_U2_DISCTH_VAL(instance->discth));
}
/* type switch for usb3/pcie/sgmii/sata */
@ -1040,6 +935,117 @@ static int phy_type_set(struct mtk_phy_instance *instance)
return 0;
}
static int phy_efuse_get(struct mtk_tphy *tphy, struct mtk_phy_instance *instance)
{
struct device *dev = &instance->phy->dev;
int ret = 0;
/* tphy v1 doesn't support sw efuse, skip it */
if (!tphy->pdata->sw_efuse_supported) {
instance->efuse_sw_en = 0;
return 0;
}
/* software efuse is optional */
instance->efuse_sw_en = device_property_read_bool(dev, "nvmem-cells");
if (!instance->efuse_sw_en)
return 0;
switch (instance->type) {
case PHY_TYPE_USB2:
ret = nvmem_cell_read_variable_le_u32(dev, "intr", &instance->efuse_intr);
if (ret) {
dev_err(dev, "fail to get u2 intr efuse, %d\n", ret);
break;
}
/* no efuse, ignore it */
if (!instance->efuse_intr) {
dev_warn(dev, "no u2 intr efuse, but dts enable it\n");
instance->efuse_sw_en = 0;
break;
}
dev_dbg(dev, "u2 efuse - intr %x\n", instance->efuse_intr);
break;
case PHY_TYPE_USB3:
case PHY_TYPE_PCIE:
ret = nvmem_cell_read_variable_le_u32(dev, "intr", &instance->efuse_intr);
if (ret) {
dev_err(dev, "fail to get u3 intr efuse, %d\n", ret);
break;
}
ret = nvmem_cell_read_variable_le_u32(dev, "rx_imp", &instance->efuse_rx_imp);
if (ret) {
dev_err(dev, "fail to get u3 rx_imp efuse, %d\n", ret);
break;
}
ret = nvmem_cell_read_variable_le_u32(dev, "tx_imp", &instance->efuse_tx_imp);
if (ret) {
dev_err(dev, "fail to get u3 tx_imp efuse, %d\n", ret);
break;
}
/* no efuse, ignore it */
if (!instance->efuse_intr &&
!instance->efuse_rx_imp &&
!instance->efuse_rx_imp) {
dev_warn(dev, "no u3 intr efuse, but dts enable it\n");
instance->efuse_sw_en = 0;
break;
}
dev_dbg(dev, "u3 efuse - intr %x, rx_imp %x, tx_imp %x\n",
instance->efuse_intr, instance->efuse_rx_imp,instance->efuse_tx_imp);
break;
default:
dev_err(dev, "no sw efuse for type %d\n", instance->type);
ret = -EINVAL;
}
return ret;
}
static void phy_efuse_set(struct mtk_phy_instance *instance)
{
struct device *dev = &instance->phy->dev;
struct u2phy_banks *u2_banks = &instance->u2_banks;
struct u3phy_banks *u3_banks = &instance->u3_banks;
if (!instance->efuse_sw_en)
return;
switch (instance->type) {
case PHY_TYPE_USB2:
mtk_phy_set_bits(u2_banks->misc + U3P_MISC_REG1, MR1_EFUSE_AUTO_LOAD_DIS);
mtk_phy_update_bits(u2_banks->com + U3P_USBPHYACR1, PA1_RG_INTR_CAL,
PA1_RG_INTR_CAL_VAL(instance->efuse_intr));
break;
case PHY_TYPE_USB3:
case PHY_TYPE_PCIE:
mtk_phy_set_bits(u3_banks->phyd + U3P_U3_PHYD_RSV, P3D_RG_EFUSE_AUTO_LOAD_DIS);
mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_TX_IMPEL,
P3D_RG_TX_IMPEL_VAL(instance->efuse_tx_imp));
mtk_phy_set_bits(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_FORCE_TX_IMPEL);
mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_RX_IMPEL,
P3D_RG_RX_IMPEL_VAL(instance->efuse_rx_imp));
mtk_phy_set_bits(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_FORCE_RX_IMPEL);
mtk_phy_update_bits(u3_banks->phya + U3P_U3_PHYA_REG0, P3A_RG_IEXT_INTR,
P3A_RG_IEXT_INTR_VAL(instance->efuse_intr));
break;
default:
dev_warn(dev, "no sw efuse for type %d\n", instance->type);
break;
}
}
static int mtk_phy_init(struct phy *phy)
{
struct mtk_phy_instance *instance = phy_get_drvdata(phy);
@ -1050,6 +1056,8 @@ static int mtk_phy_init(struct phy *phy)
if (ret)
return ret;
phy_efuse_set(instance);
switch (instance->type) {
case PHY_TYPE_USB2:
u2_phy_instance_init(tphy, instance);
@ -1134,6 +1142,7 @@ static struct phy *mtk_phy_xlate(struct device *dev,
struct mtk_phy_instance *instance = NULL;
struct device_node *phy_np = args->np;
int index;
int ret;
if (args->args_count != 1) {
dev_err(dev, "invalid number of cells in 'phy' property\n");
@ -1174,6 +1183,10 @@ static struct phy *mtk_phy_xlate(struct device *dev,
return ERR_PTR(-EINVAL);
}
ret = phy_efuse_get(tphy, instance);
if (ret)
return ERR_PTR(ret);
phy_parse_property(tphy, instance);
phy_type_set(instance);
@ -1196,10 +1209,12 @@ static const struct mtk_phy_pdata tphy_v1_pdata = {
static const struct mtk_phy_pdata tphy_v2_pdata = {
.avoid_rx_sen_degradation = false,
.sw_efuse_supported = true,
.version = MTK_PHY_V2,
};
static const struct mtk_phy_pdata tphy_v3_pdata = {
.sw_efuse_supported = true,
.version = MTK_PHY_V3,
};
@ -1210,6 +1225,7 @@ static const struct mtk_phy_pdata mt8173_pdata = {
static const struct mtk_phy_pdata mt8195_pdata = {
.sw_pll_48m_to_26m = true,
.sw_efuse_supported = true,
.version = MTK_PHY_V3,
};

View File

@ -10,13 +10,14 @@
#include <dt-bindings/phy/phy.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include "phy-mtk-io.h"
/* u2 phy banks */
#define SSUSB_SIFSLV_MISC 0x000
#define SSUSB_SIFSLV_U2FREQ 0x100
@ -126,26 +127,18 @@ static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy,
return;
/* enable USB ring oscillator */
tmp = readl(pbase + XSP_USBPHYACR5);
tmp |= P2A5_RG_HSTX_SRCAL_EN;
writel(tmp, pbase + XSP_USBPHYACR5);
mtk_phy_set_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN);
udelay(1); /* wait clock stable */
/* enable free run clock */
tmp = readl(pbase + XSP_U2FREQ_FMMONR1);
tmp |= P2F_RG_FRCK_EN;
writel(tmp, pbase + XSP_U2FREQ_FMMONR1);
mtk_phy_set_bits(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN);
/* set cycle count as 1024 */
tmp = readl(pbase + XSP_U2FREQ_FMCR0);
tmp &= ~(P2F_RG_CYCLECNT);
tmp |= P2F_RG_CYCLECNT_VAL(XSP_FM_DET_CYCLE_CNT);
writel(tmp, pbase + XSP_U2FREQ_FMCR0);
mtk_phy_update_bits(pbase + XSP_U2FREQ_FMCR0, P2F_RG_CYCLECNT,
P2F_RG_CYCLECNT_VAL(XSP_FM_DET_CYCLE_CNT));
/* enable frequency meter */
tmp = readl(pbase + XSP_U2FREQ_FMCR0);
tmp |= P2F_RG_FREQDET_EN;
writel(tmp, pbase + XSP_U2FREQ_FMCR0);
mtk_phy_set_bits(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN);
/* ignore return value */
readl_poll_timeout(pbase + XSP_U2FREQ_FMMONR1, tmp,
@ -154,14 +147,10 @@ static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy,
fm_out = readl(pbase + XSP_U2FREQ_MMONR0);
/* disable frequency meter */
tmp = readl(pbase + XSP_U2FREQ_FMCR0);
tmp &= ~P2F_RG_FREQDET_EN;
writel(tmp, pbase + XSP_U2FREQ_FMCR0);
mtk_phy_clear_bits(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN);
/* disable free run clock */
tmp = readl(pbase + XSP_U2FREQ_FMMONR1);
tmp &= ~P2F_RG_FRCK_EN;
writel(tmp, pbase + XSP_U2FREQ_FMMONR1);
mtk_phy_clear_bits(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN);
if (fm_out) {
/* (1024 / FM_OUT) x reference clock frequency x coefficient */
@ -177,31 +166,22 @@ static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy,
xsphy->src_ref_clk, xsphy->src_coef);
/* set HS slew rate */
tmp = readl(pbase + XSP_USBPHYACR5);
tmp &= ~P2A5_RG_HSTX_SRCTRL;
tmp |= P2A5_RG_HSTX_SRCTRL_VAL(calib_val);
writel(tmp, pbase + XSP_USBPHYACR5);
mtk_phy_update_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL,
P2A5_RG_HSTX_SRCTRL_VAL(calib_val));
/* disable USB ring oscillator */
tmp = readl(pbase + XSP_USBPHYACR5);
tmp &= ~P2A5_RG_HSTX_SRCAL_EN;
writel(tmp, pbase + XSP_USBPHYACR5);
mtk_phy_clear_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN);
}
static void u2_phy_instance_init(struct mtk_xsphy *xsphy,
struct xsphy_instance *inst)
{
void __iomem *pbase = inst->port_base;
u32 tmp;
/* DP/DM BC1.1 path Disable */
tmp = readl(pbase + XSP_USBPHYACR6);
tmp &= ~P2A6_RG_BC11_SW_EN;
writel(tmp, pbase + XSP_USBPHYACR6);
mtk_phy_clear_bits(pbase + XSP_USBPHYACR6, P2A6_RG_BC11_SW_EN);
tmp = readl(pbase + XSP_USBPHYACR0);
tmp |= P2A0_RG_INTR_EN;
writel(tmp, pbase + XSP_USBPHYACR0);
mtk_phy_set_bits(pbase + XSP_USBPHYACR0, P2A0_RG_INTR_EN);
}
static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy,
@ -209,16 +189,12 @@ static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy,
{
void __iomem *pbase = inst->port_base;
u32 index = inst->index;
u32 tmp;
tmp = readl(pbase + XSP_USBPHYACR6);
tmp |= P2A6_RG_OTG_VBUSCMP_EN;
writel(tmp, pbase + XSP_USBPHYACR6);
mtk_phy_set_bits(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN);
tmp = readl(pbase + XSP_U2PHYDTM1);
tmp |= P2D_RG_VBUSVALID | P2D_RG_AVALID;
tmp &= ~P2D_RG_SESSEND;
writel(tmp, pbase + XSP_U2PHYDTM1);
mtk_phy_update_bits(pbase + XSP_U2PHYDTM1,
P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND,
P2D_RG_VBUSVALID | P2D_RG_AVALID);
dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index);
}
@ -228,16 +204,12 @@ static void u2_phy_instance_power_off(struct mtk_xsphy *xsphy,
{
void __iomem *pbase = inst->port_base;
u32 index = inst->index;
u32 tmp;
tmp = readl(pbase + XSP_USBPHYACR6);
tmp &= ~P2A6_RG_OTG_VBUSCMP_EN;
writel(tmp, pbase + XSP_USBPHYACR6);
mtk_phy_clear_bits(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN);
tmp = readl(pbase + XSP_U2PHYDTM1);
tmp &= ~(P2D_RG_VBUSVALID | P2D_RG_AVALID);
tmp |= P2D_RG_SESSEND;
writel(tmp, pbase + XSP_U2PHYDTM1);
mtk_phy_update_bits(pbase + XSP_U2PHYDTM1,
P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND,
P2D_RG_SESSEND);
dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index);
}
@ -306,63 +278,43 @@ static void u2_phy_props_set(struct mtk_xsphy *xsphy,
struct xsphy_instance *inst)
{
void __iomem *pbase = inst->port_base;
u32 tmp;
if (inst->efuse_intr) {
tmp = readl(pbase + XSP_USBPHYACR1);
tmp &= ~P2A1_RG_INTR_CAL;
tmp |= P2A1_RG_INTR_CAL_VAL(inst->efuse_intr);
writel(tmp, pbase + XSP_USBPHYACR1);
}
if (inst->efuse_intr)
mtk_phy_update_bits(pbase + XSP_USBPHYACR1, P2A1_RG_INTR_CAL,
P2A1_RG_INTR_CAL_VAL(inst->efuse_intr));
if (inst->eye_src) {
tmp = readl(pbase + XSP_USBPHYACR5);
tmp &= ~P2A5_RG_HSTX_SRCTRL;
tmp |= P2A5_RG_HSTX_SRCTRL_VAL(inst->eye_src);
writel(tmp, pbase + XSP_USBPHYACR5);
}
if (inst->eye_src)
mtk_phy_update_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL,
P2A5_RG_HSTX_SRCTRL_VAL(inst->eye_src));
if (inst->eye_vrt) {
tmp = readl(pbase + XSP_USBPHYACR1);
tmp &= ~P2A1_RG_VRT_SEL;
tmp |= P2A1_RG_VRT_SEL_VAL(inst->eye_vrt);
writel(tmp, pbase + XSP_USBPHYACR1);
}
if (inst->eye_vrt)
mtk_phy_update_bits(pbase + XSP_USBPHYACR1, P2A1_RG_VRT_SEL,
P2A1_RG_VRT_SEL_VAL(inst->eye_vrt));
if (inst->eye_term) {
tmp = readl(pbase + XSP_USBPHYACR1);
tmp &= ~P2A1_RG_TERM_SEL;
tmp |= P2A1_RG_TERM_SEL_VAL(inst->eye_term);
writel(tmp, pbase + XSP_USBPHYACR1);
}
if (inst->eye_term)
mtk_phy_update_bits(pbase + XSP_USBPHYACR1, P2A1_RG_TERM_SEL,
P2A1_RG_TERM_SEL_VAL(inst->eye_term));
}
static void u3_phy_props_set(struct mtk_xsphy *xsphy,
struct xsphy_instance *inst)
{
void __iomem *pbase = inst->port_base;
u32 tmp;
if (inst->efuse_intr) {
tmp = readl(xsphy->glb_base + SSPXTP_PHYA_GLB_00);
tmp &= ~RG_XTP_GLB_BIAS_INTR_CTRL;
tmp |= RG_XTP_GLB_BIAS_INTR_CTRL_VAL(inst->efuse_intr);
writel(tmp, xsphy->glb_base + SSPXTP_PHYA_GLB_00);
}
if (inst->efuse_intr)
mtk_phy_update_bits(xsphy->glb_base + SSPXTP_PHYA_GLB_00,
RG_XTP_GLB_BIAS_INTR_CTRL,
RG_XTP_GLB_BIAS_INTR_CTRL_VAL(inst->efuse_intr));
if (inst->efuse_tx_imp) {
tmp = readl(pbase + SSPXTP_PHYA_LN_04);
tmp &= ~RG_XTP_LN0_TX_IMPSEL;
tmp |= RG_XTP_LN0_TX_IMPSEL_VAL(inst->efuse_tx_imp);
writel(tmp, pbase + SSPXTP_PHYA_LN_04);
}
if (inst->efuse_tx_imp)
mtk_phy_update_bits(pbase + SSPXTP_PHYA_LN_04,
RG_XTP_LN0_TX_IMPSEL,
RG_XTP_LN0_TX_IMPSEL_VAL(inst->efuse_tx_imp));
if (inst->efuse_rx_imp) {
tmp = readl(pbase + SSPXTP_PHYA_LN_14);
tmp &= ~RG_XTP_LN0_RX_IMPSEL;
tmp |= RG_XTP_LN0_RX_IMPSEL_VAL(inst->efuse_rx_imp);
writel(tmp, pbase + SSPXTP_PHYA_LN_14);
}
if (inst->efuse_rx_imp)
mtk_phy_update_bits(pbase + SSPXTP_PHYA_LN_14,
RG_XTP_LN0_RX_IMPSEL,
RG_XTP_LN0_RX_IMPSEL_VAL(inst->efuse_rx_imp));
}
static int mtk_phy_init(struct phy *phy)

View File

@ -11,3 +11,11 @@ config PHY_SPARX5_SERDES
depends on HAS_IOMEM
help
Enable this for support of the 10G/25G SerDes on Microchip Sparx5.
config PHY_LAN966X_SERDES
tristate "SerDes PHY driver for Microchip LAN966X"
select GENERIC_PHY
depends on OF
depends on MFD_SYSCON
help
Enable this for supporting SerDes muxing with Microchip LAN966X

View File

@ -4,3 +4,4 @@
#
obj-$(CONFIG_PHY_SPARX5_SERDES) := sparx5_serdes.o
obj-$(CONFIG_PHY_LAN966X_SERDES) := lan966x_serdes.o

View File

@ -0,0 +1,545 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <dt-bindings/phy/phy-lan966x-serdes.h>
#include "lan966x_serdes_regs.h"
#define PLL_CONF_MASK GENMASK(4, 3)
#define PLL_CONF_25MHZ 0
#define PLL_CONF_125MHZ 1
#define PLL_CONF_SERDES_125MHZ 2
#define PLL_CONF_BYPASS 3
#define lan_offset_(id, tinst, tcnt, \
gbase, ginst, gcnt, gwidth, \
raddr, rinst, rcnt, rwidth) \
(gbase + ((ginst) * gwidth) + raddr + ((rinst) * rwidth))
#define lan_offset(...) lan_offset_(__VA_ARGS__)
#define lan_rmw(val, mask, reg, off) \
lan_rmw_(val, mask, reg, lan_offset(off))
#define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \
.idx = _idx, \
.port = _port, \
.mode = _mode, \
.submode = _submode, \
.mask = _mask, \
.mux = _mux, \
}
#define SERDES_MUX_GMII(i, p, m, c) \
SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_GMII, m, c)
#define SERDES_MUX_SGMII(i, p, m, c) \
SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_SGMII, m, c)
#define SERDES_MUX_QSGMII(i, p, m, c) \
SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_QSGMII, m, c)
#define SERDES_MUX_RGMII(i, p, m, c) \
SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII, m, c)
static void lan_rmw_(u32 val, u32 mask, void __iomem *mem, u32 offset)
{
u32 v;
v = readl(mem + offset);
v = (v & ~mask) | (val & mask);
writel(v, mem + offset);
}
struct serdes_mux {
u8 idx;
u8 port;
enum phy_mode mode;
int submode;
u32 mask;
u32 mux;
};
static const struct serdes_mux lan966x_serdes_muxes[] = {
SERDES_MUX_QSGMII(SERDES6G(1), 0, HSIO_HW_CFG_QSGMII_ENA,
HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))),
SERDES_MUX_QSGMII(SERDES6G(1), 1, HSIO_HW_CFG_QSGMII_ENA,
HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))),
SERDES_MUX_QSGMII(SERDES6G(1), 2, HSIO_HW_CFG_QSGMII_ENA,
HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))),
SERDES_MUX_QSGMII(SERDES6G(1), 3, HSIO_HW_CFG_QSGMII_ENA,
HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))),
SERDES_MUX_QSGMII(SERDES6G(2), 4, HSIO_HW_CFG_QSGMII_ENA,
HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))),
SERDES_MUX_QSGMII(SERDES6G(2), 5, HSIO_HW_CFG_QSGMII_ENA,
HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))),
SERDES_MUX_QSGMII(SERDES6G(2), 6, HSIO_HW_CFG_QSGMII_ENA,
HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))),
SERDES_MUX_QSGMII(SERDES6G(2), 7, HSIO_HW_CFG_QSGMII_ENA,
HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))),
SERDES_MUX_GMII(CU(0), 0, HSIO_HW_CFG_GMII_ENA,
HSIO_HW_CFG_GMII_ENA_SET(BIT(0))),
SERDES_MUX_GMII(CU(1), 1, HSIO_HW_CFG_GMII_ENA,
HSIO_HW_CFG_GMII_ENA_SET(BIT(1))),
SERDES_MUX_SGMII(SERDES6G(0), 0, HSIO_HW_CFG_SD6G_0_CFG, 0),
SERDES_MUX_SGMII(SERDES6G(1), 1, HSIO_HW_CFG_SD6G_1_CFG, 0),
SERDES_MUX_SGMII(SERDES6G(0), 2, HSIO_HW_CFG_SD6G_0_CFG,
HSIO_HW_CFG_SD6G_0_CFG_SET(1)),
SERDES_MUX_SGMII(SERDES6G(1), 3, HSIO_HW_CFG_SD6G_1_CFG,
HSIO_HW_CFG_SD6G_1_CFG_SET(1)),
SERDES_MUX_RGMII(RGMII(0), 2, HSIO_HW_CFG_RGMII_0_CFG |
HSIO_HW_CFG_RGMII_ENA,
HSIO_HW_CFG_RGMII_0_CFG_SET(BIT(0)) |
HSIO_HW_CFG_RGMII_ENA_SET(BIT(0))),
SERDES_MUX_RGMII(RGMII(1), 3, HSIO_HW_CFG_RGMII_1_CFG |
HSIO_HW_CFG_RGMII_ENA,
HSIO_HW_CFG_RGMII_1_CFG_SET(BIT(0)) |
HSIO_HW_CFG_RGMII_ENA_SET(BIT(1))),
SERDES_MUX_RGMII(RGMII(0), 5, HSIO_HW_CFG_RGMII_0_CFG |
HSIO_HW_CFG_RGMII_ENA,
HSIO_HW_CFG_RGMII_0_CFG_SET(BIT(0)) |
HSIO_HW_CFG_RGMII_ENA_SET(BIT(0))),
SERDES_MUX_RGMII(RGMII(1), 6, HSIO_HW_CFG_RGMII_1_CFG |
HSIO_HW_CFG_RGMII_ENA,
HSIO_HW_CFG_RGMII_1_CFG_SET(BIT(0)) |
HSIO_HW_CFG_RGMII_ENA_SET(BIT(1))),
};
struct serdes_ctrl {
void __iomem *regs;
struct device *dev;
struct phy *phys[SERDES_MAX];
int ref125;
};
struct serdes_macro {
u8 idx;
int port;
struct serdes_ctrl *ctrl;
int speed;
phy_interface_t mode;
};
enum lan966x_sd6g40_mode {
LAN966X_SD6G40_MODE_QSGMII,
LAN966X_SD6G40_MODE_SGMII,
};
enum lan966x_sd6g40_ltx2rx {
LAN966X_SD6G40_TX2RX_LOOP_NONE,
LAN966X_SD6G40_LTX2RX
};
struct lan966x_sd6g40_setup_args {
enum lan966x_sd6g40_mode mode;
enum lan966x_sd6g40_ltx2rx tx2rx_loop;
bool txinvert;
bool rxinvert;
bool refclk125M;
bool mute;
};
struct lan966x_sd6g40_mode_args {
enum lan966x_sd6g40_mode mode;
u8 lane_10bit_sel;
u8 mpll_multiplier;
u8 ref_clkdiv2;
u8 tx_rate;
u8 rx_rate;
};
struct lan966x_sd6g40_setup {
u8 rx_term_en;
u8 lane_10bit_sel;
u8 tx_invert;
u8 rx_invert;
u8 mpll_multiplier;
u8 lane_loopbk_en;
u8 ref_clkdiv2;
u8 tx_rate;
u8 rx_rate;
};
static int lan966x_sd6g40_reg_cfg(struct serdes_macro *macro,
struct lan966x_sd6g40_setup *res_struct,
u32 idx)
{
u32 value;
/* Note: SerDes HSIO is configured in 1G_LAN mode */
lan_rmw(HSIO_SD_CFG_LANE_10BIT_SEL_SET(res_struct->lane_10bit_sel) |
HSIO_SD_CFG_RX_RATE_SET(res_struct->rx_rate) |
HSIO_SD_CFG_TX_RATE_SET(res_struct->tx_rate) |
HSIO_SD_CFG_TX_INVERT_SET(res_struct->tx_invert) |
HSIO_SD_CFG_RX_INVERT_SET(res_struct->rx_invert) |
HSIO_SD_CFG_LANE_LOOPBK_EN_SET(res_struct->lane_loopbk_en) |
HSIO_SD_CFG_RX_RESET_SET(0) |
HSIO_SD_CFG_TX_RESET_SET(0),
HSIO_SD_CFG_LANE_10BIT_SEL |
HSIO_SD_CFG_RX_RATE |
HSIO_SD_CFG_TX_RATE |
HSIO_SD_CFG_TX_INVERT |
HSIO_SD_CFG_RX_INVERT |
HSIO_SD_CFG_LANE_LOOPBK_EN |
HSIO_SD_CFG_RX_RESET |
HSIO_SD_CFG_TX_RESET,
macro->ctrl->regs, HSIO_SD_CFG(idx));
lan_rmw(HSIO_MPLL_CFG_MPLL_MULTIPLIER_SET(res_struct->mpll_multiplier) |
HSIO_MPLL_CFG_REF_CLKDIV2_SET(res_struct->ref_clkdiv2),
HSIO_MPLL_CFG_MPLL_MULTIPLIER |
HSIO_MPLL_CFG_REF_CLKDIV2,
macro->ctrl->regs, HSIO_MPLL_CFG(idx));
lan_rmw(HSIO_SD_CFG_RX_TERM_EN_SET(res_struct->rx_term_en),
HSIO_SD_CFG_RX_TERM_EN,
macro->ctrl->regs, HSIO_SD_CFG(idx));
lan_rmw(HSIO_MPLL_CFG_REF_SSP_EN_SET(1),
HSIO_MPLL_CFG_REF_SSP_EN,
macro->ctrl->regs, HSIO_MPLL_CFG(idx));
usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
lan_rmw(HSIO_SD_CFG_PHY_RESET_SET(0),
HSIO_SD_CFG_PHY_RESET,
macro->ctrl->regs, HSIO_SD_CFG(idx));
usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
lan_rmw(HSIO_MPLL_CFG_MPLL_EN_SET(1),
HSIO_MPLL_CFG_MPLL_EN,
macro->ctrl->regs, HSIO_MPLL_CFG(idx));
usleep_range(7 * USEC_PER_MSEC, 8 * USEC_PER_MSEC);
value = readl(macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx)));
value = HSIO_SD_STAT_MPLL_STATE_GET(value);
if (value != 0x1) {
dev_err(macro->ctrl->dev,
"Unexpected sd_sd_stat[%u] mpll_state was 0x1 but is 0x%x\n",
idx, value);
return -EIO;
}
lan_rmw(HSIO_SD_CFG_TX_CM_EN_SET(1),
HSIO_SD_CFG_TX_CM_EN,
macro->ctrl->regs, HSIO_SD_CFG(idx));
usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
value = readl(macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx)));
value = HSIO_SD_STAT_TX_CM_STATE_GET(value);
if (value != 0x1) {
dev_err(macro->ctrl->dev,
"Unexpected sd_sd_stat[%u] tx_cm_state was 0x1 but is 0x%x\n",
idx, value);
return -EIO;
}
lan_rmw(HSIO_SD_CFG_RX_PLL_EN_SET(1) |
HSIO_SD_CFG_TX_EN_SET(1),
HSIO_SD_CFG_RX_PLL_EN |
HSIO_SD_CFG_TX_EN,
macro->ctrl->regs, HSIO_SD_CFG(idx));
usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
/* Waiting for serdes 0 rx DPLL to lock... */
value = readl(macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx)));
value = HSIO_SD_STAT_RX_PLL_STATE_GET(value);
if (value != 0x1) {
dev_err(macro->ctrl->dev,
"Unexpected sd_sd_stat[%u] rx_pll_state was 0x1 but is 0x%x\n",
idx, value);
return -EIO;
}
/* Waiting for serdes 0 tx operational... */
value = readl(macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx)));
value = HSIO_SD_STAT_TX_STATE_GET(value);
if (value != 0x1) {
dev_err(macro->ctrl->dev,
"Unexpected sd_sd_stat[%u] tx_state was 0x1 but is 0x%x\n",
idx, value);
return -EIO;
}
lan_rmw(HSIO_SD_CFG_TX_DATA_EN_SET(1) |
HSIO_SD_CFG_RX_DATA_EN_SET(1),
HSIO_SD_CFG_TX_DATA_EN |
HSIO_SD_CFG_RX_DATA_EN,
macro->ctrl->regs, HSIO_SD_CFG(idx));
return 0;
}
static int lan966x_sd6g40_get_conf_from_mode(struct serdes_macro *macro,
enum lan966x_sd6g40_mode f_mode,
bool ref125M,
struct lan966x_sd6g40_mode_args *ret_val)
{
switch (f_mode) {
case LAN966X_SD6G40_MODE_QSGMII:
ret_val->lane_10bit_sel = 0;
if (ref125M) {
ret_val->mpll_multiplier = 40;
ret_val->ref_clkdiv2 = 0x1;
ret_val->tx_rate = 0x0;
ret_val->rx_rate = 0x0;
} else {
ret_val->mpll_multiplier = 100;
ret_val->ref_clkdiv2 = 0x0;
ret_val->tx_rate = 0x0;
ret_val->rx_rate = 0x0;
}
break;
case LAN966X_SD6G40_MODE_SGMII:
ret_val->lane_10bit_sel = 1;
if (ref125M) {
ret_val->mpll_multiplier = macro->speed == SPEED_2500 ? 50 : 40;
ret_val->ref_clkdiv2 = 0x1;
ret_val->tx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2;
ret_val->rx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2;
} else {
ret_val->mpll_multiplier = macro->speed == SPEED_2500 ? 125 : 100;
ret_val->ref_clkdiv2 = 0x0;
ret_val->tx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2;
ret_val->rx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2;
}
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int lan966x_calc_sd6g40_setup_lane(struct serdes_macro *macro,
struct lan966x_sd6g40_setup_args config,
struct lan966x_sd6g40_setup *ret_val)
{
struct lan966x_sd6g40_mode_args sd6g40_mode;
struct lan966x_sd6g40_mode_args *mode_args = &sd6g40_mode;
int ret;
ret = lan966x_sd6g40_get_conf_from_mode(macro, config.mode,
config.refclk125M, mode_args);
if (ret)
return ret;
ret_val->lane_10bit_sel = mode_args->lane_10bit_sel;
ret_val->rx_rate = mode_args->rx_rate;
ret_val->tx_rate = mode_args->tx_rate;
ret_val->mpll_multiplier = mode_args->mpll_multiplier;
ret_val->ref_clkdiv2 = mode_args->ref_clkdiv2;
ret_val->rx_term_en = 0;
if (config.tx2rx_loop == LAN966X_SD6G40_LTX2RX)
ret_val->lane_loopbk_en = 1;
else
ret_val->lane_loopbk_en = 0;
ret_val->tx_invert = !!config.txinvert;
ret_val->rx_invert = !!config.rxinvert;
return 0;
}
static int lan966x_sd6g40_setup_lane(struct serdes_macro *macro,
struct lan966x_sd6g40_setup_args config,
u32 idx)
{
struct lan966x_sd6g40_setup calc_results = {};
int ret;
ret = lan966x_calc_sd6g40_setup_lane(macro, config, &calc_results);
if (ret)
return ret;
return lan966x_sd6g40_reg_cfg(macro, &calc_results, idx);
}
static int lan966x_sd6g40_setup(struct serdes_macro *macro, u32 idx, int mode)
{
struct lan966x_sd6g40_setup_args conf = {};
conf.refclk125M = macro->ctrl->ref125;
if (mode == PHY_INTERFACE_MODE_QSGMII)
conf.mode = LAN966X_SD6G40_MODE_QSGMII;
else
conf.mode = LAN966X_SD6G40_MODE_SGMII;
return lan966x_sd6g40_setup_lane(macro, conf, idx);
}
static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
struct serdes_macro *macro = phy_get_drvdata(phy);
unsigned int i;
int val;
/* As of now only PHY_MODE_ETHERNET is supported */
if (mode != PHY_MODE_ETHERNET)
return -EOPNOTSUPP;
if (submode == PHY_INTERFACE_MODE_2500BASEX)
macro->speed = SPEED_2500;
else
macro->speed = SPEED_1000;
if (submode == PHY_INTERFACE_MODE_1000BASEX ||
submode == PHY_INTERFACE_MODE_2500BASEX)
submode = PHY_INTERFACE_MODE_SGMII;
for (i = 0; i < ARRAY_SIZE(lan966x_serdes_muxes); i++) {
if (macro->idx != lan966x_serdes_muxes[i].idx ||
mode != lan966x_serdes_muxes[i].mode ||
submode != lan966x_serdes_muxes[i].submode ||
macro->port != lan966x_serdes_muxes[i].port)
continue;
val = readl(macro->ctrl->regs + lan_offset(HSIO_HW_CFG));
val |= lan966x_serdes_muxes[i].mux;
lan_rmw(val, lan966x_serdes_muxes[i].mask,
macro->ctrl->regs, HSIO_HW_CFG);
macro->mode = lan966x_serdes_muxes[i].submode;
if (macro->idx < CU_MAX)
return 0;
if (macro->idx < SERDES6G_MAX)
return lan966x_sd6g40_setup(macro,
macro->idx - (CU_MAX + 1),
macro->mode);
if (macro->idx < RGMII_MAX)
return 0;
return -EOPNOTSUPP;
}
return -EINVAL;
}
static const struct phy_ops serdes_ops = {
.set_mode = serdes_set_mode,
.owner = THIS_MODULE,
};
static struct phy *serdes_simple_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct serdes_ctrl *ctrl = dev_get_drvdata(dev);
unsigned int port, idx, i;
if (args->args_count != 2)
return ERR_PTR(-EINVAL);
port = args->args[0];
idx = args->args[1];
for (i = 0; i < SERDES_MAX; i++) {
struct serdes_macro *macro = phy_get_drvdata(ctrl->phys[i]);
if (idx != macro->idx)
continue;
macro->port = port;
return ctrl->phys[i];
}
return ERR_PTR(-ENODEV);
}
static int serdes_phy_create(struct serdes_ctrl *ctrl, u8 idx, struct phy **phy)
{
struct serdes_macro *macro;
*phy = devm_phy_create(ctrl->dev, NULL, &serdes_ops);
if (IS_ERR(*phy))
return PTR_ERR(*phy);
macro = devm_kzalloc(ctrl->dev, sizeof(*macro), GFP_KERNEL);
if (!macro)
return -ENOMEM;
macro->idx = idx;
macro->ctrl = ctrl;
macro->port = -1;
phy_set_drvdata(*phy, macro);
return 0;
}
static int serdes_probe(struct platform_device *pdev)
{
struct phy_provider *provider;
struct serdes_ctrl *ctrl;
void __iomem *hw_stat;
unsigned int i;
u32 val;
int ret;
ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
return -ENOMEM;
ctrl->dev = &pdev->dev;
ctrl->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(ctrl->regs))
return PTR_ERR(ctrl->regs);
hw_stat = devm_platform_get_and_ioremap_resource(pdev, 1, NULL);
if (IS_ERR(hw_stat))
return PTR_ERR(hw_stat);
for (i = 0; i < SERDES_MAX; i++) {
ret = serdes_phy_create(ctrl, i, &ctrl->phys[i]);
if (ret)
return ret;
}
val = readl(hw_stat);
val = FIELD_GET(PLL_CONF_MASK, val);
ctrl->ref125 = (val == PLL_CONF_125MHZ ||
val == PLL_CONF_SERDES_125MHZ);
dev_set_drvdata(&pdev->dev, ctrl);
provider = devm_of_phy_provider_register(ctrl->dev,
serdes_simple_xlate);
return PTR_ERR_OR_ZERO(provider);
}
static const struct of_device_id serdes_ids[] = {
{ .compatible = "microchip,lan966x-serdes", },
{},
};
MODULE_DEVICE_TABLE(of, serdes_ids);
static struct platform_driver mscc_lan966x_serdes = {
.probe = serdes_probe,
.driver = {
.name = "microchip,lan966x-serdes",
.of_match_table = of_match_ptr(serdes_ids),
},
};
module_platform_driver(mscc_lan966x_serdes);
MODULE_DESCRIPTION("Microchip lan966x switch serdes driver");
MODULE_AUTHOR("Horatiu Vultur <horatiu.vultur@microchip.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,209 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _LAN966X_SERDES_REGS_H_
#define _LAN966X_SERDES_REGS_H_
#include <linux/bitfield.h>
#include <linux/types.h>
#include <linux/bug.h>
enum lan966x_target {
TARGET_HSIO = 32,
NUM_TARGETS = 66
};
#define __REG(...) __VA_ARGS__
/* HSIO:SD:SD_CFG */
#define HSIO_SD_CFG(g) __REG(TARGET_HSIO, 0, 1, 8, g, 3, 32, 0, 0, 1, 4)
#define HSIO_SD_CFG_PHY_RESET BIT(27)
#define HSIO_SD_CFG_PHY_RESET_SET(x)\
FIELD_PREP(HSIO_SD_CFG_PHY_RESET, x)
#define HSIO_SD_CFG_PHY_RESET_GET(x)\
FIELD_GET(HSIO_SD_CFG_PHY_RESET, x)
#define HSIO_SD_CFG_TX_RESET BIT(18)
#define HSIO_SD_CFG_TX_RESET_SET(x)\
FIELD_PREP(HSIO_SD_CFG_TX_RESET, x)
#define HSIO_SD_CFG_TX_RESET_GET(x)\
FIELD_GET(HSIO_SD_CFG_TX_RESET, x)
#define HSIO_SD_CFG_TX_RATE GENMASK(17, 16)
#define HSIO_SD_CFG_TX_RATE_SET(x)\
FIELD_PREP(HSIO_SD_CFG_TX_RATE, x)
#define HSIO_SD_CFG_TX_RATE_GET(x)\
FIELD_GET(HSIO_SD_CFG_TX_RATE, x)
#define HSIO_SD_CFG_TX_INVERT BIT(15)
#define HSIO_SD_CFG_TX_INVERT_SET(x)\
FIELD_PREP(HSIO_SD_CFG_TX_INVERT, x)
#define HSIO_SD_CFG_TX_INVERT_GET(x)\
FIELD_GET(HSIO_SD_CFG_TX_INVERT, x)
#define HSIO_SD_CFG_TX_EN BIT(14)
#define HSIO_SD_CFG_TX_EN_SET(x)\
FIELD_PREP(HSIO_SD_CFG_TX_EN, x)
#define HSIO_SD_CFG_TX_EN_GET(x)\
FIELD_GET(HSIO_SD_CFG_TX_EN, x)
#define HSIO_SD_CFG_TX_DATA_EN BIT(12)
#define HSIO_SD_CFG_TX_DATA_EN_SET(x)\
FIELD_PREP(HSIO_SD_CFG_TX_DATA_EN, x)
#define HSIO_SD_CFG_TX_DATA_EN_GET(x)\
FIELD_GET(HSIO_SD_CFG_TX_DATA_EN, x)
#define HSIO_SD_CFG_TX_CM_EN BIT(11)
#define HSIO_SD_CFG_TX_CM_EN_SET(x)\
FIELD_PREP(HSIO_SD_CFG_TX_CM_EN, x)
#define HSIO_SD_CFG_TX_CM_EN_GET(x)\
FIELD_GET(HSIO_SD_CFG_TX_CM_EN, x)
#define HSIO_SD_CFG_LANE_10BIT_SEL BIT(10)
#define HSIO_SD_CFG_LANE_10BIT_SEL_SET(x)\
FIELD_PREP(HSIO_SD_CFG_LANE_10BIT_SEL, x)
#define HSIO_SD_CFG_LANE_10BIT_SEL_GET(x)\
FIELD_GET(HSIO_SD_CFG_LANE_10BIT_SEL, x)
#define HSIO_SD_CFG_RX_TERM_EN BIT(9)
#define HSIO_SD_CFG_RX_TERM_EN_SET(x)\
FIELD_PREP(HSIO_SD_CFG_RX_TERM_EN, x)
#define HSIO_SD_CFG_RX_TERM_EN_GET(x)\
FIELD_GET(HSIO_SD_CFG_RX_TERM_EN, x)
#define HSIO_SD_CFG_RX_RESET BIT(8)
#define HSIO_SD_CFG_RX_RESET_SET(x)\
FIELD_PREP(HSIO_SD_CFG_RX_RESET, x)
#define HSIO_SD_CFG_RX_RESET_GET(x)\
FIELD_GET(HSIO_SD_CFG_RX_RESET, x)
#define HSIO_SD_CFG_RX_RATE GENMASK(7, 6)
#define HSIO_SD_CFG_RX_RATE_SET(x)\
FIELD_PREP(HSIO_SD_CFG_RX_RATE, x)
#define HSIO_SD_CFG_RX_RATE_GET(x)\
FIELD_GET(HSIO_SD_CFG_RX_RATE, x)
#define HSIO_SD_CFG_RX_PLL_EN BIT(5)
#define HSIO_SD_CFG_RX_PLL_EN_SET(x)\
FIELD_PREP(HSIO_SD_CFG_RX_PLL_EN, x)
#define HSIO_SD_CFG_RX_PLL_EN_GET(x)\
FIELD_GET(HSIO_SD_CFG_RX_PLL_EN, x)
#define HSIO_SD_CFG_RX_INVERT BIT(3)
#define HSIO_SD_CFG_RX_INVERT_SET(x)\
FIELD_PREP(HSIO_SD_CFG_RX_INVERT, x)
#define HSIO_SD_CFG_RX_INVERT_GET(x)\
FIELD_GET(HSIO_SD_CFG_RX_INVERT, x)
#define HSIO_SD_CFG_RX_DATA_EN BIT(2)
#define HSIO_SD_CFG_RX_DATA_EN_SET(x)\
FIELD_PREP(HSIO_SD_CFG_RX_DATA_EN, x)
#define HSIO_SD_CFG_RX_DATA_EN_GET(x)\
FIELD_GET(HSIO_SD_CFG_RX_DATA_EN, x)
#define HSIO_SD_CFG_LANE_LOOPBK_EN BIT(0)
#define HSIO_SD_CFG_LANE_LOOPBK_EN_SET(x)\
FIELD_PREP(HSIO_SD_CFG_LANE_LOOPBK_EN, x)
#define HSIO_SD_CFG_LANE_LOOPBK_EN_GET(x)\
FIELD_GET(HSIO_SD_CFG_LANE_LOOPBK_EN, x)
/* HSIO:SD:MPLL_CFG */
#define HSIO_MPLL_CFG(g) __REG(TARGET_HSIO, 0, 1, 8, g, 3, 32, 8, 0, 1, 4)
#define HSIO_MPLL_CFG_REF_SSP_EN BIT(18)
#define HSIO_MPLL_CFG_REF_SSP_EN_SET(x)\
FIELD_PREP(HSIO_MPLL_CFG_REF_SSP_EN, x)
#define HSIO_MPLL_CFG_REF_SSP_EN_GET(x)\
FIELD_GET(HSIO_MPLL_CFG_REF_SSP_EN, x)
#define HSIO_MPLL_CFG_REF_CLKDIV2 BIT(17)
#define HSIO_MPLL_CFG_REF_CLKDIV2_SET(x)\
FIELD_PREP(HSIO_MPLL_CFG_REF_CLKDIV2, x)
#define HSIO_MPLL_CFG_REF_CLKDIV2_GET(x)\
FIELD_GET(HSIO_MPLL_CFG_REF_CLKDIV2, x)
#define HSIO_MPLL_CFG_MPLL_EN BIT(16)
#define HSIO_MPLL_CFG_MPLL_EN_SET(x)\
FIELD_PREP(HSIO_MPLL_CFG_MPLL_EN, x)
#define HSIO_MPLL_CFG_MPLL_EN_GET(x)\
FIELD_GET(HSIO_MPLL_CFG_MPLL_EN, x)
#define HSIO_MPLL_CFG_MPLL_MULTIPLIER GENMASK(6, 0)
#define HSIO_MPLL_CFG_MPLL_MULTIPLIER_SET(x)\
FIELD_PREP(HSIO_MPLL_CFG_MPLL_MULTIPLIER, x)
#define HSIO_MPLL_CFG_MPLL_MULTIPLIER_GET(x)\
FIELD_GET(HSIO_MPLL_CFG_MPLL_MULTIPLIER, x)
/* HSIO:SD:SD_STAT */
#define HSIO_SD_STAT(g) __REG(TARGET_HSIO, 0, 1, 8, g, 3, 32, 12, 0, 1, 4)
#define HSIO_SD_STAT_MPLL_STATE BIT(6)
#define HSIO_SD_STAT_MPLL_STATE_SET(x)\
FIELD_PREP(HSIO_SD_STAT_MPLL_STATE, x)
#define HSIO_SD_STAT_MPLL_STATE_GET(x)\
FIELD_GET(HSIO_SD_STAT_MPLL_STATE, x)
#define HSIO_SD_STAT_TX_STATE BIT(5)
#define HSIO_SD_STAT_TX_STATE_SET(x)\
FIELD_PREP(HSIO_SD_STAT_TX_STATE, x)
#define HSIO_SD_STAT_TX_STATE_GET(x)\
FIELD_GET(HSIO_SD_STAT_TX_STATE, x)
#define HSIO_SD_STAT_TX_CM_STATE BIT(2)
#define HSIO_SD_STAT_TX_CM_STATE_SET(x)\
FIELD_PREP(HSIO_SD_STAT_TX_CM_STATE, x)
#define HSIO_SD_STAT_TX_CM_STATE_GET(x)\
FIELD_GET(HSIO_SD_STAT_TX_CM_STATE, x)
#define HSIO_SD_STAT_RX_PLL_STATE BIT(0)
#define HSIO_SD_STAT_RX_PLL_STATE_SET(x)\
FIELD_PREP(HSIO_SD_STAT_RX_PLL_STATE, x)
#define HSIO_SD_STAT_RX_PLL_STATE_GET(x)\
FIELD_GET(HSIO_SD_STAT_RX_PLL_STATE, x)
/* HSIO:HW_CFGSTAT:HW_CFG */
#define HSIO_HW_CFG __REG(TARGET_HSIO, 0, 1, 104, 0, 1, 52, 0, 0, 1, 4)
#define HSIO_HW_CFG_RGMII_1_CFG BIT(15)
#define HSIO_HW_CFG_RGMII_1_CFG_SET(x)\
(((x) << 15) & GENMASK(15, 15))
#define HSIO_HW_CFG_RGMII_1_CFG_GET(x)\
FIELD_GET(HSIO_HW_CFG_RGMII_1_CFG, x)
#define HSIO_HW_CFG_RGMII_0_CFG BIT(14)
#define HSIO_HW_CFG_RGMII_0_CFG_SET(x)\
(((x) << 14) & GENMASK(14, 14))
#define HSIO_HW_CFG_RGMII_0_CFG_GET(x)\
FIELD_GET(HSIO_HW_CFG_RGMII_0_CFG, x)
#define HSIO_HW_CFG_RGMII_ENA GENMASK(13, 12)
#define HSIO_HW_CFG_RGMII_ENA_SET(x)\
(((x) << 12) & GENMASK(13, 12))
#define HSIO_HW_CFG_RGMII_ENA_GET(x)\
FIELD_GET(HSIO_HW_CFG_RGMII_ENA, x)
#define HSIO_HW_CFG_SD6G_0_CFG BIT(11)
#define HSIO_HW_CFG_SD6G_0_CFG_SET(x)\
(((x) << 11) & GENMASK(11, 11))
#define HSIO_HW_CFG_SD6G_0_CFG_GET(x)\
FIELD_GET(HSIO_HW_CFG_SD6G_0_CFG, x)
#define HSIO_HW_CFG_SD6G_1_CFG BIT(10)
#define HSIO_HW_CFG_SD6G_1_CFG_SET(x)\
(((x) << 10) & GENMASK(10, 10))
#define HSIO_HW_CFG_SD6G_1_CFG_GET(x)\
FIELD_GET(HSIO_HW_CFG_SD6G_1_CFG, x)
#define HSIO_HW_CFG_GMII_ENA GENMASK(9, 2)
#define HSIO_HW_CFG_GMII_ENA_SET(x)\
(((x) << 2) & GENMASK(9, 2))
#define HSIO_HW_CFG_GMII_ENA_GET(x)\
FIELD_GET(HSIO_HW_CFG_GMII_ENA, x)
#define HSIO_HW_CFG_QSGMII_ENA GENMASK(1, 0)
#define HSIO_HW_CFG_QSGMII_ENA_SET(x)\
((x) & GENMASK(1, 0))
#define HSIO_HW_CFG_QSGMII_ENA_GET(x)\
FIELD_GET(HSIO_HW_CFG_QSGMII_ENA, x)
#endif /* _LAN966X_HSIO_REGS_H_ */

View File

@ -110,14 +110,14 @@ static int can_transceiver_phy_probe(struct platform_device *pdev)
can_transceiver_phy->generic_phy = phy;
if (drvdata->flags & CAN_TRANSCEIVER_STB_PRESENT) {
standby_gpio = devm_gpiod_get(dev, "standby", GPIOD_OUT_HIGH);
standby_gpio = devm_gpiod_get_optional(dev, "standby", GPIOD_OUT_HIGH);
if (IS_ERR(standby_gpio))
return PTR_ERR(standby_gpio);
can_transceiver_phy->standby_gpio = standby_gpio;
}
if (drvdata->flags & CAN_TRANSCEIVER_EN_PRESENT) {
enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(enable_gpio))
return PTR_ERR(enable_gpio);
can_transceiver_phy->enable_gpio = enable_gpio;

View File

@ -18,6 +18,16 @@ config PHY_QCOM_APQ8064_SATA
depends on OF
select GENERIC_PHY
config PHY_QCOM_EDP
tristate "Qualcomm eDP PHY driver"
depends on ARCH_QCOM || COMPILE_TEST
depends on OF
depends on COMMON_CLK
select GENERIC_PHY
help
Enable this driver to support the Qualcomm eDP PHY found in various
Qualcomm chipsets.
config PHY_QCOM_IPQ4019_USB
tristate "Qualcomm IPQ4019 USB PHY driver"
depends on OF && (ARCH_QCOM || COMPILE_TEST)

View File

@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_ATH79_USB) += phy-ath79-usb.o
obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
obj-$(CONFIG_PHY_QCOM_EDP) += phy-qcom-edp.o
obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o
obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o

View File

@ -0,0 +1,674 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017, 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2021, Linaro Ltd.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <dt-bindings/phy/phy.h>
#include "phy-qcom-qmp.h"
/* EDP_PHY registers */
#define DP_PHY_CFG 0x0010
#define DP_PHY_CFG_1 0x0014
#define DP_PHY_PD_CTL 0x001c
#define DP_PHY_MODE 0x0020
#define DP_PHY_AUX_CFG0 0x0024
#define DP_PHY_AUX_CFG1 0x0028
#define DP_PHY_AUX_CFG2 0x002C
#define DP_PHY_AUX_CFG3 0x0030
#define DP_PHY_AUX_CFG4 0x0034
#define DP_PHY_AUX_CFG5 0x0038
#define DP_PHY_AUX_CFG6 0x003C
#define DP_PHY_AUX_CFG7 0x0040
#define DP_PHY_AUX_CFG8 0x0044
#define DP_PHY_AUX_CFG9 0x0048
#define DP_PHY_AUX_INTERRUPT_MASK 0x0058
#define DP_PHY_VCO_DIV 0x0074
#define DP_PHY_TX0_TX1_LANE_CTL 0x007c
#define DP_PHY_TX2_TX3_LANE_CTL 0x00a0
#define DP_PHY_STATUS 0x00e0
/* LANE_TXn registers */
#define TXn_CLKBUF_ENABLE 0x0000
#define TXn_TX_EMP_POST1_LVL 0x0004
#define TXn_TX_DRV_LVL 0x0014
#define TXn_TX_DRV_LVL_OFFSET 0x0018
#define TXn_RESET_TSYNC_EN 0x001c
#define TXn_LDO_CONFIG 0x0084
#define TXn_TX_BAND 0x0028
#define TXn_RES_CODE_LANE_OFFSET_TX0 0x0044
#define TXn_RES_CODE_LANE_OFFSET_TX1 0x0048
#define TXn_TRANSCEIVER_BIAS_EN 0x0054
#define TXn_HIGHZ_DRVR_EN 0x0058
#define TXn_TX_POL_INV 0x005c
#define TXn_LANE_MODE_1 0x0064
#define TXn_TRAN_DRVR_EMP_EN 0x0078
struct qcom_edp {
struct device *dev;
struct phy *phy;
void __iomem *edp;
void __iomem *tx0;
void __iomem *tx1;
void __iomem *pll;
struct clk_hw dp_link_hw;
struct clk_hw dp_pixel_hw;
struct phy_configure_opts_dp dp_opts;
struct clk_bulk_data clks[2];
struct regulator_bulk_data supplies[2];
};
static int qcom_edp_phy_init(struct phy *phy)
{
struct qcom_edp *edp = phy_get_drvdata(phy);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(edp->supplies), edp->supplies);
if (ret)
return ret;
ret = clk_bulk_prepare_enable(ARRAY_SIZE(edp->clks), edp->clks);
if (ret)
goto out_disable_supplies;
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,
edp->edp + DP_PHY_PD_CTL);
/* Turn on BIAS current for PHY/PLL */
writel(0x17, edp->pll + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN);
writel(DP_PHY_PD_CTL_PSR_PWRDN, edp->edp + DP_PHY_PD_CTL);
msleep(20);
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,
edp->edp + DP_PHY_PD_CTL);
writel(0x00, edp->edp + DP_PHY_AUX_CFG0);
writel(0x13, edp->edp + DP_PHY_AUX_CFG1);
writel(0x24, edp->edp + DP_PHY_AUX_CFG2);
writel(0x00, edp->edp + DP_PHY_AUX_CFG3);
writel(0x0a, edp->edp + DP_PHY_AUX_CFG4);
writel(0x26, edp->edp + DP_PHY_AUX_CFG5);
writel(0x0a, edp->edp + DP_PHY_AUX_CFG6);
writel(0x03, edp->edp + DP_PHY_AUX_CFG7);
writel(0x37, edp->edp + DP_PHY_AUX_CFG8);
writel(0x03, edp->edp + DP_PHY_AUX_CFG9);
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, edp->edp + DP_PHY_AUX_INTERRUPT_MASK);
msleep(20);
return 0;
out_disable_supplies:
regulator_bulk_disable(ARRAY_SIZE(edp->supplies), edp->supplies);
return ret;
}
static int qcom_edp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
{
const struct phy_configure_opts_dp *dp_opts = &opts->dp;
struct qcom_edp *edp = phy_get_drvdata(phy);
memcpy(&edp->dp_opts, dp_opts, sizeof(*dp_opts));
return 0;
}
static int qcom_edp_configure_ssc(const struct qcom_edp *edp)
{
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
u32 step1;
u32 step2;
switch (dp_opts->link_rate) {
case 1620:
case 2700:
case 8100:
step1 = 0x45;
step2 = 0x06;
break;
case 5400:
step1 = 0x5c;
step2 = 0x08;
break;
default:
/* Other link rates aren't supported */
return -EINVAL;
}
writel(0x01, edp->pll + QSERDES_V4_COM_SSC_EN_CENTER);
writel(0x00, edp->pll + QSERDES_V4_COM_SSC_ADJ_PER1);
writel(0x36, edp->pll + QSERDES_V4_COM_SSC_PER1);
writel(0x01, edp->pll + QSERDES_V4_COM_SSC_PER2);
writel(step1, edp->pll + QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0);
writel(step2, edp->pll + QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0);
return 0;
}
static int qcom_edp_configure_pll(const struct qcom_edp *edp)
{
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
u32 div_frac_start2_mode0;
u32 div_frac_start3_mode0;
u32 dec_start_mode0;
u32 lock_cmp1_mode0;
u32 lock_cmp2_mode0;
u32 hsclk_sel;
switch (dp_opts->link_rate) {
case 1620:
hsclk_sel = 0x5;
dec_start_mode0 = 0x69;
div_frac_start2_mode0 = 0x80;
div_frac_start3_mode0 = 0x07;
lock_cmp1_mode0 = 0x6f;
lock_cmp2_mode0 = 0x08;
break;
case 2700:
hsclk_sel = 0x3;
dec_start_mode0 = 0x69;
div_frac_start2_mode0 = 0x80;
div_frac_start3_mode0 = 0x07;
lock_cmp1_mode0 = 0x0f;
lock_cmp2_mode0 = 0x0e;
break;
case 5400:
hsclk_sel = 0x1;
dec_start_mode0 = 0x8c;
div_frac_start2_mode0 = 0x00;
div_frac_start3_mode0 = 0x0a;
lock_cmp1_mode0 = 0x1f;
lock_cmp2_mode0 = 0x1c;
break;
case 8100:
hsclk_sel = 0x0;
dec_start_mode0 = 0x69;
div_frac_start2_mode0 = 0x80;
div_frac_start3_mode0 = 0x07;
lock_cmp1_mode0 = 0x2f;
lock_cmp2_mode0 = 0x2a;
break;
default:
/* Other link rates aren't supported */
return -EINVAL;
}
writel(0x01, edp->pll + QSERDES_V4_COM_SVS_MODE_CLK_SEL);
writel(0x0b, edp->pll + QSERDES_V4_COM_SYSCLK_EN_SEL);
writel(0x02, edp->pll + QSERDES_V4_COM_SYS_CLK_CTRL);
writel(0x0c, edp->pll + QSERDES_V4_COM_CLK_ENABLE1);
writel(0x06, edp->pll + QSERDES_V4_COM_SYSCLK_BUF_ENABLE);
writel(0x30, edp->pll + QSERDES_V4_COM_CLK_SELECT);
writel(hsclk_sel, edp->pll + QSERDES_V4_COM_HSCLK_SEL);
writel(0x0f, edp->pll + QSERDES_V4_COM_PLL_IVCO);
writel(0x08, edp->pll + QSERDES_V4_COM_LOCK_CMP_EN);
writel(0x36, edp->pll + QSERDES_V4_COM_PLL_CCTRL_MODE0);
writel(0x16, edp->pll + QSERDES_V4_COM_PLL_RCTRL_MODE0);
writel(0x06, edp->pll + QSERDES_V4_COM_CP_CTRL_MODE0);
writel(dec_start_mode0, edp->pll + QSERDES_V4_COM_DEC_START_MODE0);
writel(0x00, edp->pll + QSERDES_V4_COM_DIV_FRAC_START1_MODE0);
writel(div_frac_start2_mode0, edp->pll + QSERDES_V4_COM_DIV_FRAC_START2_MODE0);
writel(div_frac_start3_mode0, edp->pll + QSERDES_V4_COM_DIV_FRAC_START3_MODE0);
writel(0x02, edp->pll + QSERDES_V4_COM_CMN_CONFIG);
writel(0x3f, edp->pll + QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0);
writel(0x00, edp->pll + QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0);
writel(0x00, edp->pll + QSERDES_V4_COM_VCO_TUNE_MAP);
writel(lock_cmp1_mode0, edp->pll + QSERDES_V4_COM_LOCK_CMP1_MODE0);
writel(lock_cmp2_mode0, edp->pll + QSERDES_V4_COM_LOCK_CMP2_MODE0);
writel(0x0a, edp->pll + QSERDES_V4_COM_BG_TIMER);
writel(0x14, edp->pll + QSERDES_V4_COM_CORECLK_DIV_MODE0);
writel(0x00, edp->pll + QSERDES_V4_COM_VCO_TUNE_CTRL);
writel(0x17, edp->pll + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN);
writel(0x0f, edp->pll + QSERDES_V4_COM_CORE_CLK_EN);
writel(0xa0, edp->pll + QSERDES_V4_COM_VCO_TUNE1_MODE0);
writel(0x03, edp->pll + QSERDES_V4_COM_VCO_TUNE2_MODE0);
return 0;
}
static int qcom_edp_set_vco_div(const struct qcom_edp *edp)
{
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
unsigned long pixel_freq;
u32 vco_div;
switch (dp_opts->link_rate) {
case 1620:
vco_div = 0x1;
pixel_freq = 1620000000UL / 2;
break;
case 2700:
vco_div = 0x1;
pixel_freq = 2700000000UL / 2;
break;
case 5400:
vco_div = 0x2;
pixel_freq = 5400000000UL / 4;
break;
case 8100:
vco_div = 0x0;
pixel_freq = 8100000000UL / 6;
break;
default:
/* Other link rates aren't supported */
return -EINVAL;
}
writel(vco_div, edp->edp + DP_PHY_VCO_DIV);
clk_set_rate(edp->dp_link_hw.clk, dp_opts->link_rate * 100000);
clk_set_rate(edp->dp_pixel_hw.clk, pixel_freq);
return 0;
}
static int qcom_edp_phy_power_on(struct phy *phy)
{
const struct qcom_edp *edp = phy_get_drvdata(phy);
int timeout;
int ret;
u32 val;
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,
edp->edp + DP_PHY_PD_CTL);
writel(0xfc, edp->edp + DP_PHY_MODE);
timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_CMN_STATUS,
val, val & BIT(7), 5, 200);
if (timeout)
return timeout;
writel(0x01, edp->tx0 + TXn_LDO_CONFIG);
writel(0x01, edp->tx1 + TXn_LDO_CONFIG);
writel(0x00, edp->tx0 + TXn_LANE_MODE_1);
writel(0x00, edp->tx1 + TXn_LANE_MODE_1);
ret = qcom_edp_configure_ssc(edp);
if (ret)
return ret;
ret = qcom_edp_configure_pll(edp);
if (ret)
return ret;
/* TX Lane configuration */
writel(0x05, edp->edp + DP_PHY_TX0_TX1_LANE_CTL);
writel(0x05, edp->edp + DP_PHY_TX2_TX3_LANE_CTL);
/* TX-0 register configuration */
writel(0x03, edp->tx0 + TXn_TRANSCEIVER_BIAS_EN);
writel(0x0f, edp->tx0 + TXn_CLKBUF_ENABLE);
writel(0x03, edp->tx0 + TXn_RESET_TSYNC_EN);
writel(0x01, edp->tx0 + TXn_TRAN_DRVR_EMP_EN);
writel(0x04, edp->tx0 + TXn_TX_BAND);
/* TX-1 register configuration */
writel(0x03, edp->tx1 + TXn_TRANSCEIVER_BIAS_EN);
writel(0x0f, edp->tx1 + TXn_CLKBUF_ENABLE);
writel(0x03, edp->tx1 + TXn_RESET_TSYNC_EN);
writel(0x01, edp->tx1 + TXn_TRAN_DRVR_EMP_EN);
writel(0x04, edp->tx1 + TXn_TX_BAND);
ret = qcom_edp_set_vco_div(edp);
if (ret)
return ret;
writel(0x01, edp->edp + DP_PHY_CFG);
writel(0x05, edp->edp + DP_PHY_CFG);
writel(0x01, edp->edp + DP_PHY_CFG);
writel(0x09, edp->edp + DP_PHY_CFG);
writel(0x20, edp->pll + QSERDES_V4_COM_RESETSM_CNTRL);
timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_C_READY_STATUS,
val, val & BIT(0), 500, 10000);
if (timeout)
return timeout;
writel(0x19, edp->edp + DP_PHY_CFG);
writel(0x1f, edp->tx0 + TXn_HIGHZ_DRVR_EN);
writel(0x04, edp->tx0 + TXn_HIGHZ_DRVR_EN);
writel(0x00, edp->tx0 + TXn_TX_POL_INV);
writel(0x1f, edp->tx1 + TXn_HIGHZ_DRVR_EN);
writel(0x04, edp->tx1 + TXn_HIGHZ_DRVR_EN);
writel(0x00, edp->tx1 + TXn_TX_POL_INV);
writel(0x10, edp->tx0 + TXn_TX_DRV_LVL_OFFSET);
writel(0x10, edp->tx1 + TXn_TX_DRV_LVL_OFFSET);
writel(0x11, edp->tx0 + TXn_RES_CODE_LANE_OFFSET_TX0);
writel(0x11, edp->tx0 + TXn_RES_CODE_LANE_OFFSET_TX1);
writel(0x11, edp->tx1 + TXn_RES_CODE_LANE_OFFSET_TX0);
writel(0x11, edp->tx1 + TXn_RES_CODE_LANE_OFFSET_TX1);
writel(0x10, edp->tx0 + TXn_TX_EMP_POST1_LVL);
writel(0x10, edp->tx1 + TXn_TX_EMP_POST1_LVL);
writel(0x1f, edp->tx0 + TXn_TX_DRV_LVL);
writel(0x1f, edp->tx1 + TXn_TX_DRV_LVL);
writel(0x4, edp->tx0 + TXn_HIGHZ_DRVR_EN);
writel(0x3, edp->tx0 + TXn_TRANSCEIVER_BIAS_EN);
writel(0x4, edp->tx1 + TXn_HIGHZ_DRVR_EN);
writel(0x0, edp->tx1 + TXn_TRANSCEIVER_BIAS_EN);
writel(0x3, edp->edp + DP_PHY_CFG_1);
writel(0x18, edp->edp + DP_PHY_CFG);
usleep_range(100, 1000);
writel(0x19, edp->edp + DP_PHY_CFG);
return readl_poll_timeout(edp->edp + DP_PHY_STATUS,
val, val & BIT(1), 500, 10000);
}
static int qcom_edp_phy_power_off(struct phy *phy)
{
const struct qcom_edp *edp = phy_get_drvdata(phy);
writel(DP_PHY_PD_CTL_PSR_PWRDN, edp->edp + DP_PHY_PD_CTL);
return 0;
}
static int qcom_edp_phy_exit(struct phy *phy)
{
struct qcom_edp *edp = phy_get_drvdata(phy);
clk_bulk_disable_unprepare(ARRAY_SIZE(edp->clks), edp->clks);
regulator_bulk_disable(ARRAY_SIZE(edp->supplies), edp->supplies);
return 0;
}
static const struct phy_ops qcom_edp_ops = {
.init = qcom_edp_phy_init,
.configure = qcom_edp_phy_configure,
.power_on = qcom_edp_phy_power_on,
.power_off = qcom_edp_phy_power_off,
.exit = qcom_edp_phy_exit,
.owner = THIS_MODULE,
};
/*
* Embedded Display Port PLL driver block diagram for branch clocks
*
* +------------------------------+
* | EDP_VCO_CLK |
* | |
* | +-------------------+ |
* | | (EDP PLL/VCO) | |
* | +---------+---------+ |
* | v |
* | +----------+-----------+ |
* | | hsclk_divsel_clk_src | |
* | +----------+-----------+ |
* +------------------------------+
* |
* +---------<---------v------------>----------+
* | |
* +--------v----------------+ |
* | edp_phy_pll_link_clk | |
* | link_clk | |
* +--------+----------------+ |
* | |
* | |
* v v
* Input to DISPCC block |
* for link clk, crypto clk |
* and interface clock |
* |
* |
* +--------<------------+-----------------+---<---+
* | | |
* +----v---------+ +--------v-----+ +--------v------+
* | vco_divided | | vco_divided | | vco_divided |
* | _clk_src | | _clk_src | | _clk_src |
* | | | | | |
* |divsel_six | | divsel_two | | divsel_four |
* +-------+------+ +-----+--------+ +--------+------+
* | | |
* v---->----------v-------------<------v
* |
* +----------+-----------------+
* | edp_phy_pll_vco_div_clk |
* +---------+------------------+
* |
* v
* Input to DISPCC block
* for EDP pixel clock
*
*/
static int qcom_edp_dp_pixel_clk_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
switch (req->rate) {
case 1620000000UL / 2:
case 2700000000UL / 2:
/* 5.4 and 8.1 GHz are same link rate as 2.7GHz, i.e. div 4 and div 6 */
return 0;
default:
return -EINVAL;
}
}
static unsigned long
qcom_edp_dp_pixel_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
const struct qcom_edp *edp = container_of(hw, struct qcom_edp, dp_pixel_hw);
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
switch (dp_opts->link_rate) {
case 1620:
return 1620000000UL / 2;
case 2700:
return 2700000000UL / 2;
case 5400:
return 5400000000UL / 4;
case 8100:
return 8100000000UL / 6;
default:
return 0;
}
}
static const struct clk_ops qcom_edp_dp_pixel_clk_ops = {
.determine_rate = qcom_edp_dp_pixel_clk_determine_rate,
.recalc_rate = qcom_edp_dp_pixel_clk_recalc_rate,
};
static int qcom_edp_dp_link_clk_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
switch (req->rate) {
case 162000000:
case 270000000:
case 540000000:
case 810000000:
return 0;
default:
return -EINVAL;
}
}
static unsigned long
qcom_edp_dp_link_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
const struct qcom_edp *edp = container_of(hw, struct qcom_edp, dp_link_hw);
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
switch (dp_opts->link_rate) {
case 1620:
case 2700:
case 5400:
case 8100:
return dp_opts->link_rate * 100000;
default:
return 0;
}
}
static const struct clk_ops qcom_edp_dp_link_clk_ops = {
.determine_rate = qcom_edp_dp_link_clk_determine_rate,
.recalc_rate = qcom_edp_dp_link_clk_recalc_rate,
};
static int qcom_edp_clks_register(struct qcom_edp *edp, struct device_node *np)
{
struct clk_hw_onecell_data *data;
struct clk_init_data init = { };
int ret;
data = devm_kzalloc(edp->dev, struct_size(data, hws, 2), GFP_KERNEL);
if (!data)
return -ENOMEM;
init.ops = &qcom_edp_dp_link_clk_ops;
init.name = "edp_phy_pll_link_clk";
edp->dp_link_hw.init = &init;
ret = devm_clk_hw_register(edp->dev, &edp->dp_link_hw);
if (ret)
return ret;
init.ops = &qcom_edp_dp_pixel_clk_ops;
init.name = "edp_phy_pll_vco_div_clk";
edp->dp_pixel_hw.init = &init;
ret = devm_clk_hw_register(edp->dev, &edp->dp_pixel_hw);
if (ret)
return ret;
data->hws[0] = &edp->dp_link_hw;
data->hws[1] = &edp->dp_pixel_hw;
data->num = 2;
return devm_of_clk_add_hw_provider(edp->dev, of_clk_hw_onecell_get, data);
}
static int qcom_edp_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct qcom_edp *edp;
int ret;
edp = devm_kzalloc(dev, sizeof(*edp), GFP_KERNEL);
if (!edp)
return -ENOMEM;
edp->dev = dev;
edp->edp = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(edp->edp))
return PTR_ERR(edp->edp);
edp->tx0 = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(edp->tx0))
return PTR_ERR(edp->tx0);
edp->tx1 = devm_platform_ioremap_resource(pdev, 2);
if (IS_ERR(edp->tx1))
return PTR_ERR(edp->tx1);
edp->pll = devm_platform_ioremap_resource(pdev, 3);
if (IS_ERR(edp->pll))
return PTR_ERR(edp->pll);
edp->clks[0].id = "aux";
edp->clks[1].id = "cfg_ahb";
ret = devm_clk_bulk_get(dev, ARRAY_SIZE(edp->clks), edp->clks);
if (ret)
return ret;
edp->supplies[0].supply = "vdda-phy";
edp->supplies[1].supply = "vdda-pll";
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(edp->supplies), edp->supplies);
if (ret)
return ret;
ret = qcom_edp_clks_register(edp, pdev->dev.of_node);
if (ret)
return ret;
edp->phy = devm_phy_create(dev, pdev->dev.of_node, &qcom_edp_ops);
if (IS_ERR(edp->phy)) {
dev_err(dev, "failed to register phy\n");
return PTR_ERR(edp->phy);
}
phy_set_drvdata(edp->phy, edp);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id qcom_edp_phy_match_table[] = {
{ .compatible = "qcom,sc8180x-edp-phy" },
{ }
};
MODULE_DEVICE_TABLE(of, qcom_edp_phy_match_table);
static struct platform_driver qcom_edp_phy_driver = {
.probe = qcom_edp_phy_probe,
.driver = {
.name = "qcom-edp-phy",
.of_match_table = qcom_edp_phy_match_table,
},
};
module_platform_driver(qcom_edp_phy_driver);
MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>");
MODULE_DESCRIPTION("Qualcomm eDP QMP PHY driver");
MODULE_LICENSE("GPL v2");

View File

@ -2866,6 +2866,215 @@ static const struct qmp_phy_init_tbl qcm2290_usb3_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88),
};
static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_SELECT, 0x34),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE1, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x42),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE1_MODE0, 0x24),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE2_MODE1, 0x03),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE1_MODE1, 0xb4),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x82),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE0, 0x03),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE0, 0x55),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE0, 0x55),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x1a),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0x68),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE1, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE1, 0xaa),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE1, 0xab),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x34),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x14),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE0, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE1, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x18),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xa2),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_BUF_ENABLE, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_EN_CENTER, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER1, 0x31),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER2, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0, 0xde),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0x4c),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_ENABLE1, 0x90),
};
static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x20),
QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0x75),
QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x04),
};
static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0x7f),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0xff),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xbf),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xd8),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0xdc),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0xdc),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0x5c),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x34),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa6),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH3, 0x34),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH4, 0x38),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH1, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH2, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0xf0),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_TX_ADAPT_POST_THRESH, 0xf0),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x09),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05),
};
static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0x77),
QMP_PHY_INIT_CFG(QPHY_V5_PCS_RATE_SLEW_CNTRL1, 0x0b),
QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x05),
};
static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_pcs_misc_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00),
QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_INT_AUX_CLK_CONFIG1, 0x00),
QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_EQ_CONFIG2, 0x0f),
QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1),
};
static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER1, 0x31),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER2, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0, 0xde),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0x97),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x0c),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN, 0x14),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_ENABLE1, 0x90),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE0, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE1, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x46),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_CFG, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x1a),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x14),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x34),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x82),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0xd0),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE0, 0x55),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE0, 0x55),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE0, 0x03),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE1, 0x55),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE1, 0x55),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE1, 0x05),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_SELECT, 0x34),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x12),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE0, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE1, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_MISC1, 0x88),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORE_CLK_EN, 0x20),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_CONFIG, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_MODE, 0x14),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_DC_LEVEL_CTRL, 0x0f),
};
static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_LANE_MODE_1, 0x05),
QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_LANE_MODE_2, 0xf6),
QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_TX, 0x1a),
QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_RX, 0x0c),
};
static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_PI_CONTROLS, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B1, 0xcc),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B2, 0x12),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B3, 0xcc),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B5, 0x4a),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B6, 0x29),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B0, 0xc5),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B1, 0xad),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B2, 0xb6),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B3, 0xc0),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B4, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B5, 0xfb),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B6, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B0, 0xc7),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B1, 0xef),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B2, 0xbf),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B3, 0xa0),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B4, 0x81),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B5, 0xde),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B6, 0x7f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_PHPRE_CTRL, 0x20),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_0_1, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_2_3, 0x37),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_3, 0x05),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE3, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE3, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE3, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH4_RATE3, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH5_RATE3, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH6_RATE3, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE210, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE210, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE210, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE2, 0x0c),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE3, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_VGA_CAL_MAN_VAL, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0b),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_IDAC_SAOFFSET, 0x10),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_DAC_ENABLE1, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_GM_CAL, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH1, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH2, 0x1f),
};
/* Register names should be validated, they might be different for this PHY */
static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG2, 0x16),
QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG3, 0x22),
QMP_PHY_INIT_CFG(QPHY_V5_PCS_G3S2_PRE_GAIN, 0x2e),
QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0x99),
};
static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_pcs_misc_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1),
QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00),
QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_G4_EQ_CONFIG5, 0x02),
QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_EQ_CONFIG1, 0x16),
QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_RX_MARGINING_CONFIG3, 0x28),
QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_G4_PRE_GAIN, 0x2e),
};
struct qmp_phy;
/* struct qmp_phy_cfg - per-PHY initialization config */
@ -3094,6 +3303,10 @@ static const char * const qmp_v4_sm8250_usbphy_clk_l[] = {
"aux", "ref_clk_src", "com_aux"
};
static const char * const sm8450_ufs_phy_clk_l[] = {
"qref", "ref", "ref_aux",
};
static const char * const sdm845_ufs_phy_clk_l[] = {
"ref", "ref_aux",
};
@ -4090,6 +4303,94 @@ static const struct qmp_phy_cfg sm8350_usb3_uniphy_cfg = {
.pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
};
static const struct qmp_phy_cfg sm8450_ufsphy_cfg = {
.type = PHY_TYPE_UFS,
.nlanes = 2,
.serdes_tbl = sm8350_ufsphy_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(sm8350_ufsphy_serdes_tbl),
.tx_tbl = sm8350_ufsphy_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_tx_tbl),
.rx_tbl = sm8350_ufsphy_rx_tbl,
.rx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_rx_tbl),
.pcs_tbl = sm8350_ufsphy_pcs_tbl,
.pcs_tbl_num = ARRAY_SIZE(sm8350_ufsphy_pcs_tbl),
.clk_list = sm8450_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sm8450_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = sm8150_ufsphy_regs_layout,
.start_ctrl = SERDES_START,
.pwrdn_ctrl = SW_PWRDN,
.phy_status = PHYSTATUS,
.is_dual_lane_phy = true,
};
static const struct qmp_phy_cfg sm8450_qmp_gen3x1_pciephy_cfg = {
.type = PHY_TYPE_PCIE,
.nlanes = 1,
.serdes_tbl = sm8450_qmp_gen3x1_pcie_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_serdes_tbl),
.tx_tbl = sm8450_qmp_gen3x1_pcie_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_tx_tbl),
.rx_tbl = sm8450_qmp_gen3x1_pcie_rx_tbl,
.rx_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_rx_tbl),
.pcs_tbl = sm8450_qmp_gen3x1_pcie_pcs_tbl,
.pcs_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_pcs_tbl),
.pcs_misc_tbl = sm8450_qmp_gen3x1_pcie_pcs_misc_tbl,
.pcs_misc_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_pcs_misc_tbl),
.clk_list = sdm845_pciephy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l),
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = sm8250_pcie_regs_layout,
.start_ctrl = SERDES_START | PCS_START,
.pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
.phy_status = PHYSTATUS,
.has_pwrdn_delay = true,
.pwrdn_delay_min = 995, /* us */
.pwrdn_delay_max = 1005, /* us */
};
static const struct qmp_phy_cfg sm8450_qmp_gen4x2_pciephy_cfg = {
.type = PHY_TYPE_PCIE,
.nlanes = 2,
.serdes_tbl = sm8450_qmp_gen4x2_pcie_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_serdes_tbl),
.tx_tbl = sm8450_qmp_gen4x2_pcie_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_tx_tbl),
.rx_tbl = sm8450_qmp_gen4x2_pcie_rx_tbl,
.rx_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_rx_tbl),
.pcs_tbl = sm8450_qmp_gen4x2_pcie_pcs_tbl,
.pcs_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_pcs_tbl),
.pcs_misc_tbl = sm8450_qmp_gen4x2_pcie_pcs_misc_tbl,
.pcs_misc_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_pcs_misc_tbl),
.clk_list = sdm845_pciephy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l),
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = sm8250_pcie_regs_layout,
.start_ctrl = SERDES_START | PCS_START,
.pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
.phy_status = PHYSTATUS_4_20,
.is_dual_lane_phy = true,
.has_pwrdn_delay = true,
.pwrdn_delay_min = 995, /* us */
.pwrdn_delay_max = 1005, /* us */
};
static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = {
.type = PHY_TYPE_USB3,
.nlanes = 1,
@ -5748,6 +6049,18 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
}, {
.compatible = "qcom,sm8350-qmp-usb3-uni-phy",
.data = &sm8350_usb3_uniphy_cfg,
}, {
.compatible = "qcom,sm8450-qmp-gen3x1-pcie-phy",
.data = &sm8450_qmp_gen3x1_pciephy_cfg,
}, {
.compatible = "qcom,sm8450-qmp-gen4x2-pcie-phy",
.data = &sm8450_qmp_gen4x2_pciephy_cfg,
}, {
.compatible = "qcom,sm8450-qmp-ufs-phy",
.data = &sm8450_ufsphy_cfg,
}, {
.compatible = "qcom,sm8450-qmp-usb3-phy",
.data = &sm8350_usb3phy_cfg,
}, {
.compatible = "qcom,qcm2290-qmp-usb3-phy",
.data = &qcm2290_usb3phy_cfg,

View File

@ -551,6 +551,7 @@
/* 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_ADJ_PER1 0x014
#define QSERDES_V4_COM_SSC_PER1 0x01c
#define QSERDES_V4_COM_SSC_PER2 0x020
#define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0 0x024
@ -1069,6 +1070,16 @@
#define QPHY_V4_20_PCS_LANE1_INSIG_MX_CTRL2 0x828
/* Only for QMP V5 PHY - QSERDES COM registers */
#define QSERDES_V5_COM_SSC_EN_CENTER 0x010
#define QSERDES_V5_COM_SSC_PER1 0x01c
#define QSERDES_V5_COM_SSC_PER2 0x020
#define QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0 0x024
#define QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0 0x028
#define QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1 0x030
#define QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1 0x034
#define QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN 0x044
#define QSERDES_V5_COM_CLK_ENABLE1 0x048
#define QSERDES_V5_COM_SYSCLK_BUF_ENABLE 0x050
#define QSERDES_V5_COM_PLL_IVCO 0x058
#define QSERDES_V5_COM_CP_CTRL_MODE0 0x074
#define QSERDES_V5_COM_CP_CTRL_MODE1 0x078
@ -1078,16 +1089,35 @@
#define QSERDES_V5_COM_PLL_CCTRL_MODE1 0x088
#define QSERDES_V5_COM_SYSCLK_EN_SEL 0x094
#define QSERDES_V5_COM_LOCK_CMP_EN 0x0a4
#define QSERDES_V5_COM_LOCK_CMP_CFG 0x0a8
#define QSERDES_V5_COM_LOCK_CMP1_MODE0 0x0ac
#define QSERDES_V5_COM_LOCK_CMP2_MODE0 0x0b0
#define QSERDES_V5_COM_LOCK_CMP1_MODE1 0x0b4
#define QSERDES_V5_COM_DEC_START_MODE0 0x0bc
#define QSERDES_V5_COM_LOCK_CMP2_MODE1 0x0b8
#define QSERDES_V5_COM_DEC_START_MODE1 0x0c4
#define QSERDES_V5_COM_DIV_FRAC_START1_MODE0 0x0cc
#define QSERDES_V5_COM_DIV_FRAC_START2_MODE0 0x0d0
#define QSERDES_V5_COM_DIV_FRAC_START3_MODE0 0x0d4
#define QSERDES_V5_COM_DIV_FRAC_START1_MODE1 0x0d8
#define QSERDES_V5_COM_DIV_FRAC_START2_MODE1 0x0dc
#define QSERDES_V5_COM_DIV_FRAC_START3_MODE1 0x0e0
#define QSERDES_V5_COM_VCO_TUNE_MAP 0x10c
#define QSERDES_V5_COM_VCO_TUNE1_MODE0 0x110
#define QSERDES_V5_COM_VCO_TUNE2_MODE0 0x114
#define QSERDES_V5_COM_VCO_TUNE1_MODE1 0x118
#define QSERDES_V5_COM_VCO_TUNE2_MODE1 0x11c
#define QSERDES_V5_COM_VCO_TUNE_INITVAL2 0x124
#define QSERDES_V5_COM_CLK_SELECT 0x154
#define QSERDES_V5_COM_HSCLK_SEL 0x158
#define QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL 0x15c
#define QSERDES_V5_COM_CORECLK_DIV_MODE0 0x168
#define QSERDES_V5_COM_CORECLK_DIV_MODE1 0x16c
#define QSERDES_V5_COM_CORE_CLK_EN 0x174
#define QSERDES_V5_COM_CMN_CONFIG 0x17c
#define QSERDES_V5_COM_CMN_MISC1 0x19c
#define QSERDES_V5_COM_CMN_MODE 0x1a4
#define QSERDES_V5_COM_VCO_DC_LEVEL_CTRL 0x1a8
#define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x1ac
#define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1b0
#define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x1b4
@ -1112,6 +1142,12 @@
#define QSERDES_V5_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0x180
#define QSERDES_V5_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0x184
/* Only for QMP V5_20 PHY - TX registers */
#define QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_TX 0x30
#define QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_RX 0x34
#define QSERDES_V5_20_TX_LANE_MODE_1 0x78
#define QSERDES_V5_20_TX_LANE_MODE_2 0x7c
/* Only for QMP V5 PHY - RX registers */
#define QSERDES_V5_RX_UCDR_FO_GAIN 0x008
#define QSERDES_V5_RX_UCDR_SO_GAIN 0x014
@ -1130,6 +1166,7 @@
#define QSERDES_V5_RX_AC_JTAG_ENABLE 0x068
#define QSERDES_V5_RX_AC_JTAG_MODE 0x078
#define QSERDES_V5_RX_RX_TERM_BW 0x080
#define QSERDES_V5_RX_TX_ADAPT_POST_THRESH 0x0cc
#define QSERDES_V5_RX_VGA_CAL_CNTRL1 0x0d4
#define QSERDES_V5_RX_VGA_CAL_CNTRL2 0x0d8
#define QSERDES_V5_RX_GM_CAL 0x0dc
@ -1167,6 +1204,73 @@
#define QSERDES_V5_RX_DCC_CTRL1 0x1a8
#define QSERDES_V5_RX_VTH_CODE 0x1b0
/* Only for QMP V5_20 PHY - RX registers */
#define QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE2 0x008
#define QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE3 0x00c
#define QSERDES_V5_20_RX_UCDR_PI_CONTROLS 0x020
#define QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_0_1 0x02c
#define QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_2_3 0x030
#define QSERDES_V5_20_RX_RX_IDAC_SAOFFSET 0x07c
#define QSERDES_V5_20_RX_DFE_3 0x090
#define QSERDES_V5_20_RX_DFE_DAC_ENABLE1 0x0b4
#define QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH1 0x0c4
#define QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH2 0x0c8
#define QSERDES_V5_20_RX_VGA_CAL_MAN_VAL 0x0dc
#define QSERDES_V5_20_RX_GM_CAL 0x0ec
#define QSERDES_V5_20_RX_RX_EQU_ADAPTOR_CNTRL4 0x108
#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B1 0x164
#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B2 0x168
#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B3 0x16c
#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B5 0x174
#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B6 0x178
#define QSERDES_V5_20_RX_RX_MODE_RATE2_B0 0x17c
#define QSERDES_V5_20_RX_RX_MODE_RATE2_B1 0x180
#define QSERDES_V5_20_RX_RX_MODE_RATE2_B2 0x184
#define QSERDES_V5_20_RX_RX_MODE_RATE2_B3 0x188
#define QSERDES_V5_20_RX_RX_MODE_RATE2_B4 0x18c
#define QSERDES_V5_20_RX_RX_MODE_RATE2_B5 0x190
#define QSERDES_V5_20_RX_RX_MODE_RATE2_B6 0x194
#define QSERDES_V5_20_RX_RX_MODE_RATE3_B0 0x198
#define QSERDES_V5_20_RX_RX_MODE_RATE3_B1 0x19c
#define QSERDES_V5_20_RX_RX_MODE_RATE3_B2 0x1a0
#define QSERDES_V5_20_RX_RX_MODE_RATE3_B3 0x1a4
#define QSERDES_V5_20_RX_RX_MODE_RATE3_B4 0x1a8
#define QSERDES_V5_20_RX_RX_MODE_RATE3_B5 0x1ac
#define QSERDES_V5_20_RX_RX_MODE_RATE3_B6 0x1b0
#define QSERDES_V5_20_RX_PHPRE_CTRL 0x1b4
#define QSERDES_V5_20_RX_DFE_CTLE_POST_CAL_OFFSET 0x1c0
#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE210 0x1f4
#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE3 0x1f8
#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE210 0x1fc
#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE3 0x200
#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE210 0x204
#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE3 0x208
#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH4_RATE3 0x210
#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH5_RATE3 0x218
#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH6_RATE3 0x220
/* Only for QMP V5 PHY - USB/PCIe PCS registers */
#define QPHY_V5_PCS_REFGEN_REQ_CONFIG1 0x0dc
#define QPHY_V5_PCS_G3S2_PRE_GAIN 0x170
#define QPHY_V5_PCS_RX_SIGDET_LVL 0x188
#define QPHY_V5_PCS_RATE_SLEW_CNTRL1 0x198
#define QPHY_V5_PCS_EQ_CONFIG2 0x1e0
#define QPHY_V5_PCS_EQ_CONFIG3 0x1e4
/* Only for QMP V5 PHY - PCS_PCIE registers */
#define QPHY_V5_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x20
#define QPHY_V5_PCS_PCIE_INT_AUX_CLK_CONFIG1 0x54
#define QPHY_V5_PCS_PCIE_OSC_DTCT_ACTIONS 0x94
#define QPHY_V5_PCS_PCIE_EQ_CONFIG2 0xa8
/* Only for QMP V5_20 PHY - PCIe PCS registers */
#define QPHY_V5_20_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x01c
#define QPHY_V5_20_PCS_PCIE_OSC_DTCT_ACTIONS 0x090
#define QPHY_V5_20_PCS_PCIE_EQ_CONFIG1 0x0a0
#define QPHY_V5_20_PCS_PCIE_G4_EQ_CONFIG5 0x108
#define QPHY_V5_20_PCS_PCIE_G4_PRE_GAIN 0x15c
#define QPHY_V5_20_PCS_PCIE_RX_MARGINING_CONFIG3 0x184
/* Only for QMP V5 PHY - UFS PCS registers */
#define QPHY_V5_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB 0x00c
#define QPHY_V5_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB 0x010

View File

@ -204,6 +204,7 @@ struct rockchip_usb2phy_port {
* @dcd_retries: The retry count used to track Data contact
* detection process.
* @edev: extcon device for notification registration
* @irq: muxed interrupt for single irq configuration
* @phy_cfg: phy register configuration, assigned by driver data.
* @ports: phy port instance.
*/
@ -218,6 +219,7 @@ struct rockchip_usb2phy {
enum power_supply_type chg_type;
u8 dcd_retries;
struct extcon_dev *edev;
int irq;
const struct rockchip_usb2phy_cfg *phy_cfg;
struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS];
};
@ -750,7 +752,6 @@ static void rockchip_chg_detect_work(struct work_struct *work)
fallthrough;
case USB_CHG_STATE_SECONDARY_DONE:
rphy->chg_state = USB_CHG_STATE_DETECTED;
delay = 0;
fallthrough;
case USB_CHG_STATE_DETECTED:
/* put the controller in normal mode */
@ -927,6 +928,102 @@ static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data)
return IRQ_NONE;
}
static irqreturn_t rockchip_usb2phy_irq(int irq, void *data)
{
struct rockchip_usb2phy *rphy = data;
struct rockchip_usb2phy_port *rport;
irqreturn_t ret = IRQ_NONE;
unsigned int index;
for (index = 0; index < rphy->phy_cfg->num_ports; index++) {
rport = &rphy->ports[index];
if (!rport->phy)
continue;
/* Handle linestate irq for both otg port and host port */
ret = rockchip_usb2phy_linestate_irq(irq, rport);
}
return ret;
}
static int rockchip_usb2phy_port_irq_init(struct rockchip_usb2phy *rphy,
struct rockchip_usb2phy_port *rport,
struct device_node *child_np)
{
int ret;
/*
* If the usb2 phy used combined irq for otg and host port,
* don't need to init otg and host port irq separately.
*/
if (rphy->irq > 0)
return 0;
switch (rport->port_id) {
case USB2PHY_PORT_HOST:
rport->ls_irq = of_irq_get_byname(child_np, "linestate");
if (rport->ls_irq < 0) {
dev_err(rphy->dev, "no linestate irq provided\n");
return rport->ls_irq;
}
ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
rockchip_usb2phy_linestate_irq,
IRQF_ONESHOT,
"rockchip_usb2phy", rport);
if (ret) {
dev_err(rphy->dev, "failed to request linestate irq handle\n");
return ret;
}
break;
case USB2PHY_PORT_OTG:
/*
* Some SoCs use one interrupt with otg-id/otg-bvalid/linestate
* interrupts muxed together, so probe the otg-mux interrupt first,
* if not found, then look for the regular interrupts one by one.
*/
rport->otg_mux_irq = of_irq_get_byname(child_np, "otg-mux");
if (rport->otg_mux_irq > 0) {
ret = devm_request_threaded_irq(rphy->dev, rport->otg_mux_irq,
NULL,
rockchip_usb2phy_otg_mux_irq,
IRQF_ONESHOT,
"rockchip_usb2phy_otg",
rport);
if (ret) {
dev_err(rphy->dev,
"failed to request otg-mux irq handle\n");
return ret;
}
} else {
rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid");
if (rport->bvalid_irq < 0) {
dev_err(rphy->dev, "no vbus valid irq provided\n");
ret = rport->bvalid_irq;
return ret;
}
ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq,
NULL,
rockchip_usb2phy_bvalid_irq,
IRQF_ONESHOT,
"rockchip_usb2phy_bvalid",
rport);
if (ret) {
dev_err(rphy->dev,
"failed to request otg-bvalid irq handle\n");
return ret;
}
}
break;
default:
return -EINVAL;
}
return 0;
}
static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
struct rockchip_usb2phy_port *rport,
struct device_node *child_np)
@ -940,18 +1037,9 @@ static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
mutex_init(&rport->mutex);
INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work);
rport->ls_irq = of_irq_get_byname(child_np, "linestate");
if (rport->ls_irq < 0) {
dev_err(rphy->dev, "no linestate irq provided\n");
return rport->ls_irq;
}
ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
rockchip_usb2phy_linestate_irq,
IRQF_ONESHOT,
"rockchip_usb2phy", rport);
ret = rockchip_usb2phy_port_irq_init(rphy, rport, child_np);
if (ret) {
dev_err(rphy->dev, "failed to request linestate irq handle\n");
dev_err(rphy->dev, "failed to setup host irq\n");
return ret;
}
@ -1000,43 +1088,10 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work);
INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work);
/*
* Some SoCs use one interrupt with otg-id/otg-bvalid/linestate
* interrupts muxed together, so probe the otg-mux interrupt first,
* if not found, then look for the regular interrupts one by one.
*/
rport->otg_mux_irq = of_irq_get_byname(child_np, "otg-mux");
if (rport->otg_mux_irq > 0) {
ret = devm_request_threaded_irq(rphy->dev, rport->otg_mux_irq,
NULL,
rockchip_usb2phy_otg_mux_irq,
IRQF_ONESHOT,
"rockchip_usb2phy_otg",
rport);
if (ret) {
dev_err(rphy->dev,
"failed to request otg-mux irq handle\n");
goto out;
}
} else {
rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid");
if (rport->bvalid_irq < 0) {
dev_err(rphy->dev, "no vbus valid irq provided\n");
ret = rport->bvalid_irq;
goto out;
}
ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq,
NULL,
rockchip_usb2phy_bvalid_irq,
IRQF_ONESHOT,
"rockchip_usb2phy_bvalid",
rport);
if (ret) {
dev_err(rphy->dev,
"failed to request otg-bvalid irq handle\n");
goto out;
}
ret = rockchip_usb2phy_port_irq_init(rphy, rport, child_np);
if (ret) {
dev_err(rphy->dev, "failed to init irq for host port\n");
goto out;
}
if (!IS_ERR(rphy->edev)) {
@ -1074,12 +1129,19 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
return -EINVAL;
}
if (!dev->parent || !dev->parent->of_node)
return -EINVAL;
if (!dev->parent || !dev->parent->of_node) {
rphy->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,usbgrf");
if (IS_ERR(rphy->grf)) {
dev_err(dev, "failed to locate usbgrf\n");
return PTR_ERR(rphy->grf);
}
}
rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(rphy->grf))
return PTR_ERR(rphy->grf);
else {
rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(rphy->grf))
return PTR_ERR(rphy->grf);
}
if (of_device_is_compatible(np, "rockchip,rv1108-usb2phy")) {
rphy->usbgrf =
@ -1091,16 +1153,26 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
rphy->usbgrf = NULL;
}
if (of_property_read_u32(np, "reg", &reg)) {
if (of_property_read_u32_index(np, "reg", 0, &reg)) {
dev_err(dev, "the reg property is not assigned in %pOFn node\n",
np);
return -EINVAL;
}
/* support address_cells=2 */
if (reg == 0) {
if (of_property_read_u32_index(np, "reg", 1, &reg)) {
dev_err(dev, "the reg property is not assigned in %pOFn node\n",
np);
return -EINVAL;
}
}
rphy->dev = dev;
phy_cfgs = match->data;
rphy->chg_state = USB_CHG_STATE_UNDEFINED;
rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
rphy->irq = platform_get_irq_optional(pdev, 0);
platform_set_drvdata(pdev, rphy);
ret = rockchip_usb2phy_extcon_register(rphy);
@ -1180,6 +1252,20 @@ next_child:
}
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (rphy->irq > 0) {
ret = devm_request_threaded_irq(rphy->dev, rphy->irq, NULL,
rockchip_usb2phy_irq,
IRQF_ONESHOT,
"rockchip_usb2phy",
rphy);
if (ret) {
dev_err(rphy->dev,
"failed to request usb2phy irq handle\n");
goto put_child;
}
}
return PTR_ERR_OR_ZERO(provider);
put_child:
@ -1418,6 +1504,69 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
{ /* sentinel */ }
};
static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = {
{
.reg = 0xfe8a0000,
.num_ports = 2,
.clkout_ctl = { 0x0008, 4, 4, 1, 0 },
.port_cfgs = {
[USB2PHY_PORT_OTG] = {
.phy_sus = { 0x0000, 8, 0, 0, 0x1d1 },
.bvalid_det_en = { 0x0080, 2, 2, 0, 1 },
.bvalid_det_st = { 0x0084, 2, 2, 0, 1 },
.bvalid_det_clr = { 0x0088, 2, 2, 0, 1 },
.utmi_avalid = { 0x00c0, 10, 10, 0, 1 },
.utmi_bvalid = { 0x00c0, 9, 9, 0, 1 },
},
[USB2PHY_PORT_HOST] = {
/* Select suspend control from controller */
.phy_sus = { 0x0004, 8, 0, 0x1d2, 0x1d2 },
.ls_det_en = { 0x0080, 1, 1, 0, 1 },
.ls_det_st = { 0x0084, 1, 1, 0, 1 },
.ls_det_clr = { 0x0088, 1, 1, 0, 1 },
.utmi_ls = { 0x00c0, 17, 16, 0, 1 },
.utmi_hstdet = { 0x00c0, 19, 19, 0, 1 }
}
},
.chg_det = {
.opmode = { 0x0000, 3, 0, 5, 1 },
.cp_det = { 0x00c0, 24, 24, 0, 1 },
.dcp_det = { 0x00c0, 23, 23, 0, 1 },
.dp_det = { 0x00c0, 25, 25, 0, 1 },
.idm_sink_en = { 0x0008, 8, 8, 0, 1 },
.idp_sink_en = { 0x0008, 7, 7, 0, 1 },
.idp_src_en = { 0x0008, 9, 9, 0, 1 },
.rdm_pdwn_en = { 0x0008, 10, 10, 0, 1 },
.vdm_src_en = { 0x0008, 12, 12, 0, 1 },
.vdp_src_en = { 0x0008, 11, 11, 0, 1 },
},
},
{
.reg = 0xfe8b0000,
.num_ports = 2,
.clkout_ctl = { 0x0008, 4, 4, 1, 0 },
.port_cfgs = {
[USB2PHY_PORT_OTG] = {
.phy_sus = { 0x0000, 8, 0, 0x1d2, 0x1d1 },
.ls_det_en = { 0x0080, 0, 0, 0, 1 },
.ls_det_st = { 0x0084, 0, 0, 0, 1 },
.ls_det_clr = { 0x0088, 0, 0, 0, 1 },
.utmi_ls = { 0x00c0, 5, 4, 0, 1 },
.utmi_hstdet = { 0x00c0, 7, 7, 0, 1 }
},
[USB2PHY_PORT_HOST] = {
.phy_sus = { 0x0004, 8, 0, 0x1d2, 0x1d1 },
.ls_det_en = { 0x0080, 1, 1, 0, 1 },
.ls_det_st = { 0x0084, 1, 1, 0, 1 },
.ls_det_clr = { 0x0088, 1, 1, 0, 1 },
.utmi_ls = { 0x00c0, 17, 16, 0, 1 },
.utmi_hstdet = { 0x00c0, 19, 19, 0, 1 }
}
},
},
{ /* sentinel */ }
};
static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = {
{
.reg = 0x100,
@ -1467,6 +1616,7 @@ static const struct of_device_id rockchip_usb2phy_dt_match[] = {
{ .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs },
{ .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs },
{ .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
{ .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs },
{ .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs },
{}
};

View File

@ -43,4 +43,4 @@ config PHY_UNIPHIER_AHCI
select GENERIC_PHY
help
Enable this to support PHY implemented in AHCI controller
on UniPhier SoCs. This driver supports PXs2 and PXs3 SoCs.
on UniPhier SoCs. This driver supports Pro4, PXs2 and PXs3 SoCs.

View File

@ -19,8 +19,9 @@
struct uniphier_ahciphy_priv {
struct device *dev;
void __iomem *base;
struct clk *clk, *clk_parent;
struct reset_control *rst, *rst_parent;
struct clk *clk, *clk_parent, *clk_parent_gio;
struct reset_control *rst, *rst_parent, *rst_parent_gio;
struct reset_control *rst_pm, *rst_tx, *rst_rx;
const struct uniphier_ahciphy_soc_data *data;
};
@ -28,10 +29,30 @@ struct uniphier_ahciphy_soc_data {
int (*init)(struct uniphier_ahciphy_priv *priv);
int (*power_on)(struct uniphier_ahciphy_priv *priv);
int (*power_off)(struct uniphier_ahciphy_priv *priv);
bool is_legacy;
bool is_ready_high;
bool is_phy_clk;
};
/* for Pro4 */
#define CKCTRL0 0x0
#define CKCTRL0_CK_OFF BIT(9)
#define CKCTRL0_NCY_MASK GENMASK(8, 4)
#define CKCTRL0_NCY5_MASK GENMASK(3, 2)
#define CKCTRL0_PRESCALE_MASK GENMASK(1, 0)
#define CKCTRL1 0x4
#define CKCTRL1_LOS_LVL_MASK GENMASK(20, 16)
#define CKCTRL1_TX_LVL_MASK GENMASK(12, 8)
#define RXTXCTRL 0x8
#define RXTXCTRL_RX_EQ_VALL_MASK GENMASK(31, 29)
#define RXTXCTRL_RX_DPLL_MODE_MASK GENMASK(28, 26)
#define RXTXCTRL_TX_ATTEN_MASK GENMASK(14, 12)
#define RXTXCTRL_TX_BOOST_MASK GENMASK(11, 8)
#define RXTXCTRL_TX_EDGERATE_MASK GENMASK(3, 2)
#define RXTXCTRL_TX_CKO_EN BIT(0)
#define RSTPWR 0x30
#define RSTPWR_RX_EN_VAL BIT(18)
/* for PXs2/PXs3 */
#define CKCTRL 0x0
#define CKCTRL_P0_READY BIT(15)
@ -50,6 +71,128 @@ struct uniphier_ahciphy_soc_data {
#define RXCTRL_LOS_BIAS_MASK GENMASK(10, 8)
#define RXCTRL_RX_EQ_MASK GENMASK(2, 0)
static int uniphier_ahciphy_pro4_init(struct uniphier_ahciphy_priv *priv)
{
u32 val;
/* set phy MPLL parameters */
val = readl(priv->base + CKCTRL0);
val &= ~CKCTRL0_NCY_MASK;
val |= FIELD_PREP(CKCTRL0_NCY_MASK, 0x6);
val &= ~CKCTRL0_NCY5_MASK;
val |= FIELD_PREP(CKCTRL0_NCY5_MASK, 0x2);
val &= ~CKCTRL0_PRESCALE_MASK;
val |= FIELD_PREP(CKCTRL0_PRESCALE_MASK, 0x1);
writel(val, priv->base + CKCTRL0);
/* setup phy control parameters */
val = readl(priv->base + CKCTRL1);
val &= ~CKCTRL1_LOS_LVL_MASK;
val |= FIELD_PREP(CKCTRL1_LOS_LVL_MASK, 0x10);
val &= ~CKCTRL1_TX_LVL_MASK;
val |= FIELD_PREP(CKCTRL1_TX_LVL_MASK, 0x06);
writel(val, priv->base + CKCTRL1);
val = readl(priv->base + RXTXCTRL);
val &= ~RXTXCTRL_RX_EQ_VALL_MASK;
val |= FIELD_PREP(RXTXCTRL_RX_EQ_VALL_MASK, 0x6);
val &= ~RXTXCTRL_RX_DPLL_MODE_MASK;
val |= FIELD_PREP(RXTXCTRL_RX_DPLL_MODE_MASK, 0x3);
val &= ~RXTXCTRL_TX_ATTEN_MASK;
val |= FIELD_PREP(RXTXCTRL_TX_ATTEN_MASK, 0x3);
val &= ~RXTXCTRL_TX_BOOST_MASK;
val |= FIELD_PREP(RXTXCTRL_TX_BOOST_MASK, 0x5);
val &= ~RXTXCTRL_TX_EDGERATE_MASK;
val |= FIELD_PREP(RXTXCTRL_TX_EDGERATE_MASK, 0x0);
writel(val, priv->base + RXTXCTRL);
return 0;
}
static int uniphier_ahciphy_pro4_power_on(struct uniphier_ahciphy_priv *priv)
{
u32 val;
int ret;
/* enable reference clock for phy */
val = readl(priv->base + CKCTRL0);
val &= ~CKCTRL0_CK_OFF;
writel(val, priv->base + CKCTRL0);
/* enable TX clock */
val = readl(priv->base + RXTXCTRL);
val |= RXTXCTRL_TX_CKO_EN;
writel(val, priv->base + RXTXCTRL);
/* wait until RX is ready */
ret = readl_poll_timeout(priv->base + RSTPWR, val,
!(val & RSTPWR_RX_EN_VAL), 200, 2000);
if (ret) {
dev_err(priv->dev, "Failed to check whether Rx is ready\n");
goto out_disable_clock;
}
/* release all reset */
ret = reset_control_deassert(priv->rst_pm);
if (ret) {
dev_err(priv->dev, "Failed to release PM reset\n");
goto out_disable_clock;
}
ret = reset_control_deassert(priv->rst_tx);
if (ret) {
dev_err(priv->dev, "Failed to release Tx reset\n");
goto out_reset_pm_assert;
}
ret = reset_control_deassert(priv->rst_rx);
if (ret) {
dev_err(priv->dev, "Failed to release Rx reset\n");
goto out_reset_tx_assert;
}
return 0;
out_reset_tx_assert:
reset_control_assert(priv->rst_tx);
out_reset_pm_assert:
reset_control_assert(priv->rst_pm);
out_disable_clock:
/* disable TX clock */
val = readl(priv->base + RXTXCTRL);
val &= ~RXTXCTRL_TX_CKO_EN;
writel(val, priv->base + RXTXCTRL);
/* disable reference clock for phy */
val = readl(priv->base + CKCTRL0);
val |= CKCTRL0_CK_OFF;
writel(val, priv->base + CKCTRL0);
return ret;
}
static int uniphier_ahciphy_pro4_power_off(struct uniphier_ahciphy_priv *priv)
{
u32 val;
reset_control_assert(priv->rst_rx);
reset_control_assert(priv->rst_tx);
reset_control_assert(priv->rst_pm);
/* disable TX clock */
val = readl(priv->base + RXTXCTRL);
val &= ~RXTXCTRL_TX_CKO_EN;
writel(val, priv->base + RXTXCTRL);
/* disable reference clock for phy */
val = readl(priv->base + CKCTRL0);
val |= CKCTRL0_CK_OFF;
writel(val, priv->base + CKCTRL0);
return 0;
}
static void uniphier_ahciphy_pxs2_enable(struct uniphier_ahciphy_priv *priv,
bool enable)
{
@ -142,14 +285,22 @@ static int uniphier_ahciphy_init(struct phy *phy)
struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);
int ret;
ret = clk_prepare_enable(priv->clk_parent);
ret = clk_prepare_enable(priv->clk_parent_gio);
if (ret)
return ret;
ret = reset_control_deassert(priv->rst_parent);
ret = clk_prepare_enable(priv->clk_parent);
if (ret)
goto out_clk_gio_disable;
ret = reset_control_deassert(priv->rst_parent_gio);
if (ret)
goto out_clk_disable;
ret = reset_control_deassert(priv->rst_parent);
if (ret)
goto out_rst_gio_assert;
if (priv->data->init) {
ret = priv->data->init(priv);
if (ret)
@ -160,8 +311,12 @@ static int uniphier_ahciphy_init(struct phy *phy)
out_rst_assert:
reset_control_assert(priv->rst_parent);
out_rst_gio_assert:
reset_control_assert(priv->rst_parent_gio);
out_clk_disable:
clk_disable_unprepare(priv->clk_parent);
out_clk_gio_disable:
clk_disable_unprepare(priv->clk_parent_gio);
return ret;
}
@ -171,7 +326,9 @@ static int uniphier_ahciphy_exit(struct phy *phy)
struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);
reset_control_assert(priv->rst_parent);
reset_control_assert(priv->rst_parent_gio);
clk_disable_unprepare(priv->clk_parent);
clk_disable_unprepare(priv->clk_parent_gio);
return 0;
}
@ -265,6 +422,28 @@ static int uniphier_ahciphy_probe(struct platform_device *pdev)
if (IS_ERR(priv->rst))
return PTR_ERR(priv->rst);
if (priv->data->is_legacy) {
priv->clk_parent_gio = devm_clk_get(dev, "gio");
if (IS_ERR(priv->clk_parent_gio))
return PTR_ERR(priv->clk_parent_gio);
priv->rst_parent_gio =
devm_reset_control_get_shared(dev, "gio");
if (IS_ERR(priv->rst_parent_gio))
return PTR_ERR(priv->rst_parent_gio);
priv->rst_pm = devm_reset_control_get_shared(dev, "pm");
if (IS_ERR(priv->rst_pm))
return PTR_ERR(priv->rst_pm);
priv->rst_tx = devm_reset_control_get_shared(dev, "tx");
if (IS_ERR(priv->rst_tx))
return PTR_ERR(priv->rst_tx);
priv->rst_rx = devm_reset_control_get_shared(dev, "rx");
if (IS_ERR(priv->rst_rx))
return PTR_ERR(priv->rst_rx);
}
phy = devm_phy_create(dev, dev->of_node, &uniphier_ahciphy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create phy\n");
@ -279,9 +458,18 @@ static int uniphier_ahciphy_probe(struct platform_device *pdev)
return 0;
}
static const struct uniphier_ahciphy_soc_data uniphier_pro4_data = {
.init = uniphier_ahciphy_pro4_init,
.power_on = uniphier_ahciphy_pro4_power_on,
.power_off = uniphier_ahciphy_pro4_power_off,
.is_legacy = true,
.is_phy_clk = false,
};
static const struct uniphier_ahciphy_soc_data uniphier_pxs2_data = {
.power_on = uniphier_ahciphy_pxs2_power_on,
.power_off = uniphier_ahciphy_pxs2_power_off,
.is_legacy = false,
.is_ready_high = false,
.is_phy_clk = false,
};
@ -290,11 +478,16 @@ static const struct uniphier_ahciphy_soc_data uniphier_pxs3_data = {
.init = uniphier_ahciphy_pxs3_init,
.power_on = uniphier_ahciphy_pxs2_power_on,
.power_off = uniphier_ahciphy_pxs2_power_off,
.is_legacy = false,
.is_ready_high = true,
.is_phy_clk = true,
};
static const struct of_device_id uniphier_ahciphy_match[] = {
{
.compatible = "socionext,uniphier-pro4-ahci-phy",
.data = &uniphier_pro4_data,
},
{
.compatible = "socionext,uniphier-pxs2-ahci-phy",
.data = &uniphier_pxs2_data,

View File

@ -27,6 +27,7 @@
#define TESTI_DAT_MASK GENMASK(13, 6)
#define TESTI_ADR_MASK GENMASK(5, 1)
#define TESTI_WR_EN BIT(0)
#define TESTIO_PHY_SHIFT 16
#define PCL_PHY_TEST_O 0x2004
#define TESTO_DAT_MASK GENMASK(7, 0)
@ -39,6 +40,10 @@
#define SG_USBPCIESEL 0x590
#define SG_USBPCIESEL_PCIE BIT(0)
/* SC */
#define SC_US3SRCSEL 0x2244
#define SC_US3SRCSEL_2LANE GENMASK(9, 8)
#define PCL_PHY_R00 0
#define RX_EQ_ADJ_EN BIT(3) /* enable for EQ adjustment */
#define PCL_PHY_R06 6
@ -47,6 +52,9 @@
#define PCL_PHY_R26 26
#define VCO_CTRL GENMASK(7, 4) /* Tx VCO adjustment value */
#define VCO_CTRL_INIT_VAL 5
#define PCL_PHY_R28 28
#define VCOPLL_CLMP GENMASK(3, 2) /* Tx VCOPLL clamp mode */
#define VCOPLL_CLMP_VAL 0
struct uniphier_pciephy_priv {
void __iomem *base;
@ -58,43 +66,57 @@ struct uniphier_pciephy_priv {
struct uniphier_pciephy_soc_data {
bool is_legacy;
bool is_dual_phy;
void (*set_phymode)(struct regmap *regmap);
};
static void uniphier_pciephy_testio_write(struct uniphier_pciephy_priv *priv,
u32 data)
int id, u32 data)
{
if (id)
data <<= TESTIO_PHY_SHIFT;
/* need to read TESTO twice after accessing TESTI */
writel(data, priv->base + PCL_PHY_TEST_I);
readl(priv->base + PCL_PHY_TEST_O);
readl(priv->base + PCL_PHY_TEST_O);
}
static u32 uniphier_pciephy_testio_read(struct uniphier_pciephy_priv *priv, int id)
{
u32 val = readl(priv->base + PCL_PHY_TEST_O);
if (id)
val >>= TESTIO_PHY_SHIFT;
return val & TESTO_DAT_MASK;
}
static void uniphier_pciephy_set_param(struct uniphier_pciephy_priv *priv,
u32 reg, u32 mask, u32 param)
int id, u32 reg, u32 mask, u32 param)
{
u32 val;
/* read previous data */
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, reg);
uniphier_pciephy_testio_write(priv, val);
val = readl(priv->base + PCL_PHY_TEST_O) & TESTO_DAT_MASK;
uniphier_pciephy_testio_write(priv, id, val);
val = uniphier_pciephy_testio_read(priv, id);
/* update value */
val &= ~mask;
val |= mask & param;
val = FIELD_PREP(TESTI_DAT_MASK, val);
val |= FIELD_PREP(TESTI_ADR_MASK, reg);
uniphier_pciephy_testio_write(priv, val);
uniphier_pciephy_testio_write(priv, val | TESTI_WR_EN);
uniphier_pciephy_testio_write(priv, val);
uniphier_pciephy_testio_write(priv, id, val);
uniphier_pciephy_testio_write(priv, id, val | TESTI_WR_EN);
uniphier_pciephy_testio_write(priv, id, val);
/* read current data as dummy */
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, reg);
uniphier_pciephy_testio_write(priv, val);
readl(priv->base + PCL_PHY_TEST_O);
uniphier_pciephy_testio_write(priv, id, val);
uniphier_pciephy_testio_read(priv, id);
}
static void uniphier_pciephy_assert(struct uniphier_pciephy_priv *priv)
@ -120,7 +142,7 @@ static int uniphier_pciephy_init(struct phy *phy)
{
struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);
u32 val;
int ret;
int ret, id;
ret = clk_prepare_enable(priv->clk);
if (ret)
@ -148,12 +170,16 @@ static int uniphier_pciephy_init(struct phy *phy)
if (priv->data->is_legacy)
return 0;
uniphier_pciephy_set_param(priv, PCL_PHY_R00,
for (id = 0; id < (priv->data->is_dual_phy ? 2 : 1); id++) {
uniphier_pciephy_set_param(priv, id, PCL_PHY_R00,
RX_EQ_ADJ_EN, RX_EQ_ADJ_EN);
uniphier_pciephy_set_param(priv, PCL_PHY_R06, RX_EQ_ADJ,
uniphier_pciephy_set_param(priv, id, PCL_PHY_R06, RX_EQ_ADJ,
FIELD_PREP(RX_EQ_ADJ, RX_EQ_ADJ_VAL));
uniphier_pciephy_set_param(priv, PCL_PHY_R26, VCO_CTRL,
uniphier_pciephy_set_param(priv, id, PCL_PHY_R26, VCO_CTRL,
FIELD_PREP(VCO_CTRL, VCO_CTRL_INIT_VAL));
uniphier_pciephy_set_param(priv, id, PCL_PHY_R28, VCOPLL_CLMP,
FIELD_PREP(VCOPLL_CLMP, VCOPLL_CLMP_VAL));
}
usleep_range(1, 10);
uniphier_pciephy_deassert(priv);
@ -261,17 +287,31 @@ static void uniphier_pciephy_ld20_setmode(struct regmap *regmap)
SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE);
}
static void uniphier_pciephy_nx1_setmode(struct regmap *regmap)
{
regmap_update_bits(regmap, SC_US3SRCSEL,
SC_US3SRCSEL_2LANE, SC_US3SRCSEL_2LANE);
}
static const struct uniphier_pciephy_soc_data uniphier_pro5_data = {
.is_legacy = true,
};
static const struct uniphier_pciephy_soc_data uniphier_ld20_data = {
.is_legacy = false,
.is_dual_phy = false,
.set_phymode = uniphier_pciephy_ld20_setmode,
};
static const struct uniphier_pciephy_soc_data uniphier_pxs3_data = {
.is_legacy = false,
.is_dual_phy = false,
};
static const struct uniphier_pciephy_soc_data uniphier_nx1_data = {
.is_legacy = false,
.is_dual_phy = true,
.set_phymode = uniphier_pciephy_nx1_setmode,
};
static const struct of_device_id uniphier_pciephy_match[] = {
@ -287,6 +327,10 @@ static const struct of_device_id uniphier_pciephy_match[] = {
.compatible = "socionext,uniphier-pxs3-pcie-phy",
.data = &uniphier_pxs3_data,
},
{
.compatible = "socionext,uniphier-nx1-pcie-phy",
.data = &uniphier_nx1_data,
},
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, uniphier_pciephy_match);

View File

@ -447,6 +447,10 @@ static const struct of_device_id uniphier_u3hsphy_match[] = {
.compatible = "socionext,uniphier-pxs3-usb3-hsphy",
.data = &uniphier_pxs3_data,
},
{
.compatible = "socionext,uniphier-nx1-usb3-hsphy",
.data = &uniphier_pxs3_data,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uniphier_u3hsphy_match);

View File

@ -22,11 +22,13 @@
#include <linux/reset.h>
#define SSPHY_TESTI 0x0
#define SSPHY_TESTO 0x4
#define TESTI_DAT_MASK GENMASK(13, 6)
#define TESTI_ADR_MASK GENMASK(5, 1)
#define TESTI_WR_EN BIT(0)
#define SSPHY_TESTO 0x4
#define TESTO_DAT_MASK GENMASK(7, 0)
#define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) }
#define CDR_CPD_TRIM PHY_F(7, 3, 0) /* RxPLL charge pump current */
@ -84,12 +86,12 @@ static void uniphier_u3ssphy_set_param(struct uniphier_u3ssphy_priv *priv,
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no);
uniphier_u3ssphy_testio_write(priv, val);
val = readl(priv->base + SSPHY_TESTO);
val = readl(priv->base + SSPHY_TESTO) & TESTO_DAT_MASK;
/* update value */
val &= ~FIELD_PREP(TESTI_DAT_MASK, field_mask);
val &= ~field_mask;
data = field_mask & (p->value << p->field.lsb);
val = FIELD_PREP(TESTI_DAT_MASK, data);
val = FIELD_PREP(TESTI_DAT_MASK, data | val);
val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no);
uniphier_u3ssphy_testio_write(priv, val);
uniphier_u3ssphy_testio_write(priv, val | TESTI_WR_EN);
@ -328,6 +330,10 @@ static const struct of_device_id uniphier_u3ssphy_match[] = {
.compatible = "socionext,uniphier-pxs3-usb3-ssphy",
.data = &uniphier_ld20_data,
},
{
.compatible = "socionext,uniphier-nx1-usb3-ssphy",
.data = &uniphier_ld20_data,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uniphier_u3ssphy_match);

View File

@ -672,17 +672,15 @@ static int stm32_usbphyc_probe(struct platform_device *pdev)
usbphyc->vdda1v1 = devm_regulator_get(dev, "vdda1v1");
if (IS_ERR(usbphyc->vdda1v1)) {
ret = PTR_ERR(usbphyc->vdda1v1);
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get vdda1v1 supply: %d\n", ret);
ret = dev_err_probe(dev, PTR_ERR(usbphyc->vdda1v1),
"failed to get vdda1v1 supply\n");
goto clk_disable;
}
usbphyc->vdda1v8 = devm_regulator_get(dev, "vdda1v8");
if (IS_ERR(usbphyc->vdda1v8)) {
ret = PTR_ERR(usbphyc->vdda1v8);
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get vdda1v8 supply: %d\n", ret);
ret = dev_err_probe(dev, PTR_ERR(usbphyc->vdda1v8),
"failed to get vdda1v8 supply\n");
goto clk_disable;
}

View File

@ -455,7 +455,7 @@ tegra_xusb_find_port_node(struct tegra_xusb_padctl *padctl, const char *type,
name = kasprintf(GFP_KERNEL, "%s-%u", type, index);
if (!name) {
of_node_put(ports);
return ERR_PTR(-ENOMEM);
return NULL;
}
np = of_get_child_by_name(ports, name);
kfree(name);

View File

@ -26,7 +26,7 @@ void omap_control_pcie_pcs(struct device *dev, u8 delay)
u32 val;
struct omap_control_phy *control_phy;
if (IS_ERR(dev) || !dev) {
if (IS_ERR_OR_NULL(dev)) {
pr_err("%s: invalid device\n", __func__);
return;
}
@ -61,7 +61,7 @@ void omap_control_phy_power(struct device *dev, int on)
unsigned long rate;
struct omap_control_phy *control_phy;
if (IS_ERR(dev) || !dev) {
if (IS_ERR_OR_NULL(dev)) {
pr_err("%s: invalid device\n", __func__);
return;
}
@ -202,7 +202,7 @@ void omap_control_usb_set_mode(struct device *dev,
{
struct omap_control_phy *ctrl_phy;
if (IS_ERR(dev) || !dev)
if (IS_ERR_OR_NULL(dev))
return;
ctrl_phy = dev_get_drvdata(dev);

View File

@ -6,11 +6,11 @@
#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_SERDES_NO_SSC 0
#define CDNS_SERDES_EXTERNAL_SSC 1
#define CDNS_SERDES_INTERNAL_SSC 2
/* Torrent */
#define CDNS_TORRENT_REFCLK_DRIVER 0
#define CDNS_TORRENT_DERIVED_REFCLK 1
#define CDNS_TORRENT_RECEIVED_REFCLK 2
@ -18,5 +18,6 @@
/* Sierra */
#define CDNS_SIERRA_PLL_CMNLC 0
#define CDNS_SIERRA_PLL_CMNLC1 1
#define CDNS_SIERRA_DERIVED_REFCLK 2
#endif /* _DT_BINDINGS_CADENCE_SERDES_H */

View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* This header provides constants for i.MX8 PCIe.
*/
#ifndef _DT_BINDINGS_IMX8_PCIE_H
#define _DT_BINDINGS_IMX8_PCIE_H
/* Reference clock PAD mode */
#define IMX8_PCIE_REFCLK_PAD_UNUSED 0
#define IMX8_PCIE_REFCLK_PAD_INPUT 1
#define IMX8_PCIE_REFCLK_PAD_OUTPUT 2
#endif /* _DT_BINDINGS_IMX8_PCIE_H */

View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
#ifndef __PHY_LAN966X_SERDES_H__
#define __PHY_LAN966X_SERDES_H__
#define CU(x) (x)
#define CU_MAX CU(2)
#define SERDES6G(x) (CU_MAX + 1 + (x))
#define SERDES6G_MAX SERDES6G(3)
#define RGMII(x) (SERDES6G_MAX + 1 + (x))
#define RGMII_MAX RGMII(2)
#define SERDES_MAX (RGMII_MAX + 1)
#endif