aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/Kconfig28
-rw-r--r--drivers/phy/Makefile22
-rw-r--r--drivers/phy/allwinner/Kconfig2
-rw-r--r--drivers/phy/allwinner/phy-sun4i-usb.c60
-rw-r--r--drivers/phy/allwinner/phy-sun50i-usb3.c4
-rw-r--r--drivers/phy/allwinner/phy-sun6i-mipi-dphy.c174
-rw-r--r--drivers/phy/allwinner/phy-sun9i-usb.c4
-rw-r--r--drivers/phy/amlogic/Kconfig60
-rw-r--r--drivers/phy/amlogic/Makefile14
-rw-r--r--drivers/phy/amlogic/phy-meson-axg-mipi-dphy.c414
-rw-r--r--drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c255
-rw-r--r--drivers/phy/amlogic/phy-meson-axg-pcie.c190
-rw-r--r--drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c171
-rw-r--r--drivers/phy/amlogic/phy-meson-g12a-usb2.c91
-rw-r--r--drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c28
-rw-r--r--drivers/phy/amlogic/phy-meson-gxl-usb2.c12
-rw-r--r--drivers/phy/amlogic/phy-meson-gxl-usb3.c283
-rw-r--r--drivers/phy/amlogic/phy-meson8-hdmi-tx.c160
-rw-r--r--drivers/phy/amlogic/phy-meson8b-usb2.c166
-rw-r--r--drivers/phy/broadcom/Kconfig20
-rw-r--r--drivers/phy/broadcom/Makefile1
-rw-r--r--drivers/phy/broadcom/phy-bcm-cygnus-pcie.c18
-rw-r--r--drivers/phy/broadcom/phy-bcm-kona-usb2.c4
-rw-r--r--drivers/phy/broadcom/phy-bcm-ns-usb2.c56
-rw-r--r--drivers/phy/broadcom/phy-bcm-ns-usb3.c171
-rw-r--r--drivers/phy/broadcom/phy-bcm-ns2-pcie.c20
-rw-r--r--drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c42
-rw-r--r--drivers/phy/broadcom/phy-bcm-sr-pcie.c5
-rw-r--r--drivers/phy/broadcom/phy-bcm-sr-usb.c80
-rw-r--r--drivers/phy/broadcom/phy-bcm63xx-usbh.c457
-rw-r--r--drivers/phy/broadcom/phy-brcm-sata.c42
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c51
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb-init.c38
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb-init.h5
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb.c81
-rw-r--r--drivers/phy/cadence/Kconfig25
-rw-r--r--drivers/phy/cadence/Makefile4
-rw-r--r--drivers/phy/cadence/cdns-dphy-rx.c255
-rw-r--r--drivers/phy/cadence/cdns-dphy.c105
-rw-r--r--drivers/phy/cadence/phy-cadence-dp.c541
-rw-r--r--drivers/phy/cadence/phy-cadence-salvo.c318
-rw-r--r--drivers/phy/cadence/phy-cadence-sierra.c2020
-rw-r--r--drivers/phy/cadence/phy-cadence-torrent.c4721
-rw-r--r--drivers/phy/freescale/Kconfig33
-rw-r--r--drivers/phy/freescale/Makefile3
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c280
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8m-pcie.c244
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8mq-usb.c83
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c450
-rw-r--r--drivers/phy/freescale/phy-fsl-lynx-28g.c623
-rw-r--r--drivers/phy/hisilicon/Kconfig20
-rw-r--r--drivers/phy/hisilicon/Makefile2
-rw-r--r--drivers/phy/hisilicon/phy-hi3660-usb3.c2
-rw-r--r--drivers/phy/hisilicon/phy-hi3670-pcie.c845
-rw-r--r--drivers/phy/hisilicon/phy-hi3670-usb3.c661
-rw-r--r--drivers/phy/hisilicon/phy-hi6220-usb.c2
-rw-r--r--drivers/phy/hisilicon/phy-hisi-inno-usb2.c10
-rw-r--r--drivers/phy/hisilicon/phy-hix5hd2-sata.c2
-rw-r--r--drivers/phy/ingenic/Kconfig13
-rw-r--r--drivers/phy/ingenic/Makefile2
-rw-r--r--drivers/phy/ingenic/phy-ingenic-usb.c389
-rw-r--r--drivers/phy/intel/Kconfig55
-rw-r--r--drivers/phy/intel/Makefile6
-rw-r--r--drivers/phy/intel/phy-intel-keembay-emmc.c308
-rw-r--r--drivers/phy/intel/phy-intel-keembay-usb.c301
-rw-r--r--drivers/phy/intel/phy-intel-lgm-combo.c619
-rw-r--r--drivers/phy/intel/phy-intel-lgm-emmc.c (renamed from drivers/phy/intel/phy-intel-emmc.c)0
-rw-r--r--drivers/phy/intel/phy-intel-thunderbay-emmc.c509
-rw-r--r--drivers/phy/lantiq/phy-lantiq-rcu-usb2.c12
-rw-r--r--drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c6
-rw-r--r--drivers/phy/marvell/Kconfig24
-rw-r--r--drivers/phy/marvell/Makefile2
-rw-r--r--drivers/phy/marvell/phy-armada375-usb2.c4
-rw-r--r--drivers/phy/marvell/phy-armada38x-comphy.c45
-rw-r--r--drivers/phy/marvell/phy-berlin-usb.c4
-rw-r--r--drivers/phy/marvell/phy-mmp3-hsic.c80
-rw-r--r--drivers/phy/marvell/phy-mmp3-usb.c4
-rw-r--r--drivers/phy/marvell/phy-mvebu-a3700-comphy.c1303
-rw-r--r--drivers/phy/marvell/phy-mvebu-a3700-utmi.c2
-rw-r--r--drivers/phy/marvell/phy-mvebu-cp110-comphy.c39
-rw-r--r--drivers/phy/marvell/phy-mvebu-cp110-utmi.c384
-rw-r--r--drivers/phy/marvell/phy-mvebu-sata.c4
-rw-r--r--drivers/phy/marvell/phy-pxa-28nm-hsic.c44
-rw-r--r--drivers/phy/marvell/phy-pxa-28nm-usb2.c37
-rw-r--r--drivers/phy/marvell/phy-pxa-usb.c4
-rw-r--r--drivers/phy/mediatek/Kconfig45
-rw-r--r--drivers/phy/mediatek/Makefile12
-rw-r--r--drivers/phy/mediatek/phy-mtk-dp.c202
-rw-r--r--drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c227
-rw-r--r--drivers/phy/mediatek/phy-mtk-hdmi-mt8173.c254
-rw-r--r--drivers/phy/mediatek/phy-mtk-hdmi.c166
-rw-r--r--drivers/phy/mediatek/phy-mtk-hdmi.h49
-rw-r--r--drivers/phy/mediatek/phy-mtk-io.h46
-rw-r--r--drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8173.c292
-rw-r--r--drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8183.c177
-rw-r--r--drivers/phy/mediatek/phy-mtk-mipi-dsi.c211
-rw-r--r--drivers/phy/mediatek/phy-mtk-mipi-dsi.h48
-rw-r--r--drivers/phy/mediatek/phy-mtk-pcie.c266
-rw-r--r--drivers/phy/mediatek/phy-mtk-tphy.c845
-rw-r--r--drivers/phy/mediatek/phy-mtk-ufs.c126
-rw-r--r--drivers/phy/mediatek/phy-mtk-xsphy.c144
-rw-r--r--drivers/phy/microchip/Kconfig21
-rw-r--r--drivers/phy/microchip/Makefile7
-rw-r--r--drivers/phy/microchip/lan966x_serdes.c623
-rw-r--r--drivers/phy/microchip/lan966x_serdes_regs.h251
-rw-r--r--drivers/phy/microchip/sparx5_serdes.c2517
-rw-r--r--drivers/phy/microchip/sparx5_serdes.h136
-rw-r--r--drivers/phy/microchip/sparx5_serdes_regs.h2695
-rw-r--r--drivers/phy/motorola/phy-cpcap-usb.c36
-rw-r--r--drivers/phy/motorola/phy-mapphone-mdm6600.c6
-rw-r--r--drivers/phy/phy-can-transceiver.c168
-rw-r--r--drivers/phy/phy-core-mipi-dphy.c12
-rw-r--r--drivers/phy/phy-core.c95
-rw-r--r--drivers/phy/phy-lgm-usb.c284
-rw-r--r--drivers/phy/phy-xgene.c9
-rw-r--r--drivers/phy/qualcomm/Kconfig81
-rw-r--r--drivers/phy/qualcomm/Makefile17
-rw-r--r--drivers/phy/qualcomm/phy-qcom-apq8064-sata.c25
-rw-r--r--drivers/phy/qualcomm/phy-qcom-edp.c864
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c146
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c4
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c566
-rw-r--r--drivers/phy/qualcomm/phy-qcom-pcie2.c5
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-combo.c2928
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c935
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcie-qhp.h123
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcie.c2408
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-misc-v3.h17
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4.h72
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4_20.h17
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5.h16
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5_20.h17
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v3.h18
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v4.h31
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v5.h27
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v4.h34
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v5.h36
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v2.h46
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v3.h145
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4.h135
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4_20.h15
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v5.h30
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v3.h111
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v4.h123
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v5.h124
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com.h140
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-pll.h66
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v3.h68
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4.h233
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4_20.h43
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5.h231
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_20.h60
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_5nm.h333
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx.h205
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-ufs.c1265
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-usb.c2825
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp.c2257
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp.h505
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qusb2.c315
-rw-r--r--drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c622
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ufs-i.h131
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c172
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h168
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c226
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h226
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ufs.c648
-rw-r--r--drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c438
-rw-r--r--drivers/phy/qualcomm/phy-qcom-usb-hs.c5
-rw-r--r--drivers/phy/qualcomm/phy-qcom-usb-hsic.c8
-rw-r--r--drivers/phy/qualcomm/phy-qcom-usb-ss.c246
-rw-r--r--drivers/phy/ralink/Kconfig8
-rw-r--r--drivers/phy/ralink/Makefile1
-rw-r--r--drivers/phy/ralink/phy-mt7621-pci.c360
-rw-r--r--drivers/phy/ralink/phy-ralink-usb.c6
-rw-r--r--drivers/phy/renesas/phy-rcar-gen2.c4
-rw-r--r--drivers/phy/renesas/phy-rcar-gen3-pcie.c4
-rw-r--r--drivers/phy/renesas/phy-rcar-gen3-usb2.c158
-rw-r--r--drivers/phy/renesas/phy-rcar-gen3-usb3.c4
-rw-r--r--drivers/phy/rockchip/Kconfig39
-rw-r--r--drivers/phy/rockchip/Makefile4
-rw-r--r--drivers/phy/rockchip/phy-rockchip-dphy-rx0.c384
-rw-r--r--drivers/phy/rockchip/phy-rockchip-emmc.c37
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-csidphy.c483
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c219
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-hdmi.c8
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-usb2.c454
-rw-r--r--drivers/phy/rockchip/phy-rockchip-naneng-combphy.c581
-rw-r--r--drivers/phy/rockchip/phy-rockchip-snps-pcie3.c322
-rw-r--r--drivers/phy/rockchip/phy-rockchip-typec.c9
-rw-r--r--drivers/phy/samsung/Kconfig25
-rw-r--r--drivers/phy/samsung/Makefile5
-rw-r--r--drivers/phy/samsung/phy-exynos-dp-video.c4
-rw-r--r--drivers/phy/samsung/phy-exynos-mipi-video.c4
-rw-r--r--drivers/phy/samsung/phy-exynos-pcie.c311
-rw-r--r--drivers/phy/samsung/phy-exynos5-usbdrd.c59
-rw-r--r--drivers/phy/samsung/phy-exynos5250-sata.c26
-rw-r--r--drivers/phy/samsung/phy-exynos5250-usb2.c48
-rw-r--r--drivers/phy/samsung/phy-exynos7-ufs.c85
-rw-r--r--drivers/phy/samsung/phy-exynosautov9-ufs.c74
-rw-r--r--drivers/phy/samsung/phy-fsd-ufs.c63
-rw-r--r--drivers/phy/samsung/phy-s5pv210-usb2.c4
-rw-r--r--drivers/phy/samsung/phy-samsung-ufs.c329
-rw-r--r--drivers/phy/samsung/phy-samsung-ufs.h146
-rw-r--r--drivers/phy/samsung/phy-samsung-usb2.c10
-rw-r--r--drivers/phy/samsung/phy-samsung-usb2.h3
-rw-r--r--drivers/phy/socionext/Kconfig10
-rw-r--r--drivers/phy/socionext/Makefile1
-rw-r--r--drivers/phy/socionext/phy-uniphier-ahci.c514
-rw-r--r--drivers/phy/socionext/phy-uniphier-pcie.c181
-rw-r--r--drivers/phy/socionext/phy-uniphier-usb3hs.c96
-rw-r--r--drivers/phy/socionext/phy-uniphier-usb3ss.c22
-rw-r--r--drivers/phy/st/Kconfig1
-rw-r--r--drivers/phy/st/phy-stm32-usbphyc.c516
-rw-r--r--drivers/phy/sunplus/Kconfig12
-rw-r--r--drivers/phy/sunplus/Makefile2
-rw-r--r--drivers/phy/sunplus/phy-sunplus-usb2.c296
-rw-r--r--drivers/phy/tegra/Kconfig5
-rw-r--r--drivers/phy/tegra/Makefile1
-rw-r--r--drivers/phy/tegra/phy-tegra194-p2u.c52
-rw-r--r--drivers/phy/tegra/xusb-tegra124.c6
-rw-r--r--drivers/phy/tegra/xusb-tegra186.c819
-rw-r--r--drivers/phy/tegra/xusb-tegra210.c1659
-rw-r--r--drivers/phy/tegra/xusb.c403
-rw-r--r--drivers/phy/tegra/xusb.h49
-rw-r--r--drivers/phy/ti/Kconfig3
-rw-r--r--drivers/phy/ti/phy-am654-serdes.c439
-rw-r--r--drivers/phy/ti/phy-dm816x-usb.c39
-rw-r--r--drivers/phy/ti/phy-gmii-sel.c217
-rw-r--r--drivers/phy/ti/phy-j721e-wiz.c802
-rw-r--r--drivers/phy/ti/phy-omap-control.c23
-rw-r--r--drivers/phy/ti/phy-omap-usb2.c149
-rw-r--r--drivers/phy/ti/phy-ti-pipe3.c21
-rw-r--r--drivers/phy/ti/phy-tusb1210.c469
-rw-r--r--drivers/phy/ti/phy-twl4030-usb.c23
-rw-r--r--drivers/phy/xilinx/Kconfig13
-rw-r--r--drivers/phy/xilinx/Makefile3
-rw-r--r--drivers/phy/xilinx/phy-zynqmp.c1038
237 files changed, 53279 insertions, 8086 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index b3ed94b98d9b..7bd00a11d074 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -37,7 +37,7 @@ config PHY_LPC18XX_USB_OTG
config PHY_PISTACHIO_USB
tristate "IMG Pistachio USB2.0 PHY driver"
- depends on MACH_PISTACHIO
+ depends on MIPS || COMPILE_TEST
select GENERIC_PHY
help
Enable this to support the USB2.0 PHY on the IMG Pistachio SoC.
@@ -49,15 +49,39 @@ config PHY_XGENE
help
This option enables support for APM X-Gene SoC multi-purpose PHY.
+config USB_LGM_PHY
+ tristate "INTEL Lightning Mountain USB PHY Driver"
+ depends on USB_SUPPORT
+ depends on X86 || COMPILE_TEST
+ select USB_PHY
+ select REGULATOR
+ select REGULATOR_FIXED_VOLTAGE
+ help
+ Enable this to support Intel DWC3 PHY USB phy. This driver provides
+ interface to interact with USB GEN-II and USB 3.x PHY that is part
+ of the Intel network SOC.
+
+config PHY_CAN_TRANSCEIVER
+ tristate "CAN transceiver PHY"
+ select GENERIC_PHY
+ select MULTIPLEXER
+ help
+ This option enables support for CAN transceivers as a PHY. This
+ driver provides function for putting the transceivers in various
+ functional modes using gpios and sets the attribute max link
+ rate, for CAN drivers.
+
source "drivers/phy/allwinner/Kconfig"
source "drivers/phy/amlogic/Kconfig"
source "drivers/phy/broadcom/Kconfig"
source "drivers/phy/cadence/Kconfig"
source "drivers/phy/freescale/Kconfig"
source "drivers/phy/hisilicon/Kconfig"
+source "drivers/phy/ingenic/Kconfig"
source "drivers/phy/lantiq/Kconfig"
source "drivers/phy/marvell/Kconfig"
source "drivers/phy/mediatek/Kconfig"
+source "drivers/phy/microchip/Kconfig"
source "drivers/phy/motorola/Kconfig"
source "drivers/phy/mscc/Kconfig"
source "drivers/phy/qualcomm/Kconfig"
@@ -67,8 +91,10 @@ source "drivers/phy/rockchip/Kconfig"
source "drivers/phy/samsung/Kconfig"
source "drivers/phy/socionext/Kconfig"
source "drivers/phy/st/Kconfig"
+source "drivers/phy/sunplus/Kconfig"
source "drivers/phy/tegra/Kconfig"
source "drivers/phy/ti/Kconfig"
source "drivers/phy/intel/Kconfig"
+source "drivers/phy/xilinx/Kconfig"
endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 310c149a9df5..54f312c10a40 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -5,27 +5,33 @@
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o
+obj-$(CONFIG_PHY_CAN_TRANSCEIVER) += phy-can-transceiver.o
obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
-obj-$(CONFIG_ARCH_SUNXI) += allwinner/
-obj-$(CONFIG_ARCH_MESON) += amlogic/
-obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
-obj-$(CONFIG_ARCH_RENESAS) += renesas/
-obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
-obj-$(CONFIG_ARCH_TEGRA) += tegra/
-obj-y += broadcom/ \
+obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o
+obj-y += allwinner/ \
+ amlogic/ \
+ broadcom/ \
cadence/ \
freescale/ \
hisilicon/ \
+ ingenic/ \
intel/ \
lantiq/ \
marvell/ \
+ mediatek/ \
+ microchip/ \
motorola/ \
mscc/ \
qualcomm/ \
ralink/ \
+ renesas/ \
+ rockchip/ \
samsung/ \
socionext/ \
st/ \
- ti/
+ sunplus/ \
+ tegra/ \
+ ti/ \
+ xilinx/
diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig
index e760d89d3976..fb584518b2d0 100644
--- a/drivers/phy/allwinner/Kconfig
+++ b/drivers/phy/allwinner/Kconfig
@@ -22,7 +22,7 @@ config PHY_SUN4I_USB
config PHY_SUN6I_MIPI_DPHY
tristate "Allwinner A31 MIPI D-PHY Support"
depends on ARCH_SUNXI || COMPILE_TEST
- depends on HAS_IOMEM
+ depends on HAS_IOMEM && COMMON_CLK
depends on RESET_CONTROLLER
select GENERIC_PHY
select GENERIC_PHY_MIPI_DPHY
diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c
index 856927382248..3a3831f6059a 100644
--- a/drivers/phy/allwinner/phy-sun4i-usb.c
+++ b/drivers/phy/allwinner/phy-sun4i-usb.c
@@ -7,7 +7,7 @@
* Based on code from
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
*
- * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
+ * Modelled after: Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
*/
@@ -16,6 +16,7 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/extcon-provider.h>
+#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -42,7 +43,7 @@
#define REG_PHYCTL_A33 0x10
#define REG_PHY_OTGCTL 0x20
-#define REG_PMU_UNK1 0x10
+#define REG_HCI_PHY_CTL 0x10
#define PHYCTL_DATA BIT(7)
@@ -81,6 +82,7 @@
/* A83T specific control bits for PHY0 */
#define PHY_CTL_VBUSVLDEXT BIT(5)
#define PHY_CTL_SIDDQ BIT(3)
+#define PHY_CTL_H3_SIDDQ BIT(1)
/* A83T specific control bits for PHY2 HSIC */
#define SUNXI_EHCI_HS_FORCE BIT(20)
@@ -114,9 +116,9 @@ struct sun4i_usb_phy_cfg {
int hsic_index;
enum sun4i_usb_phy_type type;
u32 disc_thresh;
+ u32 hci_phy_ctl_clear;
u8 phyctl_offset;
bool dedicated_clocks;
- bool enable_pmu_unk1;
bool phy0_dual_route;
int missing_phys;
};
@@ -287,6 +289,12 @@ static int sun4i_usb_phy_init(struct phy *_phy)
return ret;
}
+ if (phy->pmu && data->cfg->hci_phy_ctl_clear) {
+ val = readl(phy->pmu + REG_HCI_PHY_CTL);
+ val &= ~data->cfg->hci_phy_ctl_clear;
+ writel(val, phy->pmu + REG_HCI_PHY_CTL);
+ }
+
if (data->cfg->type == sun8i_a83t_phy ||
data->cfg->type == sun50i_h6_phy) {
if (phy->index == 0) {
@@ -296,11 +304,6 @@ static int sun4i_usb_phy_init(struct phy *_phy)
writel(val, data->base + data->cfg->phyctl_offset);
}
} else {
- if (phy->pmu && data->cfg->enable_pmu_unk1) {
- val = readl(phy->pmu + REG_PMU_UNK1);
- writel(val & ~2, phy->pmu + REG_PMU_UNK1);
- }
-
/* Enable USB 45 Ohm resistor calibration */
if (phy->index == 0)
sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
@@ -545,13 +548,14 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
struct sun4i_usb_phy_data *data =
container_of(work, struct sun4i_usb_phy_data, detect.work);
struct phy *phy0 = data->phys[0].phy;
- struct sun4i_usb_phy *phy = phy_get_drvdata(phy0);
+ struct sun4i_usb_phy *phy;
bool force_session_end, id_notify = false, vbus_notify = false;
int id_det, vbus_det;
- if (phy0 == NULL)
+ if (!phy0)
return;
+ phy = phy_get_drvdata(phy0);
id_det = sun4i_usb_phy0_get_id_det(data);
vbus_det = sun4i_usb_phy0_get_vbus_det(data);
@@ -684,7 +688,6 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct phy_provider *phy_provider;
- struct resource *res;
int i, ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
@@ -698,8 +701,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
if (!data->cfg)
return -EINVAL;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
- data->base = devm_ioremap_resource(dev, res);
+ data->base = devm_platform_ioremap_resource_byname(pdev, "phy_ctrl");
if (IS_ERR(data->base))
return PTR_ERR(data->base);
@@ -766,7 +768,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
if (data->cfg->dedicated_clocks)
snprintf(name, sizeof(name), "usb%d_phy", i);
else
- strlcpy(name, "usb_phy", sizeof(name));
+ strscpy(name, "usb_phy", sizeof(name));
phy->clk = devm_clk_get(dev, name);
if (IS_ERR(phy->clk)) {
@@ -794,9 +796,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */
snprintf(name, sizeof(name), "pmu%d", i);
- res = platform_get_resource_byname(pdev,
- IORESOURCE_MEM, name);
- phy->pmu = devm_ioremap_resource(dev, res);
+ phy->pmu = devm_platform_ioremap_resource_byname(pdev, name);
if (IS_ERR(phy->pmu))
return PTR_ERR(phy->pmu);
}
@@ -865,7 +865,6 @@ static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
- .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
@@ -874,7 +873,6 @@ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
.disc_thresh = 2,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
- .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
@@ -883,7 +881,6 @@ static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true,
- .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
@@ -892,7 +889,6 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
.disc_thresh = 2,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
- .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
@@ -901,7 +897,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true,
- .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
@@ -910,7 +905,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
- .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = {
@@ -927,7 +921,7 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
- .enable_pmu_unk1 = true,
+ .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
.phy0_dual_route = true,
};
@@ -937,7 +931,7 @@ static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
- .enable_pmu_unk1 = true,
+ .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
.phy0_dual_route = true,
};
@@ -947,7 +941,16 @@ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
- .enable_pmu_unk1 = true,
+ .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
+ .phy0_dual_route = true,
+};
+
+static const struct sun4i_usb_phy_cfg sun20i_d1_cfg = {
+ .num_phys = 2,
+ .type = sun50i_h6_phy,
+ .phyctl_offset = REG_PHYCTL_A33,
+ .dedicated_clocks = true,
+ .hci_phy_ctl_clear = PHY_CTL_SIDDQ,
.phy0_dual_route = true,
};
@@ -957,17 +960,15 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
- .enable_pmu_unk1 = true,
+ .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
.phy0_dual_route = true,
};
static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = {
.num_phys = 4,
.type = sun50i_h6_phy,
- .disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
- .enable_pmu_unk1 = true,
.phy0_dual_route = true,
.missing_phys = BIT(1) | BIT(2),
};
@@ -983,6 +984,7 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = {
{ .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
{ .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg },
{ .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg },
+ { .compatible = "allwinner,sun20i-d1-usb-phy", .data = &sun20i_d1_cfg },
{ .compatible = "allwinner,sun50i-a64-usb-phy",
.data = &sun50i_a64_cfg},
{ .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg },
diff --git a/drivers/phy/allwinner/phy-sun50i-usb3.c b/drivers/phy/allwinner/phy-sun50i-usb3.c
index b1c04f71a31d..84055b720016 100644
--- a/drivers/phy/allwinner/phy-sun50i-usb3.c
+++ b/drivers/phy/allwinner/phy-sun50i-usb3.c
@@ -134,7 +134,6 @@ static int sun50i_usb3_phy_probe(struct platform_device *pdev)
struct sun50i_usb3_phy *phy;
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
- struct resource *res;
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
@@ -153,8 +152,7 @@ static int sun50i_usb3_phy_probe(struct platform_device *pdev)
return PTR_ERR(phy->reset);
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- phy->regs = devm_ioremap_resource(dev, res);
+ phy->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(phy->regs))
return PTR_ERR(phy->regs);
diff --git a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
index 79c8af5c7c1d..3900f1650851 100644
--- a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
+++ b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
@@ -24,6 +24,14 @@
#define SUN6I_DPHY_TX_CTL_REG 0x04
#define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT BIT(28)
+#define SUN6I_DPHY_RX_CTL_REG 0x08
+#define SUN6I_DPHY_RX_CTL_EN_DBC BIT(31)
+#define SUN6I_DPHY_RX_CTL_RX_CLK_FORCE BIT(24)
+#define SUN6I_DPHY_RX_CTL_RX_D3_FORCE BIT(23)
+#define SUN6I_DPHY_RX_CTL_RX_D2_FORCE BIT(22)
+#define SUN6I_DPHY_RX_CTL_RX_D1_FORCE BIT(21)
+#define SUN6I_DPHY_RX_CTL_RX_D0_FORCE BIT(20)
+
#define SUN6I_DPHY_TX_TIME0_REG 0x10
#define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n) (((n) & 0xff) << 24)
#define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n) (((n) & 0xff) << 16)
@@ -44,12 +52,29 @@
#define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n) (((n) & 0xff) << 8)
#define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n) ((n) & 0xff)
+#define SUN6I_DPHY_RX_TIME0_REG 0x30
+#define SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(n) (((n) & 0xff) << 24)
+#define SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(n) (((n) & 0xff) << 16)
+#define SUN6I_DPHY_RX_TIME0_LP_RX(n) (((n) & 0xff) << 8)
+
+#define SUN6I_DPHY_RX_TIME1_REG 0x34
+#define SUN6I_DPHY_RX_TIME1_RX_DLY(n) (((n) & 0xfff) << 20)
+#define SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(n) ((n) & 0xfffff)
+
+#define SUN6I_DPHY_RX_TIME2_REG 0x38
+#define SUN6I_DPHY_RX_TIME2_HS_RX_ANA1(n) (((n) & 0xff) << 8)
+#define SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(n) ((n) & 0xff)
+
+#define SUN6I_DPHY_RX_TIME3_REG 0x40
+#define SUN6I_DPHY_RX_TIME3_LPRST_DLY(n) (((n) & 0xffff) << 16)
+
#define SUN6I_DPHY_ANA0_REG 0x4c
#define SUN6I_DPHY_ANA0_REG_PWS BIT(31)
#define SUN6I_DPHY_ANA0_REG_DMPC BIT(28)
#define SUN6I_DPHY_ANA0_REG_DMPD(n) (((n) & 0xf) << 24)
#define SUN6I_DPHY_ANA0_REG_SLV(n) (((n) & 7) << 12)
#define SUN6I_DPHY_ANA0_REG_DEN(n) (((n) & 0xf) << 8)
+#define SUN6I_DPHY_ANA0_REG_SFB(n) (((n) & 3) << 2)
#define SUN6I_DPHY_ANA1_REG 0x50
#define SUN6I_DPHY_ANA1_REG_VTTMODE BIT(31)
@@ -84,6 +109,11 @@
#define SUN6I_DPHY_DBG5_REG 0xf4
+enum sun6i_dphy_direction {
+ SUN6I_DPHY_DIRECTION_TX,
+ SUN6I_DPHY_DIRECTION_RX,
+};
+
struct sun6i_dphy {
struct clk *bus_clk;
struct clk *mod_clk;
@@ -92,6 +122,8 @@ struct sun6i_dphy {
struct phy *phy;
struct phy_configure_opts_mipi_dphy config;
+
+ enum sun6i_dphy_direction direction;
};
static int sun6i_dphy_init(struct phy *phy)
@@ -119,9 +151,8 @@ static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
return 0;
}
-static int sun6i_dphy_power_on(struct phy *phy)
+static int sun6i_dphy_tx_power_on(struct sun6i_dphy *dphy)
{
- struct sun6i_dphy *dphy = phy_get_drvdata(phy);
u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
@@ -211,12 +242,129 @@ static int sun6i_dphy_power_on(struct phy *phy)
return 0;
}
+static int sun6i_dphy_rx_power_on(struct sun6i_dphy *dphy)
+{
+ /* Physical clock rate is actually half of symbol rate with DDR. */
+ unsigned long mipi_symbol_rate = dphy->config.hs_clk_rate;
+ unsigned long dphy_clk_rate;
+ unsigned int rx_dly;
+ unsigned int lprst_dly;
+ u32 value;
+
+ dphy_clk_rate = clk_get_rate(dphy->mod_clk);
+ if (!dphy_clk_rate)
+ return -EINVAL;
+
+ /* Hardcoded timing parameters from the Allwinner BSP. */
+ regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME0_REG,
+ SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(255) |
+ SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(255) |
+ SUN6I_DPHY_RX_TIME0_LP_RX(255));
+
+ /*
+ * Formula from the Allwinner BSP, with hardcoded coefficients
+ * (probably internal divider/multiplier).
+ */
+ rx_dly = 8 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 8));
+
+ /*
+ * The Allwinner BSP has an alternative formula for LP_RX_ULPS_WP:
+ * lp_ulps_wp_cnt = lp_ulps_wp_ms * lp_clk / 1000
+ * but does not use it and hardcodes 255 instead.
+ */
+ regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME1_REG,
+ SUN6I_DPHY_RX_TIME1_RX_DLY(rx_dly) |
+ SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(255));
+
+ /* HS_RX_ANA0 value is hardcoded in the Allwinner BSP. */
+ regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME2_REG,
+ SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(4));
+
+ /*
+ * Formula from the Allwinner BSP, with hardcoded coefficients
+ * (probably internal divider/multiplier).
+ */
+ lprst_dly = 4 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 2));
+
+ regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME3_REG,
+ SUN6I_DPHY_RX_TIME3_LPRST_DLY(lprst_dly));
+
+ /* Analog parameters are hardcoded in the Allwinner BSP. */
+ regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
+ SUN6I_DPHY_ANA0_REG_PWS |
+ SUN6I_DPHY_ANA0_REG_SLV(7) |
+ SUN6I_DPHY_ANA0_REG_SFB(2));
+
+ regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
+ SUN6I_DPHY_ANA1_REG_SVTT(4));
+
+ regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
+ SUN6I_DPHY_ANA4_REG_DMPLVC |
+ SUN6I_DPHY_ANA4_REG_DMPLVD(1));
+
+ regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
+ SUN6I_DPHY_ANA2_REG_ENIB);
+
+ regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
+ SUN6I_DPHY_ANA3_EN_LDOR |
+ SUN6I_DPHY_ANA3_EN_LDOC |
+ SUN6I_DPHY_ANA3_EN_LDOD);
+
+ /*
+ * Delay comes from the Allwinner BSP, likely for internal regulator
+ * ramp-up.
+ */
+ udelay(3);
+
+ value = SUN6I_DPHY_RX_CTL_EN_DBC | SUN6I_DPHY_RX_CTL_RX_CLK_FORCE;
+
+ /*
+ * Rx data lane force-enable bits are used as regular RX enable by the
+ * Allwinner BSP.
+ */
+ if (dphy->config.lanes >= 1)
+ value |= SUN6I_DPHY_RX_CTL_RX_D0_FORCE;
+ if (dphy->config.lanes >= 2)
+ value |= SUN6I_DPHY_RX_CTL_RX_D1_FORCE;
+ if (dphy->config.lanes >= 3)
+ value |= SUN6I_DPHY_RX_CTL_RX_D2_FORCE;
+ if (dphy->config.lanes == 4)
+ value |= SUN6I_DPHY_RX_CTL_RX_D3_FORCE;
+
+ regmap_write(dphy->regs, SUN6I_DPHY_RX_CTL_REG, value);
+
+ regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
+ SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
+ SUN6I_DPHY_GCTL_EN);
+
+ return 0;
+}
+
+static int sun6i_dphy_power_on(struct phy *phy)
+{
+ struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+
+ switch (dphy->direction) {
+ case SUN6I_DPHY_DIRECTION_TX:
+ return sun6i_dphy_tx_power_on(dphy);
+ case SUN6I_DPHY_DIRECTION_RX:
+ return sun6i_dphy_rx_power_on(dphy);
+ default:
+ return -EINVAL;
+ }
+}
+
static int sun6i_dphy_power_off(struct phy *phy)
{
struct sun6i_dphy *dphy = phy_get_drvdata(phy);
- regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
- SUN6I_DPHY_ANA1_REG_VTTMODE, 0);
+ regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG, 0);
+
+ regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, 0);
+ regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG, 0);
+ regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG, 0);
+ regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG, 0);
+ regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG, 0);
return 0;
}
@@ -233,7 +381,7 @@ static int sun6i_dphy_exit(struct phy *phy)
}
-static struct phy_ops sun6i_dphy_ops = {
+static const struct phy_ops sun6i_dphy_ops = {
.configure = sun6i_dphy_configure,
.power_on = sun6i_dphy_power_on,
.power_off = sun6i_dphy_power_off,
@@ -241,7 +389,7 @@ static struct phy_ops sun6i_dphy_ops = {
.exit = sun6i_dphy_exit,
};
-static struct regmap_config sun6i_dphy_regmap_config = {
+static const struct regmap_config sun6i_dphy_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
@@ -253,15 +401,15 @@ static int sun6i_dphy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct sun6i_dphy *dphy;
- struct resource *res;
+ const char *direction;
void __iomem *regs;
+ int ret;
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
if (!dphy)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(&pdev->dev, res);
+ regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs)) {
dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n");
return PTR_ERR(regs);
@@ -292,6 +440,14 @@ static int sun6i_dphy_probe(struct platform_device *pdev)
return PTR_ERR(dphy->phy);
}
+ dphy->direction = SUN6I_DPHY_DIRECTION_TX;
+
+ ret = of_property_read_string(pdev->dev.of_node, "allwinner,direction",
+ &direction);
+
+ if (!ret && !strncmp(direction, "rx", 2))
+ dphy->direction = SUN6I_DPHY_DIRECTION_RX;
+
phy_set_drvdata(dphy->phy, dphy);
phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
diff --git a/drivers/phy/allwinner/phy-sun9i-usb.c b/drivers/phy/allwinner/phy-sun9i-usb.c
index fc6784dd7fa0..2f9e60c188b8 100644
--- a/drivers/phy/allwinner/phy-sun9i-usb.c
+++ b/drivers/phy/allwinner/phy-sun9i-usb.c
@@ -117,7 +117,6 @@ static int sun9i_usb_phy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct phy_provider *phy_provider;
- struct resource *res;
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
@@ -156,8 +155,7 @@ static int sun9i_usb_phy_probe(struct platform_device *pdev)
}
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- phy->pmu = devm_ioremap_resource(dev, res);
+ phy->pmu = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(phy->pmu))
return PTR_ERR(phy->pmu);
diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig
index af774ac2b934..ce7ba3eb2a8e 100644
--- a/drivers/phy/amlogic/Kconfig
+++ b/drivers/phy/amlogic/Kconfig
@@ -2,13 +2,24 @@
#
# 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 and GXBB USB2 PHY driver"
+ tristate "Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY driver"
default ARCH_MESON
depends on OF && (ARCH_MESON || COMPILE_TEST)
depends on USB_SUPPORT
select USB_COMMON
select GENERIC_PHY
+ select REGMAP_MMIO
help
Enable this to support the Meson USB2 PHYs found in Meson8,
Meson8b and GXBB SoCs.
@@ -26,16 +37,16 @@ config PHY_MESON_GXL_USB2
GXL and GXM SoCs.
If unsure, say N.
-config PHY_MESON_GXL_USB3
- tristate "Meson GXL and GXM USB3 PHY drivers"
+config PHY_MESON_G12A_MIPI_DPHY_ANALOG
+ tristate "Meson G12A MIPI Analog DPHY driver"
default ARCH_MESON
depends on OF && (ARCH_MESON || COMPILE_TEST)
- depends on USB_SUPPORT
select GENERIC_PHY
- select REGMAP_MMIO
+ select MFD_SYSCON
+ select GENERIC_PHY_MIPI_DPHY
help
- Enable this to support the Meson USB3 PHY and OTG detection
- IP block found in Meson GXL and GXM SoCs.
+ Enable this to support the Meson MIPI Analog DPHY found in Meson G12A
+ SoCs.
If unsure, say N.
config PHY_MESON_G12A_USB2
@@ -59,3 +70,38 @@ config PHY_MESON_G12A_USB3_PCIE
Enable this to support the Meson USB3 + PCIE Combo PHY found
in Meson G12A SoCs.
If unsure, say N.
+
+config PHY_MESON_AXG_PCIE
+ tristate "Meson AXG PCIE PHY driver"
+ default ARCH_MESON
+ depends on OF && (ARCH_MESON || COMPILE_TEST)
+ select GENERIC_PHY
+ select REGMAP_MMIO
+ help
+ Enable this to support the Meson MIPI + PCIE PHY found
+ in Meson AXG SoCs.
+ If unsure, say N.
+
+config PHY_MESON_AXG_MIPI_PCIE_ANALOG
+ tristate "Meson AXG MIPI + PCIE analog PHY driver"
+ default ARCH_MESON
+ depends on OF && (ARCH_MESON || COMPILE_TEST)
+ select GENERIC_PHY
+ select REGMAP_MMIO
+ select GENERIC_PHY_MIPI_DPHY
+ help
+ Enable this to support the Meson MIPI + PCIE analog PHY
+ found in Meson AXG SoCs.
+ If unsure, say N.
+
+config PHY_MESON_AXG_MIPI_DPHY
+ tristate "Meson AXG MIPI DPHY driver"
+ default ARCH_MESON
+ depends on OF && (ARCH_MESON || COMPILE_TEST)
+ select GENERIC_PHY
+ select REGMAP_MMIO
+ select GENERIC_PHY_MIPI_DPHY
+ help
+ Enable this to support the Meson MIPI DPHY found in Meson AXG
+ SoCs.
+ If unsure, say N.
diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile
index 11d1c42ac2be..91e3b9790c03 100644
--- a/drivers/phy/amlogic/Makefile
+++ b/drivers/phy/amlogic/Makefile
@@ -1,6 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
-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
-obj-$(CONFIG_PHY_MESON_GXL_USB3) += phy-meson-gxl-usb3.o
-obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE) += phy-meson-g12a-usb3-pcie.o
+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
+obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE) += phy-meson-g12a-usb3-pcie.o
+obj-$(CONFIG_PHY_MESON_G12A_MIPI_DPHY_ANALOG) += phy-meson-g12a-mipi-dphy-analog.o
+obj-$(CONFIG_PHY_MESON_AXG_PCIE) += phy-meson-axg-pcie.o
+obj-$(CONFIG_PHY_MESON_AXG_MIPI_PCIE_ANALOG) += phy-meson-axg-mipi-pcie-analog.o
+obj-$(CONFIG_PHY_MESON_AXG_MIPI_DPHY) += phy-meson-axg-mipi-dphy.o
diff --git a/drivers/phy/amlogic/phy-meson-axg-mipi-dphy.c b/drivers/phy/amlogic/phy-meson-axg-mipi-dphy.c
new file mode 100644
index 000000000000..32d1ff09befb
--- /dev/null
+++ b/drivers/phy/amlogic/phy-meson-axg-mipi-dphy.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Meson AXG MIPI DPHY driver
+ *
+ * Copyright (C) 2018 Amlogic, Inc. All rights reserved
+ * Copyright (C) 2020 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+/* [31] soft reset for the phy.
+ * 1: reset. 0: dessert the reset.
+ * [30] clock lane soft reset.
+ * [29] data byte lane 3 soft reset.
+ * [28] data byte lane 2 soft reset.
+ * [27] data byte lane 1 soft reset.
+ * [26] data byte lane 0 soft reset.
+ * [25] mipi dsi pll clock selection.
+ * 1: clock from fixed 850Mhz clock source. 0: from VID2 PLL.
+ * [12] mipi HSbyteclk enable.
+ * [11] mipi divider clk selection.
+ * 1: select the mipi DDRCLKHS from clock divider.
+ * 0: from PLL clock.
+ * [10] mipi clock divider control.
+ * 1: /4. 0: /2.
+ * [9] mipi divider output enable.
+ * [8] mipi divider counter enable.
+ * [7] PLL clock enable.
+ * [5] LPDT data endian.
+ * 1 = transfer the high bit first. 0 : transfer the low bit first.
+ * [4] HS data endian.
+ * [3] force data byte lane in stop mode.
+ * [2] force data byte lane 0 in receiver mode.
+ * [1] write 1 to sync the txclkesc input. the internal logic have to
+ * use txclkesc to decide Txvalid and Txready.
+ * [0] enalbe the MIPI DPHY TxDDRClk.
+ */
+#define MIPI_DSI_PHY_CTRL 0x0
+
+/* [31] clk lane tx_hs_en control selection.
+ * 1: from register. 0: use clk lane state machine.
+ * [30] register bit for clock lane tx_hs_en.
+ * [29] clk lane tx_lp_en contrl selection.
+ * 1: from register. 0: from clk lane state machine.
+ * [28] register bit for clock lane tx_lp_en.
+ * [27] chan0 tx_hs_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [26] register bit for chan0 tx_hs_en.
+ * [25] chan0 tx_lp_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [24] register bit from chan0 tx_lp_en.
+ * [23] chan0 rx_lp_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [22] register bit from chan0 rx_lp_en.
+ * [21] chan0 contention detection enable control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [20] register bit from chan0 contention dectection enable.
+ * [19] chan1 tx_hs_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [18] register bit for chan1 tx_hs_en.
+ * [17] chan1 tx_lp_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [16] register bit from chan1 tx_lp_en.
+ * [15] chan2 tx_hs_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [14] register bit for chan2 tx_hs_en.
+ * [13] chan2 tx_lp_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [12] register bit from chan2 tx_lp_en.
+ * [11] chan3 tx_hs_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [10] register bit for chan3 tx_hs_en.
+ * [9] chan3 tx_lp_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [8] register bit from chan3 tx_lp_en.
+ * [4] clk chan power down. this bit is also used as the power down
+ * of the whole MIPI_DSI_PHY.
+ * [3] chan3 power down.
+ * [2] chan2 power down.
+ * [1] chan1 power down.
+ * [0] chan0 power down.
+ */
+#define MIPI_DSI_CHAN_CTRL 0x4
+
+/* [24] rx turn watch dog triggered.
+ * [23] rx esc watchdog triggered.
+ * [22] mbias ready.
+ * [21] txclkesc synced and ready.
+ * [20:17] clk lane state. {mbias_ready, tx_stop, tx_ulps, tx_hs_active}
+ * [16:13] chan3 state{0, tx_stop, tx_ulps, tx_hs_active}
+ * [12:9] chan2 state.{0, tx_stop, tx_ulps, tx_hs_active}
+ * [8:5] chan1 state. {0, tx_stop, tx_ulps, tx_hs_active}
+ * [4:0] chan0 state. {TX_STOP, tx_ULPS, hs_active, direction, rxulpsesc}
+ */
+#define MIPI_DSI_CHAN_STS 0x8
+
+/* [31:24] TCLK_PREPARE.
+ * [23:16] TCLK_ZERO.
+ * [15:8] TCLK_POST.
+ * [7:0] TCLK_TRAIL.
+ */
+#define MIPI_DSI_CLK_TIM 0xc
+
+/* [31:24] THS_PREPARE.
+ * [23:16] THS_ZERO.
+ * [15:8] THS_TRAIL.
+ * [7:0] THS_EXIT.
+ */
+#define MIPI_DSI_HS_TIM 0x10
+
+/* [31:24] tTA_GET.
+ * [23:16] tTA_GO.
+ * [15:8] tTA_SURE.
+ * [7:0] tLPX.
+ */
+#define MIPI_DSI_LP_TIM 0x14
+
+/* wait time to MIPI DIS analog ready. */
+#define MIPI_DSI_ANA_UP_TIM 0x18
+
+/* TINIT. */
+#define MIPI_DSI_INIT_TIM 0x1c
+
+/* TWAKEUP. */
+#define MIPI_DSI_WAKEUP_TIM 0x20
+
+/* when in RxULPS check state, after the logic enable the analog,
+ * how long we should wait to check the lP state .
+ */
+#define MIPI_DSI_LPOK_TIM 0x24
+
+/* Watchdog for RX low power state no finished. */
+#define MIPI_DSI_LP_WCHDOG 0x28
+
+/* tMBIAS, after send power up signals to analog,
+ * how long we should wait for analog powered up.
+ */
+#define MIPI_DSI_ANA_CTRL 0x2c
+
+/* [31:8] reserved for future.
+ * [7:0] tCLK_PRE.
+ */
+#define MIPI_DSI_CLK_TIM1 0x30
+
+/* watchdog for turn around waiting time. */
+#define MIPI_DSI_TURN_WCHDOG 0x34
+
+/* When in RxULPS state, how frequency we should to check
+ * if the TX side out of ULPS state.
+ */
+#define MIPI_DSI_ULPS_CHECK 0x38
+#define MIPI_DSI_TEST_CTRL0 0x3c
+#define MIPI_DSI_TEST_CTRL1 0x40
+
+struct phy_meson_axg_mipi_dphy_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *clk;
+ struct reset_control *reset;
+ struct phy *analog;
+ struct phy_configure_opts_mipi_dphy config;
+};
+
+static const struct regmap_config phy_meson_axg_mipi_dphy_regmap_conf = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = MIPI_DSI_TEST_CTRL1,
+};
+
+static int phy_meson_axg_mipi_dphy_init(struct phy *phy)
+{
+ struct phy_meson_axg_mipi_dphy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = phy_init(priv->analog);
+ if (ret)
+ return ret;
+
+ ret = reset_control_reset(priv->reset);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int phy_meson_axg_mipi_dphy_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ struct phy_meson_axg_mipi_dphy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
+ if (ret)
+ return ret;
+
+ ret = phy_configure(priv->analog, opts);
+ if (ret)
+ return ret;
+
+ memcpy(&priv->config, opts, sizeof(priv->config));
+
+ return 0;
+}
+
+static int phy_meson_axg_mipi_dphy_power_on(struct phy *phy)
+{
+ struct phy_meson_axg_mipi_dphy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+ unsigned long temp;
+
+ ret = phy_power_on(priv->analog);
+ if (ret)
+ return ret;
+
+ /* enable phy clock */
+ regmap_write(priv->regmap, MIPI_DSI_PHY_CTRL, 0x1);
+ regmap_write(priv->regmap, MIPI_DSI_PHY_CTRL,
+ BIT(0) | /* enable the DSI PLL clock . */
+ BIT(7) | /* enable pll clock which connected to DDR clock path */
+ BIT(8)); /* enable the clock divider counter */
+
+ /* enable the divider clock out */
+ regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(9), BIT(9));
+
+ /* enable the byte clock generation. */
+ regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(12), BIT(12));
+ regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(31), BIT(31));
+ regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(31), 0);
+
+ /* Calculate lanebyteclk period in ps */
+ temp = (1000000 * 100) / (priv->config.hs_clk_rate / 1000);
+ temp = temp * 8 * 10;
+
+ regmap_write(priv->regmap, MIPI_DSI_CLK_TIM,
+ DIV_ROUND_UP(priv->config.clk_trail, temp) |
+ (DIV_ROUND_UP(priv->config.clk_post +
+ priv->config.hs_trail, temp) << 8) |
+ (DIV_ROUND_UP(priv->config.clk_zero, temp) << 16) |
+ (DIV_ROUND_UP(priv->config.clk_prepare, temp) << 24));
+ regmap_write(priv->regmap, MIPI_DSI_CLK_TIM1,
+ DIV_ROUND_UP(priv->config.clk_pre, BITS_PER_BYTE));
+
+ regmap_write(priv->regmap, MIPI_DSI_HS_TIM,
+ DIV_ROUND_UP(priv->config.hs_exit, temp) |
+ (DIV_ROUND_UP(priv->config.hs_trail, temp) << 8) |
+ (DIV_ROUND_UP(priv->config.hs_zero, temp) << 16) |
+ (DIV_ROUND_UP(priv->config.hs_prepare, temp) << 24));
+
+ regmap_write(priv->regmap, MIPI_DSI_LP_TIM,
+ DIV_ROUND_UP(priv->config.lpx, temp) |
+ (DIV_ROUND_UP(priv->config.ta_sure, temp) << 8) |
+ (DIV_ROUND_UP(priv->config.ta_go, temp) << 16) |
+ (DIV_ROUND_UP(priv->config.ta_get, temp) << 24));
+
+ regmap_write(priv->regmap, MIPI_DSI_ANA_UP_TIM, 0x0100);
+ regmap_write(priv->regmap, MIPI_DSI_INIT_TIM,
+ DIV_ROUND_UP(priv->config.init * NSEC_PER_MSEC, temp));
+ regmap_write(priv->regmap, MIPI_DSI_WAKEUP_TIM,
+ DIV_ROUND_UP(priv->config.wakeup * NSEC_PER_MSEC, temp));
+ regmap_write(priv->regmap, MIPI_DSI_LPOK_TIM, 0x7C);
+ regmap_write(priv->regmap, MIPI_DSI_ULPS_CHECK, 0x927C);
+ regmap_write(priv->regmap, MIPI_DSI_LP_WCHDOG, 0x1000);
+ regmap_write(priv->regmap, MIPI_DSI_TURN_WCHDOG, 0x1000);
+
+ /* Powerup the analog circuit */
+ switch (priv->config.lanes) {
+ case 1:
+ regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0xe);
+ break;
+ case 2:
+ regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0xc);
+ break;
+ case 3:
+ regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0x8);
+ break;
+ case 4:
+ default:
+ regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0);
+ break;
+ }
+
+ /* Trigger a sync active for esc_clk */
+ regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(1), BIT(1));
+
+ return 0;
+}
+
+static int phy_meson_axg_mipi_dphy_power_off(struct phy *phy)
+{
+ struct phy_meson_axg_mipi_dphy_priv *priv = phy_get_drvdata(phy);
+
+ regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0xf);
+ regmap_write(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(31));
+
+ phy_power_off(priv->analog);
+
+ return 0;
+}
+
+static int phy_meson_axg_mipi_dphy_exit(struct phy *phy)
+{
+ struct phy_meson_axg_mipi_dphy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = phy_exit(priv->analog);
+ if (ret)
+ return ret;
+
+ return reset_control_reset(priv->reset);
+}
+
+static const struct phy_ops phy_meson_axg_mipi_dphy_ops = {
+ .configure = phy_meson_axg_mipi_dphy_configure,
+ .init = phy_meson_axg_mipi_dphy_init,
+ .exit = phy_meson_axg_mipi_dphy_exit,
+ .power_on = phy_meson_axg_mipi_dphy_power_on,
+ .power_off = phy_meson_axg_mipi_dphy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int phy_meson_axg_mipi_dphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *phy_provider;
+ struct resource *res;
+ struct phy_meson_axg_mipi_dphy_priv *priv;
+ struct phy *phy;
+ void __iomem *base;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ platform_set_drvdata(pdev, priv);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->regmap = devm_regmap_init_mmio(dev, base,
+ &phy_meson_axg_mipi_dphy_regmap_conf);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->clk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ priv->reset = devm_reset_control_get(dev, "phy");
+ if (IS_ERR(priv->reset))
+ return PTR_ERR(priv->reset);
+
+ priv->analog = devm_phy_get(dev, "analog");
+ if (IS_ERR(priv->analog))
+ return PTR_ERR(priv->analog);
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(priv->reset);
+ if (ret)
+ return ret;
+
+ phy = devm_phy_create(dev, NULL, &phy_meson_axg_mipi_dphy_ops);
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to create PHY\n");
+
+ return ret;
+ }
+
+ phy_set_drvdata(phy, priv);
+
+ 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 phy_meson_axg_mipi_dphy_of_match[] = {
+ { .compatible = "amlogic,axg-mipi-dphy", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_axg_mipi_dphy_of_match);
+
+static struct platform_driver phy_meson_axg_mipi_dphy_driver = {
+ .probe = phy_meson_axg_mipi_dphy_probe,
+ .driver = {
+ .name = "phy-meson-axg-mipi-dphy",
+ .of_match_table = phy_meson_axg_mipi_dphy_of_match,
+ },
+};
+module_platform_driver(phy_meson_axg_mipi_dphy_driver);
+
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_DESCRIPTION("Meson AXG MIPI DPHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c b/drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c
new file mode 100644
index 000000000000..a3e1108b736d
--- /dev/null
+++ b/drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Amlogic AXG MIPI + PCIE analog PHY driver
+ *
+ * Copyright (C) 2019 Remi Pommarel <repk@triplefau.lt>
+ */
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <dt-bindings/phy/phy.h>
+
+#define HHI_MIPI_CNTL0 0x00
+#define HHI_MIPI_CNTL0_COMMON_BLOCK GENMASK(31, 28)
+#define HHI_MIPI_CNTL0_ENABLE BIT(29)
+#define HHI_MIPI_CNTL0_BANDGAP BIT(26)
+#define HHI_MIPI_CNTL0_DIF_REF_CTL1 GENMASK(25, 16)
+#define HHI_MIPI_CNTL0_DIF_REF_CTL0 GENMASK(15, 0)
+
+#define HHI_MIPI_CNTL1 0x04
+#define HHI_MIPI_CNTL1_CH0_CML_PDR_EN BIT(12)
+#define HHI_MIPI_CNTL1_LP_ABILITY GENMASK(5, 4)
+#define HHI_MIPI_CNTL1_LP_RESISTER BIT(3)
+#define HHI_MIPI_CNTL1_INPUT_SETTING BIT(2)
+#define HHI_MIPI_CNTL1_INPUT_SEL BIT(1)
+#define HHI_MIPI_CNTL1_PRBS7_EN BIT(0)
+
+#define HHI_MIPI_CNTL2 0x08
+#define HHI_MIPI_CNTL2_CH_PU GENMASK(31, 25)
+#define HHI_MIPI_CNTL2_CH_CTL GENMASK(24, 19)
+#define HHI_MIPI_CNTL2_CH0_DIGDR_EN BIT(18)
+#define HHI_MIPI_CNTL2_CH_DIGDR_EN BIT(17)
+#define HHI_MIPI_CNTL2_LPULPS_EN BIT(16)
+#define HHI_MIPI_CNTL2_CH_EN GENMASK(15, 11)
+#define HHI_MIPI_CNTL2_CH0_LP_CTL GENMASK(10, 1)
+
+#define DSI_LANE_0 BIT(4)
+#define DSI_LANE_1 BIT(3)
+#define DSI_LANE_CLK BIT(2)
+#define DSI_LANE_2 BIT(1)
+#define DSI_LANE_3 BIT(0)
+
+struct phy_axg_mipi_pcie_analog_priv {
+ struct phy *phy;
+ struct regmap *regmap;
+ bool dsi_configured;
+ bool dsi_enabled;
+ bool powered;
+ struct phy_configure_opts_mipi_dphy config;
+};
+
+static void phy_bandgap_enable(struct phy_axg_mipi_pcie_analog_priv *priv)
+{
+ regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
+ HHI_MIPI_CNTL0_BANDGAP, HHI_MIPI_CNTL0_BANDGAP);
+
+ regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
+ HHI_MIPI_CNTL0_ENABLE, HHI_MIPI_CNTL0_ENABLE);
+}
+
+static void phy_bandgap_disable(struct phy_axg_mipi_pcie_analog_priv *priv)
+{
+ regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
+ HHI_MIPI_CNTL0_BANDGAP, 0);
+ regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
+ HHI_MIPI_CNTL0_ENABLE, 0);
+}
+
+static void phy_dsi_analog_enable(struct phy_axg_mipi_pcie_analog_priv *priv)
+{
+ u32 reg;
+
+ regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
+ HHI_MIPI_CNTL0_DIF_REF_CTL1,
+ FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0x1b8));
+ regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
+ BIT(31), BIT(31));
+ regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
+ HHI_MIPI_CNTL0_DIF_REF_CTL0,
+ FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL0, 0x8));
+
+ regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0x001e);
+
+ regmap_write(priv->regmap, HHI_MIPI_CNTL2,
+ (0x26e0 << 16) | (0x459 << 0));
+
+ reg = DSI_LANE_CLK;
+ switch (priv->config.lanes) {
+ case 4:
+ reg |= DSI_LANE_3;
+ fallthrough;
+ case 3:
+ reg |= DSI_LANE_2;
+ fallthrough;
+ case 2:
+ reg |= DSI_LANE_1;
+ fallthrough;
+ case 1:
+ reg |= DSI_LANE_0;
+ break;
+ default:
+ reg = 0;
+ }
+
+ regmap_update_bits(priv->regmap, HHI_MIPI_CNTL2,
+ HHI_MIPI_CNTL2_CH_EN,
+ FIELD_PREP(HHI_MIPI_CNTL2_CH_EN, reg));
+
+ priv->dsi_enabled = true;
+}
+
+static void phy_dsi_analog_disable(struct phy_axg_mipi_pcie_analog_priv *priv)
+{
+ regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
+ HHI_MIPI_CNTL0_DIF_REF_CTL1,
+ FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0));
+ regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, BIT(31), 0);
+ regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
+ HHI_MIPI_CNTL0_DIF_REF_CTL1, 0);
+
+ regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0x6);
+
+ regmap_write(priv->regmap, HHI_MIPI_CNTL2, 0x00200000);
+
+ priv->dsi_enabled = false;
+}
+
+static int phy_axg_mipi_pcie_analog_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ struct phy_axg_mipi_pcie_analog_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
+ if (ret)
+ return ret;
+
+ memcpy(&priv->config, opts, sizeof(priv->config));
+
+ priv->dsi_configured = true;
+
+ /* If PHY was already powered on, setup the DSI analog part */
+ if (priv->powered) {
+ /* If reconfiguring, disable & reconfigure */
+ if (priv->dsi_enabled)
+ phy_dsi_analog_disable(priv);
+
+ usleep_range(100, 200);
+
+ phy_dsi_analog_enable(priv);
+ }
+
+ return 0;
+}
+
+static int phy_axg_mipi_pcie_analog_power_on(struct phy *phy)
+{
+ struct phy_axg_mipi_pcie_analog_priv *priv = phy_get_drvdata(phy);
+
+ phy_bandgap_enable(priv);
+
+ if (priv->dsi_configured)
+ phy_dsi_analog_enable(priv);
+
+ priv->powered = true;
+
+ return 0;
+}
+
+static int phy_axg_mipi_pcie_analog_power_off(struct phy *phy)
+{
+ struct phy_axg_mipi_pcie_analog_priv *priv = phy_get_drvdata(phy);
+
+ phy_bandgap_disable(priv);
+
+ if (priv->dsi_enabled)
+ phy_dsi_analog_disable(priv);
+
+ priv->powered = false;
+
+ return 0;
+}
+
+static const struct phy_ops phy_axg_mipi_pcie_analog_ops = {
+ .configure = phy_axg_mipi_pcie_analog_configure,
+ .power_on = phy_axg_mipi_pcie_analog_power_on,
+ .power_off = phy_axg_mipi_pcie_analog_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int phy_axg_mipi_pcie_analog_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy;
+ struct device *dev = &pdev->dev;
+ struct phy_axg_mipi_pcie_analog_priv *priv;
+ struct device_node *np = dev->of_node, *parent_np;
+ struct regmap *map;
+ int ret;
+
+ priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Get the hhi system controller node */
+ parent_np = of_get_parent(dev->of_node);
+ map = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
+ if (IS_ERR(map)) {
+ dev_err(dev,
+ "failed to get HHI regmap\n");
+ return PTR_ERR(map);
+ }
+
+ priv->regmap = map;
+
+ priv->phy = devm_phy_create(dev, np, &phy_axg_mipi_pcie_analog_ops);
+ if (IS_ERR(priv->phy)) {
+ ret = PTR_ERR(priv->phy);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to create PHY\n");
+ return ret;
+ }
+
+ phy_set_drvdata(priv->phy, priv);
+ dev_set_drvdata(dev, priv);
+
+ phy = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy);
+}
+
+static const struct of_device_id phy_axg_mipi_pcie_analog_of_match[] = {
+ {
+ .compatible = "amlogic,axg-mipi-pcie-analog-phy",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, phy_axg_mipi_pcie_analog_of_match);
+
+static struct platform_driver phy_axg_mipi_pcie_analog_driver = {
+ .probe = phy_axg_mipi_pcie_analog_probe,
+ .driver = {
+ .name = "phy-axg-mipi-pcie-analog",
+ .of_match_table = phy_axg_mipi_pcie_analog_of_match,
+ },
+};
+module_platform_driver(phy_axg_mipi_pcie_analog_driver);
+
+MODULE_AUTHOR("Remi Pommarel <repk@triplefau.lt>");
+MODULE_DESCRIPTION("Amlogic AXG MIPI + PCIE analog PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/amlogic/phy-meson-axg-pcie.c b/drivers/phy/amlogic/phy-meson-axg-pcie.c
new file mode 100644
index 000000000000..2299bab38e05
--- /dev/null
+++ b/drivers/phy/amlogic/phy-meson-axg-pcie.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Amlogic AXG PCIE PHY driver
+ *
+ * Copyright (C) 2020 Remi Pommarel <repk@triplefau.lt>
+ */
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/platform_device.h>
+#include <linux/bitfield.h>
+#include <dt-bindings/phy/phy.h>
+
+#define MESON_PCIE_REG0 0x00
+#define MESON_PCIE_COMMON_CLK BIT(4)
+#define MESON_PCIE_PORT_SEL GENMASK(3, 2)
+#define MESON_PCIE_CLK BIT(1)
+#define MESON_PCIE_POWERDOWN BIT(0)
+
+#define MESON_PCIE_TWO_X1 FIELD_PREP(MESON_PCIE_PORT_SEL, 0x3)
+#define MESON_PCIE_COMMON_REF_CLK FIELD_PREP(MESON_PCIE_COMMON_CLK, 0x1)
+#define MESON_PCIE_PHY_INIT (MESON_PCIE_TWO_X1 | \
+ MESON_PCIE_COMMON_REF_CLK)
+#define MESON_PCIE_RESET_DELAY 500
+
+struct phy_axg_pcie_priv {
+ struct phy *phy;
+ struct phy *analog;
+ struct regmap *regmap;
+ struct reset_control *reset;
+};
+
+static const struct regmap_config phy_axg_pcie_regmap_conf = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = MESON_PCIE_REG0,
+};
+
+static int phy_axg_pcie_power_on(struct phy *phy)
+{
+ struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = phy_power_on(priv->analog);
+ if (ret != 0)
+ return ret;
+
+ regmap_update_bits(priv->regmap, MESON_PCIE_REG0,
+ MESON_PCIE_POWERDOWN, 0);
+ return 0;
+}
+
+static int phy_axg_pcie_power_off(struct phy *phy)
+{
+ struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = phy_power_off(priv->analog);
+ if (ret != 0)
+ return ret;
+
+ regmap_update_bits(priv->regmap, MESON_PCIE_REG0,
+ MESON_PCIE_POWERDOWN, 1);
+ return 0;
+}
+
+static int phy_axg_pcie_init(struct phy *phy)
+{
+ struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = phy_init(priv->analog);
+ if (ret != 0)
+ return ret;
+
+ regmap_write(priv->regmap, MESON_PCIE_REG0, MESON_PCIE_PHY_INIT);
+ return reset_control_reset(priv->reset);
+}
+
+static int phy_axg_pcie_exit(struct phy *phy)
+{
+ struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = phy_exit(priv->analog);
+ if (ret != 0)
+ return ret;
+
+ return reset_control_reset(priv->reset);
+}
+
+static int phy_axg_pcie_reset(struct phy *phy)
+{
+ struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy);
+ int ret = 0;
+
+ ret = phy_reset(priv->analog);
+ if (ret != 0)
+ goto out;
+
+ ret = reset_control_assert(priv->reset);
+ if (ret != 0)
+ goto out;
+ udelay(MESON_PCIE_RESET_DELAY);
+
+ ret = reset_control_deassert(priv->reset);
+ if (ret != 0)
+ goto out;
+ udelay(MESON_PCIE_RESET_DELAY);
+
+out:
+ return ret;
+}
+
+static const struct phy_ops phy_axg_pcie_ops = {
+ .init = phy_axg_pcie_init,
+ .exit = phy_axg_pcie_exit,
+ .power_on = phy_axg_pcie_power_on,
+ .power_off = phy_axg_pcie_power_off,
+ .reset = phy_axg_pcie_reset,
+ .owner = THIS_MODULE,
+};
+
+static int phy_axg_pcie_probe(struct platform_device *pdev)
+{
+ struct phy_provider *pphy;
+ struct device *dev = &pdev->dev;
+ struct phy_axg_pcie_priv *priv;
+ struct device_node *np = dev->of_node;
+ void __iomem *base;
+ int ret;
+
+ priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->phy = devm_phy_create(dev, np, &phy_axg_pcie_ops);
+ if (IS_ERR(priv->phy)) {
+ ret = PTR_ERR(priv->phy);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to create PHY\n");
+ return ret;
+ }
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->regmap = devm_regmap_init_mmio(dev, base,
+ &phy_axg_pcie_regmap_conf);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->reset = devm_reset_control_array_get_exclusive(dev);
+ if (IS_ERR(priv->reset))
+ return PTR_ERR(priv->reset);
+
+ priv->analog = devm_phy_get(dev, "analog");
+ if (IS_ERR(priv->analog))
+ return PTR_ERR(priv->analog);
+
+ phy_set_drvdata(priv->phy, priv);
+ dev_set_drvdata(dev, priv);
+ pphy = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(pphy);
+}
+
+static const struct of_device_id phy_axg_pcie_of_match[] = {
+ {
+ .compatible = "amlogic,axg-pcie-phy",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, phy_axg_pcie_of_match);
+
+static struct platform_driver phy_axg_pcie_driver = {
+ .probe = phy_axg_pcie_probe,
+ .driver = {
+ .name = "phy-axg-pcie",
+ .of_match_table = phy_axg_pcie_of_match,
+ },
+};
+module_platform_driver(phy_axg_pcie_driver);
+
+MODULE_AUTHOR("Remi Pommarel <repk@triplefau.lt>");
+MODULE_DESCRIPTION("Amlogic AXG PCIE PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c b/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c
new file mode 100644
index 000000000000..c14089fa7db4
--- /dev/null
+++ b/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Meson G12A MIPI DSI Analog PHY
+ *
+ * Copyright (C) 2018 Amlogic, Inc. All rights reserved
+ * Copyright (C) 2022 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <dt-bindings/phy/phy.h>
+
+#define HHI_MIPI_CNTL0 0x00
+#define HHI_MIPI_CNTL0_DIF_REF_CTL1 GENMASK(31, 16)
+#define HHI_MIPI_CNTL0_DIF_REF_CTL0 GENMASK(15, 0)
+
+#define HHI_MIPI_CNTL1 0x04
+#define HHI_MIPI_CNTL1_BANDGAP BIT(16)
+#define HHI_MIPI_CNTL2_DIF_REF_CTL2 GENMASK(15, 0)
+
+#define HHI_MIPI_CNTL2 0x08
+#define HHI_MIPI_CNTL2_DIF_TX_CTL1 GENMASK(31, 16)
+#define HHI_MIPI_CNTL2_CH_EN GENMASK(15, 11)
+#define HHI_MIPI_CNTL2_DIF_TX_CTL0 GENMASK(10, 0)
+
+#define DSI_LANE_0 BIT(4)
+#define DSI_LANE_1 BIT(3)
+#define DSI_LANE_CLK BIT(2)
+#define DSI_LANE_2 BIT(1)
+#define DSI_LANE_3 BIT(0)
+
+struct phy_g12a_mipi_dphy_analog_priv {
+ struct phy *phy;
+ struct regmap *regmap;
+ struct phy_configure_opts_mipi_dphy config;
+};
+
+static int phy_g12a_mipi_dphy_analog_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ struct phy_g12a_mipi_dphy_analog_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
+ if (ret)
+ return ret;
+
+ memcpy(&priv->config, opts, sizeof(priv->config));
+
+ return 0;
+}
+
+static int phy_g12a_mipi_dphy_analog_power_on(struct phy *phy)
+{
+ struct phy_g12a_mipi_dphy_analog_priv *priv = phy_get_drvdata(phy);
+ unsigned int reg;
+
+ regmap_write(priv->regmap, HHI_MIPI_CNTL0,
+ FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL0, 0x8) |
+ FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0xa487));
+
+ regmap_write(priv->regmap, HHI_MIPI_CNTL1,
+ FIELD_PREP(HHI_MIPI_CNTL2_DIF_REF_CTL2, 0x2e) |
+ HHI_MIPI_CNTL1_BANDGAP);
+
+ regmap_write(priv->regmap, HHI_MIPI_CNTL2,
+ FIELD_PREP(HHI_MIPI_CNTL2_DIF_TX_CTL0, 0x459) |
+ FIELD_PREP(HHI_MIPI_CNTL2_DIF_TX_CTL1, 0x2680));
+
+ reg = DSI_LANE_CLK;
+ switch (priv->config.lanes) {
+ case 4:
+ reg |= DSI_LANE_3;
+ fallthrough;
+ case 3:
+ reg |= DSI_LANE_2;
+ fallthrough;
+ case 2:
+ reg |= DSI_LANE_1;
+ fallthrough;
+ case 1:
+ reg |= DSI_LANE_0;
+ break;
+ default:
+ reg = 0;
+ }
+
+ regmap_update_bits(priv->regmap, HHI_MIPI_CNTL2,
+ HHI_MIPI_CNTL2_CH_EN,
+ FIELD_PREP(HHI_MIPI_CNTL2_CH_EN, reg));
+
+ return 0;
+}
+
+static int phy_g12a_mipi_dphy_analog_power_off(struct phy *phy)
+{
+ struct phy_g12a_mipi_dphy_analog_priv *priv = phy_get_drvdata(phy);
+
+ regmap_write(priv->regmap, HHI_MIPI_CNTL0, 0);
+ regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0);
+ regmap_write(priv->regmap, HHI_MIPI_CNTL2, 0);
+
+ return 0;
+}
+
+static const struct phy_ops phy_g12a_mipi_dphy_analog_ops = {
+ .configure = phy_g12a_mipi_dphy_analog_configure,
+ .power_on = phy_g12a_mipi_dphy_analog_power_on,
+ .power_off = phy_g12a_mipi_dphy_analog_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int phy_g12a_mipi_dphy_analog_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy;
+ struct device *dev = &pdev->dev;
+ struct phy_g12a_mipi_dphy_analog_priv *priv;
+ struct device_node *np = dev->of_node, *parent_np;
+ struct regmap *map;
+
+ priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Get the hhi system controller node */
+ parent_np = of_get_parent(np);
+ map = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
+ if (IS_ERR(map))
+ return dev_err_probe(dev, PTR_ERR(map), "failed to get HHI regmap\n");
+
+ priv->regmap = map;
+
+ priv->phy = devm_phy_create(dev, np, &phy_g12a_mipi_dphy_analog_ops);
+ if (IS_ERR(priv->phy))
+ return dev_err_probe(dev, PTR_ERR(priv->phy), "failed to create PHY\n");
+
+ phy_set_drvdata(priv->phy, priv);
+ dev_set_drvdata(dev, priv);
+
+ phy = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy);
+}
+
+static const struct of_device_id phy_g12a_mipi_dphy_analog_of_match[] = {
+ {
+ .compatible = "amlogic,g12a-mipi-dphy-analog",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, phy_g12a_mipi_dphy_analog_of_match);
+
+static struct platform_driver phy_g12a_mipi_dphy_analog_driver = {
+ .probe = phy_g12a_mipi_dphy_analog_probe,
+ .driver = {
+ .name = "phy-meson-g12a-mipi-dphy-analog",
+ .of_match_table = phy_g12a_mipi_dphy_analog_of_match,
+ },
+};
+module_platform_driver(phy_g12a_mipi_dphy_analog_driver);
+
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_DESCRIPTION("Meson G12A MIPI Analog D-PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/amlogic/phy-meson-g12a-usb2.c b/drivers/phy/amlogic/phy-meson-g12a-usb2.c
index 9065ffc85eb4..9d1efa0d9394 100644
--- a/drivers/phy/amlogic/phy-meson-g12a-usb2.c
+++ b/drivers/phy/amlogic/phy-meson-g12a-usb2.c
@@ -66,7 +66,7 @@
#define PHY_CTRL_R14 0x38
#define PHY_CTRL_R14_I_RDP_EN BIT(0)
#define PHY_CTRL_R14_I_RPU_SW1_EN BIT(1)
- #define PHY_CTRL_R14_I_RPU_SW2_EN GENMASK(2, 3)
+ #define PHY_CTRL_R14_I_RPU_SW2_EN GENMASK(3, 2)
#define PHY_CTRL_R14_PG_RSTN BIT(4)
#define PHY_CTRL_R14_I_C2L_DATA_16_8 BIT(5)
#define PHY_CTRL_R14_I_C2L_ASSERT_SINGLE_EN_ZERO BIT(6)
@@ -146,11 +146,17 @@
#define RESET_COMPLETE_TIME 1000
#define PLL_RESET_COMPLETE_TIME 100
+enum meson_soc_id {
+ MESON_SOC_G12A = 0,
+ MESON_SOC_A1,
+};
+
struct phy_meson_g12a_usb2_priv {
struct device *dev;
struct regmap *regmap;
struct clk *clk;
struct reset_control *reset;
+ int soc_id;
};
static const struct regmap_config phy_meson_g12a_usb2_regmap_conf = {
@@ -164,6 +170,7 @@ static int phy_meson_g12a_usb2_init(struct phy *phy)
{
struct phy_meson_g12a_usb2_priv *priv = phy_get_drvdata(phy);
int ret;
+ unsigned int value;
ret = reset_control_reset(priv->reset);
if (ret)
@@ -192,18 +199,22 @@ static int phy_meson_g12a_usb2_init(struct phy *phy)
FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT2, 2) |
FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT1, 9));
- regmap_write(priv->regmap, PHY_CTRL_R18,
- FIELD_PREP(PHY_CTRL_R18_MPLL_LKW_SEL, 1) |
- FIELD_PREP(PHY_CTRL_R18_MPLL_LK_W, 9) |
- FIELD_PREP(PHY_CTRL_R18_MPLL_LK_S, 0x27) |
- FIELD_PREP(PHY_CTRL_R18_MPLL_PFD_GAIN, 1) |
- FIELD_PREP(PHY_CTRL_R18_MPLL_ROU, 7) |
- FIELD_PREP(PHY_CTRL_R18_MPLL_DATA_SEL, 3) |
- FIELD_PREP(PHY_CTRL_R18_MPLL_BIAS_ADJ, 1) |
- FIELD_PREP(PHY_CTRL_R18_MPLL_BB_MODE, 0) |
- FIELD_PREP(PHY_CTRL_R18_MPLL_ALPHA, 3) |
- FIELD_PREP(PHY_CTRL_R18_MPLL_ADJ_LDO, 1) |
- PHY_CTRL_R18_MPLL_ACG_RANGE);
+ value = FIELD_PREP(PHY_CTRL_R18_MPLL_LKW_SEL, 1) |
+ FIELD_PREP(PHY_CTRL_R18_MPLL_LK_W, 9) |
+ FIELD_PREP(PHY_CTRL_R18_MPLL_LK_S, 0x27) |
+ FIELD_PREP(PHY_CTRL_R18_MPLL_PFD_GAIN, 1) |
+ FIELD_PREP(PHY_CTRL_R18_MPLL_ROU, 7) |
+ FIELD_PREP(PHY_CTRL_R18_MPLL_DATA_SEL, 3) |
+ FIELD_PREP(PHY_CTRL_R18_MPLL_BIAS_ADJ, 1) |
+ FIELD_PREP(PHY_CTRL_R18_MPLL_BB_MODE, 0) |
+ FIELD_PREP(PHY_CTRL_R18_MPLL_ALPHA, 3) |
+ FIELD_PREP(PHY_CTRL_R18_MPLL_ADJ_LDO, 1) |
+ PHY_CTRL_R18_MPLL_ACG_RANGE;
+
+ if (priv->soc_id == MESON_SOC_A1)
+ value |= PHY_CTRL_R18_MPLL_DCO_CLK_SEL;
+
+ regmap_write(priv->regmap, PHY_CTRL_R18, value);
udelay(PLL_RESET_COMPLETE_TIME);
@@ -227,13 +238,24 @@ static int phy_meson_g12a_usb2_init(struct phy *phy)
FIELD_PREP(PHY_CTRL_R20_USB2_BGR_VREF_4_0, 0) |
FIELD_PREP(PHY_CTRL_R20_USB2_BGR_DBG_1_0, 0));
- regmap_write(priv->regmap, PHY_CTRL_R4,
- FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_7_0, 0xf) |
- FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_15_8, 0xf) |
- FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_23_16, 0xf) |
- PHY_CTRL_R4_TEST_BYPASS_MODE_EN |
- FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0, 0) |
- FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2, 0));
+ if (priv->soc_id == MESON_SOC_G12A)
+ regmap_write(priv->regmap, PHY_CTRL_R4,
+ FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_7_0, 0xf) |
+ FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_15_8, 0xf) |
+ FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_23_16, 0xf) |
+ PHY_CTRL_R4_TEST_BYPASS_MODE_EN |
+ FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0, 0) |
+ FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2, 0));
+ else if (priv->soc_id == MESON_SOC_A1) {
+ regmap_write(priv->regmap, PHY_CTRL_R21,
+ PHY_CTRL_R21_USB2_CAL_ACK_EN |
+ PHY_CTRL_R21_USB2_TX_STRG_PD |
+ FIELD_PREP(PHY_CTRL_R21_USB2_OTG_ACA_TRIM_1_0, 2));
+
+ /* Analog Settings */
+ regmap_write(priv->regmap, PHY_CTRL_R13,
+ FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7));
+ }
/* Tuning Disconnect Threshold */
regmap_write(priv->regmap, PHY_CTRL_R3,
@@ -241,11 +263,13 @@ static int phy_meson_g12a_usb2_init(struct phy *phy)
FIELD_PREP(PHY_CTRL_R3_HSDIC_REF, 1) |
FIELD_PREP(PHY_CTRL_R3_DISC_THRESH, 3));
- /* Analog Settings */
- regmap_write(priv->regmap, PHY_CTRL_R14, 0);
- regmap_write(priv->regmap, PHY_CTRL_R13,
- PHY_CTRL_R13_UPDATE_PMA_SIGNALS |
- FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7));
+ if (priv->soc_id == MESON_SOC_G12A) {
+ /* Analog Settings */
+ regmap_write(priv->regmap, PHY_CTRL_R14, 0);
+ regmap_write(priv->regmap, PHY_CTRL_R13,
+ PHY_CTRL_R13_UPDATE_PMA_SIGNALS |
+ FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7));
+ }
return 0;
}
@@ -268,7 +292,6 @@ static int phy_meson_g12a_usb2_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
- struct resource *res;
struct phy_meson_g12a_usb2_priv *priv;
struct phy *phy;
void __iomem *base;
@@ -281,11 +304,12 @@ static int phy_meson_g12a_usb2_probe(struct platform_device *pdev)
priv->dev = dev;
platform_set_drvdata(pdev, priv);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
+ priv->soc_id = (enum meson_soc_id)of_device_get_match_data(&pdev->dev);
+
priv->regmap = devm_regmap_init_mmio(dev, base,
&phy_meson_g12a_usb2_regmap_conf);
if (IS_ERR(priv->regmap))
@@ -321,8 +345,15 @@ static int phy_meson_g12a_usb2_probe(struct platform_device *pdev)
}
static const struct of_device_id phy_meson_g12a_usb2_of_match[] = {
- { .compatible = "amlogic,g12a-usb2-phy", },
- { },
+ {
+ .compatible = "amlogic,g12a-usb2-phy",
+ .data = (void *)MESON_SOC_G12A,
+ },
+ {
+ .compatible = "amlogic,a1-usb2-phy",
+ .data = (void *)MESON_SOC_A1,
+ },
+ { /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, phy_meson_g12a_usb2_of_match);
diff --git a/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c b/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c
index 08e322789e59..d2a1da8d9e58 100644
--- a/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c
+++ b/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c
@@ -386,17 +386,14 @@ static int phy_g12a_usb3_pcie_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct phy_g12a_usb3_pcie_priv *priv;
- struct resource *res;
struct phy_provider *phy_provider;
void __iomem *base;
- int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -410,39 +407,24 @@ static int phy_g12a_usb3_pcie_probe(struct platform_device *pdev)
if (IS_ERR(priv->regmap_cr))
return PTR_ERR(priv->regmap_cr);
- priv->clk_ref = devm_clk_get(dev, "ref_clk");
+ priv->clk_ref = devm_clk_get_enabled(dev, "ref_clk");
if (IS_ERR(priv->clk_ref))
return PTR_ERR(priv->clk_ref);
- ret = clk_prepare_enable(priv->clk_ref);
- if (ret)
- goto err_disable_clk_ref;
-
- priv->reset = devm_reset_control_array_get(dev, false, false);
+ priv->reset = devm_reset_control_array_get_exclusive(dev);
if (IS_ERR(priv->reset))
return PTR_ERR(priv->reset);
priv->phy = devm_phy_create(dev, np, &phy_g12a_usb3_pcie_ops);
- if (IS_ERR(priv->phy)) {
- ret = PTR_ERR(priv->phy);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to create PHY\n");
-
- return ret;
- }
+ if (IS_ERR(priv->phy))
+ return dev_err_probe(dev, PTR_ERR(priv->phy), "failed to create PHY\n");
phy_set_drvdata(priv->phy, priv);
dev_set_drvdata(dev, priv);
phy_provider = devm_of_phy_provider_register(dev,
phy_g12a_usb3_pcie_xlate);
-
return PTR_ERR_OR_ZERO(phy_provider);
-
-err_disable_clk_ref:
- clk_disable_unprepare(priv->clk_ref);
-
- return ret;
}
static const struct of_device_id phy_g12a_usb3_pcie_of_match[] = {
diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb2.c b/drivers/phy/amlogic/phy-meson-gxl-usb2.c
index 43ec9bf24abf..db17c3448bfe 100644
--- a/drivers/phy/amlogic/phy-meson-gxl-usb2.c
+++ b/drivers/phy/amlogic/phy-meson-gxl-usb2.c
@@ -114,8 +114,10 @@ static int phy_meson_gxl_usb2_init(struct phy *phy)
return ret;
ret = clk_prepare_enable(priv->clk);
- if (ret)
+ if (ret) {
+ reset_control_rearm(priv->reset);
return ret;
+ }
return 0;
}
@@ -125,6 +127,7 @@ static int phy_meson_gxl_usb2_exit(struct phy *phy)
struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
clk_disable_unprepare(priv->clk);
+ reset_control_rearm(priv->reset);
return 0;
}
@@ -158,7 +161,8 @@ static int phy_meson_gxl_usb2_set_mode(struct phy *phy,
U2P_R0_DM_PULLDOWN);
regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
U2P_R0_DP_PULLDOWN);
- regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP, 0);
+ regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP,
+ U2P_R0_ID_PULLUP);
break;
case PHY_MODE_USB_DEVICE:
@@ -230,7 +234,6 @@ static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
- struct resource *res;
struct phy_meson_gxl_usb2_priv *priv;
struct phy *phy;
void __iomem *base;
@@ -242,8 +245,7 @@ static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb3.c b/drivers/phy/amlogic/phy-meson-gxl-usb3.c
deleted file mode 100644
index c0e9e4c16149..000000000000
--- a/drivers/phy/amlogic/phy-meson-gxl-usb3.c
+++ /dev/null
@@ -1,283 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Meson GXL USB3 PHY and OTG mode detection driver
- *
- * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
- */
-
-#include <linux/bitfield.h>
-#include <linux/bitops.h>
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/phy/phy.h>
-#include <linux/regmap.h>
-#include <linux/reset.h>
-#include <linux/platform_device.h>
-
-#define USB_R0 0x00
- #define USB_R0_P30_FSEL_MASK GENMASK(5, 0)
- #define USB_R0_P30_PHY_RESET BIT(6)
- #define USB_R0_P30_TEST_POWERDOWN_HSP BIT(7)
- #define USB_R0_P30_TEST_POWERDOWN_SSP BIT(8)
- #define USB_R0_P30_ACJT_LEVEL_MASK GENMASK(13, 9)
- #define USB_R0_P30_TX_BOOST_LEVEL_MASK GENMASK(16, 14)
- #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17)
- #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18)
- #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19)
- #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29)
- #define USB_R0_U2D_ACT BIT(31)
-
-#define USB_R1 0x04
- #define USB_R1_U3H_BIGENDIAN_GS BIT(0)
- #define USB_R1_U3H_PME_ENABLE BIT(1)
- #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(6, 2)
- #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(11, 7)
- #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(15, 12)
- #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16)
- #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17)
- #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18)
- #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19)
- #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25)
-
-#define USB_R2 0x08
- #define USB_R2_P30_CR_DATA_IN_MASK GENMASK(15, 0)
- #define USB_R2_P30_CR_READ BIT(16)
- #define USB_R2_P30_CR_WRITE BIT(17)
- #define USB_R2_P30_CR_CAP_ADDR BIT(18)
- #define USB_R2_P30_CR_CAP_DATA BIT(19)
- #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20)
- #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26)
-
-#define USB_R3 0x0c
- #define USB_R3_P30_SSC_ENABLE BIT(0)
- #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1)
- #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4)
- #define USB_R3_P30_REF_SSP_EN BIT(13)
- #define USB_R3_P30_LOS_BIAS_MASK GENMASK(18, 16)
- #define USB_R3_P30_LOS_LEVEL_MASK GENMASK(23, 19)
- #define USB_R3_P30_MPLL_MULTIPLIER_MASK GENMASK(30, 24)
-
-#define USB_R4 0x10
- #define USB_R4_P21_PORT_RESET_0 BIT(0)
- #define USB_R4_P21_SLEEP_M0 BIT(1)
- #define USB_R4_MEM_PD_MASK GENMASK(3, 2)
- #define USB_R4_P21_ONLY BIT(4)
-
-#define USB_R5 0x14
- #define USB_R5_ID_DIG_SYNC BIT(0)
- #define USB_R5_ID_DIG_REG BIT(1)
- #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2)
- #define USB_R5_ID_DIG_EN_0 BIT(4)
- #define USB_R5_ID_DIG_EN_1 BIT(5)
- #define USB_R5_ID_DIG_CURR BIT(6)
- #define USB_R5_ID_DIG_IRQ BIT(7)
- #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8)
- #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16)
-
-/* read-only register */
-#define USB_R6 0x18
- #define USB_R6_P30_CR_DATA_OUT_MASK GENMASK(15, 0)
- #define USB_R6_P30_CR_ACK BIT(16)
-
-struct phy_meson_gxl_usb3_priv {
- struct regmap *regmap;
- enum phy_mode mode;
- struct clk *clk_phy;
- struct clk *clk_peripheral;
- struct reset_control *reset;
-};
-
-static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
- .reg_bits = 8,
- .val_bits = 32,
- .reg_stride = 4,
- .max_register = USB_R6,
-};
-
-static int phy_meson_gxl_usb3_power_on(struct phy *phy)
-{
- struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0,
- USB_R5_ID_DIG_EN_0);
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1,
- USB_R5_ID_DIG_EN_1);
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_TH_MASK,
- FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
-
- return 0;
-}
-
-static int phy_meson_gxl_usb3_power_off(struct phy *phy)
-{
- struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0, 0);
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1, 0);
-
- return 0;
-}
-
-static int phy_meson_gxl_usb3_set_mode(struct phy *phy,
- enum phy_mode mode, int submode)
-{
- struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-
- switch (mode) {
- case PHY_MODE_USB_HOST:
- regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, 0);
- regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
- 0);
- break;
-
- case PHY_MODE_USB_DEVICE:
- regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT,
- USB_R0_U2D_ACT);
- regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
- USB_R4_P21_SLEEP_M0);
- break;
-
- default:
- dev_err(&phy->dev, "unsupported PHY mode %d\n", mode);
- return -EINVAL;
- }
-
- priv->mode = mode;
-
- return 0;
-}
-
-static int phy_meson_gxl_usb3_init(struct phy *phy)
-{
- struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
- int ret;
-
- ret = reset_control_reset(priv->reset);
- if (ret)
- goto err;
-
- ret = clk_prepare_enable(priv->clk_phy);
- if (ret)
- goto err;
-
- ret = clk_prepare_enable(priv->clk_peripheral);
- if (ret)
- goto err_disable_clk_phy;
-
- ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode, 0);
- if (ret)
- goto err_disable_clk_peripheral;
-
- regmap_update_bits(priv->regmap, USB_R1,
- USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
- FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
-
- return 0;
-
-err_disable_clk_peripheral:
- clk_disable_unprepare(priv->clk_peripheral);
-err_disable_clk_phy:
- clk_disable_unprepare(priv->clk_phy);
-err:
- return ret;
-}
-
-static int phy_meson_gxl_usb3_exit(struct phy *phy)
-{
- struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-
- clk_disable_unprepare(priv->clk_peripheral);
- clk_disable_unprepare(priv->clk_phy);
-
- return 0;
-}
-
-static const struct phy_ops phy_meson_gxl_usb3_ops = {
- .power_on = phy_meson_gxl_usb3_power_on,
- .power_off = phy_meson_gxl_usb3_power_off,
- .set_mode = phy_meson_gxl_usb3_set_mode,
- .init = phy_meson_gxl_usb3_init,
- .exit = phy_meson_gxl_usb3_exit,
- .owner = THIS_MODULE,
-};
-
-static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- struct phy_meson_gxl_usb3_priv *priv;
- struct resource *res;
- struct phy *phy;
- struct phy_provider *phy_provider;
- void __iomem *base;
- int ret;
-
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- priv->regmap = devm_regmap_init_mmio(dev, base,
- &phy_meson_gxl_usb3_regmap_conf);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
-
- priv->clk_phy = devm_clk_get(dev, "phy");
- if (IS_ERR(priv->clk_phy))
- return PTR_ERR(priv->clk_phy);
-
- priv->clk_peripheral = devm_clk_get(dev, "peripheral");
- if (IS_ERR(priv->clk_peripheral))
- return PTR_ERR(priv->clk_peripheral);
-
- priv->reset = devm_reset_control_array_get_shared(dev);
- if (IS_ERR(priv->reset))
- return PTR_ERR(priv->reset);
-
- /*
- * default to host mode as hardware defaults and/or boot-loader
- * behavior can result in this PHY starting up in device mode. this
- * default and the initialization in phy_meson_gxl_usb3_init ensure
- * that we reproducibly start in a known mode on all devices.
- */
- priv->mode = PHY_MODE_USB_HOST;
-
- phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops);
- if (IS_ERR(phy)) {
- ret = PTR_ERR(phy);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to create PHY\n");
-
- return ret;
- }
-
- phy_set_drvdata(phy, priv);
-
- 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 phy_meson_gxl_usb3_of_match[] = {
- { .compatible = "amlogic,meson-gxl-usb3-phy", },
- { },
-};
-MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match);
-
-static struct platform_driver phy_meson_gxl_usb3_driver = {
- .probe = phy_meson_gxl_usb3_probe,
- .driver = {
- .name = "phy-meson-gxl-usb3",
- .of_match_table = phy_meson_gxl_usb3_of_match,
- },
-};
-module_platform_driver(phy_meson_gxl_usb3_driver);
-
-MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
-MODULE_DESCRIPTION("Meson GXL USB3 PHY and OTG detection driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/amlogic/phy-meson8-hdmi-tx.c b/drivers/phy/amlogic/phy-meson8-hdmi-tx.c
new file mode 100644
index 000000000000..f9a6572c27d8
--- /dev/null
+++ b/drivers/phy/amlogic/phy-meson8-hdmi-tx.c
@@ -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");
diff --git a/drivers/phy/amlogic/phy-meson8b-usb2.c b/drivers/phy/amlogic/phy-meson8b-usb2.c
index bd66bd723e4a..dd96763911b8 100644
--- a/drivers/phy/amlogic/phy-meson8b-usb2.c
+++ b/drivers/phy/amlogic/phy-meson8b-usb2.c
@@ -10,6 +10,8 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
@@ -76,6 +78,17 @@
#define REG_ADP_BC_ACA_PIN_FLOAT BIT(26)
#define REG_DBG_UART 0x10
+ #define REG_DBG_UART_BYPASS_SEL BIT(0)
+ #define REG_DBG_UART_BYPASS_DM_EN BIT(1)
+ #define REG_DBG_UART_BYPASS_DP_EN BIT(2)
+ #define REG_DBG_UART_BYPASS_DM_DATA BIT(3)
+ #define REG_DBG_UART_BYPASS_DP_DATA BIT(4)
+ #define REG_DBG_UART_FSV_MINUS BIT(5)
+ #define REG_DBG_UART_FSV_PLUS BIT(6)
+ #define REG_DBG_UART_FSV_BURN_IN_TEST BIT(7)
+ #define REG_DBG_UART_LOOPBACK_EN_B BIT(8)
+ #define REG_DBG_UART_SET_IDDQ BIT(9)
+ #define REG_DBG_UART_ATE_RESET BIT(10)
#define REG_TEST 0x14
#define REG_TEST_DATA_IN_MASK GENMASK(3, 0)
@@ -104,35 +117,30 @@
#define RESET_COMPLETE_TIME 500
#define ACA_ENABLE_COMPLETE_TIME 50
-struct phy_meson8b_usb2_priv {
- void __iomem *regs;
- enum usb_dr_mode dr_mode;
- struct clk *clk_usb_general;
- struct clk *clk_usb;
- struct reset_control *reset;
+struct phy_meson8b_usb2_match_data {
+ bool host_enable_aca;
};
-static u32 phy_meson8b_usb2_read(struct phy_meson8b_usb2_priv *phy_priv,
- u32 reg)
-{
- return readl(phy_priv->regs + reg);
-}
-
-static void phy_meson8b_usb2_mask_bits(struct phy_meson8b_usb2_priv *phy_priv,
- u32 reg, u32 mask, u32 value)
-{
- u32 data;
-
- data = phy_meson8b_usb2_read(phy_priv, reg);
- data &= ~mask;
- data |= (value & mask);
+struct phy_meson8b_usb2_priv {
+ struct regmap *regmap;
+ enum usb_dr_mode dr_mode;
+ struct clk *clk_usb_general;
+ struct clk *clk_usb;
+ struct reset_control *reset;
+ const struct phy_meson8b_usb2_match_data *match;
+};
- writel(data, phy_priv->regs + reg);
-}
+static const struct regmap_config phy_meson8b_usb2_regmap_conf = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = REG_TUNE,
+};
static int phy_meson8b_usb2_power_on(struct phy *phy)
{
struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy);
+ u32 reg;
int ret;
if (!IS_ERR_OR_NULL(priv->reset)) {
@@ -146,6 +154,7 @@ static int phy_meson8b_usb2_power_on(struct phy *phy)
ret = clk_prepare_enable(priv->clk_usb_general);
if (ret) {
dev_err(&phy->dev, "Failed to enable USB general clock\n");
+ reset_control_rearm(priv->reset);
return ret;
}
@@ -153,41 +162,48 @@ static int phy_meson8b_usb2_power_on(struct phy *phy)
if (ret) {
dev_err(&phy->dev, "Failed to enable USB DDR clock\n");
clk_disable_unprepare(priv->clk_usb_general);
+ reset_control_rearm(priv->reset);
return ret;
}
- phy_meson8b_usb2_mask_bits(priv, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL,
- REG_CONFIG_CLK_32k_ALTSEL);
+ regmap_update_bits(priv->regmap, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL,
+ REG_CONFIG_CLK_32k_ALTSEL);
- phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK,
- 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT);
+ regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK,
+ 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT);
- phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_FSEL_MASK,
- 0x5 << REG_CTRL_FSEL_SHIFT);
+ regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_FSEL_MASK,
+ 0x5 << REG_CTRL_FSEL_SHIFT);
/* reset the PHY */
- phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET,
- REG_CTRL_POWER_ON_RESET);
+ regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET,
+ REG_CTRL_POWER_ON_RESET);
udelay(RESET_COMPLETE_TIME);
- phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0);
+ regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0);
udelay(RESET_COMPLETE_TIME);
- phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT,
- REG_CTRL_SOF_TOGGLE_OUT);
+ regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT,
+ REG_CTRL_SOF_TOGGLE_OUT);
if (priv->dr_mode == USB_DR_MODE_HOST) {
- phy_meson8b_usb2_mask_bits(priv, REG_ADP_BC,
+ regmap_update_bits(priv->regmap, REG_DBG_UART,
+ REG_DBG_UART_SET_IDDQ, 0);
+
+ if (priv->match->host_enable_aca) {
+ regmap_update_bits(priv->regmap, REG_ADP_BC,
REG_ADP_BC_ACA_ENABLE,
REG_ADP_BC_ACA_ENABLE);
- udelay(ACA_ENABLE_COMPLETE_TIME);
+ udelay(ACA_ENABLE_COMPLETE_TIME);
- if (phy_meson8b_usb2_read(priv, REG_ADP_BC) &
- REG_ADP_BC_ACA_PIN_FLOAT) {
- dev_warn(&phy->dev, "USB ID detect failed!\n");
- clk_disable_unprepare(priv->clk_usb);
- clk_disable_unprepare(priv->clk_usb_general);
- return -EINVAL;
+ regmap_read(priv->regmap, REG_ADP_BC, &reg);
+ if (reg & REG_ADP_BC_ACA_PIN_FLOAT) {
+ dev_warn(&phy->dev, "USB ID detect failed!\n");
+ clk_disable_unprepare(priv->clk_usb);
+ clk_disable_unprepare(priv->clk_usb_general);
+ reset_control_rearm(priv->reset);
+ return -EINVAL;
+ }
}
}
@@ -198,8 +214,18 @@ static int phy_meson8b_usb2_power_off(struct phy *phy)
{
struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy);
+ if (priv->dr_mode == USB_DR_MODE_HOST)
+ regmap_update_bits(priv->regmap, REG_DBG_UART,
+ REG_DBG_UART_SET_IDDQ,
+ REG_DBG_UART_SET_IDDQ);
+
clk_disable_unprepare(priv->clk_usb);
clk_disable_unprepare(priv->clk_usb_general);
+ reset_control_rearm(priv->reset);
+
+ /* power off the PHY by putting it into reset mode */
+ regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET,
+ REG_CTRL_POWER_ON_RESET);
return 0;
}
@@ -213,18 +239,26 @@ static const struct phy_ops phy_meson8b_usb2_ops = {
static int phy_meson8b_usb2_probe(struct platform_device *pdev)
{
struct phy_meson8b_usb2_priv *priv;
- struct resource *res;
struct phy *phy;
struct phy_provider *phy_provider;
+ void __iomem *base;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(priv->regs))
- return PTR_ERR(priv->regs);
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->match = device_get_match_data(&pdev->dev);
+ if (!priv->match)
+ return -ENODEV;
+
+ priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &phy_meson8b_usb2_regmap_conf);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
priv->clk_usb_general = devm_clk_get(&pdev->dev, "usb_general");
if (IS_ERR(priv->clk_usb_general))
@@ -235,8 +269,9 @@ static int phy_meson8b_usb2_probe(struct platform_device *pdev)
return PTR_ERR(priv->clk_usb);
priv->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
- if (PTR_ERR(priv->reset) == -EPROBE_DEFER)
- return PTR_ERR(priv->reset);
+ if (IS_ERR(priv->reset))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->reset),
+ "Failed to get the reset line");
priv->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1);
if (priv->dr_mode == USB_DR_MODE_UNKNOWN) {
@@ -247,8 +282,8 @@ static int phy_meson8b_usb2_probe(struct platform_device *pdev)
phy = devm_phy_create(&pdev->dev, NULL, &phy_meson8b_usb2_ops);
if (IS_ERR(phy)) {
- dev_err(&pdev->dev, "failed to create PHY\n");
- return PTR_ERR(phy);
+ return dev_err_probe(&pdev->dev, PTR_ERR(phy),
+ "failed to create PHY\n");
}
phy_set_drvdata(phy, priv);
@@ -259,11 +294,32 @@ static int phy_meson8b_usb2_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(phy_provider);
}
+static const struct phy_meson8b_usb2_match_data phy_meson8_usb2_match_data = {
+ .host_enable_aca = false,
+};
+
+static const struct phy_meson8b_usb2_match_data phy_meson8b_usb2_match_data = {
+ .host_enable_aca = true,
+};
+
static const struct of_device_id phy_meson8b_usb2_of_match[] = {
- { .compatible = "amlogic,meson8-usb2-phy", },
- { .compatible = "amlogic,meson8b-usb2-phy", },
- { .compatible = "amlogic,meson-gxbb-usb2-phy", },
- { },
+ {
+ .compatible = "amlogic,meson8-usb2-phy",
+ .data = &phy_meson8_usb2_match_data
+ },
+ {
+ .compatible = "amlogic,meson8b-usb2-phy",
+ .data = &phy_meson8b_usb2_match_data
+ },
+ {
+ .compatible = "amlogic,meson8m2-usb2-phy",
+ .data = &phy_meson8b_usb2_match_data
+ },
+ {
+ .compatible = "amlogic,meson-gxbb-usb2-phy",
+ .data = &phy_meson8b_usb2_match_data
+ },
+ { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, phy_meson8b_usb2_of_match);
@@ -277,5 +333,5 @@ static struct platform_driver phy_meson8b_usb2_driver = {
module_platform_driver(phy_meson8b_usb2_driver);
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
-MODULE_DESCRIPTION("Meson8, Meson8b and GXBB USB2 PHY driver");
+MODULE_DESCRIPTION("Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig
index b29f11c19155..1d89a2fd9b79 100644
--- a/drivers/phy/broadcom/Kconfig
+++ b/drivers/phy/broadcom/Kconfig
@@ -2,6 +2,16 @@
#
# Phy drivers for Broadcom platforms
#
+menu "PHY drivers for Broadcom platforms"
+
+config PHY_BCM63XX_USBH
+ tristate "BCM63xx USBH PHY driver"
+ depends on BMIPS_GENERIC || COMPILE_TEST
+ select GENERIC_PHY
+ help
+ Enable this to support the BCM63xx USBH PHY driver.
+ If unsure, say N.
+
config PHY_CYGNUS_PCIE
tristate "Broadcom Cygnus PCIe PHY driver"
depends on OF && (ARCH_BCM_CYGNUS || COMPILE_TEST)
@@ -73,7 +83,7 @@ config PHY_NS2_USB_DRD
config PHY_BRCM_SATA
tristate "Broadcom SATA PHY driver"
depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || \
- ARCH_BCM_63XX || COMPILE_TEST
+ ARCH_BCMBCA || COMPILE_TEST
depends on OF
select GENERIC_PHY
default ARCH_BCM_IPROC
@@ -83,11 +93,11 @@ config PHY_BRCM_SATA
config PHY_BRCM_USB
tristate "Broadcom STB USB PHY driver"
- depends on ARCH_BRCMSTB || COMPILE_TEST
+ depends on ARCH_BCMBCA || ARCH_BRCMSTB || COMPILE_TEST
depends on OF
select GENERIC_PHY
- select SOC_BRCMSTB
- default ARCH_BRCMSTB
+ select SOC_BRCMSTB if ARCH_BRCMSTB
+ default ARCH_BCMBCA || ARCH_BRCMSTB
help
Enable this to support the Broadcom STB USB PHY.
This driver is required by the USB XHCI, EHCI and OHCI
@@ -103,3 +113,5 @@ config PHY_BCM_SR_PCIE
help
Enable this to support the Broadcom Stingray PCIe PHY
If unsure, say N.
+
+endmenu
diff --git a/drivers/phy/broadcom/Makefile b/drivers/phy/broadcom/Makefile
index c78de546135c..7024127f86ad 100644
--- a/drivers/phy/broadcom/Makefile
+++ b/drivers/phy/broadcom/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PHY_BCM63XX_USBH) += phy-bcm63xx-usbh.o
obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o
obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o
diff --git a/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c b/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c
index b074682d9dd8..cc29b08e49eb 100644
--- a/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c
+++ b/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c
@@ -1,15 +1,5 @@
-/*
- * Copyright (C) 2015 Broadcom Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2015 Broadcom Corporation
#include <linux/delay.h>
#include <linux/io.h>
@@ -126,7 +116,6 @@ static int cygnus_pcie_phy_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node, *child;
struct cygnus_pcie_phy_core *core;
struct phy_provider *provider;
- struct resource *res;
unsigned cnt = 0;
int ret;
@@ -141,8 +130,7 @@ static int cygnus_pcie_phy_probe(struct platform_device *pdev)
core->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- core->base = devm_ioremap_resource(dev, res);
+ core->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(core->base))
return PTR_ERR(core->base);
diff --git a/drivers/phy/broadcom/phy-bcm-kona-usb2.c b/drivers/phy/broadcom/phy-bcm-kona-usb2.c
index 6459296d9bf9..e9cc5f2cb89a 100644
--- a/drivers/phy/broadcom/phy-bcm-kona-usb2.c
+++ b/drivers/phy/broadcom/phy-bcm-kona-usb2.c
@@ -94,7 +94,6 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct bcm_kona_usb *phy;
- struct resource *res;
struct phy *gphy;
struct phy_provider *phy_provider;
@@ -102,8 +101,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
if (!phy)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- phy->regs = devm_ioremap_resource(&pdev->dev, res);
+ phy->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(phy->regs))
return PTR_ERR(phy->regs);
diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb2.c b/drivers/phy/broadcom/phy-bcm-ns-usb2.c
index 9f2f84d65dcd..6a36e187d100 100644
--- a/drivers/phy/broadcom/phy-bcm-ns-usb2.c
+++ b/drivers/phy/broadcom/phy-bcm-ns-usb2.c
@@ -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);
@@ -83,7 +100,6 @@ static int bcm_ns_usb2_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct bcm_ns_usb2 *usb2;
- struct resource *res;
struct phy_provider *phy_provider;
usb2 = devm_kzalloc(&pdev->dev, sizeof(*usb2), GFP_KERNEL);
@@ -91,16 +107,32 @@ static int bcm_ns_usb2_probe(struct platform_device *pdev)
return -ENOMEM;
usb2->dev = dev;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmu");
- usb2->dmu = devm_ioremap_resource(dev, res);
- 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);
}
diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb3.c b/drivers/phy/broadcom/phy-bcm-ns-usb3.c
index 14f45bc35cc5..bbfad209c890 100644
--- a/drivers/phy/broadcom/phy-bcm-ns-usb3.c
+++ b/drivers/phy/broadcom/phy-bcm-ns-usb3.c
@@ -13,6 +13,7 @@
#include <linux/bcma/bcma.h>
#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/iopoll.h>
#include <linux/mdio.h>
#include <linux/module.h>
#include <linux/of_address.h>
@@ -21,8 +22,6 @@
#include <linux/phy/phy.h>
#include <linux/slab.h>
-#define BCM_NS_USB3_MII_MNG_TIMEOUT_US 1000 /* usecs */
-
#define BCM_NS_USB3_PHY_BASE_ADDR_REG 0x1f
#define BCM_NS_USB3_PHY_PLL30_BLOCK 0x8000
#define BCM_NS_USB3_PHY_TX_PMD_BLOCK 0x8040
@@ -50,11 +49,8 @@ struct bcm_ns_usb3 {
struct device *dev;
enum bcm_ns_family family;
void __iomem *dmp;
- void __iomem *ccb_mii;
struct mdio_device *mdiodev;
struct phy *phy;
-
- int (*phy_write)(struct bcm_ns_usb3 *usb3, u16 reg, u16 value);
};
static const struct of_device_id bcm_ns_usb3_id_table[] = {
@@ -68,13 +64,9 @@ static const struct of_device_id bcm_ns_usb3_id_table[] = {
},
{},
};
-MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table);
static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg,
- u16 value)
-{
- return usb3->phy_write(usb3, reg, value);
-}
+ u16 value);
static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3)
{
@@ -186,12 +178,12 @@ static const struct phy_ops ops = {
* MDIO driver code
**************************************************/
-static int bcm_ns_usb3_mdiodev_phy_write(struct bcm_ns_usb3 *usb3, u16 reg,
- u16 value)
+static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg,
+ u16 value)
{
struct mdio_device *mdiodev = usb3->mdiodev;
- return mdiobus_write(mdiodev->bus, mdiodev->addr, reg, value);
+ return mdiodev_write(mdiodev, reg, value);
}
static int bcm_ns_usb3_mdio_probe(struct mdio_device *mdiodev)
@@ -223,12 +215,8 @@ static int bcm_ns_usb3_mdio_probe(struct mdio_device *mdiodev)
return err;
usb3->dmp = devm_ioremap_resource(dev, &res);
- if (IS_ERR(usb3->dmp)) {
- dev_err(dev, "Failed to map DMP regs\n");
+ if (IS_ERR(usb3->dmp))
return PTR_ERR(usb3->dmp);
- }
-
- usb3->phy_write = bcm_ns_usb3_mdiodev_phy_write;
usb3->phy = devm_phy_create(dev, NULL, &ops);
if (IS_ERR(usb3->phy)) {
@@ -253,150 +241,7 @@ static struct mdio_driver bcm_ns_usb3_mdio_driver = {
.probe = bcm_ns_usb3_mdio_probe,
};
-/**************************************************
- * Platform driver code
- **************************************************/
-
-static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr,
- u32 mask, u32 value, unsigned long timeout)
-{
- unsigned long deadline = jiffies + timeout;
- u32 val;
-
- do {
- val = readl(addr);
- if ((val & mask) == value)
- return 0;
- cpu_relax();
- udelay(10);
- } while (!time_after_eq(jiffies, deadline));
-
- dev_err(usb3->dev, "Timeout waiting for register %p\n", addr);
-
- return -EBUSY;
-}
-
-static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3)
-{
- return bcm_ns_usb3_wait_reg(usb3, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL,
- 0x0100, 0x0000,
- usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US));
-}
-
-static int bcm_ns_usb3_platform_phy_write(struct bcm_ns_usb3 *usb3, u16 reg,
- u16 value)
-{
- u32 tmp = 0;
- int err;
-
- err = bcm_ns_usb3_mii_mng_wait_idle(usb3);
- if (err < 0) {
- dev_err(usb3->dev, "Couldn't write 0x%08x value\n", value);
- return err;
- }
-
- /* TODO: Use a proper MDIO bus layer */
- tmp |= 0x58020000; /* Magic value for MDIO PHY write */
- tmp |= reg << 18;
- tmp |= value;
- writel(tmp, usb3->ccb_mii + BCMA_CCB_MII_MNG_CMD_DATA);
-
- return bcm_ns_usb3_mii_mng_wait_idle(usb3);
-}
-
-static int bcm_ns_usb3_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- const struct of_device_id *of_id;
- struct bcm_ns_usb3 *usb3;
- struct resource *res;
- struct phy_provider *phy_provider;
-
- usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL);
- if (!usb3)
- return -ENOMEM;
-
- usb3->dev = dev;
-
- of_id = of_match_device(bcm_ns_usb3_id_table, dev);
- if (!of_id)
- return -EINVAL;
- usb3->family = (enum bcm_ns_family)of_id->data;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmp");
- usb3->dmp = devm_ioremap_resource(dev, res);
- if (IS_ERR(usb3->dmp)) {
- dev_err(dev, "Failed to map DMP regs\n");
- return PTR_ERR(usb3->dmp);
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ccb-mii");
- usb3->ccb_mii = devm_ioremap_resource(dev, res);
- if (IS_ERR(usb3->ccb_mii)) {
- dev_err(dev, "Failed to map ChipCommon B MII regs\n");
- return PTR_ERR(usb3->ccb_mii);
- }
-
- /* Enable MDIO. Setting MDCDIV as 26 */
- writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL);
-
- /* Wait for MDIO? */
- udelay(2);
-
- usb3->phy_write = bcm_ns_usb3_platform_phy_write;
-
- usb3->phy = devm_phy_create(dev, NULL, &ops);
- if (IS_ERR(usb3->phy)) {
- dev_err(dev, "Failed to create PHY\n");
- return PTR_ERR(usb3->phy);
- }
-
- phy_set_drvdata(usb3->phy, usb3);
- platform_set_drvdata(pdev, usb3);
-
- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
- if (!IS_ERR(phy_provider))
- dev_info(dev, "Registered Broadcom Northstar USB 3.0 PHY driver\n");
-
- return PTR_ERR_OR_ZERO(phy_provider);
-}
-
-static struct platform_driver bcm_ns_usb3_driver = {
- .probe = bcm_ns_usb3_probe,
- .driver = {
- .name = "bcm_ns_usb3",
- .of_match_table = bcm_ns_usb3_id_table,
- },
-};
-
-static int __init bcm_ns_usb3_module_init(void)
-{
- int err;
-
- /*
- * For backward compatibility we register as MDIO and platform driver.
- * After getting MDIO binding commonly used (e.g. switching all DT files
- * to use it) we should deprecate the old binding and eventually drop
- * support for it.
- */
-
- err = mdio_driver_register(&bcm_ns_usb3_mdio_driver);
- if (err)
- return err;
-
- err = platform_driver_register(&bcm_ns_usb3_driver);
- if (err)
- mdio_driver_unregister(&bcm_ns_usb3_mdio_driver);
-
- return err;
-}
-module_init(bcm_ns_usb3_module_init);
-
-static void __exit bcm_ns_usb3_module_exit(void)
-{
- platform_driver_unregister(&bcm_ns_usb3_driver);
- mdio_driver_unregister(&bcm_ns_usb3_mdio_driver);
-}
-module_exit(bcm_ns_usb3_module_exit)
+mdio_module_driver(bcm_ns_usb3_mdio_driver);
MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table);
diff --git a/drivers/phy/broadcom/phy-bcm-ns2-pcie.c b/drivers/phy/broadcom/phy-bcm-ns2-pcie.c
index 4c7d11d2b378..2eaa41f8fc70 100644
--- a/drivers/phy/broadcom/phy-bcm-ns2-pcie.c
+++ b/drivers/phy/broadcom/phy-bcm-ns2-pcie.c
@@ -1,15 +1,5 @@
-/*
- * Copyright (C) 2016 Broadcom
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2016 Broadcom
#include <linux/device.h>
#include <linux/module.h>
@@ -29,14 +19,12 @@ static int ns2_pci_phy_init(struct phy *p)
int rc;
/* select the AFE 100MHz block page */
- rc = mdiobus_write(mdiodev->bus, mdiodev->addr,
- BLK_ADDR_REG_OFFSET, PLL_AFE1_100MHZ_BLK);
+ rc = mdiodev_write(mdiodev, BLK_ADDR_REG_OFFSET, PLL_AFE1_100MHZ_BLK);
if (rc)
goto err;
/* set the 100 MHz reference clock amplitude to 2.05 v */
- rc = mdiobus_write(mdiodev->bus, mdiodev->addr,
- PLL_CLK_AMP_OFFSET, PLL_CLK_AMP_2P05V);
+ rc = mdiodev_write(mdiodev, PLL_CLK_AMP_OFFSET, PLL_CLK_AMP_2P05V);
if (rc)
goto err;
diff --git a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c
index 7ceea5ae2704..36ad02c33ac5 100644
--- a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c
+++ b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c
@@ -1,15 +1,5 @@
-/*
- * Copyright (C) 2017 Broadcom
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2017 Broadcom
#include <linux/delay.h>
#include <linux/extcon-provider.h>
@@ -18,6 +8,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
@@ -87,17 +78,11 @@ static const unsigned int usb_extcon_cable[] = {
static inline int pll_lock_stat(u32 usb_reg, int reg_mask,
struct ns2_phy_driver *driver)
{
- int retry = PLL_LOCK_RETRY;
u32 val;
- do {
- udelay(1);
- val = readl(driver->icfgdrd_regs + usb_reg);
- if (val & reg_mask)
- return 0;
- } while (--retry > 0);
-
- return -EBUSY;
+ return readl_poll_timeout_atomic(driver->icfgdrd_regs + usb_reg,
+ val, (val & reg_mask), 1,
+ PLL_LOCK_RETRY);
}
static int ns2_drd_phy_init(struct phy *phy)
@@ -279,7 +264,7 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static struct phy_ops ops = {
+static const struct phy_ops ops = {
.init = ns2_drd_phy_init,
.power_on = ns2_drd_phy_poweron,
.power_off = ns2_drd_phy_poweroff,
@@ -298,7 +283,6 @@ static int ns2_drd_phy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct ns2_phy_driver *driver;
struct ns2_phy_data *data;
- struct resource *res;
int ret;
u32 val;
@@ -312,23 +296,19 @@ static int ns2_drd_phy_probe(struct platform_device *pdev)
if (!driver->data)
return -ENOMEM;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "icfg");
- driver->icfgdrd_regs = devm_ioremap_resource(dev, res);
+ driver->icfgdrd_regs = devm_platform_ioremap_resource_byname(pdev, "icfg");
if (IS_ERR(driver->icfgdrd_regs))
return PTR_ERR(driver->icfgdrd_regs);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rst-ctrl");
- driver->idmdrd_rst_ctrl = devm_ioremap_resource(dev, res);
+ driver->idmdrd_rst_ctrl = devm_platform_ioremap_resource_byname(pdev, "rst-ctrl");
if (IS_ERR(driver->idmdrd_rst_ctrl))
return PTR_ERR(driver->idmdrd_rst_ctrl);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "crmu-ctrl");
- driver->crmu_usb2_ctrl = devm_ioremap_resource(dev, res);
+ driver->crmu_usb2_ctrl = devm_platform_ioremap_resource_byname(pdev, "crmu-ctrl");
if (IS_ERR(driver->crmu_usb2_ctrl))
return PTR_ERR(driver->crmu_usb2_ctrl);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2-strap");
- driver->usb2h_strap_reg = devm_ioremap_resource(dev, res);
+ driver->usb2h_strap_reg = devm_platform_ioremap_resource_byname(pdev, "usb2-strap");
if (IS_ERR(driver->usb2h_strap_reg))
return PTR_ERR(driver->usb2h_strap_reg);
diff --git a/drivers/phy/broadcom/phy-bcm-sr-pcie.c b/drivers/phy/broadcom/phy-bcm-sr-pcie.c
index 96a3af126a78..8a4aadf166cf 100644
--- a/drivers/phy/broadcom/phy-bcm-sr-pcie.c
+++ b/drivers/phy/broadcom/phy-bcm-sr-pcie.c
@@ -217,7 +217,6 @@ static int sr_pcie_phy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct sr_pcie_phy_core *core;
- struct resource *res;
struct phy_provider *provider;
unsigned int phy_idx = 0;
@@ -226,9 +225,7 @@ static int sr_pcie_phy_probe(struct platform_device *pdev)
return -ENOMEM;
core->dev = dev;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- core->base = devm_ioremap_resource(core->dev, res);
+ core->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(core->base))
return PTR_ERR(core->base);
diff --git a/drivers/phy/broadcom/phy-bcm-sr-usb.c b/drivers/phy/broadcom/phy-bcm-sr-usb.c
index fe6c58910e4c..0002da3b5b5d 100644
--- a/drivers/phy/broadcom/phy-bcm-sr-usb.c
+++ b/drivers/phy/broadcom/phy-bcm-sr-usb.c
@@ -5,6 +5,7 @@
#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>
@@ -16,8 +17,6 @@ enum bcm_usb_phy_version {
};
enum bcm_usb_phy_reg {
- PLL_NDIV_FRAC,
- PLL_NDIV_INT,
PLL_CTRL,
PHY_CTRL,
PHY_PLL_CTRL,
@@ -31,18 +30,11 @@ static const u8 bcm_usb_combo_phy_ss[] = {
};
static const u8 bcm_usb_combo_phy_hs[] = {
- [PLL_NDIV_FRAC] = 0x04,
- [PLL_NDIV_INT] = 0x08,
[PLL_CTRL] = 0x0c,
[PHY_CTRL] = 0x10,
};
-#define HSPLL_NDIV_INT_VAL 0x13
-#define HSPLL_NDIV_FRAC_VAL 0x1005
-
static const u8 bcm_usb_hs_phy[] = {
- [PLL_NDIV_FRAC] = 0x0,
- [PLL_NDIV_INT] = 0x4,
[PLL_CTRL] = 0x8,
[PHY_CTRL] = 0xc,
};
@@ -52,7 +44,6 @@ enum pll_ctrl_bits {
SSPLL_SUSPEND_EN,
PLL_SEQ_START,
PLL_LOCK,
- PLL_PDIV,
};
static const u8 u3pll_ctrl[] = {
@@ -66,29 +57,17 @@ static const u8 u3pll_ctrl[] = {
#define HSPLL_PDIV_VAL 0x1
static const u8 u2pll_ctrl[] = {
- [PLL_PDIV] = 1,
[PLL_RESETB] = 5,
[PLL_LOCK] = 6,
};
enum bcm_usb_phy_ctrl_bits {
CORERDY,
- AFE_LDO_PWRDWNB,
- AFE_PLL_PWRDWNB,
- AFE_BG_PWRDWNB,
- PHY_ISO,
PHY_RESETB,
PHY_PCTL,
};
#define PHY_PCTL_MASK 0xffff
-/*
- * 0x0806 of PCTL_VAL has below bits set
- * BIT-8 : refclk divider 1
- * BIT-3:2: device mode; mode is not effect
- * BIT-1: soft reset active low
- */
-#define HSPHY_PCTL_VAL 0x0806
#define SSPHY_PCTL_VAL 0x0006
static const u8 u3phy_ctrl[] = {
@@ -98,10 +77,6 @@ static const u8 u3phy_ctrl[] = {
static const u8 u2phy_ctrl[] = {
[CORERDY] = 0,
- [AFE_LDO_PWRDWNB] = 1,
- [AFE_PLL_PWRDWNB] = 2,
- [AFE_BG_PWRDWNB] = 3,
- [PHY_ISO] = 4,
[PHY_RESETB] = 5,
[PHY_PCTL] = 6,
};
@@ -135,19 +110,15 @@ static inline void bcm_usb_reg32_setbits(void __iomem *addr, uint32_t set)
static int bcm_usb_pll_lock_check(void __iomem *addr, u32 bit)
{
- int retry;
- u32 rd_data;
+ u32 data;
+ int ret;
- retry = PLL_LOCK_RETRY_COUNT;
- do {
- rd_data = readl(addr);
- if (rd_data & bit)
- return 0;
- udelay(1);
- } while (--retry > 0);
+ ret = readl_poll_timeout_atomic(addr, data, (data & bit), 1,
+ PLL_LOCK_RETRY_COUNT);
+ if (ret)
+ pr_err("%s: FAIL\n", __func__);
- pr_err("%s: FAIL\n", __func__);
- return -ETIMEDOUT;
+ return ret;
}
static int bcm_usb_ss_phy_init(struct bcm_usb_phy_cfg *phy_cfg)
@@ -186,38 +157,13 @@ static int bcm_usb_hs_phy_init(struct bcm_usb_phy_cfg *phy_cfg)
int ret = 0;
void __iomem *regs = phy_cfg->regs;
const u8 *offset;
- u32 rd_data;
offset = phy_cfg->offset;
- writel(HSPLL_NDIV_INT_VAL, regs + offset[PLL_NDIV_INT]);
- writel(HSPLL_NDIV_FRAC_VAL, regs + offset[PLL_NDIV_FRAC]);
-
- rd_data = readl(regs + offset[PLL_CTRL]);
- rd_data &= ~(HSPLL_PDIV_MASK << u2pll_ctrl[PLL_PDIV]);
- rd_data |= (HSPLL_PDIV_VAL << u2pll_ctrl[PLL_PDIV]);
- writel(rd_data, regs + offset[PLL_CTRL]);
-
- /* Set Core Ready high */
- bcm_usb_reg32_setbits(regs + offset[PHY_CTRL],
- BIT(u2phy_ctrl[CORERDY]));
-
- /* Maximum timeout for Core Ready done */
- msleep(30);
-
+ bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL],
+ BIT(u2pll_ctrl[PLL_RESETB]));
bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],
BIT(u2pll_ctrl[PLL_RESETB]));
- bcm_usb_reg32_setbits(regs + offset[PHY_CTRL],
- BIT(u2phy_ctrl[PHY_RESETB]));
-
-
- rd_data = readl(regs + offset[PHY_CTRL]);
- rd_data &= ~(PHY_PCTL_MASK << u2phy_ctrl[PHY_PCTL]);
- rd_data |= (HSPHY_PCTL_VAL << u2phy_ctrl[PHY_PCTL]);
- writel(rd_data, regs + offset[PHY_CTRL]);
-
- /* Maximum timeout for PLL reset done */
- msleep(30);
ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL],
BIT(u2pll_ctrl[PLL_LOCK]));
@@ -256,7 +202,7 @@ static int bcm_usb_phy_init(struct phy *phy)
return ret;
}
-static struct phy_ops sr_phy_ops = {
+static const struct phy_ops sr_phy_ops = {
.init = bcm_usb_phy_init,
.reset = bcm_usb_phy_reset,
.owner = THIS_MODULE,
@@ -354,14 +300,12 @@ static int bcm_usb_phy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *dn = dev->of_node;
const struct of_device_id *of_id;
- struct resource *res;
void __iomem *regs;
int ret;
enum bcm_usb_phy_version version;
struct phy_provider *phy_provider;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(dev, res);
+ regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
diff --git a/drivers/phy/broadcom/phy-bcm63xx-usbh.c b/drivers/phy/broadcom/phy-bcm63xx-usbh.c
new file mode 100644
index 000000000000..6c05ba8b08be
--- /dev/null
+++ b/drivers/phy/broadcom/phy-bcm63xx-usbh.c
@@ -0,0 +1,457 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BCM6328 USBH PHY Controller Driver
+ *
+ * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
+ * Copyright (C) 2015 Simon Arlott
+ *
+ * Derived from bcm963xx_4.12L.06B_consumer/kernel/linux/arch/mips/bcm963xx/setup.c:
+ * Copyright (C) 2002 Broadcom Corporation
+ *
+ * Derived from OpenWrt patches:
+ * Copyright (C) 2013 Jonas Gorski <jonas.gorski@gmail.com>
+ * Copyright (C) 2013 Florian Fainelli <f.fainelli@gmail.com>
+ * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+/* USBH control register offsets */
+enum usbh_regs {
+ USBH_BRT_CONTROL1 = 0,
+ USBH_BRT_CONTROL2,
+ USBH_BRT_STATUS1,
+ USBH_BRT_STATUS2,
+ USBH_UTMI_CONTROL1,
+#define USBH_UC1_DEV_MODE_SEL BIT(0)
+ USBH_TEST_PORT_CONTROL,
+ USBH_PLL_CONTROL1,
+#define USBH_PLLC_REFCLKSEL_SHIFT 0
+#define USBH_PLLC_REFCLKSEL_MASK (0x3 << USBH_PLLC_REFCLKSEL_SHIFT)
+#define USBH_PLLC_CLKSEL_SHIFT 2
+#define USBH_PLLC_CLKSEL_MASK (0x3 << USBH_PLLC_CLKSEL_MASK)
+#define USBH_PLLC_XTAL_PWRDWNB BIT(4)
+#define USBH_PLLC_PLL_PWRDWNB BIT(5)
+#define USBH_PLLC_PLL_CALEN BIT(6)
+#define USBH_PLLC_PHYPLL_BYP BIT(7)
+#define USBH_PLLC_PLL_RESET BIT(8)
+#define USBH_PLLC_PLL_IDDQ_PWRDN BIT(9)
+#define USBH_PLLC_PLL_PWRDN_DELAY BIT(10)
+#define USBH_6318_PLLC_PLL_SUSPEND_EN BIT(27)
+#define USBH_6318_PLLC_PHYPLL_BYP BIT(29)
+#define USBH_6318_PLLC_PLL_RESET BIT(30)
+#define USBH_6318_PLLC_PLL_IDDQ_PWRDN BIT(31)
+ USBH_SWAP_CONTROL,
+#define USBH_SC_OHCI_DATA_SWAP BIT(0)
+#define USBH_SC_OHCI_ENDIAN_SWAP BIT(1)
+#define USBH_SC_OHCI_LOGICAL_ADDR_EN BIT(2)
+#define USBH_SC_EHCI_DATA_SWAP BIT(3)
+#define USBH_SC_EHCI_ENDIAN_SWAP BIT(4)
+#define USBH_SC_EHCI_LOGICAL_ADDR_EN BIT(5)
+#define USBH_SC_USB_DEVICE_SEL BIT(6)
+ USBH_GENERIC_CONTROL,
+#define USBH_GC_PLL_SUSPEND_EN BIT(1)
+ USBH_FRAME_ADJUST_VALUE,
+ USBH_SETUP,
+#define USBH_S_IOC BIT(4)
+#define USBH_S_IPP BIT(5)
+ USBH_MDIO,
+ USBH_MDIO32,
+ USBH_USB_SIM_CONTROL,
+#define USBH_USC_LADDR_SEL BIT(5)
+
+ __USBH_ENUM_SIZE
+};
+
+struct bcm63xx_usbh_phy_variant {
+ /* Registers */
+ long regs[__USBH_ENUM_SIZE];
+
+ /* PLLC bits to set/clear for power on */
+ u32 power_pllc_clr;
+ u32 power_pllc_set;
+
+ /* Setup bits to set/clear for power on */
+ u32 setup_clr;
+ u32 setup_set;
+
+ /* Swap Control bits to set */
+ u32 swapctl_dev_set;
+
+ /* Test Port Control value to set if non-zero */
+ u32 tpc_val;
+
+ /* USB Sim Control bits to set */
+ u32 usc_set;
+
+ /* UTMI Control 1 bits to set */
+ u32 utmictl1_dev_set;
+};
+
+struct bcm63xx_usbh_phy {
+ void __iomem *base;
+ struct clk *usbh_clk;
+ struct clk *usb_ref_clk;
+ struct reset_control *reset;
+ const struct bcm63xx_usbh_phy_variant *variant;
+ bool device_mode;
+};
+
+static const struct bcm63xx_usbh_phy_variant usbh_bcm6318 = {
+ .regs = {
+ [USBH_BRT_CONTROL1] = -1,
+ [USBH_BRT_CONTROL2] = -1,
+ [USBH_BRT_STATUS1] = -1,
+ [USBH_BRT_STATUS2] = -1,
+ [USBH_UTMI_CONTROL1] = 0x2c,
+ [USBH_TEST_PORT_CONTROL] = 0x1c,
+ [USBH_PLL_CONTROL1] = 0x04,
+ [USBH_SWAP_CONTROL] = 0x0c,
+ [USBH_GENERIC_CONTROL] = -1,
+ [USBH_FRAME_ADJUST_VALUE] = 0x08,
+ [USBH_SETUP] = 0x00,
+ [USBH_MDIO] = 0x14,
+ [USBH_MDIO32] = 0x18,
+ [USBH_USB_SIM_CONTROL] = 0x20,
+ },
+ .power_pllc_clr = USBH_6318_PLLC_PLL_IDDQ_PWRDN,
+ .power_pllc_set = USBH_6318_PLLC_PLL_SUSPEND_EN,
+ .setup_set = USBH_S_IOC,
+ .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
+ .usc_set = USBH_USC_LADDR_SEL,
+ .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
+};
+
+static const struct bcm63xx_usbh_phy_variant usbh_bcm6328 = {
+ .regs = {
+ [USBH_BRT_CONTROL1] = 0x00,
+ [USBH_BRT_CONTROL2] = 0x04,
+ [USBH_BRT_STATUS1] = 0x08,
+ [USBH_BRT_STATUS2] = 0x0c,
+ [USBH_UTMI_CONTROL1] = 0x10,
+ [USBH_TEST_PORT_CONTROL] = 0x14,
+ [USBH_PLL_CONTROL1] = 0x18,
+ [USBH_SWAP_CONTROL] = 0x1c,
+ [USBH_GENERIC_CONTROL] = 0x20,
+ [USBH_FRAME_ADJUST_VALUE] = 0x24,
+ [USBH_SETUP] = 0x28,
+ [USBH_MDIO] = 0x2c,
+ [USBH_MDIO32] = 0x30,
+ [USBH_USB_SIM_CONTROL] = 0x34,
+ },
+ .setup_set = USBH_S_IOC,
+ .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
+ .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
+};
+
+static const struct bcm63xx_usbh_phy_variant usbh_bcm6358 = {
+ .regs = {
+ [USBH_BRT_CONTROL1] = -1,
+ [USBH_BRT_CONTROL2] = -1,
+ [USBH_BRT_STATUS1] = -1,
+ [USBH_BRT_STATUS2] = -1,
+ [USBH_UTMI_CONTROL1] = -1,
+ [USBH_TEST_PORT_CONTROL] = 0x24,
+ [USBH_PLL_CONTROL1] = -1,
+ [USBH_SWAP_CONTROL] = 0x00,
+ [USBH_GENERIC_CONTROL] = -1,
+ [USBH_FRAME_ADJUST_VALUE] = -1,
+ [USBH_SETUP] = -1,
+ [USBH_MDIO] = -1,
+ [USBH_MDIO32] = -1,
+ [USBH_USB_SIM_CONTROL] = -1,
+ },
+ /*
+ * The magic value comes for the original vendor BSP
+ * and is needed for USB to work. Datasheet does not
+ * help, so the magic value is used as-is.
+ */
+ .tpc_val = 0x1c0020,
+};
+
+static const struct bcm63xx_usbh_phy_variant usbh_bcm6368 = {
+ .regs = {
+ [USBH_BRT_CONTROL1] = 0x00,
+ [USBH_BRT_CONTROL2] = 0x04,
+ [USBH_BRT_STATUS1] = 0x08,
+ [USBH_BRT_STATUS2] = 0x0c,
+ [USBH_UTMI_CONTROL1] = 0x10,
+ [USBH_TEST_PORT_CONTROL] = 0x14,
+ [USBH_PLL_CONTROL1] = 0x18,
+ [USBH_SWAP_CONTROL] = 0x1c,
+ [USBH_GENERIC_CONTROL] = -1,
+ [USBH_FRAME_ADJUST_VALUE] = 0x24,
+ [USBH_SETUP] = 0x28,
+ [USBH_MDIO] = 0x2c,
+ [USBH_MDIO32] = 0x30,
+ [USBH_USB_SIM_CONTROL] = 0x34,
+ },
+ .power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY,
+ .setup_set = USBH_S_IOC,
+ .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
+ .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
+};
+
+static const struct bcm63xx_usbh_phy_variant usbh_bcm63268 = {
+ .regs = {
+ [USBH_BRT_CONTROL1] = 0x00,
+ [USBH_BRT_CONTROL2] = 0x04,
+ [USBH_BRT_STATUS1] = 0x08,
+ [USBH_BRT_STATUS2] = 0x0c,
+ [USBH_UTMI_CONTROL1] = 0x10,
+ [USBH_TEST_PORT_CONTROL] = 0x14,
+ [USBH_PLL_CONTROL1] = 0x18,
+ [USBH_SWAP_CONTROL] = 0x1c,
+ [USBH_GENERIC_CONTROL] = 0x20,
+ [USBH_FRAME_ADJUST_VALUE] = 0x24,
+ [USBH_SETUP] = 0x28,
+ [USBH_MDIO] = 0x2c,
+ [USBH_MDIO32] = 0x30,
+ [USBH_USB_SIM_CONTROL] = 0x34,
+ },
+ .power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY,
+ .setup_clr = USBH_S_IPP,
+ .setup_set = USBH_S_IOC,
+ .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
+ .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
+};
+
+static inline bool usbh_has_reg(struct bcm63xx_usbh_phy *usbh, int reg)
+{
+ return (usbh->variant->regs[reg] >= 0);
+}
+
+static inline u32 usbh_readl(struct bcm63xx_usbh_phy *usbh, int reg)
+{
+ return __raw_readl(usbh->base + usbh->variant->regs[reg]);
+}
+
+static inline void usbh_writel(struct bcm63xx_usbh_phy *usbh, int reg,
+ u32 value)
+{
+ __raw_writel(value, usbh->base + usbh->variant->regs[reg]);
+}
+
+static int bcm63xx_usbh_phy_init(struct phy *phy)
+{
+ struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
+ int ret;
+
+ ret = clk_prepare_enable(usbh->usbh_clk);
+ if (ret) {
+ dev_err(&phy->dev, "unable to enable usbh clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(usbh->usb_ref_clk);
+ if (ret) {
+ dev_err(&phy->dev, "unable to enable usb_ref clock: %d\n", ret);
+ clk_disable_unprepare(usbh->usbh_clk);
+ return ret;
+ }
+
+ ret = reset_control_reset(usbh->reset);
+ if (ret) {
+ dev_err(&phy->dev, "unable to reset device: %d\n", ret);
+ clk_disable_unprepare(usbh->usb_ref_clk);
+ clk_disable_unprepare(usbh->usbh_clk);
+ return ret;
+ }
+
+ /* Configure to work in native CPU endian */
+ if (usbh_has_reg(usbh, USBH_SWAP_CONTROL)) {
+ u32 val = usbh_readl(usbh, USBH_SWAP_CONTROL);
+
+ val |= USBH_SC_EHCI_DATA_SWAP;
+ val &= ~USBH_SC_EHCI_ENDIAN_SWAP;
+
+ val |= USBH_SC_OHCI_DATA_SWAP;
+ val &= ~USBH_SC_OHCI_ENDIAN_SWAP;
+
+ if (usbh->device_mode && usbh->variant->swapctl_dev_set)
+ val |= usbh->variant->swapctl_dev_set;
+
+ usbh_writel(usbh, USBH_SWAP_CONTROL, val);
+ }
+
+ if (usbh_has_reg(usbh, USBH_SETUP)) {
+ u32 val = usbh_readl(usbh, USBH_SETUP);
+
+ val |= usbh->variant->setup_set;
+ val &= ~usbh->variant->setup_clr;
+
+ usbh_writel(usbh, USBH_SETUP, val);
+ }
+
+ if (usbh_has_reg(usbh, USBH_USB_SIM_CONTROL)) {
+ u32 val = usbh_readl(usbh, USBH_USB_SIM_CONTROL);
+
+ val |= usbh->variant->usc_set;
+
+ usbh_writel(usbh, USBH_USB_SIM_CONTROL, val);
+ }
+
+ if (usbh->variant->tpc_val &&
+ usbh_has_reg(usbh, USBH_TEST_PORT_CONTROL))
+ usbh_writel(usbh, USBH_TEST_PORT_CONTROL,
+ usbh->variant->tpc_val);
+
+ if (usbh->device_mode &&
+ usbh_has_reg(usbh, USBH_UTMI_CONTROL1) &&
+ usbh->variant->utmictl1_dev_set) {
+ u32 val = usbh_readl(usbh, USBH_UTMI_CONTROL1);
+
+ val |= usbh->variant->utmictl1_dev_set;
+
+ usbh_writel(usbh, USBH_UTMI_CONTROL1, val);
+ }
+
+ return 0;
+}
+
+static int bcm63xx_usbh_phy_power_on(struct phy *phy)
+{
+ struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
+
+ if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) {
+ u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1);
+
+ val |= usbh->variant->power_pllc_set;
+ val &= ~usbh->variant->power_pllc_clr;
+
+ usbh_writel(usbh, USBH_PLL_CONTROL1, val);
+ }
+
+ return 0;
+}
+
+static int bcm63xx_usbh_phy_power_off(struct phy *phy)
+{
+ struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
+
+ if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) {
+ u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1);
+
+ val &= ~usbh->variant->power_pllc_set;
+ val |= usbh->variant->power_pllc_clr;
+
+ usbh_writel(usbh, USBH_PLL_CONTROL1, val);
+ }
+
+ return 0;
+}
+
+static int bcm63xx_usbh_phy_exit(struct phy *phy)
+{
+ struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
+
+ clk_disable_unprepare(usbh->usbh_clk);
+ clk_disable_unprepare(usbh->usb_ref_clk);
+
+ return 0;
+}
+
+static const struct phy_ops bcm63xx_usbh_phy_ops = {
+ .exit = bcm63xx_usbh_phy_exit,
+ .init = bcm63xx_usbh_phy_init,
+ .power_off = bcm63xx_usbh_phy_power_off,
+ .power_on = bcm63xx_usbh_phy_power_on,
+ .owner = THIS_MODULE,
+};
+
+static struct phy *bcm63xx_usbh_phy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct bcm63xx_usbh_phy *usbh = dev_get_drvdata(dev);
+
+ usbh->device_mode = !!args->args[0];
+
+ return of_phy_simple_xlate(dev, args);
+}
+
+static int __init bcm63xx_usbh_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct bcm63xx_usbh_phy *usbh;
+ const struct bcm63xx_usbh_phy_variant *variant;
+ struct phy *phy;
+ struct phy_provider *phy_provider;
+
+ usbh = devm_kzalloc(dev, sizeof(*usbh), GFP_KERNEL);
+ if (!usbh)
+ return -ENOMEM;
+
+ variant = device_get_match_data(dev);
+ if (!variant)
+ return -EINVAL;
+ usbh->variant = variant;
+
+ usbh->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(usbh->base))
+ return PTR_ERR(usbh->base);
+
+ usbh->reset = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(usbh->reset)) {
+ if (PTR_ERR(usbh->reset) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get reset\n");
+ return PTR_ERR(usbh->reset);
+ }
+
+ usbh->usbh_clk = devm_clk_get_optional(dev, "usbh");
+ if (IS_ERR(usbh->usbh_clk))
+ return PTR_ERR(usbh->usbh_clk);
+
+ usbh->usb_ref_clk = devm_clk_get_optional(dev, "usb_ref");
+ if (IS_ERR(usbh->usb_ref_clk))
+ return PTR_ERR(usbh->usb_ref_clk);
+
+ phy = devm_phy_create(dev, NULL, &bcm63xx_usbh_phy_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(phy);
+ }
+
+ platform_set_drvdata(pdev, usbh);
+ phy_set_drvdata(phy, usbh);
+
+ phy_provider = devm_of_phy_provider_register(dev,
+ bcm63xx_usbh_phy_xlate);
+ if (IS_ERR(phy_provider)) {
+ dev_err(dev, "failed to register PHY provider\n");
+ return PTR_ERR(phy_provider);
+ }
+
+ dev_dbg(dev, "Registered BCM63xx USB PHY driver\n");
+
+ return 0;
+}
+
+static const struct of_device_id bcm63xx_usbh_phy_ids[] __initconst = {
+ { .compatible = "brcm,bcm6318-usbh-phy", .data = &usbh_bcm6318 },
+ { .compatible = "brcm,bcm6328-usbh-phy", .data = &usbh_bcm6328 },
+ { .compatible = "brcm,bcm6358-usbh-phy", .data = &usbh_bcm6358 },
+ { .compatible = "brcm,bcm6362-usbh-phy", .data = &usbh_bcm6368 },
+ { .compatible = "brcm,bcm6368-usbh-phy", .data = &usbh_bcm6368 },
+ { .compatible = "brcm,bcm63268-usbh-phy", .data = &usbh_bcm63268 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, bcm63xx_usbh_phy_ids);
+
+static struct platform_driver bcm63xx_usbh_phy_driver __refdata = {
+ .driver = {
+ .name = "bcm63xx-usbh-phy",
+ .of_match_table = bcm63xx_usbh_phy_ids,
+ },
+ .probe = bcm63xx_usbh_phy_probe,
+};
+module_platform_driver(bcm63xx_usbh_phy_driver);
+
+MODULE_DESCRIPTION("BCM63xx USBH PHY driver");
+MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
+MODULE_AUTHOR("Simon Arlott");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/broadcom/phy-brcm-sata.c b/drivers/phy/broadcom/phy-brcm-sata.c
index 18251f232172..769c707d9b71 100644
--- a/drivers/phy/broadcom/phy-brcm-sata.c
+++ b/drivers/phy/broadcom/phy-brcm-sata.c
@@ -65,6 +65,7 @@ struct brcm_sata_port {
bool ssc_en;
enum brcm_sata_phy_rxaeq_mode rxaeq_mode;
u32 rxaeq_val;
+ u32 tx_amplitude_val;
};
struct brcm_sata_phy {
@@ -84,6 +85,10 @@ enum sata_phy_regs {
BLOCK0_SPARE_OOB_CLK_SEL_MASK = 0x3,
BLOCK0_SPARE_OOB_CLK_SEL_REFBY2 = 0x1,
+ BLOCK1_REG_BANK = 0x10,
+ BLOCK1_TEST_TX = 0x83,
+ BLOCK1_TEST_TX_AMP_SHIFT = 12,
+
PLL_REG_BANK_0 = 0x050,
PLL_REG_BANK_0_PLLCONTROL_0 = 0x81,
PLLCONTROL_0_FREQ_DET_RESTART = BIT(13),
@@ -379,6 +384,29 @@ static int brcm_stb_sata_16nm_ssc_init(struct brcm_sata_port *port)
brcm_sata_phy_wr(port, RXPMD_REG_BANK, RXPMD_RX_FREQ_MON_CONTROL1,
~tmp, RXPMD_MON_CORRECT_EN | value);
+ tmp = GENMASK(15, 12);
+ switch (port->tx_amplitude_val) {
+ case 400:
+ value = BIT(12) | BIT(13);
+ break;
+ case 500:
+ value = BIT(13);
+ break;
+ case 600:
+ value = BIT(12);
+ break;
+ case 800:
+ value = 0;
+ break;
+ default:
+ value = tmp;
+ break;
+ }
+
+ if (value != tmp)
+ brcm_sata_phy_wr(port, BLOCK1_REG_BANK, BLOCK1_TEST_TX, ~tmp,
+ value);
+
/* Turn on/off SSC */
brcm_sata_phy_wr(port, TX_REG_BANK, TX_ACTRL5, ~TX_ACTRL5_SSC_EN,
port->ssc_en ? TX_ACTRL5_SSC_EN : 0);
@@ -623,7 +651,7 @@ static int brcm_dsl_sata_init(struct brcm_sata_port *port)
break;
msleep(20);
try--;
- };
+ }
if (!try) {
/* PLL did not lock; give up */
@@ -726,7 +754,6 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
struct device_node *dn = dev->of_node, *child;
const struct of_device_id *of_id;
struct brcm_sata_phy *priv;
- struct resource *res;
struct phy_provider *provider;
int ret, count = 0;
@@ -739,8 +766,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
dev_set_drvdata(dev, priv);
priv->dev = dev;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
- priv->phy_base = devm_ioremap_resource(dev, res);
+ priv->phy_base = devm_platform_ioremap_resource_byname(pdev, "phy");
if (IS_ERR(priv->phy_base))
return PTR_ERR(priv->phy_base);
@@ -751,9 +777,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
priv->version = BRCM_SATA_PHY_STB_28NM;
if (priv->version == BRCM_SATA_PHY_IPROC_NS2) {
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "phy-ctrl");
- priv->ctrl_base = devm_ioremap_resource(dev, res);
+ priv->ctrl_base = devm_platform_ioremap_resource_byname(pdev, "phy-ctrl");
if (IS_ERR(priv->ctrl_base))
return PTR_ERR(priv->ctrl_base);
}
@@ -791,6 +815,10 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
if (port->rxaeq_mode == RXAEQ_MODE_MANUAL)
of_property_read_u32(child, "brcm,rxaeq-value",
&port->rxaeq_val);
+
+ of_property_read_u32(child, "brcm,tx-amplitude-millivolt",
+ &port->tx_amplitude_val);
+
port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc");
if (IS_ERR(port->phy)) {
dev_err(dev, "failed to create PHY\n");
diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
index 456dc4a100c2..d2524b70ea16 100644
--- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
+++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
@@ -47,6 +47,8 @@
#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000
#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000
+#define USB_CTRL_USB_PM_XHC_PME_EN_MASK 0x00000010
+#define USB_CTRL_USB_PM_XHC_S2_CLK_SWITCH_EN_MASK 0x00000008
#define USB_CTRL_USB_PM_STATUS 0x08
#define USB_CTRL_USB_DEVICE_CTL1 0x10
#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003
@@ -190,10 +192,6 @@ static void usb_init_common(struct brcm_usb_init_params *params)
pr_debug("%s\n", __func__);
- USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
- /* 1 millisecond - for USB clocks to settle down */
- usleep_range(1000, 2000);
-
if (USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
@@ -222,6 +220,17 @@ static void usb_wake_enable_7211b0(struct brcm_usb_init_params *params,
USB_CTRL_UNSET(ctrl, CTLR_CSHCR, ctl_pme_en);
}
+static void usb_wake_enable_7216(struct brcm_usb_init_params *params,
+ bool enable)
+{
+ void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+
+ if (enable)
+ USB_CTRL_SET(ctrl, USB_PM, XHC_PME_EN);
+ else
+ USB_CTRL_UNSET(ctrl, USB_PM, XHC_PME_EN);
+}
+
static void usb_init_common_7211b0(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
@@ -270,11 +279,6 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params)
reg |= params->mode << USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT;
brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1);
- /* Fix the incorrect default */
- reg = brcm_usb_readl(ctrl + USB_CTRL_SETUP);
- reg &= ~USB_CTRL_SETUP_tca_drv_sel_MASK;
- brcm_usb_writel(reg, ctrl + USB_CTRL_SETUP);
-
usb_init_common(params);
/*
@@ -300,6 +304,20 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params)
usb2_eye_fix_7211b0(params);
}
+static void usb_init_common_7216(struct brcm_usb_init_params *params)
+{
+ void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+
+ USB_CTRL_UNSET(ctrl, USB_PM, XHC_S2_CLK_SWITCH_EN);
+ USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
+
+ /* 1 millisecond - for USB clocks to settle down */
+ usleep_range(1000, 2000);
+
+ usb_wake_enable_7216(params, false);
+ usb_init_common(params);
+}
+
static void usb_init_xhci(struct brcm_usb_init_params *params)
{
pr_debug("%s\n", __func__);
@@ -307,14 +325,20 @@ static void usb_init_xhci(struct brcm_usb_init_params *params)
xhci_soft_reset(params, 0);
}
-static void usb_uninit_common(struct brcm_usb_init_params *params)
+static void usb_uninit_common_7216(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
pr_debug("%s\n", __func__);
- USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
+ if (!params->wake_enabled) {
+ USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
+ /* Switch to using slower clock during suspend to save power */
+ USB_CTRL_SET(ctrl, USB_PM, XHC_S2_CLK_SWITCH_EN);
+ } else {
+ usb_wake_enable_7216(params, true);
+ }
}
static void usb_uninit_common_7211b0(struct brcm_usb_init_params *params)
@@ -376,9 +400,9 @@ static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
static const struct brcm_usb_init_ops bcm7216_ops = {
.init_ipp = usb_init_ipp,
- .init_common = usb_init_common,
+ .init_common = usb_init_common_7216,
.init_xhci = usb_init_xhci,
- .uninit_common = usb_uninit_common,
+ .uninit_common = usb_uninit_common_7216,
.uninit_xhci = usb_uninit_xhci,
.get_dual_select = usb_get_dual_select,
.set_dual_select = usb_set_dual_select,
@@ -401,6 +425,7 @@ void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params)
params->family_name = "7216";
params->ops = &bcm7216_ops;
+ params->suspend_with_clocks = true;
}
void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params)
diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c
index 9391ab42a12b..dddcbd3cd5f3 100644
--- a/drivers/phy/broadcom/phy-brcm-usb-init.c
+++ b/drivers/phy/broadcom/phy-brcm-usb-init.c
@@ -79,6 +79,7 @@
enum brcm_family_type {
BRCM_FAMILY_3390A0,
+ BRCM_FAMILY_4908,
BRCM_FAMILY_7250B0,
BRCM_FAMILY_7271A0,
BRCM_FAMILY_7364A0,
@@ -96,6 +97,7 @@ enum brcm_family_type {
static const char *family_names[BRCM_FAMILY_COUNT] = {
USB_BRCM_FAMILY(3390A0),
+ USB_BRCM_FAMILY(4908),
USB_BRCM_FAMILY(7250B0),
USB_BRCM_FAMILY(7271A0),
USB_BRCM_FAMILY(7364A0),
@@ -203,6 +205,27 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK,
ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */
},
+ /* 4908 */
+ [BRCM_FAMILY_4908] = {
+ 0, /* USB_CTRL_SETUP_SCB1_EN_MASK */
+ 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */
+ 0, /* USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK */
+ 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
+ 0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */
+ 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
+ 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
+ USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK,
+ USB_CTRL_USB_PM_USB_PWRDN_MASK,
+ 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */
+ 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */
+ 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */
+ 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */
+ 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */
+ 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */
+ 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */
+ 0, /* USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK */
+ 0, /* USB_CTRL_SETUP ENDIAN bits */
+ },
/* 7250b0 */
[BRCM_FAMILY_7250B0] = {
USB_CTRL_SETUP_SCB1_EN_MASK,
@@ -559,6 +582,7 @@ static void brcmusb_usb3_pll_54mhz(struct brcm_usb_init_params *params)
*/
switch (params->selected_family) {
case BRCM_FAMILY_3390A0:
+ case BRCM_FAMILY_4908:
case BRCM_FAMILY_7250B0:
case BRCM_FAMILY_7366C0:
case BRCM_FAMILY_74371A0:
@@ -837,7 +861,7 @@ static void usb_init_common(struct brcm_usb_init_params *params)
brcmusb_usb2_eye_fix(ctrl);
/*
- * Make sure the the second and third memory controller
+ * Make sure the second and third memory controller
* interfaces are enabled if they exist.
*/
if (USB_CTRL_MASK_FAMILY(params, SETUP, SCB1_EN))
@@ -1004,6 +1028,18 @@ static const struct brcm_usb_init_ops bcm7445_ops = {
.set_dual_select = usb_set_dual_select,
};
+void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params)
+{
+ int fam;
+
+ fam = BRCM_FAMILY_4908;
+ params->selected_family = fam;
+ params->usb_reg_bits_map =
+ &usb_reg_bits_map_table[fam][0];
+ params->family_name = family_names[fam];
+ params->ops = &bcm7445_ops;
+}
+
void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params)
{
int fam;
diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h
index 899b9eb43fad..1ccb5ddab865 100644
--- a/drivers/phy/broadcom/phy-brcm-usb-init.h
+++ b/drivers/phy/broadcom/phy-brcm-usb-init.h
@@ -64,6 +64,7 @@ struct brcm_usb_init_params {
bool suspend_with_clocks;
};
+void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params);
void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params);
void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params);
void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params);
@@ -78,7 +79,7 @@ static inline u32 brcm_usb_readl(void __iomem *addr)
* Other architectures (e.g., ARM) either do not support big endian, or
* else leave I/O in little endian mode.
*/
- if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
return __raw_readl(addr);
else
return readl_relaxed(addr);
@@ -87,7 +88,7 @@ static inline u32 brcm_usb_readl(void __iomem *addr)
static inline void brcm_usb_writel(u32 val, void __iomem *addr)
{
/* See brcmnand_readl() comments */
- if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
__raw_writel(val, addr);
else
writel_relaxed(val, addr);
diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c
index 491bbd46c5b3..2cb3779fcdf8 100644
--- a/drivers/phy/broadcom/phy-brcm-usb.c
+++ b/drivers/phy/broadcom/phy-brcm-usb.c
@@ -11,12 +11,14 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/soc/brcmstb/brcmstb.h>
#include <dt-bindings/phy/phy.h>
#include <linux/mfd/syscon.h>
+#include <linux/suspend.h>
#include "phy-brcm-usb-init.h"
@@ -34,19 +36,19 @@ struct value_to_name_map {
};
struct match_chip_info {
- void *init_func;
+ void (*init_func)(struct brcm_usb_init_params *params);
u8 required_regs[BRCM_REGS_MAX + 1];
u8 optional_reg;
};
-static struct value_to_name_map brcm_dr_mode_to_name[] = {
+static const struct value_to_name_map brcm_dr_mode_to_name[] = {
{ USB_CTLR_MODE_HOST, "host" },
{ USB_CTLR_MODE_DEVICE, "peripheral" },
{ USB_CTLR_MODE_DRD, "drd" },
{ USB_CTLR_MODE_TYPEC_PD, "typec-pd" }
};
-static struct value_to_name_map brcm_dual_mode_to_name[] = {
+static const struct value_to_name_map brcm_dual_mode_to_name[] = {
{ 0, "host" },
{ 1, "device" },
{ 2, "auto" },
@@ -69,12 +71,35 @@ struct brcm_usb_phy_data {
int init_count;
int wake_irq;
struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX];
+ struct notifier_block pm_notifier;
+ bool pm_active;
};
static s8 *node_reg_names[BRCM_REGS_MAX] = {
"crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec"
};
+static int brcm_pm_notifier(struct notifier_block *notifier,
+ unsigned long pm_event,
+ void *unused)
+{
+ struct brcm_usb_phy_data *priv =
+ container_of(notifier, struct brcm_usb_phy_data, pm_notifier);
+
+ switch (pm_event) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ priv->pm_active = true;
+ break;
+ case PM_POST_RESTORE:
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ priv->pm_active = false;
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id)
{
struct phy *gphy = dev_id;
@@ -90,6 +115,9 @@ static int brcm_usb_phy_init(struct phy *gphy)
struct brcm_usb_phy_data *priv =
container_of(phy, struct brcm_usb_phy_data, phys[phy->id]);
+ if (priv->pm_active)
+ return 0;
+
/*
* Use a lock to make sure a second caller waits until
* the base phy is inited before using it.
@@ -119,6 +147,9 @@ static int brcm_usb_phy_exit(struct phy *gphy)
struct brcm_usb_phy_data *priv =
container_of(phy, struct brcm_usb_phy_data, phys[phy->id]);
+ if (priv->pm_active)
+ return 0;
+
dev_dbg(&gphy->dev, "EXIT\n");
if (phy->id == BRCM_USB_PHY_2_0)
brcm_usb_uninit_eohci(&priv->ini);
@@ -138,7 +169,7 @@ static int brcm_usb_phy_exit(struct phy *gphy)
return 0;
}
-static struct phy_ops brcm_usb_phy_ops = {
+static const struct phy_ops brcm_usb_phy_ops = {
.init = brcm_usb_phy_init,
.exit = brcm_usb_phy_exit,
.owner = THIS_MODULE,
@@ -170,7 +201,7 @@ static struct phy *brcm_usb_phy_xlate(struct device *dev,
return ERR_PTR(-ENODEV);
}
-static int name_to_value(struct value_to_name_map *table, int count,
+static int name_to_value(const struct value_to_name_map *table, int count,
const char *name, int *value)
{
int x;
@@ -185,7 +216,7 @@ static int name_to_value(struct value_to_name_map *table, int count,
return -EINVAL;
}
-static const char *value_to_name(struct value_to_name_map *table, int count,
+static const char *value_to_name(const struct value_to_name_map *table, int count,
int value)
{
if (value >= count)
@@ -252,7 +283,16 @@ static const struct attribute_group brcm_usb_phy_group = {
.attrs = brcm_usb_phy_attrs,
};
-static struct match_chip_info chip_info_7216 = {
+static const struct match_chip_info chip_info_4908 = {
+ .init_func = &brcm_usb_dvr_init_4908,
+ .required_regs = {
+ BRCM_REGS_CTRL,
+ BRCM_REGS_XHCI_EC,
+ -1,
+ },
+};
+
+static const struct match_chip_info chip_info_7216 = {
.init_func = &brcm_usb_dvr_init_7216,
.required_regs = {
BRCM_REGS_CTRL,
@@ -262,7 +302,7 @@ static struct match_chip_info chip_info_7216 = {
},
};
-static struct match_chip_info chip_info_7211b0 = {
+static const struct match_chip_info chip_info_7211b0 = {
.init_func = &brcm_usb_dvr_init_7211b0,
.required_regs = {
BRCM_REGS_CTRL,
@@ -275,7 +315,7 @@ static struct match_chip_info chip_info_7211b0 = {
.optional_reg = BRCM_REGS_BDC_EC,
};
-static struct match_chip_info chip_info_7445 = {
+static const struct match_chip_info chip_info_7445 = {
.init_func = &brcm_usb_dvr_init_7445,
.required_regs = {
BRCM_REGS_CTRL,
@@ -286,6 +326,10 @@ static struct match_chip_info chip_info_7445 = {
static const struct of_device_id brcm_usb_dt_ids[] = {
{
+ .compatible = "brcm,bcm4908-usb-phy",
+ .data = &chip_info_4908,
+ },
+ {
.compatible = "brcm,bcm7216-usb-phy",
.data = &chip_info_7216,
},
@@ -427,8 +471,6 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
struct device_node *dn = pdev->dev.of_node;
int err;
const char *mode;
- const struct of_device_id *match;
- void (*dvr_init)(struct brcm_usb_init_params *params);
const struct match_chip_info *info;
struct regmap *rmap;
int x;
@@ -441,10 +483,11 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
priv->ini.family_id = brcmstb_get_family_id();
priv->ini.product_id = brcmstb_get_product_id();
- match = of_match_node(brcm_usb_dt_ids, dev->of_node);
- info = match->data;
- dvr_init = info->init_func;
- (*dvr_init)(&priv->ini);
+ info = of_device_get_match_data(&pdev->dev);
+ if (!info)
+ return -ENOENT;
+
+ info->init_func(&priv->ini);
dev_dbg(dev, "Best mapping table is for %s\n",
priv->ini.family_name);
@@ -484,6 +527,9 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
if (err)
return err;
+ priv->pm_notifier.notifier_call = brcm_pm_notifier;
+ register_pm_notifier(&priv->pm_notifier);
+
mutex_init(&priv->mutex);
/* make sure invert settings are correct */
@@ -524,7 +570,10 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
static int brcm_usb_phy_remove(struct platform_device *pdev)
{
+ struct brcm_usb_phy_data *priv = dev_get_drvdata(&pdev->dev);
+
sysfs_remove_group(&pdev->dev.kobj, &brcm_usb_phy_group);
+ unregister_pm_notifier(&priv->pm_notifier);
return 0;
}
@@ -535,6 +584,7 @@ static int brcm_usb_phy_suspend(struct device *dev)
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
if (priv->init_count) {
+ dev_dbg(dev, "SUSPEND\n");
priv->ini.wake_enabled = device_may_wakeup(dev);
if (priv->phys[BRCM_USB_PHY_3_0].inited)
brcm_usb_uninit_xhci(&priv->ini);
@@ -574,6 +624,7 @@ static int brcm_usb_phy_resume(struct device *dev)
* Uninitialize anything that wasn't previously initialized.
*/
if (priv->init_count) {
+ dev_dbg(dev, "RESUME\n");
if (priv->wake_irq >= 0)
disable_irq_wake(priv->wake_irq);
brcm_usb_init_common(&priv->ini);
diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
index b2db916db64b..1adde2d99ae7 100644
--- a/drivers/phy/cadence/Kconfig
+++ b/drivers/phy/cadence/Kconfig
@@ -3,13 +3,14 @@
# Phy drivers for Cadence PHYs
#
-config PHY_CADENCE_DP
- tristate "Cadence MHDP DisplayPort PHY driver"
+config PHY_CADENCE_TORRENT
+ tristate "Cadence Torrent PHY driver"
depends on OF
depends on HAS_IOMEM
+ depends on COMMON_CLK
select GENERIC_PHY
help
- Support for Cadence MHDP DisplayPort PHY.
+ Support for Cadence Torrent PHY.
config PHY_CADENCE_DPHY
tristate "Cadence D-PHY Support"
@@ -21,9 +22,27 @@ config PHY_CADENCE_DPHY
system. If M is selected, the module will be called
cdns-dphy.
+config PHY_CADENCE_DPHY_RX
+ tristate "Cadence D-PHY Rx Support"
+ depends on HAS_IOMEM && OF
+ select GENERIC_PHY
+ select GENERIC_PHY_MIPI_DPHY
+ help
+ Support for Cadence D-PHY in Rx configuration.
+
config PHY_CADENCE_SIERRA
tristate "Cadence Sierra PHY Driver"
depends on OF && HAS_IOMEM && RESET_CONTROLLER
+ depends on COMMON_CLK
select GENERIC_PHY
help
Enable this to support the Cadence Sierra PHY driver
+
+config PHY_CADENCE_SALVO
+ tristate "Cadence Salvo PHY Driver"
+ depends on OF && HAS_IOMEM
+ select GENERIC_PHY
+ help
+ Enable this to support the Cadence SALVO PHY driver,
+ this PHY is a legacy PHY, and only are used for USB3
+ and USB2.
diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile
index 8f89560f1711..e17f035ddece 100644
--- a/drivers/phy/cadence/Makefile
+++ b/drivers/phy/cadence/Makefile
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_PHY_CADENCE_DP) += phy-cadence-dp.o
+obj-$(CONFIG_PHY_CADENCE_TORRENT) += phy-cadence-torrent.o
obj-$(CONFIG_PHY_CADENCE_DPHY) += cdns-dphy.o
+obj-$(CONFIG_PHY_CADENCE_DPHY_RX) += cdns-dphy-rx.o
obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o
+obj-$(CONFIG_PHY_CADENCE_SALVO) += phy-cadence-salvo.o
diff --git a/drivers/phy/cadence/cdns-dphy-rx.c b/drivers/phy/cadence/cdns-dphy-rx.c
new file mode 100644
index 000000000000..572c70089a94
--- /dev/null
+++ b/drivers/phy/cadence/cdns-dphy-rx.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+#include <linux/platform_device.h>
+
+#define DPHY_PMA_CMN(reg) (reg)
+#define DPHY_PCS(reg) (0xb00 + (reg))
+#define DPHY_ISO(reg) (0xc00 + (reg))
+
+#define DPHY_CMN_SSM DPHY_PMA_CMN(0x20)
+#define DPHY_CMN_RX_MODE_EN BIT(10)
+#define DPHY_CMN_RX_BANDGAP_TIMER_MASK GENMASK(8, 1)
+#define DPHY_CMN_SSM_EN BIT(0)
+
+#define DPHY_CMN_RX_BANDGAP_TIMER 0x14
+
+#define DPHY_BAND_CFG DPHY_PCS(0x0)
+#define DPHY_BAND_CFG_RIGHT_BAND GENMASK(9, 5)
+#define DPHY_BAND_CFG_LEFT_BAND GENMASK(4, 0)
+
+#define DPHY_POWER_ISLAND_EN_DATA DPHY_PCS(0x8)
+#define DPHY_POWER_ISLAND_EN_DATA_VAL 0xaaaaaaaa
+
+#define DPHY_POWER_ISLAND_EN_CLK DPHY_PCS(0xc)
+#define DPHY_POWER_ISLAND_EN_CLK_VAL 0xaa
+
+#define DPHY_ISO_CL_CTRL_L DPHY_ISO(0x10)
+#define DPHY_ISO_DL_CTRL_L0 DPHY_ISO(0x14)
+#define DPHY_ISO_DL_CTRL_L1 DPHY_ISO(0x20)
+#define DPHY_ISO_DL_CTRL_L2 DPHY_ISO(0x30)
+#define DPHY_ISO_DL_CTRL_L3 DPHY_ISO(0x3c)
+
+#define DPHY_ISO_LANE_READY_BIT 0
+#define DPHY_ISO_LANE_READY_TIMEOUT_MS 100UL
+
+#define DPHY_LANES_MIN 1
+#define DPHY_LANES_MAX 4
+
+struct cdns_dphy_rx {
+ void __iomem *regs;
+ struct device *dev;
+ struct phy *phy;
+};
+
+struct cdns_dphy_rx_band {
+ /* Rates are in Mbps. */
+ unsigned int min_rate;
+ unsigned int max_rate;
+};
+
+/* Order of bands is important since the index is the band number. */
+static const struct cdns_dphy_rx_band bands[] = {
+ { 80, 100 }, { 100, 120 }, { 120, 160 }, { 160, 200 }, { 200, 240 },
+ { 240, 280 }, { 280, 320 }, { 320, 360 }, { 360, 400 }, { 400, 480 },
+ { 480, 560 }, { 560, 640 }, { 640, 720 }, { 720, 800 }, { 800, 880 },
+ { 880, 1040 }, { 1040, 1200 }, { 1200, 1350 }, { 1350, 1500 },
+ { 1500, 1750 }, { 1750, 2000 }, { 2000, 2250 }, { 2250, 2500 }
+};
+
+static int cdns_dphy_rx_power_on(struct phy *phy)
+{
+ struct cdns_dphy_rx *dphy = phy_get_drvdata(phy);
+
+ /* Start RX state machine. */
+ writel(DPHY_CMN_SSM_EN | DPHY_CMN_RX_MODE_EN |
+ FIELD_PREP(DPHY_CMN_RX_BANDGAP_TIMER_MASK,
+ DPHY_CMN_RX_BANDGAP_TIMER),
+ dphy->regs + DPHY_CMN_SSM);
+
+ return 0;
+}
+
+static int cdns_dphy_rx_power_off(struct phy *phy)
+{
+ struct cdns_dphy_rx *dphy = phy_get_drvdata(phy);
+
+ writel(0, dphy->regs + DPHY_CMN_SSM);
+
+ return 0;
+}
+
+static int cdns_dphy_rx_get_band_ctrl(unsigned long hs_clk_rate)
+{
+ unsigned int rate, i;
+
+ rate = hs_clk_rate / 1000000UL;
+ /* Since CSI-2 clock is DDR, the bit rate is twice the clock rate. */
+ rate *= 2;
+
+ if (rate < bands[0].min_rate)
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < ARRAY_SIZE(bands); i++)
+ if (rate < bands[i].max_rate)
+ return i;
+
+ return -EOPNOTSUPP;
+}
+
+static inline int cdns_dphy_rx_wait_for_bit(void __iomem *addr,
+ unsigned int bit)
+{
+ u32 val;
+
+ return readl_relaxed_poll_timeout(addr, val, val & BIT(bit), 10,
+ DPHY_ISO_LANE_READY_TIMEOUT_MS * 1000);
+}
+
+static int cdns_dphy_rx_wait_lane_ready(struct cdns_dphy_rx *dphy,
+ unsigned int lanes)
+{
+ static const u32 data_lane_ctrl[] = {DPHY_ISO_DL_CTRL_L0,
+ DPHY_ISO_DL_CTRL_L1,
+ DPHY_ISO_DL_CTRL_L2,
+ DPHY_ISO_DL_CTRL_L3};
+ void __iomem *reg = dphy->regs;
+ unsigned int i;
+ int ret;
+
+ /* Clock lane */
+ ret = cdns_dphy_rx_wait_for_bit(reg + DPHY_ISO_CL_CTRL_L,
+ DPHY_ISO_LANE_READY_BIT);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < lanes; i++) {
+ ret = cdns_dphy_rx_wait_for_bit(reg + data_lane_ctrl[i],
+ DPHY_ISO_LANE_READY_BIT);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cdns_dphy_rx_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ struct cdns_dphy_rx *dphy = phy_get_drvdata(phy);
+ unsigned int reg, lanes = opts->mipi_dphy.lanes;
+ int band_ctrl, ret;
+
+ /* Data lanes. Minimum one lane is mandatory. */
+ if (lanes < DPHY_LANES_MIN || lanes > DPHY_LANES_MAX)
+ return -EINVAL;
+
+ band_ctrl = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate);
+ if (band_ctrl < 0)
+ return band_ctrl;
+
+ reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) |
+ FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl);
+ writel(reg, dphy->regs + DPHY_BAND_CFG);
+
+ /*
+ * Set the required power island phase 2 time. This is mandated by DPHY
+ * specs.
+ */
+ reg = DPHY_POWER_ISLAND_EN_DATA_VAL;
+ writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_DATA);
+ reg = DPHY_POWER_ISLAND_EN_CLK_VAL;
+ writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_CLK);
+
+ ret = cdns_dphy_rx_wait_lane_ready(dphy, lanes);
+ if (ret) {
+ dev_err(dphy->dev, "DPHY wait for lane ready timeout\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cdns_dphy_rx_validate(struct phy *phy, enum phy_mode mode,
+ int submode, union phy_configure_opts *opts)
+{
+ int ret;
+
+ if (mode != PHY_MODE_MIPI_DPHY)
+ return -EINVAL;
+
+ ret = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate);
+ if (ret < 0)
+ return ret;
+
+ return phy_mipi_dphy_config_validate(&opts->mipi_dphy);
+}
+
+static const struct phy_ops cdns_dphy_rx_ops = {
+ .power_on = cdns_dphy_rx_power_on,
+ .power_off = cdns_dphy_rx_power_off,
+ .configure = cdns_dphy_rx_configure,
+ .validate = cdns_dphy_rx_validate,
+};
+
+static int cdns_dphy_rx_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *provider;
+ struct cdns_dphy_rx *dphy;
+
+ dphy = devm_kzalloc(dev, sizeof(*dphy), GFP_KERNEL);
+ if (!dphy)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, dphy);
+ dphy->dev = dev;
+
+ dphy->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(dphy->regs))
+ return PTR_ERR(dphy->regs);
+
+ dphy->phy = devm_phy_create(dev, NULL, &cdns_dphy_rx_ops);
+ if (IS_ERR(dphy->phy)) {
+ dev_err(dev, "Failed to create PHY: %ld\n", PTR_ERR(dphy->phy));
+ return PTR_ERR(dphy->phy);
+ }
+
+ phy_set_drvdata(dphy->phy, dphy);
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(provider)) {
+ dev_err(dev, "Failed to register PHY provider: %ld\n",
+ PTR_ERR(provider));
+ return PTR_ERR(provider);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id cdns_dphy_rx_of_match[] = {
+ { .compatible = "cdns,dphy-rx" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, cdns_dphy_rx_of_match);
+
+static struct platform_driver cdns_dphy_rx_platform_driver = {
+ .probe = cdns_dphy_rx_probe,
+ .driver = {
+ .name = "cdns-mipi-dphy-rx",
+ .of_match_table = cdns_dphy_rx_of_match,
+ },
+};
+module_platform_driver(cdns_dphy_rx_platform_driver);
+
+MODULE_AUTHOR("Pratyush Yadav <p.yadav@ti.com>");
+MODULE_DESCRIPTION("Cadence D-PHY Rx Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c
index 90c4e9b5aac8..3dfdfb33cd0a 100644
--- a/drivers/phy/cadence/cdns-dphy.c
+++ b/drivers/phy/cadence/cdns-dphy.c
@@ -3,9 +3,11 @@
* Copyright: 2017-2018 Cadence Design Systems, Inc.
*/
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
@@ -17,6 +19,7 @@
#define REG_WAKEUP_TIME_NS 800
#define DPHY_PLL_RATE_HZ 108000000
+#define POLL_TIMEOUT_US 1000
/* DPHY registers */
#define DPHY_PMA_CMN(reg) (reg)
@@ -45,6 +48,10 @@
#define DPHY_CMN_OPDIV_FROM_REG BIT(6)
#define DPHY_CMN_OPDIV(x) ((x) << 7)
+#define DPHY_BAND_CFG DPHY_PCS(0x0)
+#define DPHY_BAND_CFG_LEFT_BAND GENMASK(4, 0)
+#define DPHY_BAND_CFG_RIGHT_BAND GENMASK(9, 5)
+
#define DPHY_PSM_CFG DPHY_PCS(0x4)
#define DPHY_PSM_CFG_FROM_REG BIT(0)
#define DPHY_PSM_CLK_DIV(x) ((x) << 1)
@@ -57,6 +64,18 @@
#define DSI_NULL_FRAME_OVERHEAD 6
#define DSI_EOT_PKT_SIZE 4
+#define DPHY_TX_J721E_WIZ_PLL_CTRL 0xF04
+#define DPHY_TX_J721E_WIZ_STATUS 0xF08
+#define DPHY_TX_J721E_WIZ_RST_CTRL 0xF0C
+#define DPHY_TX_J721E_WIZ_PSM_FREQ 0xF10
+
+#define DPHY_TX_J721E_WIZ_IPDIV GENMASK(4, 0)
+#define DPHY_TX_J721E_WIZ_OPDIV GENMASK(13, 8)
+#define DPHY_TX_J721E_WIZ_FBDIV GENMASK(25, 16)
+#define DPHY_TX_J721E_WIZ_LANE_RSTB BIT(31)
+#define DPHY_TX_WIZ_PLL_LOCK BIT(31)
+#define DPHY_TX_WIZ_O_CMN_READY BIT(31)
+
struct cdns_dphy_cfg {
u8 pll_ipdiv;
u8 pll_opdiv;
@@ -92,6 +111,12 @@ struct cdns_dphy {
struct phy *phy;
};
+/* Order of bands is important since the index is the band number. */
+static const unsigned int tx_bands[] = {
+ 80, 100, 120, 160, 200, 240, 320, 390, 450, 510, 560, 640, 690, 770,
+ 870, 950, 1000, 1200, 1400, 1600, 1800, 2000, 2200, 2500
+};
+
static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
struct cdns_dphy_cfg *cfg,
struct phy_configure_opts_mipi_dphy *opts,
@@ -199,6 +224,46 @@ static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
dphy->regs + DPHY_PSM_CFG);
}
+static unsigned long cdns_dphy_j721e_get_wakeup_time_ns(struct cdns_dphy *dphy)
+{
+ /* Minimum wakeup time as per MIPI D-PHY spec v1.2 */
+ return 1000000;
+}
+
+static void cdns_dphy_j721e_set_pll_cfg(struct cdns_dphy *dphy,
+ const struct cdns_dphy_cfg *cfg)
+{
+ u32 status;
+
+ /*
+ * set the PWM and PLL Byteclk divider settings to recommended values
+ * which is same as that of in ref ops
+ */
+ writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
+ DPHY_CMN_PWM_DIV(0x8),
+ dphy->regs + DPHY_CMN_PWM);
+
+ writel((FIELD_PREP(DPHY_TX_J721E_WIZ_IPDIV, cfg->pll_ipdiv) |
+ FIELD_PREP(DPHY_TX_J721E_WIZ_OPDIV, cfg->pll_opdiv) |
+ FIELD_PREP(DPHY_TX_J721E_WIZ_FBDIV, cfg->pll_fbdiv)),
+ dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL);
+
+ writel(DPHY_TX_J721E_WIZ_LANE_RSTB,
+ dphy->regs + DPHY_TX_J721E_WIZ_RST_CTRL);
+
+ readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status,
+ (status & DPHY_TX_WIZ_PLL_LOCK), 0, POLL_TIMEOUT_US);
+
+ readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status,
+ (status & DPHY_TX_WIZ_O_CMN_READY), 0,
+ POLL_TIMEOUT_US);
+}
+
+static void cdns_dphy_j721e_set_psm_div(struct cdns_dphy *dphy, u8 div)
+{
+ writel(div, dphy->regs + DPHY_TX_J721E_WIZ_PSM_FREQ);
+}
+
/*
* This is the reference implementation of DPHY hooks. Specific integration of
* this IP may have to re-implement some of them depending on how they decided
@@ -210,6 +275,12 @@ static const struct cdns_dphy_ops ref_dphy_ops = {
.set_psm_div = cdns_dphy_ref_set_psm_div,
};
+static const struct cdns_dphy_ops j721e_dphy_ops = {
+ .get_wakeup_time_ns = cdns_dphy_j721e_get_wakeup_time_ns,
+ .set_pll_cfg = cdns_dphy_j721e_set_pll_cfg,
+ .set_psm_div = cdns_dphy_j721e_set_psm_div,
+};
+
static int cdns_dphy_config_from_opts(struct phy *phy,
struct phy_configure_opts_mipi_dphy *opts,
struct cdns_dphy_cfg *cfg)
@@ -232,6 +303,24 @@ static int cdns_dphy_config_from_opts(struct phy *phy,
return 0;
}
+static int cdns_dphy_tx_get_band_ctrl(unsigned long hs_clk_rate)
+{
+ unsigned int rate;
+ int i;
+
+ rate = hs_clk_rate / 1000000UL;
+
+ if (rate < tx_bands[0])
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < ARRAY_SIZE(tx_bands) - 1; i++) {
+ if (rate >= tx_bands[i] && rate < tx_bands[i + 1])
+ return i;
+ }
+
+ return -EOPNOTSUPP;
+}
+
static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
union phy_configure_opts *opts)
{
@@ -247,7 +336,8 @@ static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
{
struct cdns_dphy *dphy = phy_get_drvdata(phy);
struct cdns_dphy_cfg cfg = { 0 };
- int ret;
+ int ret, band_ctrl;
+ unsigned int reg;
ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
if (ret)
@@ -276,6 +366,14 @@ static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
*/
cdns_dphy_set_pll_cfg(dphy, &cfg);
+ band_ctrl = cdns_dphy_tx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate);
+ if (band_ctrl < 0)
+ return band_ctrl;
+
+ reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) |
+ FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl);
+ writel(reg, dphy->regs + DPHY_BAND_CFG);
+
return 0;
}
@@ -314,7 +412,6 @@ static int cdns_dphy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct cdns_dphy *dphy;
- struct resource *res;
int ret;
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
@@ -326,8 +423,7 @@ static int cdns_dphy_probe(struct platform_device *pdev)
if (!dphy->ops)
return -EINVAL;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dphy->regs = devm_ioremap_resource(&pdev->dev, res);
+ dphy->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(dphy->regs))
return PTR_ERR(dphy->regs);
@@ -372,6 +468,7 @@ static int cdns_dphy_remove(struct platform_device *pdev)
static const struct of_device_id cdns_dphy_of_match[] = {
{ .compatible = "cdns,dphy", .data = &ref_dphy_ops },
+ { .compatible = "ti,j721e-dphy", .data = &j721e_dphy_ops },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, cdns_dphy_of_match);
diff --git a/drivers/phy/cadence/phy-cadence-dp.c b/drivers/phy/cadence/phy-cadence-dp.c
deleted file mode 100644
index bc10cb264b7a..000000000000
--- a/drivers/phy/cadence/phy-cadence-dp.c
+++ /dev/null
@@ -1,541 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Cadence MHDP DisplayPort SD0801 PHY driver.
- *
- * Copyright 2018 Cadence Design Systems, Inc.
- *
- */
-
-#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_address.h>
-#include <linux/of_device.h>
-#include <linux/phy/phy.h>
-#include <linux/platform_device.h>
-
-#define DEFAULT_NUM_LANES 2
-#define MAX_NUM_LANES 4
-#define DEFAULT_MAX_BIT_RATE 8100 /* in Mbps */
-
-#define POLL_TIMEOUT_US 2000
-#define LANE_MASK 0x7
-
-/*
- * register offsets from DPTX PHY register block base (i.e MHDP
- * register base + 0x30a00)
- */
-#define PHY_AUX_CONFIG 0x00
-#define PHY_AUX_CTRL 0x04
-#define PHY_RESET 0x20
-#define PHY_PMA_XCVR_PLLCLK_EN 0x24
-#define PHY_PMA_XCVR_PLLCLK_EN_ACK 0x28
-#define PHY_PMA_XCVR_POWER_STATE_REQ 0x2c
-#define PHY_POWER_STATE_LN_0 0x0000
-#define PHY_POWER_STATE_LN_1 0x0008
-#define PHY_POWER_STATE_LN_2 0x0010
-#define PHY_POWER_STATE_LN_3 0x0018
-#define PHY_PMA_XCVR_POWER_STATE_ACK 0x30
-#define PHY_PMA_CMN_READY 0x34
-#define PHY_PMA_XCVR_TX_VMARGIN 0x38
-#define PHY_PMA_XCVR_TX_DEEMPH 0x3c
-
-/*
- * register offsets from SD0801 PHY register block base (i.e MHDP
- * register base + 0x500000)
- */
-#define CMN_SSM_BANDGAP_TMR 0x00084
-#define CMN_SSM_BIAS_TMR 0x00088
-#define CMN_PLLSM0_PLLPRE_TMR 0x000a8
-#define CMN_PLLSM0_PLLLOCK_TMR 0x000b0
-#define CMN_PLLSM1_PLLPRE_TMR 0x000c8
-#define CMN_PLLSM1_PLLLOCK_TMR 0x000d0
-#define CMN_BGCAL_INIT_TMR 0x00190
-#define CMN_BGCAL_ITER_TMR 0x00194
-#define CMN_IBCAL_INIT_TMR 0x001d0
-#define CMN_PLL0_VCOCAL_INIT_TMR 0x00210
-#define CMN_PLL0_VCOCAL_ITER_TMR 0x00214
-#define CMN_PLL0_VCOCAL_REFTIM_START 0x00218
-#define CMN_PLL0_VCOCAL_PLLCNT_START 0x00220
-#define CMN_PLL0_INTDIV_M0 0x00240
-#define CMN_PLL0_FRACDIVL_M0 0x00244
-#define CMN_PLL0_FRACDIVH_M0 0x00248
-#define CMN_PLL0_HIGH_THR_M0 0x0024c
-#define CMN_PLL0_DSM_DIAG_M0 0x00250
-#define CMN_PLL0_LOCK_PLLCNT_START 0x00278
-#define CMN_PLL1_VCOCAL_INIT_TMR 0x00310
-#define CMN_PLL1_VCOCAL_ITER_TMR 0x00314
-#define CMN_PLL1_DSM_DIAG_M0 0x00350
-#define CMN_TXPUCAL_INIT_TMR 0x00410
-#define CMN_TXPUCAL_ITER_TMR 0x00414
-#define CMN_TXPDCAL_INIT_TMR 0x00430
-#define CMN_TXPDCAL_ITER_TMR 0x00434
-#define CMN_RXCAL_INIT_TMR 0x00450
-#define CMN_RXCAL_ITER_TMR 0x00454
-#define CMN_SD_CAL_INIT_TMR 0x00490
-#define CMN_SD_CAL_ITER_TMR 0x00494
-#define CMN_SD_CAL_REFTIM_START 0x00498
-#define CMN_SD_CAL_PLLCNT_START 0x004a0
-#define CMN_PDIAG_PLL0_CTRL_M0 0x00680
-#define CMN_PDIAG_PLL0_CLK_SEL_M0 0x00684
-#define CMN_PDIAG_PLL0_CP_PADJ_M0 0x00690
-#define CMN_PDIAG_PLL0_CP_IADJ_M0 0x00694
-#define CMN_PDIAG_PLL0_FILT_PADJ_M0 0x00698
-#define CMN_PDIAG_PLL0_CP_PADJ_M1 0x006d0
-#define CMN_PDIAG_PLL0_CP_IADJ_M1 0x006d4
-#define CMN_PDIAG_PLL1_CLK_SEL_M0 0x00704
-#define XCVR_DIAG_PLLDRC_CTRL 0x10394
-#define XCVR_DIAG_HSCLK_SEL 0x10398
-#define XCVR_DIAG_HSCLK_DIV 0x1039c
-#define TX_PSC_A0 0x10400
-#define TX_PSC_A1 0x10404
-#define TX_PSC_A2 0x10408
-#define TX_PSC_A3 0x1040c
-#define RX_PSC_A0 0x20000
-#define RX_PSC_A1 0x20004
-#define RX_PSC_A2 0x20008
-#define RX_PSC_A3 0x2000c
-#define PHY_PLL_CFG 0x30038
-
-struct cdns_dp_phy {
- void __iomem *base; /* DPTX registers base */
- void __iomem *sd_base; /* SD0801 registers base */
- u32 num_lanes; /* Number of lanes to use */
- u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */
- struct device *dev;
-};
-
-static int cdns_dp_phy_init(struct phy *phy);
-static void cdns_dp_phy_run(struct cdns_dp_phy *cdns_phy);
-static void cdns_dp_phy_wait_pma_cmn_ready(struct cdns_dp_phy *cdns_phy);
-static void cdns_dp_phy_pma_cfg(struct cdns_dp_phy *cdns_phy);
-static void cdns_dp_phy_pma_cmn_cfg_25mhz(struct cdns_dp_phy *cdns_phy);
-static void cdns_dp_phy_pma_lane_cfg(struct cdns_dp_phy *cdns_phy,
- unsigned int lane);
-static void cdns_dp_phy_pma_cmn_vco_cfg_25mhz(struct cdns_dp_phy *cdns_phy);
-static void cdns_dp_phy_pma_cmn_rate(struct cdns_dp_phy *cdns_phy);
-static void cdns_dp_phy_write_field(struct cdns_dp_phy *cdns_phy,
- unsigned int offset,
- unsigned char start_bit,
- unsigned char num_bits,
- unsigned int val);
-
-static const struct phy_ops cdns_dp_phy_ops = {
- .init = cdns_dp_phy_init,
- .owner = THIS_MODULE,
-};
-
-static int cdns_dp_phy_init(struct phy *phy)
-{
- unsigned char lane_bits;
-
- struct cdns_dp_phy *cdns_phy = phy_get_drvdata(phy);
-
- writel(0x0003, cdns_phy->base + PHY_AUX_CTRL); /* enable AUX */
-
- /* PHY PMA registers configuration function */
- cdns_dp_phy_pma_cfg(cdns_phy);
-
- /*
- * Set lines power state to A0
- * Set lines pll clk enable to 0
- */
-
- cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_POWER_STATE_REQ,
- PHY_POWER_STATE_LN_0, 6, 0x0000);
-
- if (cdns_phy->num_lanes >= 2) {
- cdns_dp_phy_write_field(cdns_phy,
- PHY_PMA_XCVR_POWER_STATE_REQ,
- PHY_POWER_STATE_LN_1, 6, 0x0000);
-
- if (cdns_phy->num_lanes == 4) {
- cdns_dp_phy_write_field(cdns_phy,
- PHY_PMA_XCVR_POWER_STATE_REQ,
- PHY_POWER_STATE_LN_2, 6, 0);
- cdns_dp_phy_write_field(cdns_phy,
- PHY_PMA_XCVR_POWER_STATE_REQ,
- PHY_POWER_STATE_LN_3, 6, 0);
- }
- }
-
- cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN,
- 0, 1, 0x0000);
-
- if (cdns_phy->num_lanes >= 2) {
- cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN,
- 1, 1, 0x0000);
- if (cdns_phy->num_lanes == 4) {
- cdns_dp_phy_write_field(cdns_phy,
- PHY_PMA_XCVR_PLLCLK_EN,
- 2, 1, 0x0000);
- cdns_dp_phy_write_field(cdns_phy,
- PHY_PMA_XCVR_PLLCLK_EN,
- 3, 1, 0x0000);
- }
- }
-
- /*
- * release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on
- * used lanes
- */
- lane_bits = (1 << cdns_phy->num_lanes) - 1;
- writel(((0xF & ~lane_bits) << 4) | (0xF & lane_bits),
- cdns_phy->base + PHY_RESET);
-
- /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */
- writel(0x0001, cdns_phy->base + PHY_PMA_XCVR_PLLCLK_EN);
-
- /* PHY PMA registers configuration functions */
- cdns_dp_phy_pma_cmn_vco_cfg_25mhz(cdns_phy);
- cdns_dp_phy_pma_cmn_rate(cdns_phy);
-
- /* take out of reset */
- cdns_dp_phy_write_field(cdns_phy, PHY_RESET, 8, 1, 1);
- cdns_dp_phy_wait_pma_cmn_ready(cdns_phy);
- cdns_dp_phy_run(cdns_phy);
-
- return 0;
-}
-
-static void cdns_dp_phy_wait_pma_cmn_ready(struct cdns_dp_phy *cdns_phy)
-{
- unsigned int reg;
- int ret;
-
- ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_CMN_READY, reg,
- reg & 1, 0, 500);
- if (ret == -ETIMEDOUT)
- dev_err(cdns_phy->dev,
- "timeout waiting for PMA common ready\n");
-}
-
-static void cdns_dp_phy_pma_cfg(struct cdns_dp_phy *cdns_phy)
-{
- unsigned int i;
-
- /* PMA common configuration */
- cdns_dp_phy_pma_cmn_cfg_25mhz(cdns_phy);
-
- /* PMA lane configuration to deal with multi-link operation */
- for (i = 0; i < cdns_phy->num_lanes; i++)
- cdns_dp_phy_pma_lane_cfg(cdns_phy, i);
-}
-
-static void cdns_dp_phy_pma_cmn_cfg_25mhz(struct cdns_dp_phy *cdns_phy)
-{
- /* refclock registers - assumes 25 MHz refclock */
- writel(0x0019, cdns_phy->sd_base + CMN_SSM_BIAS_TMR);
- writel(0x0032, cdns_phy->sd_base + CMN_PLLSM0_PLLPRE_TMR);
- writel(0x00D1, cdns_phy->sd_base + CMN_PLLSM0_PLLLOCK_TMR);
- writel(0x0032, cdns_phy->sd_base + CMN_PLLSM1_PLLPRE_TMR);
- writel(0x00D1, cdns_phy->sd_base + CMN_PLLSM1_PLLLOCK_TMR);
- writel(0x007D, cdns_phy->sd_base + CMN_BGCAL_INIT_TMR);
- writel(0x007D, cdns_phy->sd_base + CMN_BGCAL_ITER_TMR);
- writel(0x0019, cdns_phy->sd_base + CMN_IBCAL_INIT_TMR);
- writel(0x001E, cdns_phy->sd_base + CMN_TXPUCAL_INIT_TMR);
- writel(0x0006, cdns_phy->sd_base + CMN_TXPUCAL_ITER_TMR);
- writel(0x001E, cdns_phy->sd_base + CMN_TXPDCAL_INIT_TMR);
- writel(0x0006, cdns_phy->sd_base + CMN_TXPDCAL_ITER_TMR);
- writel(0x02EE, cdns_phy->sd_base + CMN_RXCAL_INIT_TMR);
- writel(0x0006, cdns_phy->sd_base + CMN_RXCAL_ITER_TMR);
- writel(0x0002, cdns_phy->sd_base + CMN_SD_CAL_INIT_TMR);
- writel(0x0002, cdns_phy->sd_base + CMN_SD_CAL_ITER_TMR);
- writel(0x000E, cdns_phy->sd_base + CMN_SD_CAL_REFTIM_START);
- writel(0x012B, cdns_phy->sd_base + CMN_SD_CAL_PLLCNT_START);
- /* PLL registers */
- writel(0x0409, cdns_phy->sd_base + CMN_PDIAG_PLL0_CP_PADJ_M0);
- writel(0x1001, cdns_phy->sd_base + CMN_PDIAG_PLL0_CP_IADJ_M0);
- writel(0x0F08, cdns_phy->sd_base + CMN_PDIAG_PLL0_FILT_PADJ_M0);
- writel(0x0004, cdns_phy->sd_base + CMN_PLL0_DSM_DIAG_M0);
- writel(0x00FA, cdns_phy->sd_base + CMN_PLL0_VCOCAL_INIT_TMR);
- writel(0x0004, cdns_phy->sd_base + CMN_PLL0_VCOCAL_ITER_TMR);
- writel(0x00FA, cdns_phy->sd_base + CMN_PLL1_VCOCAL_INIT_TMR);
- writel(0x0004, cdns_phy->sd_base + CMN_PLL1_VCOCAL_ITER_TMR);
- writel(0x0318, cdns_phy->sd_base + CMN_PLL0_VCOCAL_REFTIM_START);
-}
-
-static void cdns_dp_phy_pma_cmn_vco_cfg_25mhz(struct cdns_dp_phy *cdns_phy)
-{
- /* Assumes 25 MHz refclock */
- switch (cdns_phy->max_bit_rate) {
- /* Setting VCO for 10.8GHz */
- case 2700:
- case 5400:
- writel(0x01B0, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
- writel(0x0000, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
- writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
- writel(0x0120, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
- break;
- /* Setting VCO for 9.72GHz */
- case 2430:
- case 3240:
- writel(0x0184, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
- writel(0xCCCD, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
- writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
- writel(0x0104, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
- break;
- /* Setting VCO for 8.64GHz */
- case 2160:
- case 4320:
- writel(0x0159, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
- writel(0x999A, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
- writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
- writel(0x00E7, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
- break;
- /* Setting VCO for 8.1GHz */
- case 8100:
- writel(0x0144, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
- writel(0x0000, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
- writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
- writel(0x00D8, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
- break;
- }
-
- writel(0x0002, cdns_phy->sd_base + CMN_PDIAG_PLL0_CTRL_M0);
- writel(0x0318, cdns_phy->sd_base + CMN_PLL0_VCOCAL_PLLCNT_START);
-}
-
-static void cdns_dp_phy_pma_cmn_rate(struct cdns_dp_phy *cdns_phy)
-{
- unsigned int clk_sel_val = 0;
- unsigned int hsclk_div_val = 0;
- unsigned int i;
-
- /* 16'h0000 for single DP link configuration */
- writel(0x0000, cdns_phy->sd_base + PHY_PLL_CFG);
-
- switch (cdns_phy->max_bit_rate) {
- case 1620:
- clk_sel_val = 0x0f01;
- hsclk_div_val = 2;
- break;
- case 2160:
- case 2430:
- case 2700:
- clk_sel_val = 0x0701;
- hsclk_div_val = 1;
- break;
- case 3240:
- clk_sel_val = 0x0b00;
- hsclk_div_val = 2;
- break;
- case 4320:
- case 5400:
- clk_sel_val = 0x0301;
- hsclk_div_val = 0;
- break;
- case 8100:
- clk_sel_val = 0x0200;
- hsclk_div_val = 0;
- break;
- }
-
- writel(clk_sel_val, cdns_phy->sd_base + CMN_PDIAG_PLL0_CLK_SEL_M0);
-
- /* PMA lane configuration to deal with multi-link operation */
- for (i = 0; i < cdns_phy->num_lanes; i++) {
- writel(hsclk_div_val,
- cdns_phy->sd_base + (XCVR_DIAG_HSCLK_DIV | (i<<11)));
- }
-}
-
-static void cdns_dp_phy_pma_lane_cfg(struct cdns_dp_phy *cdns_phy,
- unsigned int lane)
-{
- unsigned int lane_bits = (lane & LANE_MASK) << 11;
-
- /* Writing Tx/Rx Power State Controllers registers */
- writel(0x00FB, cdns_phy->sd_base + (TX_PSC_A0 | lane_bits));
- writel(0x04AA, cdns_phy->sd_base + (TX_PSC_A2 | lane_bits));
- writel(0x04AA, cdns_phy->sd_base + (TX_PSC_A3 | lane_bits));
- writel(0x0000, cdns_phy->sd_base + (RX_PSC_A0 | lane_bits));
- writel(0x0000, cdns_phy->sd_base + (RX_PSC_A2 | lane_bits));
- writel(0x0000, cdns_phy->sd_base + (RX_PSC_A3 | lane_bits));
-
- writel(0x0001, cdns_phy->sd_base + (XCVR_DIAG_PLLDRC_CTRL | lane_bits));
- writel(0x0000, cdns_phy->sd_base + (XCVR_DIAG_HSCLK_SEL | lane_bits));
-}
-
-static void cdns_dp_phy_run(struct cdns_dp_phy *cdns_phy)
-{
- unsigned int read_val;
- u32 write_val1 = 0;
- u32 write_val2 = 0;
- u32 mask = 0;
- int ret;
-
- /*
- * waiting for ACK of pma_xcvr_pllclk_en_ln_*, only for the
- * master lane
- */
- ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_PLLCLK_EN_ACK,
- read_val, read_val & 1, 0, POLL_TIMEOUT_US);
- if (ret == -ETIMEDOUT)
- dev_err(cdns_phy->dev,
- "timeout waiting for link PLL clock enable ack\n");
-
- ndelay(100);
-
- switch (cdns_phy->num_lanes) {
-
- case 1: /* lane 0 */
- write_val1 = 0x00000004;
- write_val2 = 0x00000001;
- mask = 0x0000003f;
- break;
- case 2: /* lane 0-1 */
- write_val1 = 0x00000404;
- write_val2 = 0x00000101;
- mask = 0x00003f3f;
- break;
- case 4: /* lane 0-3 */
- write_val1 = 0x04040404;
- write_val2 = 0x01010101;
- mask = 0x3f3f3f3f;
- break;
- }
-
- writel(write_val1, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
-
- ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_ACK,
- read_val, (read_val & mask) == write_val1, 0,
- POLL_TIMEOUT_US);
- if (ret == -ETIMEDOUT)
- dev_err(cdns_phy->dev,
- "timeout waiting for link power state ack\n");
-
- writel(0, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
- ndelay(100);
-
- writel(write_val2, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
-
- ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_ACK,
- read_val, (read_val & mask) == write_val2, 0,
- POLL_TIMEOUT_US);
- if (ret == -ETIMEDOUT)
- dev_err(cdns_phy->dev,
- "timeout waiting for link power state ack\n");
-
- writel(0, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
- ndelay(100);
-}
-
-static void cdns_dp_phy_write_field(struct cdns_dp_phy *cdns_phy,
- unsigned int offset,
- unsigned char start_bit,
- unsigned char num_bits,
- unsigned int val)
-{
- unsigned int read_val;
-
- read_val = readl(cdns_phy->base + offset);
- writel(((val << start_bit) | (read_val & ~(((1 << num_bits) - 1) <<
- start_bit))), cdns_phy->base + offset);
-}
-
-static int cdns_dp_phy_probe(struct platform_device *pdev)
-{
- struct resource *regs;
- struct cdns_dp_phy *cdns_phy;
- struct device *dev = &pdev->dev;
- struct phy_provider *phy_provider;
- struct phy *phy;
- int err;
-
- cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL);
- if (!cdns_phy)
- return -ENOMEM;
-
- cdns_phy->dev = &pdev->dev;
-
- phy = devm_phy_create(dev, NULL, &cdns_dp_phy_ops);
- if (IS_ERR(phy)) {
- dev_err(dev, "failed to create DisplayPort PHY\n");
- return PTR_ERR(phy);
- }
-
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- cdns_phy->base = devm_ioremap_resource(&pdev->dev, regs);
- if (IS_ERR(cdns_phy->base))
- return PTR_ERR(cdns_phy->base);
-
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- cdns_phy->sd_base = devm_ioremap_resource(&pdev->dev, regs);
- if (IS_ERR(cdns_phy->sd_base))
- return PTR_ERR(cdns_phy->sd_base);
-
- err = device_property_read_u32(dev, "num_lanes",
- &(cdns_phy->num_lanes));
- if (err)
- cdns_phy->num_lanes = DEFAULT_NUM_LANES;
-
- switch (cdns_phy->num_lanes) {
- case 1:
- case 2:
- case 4:
- /* valid number of lanes */
- break;
- default:
- dev_err(dev, "unsupported number of lanes: %d\n",
- cdns_phy->num_lanes);
- return -EINVAL;
- }
-
- err = device_property_read_u32(dev, "max_bit_rate",
- &(cdns_phy->max_bit_rate));
- if (err)
- cdns_phy->max_bit_rate = DEFAULT_MAX_BIT_RATE;
-
- switch (cdns_phy->max_bit_rate) {
- case 2160:
- case 2430:
- case 2700:
- case 3240:
- case 4320:
- case 5400:
- case 8100:
- /* valid bit rate */
- break;
- default:
- dev_err(dev, "unsupported max bit rate: %dMbps\n",
- cdns_phy->max_bit_rate);
- return -EINVAL;
- }
-
- phy_set_drvdata(phy, cdns_phy);
-
- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
-
- dev_info(dev, "%d lanes, max bit rate %d.%03d Gbps\n",
- cdns_phy->num_lanes,
- cdns_phy->max_bit_rate / 1000,
- cdns_phy->max_bit_rate % 1000);
-
- return PTR_ERR_OR_ZERO(phy_provider);
-}
-
-static const struct of_device_id cdns_dp_phy_of_match[] = {
- {
- .compatible = "cdns,dp-phy"
- },
- {}
-};
-MODULE_DEVICE_TABLE(of, cdns_dp_phy_of_match);
-
-static struct platform_driver cdns_dp_phy_driver = {
- .probe = cdns_dp_phy_probe,
- .driver = {
- .name = "cdns-dp-phy",
- .of_match_table = cdns_dp_phy_of_match,
- }
-};
-module_platform_driver(cdns_dp_phy_driver);
-
-MODULE_AUTHOR("Cadence Design Systems, Inc.");
-MODULE_DESCRIPTION("Cadence MHDP PHY driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/cadence/phy-cadence-salvo.c b/drivers/phy/cadence/phy-cadence-salvo.c
new file mode 100644
index 000000000000..e569f5f67578
--- /dev/null
+++ b/drivers/phy/cadence/phy-cadence-salvo.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Salvo PHY is a 28nm PHY, it is a legacy PHY, and only
+ * for USB3 and USB2.
+ *
+ * Copyright (c) 2019-2020 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+/* PHY register definition */
+#define PHY_PMA_CMN_CTRL1 0xC800
+#define TB_ADDR_CMN_DIAG_HSCLK_SEL 0x01e0
+#define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR 0x0084
+#define TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR 0x0085
+#define TB_ADDR_CMN_PLL0_INTDIV 0x0094
+#define TB_ADDR_CMN_PLL0_FRACDIV 0x0095
+#define TB_ADDR_CMN_PLL0_HIGH_THR 0x0096
+#define TB_ADDR_CMN_PLL0_SS_CTRL1 0x0098
+#define TB_ADDR_CMN_PLL0_SS_CTRL2 0x0099
+#define TB_ADDR_CMN_PLL0_DSM_DIAG 0x0097
+#define TB_ADDR_CMN_DIAG_PLL0_OVRD 0x01c2
+#define TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD 0x01c0
+#define TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD 0x01c1
+#define TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE 0x01C5
+#define TB_ADDR_CMN_DIAG_PLL0_CP_TUNE 0x01C6
+#define TB_ADDR_CMN_DIAG_PLL0_LF_PROG 0x01C7
+#define TB_ADDR_CMN_DIAG_PLL0_TEST_MODE 0x01c4
+#define TB_ADDR_CMN_PSM_CLK_CTRL 0x0061
+#define TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR 0x40ea
+#define TB_ADDR_XCVR_PSM_RCTRL 0x4001
+#define TB_ADDR_TX_PSC_A0 0x4100
+#define TB_ADDR_TX_PSC_A1 0x4101
+#define TB_ADDR_TX_PSC_A2 0x4102
+#define TB_ADDR_TX_PSC_A3 0x4103
+#define TB_ADDR_TX_DIAG_ECTRL_OVRD 0x41f5
+#define TB_ADDR_TX_PSC_CAL 0x4106
+#define TB_ADDR_TX_PSC_RDY 0x4107
+#define TB_ADDR_RX_PSC_A0 0x8000
+#define TB_ADDR_RX_PSC_A1 0x8001
+#define TB_ADDR_RX_PSC_A2 0x8002
+#define TB_ADDR_RX_PSC_A3 0x8003
+#define TB_ADDR_RX_PSC_CAL 0x8006
+#define TB_ADDR_RX_PSC_RDY 0x8007
+#define TB_ADDR_TX_TXCC_MGNLS_MULT_000 0x4058
+#define TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY 0x41e7
+#define TB_ADDR_RX_SLC_CU_ITER_TMR 0x80e3
+#define TB_ADDR_RX_SIGDET_HL_FILT_TMR 0x8090
+#define TB_ADDR_RX_SAMP_DAC_CTRL 0x8058
+#define TB_ADDR_RX_DIAG_SIGDET_TUNE 0x81dc
+#define TB_ADDR_RX_DIAG_LFPSDET_TUNE2 0x81df
+#define TB_ADDR_RX_DIAG_BS_TM 0x81f5
+#define TB_ADDR_RX_DIAG_DFE_CTRL1 0x81d3
+#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM4 0x81c7
+#define TB_ADDR_RX_DIAG_ILL_E_TRIM0 0x81c2
+#define TB_ADDR_RX_DIAG_ILL_IQ_TRIM0 0x81c1
+#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM6 0x81c9
+#define TB_ADDR_RX_DIAG_RXFE_TM3 0x81f8
+#define TB_ADDR_RX_DIAG_RXFE_TM4 0x81f9
+#define TB_ADDR_RX_DIAG_LFPSDET_TUNE 0x81dd
+#define TB_ADDR_RX_DIAG_DFE_CTRL3 0x81d5
+#define TB_ADDR_RX_DIAG_SC2C_DELAY 0x81e1
+#define TB_ADDR_RX_REE_VGA_GAIN_NODFE 0x81bf
+#define TB_ADDR_XCVR_PSM_CAL_TMR 0x4002
+#define TB_ADDR_XCVR_PSM_A0BYP_TMR 0x4004
+#define TB_ADDR_XCVR_PSM_A0IN_TMR 0x4003
+#define TB_ADDR_XCVR_PSM_A1IN_TMR 0x4005
+#define TB_ADDR_XCVR_PSM_A2IN_TMR 0x4006
+#define TB_ADDR_XCVR_PSM_A3IN_TMR 0x4007
+#define TB_ADDR_XCVR_PSM_A4IN_TMR 0x4008
+#define TB_ADDR_XCVR_PSM_A5IN_TMR 0x4009
+#define TB_ADDR_XCVR_PSM_A0OUT_TMR 0x400a
+#define TB_ADDR_XCVR_PSM_A1OUT_TMR 0x400b
+#define TB_ADDR_XCVR_PSM_A2OUT_TMR 0x400c
+#define TB_ADDR_XCVR_PSM_A3OUT_TMR 0x400d
+#define TB_ADDR_XCVR_PSM_A4OUT_TMR 0x400e
+#define TB_ADDR_XCVR_PSM_A5OUT_TMR 0x400f
+#define TB_ADDR_TX_RCVDET_EN_TMR 0x4122
+#define TB_ADDR_TX_RCVDET_ST_TMR 0x4123
+#define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40f2
+#define TB_ADDR_TX_RCVDETSC_CTRL 0x4124
+
+/* TB_ADDR_TX_RCVDETSC_CTRL */
+#define RXDET_IN_P3_32KHZ BIT(0)
+
+struct cdns_reg_pairs {
+ u16 val;
+ u32 off;
+};
+
+struct cdns_salvo_data {
+ u8 reg_offset_shift;
+ const struct cdns_reg_pairs *init_sequence_val;
+ u8 init_sequence_length;
+};
+
+struct cdns_salvo_phy {
+ struct phy *phy;
+ struct clk *clk;
+ void __iomem *base;
+ struct cdns_salvo_data *data;
+};
+
+static const struct of_device_id cdns_salvo_phy_of_match[];
+static u16 cdns_salvo_read(struct cdns_salvo_phy *salvo_phy, u32 reg)
+{
+ return (u16)readl(salvo_phy->base +
+ reg * (1 << salvo_phy->data->reg_offset_shift));
+}
+
+static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy,
+ u32 reg, u16 val)
+{
+ writel(val, salvo_phy->base +
+ reg * (1 << salvo_phy->data->reg_offset_shift));
+}
+
+/*
+ * Below bringup sequence pair are from Cadence PHY's User Guide
+ * and NXP platform tuning results.
+ */
+static const struct cdns_reg_pairs cdns_nxp_sequence_pair[] = {
+ {0x0830, PHY_PMA_CMN_CTRL1},
+ {0x0010, TB_ADDR_CMN_DIAG_HSCLK_SEL},
+ {0x00f0, TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR},
+ {0x0018, TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR},
+ {0x00d0, TB_ADDR_CMN_PLL0_INTDIV},
+ {0x4aaa, TB_ADDR_CMN_PLL0_FRACDIV},
+ {0x0034, TB_ADDR_CMN_PLL0_HIGH_THR},
+ {0x01ee, TB_ADDR_CMN_PLL0_SS_CTRL1},
+ {0x7f03, TB_ADDR_CMN_PLL0_SS_CTRL2},
+ {0x0020, TB_ADDR_CMN_PLL0_DSM_DIAG},
+ {0x0000, TB_ADDR_CMN_DIAG_PLL0_OVRD},
+ {0x0000, TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD},
+ {0x0000, TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD},
+ {0x0007, TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE},
+ {0x0027, TB_ADDR_CMN_DIAG_PLL0_CP_TUNE},
+ {0x0008, TB_ADDR_CMN_DIAG_PLL0_LF_PROG},
+ {0x0022, TB_ADDR_CMN_DIAG_PLL0_TEST_MODE},
+ {0x000a, TB_ADDR_CMN_PSM_CLK_CTRL},
+ {0x0139, TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR},
+ {0xbefc, TB_ADDR_XCVR_PSM_RCTRL},
+
+ {0x7799, TB_ADDR_TX_PSC_A0},
+ {0x7798, TB_ADDR_TX_PSC_A1},
+ {0x509b, TB_ADDR_TX_PSC_A2},
+ {0x0003, TB_ADDR_TX_DIAG_ECTRL_OVRD},
+ {0x509b, TB_ADDR_TX_PSC_A3},
+ {0x2090, TB_ADDR_TX_PSC_CAL},
+ {0x2090, TB_ADDR_TX_PSC_RDY},
+
+ {0xA6FD, TB_ADDR_RX_PSC_A0},
+ {0xA6FD, TB_ADDR_RX_PSC_A1},
+ {0xA410, TB_ADDR_RX_PSC_A2},
+ {0x2410, TB_ADDR_RX_PSC_A3},
+
+ {0x23FF, TB_ADDR_RX_PSC_CAL},
+ {0x2010, TB_ADDR_RX_PSC_RDY},
+
+ {0x0020, TB_ADDR_TX_TXCC_MGNLS_MULT_000},
+ {0x00ff, TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY},
+ {0x0002, TB_ADDR_RX_SLC_CU_ITER_TMR},
+ {0x0013, TB_ADDR_RX_SIGDET_HL_FILT_TMR},
+ {0x0000, TB_ADDR_RX_SAMP_DAC_CTRL},
+ {0x1004, TB_ADDR_RX_DIAG_SIGDET_TUNE},
+ {0x4041, TB_ADDR_RX_DIAG_LFPSDET_TUNE2},
+ {0x0480, TB_ADDR_RX_DIAG_BS_TM},
+ {0x8006, TB_ADDR_RX_DIAG_DFE_CTRL1},
+ {0x003f, TB_ADDR_RX_DIAG_ILL_IQE_TRIM4},
+ {0x543f, TB_ADDR_RX_DIAG_ILL_E_TRIM0},
+ {0x543f, TB_ADDR_RX_DIAG_ILL_IQ_TRIM0},
+ {0x0000, TB_ADDR_RX_DIAG_ILL_IQE_TRIM6},
+ {0x8000, TB_ADDR_RX_DIAG_RXFE_TM3},
+ {0x0003, TB_ADDR_RX_DIAG_RXFE_TM4},
+ {0x2408, TB_ADDR_RX_DIAG_LFPSDET_TUNE},
+ {0x05ca, TB_ADDR_RX_DIAG_DFE_CTRL3},
+ {0x0258, TB_ADDR_RX_DIAG_SC2C_DELAY},
+ {0x1fff, TB_ADDR_RX_REE_VGA_GAIN_NODFE},
+
+ {0x02c6, TB_ADDR_XCVR_PSM_CAL_TMR},
+ {0x0002, TB_ADDR_XCVR_PSM_A0BYP_TMR},
+ {0x02c6, TB_ADDR_XCVR_PSM_A0IN_TMR},
+ {0x0010, TB_ADDR_XCVR_PSM_A1IN_TMR},
+ {0x0010, TB_ADDR_XCVR_PSM_A2IN_TMR},
+ {0x0010, TB_ADDR_XCVR_PSM_A3IN_TMR},
+ {0x0010, TB_ADDR_XCVR_PSM_A4IN_TMR},
+ {0x0010, TB_ADDR_XCVR_PSM_A5IN_TMR},
+
+ {0x0002, TB_ADDR_XCVR_PSM_A0OUT_TMR},
+ {0x0002, TB_ADDR_XCVR_PSM_A1OUT_TMR},
+ {0x0002, TB_ADDR_XCVR_PSM_A2OUT_TMR},
+ {0x0002, TB_ADDR_XCVR_PSM_A3OUT_TMR},
+ {0x0002, TB_ADDR_XCVR_PSM_A4OUT_TMR},
+ {0x0002, TB_ADDR_XCVR_PSM_A5OUT_TMR},
+ /* Change rx detect parameter */
+ {0x0960, TB_ADDR_TX_RCVDET_EN_TMR},
+ {0x01e0, TB_ADDR_TX_RCVDET_ST_TMR},
+ {0x0090, TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR},
+};
+
+static int cdns_salvo_phy_init(struct phy *phy)
+{
+ struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);
+ struct cdns_salvo_data *data = salvo_phy->data;
+ int ret, i;
+ u16 value;
+
+ ret = clk_prepare_enable(salvo_phy->clk);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < data->init_sequence_length; i++) {
+ const struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i;
+
+ cdns_salvo_write(salvo_phy, reg_pair->off, reg_pair->val);
+ }
+
+ /* RXDET_IN_P3_32KHZ, Receiver detect slow clock enable */
+ value = cdns_salvo_read(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL);
+ value |= RXDET_IN_P3_32KHZ;
+ cdns_salvo_write(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL,
+ RXDET_IN_P3_32KHZ);
+
+ udelay(10);
+
+ clk_disable_unprepare(salvo_phy->clk);
+
+ return ret;
+}
+
+static int cdns_salvo_phy_power_on(struct phy *phy)
+{
+ struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);
+
+ return clk_prepare_enable(salvo_phy->clk);
+}
+
+static int cdns_salvo_phy_power_off(struct phy *phy)
+{
+ struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);
+
+ clk_disable_unprepare(salvo_phy->clk);
+
+ return 0;
+}
+
+static const struct phy_ops cdns_salvo_phy_ops = {
+ .init = cdns_salvo_phy_init,
+ .power_on = cdns_salvo_phy_power_on,
+ .power_off = cdns_salvo_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int cdns_salvo_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct cdns_salvo_phy *salvo_phy;
+ struct cdns_salvo_data *data;
+
+ data = (struct cdns_salvo_data *)of_device_get_match_data(dev);
+ salvo_phy = devm_kzalloc(dev, sizeof(*salvo_phy), GFP_KERNEL);
+ if (!salvo_phy)
+ return -ENOMEM;
+
+ salvo_phy->data = data;
+ salvo_phy->clk = devm_clk_get_optional(dev, "salvo_phy_clk");
+ if (IS_ERR(salvo_phy->clk))
+ return PTR_ERR(salvo_phy->clk);
+
+ salvo_phy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(salvo_phy->base))
+ return PTR_ERR(salvo_phy->base);
+
+ salvo_phy->phy = devm_phy_create(dev, NULL, &cdns_salvo_phy_ops);
+ if (IS_ERR(salvo_phy->phy))
+ return PTR_ERR(salvo_phy->phy);
+
+ phy_set_drvdata(salvo_phy->phy, salvo_phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct cdns_salvo_data cdns_nxp_salvo_data = {
+ 2,
+ cdns_nxp_sequence_pair,
+ ARRAY_SIZE(cdns_nxp_sequence_pair),
+};
+
+static const struct of_device_id cdns_salvo_phy_of_match[] = {
+ {
+ .compatible = "nxp,salvo-phy",
+ .data = &cdns_nxp_salvo_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cdns_salvo_phy_of_match);
+
+static struct platform_driver cdns_salvo_phy_driver = {
+ .probe = cdns_salvo_phy_probe,
+ .driver = {
+ .name = "cdns-salvo-phy",
+ .of_match_table = cdns_salvo_phy_of_match,
+ }
+};
+module_platform_driver(cdns_salvo_phy_driver);
+
+MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence SALVO PHY Driver");
diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c
index a5c08e5bd2bf..6e86a6517f37 100644
--- a/drivers/phy/cadence/phy-cadence-sierra.c
+++ b/drivers/phy/cadence/phy-cadence-sierra.c
@@ -7,6 +7,7 @@
*
*/
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
@@ -20,17 +21,34 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <dt-bindings/phy/phy.h>
+#include <dt-bindings/phy/phy-cadence.h>
+
+#define NUM_SSC_MODE 3
+#define NUM_PHY_TYPE 4
/* PHY register offsets */
#define SIERRA_COMMON_CDB_OFFSET 0x0
#define SIERRA_MACRO_ID_REG 0x0
+#define SIERRA_CMN_PLLLC_GEN_PREG 0x42
#define SIERRA_CMN_PLLLC_MODE_PREG 0x48
#define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG 0x49
#define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG 0x4A
#define SIERRA_CMN_PLLLC_LOCK_CNTSTART_PREG 0x4B
+#define SIERRA_CMN_PLLLC_CLK1_PREG 0x4D
#define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F
#define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50
+#define SIERRA_CMN_PLLLC_DSMCORR_PREG 0x51
+#define SIERRA_CMN_PLLLC_SS_PREG 0x52
+#define SIERRA_CMN_PLLLC_SS_AMP_STEP_SIZE_PREG 0x53
+#define SIERRA_CMN_PLLLC_SSTWOPT_PREG 0x54
#define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG 0x62
+#define SIERRA_CMN_PLLLC_LOCK_DELAY_CTRL_PREG 0x63
+#define SIERRA_CMN_REFRCV_PREG 0x98
+#define SIERRA_CMN_REFRCV1_PREG 0xB8
+#define SIERRA_CMN_PLLLC1_GEN_PREG 0xC2
+#define SIERRA_CMN_PLLLC1_LF_COEFF_MODE0_PREG 0xCA
+#define SIERRA_CMN_PLLLC1_BWCAL_MODE0_PREG 0xD0
+#define SIERRA_CMN_PLLLC1_SS_TIME_STEPSIZE_MODE_PREG 0xE2
#define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
((0x4000 << (block_offset)) + \
@@ -43,7 +61,11 @@
#define SIERRA_DET_STANDEC_E_PREG 0x004
#define SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG 0x008
#define SIERRA_PSM_A0IN_TMR_PREG 0x009
+#define SIERRA_PSM_A3IN_TMR_PREG 0x00C
#define SIERRA_PSM_DIAG_PREG 0x015
+#define SIERRA_PSC_LN_A3_PREG 0x023
+#define SIERRA_PSC_LN_A4_PREG 0x024
+#define SIERRA_PSC_LN_IDLE_PREG 0x026
#define SIERRA_PSC_TX_A0_PREG 0x028
#define SIERRA_PSC_TX_A1_PREG 0x029
#define SIERRA_PSC_TX_A2_PREG 0x02A
@@ -53,18 +75,23 @@
#define SIERRA_PSC_RX_A2_PREG 0x032
#define SIERRA_PSC_RX_A3_PREG 0x033
#define SIERRA_PLLCTRL_SUBRATE_PREG 0x03A
+#define SIERRA_PLLCTRL_GEN_A_PREG 0x03B
#define SIERRA_PLLCTRL_GEN_D_PREG 0x03E
#define SIERRA_PLLCTRL_CPGAIN_MODE_PREG 0x03F
#define SIERRA_PLLCTRL_STATUS_PREG 0x044
#define SIERRA_CLKPATH_BIASTRIM_PREG 0x04B
#define SIERRA_DFE_BIASTRIM_PREG 0x04C
#define SIERRA_DRVCTRL_ATTEN_PREG 0x06A
+#define SIERRA_DRVCTRL_BOOST_PREG 0x06F
+#define SIERRA_TX_RCVDET_OVRD_PREG 0x072
#define SIERRA_CLKPATHCTRL_TMR_PREG 0x081
#define SIERRA_RX_CREQ_FLTR_A_MODE3_PREG 0x085
#define SIERRA_RX_CREQ_FLTR_A_MODE2_PREG 0x086
#define SIERRA_RX_CREQ_FLTR_A_MODE1_PREG 0x087
#define SIERRA_RX_CREQ_FLTR_A_MODE0_PREG 0x088
+#define SIERRA_CREQ_DCBIASATTEN_OVR_PREG 0x08C
#define SIERRA_CREQ_CCLKDET_MODE01_PREG 0x08E
+#define SIERRA_RX_CTLE_CAL_PREG 0x08F
#define SIERRA_RX_CTLE_MAINTENANCE_PREG 0x091
#define SIERRA_CREQ_FSMCLK_SEL_PREG 0x092
#define SIERRA_CREQ_EQ_CTRL_PREG 0x093
@@ -114,15 +141,28 @@
#define SIERRA_DEQ_ALUT12 0x114
#define SIERRA_DEQ_ALUT13 0x115
#define SIERRA_DEQ_DFETAP_CTRL_PREG 0x128
+#define SIERRA_DEQ_DFETAP0 0x129
+#define SIERRA_DEQ_DFETAP1 0x12B
+#define SIERRA_DEQ_DFETAP2 0x12D
+#define SIERRA_DEQ_DFETAP3 0x12F
+#define SIERRA_DEQ_DFETAP4 0x131
#define SIERRA_DFE_EN_1010_IGNORE_PREG 0x134
+#define SIERRA_DEQ_PRECUR_PREG 0x138
+#define SIERRA_DEQ_POSTCUR_PREG 0x140
+#define SIERRA_DEQ_POSTCUR_DECR_PREG 0x142
#define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150
#define SIERRA_DEQ_TAU_CTRL2_PREG 0x151
+#define SIERRA_DEQ_TAU_CTRL3_PREG 0x152
+#define SIERRA_DEQ_OPENEYE_CTRL_PREG 0x158
#define SIERRA_DEQ_PICTRL_PREG 0x161
#define SIERRA_CPICAL_TMRVAL_MODE1_PREG 0x170
#define SIERRA_CPICAL_TMRVAL_MODE0_PREG 0x171
#define SIERRA_CPICAL_PICNT_MODE1_PREG 0x174
#define SIERRA_CPI_OUTBUF_RATESEL_PREG 0x17C
+#define SIERRA_CPI_RESBIAS_BIN_PREG 0x17E
+#define SIERRA_CPI_TRIM_PREG 0x17F
#define SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG 0x183
+#define SIERRA_EPI_CTRL_PREG 0x187
#define SIERRA_LFPSDET_SUPPORT_PREG 0x188
#define SIERRA_LFPSFILT_NS_PREG 0x18A
#define SIERRA_LFPSFILT_RD_PREG 0x18B
@@ -136,27 +176,144 @@
#define SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG 0x14F
#define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150
-#define SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset) \
- (0xc000 << (block_offset))
+/* PHY PCS common registers */
+#define SIERRA_PHY_PCS_COMMON_OFFSET(block_offset) \
+ (0xc000 << (block_offset))
+#define SIERRA_PHY_PIPE_CMN_CTRL1 0x0
#define SIERRA_PHY_PLL_CFG 0xe
+/* PHY PCS lane registers */
+#define SIERRA_PHY_PCS_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
+ ((0xD000 << (block_offset)) + \
+ (((ln) << 8) << (reg_offset)))
+
+#define SIERRA_PHY_ISO_LINK_CTRL 0xB
+
+/* PHY PMA common registers */
+#define SIERRA_PHY_PMA_COMMON_OFFSET(block_offset) \
+ (0xE000 << (block_offset))
+#define SIERRA_PHY_PMA_CMN_CTRL 0x000
+
+/* PHY PMA lane registers */
+#define SIERRA_PHY_PMA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
+ ((0xF000 << (block_offset)) + \
+ (((ln) << 8) << (reg_offset)))
+
+#define SIERRA_PHY_PMA_XCVR_CTRL 0x000
+
#define SIERRA_MACRO_ID 0x00007364
#define SIERRA_MAX_LANES 16
#define PLL_LOCK_TIME 100000
+#define CDNS_SIERRA_OUTPUT_CLOCKS 3
+#define CDNS_SIERRA_INPUT_CLOCKS 5
+enum cdns_sierra_clock_input {
+ PHY_CLK,
+ CMN_REFCLK_DIG_DIV,
+ CMN_REFCLK1_DIG_DIV,
+ PLL0_REFCLK,
+ PLL1_REFCLK,
+};
+
+#define SIERRA_NUM_CMN_PLLC 2
+#define SIERRA_NUM_CMN_PLLC_PARENTS 2
+
static const struct reg_field macro_id_type =
REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15);
static const struct reg_field phy_pll_cfg_1 =
REG_FIELD(SIERRA_PHY_PLL_CFG, 1, 1);
+static const struct reg_field pma_cmn_ready =
+ REG_FIELD(SIERRA_PHY_PMA_CMN_CTRL, 0, 0);
static const struct reg_field pllctrl_lock =
REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0, 0);
+static const struct reg_field phy_iso_link_ctrl_1 =
+ REG_FIELD(SIERRA_PHY_ISO_LINK_CTRL, 1, 1);
+static const struct reg_field cmn_plllc_clk1outdiv_preg =
+ REG_FIELD(SIERRA_CMN_PLLLC_CLK1_PREG, 0, 6);
+static const struct reg_field cmn_plllc_clk1_en_preg =
+ REG_FIELD(SIERRA_CMN_PLLLC_CLK1_PREG, 12, 12);
+
+static const char * const clk_names[] = {
+ [CDNS_SIERRA_PLL_CMNLC] = "pll_cmnlc",
+ [CDNS_SIERRA_PLL_CMNLC1] = "pll_cmnlc1",
+ [CDNS_SIERRA_DERIVED_REFCLK] = "refclk_der",
+};
+
+enum cdns_sierra_cmn_plllc {
+ CMN_PLLLC,
+ CMN_PLLLC1,
+};
+
+struct cdns_sierra_pll_mux_reg_fields {
+ struct reg_field pfdclk_sel_preg;
+ struct reg_field plllc1en_field;
+ struct reg_field termen_field;
+};
+
+static const struct cdns_sierra_pll_mux_reg_fields cmn_plllc_pfdclk1_sel_preg[] = {
+ [CMN_PLLLC] = {
+ .pfdclk_sel_preg = REG_FIELD(SIERRA_CMN_PLLLC_GEN_PREG, 1, 1),
+ .plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 8, 8),
+ .termen_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 0, 0),
+ },
+ [CMN_PLLLC1] = {
+ .pfdclk_sel_preg = REG_FIELD(SIERRA_CMN_PLLLC1_GEN_PREG, 1, 1),
+ .plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 8, 8),
+ .termen_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 0, 0),
+ },
+};
+
+struct cdns_sierra_pll_mux {
+ struct clk_hw hw;
+ struct regmap_field *pfdclk_sel_preg;
+ struct regmap_field *plllc1en_field;
+ struct regmap_field *termen_field;
+ struct clk_init_data clk_data;
+};
+
+#define to_cdns_sierra_pll_mux(_hw) \
+ container_of(_hw, struct cdns_sierra_pll_mux, hw)
+
+static const int pll_mux_parent_index[][SIERRA_NUM_CMN_PLLC_PARENTS] = {
+ [CMN_PLLLC] = { PLL0_REFCLK, PLL1_REFCLK },
+ [CMN_PLLLC1] = { PLL1_REFCLK, PLL0_REFCLK },
+};
+
+static u32 cdns_sierra_pll_mux_table[][SIERRA_NUM_CMN_PLLC_PARENTS] = {
+ [CMN_PLLLC] = { 0, 1 },
+ [CMN_PLLLC1] = { 1, 0 },
+};
+
+struct cdns_sierra_derived_refclk {
+ struct clk_hw hw;
+ struct regmap_field *cmn_plllc_clk1outdiv_preg;
+ struct regmap_field *cmn_plllc_clk1_en_preg;
+ struct clk_init_data clk_data;
+};
+
+#define to_cdns_sierra_derived_refclk(_hw) \
+ container_of(_hw, struct cdns_sierra_derived_refclk, hw)
+
+enum cdns_sierra_phy_type {
+ TYPE_NONE,
+ TYPE_PCIE,
+ TYPE_USB,
+ TYPE_QSGMII
+};
+
+enum cdns_sierra_ssc_mode {
+ NO_SSC,
+ EXTERNAL_SSC,
+ INTERNAL_SSC
+};
struct cdns_sierra_inst {
struct phy *phy;
- u32 phy_type;
+ enum cdns_sierra_phy_type phy_type;
u32 num_lanes;
u32 mlane;
struct reset_control *lnk_rst;
+ enum cdns_sierra_ssc_mode ssc_mode;
};
struct cdns_reg_pairs {
@@ -164,18 +321,23 @@ struct cdns_reg_pairs {
u32 off;
};
+struct cdns_sierra_vals {
+ const struct cdns_reg_pairs *reg_pairs;
+ u32 num_regs;
+};
+
struct cdns_sierra_data {
- u32 id_value;
- u8 block_offset_shift;
- u8 reg_offset_shift;
- u32 pcie_cmn_regs;
- u32 pcie_ln_regs;
- u32 usb_cmn_regs;
- u32 usb_ln_regs;
- struct cdns_reg_pairs *pcie_cmn_vals;
- struct cdns_reg_pairs *pcie_ln_vals;
- struct cdns_reg_pairs *usb_cmn_vals;
- struct cdns_reg_pairs *usb_ln_vals;
+ u32 id_value;
+ u8 block_offset_shift;
+ u8 reg_offset_shift;
+ struct cdns_sierra_vals *pcs_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
+ [NUM_SSC_MODE];
+ struct cdns_sierra_vals *phy_pma_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
+ [NUM_SSC_MODE];
+ struct cdns_sierra_vals *pma_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
+ [NUM_SSC_MODE];
+ struct cdns_sierra_vals *pma_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
+ [NUM_SSC_MODE];
};
struct cdns_regmap_cdb_context {
@@ -186,23 +348,31 @@ struct cdns_regmap_cdb_context {
struct cdns_sierra_phy {
struct device *dev;
- struct regmap *regmap;
- struct cdns_sierra_data *init_data;
+ const struct cdns_sierra_data *init_data;
struct cdns_sierra_inst phys[SIERRA_MAX_LANES];
struct reset_control *phy_rst;
struct reset_control *apb_rst;
struct regmap *regmap_lane_cdb[SIERRA_MAX_LANES];
- struct regmap *regmap_phy_config_ctrl;
+ struct regmap *regmap_phy_pcs_common_cdb;
+ struct regmap *regmap_phy_pcs_lane_cdb[SIERRA_MAX_LANES];
+ struct regmap *regmap_phy_pma_common_cdb;
+ struct regmap *regmap_phy_pma_lane_cdb[SIERRA_MAX_LANES];
struct regmap *regmap_common_cdb;
struct regmap_field *macro_id_type;
struct regmap_field *phy_pll_cfg_1;
+ struct regmap_field *pma_cmn_ready;
struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES];
- struct clk *clk;
- struct clk *cmn_refclk_dig_div;
- struct clk *cmn_refclk1_dig_div;
+ struct regmap_field *phy_iso_link_ctrl_1[SIERRA_MAX_LANES];
+ struct regmap_field *cmn_refrcv_refclk_plllc1en_preg[SIERRA_NUM_CMN_PLLC];
+ struct regmap_field *cmn_refrcv_refclk_termen_preg[SIERRA_NUM_CMN_PLLC];
+ struct regmap_field *cmn_plllc_pfdclk1_sel_preg[SIERRA_NUM_CMN_PLLC];
+ struct clk *input_clks[CDNS_SIERRA_INPUT_CLOCKS];
int nsubnodes;
u32 num_lanes;
bool autoconf;
+ int already_configured;
+ struct clk_onecell_data clk_data;
+ struct clk *output_clks[CDNS_SIERRA_OUTPUT_CLOCKS];
};
static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val)
@@ -233,7 +403,7 @@ static int cdns_regmap_read(void *context, unsigned int reg, unsigned int *val)
.reg_read = cdns_regmap_read, \
}
-static struct regmap_config cdns_sierra_lane_cdb_config[] = {
+static const struct regmap_config cdns_sierra_lane_cdb_config[] = {
SIERRA_LANE_CDB_REGMAP_CONF("0"),
SIERRA_LANE_CDB_REGMAP_CONF("1"),
SIERRA_LANE_CDB_REGMAP_CONF("2"),
@@ -252,7 +422,7 @@ static struct regmap_config cdns_sierra_lane_cdb_config[] = {
SIERRA_LANE_CDB_REGMAP_CONF("15"),
};
-static struct regmap_config cdns_sierra_common_cdb_config = {
+static const struct regmap_config cdns_sierra_common_cdb_config = {
.name = "sierra_common_cdb",
.reg_stride = 1,
.fast_io = true,
@@ -260,51 +430,141 @@ static struct regmap_config cdns_sierra_common_cdb_config = {
.reg_read = cdns_regmap_read,
};
-static struct regmap_config cdns_sierra_phy_config_ctrl_config = {
- .name = "sierra_phy_config_ctrl",
+static const struct regmap_config cdns_sierra_phy_pcs_cmn_cdb_config = {
+ .name = "sierra_phy_pcs_cmn_cdb",
.reg_stride = 1,
.fast_io = true,
.reg_write = cdns_regmap_write,
.reg_read = cdns_regmap_read,
};
+#define SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF(n) \
+{ \
+ .name = "sierra_phy_pcs_lane" n "_cdb", \
+ .reg_stride = 1, \
+ .fast_io = true, \
+ .reg_write = cdns_regmap_write, \
+ .reg_read = cdns_regmap_read, \
+}
+
+static const struct regmap_config cdns_sierra_phy_pcs_lane_cdb_config[] = {
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("0"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("1"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("2"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("3"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("4"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("5"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("6"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("7"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("8"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("9"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("10"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("11"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("12"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("13"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("14"),
+ SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("15"),
+};
+
+static const struct regmap_config cdns_sierra_phy_pma_cmn_cdb_config = {
+ .name = "sierra_phy_pma_cmn_cdb",
+ .reg_stride = 1,
+ .fast_io = true,
+ .reg_write = cdns_regmap_write,
+ .reg_read = cdns_regmap_read,
+};
+
+#define SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF(n) \
+{ \
+ .name = "sierra_phy_pma_lane" n "_cdb", \
+ .reg_stride = 1, \
+ .fast_io = true, \
+ .reg_write = cdns_regmap_write, \
+ .reg_read = cdns_regmap_read, \
+}
+
+static const struct regmap_config cdns_sierra_phy_pma_lane_cdb_config[] = {
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("0"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("1"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("2"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("3"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("4"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("5"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("6"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("7"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("8"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("9"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("10"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("11"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("12"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("13"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("14"),
+ SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("15"),
+};
+
static int cdns_sierra_phy_init(struct phy *gphy)
{
struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent);
+ const struct cdns_sierra_data *init_data = phy->init_data;
+ struct cdns_sierra_vals *pma_cmn_vals, *pma_ln_vals;
+ enum cdns_sierra_phy_type phy_type = ins->phy_type;
+ enum cdns_sierra_ssc_mode ssc = ins->ssc_mode;
+ struct cdns_sierra_vals *phy_pma_ln_vals;
+ const struct cdns_reg_pairs *reg_pairs;
+ struct cdns_sierra_vals *pcs_cmn_vals;
struct regmap *regmap;
+ u32 num_regs;
int i, j;
- struct cdns_reg_pairs *cmn_vals, *ln_vals;
- u32 num_cmn_regs, num_ln_regs;
/* Initialise the PHY registers, unless auto configured */
- if (phy->autoconf)
+ if (phy->autoconf || phy->already_configured || phy->nsubnodes > 1)
return 0;
- clk_set_rate(phy->cmn_refclk_dig_div, 25000000);
- clk_set_rate(phy->cmn_refclk1_dig_div, 25000000);
- if (ins->phy_type == PHY_TYPE_PCIE) {
- num_cmn_regs = phy->init_data->pcie_cmn_regs;
- num_ln_regs = phy->init_data->pcie_ln_regs;
- cmn_vals = phy->init_data->pcie_cmn_vals;
- ln_vals = phy->init_data->pcie_ln_vals;
- } else if (ins->phy_type == PHY_TYPE_USB3) {
- num_cmn_regs = phy->init_data->usb_cmn_regs;
- num_ln_regs = phy->init_data->usb_ln_regs;
- cmn_vals = phy->init_data->usb_cmn_vals;
- ln_vals = phy->init_data->usb_ln_vals;
- } else {
- return -EINVAL;
+ clk_set_rate(phy->input_clks[CMN_REFCLK_DIG_DIV], 25000000);
+ clk_set_rate(phy->input_clks[CMN_REFCLK1_DIG_DIV], 25000000);
+
+ /* PHY PCS common registers configurations */
+ pcs_cmn_vals = init_data->pcs_cmn_vals[phy_type][TYPE_NONE][ssc];
+ if (pcs_cmn_vals) {
+ reg_pairs = pcs_cmn_vals->reg_pairs;
+ num_regs = pcs_cmn_vals->num_regs;
+ regmap = phy->regmap_phy_pcs_common_cdb;
+ for (i = 0; i < num_regs; i++)
+ regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val);
+ }
+
+ /* PHY PMA lane registers configurations */
+ phy_pma_ln_vals = init_data->phy_pma_ln_vals[phy_type][TYPE_NONE][ssc];
+ if (phy_pma_ln_vals) {
+ reg_pairs = phy_pma_ln_vals->reg_pairs;
+ num_regs = phy_pma_ln_vals->num_regs;
+ for (i = 0; i < ins->num_lanes; i++) {
+ regmap = phy->regmap_phy_pma_lane_cdb[i + ins->mlane];
+ for (j = 0; j < num_regs; j++)
+ regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val);
+ }
}
- regmap = phy->regmap_common_cdb;
- for (j = 0; j < num_cmn_regs ; j++)
- regmap_write(regmap, cmn_vals[j].off, cmn_vals[j].val);
+ /* PMA common registers configurations */
+ pma_cmn_vals = init_data->pma_cmn_vals[phy_type][TYPE_NONE][ssc];
+ if (pma_cmn_vals) {
+ reg_pairs = pma_cmn_vals->reg_pairs;
+ num_regs = pma_cmn_vals->num_regs;
+ regmap = phy->regmap_common_cdb;
+ for (i = 0; i < num_regs; i++)
+ regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val);
+ }
- for (i = 0; i < ins->num_lanes; i++) {
- for (j = 0; j < num_ln_regs ; j++) {
+ /* PMA lane registers configurations */
+ pma_ln_vals = init_data->pma_ln_vals[phy_type][TYPE_NONE][ssc];
+ if (pma_ln_vals) {
+ reg_pairs = pma_ln_vals->reg_pairs;
+ num_regs = pma_ln_vals->num_regs;
+ for (i = 0; i < ins->num_lanes; i++) {
regmap = phy->regmap_lane_cdb[i + ins->mlane];
- regmap_write(regmap, ln_vals[j].off, ln_vals[j].val);
+ for (j = 0; j < num_regs; j++)
+ regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val);
}
}
@@ -319,6 +579,15 @@ static int cdns_sierra_phy_on(struct phy *gphy)
u32 val;
int ret;
+ if (sp->nsubnodes == 1) {
+ /* Take the PHY out of reset */
+ ret = reset_control_deassert(sp->phy_rst);
+ if (ret) {
+ dev_err(dev, "Failed to take the PHY out of reset\n");
+ return ret;
+ }
+ }
+
/* Take the PHY lane group out of reset */
ret = reset_control_deassert(ins->lnk_rst);
if (ret) {
@@ -326,6 +595,26 @@ static int cdns_sierra_phy_on(struct phy *gphy)
return ret;
}
+ if (ins->phy_type == TYPE_PCIE || ins->phy_type == TYPE_USB) {
+ ret = regmap_field_read_poll_timeout(sp->phy_iso_link_ctrl_1[ins->mlane],
+ val, !val, 1000, PLL_LOCK_TIME);
+ if (ret) {
+ dev_err(dev, "Timeout waiting for PHY status ready\n");
+ return ret;
+ }
+ }
+
+ /*
+ * Wait for cmn_ready assertion
+ * PHY_PMA_CMN_CTRL[0] == 1
+ */
+ ret = regmap_field_read_poll_timeout(sp->pma_cmn_ready, val, val,
+ 1000, PLL_LOCK_TIME);
+ if (ret) {
+ dev_err(dev, "Timeout waiting for CMN ready\n");
+ return ret;
+ }
+
ret = regmap_field_read_poll_timeout(sp->pllctrl_lock[ins->mlane],
val, val, 1000, PLL_LOCK_TIME);
if (ret < 0)
@@ -358,23 +647,308 @@ static const struct phy_ops ops = {
.owner = THIS_MODULE,
};
+static int cdns_sierra_noop_phy_on(struct phy *gphy)
+{
+ usleep_range(5000, 10000);
+
+ return 0;
+}
+
+static const struct phy_ops noop_ops = {
+ .power_on = cdns_sierra_noop_phy_on,
+ .owner = THIS_MODULE,
+};
+
+static u8 cdns_sierra_pll_mux_get_parent(struct clk_hw *hw)
+{
+ struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw);
+ struct regmap_field *plllc1en_field = mux->plllc1en_field;
+ struct regmap_field *termen_field = mux->termen_field;
+ struct regmap_field *field = mux->pfdclk_sel_preg;
+ unsigned int val;
+ int index;
+
+ regmap_field_read(field, &val);
+
+ if (strstr(clk_hw_get_name(hw), clk_names[CDNS_SIERRA_PLL_CMNLC1])) {
+ index = clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table[CMN_PLLLC1], 0, val);
+ if (index == 1) {
+ regmap_field_write(plllc1en_field, 1);
+ regmap_field_write(termen_field, 1);
+ }
+ } else {
+ index = clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table[CMN_PLLLC], 0, val);
+ }
+
+ return index;
+}
+
+static int cdns_sierra_pll_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw);
+ struct regmap_field *plllc1en_field = mux->plllc1en_field;
+ struct regmap_field *termen_field = mux->termen_field;
+ struct regmap_field *field = mux->pfdclk_sel_preg;
+ int val, ret;
+
+ ret = regmap_field_write(plllc1en_field, 0);
+ ret |= regmap_field_write(termen_field, 0);
+ if (index == 1) {
+ ret |= regmap_field_write(plllc1en_field, 1);
+ ret |= regmap_field_write(termen_field, 1);
+ }
+
+ if (strstr(clk_hw_get_name(hw), clk_names[CDNS_SIERRA_PLL_CMNLC1]))
+ val = cdns_sierra_pll_mux_table[CMN_PLLLC1][index];
+ else
+ val = cdns_sierra_pll_mux_table[CMN_PLLLC][index];
+
+ ret |= regmap_field_write(field, val);
+
+ return ret;
+}
+
+static const struct clk_ops cdns_sierra_pll_mux_ops = {
+ .set_parent = cdns_sierra_pll_mux_set_parent,
+ .get_parent = cdns_sierra_pll_mux_get_parent,
+};
+
+static int cdns_sierra_pll_mux_register(struct cdns_sierra_phy *sp,
+ struct regmap_field *pfdclk1_sel_field,
+ struct regmap_field *plllc1en_field,
+ struct regmap_field *termen_field,
+ int clk_index)
+{
+ struct cdns_sierra_pll_mux *mux;
+ struct device *dev = sp->dev;
+ struct clk_init_data *init;
+ const char **parent_names;
+ unsigned int num_parents;
+ char clk_name[100];
+ struct clk *clk;
+ int i;
+
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return -ENOMEM;
+
+ num_parents = SIERRA_NUM_CMN_PLLC_PARENTS;
+ parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), GFP_KERNEL);
+ if (!parent_names)
+ return -ENOMEM;
+
+ for (i = 0; i < num_parents; i++) {
+ clk = sp->input_clks[pll_mux_parent_index[clk_index][i]];
+ if (IS_ERR_OR_NULL(clk)) {
+ dev_err(dev, "No parent clock for PLL mux clocks\n");
+ return IS_ERR(clk) ? PTR_ERR(clk) : -ENOENT;
+ }
+ parent_names[i] = __clk_get_name(clk);
+ }
+
+ snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), clk_names[clk_index]);
+
+ init = &mux->clk_data;
+
+ init->ops = &cdns_sierra_pll_mux_ops;
+ init->flags = CLK_SET_RATE_NO_REPARENT;
+ init->parent_names = parent_names;
+ init->num_parents = num_parents;
+ init->name = clk_name;
+
+ mux->pfdclk_sel_preg = pfdclk1_sel_field;
+ mux->plllc1en_field = plllc1en_field;
+ mux->termen_field = termen_field;
+ mux->hw.init = init;
+
+ clk = devm_clk_register(dev, &mux->hw);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ sp->output_clks[clk_index] = clk;
+
+ return 0;
+}
+
+static int cdns_sierra_phy_register_pll_mux(struct cdns_sierra_phy *sp)
+{
+ struct regmap_field *pfdclk1_sel_field;
+ struct regmap_field *plllc1en_field;
+ struct regmap_field *termen_field;
+ struct device *dev = sp->dev;
+ int ret = 0, i, clk_index;
+
+ clk_index = CDNS_SIERRA_PLL_CMNLC;
+ for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++, clk_index++) {
+ pfdclk1_sel_field = sp->cmn_plllc_pfdclk1_sel_preg[i];
+ plllc1en_field = sp->cmn_refrcv_refclk_plllc1en_preg[i];
+ termen_field = sp->cmn_refrcv_refclk_termen_preg[i];
+
+ ret = cdns_sierra_pll_mux_register(sp, pfdclk1_sel_field, plllc1en_field,
+ termen_field, clk_index);
+ if (ret) {
+ dev_err(dev, "Fail to register cmn plllc mux\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int cdns_sierra_derived_refclk_enable(struct clk_hw *hw)
+{
+ struct cdns_sierra_derived_refclk *derived_refclk = to_cdns_sierra_derived_refclk(hw);
+
+ regmap_field_write(derived_refclk->cmn_plllc_clk1_en_preg, 0x1);
+
+ /* Programming to get 100Mhz clock output in ref_der_clk_out 5GHz VCO/50 = 100MHz */
+ regmap_field_write(derived_refclk->cmn_plllc_clk1outdiv_preg, 0x2E);
+
+ return 0;
+}
+
+static void cdns_sierra_derived_refclk_disable(struct clk_hw *hw)
+{
+ struct cdns_sierra_derived_refclk *derived_refclk = to_cdns_sierra_derived_refclk(hw);
+
+ regmap_field_write(derived_refclk->cmn_plllc_clk1_en_preg, 0);
+}
+
+static int cdns_sierra_derived_refclk_is_enabled(struct clk_hw *hw)
+{
+ struct cdns_sierra_derived_refclk *derived_refclk = to_cdns_sierra_derived_refclk(hw);
+ int val;
+
+ regmap_field_read(derived_refclk->cmn_plllc_clk1_en_preg, &val);
+
+ return !!val;
+}
+
+static const struct clk_ops cdns_sierra_derived_refclk_ops = {
+ .enable = cdns_sierra_derived_refclk_enable,
+ .disable = cdns_sierra_derived_refclk_disable,
+ .is_enabled = cdns_sierra_derived_refclk_is_enabled,
+};
+
+static int cdns_sierra_derived_refclk_register(struct cdns_sierra_phy *sp)
+{
+ struct cdns_sierra_derived_refclk *derived_refclk;
+ struct device *dev = sp->dev;
+ struct regmap_field *field;
+ struct clk_init_data *init;
+ struct regmap *regmap;
+ char clk_name[100];
+ struct clk *clk;
+
+ derived_refclk = devm_kzalloc(dev, sizeof(*derived_refclk), GFP_KERNEL);
+ if (!derived_refclk)
+ return -ENOMEM;
+
+ snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
+ clk_names[CDNS_SIERRA_DERIVED_REFCLK]);
+
+ init = &derived_refclk->clk_data;
+
+ init->ops = &cdns_sierra_derived_refclk_ops;
+ init->flags = 0;
+ init->name = clk_name;
+
+ regmap = sp->regmap_common_cdb;
+
+ field = devm_regmap_field_alloc(dev, regmap, cmn_plllc_clk1outdiv_preg);
+ if (IS_ERR(field)) {
+ dev_err(dev, "cmn_plllc_clk1outdiv_preg reg field init failed\n");
+ return PTR_ERR(field);
+ }
+ derived_refclk->cmn_plllc_clk1outdiv_preg = field;
+
+ field = devm_regmap_field_alloc(dev, regmap, cmn_plllc_clk1_en_preg);
+ if (IS_ERR(field)) {
+ dev_err(dev, "cmn_plllc_clk1_en_preg reg field init failed\n");
+ return PTR_ERR(field);
+ }
+ derived_refclk->cmn_plllc_clk1_en_preg = field;
+
+ derived_refclk->hw.init = init;
+
+ clk = devm_clk_register(dev, &derived_refclk->hw);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ sp->output_clks[CDNS_SIERRA_DERIVED_REFCLK] = clk;
+
+ return 0;
+}
+
+static void cdns_sierra_clk_unregister(struct cdns_sierra_phy *sp)
+{
+ struct device *dev = sp->dev;
+ struct device_node *node = dev->of_node;
+
+ of_clk_del_provider(node);
+}
+
+static int cdns_sierra_clk_register(struct cdns_sierra_phy *sp)
+{
+ struct device *dev = sp->dev;
+ struct device_node *node = dev->of_node;
+ int ret;
+
+ ret = cdns_sierra_phy_register_pll_mux(sp);
+ if (ret) {
+ dev_err(dev, "Failed to pll mux clocks\n");
+ return ret;
+ }
+
+ ret = cdns_sierra_derived_refclk_register(sp);
+ if (ret) {
+ dev_err(dev, "Failed to register derived refclk\n");
+ return ret;
+ }
+
+ sp->clk_data.clks = sp->output_clks;
+ sp->clk_data.clk_num = CDNS_SIERRA_OUTPUT_CLOCKS;
+ ret = of_clk_add_provider(node, of_clk_src_onecell_get, &sp->clk_data);
+ if (ret)
+ dev_err(dev, "Failed to add clock provider: %s\n", node->name);
+
+ return ret;
+}
+
static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst,
struct device_node *child)
{
+ u32 phy_type;
+
if (of_property_read_u32(child, "reg", &inst->mlane))
return -EINVAL;
if (of_property_read_u32(child, "cdns,num-lanes", &inst->num_lanes))
return -EINVAL;
- if (of_property_read_u32(child, "cdns,phy-type", &inst->phy_type))
+ if (of_property_read_u32(child, "cdns,phy-type", &phy_type))
return -EINVAL;
+ switch (phy_type) {
+ case PHY_TYPE_PCIE:
+ inst->phy_type = TYPE_PCIE;
+ break;
+ case PHY_TYPE_USB3:
+ inst->phy_type = TYPE_USB;
+ break;
+ case PHY_TYPE_QSGMII:
+ inst->phy_type = TYPE_QSGMII;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ inst->ssc_mode = EXTERNAL_SSC;
+ of_property_read_u32(child, "cdns,ssc-mode", &inst->ssc_mode);
+
return 0;
}
-static const struct of_device_id cdns_sierra_id_table[];
-
static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base,
u32 block_offset, u8 reg_offset_shift,
const struct regmap_config *config)
@@ -396,6 +970,7 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp)
{
struct device *dev = sp->dev;
struct regmap_field *field;
+ struct reg_field reg_field;
struct regmap *regmap;
int i;
@@ -407,7 +982,33 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp)
}
sp->macro_id_type = field;
- regmap = sp->regmap_phy_config_ctrl;
+ for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++) {
+ reg_field = cmn_plllc_pfdclk1_sel_preg[i].pfdclk_sel_preg;
+ field = devm_regmap_field_alloc(dev, regmap, reg_field);
+ if (IS_ERR(field)) {
+ dev_err(dev, "PLLLC%d_PFDCLK1_SEL failed\n", i);
+ return PTR_ERR(field);
+ }
+ sp->cmn_plllc_pfdclk1_sel_preg[i] = field;
+
+ reg_field = cmn_plllc_pfdclk1_sel_preg[i].plllc1en_field;
+ field = devm_regmap_field_alloc(dev, regmap, reg_field);
+ if (IS_ERR(field)) {
+ dev_err(dev, "REFRCV%d_REFCLK_PLLLC1EN failed\n", i);
+ return PTR_ERR(field);
+ }
+ sp->cmn_refrcv_refclk_plllc1en_preg[i] = field;
+
+ reg_field = cmn_plllc_pfdclk1_sel_preg[i].termen_field;
+ field = devm_regmap_field_alloc(dev, regmap, reg_field);
+ if (IS_ERR(field)) {
+ dev_err(dev, "REFRCV%d_REFCLK_TERMEN failed\n", i);
+ return PTR_ERR(field);
+ }
+ sp->cmn_refrcv_refclk_termen_preg[i] = field;
+ }
+
+ regmap = sp->regmap_phy_pcs_common_cdb;
field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1);
if (IS_ERR(field)) {
dev_err(dev, "PHY_PLL_CFG_1 reg field init failed\n");
@@ -415,6 +1016,14 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp)
}
sp->phy_pll_cfg_1 = field;
+ regmap = sp->regmap_phy_pma_common_cdb;
+ field = devm_regmap_field_alloc(dev, regmap, pma_cmn_ready);
+ if (IS_ERR(field)) {
+ dev_err(dev, "PHY_PMA_CMN_CTRL reg field init failed\n");
+ return PTR_ERR(field);
+ }
+ sp->pma_cmn_ready = field;
+
for (i = 0; i < SIERRA_MAX_LANES; i++) {
regmap = sp->regmap_lane_cdb[i];
field = devm_regmap_field_alloc(dev, regmap, pllctrl_lock);
@@ -422,7 +1031,17 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp)
dev_err(dev, "P%d_ENABLE reg field init failed\n", i);
return PTR_ERR(field);
}
- sp->pllctrl_lock[i] = field;
+ sp->pllctrl_lock[i] = field;
+ }
+
+ for (i = 0; i < SIERRA_MAX_LANES; i++) {
+ regmap = sp->regmap_phy_pcs_lane_cdb[i];
+ field = devm_regmap_field_alloc(dev, regmap, phy_iso_link_ctrl_1);
+ if (IS_ERR(field)) {
+ dev_err(dev, "PHY_ISO_LINK_CTRL reg field init for lane %d failed\n", i);
+ return PTR_ERR(field);
+ }
+ sp->phy_iso_link_ctrl_1[i] = field;
}
return 0;
@@ -459,14 +1078,275 @@ static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp,
}
sp->regmap_common_cdb = regmap;
- block_offset = SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset_shift);
+ block_offset = SIERRA_PHY_PCS_COMMON_OFFSET(block_offset_shift);
regmap = cdns_regmap_init(dev, base, block_offset, reg_offset_shift,
- &cdns_sierra_phy_config_ctrl_config);
+ &cdns_sierra_phy_pcs_cmn_cdb_config);
if (IS_ERR(regmap)) {
- dev_err(dev, "Failed to init PHY config and control regmap\n");
+ dev_err(dev, "Failed to init PHY PCS common CDB regmap\n");
return PTR_ERR(regmap);
}
- sp->regmap_phy_config_ctrl = regmap;
+ sp->regmap_phy_pcs_common_cdb = regmap;
+
+ for (i = 0; i < SIERRA_MAX_LANES; i++) {
+ block_offset = SIERRA_PHY_PCS_LANE_CDB_OFFSET(i, block_offset_shift,
+ reg_offset_shift);
+ regmap = cdns_regmap_init(dev, base, block_offset,
+ reg_offset_shift,
+ &cdns_sierra_phy_pcs_lane_cdb_config[i]);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to init PHY PCS lane CDB regmap\n");
+ return PTR_ERR(regmap);
+ }
+ sp->regmap_phy_pcs_lane_cdb[i] = regmap;
+ }
+
+ block_offset = SIERRA_PHY_PMA_COMMON_OFFSET(block_offset_shift);
+ regmap = cdns_regmap_init(dev, base, block_offset, reg_offset_shift,
+ &cdns_sierra_phy_pma_cmn_cdb_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to init PHY PMA common CDB regmap\n");
+ return PTR_ERR(regmap);
+ }
+ sp->regmap_phy_pma_common_cdb = regmap;
+
+ for (i = 0; i < SIERRA_MAX_LANES; i++) {
+ block_offset = SIERRA_PHY_PMA_LANE_CDB_OFFSET(i, block_offset_shift,
+ reg_offset_shift);
+ regmap = cdns_regmap_init(dev, base, block_offset,
+ reg_offset_shift,
+ &cdns_sierra_phy_pma_lane_cdb_config[i]);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to init PHY PMA lane CDB regmap\n");
+ return PTR_ERR(regmap);
+ }
+ sp->regmap_phy_pma_lane_cdb[i] = regmap;
+ }
+
+ return 0;
+}
+
+static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp,
+ struct device *dev)
+{
+ struct clk *clk;
+ int ret;
+
+ clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "cmn_refclk_dig_div clock not found\n");
+ ret = PTR_ERR(clk);
+ return ret;
+ }
+ sp->input_clks[CMN_REFCLK_DIG_DIV] = clk;
+
+ clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "cmn_refclk1_dig_div clock not found\n");
+ ret = PTR_ERR(clk);
+ return ret;
+ }
+ sp->input_clks[CMN_REFCLK1_DIG_DIV] = clk;
+
+ clk = devm_clk_get_optional(dev, "pll0_refclk");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "pll0_refclk clock not found\n");
+ ret = PTR_ERR(clk);
+ return ret;
+ }
+ sp->input_clks[PLL0_REFCLK] = clk;
+
+ clk = devm_clk_get_optional(dev, "pll1_refclk");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "pll1_refclk clock not found\n");
+ ret = PTR_ERR(clk);
+ return ret;
+ }
+ sp->input_clks[PLL1_REFCLK] = clk;
+
+ return 0;
+}
+
+static int cdns_sierra_phy_clk(struct cdns_sierra_phy *sp)
+{
+ struct device *dev = sp->dev;
+ struct clk *clk;
+ int ret;
+
+ clk = devm_clk_get_optional(dev, "phy_clk");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "failed to get clock phy_clk\n");
+ return PTR_ERR(clk);
+ }
+ sp->input_clks[PHY_CLK] = clk;
+
+ ret = clk_prepare_enable(sp->input_clks[PHY_CLK]);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cdns_sierra_phy_enable_clocks(struct cdns_sierra_phy *sp)
+{
+ int ret;
+
+ ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]);
+ if (ret)
+ goto err_pll_cmnlc1;
+
+ return 0;
+
+err_pll_cmnlc1:
+ clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
+
+ return ret;
+}
+
+static void cdns_sierra_phy_disable_clocks(struct cdns_sierra_phy *sp)
+{
+ clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]);
+ clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
+ if (!sp->already_configured)
+ clk_disable_unprepare(sp->input_clks[PHY_CLK]);
+}
+
+static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp,
+ struct device *dev)
+{
+ struct reset_control *rst;
+
+ rst = devm_reset_control_get_exclusive(dev, "sierra_reset");
+ if (IS_ERR(rst)) {
+ dev_err(dev, "failed to get reset\n");
+ return PTR_ERR(rst);
+ }
+ sp->phy_rst = rst;
+
+ rst = devm_reset_control_get_optional_exclusive(dev, "sierra_apb");
+ if (IS_ERR(rst)) {
+ dev_err(dev, "failed to get apb reset\n");
+ return PTR_ERR(rst);
+ }
+ sp->apb_rst = rst;
+
+ return 0;
+}
+
+static int cdns_sierra_phy_configure_multilink(struct cdns_sierra_phy *sp)
+{
+ const struct cdns_sierra_data *init_data = sp->init_data;
+ struct cdns_sierra_vals *pma_cmn_vals, *pma_ln_vals;
+ enum cdns_sierra_phy_type phy_t1, phy_t2;
+ struct cdns_sierra_vals *phy_pma_ln_vals;
+ const struct cdns_reg_pairs *reg_pairs;
+ struct cdns_sierra_vals *pcs_cmn_vals;
+ int i, j, node, mlane, num_lanes, ret;
+ enum cdns_sierra_ssc_mode ssc;
+ struct regmap *regmap;
+ u32 num_regs;
+
+ /* Maximum 2 links (subnodes) are supported */
+ if (sp->nsubnodes != 2)
+ return -EINVAL;
+
+ clk_set_rate(sp->input_clks[CMN_REFCLK_DIG_DIV], 25000000);
+ clk_set_rate(sp->input_clks[CMN_REFCLK1_DIG_DIV], 25000000);
+
+ /* PHY configured to use both PLL LC and LC1 */
+ regmap_field_write(sp->phy_pll_cfg_1, 0x1);
+
+ phy_t1 = sp->phys[0].phy_type;
+ phy_t2 = sp->phys[1].phy_type;
+
+ /*
+ * PHY configuration for multi-link operation is done in two steps.
+ * e.g. Consider a case for a 4 lane PHY with PCIe using 2 lanes and QSGMII other 2 lanes.
+ * Sierra PHY has 2 PLLs, viz. PLLLC and PLLLC1. So in this case, PLLLC is used for PCIe
+ * and PLLLC1 is used for QSGMII. PHY is configured in two steps as described below.
+ *
+ * [1] For first step, phy_t1 = TYPE_PCIE and phy_t2 = TYPE_QSGMII
+ * So the register values are selected as [TYPE_PCIE][TYPE_QSGMII][ssc].
+ * This will configure PHY registers associated for PCIe (i.e. first protocol)
+ * involving PLLLC registers and registers for first 2 lanes of PHY.
+ * [2] In second step, the variables phy_t1 and phy_t2 are swapped. So now,
+ * phy_t1 = TYPE_QSGMII and phy_t2 = TYPE_PCIE. And the register values are selected as
+ * [TYPE_QSGMII][TYPE_PCIE][ssc].
+ * This will configure PHY registers associated for QSGMII (i.e. second protocol)
+ * involving PLLLC1 registers and registers for other 2 lanes of PHY.
+ *
+ * This completes the PHY configuration for multilink operation. This approach enables
+ * dividing the large number of PHY register configurations into protocol specific
+ * smaller groups.
+ */
+ for (node = 0; node < sp->nsubnodes; node++) {
+ if (node == 1) {
+ /*
+ * If first link with phy_t1 is configured, then configure the PHY for
+ * second link with phy_t2. Get the array values as [phy_t2][phy_t1][ssc].
+ */
+ swap(phy_t1, phy_t2);
+ }
+
+ mlane = sp->phys[node].mlane;
+ ssc = sp->phys[node].ssc_mode;
+ num_lanes = sp->phys[node].num_lanes;
+
+ /* PHY PCS common registers configurations */
+ pcs_cmn_vals = init_data->pcs_cmn_vals[phy_t1][phy_t2][ssc];
+ if (pcs_cmn_vals) {
+ reg_pairs = pcs_cmn_vals->reg_pairs;
+ num_regs = pcs_cmn_vals->num_regs;
+ regmap = sp->regmap_phy_pcs_common_cdb;
+ for (i = 0; i < num_regs; i++)
+ regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val);
+ }
+
+ /* PHY PMA lane registers configurations */
+ phy_pma_ln_vals = init_data->phy_pma_ln_vals[phy_t1][phy_t2][ssc];
+ if (phy_pma_ln_vals) {
+ reg_pairs = phy_pma_ln_vals->reg_pairs;
+ num_regs = phy_pma_ln_vals->num_regs;
+ for (i = 0; i < num_lanes; i++) {
+ regmap = sp->regmap_phy_pma_lane_cdb[i + mlane];
+ for (j = 0; j < num_regs; j++)
+ regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val);
+ }
+ }
+
+ /* PMA common registers configurations */
+ pma_cmn_vals = init_data->pma_cmn_vals[phy_t1][phy_t2][ssc];
+ if (pma_cmn_vals) {
+ reg_pairs = pma_cmn_vals->reg_pairs;
+ num_regs = pma_cmn_vals->num_regs;
+ regmap = sp->regmap_common_cdb;
+ for (i = 0; i < num_regs; i++)
+ regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val);
+ }
+
+ /* PMA lane registers configurations */
+ pma_ln_vals = init_data->pma_ln_vals[phy_t1][phy_t2][ssc];
+ if (pma_ln_vals) {
+ reg_pairs = pma_ln_vals->reg_pairs;
+ num_regs = pma_ln_vals->num_regs;
+ for (i = 0; i < num_lanes; i++) {
+ regmap = sp->regmap_lane_cdb[i + mlane];
+ for (j = 0; j < num_regs; j++)
+ regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val);
+ }
+ }
+
+ if (phy_t1 == TYPE_QSGMII)
+ reset_control_deassert(sp->phys[node].lnk_rst);
+ }
+
+ /* Take the PHY out of reset */
+ ret = reset_control_deassert(sp->phy_rst);
+ if (ret)
+ return ret;
return 0;
}
@@ -476,25 +1356,20 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
struct cdns_sierra_phy *sp;
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
- const struct of_device_id *match;
- struct cdns_sierra_data *data;
+ const struct cdns_sierra_data *data;
unsigned int id_value;
- struct resource *res;
- int i, ret, node = 0;
+ int ret, node = 0;
void __iomem *base;
- struct clk *clk;
struct device_node *dn = dev->of_node, *child;
if (of_get_child_count(dn) == 0)
return -ENODEV;
/* Get init data for this PHY */
- match = of_match_device(cdns_sierra_id_table, dev);
- if (!match)
+ data = of_device_get_match_data(dev);
+ if (!data)
return -EINVAL;
- data = (struct cdns_sierra_data *)match->data;
-
sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL);
if (!sp)
return -ENOMEM;
@@ -502,8 +1377,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
sp->dev = dev;
sp->init_data = data;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
dev_err(dev, "missing \"reg\"\n");
return PTR_ERR(base);
@@ -520,52 +1394,38 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, sp);
- sp->clk = devm_clk_get_optional(dev, "phy_clk");
- if (IS_ERR(sp->clk)) {
- dev_err(dev, "failed to get clock phy_clk\n");
- return PTR_ERR(sp->clk);
- }
-
- sp->phy_rst = devm_reset_control_get(dev, "sierra_reset");
- if (IS_ERR(sp->phy_rst)) {
- dev_err(dev, "failed to get reset\n");
- return PTR_ERR(sp->phy_rst);
- }
-
- sp->apb_rst = devm_reset_control_get_optional(dev, "sierra_apb");
- if (IS_ERR(sp->apb_rst)) {
- dev_err(dev, "failed to get apb reset\n");
- return PTR_ERR(sp->apb_rst);
- }
-
- clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div");
- if (IS_ERR(clk)) {
- dev_err(dev, "cmn_refclk_dig_div clock not found\n");
- ret = PTR_ERR(clk);
+ ret = cdns_sierra_phy_get_clocks(sp, dev);
+ if (ret)
return ret;
- }
- sp->cmn_refclk_dig_div = clk;
- clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div");
- if (IS_ERR(clk)) {
- dev_err(dev, "cmn_refclk1_dig_div clock not found\n");
- ret = PTR_ERR(clk);
+ ret = cdns_sierra_clk_register(sp);
+ if (ret)
return ret;
- }
- sp->cmn_refclk1_dig_div = clk;
- ret = clk_prepare_enable(sp->clk);
+ ret = cdns_sierra_phy_enable_clocks(sp);
if (ret)
- return ret;
+ goto unregister_clk;
+
+ regmap_field_read(sp->pma_cmn_ready, &sp->already_configured);
+
+ if (!sp->already_configured) {
+ ret = cdns_sierra_phy_clk(sp);
+ if (ret)
+ goto clk_disable;
- /* Enable APB */
- reset_control_deassert(sp->apb_rst);
+ ret = cdns_sierra_phy_get_resets(sp, dev);
+ if (ret)
+ goto clk_disable;
+
+ /* Enable APB */
+ reset_control_deassert(sp->apb_rst);
+ }
/* Check that PHY is present */
regmap_field_read(sp->macro_id_type, &id_value);
if (sp->init_data->id_value != id_value) {
ret = -EINVAL;
- goto clk_disable;
+ goto ctrl_assert;
}
sp->autoconf = of_property_read_bool(dn, "cdns,autoconf");
@@ -573,6 +1433,10 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
for_each_available_child_of_node(dn, child) {
struct phy *gphy;
+ if (!(of_node_name_eq(child, "phy") ||
+ of_node_name_eq(child, "link")))
+ continue;
+
sp->phys[node].lnk_rst =
of_reset_control_array_get_exclusive(child);
@@ -580,7 +1444,8 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
dev_err(dev, "failed to get reset %s\n",
child->full_name);
ret = PTR_ERR(sp->phys[node].lnk_rst);
- goto put_child2;
+ of_node_put(child);
+ goto put_control;
}
if (!sp->autoconf) {
@@ -588,17 +1453,23 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "missing property in node %s\n",
child->name);
- goto put_child;
+ of_node_put(child);
+ reset_control_put(sp->phys[node].lnk_rst);
+ goto put_control;
}
}
sp->num_lanes += sp->phys[node].num_lanes;
- gphy = devm_phy_create(dev, child, &ops);
-
+ if (!sp->already_configured)
+ gphy = devm_phy_create(dev, child, &ops);
+ else
+ gphy = devm_phy_create(dev, child, &noop_ops);
if (IS_ERR(gphy)) {
ret = PTR_ERR(gphy);
- goto put_child;
+ of_node_put(child);
+ reset_control_put(sp->phys[node].lnk_rst);
+ goto put_control;
}
sp->phys[node].phy = gphy;
phy_set_drvdata(gphy, &sp->phys[node]);
@@ -608,28 +1479,37 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
sp->nsubnodes = node;
if (sp->num_lanes > SIERRA_MAX_LANES) {
+ ret = -EINVAL;
dev_err(dev, "Invalid lane configuration\n");
- goto put_child2;
+ goto put_control;
}
/* If more than one subnode, configure the PHY as multilink */
- if (!sp->autoconf && sp->nsubnodes > 1)
- regmap_field_write(sp->phy_pll_cfg_1, 0x1);
+ if (!sp->already_configured && !sp->autoconf && sp->nsubnodes > 1) {
+ ret = cdns_sierra_phy_configure_multilink(sp);
+ if (ret)
+ goto put_control;
+ }
pm_runtime_enable(dev);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
- reset_control_deassert(sp->phy_rst);
- return PTR_ERR_OR_ZERO(phy_provider);
-
-put_child:
- node++;
-put_child2:
- for (i = 0; i < node; i++)
- reset_control_put(sp->phys[i].lnk_rst);
- of_node_put(child);
+ if (IS_ERR(phy_provider)) {
+ ret = PTR_ERR(phy_provider);
+ goto put_control;
+ }
+
+ return 0;
+
+put_control:
+ while (--node >= 0)
+ reset_control_put(sp->phys[node].lnk_rst);
+ctrl_assert:
+ if (!sp->already_configured)
+ reset_control_assert(sp->apb_rst);
clk_disable:
- clk_disable_unprepare(sp->clk);
- reset_control_assert(sp->apb_rst);
+ cdns_sierra_phy_disable_clocks(sp);
+unregister_clk:
+ cdns_sierra_clk_unregister(sp);
return ret;
}
@@ -642,6 +1522,7 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev)
reset_control_assert(phy->apb_rst);
pm_runtime_disable(&pdev->dev);
+ cdns_sierra_phy_disable_clocks(phy);
/*
* The device level resets will be put automatically.
* Need to put the subnode resets here though.
@@ -650,11 +1531,643 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev)
reset_control_assert(phy->phys[i].lnk_rst);
reset_control_put(phy->phys[i].lnk_rst);
}
+
+ cdns_sierra_clk_unregister(phy);
+
return 0;
}
+/* QSGMII PHY PMA lane configuration */
+static struct cdns_reg_pairs qsgmii_phy_pma_ln_regs[] = {
+ {0x9010, SIERRA_PHY_PMA_XCVR_CTRL}
+};
+
+static struct cdns_sierra_vals qsgmii_phy_pma_ln_vals = {
+ .reg_pairs = qsgmii_phy_pma_ln_regs,
+ .num_regs = ARRAY_SIZE(qsgmii_phy_pma_ln_regs),
+};
+
+/* QSGMII refclk 100MHz, 20b, opt1, No BW cal, no ssc, PLL LC1 */
+static const struct cdns_reg_pairs qsgmii_100_no_ssc_plllc1_cmn_regs[] = {
+ {0x2085, SIERRA_CMN_PLLLC1_LF_COEFF_MODE0_PREG},
+ {0x0000, SIERRA_CMN_PLLLC1_BWCAL_MODE0_PREG},
+ {0x0000, SIERRA_CMN_PLLLC1_SS_TIME_STEPSIZE_MODE_PREG}
+};
+
+static const struct cdns_reg_pairs qsgmii_100_no_ssc_plllc1_ln_regs[] = {
+ {0xFC08, SIERRA_DET_STANDEC_A_PREG},
+ {0x0252, SIERRA_DET_STANDEC_E_PREG},
+ {0x0004, SIERRA_PSC_LN_IDLE_PREG},
+ {0x0FFE, SIERRA_PSC_RX_A0_PREG},
+ {0x0011, SIERRA_PLLCTRL_SUBRATE_PREG},
+ {0x0001, SIERRA_PLLCTRL_GEN_A_PREG},
+ {0x5233, SIERRA_PLLCTRL_CPGAIN_MODE_PREG},
+ {0x0000, SIERRA_DRVCTRL_ATTEN_PREG},
+ {0x0089, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+ {0x3C3C, SIERRA_CREQ_CCLKDET_MODE01_PREG},
+ {0x3222, SIERRA_CREQ_FSMCLK_SEL_PREG},
+ {0x0000, SIERRA_CREQ_EQ_CTRL_PREG},
+ {0x8422, SIERRA_CTLELUT_CTRL_PREG},
+ {0x4111, SIERRA_DFE_ECMP_RATESEL_PREG},
+ {0x4111, SIERRA_DFE_SMP_RATESEL_PREG},
+ {0x0002, SIERRA_DEQ_PHALIGN_CTRL},
+ {0x9595, SIERRA_DEQ_VGATUNE_CTRL_PREG},
+ {0x0186, SIERRA_DEQ_GLUT0},
+ {0x0186, SIERRA_DEQ_GLUT1},
+ {0x0186, SIERRA_DEQ_GLUT2},
+ {0x0186, SIERRA_DEQ_GLUT3},
+ {0x0186, SIERRA_DEQ_GLUT4},
+ {0x0861, SIERRA_DEQ_ALUT0},
+ {0x07E0, SIERRA_DEQ_ALUT1},
+ {0x079E, SIERRA_DEQ_ALUT2},
+ {0x071D, SIERRA_DEQ_ALUT3},
+ {0x03F5, SIERRA_DEQ_DFETAP_CTRL_PREG},
+ {0x0C01, SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG},
+ {0x3C40, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
+ {0x1C04, SIERRA_DEQ_TAU_CTRL2_PREG},
+ {0x0033, SIERRA_DEQ_PICTRL_PREG},
+ {0x0660, SIERRA_CPICAL_TMRVAL_MODE0_PREG},
+ {0x00D5, SIERRA_CPI_OUTBUF_RATESEL_PREG},
+ {0x0B6D, SIERRA_CPI_RESBIAS_BIN_PREG},
+ {0x0102, SIERRA_RXBUFFER_CTLECTRL_PREG},
+ {0x0002, SIERRA_RXBUFFER_RCDFECTRL_PREG}
+};
+
+static struct cdns_sierra_vals qsgmii_100_no_ssc_plllc1_cmn_vals = {
+ .reg_pairs = qsgmii_100_no_ssc_plllc1_cmn_regs,
+ .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_plllc1_cmn_regs),
+};
+
+static struct cdns_sierra_vals qsgmii_100_no_ssc_plllc1_ln_vals = {
+ .reg_pairs = qsgmii_100_no_ssc_plllc1_ln_regs,
+ .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_plllc1_ln_regs),
+};
+
+/* PCIE PHY PCS common configuration */
+static struct cdns_reg_pairs pcie_phy_pcs_cmn_regs[] = {
+ {0x0430, SIERRA_PHY_PIPE_CMN_CTRL1}
+};
+
+static struct cdns_sierra_vals pcie_phy_pcs_cmn_vals = {
+ .reg_pairs = pcie_phy_pcs_cmn_regs,
+ .num_regs = ARRAY_SIZE(pcie_phy_pcs_cmn_regs),
+};
+
+/* refclk100MHz_32b_PCIe_cmn_pll_no_ssc, pcie_links_using_plllc, pipe_bw_3 */
+static const struct cdns_reg_pairs pcie_100_no_ssc_plllc_cmn_regs[] = {
+ {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
+ {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
+ {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG},
+ {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG}
+};
+
+/*
+ * refclk100MHz_32b_PCIe_ln_no_ssc, multilink, using_plllc,
+ * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz
+ */
+static const struct cdns_reg_pairs ml_pcie_100_no_ssc_ln_regs[] = {
+ {0xFC08, SIERRA_DET_STANDEC_A_PREG},
+ {0x001D, SIERRA_PSM_A3IN_TMR_PREG},
+ {0x0004, SIERRA_PSC_LN_A3_PREG},
+ {0x0004, SIERRA_PSC_LN_A4_PREG},
+ {0x0004, SIERRA_PSC_LN_IDLE_PREG},
+ {0x1555, SIERRA_DFE_BIASTRIM_PREG},
+ {0x9703, SIERRA_DRVCTRL_BOOST_PREG},
+ {0x8055, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
+ {0x80BB, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
+ {0x8351, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
+ {0x8349, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+ {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG},
+ {0x9800, SIERRA_RX_CTLE_CAL_PREG},
+ {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG},
+ {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
+ {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG},
+ {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG},
+ {0x0041, SIERRA_DEQ_GLUT0},
+ {0x0082, SIERRA_DEQ_GLUT1},
+ {0x00C3, SIERRA_DEQ_GLUT2},
+ {0x0145, SIERRA_DEQ_GLUT3},
+ {0x0186, SIERRA_DEQ_GLUT4},
+ {0x09E7, SIERRA_DEQ_ALUT0},
+ {0x09A6, SIERRA_DEQ_ALUT1},
+ {0x0965, SIERRA_DEQ_ALUT2},
+ {0x08E3, SIERRA_DEQ_ALUT3},
+ {0x00FA, SIERRA_DEQ_DFETAP0},
+ {0x00FA, SIERRA_DEQ_DFETAP1},
+ {0x00FA, SIERRA_DEQ_DFETAP2},
+ {0x00FA, SIERRA_DEQ_DFETAP3},
+ {0x00FA, SIERRA_DEQ_DFETAP4},
+ {0x000F, SIERRA_DEQ_PRECUR_PREG},
+ {0x0280, SIERRA_DEQ_POSTCUR_PREG},
+ {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG},
+ {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
+ {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG},
+ {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG},
+ {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG},
+ {0x002B, SIERRA_CPI_TRIM_PREG},
+ {0x0003, SIERRA_EPI_CTRL_PREG},
+ {0x803F, SIERRA_SDFILT_H2L_A_PREG},
+ {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG},
+ {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG},
+ {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG}
+};
+
+static struct cdns_sierra_vals pcie_100_no_ssc_plllc_cmn_vals = {
+ .reg_pairs = pcie_100_no_ssc_plllc_cmn_regs,
+ .num_regs = ARRAY_SIZE(pcie_100_no_ssc_plllc_cmn_regs),
+};
+
+static struct cdns_sierra_vals ml_pcie_100_no_ssc_ln_vals = {
+ .reg_pairs = ml_pcie_100_no_ssc_ln_regs,
+ .num_regs = ARRAY_SIZE(ml_pcie_100_no_ssc_ln_regs),
+};
+
+/*
+ * TI J721E:
+ * refclk100MHz_32b_PCIe_ln_no_ssc, multilink, using_plllc,
+ * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz
+ */
+static const struct cdns_reg_pairs ti_ml_pcie_100_no_ssc_ln_regs[] = {
+ {0xFC08, SIERRA_DET_STANDEC_A_PREG},
+ {0x001D, SIERRA_PSM_A3IN_TMR_PREG},
+ {0x0004, SIERRA_PSC_LN_A3_PREG},
+ {0x0004, SIERRA_PSC_LN_A4_PREG},
+ {0x0004, SIERRA_PSC_LN_IDLE_PREG},
+ {0x1555, SIERRA_DFE_BIASTRIM_PREG},
+ {0x9703, SIERRA_DRVCTRL_BOOST_PREG},
+ {0x8055, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
+ {0x80BB, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
+ {0x8351, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
+ {0x8349, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+ {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG},
+ {0x9800, SIERRA_RX_CTLE_CAL_PREG},
+ {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG},
+ {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
+ {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG},
+ {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG},
+ {0x0041, SIERRA_DEQ_GLUT0},
+ {0x0082, SIERRA_DEQ_GLUT1},
+ {0x00C3, SIERRA_DEQ_GLUT2},
+ {0x0145, SIERRA_DEQ_GLUT3},
+ {0x0186, SIERRA_DEQ_GLUT4},
+ {0x09E7, SIERRA_DEQ_ALUT0},
+ {0x09A6, SIERRA_DEQ_ALUT1},
+ {0x0965, SIERRA_DEQ_ALUT2},
+ {0x08E3, SIERRA_DEQ_ALUT3},
+ {0x00FA, SIERRA_DEQ_DFETAP0},
+ {0x00FA, SIERRA_DEQ_DFETAP1},
+ {0x00FA, SIERRA_DEQ_DFETAP2},
+ {0x00FA, SIERRA_DEQ_DFETAP3},
+ {0x00FA, SIERRA_DEQ_DFETAP4},
+ {0x000F, SIERRA_DEQ_PRECUR_PREG},
+ {0x0280, SIERRA_DEQ_POSTCUR_PREG},
+ {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG},
+ {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
+ {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG},
+ {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG},
+ {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG},
+ {0x002B, SIERRA_CPI_TRIM_PREG},
+ {0x0003, SIERRA_EPI_CTRL_PREG},
+ {0x803F, SIERRA_SDFILT_H2L_A_PREG},
+ {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG},
+ {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG},
+ {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG},
+ {0x0002, SIERRA_TX_RCVDET_OVRD_PREG}
+};
+
+static struct cdns_sierra_vals ti_ml_pcie_100_no_ssc_ln_vals = {
+ .reg_pairs = ti_ml_pcie_100_no_ssc_ln_regs,
+ .num_regs = ARRAY_SIZE(ti_ml_pcie_100_no_ssc_ln_regs),
+};
+
+/* refclk100MHz_32b_PCIe_cmn_pll_int_ssc, pcie_links_using_plllc, pipe_bw_3 */
+static const struct cdns_reg_pairs pcie_100_int_ssc_plllc_cmn_regs[] = {
+ {0x000E, SIERRA_CMN_PLLLC_MODE_PREG},
+ {0x4006, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
+ {0x4006, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
+ {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG},
+ {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG},
+ {0x0581, SIERRA_CMN_PLLLC_DSMCORR_PREG},
+ {0x7F80, SIERRA_CMN_PLLLC_SS_PREG},
+ {0x0041, SIERRA_CMN_PLLLC_SS_AMP_STEP_SIZE_PREG},
+ {0x0464, SIERRA_CMN_PLLLC_SSTWOPT_PREG},
+ {0x0D0D, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG},
+ {0x0060, SIERRA_CMN_PLLLC_LOCK_DELAY_CTRL_PREG}
+};
+
+/*
+ * refclk100MHz_32b_PCIe_ln_int_ssc, multilink, using_plllc,
+ * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz
+ */
+static const struct cdns_reg_pairs ml_pcie_100_int_ssc_ln_regs[] = {
+ {0xFC08, SIERRA_DET_STANDEC_A_PREG},
+ {0x001D, SIERRA_PSM_A3IN_TMR_PREG},
+ {0x0004, SIERRA_PSC_LN_A3_PREG},
+ {0x0004, SIERRA_PSC_LN_A4_PREG},
+ {0x0004, SIERRA_PSC_LN_IDLE_PREG},
+ {0x1555, SIERRA_DFE_BIASTRIM_PREG},
+ {0x9703, SIERRA_DRVCTRL_BOOST_PREG},
+ {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG},
+ {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+ {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG},
+ {0x9800, SIERRA_RX_CTLE_CAL_PREG},
+ {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG},
+ {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG},
+ {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG},
+ {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
+ {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG},
+ {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG},
+ {0x0041, SIERRA_DEQ_GLUT0},
+ {0x0082, SIERRA_DEQ_GLUT1},
+ {0x00C3, SIERRA_DEQ_GLUT2},
+ {0x0145, SIERRA_DEQ_GLUT3},
+ {0x0186, SIERRA_DEQ_GLUT4},
+ {0x09E7, SIERRA_DEQ_ALUT0},
+ {0x09A6, SIERRA_DEQ_ALUT1},
+ {0x0965, SIERRA_DEQ_ALUT2},
+ {0x08E3, SIERRA_DEQ_ALUT3},
+ {0x00FA, SIERRA_DEQ_DFETAP0},
+ {0x00FA, SIERRA_DEQ_DFETAP1},
+ {0x00FA, SIERRA_DEQ_DFETAP2},
+ {0x00FA, SIERRA_DEQ_DFETAP3},
+ {0x00FA, SIERRA_DEQ_DFETAP4},
+ {0x000F, SIERRA_DEQ_PRECUR_PREG},
+ {0x0280, SIERRA_DEQ_POSTCUR_PREG},
+ {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG},
+ {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
+ {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG},
+ {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG},
+ {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG},
+ {0x002B, SIERRA_CPI_TRIM_PREG},
+ {0x0003, SIERRA_EPI_CTRL_PREG},
+ {0x803F, SIERRA_SDFILT_H2L_A_PREG},
+ {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG},
+ {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG},
+ {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG}
+};
+
+static struct cdns_sierra_vals pcie_100_int_ssc_plllc_cmn_vals = {
+ .reg_pairs = pcie_100_int_ssc_plllc_cmn_regs,
+ .num_regs = ARRAY_SIZE(pcie_100_int_ssc_plllc_cmn_regs),
+};
+
+static struct cdns_sierra_vals ml_pcie_100_int_ssc_ln_vals = {
+ .reg_pairs = ml_pcie_100_int_ssc_ln_regs,
+ .num_regs = ARRAY_SIZE(ml_pcie_100_int_ssc_ln_regs),
+};
+
+/*
+ * TI J721E:
+ * refclk100MHz_32b_PCIe_ln_int_ssc, multilink, using_plllc,
+ * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz
+ */
+static const struct cdns_reg_pairs ti_ml_pcie_100_int_ssc_ln_regs[] = {
+ {0xFC08, SIERRA_DET_STANDEC_A_PREG},
+ {0x001D, SIERRA_PSM_A3IN_TMR_PREG},
+ {0x0004, SIERRA_PSC_LN_A3_PREG},
+ {0x0004, SIERRA_PSC_LN_A4_PREG},
+ {0x0004, SIERRA_PSC_LN_IDLE_PREG},
+ {0x1555, SIERRA_DFE_BIASTRIM_PREG},
+ {0x9703, SIERRA_DRVCTRL_BOOST_PREG},
+ {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG},
+ {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+ {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG},
+ {0x9800, SIERRA_RX_CTLE_CAL_PREG},
+ {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG},
+ {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG},
+ {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG},
+ {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
+ {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG},
+ {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG},
+ {0x0041, SIERRA_DEQ_GLUT0},
+ {0x0082, SIERRA_DEQ_GLUT1},
+ {0x00C3, SIERRA_DEQ_GLUT2},
+ {0x0145, SIERRA_DEQ_GLUT3},
+ {0x0186, SIERRA_DEQ_GLUT4},
+ {0x09E7, SIERRA_DEQ_ALUT0},
+ {0x09A6, SIERRA_DEQ_ALUT1},
+ {0x0965, SIERRA_DEQ_ALUT2},
+ {0x08E3, SIERRA_DEQ_ALUT3},
+ {0x00FA, SIERRA_DEQ_DFETAP0},
+ {0x00FA, SIERRA_DEQ_DFETAP1},
+ {0x00FA, SIERRA_DEQ_DFETAP2},
+ {0x00FA, SIERRA_DEQ_DFETAP3},
+ {0x00FA, SIERRA_DEQ_DFETAP4},
+ {0x000F, SIERRA_DEQ_PRECUR_PREG},
+ {0x0280, SIERRA_DEQ_POSTCUR_PREG},
+ {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG},
+ {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
+ {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG},
+ {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG},
+ {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG},
+ {0x002B, SIERRA_CPI_TRIM_PREG},
+ {0x0003, SIERRA_EPI_CTRL_PREG},
+ {0x803F, SIERRA_SDFILT_H2L_A_PREG},
+ {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG},
+ {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG},
+ {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG},
+ {0x0002, SIERRA_TX_RCVDET_OVRD_PREG}
+};
+
+static struct cdns_sierra_vals ti_ml_pcie_100_int_ssc_ln_vals = {
+ .reg_pairs = ti_ml_pcie_100_int_ssc_ln_regs,
+ .num_regs = ARRAY_SIZE(ti_ml_pcie_100_int_ssc_ln_regs),
+};
+
+/* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc, pcie_links_using_plllc, pipe_bw_3 */
+static const struct cdns_reg_pairs pcie_100_ext_ssc_plllc_cmn_regs[] = {
+ {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
+ {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
+ {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG},
+ {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG},
+ {0x1B1B, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG}
+};
+
+/*
+ * refclk100MHz_32b_PCIe_ln_ext_ssc, multilink, using_plllc,
+ * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz
+ */
+static const struct cdns_reg_pairs ml_pcie_100_ext_ssc_ln_regs[] = {
+ {0xFC08, SIERRA_DET_STANDEC_A_PREG},
+ {0x001D, SIERRA_PSM_A3IN_TMR_PREG},
+ {0x0004, SIERRA_PSC_LN_A3_PREG},
+ {0x0004, SIERRA_PSC_LN_A4_PREG},
+ {0x0004, SIERRA_PSC_LN_IDLE_PREG},
+ {0x1555, SIERRA_DFE_BIASTRIM_PREG},
+ {0x9703, SIERRA_DRVCTRL_BOOST_PREG},
+ {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG},
+ {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+ {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG},
+ {0x9800, SIERRA_RX_CTLE_CAL_PREG},
+ {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG},
+ {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG},
+ {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG},
+ {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
+ {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG},
+ {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG},
+ {0x0041, SIERRA_DEQ_GLUT0},
+ {0x0082, SIERRA_DEQ_GLUT1},
+ {0x00C3, SIERRA_DEQ_GLUT2},
+ {0x0145, SIERRA_DEQ_GLUT3},
+ {0x0186, SIERRA_DEQ_GLUT4},
+ {0x09E7, SIERRA_DEQ_ALUT0},
+ {0x09A6, SIERRA_DEQ_ALUT1},
+ {0x0965, SIERRA_DEQ_ALUT2},
+ {0x08E3, SIERRA_DEQ_ALUT3},
+ {0x00FA, SIERRA_DEQ_DFETAP0},
+ {0x00FA, SIERRA_DEQ_DFETAP1},
+ {0x00FA, SIERRA_DEQ_DFETAP2},
+ {0x00FA, SIERRA_DEQ_DFETAP3},
+ {0x00FA, SIERRA_DEQ_DFETAP4},
+ {0x000F, SIERRA_DEQ_PRECUR_PREG},
+ {0x0280, SIERRA_DEQ_POSTCUR_PREG},
+ {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG},
+ {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
+ {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG},
+ {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG},
+ {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG},
+ {0x002B, SIERRA_CPI_TRIM_PREG},
+ {0x0003, SIERRA_EPI_CTRL_PREG},
+ {0x803F, SIERRA_SDFILT_H2L_A_PREG},
+ {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG},
+ {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG},
+ {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG}
+};
+
+static struct cdns_sierra_vals pcie_100_ext_ssc_plllc_cmn_vals = {
+ .reg_pairs = pcie_100_ext_ssc_plllc_cmn_regs,
+ .num_regs = ARRAY_SIZE(pcie_100_ext_ssc_plllc_cmn_regs),
+};
+
+static struct cdns_sierra_vals ml_pcie_100_ext_ssc_ln_vals = {
+ .reg_pairs = ml_pcie_100_ext_ssc_ln_regs,
+ .num_regs = ARRAY_SIZE(ml_pcie_100_ext_ssc_ln_regs),
+};
+
+/*
+ * TI J721E:
+ * refclk100MHz_32b_PCIe_ln_ext_ssc, multilink, using_plllc,
+ * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz
+ */
+static const struct cdns_reg_pairs ti_ml_pcie_100_ext_ssc_ln_regs[] = {
+ {0xFC08, SIERRA_DET_STANDEC_A_PREG},
+ {0x001D, SIERRA_PSM_A3IN_TMR_PREG},
+ {0x0004, SIERRA_PSC_LN_A3_PREG},
+ {0x0004, SIERRA_PSC_LN_A4_PREG},
+ {0x0004, SIERRA_PSC_LN_IDLE_PREG},
+ {0x1555, SIERRA_DFE_BIASTRIM_PREG},
+ {0x9703, SIERRA_DRVCTRL_BOOST_PREG},
+ {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG},
+ {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+ {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG},
+ {0x9800, SIERRA_RX_CTLE_CAL_PREG},
+ {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG},
+ {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG},
+ {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG},
+ {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
+ {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG},
+ {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG},
+ {0x0041, SIERRA_DEQ_GLUT0},
+ {0x0082, SIERRA_DEQ_GLUT1},
+ {0x00C3, SIERRA_DEQ_GLUT2},
+ {0x0145, SIERRA_DEQ_GLUT3},
+ {0x0186, SIERRA_DEQ_GLUT4},
+ {0x09E7, SIERRA_DEQ_ALUT0},
+ {0x09A6, SIERRA_DEQ_ALUT1},
+ {0x0965, SIERRA_DEQ_ALUT2},
+ {0x08E3, SIERRA_DEQ_ALUT3},
+ {0x00FA, SIERRA_DEQ_DFETAP0},
+ {0x00FA, SIERRA_DEQ_DFETAP1},
+ {0x00FA, SIERRA_DEQ_DFETAP2},
+ {0x00FA, SIERRA_DEQ_DFETAP3},
+ {0x00FA, SIERRA_DEQ_DFETAP4},
+ {0x000F, SIERRA_DEQ_PRECUR_PREG},
+ {0x0280, SIERRA_DEQ_POSTCUR_PREG},
+ {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG},
+ {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
+ {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG},
+ {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG},
+ {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG},
+ {0x002B, SIERRA_CPI_TRIM_PREG},
+ {0x0003, SIERRA_EPI_CTRL_PREG},
+ {0x803F, SIERRA_SDFILT_H2L_A_PREG},
+ {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG},
+ {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG},
+ {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG},
+ {0x0002, SIERRA_TX_RCVDET_OVRD_PREG}
+};
+
+static struct cdns_sierra_vals ti_ml_pcie_100_ext_ssc_ln_vals = {
+ .reg_pairs = ti_ml_pcie_100_ext_ssc_ln_regs,
+ .num_regs = ARRAY_SIZE(ti_ml_pcie_100_ext_ssc_ln_regs),
+};
+
+/* refclk100MHz_32b_PCIe_cmn_pll_no_ssc */
+static const struct cdns_reg_pairs cdns_pcie_cmn_regs_no_ssc[] = {
+ {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
+ {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
+ {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG},
+ {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG}
+};
+
+/* refclk100MHz_32b_PCIe_ln_no_ssc */
+static const struct cdns_reg_pairs cdns_pcie_ln_regs_no_ssc[] = {
+ {0xFC08, SIERRA_DET_STANDEC_A_PREG},
+ {0x001D, SIERRA_PSM_A3IN_TMR_PREG},
+ {0x1555, SIERRA_DFE_BIASTRIM_PREG},
+ {0x9703, SIERRA_DRVCTRL_BOOST_PREG},
+ {0x8055, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
+ {0x80BB, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
+ {0x8351, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
+ {0x8349, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+ {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG},
+ {0x9800, SIERRA_RX_CTLE_CAL_PREG},
+ {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG},
+ {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
+ {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG},
+ {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG},
+ {0x0041, SIERRA_DEQ_GLUT0},
+ {0x0082, SIERRA_DEQ_GLUT1},
+ {0x00C3, SIERRA_DEQ_GLUT2},
+ {0x0145, SIERRA_DEQ_GLUT3},
+ {0x0186, SIERRA_DEQ_GLUT4},
+ {0x09E7, SIERRA_DEQ_ALUT0},
+ {0x09A6, SIERRA_DEQ_ALUT1},
+ {0x0965, SIERRA_DEQ_ALUT2},
+ {0x08E3, SIERRA_DEQ_ALUT3},
+ {0x00FA, SIERRA_DEQ_DFETAP0},
+ {0x00FA, SIERRA_DEQ_DFETAP1},
+ {0x00FA, SIERRA_DEQ_DFETAP2},
+ {0x00FA, SIERRA_DEQ_DFETAP3},
+ {0x00FA, SIERRA_DEQ_DFETAP4},
+ {0x000F, SIERRA_DEQ_PRECUR_PREG},
+ {0x0280, SIERRA_DEQ_POSTCUR_PREG},
+ {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG},
+ {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
+ {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG},
+ {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG},
+ {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG},
+ {0x002B, SIERRA_CPI_TRIM_PREG},
+ {0x0003, SIERRA_EPI_CTRL_PREG},
+ {0x803F, SIERRA_SDFILT_H2L_A_PREG},
+ {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG},
+ {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG},
+ {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG}
+};
+
+static struct cdns_sierra_vals pcie_100_no_ssc_cmn_vals = {
+ .reg_pairs = cdns_pcie_cmn_regs_no_ssc,
+ .num_regs = ARRAY_SIZE(cdns_pcie_cmn_regs_no_ssc),
+};
+
+static struct cdns_sierra_vals pcie_100_no_ssc_ln_vals = {
+ .reg_pairs = cdns_pcie_ln_regs_no_ssc,
+ .num_regs = ARRAY_SIZE(cdns_pcie_ln_regs_no_ssc),
+};
+
+/* refclk100MHz_32b_PCIe_cmn_pll_int_ssc */
+static const struct cdns_reg_pairs cdns_pcie_cmn_regs_int_ssc[] = {
+ {0x000E, SIERRA_CMN_PLLLC_MODE_PREG},
+ {0x4006, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
+ {0x4006, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
+ {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG},
+ {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG},
+ {0x0581, SIERRA_CMN_PLLLC_DSMCORR_PREG},
+ {0x7F80, SIERRA_CMN_PLLLC_SS_PREG},
+ {0x0041, SIERRA_CMN_PLLLC_SS_AMP_STEP_SIZE_PREG},
+ {0x0464, SIERRA_CMN_PLLLC_SSTWOPT_PREG},
+ {0x0D0D, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG},
+ {0x0060, SIERRA_CMN_PLLLC_LOCK_DELAY_CTRL_PREG}
+};
+
+/* refclk100MHz_32b_PCIe_ln_int_ssc */
+static const struct cdns_reg_pairs cdns_pcie_ln_regs_int_ssc[] = {
+ {0xFC08, SIERRA_DET_STANDEC_A_PREG},
+ {0x001D, SIERRA_PSM_A3IN_TMR_PREG},
+ {0x1555, SIERRA_DFE_BIASTRIM_PREG},
+ {0x9703, SIERRA_DRVCTRL_BOOST_PREG},
+ {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG},
+ {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
+ {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+ {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG},
+ {0x9800, SIERRA_RX_CTLE_CAL_PREG},
+ {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG},
+ {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG},
+ {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG},
+ {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
+ {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG},
+ {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG},
+ {0x0041, SIERRA_DEQ_GLUT0},
+ {0x0082, SIERRA_DEQ_GLUT1},
+ {0x00C3, SIERRA_DEQ_GLUT2},
+ {0x0145, SIERRA_DEQ_GLUT3},
+ {0x0186, SIERRA_DEQ_GLUT4},
+ {0x09E7, SIERRA_DEQ_ALUT0},
+ {0x09A6, SIERRA_DEQ_ALUT1},
+ {0x0965, SIERRA_DEQ_ALUT2},
+ {0x08E3, SIERRA_DEQ_ALUT3},
+ {0x00FA, SIERRA_DEQ_DFETAP0},
+ {0x00FA, SIERRA_DEQ_DFETAP1},
+ {0x00FA, SIERRA_DEQ_DFETAP2},
+ {0x00FA, SIERRA_DEQ_DFETAP3},
+ {0x00FA, SIERRA_DEQ_DFETAP4},
+ {0x000F, SIERRA_DEQ_PRECUR_PREG},
+ {0x0280, SIERRA_DEQ_POSTCUR_PREG},
+ {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG},
+ {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
+ {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG},
+ {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG},
+ {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG},
+ {0x002B, SIERRA_CPI_TRIM_PREG},
+ {0x0003, SIERRA_EPI_CTRL_PREG},
+ {0x803F, SIERRA_SDFILT_H2L_A_PREG},
+ {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG},
+ {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG},
+ {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG}
+};
+
+static struct cdns_sierra_vals pcie_100_int_ssc_cmn_vals = {
+ .reg_pairs = cdns_pcie_cmn_regs_int_ssc,
+ .num_regs = ARRAY_SIZE(cdns_pcie_cmn_regs_int_ssc),
+};
+
+static struct cdns_sierra_vals pcie_100_int_ssc_ln_vals = {
+ .reg_pairs = cdns_pcie_ln_regs_int_ssc,
+ .num_regs = ARRAY_SIZE(cdns_pcie_ln_regs_int_ssc),
+};
+
/* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc */
-static struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = {
+static const struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = {
{0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
{0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
{0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG},
@@ -663,18 +2176,67 @@ static struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = {
};
/* refclk100MHz_32b_PCIe_ln_ext_ssc */
-static struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = {
+static const struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = {
+ {0xFC08, SIERRA_DET_STANDEC_A_PREG},
+ {0x001D, SIERRA_PSM_A3IN_TMR_PREG},
+ {0x1555, SIERRA_DFE_BIASTRIM_PREG},
+ {0x9703, SIERRA_DRVCTRL_BOOST_PREG},
{0x813E, SIERRA_CLKPATHCTRL_TMR_PREG},
{0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
{0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
{0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
{0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+ {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG},
+ {0x9800, SIERRA_RX_CTLE_CAL_PREG},
{0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG},
- {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}
+ {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG},
+ {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG},
+ {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
+ {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG},
+ {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG},
+ {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG},
+ {0x0041, SIERRA_DEQ_GLUT0},
+ {0x0082, SIERRA_DEQ_GLUT1},
+ {0x00C3, SIERRA_DEQ_GLUT2},
+ {0x0145, SIERRA_DEQ_GLUT3},
+ {0x0186, SIERRA_DEQ_GLUT4},
+ {0x09E7, SIERRA_DEQ_ALUT0},
+ {0x09A6, SIERRA_DEQ_ALUT1},
+ {0x0965, SIERRA_DEQ_ALUT2},
+ {0x08E3, SIERRA_DEQ_ALUT3},
+ {0x00FA, SIERRA_DEQ_DFETAP0},
+ {0x00FA, SIERRA_DEQ_DFETAP1},
+ {0x00FA, SIERRA_DEQ_DFETAP2},
+ {0x00FA, SIERRA_DEQ_DFETAP3},
+ {0x00FA, SIERRA_DEQ_DFETAP4},
+ {0x000F, SIERRA_DEQ_PRECUR_PREG},
+ {0x0280, SIERRA_DEQ_POSTCUR_PREG},
+ {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG},
+ {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
+ {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG},
+ {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG},
+ {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG},
+ {0x002B, SIERRA_CPI_TRIM_PREG},
+ {0x0003, SIERRA_EPI_CTRL_PREG},
+ {0x803F, SIERRA_SDFILT_H2L_A_PREG},
+ {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG},
+ {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG},
+ {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG}
+};
+
+static struct cdns_sierra_vals pcie_100_ext_ssc_cmn_vals = {
+ .reg_pairs = cdns_pcie_cmn_regs_ext_ssc,
+ .num_regs = ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc),
+};
+
+static struct cdns_sierra_vals pcie_100_ext_ssc_ln_vals = {
+ .reg_pairs = cdns_pcie_ln_regs_ext_ssc,
+ .num_regs = ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc),
};
/* refclk100MHz_20b_USB_cmn_pll_ext_ssc */
-static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = {
+static const struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = {
{0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
{0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
{0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG},
@@ -682,13 +2244,13 @@ static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = {
};
/* refclk100MHz_20b_USB_ln_ext_ssc */
-static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
+static const struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
{0xFE0A, SIERRA_DET_STANDEC_A_PREG},
{0x000F, SIERRA_DET_STANDEC_B_PREG},
- {0x00A5, SIERRA_DET_STANDEC_C_PREG},
+ {0x55A5, SIERRA_DET_STANDEC_C_PREG},
{0x69ad, SIERRA_DET_STANDEC_D_PREG},
{0x0241, SIERRA_DET_STANDEC_E_PREG},
- {0x0010, SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG},
+ {0x0110, SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG},
{0x0014, SIERRA_PSM_A0IN_TMR_PREG},
{0xCF00, SIERRA_PSM_DIAG_PREG},
{0x001F, SIERRA_PSC_TX_A0_PREG},
@@ -696,7 +2258,7 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
{0x0003, SIERRA_PSC_TX_A2_PREG},
{0x0003, SIERRA_PSC_TX_A3_PREG},
{0x0FFF, SIERRA_PSC_RX_A0_PREG},
- {0x0619, SIERRA_PSC_RX_A1_PREG},
+ {0x0003, SIERRA_PSC_RX_A1_PREG},
{0x0003, SIERRA_PSC_RX_A2_PREG},
{0x0001, SIERRA_PSC_RX_A3_PREG},
{0x0001, SIERRA_PLLCTRL_SUBRATE_PREG},
@@ -705,19 +2267,19 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
{0x00CA, SIERRA_CLKPATH_BIASTRIM_PREG},
{0x2512, SIERRA_DFE_BIASTRIM_PREG},
{0x0000, SIERRA_DRVCTRL_ATTEN_PREG},
- {0x873E, SIERRA_CLKPATHCTRL_TMR_PREG},
- {0x03CF, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
- {0x01CE, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+ {0x823E, SIERRA_CLKPATHCTRL_TMR_PREG},
+ {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
+ {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
{0x7B3C, SIERRA_CREQ_CCLKDET_MODE01_PREG},
- {0x033F, SIERRA_RX_CTLE_MAINTENANCE_PREG},
+ {0x023C, SIERRA_RX_CTLE_MAINTENANCE_PREG},
{0x3232, SIERRA_CREQ_FSMCLK_SEL_PREG},
{0x0000, SIERRA_CREQ_EQ_CTRL_PREG},
- {0x8000, SIERRA_CREQ_SPARE_PREG},
+ {0x0000, SIERRA_CREQ_SPARE_PREG},
{0xCC44, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG},
- {0x8453, SIERRA_CTLELUT_CTRL_PREG},
- {0x4110, SIERRA_DFE_ECMP_RATESEL_PREG},
- {0x4110, SIERRA_DFE_SMP_RATESEL_PREG},
- {0x0002, SIERRA_DEQ_PHALIGN_CTRL},
+ {0x8452, SIERRA_CTLELUT_CTRL_PREG},
+ {0x4121, SIERRA_DFE_ECMP_RATESEL_PREG},
+ {0x4121, SIERRA_DFE_SMP_RATESEL_PREG},
+ {0x0003, SIERRA_DEQ_PHALIGN_CTRL},
{0x3200, SIERRA_DEQ_CONCUR_CTRL1_PREG},
{0x5064, SIERRA_DEQ_CONCUR_CTRL2_PREG},
{0x0030, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
@@ -725,7 +2287,7 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
{0x5A5A, SIERRA_DEQ_ERRCMP_CTRL_PREG},
{0x02F5, SIERRA_DEQ_OFFSET_CTRL_PREG},
{0x02F5, SIERRA_DEQ_GAIN_CTRL_PREG},
- {0x9A8A, SIERRA_DEQ_VGATUNE_CTRL_PREG},
+ {0x9999, SIERRA_DEQ_VGATUNE_CTRL_PREG},
{0x0014, SIERRA_DEQ_GLUT0},
{0x0014, SIERRA_DEQ_GLUT1},
{0x0014, SIERRA_DEQ_GLUT2},
@@ -772,6 +2334,7 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
{0x000F, SIERRA_LFPSFILT_NS_PREG},
{0x0009, SIERRA_LFPSFILT_RD_PREG},
{0x0001, SIERRA_LFPSFILT_MP_PREG},
+ {0x6013, SIERRA_SIGDET_SUPPORT_PREG},
{0x8013, SIERRA_SDFILT_H2L_A_PREG},
{0x8009, SIERRA_SDFILT_L2H_PREG},
{0x0024, SIERRA_RXBUFFER_CTLECTRL_PREG},
@@ -779,32 +2342,167 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
{0x4243, SIERRA_RXBUFFER_DFECTRL_PREG}
};
+static struct cdns_sierra_vals usb_100_ext_ssc_cmn_vals = {
+ .reg_pairs = cdns_usb_cmn_regs_ext_ssc,
+ .num_regs = ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc),
+};
+
+static struct cdns_sierra_vals usb_100_ext_ssc_ln_vals = {
+ .reg_pairs = cdns_usb_ln_regs_ext_ssc,
+ .num_regs = ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc),
+};
+
static const struct cdns_sierra_data cdns_map_sierra = {
- SIERRA_MACRO_ID,
- 0x2,
- 0x2,
- ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc),
- ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc),
- ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc),
- ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc),
- cdns_pcie_cmn_regs_ext_ssc,
- cdns_pcie_ln_regs_ext_ssc,
- cdns_usb_cmn_regs_ext_ssc,
- cdns_usb_ln_regs_ext_ssc,
+ .id_value = SIERRA_MACRO_ID,
+ .block_offset_shift = 0x2,
+ .reg_offset_shift = 0x2,
+ .pcs_cmn_vals = {
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &pcie_phy_pcs_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals,
+ [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &pcie_phy_pcs_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals,
+ [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals,
+ },
+ },
+ },
+ .pma_cmn_vals = {
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_100_ext_ssc_cmn_vals,
+ [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &pcie_100_no_ssc_plllc_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_100_ext_ssc_plllc_cmn_vals,
+ [INTERNAL_SSC] = &pcie_100_int_ssc_plllc_cmn_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [EXTERNAL_SSC] = &usb_100_ext_ssc_cmn_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_PCIE] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals,
+ [INTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals,
+ },
+ },
+ },
+ .pma_ln_vals = {
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &pcie_100_no_ssc_ln_vals,
+ [EXTERNAL_SSC] = &pcie_100_ext_ssc_ln_vals,
+ [INTERNAL_SSC] = &pcie_100_int_ssc_ln_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &ml_pcie_100_no_ssc_ln_vals,
+ [EXTERNAL_SSC] = &ml_pcie_100_ext_ssc_ln_vals,
+ [INTERNAL_SSC] = &ml_pcie_100_int_ssc_ln_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [EXTERNAL_SSC] = &usb_100_ext_ssc_ln_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_PCIE] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals,
+ [INTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals,
+ },
+ },
+ },
};
static const struct cdns_sierra_data cdns_ti_map_sierra = {
- SIERRA_MACRO_ID,
- 0x0,
- 0x1,
- ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc),
- ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc),
- ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc),
- ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc),
- cdns_pcie_cmn_regs_ext_ssc,
- cdns_pcie_ln_regs_ext_ssc,
- cdns_usb_cmn_regs_ext_ssc,
- cdns_usb_ln_regs_ext_ssc,
+ .id_value = SIERRA_MACRO_ID,
+ .block_offset_shift = 0x0,
+ .reg_offset_shift = 0x1,
+ .pcs_cmn_vals = {
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &pcie_phy_pcs_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals,
+ [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &pcie_phy_pcs_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals,
+ [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals,
+ },
+ },
+ },
+ .phy_pma_ln_vals = {
+ [TYPE_QSGMII] = {
+ [TYPE_PCIE] = {
+ [NO_SSC] = &qsgmii_phy_pma_ln_vals,
+ [EXTERNAL_SSC] = &qsgmii_phy_pma_ln_vals,
+ [INTERNAL_SSC] = &qsgmii_phy_pma_ln_vals,
+ },
+ },
+ },
+ .pma_cmn_vals = {
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_100_ext_ssc_cmn_vals,
+ [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &pcie_100_no_ssc_plllc_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_100_ext_ssc_plllc_cmn_vals,
+ [INTERNAL_SSC] = &pcie_100_int_ssc_plllc_cmn_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [EXTERNAL_SSC] = &usb_100_ext_ssc_cmn_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_PCIE] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals,
+ [INTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals,
+ },
+ },
+ },
+ .pma_ln_vals = {
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &pcie_100_no_ssc_ln_vals,
+ [EXTERNAL_SSC] = &pcie_100_ext_ssc_ln_vals,
+ [INTERNAL_SSC] = &pcie_100_int_ssc_ln_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &ti_ml_pcie_100_no_ssc_ln_vals,
+ [EXTERNAL_SSC] = &ti_ml_pcie_100_ext_ssc_ln_vals,
+ [INTERNAL_SSC] = &ti_ml_pcie_100_int_ssc_ln_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [EXTERNAL_SSC] = &usb_100_ext_ssc_ln_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_PCIE] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals,
+ [INTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals,
+ },
+ },
+ },
};
static const struct of_device_id cdns_sierra_id_table[] = {
diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c
new file mode 100644
index 000000000000..f099053c583c
--- /dev/null
+++ b/drivers/phy/cadence/phy-cadence-torrent.c
@@ -0,0 +1,4721 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Cadence Torrent SD0801 PHY driver.
+ *
+ * Copyright 2018 Cadence Design Systems, Inc.
+ *
+ */
+
+#include <dt-bindings/phy/phy.h>
+#include <dt-bindings/phy/phy-cadence.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+
+#define REF_CLK_19_2MHZ 19200000
+#define REF_CLK_25MHZ 25000000
+#define REF_CLK_100MHZ 100000000
+
+#define MAX_NUM_LANES 4
+#define DEFAULT_MAX_BIT_RATE 8100 /* in Mbps */
+
+#define NUM_SSC_MODE 3
+#define NUM_REF_CLK 3
+#define NUM_PHY_TYPE 6
+
+#define POLL_TIMEOUT_US 5000
+#define PLL_LOCK_TIMEOUT 100000
+
+#define TORRENT_COMMON_CDB_OFFSET 0x0
+
+#define TORRENT_TX_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
+ ((0x4000 << (block_offset)) + \
+ (((ln) << 9) << (reg_offset)))
+
+#define TORRENT_RX_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
+ ((0x8000 << (block_offset)) + \
+ (((ln) << 9) << (reg_offset)))
+
+#define TORRENT_PHY_PCS_COMMON_OFFSET(block_offset) \
+ (0xC000 << (block_offset))
+
+#define TORRENT_PHY_PCS_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
+ ((0xD000 << (block_offset)) + \
+ (((ln) << 8) << (reg_offset)))
+
+#define TORRENT_PHY_PMA_COMMON_OFFSET(block_offset) \
+ (0xE000 << (block_offset))
+
+#define TORRENT_DPTX_PHY_OFFSET 0x0
+
+/*
+ * register offsets from DPTX PHY register block base (i.e MHDP
+ * register base + 0x30a00)
+ */
+#define PHY_AUX_CTRL 0x04
+#define PHY_RESET 0x20
+#define PMA_TX_ELEC_IDLE_MASK 0xF0U
+#define PMA_TX_ELEC_IDLE_SHIFT 4
+#define PHY_L00_RESET_N_MASK 0x01U
+#define PHY_PMA_XCVR_PLLCLK_EN 0x24
+#define PHY_PMA_XCVR_PLLCLK_EN_ACK 0x28
+#define PHY_PMA_XCVR_POWER_STATE_REQ 0x2c
+#define PHY_POWER_STATE_LN_0 0x0000
+#define PHY_POWER_STATE_LN_1 0x0008
+#define PHY_POWER_STATE_LN_2 0x0010
+#define PHY_POWER_STATE_LN_3 0x0018
+#define PMA_XCVR_POWER_STATE_REQ_LN_MASK 0x3FU
+#define PHY_PMA_XCVR_POWER_STATE_ACK 0x30
+#define PHY_PMA_CMN_READY 0x34
+
+/*
+ * register offsets from SD0801 PHY register block base (i.e MHDP
+ * register base + 0x500000)
+ */
+#define CMN_SSM_BANDGAP_TMR 0x0021U
+#define CMN_SSM_BIAS_TMR 0x0022U
+#define CMN_PLLSM0_PLLPRE_TMR 0x002AU
+#define CMN_PLLSM0_PLLLOCK_TMR 0x002CU
+#define CMN_PLLSM1_PLLPRE_TMR 0x0032U
+#define CMN_PLLSM1_PLLLOCK_TMR 0x0034U
+#define CMN_CDIAG_CDB_PWRI_OVRD 0x0041U
+#define CMN_CDIAG_XCVRC_PWRI_OVRD 0x0047U
+#define CMN_CDIAG_REFCLK_OVRD 0x004CU
+#define CMN_CDIAG_REFCLK_DRV0_CTRL 0x0050U
+#define CMN_BGCAL_INIT_TMR 0x0064U
+#define CMN_BGCAL_ITER_TMR 0x0065U
+#define CMN_IBCAL_INIT_TMR 0x0074U
+#define CMN_PLL0_VCOCAL_TCTRL 0x0082U
+#define CMN_PLL0_VCOCAL_INIT_TMR 0x0084U
+#define CMN_PLL0_VCOCAL_ITER_TMR 0x0085U
+#define CMN_PLL0_VCOCAL_REFTIM_START 0x0086U
+#define CMN_PLL0_VCOCAL_PLLCNT_START 0x0088U
+#define CMN_PLL0_INTDIV_M0 0x0090U
+#define CMN_PLL0_FRACDIVL_M0 0x0091U
+#define CMN_PLL0_FRACDIVH_M0 0x0092U
+#define CMN_PLL0_HIGH_THR_M0 0x0093U
+#define CMN_PLL0_DSM_DIAG_M0 0x0094U
+#define CMN_PLL0_DSM_FBH_OVRD_M0 0x0095U
+#define CMN_PLL0_SS_CTRL1_M0 0x0098U
+#define CMN_PLL0_SS_CTRL2_M0 0x0099U
+#define CMN_PLL0_SS_CTRL3_M0 0x009AU
+#define CMN_PLL0_SS_CTRL4_M0 0x009BU
+#define CMN_PLL0_LOCK_REFCNT_START 0x009CU
+#define CMN_PLL0_LOCK_PLLCNT_START 0x009EU
+#define CMN_PLL0_LOCK_PLLCNT_THR 0x009FU
+#define CMN_PLL0_INTDIV_M1 0x00A0U
+#define CMN_PLL0_FRACDIVH_M1 0x00A2U
+#define CMN_PLL0_HIGH_THR_M1 0x00A3U
+#define CMN_PLL0_DSM_DIAG_M1 0x00A4U
+#define CMN_PLL0_SS_CTRL1_M1 0x00A8U
+#define CMN_PLL0_SS_CTRL2_M1 0x00A9U
+#define CMN_PLL0_SS_CTRL3_M1 0x00AAU
+#define CMN_PLL0_SS_CTRL4_M1 0x00ABU
+#define CMN_PLL1_VCOCAL_TCTRL 0x00C2U
+#define CMN_PLL1_VCOCAL_INIT_TMR 0x00C4U
+#define CMN_PLL1_VCOCAL_ITER_TMR 0x00C5U
+#define CMN_PLL1_VCOCAL_REFTIM_START 0x00C6U
+#define CMN_PLL1_VCOCAL_PLLCNT_START 0x00C8U
+#define CMN_PLL1_INTDIV_M0 0x00D0U
+#define CMN_PLL1_FRACDIVL_M0 0x00D1U
+#define CMN_PLL1_FRACDIVH_M0 0x00D2U
+#define CMN_PLL1_HIGH_THR_M0 0x00D3U
+#define CMN_PLL1_DSM_DIAG_M0 0x00D4U
+#define CMN_PLL1_DSM_FBH_OVRD_M0 0x00D5U
+#define CMN_PLL1_DSM_FBL_OVRD_M0 0x00D6U
+#define CMN_PLL1_SS_CTRL1_M0 0x00D8U
+#define CMN_PLL1_SS_CTRL2_M0 0x00D9U
+#define CMN_PLL1_SS_CTRL3_M0 0x00DAU
+#define CMN_PLL1_SS_CTRL4_M0 0x00DBU
+#define CMN_PLL1_LOCK_REFCNT_START 0x00DCU
+#define CMN_PLL1_LOCK_PLLCNT_START 0x00DEU
+#define CMN_PLL1_LOCK_PLLCNT_THR 0x00DFU
+#define CMN_TXPUCAL_TUNE 0x0103U
+#define CMN_TXPUCAL_INIT_TMR 0x0104U
+#define CMN_TXPUCAL_ITER_TMR 0x0105U
+#define CMN_TXPDCAL_TUNE 0x010BU
+#define CMN_TXPDCAL_INIT_TMR 0x010CU
+#define CMN_TXPDCAL_ITER_TMR 0x010DU
+#define CMN_RXCAL_INIT_TMR 0x0114U
+#define CMN_RXCAL_ITER_TMR 0x0115U
+#define CMN_SD_CAL_INIT_TMR 0x0124U
+#define CMN_SD_CAL_ITER_TMR 0x0125U
+#define CMN_SD_CAL_REFTIM_START 0x0126U
+#define CMN_SD_CAL_PLLCNT_START 0x0128U
+#define CMN_PDIAG_PLL0_CTRL_M0 0x01A0U
+#define CMN_PDIAG_PLL0_CLK_SEL_M0 0x01A1U
+#define CMN_PDIAG_PLL0_CP_PADJ_M0 0x01A4U
+#define CMN_PDIAG_PLL0_CP_IADJ_M0 0x01A5U
+#define CMN_PDIAG_PLL0_FILT_PADJ_M0 0x01A6U
+#define CMN_PDIAG_PLL0_CTRL_M1 0x01B0U
+#define CMN_PDIAG_PLL0_CLK_SEL_M1 0x01B1U
+#define CMN_PDIAG_PLL0_CP_PADJ_M1 0x01B4U
+#define CMN_PDIAG_PLL0_CP_IADJ_M1 0x01B5U
+#define CMN_PDIAG_PLL0_FILT_PADJ_M1 0x01B6U
+#define CMN_PDIAG_PLL1_CTRL_M0 0x01C0U
+#define CMN_PDIAG_PLL1_CLK_SEL_M0 0x01C1U
+#define CMN_PDIAG_PLL1_CP_PADJ_M0 0x01C4U
+#define CMN_PDIAG_PLL1_CP_IADJ_M0 0x01C5U
+#define CMN_PDIAG_PLL1_FILT_PADJ_M0 0x01C6U
+#define CMN_DIAG_BIAS_OVRD1 0x01E1U
+
+/* PMA TX Lane registers */
+#define TX_TXCC_CTRL 0x0040U
+#define TX_TXCC_CPOST_MULT_00 0x004CU
+#define TX_TXCC_CPOST_MULT_01 0x004DU
+#define TX_TXCC_MGNFS_MULT_000 0x0050U
+#define TX_TXCC_MGNFS_MULT_100 0x0054U
+#define DRV_DIAG_TX_DRV 0x00C6U
+#define XCVR_DIAG_PLLDRC_CTRL 0x00E5U
+#define XCVR_DIAG_HSCLK_SEL 0x00E6U
+#define XCVR_DIAG_HSCLK_DIV 0x00E7U
+#define XCVR_DIAG_RXCLK_CTRL 0x00E9U
+#define XCVR_DIAG_BIDI_CTRL 0x00EAU
+#define XCVR_DIAG_PSC_OVRD 0x00EBU
+#define TX_PSC_A0 0x0100U
+#define TX_PSC_A1 0x0101U
+#define TX_PSC_A2 0x0102U
+#define TX_PSC_A3 0x0103U
+#define TX_RCVDET_ST_TMR 0x0123U
+#define TX_DIAG_ACYA 0x01E7U
+#define TX_DIAG_ACYA_HBDC_MASK 0x0001U
+
+/* PMA RX Lane registers */
+#define RX_PSC_A0 0x0000U
+#define RX_PSC_A1 0x0001U
+#define RX_PSC_A2 0x0002U
+#define RX_PSC_A3 0x0003U
+#define RX_PSC_CAL 0x0006U
+#define RX_CDRLF_CNFG 0x0080U
+#define RX_CDRLF_CNFG3 0x0082U
+#define RX_SIGDET_HL_FILT_TMR 0x0090U
+#define RX_REE_GCSM1_CTRL 0x0108U
+#define RX_REE_GCSM1_EQENM_PH1 0x0109U
+#define RX_REE_GCSM1_EQENM_PH2 0x010AU
+#define RX_REE_GCSM2_CTRL 0x0110U
+#define RX_REE_PERGCSM_CTRL 0x0118U
+#define RX_REE_ATTEN_THR 0x0149U
+#define RX_REE_TAP1_CLIP 0x0171U
+#define RX_REE_TAP2TON_CLIP 0x0172U
+#define RX_REE_SMGM_CTRL1 0x0177U
+#define RX_REE_SMGM_CTRL2 0x0178U
+#define RX_DIAG_DFE_CTRL 0x01E0U
+#define RX_DIAG_DFE_AMP_TUNE_2 0x01E2U
+#define RX_DIAG_DFE_AMP_TUNE_3 0x01E3U
+#define RX_DIAG_NQST_CTRL 0x01E5U
+#define RX_DIAG_SIGDET_TUNE 0x01E8U
+#define RX_DIAG_PI_RATE 0x01F4U
+#define RX_DIAG_PI_CAP 0x01F5U
+#define RX_DIAG_ACYA 0x01FFU
+
+/* PHY PCS common registers */
+#define PHY_PIPE_CMN_CTRL1 0x0000U
+#define PHY_PLL_CFG 0x000EU
+#define PHY_PIPE_USB3_GEN2_PRE_CFG0 0x0020U
+#define PHY_PIPE_USB3_GEN2_POST_CFG0 0x0022U
+#define PHY_PIPE_USB3_GEN2_POST_CFG1 0x0023U
+
+/* PHY PCS lane registers */
+#define PHY_PCS_ISO_LINK_CTRL 0x000BU
+
+/* PHY PMA common registers */
+#define PHY_PMA_CMN_CTRL1 0x0000U
+#define PHY_PMA_CMN_CTRL2 0x0001U
+#define PHY_PMA_PLL_RAW_CTRL 0x0003U
+
+#define CDNS_TORRENT_OUTPUT_CLOCKS 3
+
+static const char * const clk_names[] = {
+ [CDNS_TORRENT_REFCLK_DRIVER] = "refclk-driver",
+ [CDNS_TORRENT_DERIVED_REFCLK] = "refclk-der",
+ [CDNS_TORRENT_RECEIVED_REFCLK] = "refclk-rec",
+};
+
+static const struct reg_field phy_pll_cfg =
+ REG_FIELD(PHY_PLL_CFG, 0, 1);
+
+static const struct reg_field phy_pma_cmn_ctrl_1 =
+ REG_FIELD(PHY_PMA_CMN_CTRL1, 0, 0);
+
+static const struct reg_field phy_pma_cmn_ctrl_2 =
+ REG_FIELD(PHY_PMA_CMN_CTRL2, 0, 7);
+
+static const struct reg_field phy_pma_pll_raw_ctrl =
+ REG_FIELD(PHY_PMA_PLL_RAW_CTRL, 0, 1);
+
+static const struct reg_field phy_reset_ctrl =
+ REG_FIELD(PHY_RESET, 8, 8);
+
+static const struct reg_field phy_pcs_iso_link_ctrl_1 =
+ REG_FIELD(PHY_PCS_ISO_LINK_CTRL, 1, 1);
+
+static const struct reg_field phy_pipe_cmn_ctrl1_0 = REG_FIELD(PHY_PIPE_CMN_CTRL1, 0, 0);
+
+static const struct reg_field cmn_cdiag_refclk_ovrd_4 =
+ REG_FIELD(CMN_CDIAG_REFCLK_OVRD, 4, 4);
+
+#define REFCLK_OUT_NUM_CMN_CONFIG 4
+
+enum cdns_torrent_refclk_out_cmn {
+ CMN_CDIAG_REFCLK_DRV0_CTRL_1,
+ CMN_CDIAG_REFCLK_DRV0_CTRL_4,
+ CMN_CDIAG_REFCLK_DRV0_CTRL_5,
+ CMN_CDIAG_REFCLK_DRV0_CTRL_6,
+};
+
+static const struct reg_field refclk_out_cmn_cfg[] = {
+ [CMN_CDIAG_REFCLK_DRV0_CTRL_1] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 1, 1),
+ [CMN_CDIAG_REFCLK_DRV0_CTRL_4] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 4, 4),
+ [CMN_CDIAG_REFCLK_DRV0_CTRL_5] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 5, 5),
+ [CMN_CDIAG_REFCLK_DRV0_CTRL_6] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 6, 6),
+};
+
+static const int refclk_driver_parent_index[] = {
+ CDNS_TORRENT_DERIVED_REFCLK,
+ CDNS_TORRENT_RECEIVED_REFCLK
+};
+
+static u32 cdns_torrent_refclk_driver_mux_table[] = { 1, 0 };
+
+enum cdns_torrent_phy_type {
+ TYPE_NONE,
+ TYPE_DP,
+ TYPE_PCIE,
+ TYPE_SGMII,
+ TYPE_QSGMII,
+ TYPE_USB,
+};
+
+enum cdns_torrent_ref_clk {
+ CLK_19_2_MHZ,
+ CLK_25_MHZ,
+ CLK_100_MHZ
+};
+
+enum cdns_torrent_ssc_mode {
+ NO_SSC,
+ EXTERNAL_SSC,
+ INTERNAL_SSC
+};
+
+struct cdns_torrent_inst {
+ struct phy *phy;
+ u32 mlane;
+ enum cdns_torrent_phy_type phy_type;
+ u32 num_lanes;
+ struct reset_control *lnk_rst;
+ enum cdns_torrent_ssc_mode ssc_mode;
+};
+
+struct cdns_torrent_phy {
+ void __iomem *base; /* DPTX registers base */
+ void __iomem *sd_base; /* SD0801 registers base */
+ u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */
+ struct reset_control *phy_rst;
+ struct reset_control *apb_rst;
+ struct device *dev;
+ struct clk *clk;
+ enum cdns_torrent_ref_clk ref_clk_rate;
+ struct cdns_torrent_inst phys[MAX_NUM_LANES];
+ int nsubnodes;
+ const struct cdns_torrent_data *init_data;
+ struct regmap *regmap_common_cdb;
+ struct regmap *regmap_phy_pcs_common_cdb;
+ struct regmap *regmap_phy_pma_common_cdb;
+ struct regmap *regmap_tx_lane_cdb[MAX_NUM_LANES];
+ struct regmap *regmap_rx_lane_cdb[MAX_NUM_LANES];
+ struct regmap *regmap_phy_pcs_lane_cdb[MAX_NUM_LANES];
+ struct regmap *regmap_dptx_phy_reg;
+ struct regmap_field *phy_pll_cfg;
+ struct regmap_field *phy_pipe_cmn_ctrl1_0;
+ struct regmap_field *cmn_cdiag_refclk_ovrd_4;
+ struct regmap_field *phy_pma_cmn_ctrl_1;
+ struct regmap_field *phy_pma_cmn_ctrl_2;
+ struct regmap_field *phy_pma_pll_raw_ctrl;
+ struct regmap_field *phy_reset_ctrl;
+ struct regmap_field *phy_pcs_iso_link_ctrl_1[MAX_NUM_LANES];
+ struct clk_hw_onecell_data *clk_hw_data;
+};
+
+enum phy_powerstate {
+ POWERSTATE_A0 = 0,
+ /* Powerstate A1 is unused */
+ POWERSTATE_A2 = 2,
+ POWERSTATE_A3 = 3,
+};
+
+struct cdns_torrent_refclk_driver {
+ struct clk_hw hw;
+ struct regmap_field *cmn_fields[REFCLK_OUT_NUM_CMN_CONFIG];
+ struct clk_init_data clk_data;
+};
+
+#define to_cdns_torrent_refclk_driver(_hw) \
+ container_of(_hw, struct cdns_torrent_refclk_driver, hw)
+
+struct cdns_torrent_derived_refclk {
+ struct clk_hw hw;
+ struct regmap_field *phy_pipe_cmn_ctrl1_0;
+ struct regmap_field *cmn_cdiag_refclk_ovrd_4;
+ struct clk_init_data clk_data;
+};
+
+#define to_cdns_torrent_derived_refclk(_hw) \
+ container_of(_hw, struct cdns_torrent_derived_refclk, hw)
+
+struct cdns_torrent_received_refclk {
+ struct clk_hw hw;
+ struct regmap_field *phy_pipe_cmn_ctrl1_0;
+ struct regmap_field *cmn_cdiag_refclk_ovrd_4;
+ struct clk_init_data clk_data;
+};
+
+#define to_cdns_torrent_received_refclk(_hw) \
+ container_of(_hw, struct cdns_torrent_received_refclk, hw)
+
+struct cdns_reg_pairs {
+ u32 val;
+ u32 off;
+};
+
+struct cdns_torrent_vals {
+ struct cdns_reg_pairs *reg_pairs;
+ u32 num_regs;
+};
+
+struct cdns_torrent_data {
+ u8 block_offset_shift;
+ u8 reg_offset_shift;
+ struct cdns_torrent_vals *link_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
+ [NUM_SSC_MODE];
+ struct cdns_torrent_vals *xcvr_diag_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
+ [NUM_SSC_MODE];
+ struct cdns_torrent_vals *pcs_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
+ [NUM_SSC_MODE];
+ struct cdns_torrent_vals *cmn_vals[NUM_REF_CLK][NUM_PHY_TYPE]
+ [NUM_PHY_TYPE][NUM_SSC_MODE];
+ struct cdns_torrent_vals *tx_ln_vals[NUM_REF_CLK][NUM_PHY_TYPE]
+ [NUM_PHY_TYPE][NUM_SSC_MODE];
+ struct cdns_torrent_vals *rx_ln_vals[NUM_REF_CLK][NUM_PHY_TYPE]
+ [NUM_PHY_TYPE][NUM_SSC_MODE];
+};
+
+struct cdns_regmap_cdb_context {
+ struct device *dev;
+ void __iomem *base;
+ u8 reg_offset_shift;
+};
+
+static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct cdns_regmap_cdb_context *ctx = context;
+ u32 offset = reg << ctx->reg_offset_shift;
+
+ writew(val, ctx->base + offset);
+
+ return 0;
+}
+
+static int cdns_regmap_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct cdns_regmap_cdb_context *ctx = context;
+ u32 offset = reg << ctx->reg_offset_shift;
+
+ *val = readw(ctx->base + offset);
+ return 0;
+}
+
+static int cdns_regmap_dptx_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct cdns_regmap_cdb_context *ctx = context;
+ u32 offset = reg;
+
+ writel(val, ctx->base + offset);
+
+ return 0;
+}
+
+static int cdns_regmap_dptx_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct cdns_regmap_cdb_context *ctx = context;
+ u32 offset = reg;
+
+ *val = readl(ctx->base + offset);
+ return 0;
+}
+
+#define TORRENT_TX_LANE_CDB_REGMAP_CONF(n) \
+{ \
+ .name = "torrent_tx_lane" n "_cdb", \
+ .reg_stride = 1, \
+ .fast_io = true, \
+ .reg_write = cdns_regmap_write, \
+ .reg_read = cdns_regmap_read, \
+}
+
+#define TORRENT_RX_LANE_CDB_REGMAP_CONF(n) \
+{ \
+ .name = "torrent_rx_lane" n "_cdb", \
+ .reg_stride = 1, \
+ .fast_io = true, \
+ .reg_write = cdns_regmap_write, \
+ .reg_read = cdns_regmap_read, \
+}
+
+static const struct regmap_config cdns_torrent_tx_lane_cdb_config[] = {
+ TORRENT_TX_LANE_CDB_REGMAP_CONF("0"),
+ TORRENT_TX_LANE_CDB_REGMAP_CONF("1"),
+ TORRENT_TX_LANE_CDB_REGMAP_CONF("2"),
+ TORRENT_TX_LANE_CDB_REGMAP_CONF("3"),
+};
+
+static const struct regmap_config cdns_torrent_rx_lane_cdb_config[] = {
+ TORRENT_RX_LANE_CDB_REGMAP_CONF("0"),
+ TORRENT_RX_LANE_CDB_REGMAP_CONF("1"),
+ TORRENT_RX_LANE_CDB_REGMAP_CONF("2"),
+ TORRENT_RX_LANE_CDB_REGMAP_CONF("3"),
+};
+
+static const struct regmap_config cdns_torrent_common_cdb_config = {
+ .name = "torrent_common_cdb",
+ .reg_stride = 1,
+ .fast_io = true,
+ .reg_write = cdns_regmap_write,
+ .reg_read = cdns_regmap_read,
+};
+
+#define TORRENT_PHY_PCS_LANE_CDB_REGMAP_CONF(n) \
+{ \
+ .name = "torrent_phy_pcs_lane" n "_cdb", \
+ .reg_stride = 1, \
+ .fast_io = true, \
+ .reg_write = cdns_regmap_write, \
+ .reg_read = cdns_regmap_read, \
+}
+
+static const struct regmap_config cdns_torrent_phy_pcs_lane_cdb_config[] = {
+ TORRENT_PHY_PCS_LANE_CDB_REGMAP_CONF("0"),
+ TORRENT_PHY_PCS_LANE_CDB_REGMAP_CONF("1"),
+ TORRENT_PHY_PCS_LANE_CDB_REGMAP_CONF("2"),
+ TORRENT_PHY_PCS_LANE_CDB_REGMAP_CONF("3"),
+};
+
+static const struct regmap_config cdns_torrent_phy_pcs_cmn_cdb_config = {
+ .name = "torrent_phy_pcs_cmn_cdb",
+ .reg_stride = 1,
+ .fast_io = true,
+ .reg_write = cdns_regmap_write,
+ .reg_read = cdns_regmap_read,
+};
+
+static const struct regmap_config cdns_torrent_phy_pma_cmn_cdb_config = {
+ .name = "torrent_phy_pma_cmn_cdb",
+ .reg_stride = 1,
+ .fast_io = true,
+ .reg_write = cdns_regmap_write,
+ .reg_read = cdns_regmap_read,
+};
+
+static const struct regmap_config cdns_torrent_dptx_phy_config = {
+ .name = "torrent_dptx_phy",
+ .reg_stride = 1,
+ .fast_io = true,
+ .reg_write = cdns_regmap_dptx_write,
+ .reg_read = cdns_regmap_dptx_read,
+};
+
+/* PHY mmr access functions */
+
+static void cdns_torrent_phy_write(struct regmap *regmap, u32 offset, u32 val)
+{
+ regmap_write(regmap, offset, val);
+}
+
+static u32 cdns_torrent_phy_read(struct regmap *regmap, u32 offset)
+{
+ unsigned int val;
+
+ regmap_read(regmap, offset, &val);
+ return val;
+}
+
+/* DPTX mmr access functions */
+
+static void cdns_torrent_dp_write(struct regmap *regmap, u32 offset, u32 val)
+{
+ regmap_write(regmap, offset, val);
+}
+
+static u32 cdns_torrent_dp_read(struct regmap *regmap, u32 offset)
+{
+ u32 val;
+
+ regmap_read(regmap, offset, &val);
+ return val;
+}
+
+/*
+ * Structure used to store values of PHY registers for voltage-related
+ * coefficients, for particular voltage swing and pre-emphasis level. Values
+ * are shared across all physical lanes.
+ */
+struct coefficients {
+ /* Value of DRV_DIAG_TX_DRV register to use */
+ u16 diag_tx_drv;
+ /* Value of TX_TXCC_MGNFS_MULT_000 register to use */
+ u16 mgnfs_mult;
+ /* Value of TX_TXCC_CPOST_MULT_00 register to use */
+ u16 cpost_mult;
+};
+
+/*
+ * Array consists of values of voltage-related registers for sd0801 PHY. A value
+ * of 0xFFFF is a placeholder for invalid combination, and will never be used.
+ */
+static const struct coefficients vltg_coeff[4][4] = {
+ /* voltage swing 0, pre-emphasis 0->3 */
+ { {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x002A,
+ .cpost_mult = 0x0000},
+ {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x001F,
+ .cpost_mult = 0x0014},
+ {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0012,
+ .cpost_mult = 0x0020},
+ {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000,
+ .cpost_mult = 0x002A}
+ },
+
+ /* voltage swing 1, pre-emphasis 0->3 */
+ { {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x001F,
+ .cpost_mult = 0x0000},
+ {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0013,
+ .cpost_mult = 0x0012},
+ {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000,
+ .cpost_mult = 0x001F},
+ {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+ .cpost_mult = 0xFFFF}
+ },
+
+ /* voltage swing 2, pre-emphasis 0->3 */
+ { {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0013,
+ .cpost_mult = 0x0000},
+ {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000,
+ .cpost_mult = 0x0013},
+ {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+ .cpost_mult = 0xFFFF},
+ {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+ .cpost_mult = 0xFFFF}
+ },
+
+ /* voltage swing 3, pre-emphasis 0->3 */
+ { {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000,
+ .cpost_mult = 0x0000},
+ {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+ .cpost_mult = 0xFFFF},
+ {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+ .cpost_mult = 0xFFFF},
+ {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+ .cpost_mult = 0xFFFF}
+ }
+};
+
+static const char *cdns_torrent_get_phy_type(enum cdns_torrent_phy_type phy_type)
+{
+ switch (phy_type) {
+ case TYPE_DP:
+ return "DisplayPort";
+ case TYPE_PCIE:
+ return "PCIe";
+ case TYPE_SGMII:
+ return "SGMII";
+ case TYPE_QSGMII:
+ return "QSGMII";
+ case TYPE_USB:
+ return "USB";
+ default:
+ return "None";
+ }
+}
+
+/*
+ * Set registers responsible for enabling and configuring SSC, with second and
+ * third register values provided by parameters.
+ */
+static
+void cdns_torrent_dp_enable_ssc_19_2mhz(struct cdns_torrent_phy *cdns_phy,
+ u32 ctrl2_val, u32 ctrl3_val)
+{
+ struct regmap *regmap = cdns_phy->regmap_common_cdb;
+
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x0001);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, ctrl2_val);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, ctrl3_val);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL4_M0, 0x0003);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x0001);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, ctrl2_val);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, ctrl3_val);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL4_M0, 0x0003);
+}
+
+static
+void cdns_torrent_dp_pma_cmn_vco_cfg_19_2mhz(struct cdns_torrent_phy *cdns_phy,
+ u32 rate, bool ssc)
+{
+ struct regmap *regmap = cdns_phy->regmap_common_cdb;
+
+ /* Assumes 19.2 MHz refclock */
+ switch (rate) {
+ /* Setting VCO for 10.8GHz */
+ case 2700:
+ case 5400:
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0119);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x4000);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x00BC);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0012);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0119);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x4000);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x00BC);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0012);
+ if (ssc)
+ cdns_torrent_dp_enable_ssc_19_2mhz(cdns_phy, 0x033A, 0x006A);
+ break;
+ /* Setting VCO for 9.72GHz */
+ case 1620:
+ case 2430:
+ case 3240:
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x01FA);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x4000);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0152);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x01FA);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x4000);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0152);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+ if (ssc)
+ cdns_torrent_dp_enable_ssc_19_2mhz(cdns_phy, 0x05DD, 0x0069);
+ break;
+ /* Setting VCO for 8.64GHz */
+ case 2160:
+ case 4320:
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x01C2);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x012C);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x01C2);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x012C);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+ if (ssc)
+ cdns_torrent_dp_enable_ssc_19_2mhz(cdns_phy, 0x0536, 0x0069);
+ break;
+ /* Setting VCO for 8.1GHz */
+ case 8100:
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x01A5);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0xE000);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x011A);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x01A5);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0xE000);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x011A);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+ if (ssc)
+ cdns_torrent_dp_enable_ssc_19_2mhz(cdns_phy, 0x04D7, 0x006A);
+ break;
+ }
+
+ if (ssc) {
+ cdns_torrent_phy_write(regmap, CMN_PLL0_VCOCAL_PLLCNT_START, 0x025E);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_PLLCNT_THR, 0x0005);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_VCOCAL_PLLCNT_START, 0x025E);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_PLLCNT_THR, 0x0005);
+ } else {
+ cdns_torrent_phy_write(regmap, CMN_PLL0_VCOCAL_PLLCNT_START, 0x0260);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_VCOCAL_PLLCNT_START, 0x0260);
+ /* Set reset register values to disable SSC */
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL2_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL3_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL4_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_PLLCNT_THR, 0x0003);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL2_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL3_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL4_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_PLLCNT_THR, 0x0003);
+ }
+
+ cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_REFCNT_START, 0x0099);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_PLLCNT_START, 0x0099);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_REFCNT_START, 0x0099);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_PLLCNT_START, 0x0099);
+}
+
+/*
+ * Set registers responsible for enabling and configuring SSC, with second
+ * register value provided by a parameter.
+ */
+static void cdns_torrent_dp_enable_ssc_25mhz(struct cdns_torrent_phy *cdns_phy,
+ u32 ctrl2_val)
+{
+ struct regmap *regmap = cdns_phy->regmap_common_cdb;
+
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x0001);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, ctrl2_val);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x007F);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL4_M0, 0x0003);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x0001);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, ctrl2_val);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x007F);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL4_M0, 0x0003);
+}
+
+static
+void cdns_torrent_dp_pma_cmn_vco_cfg_25mhz(struct cdns_torrent_phy *cdns_phy,
+ u32 rate, bool ssc)
+{
+ struct regmap *regmap = cdns_phy->regmap_common_cdb;
+
+ /* Assumes 25 MHz refclock */
+ switch (rate) {
+ /* Setting VCO for 10.8GHz */
+ case 2700:
+ case 5400:
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x01B0);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0120);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x01B0);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0120);
+ if (ssc)
+ cdns_torrent_dp_enable_ssc_25mhz(cdns_phy, 0x0423);
+ break;
+ /* Setting VCO for 9.72GHz */
+ case 1620:
+ case 2430:
+ case 3240:
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0184);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0xCCCD);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0104);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0184);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0xCCCD);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0104);
+ if (ssc)
+ cdns_torrent_dp_enable_ssc_25mhz(cdns_phy, 0x03B9);
+ break;
+ /* Setting VCO for 8.64GHz */
+ case 2160:
+ case 4320:
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0159);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x999A);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x00E7);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0159);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x999A);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x00E7);
+ if (ssc)
+ cdns_torrent_dp_enable_ssc_25mhz(cdns_phy, 0x034F);
+ break;
+ /* Setting VCO for 8.1GHz */
+ case 8100:
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0144);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x00D8);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0144);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x00D8);
+ if (ssc)
+ cdns_torrent_dp_enable_ssc_25mhz(cdns_phy, 0x031A);
+ break;
+ }
+
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+
+ if (ssc) {
+ cdns_torrent_phy_write(regmap,
+ CMN_PLL0_VCOCAL_PLLCNT_START, 0x0315);
+ cdns_torrent_phy_write(regmap,
+ CMN_PLL0_LOCK_PLLCNT_THR, 0x0005);
+ cdns_torrent_phy_write(regmap,
+ CMN_PLL1_VCOCAL_PLLCNT_START, 0x0315);
+ cdns_torrent_phy_write(regmap,
+ CMN_PLL1_LOCK_PLLCNT_THR, 0x0005);
+ } else {
+ cdns_torrent_phy_write(regmap,
+ CMN_PLL0_VCOCAL_PLLCNT_START, 0x0317);
+ cdns_torrent_phy_write(regmap,
+ CMN_PLL1_VCOCAL_PLLCNT_START, 0x0317);
+ /* Set reset register values to disable SSC */
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL2_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL3_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL4_M0, 0x0000);
+ cdns_torrent_phy_write(regmap,
+ CMN_PLL0_LOCK_PLLCNT_THR, 0x0003);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL2_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL3_M0, 0x0000);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL4_M0, 0x0000);
+ cdns_torrent_phy_write(regmap,
+ CMN_PLL1_LOCK_PLLCNT_THR, 0x0003);
+ }
+
+ cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_REFCNT_START, 0x00C7);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_PLLCNT_START, 0x00C7);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_REFCNT_START, 0x00C7);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_PLLCNT_START, 0x00C7);
+}
+
+static
+void cdns_torrent_dp_pma_cmn_vco_cfg_100mhz(struct cdns_torrent_phy *cdns_phy,
+ u32 rate, bool ssc)
+{
+ struct regmap *regmap = cdns_phy->regmap_common_cdb;
+
+ /* Assumes 100 MHz refclock */
+ switch (rate) {
+ /* Setting VCO for 10.8GHz */
+ case 2700:
+ case 5400:
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0028);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_FBH_OVRD_M0, 0x0022);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_FBH_OVRD_M0, 0x0022);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_FBL_OVRD_M0, 0x000C);
+ break;
+ /* Setting VCO for 9.72GHz */
+ case 1620:
+ case 2430:
+ case 3240:
+ cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0061);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0061);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x3333);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x3333);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0042);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0042);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+ break;
+ /* Setting VCO for 8.64GHz */
+ case 2160:
+ case 4320:
+ cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0056);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0056);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x6666);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x6666);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x003A);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x003A);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+ break;
+ /* Setting VCO for 8.1GHz */
+ case 8100:
+ cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0051);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0051);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0036);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0036);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+ break;
+ }
+}
+
+/*
+ * Enable or disable PLL for selected lanes.
+ */
+static int cdns_torrent_dp_set_pll_en(struct cdns_torrent_phy *cdns_phy,
+ struct phy_configure_opts_dp *dp,
+ bool enable)
+{
+ u32 rd_val;
+ u32 ret;
+ struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+
+ /*
+ * Used to determine, which bits to check for or enable in
+ * PHY_PMA_XCVR_PLLCLK_EN register.
+ */
+ u32 pll_bits;
+ /* Used to enable or disable lanes. */
+ u32 pll_val;
+
+ /* Select values of registers and mask, depending on enabled lane
+ * count.
+ */
+ switch (dp->lanes) {
+ /* lane 0 */
+ case (1):
+ pll_bits = 0x00000001;
+ break;
+ /* lanes 0-1 */
+ case (2):
+ pll_bits = 0x00000003;
+ break;
+ /* lanes 0-3, all */
+ default:
+ pll_bits = 0x0000000F;
+ break;
+ }
+
+ if (enable)
+ pll_val = pll_bits;
+ else
+ pll_val = 0x00000000;
+
+ cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, pll_val);
+
+ /* Wait for acknowledgment from PHY. */
+ ret = regmap_read_poll_timeout(regmap,
+ PHY_PMA_XCVR_PLLCLK_EN_ACK,
+ rd_val,
+ (rd_val & pll_bits) == pll_val,
+ 0, POLL_TIMEOUT_US);
+ ndelay(100);
+ return ret;
+}
+
+static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy,
+ u32 num_lanes,
+ enum phy_powerstate powerstate)
+{
+ /* Register value for power state for a single byte. */
+ u32 value_part;
+ u32 value;
+ u32 mask;
+ u32 read_val;
+ u32 ret;
+ struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+
+ switch (powerstate) {
+ case (POWERSTATE_A0):
+ value_part = 0x01U;
+ break;
+ case (POWERSTATE_A2):
+ value_part = 0x04U;
+ break;
+ default:
+ /* Powerstate A3 */
+ value_part = 0x08U;
+ break;
+ }
+
+ /* Select values of registers and mask, depending on enabled
+ * lane count.
+ */
+ switch (num_lanes) {
+ /* lane 0 */
+ case (1):
+ value = value_part;
+ mask = 0x0000003FU;
+ break;
+ /* lanes 0-1 */
+ case (2):
+ value = (value_part
+ | (value_part << 8));
+ mask = 0x00003F3FU;
+ break;
+ /* lanes 0-3, all */
+ default:
+ value = (value_part
+ | (value_part << 8)
+ | (value_part << 16)
+ | (value_part << 24));
+ mask = 0x3F3F3F3FU;
+ break;
+ }
+
+ /* Set power state A<n>. */
+ cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_POWER_STATE_REQ, value);
+ /* Wait, until PHY acknowledges power state completion. */
+ ret = regmap_read_poll_timeout(regmap, PHY_PMA_XCVR_POWER_STATE_ACK,
+ read_val, (read_val & mask) == value, 0,
+ POLL_TIMEOUT_US);
+ cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_POWER_STATE_REQ, 0x00000000);
+ ndelay(100);
+
+ return ret;
+}
+
+static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, u32 num_lanes)
+{
+ unsigned int read_val;
+ int ret;
+ struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+
+ /*
+ * waiting for ACK of pma_xcvr_pllclk_en_ln_*, only for the
+ * master lane
+ */
+ ret = regmap_read_poll_timeout(regmap, PHY_PMA_XCVR_PLLCLK_EN_ACK,
+ read_val, read_val & 1,
+ 0, POLL_TIMEOUT_US);
+ if (ret == -ETIMEDOUT) {
+ dev_err(cdns_phy->dev,
+ "timeout waiting for link PLL clock enable ack\n");
+ return ret;
+ }
+
+ ndelay(100);
+
+ ret = cdns_torrent_dp_set_power_state(cdns_phy, num_lanes,
+ POWERSTATE_A2);
+ if (ret)
+ return ret;
+
+ ret = cdns_torrent_dp_set_power_state(cdns_phy, num_lanes,
+ POWERSTATE_A0);
+
+ return ret;
+}
+
+static int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy)
+{
+ unsigned int reg;
+ int ret;
+ struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+
+ ret = regmap_read_poll_timeout(regmap, PHY_PMA_CMN_READY, reg,
+ reg & 1, 0, POLL_TIMEOUT_US);
+ if (ret == -ETIMEDOUT) {
+ dev_err(cdns_phy->dev,
+ "timeout waiting for PMA common ready\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void cdns_torrent_dp_pma_cmn_rate(struct cdns_torrent_phy *cdns_phy,
+ u32 rate, u32 num_lanes)
+{
+ unsigned int clk_sel_val = 0;
+ unsigned int hsclk_div_val = 0;
+ unsigned int i;
+
+ switch (rate) {
+ case 1620:
+ clk_sel_val = 0x0f01;
+ hsclk_div_val = 2;
+ break;
+ case 2160:
+ case 2430:
+ case 2700:
+ clk_sel_val = 0x0701;
+ hsclk_div_val = 1;
+ break;
+ case 3240:
+ clk_sel_val = 0x0b00;
+ hsclk_div_val = 2;
+ break;
+ case 4320:
+ case 5400:
+ clk_sel_val = 0x0301;
+ hsclk_div_val = 0;
+ break;
+ case 8100:
+ clk_sel_val = 0x0200;
+ hsclk_div_val = 0;
+ break;
+ }
+
+ cdns_torrent_phy_write(cdns_phy->regmap_common_cdb,
+ CMN_PDIAG_PLL0_CLK_SEL_M0, clk_sel_val);
+ cdns_torrent_phy_write(cdns_phy->regmap_common_cdb,
+ CMN_PDIAG_PLL1_CLK_SEL_M0, clk_sel_val);
+
+ /* PMA lane configuration to deal with multi-link operation */
+ for (i = 0; i < num_lanes; i++)
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[i],
+ XCVR_DIAG_HSCLK_DIV, hsclk_div_val);
+}
+
+/*
+ * Perform register operations related to setting link rate, once powerstate is
+ * set and PLL disable request was processed.
+ */
+static int cdns_torrent_dp_configure_rate(struct cdns_torrent_phy *cdns_phy,
+ struct phy_configure_opts_dp *dp)
+{
+ u32 read_val, ret;
+
+ /* Disable the cmn_pll0_en before re-programming the new data rate. */
+ regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, 0x0);
+
+ /*
+ * Wait for PLL ready de-assertion.
+ * For PLL0 - PHY_PMA_CMN_CTRL2[2] == 1
+ */
+ ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2,
+ read_val,
+ ((read_val >> 2) & 0x01) != 0,
+ 0, POLL_TIMEOUT_US);
+ if (ret)
+ return ret;
+ ndelay(200);
+
+ /* DP Rate Change - VCO Output settings. */
+ if (cdns_phy->ref_clk_rate == CLK_19_2_MHZ)
+ /* PMA common configuration 19.2MHz */
+ cdns_torrent_dp_pma_cmn_vco_cfg_19_2mhz(cdns_phy, dp->link_rate, dp->ssc);
+ else if (cdns_phy->ref_clk_rate == CLK_25_MHZ)
+ /* PMA common configuration 25MHz */
+ cdns_torrent_dp_pma_cmn_vco_cfg_25mhz(cdns_phy, dp->link_rate, dp->ssc);
+ else if (cdns_phy->ref_clk_rate == CLK_100_MHZ)
+ /* PMA common configuration 100MHz */
+ cdns_torrent_dp_pma_cmn_vco_cfg_100mhz(cdns_phy, dp->link_rate, dp->ssc);
+
+ cdns_torrent_dp_pma_cmn_rate(cdns_phy, dp->link_rate, dp->lanes);
+
+ /* Enable the cmn_pll0_en. */
+ regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, 0x3);
+
+ /*
+ * Wait for PLL ready assertion.
+ * For PLL0 - PHY_PMA_CMN_CTRL2[0] == 1
+ */
+ ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2,
+ read_val,
+ (read_val & 0x01) != 0,
+ 0, POLL_TIMEOUT_US);
+ return ret;
+}
+
+/*
+ * Verify, that parameters to configure PHY with are correct.
+ */
+static int cdns_torrent_dp_verify_config(struct cdns_torrent_inst *inst,
+ struct phy_configure_opts_dp *dp)
+{
+ u8 i;
+
+ /* If changing link rate was required, verify it's supported. */
+ if (dp->set_rate) {
+ switch (dp->link_rate) {
+ case 1620:
+ case 2160:
+ case 2430:
+ case 2700:
+ case 3240:
+ case 4320:
+ case 5400:
+ case 8100:
+ /* valid bit rate */
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ /* Verify lane count. */
+ switch (dp->lanes) {
+ case 1:
+ case 2:
+ case 4:
+ /* valid lane count. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Check against actual number of PHY's lanes. */
+ if (dp->lanes > inst->num_lanes)
+ return -EINVAL;
+
+ /*
+ * If changing voltages is required, check swing and pre-emphasis
+ * levels, per-lane.
+ */
+ if (dp->set_voltages) {
+ /* Lane count verified previously. */
+ for (i = 0; i < dp->lanes; i++) {
+ if (dp->voltage[i] > 3 || dp->pre[i] > 3)
+ return -EINVAL;
+
+ /* Sum of voltage swing and pre-emphasis levels cannot
+ * exceed 3.
+ */
+ if (dp->voltage[i] + dp->pre[i] > 3)
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/* Set power state A0 and PLL clock enable to 0 on enabled lanes. */
+static void cdns_torrent_dp_set_a0_pll(struct cdns_torrent_phy *cdns_phy,
+ u32 num_lanes)
+{
+ struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+ u32 pwr_state = cdns_torrent_dp_read(regmap,
+ PHY_PMA_XCVR_POWER_STATE_REQ);
+ u32 pll_clk_en = cdns_torrent_dp_read(regmap,
+ PHY_PMA_XCVR_PLLCLK_EN);
+
+ /* Lane 0 is always enabled. */
+ pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK <<
+ PHY_POWER_STATE_LN_0);
+ pll_clk_en &= ~0x01U;
+
+ if (num_lanes > 1) {
+ /* lane 1 */
+ pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK <<
+ PHY_POWER_STATE_LN_1);
+ pll_clk_en &= ~(0x01U << 1);
+ }
+
+ if (num_lanes > 2) {
+ /* lanes 2 and 3 */
+ pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK <<
+ PHY_POWER_STATE_LN_2);
+ pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK <<
+ PHY_POWER_STATE_LN_3);
+ pll_clk_en &= ~(0x01U << 2);
+ pll_clk_en &= ~(0x01U << 3);
+ }
+
+ cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_POWER_STATE_REQ, pwr_state);
+ cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, pll_clk_en);
+}
+
+/* Configure lane count as required. */
+static int cdns_torrent_dp_set_lanes(struct cdns_torrent_phy *cdns_phy,
+ struct phy_configure_opts_dp *dp)
+{
+ u32 value;
+ u32 ret;
+ struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+ u8 lane_mask = (1 << dp->lanes) - 1;
+
+ value = cdns_torrent_dp_read(regmap, PHY_RESET);
+ /* clear pma_tx_elec_idle_ln_* bits. */
+ value &= ~PMA_TX_ELEC_IDLE_MASK;
+ /* Assert pma_tx_elec_idle_ln_* for disabled lanes. */
+ value |= ((~lane_mask) << PMA_TX_ELEC_IDLE_SHIFT) &
+ PMA_TX_ELEC_IDLE_MASK;
+ cdns_torrent_dp_write(regmap, PHY_RESET, value);
+
+ /* reset the link by asserting phy_l00_reset_n low */
+ cdns_torrent_dp_write(regmap, PHY_RESET,
+ value & (~PHY_L00_RESET_N_MASK));
+
+ /*
+ * Assert lane reset on unused lanes and lane 0 so they remain in reset
+ * and powered down when re-enabling the link
+ */
+ value = (value & 0x0000FFF0) | (0x0000000E & lane_mask);
+ cdns_torrent_dp_write(regmap, PHY_RESET, value);
+
+ cdns_torrent_dp_set_a0_pll(cdns_phy, dp->lanes);
+
+ /* release phy_l0*_reset_n based on used laneCount */
+ value = (value & 0x0000FFF0) | (0x0000000F & lane_mask);
+ cdns_torrent_dp_write(regmap, PHY_RESET, value);
+
+ /* Wait, until PHY gets ready after releasing PHY reset signal. */
+ ret = cdns_torrent_dp_wait_pma_cmn_ready(cdns_phy);
+ if (ret)
+ return ret;
+
+ ndelay(100);
+
+ /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */
+ cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, 0x0001);
+
+ ret = cdns_torrent_dp_run(cdns_phy, dp->lanes);
+
+ return ret;
+}
+
+/* Configure link rate as required. */
+static int cdns_torrent_dp_set_rate(struct cdns_torrent_phy *cdns_phy,
+ struct phy_configure_opts_dp *dp)
+{
+ u32 ret;
+
+ ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
+ POWERSTATE_A3);
+ if (ret)
+ return ret;
+ ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, false);
+ if (ret)
+ return ret;
+ ndelay(200);
+
+ ret = cdns_torrent_dp_configure_rate(cdns_phy, dp);
+ if (ret)
+ return ret;
+ ndelay(200);
+
+ ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, true);
+ if (ret)
+ return ret;
+ ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
+ POWERSTATE_A2);
+ if (ret)
+ return ret;
+ ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
+ POWERSTATE_A0);
+ if (ret)
+ return ret;
+ ndelay(900);
+
+ return ret;
+}
+
+/* Configure voltage swing and pre-emphasis for all enabled lanes. */
+static void cdns_torrent_dp_set_voltages(struct cdns_torrent_phy *cdns_phy,
+ struct phy_configure_opts_dp *dp)
+{
+ u8 lane;
+ u16 val;
+
+ for (lane = 0; lane < dp->lanes; lane++) {
+ val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[lane],
+ TX_DIAG_ACYA);
+ /*
+ * Write 1 to register bit TX_DIAG_ACYA[0] to freeze the
+ * current state of the analog TX driver.
+ */
+ val |= TX_DIAG_ACYA_HBDC_MASK;
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+ TX_DIAG_ACYA, val);
+
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+ TX_TXCC_CTRL, 0x08A4);
+ val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].diag_tx_drv;
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+ DRV_DIAG_TX_DRV, val);
+ val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].mgnfs_mult;
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+ TX_TXCC_MGNFS_MULT_000,
+ val);
+ val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].cpost_mult;
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+ TX_TXCC_CPOST_MULT_00,
+ val);
+
+ val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[lane],
+ TX_DIAG_ACYA);
+ /*
+ * Write 0 to register bit TX_DIAG_ACYA[0] to allow the state of
+ * analog TX driver to reflect the new programmed one.
+ */
+ val &= ~TX_DIAG_ACYA_HBDC_MASK;
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+ TX_DIAG_ACYA, val);
+ }
+};
+
+static int cdns_torrent_dp_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
+ struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
+ int ret;
+
+ ret = cdns_torrent_dp_verify_config(inst, &opts->dp);
+ if (ret) {
+ dev_err(&phy->dev, "invalid params for phy configure\n");
+ return ret;
+ }
+
+ if (opts->dp.set_lanes) {
+ ret = cdns_torrent_dp_set_lanes(cdns_phy, &opts->dp);
+ if (ret) {
+ dev_err(&phy->dev, "cdns_torrent_dp_set_lanes failed\n");
+ return ret;
+ }
+ }
+
+ if (opts->dp.set_rate) {
+ ret = cdns_torrent_dp_set_rate(cdns_phy, &opts->dp);
+ if (ret) {
+ dev_err(&phy->dev, "cdns_torrent_dp_set_rate failed\n");
+ return ret;
+ }
+ }
+
+ if (opts->dp.set_voltages)
+ cdns_torrent_dp_set_voltages(cdns_phy, &opts->dp);
+
+ return ret;
+}
+
+static int cdns_torrent_phy_on(struct phy *phy)
+{
+ struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
+ struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
+ u32 read_val;
+ int ret;
+
+ if (cdns_phy->nsubnodes == 1) {
+ /* Take the PHY lane group out of reset */
+ reset_control_deassert(inst->lnk_rst);
+
+ /* Take the PHY out of reset */
+ ret = reset_control_deassert(cdns_phy->phy_rst);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Wait for cmn_ready assertion
+ * PHY_PMA_CMN_CTRL1[0] == 1
+ */
+ ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_1,
+ read_val, read_val, 1000,
+ PLL_LOCK_TIMEOUT);
+ if (ret) {
+ dev_err(cdns_phy->dev, "Timeout waiting for CMN ready\n");
+ return ret;
+ }
+
+ if (inst->phy_type == TYPE_PCIE || inst->phy_type == TYPE_USB) {
+ ret = regmap_field_read_poll_timeout(cdns_phy->phy_pcs_iso_link_ctrl_1[inst->mlane],
+ read_val, !read_val, 1000,
+ PLL_LOCK_TIMEOUT);
+ if (ret == -ETIMEDOUT) {
+ dev_err(cdns_phy->dev, "Timeout waiting for PHY status ready\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int cdns_torrent_phy_off(struct phy *phy)
+{
+ struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
+ struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
+ int ret;
+
+ if (cdns_phy->nsubnodes != 1)
+ return 0;
+
+ ret = reset_control_assert(cdns_phy->phy_rst);
+ if (ret)
+ return ret;
+
+ return reset_control_assert(inst->lnk_rst);
+}
+
+static void cdns_torrent_dp_common_init(struct cdns_torrent_phy *cdns_phy,
+ struct cdns_torrent_inst *inst)
+{
+ struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+ unsigned char lane_bits;
+
+ cdns_torrent_dp_write(regmap, PHY_AUX_CTRL, 0x0003); /* enable AUX */
+
+ /*
+ * Set lines power state to A0
+ * Set lines pll clk enable to 0
+ */
+ cdns_torrent_dp_set_a0_pll(cdns_phy, inst->num_lanes);
+
+ /*
+ * release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on
+ * used lanes
+ */
+ lane_bits = (1 << inst->num_lanes) - 1;
+ cdns_torrent_dp_write(regmap, PHY_RESET,
+ ((0xF & ~lane_bits) << 4) | (0xF & lane_bits));
+
+ /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */
+ cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, 0x0001);
+
+ /*
+ * PHY PMA registers configuration functions
+ * Initialize PHY with max supported link rate, without SSC.
+ */
+ if (cdns_phy->ref_clk_rate == CLK_19_2_MHZ)
+ cdns_torrent_dp_pma_cmn_vco_cfg_19_2mhz(cdns_phy,
+ cdns_phy->max_bit_rate,
+ false);
+ else if (cdns_phy->ref_clk_rate == CLK_25_MHZ)
+ cdns_torrent_dp_pma_cmn_vco_cfg_25mhz(cdns_phy,
+ cdns_phy->max_bit_rate,
+ false);
+ else if (cdns_phy->ref_clk_rate == CLK_100_MHZ)
+ cdns_torrent_dp_pma_cmn_vco_cfg_100mhz(cdns_phy,
+ cdns_phy->max_bit_rate,
+ false);
+
+ cdns_torrent_dp_pma_cmn_rate(cdns_phy, cdns_phy->max_bit_rate,
+ inst->num_lanes);
+
+ /* take out of reset */
+ regmap_field_write(cdns_phy->phy_reset_ctrl, 0x1);
+}
+
+static int cdns_torrent_dp_start(struct cdns_torrent_phy *cdns_phy,
+ struct cdns_torrent_inst *inst,
+ struct phy *phy)
+{
+ int ret;
+
+ cdns_torrent_phy_on(phy);
+
+ ret = cdns_torrent_dp_wait_pma_cmn_ready(cdns_phy);
+ if (ret)
+ return ret;
+
+ ret = cdns_torrent_dp_run(cdns_phy, inst->num_lanes);
+
+ return ret;
+}
+
+static int cdns_torrent_dp_init(struct phy *phy)
+{
+ struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
+ struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
+
+ switch (cdns_phy->ref_clk_rate) {
+ case CLK_19_2_MHZ:
+ case CLK_25_MHZ:
+ case CLK_100_MHZ:
+ /* Valid Ref Clock Rate */
+ break;
+ default:
+ dev_err(cdns_phy->dev, "Unsupported Ref Clock Rate\n");
+ return -EINVAL;
+ }
+
+ cdns_torrent_dp_common_init(cdns_phy, inst);
+
+ return cdns_torrent_dp_start(cdns_phy, inst, phy);
+}
+
+static int cdns_torrent_derived_refclk_enable(struct clk_hw *hw)
+{
+ struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw);
+
+ regmap_field_write(derived_refclk->cmn_cdiag_refclk_ovrd_4, 1);
+ regmap_field_write(derived_refclk->phy_pipe_cmn_ctrl1_0, 1);
+
+ return 0;
+}
+
+static void cdns_torrent_derived_refclk_disable(struct clk_hw *hw)
+{
+ struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw);
+
+ regmap_field_write(derived_refclk->phy_pipe_cmn_ctrl1_0, 0);
+ regmap_field_write(derived_refclk->cmn_cdiag_refclk_ovrd_4, 0);
+}
+
+static int cdns_torrent_derived_refclk_is_enabled(struct clk_hw *hw)
+{
+ struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw);
+ int val;
+
+ regmap_field_read(derived_refclk->cmn_cdiag_refclk_ovrd_4, &val);
+
+ return !!val;
+}
+
+static const struct clk_ops cdns_torrent_derived_refclk_ops = {
+ .enable = cdns_torrent_derived_refclk_enable,
+ .disable = cdns_torrent_derived_refclk_disable,
+ .is_enabled = cdns_torrent_derived_refclk_is_enabled,
+};
+
+static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_phy)
+{
+ struct cdns_torrent_derived_refclk *derived_refclk;
+ struct device *dev = cdns_phy->dev;
+ struct clk_init_data *init;
+ const char *parent_name;
+ char clk_name[100];
+ struct clk_hw *hw;
+ struct clk *clk;
+ int ret;
+
+ derived_refclk = devm_kzalloc(dev, sizeof(*derived_refclk), GFP_KERNEL);
+ if (!derived_refclk)
+ return -ENOMEM;
+
+ snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
+ clk_names[CDNS_TORRENT_DERIVED_REFCLK]);
+
+ clk = devm_clk_get_optional(dev, "phy_en_refclk");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "No parent clock for derived_refclk\n");
+ return PTR_ERR(clk);
+ }
+
+ init = &derived_refclk->clk_data;
+
+ if (clk) {
+ parent_name = __clk_get_name(clk);
+ init->parent_names = &parent_name;
+ init->num_parents = 1;
+ }
+ init->ops = &cdns_torrent_derived_refclk_ops;
+ init->flags = 0;
+ init->name = clk_name;
+
+ derived_refclk->phy_pipe_cmn_ctrl1_0 = cdns_phy->phy_pipe_cmn_ctrl1_0;
+ derived_refclk->cmn_cdiag_refclk_ovrd_4 = cdns_phy->cmn_cdiag_refclk_ovrd_4;
+
+ derived_refclk->hw.init = init;
+
+ hw = &derived_refclk->hw;
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return ret;
+
+ cdns_phy->clk_hw_data->hws[CDNS_TORRENT_DERIVED_REFCLK] = hw;
+
+ return 0;
+}
+
+static int cdns_torrent_received_refclk_enable(struct clk_hw *hw)
+{
+ struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw);
+
+ regmap_field_write(received_refclk->phy_pipe_cmn_ctrl1_0, 1);
+
+ return 0;
+}
+
+static void cdns_torrent_received_refclk_disable(struct clk_hw *hw)
+{
+ struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw);
+
+ regmap_field_write(received_refclk->phy_pipe_cmn_ctrl1_0, 0);
+}
+
+static int cdns_torrent_received_refclk_is_enabled(struct clk_hw *hw)
+{
+ struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw);
+ int val, cmn_val;
+
+ regmap_field_read(received_refclk->phy_pipe_cmn_ctrl1_0, &val);
+ regmap_field_read(received_refclk->cmn_cdiag_refclk_ovrd_4, &cmn_val);
+
+ return val && !cmn_val;
+}
+
+static const struct clk_ops cdns_torrent_received_refclk_ops = {
+ .enable = cdns_torrent_received_refclk_enable,
+ .disable = cdns_torrent_received_refclk_disable,
+ .is_enabled = cdns_torrent_received_refclk_is_enabled,
+};
+
+static int cdns_torrent_received_refclk_register(struct cdns_torrent_phy *cdns_phy)
+{
+ struct cdns_torrent_received_refclk *received_refclk;
+ struct device *dev = cdns_phy->dev;
+ struct clk_init_data *init;
+ const char *parent_name;
+ char clk_name[100];
+ struct clk_hw *hw;
+ struct clk *clk;
+ int ret;
+
+ received_refclk = devm_kzalloc(dev, sizeof(*received_refclk), GFP_KERNEL);
+ if (!received_refclk)
+ return -ENOMEM;
+
+ snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
+ clk_names[CDNS_TORRENT_RECEIVED_REFCLK]);
+
+ clk = devm_clk_get_optional(dev, "phy_en_refclk");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "No parent clock for received_refclk\n");
+ return PTR_ERR(clk);
+ }
+
+ init = &received_refclk->clk_data;
+
+ if (clk) {
+ parent_name = __clk_get_name(clk);
+ init->parent_names = &parent_name;
+ init->num_parents = 1;
+ }
+ init->ops = &cdns_torrent_received_refclk_ops;
+ init->flags = 0;
+ init->name = clk_name;
+
+ received_refclk->phy_pipe_cmn_ctrl1_0 = cdns_phy->phy_pipe_cmn_ctrl1_0;
+ received_refclk->cmn_cdiag_refclk_ovrd_4 = cdns_phy->cmn_cdiag_refclk_ovrd_4;
+
+ received_refclk->hw.init = init;
+
+ hw = &received_refclk->hw;
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return ret;
+
+ cdns_phy->clk_hw_data->hws[CDNS_TORRENT_RECEIVED_REFCLK] = hw;
+
+ return 0;
+}
+
+static int cdns_torrent_refclk_driver_enable(struct clk_hw *hw)
+{
+ struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw);
+
+ regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_6], 0);
+ regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_5], 1);
+ regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 0);
+
+ return 0;
+}
+
+static void cdns_torrent_refclk_driver_disable(struct clk_hw *hw)
+{
+ struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw);
+
+ regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 1);
+}
+
+static int cdns_torrent_refclk_driver_is_enabled(struct clk_hw *hw)
+{
+ struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw);
+ int val;
+
+ regmap_field_read(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], &val);
+
+ return !val;
+}
+
+static u8 cdns_torrent_refclk_driver_get_parent(struct clk_hw *hw)
+{
+ struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw);
+ unsigned int val;
+
+ regmap_field_read(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], &val);
+ return clk_mux_val_to_index(hw, cdns_torrent_refclk_driver_mux_table, 0, val);
+}
+
+static int cdns_torrent_refclk_driver_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw);
+ unsigned int val;
+
+ val = cdns_torrent_refclk_driver_mux_table[index];
+ return regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], val);
+}
+
+static const struct clk_ops cdns_torrent_refclk_driver_ops = {
+ .enable = cdns_torrent_refclk_driver_enable,
+ .disable = cdns_torrent_refclk_driver_disable,
+ .is_enabled = cdns_torrent_refclk_driver_is_enabled,
+ .set_parent = cdns_torrent_refclk_driver_set_parent,
+ .get_parent = cdns_torrent_refclk_driver_get_parent,
+};
+
+static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy)
+{
+ struct cdns_torrent_refclk_driver *refclk_driver;
+ struct device *dev = cdns_phy->dev;
+ struct regmap_field *field;
+ struct clk_init_data *init;
+ const char **parent_names;
+ unsigned int num_parents;
+ struct regmap *regmap;
+ char clk_name[100];
+ struct clk_hw *hw;
+ int i, ret;
+
+ refclk_driver = devm_kzalloc(dev, sizeof(*refclk_driver), GFP_KERNEL);
+ if (!refclk_driver)
+ return -ENOMEM;
+
+ num_parents = ARRAY_SIZE(refclk_driver_parent_index);
+ parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), GFP_KERNEL);
+ if (!parent_names)
+ return -ENOMEM;
+
+ for (i = 0; i < num_parents; i++) {
+ hw = cdns_phy->clk_hw_data->hws[refclk_driver_parent_index[i]];
+ if (IS_ERR_OR_NULL(hw)) {
+ dev_err(dev, "No parent clock for refclk driver clock\n");
+ return IS_ERR(hw) ? PTR_ERR(hw) : -ENOENT;
+ }
+ parent_names[i] = clk_hw_get_name(hw);
+ }
+
+ snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
+ clk_names[CDNS_TORRENT_REFCLK_DRIVER]);
+
+ init = &refclk_driver->clk_data;
+
+ init->ops = &cdns_torrent_refclk_driver_ops;
+ init->flags = CLK_SET_RATE_NO_REPARENT;
+ init->parent_names = parent_names;
+ init->num_parents = num_parents;
+ init->name = clk_name;
+
+ regmap = cdns_phy->regmap_common_cdb;
+
+ for (i = 0; i < REFCLK_OUT_NUM_CMN_CONFIG; i++) {
+ field = devm_regmap_field_alloc(dev, regmap, refclk_out_cmn_cfg[i]);
+ if (IS_ERR(field)) {
+ dev_err(dev, "Refclk driver CMN reg field init failed\n");
+ return PTR_ERR(field);
+ }
+ refclk_driver->cmn_fields[i] = field;
+ }
+
+ /* Enable Derived reference clock as default */
+ regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1);
+
+ refclk_driver->hw.init = init;
+
+ hw = &refclk_driver->hw;
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return ret;
+
+ cdns_phy->clk_hw_data->hws[CDNS_TORRENT_REFCLK_DRIVER] = hw;
+
+ return 0;
+}
+
+static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base,
+ u32 block_offset,
+ u8 reg_offset_shift,
+ const struct regmap_config *config)
+{
+ struct cdns_regmap_cdb_context *ctx;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ ctx->dev = dev;
+ ctx->base = base + block_offset;
+ ctx->reg_offset_shift = reg_offset_shift;
+
+ return devm_regmap_init(dev, NULL, ctx, config);
+}
+
+static int cdns_torrent_dp_regfield_init(struct cdns_torrent_phy *cdns_phy)
+{
+ struct device *dev = cdns_phy->dev;
+ struct regmap_field *field;
+ struct regmap *regmap;
+
+ regmap = cdns_phy->regmap_dptx_phy_reg;
+ field = devm_regmap_field_alloc(dev, regmap, phy_reset_ctrl);
+ if (IS_ERR(field)) {
+ dev_err(dev, "PHY_RESET reg field init failed\n");
+ return PTR_ERR(field);
+ }
+ cdns_phy->phy_reset_ctrl = field;
+
+ return 0;
+}
+
+static int cdns_torrent_regfield_init(struct cdns_torrent_phy *cdns_phy)
+{
+ struct device *dev = cdns_phy->dev;
+ struct regmap_field *field;
+ struct regmap *regmap;
+ int i;
+
+ regmap = cdns_phy->regmap_phy_pcs_common_cdb;
+ field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg);
+ if (IS_ERR(field)) {
+ dev_err(dev, "PHY_PLL_CFG reg field init failed\n");
+ return PTR_ERR(field);
+ }
+ cdns_phy->phy_pll_cfg = field;
+
+ regmap = cdns_phy->regmap_phy_pcs_common_cdb;
+ field = devm_regmap_field_alloc(dev, regmap, phy_pipe_cmn_ctrl1_0);
+ if (IS_ERR(field)) {
+ dev_err(dev, "phy_pipe_cmn_ctrl1_0 reg field init failed\n");
+ return PTR_ERR(field);
+ }
+ cdns_phy->phy_pipe_cmn_ctrl1_0 = field;
+
+ regmap = cdns_phy->regmap_common_cdb;
+ field = devm_regmap_field_alloc(dev, regmap, cmn_cdiag_refclk_ovrd_4);
+ if (IS_ERR(field)) {
+ dev_err(dev, "cmn_cdiag_refclk_ovrd_4 reg field init failed\n");
+ return PTR_ERR(field);
+ }
+ cdns_phy->cmn_cdiag_refclk_ovrd_4 = field;
+
+ regmap = cdns_phy->regmap_phy_pma_common_cdb;
+ field = devm_regmap_field_alloc(dev, regmap, phy_pma_cmn_ctrl_1);
+ if (IS_ERR(field)) {
+ dev_err(dev, "PHY_PMA_CMN_CTRL1 reg field init failed\n");
+ return PTR_ERR(field);
+ }
+ cdns_phy->phy_pma_cmn_ctrl_1 = field;
+
+ regmap = cdns_phy->regmap_phy_pma_common_cdb;
+ field = devm_regmap_field_alloc(dev, regmap, phy_pma_cmn_ctrl_2);
+ if (IS_ERR(field)) {
+ dev_err(dev, "PHY_PMA_CMN_CTRL2 reg field init failed\n");
+ return PTR_ERR(field);
+ }
+ cdns_phy->phy_pma_cmn_ctrl_2 = field;
+
+ regmap = cdns_phy->regmap_phy_pma_common_cdb;
+ field = devm_regmap_field_alloc(dev, regmap, phy_pma_pll_raw_ctrl);
+ if (IS_ERR(field)) {
+ dev_err(dev, "PHY_PMA_PLL_RAW_CTRL reg field init failed\n");
+ return PTR_ERR(field);
+ }
+ cdns_phy->phy_pma_pll_raw_ctrl = field;
+
+ for (i = 0; i < MAX_NUM_LANES; i++) {
+ regmap = cdns_phy->regmap_phy_pcs_lane_cdb[i];
+ field = devm_regmap_field_alloc(dev, regmap, phy_pcs_iso_link_ctrl_1);
+ if (IS_ERR(field)) {
+ dev_err(dev, "PHY_PCS_ISO_LINK_CTRL reg field init for ln %d failed\n", i);
+ return PTR_ERR(field);
+ }
+ cdns_phy->phy_pcs_iso_link_ctrl_1[i] = field;
+ }
+
+ return 0;
+}
+
+static int cdns_torrent_dp_regmap_init(struct cdns_torrent_phy *cdns_phy)
+{
+ void __iomem *base = cdns_phy->base;
+ struct device *dev = cdns_phy->dev;
+ struct regmap *regmap;
+ u8 reg_offset_shift;
+ u32 block_offset;
+
+ reg_offset_shift = cdns_phy->init_data->reg_offset_shift;
+
+ block_offset = TORRENT_DPTX_PHY_OFFSET;
+ regmap = cdns_regmap_init(dev, base, block_offset,
+ reg_offset_shift,
+ &cdns_torrent_dptx_phy_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to init DPTX PHY regmap\n");
+ return PTR_ERR(regmap);
+ }
+ cdns_phy->regmap_dptx_phy_reg = regmap;
+
+ return 0;
+}
+
+static int cdns_torrent_regmap_init(struct cdns_torrent_phy *cdns_phy)
+{
+ void __iomem *sd_base = cdns_phy->sd_base;
+ u8 block_offset_shift, reg_offset_shift;
+ struct device *dev = cdns_phy->dev;
+ struct regmap *regmap;
+ u32 block_offset;
+ int i;
+
+ block_offset_shift = cdns_phy->init_data->block_offset_shift;
+ reg_offset_shift = cdns_phy->init_data->reg_offset_shift;
+
+ for (i = 0; i < MAX_NUM_LANES; i++) {
+ block_offset = TORRENT_TX_LANE_CDB_OFFSET(i, block_offset_shift,
+ reg_offset_shift);
+ regmap = cdns_regmap_init(dev, sd_base, block_offset,
+ reg_offset_shift,
+ &cdns_torrent_tx_lane_cdb_config[i]);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to init tx lane CDB regmap\n");
+ return PTR_ERR(regmap);
+ }
+ cdns_phy->regmap_tx_lane_cdb[i] = regmap;
+
+ block_offset = TORRENT_RX_LANE_CDB_OFFSET(i, block_offset_shift,
+ reg_offset_shift);
+ regmap = cdns_regmap_init(dev, sd_base, block_offset,
+ reg_offset_shift,
+ &cdns_torrent_rx_lane_cdb_config[i]);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to init rx lane CDB regmap\n");
+ return PTR_ERR(regmap);
+ }
+ cdns_phy->regmap_rx_lane_cdb[i] = regmap;
+
+ block_offset = TORRENT_PHY_PCS_LANE_CDB_OFFSET(i, block_offset_shift,
+ reg_offset_shift);
+ regmap = cdns_regmap_init(dev, sd_base, block_offset,
+ reg_offset_shift,
+ &cdns_torrent_phy_pcs_lane_cdb_config[i]);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to init PHY PCS lane CDB regmap\n");
+ return PTR_ERR(regmap);
+ }
+ cdns_phy->regmap_phy_pcs_lane_cdb[i] = regmap;
+ }
+
+ block_offset = TORRENT_COMMON_CDB_OFFSET;
+ regmap = cdns_regmap_init(dev, sd_base, block_offset,
+ reg_offset_shift,
+ &cdns_torrent_common_cdb_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to init common CDB regmap\n");
+ return PTR_ERR(regmap);
+ }
+ cdns_phy->regmap_common_cdb = regmap;
+
+ block_offset = TORRENT_PHY_PCS_COMMON_OFFSET(block_offset_shift);
+ regmap = cdns_regmap_init(dev, sd_base, block_offset,
+ reg_offset_shift,
+ &cdns_torrent_phy_pcs_cmn_cdb_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to init PHY PCS common CDB regmap\n");
+ return PTR_ERR(regmap);
+ }
+ cdns_phy->regmap_phy_pcs_common_cdb = regmap;
+
+ block_offset = TORRENT_PHY_PMA_COMMON_OFFSET(block_offset_shift);
+ regmap = cdns_regmap_init(dev, sd_base, block_offset,
+ reg_offset_shift,
+ &cdns_torrent_phy_pma_cmn_cdb_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to init PHY PMA common CDB regmap\n");
+ return PTR_ERR(regmap);
+ }
+ cdns_phy->regmap_phy_pma_common_cdb = regmap;
+
+ return 0;
+}
+
+static int cdns_torrent_phy_init(struct phy *phy)
+{
+ struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
+ const struct cdns_torrent_data *init_data = cdns_phy->init_data;
+ 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;
+ struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
+ enum cdns_torrent_phy_type phy_type = inst->phy_type;
+ enum cdns_torrent_ssc_mode ssc = inst->ssc_mode;
+ struct cdns_torrent_vals *pcs_cmn_vals;
+ struct cdns_reg_pairs *reg_pairs;
+ struct regmap *regmap;
+ u32 num_regs;
+ int i, j;
+
+ if (cdns_phy->nsubnodes > 1)
+ return 0;
+
+ /**
+ * Spread spectrum generation is not required or supported
+ * for SGMII/QSGMII
+ */
+ if (phy_type == TYPE_SGMII || phy_type == TYPE_QSGMII)
+ ssc = NO_SSC;
+
+ /* PHY configuration specific registers for single link */
+ link_cmn_vals = init_data->link_cmn_vals[phy_type][TYPE_NONE][ssc];
+ if (link_cmn_vals) {
+ reg_pairs = link_cmn_vals->reg_pairs;
+ num_regs = link_cmn_vals->num_regs;
+ regmap = cdns_phy->regmap_common_cdb;
+
+ /**
+ * First array value in link_cmn_vals must be of
+ * PHY_PLL_CFG register
+ */
+ regmap_field_write(cdns_phy->phy_pll_cfg, reg_pairs[0].val);
+
+ for (i = 1; i < num_regs; i++)
+ regmap_write(regmap, reg_pairs[i].off,
+ reg_pairs[i].val);
+ }
+
+ xcvr_diag_vals = init_data->xcvr_diag_vals[phy_type][TYPE_NONE][ssc];
+ if (xcvr_diag_vals) {
+ reg_pairs = xcvr_diag_vals->reg_pairs;
+ num_regs = xcvr_diag_vals->num_regs;
+ for (i = 0; i < inst->num_lanes; i++) {
+ regmap = cdns_phy->regmap_tx_lane_cdb[i + inst->mlane];
+ for (j = 0; j < num_regs; j++)
+ regmap_write(regmap, reg_pairs[j].off,
+ reg_pairs[j].val);
+ }
+ }
+
+ /* PHY PCS common registers configurations */
+ pcs_cmn_vals = init_data->pcs_cmn_vals[phy_type][TYPE_NONE][ssc];
+ if (pcs_cmn_vals) {
+ reg_pairs = pcs_cmn_vals->reg_pairs;
+ num_regs = pcs_cmn_vals->num_regs;
+ regmap = cdns_phy->regmap_phy_pcs_common_cdb;
+ for (i = 0; i < num_regs; i++)
+ regmap_write(regmap, reg_pairs[i].off,
+ reg_pairs[i].val);
+ }
+
+ /* PMA common registers configurations */
+ cmn_vals = init_data->cmn_vals[ref_clk][phy_type][TYPE_NONE][ssc];
+ if (cmn_vals) {
+ reg_pairs = cmn_vals->reg_pairs;
+ num_regs = cmn_vals->num_regs;
+ regmap = cdns_phy->regmap_common_cdb;
+ for (i = 0; i < num_regs; i++)
+ regmap_write(regmap, reg_pairs[i].off,
+ reg_pairs[i].val);
+ }
+
+ /* PMA TX lane registers configurations */
+ tx_ln_vals = init_data->tx_ln_vals[ref_clk][phy_type][TYPE_NONE][ssc];
+ if (tx_ln_vals) {
+ reg_pairs = tx_ln_vals->reg_pairs;
+ num_regs = tx_ln_vals->num_regs;
+ for (i = 0; i < inst->num_lanes; i++) {
+ regmap = cdns_phy->regmap_tx_lane_cdb[i + inst->mlane];
+ for (j = 0; j < num_regs; j++)
+ regmap_write(regmap, reg_pairs[j].off,
+ reg_pairs[j].val);
+ }
+ }
+
+ /* PMA RX lane registers configurations */
+ rx_ln_vals = init_data->rx_ln_vals[ref_clk][phy_type][TYPE_NONE][ssc];
+ if (rx_ln_vals) {
+ reg_pairs = rx_ln_vals->reg_pairs;
+ num_regs = rx_ln_vals->num_regs;
+ for (i = 0; i < inst->num_lanes; i++) {
+ regmap = cdns_phy->regmap_rx_lane_cdb[i + inst->mlane];
+ for (j = 0; j < num_regs; j++)
+ regmap_write(regmap, reg_pairs[j].off,
+ reg_pairs[j].val);
+ }
+ }
+
+ if (phy_type == TYPE_DP)
+ return cdns_torrent_dp_init(phy);
+
+ return 0;
+}
+
+static const struct phy_ops cdns_torrent_phy_ops = {
+ .init = cdns_torrent_phy_init,
+ .configure = cdns_torrent_dp_configure,
+ .power_on = cdns_torrent_phy_on,
+ .power_off = cdns_torrent_phy_off,
+ .owner = THIS_MODULE,
+};
+
+static int cdns_torrent_noop_phy_on(struct phy *phy)
+{
+ /* Give 5ms to 10ms delay for the PIPE clock to be stable */
+ usleep_range(5000, 10000);
+
+ return 0;
+}
+
+static const struct phy_ops noop_ops = {
+ .power_on = cdns_torrent_noop_phy_on,
+ .owner = THIS_MODULE,
+};
+
+static
+int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy)
+{
+ const struct cdns_torrent_data *init_data = cdns_phy->init_data;
+ 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;
+ struct cdns_torrent_vals *pcs_cmn_vals;
+ int i, j, node, mlane, num_lanes, ret;
+ struct cdns_reg_pairs *reg_pairs;
+ enum cdns_torrent_ssc_mode ssc;
+ struct regmap *regmap;
+ u32 num_regs;
+
+ /* Maximum 2 links (subnodes) are supported */
+ if (cdns_phy->nsubnodes != 2)
+ return -EINVAL;
+
+ phy_t1 = cdns_phy->phys[0].phy_type;
+ phy_t2 = cdns_phy->phys[1].phy_type;
+
+ /**
+ * First configure the PHY for first link with phy_t1. Get the array
+ * values as [phy_t1][phy_t2][ssc].
+ */
+ for (node = 0; node < cdns_phy->nsubnodes; node++) {
+ if (node == 1) {
+ /**
+ * If first link with phy_t1 is configured, then
+ * configure the PHY for second link with phy_t2.
+ * Get the array values as [phy_t2][phy_t1][ssc].
+ */
+ swap(phy_t1, phy_t2);
+ }
+
+ mlane = cdns_phy->phys[node].mlane;
+ ssc = cdns_phy->phys[node].ssc_mode;
+ num_lanes = cdns_phy->phys[node].num_lanes;
+
+ /**
+ * PHY configuration specific registers:
+ * link_cmn_vals depend on combination of PHY types being
+ * configured and are common for both PHY types, so array
+ * values should be same for [phy_t1][phy_t2][ssc] and
+ * [phy_t2][phy_t1][ssc].
+ * xcvr_diag_vals also depend on combination of PHY types
+ * being configured, but these can be different for particular
+ * PHY type and are per lane.
+ */
+ link_cmn_vals = init_data->link_cmn_vals[phy_t1][phy_t2][ssc];
+ if (link_cmn_vals) {
+ reg_pairs = link_cmn_vals->reg_pairs;
+ num_regs = link_cmn_vals->num_regs;
+ regmap = cdns_phy->regmap_common_cdb;
+
+ /**
+ * First array value in link_cmn_vals must be of
+ * PHY_PLL_CFG register
+ */
+ regmap_field_write(cdns_phy->phy_pll_cfg,
+ reg_pairs[0].val);
+
+ for (i = 1; i < num_regs; i++)
+ regmap_write(regmap, reg_pairs[i].off,
+ reg_pairs[i].val);
+ }
+
+ xcvr_diag_vals = init_data->xcvr_diag_vals[phy_t1][phy_t2][ssc];
+ if (xcvr_diag_vals) {
+ reg_pairs = xcvr_diag_vals->reg_pairs;
+ num_regs = xcvr_diag_vals->num_regs;
+ for (i = 0; i < num_lanes; i++) {
+ regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane];
+ for (j = 0; j < num_regs; j++)
+ regmap_write(regmap, reg_pairs[j].off,
+ reg_pairs[j].val);
+ }
+ }
+
+ /* PHY PCS common registers configurations */
+ pcs_cmn_vals = init_data->pcs_cmn_vals[phy_t1][phy_t2][ssc];
+ if (pcs_cmn_vals) {
+ reg_pairs = pcs_cmn_vals->reg_pairs;
+ num_regs = pcs_cmn_vals->num_regs;
+ regmap = cdns_phy->regmap_phy_pcs_common_cdb;
+ for (i = 0; i < num_regs; i++)
+ regmap_write(regmap, reg_pairs[i].off,
+ reg_pairs[i].val);
+ }
+
+ /* PMA common registers configurations */
+ cmn_vals = init_data->cmn_vals[ref_clk][phy_t1][phy_t2][ssc];
+ if (cmn_vals) {
+ reg_pairs = cmn_vals->reg_pairs;
+ num_regs = cmn_vals->num_regs;
+ regmap = cdns_phy->regmap_common_cdb;
+ for (i = 0; i < num_regs; i++)
+ regmap_write(regmap, reg_pairs[i].off,
+ reg_pairs[i].val);
+ }
+
+ /* PMA TX lane registers configurations */
+ tx_ln_vals = init_data->tx_ln_vals[ref_clk][phy_t1][phy_t2][ssc];
+ if (tx_ln_vals) {
+ reg_pairs = tx_ln_vals->reg_pairs;
+ num_regs = tx_ln_vals->num_regs;
+ for (i = 0; i < num_lanes; i++) {
+ regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane];
+ for (j = 0; j < num_regs; j++)
+ regmap_write(regmap, reg_pairs[j].off,
+ reg_pairs[j].val);
+ }
+ }
+
+ /* PMA RX lane registers configurations */
+ rx_ln_vals = init_data->rx_ln_vals[ref_clk][phy_t1][phy_t2][ssc];
+ if (rx_ln_vals) {
+ reg_pairs = rx_ln_vals->reg_pairs;
+ num_regs = rx_ln_vals->num_regs;
+ for (i = 0; i < num_lanes; i++) {
+ regmap = cdns_phy->regmap_rx_lane_cdb[i + mlane];
+ for (j = 0; j < num_regs; j++)
+ regmap_write(regmap, reg_pairs[j].off,
+ reg_pairs[j].val);
+ }
+ }
+
+ reset_control_deassert(cdns_phy->phys[node].lnk_rst);
+ }
+
+ /* Take the PHY out of reset */
+ ret = reset_control_deassert(cdns_phy->phy_rst);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void cdns_torrent_clk_cleanup(struct cdns_torrent_phy *cdns_phy)
+{
+ struct device *dev = cdns_phy->dev;
+
+ of_clk_del_provider(dev->of_node);
+}
+
+static int cdns_torrent_clk_register(struct cdns_torrent_phy *cdns_phy)
+{
+ struct device *dev = cdns_phy->dev;
+ struct device_node *node = dev->of_node;
+ struct clk_hw_onecell_data *data;
+ int ret;
+
+ data = devm_kzalloc(dev, struct_size(data, hws, CDNS_TORRENT_OUTPUT_CLOCKS), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->num = CDNS_TORRENT_OUTPUT_CLOCKS;
+ cdns_phy->clk_hw_data = data;
+
+ ret = cdns_torrent_derived_refclk_register(cdns_phy);
+ if (ret) {
+ dev_err(dev, "failed to register derived refclk\n");
+ return ret;
+ }
+
+ ret = cdns_torrent_received_refclk_register(cdns_phy);
+ if (ret) {
+ dev_err(dev, "failed to register received refclk\n");
+ return ret;
+ }
+
+ ret = cdns_torrent_refclk_driver_register(cdns_phy);
+ if (ret) {
+ dev_err(dev, "failed to register refclk driver\n");
+ return ret;
+ }
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data);
+ if (ret) {
+ dev_err(dev, "Failed to add clock provider: %s\n", node->name);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cdns_torrent_reset(struct cdns_torrent_phy *cdns_phy)
+{
+ struct device *dev = cdns_phy->dev;
+
+ cdns_phy->phy_rst = devm_reset_control_get_exclusive_by_index(dev, 0);
+ if (IS_ERR(cdns_phy->phy_rst)) {
+ dev_err(dev, "%s: failed to get reset\n",
+ dev->of_node->full_name);
+ return PTR_ERR(cdns_phy->phy_rst);
+ }
+
+ cdns_phy->apb_rst = devm_reset_control_get_optional_exclusive(dev, "torrent_apb");
+ if (IS_ERR(cdns_phy->apb_rst)) {
+ dev_err(dev, "%s: failed to get apb reset\n",
+ dev->of_node->full_name);
+ return PTR_ERR(cdns_phy->apb_rst);
+ }
+
+ return 0;
+}
+
+static int cdns_torrent_clk(struct cdns_torrent_phy *cdns_phy)
+{
+ struct device *dev = cdns_phy->dev;
+ unsigned long ref_clk_rate;
+ int ret;
+
+ cdns_phy->clk = devm_clk_get(dev, "refclk");
+ if (IS_ERR(cdns_phy->clk)) {
+ dev_err(dev, "phy ref clock not found\n");
+ return PTR_ERR(cdns_phy->clk);
+ }
+
+ ret = clk_prepare_enable(cdns_phy->clk);
+ if (ret) {
+ dev_err(cdns_phy->dev, "Failed to prepare ref clock\n");
+ return ret;
+ }
+
+ ref_clk_rate = clk_get_rate(cdns_phy->clk);
+ if (!ref_clk_rate) {
+ dev_err(cdns_phy->dev, "Failed to get ref clock rate\n");
+ clk_disable_unprepare(cdns_phy->clk);
+ return -EINVAL;
+ }
+
+ switch (ref_clk_rate) {
+ case REF_CLK_19_2MHZ:
+ cdns_phy->ref_clk_rate = CLK_19_2_MHZ;
+ break;
+ case REF_CLK_25MHZ:
+ cdns_phy->ref_clk_rate = CLK_25_MHZ;
+ break;
+ case REF_CLK_100MHZ:
+ cdns_phy->ref_clk_rate = CLK_100_MHZ;
+ break;
+ default:
+ dev_err(cdns_phy->dev, "Invalid Ref Clock Rate\n");
+ clk_disable_unprepare(cdns_phy->clk);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cdns_torrent_phy_probe(struct platform_device *pdev)
+{
+ struct cdns_torrent_phy *cdns_phy;
+ struct device *dev = &pdev->dev;
+ struct phy_provider *phy_provider;
+ const struct cdns_torrent_data *data;
+ struct device_node *child;
+ int ret, subnodes, node = 0, i;
+ u32 total_num_lanes = 0;
+ int already_configured;
+ u8 init_dp_regmap = 0;
+ u32 phy_type;
+
+ /* Get init data for this PHY */
+ data = of_device_get_match_data(dev);
+ if (!data)
+ return -EINVAL;
+
+ cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL);
+ if (!cdns_phy)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, cdns_phy);
+ cdns_phy->dev = dev;
+ cdns_phy->init_data = data;
+
+ cdns_phy->sd_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(cdns_phy->sd_base))
+ return PTR_ERR(cdns_phy->sd_base);
+
+ subnodes = of_get_available_child_count(dev->of_node);
+ if (subnodes == 0) {
+ dev_err(dev, "No available link subnodes found\n");
+ return -EINVAL;
+ }
+
+ ret = cdns_torrent_regmap_init(cdns_phy);
+ if (ret)
+ return ret;
+
+ ret = cdns_torrent_regfield_init(cdns_phy);
+ if (ret)
+ return ret;
+
+ ret = cdns_torrent_clk_register(cdns_phy);
+ if (ret)
+ return ret;
+
+ regmap_field_read(cdns_phy->phy_pma_cmn_ctrl_1, &already_configured);
+
+ if (!already_configured) {
+ ret = cdns_torrent_reset(cdns_phy);
+ if (ret)
+ goto clk_cleanup;
+
+ ret = cdns_torrent_clk(cdns_phy);
+ if (ret)
+ goto clk_cleanup;
+
+ /* Enable APB */
+ reset_control_deassert(cdns_phy->apb_rst);
+ }
+
+ for_each_available_child_of_node(dev->of_node, child) {
+ struct phy *gphy;
+
+ /* PHY subnode name must be 'phy'. */
+ if (!(of_node_name_eq(child, "phy")))
+ continue;
+
+ cdns_phy->phys[node].lnk_rst =
+ of_reset_control_array_get_exclusive(child);
+ if (IS_ERR(cdns_phy->phys[node].lnk_rst)) {
+ dev_err(dev, "%s: failed to get reset\n",
+ child->full_name);
+ ret = PTR_ERR(cdns_phy->phys[node].lnk_rst);
+ goto put_lnk_rst;
+ }
+
+ if (of_property_read_u32(child, "reg",
+ &cdns_phy->phys[node].mlane)) {
+ dev_err(dev, "%s: No \"reg\"-property.\n",
+ child->full_name);
+ ret = -EINVAL;
+ goto put_child;
+ }
+
+ if (of_property_read_u32(child, "cdns,phy-type", &phy_type)) {
+ dev_err(dev, "%s: No \"cdns,phy-type\"-property.\n",
+ child->full_name);
+ ret = -EINVAL;
+ goto put_child;
+ }
+
+ switch (phy_type) {
+ case PHY_TYPE_PCIE:
+ cdns_phy->phys[node].phy_type = TYPE_PCIE;
+ break;
+ case PHY_TYPE_DP:
+ cdns_phy->phys[node].phy_type = TYPE_DP;
+ break;
+ case PHY_TYPE_SGMII:
+ cdns_phy->phys[node].phy_type = TYPE_SGMII;
+ break;
+ case PHY_TYPE_QSGMII:
+ cdns_phy->phys[node].phy_type = TYPE_QSGMII;
+ break;
+ case PHY_TYPE_USB3:
+ cdns_phy->phys[node].phy_type = TYPE_USB;
+ break;
+ default:
+ dev_err(dev, "Unsupported protocol\n");
+ ret = -EINVAL;
+ goto put_child;
+ }
+
+ if (of_property_read_u32(child, "cdns,num-lanes",
+ &cdns_phy->phys[node].num_lanes)) {
+ dev_err(dev, "%s: No \"cdns,num-lanes\"-property.\n",
+ child->full_name);
+ ret = -EINVAL;
+ goto put_child;
+ }
+
+ total_num_lanes += cdns_phy->phys[node].num_lanes;
+
+ /* Get SSC mode */
+ cdns_phy->phys[node].ssc_mode = NO_SSC;
+ of_property_read_u32(child, "cdns,ssc-mode",
+ &cdns_phy->phys[node].ssc_mode);
+
+ if (!already_configured)
+ gphy = devm_phy_create(dev, child, &cdns_torrent_phy_ops);
+ else
+ gphy = devm_phy_create(dev, child, &noop_ops);
+ if (IS_ERR(gphy)) {
+ ret = PTR_ERR(gphy);
+ goto put_child;
+ }
+
+ if (cdns_phy->phys[node].phy_type == TYPE_DP) {
+ switch (cdns_phy->phys[node].num_lanes) {
+ case 1:
+ case 2:
+ case 4:
+ /* valid number of lanes */
+ break;
+ default:
+ dev_err(dev, "unsupported number of lanes: %d\n",
+ cdns_phy->phys[node].num_lanes);
+ ret = -EINVAL;
+ goto put_child;
+ }
+
+ cdns_phy->max_bit_rate = DEFAULT_MAX_BIT_RATE;
+ of_property_read_u32(child, "cdns,max-bit-rate",
+ &cdns_phy->max_bit_rate);
+
+ switch (cdns_phy->max_bit_rate) {
+ case 1620:
+ case 2160:
+ case 2430:
+ case 2700:
+ case 3240:
+ case 4320:
+ case 5400:
+ case 8100:
+ /* valid bit rate */
+ break;
+ default:
+ dev_err(dev, "unsupported max bit rate: %dMbps\n",
+ cdns_phy->max_bit_rate);
+ ret = -EINVAL;
+ goto put_child;
+ }
+
+ /* DPTX registers */
+ cdns_phy->base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(cdns_phy->base)) {
+ ret = PTR_ERR(cdns_phy->base);
+ goto put_child;
+ }
+
+ if (!init_dp_regmap) {
+ ret = cdns_torrent_dp_regmap_init(cdns_phy);
+ if (ret)
+ goto put_child;
+
+ ret = cdns_torrent_dp_regfield_init(cdns_phy);
+ if (ret)
+ goto put_child;
+
+ init_dp_regmap++;
+ }
+
+ dev_dbg(dev, "DP max bit rate %d.%03d Gbps\n",
+ cdns_phy->max_bit_rate / 1000,
+ cdns_phy->max_bit_rate % 1000);
+
+ gphy->attrs.bus_width = cdns_phy->phys[node].num_lanes;
+ gphy->attrs.max_link_rate = cdns_phy->max_bit_rate;
+ gphy->attrs.mode = PHY_MODE_DP;
+ }
+
+ cdns_phy->phys[node].phy = gphy;
+ phy_set_drvdata(gphy, &cdns_phy->phys[node]);
+
+ node++;
+ }
+ cdns_phy->nsubnodes = node;
+
+ if (total_num_lanes > MAX_NUM_LANES) {
+ dev_err(dev, "Invalid lane configuration\n");
+ ret = -EINVAL;
+ goto put_lnk_rst;
+ }
+
+ if (cdns_phy->nsubnodes > 1 && !already_configured) {
+ ret = cdns_torrent_phy_configure_multilink(cdns_phy);
+ if (ret)
+ goto put_lnk_rst;
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider)) {
+ ret = PTR_ERR(phy_provider);
+ goto put_lnk_rst;
+ }
+
+ if (cdns_phy->nsubnodes > 1)
+ dev_dbg(dev, "Multi-link: %s (%d lanes) & %s (%d lanes)",
+ cdns_torrent_get_phy_type(cdns_phy->phys[0].phy_type),
+ cdns_phy->phys[0].num_lanes,
+ cdns_torrent_get_phy_type(cdns_phy->phys[1].phy_type),
+ cdns_phy->phys[1].num_lanes);
+ else
+ dev_dbg(dev, "Single link: %s (%d lanes)",
+ cdns_torrent_get_phy_type(cdns_phy->phys[0].phy_type),
+ cdns_phy->phys[0].num_lanes);
+
+ return 0;
+
+put_child:
+ node++;
+put_lnk_rst:
+ for (i = 0; i < node; i++)
+ reset_control_put(cdns_phy->phys[i].lnk_rst);
+ of_node_put(child);
+ reset_control_assert(cdns_phy->apb_rst);
+ clk_disable_unprepare(cdns_phy->clk);
+clk_cleanup:
+ cdns_torrent_clk_cleanup(cdns_phy);
+ return ret;
+}
+
+static int cdns_torrent_phy_remove(struct platform_device *pdev)
+{
+ struct cdns_torrent_phy *cdns_phy = platform_get_drvdata(pdev);
+ int i;
+
+ reset_control_assert(cdns_phy->phy_rst);
+ reset_control_assert(cdns_phy->apb_rst);
+ for (i = 0; i < cdns_phy->nsubnodes; i++) {
+ reset_control_assert(cdns_phy->phys[i].lnk_rst);
+ reset_control_put(cdns_phy->phys[i].lnk_rst);
+ }
+
+ clk_disable_unprepare(cdns_phy->clk);
+ cdns_torrent_clk_cleanup(cdns_phy);
+
+ return 0;
+}
+
+/* Single DisplayPort(DP) link configuration */
+static struct cdns_reg_pairs sl_dp_link_cmn_regs[] = {
+ {0x0000, PHY_PLL_CFG},
+};
+
+static struct cdns_reg_pairs sl_dp_xcvr_diag_ln_regs[] = {
+ {0x0000, XCVR_DIAG_HSCLK_SEL},
+ {0x0001, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_torrent_vals sl_dp_link_cmn_vals = {
+ .reg_pairs = sl_dp_link_cmn_regs,
+ .num_regs = ARRAY_SIZE(sl_dp_link_cmn_regs),
+};
+
+static struct cdns_torrent_vals sl_dp_xcvr_diag_ln_vals = {
+ .reg_pairs = sl_dp_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(sl_dp_xcvr_diag_ln_regs),
+};
+
+/* Single DP, 19.2 MHz Ref clk, no SSC */
+static struct cdns_reg_pairs sl_dp_19_2_no_ssc_cmn_regs[] = {
+ {0x0014, CMN_SSM_BIAS_TMR},
+ {0x0027, CMN_PLLSM0_PLLPRE_TMR},
+ {0x00A1, CMN_PLLSM0_PLLLOCK_TMR},
+ {0x0027, CMN_PLLSM1_PLLPRE_TMR},
+ {0x00A1, CMN_PLLSM1_PLLLOCK_TMR},
+ {0x0060, CMN_BGCAL_INIT_TMR},
+ {0x0060, CMN_BGCAL_ITER_TMR},
+ {0x0014, CMN_IBCAL_INIT_TMR},
+ {0x0018, CMN_TXPUCAL_INIT_TMR},
+ {0x0005, CMN_TXPUCAL_ITER_TMR},
+ {0x0018, CMN_TXPDCAL_INIT_TMR},
+ {0x0005, CMN_TXPDCAL_ITER_TMR},
+ {0x0240, CMN_RXCAL_INIT_TMR},
+ {0x0005, CMN_RXCAL_ITER_TMR},
+ {0x0002, CMN_SD_CAL_INIT_TMR},
+ {0x0002, CMN_SD_CAL_ITER_TMR},
+ {0x000B, CMN_SD_CAL_REFTIM_START},
+ {0x0137, CMN_SD_CAL_PLLCNT_START},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
+ {0x0004, CMN_PLL0_DSM_DIAG_M0},
+ {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
+ {0x0004, CMN_PLL1_DSM_DIAG_M0},
+ {0x00C0, CMN_PLL0_VCOCAL_INIT_TMR},
+ {0x0004, CMN_PLL0_VCOCAL_ITER_TMR},
+ {0x00C0, CMN_PLL1_VCOCAL_INIT_TMR},
+ {0x0004, CMN_PLL1_VCOCAL_ITER_TMR},
+ {0x0260, CMN_PLL0_VCOCAL_REFTIM_START},
+ {0x0003, CMN_PLL0_VCOCAL_TCTRL},
+ {0x0260, CMN_PLL1_VCOCAL_REFTIM_START},
+ {0x0003, CMN_PLL1_VCOCAL_TCTRL}
+};
+
+static struct cdns_reg_pairs sl_dp_19_2_no_ssc_tx_ln_regs[] = {
+ {0x0780, TX_RCVDET_ST_TMR},
+ {0x00FB, TX_PSC_A0},
+ {0x04AA, TX_PSC_A2},
+ {0x04AA, TX_PSC_A3},
+ {0x000F, XCVR_DIAG_BIDI_CTRL}
+};
+
+static struct cdns_reg_pairs sl_dp_19_2_no_ssc_rx_ln_regs[] = {
+ {0x0000, RX_PSC_A0},
+ {0x0000, RX_PSC_A2},
+ {0x0000, RX_PSC_A3},
+ {0x0000, RX_PSC_CAL},
+ {0x0000, RX_REE_GCSM1_CTRL},
+ {0x0000, RX_REE_GCSM2_CTRL},
+ {0x0000, RX_REE_PERGCSM_CTRL}
+};
+
+static struct cdns_torrent_vals sl_dp_19_2_no_ssc_cmn_vals = {
+ .reg_pairs = sl_dp_19_2_no_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(sl_dp_19_2_no_ssc_cmn_regs),
+};
+
+static struct cdns_torrent_vals sl_dp_19_2_no_ssc_tx_ln_vals = {
+ .reg_pairs = sl_dp_19_2_no_ssc_tx_ln_regs,
+ .num_regs = ARRAY_SIZE(sl_dp_19_2_no_ssc_tx_ln_regs),
+};
+
+static struct cdns_torrent_vals sl_dp_19_2_no_ssc_rx_ln_vals = {
+ .reg_pairs = sl_dp_19_2_no_ssc_rx_ln_regs,
+ .num_regs = ARRAY_SIZE(sl_dp_19_2_no_ssc_rx_ln_regs),
+};
+
+/* Single DP, 25 MHz Ref clk, no SSC */
+static struct cdns_reg_pairs sl_dp_25_no_ssc_cmn_regs[] = {
+ {0x0019, CMN_SSM_BIAS_TMR},
+ {0x0032, CMN_PLLSM0_PLLPRE_TMR},
+ {0x00D1, CMN_PLLSM0_PLLLOCK_TMR},
+ {0x0032, CMN_PLLSM1_PLLPRE_TMR},
+ {0x00D1, CMN_PLLSM1_PLLLOCK_TMR},
+ {0x007D, CMN_BGCAL_INIT_TMR},
+ {0x007D, CMN_BGCAL_ITER_TMR},
+ {0x0019, CMN_IBCAL_INIT_TMR},
+ {0x001E, CMN_TXPUCAL_INIT_TMR},
+ {0x0006, CMN_TXPUCAL_ITER_TMR},
+ {0x001E, CMN_TXPDCAL_INIT_TMR},
+ {0x0006, CMN_TXPDCAL_ITER_TMR},
+ {0x02EE, CMN_RXCAL_INIT_TMR},
+ {0x0006, CMN_RXCAL_ITER_TMR},
+ {0x0002, CMN_SD_CAL_INIT_TMR},
+ {0x0002, CMN_SD_CAL_ITER_TMR},
+ {0x000E, CMN_SD_CAL_REFTIM_START},
+ {0x012B, CMN_SD_CAL_PLLCNT_START},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
+ {0x0004, CMN_PLL0_DSM_DIAG_M0},
+ {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
+ {0x0004, CMN_PLL1_DSM_DIAG_M0},
+ {0x00FA, CMN_PLL0_VCOCAL_INIT_TMR},
+ {0x0004, CMN_PLL0_VCOCAL_ITER_TMR},
+ {0x00FA, CMN_PLL1_VCOCAL_INIT_TMR},
+ {0x0004, CMN_PLL1_VCOCAL_ITER_TMR},
+ {0x0317, CMN_PLL0_VCOCAL_REFTIM_START},
+ {0x0003, CMN_PLL0_VCOCAL_TCTRL},
+ {0x0317, CMN_PLL1_VCOCAL_REFTIM_START},
+ {0x0003, CMN_PLL1_VCOCAL_TCTRL}
+};
+
+static struct cdns_reg_pairs sl_dp_25_no_ssc_tx_ln_regs[] = {
+ {0x09C4, TX_RCVDET_ST_TMR},
+ {0x00FB, TX_PSC_A0},
+ {0x04AA, TX_PSC_A2},
+ {0x04AA, TX_PSC_A3},
+ {0x000F, XCVR_DIAG_BIDI_CTRL}
+};
+
+static struct cdns_reg_pairs sl_dp_25_no_ssc_rx_ln_regs[] = {
+ {0x0000, RX_PSC_A0},
+ {0x0000, RX_PSC_A2},
+ {0x0000, RX_PSC_A3},
+ {0x0000, RX_PSC_CAL},
+ {0x0000, RX_REE_GCSM1_CTRL},
+ {0x0000, RX_REE_GCSM2_CTRL},
+ {0x0000, RX_REE_PERGCSM_CTRL}
+};
+
+static struct cdns_torrent_vals sl_dp_25_no_ssc_cmn_vals = {
+ .reg_pairs = sl_dp_25_no_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(sl_dp_25_no_ssc_cmn_regs),
+};
+
+static struct cdns_torrent_vals sl_dp_25_no_ssc_tx_ln_vals = {
+ .reg_pairs = sl_dp_25_no_ssc_tx_ln_regs,
+ .num_regs = ARRAY_SIZE(sl_dp_25_no_ssc_tx_ln_regs),
+};
+
+static struct cdns_torrent_vals sl_dp_25_no_ssc_rx_ln_vals = {
+ .reg_pairs = sl_dp_25_no_ssc_rx_ln_regs,
+ .num_regs = ARRAY_SIZE(sl_dp_25_no_ssc_rx_ln_regs),
+};
+
+/* Single DP, 100 MHz Ref clk, no SSC */
+static struct cdns_reg_pairs sl_dp_100_no_ssc_cmn_regs[] = {
+ {0x0003, CMN_PLL0_VCOCAL_TCTRL},
+ {0x0003, CMN_PLL1_VCOCAL_TCTRL}
+};
+
+static struct cdns_reg_pairs sl_dp_100_no_ssc_tx_ln_regs[] = {
+ {0x00FB, TX_PSC_A0},
+ {0x04AA, TX_PSC_A2},
+ {0x04AA, TX_PSC_A3},
+ {0x000F, XCVR_DIAG_BIDI_CTRL}
+};
+
+static struct cdns_reg_pairs sl_dp_100_no_ssc_rx_ln_regs[] = {
+ {0x0000, RX_PSC_A0},
+ {0x0000, RX_PSC_A2},
+ {0x0000, RX_PSC_A3},
+ {0x0000, RX_PSC_CAL},
+ {0x0000, RX_REE_GCSM1_CTRL},
+ {0x0000, RX_REE_GCSM2_CTRL},
+ {0x0000, RX_REE_PERGCSM_CTRL}
+};
+
+static struct cdns_torrent_vals sl_dp_100_no_ssc_cmn_vals = {
+ .reg_pairs = sl_dp_100_no_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(sl_dp_100_no_ssc_cmn_regs),
+};
+
+static struct cdns_torrent_vals sl_dp_100_no_ssc_tx_ln_vals = {
+ .reg_pairs = sl_dp_100_no_ssc_tx_ln_regs,
+ .num_regs = ARRAY_SIZE(sl_dp_100_no_ssc_tx_ln_regs),
+};
+
+static struct cdns_torrent_vals sl_dp_100_no_ssc_rx_ln_vals = {
+ .reg_pairs = sl_dp_100_no_ssc_rx_ln_regs,
+ .num_regs = ARRAY_SIZE(sl_dp_100_no_ssc_rx_ln_regs),
+};
+
+/* USB and SGMII/QSGMII link configuration */
+static struct cdns_reg_pairs usb_sgmii_link_cmn_regs[] = {
+ {0x0002, PHY_PLL_CFG},
+ {0x8600, CMN_PDIAG_PLL0_CLK_SEL_M0},
+ {0x0601, CMN_PDIAG_PLL1_CLK_SEL_M0}
+};
+
+static struct cdns_reg_pairs usb_sgmii_xcvr_diag_ln_regs[] = {
+ {0x0000, XCVR_DIAG_HSCLK_SEL},
+ {0x0001, XCVR_DIAG_HSCLK_DIV},
+ {0x0041, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_reg_pairs sgmii_usb_xcvr_diag_ln_regs[] = {
+ {0x0011, XCVR_DIAG_HSCLK_SEL},
+ {0x0003, XCVR_DIAG_HSCLK_DIV},
+ {0x009B, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_torrent_vals usb_sgmii_link_cmn_vals = {
+ .reg_pairs = usb_sgmii_link_cmn_regs,
+ .num_regs = ARRAY_SIZE(usb_sgmii_link_cmn_regs),
+};
+
+static struct cdns_torrent_vals usb_sgmii_xcvr_diag_ln_vals = {
+ .reg_pairs = usb_sgmii_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(usb_sgmii_xcvr_diag_ln_regs),
+};
+
+static struct cdns_torrent_vals sgmii_usb_xcvr_diag_ln_vals = {
+ .reg_pairs = sgmii_usb_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(sgmii_usb_xcvr_diag_ln_regs),
+};
+
+/* PCIe and USB Unique SSC link configuration */
+static struct cdns_reg_pairs pcie_usb_link_cmn_regs[] = {
+ {0x0003, PHY_PLL_CFG},
+ {0x0601, CMN_PDIAG_PLL0_CLK_SEL_M0},
+ {0x0400, CMN_PDIAG_PLL0_CLK_SEL_M1},
+ {0x8600, CMN_PDIAG_PLL1_CLK_SEL_M0}
+};
+
+static struct cdns_reg_pairs pcie_usb_xcvr_diag_ln_regs[] = {
+ {0x0000, XCVR_DIAG_HSCLK_SEL},
+ {0x0001, XCVR_DIAG_HSCLK_DIV},
+ {0x0012, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_reg_pairs usb_pcie_xcvr_diag_ln_regs[] = {
+ {0x0011, XCVR_DIAG_HSCLK_SEL},
+ {0x0001, XCVR_DIAG_HSCLK_DIV},
+ {0x00C9, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_torrent_vals pcie_usb_link_cmn_vals = {
+ .reg_pairs = pcie_usb_link_cmn_regs,
+ .num_regs = ARRAY_SIZE(pcie_usb_link_cmn_regs),
+};
+
+static struct cdns_torrent_vals pcie_usb_xcvr_diag_ln_vals = {
+ .reg_pairs = pcie_usb_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(pcie_usb_xcvr_diag_ln_regs),
+};
+
+static struct cdns_torrent_vals usb_pcie_xcvr_diag_ln_vals = {
+ .reg_pairs = usb_pcie_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(usb_pcie_xcvr_diag_ln_regs),
+};
+
+/* USB 100 MHz Ref clk, internal SSC */
+static struct cdns_reg_pairs usb_100_int_ssc_cmn_regs[] = {
+ {0x0004, CMN_PLL0_DSM_DIAG_M0},
+ {0x0004, CMN_PLL0_DSM_DIAG_M1},
+ {0x0004, CMN_PLL1_DSM_DIAG_M0},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1},
+ {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1},
+ {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1},
+ {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
+ {0x0064, CMN_PLL0_INTDIV_M0},
+ {0x0050, CMN_PLL0_INTDIV_M1},
+ {0x0064, CMN_PLL1_INTDIV_M0},
+ {0x0002, CMN_PLL0_FRACDIVH_M0},
+ {0x0002, CMN_PLL0_FRACDIVH_M1},
+ {0x0002, CMN_PLL1_FRACDIVH_M0},
+ {0x0044, CMN_PLL0_HIGH_THR_M0},
+ {0x0036, CMN_PLL0_HIGH_THR_M1},
+ {0x0044, CMN_PLL1_HIGH_THR_M0},
+ {0x0002, CMN_PDIAG_PLL0_CTRL_M0},
+ {0x0002, CMN_PDIAG_PLL0_CTRL_M1},
+ {0x0002, CMN_PDIAG_PLL1_CTRL_M0},
+ {0x0001, CMN_PLL0_SS_CTRL1_M0},
+ {0x0001, CMN_PLL0_SS_CTRL1_M1},
+ {0x0001, CMN_PLL1_SS_CTRL1_M0},
+ {0x011B, CMN_PLL0_SS_CTRL2_M0},
+ {0x011B, CMN_PLL0_SS_CTRL2_M1},
+ {0x011B, CMN_PLL1_SS_CTRL2_M0},
+ {0x006E, CMN_PLL0_SS_CTRL3_M0},
+ {0x0058, CMN_PLL0_SS_CTRL3_M1},
+ {0x006E, CMN_PLL1_SS_CTRL3_M0},
+ {0x000E, CMN_PLL0_SS_CTRL4_M0},
+ {0x0012, CMN_PLL0_SS_CTRL4_M1},
+ {0x000E, CMN_PLL1_SS_CTRL4_M0},
+ {0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START},
+ {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
+ {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
+ {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
+ {0x00C7, CMN_PLL0_LOCK_REFCNT_START},
+ {0x00C7, CMN_PLL1_LOCK_REFCNT_START},
+ {0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
+ {0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
+ {0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
+ {0x0005, CMN_PLL1_LOCK_PLLCNT_THR},
+ {0x8200, CMN_CDIAG_CDB_PWRI_OVRD},
+ {0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD},
+ {0x007F, CMN_TXPUCAL_TUNE},
+ {0x007F, CMN_TXPDCAL_TUNE}
+};
+
+static struct cdns_torrent_vals usb_100_int_ssc_cmn_vals = {
+ .reg_pairs = usb_100_int_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(usb_100_int_ssc_cmn_regs),
+};
+
+/* Single USB link configuration */
+static struct cdns_reg_pairs sl_usb_link_cmn_regs[] = {
+ {0x0000, PHY_PLL_CFG},
+ {0x8600, CMN_PDIAG_PLL0_CLK_SEL_M0}
+};
+
+static struct cdns_reg_pairs sl_usb_xcvr_diag_ln_regs[] = {
+ {0x0000, XCVR_DIAG_HSCLK_SEL},
+ {0x0001, XCVR_DIAG_HSCLK_DIV},
+ {0x0041, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_torrent_vals sl_usb_link_cmn_vals = {
+ .reg_pairs = sl_usb_link_cmn_regs,
+ .num_regs = ARRAY_SIZE(sl_usb_link_cmn_regs),
+};
+
+static struct cdns_torrent_vals sl_usb_xcvr_diag_ln_vals = {
+ .reg_pairs = sl_usb_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(sl_usb_xcvr_diag_ln_regs),
+};
+
+/* USB PHY PCS common configuration */
+static struct cdns_reg_pairs usb_phy_pcs_cmn_regs[] = {
+ {0x0A0A, PHY_PIPE_USB3_GEN2_PRE_CFG0},
+ {0x1000, PHY_PIPE_USB3_GEN2_POST_CFG0},
+ {0x0010, PHY_PIPE_USB3_GEN2_POST_CFG1}
+};
+
+static struct cdns_torrent_vals usb_phy_pcs_cmn_vals = {
+ .reg_pairs = usb_phy_pcs_cmn_regs,
+ .num_regs = ARRAY_SIZE(usb_phy_pcs_cmn_regs),
+};
+
+/* USB 100 MHz Ref clk, no SSC */
+static struct cdns_reg_pairs sl_usb_100_no_ssc_cmn_regs[] = {
+ {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0},
+ {0x001E, CMN_PLL1_DSM_FBH_OVRD_M0},
+ {0x000C, CMN_PLL1_DSM_FBL_OVRD_M0},
+ {0x0003, CMN_PLL0_VCOCAL_TCTRL},
+ {0x0003, CMN_PLL1_VCOCAL_TCTRL},
+ {0x8200, CMN_CDIAG_CDB_PWRI_OVRD},
+ {0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD}
+};
+
+static struct cdns_torrent_vals sl_usb_100_no_ssc_cmn_vals = {
+ .reg_pairs = sl_usb_100_no_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(sl_usb_100_no_ssc_cmn_regs),
+};
+
+static struct cdns_reg_pairs usb_100_no_ssc_cmn_regs[] = {
+ {0x8200, CMN_CDIAG_CDB_PWRI_OVRD},
+ {0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD},
+ {0x007F, CMN_TXPUCAL_TUNE},
+ {0x007F, CMN_TXPDCAL_TUNE}
+};
+
+static struct cdns_reg_pairs usb_100_no_ssc_tx_ln_regs[] = {
+ {0x02FF, TX_PSC_A0},
+ {0x06AF, TX_PSC_A1},
+ {0x06AE, TX_PSC_A2},
+ {0x06AE, TX_PSC_A3},
+ {0x2A82, TX_TXCC_CTRL},
+ {0x0014, TX_TXCC_CPOST_MULT_01},
+ {0x0003, XCVR_DIAG_PSC_OVRD}
+};
+
+static struct cdns_reg_pairs usb_100_no_ssc_rx_ln_regs[] = {
+ {0x0D1D, RX_PSC_A0},
+ {0x0D1D, RX_PSC_A1},
+ {0x0D00, RX_PSC_A2},
+ {0x0500, RX_PSC_A3},
+ {0x0013, RX_SIGDET_HL_FILT_TMR},
+ {0x0000, RX_REE_GCSM1_CTRL},
+ {0x0C02, RX_REE_ATTEN_THR},
+ {0x0330, RX_REE_SMGM_CTRL1},
+ {0x0300, RX_REE_SMGM_CTRL2},
+ {0x0019, RX_REE_TAP1_CLIP},
+ {0x0019, RX_REE_TAP2TON_CLIP},
+ {0x1004, RX_DIAG_SIGDET_TUNE},
+ {0x00F9, RX_DIAG_NQST_CTRL},
+ {0x0C01, RX_DIAG_DFE_AMP_TUNE_2},
+ {0x0002, RX_DIAG_DFE_AMP_TUNE_3},
+ {0x0000, RX_DIAG_PI_CAP},
+ {0x0031, RX_DIAG_PI_RATE},
+ {0x0001, RX_DIAG_ACYA},
+ {0x018C, RX_CDRLF_CNFG},
+ {0x0003, RX_CDRLF_CNFG3}
+};
+
+static struct cdns_torrent_vals usb_100_no_ssc_cmn_vals = {
+ .reg_pairs = usb_100_no_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(usb_100_no_ssc_cmn_regs),
+};
+
+static struct cdns_torrent_vals usb_100_no_ssc_tx_ln_vals = {
+ .reg_pairs = usb_100_no_ssc_tx_ln_regs,
+ .num_regs = ARRAY_SIZE(usb_100_no_ssc_tx_ln_regs),
+};
+
+static struct cdns_torrent_vals usb_100_no_ssc_rx_ln_vals = {
+ .reg_pairs = usb_100_no_ssc_rx_ln_regs,
+ .num_regs = ARRAY_SIZE(usb_100_no_ssc_rx_ln_regs),
+};
+
+/* Single link USB, 100 MHz Ref clk, internal SSC */
+static struct cdns_reg_pairs sl_usb_100_int_ssc_cmn_regs[] = {
+ {0x0004, CMN_PLL0_DSM_DIAG_M0},
+ {0x0004, CMN_PLL1_DSM_DIAG_M0},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
+ {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
+ {0x0064, CMN_PLL0_INTDIV_M0},
+ {0x0064, CMN_PLL1_INTDIV_M0},
+ {0x0002, CMN_PLL0_FRACDIVH_M0},
+ {0x0002, CMN_PLL1_FRACDIVH_M0},
+ {0x0044, CMN_PLL0_HIGH_THR_M0},
+ {0x0044, CMN_PLL1_HIGH_THR_M0},
+ {0x0002, CMN_PDIAG_PLL0_CTRL_M0},
+ {0x0002, CMN_PDIAG_PLL1_CTRL_M0},
+ {0x0001, CMN_PLL0_SS_CTRL1_M0},
+ {0x0001, CMN_PLL1_SS_CTRL1_M0},
+ {0x011B, CMN_PLL0_SS_CTRL2_M0},
+ {0x011B, CMN_PLL1_SS_CTRL2_M0},
+ {0x006E, CMN_PLL0_SS_CTRL3_M0},
+ {0x006E, CMN_PLL1_SS_CTRL3_M0},
+ {0x000E, CMN_PLL0_SS_CTRL4_M0},
+ {0x000E, CMN_PLL1_SS_CTRL4_M0},
+ {0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START},
+ {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
+ {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
+ {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
+ {0x0003, CMN_PLL0_VCOCAL_TCTRL},
+ {0x0003, CMN_PLL1_VCOCAL_TCTRL},
+ {0x00C7, CMN_PLL0_LOCK_REFCNT_START},
+ {0x00C7, CMN_PLL1_LOCK_REFCNT_START},
+ {0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
+ {0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
+ {0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
+ {0x0005, CMN_PLL1_LOCK_PLLCNT_THR},
+ {0x8200, CMN_CDIAG_CDB_PWRI_OVRD},
+ {0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD}
+};
+
+static struct cdns_torrent_vals sl_usb_100_int_ssc_cmn_vals = {
+ .reg_pairs = sl_usb_100_int_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(sl_usb_100_int_ssc_cmn_regs),
+};
+
+/* PCIe and SGMII/QSGMII Unique SSC link configuration */
+static struct cdns_reg_pairs pcie_sgmii_link_cmn_regs[] = {
+ {0x0003, PHY_PLL_CFG},
+ {0x0601, CMN_PDIAG_PLL0_CLK_SEL_M0},
+ {0x0400, CMN_PDIAG_PLL0_CLK_SEL_M1},
+ {0x0601, CMN_PDIAG_PLL1_CLK_SEL_M0}
+};
+
+static struct cdns_reg_pairs pcie_sgmii_xcvr_diag_ln_regs[] = {
+ {0x0000, XCVR_DIAG_HSCLK_SEL},
+ {0x0001, XCVR_DIAG_HSCLK_DIV},
+ {0x0012, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_reg_pairs sgmii_pcie_xcvr_diag_ln_regs[] = {
+ {0x0011, XCVR_DIAG_HSCLK_SEL},
+ {0x0003, XCVR_DIAG_HSCLK_DIV},
+ {0x009B, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_torrent_vals pcie_sgmii_link_cmn_vals = {
+ .reg_pairs = pcie_sgmii_link_cmn_regs,
+ .num_regs = ARRAY_SIZE(pcie_sgmii_link_cmn_regs),
+};
+
+static struct cdns_torrent_vals pcie_sgmii_xcvr_diag_ln_vals = {
+ .reg_pairs = pcie_sgmii_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(pcie_sgmii_xcvr_diag_ln_regs),
+};
+
+static struct cdns_torrent_vals sgmii_pcie_xcvr_diag_ln_vals = {
+ .reg_pairs = sgmii_pcie_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(sgmii_pcie_xcvr_diag_ln_regs),
+};
+
+/* SGMII 100 MHz Ref clk, no SSC */
+static struct cdns_reg_pairs sl_sgmii_100_no_ssc_cmn_regs[] = {
+ {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0},
+ {0x001E, CMN_PLL1_DSM_FBH_OVRD_M0},
+ {0x000C, CMN_PLL1_DSM_FBL_OVRD_M0},
+ {0x0003, CMN_PLL0_VCOCAL_TCTRL},
+ {0x0003, CMN_PLL1_VCOCAL_TCTRL}
+};
+
+static struct cdns_torrent_vals sl_sgmii_100_no_ssc_cmn_vals = {
+ .reg_pairs = sl_sgmii_100_no_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(sl_sgmii_100_no_ssc_cmn_regs),
+};
+
+static struct cdns_reg_pairs sgmii_100_no_ssc_cmn_regs[] = {
+ {0x007F, CMN_TXPUCAL_TUNE},
+ {0x007F, CMN_TXPDCAL_TUNE}
+};
+
+static struct cdns_reg_pairs sgmii_100_no_ssc_tx_ln_regs[] = {
+ {0x00F3, TX_PSC_A0},
+ {0x04A2, TX_PSC_A2},
+ {0x04A2, TX_PSC_A3},
+ {0x0000, TX_TXCC_CPOST_MULT_00},
+ {0x00B3, DRV_DIAG_TX_DRV}
+};
+
+static struct cdns_reg_pairs ti_sgmii_100_no_ssc_tx_ln_regs[] = {
+ {0x00F3, TX_PSC_A0},
+ {0x04A2, TX_PSC_A2},
+ {0x04A2, TX_PSC_A3},
+ {0x0000, TX_TXCC_CPOST_MULT_00},
+ {0x00B3, DRV_DIAG_TX_DRV},
+ {0x4000, XCVR_DIAG_RXCLK_CTRL},
+};
+
+static struct cdns_reg_pairs sgmii_100_no_ssc_rx_ln_regs[] = {
+ {0x091D, RX_PSC_A0},
+ {0x0900, RX_PSC_A2},
+ {0x0100, RX_PSC_A3},
+ {0x03C7, RX_REE_GCSM1_EQENM_PH1},
+ {0x01C7, RX_REE_GCSM1_EQENM_PH2},
+ {0x0000, RX_DIAG_DFE_CTRL},
+ {0x0019, RX_REE_TAP1_CLIP},
+ {0x0019, RX_REE_TAP2TON_CLIP},
+ {0x0098, RX_DIAG_NQST_CTRL},
+ {0x0C01, RX_DIAG_DFE_AMP_TUNE_2},
+ {0x0000, RX_DIAG_DFE_AMP_TUNE_3},
+ {0x0000, RX_DIAG_PI_CAP},
+ {0x0010, RX_DIAG_PI_RATE},
+ {0x0001, RX_DIAG_ACYA},
+ {0x018C, RX_CDRLF_CNFG},
+};
+
+static struct cdns_torrent_vals sgmii_100_no_ssc_cmn_vals = {
+ .reg_pairs = sgmii_100_no_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(sgmii_100_no_ssc_cmn_regs),
+};
+
+static struct cdns_torrent_vals sgmii_100_no_ssc_tx_ln_vals = {
+ .reg_pairs = sgmii_100_no_ssc_tx_ln_regs,
+ .num_regs = ARRAY_SIZE(sgmii_100_no_ssc_tx_ln_regs),
+};
+
+static struct cdns_torrent_vals ti_sgmii_100_no_ssc_tx_ln_vals = {
+ .reg_pairs = ti_sgmii_100_no_ssc_tx_ln_regs,
+ .num_regs = ARRAY_SIZE(ti_sgmii_100_no_ssc_tx_ln_regs),
+};
+
+static struct cdns_torrent_vals sgmii_100_no_ssc_rx_ln_vals = {
+ .reg_pairs = sgmii_100_no_ssc_rx_ln_regs,
+ .num_regs = ARRAY_SIZE(sgmii_100_no_ssc_rx_ln_regs),
+};
+
+/* SGMII 100 MHz Ref clk, internal SSC */
+static struct cdns_reg_pairs sgmii_100_int_ssc_cmn_regs[] = {
+ {0x0004, CMN_PLL0_DSM_DIAG_M0},
+ {0x0004, CMN_PLL0_DSM_DIAG_M1},
+ {0x0004, CMN_PLL1_DSM_DIAG_M0},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1},
+ {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1},
+ {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1},
+ {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
+ {0x0064, CMN_PLL0_INTDIV_M0},
+ {0x0050, CMN_PLL0_INTDIV_M1},
+ {0x0064, CMN_PLL1_INTDIV_M0},
+ {0x0002, CMN_PLL0_FRACDIVH_M0},
+ {0x0002, CMN_PLL0_FRACDIVH_M1},
+ {0x0002, CMN_PLL1_FRACDIVH_M0},
+ {0x0044, CMN_PLL0_HIGH_THR_M0},
+ {0x0036, CMN_PLL0_HIGH_THR_M1},
+ {0x0044, CMN_PLL1_HIGH_THR_M0},
+ {0x0002, CMN_PDIAG_PLL0_CTRL_M0},
+ {0x0002, CMN_PDIAG_PLL0_CTRL_M1},
+ {0x0002, CMN_PDIAG_PLL1_CTRL_M0},
+ {0x0001, CMN_PLL0_SS_CTRL1_M0},
+ {0x0001, CMN_PLL0_SS_CTRL1_M1},
+ {0x0001, CMN_PLL1_SS_CTRL1_M0},
+ {0x011B, CMN_PLL0_SS_CTRL2_M0},
+ {0x011B, CMN_PLL0_SS_CTRL2_M1},
+ {0x011B, CMN_PLL1_SS_CTRL2_M0},
+ {0x006E, CMN_PLL0_SS_CTRL3_M0},
+ {0x0058, CMN_PLL0_SS_CTRL3_M1},
+ {0x006E, CMN_PLL1_SS_CTRL3_M0},
+ {0x000E, CMN_PLL0_SS_CTRL4_M0},
+ {0x0012, CMN_PLL0_SS_CTRL4_M1},
+ {0x000E, CMN_PLL1_SS_CTRL4_M0},
+ {0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START},
+ {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
+ {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
+ {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
+ {0x00C7, CMN_PLL0_LOCK_REFCNT_START},
+ {0x00C7, CMN_PLL1_LOCK_REFCNT_START},
+ {0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
+ {0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
+ {0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
+ {0x0005, CMN_PLL1_LOCK_PLLCNT_THR},
+ {0x007F, CMN_TXPUCAL_TUNE},
+ {0x007F, CMN_TXPDCAL_TUNE}
+};
+
+static struct cdns_torrent_vals sgmii_100_int_ssc_cmn_vals = {
+ .reg_pairs = sgmii_100_int_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(sgmii_100_int_ssc_cmn_regs),
+};
+
+/* QSGMII 100 MHz Ref clk, no SSC */
+static struct cdns_reg_pairs sl_qsgmii_100_no_ssc_cmn_regs[] = {
+ {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0},
+ {0x001E, CMN_PLL1_DSM_FBH_OVRD_M0},
+ {0x000C, CMN_PLL1_DSM_FBL_OVRD_M0},
+ {0x0003, CMN_PLL0_VCOCAL_TCTRL},
+ {0x0003, CMN_PLL1_VCOCAL_TCTRL}
+};
+
+static struct cdns_torrent_vals sl_qsgmii_100_no_ssc_cmn_vals = {
+ .reg_pairs = sl_qsgmii_100_no_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(sl_qsgmii_100_no_ssc_cmn_regs),
+};
+
+static struct cdns_reg_pairs qsgmii_100_no_ssc_cmn_regs[] = {
+ {0x007F, CMN_TXPUCAL_TUNE},
+ {0x007F, CMN_TXPDCAL_TUNE}
+};
+
+static struct cdns_reg_pairs qsgmii_100_no_ssc_tx_ln_regs[] = {
+ {0x00F3, TX_PSC_A0},
+ {0x04A2, TX_PSC_A2},
+ {0x04A2, TX_PSC_A3},
+ {0x0000, TX_TXCC_CPOST_MULT_00},
+ {0x0011, TX_TXCC_MGNFS_MULT_100},
+ {0x0003, DRV_DIAG_TX_DRV}
+};
+
+static struct cdns_reg_pairs ti_qsgmii_100_no_ssc_tx_ln_regs[] = {
+ {0x00F3, TX_PSC_A0},
+ {0x04A2, TX_PSC_A2},
+ {0x04A2, TX_PSC_A3},
+ {0x0000, TX_TXCC_CPOST_MULT_00},
+ {0x0011, TX_TXCC_MGNFS_MULT_100},
+ {0x0003, DRV_DIAG_TX_DRV},
+ {0x4000, XCVR_DIAG_RXCLK_CTRL},
+};
+
+static struct cdns_reg_pairs qsgmii_100_no_ssc_rx_ln_regs[] = {
+ {0x091D, RX_PSC_A0},
+ {0x0900, RX_PSC_A2},
+ {0x0100, RX_PSC_A3},
+ {0x03C7, RX_REE_GCSM1_EQENM_PH1},
+ {0x01C7, RX_REE_GCSM1_EQENM_PH2},
+ {0x0000, RX_DIAG_DFE_CTRL},
+ {0x0019, RX_REE_TAP1_CLIP},
+ {0x0019, RX_REE_TAP2TON_CLIP},
+ {0x0098, RX_DIAG_NQST_CTRL},
+ {0x0C01, RX_DIAG_DFE_AMP_TUNE_2},
+ {0x0000, RX_DIAG_DFE_AMP_TUNE_3},
+ {0x0000, RX_DIAG_PI_CAP},
+ {0x0010, RX_DIAG_PI_RATE},
+ {0x0001, RX_DIAG_ACYA},
+ {0x018C, RX_CDRLF_CNFG},
+};
+
+static struct cdns_torrent_vals qsgmii_100_no_ssc_cmn_vals = {
+ .reg_pairs = qsgmii_100_no_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_cmn_regs),
+};
+
+static struct cdns_torrent_vals qsgmii_100_no_ssc_tx_ln_vals = {
+ .reg_pairs = qsgmii_100_no_ssc_tx_ln_regs,
+ .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_tx_ln_regs),
+};
+
+static struct cdns_torrent_vals ti_qsgmii_100_no_ssc_tx_ln_vals = {
+ .reg_pairs = ti_qsgmii_100_no_ssc_tx_ln_regs,
+ .num_regs = ARRAY_SIZE(ti_qsgmii_100_no_ssc_tx_ln_regs),
+};
+
+static struct cdns_torrent_vals qsgmii_100_no_ssc_rx_ln_vals = {
+ .reg_pairs = qsgmii_100_no_ssc_rx_ln_regs,
+ .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_rx_ln_regs),
+};
+
+/* QSGMII 100 MHz Ref clk, internal SSC */
+static struct cdns_reg_pairs qsgmii_100_int_ssc_cmn_regs[] = {
+ {0x0004, CMN_PLL0_DSM_DIAG_M0},
+ {0x0004, CMN_PLL0_DSM_DIAG_M1},
+ {0x0004, CMN_PLL1_DSM_DIAG_M0},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1},
+ {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1},
+ {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1},
+ {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
+ {0x0064, CMN_PLL0_INTDIV_M0},
+ {0x0050, CMN_PLL0_INTDIV_M1},
+ {0x0064, CMN_PLL1_INTDIV_M0},
+ {0x0002, CMN_PLL0_FRACDIVH_M0},
+ {0x0002, CMN_PLL0_FRACDIVH_M1},
+ {0x0002, CMN_PLL1_FRACDIVH_M0},
+ {0x0044, CMN_PLL0_HIGH_THR_M0},
+ {0x0036, CMN_PLL0_HIGH_THR_M1},
+ {0x0044, CMN_PLL1_HIGH_THR_M0},
+ {0x0002, CMN_PDIAG_PLL0_CTRL_M0},
+ {0x0002, CMN_PDIAG_PLL0_CTRL_M1},
+ {0x0002, CMN_PDIAG_PLL1_CTRL_M0},
+ {0x0001, CMN_PLL0_SS_CTRL1_M0},
+ {0x0001, CMN_PLL0_SS_CTRL1_M1},
+ {0x0001, CMN_PLL1_SS_CTRL1_M0},
+ {0x011B, CMN_PLL0_SS_CTRL2_M0},
+ {0x011B, CMN_PLL0_SS_CTRL2_M1},
+ {0x011B, CMN_PLL1_SS_CTRL2_M0},
+ {0x006E, CMN_PLL0_SS_CTRL3_M0},
+ {0x0058, CMN_PLL0_SS_CTRL3_M1},
+ {0x006E, CMN_PLL1_SS_CTRL3_M0},
+ {0x000E, CMN_PLL0_SS_CTRL4_M0},
+ {0x0012, CMN_PLL0_SS_CTRL4_M1},
+ {0x000E, CMN_PLL1_SS_CTRL4_M0},
+ {0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START},
+ {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
+ {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
+ {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
+ {0x00C7, CMN_PLL0_LOCK_REFCNT_START},
+ {0x00C7, CMN_PLL1_LOCK_REFCNT_START},
+ {0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
+ {0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
+ {0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
+ {0x0005, CMN_PLL1_LOCK_PLLCNT_THR},
+ {0x007F, CMN_TXPUCAL_TUNE},
+ {0x007F, CMN_TXPDCAL_TUNE}
+};
+
+static struct cdns_torrent_vals qsgmii_100_int_ssc_cmn_vals = {
+ .reg_pairs = qsgmii_100_int_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(qsgmii_100_int_ssc_cmn_regs),
+};
+
+/* Single SGMII/QSGMII link configuration */
+static struct cdns_reg_pairs sl_sgmii_link_cmn_regs[] = {
+ {0x0000, PHY_PLL_CFG},
+ {0x0601, CMN_PDIAG_PLL0_CLK_SEL_M0}
+};
+
+static struct cdns_reg_pairs sl_sgmii_xcvr_diag_ln_regs[] = {
+ {0x0000, XCVR_DIAG_HSCLK_SEL},
+ {0x0003, XCVR_DIAG_HSCLK_DIV},
+ {0x0013, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_torrent_vals sl_sgmii_link_cmn_vals = {
+ .reg_pairs = sl_sgmii_link_cmn_regs,
+ .num_regs = ARRAY_SIZE(sl_sgmii_link_cmn_regs),
+};
+
+static struct cdns_torrent_vals sl_sgmii_xcvr_diag_ln_vals = {
+ .reg_pairs = sl_sgmii_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(sl_sgmii_xcvr_diag_ln_regs),
+};
+
+/* Multi link PCIe, 100 MHz Ref clk, internal SSC */
+static struct cdns_reg_pairs pcie_100_int_ssc_cmn_regs[] = {
+ {0x0004, CMN_PLL0_DSM_DIAG_M0},
+ {0x0004, CMN_PLL0_DSM_DIAG_M1},
+ {0x0004, CMN_PLL1_DSM_DIAG_M0},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1},
+ {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1},
+ {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1},
+ {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
+ {0x0064, CMN_PLL0_INTDIV_M0},
+ {0x0050, CMN_PLL0_INTDIV_M1},
+ {0x0064, CMN_PLL1_INTDIV_M0},
+ {0x0002, CMN_PLL0_FRACDIVH_M0},
+ {0x0002, CMN_PLL0_FRACDIVH_M1},
+ {0x0002, CMN_PLL1_FRACDIVH_M0},
+ {0x0044, CMN_PLL0_HIGH_THR_M0},
+ {0x0036, CMN_PLL0_HIGH_THR_M1},
+ {0x0044, CMN_PLL1_HIGH_THR_M0},
+ {0x0002, CMN_PDIAG_PLL0_CTRL_M0},
+ {0x0002, CMN_PDIAG_PLL0_CTRL_M1},
+ {0x0002, CMN_PDIAG_PLL1_CTRL_M0},
+ {0x0001, CMN_PLL0_SS_CTRL1_M0},
+ {0x0001, CMN_PLL0_SS_CTRL1_M1},
+ {0x0001, CMN_PLL1_SS_CTRL1_M0},
+ {0x011B, CMN_PLL0_SS_CTRL2_M0},
+ {0x011B, CMN_PLL0_SS_CTRL2_M1},
+ {0x011B, CMN_PLL1_SS_CTRL2_M0},
+ {0x006E, CMN_PLL0_SS_CTRL3_M0},
+ {0x0058, CMN_PLL0_SS_CTRL3_M1},
+ {0x006E, CMN_PLL1_SS_CTRL3_M0},
+ {0x000E, CMN_PLL0_SS_CTRL4_M0},
+ {0x0012, CMN_PLL0_SS_CTRL4_M1},
+ {0x000E, CMN_PLL1_SS_CTRL4_M0},
+ {0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START},
+ {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
+ {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
+ {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
+ {0x00C7, CMN_PLL0_LOCK_REFCNT_START},
+ {0x00C7, CMN_PLL1_LOCK_REFCNT_START},
+ {0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
+ {0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
+ {0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
+ {0x0005, CMN_PLL1_LOCK_PLLCNT_THR}
+};
+
+static struct cdns_torrent_vals pcie_100_int_ssc_cmn_vals = {
+ .reg_pairs = pcie_100_int_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(pcie_100_int_ssc_cmn_regs),
+};
+
+/* Single link PCIe, 100 MHz Ref clk, internal SSC */
+static struct cdns_reg_pairs sl_pcie_100_int_ssc_cmn_regs[] = {
+ {0x0004, CMN_PLL0_DSM_DIAG_M0},
+ {0x0004, CMN_PLL0_DSM_DIAG_M1},
+ {0x0004, CMN_PLL1_DSM_DIAG_M0},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
+ {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1},
+ {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
+ {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1},
+ {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
+ {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1},
+ {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
+ {0x0064, CMN_PLL0_INTDIV_M0},
+ {0x0050, CMN_PLL0_INTDIV_M1},
+ {0x0050, CMN_PLL1_INTDIV_M0},
+ {0x0002, CMN_PLL0_FRACDIVH_M0},
+ {0x0002, CMN_PLL0_FRACDIVH_M1},
+ {0x0002, CMN_PLL1_FRACDIVH_M0},
+ {0x0044, CMN_PLL0_HIGH_THR_M0},
+ {0x0036, CMN_PLL0_HIGH_THR_M1},
+ {0x0036, CMN_PLL1_HIGH_THR_M0},
+ {0x0002, CMN_PDIAG_PLL0_CTRL_M0},
+ {0x0002, CMN_PDIAG_PLL0_CTRL_M1},
+ {0x0002, CMN_PDIAG_PLL1_CTRL_M0},
+ {0x0001, CMN_PLL0_SS_CTRL1_M0},
+ {0x0001, CMN_PLL0_SS_CTRL1_M1},
+ {0x0001, CMN_PLL1_SS_CTRL1_M0},
+ {0x011B, CMN_PLL0_SS_CTRL2_M0},
+ {0x011B, CMN_PLL0_SS_CTRL2_M1},
+ {0x011B, CMN_PLL1_SS_CTRL2_M0},
+ {0x006E, CMN_PLL0_SS_CTRL3_M0},
+ {0x0058, CMN_PLL0_SS_CTRL3_M1},
+ {0x0058, CMN_PLL1_SS_CTRL3_M0},
+ {0x000E, CMN_PLL0_SS_CTRL4_M0},
+ {0x0012, CMN_PLL0_SS_CTRL4_M1},
+ {0x0012, CMN_PLL1_SS_CTRL4_M0},
+ {0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START},
+ {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
+ {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
+ {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
+ {0x00C7, CMN_PLL0_LOCK_REFCNT_START},
+ {0x00C7, CMN_PLL1_LOCK_REFCNT_START},
+ {0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
+ {0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
+ {0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
+ {0x0005, CMN_PLL1_LOCK_PLLCNT_THR}
+};
+
+static struct cdns_torrent_vals sl_pcie_100_int_ssc_cmn_vals = {
+ .reg_pairs = sl_pcie_100_int_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(sl_pcie_100_int_ssc_cmn_regs),
+};
+
+/* PCIe, 100 MHz Ref clk, no SSC & external SSC */
+static struct cdns_reg_pairs pcie_100_ext_no_ssc_cmn_regs[] = {
+ {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0},
+ {0x001E, CMN_PLL1_DSM_FBH_OVRD_M0},
+ {0x000C, CMN_PLL1_DSM_FBL_OVRD_M0}
+};
+
+static struct cdns_reg_pairs pcie_100_ext_no_ssc_rx_ln_regs[] = {
+ {0x0019, RX_REE_TAP1_CLIP},
+ {0x0019, RX_REE_TAP2TON_CLIP},
+ {0x0001, RX_DIAG_ACYA}
+};
+
+static struct cdns_torrent_vals pcie_100_no_ssc_cmn_vals = {
+ .reg_pairs = pcie_100_ext_no_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(pcie_100_ext_no_ssc_cmn_regs),
+};
+
+static struct cdns_torrent_vals pcie_100_no_ssc_rx_ln_vals = {
+ .reg_pairs = pcie_100_ext_no_ssc_rx_ln_regs,
+ .num_regs = ARRAY_SIZE(pcie_100_ext_no_ssc_rx_ln_regs),
+};
+
+static const struct cdns_torrent_data cdns_map_torrent = {
+ .block_offset_shift = 0x2,
+ .reg_offset_shift = 0x2,
+ .link_cmn_vals = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_link_cmn_vals,
+ },
+ },
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = NULL,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &pcie_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &pcie_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &pcie_usb_link_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals,
+ [INTERNAL_SSC] = &pcie_usb_link_cmn_vals,
+ },
+ },
+ [TYPE_SGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_sgmii_link_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &pcie_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &usb_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_sgmii_link_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &pcie_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &usb_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_usb_link_cmn_vals,
+ [EXTERNAL_SSC] = &sl_usb_link_cmn_vals,
+ [INTERNAL_SSC] = &sl_usb_link_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &pcie_usb_link_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals,
+ [INTERNAL_SSC] = &pcie_usb_link_cmn_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &usb_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &usb_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ },
+ },
+ },
+ .xcvr_diag_vals = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_xcvr_diag_ln_vals,
+ },
+ },
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = NULL,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &pcie_usb_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals,
+ },
+ },
+ [TYPE_SGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_usb_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &usb_pcie_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
+ },
+ },
+ },
+ .pcs_cmn_vals = {
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &usb_phy_pcs_cmn_vals,
+ [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &usb_phy_pcs_cmn_vals,
+ [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &usb_phy_pcs_cmn_vals,
+ [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &usb_phy_pcs_cmn_vals,
+ [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ },
+ },
+ },
+ .cmn_vals = {
+ [CLK_19_2_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_19_2_no_ssc_cmn_vals,
+ },
+ },
+ },
+ [CLK_25_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_25_no_ssc_cmn_vals,
+ },
+ },
+ },
+ [CLK_100_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals,
+ },
+ },
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = &sl_pcie_100_int_ssc_cmn_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
+ },
+ },
+ [TYPE_SGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_sgmii_100_no_ssc_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &sgmii_100_int_ssc_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_qsgmii_100_no_ssc_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &qsgmii_100_int_ssc_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &usb_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &usb_100_int_ssc_cmn_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
+ },
+ },
+ },
+ },
+ .tx_ln_vals = {
+ [CLK_19_2_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_19_2_no_ssc_tx_ln_vals,
+ },
+ },
+ },
+ [CLK_25_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_25_no_ssc_tx_ln_vals,
+ },
+ },
+ },
+ [CLK_100_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_100_no_ssc_tx_ln_vals,
+ },
+ },
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = NULL,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = NULL,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = NULL,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = NULL,
+ },
+ },
+ [TYPE_SGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ },
+ },
+ },
+ },
+ .rx_ln_vals = {
+ [CLK_19_2_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_19_2_no_ssc_rx_ln_vals,
+ },
+ },
+ },
+ [CLK_25_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_25_no_ssc_rx_ln_vals,
+ },
+ },
+ },
+ [CLK_100_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_100_no_ssc_rx_ln_vals,
+ },
+ },
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ },
+ },
+ [TYPE_SGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ },
+ },
+ },
+ },
+};
+
+static const struct cdns_torrent_data ti_j721e_map_torrent = {
+ .block_offset_shift = 0x0,
+ .reg_offset_shift = 0x1,
+ .link_cmn_vals = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_link_cmn_vals,
+ },
+ },
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = NULL,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &pcie_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &pcie_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &pcie_usb_link_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals,
+ [INTERNAL_SSC] = &pcie_usb_link_cmn_vals,
+ },
+ },
+ [TYPE_SGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_sgmii_link_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &pcie_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &usb_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_sgmii_link_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &pcie_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &usb_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_usb_link_cmn_vals,
+ [EXTERNAL_SSC] = &sl_usb_link_cmn_vals,
+ [INTERNAL_SSC] = &sl_usb_link_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &pcie_usb_link_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals,
+ [INTERNAL_SSC] = &pcie_usb_link_cmn_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &usb_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &usb_sgmii_link_cmn_vals,
+ [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
+ },
+ },
+ },
+ .xcvr_diag_vals = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_xcvr_diag_ln_vals,
+ },
+ },
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = NULL,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &pcie_usb_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals,
+ },
+ },
+ [TYPE_SGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_usb_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &usb_pcie_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
+ [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
+ [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
+ },
+ },
+ },
+ .pcs_cmn_vals = {
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &usb_phy_pcs_cmn_vals,
+ [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &usb_phy_pcs_cmn_vals,
+ [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &usb_phy_pcs_cmn_vals,
+ [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &usb_phy_pcs_cmn_vals,
+ [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
+ },
+ },
+ },
+ .cmn_vals = {
+ [CLK_19_2_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_19_2_no_ssc_cmn_vals,
+ },
+ },
+ },
+ [CLK_25_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_25_no_ssc_cmn_vals,
+ },
+ },
+ },
+ [CLK_100_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals,
+ },
+ },
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = &sl_pcie_100_int_ssc_cmn_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
+ },
+ },
+ [TYPE_SGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_sgmii_100_no_ssc_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &sgmii_100_int_ssc_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_qsgmii_100_no_ssc_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &qsgmii_100_int_ssc_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &usb_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &usb_100_int_ssc_cmn_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals,
+ [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
+ [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
+ },
+ },
+ },
+ },
+ .tx_ln_vals = {
+ [CLK_19_2_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_19_2_no_ssc_tx_ln_vals,
+ },
+ },
+ },
+ [CLK_25_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_25_no_ssc_tx_ln_vals,
+ },
+ },
+ },
+ [CLK_100_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_100_no_ssc_tx_ln_vals,
+ },
+ },
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = NULL,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = NULL,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = NULL,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = NULL,
+ [EXTERNAL_SSC] = NULL,
+ [INTERNAL_SSC] = NULL,
+ },
+ },
+ [TYPE_SGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ },
+ },
+ },
+ },
+ .rx_ln_vals = {
+ [CLK_19_2_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_19_2_no_ssc_rx_ln_vals,
+ },
+ },
+ },
+ [CLK_25_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_25_no_ssc_rx_ln_vals,
+ },
+ },
+ },
+ [CLK_100_MHZ] = {
+ [TYPE_DP] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sl_dp_100_no_ssc_rx_ln_vals,
+ },
+ },
+ [TYPE_PCIE] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ },
+ },
+ [TYPE_SGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
+ },
+ },
+ [TYPE_QSGMII] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
+ },
+ },
+ [TYPE_USB] = {
+ [TYPE_NONE] = {
+ [NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_PCIE] = {
+ [NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_SGMII] = {
+ [NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_QSGMII] = {
+ [NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ },
+ },
+ },
+ },
+};
+
+static const struct of_device_id cdns_torrent_phy_of_match[] = {
+ {
+ .compatible = "cdns,torrent-phy",
+ .data = &cdns_map_torrent,
+ },
+ {
+ .compatible = "ti,j721e-serdes-10g",
+ .data = &ti_j721e_map_torrent,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cdns_torrent_phy_of_match);
+
+static struct platform_driver cdns_torrent_phy_driver = {
+ .probe = cdns_torrent_phy_probe,
+ .remove = cdns_torrent_phy_remove,
+ .driver = {
+ .name = "cdns-torrent-phy",
+ .of_match_table = cdns_torrent_phy_of_match,
+ }
+};
+module_platform_driver(cdns_torrent_phy_driver);
+
+MODULE_AUTHOR("Cadence Design Systems, Inc.");
+MODULE_DESCRIPTION("Cadence Torrent PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
index 320630ffe3cd..853958fb2c06 100644
--- a/drivers/phy/freescale/Kconfig
+++ b/drivers/phy/freescale/Kconfig
@@ -1,10 +1,22 @@
# SPDX-License-Identifier: GPL-2.0-only
+
+if (ARCH_MXC && ARM64) || COMPILE_TEST
+
config PHY_FSL_IMX8MQ_USB
tristate "Freescale i.MX8M USB3 PHY"
depends on OF && HAS_IOMEM
select GENERIC_PHY
default ARCH_MXC && ARM64
+config PHY_MIXEL_LVDS_PHY
+ tristate "Mixel LVDS PHY support"
+ depends on OF
+ select GENERIC_PHY
+ select REGMAP_MMIO
+ help
+ Enable this to add support for the Mixel LVDS PHY as found
+ on NXP's i.MX8qm SoC.
+
config PHY_MIXEL_MIPI_DPHY
tristate "Mixel MIPI DSI PHY support"
depends on OF && HAS_IOMEM
@@ -14,3 +26,24 @@ 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.
+
+endif
+
+config PHY_FSL_LYNX_28G
+ tristate "Freescale Layerscape Lynx 28G SerDes PHY support"
+ depends on OF
+ depends on ARCH_LAYERSCAPE || COMPILE_TEST
+ select GENERIC_PHY
+ help
+ Enable this to add support for the Lynx SerDes 28G PHY as
+ found on NXP's Layerscape platforms such as LX2160A.
+ Used to change the protocol running on SerDes lanes at runtime.
+ Only useful for a restricted set of Ethernet protocols.
diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile
index 1d02e3869b45..cedb328bc4d2 100644
--- a/drivers/phy/freescale/Makefile
+++ b/drivers/phy/freescale/Makefile
@@ -1,3 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o
+obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += phy-fsl-imx8qm-lvds-phy.o
obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o
+obj-$(CONFIG_PHY_FSL_IMX8M_PCIE) += phy-fsl-imx8m-pcie.o
+obj-$(CONFIG_PHY_FSL_LYNX_28G) += phy-fsl-lynx-28g.o
diff --git a/drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c b/drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c
index 9f2c1da14f5a..e625b32889bf 100644
--- a/drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c
+++ b/drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c
@@ -4,17 +4,33 @@
* Copyright 2019 Purism SPC
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
+#include <linux/firmware/imx/ipc.h>
+#include <linux/firmware/imx/svc/misc.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <dt-bindings/firmware/imx/rsrc.h>
+
+/* Control and Status Registers(CSR) */
+#define PHY_CTRL 0x00
+#define CCM_MASK GENMASK(7, 5)
+#define CCM(n) FIELD_PREP(CCM_MASK, (n))
+#define CCM_1_2V 0x5
+#define CA_MASK GENMASK(4, 2)
+#define CA_3_51MA 0x4
+#define CA(n) FIELD_PREP(CA_MASK, (n))
+#define RFB BIT(1)
+#define LVDS_EN BIT(0)
/* DPHY registers */
#define DPHY_PD_DPHY 0x00
@@ -55,8 +71,15 @@
#define PWR_ON 0
#define PWR_OFF 1
+#define MIN_VCO_FREQ 640000000
+#define MAX_VCO_FREQ 1500000000
+
+#define MIN_LVDS_REFCLK_FREQ 24000000
+#define MAX_LVDS_REFCLK_FREQ 150000000
+
enum mixel_dphy_devtype {
MIXEL_IMX8MQ,
+ MIXEL_IMX8QXP,
};
struct mixel_dphy_devdata {
@@ -65,6 +88,7 @@ struct mixel_dphy_devdata {
u8 reg_rxlprp;
u8 reg_rxcdrp;
u8 reg_rxhs_settle;
+ bool is_combo; /* MIPI DPHY and LVDS PHY combo */
};
static const struct mixel_dphy_devdata mixel_dphy_devdata[] = {
@@ -74,6 +98,10 @@ static const struct mixel_dphy_devdata mixel_dphy_devdata[] = {
.reg_rxlprp = 0x40,
.reg_rxcdrp = 0x44,
.reg_rxhs_settle = 0x48,
+ .is_combo = false,
+ },
+ [MIXEL_IMX8QXP] = {
+ .is_combo = true,
},
};
@@ -95,8 +123,12 @@ struct mixel_dphy_cfg {
struct mixel_dphy_priv {
struct mixel_dphy_cfg cfg;
struct regmap *regmap;
+ struct regmap *lvds_regmap;
struct clk *phy_ref_clk;
const struct mixel_dphy_devdata *devdata;
+ struct imx_sc_ipc *ipc_handle;
+ bool is_slave;
+ int id;
};
static const struct regmap_config mixel_dphy_regmap_config = {
@@ -317,7 +349,8 @@ static int mixel_dphy_set_pll_params(struct phy *phy)
return 0;
}
-static int mixel_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
+static int
+mixel_dphy_configure_mipi_dphy(struct phy *phy, union phy_configure_opts *opts)
{
struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
struct mixel_dphy_cfg cfg = { 0 };
@@ -345,15 +378,126 @@ static int mixel_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
return 0;
}
+static int
+mixel_dphy_configure_lvds_phy(struct phy *phy, union phy_configure_opts *opts)
+{
+ struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
+ struct phy_configure_opts_lvds *lvds_opts = &opts->lvds;
+ unsigned long data_rate;
+ unsigned long fvco;
+ u32 rsc;
+ u32 co;
+ int ret;
+
+ priv->is_slave = lvds_opts->is_slave;
+
+ /* LVDS interface pins */
+ regmap_write(priv->lvds_regmap, PHY_CTRL,
+ CCM(CCM_1_2V) | CA(CA_3_51MA) | RFB);
+
+ /* enable MODE8 only for slave LVDS PHY */
+ rsc = priv->id ? IMX_SC_R_MIPI_1 : IMX_SC_R_MIPI_0;
+ ret = imx_sc_misc_set_control(priv->ipc_handle, rsc, IMX_SC_C_DUAL_MODE,
+ lvds_opts->is_slave);
+ if (ret) {
+ dev_err(&phy->dev, "Failed to configure MODE8: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Choose an appropriate divider ratio to meet the requirement of
+ * PLL VCO frequency range.
+ *
+ * ----- 640MHz ~ 1500MHz ------------ ---------------
+ * | VCO | ----------------> | CO divider | -> | LVDS data rate|
+ * ----- FVCO ------------ ---------------
+ * 1/2/4/8 div 7 * differential_clk_rate
+ */
+ data_rate = 7 * lvds_opts->differential_clk_rate;
+ for (co = 1; co <= 8; co *= 2) {
+ fvco = data_rate * co;
+
+ if (fvco >= MIN_VCO_FREQ)
+ break;
+ }
+
+ if (fvco < MIN_VCO_FREQ || fvco > MAX_VCO_FREQ) {
+ dev_err(&phy->dev, "VCO frequency %lu is out of range\n", fvco);
+ return -ERANGE;
+ }
+
+ /*
+ * CO is configurable, while CN and CM are not,
+ * as fixed ratios 1 and 7 are applied respectively.
+ */
+ phy_write(phy, __ffs(co), DPHY_CO);
+
+ /* set reference clock rate */
+ clk_set_rate(priv->phy_ref_clk, lvds_opts->differential_clk_rate);
+
+ return ret;
+}
+
+static int mixel_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+ if (!opts) {
+ dev_err(&phy->dev, "No configuration options\n");
+ return -EINVAL;
+ }
+
+ if (phy->attrs.mode == PHY_MODE_MIPI_DPHY)
+ return mixel_dphy_configure_mipi_dphy(phy, opts);
+ else if (phy->attrs.mode == PHY_MODE_LVDS)
+ return mixel_dphy_configure_lvds_phy(phy, opts);
+
+ dev_err(&phy->dev,
+ "Failed to configure PHY with invalid PHY mode: %d\n", phy->attrs.mode);
+
+ return -EINVAL;
+}
+
+static int
+mixel_dphy_validate_lvds_phy(struct phy *phy, union phy_configure_opts *opts)
+{
+ struct phy_configure_opts_lvds *lvds_cfg = &opts->lvds;
+
+ if (lvds_cfg->bits_per_lane_and_dclk_cycle != 7) {
+ dev_err(&phy->dev, "Invalid bits per LVDS data lane: %u\n",
+ lvds_cfg->bits_per_lane_and_dclk_cycle);
+ return -EINVAL;
+ }
+
+ if (lvds_cfg->lanes != 4) {
+ dev_err(&phy->dev, "Invalid LVDS data lanes: %u\n", lvds_cfg->lanes);
+ return -EINVAL;
+ }
+
+ if (lvds_cfg->differential_clk_rate < MIN_LVDS_REFCLK_FREQ ||
+ lvds_cfg->differential_clk_rate > MAX_LVDS_REFCLK_FREQ) {
+ dev_err(&phy->dev,
+ "Invalid LVDS differential clock rate: %lu\n",
+ lvds_cfg->differential_clk_rate);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int mixel_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
union phy_configure_opts *opts)
{
- struct mixel_dphy_cfg cfg = { 0 };
+ if (mode == PHY_MODE_MIPI_DPHY) {
+ struct mixel_dphy_cfg mipi_dphy_cfg = { 0 };
- if (mode != PHY_MODE_MIPI_DPHY)
- return -EINVAL;
+ return mixel_dphy_config_from_opts(phy, &opts->mipi_dphy,
+ &mipi_dphy_cfg);
+ } else if (mode == PHY_MODE_LVDS) {
+ return mixel_dphy_validate_lvds_phy(phy, opts);
+ }
- return mixel_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
+ dev_err(&phy->dev,
+ "Failed to validate PHY with invalid PHY mode: %d\n", mode);
+ return -EINVAL;
}
static int mixel_dphy_init(struct phy *phy)
@@ -373,27 +517,75 @@ static int mixel_dphy_exit(struct phy *phy)
return 0;
}
-static int mixel_dphy_power_on(struct phy *phy)
+static int mixel_dphy_power_on_mipi_dphy(struct phy *phy)
{
struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
u32 locked;
int ret;
- ret = clk_prepare_enable(priv->phy_ref_clk);
- if (ret < 0)
- return ret;
-
phy_write(phy, PWR_ON, DPHY_PD_PLL);
ret = regmap_read_poll_timeout(priv->regmap, DPHY_LOCK, locked,
locked, PLL_LOCK_SLEEP,
PLL_LOCK_TIMEOUT);
if (ret < 0) {
dev_err(&phy->dev, "Could not get DPHY lock (%d)!\n", ret);
- goto clock_disable;
+ return ret;
}
phy_write(phy, PWR_ON, DPHY_PD_DPHY);
return 0;
+}
+
+static int mixel_dphy_power_on_lvds_phy(struct phy *phy)
+{
+ struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
+ u32 locked;
+ int ret;
+
+ regmap_update_bits(priv->lvds_regmap, PHY_CTRL, LVDS_EN, LVDS_EN);
+
+ phy_write(phy, PWR_ON, DPHY_PD_DPHY);
+ phy_write(phy, PWR_ON, DPHY_PD_PLL);
+
+ /* do not wait for slave LVDS PHY being locked */
+ if (priv->is_slave)
+ return 0;
+
+ ret = regmap_read_poll_timeout(priv->regmap, DPHY_LOCK, locked,
+ locked, PLL_LOCK_SLEEP,
+ PLL_LOCK_TIMEOUT);
+ if (ret < 0) {
+ dev_err(&phy->dev, "Could not get LVDS PHY lock (%d)!\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mixel_dphy_power_on(struct phy *phy)
+{
+ struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = clk_prepare_enable(priv->phy_ref_clk);
+ if (ret < 0)
+ return ret;
+
+ if (phy->attrs.mode == PHY_MODE_MIPI_DPHY) {
+ ret = mixel_dphy_power_on_mipi_dphy(phy);
+ } else if (phy->attrs.mode == PHY_MODE_LVDS) {
+ ret = mixel_dphy_power_on_lvds_phy(phy);
+ } else {
+ dev_err(&phy->dev,
+ "Failed to power on PHY with invalid PHY mode: %d\n",
+ phy->attrs.mode);
+ ret = -EINVAL;
+ }
+
+ if (ret)
+ goto clock_disable;
+
+ return 0;
clock_disable:
clk_disable_unprepare(priv->phy_ref_clk);
return ret;
@@ -406,16 +598,51 @@ static int mixel_dphy_power_off(struct phy *phy)
phy_write(phy, PWR_OFF, DPHY_PD_PLL);
phy_write(phy, PWR_OFF, DPHY_PD_DPHY);
+ if (phy->attrs.mode == PHY_MODE_LVDS)
+ regmap_update_bits(priv->lvds_regmap, PHY_CTRL, LVDS_EN, 0);
+
clk_disable_unprepare(priv->phy_ref_clk);
return 0;
}
+static int mixel_dphy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ if (priv->devdata->is_combo && mode != PHY_MODE_LVDS) {
+ dev_err(&phy->dev, "Failed to set PHY mode for combo PHY\n");
+ return -EINVAL;
+ }
+
+ if (!priv->devdata->is_combo && mode != PHY_MODE_MIPI_DPHY) {
+ dev_err(&phy->dev, "Failed to set PHY mode to MIPI DPHY\n");
+ return -EINVAL;
+ }
+
+ if (priv->devdata->is_combo) {
+ u32 rsc = priv->id ? IMX_SC_R_MIPI_1 : IMX_SC_R_MIPI_0;
+
+ ret = imx_sc_misc_set_control(priv->ipc_handle,
+ rsc, IMX_SC_C_MODE,
+ mode == PHY_MODE_LVDS);
+ if (ret) {
+ dev_err(&phy->dev,
+ "Failed to set PHY mode via SCU ipc: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static const struct phy_ops mixel_dphy_phy_ops = {
.init = mixel_dphy_init,
.exit = mixel_dphy_exit,
.power_on = mixel_dphy_power_on,
.power_off = mixel_dphy_power_off,
+ .set_mode = mixel_dphy_set_mode,
.configure = mixel_dphy_configure,
.validate = mixel_dphy_validate,
.owner = THIS_MODULE,
@@ -424,6 +651,8 @@ static const struct phy_ops mixel_dphy_phy_ops = {
static const struct of_device_id mixel_dphy_of_match[] = {
{ .compatible = "fsl,imx8mq-mipi-dphy",
.data = &mixel_dphy_devdata[MIXEL_IMX8MQ] },
+ { .compatible = "fsl,imx8qxp-mipi-dphy",
+ .data = &mixel_dphy_devdata[MIXEL_IMX8QXP] },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, mixel_dphy_of_match);
@@ -434,9 +663,9 @@ static int mixel_dphy_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
struct phy_provider *phy_provider;
struct mixel_dphy_priv *priv;
- struct resource *res;
struct phy *phy;
void __iomem *base;
+ int ret;
if (!np)
return -ENODEV;
@@ -449,8 +678,7 @@ static int mixel_dphy_probe(struct platform_device *pdev)
if (!priv->devdata)
return -EINVAL;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -469,6 +697,30 @@ static int mixel_dphy_probe(struct platform_device *pdev)
dev_dbg(dev, "phy_ref clock rate: %lu\n",
clk_get_rate(priv->phy_ref_clk));
+ if (priv->devdata->is_combo) {
+ priv->lvds_regmap =
+ syscon_regmap_lookup_by_phandle(np, "fsl,syscon");
+ if (IS_ERR(priv->lvds_regmap)) {
+ ret = PTR_ERR(priv->lvds_regmap);
+ dev_err_probe(dev, ret, "Failed to get LVDS regmap\n");
+ return ret;
+ }
+
+ priv->id = of_alias_get_id(np, "mipi_dphy");
+ if (priv->id < 0) {
+ dev_err(dev, "Failed to get phy node alias id: %d\n",
+ priv->id);
+ return priv->id;
+ }
+
+ ret = imx_scu_get_handle(&priv->ipc_handle);
+ if (ret) {
+ dev_err_probe(dev, ret,
+ "Failed to get SCU ipc handle\n");
+ return ret;
+ }
+ }
+
dev_set_drvdata(dev, priv);
phy = devm_phy_create(dev, np, &mixel_dphy_phy_ops);
diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
new file mode 100644
index 000000000000..c93286483b42
--- /dev/null
+++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 NXP
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.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_power_on(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 ||
+ pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) {
+ /* 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 {
+ /* 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);
+ }
+
+ if (pad_mode == IMX8_PCIE_REFCLK_PAD_OUTPUT ||
+ pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) {
+ /* Source clock from SoC internal PLL */
+ 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_init(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_exit(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,
+ .exit = imx8_pcie_phy_exit,
+ .power_on = imx8_pcie_phy_power_on,
+ .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");
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index 0c4833da7be0..a29b4a6f7c24 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -1,15 +1,20 @@
// SPDX-License-Identifier: GPL-2.0+
/* Copyright (c) 2017 NXP. */
+#include <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#define PHY_CTRL0 0x0
#define PHY_CTRL0_REF_SSP_EN BIT(2)
+#define PHY_CTRL0_FSEL_MASK GENMASK(10, 5)
+#define PHY_CTRL0_FSEL_24M 0x2a
#define PHY_CTRL1 0x4
#define PHY_CTRL1_RESET BIT(0)
@@ -20,6 +25,11 @@
#define PHY_CTRL2 0x8
#define PHY_CTRL2_TXENABLEN0 BIT(8)
+#define PHY_CTRL2_OTG_DISABLE BIT(9)
+
+#define PHY_CTRL6 0x18
+#define PHY_CTRL6_ALT_CLK_EN BIT(1)
+#define PHY_CTRL6_ALT_CLK_SEL BIT(0)
struct imx8mq_usb_phy {
struct phy *phy;
@@ -54,6 +64,44 @@ static int imx8mq_usb_phy_init(struct phy *phy)
return 0;
}
+static int imx8mp_usb_phy_init(struct phy *phy)
+{
+ struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
+ u32 value;
+
+ /* USB3.0 PHY signal fsel for 24M ref */
+ value = readl(imx_phy->base + PHY_CTRL0);
+ value &= ~PHY_CTRL0_FSEL_MASK;
+ value |= FIELD_PREP(PHY_CTRL0_FSEL_MASK, PHY_CTRL0_FSEL_24M);
+ writel(value, imx_phy->base + PHY_CTRL0);
+
+ /* Disable alt_clk_en and use internal MPLL clocks */
+ value = readl(imx_phy->base + PHY_CTRL6);
+ value &= ~(PHY_CTRL6_ALT_CLK_SEL | PHY_CTRL6_ALT_CLK_EN);
+ writel(value, imx_phy->base + PHY_CTRL6);
+
+ value = readl(imx_phy->base + PHY_CTRL1);
+ value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0);
+ value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET;
+ writel(value, imx_phy->base + PHY_CTRL1);
+
+ value = readl(imx_phy->base + PHY_CTRL0);
+ value |= PHY_CTRL0_REF_SSP_EN;
+ writel(value, imx_phy->base + PHY_CTRL0);
+
+ value = readl(imx_phy->base + PHY_CTRL2);
+ value |= PHY_CTRL2_TXENABLEN0 | PHY_CTRL2_OTG_DISABLE;
+ writel(value, imx_phy->base + PHY_CTRL2);
+
+ udelay(10);
+
+ value = readl(imx_phy->base + PHY_CTRL1);
+ value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);
+ writel(value, imx_phy->base + PHY_CTRL1);
+
+ return 0;
+}
+
static int imx8mq_phy_power_on(struct phy *phy)
{
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
@@ -76,19 +124,35 @@ static int imx8mq_phy_power_off(struct phy *phy)
return 0;
}
-static struct phy_ops imx8mq_usb_phy_ops = {
+static const struct phy_ops imx8mq_usb_phy_ops = {
.init = imx8mq_usb_phy_init,
.power_on = imx8mq_phy_power_on,
.power_off = imx8mq_phy_power_off,
.owner = THIS_MODULE,
};
+static const struct phy_ops imx8mp_usb_phy_ops = {
+ .init = imx8mp_usb_phy_init,
+ .power_on = imx8mq_phy_power_on,
+ .power_off = imx8mq_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static const struct of_device_id imx8mq_usb_phy_of_match[] = {
+ {.compatible = "fsl,imx8mq-usb-phy",
+ .data = &imx8mq_usb_phy_ops,},
+ {.compatible = "fsl,imx8mp-usb-phy",
+ .data = &imx8mp_usb_phy_ops,},
+ { }
+};
+MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
+
static int imx8mq_usb_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct imx8mq_usb_phy *imx_phy;
- struct resource *res;
+ const struct phy_ops *phy_ops;
imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL);
if (!imx_phy)
@@ -100,12 +164,15 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR(imx_phy->clk);
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- imx_phy->base = devm_ioremap_resource(dev, res);
+ imx_phy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(imx_phy->base))
return PTR_ERR(imx_phy->base);
- imx_phy->phy = devm_phy_create(dev, NULL, &imx8mq_usb_phy_ops);
+ phy_ops = of_device_get_match_data(dev);
+ if (!phy_ops)
+ return -EINVAL;
+
+ imx_phy->phy = devm_phy_create(dev, NULL, phy_ops);
if (IS_ERR(imx_phy->phy))
return PTR_ERR(imx_phy->phy);
@@ -120,12 +187,6 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(phy_provider);
}
-static const struct of_device_id imx8mq_usb_phy_of_match[] = {
- {.compatible = "fsl,imx8mq-usb-phy",},
- { },
-};
-MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
-
static struct platform_driver imx8mq_usb_phy_driver = {
.probe = imx8mq_usb_phy_probe,
.driver = {
diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c
new file mode 100644
index 000000000000..e514b64bfdab
--- /dev/null
+++ b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c
@@ -0,0 +1,450 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2017-2020,2022 NXP
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+
+#define REG_SET 0x4
+#define REG_CLR 0x8
+
+#define PHY_CTRL 0x0
+#define M_MASK GENMASK(18, 17)
+#define M(n) FIELD_PREP(M_MASK, (n))
+#define CCM_MASK GENMASK(16, 14)
+#define CCM(n) FIELD_PREP(CCM_MASK, (n))
+#define CA_MASK GENMASK(13, 11)
+#define CA(n) FIELD_PREP(CA_MASK, (n))
+#define TST_MASK GENMASK(10, 5)
+#define TST(n) FIELD_PREP(TST_MASK, (n))
+#define CH_EN(id) BIT(3 + (id))
+#define NB BIT(2)
+#define RFB BIT(1)
+#define PD BIT(0)
+
+/* Power On Reset(POR) value */
+#define CTRL_RESET_VAL (M(0x0) | CCM(0x4) | CA(0x4) | TST(0x25))
+
+/* PHY initialization value and mask */
+#define CTRL_INIT_MASK (M_MASK | CCM_MASK | CA_MASK | TST_MASK | NB | RFB)
+#define CTRL_INIT_VAL (M(0x0) | CCM(0x5) | CA(0x4) | TST(0x25) | RFB)
+
+#define PHY_STATUS 0x10
+#define LOCK BIT(0)
+
+#define PHY_NUM 2
+
+#define MIN_CLKIN_FREQ (25 * MEGA)
+#define MAX_CLKIN_FREQ (165 * MEGA)
+
+#define PLL_LOCK_SLEEP 10
+#define PLL_LOCK_TIMEOUT 1000
+
+struct mixel_lvds_phy {
+ struct phy *phy;
+ struct phy_configure_opts_lvds cfg;
+ unsigned int id;
+};
+
+struct mixel_lvds_phy_priv {
+ struct regmap *regmap;
+ struct mutex lock; /* protect remap access and cfg of our own */
+ struct clk *phy_ref_clk;
+ struct mixel_lvds_phy *phys[PHY_NUM];
+};
+
+static int mixel_lvds_phy_init(struct phy *phy)
+{
+ struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
+
+ mutex_lock(&priv->lock);
+ regmap_update_bits(priv->regmap,
+ PHY_CTRL, CTRL_INIT_MASK, CTRL_INIT_VAL);
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int mixel_lvds_phy_power_on(struct phy *phy)
+{
+ struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
+ struct mixel_lvds_phy *lvds_phy = phy_get_drvdata(phy);
+ struct mixel_lvds_phy *companion = priv->phys[lvds_phy->id ^ 1];
+ struct phy_configure_opts_lvds *cfg = &lvds_phy->cfg;
+ u32 val = 0;
+ u32 locked;
+ int ret;
+
+ /* The master PHY would power on the slave PHY. */
+ if (cfg->is_slave)
+ return 0;
+
+ ret = clk_prepare_enable(priv->phy_ref_clk);
+ if (ret < 0) {
+ dev_err(&phy->dev,
+ "failed to enable PHY reference clock: %d\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&priv->lock);
+ if (cfg->bits_per_lane_and_dclk_cycle == 7) {
+ if (cfg->differential_clk_rate < 44000000)
+ val |= M(0x2);
+ else if (cfg->differential_clk_rate < 90000000)
+ val |= M(0x1);
+ else
+ val |= M(0x0);
+ } else {
+ val = NB;
+
+ if (cfg->differential_clk_rate < 32000000)
+ val |= M(0x2);
+ else if (cfg->differential_clk_rate < 63000000)
+ val |= M(0x1);
+ else
+ val |= M(0x0);
+ }
+ regmap_update_bits(priv->regmap, PHY_CTRL, M_MASK | NB, val);
+
+ /*
+ * Enable two channels synchronously,
+ * if the companion PHY is a slave PHY.
+ */
+ if (companion->cfg.is_slave)
+ val = CH_EN(0) | CH_EN(1);
+ else
+ val = CH_EN(lvds_phy->id);
+ regmap_write(priv->regmap, PHY_CTRL + REG_SET, val);
+
+ ret = regmap_read_poll_timeout(priv->regmap, PHY_STATUS, locked,
+ locked, PLL_LOCK_SLEEP,
+ PLL_LOCK_TIMEOUT);
+ if (ret < 0) {
+ dev_err(&phy->dev, "failed to get PHY lock: %d\n", ret);
+ clk_disable_unprepare(priv->phy_ref_clk);
+ }
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int mixel_lvds_phy_power_off(struct phy *phy)
+{
+ struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
+ struct mixel_lvds_phy *lvds_phy = phy_get_drvdata(phy);
+ struct mixel_lvds_phy *companion = priv->phys[lvds_phy->id ^ 1];
+ struct phy_configure_opts_lvds *cfg = &lvds_phy->cfg;
+
+ /* The master PHY would power off the slave PHY. */
+ if (cfg->is_slave)
+ return 0;
+
+ mutex_lock(&priv->lock);
+ if (companion->cfg.is_slave)
+ regmap_write(priv->regmap, PHY_CTRL + REG_CLR,
+ CH_EN(0) | CH_EN(1));
+ else
+ regmap_write(priv->regmap, PHY_CTRL + REG_CLR,
+ CH_EN(lvds_phy->id));
+ mutex_unlock(&priv->lock);
+
+ clk_disable_unprepare(priv->phy_ref_clk);
+
+ return 0;
+}
+
+static int mixel_lvds_phy_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
+ struct phy_configure_opts_lvds *cfg = &opts->lvds;
+ int ret;
+
+ ret = clk_set_rate(priv->phy_ref_clk, cfg->differential_clk_rate);
+ if (ret)
+ dev_err(&phy->dev, "failed to set PHY reference clock rate(%lu): %d\n",
+ cfg->differential_clk_rate, ret);
+
+ return ret;
+}
+
+/* Assume the master PHY's configuration set is cached first. */
+static int mixel_lvds_phy_check_slave(struct phy *slave_phy)
+{
+ struct device *dev = &slave_phy->dev;
+ struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev->parent);
+ struct mixel_lvds_phy *slv = phy_get_drvdata(slave_phy);
+ struct mixel_lvds_phy *mst = priv->phys[slv->id ^ 1];
+ struct phy_configure_opts_lvds *mst_cfg = &mst->cfg;
+ struct phy_configure_opts_lvds *slv_cfg = &slv->cfg;
+
+ if (mst_cfg->bits_per_lane_and_dclk_cycle !=
+ slv_cfg->bits_per_lane_and_dclk_cycle) {
+ dev_err(dev, "number bits mismatch(mst: %u vs slv: %u)\n",
+ mst_cfg->bits_per_lane_and_dclk_cycle,
+ slv_cfg->bits_per_lane_and_dclk_cycle);
+ return -EINVAL;
+ }
+
+ if (mst_cfg->differential_clk_rate !=
+ slv_cfg->differential_clk_rate) {
+ dev_err(dev, "dclk rate mismatch(mst: %lu vs slv: %lu)\n",
+ mst_cfg->differential_clk_rate,
+ slv_cfg->differential_clk_rate);
+ return -EINVAL;
+ }
+
+ if (mst_cfg->lanes != slv_cfg->lanes) {
+ dev_err(dev, "lanes mismatch(mst: %u vs slv: %u)\n",
+ mst_cfg->lanes, slv_cfg->lanes);
+ return -EINVAL;
+ }
+
+ if (mst_cfg->is_slave == slv_cfg->is_slave) {
+ dev_err(dev, "master PHY is not found\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mixel_lvds_phy_validate(struct phy *phy, enum phy_mode mode,
+ int submode, union phy_configure_opts *opts)
+{
+ struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
+ struct mixel_lvds_phy *lvds_phy = phy_get_drvdata(phy);
+ struct phy_configure_opts_lvds *cfg = &opts->lvds;
+ int ret = 0;
+
+ if (mode != PHY_MODE_LVDS) {
+ dev_err(&phy->dev, "invalid PHY mode(%d)\n", mode);
+ return -EINVAL;
+ }
+
+ if (cfg->bits_per_lane_and_dclk_cycle != 7 &&
+ cfg->bits_per_lane_and_dclk_cycle != 10) {
+ dev_err(&phy->dev, "invalid bits per data lane(%u)\n",
+ cfg->bits_per_lane_and_dclk_cycle);
+ return -EINVAL;
+ }
+
+ if (cfg->lanes != 4 && cfg->lanes != 3) {
+ dev_err(&phy->dev, "invalid data lanes(%u)\n", cfg->lanes);
+ return -EINVAL;
+ }
+
+ if (cfg->differential_clk_rate < MIN_CLKIN_FREQ ||
+ cfg->differential_clk_rate > MAX_CLKIN_FREQ) {
+ dev_err(&phy->dev, "invalid differential clock rate(%lu)\n",
+ cfg->differential_clk_rate);
+ return -EINVAL;
+ }
+
+ mutex_lock(&priv->lock);
+ /* cache configuration set of our own for check */
+ memcpy(&lvds_phy->cfg, cfg, sizeof(*cfg));
+
+ if (cfg->is_slave) {
+ ret = mixel_lvds_phy_check_slave(phy);
+ if (ret)
+ dev_err(&phy->dev, "failed to check slave PHY: %d\n", ret);
+ }
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static const struct phy_ops mixel_lvds_phy_ops = {
+ .init = mixel_lvds_phy_init,
+ .power_on = mixel_lvds_phy_power_on,
+ .power_off = mixel_lvds_phy_power_off,
+ .configure = mixel_lvds_phy_configure,
+ .validate = mixel_lvds_phy_validate,
+ .owner = THIS_MODULE,
+};
+
+static int mixel_lvds_phy_reset(struct device *dev)
+{
+ struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to get PM runtime: %d\n", ret);
+ return ret;
+ }
+
+ regmap_write(priv->regmap, PHY_CTRL, CTRL_RESET_VAL);
+
+ ret = pm_runtime_put(dev);
+ if (ret < 0)
+ dev_err(dev, "failed to put PM runtime: %d\n", ret);
+
+ return ret;
+}
+
+static struct phy *mixel_lvds_phy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev);
+ unsigned int phy_id;
+
+ if (args->args_count != 1) {
+ dev_err(dev,
+ "invalid argument number(%d) for 'phys' property\n",
+ args->args_count);
+ return ERR_PTR(-EINVAL);
+ }
+
+ phy_id = args->args[0];
+
+ if (phy_id >= PHY_NUM) {
+ dev_err(dev, "invalid PHY index(%d)\n", phy_id);
+ return ERR_PTR(-ENODEV);
+ }
+
+ return priv->phys[phy_id]->phy;
+}
+
+static int mixel_lvds_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *phy_provider;
+ struct mixel_lvds_phy_priv *priv;
+ struct mixel_lvds_phy *lvds_phy;
+ struct phy *phy;
+ int i;
+ int ret;
+
+ if (!dev->of_node)
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = syscon_node_to_regmap(dev->of_node->parent);
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap),
+ "failed to get regmap\n");
+
+ priv->phy_ref_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->phy_ref_clk))
+ return dev_err_probe(dev, PTR_ERR(priv->phy_ref_clk),
+ "failed to get PHY reference clock\n");
+
+ mutex_init(&priv->lock);
+
+ dev_set_drvdata(dev, priv);
+
+ pm_runtime_enable(dev);
+
+ ret = mixel_lvds_phy_reset(dev);
+ if (ret) {
+ dev_err(dev, "failed to do POR reset: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < PHY_NUM; i++) {
+ lvds_phy = devm_kzalloc(dev, sizeof(*lvds_phy), GFP_KERNEL);
+ if (!lvds_phy) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ phy = devm_phy_create(dev, NULL, &mixel_lvds_phy_ops);
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ dev_err(dev, "failed to create PHY for channel%d: %d\n",
+ i, ret);
+ goto err;
+ }
+
+ lvds_phy->phy = phy;
+ lvds_phy->id = i;
+ priv->phys[i] = lvds_phy;
+
+ phy_set_drvdata(phy, lvds_phy);
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev, mixel_lvds_phy_xlate);
+ if (IS_ERR(phy_provider)) {
+ ret = PTR_ERR(phy_provider);
+ dev_err(dev, "failed to register PHY provider: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+err:
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+static int mixel_lvds_phy_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static int __maybe_unused mixel_lvds_phy_runtime_suspend(struct device *dev)
+{
+ struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev);
+
+ /* power down */
+ mutex_lock(&priv->lock);
+ regmap_write(priv->regmap, PHY_CTRL + REG_SET, PD);
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int __maybe_unused mixel_lvds_phy_runtime_resume(struct device *dev)
+{
+ struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev);
+
+ /* power up + control initialization */
+ mutex_lock(&priv->lock);
+ regmap_update_bits(priv->regmap, PHY_CTRL,
+ CTRL_INIT_MASK | PD, CTRL_INIT_VAL);
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static const struct dev_pm_ops mixel_lvds_phy_pm_ops = {
+ SET_RUNTIME_PM_OPS(mixel_lvds_phy_runtime_suspend,
+ mixel_lvds_phy_runtime_resume, NULL)
+};
+
+static const struct of_device_id mixel_lvds_phy_of_match[] = {
+ { .compatible = "fsl,imx8qm-lvds-phy" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mixel_lvds_phy_of_match);
+
+static struct platform_driver mixel_lvds_phy_driver = {
+ .probe = mixel_lvds_phy_probe,
+ .remove = mixel_lvds_phy_remove,
+ .driver = {
+ .pm = &mixel_lvds_phy_pm_ops,
+ .name = "mixel-lvds-phy",
+ .of_match_table = mixel_lvds_phy_of_match,
+ }
+};
+module_platform_driver(mixel_lvds_phy_driver);
+
+MODULE_DESCRIPTION("Mixel LVDS PHY driver");
+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c
new file mode 100644
index 000000000000..569f12af2aaf
--- /dev/null
+++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2021-2022 NXP. */
+
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+
+#define LYNX_28G_NUM_LANE 8
+#define LYNX_28G_NUM_PLL 2
+
+/* General registers per SerDes block */
+#define LYNX_28G_PCC8 0x10a0
+#define LYNX_28G_PCC8_SGMII 0x1
+#define LYNX_28G_PCC8_SGMII_DIS 0x0
+
+#define LYNX_28G_PCCC 0x10b0
+#define LYNX_28G_PCCC_10GBASER 0x9
+#define LYNX_28G_PCCC_USXGMII 0x1
+#define LYNX_28G_PCCC_SXGMII_DIS 0x0
+
+#define LYNX_28G_LNa_PCC_OFFSET(lane) (4 * (LYNX_28G_NUM_LANE - (lane->id) - 1))
+
+/* Per PLL registers */
+#define LYNX_28G_PLLnRSTCTL(pll) (0x400 + (pll) * 0x100 + 0x0)
+#define LYNX_28G_PLLnRSTCTL_DIS(rstctl) (((rstctl) & BIT(24)) >> 24)
+#define LYNX_28G_PLLnRSTCTL_LOCK(rstctl) (((rstctl) & BIT(23)) >> 23)
+
+#define LYNX_28G_PLLnCR0(pll) (0x400 + (pll) * 0x100 + 0x4)
+#define LYNX_28G_PLLnCR0_REFCLK_SEL(cr0) (((cr0) & GENMASK(20, 16)))
+#define LYNX_28G_PLLnCR0_REFCLK_SEL_100MHZ 0x0
+#define LYNX_28G_PLLnCR0_REFCLK_SEL_125MHZ 0x10000
+#define LYNX_28G_PLLnCR0_REFCLK_SEL_156MHZ 0x20000
+#define LYNX_28G_PLLnCR0_REFCLK_SEL_150MHZ 0x30000
+#define LYNX_28G_PLLnCR0_REFCLK_SEL_161MHZ 0x40000
+
+#define LYNX_28G_PLLnCR1(pll) (0x400 + (pll) * 0x100 + 0x8)
+#define LYNX_28G_PLLnCR1_FRATE_SEL(cr1) (((cr1) & GENMASK(28, 24)))
+#define LYNX_28G_PLLnCR1_FRATE_5G_10GVCO 0x0
+#define LYNX_28G_PLLnCR1_FRATE_5G_25GVCO 0x10000000
+#define LYNX_28G_PLLnCR1_FRATE_10G_20GVCO 0x6000000
+
+/* Per SerDes lane registers */
+/* Lane a General Control Register */
+#define LYNX_28G_LNaGCR0(lane) (0x800 + (lane) * 0x100 + 0x0)
+#define LYNX_28G_LNaGCR0_PROTO_SEL_MSK GENMASK(7, 3)
+#define LYNX_28G_LNaGCR0_PROTO_SEL_SGMII 0x8
+#define LYNX_28G_LNaGCR0_PROTO_SEL_XFI 0x50
+#define LYNX_28G_LNaGCR0_IF_WIDTH_MSK GENMASK(2, 0)
+#define LYNX_28G_LNaGCR0_IF_WIDTH_10_BIT 0x0
+#define LYNX_28G_LNaGCR0_IF_WIDTH_20_BIT 0x2
+
+/* Lane a Tx Reset Control Register */
+#define LYNX_28G_LNaTRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x20)
+#define LYNX_28G_LNaTRSTCTL_HLT_REQ BIT(27)
+#define LYNX_28G_LNaTRSTCTL_RST_DONE BIT(30)
+#define LYNX_28G_LNaTRSTCTL_RST_REQ BIT(31)
+
+/* Lane a Tx General Control Register */
+#define LYNX_28G_LNaTGCR0(lane) (0x800 + (lane) * 0x100 + 0x24)
+#define LYNX_28G_LNaTGCR0_USE_PLLF 0x0
+#define LYNX_28G_LNaTGCR0_USE_PLLS BIT(28)
+#define LYNX_28G_LNaTGCR0_USE_PLL_MSK BIT(28)
+#define LYNX_28G_LNaTGCR0_N_RATE_FULL 0x0
+#define LYNX_28G_LNaTGCR0_N_RATE_HALF 0x1000000
+#define LYNX_28G_LNaTGCR0_N_RATE_QUARTER 0x2000000
+#define LYNX_28G_LNaTGCR0_N_RATE_MSK GENMASK(26, 24)
+
+#define LYNX_28G_LNaTECR0(lane) (0x800 + (lane) * 0x100 + 0x30)
+
+/* Lane a Rx Reset Control Register */
+#define LYNX_28G_LNaRRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x40)
+#define LYNX_28G_LNaRRSTCTL_HLT_REQ BIT(27)
+#define LYNX_28G_LNaRRSTCTL_RST_DONE BIT(30)
+#define LYNX_28G_LNaRRSTCTL_RST_REQ BIT(31)
+#define LYNX_28G_LNaRRSTCTL_CDR_LOCK BIT(12)
+
+/* Lane a Rx General Control Register */
+#define LYNX_28G_LNaRGCR0(lane) (0x800 + (lane) * 0x100 + 0x44)
+#define LYNX_28G_LNaRGCR0_USE_PLLF 0x0
+#define LYNX_28G_LNaRGCR0_USE_PLLS BIT(28)
+#define LYNX_28G_LNaRGCR0_USE_PLL_MSK BIT(28)
+#define LYNX_28G_LNaRGCR0_N_RATE_MSK GENMASK(26, 24)
+#define LYNX_28G_LNaRGCR0_N_RATE_FULL 0x0
+#define LYNX_28G_LNaRGCR0_N_RATE_HALF 0x1000000
+#define LYNX_28G_LNaRGCR0_N_RATE_QUARTER 0x2000000
+#define LYNX_28G_LNaRGCR0_N_RATE_MSK GENMASK(26, 24)
+
+#define LYNX_28G_LNaRGCR1(lane) (0x800 + (lane) * 0x100 + 0x48)
+
+#define LYNX_28G_LNaRECR0(lane) (0x800 + (lane) * 0x100 + 0x50)
+#define LYNX_28G_LNaRECR1(lane) (0x800 + (lane) * 0x100 + 0x54)
+#define LYNX_28G_LNaRECR2(lane) (0x800 + (lane) * 0x100 + 0x58)
+
+#define LYNX_28G_LNaRSCCR0(lane) (0x800 + (lane) * 0x100 + 0x74)
+
+#define LYNX_28G_LNaPSS(lane) (0x1000 + (lane) * 0x4)
+#define LYNX_28G_LNaPSS_TYPE(pss) (((pss) & GENMASK(30, 24)) >> 24)
+#define LYNX_28G_LNaPSS_TYPE_SGMII 0x4
+#define LYNX_28G_LNaPSS_TYPE_XFI 0x28
+
+#define LYNX_28G_SGMIIaCR1(lane) (0x1804 + (lane) * 0x10)
+#define LYNX_28G_SGMIIaCR1_SGPCS_EN BIT(11)
+#define LYNX_28G_SGMIIaCR1_SGPCS_DIS 0x0
+#define LYNX_28G_SGMIIaCR1_SGPCS_MSK BIT(11)
+
+struct lynx_28g_priv;
+
+struct lynx_28g_pll {
+ struct lynx_28g_priv *priv;
+ u32 rstctl, cr0, cr1;
+ int id;
+ DECLARE_PHY_INTERFACE_MASK(supported);
+};
+
+struct lynx_28g_lane {
+ struct lynx_28g_priv *priv;
+ struct phy *phy;
+ bool powered_up;
+ bool init;
+ unsigned int id;
+ phy_interface_t interface;
+};
+
+struct lynx_28g_priv {
+ void __iomem *base;
+ struct device *dev;
+ struct lynx_28g_pll pll[LYNX_28G_NUM_PLL];
+ struct lynx_28g_lane lane[LYNX_28G_NUM_LANE];
+
+ struct delayed_work cdr_check;
+};
+
+static void lynx_28g_rmw(struct lynx_28g_priv *priv, unsigned long off,
+ u32 val, u32 mask)
+{
+ void __iomem *reg = priv->base + off;
+ u32 orig, tmp;
+
+ orig = ioread32(reg);
+ tmp = orig & ~mask;
+ tmp |= val;
+ iowrite32(tmp, reg);
+}
+
+#define lynx_28g_lane_rmw(lane, reg, val, mask) \
+ lynx_28g_rmw((lane)->priv, LYNX_28G_##reg(lane->id), \
+ LYNX_28G_##reg##_##val, LYNX_28G_##reg##_##mask)
+#define lynx_28g_lane_read(lane, reg) \
+ ioread32((lane)->priv->base + LYNX_28G_##reg((lane)->id))
+#define lynx_28g_pll_read(pll, reg) \
+ ioread32((pll)->priv->base + LYNX_28G_##reg((pll)->id))
+
+static bool lynx_28g_supports_interface(struct lynx_28g_priv *priv, int intf)
+{
+ int i;
+
+ for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
+ if (LYNX_28G_PLLnRSTCTL_DIS(priv->pll[i].rstctl))
+ continue;
+
+ if (test_bit(intf, priv->pll[i].supported))
+ return true;
+ }
+
+ return false;
+}
+
+static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv,
+ phy_interface_t intf)
+{
+ struct lynx_28g_pll *pll;
+ int i;
+
+ for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
+ pll = &priv->pll[i];
+
+ if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl))
+ continue;
+
+ if (test_bit(intf, pll->supported))
+ return pll;
+ }
+
+ return NULL;
+}
+
+static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane,
+ struct lynx_28g_pll *pll,
+ phy_interface_t intf)
+{
+ switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) {
+ case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO:
+ case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO:
+ switch (intf) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_QUARTER, N_RATE_MSK);
+ lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_QUARTER, N_RATE_MSK);
+ break;
+ default:
+ break;
+ }
+ break;
+ case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO:
+ switch (intf) {
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_USXGMII:
+ lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_FULL, N_RATE_MSK);
+ lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_FULL, N_RATE_MSK);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void lynx_28g_lane_set_pll(struct lynx_28g_lane *lane,
+ struct lynx_28g_pll *pll)
+{
+ if (pll->id == 0) {
+ lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLF, USE_PLL_MSK);
+ lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLF, USE_PLL_MSK);
+ } else {
+ lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLS, USE_PLL_MSK);
+ lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLS, USE_PLL_MSK);
+ }
+}
+
+static void lynx_28g_cleanup_lane(struct lynx_28g_lane *lane)
+{
+ u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
+ struct lynx_28g_priv *priv = lane->priv;
+
+ /* Cleanup the protocol configuration registers of the current protocol */
+ switch (lane->interface) {
+ case PHY_INTERFACE_MODE_10GBASER:
+ lynx_28g_rmw(priv, LYNX_28G_PCCC,
+ LYNX_28G_PCCC_SXGMII_DIS << lane_offset,
+ GENMASK(3, 0) << lane_offset);
+ break;
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ lynx_28g_rmw(priv, LYNX_28G_PCC8,
+ LYNX_28G_PCC8_SGMII_DIS << lane_offset,
+ GENMASK(3, 0) << lane_offset);
+ break;
+ default:
+ break;
+ }
+}
+
+static void lynx_28g_lane_set_sgmii(struct lynx_28g_lane *lane)
+{
+ u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
+ struct lynx_28g_priv *priv = lane->priv;
+ struct lynx_28g_pll *pll;
+
+ lynx_28g_cleanup_lane(lane);
+
+ /* Setup the lane to run in SGMII */
+ lynx_28g_rmw(priv, LYNX_28G_PCC8,
+ LYNX_28G_PCC8_SGMII << lane_offset,
+ GENMASK(3, 0) << lane_offset);
+
+ /* Setup the protocol select and SerDes parallel interface width */
+ lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_SGMII, PROTO_SEL_MSK);
+ lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_10_BIT, IF_WIDTH_MSK);
+
+ /* Switch to the PLL that works with this interface type */
+ pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_SGMII);
+ lynx_28g_lane_set_pll(lane, pll);
+
+ /* Choose the portion of clock net to be used on this lane */
+ lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_SGMII);
+
+ /* Enable the SGMII PCS */
+ lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_EN, SGPCS_MSK);
+
+ /* Configure the appropriate equalization parameters for the protocol */
+ iowrite32(0x00808006, priv->base + LYNX_28G_LNaTECR0(lane->id));
+ iowrite32(0x04310000, priv->base + LYNX_28G_LNaRGCR1(lane->id));
+ iowrite32(0x9f800000, priv->base + LYNX_28G_LNaRECR0(lane->id));
+ iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id));
+ iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR2(lane->id));
+ iowrite32(0x00000000, priv->base + LYNX_28G_LNaRSCCR0(lane->id));
+}
+
+static void lynx_28g_lane_set_10gbaser(struct lynx_28g_lane *lane)
+{
+ u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
+ struct lynx_28g_priv *priv = lane->priv;
+ struct lynx_28g_pll *pll;
+
+ lynx_28g_cleanup_lane(lane);
+
+ /* Enable the SXGMII lane */
+ lynx_28g_rmw(priv, LYNX_28G_PCCC,
+ LYNX_28G_PCCC_10GBASER << lane_offset,
+ GENMASK(3, 0) << lane_offset);
+
+ /* Setup the protocol select and SerDes parallel interface width */
+ lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_XFI, PROTO_SEL_MSK);
+ lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_20_BIT, IF_WIDTH_MSK);
+
+ /* Switch to the PLL that works with this interface type */
+ pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_10GBASER);
+ lynx_28g_lane_set_pll(lane, pll);
+
+ /* Choose the portion of clock net to be used on this lane */
+ lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_10GBASER);
+
+ /* Disable the SGMII PCS */
+ lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_DIS, SGPCS_MSK);
+
+ /* Configure the appropriate equalization parameters for the protocol */
+ iowrite32(0x10808307, priv->base + LYNX_28G_LNaTECR0(lane->id));
+ iowrite32(0x10000000, priv->base + LYNX_28G_LNaRGCR1(lane->id));
+ iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR0(lane->id));
+ iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id));
+ iowrite32(0x81000020, priv->base + LYNX_28G_LNaRECR2(lane->id));
+ iowrite32(0x00002000, priv->base + LYNX_28G_LNaRSCCR0(lane->id));
+}
+
+static int lynx_28g_power_off(struct phy *phy)
+{
+ struct lynx_28g_lane *lane = phy_get_drvdata(phy);
+ u32 trstctl, rrstctl;
+
+ if (!lane->powered_up)
+ return 0;
+
+ /* Issue a halt request */
+ lynx_28g_lane_rmw(lane, LNaTRSTCTL, HLT_REQ, HLT_REQ);
+ lynx_28g_lane_rmw(lane, LNaRRSTCTL, HLT_REQ, HLT_REQ);
+
+ /* Wait until the halting process is complete */
+ do {
+ trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL);
+ rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
+ } while ((trstctl & LYNX_28G_LNaTRSTCTL_HLT_REQ) ||
+ (rrstctl & LYNX_28G_LNaRRSTCTL_HLT_REQ));
+
+ lane->powered_up = false;
+
+ return 0;
+}
+
+static int lynx_28g_power_on(struct phy *phy)
+{
+ struct lynx_28g_lane *lane = phy_get_drvdata(phy);
+ u32 trstctl, rrstctl;
+
+ if (lane->powered_up)
+ return 0;
+
+ /* Issue a reset request on the lane */
+ lynx_28g_lane_rmw(lane, LNaTRSTCTL, RST_REQ, RST_REQ);
+ lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ);
+
+ /* Wait until the reset sequence is completed */
+ do {
+ trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL);
+ rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
+ } while (!(trstctl & LYNX_28G_LNaTRSTCTL_RST_DONE) ||
+ !(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE));
+
+ lane->powered_up = true;
+
+ return 0;
+}
+
+static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct lynx_28g_lane *lane = phy_get_drvdata(phy);
+ struct lynx_28g_priv *priv = lane->priv;
+ int powered_up = lane->powered_up;
+ int err = 0;
+
+ if (mode != PHY_MODE_ETHERNET)
+ return -EOPNOTSUPP;
+
+ if (lane->interface == PHY_INTERFACE_MODE_NA)
+ return -EOPNOTSUPP;
+
+ if (!lynx_28g_supports_interface(priv, submode))
+ return -EOPNOTSUPP;
+
+ /* If the lane is powered up, put the lane into the halt state while
+ * the reconfiguration is being done.
+ */
+ if (powered_up)
+ lynx_28g_power_off(phy);
+
+ switch (submode) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ lynx_28g_lane_set_sgmii(lane);
+ break;
+ case PHY_INTERFACE_MODE_10GBASER:
+ lynx_28g_lane_set_10gbaser(lane);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ lane->interface = submode;
+
+out:
+ /* Power up the lane if necessary */
+ if (powered_up)
+ lynx_28g_power_on(phy);
+
+ return err;
+}
+
+static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode,
+ union phy_configure_opts *opts __always_unused)
+{
+ struct lynx_28g_lane *lane = phy_get_drvdata(phy);
+ struct lynx_28g_priv *priv = lane->priv;
+
+ if (mode != PHY_MODE_ETHERNET)
+ return -EOPNOTSUPP;
+
+ if (!lynx_28g_supports_interface(priv, submode))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int lynx_28g_init(struct phy *phy)
+{
+ struct lynx_28g_lane *lane = phy_get_drvdata(phy);
+
+ /* Mark the fact that the lane was init */
+ lane->init = true;
+
+ /* SerDes lanes are powered on at boot time. Any lane that is managed
+ * by this driver will get powered down at init time aka at dpaa2-eth
+ * probe time.
+ */
+ lane->powered_up = true;
+ lynx_28g_power_off(phy);
+
+ return 0;
+}
+
+static const struct phy_ops lynx_28g_ops = {
+ .init = lynx_28g_init,
+ .power_on = lynx_28g_power_on,
+ .power_off = lynx_28g_power_off,
+ .set_mode = lynx_28g_set_mode,
+ .validate = lynx_28g_validate,
+ .owner = THIS_MODULE,
+};
+
+static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv)
+{
+ struct lynx_28g_pll *pll;
+ int i;
+
+ for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
+ pll = &priv->pll[i];
+ pll->priv = priv;
+ pll->id = i;
+
+ pll->rstctl = lynx_28g_pll_read(pll, PLLnRSTCTL);
+ pll->cr0 = lynx_28g_pll_read(pll, PLLnCR0);
+ pll->cr1 = lynx_28g_pll_read(pll, PLLnCR1);
+
+ if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl))
+ continue;
+
+ switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) {
+ case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO:
+ case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO:
+ /* 5GHz clock net */
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, pll->supported);
+ __set_bit(PHY_INTERFACE_MODE_SGMII, pll->supported);
+ break;
+ case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO:
+ /* 10.3125GHz clock net */
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, pll->supported);
+ break;
+ default:
+ /* 6GHz, 12.890625GHz, 8GHz */
+ break;
+ }
+ }
+}
+
+#define work_to_lynx(w) container_of((w), struct lynx_28g_priv, cdr_check.work)
+
+static void lynx_28g_cdr_lock_check(struct work_struct *work)
+{
+ struct lynx_28g_priv *priv = work_to_lynx(work);
+ struct lynx_28g_lane *lane;
+ u32 rrstctl;
+ int i;
+
+ for (i = 0; i < LYNX_28G_NUM_LANE; i++) {
+ lane = &priv->lane[i];
+
+ if (!lane->init)
+ continue;
+
+ if (!lane->powered_up)
+ continue;
+
+ rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
+ if (!(rrstctl & LYNX_28G_LNaRRSTCTL_CDR_LOCK)) {
+ lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ);
+ do {
+ rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
+ } while (!(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE));
+ }
+ }
+ queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
+ msecs_to_jiffies(1000));
+}
+
+static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane)
+{
+ u32 pss, protocol;
+
+ pss = lynx_28g_lane_read(lane, LNaPSS);
+ protocol = LYNX_28G_LNaPSS_TYPE(pss);
+ switch (protocol) {
+ case LYNX_28G_LNaPSS_TYPE_SGMII:
+ lane->interface = PHY_INTERFACE_MODE_SGMII;
+ break;
+ case LYNX_28G_LNaPSS_TYPE_XFI:
+ lane->interface = PHY_INTERFACE_MODE_10GBASER;
+ break;
+ default:
+ lane->interface = PHY_INTERFACE_MODE_NA;
+ }
+}
+
+static struct phy *lynx_28g_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct lynx_28g_priv *priv = dev_get_drvdata(dev);
+ int idx = args->args[0];
+
+ if (WARN_ON(idx >= LYNX_28G_NUM_LANE))
+ return ERR_PTR(-EINVAL);
+
+ return priv->lane[idx].phy;
+}
+
+static int lynx_28g_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *provider;
+ struct lynx_28g_priv *priv;
+ int i;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->dev = &pdev->dev;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ lynx_28g_pll_read_configuration(priv);
+
+ for (i = 0; i < LYNX_28G_NUM_LANE; i++) {
+ struct lynx_28g_lane *lane = &priv->lane[i];
+ struct phy *phy;
+
+ memset(lane, 0, sizeof(*lane));
+
+ phy = devm_phy_create(&pdev->dev, NULL, &lynx_28g_ops);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ lane->priv = priv;
+ lane->phy = phy;
+ lane->id = i;
+ phy_set_drvdata(phy, lane);
+ lynx_28g_lane_read_configuration(lane);
+ }
+
+ dev_set_drvdata(dev, priv);
+
+ INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check);
+
+ queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
+ msecs_to_jiffies(1000));
+
+ dev_set_drvdata(&pdev->dev, priv);
+ provider = devm_of_phy_provider_register(&pdev->dev, lynx_28g_xlate);
+
+ return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id lynx_28g_of_match_table[] = {
+ { .compatible = "fsl,lynx-28g" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table);
+
+static struct platform_driver lynx_28g_driver = {
+ .probe = lynx_28g_probe,
+ .driver = {
+ .name = "lynx-28g",
+ .of_match_table = lynx_28g_of_match_table,
+ },
+};
+module_platform_driver(lynx_28g_driver);
+
+MODULE_AUTHOR("Ioana Ciornei <ioana.ciornei@nxp.com>");
+MODULE_DESCRIPTION("Lynx 28G SerDes PHY driver for Layerscape SoCs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig
index 1c73053bcc98..d3b92c288554 100644
--- a/drivers/phy/hisilicon/Kconfig
+++ b/drivers/phy/hisilicon/Kconfig
@@ -23,6 +23,26 @@ config PHY_HI3660_USB
To compile this driver as a module, choose M here.
+config PHY_HI3670_USB
+ tristate "hi3670 USB PHY support"
+ depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+ select GENERIC_PHY
+ select MFD_SYSCON
+ help
+ Enable this to support the HISILICON HI3670 USB PHY.
+
+ To compile this driver as a module, choose M here.
+
+config PHY_HI3670_PCIE
+ tristate "hi3670 PCIe PHY support"
+ depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+ select GENERIC_PHY
+ select MFD_SYSCON
+ help
+ Enable this to support the HiSilicon hi3670 PCIe PHY.
+
+ To compile this driver as a module, choose M here.
+
config PHY_HISTB_COMBPHY
tristate "HiSilicon STB SoCs COMBPHY support"
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
diff --git a/drivers/phy/hisilicon/Makefile b/drivers/phy/hisilicon/Makefile
index 92e874ae9c74..4029d3813b1e 100644
--- a/drivers/phy/hisilicon/Makefile
+++ b/drivers/phy/hisilicon/Makefile
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
obj-$(CONFIG_PHY_HI3660_USB) += phy-hi3660-usb3.o
+obj-$(CONFIG_PHY_HI3670_USB) += phy-hi3670-usb3.o
+obj-$(CONFIG_PHY_HI3670_PCIE) += phy-hi3670-pcie.o
obj-$(CONFIG_PHY_HISTB_COMBPHY) += phy-histb-combphy.o
obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
diff --git a/drivers/phy/hisilicon/phy-hi3660-usb3.c b/drivers/phy/hisilicon/phy-hi3660-usb3.c
index cc0af2c044d0..84adce9b4277 100644
--- a/drivers/phy/hisilicon/phy-hi3660-usb3.c
+++ b/drivers/phy/hisilicon/phy-hi3660-usb3.c
@@ -161,7 +161,7 @@ out:
return ret;
}
-static struct phy_ops hi3660_phy_ops = {
+static const struct phy_ops hi3660_phy_ops = {
.init = hi3660_phy_init,
.exit = hi3660_phy_exit,
.owner = THIS_MODULE,
diff --git a/drivers/phy/hisilicon/phy-hi3670-pcie.c b/drivers/phy/hisilicon/phy-hi3670-pcie.c
new file mode 100644
index 000000000000..0ac9634b398d
--- /dev/null
+++ b/drivers/phy/hisilicon/phy-hi3670-pcie.c
@@ -0,0 +1,845 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe phy driver for Kirin 970
+ *
+ * Copyright (C) 2017 HiSilicon Electronics Co., Ltd.
+ * https://www.huawei.com
+ * Copyright (C) 2021 Huawei Technologies Co., Ltd.
+ * https://www.huawei.com
+ *
+ * Authors:
+ * Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+ * Manivannan Sadhasivam <mani@kernel.org>
+ *
+ * Based on:
+ * https://lore.kernel.org/lkml/4c9d6581478aa966698758c0420933f5defab4dd.1612335031.git.mchehab+huawei@kernel.org/
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define AXI_CLK_FREQ 207500000
+#define REF_CLK_FREQ 100000000
+
+/* PCIe CTRL registers */
+#define SOC_PCIECTRL_CTRL7_ADDR 0x01c
+#define SOC_PCIECTRL_CTRL12_ADDR 0x030
+#define SOC_PCIECTRL_CTRL20_ADDR 0x050
+#define SOC_PCIECTRL_CTRL21_ADDR 0x054
+
+#define PCIE_OUTPUT_PULL_BITS GENMASK(3, 0)
+#define SOC_PCIECTRL_CTRL20_2P_MEM_CTRL 0x02605550
+#define SOC_PCIECTRL_CTRL21_DEFAULT 0x20000070
+#define PCIE_PULL_UP_SYS_AUX_PWR_DET BIT(10)
+#define PCIE_OUTPUT_PULL_DOWN BIT(1)
+
+/* PCIe PHY registers */
+#define SOC_PCIEPHY_CTRL0_ADDR 0x000
+#define SOC_PCIEPHY_CTRL1_ADDR 0x004
+#define SOC_PCIEPHY_CTRL38_ADDR 0x0098
+#define SOC_PCIEPHY_STATE0_ADDR 0x400
+
+#define RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1 0xc004
+#define SUP_DIG_LVL_OVRD_IN 0x003c
+#define LANEN_DIG_ASIC_TX_OVRD_IN_1 0x4008
+#define LANEN_DIG_ASIC_TX_OVRD_IN_2 0x400c
+
+#define PCIEPHY_RESET_BIT BIT(17)
+#define PCIEPHY_PIPE_LINE0_RESET_BIT BIT(19)
+#define PCIE_TXDETECT_RX_FAIL BIT(2)
+#define PCIE_CLK_SOURCE BIT(8)
+#define PCIE_IS_CLOCK_STABLE BIT(19)
+#define PCIE_PULL_DOWN_PHY_TEST_POWERDOWN BIT(22)
+#define PCIE_DEASSERT_CONTROLLER_PERST BIT(2)
+
+#define EYEPARAM_NOCFG 0xffffffff
+#define EYE_PARM0_MASK GENMASK(8, 6)
+#define EYE_PARM1_MASK GENMASK(11, 8)
+#define EYE_PARM2_MASK GENMASK(5, 0)
+#define EYE_PARM3_MASK GENMASK(12, 7)
+#define EYE_PARM4_MASK GENMASK(14, 9)
+#define EYE_PARM0_EN BIT(9)
+#define EYE_PARM1_EN BIT(12)
+#define EYE_PARM2_EN BIT(6)
+#define EYE_PARM3_EN BIT(13)
+#define EYE_PARM4_EN BIT(15)
+
+/* hi3670 pciephy register */
+#define APB_PHY_START_ADDR 0x40000
+#define SOC_PCIEPHY_MMC1PLL_CTRL1 0xc04
+#define SOC_PCIEPHY_MMC1PLL_CTRL16 0xC40
+#define SOC_PCIEPHY_MMC1PLL_CTRL17 0xC44
+#define SOC_PCIEPHY_MMC1PLL_CTRL20 0xC50
+#define SOC_PCIEPHY_MMC1PLL_CTRL21 0xC54
+#define SOC_PCIEPHY_MMC1PLL_STAT0 0xE00
+
+#define CRGPERIPH_PEREN12 0x470
+#define CRGPERIPH_PERDIS12 0x474
+#define CRGPERIPH_PCIECTRL0 0x800
+
+#define PCIE_FNPLL_FBDIV_MASK GENMASK(27, 16)
+#define PCIE_FNPLL_FRACDIV_MASK GENMASK(23, 0)
+#define PCIE_FNPLL_POSTDIV1_MASK GENMASK(10, 8)
+#define PCIE_FNPLL_POSTDIV2_MASK GENMASK(14, 12)
+#define PCIE_FNPLL_PLL_MODE_MASK BIT(25)
+
+#define PCIE_FNPLL_DLL_EN BIT(27)
+#define PCIE_FNPLL_FBDIV 0xd0
+#define PCIE_FNPLL_FRACDIV 0x555555
+#define PCIE_FNPLL_POSTDIV1 0x5
+#define PCIE_FNPLL_POSTDIV2 0x4
+#define PCIE_FNPLL_PLL_MODE 0x0
+
+#define PCIE_PHY_MMC1PLL 0x20
+#define PCIE_PHY_CHOOSE_FNPLL BIT(27)
+#define PCIE_PHY_MMC1PLL_DISABLE BIT(0)
+#define PCIE_PHY_PCIEPL_BP BIT(16)
+
+/* define ie,oe cfg */
+#define IO_OE_HARD_GT_MODE BIT(1)
+#define IO_IE_EN_HARD_BYPASS BIT(27)
+#define IO_OE_EN_HARD_BYPASS BIT(11)
+#define IO_HARD_CTRL_DEBOUNCE_BYPASS BIT(10)
+#define IO_OE_GT_MODE BIT(8)
+#define DEBOUNCE_WAITCFG_IN GENMASK(23, 20)
+#define DEBOUNCE_WAITCFG_OUT GENMASK(16, 13)
+
+#define IO_HP_DEBOUNCE_GT (BIT(12) | BIT(15))
+#define IO_PHYREF_SOFT_GT_MODE BIT(14)
+#define IO_REF_SOFT_GT_MODE BIT(13)
+#define IO_REF_HARD_GT_MODE BIT(0)
+
+/* noc power domain */
+#define NOC_POWER_IDLEREQ_1 0x38c
+#define NOC_POWER_IDLE_1 0x394
+#define NOC_PW_MASK 0x10000
+#define NOC_PW_SET_BIT 0x1
+
+#define NUM_EYEPARAM 5
+
+/* info located in sysctrl */
+#define SCTRL_PCIE_CMOS_OFFSET 0x60
+#define SCTRL_PCIE_CMOS_BIT 0x10
+#define SCTRL_PCIE_ISO_OFFSET 0x44
+#define SCTRL_PCIE_ISO_BIT 0x30
+#define SCTRL_PCIE_HPCLK_OFFSET 0x190
+#define SCTRL_PCIE_HPCLK_BIT 0x184000
+#define SCTRL_PCIE_OE_OFFSET 0x14a
+#define PCIE_DEBOUNCE_PARAM 0xf0f400
+#define PCIE_OE_BYPASS GENMASK(29, 28)
+
+/* peri_crg ctrl */
+#define CRGCTRL_PCIE_ASSERT_OFFSET 0x88
+#define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000
+
+#define FNPLL_HAS_LOCKED BIT(4)
+
+/* Time for delay */
+#define TIME_CMOS_MIN 100
+#define TIME_CMOS_MAX 105
+#define PIPE_CLK_STABLE_TIME 100
+#define PLL_CTRL_WAIT_TIME 200
+#define NOC_POWER_TIME 100
+
+struct hi3670_pcie_phy {
+ struct device *dev;
+ void __iomem *base;
+ struct regmap *apb;
+ struct regmap *crgctrl;
+ struct regmap *sysctrl;
+ struct regmap *pmctrl;
+ struct clk *apb_sys_clk;
+ struct clk *apb_phy_clk;
+ struct clk *phy_ref_clk;
+ struct clk *aclk;
+ struct clk *aux_clk;
+ u32 eye_param[NUM_EYEPARAM];
+};
+
+/* Registers in PCIePHY */
+static inline void hi3670_apb_phy_writel(struct hi3670_pcie_phy *phy, u32 val,
+ u32 reg)
+{
+ writel(val, phy->base + APB_PHY_START_ADDR + reg);
+}
+
+static inline u32 hi3670_apb_phy_readl(struct hi3670_pcie_phy *phy, u32 reg)
+{
+ return readl(phy->base + APB_PHY_START_ADDR + reg);
+}
+
+static inline void hi3670_apb_phy_updatel(struct hi3670_pcie_phy *phy,
+ u32 val, u32 mask, u32 reg)
+{
+ u32 regval;
+
+ regval = hi3670_apb_phy_readl(phy, reg);
+ regval &= ~mask;
+ regval |= val;
+ hi3670_apb_phy_writel(phy, regval, reg);
+}
+
+static inline void kirin_apb_natural_phy_writel(struct hi3670_pcie_phy *phy,
+ u32 val, u32 reg)
+{
+ writel(val, phy->base + reg);
+}
+
+static inline u32 kirin_apb_natural_phy_readl(struct hi3670_pcie_phy *phy,
+ u32 reg)
+{
+ return readl(phy->base + reg);
+}
+
+static void hi3670_pcie_phy_oe_enable(struct hi3670_pcie_phy *phy, bool enable)
+{
+ u32 val;
+
+ regmap_read(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, &val);
+ val |= PCIE_DEBOUNCE_PARAM;
+ if (enable)
+ val &= ~PCIE_OE_BYPASS;
+ else
+ val |= PCIE_OE_BYPASS;
+ regmap_write(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, val);
+}
+
+static void hi3670_pcie_get_eyeparam(struct hi3670_pcie_phy *phy)
+{
+ struct device *dev = phy->dev;
+ struct device_node *np;
+ int ret, i;
+
+ np = dev->of_node;
+
+ ret = of_property_read_u32_array(np, "hisilicon,eye-diagram-param",
+ phy->eye_param, NUM_EYEPARAM);
+ if (!ret)
+ return;
+
+ /* There's no optional eye_param property. Set array to default */
+ for (i = 0; i < NUM_EYEPARAM; i++)
+ phy->eye_param[i] = EYEPARAM_NOCFG;
+}
+
+static void hi3670_pcie_set_eyeparam(struct hi3670_pcie_phy *phy)
+{
+ u32 val;
+
+ val = kirin_apb_natural_phy_readl(phy, RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
+
+ if (phy->eye_param[1] != EYEPARAM_NOCFG) {
+ val &= ~EYE_PARM1_MASK;
+ val |= FIELD_PREP(EYE_PARM1_MASK, phy->eye_param[1]);
+ val |= EYE_PARM1_EN;
+ }
+ kirin_apb_natural_phy_writel(phy, val,
+ RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
+
+ val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_2);
+ val &= ~(EYE_PARM2_MASK | EYE_PARM3_MASK);
+ if (phy->eye_param[2] != EYEPARAM_NOCFG) {
+ val |= FIELD_PREP(EYE_PARM2_MASK, phy->eye_param[2]);
+ val |= EYE_PARM2_EN;
+ }
+
+ if (phy->eye_param[3] != EYEPARAM_NOCFG) {
+ val |= FIELD_PREP(EYE_PARM3_MASK, phy->eye_param[3]);
+ val |= EYE_PARM3_EN;
+ }
+
+ kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_2);
+
+ val = kirin_apb_natural_phy_readl(phy, SUP_DIG_LVL_OVRD_IN);
+ if (phy->eye_param[0] != EYEPARAM_NOCFG) {
+ val &= ~EYE_PARM0_MASK;
+ val |= FIELD_PREP(EYE_PARM0_MASK, phy->eye_param[0]);
+ val |= EYE_PARM0_EN;
+ }
+ kirin_apb_natural_phy_writel(phy, val, SUP_DIG_LVL_OVRD_IN);
+
+ val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_1);
+ if (phy->eye_param[4] != EYEPARAM_NOCFG) {
+ val &= ~EYE_PARM4_MASK;
+ val |= FIELD_PREP(EYE_PARM4_MASK, phy->eye_param[4]);
+ val |= EYE_PARM4_EN;
+ }
+ kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_1);
+}
+
+static void hi3670_pcie_natural_cfg(struct hi3670_pcie_phy *phy)
+{
+ u32 val;
+
+ /* change 2p mem_ctrl */
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL20_ADDR,
+ SOC_PCIECTRL_CTRL20_2P_MEM_CTRL);
+
+ regmap_read(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, &val);
+ val |= PCIE_PULL_UP_SYS_AUX_PWR_DET;
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, val);
+
+ /* output, pull down */
+ regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val);
+ val &= ~PCIE_OUTPUT_PULL_BITS;
+ val |= PCIE_OUTPUT_PULL_DOWN;
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val);
+
+ /* Handle phy_reset and lane0_reset to HW */
+ hi3670_apb_phy_updatel(phy, PCIEPHY_RESET_BIT,
+ PCIEPHY_PIPE_LINE0_RESET_BIT | PCIEPHY_RESET_BIT,
+ SOC_PCIEPHY_CTRL1_ADDR);
+
+ /* fix chip bug: TxDetectRx fail */
+ hi3670_apb_phy_updatel(phy, PCIE_TXDETECT_RX_FAIL, PCIE_TXDETECT_RX_FAIL,
+ SOC_PCIEPHY_CTRL38_ADDR);
+}
+
+static void hi3670_pcie_pll_init(struct hi3670_pcie_phy *phy)
+{
+ hi3670_apb_phy_updatel(phy, PCIE_PHY_CHOOSE_FNPLL, PCIE_PHY_CHOOSE_FNPLL,
+ SOC_PCIEPHY_MMC1PLL_CTRL1);
+
+ hi3670_apb_phy_updatel(phy,
+ FIELD_PREP(PCIE_FNPLL_FBDIV_MASK, PCIE_FNPLL_FBDIV),
+ PCIE_FNPLL_FBDIV_MASK,
+ SOC_PCIEPHY_MMC1PLL_CTRL16);
+
+ hi3670_apb_phy_updatel(phy,
+ FIELD_PREP(PCIE_FNPLL_FRACDIV_MASK, PCIE_FNPLL_FRACDIV),
+ PCIE_FNPLL_FRACDIV_MASK, SOC_PCIEPHY_MMC1PLL_CTRL17);
+
+ hi3670_apb_phy_updatel(phy,
+ PCIE_FNPLL_DLL_EN |
+ FIELD_PREP(PCIE_FNPLL_POSTDIV1_MASK, PCIE_FNPLL_POSTDIV1) |
+ FIELD_PREP(PCIE_FNPLL_POSTDIV2_MASK, PCIE_FNPLL_POSTDIV2) |
+ FIELD_PREP(PCIE_FNPLL_PLL_MODE_MASK, PCIE_FNPLL_PLL_MODE),
+ PCIE_FNPLL_POSTDIV1_MASK |
+ PCIE_FNPLL_POSTDIV2_MASK |
+ PCIE_FNPLL_PLL_MODE_MASK | PCIE_FNPLL_DLL_EN,
+ SOC_PCIEPHY_MMC1PLL_CTRL20);
+
+ hi3670_apb_phy_writel(phy, PCIE_PHY_MMC1PLL,
+ SOC_PCIEPHY_MMC1PLL_CTRL21);
+}
+
+static int hi3670_pcie_pll_ctrl(struct hi3670_pcie_phy *phy, bool enable)
+{
+ struct device *dev = phy->dev;
+ u32 val;
+ int time = PLL_CTRL_WAIT_TIME;
+
+ if (enable) {
+ /* pd = 0 */
+ hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_MMC1PLL_DISABLE,
+ SOC_PCIEPHY_MMC1PLL_CTRL16);
+
+ /* choose FNPLL */
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0);
+ while (!(val & FNPLL_HAS_LOCKED)) {
+ if (!time) {
+ dev_err(dev, "wait for pll_lock timeout\n");
+ return -EINVAL;
+ }
+ time--;
+ udelay(1);
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0);
+ }
+
+ hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_PCIEPL_BP,
+ SOC_PCIEPHY_MMC1PLL_CTRL20);
+
+ } else {
+ hi3670_apb_phy_updatel(phy,
+ PCIE_PHY_MMC1PLL_DISABLE,
+ PCIE_PHY_MMC1PLL_DISABLE,
+ SOC_PCIEPHY_MMC1PLL_CTRL16);
+
+ hi3670_apb_phy_updatel(phy, PCIE_PHY_PCIEPL_BP,
+ PCIE_PHY_PCIEPL_BP,
+ SOC_PCIEPHY_MMC1PLL_CTRL20);
+ }
+
+ return 0;
+}
+
+static void hi3670_pcie_hp_debounce_gt(struct hi3670_pcie_phy *phy, bool open)
+{
+ if (open)
+ /* gt_clk_pcie_hp/gt_clk_pcie_debounce open */
+ regmap_write(phy->crgctrl, CRGPERIPH_PEREN12,
+ IO_HP_DEBOUNCE_GT);
+ else
+ /* gt_clk_pcie_hp/gt_clk_pcie_debounce close */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
+ IO_HP_DEBOUNCE_GT);
+}
+
+static void hi3670_pcie_phyref_gt(struct hi3670_pcie_phy *phy, bool open)
+{
+ unsigned int val;
+
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+
+ if (open)
+ val &= ~IO_OE_HARD_GT_MODE; /* enable hard gt mode */
+ else
+ val |= IO_OE_HARD_GT_MODE; /* disable hard gt mode */
+
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+
+ /* disable soft gt mode */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, IO_PHYREF_SOFT_GT_MODE);
+}
+
+static void hi3670_pcie_oe_ctrl(struct hi3670_pcie_phy *phy, bool en_flag)
+{
+ unsigned int val;
+
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+
+ /* set ie cfg */
+ val |= IO_IE_EN_HARD_BYPASS;
+
+ /* set oe cfg */
+ val &= ~IO_HARD_CTRL_DEBOUNCE_BYPASS;
+
+ /* set phy_debounce in&out time */
+ val |= (DEBOUNCE_WAITCFG_IN | DEBOUNCE_WAITCFG_OUT);
+
+ /* select oe_gt_mode */
+ val |= IO_OE_GT_MODE;
+
+ if (en_flag)
+ val &= ~IO_OE_EN_HARD_BYPASS;
+ else
+ val |= IO_OE_EN_HARD_BYPASS;
+
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+}
+
+static void hi3670_pcie_ioref_gt(struct hi3670_pcie_phy *phy, bool open)
+{
+ unsigned int val;
+
+ if (open) {
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL21_ADDR,
+ SOC_PCIECTRL_CTRL21_DEFAULT);
+
+ hi3670_pcie_oe_ctrl(phy, true);
+
+ /* en hard gt mode */
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+ val &= ~IO_REF_HARD_GT_MODE;
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+
+ /* disable soft gt mode */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
+ IO_REF_SOFT_GT_MODE);
+
+ } else {
+ /* disable hard gt mode */
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+ val |= IO_REF_HARD_GT_MODE;
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+
+ /* disable soft gt mode */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
+ IO_REF_SOFT_GT_MODE);
+
+ hi3670_pcie_oe_ctrl(phy, false);
+ }
+}
+
+static int hi3670_pcie_allclk_ctrl(struct hi3670_pcie_phy *phy, bool clk_on)
+{
+ struct device *dev = phy->dev;
+ int ret = 0;
+
+ if (!clk_on)
+ goto close_clocks;
+
+ /* choose 100MHz clk src: Bit[8]==1 pad, Bit[8]==0 pll */
+ hi3670_apb_phy_updatel(phy, 0, PCIE_CLK_SOURCE,
+ SOC_PCIEPHY_CTRL1_ADDR);
+
+ hi3670_pcie_pll_init(phy);
+
+ ret = hi3670_pcie_pll_ctrl(phy, true);
+ if (ret) {
+ dev_err(dev, "Failed to enable pll\n");
+ return -EINVAL;
+ }
+ hi3670_pcie_hp_debounce_gt(phy, true);
+ hi3670_pcie_phyref_gt(phy, true);
+ hi3670_pcie_ioref_gt(phy, true);
+
+ ret = clk_set_rate(phy->aclk, AXI_CLK_FREQ);
+ if (ret) {
+ dev_err(dev, "Failed to set rate\n");
+ goto close_clocks;
+ }
+
+ return 0;
+
+close_clocks:
+ hi3670_pcie_ioref_gt(phy, false);
+ hi3670_pcie_phyref_gt(phy, false);
+ hi3670_pcie_hp_debounce_gt(phy, false);
+
+ hi3670_pcie_pll_ctrl(phy, false);
+
+ return ret;
+}
+
+static bool is_pipe_clk_stable(struct hi3670_pcie_phy *phy)
+{
+ struct device *dev = phy->dev;
+ u32 val;
+ u32 time = PIPE_CLK_STABLE_TIME;
+ u32 pipe_clk_stable = PCIE_IS_CLOCK_STABLE;
+
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR);
+ while (val & pipe_clk_stable) {
+ mdelay(1);
+ if (!time) {
+ dev_err(dev, "PIPE clk is not stable\n");
+ return false;
+ }
+ time--;
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR);
+ }
+
+ return true;
+}
+
+static int hi3670_pcie_noc_power(struct hi3670_pcie_phy *phy, bool enable)
+{
+ struct device *dev = phy->dev;
+ u32 time = NOC_POWER_TIME;
+ unsigned int val = NOC_PW_MASK;
+ int rst;
+
+ if (enable)
+ val = NOC_PW_MASK | NOC_PW_SET_BIT;
+ else
+ val = NOC_PW_MASK;
+ rst = enable ? 1 : 0;
+
+ regmap_write(phy->pmctrl, NOC_POWER_IDLEREQ_1, val);
+
+ time = NOC_POWER_TIME;
+ regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val);
+ while ((val & NOC_PW_SET_BIT) != rst) {
+ udelay(10);
+ if (!time) {
+ dev_err(dev, "Failed to reverse noc power-status\n");
+ return -EINVAL;
+ }
+ time--;
+ regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val);
+ }
+
+ return 0;
+}
+
+static int hi3670_pcie_get_resources_from_pcie(struct hi3670_pcie_phy *phy)
+{
+ struct device_node *pcie_port;
+ struct device *dev = phy->dev;
+ struct device *pcie_dev;
+
+ pcie_port = of_get_child_by_name(dev->parent->of_node, "pcie");
+ if (!pcie_port) {
+ dev_err(dev, "no pcie node found in %s\n",
+ dev->parent->of_node->full_name);
+ return -ENODEV;
+ }
+
+ pcie_dev = bus_find_device_by_of_node(&platform_bus_type, pcie_port);
+ if (!pcie_dev) {
+ dev_err(dev, "Didn't find pcie device\n");
+ return -ENODEV;
+ }
+
+ /*
+ * We might just use NULL instead of the APB name, as the
+ * pcie-kirin currently registers directly just one regmap (although
+ * the DWC driver register other regmaps).
+ *
+ * Yet, it sounds safer to warrant that it will be accessing the
+ * right regmap. So, let's use the named version.
+ */
+ phy->apb = dev_get_regmap(pcie_dev, "kirin_pcie_apb");
+ if (!phy->apb) {
+ dev_err(dev, "Failed to get APB regmap\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int kirin_pcie_clk_ctrl(struct hi3670_pcie_phy *phy, bool enable)
+{
+ int ret = 0;
+
+ if (!enable)
+ goto close_clk;
+
+ ret = clk_set_rate(phy->phy_ref_clk, REF_CLK_FREQ);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(phy->phy_ref_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(phy->apb_sys_clk);
+ if (ret)
+ goto apb_sys_fail;
+
+ ret = clk_prepare_enable(phy->apb_phy_clk);
+ if (ret)
+ goto apb_phy_fail;
+
+ ret = clk_prepare_enable(phy->aclk);
+ if (ret)
+ goto aclk_fail;
+
+ ret = clk_prepare_enable(phy->aux_clk);
+ if (ret)
+ goto aux_clk_fail;
+
+ return 0;
+
+close_clk:
+ clk_disable_unprepare(phy->aux_clk);
+aux_clk_fail:
+ clk_disable_unprepare(phy->aclk);
+aclk_fail:
+ clk_disable_unprepare(phy->apb_phy_clk);
+apb_phy_fail:
+ clk_disable_unprepare(phy->apb_sys_clk);
+apb_sys_fail:
+ clk_disable_unprepare(phy->phy_ref_clk);
+
+ return ret;
+}
+
+static int hi3670_pcie_phy_init(struct phy *generic_phy)
+{
+ struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
+ int ret;
+
+ /*
+ * The code under hi3670_pcie_get_resources_from_pcie() need to
+ * access the reset-gpios and the APB registers, both from the
+ * pcie-kirin driver.
+ *
+ * The APB is obtained via the pcie driver's regmap
+ * Such kind of resource can only be obtained during the PCIe
+ * power_on sequence, as the code inside pcie-kirin needs to
+ * be already probed, as it needs to register the APB regmap.
+ */
+
+ ret = hi3670_pcie_get_resources_from_pcie(phy);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int hi3670_pcie_phy_power_on(struct phy *generic_phy)
+{
+ struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
+ int val, ret;
+
+ /* Power supply for Host */
+ regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, SCTRL_PCIE_CMOS_BIT);
+ usleep_range(TIME_CMOS_MIN, TIME_CMOS_MAX);
+
+ hi3670_pcie_phy_oe_enable(phy, true);
+
+ ret = kirin_pcie_clk_ctrl(phy, true);
+ if (ret)
+ return ret;
+
+ /* ISO disable, PCIeCtrl, PHY assert and clk gate clear */
+ regmap_write(phy->sysctrl, SCTRL_PCIE_ISO_OFFSET, SCTRL_PCIE_ISO_BIT);
+ regmap_write(phy->crgctrl, CRGCTRL_PCIE_ASSERT_OFFSET,
+ CRGCTRL_PCIE_ASSERT_BIT);
+ regmap_write(phy->sysctrl, SCTRL_PCIE_HPCLK_OFFSET,
+ SCTRL_PCIE_HPCLK_BIT);
+
+ hi3670_pcie_natural_cfg(phy);
+
+ ret = hi3670_pcie_allclk_ctrl(phy, true);
+ if (ret)
+ goto disable_clks;
+
+ /* pull down phy_test_powerdown signal */
+ hi3670_apb_phy_updatel(phy, 0, PCIE_PULL_DOWN_PHY_TEST_POWERDOWN,
+ SOC_PCIEPHY_CTRL0_ADDR);
+
+ /* deassert controller perst_n */
+ regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val);
+ val |= PCIE_DEASSERT_CONTROLLER_PERST;
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val);
+ udelay(10);
+
+ ret = is_pipe_clk_stable(phy);
+ if (!ret)
+ goto disable_clks;
+
+ hi3670_pcie_set_eyeparam(phy);
+
+ ret = hi3670_pcie_noc_power(phy, false);
+ if (ret)
+ goto disable_clks;
+
+ return 0;
+
+disable_clks:
+ kirin_pcie_clk_ctrl(phy, false);
+ return ret;
+}
+
+static int hi3670_pcie_phy_power_off(struct phy *generic_phy)
+{
+ struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
+
+ hi3670_pcie_phy_oe_enable(phy, false);
+
+ hi3670_pcie_allclk_ctrl(phy, false);
+
+ /* Drop power supply for Host */
+ regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, 0);
+
+ /*
+ * FIXME: The enabled clocks should be disabled here by calling
+ * kirin_pcie_clk_ctrl(phy, false);
+ * However, some clocks used at Kirin 970 should be marked as
+ * CLK_IS_CRITICAL at clk-hi3670 driver, as powering such clocks off
+ * cause an Asynchronous SError interrupt, which produces panic().
+ * While clk-hi3670 is not fixed, we cannot risk disabling clocks here.
+ */
+
+ return 0;
+}
+
+static const struct phy_ops hi3670_phy_ops = {
+ .init = hi3670_pcie_phy_init,
+ .power_on = hi3670_pcie_phy_power_on,
+ .power_off = hi3670_pcie_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int hi3670_pcie_phy_get_resources(struct hi3670_pcie_phy *phy,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ /* syscon */
+ phy->crgctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-crgctrl");
+ if (IS_ERR(phy->crgctrl))
+ return PTR_ERR(phy->crgctrl);
+
+ phy->sysctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-sctrl");
+ if (IS_ERR(phy->sysctrl))
+ return PTR_ERR(phy->sysctrl);
+
+ phy->pmctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-pmctrl");
+ if (IS_ERR(phy->pmctrl))
+ return PTR_ERR(phy->pmctrl);
+
+ /* clocks */
+ phy->phy_ref_clk = devm_clk_get(dev, "phy_ref");
+ if (IS_ERR(phy->phy_ref_clk))
+ return PTR_ERR(phy->phy_ref_clk);
+
+ phy->aux_clk = devm_clk_get(dev, "aux");
+ if (IS_ERR(phy->aux_clk))
+ return PTR_ERR(phy->aux_clk);
+
+ phy->apb_phy_clk = devm_clk_get(dev, "apb_phy");
+ if (IS_ERR(phy->apb_phy_clk))
+ return PTR_ERR(phy->apb_phy_clk);
+
+ phy->apb_sys_clk = devm_clk_get(dev, "apb_sys");
+ if (IS_ERR(phy->apb_sys_clk))
+ return PTR_ERR(phy->apb_sys_clk);
+
+ phy->aclk = devm_clk_get(dev, "aclk");
+ if (IS_ERR(phy->aclk))
+ return PTR_ERR(phy->aclk);
+
+ /* registers */
+ phy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(phy->base))
+ return PTR_ERR(phy->base);
+
+ hi3670_pcie_get_eyeparam(phy);
+
+ return 0;
+}
+
+static int hi3670_pcie_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct hi3670_pcie_phy *phy;
+ struct phy *generic_phy;
+ int ret;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ phy->dev = dev;
+
+ ret = hi3670_pcie_phy_get_resources(phy, pdev);
+ if (ret)
+ return ret;
+
+ generic_phy = devm_phy_create(dev, dev->of_node, &hi3670_phy_ops);
+ if (IS_ERR(generic_phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(generic_phy);
+ }
+
+ phy_set_drvdata(generic_phy, 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 hi3670_pcie_phy_match[] = {
+ {
+ .compatible = "hisilicon,hi970-pcie-phy",
+ },
+ {},
+};
+
+static struct platform_driver hi3670_pcie_phy_driver = {
+ .probe = hi3670_pcie_phy_probe,
+ .driver = {
+ .of_match_table = hi3670_pcie_phy_match,
+ .name = "hi3670_pcie_phy",
+ .suppress_bind_attrs = true,
+ }
+};
+builtin_platform_driver(hi3670_pcie_phy_driver);
+
+MODULE_DEVICE_TABLE(of, hi3670_pcie_phy_match);
+MODULE_DESCRIPTION("PCIe phy driver for Kirin 970");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
+MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/hisilicon/phy-hi3670-usb3.c b/drivers/phy/hisilicon/phy-hi3670-usb3.c
new file mode 100644
index 000000000000..b9ffe08abaab
--- /dev/null
+++ b/drivers/phy/hisilicon/phy-hi3670-usb3.c
@@ -0,0 +1,661 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Phy provider for USB 3.1 controller on HiSilicon Kirin970 platform
+ *
+ * Copyright (C) 2017-2020 Hilisicon Electronics Co., Ltd.
+ * http://www.huawei.com
+ *
+ * Authors: Yu Chen <chenyu56@huawei.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define SCTRL_SCDEEPSLEEPED (0x0)
+#define USB_CLK_SELECTED BIT(20)
+
+#define PERI_CRG_PEREN0 (0x00)
+#define PERI_CRG_PERDIS0 (0x04)
+#define PERI_CRG_PEREN4 (0x40)
+#define PERI_CRG_PERDIS4 (0x44)
+#define PERI_CRG_PERRSTEN4 (0x90)
+#define PERI_CRG_PERRSTDIS4 (0x94)
+#define PERI_CRG_ISODIS (0x148)
+#define PERI_CRG_PEREN6 (0x410)
+#define PERI_CRG_PERDIS6 (0x414)
+
+#define USB_REFCLK_ISO_EN BIT(25)
+
+#define GT_CLK_USB2PHY_REF BIT(19)
+
+#define PCTRL_PERI_CTRL3 (0x10)
+#define PCTRL_PERI_CTRL3_MSK_START (16)
+#define USB_TCXO_EN BIT(1)
+
+#define PCTRL_PERI_CTRL24 (0x64)
+#define SC_CLK_USB3PHY_3MUX1_SEL BIT(25)
+
+#define USB3OTG_CTRL0 (0x00)
+#define USB3OTG_CTRL3 (0x0c)
+#define USB3OTG_CTRL4 (0x10)
+#define USB3OTG_CTRL5 (0x14)
+#define USB3OTG_CTRL7 (0x1c)
+#define USB_MISC_CFG50 (0x50)
+#define USB_MISC_CFG54 (0x54)
+#define USB_MISC_CFG58 (0x58)
+#define USB_MISC_CFG5C (0x5c)
+#define USB_MISC_CFGA0 (0xa0)
+#define TCA_CLK_RST (0x200)
+#define TCA_INTR_EN (0x204)
+#define TCA_INTR_STS (0x208)
+#define TCA_GCFG (0x210)
+#define TCA_TCPC (0x214)
+#define TCA_SYSMODE_CFG (0x218)
+#define TCA_VBUS_CTRL (0x240)
+
+#define CTRL0_USB3_VBUSVLD BIT(7)
+#define CTRL0_USB3_VBUSVLD_SEL BIT(6)
+
+#define CTRL3_USB2_VBUSVLDEXT0 BIT(6)
+#define CTRL3_USB2_VBUSVLDEXTSEL0 BIT(5)
+
+#define CTRL5_USB2_SIDDQ BIT(0)
+
+#define CTRL7_USB2_REFCLKSEL_MASK GENMASK(4, 3)
+#define CTRL7_USB2_REFCLKSEL_ABB (BIT(4) | BIT(3))
+#define CTRL7_USB2_REFCLKSEL_PAD BIT(4)
+
+#define CFG50_USB3_PHY_TEST_POWERDOWN BIT(23)
+
+#define CFG54_USB31PHY_CR_ADDR_MASK GENMASK(31, 16)
+
+#define CFG54_USB3PHY_REF_USE_PAD BIT(12)
+#define CFG54_PHY0_PMA_PWR_STABLE BIT(11)
+#define CFG54_PHY0_PCS_PWR_STABLE BIT(9)
+#define CFG54_USB31PHY_CR_ACK BIT(7)
+#define CFG54_USB31PHY_CR_WR_EN BIT(5)
+#define CFG54_USB31PHY_CR_SEL BIT(4)
+#define CFG54_USB31PHY_CR_RD_EN BIT(3)
+#define CFG54_USB31PHY_CR_CLK BIT(2)
+#define CFG54_USB3_PHY0_ANA_PWR_EN BIT(1)
+
+#define CFG58_USB31PHY_CR_DATA_MASK GENMASK(31, 16)
+
+#define CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN BIT(1)
+
+#define CFGA0_VAUX_RESET BIT(9)
+#define CFGA0_USB31C_RESET BIT(8)
+#define CFGA0_USB2PHY_REFCLK_SELECT BIT(4)
+#define CFGA0_USB3PHY_RESET BIT(1)
+#define CFGA0_USB2PHY_POR BIT(0)
+
+#define INTR_EN_XA_TIMEOUT_EVT_EN BIT(1)
+#define INTR_EN_XA_ACK_EVT_EN BIT(0)
+
+#define CLK_RST_TCA_REF_CLK_EN BIT(1)
+#define CLK_RST_SUSPEND_CLK_EN BIT(0)
+
+#define GCFG_ROLE_HSTDEV BIT(4)
+#define GCFG_OP_MODE GENMASK(1, 0)
+#define GCFG_OP_MODE_CTRL_SYNC_MODE BIT(0)
+
+#define TCPC_VALID BIT(4)
+#define TCPC_LOW_POWER_EN BIT(3)
+#define TCPC_MUX_CONTROL_MASK GENMASK(1, 0)
+#define TCPC_MUX_CONTROL_USB31 BIT(0)
+
+#define SYSMODE_CFG_TYPEC_DISABLE BIT(3)
+
+#define VBUS_CTRL_POWERPRESENT_OVERRD GENMASK(3, 2)
+#define VBUS_CTRL_VBUSVALID_OVERRD GENMASK(1, 0)
+
+#define KIRIN970_USB_DEFAULT_PHY_PARAM (0xfdfee4)
+#define KIRIN970_USB_DEFAULT_PHY_VBOOST (0x5)
+
+#define TX_VBOOST_LVL_REG (0xf)
+#define TX_VBOOST_LVL_START (6)
+#define TX_VBOOST_LVL_ENABLE BIT(9)
+
+struct hi3670_priv {
+ struct device *dev;
+ struct regmap *peri_crg;
+ struct regmap *pctrl;
+ struct regmap *sctrl;
+ struct regmap *usb31misc;
+
+ u32 eye_diagram_param;
+ u32 tx_vboost_lvl;
+
+ u32 peri_crg_offset;
+ u32 pctrl_offset;
+ u32 usb31misc_offset;
+};
+
+static int hi3670_phy_cr_clk(struct regmap *usb31misc)
+{
+ int ret;
+
+ /* Clock up */
+ ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
+ CFG54_USB31PHY_CR_CLK, CFG54_USB31PHY_CR_CLK);
+ if (ret)
+ return ret;
+
+ /* Clock down */
+ return regmap_update_bits(usb31misc, USB_MISC_CFG54,
+ CFG54_USB31PHY_CR_CLK, 0);
+}
+
+static int hi3670_phy_cr_set_sel(struct regmap *usb31misc)
+{
+ return regmap_update_bits(usb31misc, USB_MISC_CFG54,
+ CFG54_USB31PHY_CR_SEL, CFG54_USB31PHY_CR_SEL);
+}
+
+static int hi3670_phy_cr_start(struct regmap *usb31misc, int direction)
+{
+ int ret, reg;
+
+ if (direction)
+ reg = CFG54_USB31PHY_CR_WR_EN;
+ else
+ reg = CFG54_USB31PHY_CR_RD_EN;
+
+ ret = regmap_update_bits(usb31misc, USB_MISC_CFG54, reg, reg);
+
+ if (ret)
+ return ret;
+
+ ret = hi3670_phy_cr_clk(usb31misc);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(usb31misc, USB_MISC_CFG54,
+ CFG54_USB31PHY_CR_RD_EN | CFG54_USB31PHY_CR_WR_EN, 0);
+}
+
+static int hi3670_phy_cr_wait_ack(struct regmap *usb31misc)
+{
+ u32 reg;
+ int retry = 10;
+ int ret;
+
+ while (retry-- > 0) {
+ ret = regmap_read(usb31misc, USB_MISC_CFG54, &reg);
+ if (ret)
+ return ret;
+ if ((reg & CFG54_USB31PHY_CR_ACK) == CFG54_USB31PHY_CR_ACK)
+ return 0;
+
+ ret = hi3670_phy_cr_clk(usb31misc);
+ if (ret)
+ return ret;
+
+ usleep_range(10, 20);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int hi3670_phy_cr_set_addr(struct regmap *usb31misc, u32 addr)
+{
+ u32 reg;
+ int ret;
+
+ ret = regmap_read(usb31misc, USB_MISC_CFG54, &reg);
+ if (ret)
+ return ret;
+
+ reg = FIELD_PREP(CFG54_USB31PHY_CR_ADDR_MASK, addr);
+
+ return regmap_update_bits(usb31misc, USB_MISC_CFG54,
+ CFG54_USB31PHY_CR_ADDR_MASK, reg);
+}
+
+static int hi3670_phy_cr_read(struct regmap *usb31misc, u32 addr, u32 *val)
+{
+ int reg, i, ret;
+
+ for (i = 0; i < 100; i++) {
+ ret = hi3670_phy_cr_clk(usb31misc);
+ if (ret)
+ return ret;
+ }
+
+ ret = hi3670_phy_cr_set_sel(usb31misc);
+ if (ret)
+ return ret;
+
+ ret = hi3670_phy_cr_set_addr(usb31misc, addr);
+ if (ret)
+ return ret;
+
+ ret = hi3670_phy_cr_start(usb31misc, 0);
+ if (ret)
+ return ret;
+
+ ret = hi3670_phy_cr_wait_ack(usb31misc);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(usb31misc, USB_MISC_CFG58, &reg);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(CFG58_USB31PHY_CR_DATA_MASK, reg);
+
+ return 0;
+}
+
+static int hi3670_phy_cr_write(struct regmap *usb31misc, u32 addr, u32 val)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < 100; i++) {
+ ret = hi3670_phy_cr_clk(usb31misc);
+ if (ret)
+ return ret;
+ }
+
+ ret = hi3670_phy_cr_set_sel(usb31misc);
+ if (ret)
+ return ret;
+
+ ret = hi3670_phy_cr_set_addr(usb31misc, addr);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(usb31misc, USB_MISC_CFG58,
+ FIELD_PREP(CFG58_USB31PHY_CR_DATA_MASK, val));
+ if (ret)
+ return ret;
+
+ ret = hi3670_phy_cr_start(usb31misc, 1);
+ if (ret)
+ return ret;
+
+ return hi3670_phy_cr_wait_ack(usb31misc);
+}
+
+static int hi3670_phy_set_params(struct hi3670_priv *priv)
+{
+ u32 reg;
+ int ret;
+ int retry = 3;
+
+ ret = regmap_write(priv->usb31misc, USB3OTG_CTRL4,
+ priv->eye_diagram_param);
+ if (ret) {
+ dev_err(priv->dev, "set USB3OTG_CTRL4 failed\n");
+ return ret;
+ }
+
+ while (retry-- > 0) {
+ ret = hi3670_phy_cr_read(priv->usb31misc,
+ TX_VBOOST_LVL_REG, &reg);
+ if (!ret)
+ break;
+
+ if (ret != -ETIMEDOUT) {
+ dev_err(priv->dev, "read TX_VBOOST_LVL_REG failed\n");
+ return ret;
+ }
+ }
+ if (ret)
+ return ret;
+
+ reg |= (TX_VBOOST_LVL_ENABLE | (priv->tx_vboost_lvl << TX_VBOOST_LVL_START));
+ ret = hi3670_phy_cr_write(priv->usb31misc, TX_VBOOST_LVL_REG, reg);
+ if (ret)
+ dev_err(priv->dev, "write TX_VBOOST_LVL_REG failed\n");
+
+ return ret;
+}
+
+static bool hi3670_is_abbclk_selected(struct hi3670_priv *priv)
+{
+ u32 reg;
+
+ if (!priv->sctrl) {
+ dev_err(priv->dev, "priv->sctrl is null!\n");
+ return false;
+ }
+
+ if (regmap_read(priv->sctrl, SCTRL_SCDEEPSLEEPED, &reg)) {
+ dev_err(priv->dev, "SCTRL_SCDEEPSLEEPED read failed!\n");
+ return false;
+ }
+
+ if ((reg & USB_CLK_SELECTED) == 0)
+ return false;
+
+ return true;
+}
+
+static int hi3670_config_phy_clock(struct hi3670_priv *priv)
+{
+ u32 val, mask;
+ int ret;
+
+ if (!hi3670_is_abbclk_selected(priv)) {
+ /* usb refclk iso disable */
+ ret = regmap_write(priv->peri_crg, PERI_CRG_ISODIS,
+ USB_REFCLK_ISO_EN);
+ if (ret)
+ goto out;
+
+ /* enable usb_tcxo_en */
+ ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3,
+ USB_TCXO_EN |
+ (USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START));
+
+ /* select usbphy clk from abb */
+ mask = SC_CLK_USB3PHY_3MUX1_SEL;
+ ret = regmap_update_bits(priv->pctrl,
+ PCTRL_PERI_CTRL24, mask, 0);
+ if (ret)
+ goto out;
+
+ ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFGA0,
+ CFGA0_USB2PHY_REFCLK_SELECT, 0);
+ if (ret)
+ goto out;
+
+ ret = regmap_read(priv->usb31misc, USB3OTG_CTRL7, &val);
+ if (ret)
+ goto out;
+ val &= ~CTRL7_USB2_REFCLKSEL_MASK;
+ val |= CTRL7_USB2_REFCLKSEL_ABB;
+ ret = regmap_write(priv->usb31misc, USB3OTG_CTRL7, val);
+ if (ret)
+ goto out;
+
+ return 0;
+ }
+
+ ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFG54,
+ CFG54_USB3PHY_REF_USE_PAD,
+ CFG54_USB3PHY_REF_USE_PAD);
+ if (ret)
+ goto out;
+
+ ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFGA0,
+ CFGA0_USB2PHY_REFCLK_SELECT,
+ CFGA0_USB2PHY_REFCLK_SELECT);
+ if (ret)
+ goto out;
+
+ ret = regmap_read(priv->usb31misc, USB3OTG_CTRL7, &val);
+ if (ret)
+ goto out;
+ val &= ~CTRL7_USB2_REFCLKSEL_MASK;
+ val |= CTRL7_USB2_REFCLKSEL_PAD;
+ ret = regmap_write(priv->usb31misc, USB3OTG_CTRL7, val);
+ if (ret)
+ goto out;
+
+ ret = regmap_write(priv->peri_crg,
+ PERI_CRG_PEREN6, GT_CLK_USB2PHY_REF);
+ if (ret)
+ goto out;
+
+ return 0;
+out:
+ dev_err(priv->dev, "failed to config phy clock ret: %d\n", ret);
+ return ret;
+}
+
+static int hi3670_config_tca(struct hi3670_priv *priv)
+{
+ u32 val, mask;
+ int ret;
+
+ ret = regmap_write(priv->usb31misc, TCA_INTR_STS, 0xffff);
+ if (ret)
+ goto out;
+
+ ret = regmap_write(priv->usb31misc, TCA_INTR_EN,
+ INTR_EN_XA_TIMEOUT_EVT_EN | INTR_EN_XA_ACK_EVT_EN);
+ if (ret)
+ goto out;
+
+ mask = CLK_RST_TCA_REF_CLK_EN | CLK_RST_SUSPEND_CLK_EN;
+ ret = regmap_update_bits(priv->usb31misc, TCA_CLK_RST, mask, 0);
+ if (ret)
+ goto out;
+
+ ret = regmap_update_bits(priv->usb31misc, TCA_GCFG,
+ GCFG_ROLE_HSTDEV | GCFG_OP_MODE,
+ GCFG_ROLE_HSTDEV | GCFG_OP_MODE_CTRL_SYNC_MODE);
+ if (ret)
+ goto out;
+
+ ret = regmap_update_bits(priv->usb31misc, TCA_SYSMODE_CFG,
+ SYSMODE_CFG_TYPEC_DISABLE, 0);
+ if (ret)
+ goto out;
+
+ ret = regmap_read(priv->usb31misc, TCA_TCPC, &val);
+ if (ret)
+ goto out;
+ val &= ~(TCPC_VALID | TCPC_LOW_POWER_EN | TCPC_MUX_CONTROL_MASK);
+ val |= (TCPC_VALID | TCPC_MUX_CONTROL_USB31);
+ ret = regmap_write(priv->usb31misc, TCA_TCPC, val);
+ if (ret)
+ goto out;
+
+ ret = regmap_write(priv->usb31misc, TCA_VBUS_CTRL,
+ VBUS_CTRL_POWERPRESENT_OVERRD | VBUS_CTRL_VBUSVALID_OVERRD);
+ if (ret)
+ goto out;
+
+ return 0;
+out:
+ dev_err(priv->dev, "failed to config phy clock ret: %d\n", ret);
+ return ret;
+}
+
+static int hi3670_phy_init(struct phy *phy)
+{
+ struct hi3670_priv *priv = phy_get_drvdata(phy);
+ u32 val;
+ int ret;
+
+ /* assert controller */
+ val = CFGA0_VAUX_RESET | CFGA0_USB31C_RESET |
+ CFGA0_USB3PHY_RESET | CFGA0_USB2PHY_POR;
+ ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFGA0, val, 0);
+ if (ret)
+ goto out;
+
+ ret = hi3670_config_phy_clock(priv);
+ if (ret)
+ goto out;
+
+ /* Exit from IDDQ mode */
+ ret = regmap_update_bits(priv->usb31misc, USB3OTG_CTRL5,
+ CTRL5_USB2_SIDDQ, 0);
+ if (ret)
+ goto out;
+
+ /* Release USB31 PHY out of TestPowerDown mode */
+ ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFG50,
+ CFG50_USB3_PHY_TEST_POWERDOWN, 0);
+ if (ret)
+ goto out;
+
+ /* Deassert phy */
+ val = CFGA0_USB3PHY_RESET | CFGA0_USB2PHY_POR;
+ ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFGA0, val, val);
+ if (ret)
+ goto out;
+
+ usleep_range(100, 120);
+
+ /* Tell the PHY power is stable */
+ val = CFG54_USB3_PHY0_ANA_PWR_EN | CFG54_PHY0_PCS_PWR_STABLE |
+ CFG54_PHY0_PMA_PWR_STABLE;
+ ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFG54,
+ val, val);
+ if (ret)
+ goto out;
+
+ ret = hi3670_config_tca(priv);
+ if (ret)
+ goto out;
+
+ /* Enable SSC */
+ ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFG5C,
+ CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN,
+ CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN);
+ if (ret)
+ goto out;
+
+ /* Deassert controller */
+ val = CFGA0_VAUX_RESET | CFGA0_USB31C_RESET;
+ ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFGA0, val, val);
+ if (ret)
+ goto out;
+
+ usleep_range(100, 120);
+
+ /* Set fake vbus valid signal */
+ val = CTRL0_USB3_VBUSVLD | CTRL0_USB3_VBUSVLD_SEL;
+ ret = regmap_update_bits(priv->usb31misc, USB3OTG_CTRL0, val, val);
+ if (ret)
+ goto out;
+
+ val = CTRL3_USB2_VBUSVLDEXT0 | CTRL3_USB2_VBUSVLDEXTSEL0;
+ ret = regmap_update_bits(priv->usb31misc, USB3OTG_CTRL3, val, val);
+ if (ret)
+ goto out;
+
+ usleep_range(100, 120);
+
+ ret = hi3670_phy_set_params(priv);
+ if (ret)
+ goto out;
+
+ return 0;
+out:
+ dev_err(priv->dev, "failed to init phy ret: %d\n", ret);
+ return ret;
+}
+
+static int hi3670_phy_exit(struct phy *phy)
+{
+ struct hi3670_priv *priv = phy_get_drvdata(phy);
+ u32 mask;
+ int ret;
+
+ /* Assert phy */
+ mask = CFGA0_USB3PHY_RESET | CFGA0_USB2PHY_POR;
+ ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFGA0, mask, 0);
+ if (ret)
+ goto out;
+
+ if (!hi3670_is_abbclk_selected(priv)) {
+ /* disable usb_tcxo_en */
+ ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3,
+ USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START);
+ } else {
+ ret = regmap_write(priv->peri_crg, PERI_CRG_PERDIS6,
+ GT_CLK_USB2PHY_REF);
+ if (ret)
+ goto out;
+ }
+
+ return 0;
+out:
+ dev_err(priv->dev, "failed to exit phy ret: %d\n", ret);
+ return ret;
+}
+
+static const struct phy_ops hi3670_phy_ops = {
+ .init = hi3670_phy_init,
+ .exit = hi3670_phy_exit,
+ .owner = THIS_MODULE,
+};
+
+static int hi3670_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct phy *phy;
+ struct hi3670_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->peri_crg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "hisilicon,pericrg-syscon");
+ if (IS_ERR(priv->peri_crg)) {
+ dev_err(dev, "no hisilicon,pericrg-syscon\n");
+ return PTR_ERR(priv->peri_crg);
+ }
+
+ priv->pctrl = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "hisilicon,pctrl-syscon");
+ if (IS_ERR(priv->pctrl)) {
+ dev_err(dev, "no hisilicon,pctrl-syscon\n");
+ return PTR_ERR(priv->pctrl);
+ }
+
+ priv->sctrl = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "hisilicon,sctrl-syscon");
+ if (IS_ERR(priv->sctrl)) {
+ dev_err(dev, "no hisilicon,sctrl-syscon\n");
+ return PTR_ERR(priv->sctrl);
+ }
+
+ /* node of hi3670 phy is a sub-node of usb3_otg_bc */
+ priv->usb31misc = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(priv->usb31misc)) {
+ dev_err(dev, "no hisilicon,usb3-otg-bc-syscon\n");
+ return PTR_ERR(priv->usb31misc);
+ }
+
+ if (of_property_read_u32(dev->of_node, "hisilicon,eye-diagram-param",
+ &priv->eye_diagram_param))
+ priv->eye_diagram_param = KIRIN970_USB_DEFAULT_PHY_PARAM;
+
+ if (of_property_read_u32(dev->of_node, "hisilicon,tx-vboost-lvl",
+ &priv->tx_vboost_lvl))
+ priv->tx_vboost_lvl = KIRIN970_USB_DEFAULT_PHY_VBOOST;
+
+ phy = devm_phy_create(dev, NULL, &hi3670_phy_ops);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ phy_set_drvdata(phy, priv);
+ 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 hi3670_phy_of_match[] = {
+ { .compatible = "hisilicon,hi3670-usb-phy" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, hi3670_phy_of_match);
+
+static struct platform_driver hi3670_phy_driver = {
+ .probe = hi3670_phy_probe,
+ .driver = {
+ .name = "hi3670-usb-phy",
+ .of_match_table = hi3670_phy_of_match,
+ }
+};
+module_platform_driver(hi3670_phy_driver);
+
+MODULE_AUTHOR("Yu Chen <chenyu56@huawei.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Hilisicon Kirin970 USB31 PHY Driver");
diff --git a/drivers/phy/hisilicon/phy-hi6220-usb.c b/drivers/phy/hisilicon/phy-hi6220-usb.c
index be05292df8b8..e92ba78da4c8 100644
--- a/drivers/phy/hisilicon/phy-hi6220-usb.c
+++ b/drivers/phy/hisilicon/phy-hi6220-usb.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2015 Linaro Ltd.
- * Copyright (c) 2015 Hisilicon Limited.
+ * Copyright (c) 2015 HiSilicon Limited.
*/
#include <linux/mfd/syscon.h>
diff --git a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c
index 34a6a9a1ceb2..b133ae06757a 100644
--- a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c
+++ b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c
@@ -140,14 +140,19 @@ static int hisi_inno_phy_probe(struct platform_device *pdev)
struct phy *phy;
rst = of_reset_control_get_exclusive(child, NULL);
- if (IS_ERR(rst))
+ if (IS_ERR(rst)) {
+ of_node_put(child);
return PTR_ERR(rst);
+ }
+
priv->ports[i].utmi_rst = rst;
priv->ports[i].priv = priv;
phy = devm_phy_create(dev, child, &hisi_inno_phy_ops);
- if (IS_ERR(phy))
+ if (IS_ERR(phy)) {
+ of_node_put(child);
return PTR_ERR(phy);
+ }
phy_set_bus_width(phy, 8);
phy_set_drvdata(phy, &priv->ports[i]);
@@ -155,6 +160,7 @@ static int hisi_inno_phy_probe(struct platform_device *pdev)
if (i > INNO_PHY_PORT_NUM) {
dev_warn(dev, "Support %d ports in maximum\n", i);
+ of_node_put(child);
break;
}
}
diff --git a/drivers/phy/hisilicon/phy-hix5hd2-sata.c b/drivers/phy/hisilicon/phy-hix5hd2-sata.c
index c67b78cd2602..b0f99a9ac857 100644
--- a/drivers/phy/hisilicon/phy-hix5hd2-sata.c
+++ b/drivers/phy/hisilicon/phy-hix5hd2-sata.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2014 Linaro Ltd.
- * Copyright (c) 2014 Hisilicon Limited.
+ * Copyright (c) 2014 HiSilicon Limited.
*/
#include <linux/delay.h>
diff --git a/drivers/phy/ingenic/Kconfig b/drivers/phy/ingenic/Kconfig
new file mode 100644
index 000000000000..f23cc109324b
--- /dev/null
+++ b/drivers/phy/ingenic/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Phy drivers for Ingenic platforms
+#
+config PHY_INGENIC_USB
+ tristate "Ingenic SoCs USB PHY Driver"
+ depends on MIPS || COMPILE_TEST
+ depends on USB_SUPPORT
+ depends on HAS_IOMEM
+ select GENERIC_PHY
+ help
+ This driver provides USB PHY support for the USB controller found
+ on the JZ-series and X-series SoCs from Ingenic.
diff --git a/drivers/phy/ingenic/Makefile b/drivers/phy/ingenic/Makefile
new file mode 100644
index 000000000000..1cb158d7233f
--- /dev/null
+++ b/drivers/phy/ingenic/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PHY_INGENIC_USB) += phy-ingenic-usb.o
diff --git a/drivers/phy/ingenic/phy-ingenic-usb.c b/drivers/phy/ingenic/phy-ingenic-usb.c
new file mode 100644
index 000000000000..28c28d816484
--- /dev/null
+++ b/drivers/phy/ingenic/phy-ingenic-usb.c
@@ -0,0 +1,389 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Ingenic SoCs USB PHY driver
+ * Copyright (c) Paul Cercueil <paul@crapouillou.net>
+ * Copyright (c) 漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>
+ * Copyright (c) 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+/* OTGPHY register offsets */
+#define REG_USBPCR_OFFSET 0x00
+#define REG_USBRDT_OFFSET 0x04
+#define REG_USBVBFIL_OFFSET 0x08
+#define REG_USBPCR1_OFFSET 0x0c
+
+/* bits within the USBPCR register */
+#define USBPCR_USB_MODE BIT(31)
+#define USBPCR_AVLD_REG BIT(30)
+#define USBPCR_COMMONONN BIT(25)
+#define USBPCR_VBUSVLDEXT BIT(24)
+#define USBPCR_VBUSVLDEXTSEL BIT(23)
+#define USBPCR_POR BIT(22)
+#define USBPCR_SIDDQ BIT(21)
+#define USBPCR_OTG_DISABLE BIT(20)
+#define USBPCR_TXPREEMPHTUNE BIT(6)
+
+#define USBPCR_IDPULLUP_MASK GENMASK(29, 28)
+#define USBPCR_IDPULLUP_ALWAYS 0x2
+#define USBPCR_IDPULLUP_SUSPEND 0x1
+#define USBPCR_IDPULLUP_OTG 0x0
+
+#define USBPCR_COMPDISTUNE_MASK GENMASK(19, 17)
+#define USBPCR_COMPDISTUNE_DFT 0x4
+
+#define USBPCR_OTGTUNE_MASK GENMASK(16, 14)
+#define USBPCR_OTGTUNE_DFT 0x4
+
+#define USBPCR_SQRXTUNE_MASK GENMASK(13, 11)
+#define USBPCR_SQRXTUNE_DCR_20PCT 0x7
+#define USBPCR_SQRXTUNE_DFT 0x3
+
+#define USBPCR_TXFSLSTUNE_MASK GENMASK(10, 7)
+#define USBPCR_TXFSLSTUNE_DCR_50PPT 0xf
+#define USBPCR_TXFSLSTUNE_DCR_25PPT 0x7
+#define USBPCR_TXFSLSTUNE_DFT 0x3
+#define USBPCR_TXFSLSTUNE_INC_25PPT 0x1
+#define USBPCR_TXFSLSTUNE_INC_50PPT 0x0
+
+#define USBPCR_TXHSXVTUNE_MASK GENMASK(5, 4)
+#define USBPCR_TXHSXVTUNE_DFT 0x3
+#define USBPCR_TXHSXVTUNE_DCR_15MV 0x1
+
+#define USBPCR_TXRISETUNE_MASK GENMASK(5, 4)
+#define USBPCR_TXRISETUNE_DFT 0x3
+
+#define USBPCR_TXVREFTUNE_MASK GENMASK(3, 0)
+#define USBPCR_TXVREFTUNE_INC_75PPT 0xb
+#define USBPCR_TXVREFTUNE_INC_25PPT 0x7
+#define USBPCR_TXVREFTUNE_DFT 0x5
+
+/* bits within the USBRDTR register */
+#define USBRDT_UTMI_RST BIT(27)
+#define USBRDT_HB_MASK BIT(26)
+#define USBRDT_VBFIL_LD_EN BIT(25)
+#define USBRDT_IDDIG_EN BIT(24)
+#define USBRDT_IDDIG_REG BIT(23)
+#define USBRDT_VBFIL_EN BIT(2)
+
+/* bits within the USBPCR1 register */
+#define USBPCR1_BVLD_REG BIT(31)
+#define USBPCR1_DPPD BIT(29)
+#define USBPCR1_DMPD BIT(28)
+#define USBPCR1_USB_SEL BIT(28)
+#define USBPCR1_PORT_RST BIT(21)
+#define USBPCR1_WORD_IF_16BIT BIT(19)
+
+struct ingenic_soc_info {
+ void (*usb_phy_init)(struct phy *phy);
+};
+
+struct ingenic_usb_phy {
+ const struct ingenic_soc_info *soc_info;
+
+ struct phy *phy;
+ void __iomem *base;
+ struct clk *clk;
+ struct regulator *vcc_supply;
+};
+
+static int ingenic_usb_phy_init(struct phy *phy)
+{
+ struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
+ int err;
+ u32 reg;
+
+ err = clk_prepare_enable(priv->clk);
+ if (err) {
+ dev_err(&phy->dev, "Unable to start clock: %d\n", err);
+ return err;
+ }
+
+ priv->soc_info->usb_phy_init(phy);
+
+ /* Wait for PHY to reset */
+ usleep_range(30, 300);
+ reg = readl(priv->base + REG_USBPCR_OFFSET);
+ writel(reg & ~USBPCR_POR, priv->base + REG_USBPCR_OFFSET);
+ usleep_range(300, 1000);
+
+ return 0;
+}
+
+static int ingenic_usb_phy_exit(struct phy *phy)
+{
+ struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
+
+ clk_disable_unprepare(priv->clk);
+ regulator_disable(priv->vcc_supply);
+
+ return 0;
+}
+
+static int ingenic_usb_phy_power_on(struct phy *phy)
+{
+ struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
+ int err;
+
+ err = regulator_enable(priv->vcc_supply);
+ if (err) {
+ dev_err(&phy->dev, "Unable to enable VCC: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int ingenic_usb_phy_power_off(struct phy *phy)
+{
+ struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
+
+ regulator_disable(priv->vcc_supply);
+
+ return 0;
+}
+
+static int ingenic_usb_phy_set_mode(struct phy *phy,
+ enum phy_mode mode, int submode)
+{
+ struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
+ u32 reg;
+
+ switch (mode) {
+ case PHY_MODE_USB_HOST:
+ reg = readl(priv->base + REG_USBPCR_OFFSET);
+ u32p_replace_bits(&reg, 1, USBPCR_USB_MODE);
+ u32p_replace_bits(&reg, 0, USBPCR_VBUSVLDEXT);
+ u32p_replace_bits(&reg, 0, USBPCR_VBUSVLDEXTSEL);
+ u32p_replace_bits(&reg, 0, USBPCR_OTG_DISABLE);
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+
+ break;
+ case PHY_MODE_USB_DEVICE:
+ reg = readl(priv->base + REG_USBPCR_OFFSET);
+ u32p_replace_bits(&reg, 0, USBPCR_USB_MODE);
+ u32p_replace_bits(&reg, 1, USBPCR_VBUSVLDEXT);
+ u32p_replace_bits(&reg, 1, USBPCR_VBUSVLDEXTSEL);
+ u32p_replace_bits(&reg, 1, USBPCR_OTG_DISABLE);
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+
+ break;
+ case PHY_MODE_USB_OTG:
+ reg = readl(priv->base + REG_USBPCR_OFFSET);
+ u32p_replace_bits(&reg, 1, USBPCR_USB_MODE);
+ u32p_replace_bits(&reg, 1, USBPCR_VBUSVLDEXT);
+ u32p_replace_bits(&reg, 1, USBPCR_VBUSVLDEXTSEL);
+ u32p_replace_bits(&reg, 0, USBPCR_OTG_DISABLE);
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct phy_ops ingenic_usb_phy_ops = {
+ .init = ingenic_usb_phy_init,
+ .exit = ingenic_usb_phy_exit,
+ .power_on = ingenic_usb_phy_power_on,
+ .power_off = ingenic_usb_phy_power_off,
+ .set_mode = ingenic_usb_phy_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static void jz4770_usb_phy_init(struct phy *phy)
+{
+ struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
+ u32 reg;
+
+ reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_POR |
+ FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_ALWAYS) |
+ FIELD_PREP(USBPCR_COMPDISTUNE_MASK, USBPCR_COMPDISTUNE_DFT) |
+ FIELD_PREP(USBPCR_OTGTUNE_MASK, USBPCR_OTGTUNE_DFT) |
+ FIELD_PREP(USBPCR_SQRXTUNE_MASK, USBPCR_SQRXTUNE_DFT) |
+ FIELD_PREP(USBPCR_TXFSLSTUNE_MASK, USBPCR_TXFSLSTUNE_DFT) |
+ FIELD_PREP(USBPCR_TXRISETUNE_MASK, USBPCR_TXRISETUNE_DFT) |
+ FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_DFT);
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+}
+
+static void jz4775_usb_phy_init(struct phy *phy)
+{
+ struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
+ u32 reg;
+
+ reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL |
+ USBPCR1_WORD_IF_16BIT;
+ writel(reg, priv->base + REG_USBPCR1_OFFSET);
+
+ reg = USBPCR_COMMONONN | USBPCR_POR |
+ FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_INC_75PPT);
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+}
+
+static void jz4780_usb_phy_init(struct phy *phy)
+{
+ struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
+ u32 reg;
+
+ reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL |
+ USBPCR1_WORD_IF_16BIT;
+ writel(reg, priv->base + REG_USBPCR1_OFFSET);
+
+ reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR;
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+}
+
+static void x1000_usb_phy_init(struct phy *phy)
+{
+ struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
+ u32 reg;
+
+ reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT;
+ writel(reg, priv->base + REG_USBPCR1_OFFSET);
+
+ reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR |
+ FIELD_PREP(USBPCR_SQRXTUNE_MASK, USBPCR_SQRXTUNE_DCR_20PCT) |
+ FIELD_PREP(USBPCR_TXHSXVTUNE_MASK, USBPCR_TXHSXVTUNE_DCR_15MV) |
+ FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_INC_25PPT);
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+}
+
+static void x1830_usb_phy_init(struct phy *phy)
+{
+ struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
+ u32 reg;
+
+ /* rdt */
+ writel(USBRDT_VBFIL_EN | USBRDT_UTMI_RST, priv->base + REG_USBRDT_OFFSET);
+
+ reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT |
+ USBPCR1_DMPD | USBPCR1_DPPD;
+ writel(reg, priv->base + REG_USBPCR1_OFFSET);
+
+ reg = USBPCR_VBUSVLDEXT | USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR |
+ FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_OTG);
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+}
+
+static void x2000_usb_phy_init(struct phy *phy)
+{
+ struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
+ u32 reg;
+
+ reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_DPPD | USBPCR1_DMPD;
+ writel(reg & ~USBPCR1_PORT_RST, priv->base + REG_USBPCR1_OFFSET);
+
+ reg = USBPCR_POR | FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_OTG);
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+}
+
+static const struct ingenic_soc_info jz4770_soc_info = {
+ .usb_phy_init = jz4770_usb_phy_init,
+};
+
+static const struct ingenic_soc_info jz4775_soc_info = {
+ .usb_phy_init = jz4775_usb_phy_init,
+};
+
+static const struct ingenic_soc_info jz4780_soc_info = {
+ .usb_phy_init = jz4780_usb_phy_init,
+};
+
+static const struct ingenic_soc_info x1000_soc_info = {
+ .usb_phy_init = x1000_usb_phy_init,
+};
+
+static const struct ingenic_soc_info x1830_soc_info = {
+ .usb_phy_init = x1830_usb_phy_init,
+};
+
+static const struct ingenic_soc_info x2000_soc_info = {
+ .usb_phy_init = x2000_usb_phy_init,
+};
+
+static int ingenic_usb_phy_probe(struct platform_device *pdev)
+{
+ struct ingenic_usb_phy *priv;
+ struct phy_provider *provider;
+ struct device *dev = &pdev->dev;
+ int err;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->soc_info = device_get_match_data(dev);
+ if (!priv->soc_info) {
+ dev_err(dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base)) {
+ dev_err(dev, "Failed to map registers\n");
+ return PTR_ERR(priv->base);
+ }
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ err = PTR_ERR(priv->clk);
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get clock\n");
+ return err;
+ }
+
+ priv->vcc_supply = devm_regulator_get(dev, "vcc");
+ if (IS_ERR(priv->vcc_supply)) {
+ err = PTR_ERR(priv->vcc_supply);
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get regulator\n");
+ return err;
+ }
+
+ priv->phy = devm_phy_create(dev, NULL, &ingenic_usb_phy_ops);
+ if (IS_ERR(priv->phy))
+ return PTR_ERR(priv->phy);
+
+ phy_set_drvdata(priv->phy, priv);
+
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id ingenic_usb_phy_of_matches[] = {
+ { .compatible = "ingenic,jz4770-phy", .data = &jz4770_soc_info },
+ { .compatible = "ingenic,jz4775-phy", .data = &jz4775_soc_info },
+ { .compatible = "ingenic,jz4780-phy", .data = &jz4780_soc_info },
+ { .compatible = "ingenic,x1000-phy", .data = &x1000_soc_info },
+ { .compatible = "ingenic,x1830-phy", .data = &x1830_soc_info },
+ { .compatible = "ingenic,x2000-phy", .data = &x2000_soc_info },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ingenic_usb_phy_of_matches);
+
+static struct platform_driver ingenic_usb_phy_driver = {
+ .probe = ingenic_usb_phy_probe,
+ .driver = {
+ .name = "ingenic-usb-phy",
+ .of_match_table = ingenic_usb_phy_of_matches,
+ },
+};
+module_platform_driver(ingenic_usb_phy_driver);
+
+MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>");
+MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>");
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_DESCRIPTION("Ingenic SoCs USB PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/intel/Kconfig b/drivers/phy/intel/Kconfig
index 4ea6a8897cd7..18a3cc5b98c0 100644
--- a/drivers/phy/intel/Kconfig
+++ b/drivers/phy/intel/Kconfig
@@ -1,9 +1,58 @@
# SPDX-License-Identifier: GPL-2.0
#
-# Phy drivers for Intel Lightning Mountain(LGM) platform
+# Phy drivers for Intel platforms
#
-config PHY_INTEL_EMMC
- tristate "Intel EMMC PHY driver"
+config PHY_INTEL_KEEMBAY_EMMC
+ tristate "Intel Keem Bay EMMC PHY driver"
+ depends on ARCH_KEEMBAY || COMPILE_TEST
+ depends on HAS_IOMEM
+ select GENERIC_PHY
+ select REGMAP_MMIO
+ help
+ Choose this option if you have an Intel Keem Bay SoC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called phy-keembay-emmc.ko.
+
+config PHY_INTEL_KEEMBAY_USB
+ tristate "Intel Keem Bay USB PHY driver"
+ depends on ARCH_KEEMBAY || COMPILE_TEST
+ depends on HAS_IOMEM
+ select GENERIC_PHY
+ select REGMAP_MMIO
+ help
+ Choose this option if you have an Intel Keem Bay SoC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called phy-keembay-usb.ko.
+
+config PHY_INTEL_LGM_COMBO
+ bool "Intel Lightning Mountain ComboPHY driver"
+ depends on X86 || COMPILE_TEST
+ depends on OF && HAS_IOMEM
+ select MFD_SYSCON
+ select GENERIC_PHY
+ select REGMAP
+ help
+ Enable this to support Intel ComboPhy.
+
+ This driver configures ComboPhy subsystem on Intel gateway
+ chipsets which provides PHYs for various controllers, EMAC,
+ SATA and PCIe.
+
+config PHY_INTEL_LGM_EMMC
+ tristate "Intel Lightning Mountain EMMC PHY driver"
+ depends on X86 || COMPILE_TEST
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.
diff --git a/drivers/phy/intel/Makefile b/drivers/phy/intel/Makefile
index 6b876a75599d..b7321d56b0bb 100644
--- a/drivers/phy/intel/Makefile
+++ b/drivers/phy/intel/Makefile
@@ -1,2 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_PHY_INTEL_EMMC) += phy-intel-emmc.o
+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
diff --git a/drivers/phy/intel/phy-intel-keembay-emmc.c b/drivers/phy/intel/phy-intel-keembay-emmc.c
new file mode 100644
index 000000000000..0eb11ac7c2e2
--- /dev/null
+++ b/drivers/phy/intel/phy-intel-keembay-emmc.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel Keem Bay eMMC PHY driver
+ * Copyright (C) 2020 Intel Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* eMMC/SD/SDIO core/phy configuration registers */
+#define PHY_CFG_0 0x24
+#define SEL_DLY_TXCLK_MASK BIT(29)
+#define OTAP_DLY_ENA_MASK BIT(27)
+#define OTAP_DLY_SEL_MASK GENMASK(26, 23)
+#define DLL_EN_MASK BIT(10)
+#define PWR_DOWN_MASK BIT(0)
+
+#define PHY_CFG_2 0x2c
+#define SEL_FREQ_MASK GENMASK(12, 10)
+
+#define PHY_STAT 0x40
+#define CAL_DONE_MASK BIT(6)
+#define IS_CALDONE(x) ((x) & CAL_DONE_MASK)
+#define DLL_RDY_MASK BIT(5)
+#define IS_DLLRDY(x) ((x) & DLL_RDY_MASK)
+
+/* 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
+
+struct keembay_emmc_phy {
+ struct regmap *syscfg;
+ struct clk *emmcclk;
+};
+
+static const struct regmap_config keembay_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static int keembay_emmc_phy_power(struct phy *phy, bool on_off)
+{
+ struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
+ unsigned int caldone;
+ unsigned int dllrdy;
+ unsigned int freqsel;
+ unsigned int mhz;
+ int ret;
+
+ /*
+ * Keep phyctrl_pdb and phyctrl_endll low to allow
+ * initialization of CALIO state M/C DFFs
+ */
+ ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, PWR_DOWN_MASK,
+ FIELD_PREP(PWR_DOWN_MASK, 0));
+ if (ret) {
+ dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, DLL_EN_MASK,
+ FIELD_PREP(DLL_EN_MASK, 0));
+ if (ret) {
+ dev_err(&phy->dev, "turn off the dll failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Already finish power off above */
+ if (!on_off)
+ return 0;
+
+ mhz = DIV_ROUND_CLOSEST(clk_get_rate(priv->emmcclk), 1000000);
+ if (mhz <= 200 && mhz >= 170)
+ freqsel = FREQSEL_200M_170M;
+ else if (mhz <= 170 && mhz >= 140)
+ freqsel = FREQSEL_170M_140M;
+ else if (mhz <= 140 && mhz >= 110)
+ freqsel = FREQSEL_140M_110M;
+ else if (mhz <= 110 && mhz >= 80)
+ freqsel = FREQSEL_110M_80M;
+ else if (mhz <= 80 && mhz >= 50)
+ freqsel = FREQSEL_80M_50M;
+ else
+ freqsel = 0x0;
+
+ /* Check for EMMC clock rate*/
+ if (mhz > 175)
+ dev_warn(&phy->dev, "Unsupported rate: %d MHz\n", mhz);
+
+ /*
+ * According to the user manual, calpad calibration
+ * cycle takes more than 2us without the minimal recommended
+ * value, so we may need a little margin here
+ */
+ udelay(5);
+
+ ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, PWR_DOWN_MASK,
+ FIELD_PREP(PWR_DOWN_MASK, 1));
+ if (ret) {
+ dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * 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 = regmap_read_poll_timeout(priv->syscfg, PHY_STAT,
+ caldone, IS_CALDONE(caldone),
+ 0, 50);
+ if (ret) {
+ dev_err(&phy->dev, "caldone failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ /* Set the frequency of the DLL operation */
+ ret = regmap_update_bits(priv->syscfg, PHY_CFG_2, SEL_FREQ_MASK,
+ FIELD_PREP(SEL_FREQ_MASK, freqsel));
+ if (ret) {
+ dev_err(&phy->dev, "set the frequency of dll failed:%d\n", ret);
+ return ret;
+ }
+
+ /* Turn on the DLL */
+ ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, DLL_EN_MASK,
+ FIELD_PREP(DLL_EN_MASK, 1));
+ if (ret) {
+ dev_err(&phy->dev, "turn on the dll failed: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * We turned on the DLL even though the rate was 0 because we the
+ * clock might be turned on later. ...but we can't wait for the DLL
+ * to lock when the rate is 0 because it will never lock with no
+ * input clock.
+ *
+ * Technically we should be checking the lock later when the clock
+ * is turned on, but for now we won't.
+ */
+ if (mhz == 0)
+ return 0;
+
+ /*
+ * 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 = regmap_read_poll_timeout(priv->syscfg, PHY_STAT,
+ dllrdy, IS_DLLRDY(dllrdy),
+ 0, 50 * USEC_PER_MSEC);
+ if (ret)
+ dev_err(&phy->dev, "dllrdy failed, ret=%d\n", ret);
+
+ return ret;
+}
+
+static int keembay_emmc_phy_init(struct phy *phy)
+{
+ struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
+
+ /*
+ * We purposely get the clock here and not in probe to avoid the
+ * circular dependency problem. We expect:
+ * - PHY driver to probe
+ * - SDHCI driver to start probe
+ * - SDHCI driver to register it's clock
+ * - SDHCI driver to get the PHY
+ * - SDHCI driver to init the PHY
+ *
+ * The clock is optional, so upon any error just return it like
+ * any other error to user.
+ */
+ priv->emmcclk = clk_get_optional(&phy->dev, "emmcclk");
+
+ return PTR_ERR_OR_ZERO(priv->emmcclk);
+}
+
+static int keembay_emmc_phy_exit(struct phy *phy)
+{
+ struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
+
+ clk_put(priv->emmcclk);
+
+ return 0;
+};
+
+static int keembay_emmc_phy_power_on(struct phy *phy)
+{
+ struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
+ int ret;
+
+ /* Delay chain based txclk: enable */
+ ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, SEL_DLY_TXCLK_MASK,
+ FIELD_PREP(SEL_DLY_TXCLK_MASK, 1));
+ if (ret) {
+ dev_err(&phy->dev, "ERROR: delay chain txclk set: %d\n", ret);
+ return ret;
+ }
+
+ /* Output tap delay: enable */
+ ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, OTAP_DLY_ENA_MASK,
+ FIELD_PREP(OTAP_DLY_ENA_MASK, 1));
+ if (ret) {
+ dev_err(&phy->dev, "ERROR: output tap delay set: %d\n", ret);
+ return ret;
+ }
+
+ /* Output tap delay */
+ ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, OTAP_DLY_SEL_MASK,
+ FIELD_PREP(OTAP_DLY_SEL_MASK, 2));
+ if (ret) {
+ dev_err(&phy->dev, "ERROR: output tap delay select: %d\n", ret);
+ return ret;
+ }
+
+ /* Power up eMMC phy analog blocks */
+ return keembay_emmc_phy_power(phy, true);
+}
+
+static int keembay_emmc_phy_power_off(struct phy *phy)
+{
+ /* Power down eMMC phy analog blocks */
+ return keembay_emmc_phy_power(phy, false);
+}
+
+static const struct phy_ops ops = {
+ .init = keembay_emmc_phy_init,
+ .exit = keembay_emmc_phy_exit,
+ .power_on = keembay_emmc_phy_power_on,
+ .power_off = keembay_emmc_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int keembay_emmc_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct keembay_emmc_phy *priv;
+ struct phy *generic_phy;
+ struct phy_provider *phy_provider;
+ void __iomem *base;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->syscfg = devm_regmap_init_mmio(dev, base, &keembay_regmap_config);
+ if (IS_ERR(priv->syscfg))
+ return PTR_ERR(priv->syscfg);
+
+ generic_phy = devm_phy_create(dev, np, &ops);
+ if (IS_ERR(generic_phy))
+ return dev_err_probe(dev, PTR_ERR(generic_phy),
+ "failed to create PHY\n");
+
+ phy_set_drvdata(generic_phy, priv);
+ 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 keembay_emmc_phy_dt_ids[] = {
+ { .compatible = "intel,keembay-emmc-phy" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, keembay_emmc_phy_dt_ids);
+
+static struct platform_driver keembay_emmc_phy_driver = {
+ .probe = keembay_emmc_phy_probe,
+ .driver = {
+ .name = "keembay-emmc-phy",
+ .of_match_table = keembay_emmc_phy_dt_ids,
+ },
+};
+module_platform_driver(keembay_emmc_phy_driver);
+
+MODULE_AUTHOR("Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>");
+MODULE_DESCRIPTION("Intel Keem Bay eMMC PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/intel/phy-intel-keembay-usb.c b/drivers/phy/intel/phy-intel-keembay-usb.c
new file mode 100644
index 000000000000..c8b05f7b2445
--- /dev/null
+++ b/drivers/phy/intel/phy-intel-keembay-usb.c
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel Keem Bay USB PHY driver
+ * Copyright (C) 2020 Intel Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* USS (USB Subsystem) clock control registers */
+#define USS_CPR_CLK_EN 0x00
+#define USS_CPR_CLK_SET 0x04
+#define USS_CPR_CLK_CLR 0x08
+#define USS_CPR_RST_EN 0x10
+#define USS_CPR_RST_SET 0x14
+#define USS_CPR_RST_CLR 0x18
+
+/* USS clock/reset bit fields */
+#define USS_CPR_PHY_TST BIT(6)
+#define USS_CPR_LOW_JIT BIT(5)
+#define USS_CPR_CORE BIT(4)
+#define USS_CPR_SUSPEND BIT(3)
+#define USS_CPR_ALT_REF BIT(2)
+#define USS_CPR_REF BIT(1)
+#define USS_CPR_SYS BIT(0)
+#define USS_CPR_MASK GENMASK(6, 0)
+
+/* USS APB slave registers */
+#define USS_USB_CTRL_CFG0 0x10
+#define VCC_RESET_N_MASK BIT(31)
+
+#define USS_USB_PHY_CFG0 0x30
+#define POR_MASK BIT(15)
+#define PHY_RESET_MASK BIT(14)
+#define PHY_REF_USE_PAD_MASK BIT(5)
+
+#define USS_USB_PHY_CFG6 0x64
+#define PHY0_SRAM_EXT_LD_DONE_MASK BIT(23)
+
+#define USS_USB_PARALLEL_IF_CTRL 0xa0
+#define USB_PHY_CR_PARA_SEL_MASK BIT(2)
+
+#define USS_USB_TSET_SIGNALS_AND_GLOB 0xac
+#define USB_PHY_CR_PARA_CLK_EN_MASK BIT(7)
+
+#define USS_USB_STATUS_REG 0xb8
+#define PHY0_SRAM_INIT_DONE_MASK BIT(3)
+
+#define USS_USB_TIEOFFS_CONSTANTS_REG1 0xc0
+#define IDDQ_ENABLE_MASK BIT(10)
+
+struct keembay_usb_phy {
+ struct device *dev;
+ struct regmap *regmap_cpr;
+ struct regmap *regmap_slv;
+};
+
+static const struct regmap_config keembay_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = USS_USB_TIEOFFS_CONSTANTS_REG1,
+};
+
+static int keembay_usb_clocks_on(struct keembay_usb_phy *priv)
+{
+ int ret;
+
+ ret = regmap_update_bits(priv->regmap_cpr, USS_CPR_CLK_SET,
+ USS_CPR_MASK, USS_CPR_MASK);
+ if (ret) {
+ dev_err(priv->dev, "error clock set: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(priv->regmap_cpr, USS_CPR_RST_SET,
+ USS_CPR_MASK, USS_CPR_MASK);
+ if (ret) {
+ dev_err(priv->dev, "error reset set: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(priv->regmap_slv,
+ USS_USB_TIEOFFS_CONSTANTS_REG1,
+ IDDQ_ENABLE_MASK,
+ FIELD_PREP(IDDQ_ENABLE_MASK, 0));
+ if (ret) {
+ dev_err(priv->dev, "error iddq disable: %d\n", ret);
+ return ret;
+ }
+
+ /* Wait 30us to ensure all analog blocks are powered up. */
+ usleep_range(30, 60);
+
+ ret = regmap_update_bits(priv->regmap_slv, USS_USB_PHY_CFG0,
+ PHY_REF_USE_PAD_MASK,
+ FIELD_PREP(PHY_REF_USE_PAD_MASK, 1));
+ if (ret)
+ dev_err(priv->dev, "error ref clock select: %d\n", ret);
+
+ return ret;
+}
+
+static int keembay_usb_core_off(struct keembay_usb_phy *priv)
+{
+ int ret;
+
+ ret = regmap_update_bits(priv->regmap_slv, USS_USB_CTRL_CFG0,
+ VCC_RESET_N_MASK,
+ FIELD_PREP(VCC_RESET_N_MASK, 0));
+ if (ret)
+ dev_err(priv->dev, "error core reset: %d\n", ret);
+
+ return ret;
+}
+
+static int keembay_usb_core_on(struct keembay_usb_phy *priv)
+{
+ int ret;
+
+ ret = regmap_update_bits(priv->regmap_slv, USS_USB_CTRL_CFG0,
+ VCC_RESET_N_MASK,
+ FIELD_PREP(VCC_RESET_N_MASK, 1));
+ if (ret)
+ dev_err(priv->dev, "error core on: %d\n", ret);
+
+ return ret;
+}
+
+static int keembay_usb_phys_on(struct keembay_usb_phy *priv)
+{
+ int ret;
+
+ ret = regmap_update_bits(priv->regmap_slv, USS_USB_PHY_CFG0,
+ POR_MASK | PHY_RESET_MASK,
+ FIELD_PREP(POR_MASK | PHY_RESET_MASK, 0));
+ if (ret)
+ dev_err(priv->dev, "error phys on: %d\n", ret);
+
+ return ret;
+}
+
+static int keembay_usb_phy_init(struct phy *phy)
+{
+ struct keembay_usb_phy *priv = phy_get_drvdata(phy);
+ u32 val;
+ int ret;
+
+ ret = keembay_usb_core_off(priv);
+ if (ret)
+ return ret;
+
+ /*
+ * According to Keem Bay datasheet, wait minimum 20us after clock
+ * enable before bringing PHYs out of reset.
+ */
+ usleep_range(20, 40);
+
+ ret = keembay_usb_phys_on(priv);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap_slv,
+ USS_USB_TSET_SIGNALS_AND_GLOB,
+ USB_PHY_CR_PARA_CLK_EN_MASK,
+ FIELD_PREP(USB_PHY_CR_PARA_CLK_EN_MASK, 0));
+ if (ret) {
+ dev_err(priv->dev, "error cr clock disable: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * According to Keem Bay datasheet, wait 2us after disabling the
+ * clock into the USB 3.x parallel interface.
+ */
+ udelay(2);
+
+ ret = regmap_update_bits(priv->regmap_slv,
+ USS_USB_PARALLEL_IF_CTRL,
+ USB_PHY_CR_PARA_SEL_MASK,
+ FIELD_PREP(USB_PHY_CR_PARA_SEL_MASK, 1));
+ if (ret) {
+ dev_err(priv->dev, "error cr select: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(priv->regmap_slv,
+ USS_USB_TSET_SIGNALS_AND_GLOB,
+ USB_PHY_CR_PARA_CLK_EN_MASK,
+ FIELD_PREP(USB_PHY_CR_PARA_CLK_EN_MASK, 1));
+ if (ret) {
+ dev_err(priv->dev, "error cr clock enable: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read_poll_timeout(priv->regmap_slv, USS_USB_STATUS_REG,
+ val, val & PHY0_SRAM_INIT_DONE_MASK,
+ USEC_PER_MSEC, 10 * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(priv->dev, "SRAM init not done: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(priv->regmap_slv, USS_USB_PHY_CFG6,
+ PHY0_SRAM_EXT_LD_DONE_MASK,
+ FIELD_PREP(PHY0_SRAM_EXT_LD_DONE_MASK, 1));
+ if (ret) {
+ dev_err(priv->dev, "error SRAM init done set: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * According to Keem Bay datasheet, wait 20us after setting the
+ * SRAM load done bit, before releasing the controller reset.
+ */
+ usleep_range(20, 40);
+
+ return keembay_usb_core_on(priv);
+}
+
+static const struct phy_ops ops = {
+ .init = keembay_usb_phy_init,
+ .owner = THIS_MODULE,
+};
+
+static int keembay_usb_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct keembay_usb_phy *priv;
+ struct phy *generic_phy;
+ struct phy_provider *phy_provider;
+ void __iomem *base;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource_byname(pdev, "cpr-apb-base");
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->regmap_cpr = devm_regmap_init_mmio(dev, base,
+ &keembay_regmap_config);
+ if (IS_ERR(priv->regmap_cpr))
+ return PTR_ERR(priv->regmap_cpr);
+
+ base = devm_platform_ioremap_resource_byname(pdev, "slv-apb-base");
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->regmap_slv = devm_regmap_init_mmio(dev, base,
+ &keembay_regmap_config);
+ if (IS_ERR(priv->regmap_slv))
+ return PTR_ERR(priv->regmap_slv);
+
+ generic_phy = devm_phy_create(dev, dev->of_node, &ops);
+ if (IS_ERR(generic_phy))
+ return dev_err_probe(dev, PTR_ERR(generic_phy),
+ "failed to create PHY\n");
+
+ phy_set_drvdata(generic_phy, priv);
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return dev_err_probe(dev, PTR_ERR(phy_provider),
+ "failed to register phy provider\n");
+
+ /* Setup USB subsystem clocks */
+ ret = keembay_usb_clocks_on(priv);
+ if (ret)
+ return ret;
+
+ /* and turn on the DWC3 core, prior to DWC3 driver init. */
+ return keembay_usb_core_on(priv);
+}
+
+static const struct of_device_id keembay_usb_phy_dt_ids[] = {
+ { .compatible = "intel,keembay-usb-phy" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, keembay_usb_phy_dt_ids);
+
+static struct platform_driver keembay_usb_phy_driver = {
+ .probe = keembay_usb_phy_probe,
+ .driver = {
+ .name = "keembay-usb-phy",
+ .of_match_table = keembay_usb_phy_dt_ids,
+ },
+};
+module_platform_driver(keembay_usb_phy_driver);
+
+MODULE_AUTHOR("Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>");
+MODULE_DESCRIPTION("Intel Keem Bay USB PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/intel/phy-intel-lgm-combo.c b/drivers/phy/intel/phy-intel-lgm-combo.c
new file mode 100644
index 000000000000..6010e246d52e
--- /dev/null
+++ b/drivers/phy/intel/phy-intel-lgm-combo.c
@@ -0,0 +1,619 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Combo-PHY driver
+ *
+ * Copyright (C) 2019-2020 Intel Corporation.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <dt-bindings/phy/phy.h>
+
+#define PCIE_PHY_GEN_CTRL 0x00
+#define PCIE_PHY_CLK_PAD BIT(17)
+
+#define PAD_DIS_CFG 0x174
+
+#define PCS_XF_ATE_OVRD_IN_2 0x3008
+#define ADAPT_REQ_MSK GENMASK(5, 4)
+
+#define PCS_XF_RX_ADAPT_ACK 0x3010
+#define RX_ADAPT_ACK_BIT BIT(0)
+
+#define CR_ADDR(addr, lane) (((addr) + (lane) * 0x100) << 2)
+#define REG_COMBO_MODE(x) ((x) * 0x200)
+#define REG_CLK_DISABLE(x) ((x) * 0x200 + 0x124)
+
+#define COMBO_PHY_ID(x) ((x)->parent->id)
+#define PHY_ID(x) ((x)->id)
+
+#define CLK_100MHZ 100000000
+#define CLK_156_25MHZ 156250000
+
+static const unsigned long intel_iphy_clk_rates[] = {
+ CLK_100MHZ, CLK_156_25MHZ, CLK_100MHZ,
+};
+
+enum {
+ PHY_0,
+ PHY_1,
+ PHY_MAX_NUM
+};
+
+/*
+ * Clock Register bit fields to enable clocks
+ * for ComboPhy according to the mode.
+ */
+enum intel_phy_mode {
+ PHY_PCIE_MODE = 0,
+ PHY_XPCS_MODE,
+ PHY_SATA_MODE,
+};
+
+/* ComboPhy mode Register values */
+enum intel_combo_mode {
+ PCIE0_PCIE1_MODE = 0,
+ PCIE_DL_MODE,
+ RXAUI_MODE,
+ XPCS0_XPCS1_MODE,
+ SATA0_SATA1_MODE,
+};
+
+enum aggregated_mode {
+ PHY_SL_MODE,
+ PHY_DL_MODE,
+};
+
+struct intel_combo_phy;
+
+struct intel_cbphy_iphy {
+ struct phy *phy;
+ struct intel_combo_phy *parent;
+ struct reset_control *app_rst;
+ u32 id;
+};
+
+struct intel_combo_phy {
+ struct device *dev;
+ struct clk *core_clk;
+ unsigned long clk_rate;
+ void __iomem *app_base;
+ void __iomem *cr_base;
+ struct regmap *syscfg;
+ struct regmap *hsiocfg;
+ u32 id;
+ u32 bid;
+ struct reset_control *phy_rst;
+ struct reset_control *core_rst;
+ struct intel_cbphy_iphy iphy[PHY_MAX_NUM];
+ enum intel_phy_mode phy_mode;
+ enum aggregated_mode aggr_mode;
+ u32 init_cnt;
+ struct mutex lock;
+};
+
+static int intel_cbphy_iphy_enable(struct intel_cbphy_iphy *iphy, bool set)
+{
+ struct intel_combo_phy *cbphy = iphy->parent;
+ u32 mask = BIT(cbphy->phy_mode * 2 + iphy->id);
+ u32 val;
+
+ /* Register: 0 is enable, 1 is disable */
+ val = set ? 0 : mask;
+
+ return regmap_update_bits(cbphy->hsiocfg, REG_CLK_DISABLE(cbphy->bid),
+ mask, val);
+}
+
+static int intel_cbphy_pcie_refclk_cfg(struct intel_cbphy_iphy *iphy, bool set)
+{
+ struct intel_combo_phy *cbphy = iphy->parent;
+ u32 mask = BIT(cbphy->id * 2 + iphy->id);
+ u32 val;
+
+ /* Register: 0 is enable, 1 is disable */
+ val = set ? 0 : mask;
+
+ return regmap_update_bits(cbphy->syscfg, PAD_DIS_CFG, mask, val);
+}
+
+static inline void combo_phy_w32_off_mask(void __iomem *base, unsigned int reg,
+ u32 mask, u32 val)
+{
+ u32 reg_val;
+
+ reg_val = readl(base + reg);
+ reg_val &= ~mask;
+ reg_val |= val;
+ writel(reg_val, base + reg);
+}
+
+static int intel_cbphy_iphy_cfg(struct intel_cbphy_iphy *iphy,
+ int (*phy_cfg)(struct intel_cbphy_iphy *))
+{
+ struct intel_combo_phy *cbphy = iphy->parent;
+ int ret;
+
+ ret = phy_cfg(iphy);
+ if (ret)
+ return ret;
+
+ if (cbphy->aggr_mode != PHY_DL_MODE)
+ return 0;
+
+ return phy_cfg(&cbphy->iphy[PHY_1]);
+}
+
+static int intel_cbphy_pcie_en_pad_refclk(struct intel_cbphy_iphy *iphy)
+{
+ struct intel_combo_phy *cbphy = iphy->parent;
+ int ret;
+
+ ret = intel_cbphy_pcie_refclk_cfg(iphy, true);
+ if (ret) {
+ dev_err(cbphy->dev, "Failed to enable PCIe pad refclk\n");
+ return ret;
+ }
+
+ if (cbphy->init_cnt)
+ return 0;
+
+ combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL,
+ PCIE_PHY_CLK_PAD, FIELD_PREP(PCIE_PHY_CLK_PAD, 0));
+
+ /* Delay for stable clock PLL */
+ usleep_range(50, 100);
+
+ return 0;
+}
+
+static int intel_cbphy_pcie_dis_pad_refclk(struct intel_cbphy_iphy *iphy)
+{
+ struct intel_combo_phy *cbphy = iphy->parent;
+ int ret;
+
+ ret = intel_cbphy_pcie_refclk_cfg(iphy, false);
+ if (ret) {
+ dev_err(cbphy->dev, "Failed to disable PCIe pad refclk\n");
+ return ret;
+ }
+
+ if (cbphy->init_cnt)
+ return 0;
+
+ combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL,
+ PCIE_PHY_CLK_PAD, FIELD_PREP(PCIE_PHY_CLK_PAD, 1));
+
+ return 0;
+}
+
+static int intel_cbphy_set_mode(struct intel_combo_phy *cbphy)
+{
+ enum intel_combo_mode cb_mode;
+ enum aggregated_mode aggr = cbphy->aggr_mode;
+ struct device *dev = cbphy->dev;
+ enum intel_phy_mode mode;
+ int ret;
+
+ mode = cbphy->phy_mode;
+
+ switch (mode) {
+ case PHY_PCIE_MODE:
+ cb_mode = (aggr == PHY_DL_MODE) ? PCIE_DL_MODE : PCIE0_PCIE1_MODE;
+ break;
+
+ case PHY_XPCS_MODE:
+ cb_mode = (aggr == PHY_DL_MODE) ? RXAUI_MODE : XPCS0_XPCS1_MODE;
+ break;
+
+ case PHY_SATA_MODE:
+ if (aggr == PHY_DL_MODE) {
+ dev_err(dev, "Mode:%u not support dual lane!\n", mode);
+ return -EINVAL;
+ }
+
+ cb_mode = SATA0_SATA1_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_write(cbphy->hsiocfg, REG_COMBO_MODE(cbphy->bid), cb_mode);
+ if (ret)
+ dev_err(dev, "Failed to set ComboPhy mode: %d\n", ret);
+
+ return ret;
+}
+
+static void intel_cbphy_rst_assert(struct intel_combo_phy *cbphy)
+{
+ reset_control_assert(cbphy->core_rst);
+ reset_control_assert(cbphy->phy_rst);
+}
+
+static void intel_cbphy_rst_deassert(struct intel_combo_phy *cbphy)
+{
+ reset_control_deassert(cbphy->core_rst);
+ reset_control_deassert(cbphy->phy_rst);
+ /* Delay to ensure reset process is done */
+ usleep_range(10, 20);
+}
+
+static int intel_cbphy_iphy_power_on(struct intel_cbphy_iphy *iphy)
+{
+ struct intel_combo_phy *cbphy = iphy->parent;
+ int ret;
+
+ if (!cbphy->init_cnt) {
+ ret = clk_prepare_enable(cbphy->core_clk);
+ if (ret) {
+ dev_err(cbphy->dev, "Clock enable failed!\n");
+ return ret;
+ }
+
+ ret = clk_set_rate(cbphy->core_clk, cbphy->clk_rate);
+ if (ret) {
+ dev_err(cbphy->dev, "Clock freq set to %lu failed!\n",
+ cbphy->clk_rate);
+ goto clk_err;
+ }
+
+ intel_cbphy_rst_assert(cbphy);
+ intel_cbphy_rst_deassert(cbphy);
+ ret = intel_cbphy_set_mode(cbphy);
+ if (ret)
+ goto clk_err;
+ }
+
+ ret = intel_cbphy_iphy_enable(iphy, true);
+ if (ret) {
+ dev_err(cbphy->dev, "Failed enabling PHY core\n");
+ goto clk_err;
+ }
+
+ ret = reset_control_deassert(iphy->app_rst);
+ if (ret) {
+ dev_err(cbphy->dev, "PHY(%u:%u) reset deassert failed!\n",
+ COMBO_PHY_ID(iphy), PHY_ID(iphy));
+ goto clk_err;
+ }
+
+ /* Delay to ensure reset process is done */
+ udelay(1);
+
+ return 0;
+
+clk_err:
+ clk_disable_unprepare(cbphy->core_clk);
+
+ return ret;
+}
+
+static int intel_cbphy_iphy_power_off(struct intel_cbphy_iphy *iphy)
+{
+ struct intel_combo_phy *cbphy = iphy->parent;
+ int ret;
+
+ ret = reset_control_assert(iphy->app_rst);
+ if (ret) {
+ dev_err(cbphy->dev, "PHY(%u:%u) reset assert failed!\n",
+ COMBO_PHY_ID(iphy), PHY_ID(iphy));
+ return ret;
+ }
+
+ ret = intel_cbphy_iphy_enable(iphy, false);
+ if (ret) {
+ dev_err(cbphy->dev, "Failed disabling PHY core\n");
+ return ret;
+ }
+
+ if (cbphy->init_cnt)
+ return 0;
+
+ clk_disable_unprepare(cbphy->core_clk);
+ intel_cbphy_rst_assert(cbphy);
+
+ return 0;
+}
+
+static int intel_cbphy_init(struct phy *phy)
+{
+ struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy);
+ struct intel_combo_phy *cbphy = iphy->parent;
+ int ret;
+
+ mutex_lock(&cbphy->lock);
+ ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_on);
+ if (ret)
+ goto err;
+
+ if (cbphy->phy_mode == PHY_PCIE_MODE) {
+ ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_en_pad_refclk);
+ if (ret)
+ goto err;
+ }
+
+ cbphy->init_cnt++;
+
+err:
+ mutex_unlock(&cbphy->lock);
+
+ return ret;
+}
+
+static int intel_cbphy_exit(struct phy *phy)
+{
+ struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy);
+ struct intel_combo_phy *cbphy = iphy->parent;
+ int ret;
+
+ mutex_lock(&cbphy->lock);
+ cbphy->init_cnt--;
+ if (cbphy->phy_mode == PHY_PCIE_MODE) {
+ ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_dis_pad_refclk);
+ if (ret)
+ goto err;
+ }
+
+ ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_off);
+
+err:
+ mutex_unlock(&cbphy->lock);
+
+ return ret;
+}
+
+static int intel_cbphy_calibrate(struct phy *phy)
+{
+ struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy);
+ struct intel_combo_phy *cbphy = iphy->parent;
+ void __iomem *cr_base = cbphy->cr_base;
+ int val, ret, id;
+
+ if (cbphy->phy_mode != PHY_XPCS_MODE)
+ return 0;
+
+ id = PHY_ID(iphy);
+
+ /* trigger auto RX adaptation */
+ combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id),
+ ADAPT_REQ_MSK, FIELD_PREP(ADAPT_REQ_MSK, 3));
+ /* Wait RX adaptation to finish */
+ ret = readl_poll_timeout(cr_base + CR_ADDR(PCS_XF_RX_ADAPT_ACK, id),
+ val, val & RX_ADAPT_ACK_BIT, 10, 5000);
+ if (ret)
+ dev_err(cbphy->dev, "RX Adaptation failed!\n");
+ else
+ dev_dbg(cbphy->dev, "RX Adaptation success!\n");
+
+ /* Stop RX adaptation */
+ combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id),
+ ADAPT_REQ_MSK, FIELD_PREP(ADAPT_REQ_MSK, 0));
+
+ return ret;
+}
+
+static int intel_cbphy_fwnode_parse(struct intel_combo_phy *cbphy)
+{
+ struct device *dev = cbphy->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+ struct fwnode_reference_args ref;
+ int ret;
+ u32 val;
+
+ cbphy->core_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(cbphy->core_clk))
+ return dev_err_probe(dev, PTR_ERR(cbphy->core_clk),
+ "Get clk failed!\n");
+
+ cbphy->core_rst = devm_reset_control_get_optional(dev, "core");
+ if (IS_ERR(cbphy->core_rst))
+ return dev_err_probe(dev, PTR_ERR(cbphy->core_rst),
+ "Get core reset control err!\n");
+
+ cbphy->phy_rst = devm_reset_control_get_optional(dev, "phy");
+ if (IS_ERR(cbphy->phy_rst))
+ return dev_err_probe(dev, PTR_ERR(cbphy->phy_rst),
+ "Get PHY reset control err!\n");
+
+ cbphy->iphy[0].app_rst = devm_reset_control_get_optional(dev, "iphy0");
+ if (IS_ERR(cbphy->iphy[0].app_rst))
+ return dev_err_probe(dev, PTR_ERR(cbphy->iphy[0].app_rst),
+ "Get phy0 reset control err!\n");
+
+ cbphy->iphy[1].app_rst = devm_reset_control_get_optional(dev, "iphy1");
+ if (IS_ERR(cbphy->iphy[1].app_rst))
+ return dev_err_probe(dev, PTR_ERR(cbphy->iphy[1].app_rst),
+ "Get phy1 reset control err!\n");
+
+ cbphy->app_base = devm_platform_ioremap_resource_byname(pdev, "app");
+ if (IS_ERR(cbphy->app_base))
+ return PTR_ERR(cbphy->app_base);
+
+ cbphy->cr_base = devm_platform_ioremap_resource_byname(pdev, "core");
+ if (IS_ERR(cbphy->cr_base))
+ return PTR_ERR(cbphy->cr_base);
+
+ /*
+ * syscfg and hsiocfg variables stores the handle of the registers set
+ * in which ComboPhy subsystem specific registers are subset. Using
+ * Register map framework to access the registers set.
+ */
+ ret = fwnode_property_get_reference_args(fwnode, "intel,syscfg", NULL,
+ 1, 0, &ref);
+ if (ret < 0)
+ return ret;
+
+ cbphy->id = ref.args[0];
+ cbphy->syscfg = device_node_to_regmap(to_of_node(ref.fwnode));
+ fwnode_handle_put(ref.fwnode);
+
+ ret = fwnode_property_get_reference_args(fwnode, "intel,hsio", NULL, 1,
+ 0, &ref);
+ if (ret < 0)
+ return ret;
+
+ cbphy->bid = ref.args[0];
+ cbphy->hsiocfg = device_node_to_regmap(to_of_node(ref.fwnode));
+ fwnode_handle_put(ref.fwnode);
+
+ ret = fwnode_property_read_u32_array(fwnode, "intel,phy-mode", &val, 1);
+ if (ret)
+ return ret;
+
+ switch (val) {
+ case PHY_TYPE_PCIE:
+ cbphy->phy_mode = PHY_PCIE_MODE;
+ break;
+
+ case PHY_TYPE_SATA:
+ cbphy->phy_mode = PHY_SATA_MODE;
+ break;
+
+ case PHY_TYPE_XPCS:
+ cbphy->phy_mode = PHY_XPCS_MODE;
+ break;
+
+ default:
+ dev_err(dev, "Invalid PHY mode: %u\n", val);
+ return -EINVAL;
+ }
+
+ cbphy->clk_rate = intel_iphy_clk_rates[cbphy->phy_mode];
+
+ if (fwnode_property_present(fwnode, "intel,aggregation"))
+ cbphy->aggr_mode = PHY_DL_MODE;
+ else
+ cbphy->aggr_mode = PHY_SL_MODE;
+
+ return 0;
+}
+
+static const struct phy_ops intel_cbphy_ops = {
+ .init = intel_cbphy_init,
+ .exit = intel_cbphy_exit,
+ .calibrate = intel_cbphy_calibrate,
+ .owner = THIS_MODULE,
+};
+
+static struct phy *intel_cbphy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct intel_combo_phy *cbphy = dev_get_drvdata(dev);
+ u32 iphy_id;
+
+ if (args->args_count < 1) {
+ dev_err(dev, "Invalid number of arguments\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ iphy_id = args->args[0];
+ if (iphy_id >= PHY_MAX_NUM) {
+ dev_err(dev, "Invalid phy instance %d\n", iphy_id);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (cbphy->aggr_mode == PHY_DL_MODE && iphy_id == PHY_1) {
+ dev_err(dev, "Invalid. ComboPhy is in Dual lane mode %d\n", iphy_id);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return cbphy->iphy[iphy_id].phy;
+}
+
+static int intel_cbphy_create(struct intel_combo_phy *cbphy)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = cbphy->dev;
+ struct intel_cbphy_iphy *iphy;
+ int i;
+
+ for (i = 0; i < PHY_MAX_NUM; i++) {
+ iphy = &cbphy->iphy[i];
+ iphy->parent = cbphy;
+ iphy->id = i;
+
+ /* In dual lane mode skip phy creation for the second phy */
+ if (cbphy->aggr_mode == PHY_DL_MODE && iphy->id == PHY_1)
+ continue;
+
+ iphy->phy = devm_phy_create(dev, NULL, &intel_cbphy_ops);
+ if (IS_ERR(iphy->phy)) {
+ dev_err(dev, "PHY[%u:%u]: create PHY instance failed!\n",
+ COMBO_PHY_ID(iphy), PHY_ID(iphy));
+
+ return PTR_ERR(iphy->phy);
+ }
+
+ phy_set_drvdata(iphy->phy, iphy);
+ }
+
+ dev_set_drvdata(dev, cbphy);
+ phy_provider = devm_of_phy_provider_register(dev, intel_cbphy_xlate);
+ if (IS_ERR(phy_provider))
+ dev_err(dev, "Register PHY provider failed!\n");
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static int intel_cbphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct intel_combo_phy *cbphy;
+ int ret;
+
+ cbphy = devm_kzalloc(dev, sizeof(*cbphy), GFP_KERNEL);
+ if (!cbphy)
+ return -ENOMEM;
+
+ cbphy->dev = dev;
+ cbphy->init_cnt = 0;
+ mutex_init(&cbphy->lock);
+ ret = intel_cbphy_fwnode_parse(cbphy);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, cbphy);
+
+ return intel_cbphy_create(cbphy);
+}
+
+static int intel_cbphy_remove(struct platform_device *pdev)
+{
+ struct intel_combo_phy *cbphy = platform_get_drvdata(pdev);
+
+ intel_cbphy_rst_assert(cbphy);
+ clk_disable_unprepare(cbphy->core_clk);
+ return 0;
+}
+
+static const struct of_device_id of_intel_cbphy_match[] = {
+ { .compatible = "intel,combo-phy" },
+ { .compatible = "intel,combophy-lgm" },
+ {}
+};
+
+static struct platform_driver intel_cbphy_driver = {
+ .probe = intel_cbphy_probe,
+ .remove = intel_cbphy_remove,
+ .driver = {
+ .name = "intel-combo-phy",
+ .of_match_table = of_intel_cbphy_match,
+ }
+};
+
+module_platform_driver(intel_cbphy_driver);
+
+MODULE_DESCRIPTION("Intel Combo-phy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/intel/phy-intel-emmc.c b/drivers/phy/intel/phy-intel-lgm-emmc.c
index 703aeb122541..703aeb122541 100644
--- a/drivers/phy/intel/phy-intel-emmc.c
+++ b/drivers/phy/intel/phy-intel-lgm-emmc.c
diff --git a/drivers/phy/intel/phy-intel-thunderbay-emmc.c b/drivers/phy/intel/phy-intel-thunderbay-emmc.c
new file mode 100644
index 000000000000..593f6970b81e
--- /dev/null
+++ b/drivers/phy/intel/phy-intel-thunderbay-emmc.c
@@ -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");
diff --git a/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c b/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c
index be09b1530ae6..29d246ea24b4 100644
--- a/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c
+++ b/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c
@@ -124,8 +124,16 @@ static int ltq_rcu_usb2_phy_power_on(struct phy *phy)
reset_control_deassert(priv->phy_reset);
ret = clk_prepare_enable(priv->phy_gate_clk);
- if (ret)
+ if (ret) {
dev_err(dev, "failed to enable PHY gate\n");
+ return ret;
+ }
+
+ /*
+ * at least the xrx200 usb2 phy requires some extra time to be
+ * operational after enabling the clock
+ */
+ usleep_range(100, 200);
return ret;
}
@@ -141,7 +149,7 @@ static int ltq_rcu_usb2_phy_power_off(struct phy *phy)
return 0;
}
-static struct phy_ops ltq_rcu_usb2_phy_ops = {
+static const struct phy_ops ltq_rcu_usb2_phy_ops = {
.init = ltq_rcu_usb2_phy_init,
.power_on = ltq_rcu_usb2_phy_power_on,
.power_off = ltq_rcu_usb2_phy_power_off,
diff --git a/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c
index 2ff9a48d833e..ef93bf2cba10 100644
--- a/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c
+++ b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c
@@ -349,7 +349,7 @@ static int ltq_vrx200_pcie_phy_power_off(struct phy *phy)
return 0;
}
-static struct phy_ops ltq_vrx200_pcie_phy_ops = {
+static const struct phy_ops ltq_vrx200_pcie_phy_ops = {
.init = ltq_vrx200_pcie_phy_init,
.exit = ltq_vrx200_pcie_phy_exit,
.power_on = ltq_vrx200_pcie_phy_power_on,
@@ -402,7 +402,6 @@ static int ltq_vrx200_pcie_phy_probe(struct platform_device *pdev)
struct ltq_vrx200_pcie_phy_priv *priv;
struct device *dev = &pdev->dev;
struct phy_provider *provider;
- struct resource *res;
void __iomem *base;
int ret;
@@ -410,8 +409,7 @@ static int ltq_vrx200_pcie_phy_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/drivers/phy/marvell/Kconfig b/drivers/phy/marvell/Kconfig
index 8f6273c837ec..bdb87c976243 100644
--- a/drivers/phy/marvell/Kconfig
+++ b/drivers/phy/marvell/Kconfig
@@ -3,8 +3,8 @@
# Phy drivers for Marvell platforms
#
config ARMADA375_USBCLUSTER_PHY
- def_bool y
- depends on MACH_ARMADA_375 || COMPILE_TEST
+ bool "Armada 375 USB cluster PHY support" if COMPILE_TEST
+ default y if MACH_ARMADA_375
depends on OF && HAS_IOMEM
select GENERIC_PHY
@@ -67,6 +67,14 @@ config PHY_MVEBU_CP110_COMPHY
lanes can be used by various controllers (Ethernet, sata, usb,
PCIe...).
+config PHY_MVEBU_CP110_UTMI
+ tristate "Marvell CP110 UTMI driver"
+ depends on ARCH_MVEBU || COMPILE_TEST
+ depends on OF && USB_COMMON
+ select GENERIC_PHY
+ help
+ Enable this to support Marvell CP110 UTMI PHY driver.
+
config PHY_MVEBU_SATA
def_bool y
depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
@@ -116,3 +124,15 @@ config PHY_MMP3_USB
The PHY driver will be used by Marvell udc/ehci/otg driver.
To compile this driver as a module, choose M here.
+
+config PHY_MMP3_HSIC
+ tristate "Marvell MMP3 USB HSIC PHY Driver"
+ depends on MACH_MMP3_DT || COMPILE_TEST
+ select GENERIC_PHY
+ help
+ Enable this to support Marvell MMP3 USB HSIC PHY driver for
+ Marvell MMP3 SoC. This driver will be used my the Marvell EHCI
+ driver to initialize the interface to internal USB HSIC
+ components on MMP3-based boards.
+
+ To compile this driver as a module, choose M here.
diff --git a/drivers/phy/marvell/Makefile b/drivers/phy/marvell/Makefile
index 5a106b1549f4..90862c4daa26 100644
--- a/drivers/phy/marvell/Makefile
+++ b/drivers/phy/marvell/Makefile
@@ -3,10 +3,12 @@ obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
obj-$(CONFIG_PHY_MMP3_USB) += phy-mmp3-usb.o
+obj-$(CONFIG_PHY_MMP3_HSIC) += phy-mmp3-hsic.o
obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o
obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o
obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY) += phy-armada38x-comphy.o
obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o
+obj-$(CONFIG_PHY_MVEBU_CP110_UTMI) += phy-mvebu-cp110-utmi.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o
obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o
diff --git a/drivers/phy/marvell/phy-armada375-usb2.c b/drivers/phy/marvell/phy-armada375-usb2.c
index fa5dc9462d09..b141e3cd8a94 100644
--- a/drivers/phy/marvell/phy-armada375-usb2.c
+++ b/drivers/phy/marvell/phy-armada375-usb2.c
@@ -105,15 +105,13 @@ static int armada375_usb_phy_probe(struct platform_device *pdev)
struct phy *phy;
struct phy_provider *phy_provider;
void __iomem *usb_cluster_base;
- struct resource *res;
struct armada375_cluster_phy *cluster_phy;
cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL);
if (!cluster_phy)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- usb_cluster_base = devm_ioremap_resource(&pdev->dev, res);
+ usb_cluster_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(usb_cluster_base))
return PTR_ERR(usb_cluster_base);
diff --git a/drivers/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c
index 6960dfd8ad8c..0fe408964334 100644
--- a/drivers/phy/marvell/phy-armada38x-comphy.c
+++ b/drivers/phy/marvell/phy-armada38x-comphy.c
@@ -41,6 +41,7 @@ struct a38x_comphy_lane {
struct a38x_comphy {
void __iomem *base;
+ void __iomem *conf;
struct device *dev;
struct a38x_comphy_lane lane[MAX_A38X_COMPHY];
};
@@ -54,6 +55,21 @@ static const u8 gbe_mux[MAX_A38X_COMPHY][MAX_A38X_PORTS] = {
{ 0, 0, 3 },
};
+static void a38x_set_conf(struct a38x_comphy_lane *lane, bool enable)
+{
+ struct a38x_comphy *priv = lane->priv;
+ u32 conf;
+
+ if (priv->conf) {
+ conf = readl_relaxed(priv->conf);
+ if (enable)
+ conf |= BIT(lane->port);
+ else
+ conf &= ~BIT(lane->port);
+ writel(conf, priv->conf);
+ }
+}
+
static void a38x_comphy_set_reg(struct a38x_comphy_lane *lane,
unsigned int offset, u32 mask, u32 value)
{
@@ -97,6 +113,7 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub)
{
struct a38x_comphy_lane *lane = phy_get_drvdata(phy);
unsigned int gen;
+ int ret;
if (mode != PHY_MODE_ETHERNET)
return -EINVAL;
@@ -115,13 +132,20 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub)
return -EINVAL;
}
+ a38x_set_conf(lane, false);
+
a38x_comphy_set_speed(lane, gen, gen);
- return a38x_comphy_poll(lane, COMPHY_STAT1,
- COMPHY_STAT1_PLL_RDY_TX |
- COMPHY_STAT1_PLL_RDY_RX,
- COMPHY_STAT1_PLL_RDY_TX |
- COMPHY_STAT1_PLL_RDY_RX);
+ ret = a38x_comphy_poll(lane, COMPHY_STAT1,
+ COMPHY_STAT1_PLL_RDY_TX |
+ COMPHY_STAT1_PLL_RDY_RX,
+ COMPHY_STAT1_PLL_RDY_TX |
+ COMPHY_STAT1_PLL_RDY_RX);
+
+ if (ret == 0)
+ a38x_set_conf(lane, true);
+
+ return ret;
}
static const struct phy_ops a38x_comphy_ops = {
@@ -174,14 +198,21 @@ static int a38x_comphy_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
priv->dev = &pdev->dev;
priv->base = base;
+ /* Optional */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "conf");
+ if (res) {
+ priv->conf = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->conf))
+ return PTR_ERR(priv->conf);
+ }
+
for_each_available_child_of_node(pdev->dev.of_node, child) {
struct phy *phy;
int ret;
diff --git a/drivers/phy/marvell/phy-berlin-usb.c b/drivers/phy/marvell/phy-berlin-usb.c
index a43df63007c5..78ef6ae72a9a 100644
--- a/drivers/phy/marvell/phy-berlin-usb.c
+++ b/drivers/phy/marvell/phy-berlin-usb.c
@@ -165,7 +165,6 @@ static int phy_berlin_usb_probe(struct platform_device *pdev)
const struct of_device_id *match =
of_match_device(phy_berlin_usb_of_match, &pdev->dev);
struct phy_berlin_usb_priv *priv;
- struct resource *res;
struct phy *phy;
struct phy_provider *phy_provider;
@@ -173,8 +172,7 @@ static int phy_berlin_usb_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(&pdev->dev, res);
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
diff --git a/drivers/phy/marvell/phy-mmp3-hsic.c b/drivers/phy/marvell/phy-mmp3-hsic.c
new file mode 100644
index 000000000000..7cccf01848d8
--- /dev/null
+++ b/drivers/phy/marvell/phy-mmp3-hsic.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk>
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+#define HSIC_CTRL 0x08
+#define HSIC_ENABLE BIT(7)
+#define PLL_BYPASS BIT(4)
+
+static int mmp3_hsic_phy_init(struct phy *phy)
+{
+ void __iomem *base = (void __iomem *)phy_get_drvdata(phy);
+ u32 hsic_ctrl;
+
+ hsic_ctrl = readl_relaxed(base + HSIC_CTRL);
+ hsic_ctrl |= HSIC_ENABLE;
+ hsic_ctrl |= PLL_BYPASS;
+ writel_relaxed(hsic_ctrl, base + HSIC_CTRL);
+
+ return 0;
+}
+
+static const struct phy_ops mmp3_hsic_phy_ops = {
+ .init = mmp3_hsic_phy_init,
+ .owner = THIS_MODULE,
+};
+
+static const struct of_device_id mmp3_hsic_phy_of_match[] = {
+ { .compatible = "marvell,mmp3-hsic-phy", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mmp3_hsic_phy_of_match);
+
+static int mmp3_hsic_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *provider;
+ struct resource *resource;
+ void __iomem *base;
+ struct phy *phy;
+
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, resource);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ phy = devm_phy_create(dev, NULL, &mmp3_hsic_phy_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(phy);
+ }
+
+ phy_set_drvdata(phy, (void *)base);
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(provider)) {
+ dev_err(dev, "failed to register PHY provider\n");
+ return PTR_ERR(provider);
+ }
+
+ return 0;
+}
+
+static struct platform_driver mmp3_hsic_phy_driver = {
+ .probe = mmp3_hsic_phy_probe,
+ .driver = {
+ .name = "mmp3-hsic-phy",
+ .of_match_table = mmp3_hsic_phy_of_match,
+ },
+};
+module_platform_driver(mmp3_hsic_phy_driver);
+
+MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
+MODULE_DESCRIPTION("Marvell MMP3 USB HSIC PHY Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/marvell/phy-mmp3-usb.c b/drivers/phy/marvell/phy-mmp3-usb.c
index 499869595a58..04c0bada3519 100644
--- a/drivers/phy/marvell/phy-mmp3-usb.c
+++ b/drivers/phy/marvell/phy-mmp3-usb.c
@@ -246,7 +246,6 @@ MODULE_DEVICE_TABLE(of, mmp3_usb_phy_of_match);
static int mmp3_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct resource *resource;
struct mmp3_usb_phy *mmp3_usb_phy;
struct phy_provider *provider;
@@ -254,8 +253,7 @@ static int mmp3_usb_phy_probe(struct platform_device *pdev)
if (!mmp3_usb_phy)
return -ENOMEM;
- resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- mmp3_usb_phy->base = devm_ioremap_resource(dev, resource);
+ mmp3_usb_phy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mmp3_usb_phy->base)) {
dev_err(dev, "failed to remap PHY regs\n");
return PTR_ERR(mmp3_usb_phy->base);
diff --git a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
index 1a138be8bd6a..67712c77d806 100644
--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
+++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
@@ -5,12 +5,16 @@
* Authors:
* Evan Wang <xswang@marvell.com>
* Miquèl Raynal <miquel.raynal@bootlin.com>
+ * Pali Rohár <pali@kernel.org>
+ * Marek Behún <kabel@kernel.org>
*
* Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart.
- * SMC call initial support done by Grzegorz Jaszczyk.
+ * Comphy code from ARM Trusted Firmware ported by Pali Rohár <pali@kernel.org>
+ * and Marek Behún <kabel@kernel.org>.
*/
-#include <linux/arm-smccc.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
@@ -18,107 +22,1089 @@
#include <linux/phy.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/spinlock.h>
-#define MVEBU_A3700_COMPHY_LANES 3
-#define MVEBU_A3700_COMPHY_PORTS 2
-
-/* COMPHY Fast SMC function identifiers */
-#define COMPHY_SIP_POWER_ON 0x82000001
-#define COMPHY_SIP_POWER_OFF 0x82000002
-#define COMPHY_SIP_PLL_LOCK 0x82000003
-#define COMPHY_FW_NOT_SUPPORTED (-1)
-
-#define COMPHY_FW_MODE_SATA 0x1
-#define COMPHY_FW_MODE_SGMII 0x2
-#define COMPHY_FW_MODE_HS_SGMII 0x3
-#define COMPHY_FW_MODE_USB3H 0x4
-#define COMPHY_FW_MODE_USB3D 0x5
-#define COMPHY_FW_MODE_PCIE 0x6
-#define COMPHY_FW_MODE_RXAUI 0x7
-#define COMPHY_FW_MODE_XFI 0x8
-#define COMPHY_FW_MODE_SFI 0x9
-#define COMPHY_FW_MODE_USB3 0xa
-
-#define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */
-#define COMPHY_FW_SPEED_2_5G 1
-#define COMPHY_FW_SPEED_3_125G 2 /* SGMII 2.5G */
-#define COMPHY_FW_SPEED_5G 3
-#define COMPHY_FW_SPEED_5_15625G 4 /* XFI 5G */
-#define COMPHY_FW_SPEED_6G 5
-#define COMPHY_FW_SPEED_10_3125G 6 /* XFI 10G */
-#define COMPHY_FW_SPEED_MAX 0x3F
-
-#define COMPHY_FW_MODE(mode) ((mode) << 12)
-#define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \
- ((idx) << 8) | \
- ((speed) << 2))
-#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \
- ((width) << 18))
+#define PLL_SET_DELAY_US 600
+#define COMPHY_PLL_SLEEP 1000
+#define COMPHY_PLL_TIMEOUT 150000
+
+/* Comphy lane2 indirect access register offset */
+#define COMPHY_LANE2_INDIR_ADDR 0x0
+#define COMPHY_LANE2_INDIR_DATA 0x4
+
+/* SATA and USB3 PHY offset compared to SATA PHY */
+#define COMPHY_LANE2_REGS_BASE 0x200
+
+/*
+ * When accessing common PHY lane registers directly, we need to shift by 1,
+ * since the registers are 16-bit.
+ */
+#define COMPHY_LANE_REG_DIRECT(reg) (((reg) & 0x7FF) << 1)
+
+/* COMPHY registers */
+#define COMPHY_POWER_PLL_CTRL 0x01
+#define PU_IVREF_BIT BIT(15)
+#define PU_PLL_BIT BIT(14)
+#define PU_RX_BIT BIT(13)
+#define PU_TX_BIT BIT(12)
+#define PU_TX_INTP_BIT BIT(11)
+#define PU_DFE_BIT BIT(10)
+#define RESET_DTL_RX_BIT BIT(9)
+#define PLL_LOCK_BIT BIT(8)
+#define REF_FREF_SEL_MASK GENMASK(4, 0)
+#define REF_FREF_SEL_SERDES_25MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x1)
+#define REF_FREF_SEL_SERDES_40MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x3)
+#define REF_FREF_SEL_SERDES_50MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x4)
+#define REF_FREF_SEL_PCIE_USB3_25MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x2)
+#define REF_FREF_SEL_PCIE_USB3_40MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x3)
+#define COMPHY_MODE_MASK GENMASK(7, 5)
+#define COMPHY_MODE_SATA FIELD_PREP(COMPHY_MODE_MASK, 0x0)
+#define COMPHY_MODE_PCIE FIELD_PREP(COMPHY_MODE_MASK, 0x3)
+#define COMPHY_MODE_SERDES FIELD_PREP(COMPHY_MODE_MASK, 0x4)
+#define COMPHY_MODE_USB3 FIELD_PREP(COMPHY_MODE_MASK, 0x5)
+
+#define COMPHY_KVCO_CAL_CTRL 0x02
+#define USE_MAX_PLL_RATE_BIT BIT(12)
+#define SPEED_PLL_MASK GENMASK(7, 2)
+#define SPEED_PLL_VALUE_16 FIELD_PREP(SPEED_PLL_MASK, 0x10)
+
+#define COMPHY_DIG_LOOPBACK_EN 0x23
+#define SEL_DATA_WIDTH_MASK GENMASK(11, 10)
+#define DATA_WIDTH_10BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x0)
+#define DATA_WIDTH_20BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x1)
+#define DATA_WIDTH_40BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x2)
+#define PLL_READY_TX_BIT BIT(4)
+
+#define COMPHY_SYNC_PATTERN 0x24
+#define TXD_INVERT_BIT BIT(10)
+#define RXD_INVERT_BIT BIT(11)
+
+#define COMPHY_SYNC_MASK_GEN 0x25
+#define PHY_GEN_MAX_MASK GENMASK(11, 10)
+#define PHY_GEN_MAX_USB3_5G FIELD_PREP(PHY_GEN_MAX_MASK, 0x1)
+
+#define COMPHY_ISOLATION_CTRL 0x26
+#define PHY_ISOLATE_MODE BIT(15)
+
+#define COMPHY_GEN2_SET2 0x3e
+#define GS2_TX_SSC_AMP_MASK GENMASK(15, 9)
+#define GS2_TX_SSC_AMP_4128 FIELD_PREP(GS2_TX_SSC_AMP_MASK, 0x20)
+#define GS2_VREG_RXTX_MAS_ISET_MASK GENMASK(8, 7)
+#define GS2_VREG_RXTX_MAS_ISET_60U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
+ 0x0)
+#define GS2_VREG_RXTX_MAS_ISET_80U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
+ 0x1)
+#define GS2_VREG_RXTX_MAS_ISET_100U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
+ 0x2)
+#define GS2_VREG_RXTX_MAS_ISET_120U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\
+ 0x3)
+#define GS2_RSVD_6_0_MASK GENMASK(6, 0)
+
+#define COMPHY_GEN3_SET2 0x3f
+
+#define COMPHY_IDLE_SYNC_EN 0x48
+#define IDLE_SYNC_EN BIT(12)
+
+#define COMPHY_MISC_CTRL0 0x4F
+#define CLK100M_125M_EN BIT(4)
+#define TXDCLK_2X_SEL BIT(6)
+#define CLK500M_EN BIT(7)
+#define PHY_REF_CLK_SEL BIT(10)
+
+#define COMPHY_SFT_RESET 0x52
+#define SFT_RST BIT(9)
+#define SFT_RST_NO_REG BIT(10)
+
+#define COMPHY_MISC_CTRL1 0x73
+#define SEL_BITS_PCIE_FORCE BIT(15)
+
+#define COMPHY_GEN2_SET3 0x112
+#define GS3_FFE_CAP_SEL_MASK GENMASK(3, 0)
+#define GS3_FFE_CAP_SEL_VALUE FIELD_PREP(GS3_FFE_CAP_SEL_MASK, 0xF)
+
+/* PIPE registers */
+#define COMPHY_PIPE_LANE_CFG0 0x180
+#define PRD_TXDEEMPH0_MASK BIT(0)
+#define PRD_TXMARGIN_MASK GENMASK(3, 1)
+#define PRD_TXSWING_MASK BIT(4)
+#define CFG_TX_ALIGN_POS_MASK GENMASK(8, 5)
+
+#define COMPHY_PIPE_LANE_CFG1 0x181
+#define PRD_TXDEEMPH1_MASK BIT(15)
+#define USE_MAX_PLL_RATE_EN BIT(9)
+#define TX_DET_RX_MODE BIT(6)
+#define GEN2_TX_DATA_DLY_MASK GENMASK(4, 3)
+#define GEN2_TX_DATA_DLY_DEFT FIELD_PREP(GEN2_TX_DATA_DLY_MASK, 2)
+#define TX_ELEC_IDLE_MODE_EN BIT(0)
+
+#define COMPHY_PIPE_LANE_STAT1 0x183
+#define TXDCLK_PCLK_EN BIT(0)
+
+#define COMPHY_PIPE_LANE_CFG4 0x188
+#define SPREAD_SPECTRUM_CLK_EN BIT(7)
+
+#define COMPHY_PIPE_RST_CLK_CTRL 0x1C1
+#define PIPE_SOFT_RESET BIT(0)
+#define PIPE_REG_RESET BIT(1)
+#define MODE_CORE_CLK_FREQ_SEL BIT(9)
+#define MODE_PIPE_WIDTH_32 BIT(3)
+#define MODE_REFDIV_MASK GENMASK(5, 4)
+#define MODE_REFDIV_BY_4 FIELD_PREP(MODE_REFDIV_MASK, 0x2)
+
+#define COMPHY_PIPE_TEST_MODE_CTRL 0x1C2
+#define MODE_MARGIN_OVERRIDE BIT(2)
+
+#define COMPHY_PIPE_CLK_SRC_LO 0x1C3
+#define MODE_CLK_SRC BIT(0)
+#define BUNDLE_PERIOD_SEL BIT(1)
+#define BUNDLE_PERIOD_SCALE_MASK GENMASK(3, 2)
+#define BUNDLE_SAMPLE_CTRL BIT(4)
+#define PLL_READY_DLY_MASK GENMASK(7, 5)
+#define CFG_SEL_20B BIT(15)
+
+#define COMPHY_PIPE_PWR_MGM_TIM1 0x1D0
+#define CFG_PM_OSCCLK_WAIT_MASK GENMASK(15, 12)
+#define CFG_PM_RXDEN_WAIT_MASK GENMASK(11, 8)
+#define CFG_PM_RXDEN_WAIT_1_UNIT FIELD_PREP(CFG_PM_RXDEN_WAIT_MASK, 0x1)
+#define CFG_PM_RXDLOZ_WAIT_MASK GENMASK(7, 0)
+#define CFG_PM_RXDLOZ_WAIT_7_UNIT FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0x7)
+#define CFG_PM_RXDLOZ_WAIT_12_UNIT FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0xC)
+
+/*
+ * This register is not from PHY lane register space. It only exists in the
+ * indirect register space, before the actual PHY lane 2 registers. So the
+ * offset is absolute, not relative to COMPHY_LANE2_REGS_BASE.
+ * It is used only for SATA PHY initialization.
+ */
+#define COMPHY_RESERVED_REG 0x0E
+#define PHYCTRL_FRM_PIN_BIT BIT(13)
+
+/* South Bridge PHY Configuration Registers */
+#define COMPHY_PHY_REG(lane, reg) (((1 - (lane)) * 0x28) + ((reg) & 0x3f))
+
+/*
+ * lane0: USB3/GbE1 PHY Configuration 1
+ * lane1: PCIe/GbE0 PHY Configuration 1
+ * (used only by SGMII code)
+ */
+#define COMPHY_PHY_CFG1 0x0
+#define PIN_PU_IVREF_BIT BIT(1)
+#define PIN_RESET_CORE_BIT BIT(11)
+#define PIN_RESET_COMPHY_BIT BIT(12)
+#define PIN_PU_PLL_BIT BIT(16)
+#define PIN_PU_RX_BIT BIT(17)
+#define PIN_PU_TX_BIT BIT(18)
+#define PIN_TX_IDLE_BIT BIT(19)
+#define GEN_RX_SEL_MASK GENMASK(25, 22)
+#define GEN_RX_SEL_VALUE(val) FIELD_PREP(GEN_RX_SEL_MASK, (val))
+#define GEN_TX_SEL_MASK GENMASK(29, 26)
+#define GEN_TX_SEL_VALUE(val) FIELD_PREP(GEN_TX_SEL_MASK, (val))
+#define SERDES_SPEED_1_25_G 0x6
+#define SERDES_SPEED_3_125_G 0x8
+#define PHY_RX_INIT_BIT BIT(30)
+
+/*
+ * lane0: USB3/GbE1 PHY Status 1
+ * lane1: PCIe/GbE0 PHY Status 1
+ * (used only by SGMII code)
+ */
+#define COMPHY_PHY_STAT1 0x18
+#define PHY_RX_INIT_DONE_BIT BIT(0)
+#define PHY_PLL_READY_RX_BIT BIT(2)
+#define PHY_PLL_READY_TX_BIT BIT(3)
+
+/* PHY Selector */
+#define COMPHY_SELECTOR_PHY_REG 0xFC
+/* bit0: 0: Lane1 is GbE0; 1: Lane1 is PCIe */
+#define COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT BIT(0)
+/* bit4: 0: Lane0 is GbE1; 1: Lane0 is USB3 */
+#define COMPHY_SELECTOR_USB3_GBE1_SEL_BIT BIT(4)
+/* bit8: 0: Lane0 is USB3 instead of GbE1, Lane2 is SATA; 1: Lane2 is USB3 */
+#define COMPHY_SELECTOR_USB3_PHY_SEL_BIT BIT(8)
struct mvebu_a3700_comphy_conf {
unsigned int lane;
enum phy_mode mode;
int submode;
- unsigned int port;
- u32 fw_mode;
};
-#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \
+#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode) \
{ \
.lane = _lane, \
.mode = _mode, \
.submode = _smode, \
- .port = _port, \
- .fw_mode = _fw, \
}
-#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \
- MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw)
+#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode) \
+ MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA)
-#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \
- MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw)
+#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode) \
+ MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode)
static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = {
/* lane 0 */
- MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0,
- COMPHY_FW_MODE_USB3H),
- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1,
- COMPHY_FW_MODE_SGMII),
- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1,
- COMPHY_FW_MODE_HS_SGMII),
+ MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS),
+ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII),
+ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_1000BASEX),
+ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX),
/* lane 1 */
- MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0,
- COMPHY_FW_MODE_PCIE),
- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0,
- COMPHY_FW_MODE_SGMII),
- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0,
- COMPHY_FW_MODE_HS_SGMII),
+ MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE),
+ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII),
+ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_1000BASEX),
+ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX),
/* lane 2 */
- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0,
- COMPHY_FW_MODE_SATA),
- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0,
- COMPHY_FW_MODE_USB3H),
+ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA),
+ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS),
+};
+
+struct mvebu_a3700_comphy_priv {
+ void __iomem *comphy_regs;
+ void __iomem *lane0_phy_regs; /* USB3 and GbE1 */
+ void __iomem *lane1_phy_regs; /* PCIe and GbE0 */
+ void __iomem *lane2_phy_indirect; /* SATA and USB3 */
+ spinlock_t lock; /* for PHY selector access */
+ bool xtal_is_40m;
};
struct mvebu_a3700_comphy_lane {
+ struct mvebu_a3700_comphy_priv *priv;
struct device *dev;
unsigned int id;
enum phy_mode mode;
int submode;
- int port;
+ bool invert_tx;
+ bool invert_rx;
+};
+
+struct gbe_phy_init_data_fix {
+ u16 addr;
+ u16 value;
};
-static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
- unsigned long mode)
+/* Changes to 40M1G25 mode data required for running 40M3G125 init mode */
+static struct gbe_phy_init_data_fix gbe_phy_init_fix[] = {
+ { 0x005, 0x07CC }, { 0x015, 0x0000 }, { 0x01B, 0x0000 },
+ { 0x01D, 0x0000 }, { 0x01E, 0x0000 }, { 0x01F, 0x0000 },
+ { 0x020, 0x0000 }, { 0x021, 0x0030 }, { 0x026, 0x0888 },
+ { 0x04D, 0x0152 }, { 0x04F, 0xA020 }, { 0x050, 0x07CC },
+ { 0x053, 0xE9CA }, { 0x055, 0xBD97 }, { 0x071, 0x3015 },
+ { 0x076, 0x03AA }, { 0x07C, 0x0FDF }, { 0x0C2, 0x3030 },
+ { 0x0C3, 0x8000 }, { 0x0E2, 0x5550 }, { 0x0E3, 0x12A4 },
+ { 0x0E4, 0x7D00 }, { 0x0E6, 0x0C83 }, { 0x101, 0xFCC0 },
+ { 0x104, 0x0C10 }
+};
+
+/* 40M1G25 mode init data */
+static u16 gbe_phy_init[512] = {
+ /* 0 1 2 3 4 5 6 7 */
+ /*-----------------------------------------------------------*/
+ /* 8 9 A B C D E F */
+ 0x3110, 0xFD83, 0x6430, 0x412F, 0x82C0, 0x06FA, 0x4500, 0x6D26, /* 00 */
+ 0xAFC0, 0x8000, 0xC000, 0x0000, 0x2000, 0x49CC, 0x0BC9, 0x2A52, /* 08 */
+ 0x0BD2, 0x0CDE, 0x13D2, 0x0CE8, 0x1149, 0x10E0, 0x0000, 0x0000, /* 10 */
+ 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x4134, 0x0D2D, 0xFFFF, /* 18 */
+ 0xFFE0, 0x4030, 0x1016, 0x0030, 0x0000, 0x0800, 0x0866, 0x0000, /* 20 */
+ 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, /* 28 */
+ 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 30 */
+ 0x0000, 0x0000, 0x000F, 0x6A62, 0x1988, 0x3100, 0x3100, 0x3100, /* 38 */
+ 0x3100, 0xA708, 0x2430, 0x0830, 0x1030, 0x4610, 0xFF00, 0xFF00, /* 40 */
+ 0x0060, 0x1000, 0x0400, 0x0040, 0x00F0, 0x0155, 0x1100, 0xA02A, /* 48 */
+ 0x06FA, 0x0080, 0xB008, 0xE3ED, 0x5002, 0xB592, 0x7A80, 0x0001, /* 50 */
+ 0x020A, 0x8820, 0x6014, 0x8054, 0xACAA, 0xFC88, 0x2A02, 0x45CF, /* 58 */
+ 0x000F, 0x1817, 0x2860, 0x064F, 0x0000, 0x0204, 0x1800, 0x6000, /* 60 */
+ 0x810F, 0x4F23, 0x4000, 0x4498, 0x0850, 0x0000, 0x000E, 0x1002, /* 68 */
+ 0x9D3A, 0x3009, 0xD066, 0x0491, 0x0001, 0x6AB0, 0x0399, 0x3780, /* 70 */
+ 0x0040, 0x5AC0, 0x4A80, 0x0000, 0x01DF, 0x0000, 0x0007, 0x0000, /* 78 */
+ 0x2D54, 0x00A1, 0x4000, 0x0100, 0xA20A, 0x0000, 0x0000, 0x0000, /* 80 */
+ 0x0000, 0x0000, 0x0000, 0x7400, 0x0E81, 0x1000, 0x1242, 0x0210, /* 88 */
+ 0x80DF, 0x0F1F, 0x2F3F, 0x4F5F, 0x6F7F, 0x0F1F, 0x2F3F, 0x4F5F, /* 90 */
+ 0x6F7F, 0x4BAD, 0x0000, 0x0000, 0x0800, 0x0000, 0x2400, 0xB651, /* 98 */
+ 0xC9E0, 0x4247, 0x0A24, 0x0000, 0xAF19, 0x1004, 0x0000, 0x0000, /* A0 */
+ 0x0000, 0x0013, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* A8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* B0 */
+ 0x0000, 0x0000, 0x0000, 0x0060, 0x0000, 0x0000, 0x0000, 0x0000, /* B8 */
+ 0x0000, 0x0000, 0x3010, 0xFA00, 0x0000, 0x0000, 0x0000, 0x0003, /* C0 */
+ 0x1618, 0x8200, 0x8000, 0x0400, 0x050F, 0x0000, 0x0000, 0x0000, /* C8 */
+ 0x4C93, 0x0000, 0x1000, 0x1120, 0x0010, 0x1242, 0x1242, 0x1E00, /* D0 */
+ 0x0000, 0x0000, 0x0000, 0x00F8, 0x0000, 0x0041, 0x0800, 0x0000, /* D8 */
+ 0x82A0, 0x572E, 0x2490, 0x14A9, 0x4E00, 0x0000, 0x0803, 0x0541, /* E0 */
+ 0x0C15, 0x0000, 0x0000, 0x0400, 0x2626, 0x0000, 0x0000, 0x4200, /* E8 */
+ 0x0000, 0xAA55, 0x1020, 0x0000, 0x0000, 0x5010, 0x0000, 0x0000, /* F0 */
+ 0x0000, 0x0000, 0x5000, 0x0000, 0x0000, 0x0000, 0x02F2, 0x0000, /* F8 */
+ 0x101F, 0xFDC0, 0x4000, 0x8010, 0x0110, 0x0006, 0x0000, 0x0000, /*100 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*108 */
+ 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04C6, 0x0000, /*110 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*118 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*120 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*128 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*130 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*138 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*140 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*148 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*150 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*158 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*160 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*168 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*170 */
+ 0x0000, 0x0000, 0x0000, 0x00F0, 0x08A2, 0x3112, 0x0A14, 0x0000, /*178 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*180 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*188 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*190 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*198 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1A0 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1A8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1B0 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1B8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1C0 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1C8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1D0 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1D8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1E0 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1E8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1F0 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 /*1F8 */
+};
+
+static inline void comphy_reg_set(void __iomem *addr, u32 data, u32 mask)
{
- struct arm_smccc_res res;
+ u32 val;
+
+ val = readl(addr);
+ val = (val & ~mask) | (data & mask);
+ writel(val, addr);
+}
- arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res);
+static inline void comphy_reg_set16(void __iomem *addr, u16 data, u16 mask)
+{
+ u16 val;
- return res.a0;
+ val = readw(addr);
+ val = (val & ~mask) | (data & mask);
+ writew(val, addr);
}
-static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
+/* Used for accessing lane 2 registers (SATA/USB3 PHY) */
+static void comphy_set_indirect(struct mvebu_a3700_comphy_priv *priv,
+ u32 offset, u16 data, u16 mask)
+{
+ writel(offset,
+ priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_ADDR);
+ comphy_reg_set(priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_DATA,
+ data, mask);
+}
+
+static void comphy_lane_reg_set(struct mvebu_a3700_comphy_lane *lane,
+ u16 reg, u16 data, u16 mask)
+{
+ if (lane->id == 2) {
+ /* lane 2 PHY registers are accessed indirectly */
+ comphy_set_indirect(lane->priv,
+ reg + COMPHY_LANE2_REGS_BASE,
+ data, mask);
+ } else {
+ void __iomem *base = lane->id == 1 ?
+ lane->priv->lane1_phy_regs :
+ lane->priv->lane0_phy_regs;
+
+ comphy_reg_set16(base + COMPHY_LANE_REG_DIRECT(reg),
+ data, mask);
+ }
+}
+
+static int comphy_lane_reg_poll(struct mvebu_a3700_comphy_lane *lane,
+ u16 reg, u16 bits,
+ ulong sleep_us, ulong timeout_us)
+{
+ int ret;
+
+ if (lane->id == 2) {
+ u32 data;
+
+ /* lane 2 PHY registers are accessed indirectly */
+ writel(reg + COMPHY_LANE2_REGS_BASE,
+ lane->priv->lane2_phy_indirect +
+ COMPHY_LANE2_INDIR_ADDR);
+
+ ret = readl_poll_timeout(lane->priv->lane2_phy_indirect +
+ COMPHY_LANE2_INDIR_DATA,
+ data, (data & bits) == bits,
+ sleep_us, timeout_us);
+ } else {
+ void __iomem *base = lane->id == 1 ?
+ lane->priv->lane1_phy_regs :
+ lane->priv->lane0_phy_regs;
+ u16 data;
+
+ ret = readw_poll_timeout(base + COMPHY_LANE_REG_DIRECT(reg),
+ data, (data & bits) == bits,
+ sleep_us, timeout_us);
+ }
+
+ return ret;
+}
+
+static void comphy_periph_reg_set(struct mvebu_a3700_comphy_lane *lane,
+ u8 reg, u32 data, u32 mask)
+{
+ comphy_reg_set(lane->priv->comphy_regs + COMPHY_PHY_REG(lane->id, reg),
+ data, mask);
+}
+
+static int comphy_periph_reg_poll(struct mvebu_a3700_comphy_lane *lane,
+ u8 reg, u32 bits,
+ ulong sleep_us, ulong timeout_us)
+{
+ u32 data;
+
+ return readl_poll_timeout(lane->priv->comphy_regs +
+ COMPHY_PHY_REG(lane->id, reg),
+ data, (data & bits) == bits,
+ sleep_us, timeout_us);
+}
+
+/* PHY selector configures with corresponding modes */
+static int
+mvebu_a3700_comphy_set_phy_selector(struct mvebu_a3700_comphy_lane *lane)
+{
+ u32 old, new, clr = 0, set = 0;
+ unsigned long flags;
+
+ switch (lane->mode) {
+ case PHY_MODE_SATA:
+ /* SATA must be in Lane2 */
+ if (lane->id == 2)
+ clr = COMPHY_SELECTOR_USB3_PHY_SEL_BIT;
+ else
+ goto error;
+ break;
+
+ case PHY_MODE_ETHERNET:
+ if (lane->id == 0)
+ clr = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT;
+ else if (lane->id == 1)
+ clr = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT;
+ else
+ goto error;
+ break;
+
+ case PHY_MODE_USB_HOST_SS:
+ if (lane->id == 2)
+ set = COMPHY_SELECTOR_USB3_PHY_SEL_BIT;
+ else if (lane->id == 0)
+ set = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT;
+ else
+ goto error;
+ break;
+
+ case PHY_MODE_PCIE:
+ /* PCIE must be in Lane1 */
+ if (lane->id == 1)
+ set = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT;
+ else
+ goto error;
+ break;
+
+ default:
+ goto error;
+ }
+
+ spin_lock_irqsave(&lane->priv->lock, flags);
+
+ old = readl(lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG);
+ new = (old & ~clr) | set;
+ writel(new, lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG);
+
+ spin_unlock_irqrestore(&lane->priv->lock, flags);
+
+ dev_dbg(lane->dev,
+ "COMPHY[%d] mode[%d] changed PHY selector 0x%08x -> 0x%08x\n",
+ lane->id, lane->mode, old, new);
+
+ return 0;
+error:
+ dev_err(lane->dev, "COMPHY[%d] mode[%d] is invalid\n", lane->id,
+ lane->mode);
+ return -EINVAL;
+}
+
+static int
+mvebu_a3700_comphy_sata_power_on(struct mvebu_a3700_comphy_lane *lane)
+{
+ u32 mask, data, ref_clk;
+ int ret;
+
+ /* Configure phy selector for SATA */
+ ret = mvebu_a3700_comphy_set_phy_selector(lane);
+ if (ret)
+ return ret;
+
+ /* Clear phy isolation mode to make it work in normal mode */
+ comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL,
+ 0x0, PHY_ISOLATE_MODE);
+
+ /* 0. Check the Polarity invert bits */
+ data = 0x0;
+ if (lane->invert_tx)
+ data |= TXD_INVERT_BIT;
+ if (lane->invert_rx)
+ data |= RXD_INVERT_BIT;
+ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
+ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
+
+ /* 1. Select 40-bit data width */
+ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN,
+ DATA_WIDTH_40BIT, SEL_DATA_WIDTH_MASK);
+
+ /* 2. Select reference clock(25M) and PHY mode (SATA) */
+ if (lane->priv->xtal_is_40m)
+ ref_clk = REF_FREF_SEL_SERDES_40MHZ;
+ else
+ ref_clk = REF_FREF_SEL_SERDES_25MHZ;
+
+ data = ref_clk | COMPHY_MODE_SATA;
+ mask = REF_FREF_SEL_MASK | COMPHY_MODE_MASK;
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+
+ /* 3. Use maximum PLL rate (no power save) */
+ comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL,
+ USE_MAX_PLL_RATE_BIT, USE_MAX_PLL_RATE_BIT);
+
+ /* 4. Reset reserved bit */
+ comphy_set_indirect(lane->priv, COMPHY_RESERVED_REG,
+ 0x0, PHYCTRL_FRM_PIN_BIT);
+
+ /* 5. Set vendor-specific configuration (It is done in sata driver) */
+ /* XXX: in U-Boot below sequence was executed in this place, in Linux
+ * not. Now it is done only in U-Boot before this comphy
+ * initialization - tests shows that it works ok, but in case of any
+ * future problem it is left for reference.
+ * reg_set(MVEBU_REGS_BASE + 0xe00a0, 0, 0xffffffff);
+ * reg_set(MVEBU_REGS_BASE + 0xe00a4, BIT(6), BIT(6));
+ */
+
+ /* Wait for > 55 us to allow PLL be enabled */
+ udelay(PLL_SET_DELAY_US);
+
+ /* Polling status */
+ ret = comphy_lane_reg_poll(lane, COMPHY_DIG_LOOPBACK_EN,
+ PLL_READY_TX_BIT, COMPHY_PLL_SLEEP,
+ COMPHY_PLL_TIMEOUT);
+ if (ret)
+ dev_err(lane->dev, "Failed to lock SATA PLL\n");
+
+ return ret;
+}
+
+static void comphy_gbe_phy_init(struct mvebu_a3700_comphy_lane *lane,
+ bool is_1gbps)
+{
+ int addr, fix_idx;
+ u16 val;
+
+ fix_idx = 0;
+ for (addr = 0; addr < 512; addr++) {
+ /*
+ * All PHY register values are defined in full for 3.125Gbps
+ * SERDES speed. The values required for 1.25 Gbps are almost
+ * the same and only few registers should be "fixed" in
+ * comparison to 3.125 Gbps values. These register values are
+ * stored in "gbe_phy_init_fix" array.
+ */
+ if (!is_1gbps && gbe_phy_init_fix[fix_idx].addr == addr) {
+ /* Use new value */
+ val = gbe_phy_init_fix[fix_idx].value;
+ if (fix_idx < ARRAY_SIZE(gbe_phy_init_fix))
+ fix_idx++;
+ } else {
+ val = gbe_phy_init[addr];
+ }
+
+ comphy_lane_reg_set(lane, addr, val, 0xFFFF);
+ }
+}
+
+static int
+mvebu_a3700_comphy_ethernet_power_on(struct mvebu_a3700_comphy_lane *lane)
+{
+ u32 mask, data, speed_sel;
+ int ret;
+
+ /* Set selector */
+ ret = mvebu_a3700_comphy_set_phy_selector(lane);
+ if (ret)
+ return ret;
+
+ /*
+ * 1. Reset PHY by setting PHY input port PIN_RESET=1.
+ * 2. Set PHY input port PIN_TX_IDLE=1, PIN_PU_IVREF=1 to keep
+ * PHY TXP/TXN output to idle state during PHY initialization
+ * 3. Set PHY input port PIN_PU_PLL=0, PIN_PU_RX=0, PIN_PU_TX=0.
+ */
+ data = PIN_PU_IVREF_BIT | PIN_TX_IDLE_BIT | PIN_RESET_COMPHY_BIT;
+ mask = data | PIN_RESET_CORE_BIT | PIN_PU_PLL_BIT | PIN_PU_RX_BIT |
+ PIN_PU_TX_BIT | PHY_RX_INIT_BIT;
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+
+ /* 4. Release reset to the PHY by setting PIN_RESET=0. */
+ data = 0x0;
+ mask = PIN_RESET_COMPHY_BIT;
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+
+ /*
+ * 5. Set PIN_PHY_GEN_TX[3:0] and PIN_PHY_GEN_RX[3:0] to decide COMPHY
+ * bit rate
+ */
+ switch (lane->submode) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ /* SGMII 1G, SerDes speed 1.25G */
+ speed_sel = SERDES_SPEED_1_25_G;
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ /* 2500Base-X, SerDes speed 3.125G */
+ speed_sel = SERDES_SPEED_3_125_G;
+ break;
+ default:
+ /* Other rates are not supported */
+ dev_err(lane->dev,
+ "unsupported phy speed %d on comphy lane%d\n",
+ lane->submode, lane->id);
+ return -EINVAL;
+ }
+ data = GEN_RX_SEL_VALUE(speed_sel) | GEN_TX_SEL_VALUE(speed_sel);
+ mask = GEN_RX_SEL_MASK | GEN_TX_SEL_MASK;
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+
+ /*
+ * 6. Wait 10mS for bandgap and reference clocks to stabilize; then
+ * start SW programming.
+ */
+ mdelay(10);
+
+ /* 7. Program COMPHY register PHY_MODE */
+ data = COMPHY_MODE_SERDES;
+ mask = COMPHY_MODE_MASK;
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+
+ /*
+ * 8. Set COMPHY register REFCLK_SEL to select the correct REFCLK
+ * source
+ */
+ data = 0x0;
+ mask = PHY_REF_CLK_SEL;
+ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask);
+
+ /*
+ * 9. Set correct reference clock frequency in COMPHY register
+ * REF_FREF_SEL.
+ */
+ if (lane->priv->xtal_is_40m)
+ data = REF_FREF_SEL_SERDES_50MHZ;
+ else
+ data = REF_FREF_SEL_SERDES_25MHZ;
+
+ mask = REF_FREF_SEL_MASK;
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+
+ /*
+ * 10. Program COMPHY register PHY_GEN_MAX[1:0]
+ * This step is mentioned in the flow received from verification team.
+ * However the PHY_GEN_MAX value is only meaningful for other interfaces
+ * (not SERDES). For instance, it selects SATA speed 1.5/3/6 Gbps or
+ * PCIe speed 2.5/5 Gbps
+ */
+
+ /*
+ * 11. Program COMPHY register SEL_BITS to set correct parallel data
+ * bus width
+ */
+ data = DATA_WIDTH_10BIT;
+ mask = SEL_DATA_WIDTH_MASK;
+ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, data, mask);
+
+ /*
+ * 12. As long as DFE function needs to be enabled in any mode,
+ * COMPHY register DFE_UPDATE_EN[5:0] shall be programmed to 0x3F
+ * for real chip during COMPHY power on.
+ * The value of the DFE_UPDATE_EN already is 0x3F, because it is the
+ * default value after reset of the PHY.
+ */
+
+ /*
+ * 13. Program COMPHY GEN registers.
+ * These registers should be programmed based on the lab testing result
+ * to achieve optimal performance. Please contact the CEA group to get
+ * the related GEN table during real chip bring-up. We only required to
+ * run though the entire registers programming flow defined by
+ * "comphy_gbe_phy_init" when the REF clock is 40 MHz. For REF clock
+ * 25 MHz the default values stored in PHY registers are OK.
+ */
+ dev_dbg(lane->dev, "Running C-DPI phy init %s mode\n",
+ lane->submode == PHY_INTERFACE_MODE_2500BASEX ? "2G5" : "1G");
+ if (lane->priv->xtal_is_40m)
+ comphy_gbe_phy_init(lane,
+ lane->submode != PHY_INTERFACE_MODE_2500BASEX);
+
+ /*
+ * 14. Check the PHY Polarity invert bit
+ */
+ data = 0x0;
+ if (lane->invert_tx)
+ data |= TXD_INVERT_BIT;
+ if (lane->invert_rx)
+ data |= RXD_INVERT_BIT;
+ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
+ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
+
+ /*
+ * 15. Set PHY input ports PIN_PU_PLL, PIN_PU_TX and PIN_PU_RX to 1 to
+ * start PHY power up sequence. All the PHY register programming should
+ * be done before PIN_PU_PLL=1. There should be no register programming
+ * for normal PHY operation from this point.
+ */
+ data = PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT;
+ mask = data;
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+
+ /*
+ * 16. Wait for PHY power up sequence to finish by checking output ports
+ * PIN_PLL_READY_TX=1 and PIN_PLL_READY_RX=1.
+ */
+ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
+ PHY_PLL_READY_TX_BIT |
+ PHY_PLL_READY_RX_BIT,
+ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
+ if (ret) {
+ dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n",
+ lane->id);
+ return ret;
+ }
+
+ /*
+ * 17. Set COMPHY input port PIN_TX_IDLE=0
+ */
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, 0x0, PIN_TX_IDLE_BIT);
+
+ /*
+ * 18. After valid data appear on PIN_RXDATA bus, set PIN_RX_INIT=1. To
+ * start RX initialization. PIN_RX_INIT_DONE will be cleared to 0 by the
+ * PHY After RX initialization is done, PIN_RX_INIT_DONE will be set to
+ * 1 by COMPHY Set PIN_RX_INIT=0 after PIN_RX_INIT_DONE= 1. Please
+ * refer to RX initialization part for details.
+ */
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1,
+ PHY_RX_INIT_BIT, PHY_RX_INIT_BIT);
+
+ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
+ PHY_PLL_READY_TX_BIT |
+ PHY_PLL_READY_RX_BIT,
+ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
+ if (ret) {
+ dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n",
+ lane->id);
+ return ret;
+ }
+
+ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
+ PHY_RX_INIT_DONE_BIT,
+ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
+ if (ret)
+ dev_err(lane->dev, "Failed to init RX of SERDES PHY %d\n",
+ lane->id);
+
+ return ret;
+}
+
+static int
+mvebu_a3700_comphy_usb3_power_on(struct mvebu_a3700_comphy_lane *lane)
+{
+ u32 mask, data, cfg, ref_clk;
+ int ret;
+
+ /* Set phy seclector */
+ ret = mvebu_a3700_comphy_set_phy_selector(lane);
+ if (ret)
+ return ret;
+
+ /*
+ * 0. Set PHY OTG Control(0x5d034), bit 4, Power up OTG module The
+ * register belong to UTMI module, so it is set in UTMI phy driver.
+ */
+
+ /*
+ * 1. Set PRD_TXDEEMPH (3.5db de-emph)
+ */
+ data = PRD_TXDEEMPH0_MASK;
+ mask = PRD_TXDEEMPH0_MASK | PRD_TXMARGIN_MASK | PRD_TXSWING_MASK |
+ CFG_TX_ALIGN_POS_MASK;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG0, data, mask);
+
+ /*
+ * 2. Set BIT0: enable transmitter in high impedance mode
+ * Set BIT[3:4]: delay 2 clock cycles for HiZ off latency
+ * Set BIT6: Tx detect Rx at HiZ mode
+ * Unset BIT15: set to 0 to set USB3 De-emphasize level to -3.5db
+ * together with bit 0 of COMPHY_PIPE_LANE_CFG0 register
+ */
+ data = TX_DET_RX_MODE | GEN2_TX_DATA_DLY_DEFT | TX_ELEC_IDLE_MODE_EN;
+ mask = PRD_TXDEEMPH1_MASK | TX_DET_RX_MODE | GEN2_TX_DATA_DLY_MASK |
+ TX_ELEC_IDLE_MODE_EN;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1, data, mask);
+
+ /*
+ * 3. Set Spread Spectrum Clock Enabled
+ */
+ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG4,
+ SPREAD_SPECTRUM_CLK_EN, SPREAD_SPECTRUM_CLK_EN);
+
+ /*
+ * 4. Set Override Margining Controls From the MAC:
+ * Use margining signals from lane configuration
+ */
+ comphy_lane_reg_set(lane, COMPHY_PIPE_TEST_MODE_CTRL,
+ MODE_MARGIN_OVERRIDE, 0xFFFF);
+
+ /*
+ * 5. Set Lane-to-Lane Bundle Clock Sampling Period = per PCLK cycles
+ * set Mode Clock Source = PCLK is generated from REFCLK
+ */
+ data = 0x0;
+ mask = MODE_CLK_SRC | BUNDLE_PERIOD_SEL | BUNDLE_PERIOD_SCALE_MASK |
+ BUNDLE_SAMPLE_CTRL | PLL_READY_DLY_MASK;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO, data, mask);
+
+ /*
+ * 6. Set G2 Spread Spectrum Clock Amplitude at 4K
+ */
+ comphy_lane_reg_set(lane, COMPHY_GEN2_SET2,
+ GS2_TX_SSC_AMP_4128, GS2_TX_SSC_AMP_MASK);
+
+ /*
+ * 7. Unset G3 Spread Spectrum Clock Amplitude
+ * set G3 TX and RX Register Master Current Select
+ */
+ data = GS2_VREG_RXTX_MAS_ISET_60U;
+ mask = GS2_TX_SSC_AMP_MASK | GS2_VREG_RXTX_MAS_ISET_MASK |
+ GS2_RSVD_6_0_MASK;
+ comphy_lane_reg_set(lane, COMPHY_GEN3_SET2, data, mask);
+
+ /*
+ * 8. Check crystal jumper setting and program the Power and PLL Control
+ * accordingly Change RX wait
+ */
+ if (lane->priv->xtal_is_40m) {
+ ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ;
+ cfg = CFG_PM_RXDLOZ_WAIT_12_UNIT;
+ } else {
+ ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ;
+ cfg = CFG_PM_RXDLOZ_WAIT_7_UNIT;
+ }
+
+ data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT |
+ PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_USB3 | ref_clk;
+ mask = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT |
+ PU_TX_INTP_BIT | PU_DFE_BIT | PLL_LOCK_BIT | COMPHY_MODE_MASK |
+ REF_FREF_SEL_MASK;
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+
+ data = CFG_PM_RXDEN_WAIT_1_UNIT | cfg;
+ mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK |
+ CFG_PM_RXDLOZ_WAIT_MASK;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask);
+
+ /*
+ * 9. Enable idle sync
+ */
+ comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN,
+ IDLE_SYNC_EN, IDLE_SYNC_EN);
+
+ /*
+ * 10. Enable the output of 500M clock
+ */
+ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, CLK500M_EN, CLK500M_EN);
+
+ /*
+ * 11. Set 20-bit data width
+ */
+ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN,
+ DATA_WIDTH_20BIT, 0xFFFF);
+
+ /*
+ * 12. Override Speed_PLL value and use MAC PLL
+ */
+ data = SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT;
+ mask = 0xFFFF;
+ comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, data, mask);
+
+ /*
+ * 13. Check the Polarity invert bit
+ */
+ data = 0x0;
+ if (lane->invert_tx)
+ data |= TXD_INVERT_BIT;
+ if (lane->invert_rx)
+ data |= RXD_INVERT_BIT;
+ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
+ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
+
+ /*
+ * 14. Set max speed generation to USB3.0 5Gbps
+ */
+ comphy_lane_reg_set(lane, COMPHY_SYNC_MASK_GEN,
+ PHY_GEN_MAX_USB3_5G, PHY_GEN_MAX_MASK);
+
+ /*
+ * 15. Set capacitor value for FFE gain peaking to 0xF
+ */
+ comphy_lane_reg_set(lane, COMPHY_GEN2_SET3,
+ GS3_FFE_CAP_SEL_VALUE, GS3_FFE_CAP_SEL_MASK);
+
+ /*
+ * 16. Release SW reset
+ */
+ data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32 | MODE_REFDIV_BY_4;
+ mask = 0xFFFF;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask);
+
+ /* Wait for > 55 us to allow PCLK be enabled */
+ udelay(PLL_SET_DELAY_US);
+
+ ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN,
+ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
+ if (ret)
+ dev_err(lane->dev, "Failed to lock USB3 PLL\n");
+
+ return ret;
+}
+
+static int
+mvebu_a3700_comphy_pcie_power_on(struct mvebu_a3700_comphy_lane *lane)
+{
+ u32 mask, data, ref_clk;
+ int ret;
+
+ /* Configure phy selector for PCIe */
+ ret = mvebu_a3700_comphy_set_phy_selector(lane);
+ if (ret)
+ return ret;
+
+ /* 1. Enable max PLL. */
+ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1,
+ USE_MAX_PLL_RATE_EN, USE_MAX_PLL_RATE_EN);
+
+ /* 2. Select 20 bit SERDES interface. */
+ comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO,
+ CFG_SEL_20B, CFG_SEL_20B);
+
+ /* 3. Force to use reg setting for PCIe mode */
+ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL1,
+ SEL_BITS_PCIE_FORCE, SEL_BITS_PCIE_FORCE);
+
+ /* 4. Change RX wait */
+ data = CFG_PM_RXDEN_WAIT_1_UNIT | CFG_PM_RXDLOZ_WAIT_12_UNIT;
+ mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK |
+ CFG_PM_RXDLOZ_WAIT_MASK;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask);
+
+ /* 5. Enable idle sync */
+ comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN,
+ IDLE_SYNC_EN, IDLE_SYNC_EN);
+
+ /* 6. Enable the output of 100M/125M/500M clock */
+ data = CLK500M_EN | TXDCLK_2X_SEL | CLK100M_125M_EN;
+ mask = data;
+ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask);
+
+ /*
+ * 7. Enable TX, PCIE global register, 0xd0074814, it is done in
+ * PCI-E driver
+ */
+
+ /*
+ * 8. Check crystal jumper setting and program the Power and PLL
+ * Control accordingly
+ */
+
+ if (lane->priv->xtal_is_40m)
+ ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ;
+ else
+ ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ;
+
+ data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT |
+ PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_PCIE | ref_clk;
+ mask = 0xFFFF;
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
+
+ /* 9. Override Speed_PLL value and use MAC PLL */
+ comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL,
+ SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT,
+ 0xFFFF);
+
+ /* 10. Check the Polarity invert bit */
+ data = 0x0;
+ if (lane->invert_tx)
+ data |= TXD_INVERT_BIT;
+ if (lane->invert_rx)
+ data |= RXD_INVERT_BIT;
+ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
+ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
+
+ /* 11. Release SW reset */
+ data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32;
+ mask = data | PIPE_SOFT_RESET | MODE_REFDIV_MASK;
+ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask);
+
+ /* Wait for > 55 us to allow PCLK be enabled */
+ udelay(PLL_SET_DELAY_US);
+
+ ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN,
+ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
+ if (ret)
+ dev_err(lane->dev, "Failed to lock PCIE PLL\n");
+
+ return ret;
+}
+
+static void
+mvebu_a3700_comphy_sata_power_off(struct mvebu_a3700_comphy_lane *lane)
+{
+ /* Set phy isolation mode */
+ comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL,
+ PHY_ISOLATE_MODE, PHY_ISOLATE_MODE);
+
+ /* Power off PLL, Tx, Rx */
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL,
+ 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT);
+}
+
+static void
+mvebu_a3700_comphy_ethernet_power_off(struct mvebu_a3700_comphy_lane *lane)
+{
+ u32 mask, data;
+
+ data = PIN_RESET_CORE_BIT | PIN_RESET_COMPHY_BIT | PIN_PU_IVREF_BIT |
+ PHY_RX_INIT_BIT;
+ mask = data;
+ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
+}
+
+static void
+mvebu_a3700_comphy_pcie_power_off(struct mvebu_a3700_comphy_lane *lane)
+{
+ /* Power off PLL, Tx, Rx */
+ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL,
+ 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT);
+}
+
+static void mvebu_a3700_comphy_usb3_power_off(struct mvebu_a3700_comphy_lane *lane)
+{
+ /*
+ * The USB3 MAC sets the USB3 PHY to low state, so we do not
+ * need to power off USB3 PHY again.
+ */
+}
+
+static bool mvebu_a3700_comphy_check_mode(int lane,
enum phy_mode mode,
int submode)
{
@@ -126,38 +1112,36 @@ static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
/* Unused PHY mux value is 0x0 */
if (mode == PHY_MODE_INVALID)
- return -EINVAL;
+ return false;
for (i = 0; i < n; i++) {
if (mvebu_a3700_comphy_modes[i].lane == lane &&
- mvebu_a3700_comphy_modes[i].port == port &&
mvebu_a3700_comphy_modes[i].mode == mode &&
mvebu_a3700_comphy_modes[i].submode == submode)
break;
}
if (i == n)
- return -EINVAL;
+ return false;
- return mvebu_a3700_comphy_modes[i].fw_mode;
+ return true;
}
static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode,
int submode)
{
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
- int fw_mode;
-
- if (submode == PHY_INTERFACE_MODE_1000BASEX)
- submode = PHY_INTERFACE_MODE_SGMII;
- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode,
- submode);
- if (fw_mode < 0) {
+ if (!mvebu_a3700_comphy_check_mode(lane->id, mode, submode)) {
dev_err(lane->dev, "invalid COMPHY mode\n");
- return fw_mode;
+ return -EINVAL;
}
+ /* Mode cannot be changed while the PHY is powered on */
+ if (phy->power_count &&
+ (lane->mode != mode || lane->submode != submode))
+ return -EBUSY;
+
/* Just remember the mode, ->power_on() will do the real setup */
lane->mode = mode;
lane->submode = submode;
@@ -168,70 +1152,53 @@ static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode,
static int mvebu_a3700_comphy_power_on(struct phy *phy)
{
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
- u32 fw_param;
- int fw_mode;
- int ret;
- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port,
- lane->mode, lane->submode);
- if (fw_mode < 0) {
+ if (!mvebu_a3700_comphy_check_mode(lane->id, lane->mode,
+ lane->submode)) {
dev_err(lane->dev, "invalid COMPHY mode\n");
- return fw_mode;
+ return -EINVAL;
}
switch (lane->mode) {
case PHY_MODE_USB_HOST_SS:
dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id);
- fw_param = COMPHY_FW_MODE(fw_mode);
- break;
+ return mvebu_a3700_comphy_usb3_power_on(lane);
case PHY_MODE_SATA:
dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id);
- fw_param = COMPHY_FW_MODE(fw_mode);
- break;
+ return mvebu_a3700_comphy_sata_power_on(lane);
case PHY_MODE_ETHERNET:
- switch (lane->submode) {
- case PHY_INTERFACE_MODE_SGMII:
- dev_dbg(lane->dev, "set lane %d to SGMII mode\n",
- lane->id);
- fw_param = COMPHY_FW_NET(fw_mode, lane->port,
- COMPHY_FW_SPEED_1_25G);
- break;
- case PHY_INTERFACE_MODE_2500BASEX:
- dev_dbg(lane->dev, "set lane %d to HS SGMII mode\n",
- lane->id);
- fw_param = COMPHY_FW_NET(fw_mode, lane->port,
- COMPHY_FW_SPEED_3_125G);
- break;
- default:
- dev_err(lane->dev, "unsupported PHY submode (%d)\n",
- lane->submode);
- return -ENOTSUPP;
- }
- break;
+ dev_dbg(lane->dev, "set lane %d to Ethernet mode\n", lane->id);
+ return mvebu_a3700_comphy_ethernet_power_on(lane);
case PHY_MODE_PCIE:
dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id);
- fw_param = COMPHY_FW_PCIE(fw_mode, lane->port,
- COMPHY_FW_SPEED_5G,
- phy->attrs.bus_width);
- break;
+ return mvebu_a3700_comphy_pcie_power_on(lane);
default:
dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode);
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
-
- ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param);
- if (ret == COMPHY_FW_NOT_SUPPORTED)
- dev_err(lane->dev,
- "unsupported SMC call, try updating your firmware\n");
-
- return ret;
}
static int mvebu_a3700_comphy_power_off(struct phy *phy)
{
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
- return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0);
+ switch (lane->id) {
+ case 0:
+ mvebu_a3700_comphy_usb3_power_off(lane);
+ mvebu_a3700_comphy_ethernet_power_off(lane);
+ return 0;
+ case 1:
+ mvebu_a3700_comphy_pcie_power_off(lane);
+ mvebu_a3700_comphy_ethernet_power_off(lane);
+ return 0;
+ case 2:
+ mvebu_a3700_comphy_usb3_power_off(lane);
+ mvebu_a3700_comphy_sata_power_off(lane);
+ return 0;
+ default:
+ dev_err(lane->dev, "invalid COMPHY mode\n");
+ return -EINVAL;
+ }
}
static const struct phy_ops mvebu_a3700_comphy_ops = {
@@ -245,25 +1212,90 @@ static struct phy *mvebu_a3700_comphy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct mvebu_a3700_comphy_lane *lane;
+ unsigned int port;
struct phy *phy;
- if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS))
- return ERR_PTR(-EINVAL);
-
phy = of_phy_simple_xlate(dev, args);
if (IS_ERR(phy))
return phy;
lane = phy_get_drvdata(phy);
- lane->port = args->args[0];
+
+ port = args->args[0];
+ if (port != 0 && (port != 1 || lane->id != 0)) {
+ dev_err(lane->dev, "invalid port number %u\n", port);
+ return ERR_PTR(-EINVAL);
+ }
+
+ lane->invert_tx = args->args[1] & BIT(0);
+ lane->invert_rx = args->args[1] & BIT(1);
return phy;
}
static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
{
+ struct mvebu_a3700_comphy_priv *priv;
struct phy_provider *provider;
struct device_node *child;
+ struct resource *res;
+ struct clk *clk;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spin_lock_init(&priv->lock);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "comphy");
+ priv->comphy_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->comphy_regs))
+ return PTR_ERR(priv->comphy_regs);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "lane1_pcie_gbe");
+ priv->lane1_phy_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->lane1_phy_regs))
+ return PTR_ERR(priv->lane1_phy_regs);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "lane0_usb3_gbe");
+ priv->lane0_phy_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->lane0_phy_regs))
+ return PTR_ERR(priv->lane0_phy_regs);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "lane2_sata_usb3");
+ priv->lane2_phy_indirect = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->lane2_phy_indirect))
+ return PTR_ERR(priv->lane2_phy_indirect);
+
+ /*
+ * Driver needs to know if reference xtal clock is 40MHz or 25MHz.
+ * Old DT bindings do not have xtal clk present. So do not fail here
+ * and expects that default 25MHz reference clock is used.
+ */
+ clk = clk_get(&pdev->dev, "xtal");
+ if (IS_ERR(clk)) {
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_warn(&pdev->dev, "missing 'xtal' clk (%ld)\n",
+ PTR_ERR(clk));
+ } else {
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_warn(&pdev->dev, "enabling xtal clk failed (%d)\n",
+ ret);
+ } else {
+ if (clk_get_rate(clk) == 40000000)
+ priv->xtal_is_40m = true;
+ clk_disable_unprepare(clk);
+ }
+ clk_put(clk);
+ }
+
+ dev_set_drvdata(&pdev->dev, priv);
for_each_available_child_of_node(pdev->dev.of_node, child) {
struct mvebu_a3700_comphy_lane *lane;
@@ -278,7 +1310,7 @@ static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
continue;
}
- if (lane_id >= MVEBU_A3700_COMPHY_LANES) {
+ if (lane_id >= 3) {
dev_err(&pdev->dev, "invalid 'reg' property\n");
continue;
}
@@ -296,16 +1328,25 @@ static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
return PTR_ERR(phy);
}
+ lane->priv = priv;
lane->dev = &pdev->dev;
lane->mode = PHY_MODE_INVALID;
lane->submode = PHY_INTERFACE_MODE_NA;
lane->id = lane_id;
- lane->port = -1;
+ lane->invert_tx = false;
+ lane->invert_rx = false;
phy_set_drvdata(phy, lane);
+
+ /*
+ * To avoid relying on the bootloader/firmware configuration,
+ * power off all comphys.
+ */
+ mvebu_a3700_comphy_power_off(phy);
}
provider = devm_of_phy_provider_register(&pdev->dev,
mvebu_a3700_comphy_xlate);
+
return PTR_ERR_OR_ZERO(provider);
}
@@ -325,5 +1366,7 @@ static struct platform_driver mvebu_a3700_comphy_driver = {
module_platform_driver(mvebu_a3700_comphy_driver);
MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>");
+MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
+MODULE_AUTHOR("Marek Behún <kabel@kernel.org>");
MODULE_DESCRIPTION("Common PHY driver for A3700");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c
index 23bc3bf5c4c0..8834436bc9db 100644
--- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c
+++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c
@@ -72,7 +72,7 @@ struct mvebu_a3700_utmi_caps {
* struct mvebu_a3700_utmi - PHY driver data
*
* @regs: PHY registers
- * @usb_mis: Regmap with USB miscellaneous registers including PHY ones
+ * @usb_misc: Regmap with USB miscellaneous registers including PHY ones
* @caps: PHY capabilities
* @phy: PHY handle
*/
diff --git a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c
index e41367f36ee1..34672e868a1e 100644
--- a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c
+++ b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c
@@ -123,7 +123,6 @@
#define COMPHY_SIP_POWER_ON 0x82000001
#define COMPHY_SIP_POWER_OFF 0x82000002
-#define COMPHY_FW_NOT_SUPPORTED (-1)
/*
* A lane is described by the following bitfields:
@@ -142,6 +141,7 @@
#define COMPHY_FW_SPEED_1250 0
#define COMPHY_FW_SPEED_3125 2
#define COMPHY_FW_SPEED_5000 3
+#define COMPHY_FW_SPEED_515625 4
#define COMPHY_FW_SPEED_103125 6
#define COMPHY_FW_PORT_OFFSET 8
#define COMPHY_FW_PORT_MASK GENMASK(11, 8)
@@ -168,7 +168,7 @@
#define COMPHY_FW_MODE_SATA 0x1
#define COMPHY_FW_MODE_SGMII 0x2 /* SGMII 1G */
-#define COMPHY_FW_MODE_HS_SGMII 0x3 /* SGMII 2.5G */
+#define COMPHY_FW_MODE_2500BASEX 0x3 /* 2500BASE-X */
#define COMPHY_FW_MODE_USB3H 0x4
#define COMPHY_FW_MODE_USB3D 0x5
#define COMPHY_FW_MODE_PCIE 0x6
@@ -208,7 +208,7 @@ static const struct mvebu_comphy_conf mvebu_comphy_cp110_modes[] = {
/* lane 0 */
GEN_CONF(0, 0, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE),
ETH_CONF(0, 1, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII),
- ETH_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII),
+ ETH_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_2500BASEX),
GEN_CONF(0, 1, PHY_MODE_SATA, COMPHY_FW_MODE_SATA),
/* lane 1 */
GEN_CONF(1, 0, PHY_MODE_USB_HOST_SS, COMPHY_FW_MODE_USB3H),
@@ -216,11 +216,12 @@ static const struct mvebu_comphy_conf mvebu_comphy_cp110_modes[] = {
GEN_CONF(1, 0, PHY_MODE_SATA, COMPHY_FW_MODE_SATA),
GEN_CONF(1, 0, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE),
ETH_CONF(1, 2, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII),
- ETH_CONF(1, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII),
+ ETH_CONF(1, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_2500BASEX),
/* lane 2 */
ETH_CONF(2, 0, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII),
- ETH_CONF(2, 0, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII),
+ ETH_CONF(2, 0, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_2500BASEX),
ETH_CONF(2, 0, PHY_INTERFACE_MODE_RXAUI, 0x1, COMPHY_FW_MODE_RXAUI),
+ ETH_CONF(2, 0, PHY_INTERFACE_MODE_5GBASER, 0x1, COMPHY_FW_MODE_XFI),
ETH_CONF(2, 0, PHY_INTERFACE_MODE_10GBASER, 0x1, COMPHY_FW_MODE_XFI),
GEN_CONF(2, 0, PHY_MODE_USB_HOST_SS, COMPHY_FW_MODE_USB3H),
GEN_CONF(2, 0, PHY_MODE_SATA, COMPHY_FW_MODE_SATA),
@@ -228,26 +229,28 @@ static const struct mvebu_comphy_conf mvebu_comphy_cp110_modes[] = {
/* lane 3 */
GEN_CONF(3, 0, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE),
ETH_CONF(3, 1, PHY_INTERFACE_MODE_SGMII, 0x2, COMPHY_FW_MODE_SGMII),
- ETH_CONF(3, 1, PHY_INTERFACE_MODE_2500BASEX, 0x2, COMPHY_FW_MODE_HS_SGMII),
+ ETH_CONF(3, 1, PHY_INTERFACE_MODE_2500BASEX, 0x2, COMPHY_FW_MODE_2500BASEX),
ETH_CONF(3, 1, PHY_INTERFACE_MODE_RXAUI, 0x1, COMPHY_FW_MODE_RXAUI),
GEN_CONF(3, 1, PHY_MODE_USB_HOST_SS, COMPHY_FW_MODE_USB3H),
GEN_CONF(3, 1, PHY_MODE_SATA, COMPHY_FW_MODE_SATA),
/* lane 4 */
ETH_CONF(4, 0, PHY_INTERFACE_MODE_SGMII, 0x2, COMPHY_FW_MODE_SGMII),
- ETH_CONF(4, 0, PHY_INTERFACE_MODE_2500BASEX, 0x2, COMPHY_FW_MODE_HS_SGMII),
+ ETH_CONF(4, 0, PHY_INTERFACE_MODE_2500BASEX, 0x2, COMPHY_FW_MODE_2500BASEX),
+ ETH_CONF(4, 0, PHY_INTERFACE_MODE_5GBASER, 0x2, COMPHY_FW_MODE_XFI),
ETH_CONF(4, 0, PHY_INTERFACE_MODE_10GBASER, 0x2, COMPHY_FW_MODE_XFI),
ETH_CONF(4, 0, PHY_INTERFACE_MODE_RXAUI, 0x2, COMPHY_FW_MODE_RXAUI),
GEN_CONF(4, 0, PHY_MODE_USB_DEVICE_SS, COMPHY_FW_MODE_USB3D),
GEN_CONF(4, 1, PHY_MODE_USB_HOST_SS, COMPHY_FW_MODE_USB3H),
GEN_CONF(4, 1, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE),
ETH_CONF(4, 1, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII),
- ETH_CONF(4, 1, PHY_INTERFACE_MODE_2500BASEX, -1, COMPHY_FW_MODE_HS_SGMII),
+ ETH_CONF(4, 1, PHY_INTERFACE_MODE_2500BASEX, -1, COMPHY_FW_MODE_2500BASEX),
+ ETH_CONF(4, 1, PHY_INTERFACE_MODE_5GBASER, -1, COMPHY_FW_MODE_XFI),
ETH_CONF(4, 1, PHY_INTERFACE_MODE_10GBASER, -1, COMPHY_FW_MODE_XFI),
/* lane 5 */
ETH_CONF(5, 1, PHY_INTERFACE_MODE_RXAUI, 0x2, COMPHY_FW_MODE_RXAUI),
GEN_CONF(5, 1, PHY_MODE_SATA, COMPHY_FW_MODE_SATA),
ETH_CONF(5, 2, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII),
- ETH_CONF(5, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII),
+ ETH_CONF(5, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_2500BASEX),
GEN_CONF(5, 2, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE),
};
@@ -273,10 +276,19 @@ static int mvebu_comphy_smc(unsigned long function, unsigned long phys,
unsigned long lane, unsigned long mode)
{
struct arm_smccc_res res;
+ s32 ret;
arm_smccc_smc(function, phys, lane, mode, 0, 0, 0, 0, &res);
+ ret = res.a0;
- return res.a0;
+ switch (ret) {
+ case SMCCC_RET_SUCCESS:
+ return 0;
+ case SMCCC_RET_NOT_SUPPORTED:
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
}
static int mvebu_comphy_get_mode(bool fw_mode, int lane, int port,
@@ -782,6 +794,11 @@ static int mvebu_comphy_power_on(struct phy *phy)
lane->id);
fw_speed = COMPHY_FW_SPEED_3125;
break;
+ case PHY_INTERFACE_MODE_5GBASER:
+ dev_dbg(priv->dev, "set lane %d to 5GBASE-R mode\n",
+ lane->id);
+ fw_speed = COMPHY_FW_SPEED_515625;
+ break;
case PHY_INTERFACE_MODE_10GBASER:
dev_dbg(priv->dev, "set lane %d to 10GBASE-R mode\n",
lane->id);
@@ -819,7 +836,7 @@ static int mvebu_comphy_power_on(struct phy *phy)
if (!ret)
return ret;
- if (ret == COMPHY_FW_NOT_SUPPORTED)
+ if (ret == -EOPNOTSUPP)
dev_err(priv->dev,
"unsupported SMC call, try updating your firmware\n");
diff --git a/drivers/phy/marvell/phy-mvebu-cp110-utmi.c b/drivers/phy/marvell/phy-mvebu-cp110-utmi.c
new file mode 100644
index 000000000000..aa27c7994610
--- /dev/null
+++ b/drivers/phy/marvell/phy-mvebu-cp110-utmi.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Marvell
+ *
+ * Authors:
+ * Konstantin Porotchkin <kostap@marvell.com>
+ *
+ * Marvell CP110 UTMI PHY driver
+ */
+
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/usb/of.h>
+#include <linux/usb/otg.h>
+
+#define UTMI_PHY_PORTS 2
+
+/* CP110 UTMI register macro definetions */
+#define SYSCON_USB_CFG_REG 0x420
+#define USB_CFG_DEVICE_EN_MASK BIT(0)
+#define USB_CFG_DEVICE_MUX_OFFSET 1
+#define USB_CFG_DEVICE_MUX_MASK BIT(1)
+#define USB_CFG_PLL_MASK BIT(25)
+
+#define SYSCON_UTMI_CFG_REG(id) (0x440 + (id) * 4)
+#define UTMI_PHY_CFG_PU_MASK BIT(5)
+
+#define UTMI_PLL_CTRL_REG 0x0
+#define PLL_REFDIV_OFFSET 0
+#define PLL_REFDIV_MASK GENMASK(6, 0)
+#define PLL_REFDIV_VAL 0x5
+#define PLL_FBDIV_OFFSET 16
+#define PLL_FBDIV_MASK GENMASK(24, 16)
+#define PLL_FBDIV_VAL 0x60
+#define PLL_SEL_LPFR_MASK GENMASK(29, 28)
+#define PLL_RDY BIT(31)
+#define UTMI_CAL_CTRL_REG 0x8
+#define IMPCAL_VTH_OFFSET 8
+#define IMPCAL_VTH_MASK GENMASK(10, 8)
+#define IMPCAL_VTH_VAL 0x7
+#define IMPCAL_DONE BIT(23)
+#define PLLCAL_DONE BIT(31)
+#define UTMI_TX_CH_CTRL_REG 0xC
+#define DRV_EN_LS_OFFSET 12
+#define DRV_EN_LS_MASK GENMASK(15, 12)
+#define IMP_SEL_LS_OFFSET 16
+#define IMP_SEL_LS_MASK GENMASK(19, 16)
+#define TX_AMP_OFFSET 20
+#define TX_AMP_MASK GENMASK(22, 20)
+#define TX_AMP_VAL 0x4
+#define UTMI_RX_CH_CTRL0_REG 0x14
+#define SQ_DET_EN BIT(15)
+#define SQ_ANA_DTC_SEL BIT(28)
+#define UTMI_RX_CH_CTRL1_REG 0x18
+#define SQ_AMP_CAL_OFFSET 0
+#define SQ_AMP_CAL_MASK GENMASK(2, 0)
+#define SQ_AMP_CAL_VAL 1
+#define SQ_AMP_CAL_EN BIT(3)
+#define UTMI_CTRL_STATUS0_REG 0x24
+#define SUSPENDM BIT(22)
+#define TEST_SEL BIT(25)
+#define UTMI_CHGDTC_CTRL_REG 0x38
+#define VDAT_OFFSET 8
+#define VDAT_MASK GENMASK(9, 8)
+#define VDAT_VAL 1
+#define VSRC_OFFSET 10
+#define VSRC_MASK GENMASK(11, 10)
+#define VSRC_VAL 1
+
+#define PLL_LOCK_DELAY_US 10000
+#define PLL_LOCK_TIMEOUT_US 1000000
+
+#define PORT_REGS(p) ((p)->priv->regs + (p)->id * 0x1000)
+
+/**
+ * struct mvebu_cp110_utmi - PHY driver data
+ *
+ * @regs: PHY registers
+ * @syscon: Regmap with system controller registers
+ * @dev: device driver handle
+ * @ops: phy ops
+ */
+struct mvebu_cp110_utmi {
+ void __iomem *regs;
+ struct regmap *syscon;
+ struct device *dev;
+ const struct phy_ops *ops;
+};
+
+/**
+ * struct mvebu_cp110_utmi_port - PHY port data
+ *
+ * @priv: PHY driver data
+ * @id: PHY port ID
+ * @dr_mode: PHY connection: USB_DR_MODE_HOST or USB_DR_MODE_PERIPHERAL
+ */
+struct mvebu_cp110_utmi_port {
+ struct mvebu_cp110_utmi *priv;
+ u32 id;
+ enum usb_dr_mode dr_mode;
+};
+
+static void mvebu_cp110_utmi_port_setup(struct mvebu_cp110_utmi_port *port)
+{
+ u32 reg;
+
+ /*
+ * Setup PLL.
+ * The reference clock is the frequency of quartz resonator
+ * connected to pins REFCLK_XIN and REFCLK_XOUT of the SoC.
+ * Register init values are matching the 40MHz default clock.
+ * The crystal used for all platform boards is now 25MHz.
+ * See the functional specification for details.
+ */
+ reg = readl(PORT_REGS(port) + UTMI_PLL_CTRL_REG);
+ reg &= ~(PLL_REFDIV_MASK | PLL_FBDIV_MASK | PLL_SEL_LPFR_MASK);
+ reg |= (PLL_REFDIV_VAL << PLL_REFDIV_OFFSET) |
+ (PLL_FBDIV_VAL << PLL_FBDIV_OFFSET);
+ writel(reg, PORT_REGS(port) + UTMI_PLL_CTRL_REG);
+
+ /* Impedance Calibration Threshold Setting */
+ reg = readl(PORT_REGS(port) + UTMI_CAL_CTRL_REG);
+ reg &= ~IMPCAL_VTH_MASK;
+ reg |= IMPCAL_VTH_VAL << IMPCAL_VTH_OFFSET;
+ writel(reg, PORT_REGS(port) + UTMI_CAL_CTRL_REG);
+
+ /* Set LS TX driver strength coarse control */
+ reg = readl(PORT_REGS(port) + UTMI_TX_CH_CTRL_REG);
+ reg &= ~TX_AMP_MASK;
+ reg |= TX_AMP_VAL << TX_AMP_OFFSET;
+ writel(reg, PORT_REGS(port) + UTMI_TX_CH_CTRL_REG);
+
+ /* Disable SQ and enable analog squelch detect */
+ reg = readl(PORT_REGS(port) + UTMI_RX_CH_CTRL0_REG);
+ reg &= ~SQ_DET_EN;
+ reg |= SQ_ANA_DTC_SEL;
+ writel(reg, PORT_REGS(port) + UTMI_RX_CH_CTRL0_REG);
+
+ /*
+ * Set External squelch calibration number and
+ * enable the External squelch calibration
+ */
+ reg = readl(PORT_REGS(port) + UTMI_RX_CH_CTRL1_REG);
+ reg &= ~SQ_AMP_CAL_MASK;
+ reg |= (SQ_AMP_CAL_VAL << SQ_AMP_CAL_OFFSET) | SQ_AMP_CAL_EN;
+ writel(reg, PORT_REGS(port) + UTMI_RX_CH_CTRL1_REG);
+
+ /*
+ * Set Control VDAT Reference Voltage - 0.325V and
+ * Control VSRC Reference Voltage - 0.6V
+ */
+ reg = readl(PORT_REGS(port) + UTMI_CHGDTC_CTRL_REG);
+ reg &= ~(VDAT_MASK | VSRC_MASK);
+ reg |= (VDAT_VAL << VDAT_OFFSET) | (VSRC_VAL << VSRC_OFFSET);
+ writel(reg, PORT_REGS(port) + UTMI_CHGDTC_CTRL_REG);
+}
+
+static int mvebu_cp110_utmi_phy_power_off(struct phy *phy)
+{
+ struct mvebu_cp110_utmi_port *port = phy_get_drvdata(phy);
+ struct mvebu_cp110_utmi *utmi = port->priv;
+ int i;
+
+ /* Power down UTMI PHY port */
+ regmap_clear_bits(utmi->syscon, SYSCON_UTMI_CFG_REG(port->id),
+ UTMI_PHY_CFG_PU_MASK);
+
+ for (i = 0; i < UTMI_PHY_PORTS; i++) {
+ int test = regmap_test_bits(utmi->syscon,
+ SYSCON_UTMI_CFG_REG(i),
+ UTMI_PHY_CFG_PU_MASK);
+ /* skip PLL shutdown if there are active UTMI PHY ports */
+ if (test != 0)
+ return 0;
+ }
+
+ /* PLL Power down if all UTMI PHYs are down */
+ regmap_clear_bits(utmi->syscon, SYSCON_USB_CFG_REG, USB_CFG_PLL_MASK);
+
+ return 0;
+}
+
+static int mvebu_cp110_utmi_phy_power_on(struct phy *phy)
+{
+ struct mvebu_cp110_utmi_port *port = phy_get_drvdata(phy);
+ struct mvebu_cp110_utmi *utmi = port->priv;
+ struct device *dev = &phy->dev;
+ int ret;
+ u32 reg;
+
+ /* It is necessary to power off UTMI before configuration */
+ ret = mvebu_cp110_utmi_phy_power_off(phy);
+ if (ret) {
+ dev_err(dev, "UTMI power OFF before power ON failed\n");
+ return ret;
+ }
+
+ /*
+ * If UTMI port is connected to USB Device controller,
+ * configure the USB MUX prior to UTMI PHY initialization.
+ * The single USB device controller can be connected
+ * to UTMI0 or to UTMI1 PHY port, but not to both.
+ */
+ if (port->dr_mode == USB_DR_MODE_PERIPHERAL) {
+ regmap_update_bits(utmi->syscon, SYSCON_USB_CFG_REG,
+ USB_CFG_DEVICE_EN_MASK | USB_CFG_DEVICE_MUX_MASK,
+ USB_CFG_DEVICE_EN_MASK |
+ (port->id << USB_CFG_DEVICE_MUX_OFFSET));
+ }
+
+ /* Set Test suspendm mode and enable Test UTMI select */
+ reg = readl(PORT_REGS(port) + UTMI_CTRL_STATUS0_REG);
+ reg |= SUSPENDM | TEST_SEL;
+ writel(reg, PORT_REGS(port) + UTMI_CTRL_STATUS0_REG);
+
+ /* Wait for UTMI power down */
+ mdelay(1);
+
+ /* PHY port setup first */
+ mvebu_cp110_utmi_port_setup(port);
+
+ /* Power UP UTMI PHY */
+ regmap_set_bits(utmi->syscon, SYSCON_UTMI_CFG_REG(port->id),
+ UTMI_PHY_CFG_PU_MASK);
+
+ /* Disable Test UTMI select */
+ reg = readl(PORT_REGS(port) + UTMI_CTRL_STATUS0_REG);
+ reg &= ~TEST_SEL;
+ writel(reg, PORT_REGS(port) + UTMI_CTRL_STATUS0_REG);
+
+ /* Wait for impedance calibration */
+ ret = readl_poll_timeout(PORT_REGS(port) + UTMI_CAL_CTRL_REG, reg,
+ reg & IMPCAL_DONE,
+ PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "Failed to end UTMI impedance calibration\n");
+ return ret;
+ }
+
+ /* Wait for PLL calibration */
+ ret = readl_poll_timeout(PORT_REGS(port) + UTMI_CAL_CTRL_REG, reg,
+ reg & PLLCAL_DONE,
+ PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "Failed to end UTMI PLL calibration\n");
+ return ret;
+ }
+
+ /* Wait for PLL ready */
+ ret = readl_poll_timeout(PORT_REGS(port) + UTMI_PLL_CTRL_REG, reg,
+ reg & PLL_RDY,
+ PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "PLL is not ready\n");
+ return ret;
+ }
+
+ /* PLL Power up */
+ regmap_set_bits(utmi->syscon, SYSCON_USB_CFG_REG, USB_CFG_PLL_MASK);
+
+ return 0;
+}
+
+static const struct phy_ops mvebu_cp110_utmi_phy_ops = {
+ .power_on = mvebu_cp110_utmi_phy_power_on,
+ .power_off = mvebu_cp110_utmi_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static const struct of_device_id mvebu_cp110_utmi_of_match[] = {
+ { .compatible = "marvell,cp110-utmi-phy" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mvebu_cp110_utmi_of_match);
+
+static int mvebu_cp110_utmi_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mvebu_cp110_utmi *utmi;
+ struct phy_provider *provider;
+ struct device_node *child;
+ u32 usb_devices = 0;
+
+ utmi = devm_kzalloc(dev, sizeof(*utmi), GFP_KERNEL);
+ if (!utmi)
+ return -ENOMEM;
+
+ utmi->dev = dev;
+
+ /* Get system controller region */
+ utmi->syscon = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "marvell,system-controller");
+ if (IS_ERR(utmi->syscon)) {
+ dev_err(dev, "Missing UTMI system controller\n");
+ return PTR_ERR(utmi->syscon);
+ }
+
+ /* Get UTMI memory region */
+ utmi->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(utmi->regs))
+ return PTR_ERR(utmi->regs);
+
+ for_each_available_child_of_node(dev->of_node, child) {
+ struct mvebu_cp110_utmi_port *port;
+ struct phy *phy;
+ int ret;
+ u32 port_id;
+
+ ret = of_property_read_u32(child, "reg", &port_id);
+ if ((ret < 0) || (port_id >= UTMI_PHY_PORTS)) {
+ dev_err(dev,
+ "invalid 'reg' property on child %pOF\n",
+ child);
+ continue;
+ }
+
+ port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+ if (!port) {
+ of_node_put(child);
+ return -ENOMEM;
+ }
+
+ port->dr_mode = of_usb_get_dr_mode_by_phy(child, -1);
+ if ((port->dr_mode != USB_DR_MODE_HOST) &&
+ (port->dr_mode != USB_DR_MODE_PERIPHERAL)) {
+ dev_err(&pdev->dev,
+ "Missing dual role setting of the port%d, will use HOST mode\n",
+ port_id);
+ port->dr_mode = USB_DR_MODE_HOST;
+ }
+
+ if (port->dr_mode == USB_DR_MODE_PERIPHERAL) {
+ usb_devices++;
+ if (usb_devices > 1) {
+ dev_err(dev,
+ "Single USB device allowed! Port%d will use HOST mode\n",
+ port_id);
+ port->dr_mode = USB_DR_MODE_HOST;
+ }
+ }
+
+ /* Retrieve PHY capabilities */
+ utmi->ops = &mvebu_cp110_utmi_phy_ops;
+
+ /* Instantiate the PHY */
+ phy = devm_phy_create(dev, child, utmi->ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "Failed to create the UTMI PHY\n");
+ of_node_put(child);
+ return PTR_ERR(phy);
+ }
+
+ port->priv = utmi;
+ port->id = port_id;
+ phy_set_drvdata(phy, port);
+
+ /* Ensure the PHY is powered off */
+ mvebu_cp110_utmi_phy_power_off(phy);
+ }
+
+ dev_set_drvdata(dev, utmi);
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(provider);
+}
+
+static struct platform_driver mvebu_cp110_utmi_driver = {
+ .probe = mvebu_cp110_utmi_phy_probe,
+ .driver = {
+ .name = "mvebu-cp110-utmi-phy",
+ .of_match_table = mvebu_cp110_utmi_of_match,
+ },
+};
+module_platform_driver(mvebu_cp110_utmi_driver);
+
+MODULE_AUTHOR("Konstatin Porotchkin <kostap@marvell.com>");
+MODULE_DESCRIPTION("Marvell Armada CP110 UTMI PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/marvell/phy-mvebu-sata.c b/drivers/phy/marvell/phy-mvebu-sata.c
index 3c01b5dceaae..51a4646e2933 100644
--- a/drivers/phy/marvell/phy-mvebu-sata.c
+++ b/drivers/phy/marvell/phy-mvebu-sata.c
@@ -80,7 +80,6 @@ static const struct phy_ops phy_mvebu_sata_ops = {
static int phy_mvebu_sata_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
- struct resource *res;
struct priv *priv;
struct phy *phy;
@@ -88,8 +87,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(&pdev->dev, res);
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
diff --git a/drivers/phy/marvell/phy-pxa-28nm-hsic.c b/drivers/phy/marvell/phy-pxa-28nm-hsic.c
index ae8370af59c0..c5c100563f55 100644
--- a/drivers/phy/marvell/phy-pxa-28nm-hsic.c
+++ b/drivers/phy/marvell/phy-pxa-28nm-hsic.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/module.h>
@@ -44,15 +45,12 @@ struct mv_hsic_phy {
struct clk *clk;
};
-static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout)
+static int wait_for_reg(void __iomem *reg, u32 mask, u32 ms)
{
- timeout += jiffies;
- while (time_is_after_eq_jiffies(timeout)) {
- if ((readl(reg) & mask) == mask)
- return true;
- msleep(1);
- }
- return false;
+ u32 val;
+
+ return readl_poll_timeout(reg, val, ((val & mask) == mask),
+ 1000, 1000 * ms);
}
static int mv_hsic_phy_init(struct phy *phy)
@@ -60,6 +58,7 @@ static int mv_hsic_phy_init(struct phy *phy)
struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
struct platform_device *pdev = mv_phy->pdev;
void __iomem *base = mv_phy->base;
+ int ret;
clk_prepare_enable(mv_phy->clk);
@@ -75,14 +74,14 @@ static int mv_hsic_phy_init(struct phy *phy)
base + PHY_28NM_HSIC_PLL_CTRL2);
/* Make sure PHY PLL is locked */
- if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2,
- PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) {
+ ret = wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2,
+ PHY_28NM_HSIC_H2S_PLL_LOCK, 100);
+ if (ret) {
dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS.");
clk_disable_unprepare(mv_phy->clk);
- return -ETIMEDOUT;
}
- return 0;
+ return ret;
}
static int mv_hsic_phy_power_on(struct phy *phy)
@@ -91,6 +90,7 @@ static int mv_hsic_phy_power_on(struct phy *phy)
struct platform_device *pdev = mv_phy->pdev;
void __iomem *base = mv_phy->base;
u32 reg;
+ int ret;
reg = readl(base + PHY_28NM_HSIC_CTRL);
/* Avoid SE0 state when resume for some device will take it as reset */
@@ -108,20 +108,20 @@ static int mv_hsic_phy_power_on(struct phy *phy)
*/
/* Make sure PHY Calibration is ready */
- if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL,
- PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) {
+ ret = wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL,
+ PHY_28NM_HSIC_H2S_IMPCAL_DONE, 100);
+ if (ret) {
dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS.");
- return -ETIMEDOUT;
+ return ret;
}
/* Waiting for HSIC connect int*/
- if (!wait_for_reg(base + PHY_28NM_HSIC_INT,
- PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) {
+ ret = wait_for_reg(base + PHY_28NM_HSIC_INT,
+ PHY_28NM_HSIC_CONNECT_INT, 200);
+ if (ret)
dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout.");
- return -ETIMEDOUT;
- }
- return 0;
+ return ret;
}
static int mv_hsic_phy_power_off(struct phy *phy)
@@ -162,7 +162,6 @@ static int mv_hsic_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct mv_hsic_phy *mv_phy;
- struct resource *r;
mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL);
if (!mv_phy)
@@ -176,8 +175,7 @@ static int mv_hsic_phy_probe(struct platform_device *pdev)
return PTR_ERR(mv_phy->clk);
}
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- mv_phy->base = devm_ioremap_resource(&pdev->dev, r);
+ mv_phy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mv_phy->base))
return PTR_ERR(mv_phy->base);
diff --git a/drivers/phy/marvell/phy-pxa-28nm-usb2.c b/drivers/phy/marvell/phy-pxa-28nm-usb2.c
index 9fd881787fa6..0b390b9d2ae1 100644
--- a/drivers/phy/marvell/phy-pxa-28nm-usb2.c
+++ b/drivers/phy/marvell/phy-pxa-28nm-usb2.c
@@ -13,6 +13,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/module.h>
@@ -138,15 +139,12 @@ struct mv_usb2_phy {
struct clk *clk;
};
-static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout)
+static int wait_for_reg(void __iomem *reg, u32 mask, u32 ms)
{
- timeout += jiffies;
- while (time_is_after_eq_jiffies(timeout)) {
- if ((readl(reg) & mask) == mask)
- return true;
- msleep(1);
- }
- return false;
+ u32 val;
+
+ return readl_poll_timeout(reg, val, ((val & mask) == mask),
+ 1000, 1000 * ms);
}
static int mv_usb2_phy_28nm_init(struct phy *phy)
@@ -208,24 +206,23 @@ static int mv_usb2_phy_28nm_init(struct phy *phy)
*/
/* Make sure PHY Calibration is ready */
- if (!wait_for_reg(base + PHY_28NM_CAL_REG,
- PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE,
- HZ / 10)) {
+ ret = wait_for_reg(base + PHY_28NM_CAL_REG,
+ PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE,
+ 100);
+ if (ret) {
dev_warn(&pdev->dev, "USB PHY PLL calibrate not done after 100mS.");
- ret = -ETIMEDOUT;
goto err_clk;
}
- if (!wait_for_reg(base + PHY_28NM_RX_REG1,
- PHY_28NM_RX_SQCAL_DONE, HZ / 10)) {
+ ret = wait_for_reg(base + PHY_28NM_RX_REG1,
+ PHY_28NM_RX_SQCAL_DONE, 100);
+ if (ret) {
dev_warn(&pdev->dev, "USB PHY RX SQ calibrate not done after 100mS.");
- ret = -ETIMEDOUT;
goto err_clk;
}
/* Make sure PHY PLL is ready */
- if (!wait_for_reg(base + PHY_28NM_PLL_REG0,
- PHY_28NM_PLL_READY, HZ / 10)) {
+ ret = wait_for_reg(base + PHY_28NM_PLL_REG0, PHY_28NM_PLL_READY, 100);
+ if (ret) {
dev_warn(&pdev->dev, "PLL_READY not set after 100mS.");
- ret = -ETIMEDOUT;
goto err_clk;
}
@@ -297,7 +294,6 @@ static int mv_usb2_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct mv_usb2_phy *mv_phy;
- struct resource *r;
mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL);
if (!mv_phy)
@@ -311,8 +307,7 @@ static int mv_usb2_phy_probe(struct platform_device *pdev)
return PTR_ERR(mv_phy->clk);
}
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- mv_phy->base = devm_ioremap_resource(&pdev->dev, r);
+ mv_phy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mv_phy->base))
return PTR_ERR(mv_phy->base);
diff --git a/drivers/phy/marvell/phy-pxa-usb.c b/drivers/phy/marvell/phy-pxa-usb.c
index 87ff7550b912..ffe889893ff4 100644
--- a/drivers/phy/marvell/phy-pxa-usb.c
+++ b/drivers/phy/marvell/phy-pxa-usb.c
@@ -286,7 +286,6 @@ MODULE_DEVICE_TABLE(of, pxa_usb_phy_of_match);
static int pxa_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct resource *resource;
struct pxa_usb_phy *pxa_usb_phy;
struct phy_provider *provider;
const struct of_device_id *of_id;
@@ -301,8 +300,7 @@ static int pxa_usb_phy_probe(struct platform_device *pdev)
else
pxa_usb_phy->version = PXA_USB_PHY_MMP2;
- resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pxa_usb_phy->base = devm_ioremap_resource(dev, resource);
+ pxa_usb_phy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pxa_usb_phy->base)) {
dev_err(dev, "failed to remap PHY regs\n");
return PTR_ERR(pxa_usb_phy->base);
diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig
index dee757c957f2..3125ecb5d119 100644
--- a/drivers/phy/mediatek/Kconfig
+++ b/drivers/phy/mediatek/Kconfig
@@ -2,17 +2,29 @@
#
# Phy drivers for Mediatek devices
#
+config PHY_MTK_PCIE
+ tristate "MediaTek PCIe-PHY Driver"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on OF
+ select GENERIC_PHY
+ help
+ Say 'Y' here to add support for MediaTek PCIe PHY driver.
+ This driver create the basic PHY instance and provides initialize
+ callback for PCIe GEN3 port, it supports software efuse
+ initialization.
+
config PHY_MTK_TPHY
tristate "MediaTek T-PHY Driver"
depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on OF
+ depends on OF && OF_ADDRESS
+ depends on HAS_IOMEM
select GENERIC_PHY
help
Say 'Y' here to add support for MediaTek T-PHY driver,
it supports multiple usb2.0, usb3.0 ports, PCIe and
SATA, and meanwhile supports two version T-PHY which have
different banks layout, the T-PHY with shared banks between
- multi-ports is first version, otherwise is second veriosn,
+ multi-ports is first version, otherwise is second version,
so you can easily distinguish them by banks layout.
config PHY_MTK_UFS
@@ -29,9 +41,36 @@ config PHY_MTK_UFS
config PHY_MTK_XSPHY
tristate "MediaTek XS-PHY Driver"
depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on OF
+ depends on OF && OF_ADDRESS
+ depends on HAS_IOMEM
select GENERIC_PHY
help
Enable this to support the SuperSpeedPlus XS-PHY transceiver for
USB3.1 GEN2 controllers on MediaTek chips. The driver supports
multiple USB2.0, USB3.1 GEN2 ports.
+
+config PHY_MTK_HDMI
+ tristate "MediaTek HDMI-PHY Driver"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on COMMON_CLK
+ depends on OF
+ select GENERIC_PHY
+ help
+ Support HDMI PHY for Mediatek SoCs.
+
+config PHY_MTK_MIPI_DSI
+ tristate "MediaTek MIPI-DSI Driver"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on COMMON_CLK
+ depends on OF
+ select GENERIC_PHY
+ help
+ Support MIPI DSI for Mediatek SoCs.
+
+config PHY_MTK_DP
+ tristate "MediaTek DP-PHY Driver"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on OF
+ select GENERIC_PHY
+ help
+ Support DisplayPort PHY for MediaTek SoCs.
diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile
index 08a8e6a97b1e..fb1f8edaffa7 100644
--- a/drivers/phy/mediatek/Makefile
+++ b/drivers/phy/mediatek/Makefile
@@ -3,6 +3,18 @@
# Makefile for the phy drivers.
#
+obj-$(CONFIG_PHY_MTK_DP) += phy-mtk-dp.o
+obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-pcie.o
obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o
obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o
+
+phy-mtk-hdmi-drv-y := phy-mtk-hdmi.o
+phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt2701.o
+phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt8173.o
+obj-$(CONFIG_PHY_MTK_HDMI) += phy-mtk-hdmi-drv.o
+
+phy-mtk-mipi-dsi-drv-y := phy-mtk-mipi-dsi.o
+phy-mtk-mipi-dsi-drv-y += phy-mtk-mipi-dsi-mt8173.o
+phy-mtk-mipi-dsi-drv-y += phy-mtk-mipi-dsi-mt8183.o
+obj-$(CONFIG_PHY_MTK_MIPI_DSI) += phy-mtk-mipi-dsi-drv.o
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
new file mode 100644
index 000000000000..232fd3f1ff1b
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek DisplayPort PHY driver
+ *
+ * Copyright (c) 2022, BayLibre Inc.
+ * Copyright (c) 2022, MediaTek Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define PHY_OFFSET 0x1000
+
+#define MTK_DP_PHY_DIG_PLL_CTL_1 (PHY_OFFSET + 0x14)
+#define TPLL_SSC_EN BIT(3)
+
+#define MTK_DP_PHY_DIG_BIT_RATE (PHY_OFFSET + 0x3C)
+#define BIT_RATE_RBR 0
+#define BIT_RATE_HBR 1
+#define BIT_RATE_HBR2 2
+#define BIT_RATE_HBR3 3
+
+#define MTK_DP_PHY_DIG_SW_RST (PHY_OFFSET + 0x38)
+#define DP_GLB_SW_RST_PHYD BIT(0)
+
+#define MTK_DP_LANE0_DRIVING_PARAM_3 (PHY_OFFSET + 0x138)
+#define MTK_DP_LANE1_DRIVING_PARAM_3 (PHY_OFFSET + 0x238)
+#define MTK_DP_LANE2_DRIVING_PARAM_3 (PHY_OFFSET + 0x338)
+#define MTK_DP_LANE3_DRIVING_PARAM_3 (PHY_OFFSET + 0x438)
+#define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT BIT(4)
+#define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT (BIT(10) | BIT(12))
+#define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT GENMASK(20, 19)
+#define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT GENMASK(29, 29)
+#define DRIVING_PARAM_3_DEFAULT (XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \
+ XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT | \
+ XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT | \
+ XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT)
+
+#define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT GENMASK(4, 3)
+#define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT GENMASK(12, 9)
+#define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT (BIT(18) | BIT(21))
+#define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT GENMASK(29, 29)
+#define DRIVING_PARAM_4_DEFAULT (XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \
+ XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT | \
+ XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT | \
+ XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT)
+
+#define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT (BIT(3) | BIT(5))
+#define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT GENMASK(13, 12)
+#define DRIVING_PARAM_5_DEFAULT (XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \
+ XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT)
+
+#define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT 0
+#define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT GENMASK(10, 10)
+#define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT GENMASK(19, 19)
+#define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT GENMASK(28, 28)
+#define DRIVING_PARAM_6_DEFAULT (XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \
+ XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT | \
+ XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT | \
+ XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT)
+
+#define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT 0
+#define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT GENMASK(10, 9)
+#define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT GENMASK(19, 18)
+#define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT 0
+#define DRIVING_PARAM_7_DEFAULT (XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \
+ XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT | \
+ XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT | \
+ XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT)
+
+#define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT GENMASK(3, 3)
+#define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT 0
+#define DRIVING_PARAM_8_DEFAULT (XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
+ XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
+
+struct mtk_dp_phy {
+ struct regmap *regs;
+};
+
+static int mtk_dp_phy_init(struct phy *phy)
+{
+ struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+ static const u32 driving_params[] = {
+ DRIVING_PARAM_3_DEFAULT,
+ DRIVING_PARAM_4_DEFAULT,
+ DRIVING_PARAM_5_DEFAULT,
+ DRIVING_PARAM_6_DEFAULT,
+ DRIVING_PARAM_7_DEFAULT,
+ DRIVING_PARAM_8_DEFAULT
+ };
+
+ regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
+ driving_params, ARRAY_SIZE(driving_params));
+ regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
+ driving_params, ARRAY_SIZE(driving_params));
+ regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
+ driving_params, ARRAY_SIZE(driving_params));
+ regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
+ driving_params, ARRAY_SIZE(driving_params));
+
+ return 0;
+}
+
+static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+ struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+ u32 val;
+
+ if (opts->dp.set_rate) {
+ switch (opts->dp.link_rate) {
+ default:
+ dev_err(&phy->dev,
+ "Implementation error, unknown linkrate %x\n",
+ opts->dp.link_rate);
+ return -EINVAL;
+ case 1620:
+ val = BIT_RATE_RBR;
+ break;
+ case 2700:
+ val = BIT_RATE_HBR;
+ break;
+ case 5400:
+ val = BIT_RATE_HBR2;
+ break;
+ case 8100:
+ val = BIT_RATE_HBR3;
+ break;
+ }
+ regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE, val);
+ }
+
+ regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
+ TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0);
+
+ return 0;
+}
+
+static int mtk_dp_phy_reset(struct phy *phy)
+{
+ struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+
+ regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+ DP_GLB_SW_RST_PHYD, 0);
+ usleep_range(50, 200);
+ regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
+ DP_GLB_SW_RST_PHYD, 1);
+
+ return 0;
+}
+
+static const struct phy_ops mtk_dp_phy_dev_ops = {
+ .init = mtk_dp_phy_init,
+ .configure = mtk_dp_phy_configure,
+ .reset = mtk_dp_phy_reset,
+ .owner = THIS_MODULE,
+};
+
+static int mtk_dp_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_dp_phy *dp_phy;
+ struct phy *phy;
+ struct regmap *regs;
+
+ regs = *(struct regmap **)dev->platform_data;
+ if (!regs)
+ return dev_err_probe(dev, EINVAL,
+ "No data passed, requires struct regmap**\n");
+
+ dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
+ if (!dp_phy)
+ return -ENOMEM;
+
+ dp_phy->regs = regs;
+ phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
+ if (IS_ERR(phy))
+ return dev_err_probe(dev, PTR_ERR(phy),
+ "Failed to create DP PHY\n");
+
+ phy_set_drvdata(phy, dp_phy);
+ if (!dev->of_node)
+ phy_create_lookup(phy, "dp", dev_name(dev));
+
+ return 0;
+}
+
+static struct platform_driver mtk_dp_phy_driver = {
+ .probe = mtk_dp_phy_probe,
+ .driver = {
+ .name = "mediatek-dp-phy",
+ },
+};
+module_platform_driver(mtk_dp_phy_driver);
+
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_DESCRIPTION("MediaTek DP PHY Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c b/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c
new file mode 100644
index 000000000000..e51b2d13eab4
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Chunhui Dai <chunhui.dai@mediatek.com>
+ */
+
+#include "phy-mtk-hdmi.h"
+#include "phy-mtk-io.h"
+
+#define HDMI_CON0 0x00
+#define RG_HDMITX_DRV_IBIAS_MASK GENMASK(5, 0)
+#define RG_HDMITX_EN_SER_MASK GENMASK(15, 12)
+#define RG_HDMITX_EN_SLDO_MASK GENMASK(19, 16)
+#define RG_HDMITX_EN_PRED_MASK GENMASK(23, 20)
+#define RG_HDMITX_EN_IMP_MASK GENMASK(27, 24)
+#define RG_HDMITX_EN_DRV_MASK GENMASK(31, 28)
+
+#define HDMI_CON1 0x04
+#define RG_HDMITX_PRED_IBIAS_MASK GENMASK(21, 18)
+#define RG_HDMITX_PRED_IMP BIT(22)
+#define RG_HDMITX_DRV_IMP_MASK GENMASK(31, 26)
+
+#define HDMI_CON2 0x08
+#define RG_HDMITX_EN_TX_CKLDO BIT(0)
+#define RG_HDMITX_EN_TX_POSDIV BIT(1)
+#define RG_HDMITX_TX_POSDIV_MASK GENMASK(4, 3)
+#define RG_HDMITX_EN_MBIAS BIT(6)
+#define RG_HDMITX_MBIAS_LPF_EN BIT(7)
+
+#define HDMI_CON4 0x10
+#define RG_HDMITX_RESERVE_MASK GENMASK(31, 0)
+
+#define HDMI_CON6 0x18
+#define RG_HTPLL_BR_MASK GENMASK(1, 0)
+#define RG_HTPLL_BC_MASK GENMASK(3, 2)
+#define RG_HTPLL_BP_MASK GENMASK(7, 4)
+#define RG_HTPLL_IR_MASK GENMASK(11, 8)
+#define RG_HTPLL_IC_MASK GENMASK(15, 12)
+#define RG_HTPLL_POSDIV_MASK GENMASK(17, 16)
+#define RG_HTPLL_PREDIV_MASK GENMASK(19, 18)
+#define RG_HTPLL_FBKSEL_MASK GENMASK(21, 20)
+#define RG_HTPLL_RLH_EN BIT(22)
+#define RG_HTPLL_FBKDIV_MASK GENMASK(30, 24)
+#define RG_HTPLL_EN BIT(31)
+
+#define HDMI_CON7 0x1c
+#define RG_HTPLL_AUTOK_EN BIT(23)
+#define RG_HTPLL_DIVEN_MASK GENMASK(30, 28)
+
+static int mtk_hdmi_pll_prepare(struct clk_hw *hw)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+ void __iomem *base = hdmi_phy->regs;
+
+ mtk_phy_set_bits(base + HDMI_CON7, RG_HTPLL_AUTOK_EN);
+ mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_RLH_EN);
+ mtk_phy_set_bits(base + HDMI_CON6, RG_HTPLL_POSDIV_MASK);
+ mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_EN_MBIAS);
+ usleep_range(80, 100);
+ mtk_phy_set_bits(base + HDMI_CON6, RG_HTPLL_EN);
+ mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_EN_TX_CKLDO);
+ mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_SLDO_MASK);
+ usleep_range(80, 100);
+ mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_MBIAS_LPF_EN);
+ mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_SER_MASK);
+ mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_PRED_MASK);
+ mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_DRV_MASK);
+ usleep_range(80, 100);
+ return 0;
+}
+
+static void mtk_hdmi_pll_unprepare(struct clk_hw *hw)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+ void __iomem *base = hdmi_phy->regs;
+
+ mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_DRV_MASK);
+ mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_PRED_MASK);
+ mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_SER_MASK);
+ mtk_phy_clear_bits(base + HDMI_CON2, RG_HDMITX_MBIAS_LPF_EN);
+ usleep_range(80, 100);
+ mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_SLDO_MASK);
+ mtk_phy_clear_bits(base + HDMI_CON2, RG_HDMITX_EN_TX_CKLDO);
+ mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_EN);
+ usleep_range(80, 100);
+ mtk_phy_clear_bits(base + HDMI_CON2, RG_HDMITX_EN_MBIAS);
+ mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_POSDIV_MASK);
+ mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_RLH_EN);
+ mtk_phy_clear_bits(base + HDMI_CON7, RG_HTPLL_AUTOK_EN);
+ usleep_range(80, 100);
+}
+
+static long mtk_hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ return rate;
+}
+
+static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+ void __iomem *base = hdmi_phy->regs;
+ u32 pos_div;
+
+ if (rate <= 64000000)
+ pos_div = 3;
+ else if (rate <= 128000000)
+ pos_div = 2;
+ else
+ pos_div = 1;
+
+ mtk_phy_set_bits(base + HDMI_CON6, RG_HTPLL_PREDIV_MASK);
+ mtk_phy_set_bits(base + HDMI_CON6, RG_HTPLL_POSDIV_MASK);
+ mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_EN_TX_POSDIV);
+ mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_IC_MASK, 0x1);
+ mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_IR_MASK, 0x1);
+ mtk_phy_update_field(base + HDMI_CON2, RG_HDMITX_TX_POSDIV_MASK, pos_div);
+ mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_FBKSEL_MASK, 1);
+ mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_FBKDIV_MASK, 19);
+ mtk_phy_update_field(base + HDMI_CON7, RG_HTPLL_DIVEN_MASK, 0x2);
+ mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_BP_MASK, 0xc);
+ mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_BC_MASK, 0x2);
+ mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_BR_MASK, 0x1);
+
+ mtk_phy_clear_bits(base + HDMI_CON1, RG_HDMITX_PRED_IMP);
+ mtk_phy_update_field(base + HDMI_CON1, RG_HDMITX_PRED_IBIAS_MASK, 0x3);
+ mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_IMP_MASK);
+ mtk_phy_update_field(base + HDMI_CON1, RG_HDMITX_DRV_IMP_MASK, 0x28);
+ mtk_phy_update_field(base + HDMI_CON4, RG_HDMITX_RESERVE_MASK, 0x28);
+ mtk_phy_update_field(base + HDMI_CON0, RG_HDMITX_DRV_IBIAS_MASK, 0xa);
+ return 0;
+}
+
+static unsigned long mtk_hdmi_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+ unsigned long out_rate, val;
+ u32 tmp;
+
+ tmp = readl(hdmi_phy->regs + HDMI_CON6);
+ val = FIELD_GET(RG_HTPLL_PREDIV_MASK, tmp);
+ switch (val) {
+ case 0x00:
+ out_rate = parent_rate;
+ break;
+ case 0x01:
+ out_rate = parent_rate / 2;
+ break;
+ default:
+ out_rate = parent_rate / 4;
+ break;
+ }
+
+ val = FIELD_GET(RG_HTPLL_FBKDIV_MASK, tmp);
+ out_rate *= (val + 1) * 2;
+
+ tmp = readl(hdmi_phy->regs + HDMI_CON2);
+ val = FIELD_GET(RG_HDMITX_TX_POSDIV_MASK, tmp);
+ out_rate >>= val;
+
+ if (tmp & RG_HDMITX_EN_TX_POSDIV)
+ out_rate /= 5;
+
+ return out_rate;
+}
+
+static const struct clk_ops mtk_hdmi_phy_pll_ops = {
+ .prepare = mtk_hdmi_pll_prepare,
+ .unprepare = mtk_hdmi_pll_unprepare,
+ .set_rate = mtk_hdmi_pll_set_rate,
+ .round_rate = mtk_hdmi_pll_round_rate,
+ .recalc_rate = mtk_hdmi_pll_recalc_rate,
+};
+
+static void mtk_hdmi_phy_enable_tmds(struct mtk_hdmi_phy *hdmi_phy)
+{
+ void __iomem *base = hdmi_phy->regs;
+
+ mtk_phy_set_bits(base + HDMI_CON7, RG_HTPLL_AUTOK_EN);
+ mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_RLH_EN);
+ mtk_phy_set_bits(base + HDMI_CON6, RG_HTPLL_POSDIV_MASK);
+ mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_EN_MBIAS);
+ usleep_range(80, 100);
+ mtk_phy_set_bits(base + HDMI_CON6, RG_HTPLL_EN);
+ mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_EN_TX_CKLDO);
+ mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_SLDO_MASK);
+ usleep_range(80, 100);
+ mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_MBIAS_LPF_EN);
+ mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_SER_MASK);
+ mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_PRED_MASK);
+ mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_DRV_MASK);
+ usleep_range(80, 100);
+}
+
+static void mtk_hdmi_phy_disable_tmds(struct mtk_hdmi_phy *hdmi_phy)
+{
+ void __iomem *base = hdmi_phy->regs;
+
+ mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_DRV_MASK);
+ mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_PRED_MASK);
+ mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_SER_MASK);
+ mtk_phy_clear_bits(base + HDMI_CON2, RG_HDMITX_MBIAS_LPF_EN);
+ usleep_range(80, 100);
+ mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_SLDO_MASK);
+ mtk_phy_clear_bits(base + HDMI_CON2, RG_HDMITX_EN_TX_CKLDO);
+ mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_EN);
+ usleep_range(80, 100);
+ mtk_phy_clear_bits(base + HDMI_CON2, RG_HDMITX_EN_MBIAS);
+ mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_POSDIV_MASK);
+ mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_RLH_EN);
+ mtk_phy_clear_bits(base + HDMI_CON7, RG_HTPLL_AUTOK_EN);
+ usleep_range(80, 100);
+}
+
+struct mtk_hdmi_phy_conf mtk_hdmi_phy_2701_conf = {
+ .flags = CLK_SET_RATE_GATE,
+ .pll_default_off = true,
+ .hdmi_phy_clk_ops = &mtk_hdmi_phy_pll_ops,
+ .hdmi_phy_enable_tmds = mtk_hdmi_phy_enable_tmds,
+ .hdmi_phy_disable_tmds = mtk_hdmi_phy_disable_tmds,
+};
+
+MODULE_AUTHOR("Chunhui Dai <chunhui.dai@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek HDMI PHY Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/mediatek/phy-mtk-hdmi-mt8173.c b/drivers/phy/mediatek/phy-mtk-hdmi-mt8173.c
new file mode 100644
index 000000000000..d04758396046
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-hdmi-mt8173.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Jie Qiu <jie.qiu@mediatek.com>
+ */
+
+#include "phy-mtk-hdmi.h"
+#include "phy-mtk-io.h"
+
+#define HDMI_CON0 0x00
+#define RG_HDMITX_PLL_EN BIT(31)
+#define RG_HDMITX_PLL_FBKDIV GENMASK(30, 24)
+#define RG_HDMITX_PLL_FBKSEL GENMASK(23, 22)
+#define RG_HDMITX_PLL_PREDIV GENMASK(21, 20)
+#define RG_HDMITX_PLL_POSDIV GENMASK(19, 18)
+#define RG_HDMITX_PLL_RST_DLY GENMASK(17, 16)
+#define RG_HDMITX_PLL_IR GENMASK(15, 12)
+#define RG_HDMITX_PLL_IC GENMASK(11, 8)
+#define RG_HDMITX_PLL_BP GENMASK(7, 4)
+#define RG_HDMITX_PLL_BR GENMASK(3, 2)
+#define RG_HDMITX_PLL_BC GENMASK(1, 0)
+#define HDMI_CON1 0x04
+#define RG_HDMITX_PLL_DIVEN GENMASK(31, 29)
+#define RG_HDMITX_PLL_AUTOK_EN BIT(28)
+#define RG_HDMITX_PLL_AUTOK_KF GENMASK(27, 26)
+#define RG_HDMITX_PLL_AUTOK_KS GENMASK(25, 24)
+#define RG_HDMITX_PLL_AUTOK_LOAD BIT(23)
+#define RG_HDMITX_PLL_BAND GENMASK(21, 16)
+#define RG_HDMITX_PLL_REF_SEL BIT(15)
+#define RG_HDMITX_PLL_BIAS_EN BIT(14)
+#define RG_HDMITX_PLL_BIAS_LPF_EN BIT(13)
+#define RG_HDMITX_PLL_TXDIV_EN BIT(12)
+#define RG_HDMITX_PLL_TXDIV GENMASK(11, 10)
+#define RG_HDMITX_PLL_LVROD_EN BIT(9)
+#define RG_HDMITX_PLL_MONVC_EN BIT(8)
+#define RG_HDMITX_PLL_MONCK_EN BIT(7)
+#define RG_HDMITX_PLL_MONREF_EN BIT(6)
+#define RG_HDMITX_PLL_TST_EN BIT(5)
+#define RG_HDMITX_PLL_TST_CK_EN BIT(4)
+#define RG_HDMITX_PLL_TST_SEL GENMASK(3, 0)
+#define HDMI_CON2 0x08
+#define RGS_HDMITX_PLL_AUTOK_BAND GENMASK(14, 8)
+#define RGS_HDMITX_PLL_AUTOK_FAIL BIT(1)
+#define RG_HDMITX_EN_TX_CKLDO BIT(0)
+#define HDMI_CON3 0x0c
+#define RG_HDMITX_SER_EN GENMASK(31, 28)
+#define RG_HDMITX_PRD_EN GENMASK(27, 24)
+#define RG_HDMITX_PRD_IMP_EN GENMASK(23, 20)
+#define RG_HDMITX_DRV_EN GENMASK(19, 16)
+#define RG_HDMITX_DRV_IMP_EN GENMASK(15, 12)
+#define RG_HDMITX_MHLCK_FORCE BIT(10)
+#define RG_HDMITX_MHLCK_PPIX_EN BIT(9)
+#define RG_HDMITX_MHLCK_EN BIT(8)
+#define RG_HDMITX_SER_DIN_SEL GENMASK(7, 4)
+#define RG_HDMITX_SER_5T1_BIST_EN BIT(3)
+#define RG_HDMITX_SER_BIST_TOG BIT(2)
+#define RG_HDMITX_SER_DIN_TOG BIT(1)
+#define RG_HDMITX_SER_CLKDIG_INV BIT(0)
+#define HDMI_CON4 0x10
+#define RG_HDMITX_PRD_IBIAS_CLK GENMASK(27, 24)
+#define RG_HDMITX_PRD_IBIAS_D2 GENMASK(19, 16)
+#define RG_HDMITX_PRD_IBIAS_D1 GENMASK(11, 8)
+#define RG_HDMITX_PRD_IBIAS_D0 GENMASK(3, 0)
+#define HDMI_CON5 0x14
+#define RG_HDMITX_DRV_IBIAS_CLK GENMASK(29, 24)
+#define RG_HDMITX_DRV_IBIAS_D2 GENMASK(21, 16)
+#define RG_HDMITX_DRV_IBIAS_D1 GENMASK(13, 8)
+#define RG_HDMITX_DRV_IBIAS_D0 GENMASK(5, 0)
+#define HDMI_CON6 0x18
+#define RG_HDMITX_DRV_IMP_CLK GENMASK(29, 24)
+#define RG_HDMITX_DRV_IMP_D2 GENMASK(21, 16)
+#define RG_HDMITX_DRV_IMP_D1 GENMASK(13, 8)
+#define RG_HDMITX_DRV_IMP_D0 GENMASK(5, 0)
+#define HDMI_CON7 0x1c
+#define RG_HDMITX_MHLCK_DRV_IBIAS GENMASK(31, 27)
+#define RG_HDMITX_SER_DIN GENMASK(25, 16)
+#define RG_HDMITX_CHLDC_TST GENMASK(15, 12)
+#define RG_HDMITX_CHLCK_TST GENMASK(11, 8)
+#define RG_HDMITX_RESERVE GENMASK(7, 0)
+#define HDMI_CON8 0x20
+#define RGS_HDMITX_2T1_LEV GENMASK(19, 16)
+#define RGS_HDMITX_2T1_EDG GENMASK(15, 12)
+#define RGS_HDMITX_5T1_LEV GENMASK(11, 8)
+#define RGS_HDMITX_5T1_EDG GENMASK(7, 4)
+#define RGS_HDMITX_PLUG_TST BIT(0)
+
+static int mtk_hdmi_pll_prepare(struct clk_hw *hw)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+ void __iomem *base = hdmi_phy->regs;
+
+ mtk_phy_set_bits(base + HDMI_CON1, RG_HDMITX_PLL_AUTOK_EN);
+ mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_PLL_POSDIV);
+ mtk_phy_clear_bits(base + HDMI_CON3, RG_HDMITX_MHLCK_EN);
+ mtk_phy_set_bits(base + HDMI_CON1, RG_HDMITX_PLL_BIAS_EN);
+ usleep_range(100, 150);
+ mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_PLL_EN);
+ usleep_range(100, 150);
+ mtk_phy_set_bits(base + HDMI_CON1, RG_HDMITX_PLL_BIAS_LPF_EN);
+ mtk_phy_set_bits(base + HDMI_CON1, RG_HDMITX_PLL_TXDIV_EN);
+
+ return 0;
+}
+
+static void mtk_hdmi_pll_unprepare(struct clk_hw *hw)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+ void __iomem *base = hdmi_phy->regs;
+
+ mtk_phy_clear_bits(base + HDMI_CON1, RG_HDMITX_PLL_TXDIV_EN);
+ mtk_phy_clear_bits(base + HDMI_CON1, RG_HDMITX_PLL_BIAS_LPF_EN);
+ usleep_range(100, 150);
+ mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_PLL_EN);
+ usleep_range(100, 150);
+ mtk_phy_clear_bits(base + HDMI_CON1, RG_HDMITX_PLL_BIAS_EN);
+ mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_PLL_POSDIV);
+ mtk_phy_clear_bits(base + HDMI_CON1, RG_HDMITX_PLL_AUTOK_EN);
+ usleep_range(100, 150);
+}
+
+static long mtk_hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+
+ hdmi_phy->pll_rate = rate;
+ if (rate <= 74250000)
+ *parent_rate = rate;
+ else
+ *parent_rate = rate / 2;
+
+ return rate;
+}
+
+static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+ void __iomem *base = hdmi_phy->regs;
+ unsigned int pre_div;
+ unsigned int div;
+ unsigned int pre_ibias;
+ unsigned int hdmi_ibias;
+ unsigned int imp_en;
+
+ dev_dbg(hdmi_phy->dev, "%s: %lu Hz, parent: %lu Hz\n", __func__,
+ rate, parent_rate);
+
+ if (rate <= 27000000) {
+ pre_div = 0;
+ div = 3;
+ } else if (rate <= 74250000) {
+ pre_div = 1;
+ div = 2;
+ } else {
+ pre_div = 1;
+ div = 1;
+ }
+
+ mtk_phy_update_field(base + HDMI_CON0, RG_HDMITX_PLL_PREDIV, pre_div);
+ mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_PLL_POSDIV);
+ mtk_phy_update_bits(base + HDMI_CON0,
+ RG_HDMITX_PLL_IC | RG_HDMITX_PLL_IR,
+ FIELD_PREP(RG_HDMITX_PLL_IC, 0x1) |
+ FIELD_PREP(RG_HDMITX_PLL_IR, 0x1));
+ mtk_phy_update_field(base + HDMI_CON1, RG_HDMITX_PLL_TXDIV, div);
+ mtk_phy_update_bits(base + HDMI_CON0,
+ RG_HDMITX_PLL_FBKSEL | RG_HDMITX_PLL_FBKDIV,
+ FIELD_PREP(RG_HDMITX_PLL_FBKSEL, 0x1) |
+ FIELD_PREP(RG_HDMITX_PLL_FBKDIV, 19));
+ mtk_phy_update_field(base + HDMI_CON1, RG_HDMITX_PLL_DIVEN, 0x2);
+ mtk_phy_update_bits(base + HDMI_CON0,
+ RG_HDMITX_PLL_BP | RG_HDMITX_PLL_BC |
+ RG_HDMITX_PLL_BR,
+ FIELD_PREP(RG_HDMITX_PLL_BP, 0xc) |
+ FIELD_PREP(RG_HDMITX_PLL_BC, 0x2) |
+ FIELD_PREP(RG_HDMITX_PLL_BR, 0x1));
+ if (rate < 165000000) {
+ mtk_phy_clear_bits(base + HDMI_CON3, RG_HDMITX_PRD_IMP_EN);
+ pre_ibias = 0x3;
+ imp_en = 0x0;
+ hdmi_ibias = hdmi_phy->ibias;
+ } else {
+ mtk_phy_set_bits(base + HDMI_CON3, RG_HDMITX_PRD_IMP_EN);
+ pre_ibias = 0x6;
+ imp_en = 0xf;
+ hdmi_ibias = hdmi_phy->ibias_up;
+ }
+ mtk_phy_update_bits(base + HDMI_CON4,
+ RG_HDMITX_PRD_IBIAS_CLK | RG_HDMITX_PRD_IBIAS_D2 |
+ RG_HDMITX_PRD_IBIAS_D1 | RG_HDMITX_PRD_IBIAS_D0,
+ FIELD_PREP(RG_HDMITX_PRD_IBIAS_CLK, pre_ibias) |
+ FIELD_PREP(RG_HDMITX_PRD_IBIAS_D2, pre_ibias) |
+ FIELD_PREP(RG_HDMITX_PRD_IBIAS_D1, pre_ibias) |
+ FIELD_PREP(RG_HDMITX_PRD_IBIAS_D0, pre_ibias));
+ mtk_phy_update_field(base + HDMI_CON3, RG_HDMITX_DRV_IMP_EN, imp_en);
+ mtk_phy_update_bits(base + HDMI_CON6,
+ RG_HDMITX_DRV_IMP_CLK | RG_HDMITX_DRV_IMP_D2 |
+ RG_HDMITX_DRV_IMP_D1 | RG_HDMITX_DRV_IMP_D0,
+ FIELD_PREP(RG_HDMITX_DRV_IMP_CLK, hdmi_phy->drv_imp_clk) |
+ FIELD_PREP(RG_HDMITX_DRV_IMP_D2, hdmi_phy->drv_imp_d2) |
+ FIELD_PREP(RG_HDMITX_DRV_IMP_D1, hdmi_phy->drv_imp_d1) |
+ FIELD_PREP(RG_HDMITX_DRV_IMP_D0, hdmi_phy->drv_imp_d0));
+ mtk_phy_update_bits(base + HDMI_CON5,
+ RG_HDMITX_DRV_IBIAS_CLK | RG_HDMITX_DRV_IBIAS_D2 |
+ RG_HDMITX_DRV_IBIAS_D1 | RG_HDMITX_DRV_IBIAS_D0,
+ FIELD_PREP(RG_HDMITX_DRV_IBIAS_CLK, hdmi_ibias) |
+ FIELD_PREP(RG_HDMITX_DRV_IBIAS_D2, hdmi_ibias) |
+ FIELD_PREP(RG_HDMITX_DRV_IBIAS_D1, hdmi_ibias) |
+ FIELD_PREP(RG_HDMITX_DRV_IBIAS_D0, hdmi_ibias));
+ return 0;
+}
+
+static unsigned long mtk_hdmi_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
+
+ return hdmi_phy->pll_rate;
+}
+
+static const struct clk_ops mtk_hdmi_phy_pll_ops = {
+ .prepare = mtk_hdmi_pll_prepare,
+ .unprepare = mtk_hdmi_pll_unprepare,
+ .set_rate = mtk_hdmi_pll_set_rate,
+ .round_rate = mtk_hdmi_pll_round_rate,
+ .recalc_rate = mtk_hdmi_pll_recalc_rate,
+};
+
+static void mtk_hdmi_phy_enable_tmds(struct mtk_hdmi_phy *hdmi_phy)
+{
+ mtk_phy_set_bits(hdmi_phy->regs + HDMI_CON3,
+ RG_HDMITX_SER_EN | RG_HDMITX_PRD_EN |
+ RG_HDMITX_DRV_EN);
+ usleep_range(100, 150);
+}
+
+static void mtk_hdmi_phy_disable_tmds(struct mtk_hdmi_phy *hdmi_phy)
+{
+ mtk_phy_clear_bits(hdmi_phy->regs + HDMI_CON3,
+ RG_HDMITX_DRV_EN | RG_HDMITX_PRD_EN |
+ RG_HDMITX_SER_EN);
+}
+
+struct mtk_hdmi_phy_conf mtk_hdmi_phy_8173_conf = {
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
+ .hdmi_phy_clk_ops = &mtk_hdmi_phy_pll_ops,
+ .hdmi_phy_enable_tmds = mtk_hdmi_phy_enable_tmds,
+ .hdmi_phy_disable_tmds = mtk_hdmi_phy_disable_tmds,
+};
+
+MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek MT8173 HDMI PHY Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/mediatek/phy-mtk-hdmi.c b/drivers/phy/mediatek/phy-mtk-hdmi.c
new file mode 100644
index 000000000000..b16d437d6721
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-hdmi.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Jie Qiu <jie.qiu@mediatek.com>
+ */
+
+#include "phy-mtk-hdmi.h"
+
+static int mtk_hdmi_phy_power_on(struct phy *phy);
+static int mtk_hdmi_phy_power_off(struct phy *phy);
+
+static const struct phy_ops mtk_hdmi_phy_dev_ops = {
+ .power_on = mtk_hdmi_phy_power_on,
+ .power_off = mtk_hdmi_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+inline struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw)
+{
+ return container_of(hw, struct mtk_hdmi_phy, pll_hw);
+}
+
+static int mtk_hdmi_phy_power_on(struct phy *phy)
+{
+ struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy);
+ int ret;
+
+ ret = clk_prepare_enable(hdmi_phy->pll);
+ if (ret < 0)
+ return ret;
+
+ hdmi_phy->conf->hdmi_phy_enable_tmds(hdmi_phy);
+ return 0;
+}
+
+static int mtk_hdmi_phy_power_off(struct phy *phy)
+{
+ struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy);
+
+ hdmi_phy->conf->hdmi_phy_disable_tmds(hdmi_phy);
+ clk_disable_unprepare(hdmi_phy->pll);
+
+ return 0;
+}
+
+static const struct phy_ops *
+mtk_hdmi_phy_dev_get_ops(const struct mtk_hdmi_phy *hdmi_phy)
+{
+ if (hdmi_phy && hdmi_phy->conf &&
+ hdmi_phy->conf->hdmi_phy_enable_tmds &&
+ hdmi_phy->conf->hdmi_phy_disable_tmds)
+ return &mtk_hdmi_phy_dev_ops;
+
+ if (hdmi_phy)
+ dev_err(hdmi_phy->dev, "Failed to get dev ops of phy\n");
+ return NULL;
+}
+
+static void mtk_hdmi_phy_clk_get_data(struct mtk_hdmi_phy *hdmi_phy,
+ struct clk_init_data *clk_init)
+{
+ clk_init->flags = hdmi_phy->conf->flags;
+ clk_init->ops = hdmi_phy->conf->hdmi_phy_clk_ops;
+}
+
+static int mtk_hdmi_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_hdmi_phy *hdmi_phy;
+ struct clk *ref_clk;
+ const char *ref_clk_name;
+ struct clk_init_data clk_init = {
+ .num_parents = 1,
+ .parent_names = (const char * const *)&ref_clk_name,
+ };
+
+ struct phy *phy;
+ struct phy_provider *phy_provider;
+ int ret;
+
+ hdmi_phy = devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL);
+ if (!hdmi_phy)
+ return -ENOMEM;
+
+ hdmi_phy->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hdmi_phy->regs))
+ return PTR_ERR(hdmi_phy->regs);
+
+ ref_clk = devm_clk_get(dev, "pll_ref");
+ if (IS_ERR(ref_clk))
+ return dev_err_probe(dev, PTR_ERR(ref_clk),
+ "Failed to get PLL reference clock\n");
+
+ ref_clk_name = __clk_get_name(ref_clk);
+
+ ret = of_property_read_string(dev->of_node, "clock-output-names",
+ &clk_init.name);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read clock-output-names\n");
+
+ hdmi_phy->dev = dev;
+ hdmi_phy->conf =
+ (struct mtk_hdmi_phy_conf *)of_device_get_match_data(dev);
+ mtk_hdmi_phy_clk_get_data(hdmi_phy, &clk_init);
+ hdmi_phy->pll_hw.init = &clk_init;
+ hdmi_phy->pll = devm_clk_register(dev, &hdmi_phy->pll_hw);
+ if (IS_ERR(hdmi_phy->pll))
+ return dev_err_probe(dev, PTR_ERR(hdmi_phy->pll),
+ "Failed to register PLL\n");
+
+ ret = of_property_read_u32(dev->of_node, "mediatek,ibias",
+ &hdmi_phy->ibias);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get ibias\n");
+
+ ret = of_property_read_u32(dev->of_node, "mediatek,ibias_up",
+ &hdmi_phy->ibias_up);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get ibias_up\n");
+
+ dev_info(dev, "Using default TX DRV impedance: 4.2k/36\n");
+ hdmi_phy->drv_imp_clk = 0x30;
+ hdmi_phy->drv_imp_d2 = 0x30;
+ hdmi_phy->drv_imp_d1 = 0x30;
+ hdmi_phy->drv_imp_d0 = 0x30;
+
+ phy = devm_phy_create(dev, NULL, mtk_hdmi_phy_dev_get_ops(hdmi_phy));
+ if (IS_ERR(phy))
+ return dev_err_probe(dev, PTR_ERR(phy), "Cannot create HDMI PHY\n");
+
+ phy_set_drvdata(phy, hdmi_phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return dev_err_probe(dev, PTR_ERR(phy_provider),
+ "Failed to register HDMI PHY\n");
+
+ if (hdmi_phy->conf->pll_default_off)
+ hdmi_phy->conf->hdmi_phy_disable_tmds(hdmi_phy);
+
+ return of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
+ hdmi_phy->pll);
+}
+
+static const struct of_device_id mtk_hdmi_phy_match[] = {
+ { .compatible = "mediatek,mt2701-hdmi-phy",
+ .data = &mtk_hdmi_phy_2701_conf,
+ },
+ { .compatible = "mediatek,mt8173-hdmi-phy",
+ .data = &mtk_hdmi_phy_8173_conf,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mtk_hdmi_phy_match);
+
+static struct platform_driver mtk_hdmi_phy_driver = {
+ .probe = mtk_hdmi_phy_probe,
+ .driver = {
+ .name = "mediatek-hdmi-phy",
+ .of_match_table = mtk_hdmi_phy_match,
+ },
+};
+module_platform_driver(mtk_hdmi_phy_driver);
+
+MODULE_DESCRIPTION("MediaTek HDMI PHY Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/mediatek/phy-mtk-hdmi.h b/drivers/phy/mediatek/phy-mtk-hdmi.h
new file mode 100644
index 000000000000..c7fa65cff989
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-hdmi.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Chunhui Dai <chunhui.dai@mediatek.com>
+ */
+
+#ifndef _MTK_HDMI_PHY_H
+#define _MTK_HDMI_PHY_H
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.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/types.h>
+
+struct mtk_hdmi_phy;
+
+struct mtk_hdmi_phy_conf {
+ unsigned long flags;
+ bool pll_default_off;
+ const struct clk_ops *hdmi_phy_clk_ops;
+ void (*hdmi_phy_enable_tmds)(struct mtk_hdmi_phy *hdmi_phy);
+ void (*hdmi_phy_disable_tmds)(struct mtk_hdmi_phy *hdmi_phy);
+};
+
+struct mtk_hdmi_phy {
+ void __iomem *regs;
+ struct device *dev;
+ struct mtk_hdmi_phy_conf *conf;
+ struct clk *pll;
+ struct clk_hw pll_hw;
+ unsigned long pll_rate;
+ unsigned char drv_imp_clk;
+ unsigned char drv_imp_d2;
+ unsigned char drv_imp_d1;
+ unsigned char drv_imp_d0;
+ unsigned int ibias;
+ unsigned int ibias_up;
+};
+
+struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw);
+
+extern struct mtk_hdmi_phy_conf mtk_hdmi_phy_8173_conf;
+extern struct mtk_hdmi_phy_conf mtk_hdmi_phy_2701_conf;
+
+#endif /* _MTK_HDMI_PHY_H */
diff --git a/drivers/phy/mediatek/phy-mtk-io.h b/drivers/phy/mediatek/phy-mtk-io.h
new file mode 100644
index 000000000000..d20ad5e5be81
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-io.h
@@ -0,0 +1,46 @@
+/* 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/bitfield.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);
+}
+
+/* field @mask shall be constant and continuous */
+#define mtk_phy_update_field(reg, mask, val) \
+({ \
+ typeof(mask) mask_ = (mask); \
+ mtk_phy_update_bits(reg, mask_, FIELD_PREP(mask_, val)); \
+})
+
+#endif
diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8173.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8173.c
new file mode 100644
index 000000000000..673cb0f08959
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8173.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: jitao.shi <jitao.shi@mediatek.com>
+ */
+
+#include "phy-mtk-io.h"
+#include "phy-mtk-mipi-dsi.h"
+
+#define MIPITX_DSI_CON 0x00
+#define RG_DSI_LDOCORE_EN BIT(0)
+#define RG_DSI_CKG_LDOOUT_EN BIT(1)
+#define RG_DSI_BCLK_SEL GENMASK(3, 2)
+#define RG_DSI_LD_IDX_SEL GENMASK(6, 4)
+#define RG_DSI_PHYCLK_SEL GENMASK(9, 8)
+#define RG_DSI_DSICLK_FREQ_SEL BIT(10)
+#define RG_DSI_LPTX_CLMP_EN BIT(11)
+
+#define MIPITX_DSI_CLOCK_LANE 0x04
+#define MIPITX_DSI_DATA_LANE0 0x08
+#define MIPITX_DSI_DATA_LANE1 0x0c
+#define MIPITX_DSI_DATA_LANE2 0x10
+#define MIPITX_DSI_DATA_LANE3 0x14
+#define RG_DSI_LNTx_LDOOUT_EN BIT(0)
+#define RG_DSI_LNTx_CKLANE_EN BIT(1)
+#define RG_DSI_LNTx_LPTX_IPLUS1 BIT(2)
+#define RG_DSI_LNTx_LPTX_IPLUS2 BIT(3)
+#define RG_DSI_LNTx_LPTX_IMINUS BIT(4)
+#define RG_DSI_LNTx_LPCD_IPLUS BIT(5)
+#define RG_DSI_LNTx_LPCD_IMINUS BIT(6)
+#define RG_DSI_LNTx_RT_CODE GENMASK(11, 8)
+
+#define MIPITX_DSI_TOP_CON 0x40
+#define RG_DSI_LNT_INTR_EN BIT(0)
+#define RG_DSI_LNT_HS_BIAS_EN BIT(1)
+#define RG_DSI_LNT_IMP_CAL_EN BIT(2)
+#define RG_DSI_LNT_TESTMODE_EN BIT(3)
+#define RG_DSI_LNT_IMP_CAL_CODE GENMASK(7, 4)
+#define RG_DSI_LNT_AIO_SEL GENMASK(10, 8)
+#define RG_DSI_PAD_TIE_LOW_EN BIT(11)
+#define RG_DSI_DEBUG_INPUT_EN BIT(12)
+#define RG_DSI_PRESERVE GENMASK(15, 13)
+
+#define MIPITX_DSI_BG_CON 0x44
+#define RG_DSI_BG_CORE_EN BIT(0)
+#define RG_DSI_BG_CKEN BIT(1)
+#define RG_DSI_BG_DIV GENMASK(3, 2)
+#define RG_DSI_BG_FAST_CHARGE BIT(4)
+
+#define RG_DSI_V12_SEL GENMASK(7, 5)
+#define RG_DSI_V10_SEL GENMASK(10, 8)
+#define RG_DSI_V072_SEL GENMASK(13, 11)
+#define RG_DSI_V04_SEL GENMASK(16, 14)
+#define RG_DSI_V032_SEL GENMASK(19, 17)
+#define RG_DSI_V02_SEL GENMASK(22, 20)
+#define RG_DSI_VOUT_MSK \
+ (RG_DSI_V12_SEL | RG_DSI_V10_SEL | RG_DSI_V072_SEL | \
+ RG_DSI_V04_SEL | RG_DSI_V032_SEL | RG_DSI_V02_SEL)
+#define RG_DSI_BG_R1_TRIM GENMASK(27, 24)
+#define RG_DSI_BG_R2_TRIM GENMASK(31, 28)
+
+#define MIPITX_DSI_PLL_CON0 0x50
+#define RG_DSI_MPPLL_PLL_EN BIT(0)
+#define RG_DSI_MPPLL_PREDIV GENMASK(2, 1)
+#define RG_DSI_MPPLL_TXDIV0 GENMASK(4, 3)
+#define RG_DSI_MPPLL_TXDIV1 GENMASK(6, 5)
+#define RG_DSI_MPPLL_POSDIV GENMASK(9, 7)
+#define RG_DSI_MPPLL_DIV_MSK \
+ (RG_DSI_MPPLL_PREDIV | RG_DSI_MPPLL_TXDIV0 | \
+ RG_DSI_MPPLL_TXDIV1 | RG_DSI_MPPLL_POSDIV)
+#define RG_DSI_MPPLL_MONVC_EN BIT(10)
+#define RG_DSI_MPPLL_MONREF_EN BIT(11)
+#define RG_DSI_MPPLL_VOD_EN BIT(12)
+
+#define MIPITX_DSI_PLL_CON1 0x54
+#define RG_DSI_MPPLL_SDM_FRA_EN BIT(0)
+#define RG_DSI_MPPLL_SDM_SSC_PH_INIT BIT(1)
+#define RG_DSI_MPPLL_SDM_SSC_EN BIT(2)
+#define RG_DSI_MPPLL_SDM_SSC_PRD GENMASK(31, 16)
+
+#define MIPITX_DSI_PLL_CON2 0x58
+
+#define MIPITX_DSI_PLL_TOP 0x64
+#define RG_DSI_MPPLL_PRESERVE GENMASK(15, 8)
+
+#define MIPITX_DSI_PLL_PWR 0x68
+#define RG_DSI_MPPLL_SDM_PWR_ON BIT(0)
+#define RG_DSI_MPPLL_SDM_ISO_EN BIT(1)
+#define RG_DSI_MPPLL_SDM_PWR_ACK BIT(8)
+
+#define MIPITX_DSI_SW_CTRL 0x80
+#define SW_CTRL_EN BIT(0)
+
+#define MIPITX_DSI_SW_CTRL_CON0 0x84
+#define SW_LNTC_LPTX_PRE_OE BIT(0)
+#define SW_LNTC_LPTX_OE BIT(1)
+#define SW_LNTC_LPTX_P BIT(2)
+#define SW_LNTC_LPTX_N BIT(3)
+#define SW_LNTC_HSTX_PRE_OE BIT(4)
+#define SW_LNTC_HSTX_OE BIT(5)
+#define SW_LNTC_HSTX_ZEROCLK BIT(6)
+#define SW_LNT0_LPTX_PRE_OE BIT(7)
+#define SW_LNT0_LPTX_OE BIT(8)
+#define SW_LNT0_LPTX_P BIT(9)
+#define SW_LNT0_LPTX_N BIT(10)
+#define SW_LNT0_HSTX_PRE_OE BIT(11)
+#define SW_LNT0_HSTX_OE BIT(12)
+#define SW_LNT0_LPRX_EN BIT(13)
+#define SW_LNT1_LPTX_PRE_OE BIT(14)
+#define SW_LNT1_LPTX_OE BIT(15)
+#define SW_LNT1_LPTX_P BIT(16)
+#define SW_LNT1_LPTX_N BIT(17)
+#define SW_LNT1_HSTX_PRE_OE BIT(18)
+#define SW_LNT1_HSTX_OE BIT(19)
+#define SW_LNT2_LPTX_PRE_OE BIT(20)
+#define SW_LNT2_LPTX_OE BIT(21)
+#define SW_LNT2_LPTX_P BIT(22)
+#define SW_LNT2_LPTX_N BIT(23)
+#define SW_LNT2_HSTX_PRE_OE BIT(24)
+#define SW_LNT2_HSTX_OE BIT(25)
+
+static int mtk_mipi_tx_pll_prepare(struct clk_hw *hw)
+{
+ struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw);
+ void __iomem *base = mipi_tx->regs;
+ u8 txdiv, txdiv0, txdiv1;
+ u64 pcw;
+
+ dev_dbg(mipi_tx->dev, "prepare: %u Hz\n", mipi_tx->data_rate);
+
+ if (mipi_tx->data_rate >= 500000000) {
+ txdiv = 1;
+ txdiv0 = 0;
+ txdiv1 = 0;
+ } else if (mipi_tx->data_rate >= 250000000) {
+ txdiv = 2;
+ txdiv0 = 1;
+ txdiv1 = 0;
+ } else if (mipi_tx->data_rate >= 125000000) {
+ txdiv = 4;
+ txdiv0 = 2;
+ txdiv1 = 0;
+ } else if (mipi_tx->data_rate > 62000000) {
+ txdiv = 8;
+ txdiv0 = 2;
+ txdiv1 = 1;
+ } else if (mipi_tx->data_rate >= 50000000) {
+ txdiv = 16;
+ txdiv0 = 2;
+ txdiv1 = 2;
+ } else {
+ return -EINVAL;
+ }
+
+ mtk_phy_update_bits(base + MIPITX_DSI_BG_CON,
+ RG_DSI_VOUT_MSK | RG_DSI_BG_CKEN |
+ RG_DSI_BG_CORE_EN,
+ FIELD_PREP(RG_DSI_V02_SEL, 4) |
+ FIELD_PREP(RG_DSI_V032_SEL, 4) |
+ FIELD_PREP(RG_DSI_V04_SEL, 4) |
+ FIELD_PREP(RG_DSI_V072_SEL, 4) |
+ FIELD_PREP(RG_DSI_V10_SEL, 4) |
+ FIELD_PREP(RG_DSI_V12_SEL, 4) |
+ RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN);
+
+ usleep_range(30, 100);
+
+ mtk_phy_update_bits(base + MIPITX_DSI_TOP_CON,
+ RG_DSI_LNT_IMP_CAL_CODE | RG_DSI_LNT_HS_BIAS_EN,
+ FIELD_PREP(RG_DSI_LNT_IMP_CAL_CODE, 8) |
+ RG_DSI_LNT_HS_BIAS_EN);
+
+ mtk_phy_set_bits(base + MIPITX_DSI_CON,
+ RG_DSI_CKG_LDOOUT_EN | RG_DSI_LDOCORE_EN);
+
+ mtk_phy_update_bits(base + MIPITX_DSI_PLL_PWR,
+ RG_DSI_MPPLL_SDM_PWR_ON | RG_DSI_MPPLL_SDM_ISO_EN,
+ RG_DSI_MPPLL_SDM_PWR_ON);
+
+ mtk_phy_clear_bits(base + MIPITX_DSI_PLL_CON0, RG_DSI_MPPLL_PLL_EN);
+
+ mtk_phy_update_bits(base + MIPITX_DSI_PLL_CON0,
+ RG_DSI_MPPLL_TXDIV0 | RG_DSI_MPPLL_TXDIV1 |
+ RG_DSI_MPPLL_PREDIV,
+ FIELD_PREP(RG_DSI_MPPLL_TXDIV0, txdiv0) |
+ FIELD_PREP(RG_DSI_MPPLL_TXDIV1, txdiv1));
+
+ /*
+ * PLL PCW config
+ * PCW bit 24~30 = integer part of pcw
+ * PCW bit 0~23 = fractional part of pcw
+ * pcw = data_Rate*4*txdiv/(Ref_clk*2);
+ * Post DIV =4, so need data_Rate*4
+ * Ref_clk is 26MHz
+ */
+ pcw = div_u64(((u64)mipi_tx->data_rate * 2 * txdiv) << 24, 26000000);
+ writel(pcw, base + MIPITX_DSI_PLL_CON2);
+
+ mtk_phy_set_bits(base + MIPITX_DSI_PLL_CON1, RG_DSI_MPPLL_SDM_FRA_EN);
+
+ mtk_phy_set_bits(base + MIPITX_DSI_PLL_CON0, RG_DSI_MPPLL_PLL_EN);
+
+ usleep_range(20, 100);
+
+ mtk_phy_clear_bits(base + MIPITX_DSI_PLL_CON1, RG_DSI_MPPLL_SDM_SSC_EN);
+
+ mtk_phy_update_field(base + MIPITX_DSI_PLL_TOP,
+ RG_DSI_MPPLL_PRESERVE,
+ mipi_tx->driver_data->mppll_preserve);
+
+ return 0;
+}
+
+static void mtk_mipi_tx_pll_unprepare(struct clk_hw *hw)
+{
+ struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw);
+ void __iomem *base = mipi_tx->regs;
+
+ dev_dbg(mipi_tx->dev, "unprepare\n");
+
+ mtk_phy_clear_bits(base + MIPITX_DSI_PLL_CON0, RG_DSI_MPPLL_PLL_EN);
+
+ mtk_phy_clear_bits(base + MIPITX_DSI_PLL_TOP, RG_DSI_MPPLL_PRESERVE);
+
+ mtk_phy_update_bits(base + MIPITX_DSI_PLL_PWR,
+ RG_DSI_MPPLL_SDM_ISO_EN | RG_DSI_MPPLL_SDM_PWR_ON,
+ RG_DSI_MPPLL_SDM_ISO_EN);
+
+ mtk_phy_clear_bits(base + MIPITX_DSI_TOP_CON, RG_DSI_LNT_HS_BIAS_EN);
+
+ mtk_phy_clear_bits(base + MIPITX_DSI_CON,
+ RG_DSI_CKG_LDOOUT_EN | RG_DSI_LDOCORE_EN);
+
+ mtk_phy_clear_bits(base + MIPITX_DSI_BG_CON,
+ RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN);
+
+ mtk_phy_clear_bits(base + MIPITX_DSI_PLL_CON0, RG_DSI_MPPLL_DIV_MSK);
+}
+
+static long mtk_mipi_tx_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ return clamp_val(rate, 50000000, 1250000000);
+}
+
+static const struct clk_ops mtk_mipi_tx_pll_ops = {
+ .prepare = mtk_mipi_tx_pll_prepare,
+ .unprepare = mtk_mipi_tx_pll_unprepare,
+ .round_rate = mtk_mipi_tx_pll_round_rate,
+ .set_rate = mtk_mipi_tx_pll_set_rate,
+ .recalc_rate = mtk_mipi_tx_pll_recalc_rate,
+};
+
+static void mtk_mipi_tx_power_on_signal(struct phy *phy)
+{
+ struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy);
+ u32 reg;
+
+ for (reg = MIPITX_DSI_CLOCK_LANE;
+ reg <= MIPITX_DSI_DATA_LANE3; reg += 4)
+ mtk_phy_set_bits(mipi_tx->regs + reg, RG_DSI_LNTx_LDOOUT_EN);
+
+ mtk_phy_clear_bits(mipi_tx->regs + MIPITX_DSI_TOP_CON,
+ RG_DSI_PAD_TIE_LOW_EN);
+}
+
+static void mtk_mipi_tx_power_off_signal(struct phy *phy)
+{
+ struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy);
+ u32 reg;
+
+ mtk_phy_set_bits(mipi_tx->regs + MIPITX_DSI_TOP_CON,
+ RG_DSI_PAD_TIE_LOW_EN);
+
+ for (reg = MIPITX_DSI_CLOCK_LANE;
+ reg <= MIPITX_DSI_DATA_LANE3; reg += 4)
+ mtk_phy_clear_bits(mipi_tx->regs + reg, RG_DSI_LNTx_LDOOUT_EN);
+}
+
+const struct mtk_mipitx_data mt2701_mipitx_data = {
+ .mppll_preserve = 3,
+ .mipi_tx_clk_ops = &mtk_mipi_tx_pll_ops,
+ .mipi_tx_enable_signal = mtk_mipi_tx_power_on_signal,
+ .mipi_tx_disable_signal = mtk_mipi_tx_power_off_signal,
+};
+
+const struct mtk_mipitx_data mt8173_mipitx_data = {
+ .mppll_preserve = 0,
+ .mipi_tx_clk_ops = &mtk_mipi_tx_pll_ops,
+ .mipi_tx_enable_signal = mtk_mipi_tx_power_on_signal,
+ .mipi_tx_disable_signal = mtk_mipi_tx_power_off_signal,
+};
diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8183.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8183.c
new file mode 100644
index 000000000000..f021ec5a70e5
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8183.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: jitao.shi <jitao.shi@mediatek.com>
+ */
+
+#include "phy-mtk-io.h"
+#include "phy-mtk-mipi-dsi.h"
+
+#define MIPITX_LANE_CON 0x000c
+#define RG_DSI_CPHY_T1DRV_EN BIT(0)
+#define RG_DSI_ANA_CK_SEL BIT(1)
+#define RG_DSI_PHY_CK_SEL BIT(2)
+#define RG_DSI_CPHY_EN BIT(3)
+#define RG_DSI_PHYCK_INV_EN BIT(4)
+#define RG_DSI_PWR04_EN BIT(5)
+#define RG_DSI_BG_LPF_EN BIT(6)
+#define RG_DSI_BG_CORE_EN BIT(7)
+#define RG_DSI_PAD_TIEL_SEL BIT(8)
+
+#define MIPITX_VOLTAGE_SEL 0x0010
+#define RG_DSI_HSTX_LDO_REF_SEL GENMASK(9, 6)
+
+#define MIPITX_PLL_PWR 0x0028
+#define MIPITX_PLL_CON0 0x002c
+#define MIPITX_PLL_CON1 0x0030
+#define MIPITX_PLL_CON2 0x0034
+#define MIPITX_PLL_CON3 0x0038
+#define MIPITX_PLL_CON4 0x003c
+#define RG_DSI_PLL_IBIAS GENMASK(11, 10)
+
+#define MIPITX_D2P_RTCODE 0x0100
+#define MIPITX_D2_SW_CTL_EN 0x0144
+#define MIPITX_D0_SW_CTL_EN 0x0244
+#define MIPITX_CK_CKMODE_EN 0x0328
+#define DSI_CK_CKMODE_EN BIT(0)
+#define MIPITX_CK_SW_CTL_EN 0x0344
+#define MIPITX_D1_SW_CTL_EN 0x0444
+#define MIPITX_D3_SW_CTL_EN 0x0544
+#define DSI_SW_CTL_EN BIT(0)
+#define AD_DSI_PLL_SDM_PWR_ON BIT(0)
+#define AD_DSI_PLL_SDM_ISO_EN BIT(1)
+
+#define RG_DSI_PLL_EN BIT(4)
+#define RG_DSI_PLL_POSDIV GENMASK(10, 8)
+
+static int mtk_mipi_tx_pll_enable(struct clk_hw *hw)
+{
+ struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw);
+ void __iomem *base = mipi_tx->regs;
+ unsigned int txdiv, txdiv0;
+ u64 pcw;
+
+ dev_dbg(mipi_tx->dev, "enable: %u bps\n", mipi_tx->data_rate);
+
+ if (mipi_tx->data_rate >= 2000000000) {
+ txdiv = 1;
+ txdiv0 = 0;
+ } else if (mipi_tx->data_rate >= 1000000000) {
+ txdiv = 2;
+ txdiv0 = 1;
+ } else if (mipi_tx->data_rate >= 500000000) {
+ txdiv = 4;
+ txdiv0 = 2;
+ } else if (mipi_tx->data_rate > 250000000) {
+ txdiv = 8;
+ txdiv0 = 3;
+ } else if (mipi_tx->data_rate >= 125000000) {
+ txdiv = 16;
+ txdiv0 = 4;
+ } else {
+ return -EINVAL;
+ }
+
+ mtk_phy_clear_bits(base + MIPITX_PLL_CON4, RG_DSI_PLL_IBIAS);
+
+ mtk_phy_set_bits(base + MIPITX_PLL_PWR, AD_DSI_PLL_SDM_PWR_ON);
+ mtk_phy_clear_bits(base + MIPITX_PLL_CON1, RG_DSI_PLL_EN);
+ udelay(1);
+ mtk_phy_clear_bits(base + MIPITX_PLL_PWR, AD_DSI_PLL_SDM_ISO_EN);
+ pcw = div_u64(((u64)mipi_tx->data_rate * txdiv) << 24, 26000000);
+ writel(pcw, base + MIPITX_PLL_CON0);
+ mtk_phy_update_field(base + MIPITX_PLL_CON1, RG_DSI_PLL_POSDIV, txdiv0);
+ mtk_phy_set_bits(base + MIPITX_PLL_CON1, RG_DSI_PLL_EN);
+
+ return 0;
+}
+
+static void mtk_mipi_tx_pll_disable(struct clk_hw *hw)
+{
+ struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw);
+ void __iomem *base = mipi_tx->regs;
+
+ mtk_phy_clear_bits(base + MIPITX_PLL_CON1, RG_DSI_PLL_EN);
+
+ mtk_phy_set_bits(base + MIPITX_PLL_PWR, AD_DSI_PLL_SDM_ISO_EN);
+ mtk_phy_clear_bits(base + MIPITX_PLL_PWR, AD_DSI_PLL_SDM_PWR_ON);
+}
+
+static long mtk_mipi_tx_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ return clamp_val(rate, 50000000, 1600000000);
+}
+
+static const struct clk_ops mtk_mipi_tx_pll_ops = {
+ .enable = mtk_mipi_tx_pll_enable,
+ .disable = mtk_mipi_tx_pll_disable,
+ .round_rate = mtk_mipi_tx_pll_round_rate,
+ .set_rate = mtk_mipi_tx_pll_set_rate,
+ .recalc_rate = mtk_mipi_tx_pll_recalc_rate,
+};
+
+static void mtk_mipi_tx_config_calibration_data(struct mtk_mipi_tx *mipi_tx)
+{
+ int i, j;
+
+ for (i = 0; i < 5; i++) {
+ if ((mipi_tx->rt_code[i] & 0x1f) == 0)
+ mipi_tx->rt_code[i] |= 0x10;
+
+ if ((mipi_tx->rt_code[i] >> 5 & 0x1f) == 0)
+ mipi_tx->rt_code[i] |= 0x10 << 5;
+
+ for (j = 0; j < 10; j++)
+ mtk_phy_update_bits(mipi_tx->regs +
+ MIPITX_D2P_RTCODE * (i + 1) + j * 4,
+ 1, mipi_tx->rt_code[i] >> j & 1);
+ }
+}
+
+static void mtk_mipi_tx_power_on_signal(struct phy *phy)
+{
+ struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy);
+ void __iomem *base = mipi_tx->regs;
+
+ /* BG_LPF_EN / BG_CORE_EN */
+ writel(RG_DSI_PAD_TIEL_SEL | RG_DSI_BG_CORE_EN, base + MIPITX_LANE_CON);
+ usleep_range(30, 100);
+ writel(RG_DSI_BG_CORE_EN | RG_DSI_BG_LPF_EN, base + MIPITX_LANE_CON);
+
+ /* Switch OFF each Lane */
+ mtk_phy_clear_bits(base + MIPITX_D0_SW_CTL_EN, DSI_SW_CTL_EN);
+ mtk_phy_clear_bits(base + MIPITX_D1_SW_CTL_EN, DSI_SW_CTL_EN);
+ mtk_phy_clear_bits(base + MIPITX_D2_SW_CTL_EN, DSI_SW_CTL_EN);
+ mtk_phy_clear_bits(base + MIPITX_D3_SW_CTL_EN, DSI_SW_CTL_EN);
+ mtk_phy_clear_bits(base + MIPITX_CK_SW_CTL_EN, DSI_SW_CTL_EN);
+
+ mtk_phy_update_field(base + MIPITX_VOLTAGE_SEL, RG_DSI_HSTX_LDO_REF_SEL,
+ (mipi_tx->mipitx_drive - 3000) / 200);
+
+ mtk_mipi_tx_config_calibration_data(mipi_tx);
+
+ mtk_phy_set_bits(base + MIPITX_CK_CKMODE_EN, DSI_CK_CKMODE_EN);
+}
+
+static void mtk_mipi_tx_power_off_signal(struct phy *phy)
+{
+ struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy);
+ void __iomem *base = mipi_tx->regs;
+
+ /* Switch ON each Lane */
+ mtk_phy_set_bits(base + MIPITX_D0_SW_CTL_EN, DSI_SW_CTL_EN);
+ mtk_phy_set_bits(base + MIPITX_D1_SW_CTL_EN, DSI_SW_CTL_EN);
+ mtk_phy_set_bits(base + MIPITX_D2_SW_CTL_EN, DSI_SW_CTL_EN);
+ mtk_phy_set_bits(base + MIPITX_D3_SW_CTL_EN, DSI_SW_CTL_EN);
+ mtk_phy_set_bits(base + MIPITX_CK_SW_CTL_EN, DSI_SW_CTL_EN);
+
+ writel(RG_DSI_PAD_TIEL_SEL | RG_DSI_BG_CORE_EN, base + MIPITX_LANE_CON);
+ writel(RG_DSI_PAD_TIEL_SEL, base + MIPITX_LANE_CON);
+}
+
+const struct mtk_mipitx_data mt8183_mipitx_data = {
+ .mipi_tx_clk_ops = &mtk_mipi_tx_pll_ops,
+ .mipi_tx_enable_signal = mtk_mipi_tx_power_on_signal,
+ .mipi_tx_disable_signal = mtk_mipi_tx_power_off_signal,
+};
diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c
new file mode 100644
index 000000000000..cf9c386385bb
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ */
+
+#include "phy-mtk-mipi-dsi.h"
+
+inline struct mtk_mipi_tx *mtk_mipi_tx_from_clk_hw(struct clk_hw *hw)
+{
+ return container_of(hw, struct mtk_mipi_tx, pll_hw);
+}
+
+int mtk_mipi_tx_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw);
+
+ dev_dbg(mipi_tx->dev, "set rate: %lu Hz\n", rate);
+
+ mipi_tx->data_rate = rate;
+
+ return 0;
+}
+
+unsigned long mtk_mipi_tx_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw);
+
+ return mipi_tx->data_rate;
+}
+
+static int mtk_mipi_tx_power_on(struct phy *phy)
+{
+ struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy);
+ int ret;
+
+ /* Power up core and enable PLL */
+ ret = clk_prepare_enable(mipi_tx->pll);
+ if (ret < 0)
+ return ret;
+
+ /* Enable DSI Lane LDO outputs, disable pad tie low */
+ mipi_tx->driver_data->mipi_tx_enable_signal(phy);
+ return 0;
+}
+
+static int mtk_mipi_tx_power_off(struct phy *phy)
+{
+ struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy);
+
+ /* Enable pad tie low, disable DSI Lane LDO outputs */
+ mipi_tx->driver_data->mipi_tx_disable_signal(phy);
+
+ /* Disable PLL and power down core */
+ clk_disable_unprepare(mipi_tx->pll);
+
+ return 0;
+}
+
+static const struct phy_ops mtk_mipi_tx_ops = {
+ .power_on = mtk_mipi_tx_power_on,
+ .power_off = mtk_mipi_tx_power_off,
+ .owner = THIS_MODULE,
+};
+
+static void mtk_mipi_tx_get_calibration_datal(struct mtk_mipi_tx *mipi_tx)
+{
+ struct nvmem_cell *cell;
+ size_t len;
+ u32 *buf;
+
+ cell = nvmem_cell_get(mipi_tx->dev, "calibration-data");
+ if (IS_ERR(cell)) {
+ dev_info(mipi_tx->dev, "can't get nvmem_cell_get, ignore it\n");
+ return;
+ }
+ buf = (u32 *)nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(buf)) {
+ dev_info(mipi_tx->dev, "can't get data, ignore it\n");
+ return;
+ }
+
+ if (len < 3 * sizeof(u32)) {
+ dev_info(mipi_tx->dev, "invalid calibration data\n");
+ kfree(buf);
+ return;
+ }
+
+ mipi_tx->rt_code[0] = ((buf[0] >> 6 & 0x1f) << 5) |
+ (buf[0] >> 11 & 0x1f);
+ mipi_tx->rt_code[1] = ((buf[1] >> 27 & 0x1f) << 5) |
+ (buf[0] >> 1 & 0x1f);
+ mipi_tx->rt_code[2] = ((buf[1] >> 17 & 0x1f) << 5) |
+ (buf[1] >> 22 & 0x1f);
+ mipi_tx->rt_code[3] = ((buf[1] >> 7 & 0x1f) << 5) |
+ (buf[1] >> 12 & 0x1f);
+ mipi_tx->rt_code[4] = ((buf[2] >> 27 & 0x1f) << 5) |
+ (buf[1] >> 2 & 0x1f);
+ kfree(buf);
+}
+
+static int mtk_mipi_tx_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_mipi_tx *mipi_tx;
+ const char *ref_clk_name;
+ struct clk *ref_clk;
+ struct clk_init_data clk_init = {
+ .num_parents = 1,
+ .parent_names = (const char * const *)&ref_clk_name,
+ .flags = CLK_SET_RATE_GATE,
+ };
+ struct phy *phy;
+ struct phy_provider *phy_provider;
+ int ret;
+
+ mipi_tx = devm_kzalloc(dev, sizeof(*mipi_tx), GFP_KERNEL);
+ if (!mipi_tx)
+ 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))
+ return PTR_ERR(mipi_tx->regs);
+
+ ref_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ref_clk))
+ return dev_err_probe(dev, PTR_ERR(ref_clk),
+ "Failed to get reference clock\n");
+
+ ret = of_property_read_u32(dev->of_node, "drive-strength-microamp",
+ &mipi_tx->mipitx_drive);
+ /* If can't get the "mipi_tx->mipitx_drive", set it default 0x8 */
+ if (ret < 0)
+ mipi_tx->mipitx_drive = 4600;
+
+ /* check the mipitx_drive valid */
+ if (mipi_tx->mipitx_drive > 6000 || mipi_tx->mipitx_drive < 3000) {
+ dev_warn(dev, "drive-strength-microamp is invalid %d, not in 3000 ~ 6000\n",
+ mipi_tx->mipitx_drive);
+ mipi_tx->mipitx_drive = clamp_val(mipi_tx->mipitx_drive, 3000,
+ 6000);
+ }
+
+ ref_clk_name = __clk_get_name(ref_clk);
+
+ ret = of_property_read_string(dev->of_node, "clock-output-names",
+ &clk_init.name);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read clock-output-names\n");
+
+ clk_init.ops = mipi_tx->driver_data->mipi_tx_clk_ops;
+
+ mipi_tx->pll_hw.init = &clk_init;
+ mipi_tx->pll = devm_clk_register(dev, &mipi_tx->pll_hw);
+ if (IS_ERR(mipi_tx->pll))
+ return dev_err_probe(dev, PTR_ERR(mipi_tx->pll), "Failed to register PLL\n");
+
+ phy = devm_phy_create(dev, NULL, &mtk_mipi_tx_ops);
+ if (IS_ERR(phy))
+ return dev_err_probe(dev, PTR_ERR(phy), "Failed to create MIPI D-PHY\n");
+
+ phy_set_drvdata(phy, mipi_tx);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ mipi_tx->dev = dev;
+
+ mtk_mipi_tx_get_calibration_datal(mipi_tx);
+
+ return of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
+ mipi_tx->pll);
+}
+
+static int mtk_mipi_tx_remove(struct platform_device *pdev)
+{
+ of_clk_del_provider(pdev->dev.of_node);
+ return 0;
+}
+
+static const struct of_device_id mtk_mipi_tx_match[] = {
+ { .compatible = "mediatek,mt2701-mipi-tx",
+ .data = &mt2701_mipitx_data },
+ { .compatible = "mediatek,mt8173-mipi-tx",
+ .data = &mt8173_mipitx_data },
+ { .compatible = "mediatek,mt8183-mipi-tx",
+ .data = &mt8183_mipitx_data },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mtk_mipi_tx_match);
+
+static struct platform_driver mtk_mipi_tx_driver = {
+ .probe = mtk_mipi_tx_probe,
+ .remove = mtk_mipi_tx_remove,
+ .driver = {
+ .name = "mediatek-mipi-tx",
+ .of_match_table = mtk_mipi_tx_match,
+ },
+};
+module_platform_driver(mtk_mipi_tx_driver);
+
+MODULE_DESCRIPTION("MediaTek MIPI TX Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi.h b/drivers/phy/mediatek/phy-mtk-mipi-dsi.h
new file mode 100644
index 000000000000..47b60b1a7226
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Jitao Shi <jitao.shi@mediatek.com>
+ */
+
+#ifndef _MTK_MIPI_TX_H
+#define _MTK_MIPI_TX_H
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/slab.h>
+
+struct mtk_mipitx_data {
+ const u32 mppll_preserve;
+ const struct clk_ops *mipi_tx_clk_ops;
+ void (*mipi_tx_enable_signal)(struct phy *phy);
+ void (*mipi_tx_disable_signal)(struct phy *phy);
+};
+
+struct mtk_mipi_tx {
+ struct device *dev;
+ void __iomem *regs;
+ u32 data_rate;
+ u32 mipitx_drive;
+ u32 rt_code[5];
+ const struct mtk_mipitx_data *driver_data;
+ struct clk_hw pll_hw;
+ struct clk *pll;
+};
+
+struct mtk_mipi_tx *mtk_mipi_tx_from_clk_hw(struct clk_hw *hw);
+int mtk_mipi_tx_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate);
+unsigned long mtk_mipi_tx_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate);
+
+extern const struct mtk_mipitx_data mt2701_mipitx_data;
+extern const struct mtk_mipitx_data mt8173_mipitx_data;
+extern const struct mtk_mipitx_data mt8183_mipitx_data;
+
+#endif
diff --git a/drivers/phy/mediatek/phy-mtk-pcie.c b/drivers/phy/mediatek/phy-mtk-pcie.c
new file mode 100644
index 000000000000..25dbd6e35722
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-pcie.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Jianjun Wang <jianjun.wang@mediatek.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "phy-mtk-io.h"
+
+#define PEXTP_ANA_GLB_00_REG 0x9000
+/* Internal Resistor Selection of TX Bias Current */
+#define EFUSE_GLB_INTR_SEL GENMASK(28, 24)
+
+#define PEXTP_ANA_LN0_TRX_REG 0xa000
+
+#define PEXTP_ANA_TX_REG 0x04
+/* TX PMOS impedance selection */
+#define EFUSE_LN_TX_PMOS_SEL GENMASK(5, 2)
+/* TX NMOS impedance selection */
+#define EFUSE_LN_TX_NMOS_SEL GENMASK(11, 8)
+
+#define PEXTP_ANA_RX_REG 0x3c
+/* RX impedance selection */
+#define EFUSE_LN_RX_SEL GENMASK(3, 0)
+
+#define PEXTP_ANA_LANE_OFFSET 0x100
+
+/**
+ * struct mtk_pcie_lane_efuse - eFuse data for each lane
+ * @tx_pmos: TX PMOS impedance selection data
+ * @tx_nmos: TX NMOS impedance selection data
+ * @rx_data: RX impedance selection data
+ * @lane_efuse_supported: software eFuse data is supported for this lane
+ */
+struct mtk_pcie_lane_efuse {
+ u32 tx_pmos;
+ u32 tx_nmos;
+ u32 rx_data;
+ bool lane_efuse_supported;
+};
+
+/**
+ * struct mtk_pcie_phy_data - phy data for each SoC
+ * @num_lanes: supported lane numbers
+ * @sw_efuse_supported: support software to load eFuse data
+ */
+struct mtk_pcie_phy_data {
+ int num_lanes;
+ bool sw_efuse_supported;
+};
+
+/**
+ * struct mtk_pcie_phy - PCIe phy driver main structure
+ * @dev: pointer to device
+ * @phy: pointer to generic phy
+ * @sif_base: IO mapped register base address of system interface
+ * @data: pointer to SoC dependent data
+ * @sw_efuse_en: software eFuse enable status
+ * @efuse_glb_intr: internal resistor selection of TX bias current data
+ * @efuse: pointer to eFuse data for each lane
+ */
+struct mtk_pcie_phy {
+ struct device *dev;
+ struct phy *phy;
+ void __iomem *sif_base;
+ const struct mtk_pcie_phy_data *data;
+
+ bool sw_efuse_en;
+ u32 efuse_glb_intr;
+ struct mtk_pcie_lane_efuse *efuse;
+};
+
+static void mtk_pcie_efuse_set_lane(struct mtk_pcie_phy *pcie_phy,
+ unsigned int lane)
+{
+ struct mtk_pcie_lane_efuse *data = &pcie_phy->efuse[lane];
+ void __iomem *addr;
+
+ if (!data->lane_efuse_supported)
+ return;
+
+ addr = pcie_phy->sif_base + PEXTP_ANA_LN0_TRX_REG +
+ lane * PEXTP_ANA_LANE_OFFSET;
+
+ mtk_phy_update_field(addr + PEXTP_ANA_TX_REG, EFUSE_LN_TX_PMOS_SEL,
+ data->tx_pmos);
+
+ mtk_phy_update_field(addr + PEXTP_ANA_TX_REG, EFUSE_LN_TX_NMOS_SEL,
+ data->tx_nmos);
+
+ mtk_phy_update_field(addr + PEXTP_ANA_RX_REG, EFUSE_LN_RX_SEL,
+ data->rx_data);
+}
+
+/**
+ * mtk_pcie_phy_init() - Initialize the phy
+ * @phy: the phy to be initialized
+ *
+ * Initialize the phy by setting the efuse data.
+ * The hardware settings will be reset during suspend, it should be
+ * reinitialized when the consumer calls phy_init() again on resume.
+ */
+static int mtk_pcie_phy_init(struct phy *phy)
+{
+ struct mtk_pcie_phy *pcie_phy = phy_get_drvdata(phy);
+ int i;
+
+ if (!pcie_phy->sw_efuse_en)
+ return 0;
+
+ /* Set global data */
+ mtk_phy_update_field(pcie_phy->sif_base + PEXTP_ANA_GLB_00_REG,
+ EFUSE_GLB_INTR_SEL, pcie_phy->efuse_glb_intr);
+
+ for (i = 0; i < pcie_phy->data->num_lanes; i++)
+ mtk_pcie_efuse_set_lane(pcie_phy, i);
+
+ return 0;
+}
+
+static const struct phy_ops mtk_pcie_phy_ops = {
+ .init = mtk_pcie_phy_init,
+ .owner = THIS_MODULE,
+};
+
+static int mtk_pcie_efuse_read_for_lane(struct mtk_pcie_phy *pcie_phy,
+ unsigned int lane)
+{
+ struct mtk_pcie_lane_efuse *efuse = &pcie_phy->efuse[lane];
+ struct device *dev = pcie_phy->dev;
+ char efuse_id[16];
+ int ret;
+
+ snprintf(efuse_id, sizeof(efuse_id), "tx_ln%d_pmos", lane);
+ ret = nvmem_cell_read_variable_le_u32(dev, efuse_id, &efuse->tx_pmos);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read %s\n", efuse_id);
+
+ snprintf(efuse_id, sizeof(efuse_id), "tx_ln%d_nmos", lane);
+ ret = nvmem_cell_read_variable_le_u32(dev, efuse_id, &efuse->tx_nmos);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read %s\n", efuse_id);
+
+ snprintf(efuse_id, sizeof(efuse_id), "rx_ln%d", lane);
+ ret = nvmem_cell_read_variable_le_u32(dev, efuse_id, &efuse->rx_data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read %s\n", efuse_id);
+
+ if (!(efuse->tx_pmos || efuse->tx_nmos || efuse->rx_data))
+ return dev_err_probe(dev, -EINVAL,
+ "No eFuse data found for lane%d, but dts enable it\n",
+ lane);
+
+ efuse->lane_efuse_supported = true;
+
+ return 0;
+}
+
+static int mtk_pcie_read_efuse(struct mtk_pcie_phy *pcie_phy)
+{
+ struct device *dev = pcie_phy->dev;
+ bool nvmem_enabled;
+ int ret, i;
+
+ /* nvmem data is optional */
+ nvmem_enabled = device_property_present(dev, "nvmem-cells");
+ if (!nvmem_enabled)
+ return 0;
+
+ ret = nvmem_cell_read_variable_le_u32(dev, "glb_intr",
+ &pcie_phy->efuse_glb_intr);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read glb_intr\n");
+
+ pcie_phy->sw_efuse_en = true;
+
+ pcie_phy->efuse = devm_kzalloc(dev, pcie_phy->data->num_lanes *
+ sizeof(*pcie_phy->efuse), GFP_KERNEL);
+ if (!pcie_phy->efuse)
+ return -ENOMEM;
+
+ for (i = 0; i < pcie_phy->data->num_lanes; i++) {
+ ret = mtk_pcie_efuse_read_for_lane(pcie_phy, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mtk_pcie_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *provider;
+ struct mtk_pcie_phy *pcie_phy;
+ int ret;
+
+ pcie_phy = devm_kzalloc(dev, sizeof(*pcie_phy), GFP_KERNEL);
+ if (!pcie_phy)
+ return -ENOMEM;
+
+ pcie_phy->sif_base = devm_platform_ioremap_resource_byname(pdev, "sif");
+ if (IS_ERR(pcie_phy->sif_base))
+ return dev_err_probe(dev, PTR_ERR(pcie_phy->sif_base),
+ "Failed to map phy-sif base\n");
+
+ pcie_phy->phy = devm_phy_create(dev, dev->of_node, &mtk_pcie_phy_ops);
+ if (IS_ERR(pcie_phy->phy))
+ return dev_err_probe(dev, PTR_ERR(pcie_phy->phy),
+ "Failed to create PCIe phy\n");
+
+ pcie_phy->dev = dev;
+ pcie_phy->data = of_device_get_match_data(dev);
+ if (!pcie_phy->data)
+ return dev_err_probe(dev, -EINVAL, "Failed to get phy data\n");
+
+ if (pcie_phy->data->sw_efuse_supported) {
+ /*
+ * Failed to read the efuse data is not a fatal problem,
+ * ignore the failure and keep going.
+ */
+ ret = mtk_pcie_read_efuse(pcie_phy);
+ if (ret == -EPROBE_DEFER || ret == -ENOMEM)
+ return ret;
+ }
+
+ phy_set_drvdata(pcie_phy->phy, pcie_phy);
+
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(provider))
+ return dev_err_probe(dev, PTR_ERR(provider),
+ "PCIe phy probe failed\n");
+
+ return 0;
+}
+
+static const struct mtk_pcie_phy_data mt8195_data = {
+ .num_lanes = 2,
+ .sw_efuse_supported = true,
+};
+
+static const struct of_device_id mtk_pcie_phy_of_match[] = {
+ { .compatible = "mediatek,mt8195-pcie-phy", .data = &mt8195_data },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mtk_pcie_phy_of_match);
+
+static struct platform_driver mtk_pcie_phy_driver = {
+ .probe = mtk_pcie_phy_probe,
+ .driver = {
+ .name = "mtk-pcie-phy",
+ .of_match_table = mtk_pcie_phy_of_match,
+ },
+};
+module_platform_driver(mtk_pcie_phy_driver);
+
+MODULE_DESCRIPTION("MediaTek PCIe PHY driver");
+MODULE_AUTHOR("Jianjun Wang <jianjun.wang@mediatek.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c
index cb2ed3b25068..e906a82791bd 100644
--- a/drivers/phy/mediatek/phy-mtk-tphy.c
+++ b/drivers/phy/mediatek/phy-mtk-tphy.c
@@ -8,13 +8,17 @@
#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 */
@@ -27,7 +31,8 @@
#define SSUSB_SIFSLV_V1_U3PHYD 0x000
#define SSUSB_SIFSLV_V1_U3PHYA 0x200
-/* version V2 sub-banks offset base address */
+/* version V2/V3 sub-banks offset base address */
+/* V3: U2FREQ is not used anymore, but reserved */
/* u2 phy banks */
#define SSUSB_SIFSLV_V2_MISC 0x000
#define SSUSB_SIFSLV_V2_U2FREQ 0x100
@@ -38,36 +43,48 @@
#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)
#define PA0_RG_USB20_INTR_EN BIT(5)
#define U3P_USBPHYACR1 0x004
+#define PA1_RG_INTR_CAL GENMASK(23, 19)
#define PA1_RG_VRT_SEL GENMASK(14, 12)
-#define PA1_RG_VRT_SEL_VAL(x) ((0x7 & (x)) << 12)
#define PA1_RG_TERM_SEL GENMASK(10, 8)
-#define PA1_RG_TERM_SEL_VAL(x) ((0x7 & (x)) << 8)
#define U3P_USBPHYACR2 0x008
+#define PA2_RG_U2PLL_BW GENMASK(21, 19)
#define PA2_RG_SIF_U2PLL_FORCE_EN BIT(18)
#define U3P_USBPHYACR5 0x014
#define PA5_RG_U2_HSTX_SRCAL_EN BIT(15)
#define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12)
-#define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12)
#define PA5_RG_U2_HS_100U_U3_EN BIT(11)
#define U3P_USBPHYACR6 0x018
+#define PA6_RG_U2_PRE_EMP GENMASK(31, 30)
#define PA6_RG_U2_BC11_SW_EN BIT(23)
#define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20)
+#define PA6_RG_U2_DISCTH GENMASK(7, 4)
#define PA6_RG_U2_SQTH GENMASK(3, 0)
-#define PA6_RG_U2_SQTH_VAL(x) (0xf & (x))
#define U3P_U2PHYACR4 0x020
#define P2C_RG_USB20_GPIO_CTL BIT(9)
#define P2C_USB20_GPIO_MODE BIT(8)
#define P2C_U2_GPIO_CTR_MSK (P2C_RG_USB20_GPIO_CTL | P2C_USB20_GPIO_MODE)
+#define U3P_U2PHYA_RESV 0x030
+#define P2R_RG_U2PLL_FBDIV_26M 0x1bb13b
+#define P2R_RG_U2PLL_FBDIV_48M 0x3c0000
+
+#define U3P_U2PHYA_RESV1 0x044
+#define P2R_RG_U2PLL_REFCLK_SEL BIT(5)
+#define P2R_RG_U2PLL_FRA_EN BIT(3)
+
#define U3D_U2PHYDCR0 0x060
#define P2C_RG_SIF_U2PLL_FORCE_ON BIT(24)
@@ -80,11 +97,9 @@
#define P2C_FORCE_SUSPENDM BIT(18)
#define P2C_FORCE_TERMSEL BIT(17)
#define P2C_RG_DATAIN GENMASK(13, 10)
-#define P2C_RG_DATAIN_VAL(x) ((0xf & (x)) << 10)
#define P2C_RG_DMPULLDOWN BIT(7)
#define P2C_RG_DPPULLDOWN BIT(6)
#define P2C_RG_XCVRSEL GENMASK(5, 4)
-#define P2C_RG_XCVRSEL_VAL(x) ((0x3 & (x)) << 4)
#define P2C_RG_SUSPENDM BIT(3)
#define P2C_RG_TERMSEL BIT(2)
#define P2C_DTM0_PART_MASK \
@@ -114,73 +129,66 @@
#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_CLKDRV_OFF GENMASK(3, 2)
-#define P3A_RG_CLKDRV_OFF_VAL(x) ((0x3 & (x)) << 2)
#define U3P_U3_PHYA_REG1 0x004
#define P3A_RG_CLKDRV_AMP GENMASK(31, 29)
-#define P3A_RG_CLKDRV_AMP_VAL(x) ((0x7 & (x)) << 29)
#define U3P_U3_PHYA_REG6 0x018
#define P3A_RG_TX_EIDLE_CM GENMASK(31, 28)
-#define P3A_RG_TX_EIDLE_CM_VAL(x) ((0xf & (x)) << 28)
#define U3P_U3_PHYA_REG9 0x024
#define P3A_RG_RX_DAC_MUX GENMASK(5, 1)
-#define P3A_RG_RX_DAC_MUX_VAL(x) ((0x1f & (x)) << 1)
#define U3P_U3_PHYA_DA_REG0 0x100
#define P3A_RG_XTAL_EXT_PE2H GENMASK(17, 16)
-#define P3A_RG_XTAL_EXT_PE2H_VAL(x) ((0x3 & (x)) << 16)
#define P3A_RG_XTAL_EXT_PE1H GENMASK(13, 12)
-#define P3A_RG_XTAL_EXT_PE1H_VAL(x) ((0x3 & (x)) << 12)
#define P3A_RG_XTAL_EXT_EN_U3 GENMASK(11, 10)
-#define P3A_RG_XTAL_EXT_EN_U3_VAL(x) ((0x3 & (x)) << 10)
#define U3P_U3_PHYA_DA_REG4 0x108
#define P3A_RG_PLL_DIVEN_PE2H GENMASK(21, 19)
#define P3A_RG_PLL_BC_PE2H GENMASK(7, 6)
-#define P3A_RG_PLL_BC_PE2H_VAL(x) ((0x3 & (x)) << 6)
#define U3P_U3_PHYA_DA_REG5 0x10c
#define P3A_RG_PLL_BR_PE2H GENMASK(29, 28)
-#define P3A_RG_PLL_BR_PE2H_VAL(x) ((0x3 & (x)) << 28)
#define P3A_RG_PLL_IC_PE2H GENMASK(15, 12)
-#define P3A_RG_PLL_IC_PE2H_VAL(x) ((0xf & (x)) << 12)
#define U3P_U3_PHYA_DA_REG6 0x110
#define P3A_RG_PLL_IR_PE2H GENMASK(19, 16)
-#define P3A_RG_PLL_IR_PE2H_VAL(x) ((0xf & (x)) << 16)
#define U3P_U3_PHYA_DA_REG7 0x114
#define P3A_RG_PLL_BP_PE2H GENMASK(19, 16)
-#define P3A_RG_PLL_BP_PE2H_VAL(x) ((0xf & (x)) << 16)
#define U3P_U3_PHYA_DA_REG20 0x13c
#define P3A_RG_PLL_DELTA1_PE2H GENMASK(31, 16)
-#define P3A_RG_PLL_DELTA1_PE2H_VAL(x) ((0xffff & (x)) << 16)
#define U3P_U3_PHYA_DA_REG25 0x148
#define P3A_RG_PLL_DELTA_PE2H GENMASK(15, 0)
-#define P3A_RG_PLL_DELTA_PE2H_VAL(x) (0xffff & (x))
#define U3P_U3_PHYD_LFPS1 0x00c
#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 U3P_U3_PHYD_IMPCAL1 0x014
+#define P3D_RG_FORCE_RX_IMPEL BIT(31)
+#define P3D_RG_RX_IMPEL GENMASK(28, 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)
#define P3D_RG_CDR_BIR_LTD0 GENMASK(12, 8)
-#define P3D_RG_CDR_BIR_LTD0_VAL(x) ((0x1f & (x)) << 8)
#define U3P_U3_PHYD_RXDET1 0x128
#define P3D_RG_RXDET_STB2_SET GENMASK(17, 9)
-#define P3D_RG_RXDET_STB2_SET_VAL(x) ((0x1ff & (x)) << 9)
#define U3P_U3_PHYD_RXDET2 0x12c
#define P3D_RG_RXDET_STB2_SET_P3 GENMASK(8, 0)
-#define P3D_RG_RXDET_STB2_SET_P3_VAL(x) (0x1ff & (x))
#define U3P_SPLLC_XTALCTL3 0x018
#define XC3_RG_U3_XTAL_RX_PWD BIT(9)
@@ -188,10 +196,8 @@
#define U3P_U2FREQ_FMCR0 0x00
#define P2F_RG_MONCLK_SEL GENMASK(27, 26)
-#define P2F_RG_MONCLK_SEL_VAL(x) ((0x3 & (x)) << 26)
#define P2F_RG_FREQDET_EN BIT(24)
#define P2F_RG_CYCLECNT GENMASK(23, 0)
-#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x))
#define U3P_U2FREQ_VALUE 0x0c
@@ -208,69 +214,76 @@
#define PHYD_CTRL_SIGNAL_MODE4 0x1c
/* CDR Charge Pump P-path current adjustment */
#define RG_CDR_BICLTD1_GEN1_MSK GENMASK(23, 20)
-#define RG_CDR_BICLTD1_GEN1_VAL(x) ((0xf & (x)) << 20)
#define RG_CDR_BICLTD0_GEN1_MSK GENMASK(11, 8)
-#define RG_CDR_BICLTD0_GEN1_VAL(x) ((0xf & (x)) << 8)
#define PHYD_DESIGN_OPTION2 0x24
/* Symbol lock count selection */
#define RG_LOCK_CNT_SEL_MSK GENMASK(5, 4)
-#define RG_LOCK_CNT_SEL_VAL(x) ((0x3 & (x)) << 4)
#define PHYD_DESIGN_OPTION9 0x40
/* COMWAK GAP width window */
#define RG_TG_MAX_MSK GENMASK(20, 16)
-#define RG_TG_MAX_VAL(x) ((0x1f & (x)) << 16)
/* COMINIT GAP width window */
#define RG_T2_MAX_MSK GENMASK(13, 8)
-#define RG_T2_MAX_VAL(x) ((0x3f & (x)) << 8)
/* COMWAK GAP width window */
#define RG_TG_MIN_MSK GENMASK(7, 5)
-#define RG_TG_MIN_VAL(x) ((0x7 & (x)) << 5)
/* COMINIT GAP width window */
#define RG_T2_MIN_MSK GENMASK(4, 0)
-#define RG_T2_MIN_VAL(x) (0x1f & (x))
#define ANA_RG_CTRL_SIGNAL1 0x4c
/* TX driver tail current control for 0dB de-empahsis mdoe for Gen1 speed */
#define RG_IDRV_0DB_GEN1_MSK GENMASK(13, 8)
-#define RG_IDRV_0DB_GEN1_VAL(x) ((0x3f & (x)) << 8)
#define ANA_RG_CTRL_SIGNAL4 0x58
#define RG_CDR_BICLTR_GEN1_MSK GENMASK(23, 20)
-#define RG_CDR_BICLTR_GEN1_VAL(x) ((0xf & (x)) << 20)
/* Loop filter R1 resistance adjustment for Gen1 speed */
#define RG_CDR_BR_GEN2_MSK GENMASK(10, 8)
-#define RG_CDR_BR_GEN2_VAL(x) ((0x7 & (x)) << 8)
#define ANA_RG_CTRL_SIGNAL6 0x60
/* I-path capacitance adjustment for Gen1 */
#define RG_CDR_BC_GEN1_MSK GENMASK(28, 24)
-#define RG_CDR_BC_GEN1_VAL(x) ((0x1f & (x)) << 24)
#define RG_CDR_BIRLTR_GEN1_MSK GENMASK(4, 0)
-#define RG_CDR_BIRLTR_GEN1_VAL(x) (0x1f & (x))
#define ANA_EQ_EYE_CTRL_SIGNAL1 0x6c
/* RX Gen1 LEQ tuning step */
#define RG_EQ_DLEQ_LFI_GEN1_MSK GENMASK(11, 8)
-#define RG_EQ_DLEQ_LFI_GEN1_VAL(x) ((0xf & (x)) << 8)
#define ANA_EQ_EYE_CTRL_SIGNAL4 0xd8
#define RG_CDR_BIRLTD0_GEN1_MSK GENMASK(20, 16)
-#define RG_CDR_BIRLTD0_GEN1_VAL(x) ((0x1f & (x)) << 16)
#define ANA_EQ_EYE_CTRL_SIGNAL5 0xdc
#define RG_CDR_BIRLTD0_GEN3_MSK GENMASK(4, 0)
-#define RG_CDR_BIRLTD0_GEN3_VAL(x) (0x1f & (x))
+
+/* PHY switch between pcie/usb3/sgmii/sata */
+#define USB_PHY_SWITCH_CTRL 0x0
+#define RG_PHY_SW_TYPE GENMASK(3, 0)
+#define RG_PHY_SW_PCIE 0x0
+#define RG_PHY_SW_USB3 0x1
+#define RG_PHY_SW_SGMII 0x2
+#define RG_PHY_SW_SATA 0x3
+
+#define TPHY_CLKS_CNT 2
enum mtk_phy_version {
MTK_PHY_V1 = 1,
MTK_PHY_V2,
+ MTK_PHY_V3,
};
struct mtk_phy_pdata {
/* avoid RX sensitivity level degradation only for mt8173 */
bool avoid_rx_sen_degradation;
+ /*
+ * workaround only for mt8195, HW fix it for others of V3,
+ * u2phy should use integer mode instead of fractional mode of
+ * 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;
};
@@ -294,20 +307,28 @@ struct mtk_phy_instance {
struct u2phy_banks u2_banks;
struct u3phy_banks u3_banks;
};
- struct clk *ref_clk; /* reference clock of anolog phy */
+ struct clk_bulk_data clks[TPHY_CLKS_CNT];
u32 index;
- u8 type;
+ u32 type;
+ 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;
+ int intr;
+ int discth;
+ int pre_emphasis;
bool bc12_en;
};
struct mtk_tphy {
struct device *dev;
void __iomem *sif_base; /* only shared sif */
- /* deprecated, use @ref_clk instead in phy instance */
- struct clk *u3phya_ref; /* reference clock of usb3 anolog phy */
const struct mtk_phy_pdata *pdata;
struct mtk_phy_instance **phys;
int nphys;
@@ -325,34 +346,32 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
int fm_out;
u32 tmp;
+ /* HW V3 doesn't support slew rate cal anymore */
+ if (tphy->pdata->version == MTK_PHY_V3)
+ return;
+
/* use force value */
if (instance->eye_src)
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);
tmp &= ~(P2F_RG_CYCLECNT | P2F_RG_MONCLK_SEL);
- tmp |= P2F_RG_CYCLECNT_VAL(U3P_FM_DET_CYCLE_CNT);
+ tmp |= FIELD_PREP(P2F_RG_CYCLECNT, U3P_FM_DET_CYCLE_CNT);
if (tphy->pdata->version == MTK_PHY_V1)
- tmp |= P2F_RG_MONCLK_SEL_VAL(instance->index >> 1);
+ tmp |= FIELD_PREP(P2F_RG_MONCLK_SEL, instance->index >> 1);
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,
@@ -361,14 +380,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 */
@@ -384,125 +399,106 @@ 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_field(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCTRL,
+ 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;
+ void __iomem *phya = u3_banks->phya;
+ void __iomem *phyd = u3_banks->phyd;
/* 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);
-
- 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);
-
- 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);
-
- 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);
-
- 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);
-
- 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);
-
- 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_field(phya + U3P_U3_PHYA_DA_REG0, P3A_RG_XTAL_EXT_EN_U3, 2);
+
+ mtk_phy_update_field(phya + U3P_U3_PHYA_REG9, P3A_RG_RX_DAC_MUX, 4);
+
+ mtk_phy_update_field(phya + U3P_U3_PHYA_REG6, P3A_RG_TX_EIDLE_CM, 0xe);
+
+ mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_CDR1,
+ P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1,
+ FIELD_PREP(P3D_RG_CDR_BIR_LTD0, 0xc) |
+ FIELD_PREP(P3D_RG_CDR_BIR_LTD1, 0x3));
+
+ mtk_phy_update_field(phyd + U3P_U3_PHYD_LFPS1, P3D_RG_FWAKE_TH, 0x34);
+
+ mtk_phy_update_field(phyd + U3P_U3_PHYD_RXDET1, P3D_RG_RXDET_STB2_SET, 0x10);
+
+ mtk_phy_update_field(phyd + U3P_U3_PHYD_RXDET2, P3D_RG_RXDET_STB2_SET_P3, 0x10);
dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index);
}
+static void u2_phy_pll_26m_set(struct mtk_tphy *tphy,
+ struct mtk_phy_instance *instance)
+{
+ struct u2phy_banks *u2_banks = &instance->u2_banks;
+ void __iomem *com = u2_banks->com;
+
+ if (!tphy->pdata->sw_pll_48m_to_26m)
+ return;
+
+ mtk_phy_update_field(com + U3P_USBPHYACR0, PA0_USB20_PLL_PREDIV, 0);
+
+ mtk_phy_update_field(com + U3P_USBPHYACR2, PA2_RG_U2PLL_BW, 3);
+
+ writel(P2R_RG_U2PLL_FBDIV_26M, com + U3P_U2PHYA_RESV);
+
+ 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,
struct mtk_phy_instance *instance)
{
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_clear_bits(com + U3P_U2PHYDTM0,
+ P2C_RG_XCVRSEL | P2C_RG_DATAIN | P2C_DTM0_PART_MASK);
- 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);
-
- if (!index) {
- tmp = readl(com + U3P_U2PHYACR4);
- tmp &= ~P2C_U2_GPIO_CTR_MSK;
- writel(tmp, com + U3P_U2PHYACR4);
- }
+ mtk_phy_clear_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HS_100U_U3_EN);
+
+ 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_field(com + U3P_USBPHYACR6, PA6_RG_U2_SQTH, 2);
+
+ /* Workaround only for mt8195, HW fix it for others (V3) */
+ u2_phy_pll_26m_set(tphy, instance);
dev_dbg(tphy->dev, "%s(%d)\n", __func__, index);
}
@@ -513,30 +509,18 @@ 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);
/* 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);
}
@@ -547,30 +531,18 @@ 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);
/* 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);
@@ -582,16 +554,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);
}
}
@@ -624,69 +591,46 @@ 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,
+ FIELD_PREP(P3A_RG_XTAL_EXT_PE1H, 0x2) |
+ FIELD_PREP(P3A_RG_XTAL_EXT_PE2H, 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_field(phya + U3P_U3_PHYA_REG1, P3A_RG_CLKDRV_AMP, 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_field(phya + U3P_U3_PHYA_REG0, P3A_RG_CLKDRV_OFF, 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_field(phya + U3P_U3_PHYA_DA_REG20, P3A_RG_PLL_DELTA1_PE2H, 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_field(phya + U3P_U3_PHYA_DA_REG25, P3A_RG_PLL_DELTA_PE2H, 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);
-
- 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);
-
- 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);
-
- 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_REG5,
+ P3A_RG_PLL_BR_PE2H | P3A_RG_PLL_IC_PE2H,
+ FIELD_PREP(P3A_RG_PLL_BR_PE2H, 0x1) |
+ FIELD_PREP(P3A_RG_PLL_IC_PE2H, 0x1));
+
+ mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG4,
+ P3A_RG_PLL_DIVEN_PE2H | P3A_RG_PLL_BC_PE2H,
+ FIELD_PREP(P3A_RG_PLL_BC_PE2H, 0x3));
+
+ mtk_phy_update_field(phya + U3P_U3_PHYA_DA_REG6, P3A_RG_PLL_IR_PE2H, 0x2);
+
+ mtk_phy_update_field(phya + U3P_U3_PHYA_DA_REG7, P3A_RG_PLL_BP_PE2H, 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_field(u3_banks->phyd + U3P_U3_PHYD_RXDET1,
+ P3D_RG_RXDET_STB2_SET, 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_field(u3_banks->phyd + U3P_U3_PHYD_RXDET2,
+ P3D_RG_RXDET_STB2_SET_P3, 0x10);
/* wait for PCIe subsys register to active */
usleep_range(2500, 3000);
@@ -697,15 +641,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,
@@ -713,15 +654,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,
@@ -729,55 +667,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);
-
- 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);
-
- 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);
-
- 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);
-
- 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);
-
- 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);
-
- 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);
-
- 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);
-
- 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_SIGNAL6,
+ RG_CDR_BIRLTR_GEN1_MSK | RG_CDR_BC_GEN1_MSK,
+ FIELD_PREP(RG_CDR_BIRLTR_GEN1_MSK, 0x6) |
+ FIELD_PREP(RG_CDR_BC_GEN1_MSK, 0x1a));
+
+ mtk_phy_update_field(phyd + ANA_EQ_EYE_CTRL_SIGNAL4, RG_CDR_BIRLTD0_GEN1_MSK, 0x18);
+
+ mtk_phy_update_field(phyd + ANA_EQ_EYE_CTRL_SIGNAL5, RG_CDR_BIRLTD0_GEN3_MSK, 0x06);
+
+ mtk_phy_update_bits(phyd + ANA_RG_CTRL_SIGNAL4,
+ RG_CDR_BICLTR_GEN1_MSK | RG_CDR_BR_GEN2_MSK,
+ FIELD_PREP(RG_CDR_BICLTR_GEN1_MSK, 0x0c) |
+ FIELD_PREP(RG_CDR_BR_GEN2_MSK, 0x07));
+
+ mtk_phy_update_bits(phyd + PHYD_CTRL_SIGNAL_MODE4,
+ RG_CDR_BICLTD0_GEN1_MSK | RG_CDR_BICLTD1_GEN1_MSK,
+ FIELD_PREP(RG_CDR_BICLTD0_GEN1_MSK, 0x08) |
+ FIELD_PREP(RG_CDR_BICLTD1_GEN1_MSK, 0x02));
+
+ mtk_phy_update_field(phyd + PHYD_DESIGN_OPTION2, RG_LOCK_CNT_SEL_MSK, 0x02);
+
+ mtk_phy_update_bits(phyd + PHYD_DESIGN_OPTION9,
+ RG_T2_MIN_MSK | RG_TG_MIN_MSK,
+ FIELD_PREP(RG_T2_MIN_MSK, 0x12) |
+ FIELD_PREP(RG_TG_MIN_MSK, 0x04));
+
+ mtk_phy_update_bits(phyd + PHYD_DESIGN_OPTION9,
+ RG_T2_MAX_MSK | RG_TG_MAX_MSK,
+ FIELD_PREP(RG_T2_MAX_MSK, 0x31) |
+ FIELD_PREP(RG_TG_MAX_MSK, 0x0e));
+
+ mtk_phy_update_field(phyd + ANA_RG_CTRL_SIGNAL1, RG_IDRV_0DB_GEN1_MSK, 0x20);
+
+ mtk_phy_update_field(phyd + ANA_EQ_EYE_CTRL_SIGNAL1, RG_EQ_DLEQ_LFI_GEN1_MSK, 0x03);
dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index);
}
@@ -850,9 +775,17 @@ static void phy_parse_property(struct mtk_tphy *tphy,
&instance->eye_vrt);
device_property_read_u32(dev, "mediatek,eye-term",
&instance->eye_term);
- dev_dbg(dev, "bc12:%d, src:%d, vrt:%d, term:%d\n",
+ device_property_read_u32(dev, "mediatek,intr",
+ &instance->intr);
+ device_property_read_u32(dev, "mediatek,discth",
+ &instance->discth);
+ device_property_read_u32(dev, "mediatek,pre-emphasis",
+ &instance->pre_emphasis);
+ dev_dbg(dev, "bc12:%d, src:%d, vrt:%d, term:%d, intr:%d, disc:%d\n",
instance->bc12_en, instance->eye_src,
- instance->eye_vrt, instance->eye_term);
+ instance->eye_vrt, instance->eye_term,
+ instance->intr, instance->discth);
+ dev_dbg(dev, "pre-emp:%d\n", instance->pre_emphasis);
}
static void u2_phy_props_set(struct mtk_tphy *tphy,
@@ -860,33 +793,207 @@ 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)
+ mtk_phy_update_field(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCTRL,
+ instance->eye_src);
+
+ if (instance->eye_vrt)
+ mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_VRT_SEL,
+ instance->eye_vrt);
+
+ if (instance->eye_term)
+ mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_TERM_SEL,
+ instance->eye_term);
+
+ if (instance->intr) {
+ if (u2_banks->misc)
+ mtk_phy_set_bits(u2_banks->misc + U3P_MISC_REG1,
+ MR1_EFUSE_AUTO_LOAD_DIS);
+
+ mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_INTR_CAL,
+ instance->intr);
+ }
+
+ if (instance->discth)
+ mtk_phy_update_field(com + U3P_USBPHYACR6, PA6_RG_U2_DISCTH,
+ instance->discth);
+
+ if (instance->pre_emphasis)
+ mtk_phy_update_field(com + U3P_USBPHYACR6, PA6_RG_U2_PRE_EMP,
+ instance->pre_emphasis);
+}
+
+/* type switch for usb3/pcie/sgmii/sata */
+static int phy_type_syscon_get(struct mtk_phy_instance *instance,
+ struct device_node *dn)
+{
+ struct of_phandle_args args;
+ int ret;
+
+ /* type switch function is optional */
+ if (!of_property_read_bool(dn, "mediatek,syscon-type"))
+ return 0;
+
+ ret = of_parse_phandle_with_fixed_args(dn, "mediatek,syscon-type",
+ 2, 0, &args);
+ if (ret)
+ return ret;
+
+ instance->type_sw_reg = args.args[0];
+ instance->type_sw_index = args.args[1] & 0x3; /* <=3 */
+ instance->type_sw = syscon_node_to_regmap(args.np);
+ of_node_put(args.np);
+ dev_info(&instance->phy->dev, "type_sw - reg %#x, index %d\n",
+ instance->type_sw_reg, instance->type_sw_index);
+
+ return PTR_ERR_OR_ZERO(instance->type_sw);
+}
+
+static int phy_type_set(struct mtk_phy_instance *instance)
+{
+ int type;
+ u32 offset;
+
+ if (!instance->type_sw)
+ return 0;
+
+ switch (instance->type) {
+ case PHY_TYPE_USB3:
+ type = RG_PHY_SW_USB3;
+ break;
+ case PHY_TYPE_PCIE:
+ type = RG_PHY_SW_PCIE;
+ break;
+ case PHY_TYPE_SGMII:
+ type = RG_PHY_SW_SGMII;
+ break;
+ case PHY_TYPE_SATA:
+ type = RG_PHY_SW_SATA;
+ break;
+ case PHY_TYPE_USB2:
+ default:
+ return 0;
}
- if (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);
+ offset = instance->type_sw_index * BITS_PER_BYTE;
+ regmap_update_bits(instance->type_sw, instance->type_sw_reg,
+ RG_PHY_SW_TYPE << offset, type << offset);
+
+ 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;
}
- 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);
+ /* 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_tx_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;
}
- 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);
+ 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_field(u2_banks->com + U3P_USBPHYACR1, PA1_RG_INTR_CAL,
+ 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_field(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_TX_IMPEL,
+ instance->efuse_tx_imp);
+ mtk_phy_set_bits(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_FORCE_TX_IMPEL);
+
+ mtk_phy_update_field(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_RX_IMPEL,
+ instance->efuse_rx_imp);
+ mtk_phy_set_bits(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_FORCE_RX_IMPEL);
+
+ mtk_phy_update_field(u3_banks->phya + U3P_U3_PHYA_REG0, P3A_RG_IEXT_INTR,
+ instance->efuse_intr);
+ break;
+ default:
+ dev_warn(dev, "no sw efuse for type %d\n", instance->type);
+ break;
}
}
@@ -896,17 +1003,11 @@ static int mtk_phy_init(struct phy *phy)
struct mtk_tphy *tphy = dev_get_drvdata(phy->dev.parent);
int ret;
- ret = clk_prepare_enable(tphy->u3phya_ref);
- if (ret) {
- dev_err(tphy->dev, "failed to enable u3phya_ref\n");
+ ret = clk_bulk_prepare_enable(TPHY_CLKS_CNT, instance->clks);
+ if (ret)
return ret;
- }
- ret = clk_prepare_enable(instance->ref_clk);
- if (ret) {
- dev_err(tphy->dev, "failed to enable ref_clk\n");
- return ret;
- }
+ phy_efuse_set(instance);
switch (instance->type) {
case PHY_TYPE_USB2:
@@ -922,8 +1023,12 @@ static int mtk_phy_init(struct phy *phy)
case PHY_TYPE_SATA:
sata_phy_instance_init(tphy, instance);
break;
+ case PHY_TYPE_SGMII:
+ /* nothing to do, only used to set type */
+ break;
default:
dev_err(tphy->dev, "incompatible PHY type\n");
+ clk_bulk_disable_unprepare(TPHY_CLKS_CNT, instance->clks);
return -EINVAL;
}
@@ -966,8 +1071,7 @@ static int mtk_phy_exit(struct phy *phy)
if (instance->type == PHY_TYPE_USB2)
u2_phy_instance_exit(tphy, instance);
- clk_disable_unprepare(instance->ref_clk);
- clk_disable_unprepare(tphy->u3phya_ref);
+ clk_bulk_disable_unprepare(TPHY_CLKS_CNT, instance->clks);
return 0;
}
@@ -989,6 +1093,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");
@@ -1010,21 +1115,31 @@ static struct phy *mtk_phy_xlate(struct device *dev,
if (!(instance->type == PHY_TYPE_USB2 ||
instance->type == PHY_TYPE_USB3 ||
instance->type == PHY_TYPE_PCIE ||
- instance->type == PHY_TYPE_SATA)) {
+ instance->type == PHY_TYPE_SATA ||
+ instance->type == PHY_TYPE_SGMII)) {
dev_err(dev, "unsupported device type: %d\n", instance->type);
return ERR_PTR(-EINVAL);
}
- if (tphy->pdata->version == MTK_PHY_V1) {
+ switch (tphy->pdata->version) {
+ case MTK_PHY_V1:
phy_v1_banks_init(tphy, instance);
- } else if (tphy->pdata->version == MTK_PHY_V2) {
+ break;
+ case MTK_PHY_V2:
+ case MTK_PHY_V3:
phy_v2_banks_init(tphy, instance);
- } else {
+ break;
+ default:
dev_err(dev, "phy version is not supported\n");
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);
return instance->phy;
}
@@ -1045,20 +1160,34 @@ 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,
+};
+
static const struct mtk_phy_pdata mt8173_pdata = {
.avoid_rx_sen_degradation = true,
.version = MTK_PHY_V1,
};
+static const struct mtk_phy_pdata mt8195_pdata = {
+ .sw_pll_48m_to_26m = true,
+ .sw_efuse_supported = true,
+ .version = MTK_PHY_V3,
+};
+
static const struct of_device_id mtk_tphy_id_table[] = {
{ .compatible = "mediatek,mt2701-u3phy", .data = &tphy_v1_pdata },
{ .compatible = "mediatek,mt2712-u3phy", .data = &tphy_v2_pdata },
{ .compatible = "mediatek,mt8173-u3phy", .data = &mt8173_pdata },
+ { .compatible = "mediatek,mt8195-tphy", .data = &mt8195_pdata },
{ .compatible = "mediatek,generic-tphy-v1", .data = &tphy_v1_pdata },
{ .compatible = "mediatek,generic-tphy-v2", .data = &tphy_v2_pdata },
+ { .compatible = "mediatek,generic-tphy-v3", .data = &tphy_v3_pdata },
{ },
};
MODULE_DEVICE_TABLE(of, mtk_tphy_id_table);
@@ -1102,21 +1231,21 @@ static int mtk_tphy_probe(struct platform_device *pdev)
}
}
- /* it's deprecated, make it optional for backward compatibility */
- tphy->u3phya_ref = devm_clk_get_optional(dev, "u3phya_ref");
- if (IS_ERR(tphy->u3phya_ref))
- return PTR_ERR(tphy->u3phya_ref);
-
- tphy->src_ref_clk = U3P_REF_CLK;
- tphy->src_coef = U3P_SLEW_RATE_COEF;
- /* update parameters of slew rate calibrate if exist */
- device_property_read_u32(dev, "mediatek,src-ref-clk-mhz",
- &tphy->src_ref_clk);
- device_property_read_u32(dev, "mediatek,src-coef", &tphy->src_coef);
+ if (tphy->pdata->version < MTK_PHY_V3) {
+ tphy->src_ref_clk = U3P_REF_CLK;
+ tphy->src_coef = U3P_SLEW_RATE_COEF;
+ /* update parameters of slew rate calibrate if exist */
+ device_property_read_u32(dev, "mediatek,src-ref-clk-mhz",
+ &tphy->src_ref_clk);
+ device_property_read_u32(dev, "mediatek,src-coef",
+ &tphy->src_coef);
+ }
port = 0;
for_each_child_of_node(np, child_np) {
struct mtk_phy_instance *instance;
+ struct clk_bulk_data *clks;
+ struct device *subdev;
struct phy *phy;
instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL);
@@ -1134,16 +1263,16 @@ static int mtk_tphy_probe(struct platform_device *pdev)
goto put_child;
}
+ subdev = &phy->dev;
retval = of_address_to_resource(child_np, 0, &res);
if (retval) {
- dev_err(dev, "failed to get address resource(id-%d)\n",
+ dev_err(subdev, "failed to get address resource(id-%d)\n",
port);
goto put_child;
}
- instance->port_base = devm_ioremap_resource(&phy->dev, &res);
+ instance->port_base = devm_ioremap_resource(subdev, &res);
if (IS_ERR(instance->port_base)) {
- dev_err(dev, "failed to remap phy regs\n");
retval = PTR_ERR(instance->port_base);
goto put_child;
}
@@ -1153,16 +1282,16 @@ static int mtk_tphy_probe(struct platform_device *pdev)
phy_set_drvdata(phy, instance);
port++;
- /* if deprecated clock is provided, ignore instance's one */
- if (tphy->u3phya_ref)
- continue;
+ clks = instance->clks;
+ clks[0].id = "ref"; /* digital (& analog) clock */
+ clks[1].id = "da_ref"; /* analog clock */
+ retval = devm_clk_bulk_get_optional(subdev, TPHY_CLKS_CNT, clks);
+ if (retval)
+ goto put_child;
- instance->ref_clk = devm_clk_get(&phy->dev, "ref");
- if (IS_ERR(instance->ref_clk)) {
- dev_err(dev, "failed to get ref_clk(id-%d)\n", port);
- retval = PTR_ERR(instance->ref_clk);
+ retval = phy_type_syscon_get(instance, child_np);
+ if (retval)
goto put_child;
- }
}
provider = devm_of_phy_provider_register(dev, mtk_phy_xlate);
diff --git a/drivers/phy/mediatek/phy-mtk-ufs.c b/drivers/phy/mediatek/phy-mtk-ufs.c
index cf94f5c35dc5..fc19e0fa8ed5 100644
--- a/drivers/phy/mediatek/phy-mtk-ufs.c
+++ b/drivers/phy/mediatek/phy-mtk-ufs.c
@@ -11,6 +11,8 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include "phy-mtk-io.h"
+
/* mphy register and offsets */
#define MP_GLB_DIG_8C 0x008C
#define FRC_PLL_ISO_EN BIT(8)
@@ -31,41 +33,14 @@
#define FRC_CDR_ISO_EN BIT(19)
#define CDR_ISO_EN BIT(20)
+#define UFSPHY_CLKS_CNT 2
+
struct ufs_mtk_phy {
struct device *dev;
void __iomem *mmio;
- struct clk *mp_clk;
- struct clk *unipro_clk;
+ struct clk_bulk_data clks[UFSPHY_CLKS_CNT];
};
-static inline u32 mphy_readl(struct ufs_mtk_phy *phy, u32 reg)
-{
- return readl(phy->mmio + reg);
-}
-
-static inline void mphy_writel(struct ufs_mtk_phy *phy, u32 val, u32 reg)
-{
- writel(val, phy->mmio + reg);
-}
-
-static void mphy_set_bit(struct ufs_mtk_phy *phy, u32 reg, u32 bit)
-{
- u32 val;
-
- val = mphy_readl(phy, reg);
- val |= bit;
- mphy_writel(phy, val, reg);
-}
-
-static void mphy_clr_bit(struct ufs_mtk_phy *phy, u32 reg, u32 bit)
-{
- u32 val;
-
- val = mphy_readl(phy, reg);
- val &= ~bit;
- mphy_writel(phy, val, reg);
-}
-
static struct ufs_mtk_phy *get_ufs_mtk_phy(struct phy *generic_phy)
{
return (struct ufs_mtk_phy *)phy_get_drvdata(generic_phy);
@@ -74,75 +49,70 @@ static struct ufs_mtk_phy *get_ufs_mtk_phy(struct phy *generic_phy)
static int ufs_mtk_phy_clk_init(struct ufs_mtk_phy *phy)
{
struct device *dev = phy->dev;
+ struct clk_bulk_data *clks = phy->clks;
- phy->unipro_clk = devm_clk_get(dev, "unipro");
- if (IS_ERR(phy->unipro_clk)) {
- dev_err(dev, "failed to get clock: unipro");
- return PTR_ERR(phy->unipro_clk);
- }
-
- phy->mp_clk = devm_clk_get(dev, "mp");
- if (IS_ERR(phy->mp_clk)) {
- dev_err(dev, "failed to get clock: mp");
- return PTR_ERR(phy->mp_clk);
- }
-
- return 0;
+ clks[0].id = "unipro";
+ clks[1].id = "mp";
+ return devm_clk_bulk_get(dev, UFSPHY_CLKS_CNT, clks);
}
static void ufs_mtk_phy_set_active(struct ufs_mtk_phy *phy)
{
+ void __iomem *mmio = phy->mmio;
+
/* release DA_MP_PLL_PWR_ON */
- mphy_set_bit(phy, MP_GLB_DIG_8C, PLL_PWR_ON);
- mphy_clr_bit(phy, MP_GLB_DIG_8C, FRC_FRC_PWR_ON);
+ mtk_phy_set_bits(mmio + MP_GLB_DIG_8C, PLL_PWR_ON);
+ mtk_phy_clear_bits(mmio + MP_GLB_DIG_8C, FRC_FRC_PWR_ON);
/* release DA_MP_PLL_ISO_EN */
- mphy_clr_bit(phy, MP_GLB_DIG_8C, PLL_ISO_EN);
- mphy_clr_bit(phy, MP_GLB_DIG_8C, FRC_PLL_ISO_EN);
+ mtk_phy_clear_bits(mmio + MP_GLB_DIG_8C, PLL_ISO_EN);
+ mtk_phy_clear_bits(mmio + MP_GLB_DIG_8C, FRC_PLL_ISO_EN);
/* release DA_MP_CDR_PWR_ON */
- mphy_set_bit(phy, MP_LN_RX_44, CDR_PWR_ON);
- mphy_clr_bit(phy, MP_LN_RX_44, FRC_CDR_PWR_ON);
+ mtk_phy_set_bits(mmio + MP_LN_RX_44, CDR_PWR_ON);
+ mtk_phy_clear_bits(mmio + MP_LN_RX_44, FRC_CDR_PWR_ON);
/* release DA_MP_CDR_ISO_EN */
- mphy_clr_bit(phy, MP_LN_RX_44, CDR_ISO_EN);
- mphy_clr_bit(phy, MP_LN_RX_44, FRC_CDR_ISO_EN);
+ mtk_phy_clear_bits(mmio + MP_LN_RX_44, CDR_ISO_EN);
+ mtk_phy_clear_bits(mmio + MP_LN_RX_44, FRC_CDR_ISO_EN);
/* release DA_MP_RX0_SQ_EN */
- mphy_set_bit(phy, MP_LN_DIG_RX_AC, RX_SQ_EN);
- mphy_clr_bit(phy, MP_LN_DIG_RX_AC, FRC_RX_SQ_EN);
+ mtk_phy_set_bits(mmio + MP_LN_DIG_RX_AC, RX_SQ_EN);
+ mtk_phy_clear_bits(mmio + MP_LN_DIG_RX_AC, FRC_RX_SQ_EN);
/* delay 1us to wait DIFZ stable */
udelay(1);
/* release DIFZ */
- mphy_clr_bit(phy, MP_LN_DIG_RX_9C, FSM_DIFZ_FRC);
+ mtk_phy_clear_bits(mmio + MP_LN_DIG_RX_9C, FSM_DIFZ_FRC);
}
static void ufs_mtk_phy_set_deep_hibern(struct ufs_mtk_phy *phy)
{
+ void __iomem *mmio = phy->mmio;
+
/* force DIFZ */
- mphy_set_bit(phy, MP_LN_DIG_RX_9C, FSM_DIFZ_FRC);
+ mtk_phy_set_bits(mmio + MP_LN_DIG_RX_9C, FSM_DIFZ_FRC);
/* force DA_MP_RX0_SQ_EN */
- mphy_set_bit(phy, MP_LN_DIG_RX_AC, FRC_RX_SQ_EN);
- mphy_clr_bit(phy, MP_LN_DIG_RX_AC, RX_SQ_EN);
+ mtk_phy_set_bits(mmio + MP_LN_DIG_RX_AC, FRC_RX_SQ_EN);
+ mtk_phy_clear_bits(mmio + MP_LN_DIG_RX_AC, RX_SQ_EN);
/* force DA_MP_CDR_ISO_EN */
- mphy_set_bit(phy, MP_LN_RX_44, FRC_CDR_ISO_EN);
- mphy_set_bit(phy, MP_LN_RX_44, CDR_ISO_EN);
+ mtk_phy_set_bits(mmio + MP_LN_RX_44, FRC_CDR_ISO_EN);
+ mtk_phy_set_bits(mmio + MP_LN_RX_44, CDR_ISO_EN);
/* force DA_MP_CDR_PWR_ON */
- mphy_set_bit(phy, MP_LN_RX_44, FRC_CDR_PWR_ON);
- mphy_clr_bit(phy, MP_LN_RX_44, CDR_PWR_ON);
+ mtk_phy_set_bits(mmio + MP_LN_RX_44, FRC_CDR_PWR_ON);
+ mtk_phy_clear_bits(mmio + MP_LN_RX_44, CDR_PWR_ON);
/* force DA_MP_PLL_ISO_EN */
- mphy_set_bit(phy, MP_GLB_DIG_8C, FRC_PLL_ISO_EN);
- mphy_set_bit(phy, MP_GLB_DIG_8C, PLL_ISO_EN);
+ mtk_phy_set_bits(mmio + MP_GLB_DIG_8C, FRC_PLL_ISO_EN);
+ mtk_phy_set_bits(mmio + MP_GLB_DIG_8C, PLL_ISO_EN);
/* force DA_MP_PLL_PWR_ON */
- mphy_set_bit(phy, MP_GLB_DIG_8C, FRC_FRC_PWR_ON);
- mphy_clr_bit(phy, MP_GLB_DIG_8C, PLL_PWR_ON);
+ mtk_phy_set_bits(mmio + MP_GLB_DIG_8C, FRC_FRC_PWR_ON);
+ mtk_phy_clear_bits(mmio + MP_GLB_DIG_8C, PLL_PWR_ON);
}
static int ufs_mtk_phy_power_on(struct phy *generic_phy)
@@ -150,26 +120,13 @@ static int ufs_mtk_phy_power_on(struct phy *generic_phy)
struct ufs_mtk_phy *phy = get_ufs_mtk_phy(generic_phy);
int ret;
- ret = clk_prepare_enable(phy->unipro_clk);
- if (ret) {
- dev_err(phy->dev, "unipro_clk enable failed %d\n", ret);
- goto out;
- }
-
- ret = clk_prepare_enable(phy->mp_clk);
- if (ret) {
- dev_err(phy->dev, "mp_clk enable failed %d\n", ret);
- goto out_unprepare_unipro_clk;
- }
+ ret = clk_bulk_prepare_enable(UFSPHY_CLKS_CNT, phy->clks);
+ if (ret)
+ return ret;
ufs_mtk_phy_set_active(phy);
return 0;
-
-out_unprepare_unipro_clk:
- clk_disable_unprepare(phy->unipro_clk);
-out:
- return ret;
}
static int ufs_mtk_phy_power_off(struct phy *generic_phy)
@@ -178,8 +135,7 @@ static int ufs_mtk_phy_power_off(struct phy *generic_phy)
ufs_mtk_phy_set_deep_hibern(phy);
- clk_disable_unprepare(phy->unipro_clk);
- clk_disable_unprepare(phy->mp_clk);
+ clk_bulk_disable_unprepare(UFSPHY_CLKS_CNT, phy->clks);
return 0;
}
@@ -195,7 +151,6 @@ static int ufs_mtk_phy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct phy *generic_phy;
struct phy_provider *phy_provider;
- struct resource *res;
struct ufs_mtk_phy *phy;
int ret;
@@ -203,8 +158,7 @@ static int ufs_mtk_phy_probe(struct platform_device *pdev)
if (!phy)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- phy->mmio = devm_ioremap_resource(dev, res);
+ phy->mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(phy->mmio))
return PTR_ERR(phy->mmio);
diff --git a/drivers/phy/mediatek/phy-mtk-xsphy.c b/drivers/phy/mediatek/phy-mtk-xsphy.c
index 8c51131945c0..b222fbbd71d1 100644
--- a/drivers/phy/mediatek/phy-mtk-xsphy.c
+++ b/drivers/phy/mediatek/phy-mtk-xsphy.c
@@ -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
@@ -36,7 +37,6 @@
#define XSP_U2FREQ_FMCR0 ((SSUSB_SIFSLV_U2FREQ) + 0x00)
#define P2F_RG_FREQDET_EN BIT(24)
#define P2F_RG_CYCLECNT GENMASK(23, 0)
-#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x))
#define XSP_U2FREQ_MMONR0 ((SSUSB_SIFSLV_U2FREQ) + 0x0c)
@@ -49,16 +49,12 @@
#define XSP_USBPHYACR1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x04)
#define P2A1_RG_INTR_CAL GENMASK(23, 19)
-#define P2A1_RG_INTR_CAL_VAL(x) ((0x1f & (x)) << 19)
#define P2A1_RG_VRT_SEL GENMASK(14, 12)
-#define P2A1_RG_VRT_SEL_VAL(x) ((0x7 & (x)) << 12)
#define P2A1_RG_TERM_SEL GENMASK(10, 8)
-#define P2A1_RG_TERM_SEL_VAL(x) ((0x7 & (x)) << 8)
#define XSP_USBPHYACR5 ((SSUSB_SIFSLV_U2PHY_COM) + 0x014)
#define P2A5_RG_HSTX_SRCAL_EN BIT(15)
#define P2A5_RG_HSTX_SRCTRL GENMASK(14, 12)
-#define P2A5_RG_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12)
#define XSP_USBPHYACR6 ((SSUSB_SIFSLV_U2PHY_COM) + 0x018)
#define P2A6_RG_BC11_SW_EN BIT(23)
@@ -73,15 +69,12 @@
#define SSPXTP_PHYA_GLB_00 ((SSPXTP_SIFSLV_PHYA_GLB) + 0x00)
#define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(21, 16)
-#define RG_XTP_GLB_BIAS_INTR_CTRL_VAL(x) ((0x3f & (x)) << 16)
#define SSPXTP_PHYA_LN_04 ((SSPXTP_SIFSLV_PHYA_LN) + 0x04)
#define RG_XTP_LN0_TX_IMPSEL GENMASK(4, 0)
-#define RG_XTP_LN0_TX_IMPSEL_VAL(x) (0x1f & (x))
#define SSPXTP_PHYA_LN_14 ((SSPXTP_SIFSLV_PHYA_LN) + 0x014)
#define RG_XTP_LN0_RX_IMPSEL GENMASK(4, 0)
-#define RG_XTP_LN0_RX_IMPSEL_VAL(x) (0x1f & (x))
#define XSP_REF_CLK 26 /* MHZ */
#define XSP_SLEW_RATE_COEF 17
@@ -126,26 +119,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_field(pbase + XSP_U2FREQ_FMCR0, P2F_RG_CYCLECNT,
+ 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 +139,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 +158,21 @@ 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_field(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL, 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 +180,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 +195,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 +269,40 @@ 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_field(pbase + XSP_USBPHYACR1, P2A1_RG_INTR_CAL,
+ 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_field(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL,
+ 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_field(pbase + XSP_USBPHYACR1, P2A1_RG_VRT_SEL,
+ 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_field(pbase + XSP_USBPHYACR1, P2A1_RG_TERM_SEL,
+ 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_field(xsphy->glb_base + SSPXTP_PHYA_GLB_00,
+ RG_XTP_GLB_BIAS_INTR_CTRL, 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_field(pbase + SSPXTP_PHYA_LN_04,
+ RG_XTP_LN0_TX_IMPSEL, 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_field(pbase + SSPXTP_PHYA_LN_14,
+ RG_XTP_LN0_RX_IMPSEL, inst->efuse_rx_imp);
}
static int mtk_phy_init(struct phy *phy)
diff --git a/drivers/phy/microchip/Kconfig b/drivers/phy/microchip/Kconfig
new file mode 100644
index 000000000000..38039ed0754c
--- /dev/null
+++ b/drivers/phy/microchip/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Phy drivers for Microchip devices
+#
+
+config PHY_SPARX5_SERDES
+ tristate "Microchip Sparx5 SerDes PHY driver"
+ select GENERIC_PHY
+ depends on ARCH_SPARX5 || COMPILE_TEST
+ depends on OF
+ depends on HAS_IOMEM
+ help
+ Enable this for support of the 10G/25G SerDes on Microchip Sparx5.
+
+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
diff --git a/drivers/phy/microchip/Makefile b/drivers/phy/microchip/Makefile
new file mode 100644
index 000000000000..fd73b87960a5
--- /dev/null
+++ b/drivers/phy/microchip/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the Microchip phy drivers.
+#
+
+obj-$(CONFIG_PHY_SPARX5_SERDES) := sparx5_serdes.o
+obj-$(CONFIG_PHY_LAN966X_SERDES) := lan966x_serdes.o
diff --git a/drivers/phy/microchip/lan966x_serdes.c b/drivers/phy/microchip/lan966x_serdes.c
new file mode 100644
index 000000000000..c1a41b6cd29b
--- /dev/null
+++ b/drivers/phy/microchip/lan966x_serdes.c
@@ -0,0 +1,623 @@
+// 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), \
+ SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII_TXID, m, c), \
+ SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII_RXID, m, c), \
+ SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII_ID, 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_GMII_ENA,
+ HSIO_HW_CFG_RGMII_0_CFG_SET(0) |
+ HSIO_HW_CFG_RGMII_ENA_SET(BIT(0)) |
+ HSIO_HW_CFG_GMII_ENA_SET(BIT(2))),
+ SERDES_MUX_RGMII(RGMII(1), 3, HSIO_HW_CFG_RGMII_1_CFG |
+ HSIO_HW_CFG_RGMII_ENA |
+ HSIO_HW_CFG_GMII_ENA,
+ HSIO_HW_CFG_RGMII_1_CFG_SET(0) |
+ HSIO_HW_CFG_RGMII_ENA_SET(BIT(1)) |
+ HSIO_HW_CFG_GMII_ENA_SET(BIT(3))),
+ SERDES_MUX_RGMII(RGMII(0), 5, HSIO_HW_CFG_RGMII_0_CFG |
+ HSIO_HW_CFG_RGMII_ENA |
+ HSIO_HW_CFG_GMII_ENA,
+ HSIO_HW_CFG_RGMII_0_CFG_SET(BIT(0)) |
+ HSIO_HW_CFG_RGMII_ENA_SET(BIT(0)) |
+ HSIO_HW_CFG_GMII_ENA_SET(BIT(5))),
+ SERDES_MUX_RGMII(RGMII(1), 6, HSIO_HW_CFG_RGMII_1_CFG |
+ HSIO_HW_CFG_RGMII_ENA |
+ HSIO_HW_CFG_GMII_ENA,
+ HSIO_HW_CFG_RGMII_1_CFG_SET(BIT(0)) |
+ HSIO_HW_CFG_RGMII_ENA_SET(BIT(1)) |
+ HSIO_HW_CFG_GMII_ENA_SET(BIT(6))),
+};
+
+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 lan966x_rgmii_setup(struct serdes_macro *macro, u32 idx, int mode)
+{
+ bool tx_delay = false;
+ bool rx_delay = false;
+
+ /* Configure RGMII */
+ lan_rmw(HSIO_RGMII_CFG_RGMII_RX_RST_SET(0) |
+ HSIO_RGMII_CFG_RGMII_TX_RST_SET(0) |
+ HSIO_RGMII_CFG_TX_CLK_CFG_SET(macro->speed == SPEED_1000 ? 1 :
+ macro->speed == SPEED_100 ? 2 :
+ macro->speed == SPEED_10 ? 3 : 0),
+ HSIO_RGMII_CFG_RGMII_RX_RST |
+ HSIO_RGMII_CFG_RGMII_TX_RST |
+ HSIO_RGMII_CFG_TX_CLK_CFG,
+ macro->ctrl->regs, HSIO_RGMII_CFG(idx));
+
+ if (mode == PHY_INTERFACE_MODE_RGMII ||
+ mode == PHY_INTERFACE_MODE_RGMII_TXID)
+ rx_delay = true;
+
+ if (mode == PHY_INTERFACE_MODE_RGMII ||
+ mode == PHY_INTERFACE_MODE_RGMII_RXID)
+ tx_delay = true;
+
+ /* Setup DLL configuration */
+ lan_rmw(HSIO_DLL_CFG_DLL_RST_SET(0) |
+ HSIO_DLL_CFG_DLL_ENA_SET(rx_delay),
+ HSIO_DLL_CFG_DLL_RST |
+ HSIO_DLL_CFG_DLL_ENA,
+ macro->ctrl->regs, HSIO_DLL_CFG(idx == 0 ? 0x0 : 0x2));
+
+ lan_rmw(HSIO_DLL_CFG_DELAY_ENA_SET(rx_delay),
+ HSIO_DLL_CFG_DELAY_ENA,
+ macro->ctrl->regs, HSIO_DLL_CFG(idx == 0 ? 0x0 : 0x2));
+
+ lan_rmw(HSIO_DLL_CFG_DLL_RST_SET(0) |
+ HSIO_DLL_CFG_DLL_ENA_SET(tx_delay),
+ HSIO_DLL_CFG_DLL_RST |
+ HSIO_DLL_CFG_DLL_ENA,
+ macro->ctrl->regs, HSIO_DLL_CFG(idx == 0 ? 0x1 : 0x3));
+
+ lan_rmw(HSIO_DLL_CFG_DELAY_ENA_SET(tx_delay),
+ HSIO_DLL_CFG_DELAY_ENA,
+ macro->ctrl->regs, HSIO_DLL_CFG(idx == 0 ? 0x1 : 0x3));
+
+ return 0;
+}
+
+static int serdes_set_speed(struct phy *phy, int speed)
+{
+ struct serdes_macro *macro = phy_get_drvdata(phy);
+
+ if (!phy_interface_mode_is_rgmii(macro->mode))
+ return 0;
+
+ macro->speed = speed;
+ lan966x_rgmii_setup(macro, macro->idx - (SERDES6G_MAX + 1), macro->mode);
+
+ return 0;
+}
+
+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;
+
+ if (submode == PHY_INTERFACE_MODE_QUSGMII)
+ submode = PHY_INTERFACE_MODE_QSGMII;
+
+ 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 lan966x_rgmii_setup(macro,
+ macro->idx - (SERDES6G_MAX + 1),
+ macro->mode);
+
+ return -EOPNOTSUPP;
+ }
+
+ return -EINVAL;
+}
+
+static const struct phy_ops serdes_ops = {
+ .set_mode = serdes_set_mode,
+ .set_speed = serdes_set_speed,
+ .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");
diff --git a/drivers/phy/microchip/lan966x_serdes_regs.h b/drivers/phy/microchip/lan966x_serdes_regs.h
new file mode 100644
index 000000000000..ac54cd01fea6
--- /dev/null
+++ b/drivers/phy/microchip/lan966x_serdes_regs.h
@@ -0,0 +1,251 @@
+/* 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)
+
+/* HSIO:HW_CFGSTAT:RGMII_CFG */
+#define HSIO_RGMII_CFG(r) __REG(TARGET_HSIO, 0, 1, 104, 0, 1, 52, 20, r, 2, 4)
+
+#define HSIO_RGMII_CFG_TX_CLK_CFG GENMASK(4, 2)
+#define HSIO_RGMII_CFG_TX_CLK_CFG_SET(x)\
+ FIELD_PREP(HSIO_RGMII_CFG_TX_CLK_CFG, x)
+#define HSIO_RGMII_CFG_TX_CLK_CFG_GET(x)\
+ FIELD_GET(HSIO_RGMII_CFG_TX_CLK_CFG, x)
+
+#define HSIO_RGMII_CFG_RGMII_TX_RST BIT(1)
+#define HSIO_RGMII_CFG_RGMII_TX_RST_SET(x)\
+ FIELD_PREP(HSIO_RGMII_CFG_RGMII_TX_RST, x)
+#define HSIO_RGMII_CFG_RGMII_TX_RST_GET(x)\
+ FIELD_GET(HSIO_RGMII_CFG_RGMII_TX_RST, x)
+
+#define HSIO_RGMII_CFG_RGMII_RX_RST BIT(0)
+#define HSIO_RGMII_CFG_RGMII_RX_RST_SET(x)\
+ FIELD_PREP(HSIO_RGMII_CFG_RGMII_RX_RST, x)
+#define HSIO_RGMII_CFG_RGMII_RX_RST_GET(x)\
+ FIELD_GET(HSIO_RGMII_CFG_RGMII_RX_RST, x)
+
+/* HSIO:HW_CFGSTAT:DLL_CFG */
+#define HSIO_DLL_CFG(r) __REG(TARGET_HSIO, 0, 1, 104, 0, 1, 52, 36, r, 4, 4)
+
+#define HSIO_DLL_CFG_DELAY_ENA BIT(2)
+#define HSIO_DLL_CFG_DELAY_ENA_SET(x)\
+ FIELD_PREP(HSIO_DLL_CFG_DELAY_ENA, x)
+#define HSIO_DLL_CFG_DELAY_ENA_GET(x)\
+ FIELD_GET(HSIO_DLL_CFG_DELAY_ENA, x)
+
+#define HSIO_DLL_CFG_DLL_ENA BIT(1)
+#define HSIO_DLL_CFG_DLL_ENA_SET(x)\
+ FIELD_PREP(HSIO_DLL_CFG_DLL_ENA, x)
+#define HSIO_DLL_CFG_DLL_ENA_GET(x)\
+ FIELD_GET(HSIO_DLL_CFG_DLL_ENA, x)
+
+#define HSIO_DLL_CFG_DLL_RST BIT(0)
+#define HSIO_DLL_CFG_DLL_RST_SET(x)\
+ FIELD_PREP(HSIO_DLL_CFG_DLL_RST, x)
+#define HSIO_DLL_CFG_DLL_RST_GET(x)\
+ FIELD_GET(HSIO_DLL_CFG_DLL_RST, x)
+
+#endif /* _LAN966X_HSIO_REGS_H_ */
diff --git a/drivers/phy/microchip/sparx5_serdes.c b/drivers/phy/microchip/sparx5_serdes.c
new file mode 100644
index 000000000000..ab1b0986aa67
--- /dev/null
+++ b/drivers/phy/microchip/sparx5_serdes.c
@@ -0,0 +1,2517 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Microchip Sparx5 Switch SerDes driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ *
+ * The Sparx5 Chip Register Model can be browsed at this location:
+ * https://github.com/microchip-ung/sparx-5_reginfo
+ * and the datasheet is available here:
+ * https://ww1.microchip.com/downloads/en/DeviceDoc/SparX-5_Family_L2L3_Enterprise_10G_Ethernet_Switches_Datasheet_00003822B.pdf
+ */
+#include <linux/printk.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/phy.h>
+#include <linux/phy/phy.h>
+
+#include "sparx5_serdes.h"
+
+#define SPX5_CMU_MAX 14
+
+#define SPX5_SERDES_10G_START 13
+#define SPX5_SERDES_25G_START 25
+
+enum sparx5_10g28cmu_mode {
+ SPX5_SD10G28_CMU_MAIN = 0,
+ SPX5_SD10G28_CMU_AUX1 = 1,
+ SPX5_SD10G28_CMU_AUX2 = 3,
+ SPX5_SD10G28_CMU_NONE = 4,
+};
+
+enum sparx5_sd25g28_mode_preset_type {
+ SPX5_SD25G28_MODE_PRESET_25000,
+ SPX5_SD25G28_MODE_PRESET_10000,
+ SPX5_SD25G28_MODE_PRESET_5000,
+ SPX5_SD25G28_MODE_PRESET_SD_2G5,
+ SPX5_SD25G28_MODE_PRESET_1000BASEX,
+};
+
+enum sparx5_sd10g28_mode_preset_type {
+ SPX5_SD10G28_MODE_PRESET_10000,
+ SPX5_SD10G28_MODE_PRESET_SFI_5000_6G,
+ SPX5_SD10G28_MODE_PRESET_SFI_5000_10G,
+ SPX5_SD10G28_MODE_PRESET_QSGMII,
+ SPX5_SD10G28_MODE_PRESET_SD_2G5,
+ SPX5_SD10G28_MODE_PRESET_1000BASEX,
+};
+
+struct sparx5_serdes_io_resource {
+ enum sparx5_serdes_target id;
+ phys_addr_t offset;
+};
+
+struct sparx5_sd25g28_mode_preset {
+ u8 bitwidth;
+ u8 tx_pre_div;
+ u8 fifo_ck_div;
+ u8 pre_divsel;
+ u8 vco_div_mode;
+ u8 sel_div;
+ u8 ck_bitwidth;
+ u8 subrate;
+ u8 com_txcal_en;
+ u8 com_tx_reserve_msb;
+ u8 com_tx_reserve_lsb;
+ u8 cfg_itx_ipcml_base;
+ u8 tx_reserve_lsb;
+ u8 tx_reserve_msb;
+ u8 bw;
+ u8 rxterm;
+ u8 dfe_tap;
+ u8 dfe_enable;
+ bool txmargin;
+ u8 cfg_ctle_rstn;
+ u8 r_dfe_rstn;
+ u8 cfg_pi_bw_3_0;
+ u8 tx_tap_dly;
+ u8 tx_tap_adv;
+};
+
+struct sparx5_sd25g28_media_preset {
+ u8 cfg_eq_c_force_3_0;
+ u8 cfg_vga_ctrl_byp_4_0;
+ u8 cfg_eq_r_force_3_0;
+ u8 cfg_en_adv;
+ u8 cfg_en_main;
+ u8 cfg_en_dly;
+ u8 cfg_tap_adv_3_0;
+ u8 cfg_tap_main;
+ u8 cfg_tap_dly_4_0;
+ u8 cfg_alos_thr_2_0;
+};
+
+struct sparx5_sd25g28_args {
+ u8 if_width; /* UDL if-width: 10/16/20/32/64 */
+ bool skip_cmu_cfg:1; /* Enable/disable CMU cfg */
+ enum sparx5_10g28cmu_mode cmu_sel; /* Device/Mode serdes uses */
+ bool no_pwrcycle:1; /* Omit initial power-cycle */
+ bool txinvert:1; /* Enable inversion of output data */
+ bool rxinvert:1; /* Enable inversion of input data */
+ u16 txswing; /* Set output level */
+ u8 rate; /* Rate of network interface */
+ u8 pi_bw_gen1;
+ u8 duty_cycle; /* Set output level to half/full */
+ bool mute:1; /* Mute Output Buffer */
+ bool reg_rst:1;
+ u8 com_pll_reserve;
+};
+
+struct sparx5_sd25g28_params {
+ u8 reg_rst;
+ u8 cfg_jc_byp;
+ u8 cfg_common_reserve_7_0;
+ u8 r_reg_manual;
+ u8 r_d_width_ctrl_from_hwt;
+ u8 r_d_width_ctrl_2_0;
+ u8 r_txfifo_ck_div_pmad_2_0;
+ u8 r_rxfifo_ck_div_pmad_2_0;
+ u8 cfg_pll_lol_set;
+ u8 cfg_vco_div_mode_1_0;
+ u8 cfg_pre_divsel_1_0;
+ u8 cfg_sel_div_3_0;
+ u8 cfg_vco_start_code_3_0;
+ u8 cfg_pma_tx_ck_bitwidth_2_0;
+ u8 cfg_tx_prediv_1_0;
+ u8 cfg_rxdiv_sel_2_0;
+ u8 cfg_tx_subrate_2_0;
+ u8 cfg_rx_subrate_2_0;
+ u8 r_multi_lane_mode;
+ u8 cfg_cdrck_en;
+ u8 cfg_dfeck_en;
+ u8 cfg_dfe_pd;
+ u8 cfg_dfedmx_pd;
+ u8 cfg_dfetap_en_5_1;
+ u8 cfg_dmux_pd;
+ u8 cfg_dmux_clk_pd;
+ u8 cfg_erramp_pd;
+ u8 cfg_pi_dfe_en;
+ u8 cfg_pi_en;
+ u8 cfg_pd_ctle;
+ u8 cfg_summer_en;
+ u8 cfg_pmad_ck_pd;
+ u8 cfg_pd_clk;
+ u8 cfg_pd_cml;
+ u8 cfg_pd_driver;
+ u8 cfg_rx_reg_pu;
+ u8 cfg_pd_rms_det;
+ u8 cfg_dcdr_pd;
+ u8 cfg_ecdr_pd;
+ u8 cfg_pd_sq;
+ u8 cfg_itx_ipdriver_base_2_0;
+ u8 cfg_tap_dly_4_0;
+ u8 cfg_tap_main;
+ u8 cfg_en_main;
+ u8 cfg_tap_adv_3_0;
+ u8 cfg_en_adv;
+ u8 cfg_en_dly;
+ u8 cfg_iscan_en;
+ u8 l1_pcs_en_fast_iscan;
+ u8 l0_cfg_bw_1_0;
+ u8 l0_cfg_txcal_en;
+ u8 cfg_en_dummy;
+ u8 cfg_pll_reserve_3_0;
+ u8 l0_cfg_tx_reserve_15_8;
+ u8 l0_cfg_tx_reserve_7_0;
+ u8 cfg_tx_reserve_15_8;
+ u8 cfg_tx_reserve_7_0;
+ u8 cfg_bw_1_0;
+ u8 cfg_txcal_man_en;
+ u8 cfg_phase_man_4_0;
+ u8 cfg_quad_man_1_0;
+ u8 cfg_txcal_shift_code_5_0;
+ u8 cfg_txcal_valid_sel_3_0;
+ u8 cfg_txcal_en;
+ u8 cfg_cdr_kf_2_0;
+ u8 cfg_cdr_m_7_0;
+ u8 cfg_pi_bw_3_0;
+ u8 cfg_pi_steps_1_0;
+ u8 cfg_dis_2ndorder;
+ u8 cfg_ctle_rstn;
+ u8 r_dfe_rstn;
+ u8 cfg_alos_thr_2_0;
+ u8 cfg_itx_ipcml_base_1_0;
+ u8 cfg_rx_reserve_7_0;
+ u8 cfg_rx_reserve_15_8;
+ u8 cfg_rxterm_2_0;
+ u8 cfg_fom_selm;
+ u8 cfg_rx_sp_ctle_1_0;
+ u8 cfg_isel_ctle_1_0;
+ u8 cfg_vga_ctrl_byp_4_0;
+ u8 cfg_vga_byp;
+ u8 cfg_agc_adpt_byp;
+ u8 cfg_eqr_byp;
+ u8 cfg_eqr_force_3_0;
+ u8 cfg_eqc_force_3_0;
+ u8 cfg_sum_setcm_en;
+ u8 cfg_init_pos_iscan_6_0;
+ u8 cfg_init_pos_ipi_6_0;
+ u8 cfg_dfedig_m_2_0;
+ u8 cfg_en_dfedig;
+ u8 cfg_pi_DFE_en;
+ u8 cfg_tx2rx_lp_en;
+ u8 cfg_txlb_en;
+ u8 cfg_rx2tx_lp_en;
+ u8 cfg_rxlb_en;
+ u8 r_tx_pol_inv;
+ u8 r_rx_pol_inv;
+};
+
+struct sparx5_sd10g28_media_preset {
+ u8 cfg_en_adv;
+ u8 cfg_en_main;
+ u8 cfg_en_dly;
+ u8 cfg_tap_adv_3_0;
+ u8 cfg_tap_main;
+ u8 cfg_tap_dly_4_0;
+ u8 cfg_vga_ctrl_3_0;
+ u8 cfg_vga_cp_2_0;
+ u8 cfg_eq_res_3_0;
+ u8 cfg_eq_r_byp;
+ u8 cfg_eq_c_force_3_0;
+ u8 cfg_alos_thr_3_0;
+};
+
+struct sparx5_sd10g28_mode_preset {
+ u8 bwidth; /* interface width: 10/16/20/32/64 */
+ enum sparx5_10g28cmu_mode cmu_sel; /* Device/Mode serdes uses */
+ u8 rate; /* Rate of network interface */
+ u8 dfe_tap;
+ u8 dfe_enable;
+ u8 pi_bw_gen1;
+ u8 duty_cycle; /* Set output level to half/full */
+};
+
+struct sparx5_sd10g28_args {
+ bool skip_cmu_cfg:1; /* Enable/disable CMU cfg */
+ bool no_pwrcycle:1; /* Omit initial power-cycle */
+ bool txinvert:1; /* Enable inversion of output data */
+ bool rxinvert:1; /* Enable inversion of input data */
+ bool txmargin:1; /* Set output level to half/full */
+ u16 txswing; /* Set output level */
+ bool mute:1; /* Mute Output Buffer */
+ bool is_6g:1;
+ bool reg_rst:1;
+};
+
+struct sparx5_sd10g28_params {
+ u8 cmu_sel;
+ u8 is_6g;
+ u8 skip_cmu_cfg;
+ u8 cfg_lane_reserve_7_0;
+ u8 cfg_ssc_rtl_clk_sel;
+ u8 cfg_lane_reserve_15_8;
+ u8 cfg_txrate_1_0;
+ u8 cfg_rxrate_1_0;
+ u8 r_d_width_ctrl_2_0;
+ u8 cfg_pma_tx_ck_bitwidth_2_0;
+ u8 cfg_rxdiv_sel_2_0;
+ u8 r_pcs2pma_phymode_4_0;
+ u8 cfg_lane_id_2_0;
+ u8 cfg_cdrck_en;
+ u8 cfg_dfeck_en;
+ u8 cfg_dfe_pd;
+ u8 cfg_dfetap_en_5_1;
+ u8 cfg_erramp_pd;
+ u8 cfg_pi_DFE_en;
+ u8 cfg_pi_en;
+ u8 cfg_pd_ctle;
+ u8 cfg_summer_en;
+ u8 cfg_pd_rx_cktree;
+ u8 cfg_pd_clk;
+ u8 cfg_pd_cml;
+ u8 cfg_pd_driver;
+ u8 cfg_rx_reg_pu;
+ u8 cfg_d_cdr_pd;
+ u8 cfg_pd_sq;
+ u8 cfg_rxdet_en;
+ u8 cfg_rxdet_str;
+ u8 r_multi_lane_mode;
+ u8 cfg_en_adv;
+ u8 cfg_en_main;
+ u8 cfg_en_dly;
+ u8 cfg_tap_adv_3_0;
+ u8 cfg_tap_main;
+ u8 cfg_tap_dly_4_0;
+ u8 cfg_vga_ctrl_3_0;
+ u8 cfg_vga_cp_2_0;
+ u8 cfg_eq_res_3_0;
+ u8 cfg_eq_r_byp;
+ u8 cfg_eq_c_force_3_0;
+ u8 cfg_en_dfedig;
+ u8 cfg_sum_setcm_en;
+ u8 cfg_en_preemph;
+ u8 cfg_itx_ippreemp_base_1_0;
+ u8 cfg_itx_ipdriver_base_2_0;
+ u8 cfg_ibias_tune_reserve_5_0;
+ u8 cfg_txswing_half;
+ u8 cfg_dis_2nd_order;
+ u8 cfg_rx_ssc_lh;
+ u8 cfg_pi_floop_steps_1_0;
+ u8 cfg_pi_ext_dac_23_16;
+ u8 cfg_pi_ext_dac_15_8;
+ u8 cfg_iscan_ext_dac_7_0;
+ u8 cfg_cdr_kf_gen1_2_0;
+ u8 cfg_cdr_kf_gen2_2_0;
+ u8 cfg_cdr_kf_gen3_2_0;
+ u8 cfg_cdr_kf_gen4_2_0;
+ u8 r_cdr_m_gen1_7_0;
+ u8 cfg_pi_bw_gen1_3_0;
+ u8 cfg_pi_bw_gen2;
+ u8 cfg_pi_bw_gen3;
+ u8 cfg_pi_bw_gen4;
+ u8 cfg_pi_ext_dac_7_0;
+ u8 cfg_pi_steps;
+ u8 cfg_mp_max_3_0;
+ u8 cfg_rstn_dfedig;
+ u8 cfg_alos_thr_3_0;
+ u8 cfg_predrv_slewrate_1_0;
+ u8 cfg_itx_ipcml_base_1_0;
+ u8 cfg_ip_pre_base_1_0;
+ u8 r_cdr_m_gen2_7_0;
+ u8 r_cdr_m_gen3_7_0;
+ u8 r_cdr_m_gen4_7_0;
+ u8 r_en_auto_cdr_rstn;
+ u8 cfg_oscal_afe;
+ u8 cfg_pd_osdac_afe;
+ u8 cfg_resetb_oscal_afe[2];
+ u8 cfg_center_spreading;
+ u8 cfg_m_cnt_maxval_4_0;
+ u8 cfg_ncnt_maxval_7_0;
+ u8 cfg_ncnt_maxval_10_8;
+ u8 cfg_ssc_en;
+ u8 cfg_tx2rx_lp_en;
+ u8 cfg_txlb_en;
+ u8 cfg_rx2tx_lp_en;
+ u8 cfg_rxlb_en;
+ u8 r_tx_pol_inv;
+ u8 r_rx_pol_inv;
+ u8 fx_100;
+};
+
+static struct sparx5_sd25g28_media_preset media_presets_25g[] = {
+ { /* ETH_MEDIA_DEFAULT */
+ .cfg_en_adv = 0,
+ .cfg_en_main = 1,
+ .cfg_en_dly = 0,
+ .cfg_tap_adv_3_0 = 0,
+ .cfg_tap_main = 1,
+ .cfg_tap_dly_4_0 = 0,
+ .cfg_eq_c_force_3_0 = 0xf,
+ .cfg_vga_ctrl_byp_4_0 = 4,
+ .cfg_eq_r_force_3_0 = 12,
+ .cfg_alos_thr_2_0 = 7,
+ },
+ { /* ETH_MEDIA_SR */
+ .cfg_en_adv = 1,
+ .cfg_en_main = 1,
+ .cfg_en_dly = 1,
+ .cfg_tap_adv_3_0 = 0,
+ .cfg_tap_main = 1,
+ .cfg_tap_dly_4_0 = 0x10,
+ .cfg_eq_c_force_3_0 = 0xf,
+ .cfg_vga_ctrl_byp_4_0 = 8,
+ .cfg_eq_r_force_3_0 = 4,
+ .cfg_alos_thr_2_0 = 0,
+ },
+ { /* ETH_MEDIA_DAC */
+ .cfg_en_adv = 0,
+ .cfg_en_main = 1,
+ .cfg_en_dly = 0,
+ .cfg_tap_adv_3_0 = 0,
+ .cfg_tap_main = 1,
+ .cfg_tap_dly_4_0 = 0,
+ .cfg_eq_c_force_3_0 = 0xf,
+ .cfg_vga_ctrl_byp_4_0 = 8,
+ .cfg_eq_r_force_3_0 = 0xc,
+ .cfg_alos_thr_2_0 = 0,
+ },
+};
+
+static struct sparx5_sd25g28_mode_preset mode_presets_25g[] = {
+ { /* SPX5_SD25G28_MODE_PRESET_25000 */
+ .bitwidth = 40,
+ .tx_pre_div = 0,
+ .fifo_ck_div = 0,
+ .pre_divsel = 1,
+ .vco_div_mode = 0,
+ .sel_div = 15,
+ .ck_bitwidth = 3,
+ .subrate = 0,
+ .com_txcal_en = 0,
+ .com_tx_reserve_msb = (0x26 << 1),
+ .com_tx_reserve_lsb = 0xf0,
+ .cfg_itx_ipcml_base = 0,
+ .tx_reserve_msb = 0xcc,
+ .tx_reserve_lsb = 0xfe,
+ .bw = 3,
+ .rxterm = 0,
+ .dfe_enable = 1,
+ .dfe_tap = 0x1f,
+ .txmargin = 1,
+ .cfg_ctle_rstn = 1,
+ .r_dfe_rstn = 1,
+ .cfg_pi_bw_3_0 = 0,
+ .tx_tap_dly = 8,
+ .tx_tap_adv = 0xc,
+ },
+ { /* SPX5_SD25G28_MODE_PRESET_10000 */
+ .bitwidth = 64,
+ .tx_pre_div = 0,
+ .fifo_ck_div = 2,
+ .pre_divsel = 0,
+ .vco_div_mode = 1,
+ .sel_div = 9,
+ .ck_bitwidth = 0,
+ .subrate = 0,
+ .com_txcal_en = 1,
+ .com_tx_reserve_msb = (0x20 << 1),
+ .com_tx_reserve_lsb = 0x40,
+ .cfg_itx_ipcml_base = 0,
+ .tx_reserve_msb = 0x4c,
+ .tx_reserve_lsb = 0x44,
+ .bw = 3,
+ .cfg_pi_bw_3_0 = 0,
+ .rxterm = 3,
+ .dfe_enable = 1,
+ .dfe_tap = 0x1f,
+ .txmargin = 0,
+ .cfg_ctle_rstn = 1,
+ .r_dfe_rstn = 1,
+ .tx_tap_dly = 0,
+ .tx_tap_adv = 0,
+ },
+ { /* SPX5_SD25G28_MODE_PRESET_5000 */
+ .bitwidth = 64,
+ .tx_pre_div = 0,
+ .fifo_ck_div = 2,
+ .pre_divsel = 0,
+ .vco_div_mode = 2,
+ .sel_div = 9,
+ .ck_bitwidth = 0,
+ .subrate = 0,
+ .com_txcal_en = 1,
+ .com_tx_reserve_msb = (0x20 << 1),
+ .com_tx_reserve_lsb = 0,
+ .cfg_itx_ipcml_base = 0,
+ .tx_reserve_msb = 0xe,
+ .tx_reserve_lsb = 0x80,
+ .bw = 0,
+ .rxterm = 0,
+ .cfg_pi_bw_3_0 = 6,
+ .dfe_enable = 0,
+ .dfe_tap = 0,
+ .tx_tap_dly = 0,
+ .tx_tap_adv = 0,
+ },
+ { /* SPX5_SD25G28_MODE_PRESET_SD_2G5 */
+ .bitwidth = 10,
+ .tx_pre_div = 0,
+ .fifo_ck_div = 0,
+ .pre_divsel = 0,
+ .vco_div_mode = 1,
+ .sel_div = 6,
+ .ck_bitwidth = 3,
+ .subrate = 2,
+ .com_txcal_en = 1,
+ .com_tx_reserve_msb = (0x26 << 1),
+ .com_tx_reserve_lsb = (0xf << 4),
+ .cfg_itx_ipcml_base = 2,
+ .tx_reserve_msb = 0x8,
+ .tx_reserve_lsb = 0x8a,
+ .bw = 0,
+ .cfg_pi_bw_3_0 = 0,
+ .rxterm = (1 << 2),
+ .dfe_enable = 0,
+ .dfe_tap = 0,
+ .tx_tap_dly = 0,
+ .tx_tap_adv = 0,
+ },
+ { /* SPX5_SD25G28_MODE_PRESET_1000BASEX */
+ .bitwidth = 10,
+ .tx_pre_div = 0,
+ .fifo_ck_div = 1,
+ .pre_divsel = 0,
+ .vco_div_mode = 1,
+ .sel_div = 8,
+ .ck_bitwidth = 3,
+ .subrate = 3,
+ .com_txcal_en = 1,
+ .com_tx_reserve_msb = (0x26 << 1),
+ .com_tx_reserve_lsb = 0xf0,
+ .cfg_itx_ipcml_base = 0,
+ .tx_reserve_msb = 0x8,
+ .tx_reserve_lsb = 0xce,
+ .bw = 0,
+ .rxterm = 0,
+ .cfg_pi_bw_3_0 = 0,
+ .dfe_enable = 0,
+ .dfe_tap = 0,
+ .tx_tap_dly = 0,
+ .tx_tap_adv = 0,
+ },
+};
+
+static struct sparx5_sd10g28_media_preset media_presets_10g[] = {
+ { /* ETH_MEDIA_DEFAULT */
+ .cfg_en_adv = 0,
+ .cfg_en_main = 1,
+ .cfg_en_dly = 0,
+ .cfg_tap_adv_3_0 = 0,
+ .cfg_tap_main = 1,
+ .cfg_tap_dly_4_0 = 0,
+ .cfg_vga_ctrl_3_0 = 5,
+ .cfg_vga_cp_2_0 = 0,
+ .cfg_eq_res_3_0 = 0xa,
+ .cfg_eq_r_byp = 1,
+ .cfg_eq_c_force_3_0 = 0x8,
+ .cfg_alos_thr_3_0 = 0x3,
+ },
+ { /* ETH_MEDIA_SR */
+ .cfg_en_adv = 1,
+ .cfg_en_main = 1,
+ .cfg_en_dly = 1,
+ .cfg_tap_adv_3_0 = 0,
+ .cfg_tap_main = 1,
+ .cfg_tap_dly_4_0 = 0xc,
+ .cfg_vga_ctrl_3_0 = 0xa,
+ .cfg_vga_cp_2_0 = 0x4,
+ .cfg_eq_res_3_0 = 0xa,
+ .cfg_eq_r_byp = 1,
+ .cfg_eq_c_force_3_0 = 0xF,
+ .cfg_alos_thr_3_0 = 0x3,
+ },
+ { /* ETH_MEDIA_DAC */
+ .cfg_en_adv = 1,
+ .cfg_en_main = 1,
+ .cfg_en_dly = 1,
+ .cfg_tap_adv_3_0 = 12,
+ .cfg_tap_main = 1,
+ .cfg_tap_dly_4_0 = 8,
+ .cfg_vga_ctrl_3_0 = 0xa,
+ .cfg_vga_cp_2_0 = 4,
+ .cfg_eq_res_3_0 = 0xa,
+ .cfg_eq_r_byp = 1,
+ .cfg_eq_c_force_3_0 = 0xf,
+ .cfg_alos_thr_3_0 = 0x0,
+ }
+};
+
+static struct sparx5_sd10g28_mode_preset mode_presets_10g[] = {
+ { /* SPX5_SD10G28_MODE_PRESET_10000 */
+ .bwidth = 64,
+ .cmu_sel = SPX5_SD10G28_CMU_MAIN,
+ .rate = 0x0,
+ .dfe_enable = 1,
+ .dfe_tap = 0x1f,
+ .pi_bw_gen1 = 0x0,
+ .duty_cycle = 0x2,
+ },
+ { /* SPX5_SD10G28_MODE_PRESET_SFI_5000_6G */
+ .bwidth = 16,
+ .cmu_sel = SPX5_SD10G28_CMU_MAIN,
+ .rate = 0x1,
+ .dfe_enable = 0,
+ .dfe_tap = 0,
+ .pi_bw_gen1 = 0x5,
+ .duty_cycle = 0x0,
+ },
+ { /* SPX5_SD10G28_MODE_PRESET_SFI_5000_10G */
+ .bwidth = 64,
+ .cmu_sel = SPX5_SD10G28_CMU_MAIN,
+ .rate = 0x1,
+ .dfe_enable = 0,
+ .dfe_tap = 0,
+ .pi_bw_gen1 = 0x5,
+ .duty_cycle = 0x0,
+ },
+ { /* SPX5_SD10G28_MODE_PRESET_QSGMII */
+ .bwidth = 20,
+ .cmu_sel = SPX5_SD10G28_CMU_AUX1,
+ .rate = 0x1,
+ .dfe_enable = 0,
+ .dfe_tap = 0,
+ .pi_bw_gen1 = 0x5,
+ .duty_cycle = 0x0,
+ },
+ { /* SPX5_SD10G28_MODE_PRESET_SD_2G5 */
+ .bwidth = 10,
+ .cmu_sel = SPX5_SD10G28_CMU_AUX2,
+ .rate = 0x2,
+ .dfe_enable = 0,
+ .dfe_tap = 0,
+ .pi_bw_gen1 = 0x7,
+ .duty_cycle = 0x0,
+ },
+ { /* SPX5_SD10G28_MODE_PRESET_1000BASEX */
+ .bwidth = 10,
+ .cmu_sel = SPX5_SD10G28_CMU_AUX1,
+ .rate = 0x3,
+ .dfe_enable = 0,
+ .dfe_tap = 0,
+ .pi_bw_gen1 = 0x7,
+ .duty_cycle = 0x0,
+ },
+};
+
+/* map from SD25G28 interface width to configuration value */
+static u8 sd25g28_get_iw_setting(struct device *dev, const u8 interface_width)
+{
+ switch (interface_width) {
+ case 10: return 0;
+ case 16: return 1;
+ case 32: return 3;
+ case 40: return 4;
+ case 64: return 5;
+ default:
+ dev_err(dev, "%s: Illegal value %d for interface width\n",
+ __func__, interface_width);
+ }
+ return 0;
+}
+
+/* map from SD10G28 interface width to configuration value */
+static u8 sd10g28_get_iw_setting(struct device *dev, const u8 interface_width)
+{
+ switch (interface_width) {
+ case 10: return 0;
+ case 16: return 1;
+ case 20: return 2;
+ case 32: return 3;
+ case 40: return 4;
+ case 64: return 7;
+ default:
+ dev_err(dev, "%s: Illegal value %d for interface width\n", __func__,
+ interface_width);
+ return 0;
+ }
+}
+
+static int sparx5_sd10g25_get_mode_preset(struct sparx5_serdes_macro *macro,
+ struct sparx5_sd25g28_mode_preset *mode)
+{
+ switch (macro->serdesmode) {
+ case SPX5_SD_MODE_SFI:
+ if (macro->speed == SPEED_25000)
+ *mode = mode_presets_25g[SPX5_SD25G28_MODE_PRESET_25000];
+ else if (macro->speed == SPEED_10000)
+ *mode = mode_presets_25g[SPX5_SD25G28_MODE_PRESET_10000];
+ else if (macro->speed == SPEED_5000)
+ *mode = mode_presets_25g[SPX5_SD25G28_MODE_PRESET_5000];
+ break;
+ case SPX5_SD_MODE_2G5:
+ *mode = mode_presets_25g[SPX5_SD25G28_MODE_PRESET_SD_2G5];
+ break;
+ case SPX5_SD_MODE_1000BASEX:
+ *mode = mode_presets_25g[SPX5_SD25G28_MODE_PRESET_1000BASEX];
+ break;
+ case SPX5_SD_MODE_100FX:
+ /* Not supported */
+ return -EINVAL;
+ default:
+ *mode = mode_presets_25g[SPX5_SD25G28_MODE_PRESET_25000];
+ break;
+ }
+ return 0;
+}
+
+static int sparx5_sd10g28_get_mode_preset(struct sparx5_serdes_macro *macro,
+ struct sparx5_sd10g28_mode_preset *mode,
+ struct sparx5_sd10g28_args *args)
+{
+ switch (macro->serdesmode) {
+ case SPX5_SD_MODE_SFI:
+ if (macro->speed == SPEED_10000) {
+ *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_10000];
+ } else if (macro->speed == SPEED_5000) {
+ if (args->is_6g)
+ *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_SFI_5000_6G];
+ else
+ *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_SFI_5000_10G];
+ } else {
+ dev_err(macro->priv->dev, "%s: Illegal speed: %02u, sidx: %02u, mode (%u)",
+ __func__, macro->speed, macro->sidx,
+ macro->serdesmode);
+ return -EINVAL;
+ }
+ break;
+ case SPX5_SD_MODE_QSGMII:
+ *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_QSGMII];
+ break;
+ case SPX5_SD_MODE_2G5:
+ *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_SD_2G5];
+ break;
+ case SPX5_SD_MODE_100FX:
+ case SPX5_SD_MODE_1000BASEX:
+ *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_1000BASEX];
+ break;
+ default:
+ *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_10000];
+ break;
+ }
+ return 0;
+}
+
+static void sparx5_sd25g28_get_params(struct sparx5_serdes_macro *macro,
+ struct sparx5_sd25g28_media_preset *media,
+ struct sparx5_sd25g28_mode_preset *mode,
+ struct sparx5_sd25g28_args *args,
+ struct sparx5_sd25g28_params *params)
+{
+ u8 iw = sd25g28_get_iw_setting(macro->priv->dev, mode->bitwidth);
+ struct sparx5_sd25g28_params init = {
+ .r_d_width_ctrl_2_0 = iw,
+ .r_txfifo_ck_div_pmad_2_0 = mode->fifo_ck_div,
+ .r_rxfifo_ck_div_pmad_2_0 = mode->fifo_ck_div,
+ .cfg_vco_div_mode_1_0 = mode->vco_div_mode,
+ .cfg_pre_divsel_1_0 = mode->pre_divsel,
+ .cfg_sel_div_3_0 = mode->sel_div,
+ .cfg_vco_start_code_3_0 = 0,
+ .cfg_pma_tx_ck_bitwidth_2_0 = mode->ck_bitwidth,
+ .cfg_tx_prediv_1_0 = mode->tx_pre_div,
+ .cfg_rxdiv_sel_2_0 = mode->ck_bitwidth,
+ .cfg_tx_subrate_2_0 = mode->subrate,
+ .cfg_rx_subrate_2_0 = mode->subrate,
+ .r_multi_lane_mode = 0,
+ .cfg_cdrck_en = 1,
+ .cfg_dfeck_en = mode->dfe_enable,
+ .cfg_dfe_pd = mode->dfe_enable == 1 ? 0 : 1,
+ .cfg_dfedmx_pd = 1,
+ .cfg_dfetap_en_5_1 = mode->dfe_tap,
+ .cfg_dmux_pd = 0,
+ .cfg_dmux_clk_pd = 1,
+ .cfg_erramp_pd = mode->dfe_enable == 1 ? 0 : 1,
+ .cfg_pi_DFE_en = mode->dfe_enable,
+ .cfg_pi_en = 1,
+ .cfg_pd_ctle = 0,
+ .cfg_summer_en = 1,
+ .cfg_pmad_ck_pd = 0,
+ .cfg_pd_clk = 0,
+ .cfg_pd_cml = 0,
+ .cfg_pd_driver = 0,
+ .cfg_rx_reg_pu = 1,
+ .cfg_pd_rms_det = 1,
+ .cfg_dcdr_pd = 0,
+ .cfg_ecdr_pd = 1,
+ .cfg_pd_sq = 1,
+ .cfg_itx_ipdriver_base_2_0 = mode->txmargin,
+ .cfg_tap_dly_4_0 = media->cfg_tap_dly_4_0,
+ .cfg_tap_main = media->cfg_tap_main,
+ .cfg_en_main = media->cfg_en_main,
+ .cfg_tap_adv_3_0 = media->cfg_tap_adv_3_0,
+ .cfg_en_adv = media->cfg_en_adv,
+ .cfg_en_dly = media->cfg_en_dly,
+ .cfg_iscan_en = 0,
+ .l1_pcs_en_fast_iscan = 0,
+ .l0_cfg_bw_1_0 = 0,
+ .cfg_en_dummy = 0,
+ .cfg_pll_reserve_3_0 = args->com_pll_reserve,
+ .l0_cfg_txcal_en = mode->com_txcal_en,
+ .l0_cfg_tx_reserve_15_8 = mode->com_tx_reserve_msb,
+ .l0_cfg_tx_reserve_7_0 = mode->com_tx_reserve_lsb,
+ .cfg_tx_reserve_15_8 = mode->tx_reserve_msb,
+ .cfg_tx_reserve_7_0 = mode->tx_reserve_lsb,
+ .cfg_bw_1_0 = mode->bw,
+ .cfg_txcal_man_en = 1,
+ .cfg_phase_man_4_0 = 0,
+ .cfg_quad_man_1_0 = 0,
+ .cfg_txcal_shift_code_5_0 = 2,
+ .cfg_txcal_valid_sel_3_0 = 4,
+ .cfg_txcal_en = 0,
+ .cfg_cdr_kf_2_0 = 1,
+ .cfg_cdr_m_7_0 = 6,
+ .cfg_pi_bw_3_0 = mode->cfg_pi_bw_3_0,
+ .cfg_pi_steps_1_0 = 0,
+ .cfg_dis_2ndorder = 1,
+ .cfg_ctle_rstn = mode->cfg_ctle_rstn,
+ .r_dfe_rstn = mode->r_dfe_rstn,
+ .cfg_alos_thr_2_0 = media->cfg_alos_thr_2_0,
+ .cfg_itx_ipcml_base_1_0 = mode->cfg_itx_ipcml_base,
+ .cfg_rx_reserve_7_0 = 0xbf,
+ .cfg_rx_reserve_15_8 = 0x61,
+ .cfg_rxterm_2_0 = mode->rxterm,
+ .cfg_fom_selm = 0,
+ .cfg_rx_sp_ctle_1_0 = 0,
+ .cfg_isel_ctle_1_0 = 0,
+ .cfg_vga_ctrl_byp_4_0 = media->cfg_vga_ctrl_byp_4_0,
+ .cfg_vga_byp = 1,
+ .cfg_agc_adpt_byp = 1,
+ .cfg_eqr_byp = 1,
+ .cfg_eqr_force_3_0 = media->cfg_eq_r_force_3_0,
+ .cfg_eqc_force_3_0 = media->cfg_eq_c_force_3_0,
+ .cfg_sum_setcm_en = 1,
+ .cfg_pi_dfe_en = 1,
+ .cfg_init_pos_iscan_6_0 = 6,
+ .cfg_init_pos_ipi_6_0 = 9,
+ .cfg_dfedig_m_2_0 = 6,
+ .cfg_en_dfedig = mode->dfe_enable,
+ .r_d_width_ctrl_from_hwt = 0,
+ .r_reg_manual = 1,
+ .reg_rst = args->reg_rst,
+ .cfg_jc_byp = 1,
+ .cfg_common_reserve_7_0 = 1,
+ .cfg_pll_lol_set = 1,
+ .cfg_tx2rx_lp_en = 0,
+ .cfg_txlb_en = 0,
+ .cfg_rx2tx_lp_en = 0,
+ .cfg_rxlb_en = 0,
+ .r_tx_pol_inv = args->txinvert,
+ .r_rx_pol_inv = args->rxinvert,
+ };
+
+ *params = init;
+}
+
+static void sparx5_sd10g28_get_params(struct sparx5_serdes_macro *macro,
+ struct sparx5_sd10g28_media_preset *media,
+ struct sparx5_sd10g28_mode_preset *mode,
+ struct sparx5_sd10g28_args *args,
+ struct sparx5_sd10g28_params *params)
+{
+ u8 iw = sd10g28_get_iw_setting(macro->priv->dev, mode->bwidth);
+ struct sparx5_sd10g28_params init = {
+ .skip_cmu_cfg = args->skip_cmu_cfg,
+ .is_6g = args->is_6g,
+ .cmu_sel = mode->cmu_sel,
+ .cfg_lane_reserve_7_0 = (mode->cmu_sel % 2) << 6,
+ .cfg_ssc_rtl_clk_sel = (mode->cmu_sel / 2),
+ .cfg_lane_reserve_15_8 = mode->duty_cycle,
+ .cfg_txrate_1_0 = mode->rate,
+ .cfg_rxrate_1_0 = mode->rate,
+ .fx_100 = macro->serdesmode == SPX5_SD_MODE_100FX,
+ .r_d_width_ctrl_2_0 = iw,
+ .cfg_pma_tx_ck_bitwidth_2_0 = iw,
+ .cfg_rxdiv_sel_2_0 = iw,
+ .r_pcs2pma_phymode_4_0 = 0,
+ .cfg_lane_id_2_0 = 0,
+ .cfg_cdrck_en = 1,
+ .cfg_dfeck_en = mode->dfe_enable,
+ .cfg_dfe_pd = (mode->dfe_enable == 1) ? 0 : 1,
+ .cfg_dfetap_en_5_1 = mode->dfe_tap,
+ .cfg_erramp_pd = (mode->dfe_enable == 1) ? 0 : 1,
+ .cfg_pi_DFE_en = mode->dfe_enable,
+ .cfg_pi_en = 1,
+ .cfg_pd_ctle = 0,
+ .cfg_summer_en = 1,
+ .cfg_pd_rx_cktree = 0,
+ .cfg_pd_clk = 0,
+ .cfg_pd_cml = 0,
+ .cfg_pd_driver = 0,
+ .cfg_rx_reg_pu = 1,
+ .cfg_d_cdr_pd = 0,
+ .cfg_pd_sq = mode->dfe_enable,
+ .cfg_rxdet_en = 0,
+ .cfg_rxdet_str = 0,
+ .r_multi_lane_mode = 0,
+ .cfg_en_adv = media->cfg_en_adv,
+ .cfg_en_main = 1,
+ .cfg_en_dly = media->cfg_en_dly,
+ .cfg_tap_adv_3_0 = media->cfg_tap_adv_3_0,
+ .cfg_tap_main = media->cfg_tap_main,
+ .cfg_tap_dly_4_0 = media->cfg_tap_dly_4_0,
+ .cfg_vga_ctrl_3_0 = media->cfg_vga_ctrl_3_0,
+ .cfg_vga_cp_2_0 = media->cfg_vga_cp_2_0,
+ .cfg_eq_res_3_0 = media->cfg_eq_res_3_0,
+ .cfg_eq_r_byp = media->cfg_eq_r_byp,
+ .cfg_eq_c_force_3_0 = media->cfg_eq_c_force_3_0,
+ .cfg_en_dfedig = mode->dfe_enable,
+ .cfg_sum_setcm_en = 1,
+ .cfg_en_preemph = 0,
+ .cfg_itx_ippreemp_base_1_0 = 0,
+ .cfg_itx_ipdriver_base_2_0 = (args->txswing >> 6),
+ .cfg_ibias_tune_reserve_5_0 = (args->txswing & 63),
+ .cfg_txswing_half = (args->txmargin),
+ .cfg_dis_2nd_order = 0x1,
+ .cfg_rx_ssc_lh = 0x0,
+ .cfg_pi_floop_steps_1_0 = 0x0,
+ .cfg_pi_ext_dac_23_16 = (1 << 5),
+ .cfg_pi_ext_dac_15_8 = (0 << 6),
+ .cfg_iscan_ext_dac_7_0 = (1 << 7) + 9,
+ .cfg_cdr_kf_gen1_2_0 = 1,
+ .cfg_cdr_kf_gen2_2_0 = 1,
+ .cfg_cdr_kf_gen3_2_0 = 1,
+ .cfg_cdr_kf_gen4_2_0 = 1,
+ .r_cdr_m_gen1_7_0 = 4,
+ .cfg_pi_bw_gen1_3_0 = mode->pi_bw_gen1,
+ .cfg_pi_bw_gen2 = mode->pi_bw_gen1,
+ .cfg_pi_bw_gen3 = mode->pi_bw_gen1,
+ .cfg_pi_bw_gen4 = mode->pi_bw_gen1,
+ .cfg_pi_ext_dac_7_0 = 3,
+ .cfg_pi_steps = 0,
+ .cfg_mp_max_3_0 = 1,
+ .cfg_rstn_dfedig = mode->dfe_enable,
+ .cfg_alos_thr_3_0 = media->cfg_alos_thr_3_0,
+ .cfg_predrv_slewrate_1_0 = 3,
+ .cfg_itx_ipcml_base_1_0 = 0,
+ .cfg_ip_pre_base_1_0 = 0,
+ .r_cdr_m_gen2_7_0 = 2,
+ .r_cdr_m_gen3_7_0 = 2,
+ .r_cdr_m_gen4_7_0 = 2,
+ .r_en_auto_cdr_rstn = 0,
+ .cfg_oscal_afe = 1,
+ .cfg_pd_osdac_afe = 0,
+ .cfg_resetb_oscal_afe[0] = 0,
+ .cfg_resetb_oscal_afe[1] = 1,
+ .cfg_center_spreading = 0,
+ .cfg_m_cnt_maxval_4_0 = 15,
+ .cfg_ncnt_maxval_7_0 = 32,
+ .cfg_ncnt_maxval_10_8 = 6,
+ .cfg_ssc_en = 1,
+ .cfg_tx2rx_lp_en = 0,
+ .cfg_txlb_en = 0,
+ .cfg_rx2tx_lp_en = 0,
+ .cfg_rxlb_en = 0,
+ .r_tx_pol_inv = args->txinvert,
+ .r_rx_pol_inv = args->rxinvert,
+ };
+
+ *params = init;
+}
+
+static void sparx5_sd25g28_reset(void __iomem *regs[],
+ struct sparx5_sd25g28_params *params,
+ u32 sd_index)
+{
+ if (params->reg_rst == 1) {
+ sdx5_rmw_addr(SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST_SET(1),
+ SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST,
+ sdx5_addr(regs, SD_LANE_25G_SD_LANE_CFG(sd_index)));
+
+ usleep_range(1000, 2000);
+
+ sdx5_rmw_addr(SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST_SET(0),
+ SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST,
+ sdx5_addr(regs, SD_LANE_25G_SD_LANE_CFG(sd_index)));
+ }
+}
+
+static int sparx5_sd25g28_apply_params(struct sparx5_serdes_macro *macro,
+ struct sparx5_sd25g28_params *params)
+{
+ struct sparx5_serdes_private *priv = macro->priv;
+ void __iomem **regs = priv->regs;
+ struct device *dev = priv->dev;
+ u32 sd_index = macro->stpidx;
+ u32 value;
+
+ sdx5_rmw(SD_LANE_25G_SD_LANE_CFG_MACRO_RST_SET(1),
+ SD_LANE_25G_SD_LANE_CFG_MACRO_RST,
+ priv,
+ SD_LANE_25G_SD_LANE_CFG(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_SET(0xFF),
+ SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX,
+ priv,
+ SD25G_LANE_CMU_FF(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT_SET
+ (params->r_d_width_ctrl_from_hwt) |
+ SD25G_LANE_CMU_1A_R_REG_MANUAL_SET(params->r_reg_manual),
+ SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT |
+ SD25G_LANE_CMU_1A_R_REG_MANUAL,
+ priv,
+ SD25G_LANE_CMU_1A(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0_SET
+ (params->cfg_common_reserve_7_0),
+ SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0,
+ priv,
+ SD25G_LANE_CMU_31(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_09_CFG_EN_DUMMY_SET(params->cfg_en_dummy),
+ SD25G_LANE_CMU_09_CFG_EN_DUMMY,
+ priv,
+ SD25G_LANE_CMU_09(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0_SET
+ (params->cfg_pll_reserve_3_0),
+ SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0,
+ priv,
+ SD25G_LANE_CMU_13(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN_SET(params->l0_cfg_txcal_en),
+ SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN,
+ priv,
+ SD25G_LANE_CMU_40(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8_SET
+ (params->l0_cfg_tx_reserve_15_8),
+ SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8,
+ priv,
+ SD25G_LANE_CMU_46(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0_SET
+ (params->l0_cfg_tx_reserve_7_0),
+ SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0,
+ priv,
+ SD25G_LANE_CMU_45(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN_SET(0),
+ SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN,
+ priv,
+ SD25G_LANE_CMU_0B(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN_SET(1),
+ SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN,
+ priv,
+ SD25G_LANE_CMU_0B(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_19_R_CK_RESETB_SET(0),
+ SD25G_LANE_CMU_19_R_CK_RESETB,
+ priv,
+ SD25G_LANE_CMU_19(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_19_R_CK_RESETB_SET(1),
+ SD25G_LANE_CMU_19_R_CK_RESETB,
+ priv,
+ SD25G_LANE_CMU_19(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_18_R_PLL_RSTN_SET(0),
+ SD25G_LANE_CMU_18_R_PLL_RSTN,
+ priv,
+ SD25G_LANE_CMU_18(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_18_R_PLL_RSTN_SET(1),
+ SD25G_LANE_CMU_18_R_PLL_RSTN,
+ priv,
+ SD25G_LANE_CMU_18(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0_SET(params->r_d_width_ctrl_2_0),
+ SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0,
+ priv,
+ SD25G_LANE_CMU_1A(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0_SET
+ (params->r_txfifo_ck_div_pmad_2_0) |
+ SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0_SET
+ (params->r_rxfifo_ck_div_pmad_2_0),
+ SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0 |
+ SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0,
+ priv,
+ SD25G_LANE_CMU_30(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET_SET(params->cfg_pll_lol_set) |
+ SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0_SET
+ (params->cfg_vco_div_mode_1_0),
+ SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET |
+ SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0,
+ priv,
+ SD25G_LANE_CMU_0C(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0_SET
+ (params->cfg_pre_divsel_1_0),
+ SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0,
+ priv,
+ SD25G_LANE_CMU_0D(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0_SET(params->cfg_sel_div_3_0),
+ SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0,
+ priv,
+ SD25G_LANE_CMU_0E(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_SET(0x00),
+ SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX,
+ priv,
+ SD25G_LANE_CMU_FF(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0_SET
+ (params->cfg_pma_tx_ck_bitwidth_2_0),
+ SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0,
+ priv,
+ SD25G_LANE_LANE_0C(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0_SET
+ (params->cfg_tx_prediv_1_0),
+ SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0,
+ priv,
+ SD25G_LANE_LANE_01(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0_SET
+ (params->cfg_rxdiv_sel_2_0),
+ SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0,
+ priv,
+ SD25G_LANE_LANE_18(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0_SET
+ (params->cfg_tx_subrate_2_0),
+ SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0,
+ priv,
+ SD25G_LANE_LANE_2C(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0_SET
+ (params->cfg_rx_subrate_2_0),
+ SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0,
+ priv,
+ SD25G_LANE_LANE_28(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN_SET(params->cfg_cdrck_en),
+ SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN,
+ priv,
+ SD25G_LANE_LANE_18(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1_SET
+ (params->cfg_dfetap_en_5_1),
+ SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1,
+ priv,
+ SD25G_LANE_LANE_0F(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD_SET(params->cfg_erramp_pd),
+ SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD,
+ priv,
+ SD25G_LANE_LANE_18(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN_SET(params->cfg_pi_dfe_en),
+ SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN,
+ priv,
+ SD25G_LANE_LANE_1D(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_19_LN_CFG_ECDR_PD_SET(params->cfg_ecdr_pd),
+ SD25G_LANE_LANE_19_LN_CFG_ECDR_PD,
+ priv,
+ SD25G_LANE_LANE_19(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0_SET
+ (params->cfg_itx_ipdriver_base_2_0),
+ SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0,
+ priv,
+ SD25G_LANE_LANE_01(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0_SET(params->cfg_tap_dly_4_0),
+ SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0,
+ priv,
+ SD25G_LANE_LANE_03(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0_SET(params->cfg_tap_adv_3_0),
+ SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0,
+ priv,
+ SD25G_LANE_LANE_06(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_07_LN_CFG_EN_ADV_SET(params->cfg_en_adv) |
+ SD25G_LANE_LANE_07_LN_CFG_EN_DLY_SET(params->cfg_en_dly),
+ SD25G_LANE_LANE_07_LN_CFG_EN_ADV |
+ SD25G_LANE_LANE_07_LN_CFG_EN_DLY,
+ priv,
+ SD25G_LANE_LANE_07(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8_SET
+ (params->cfg_tx_reserve_15_8),
+ SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8,
+ priv,
+ SD25G_LANE_LANE_43(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0_SET
+ (params->cfg_tx_reserve_7_0),
+ SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0,
+ priv,
+ SD25G_LANE_LANE_42(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_05_LN_CFG_BW_1_0_SET(params->cfg_bw_1_0),
+ SD25G_LANE_LANE_05_LN_CFG_BW_1_0,
+ priv,
+ SD25G_LANE_LANE_05(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN_SET
+ (params->cfg_txcal_man_en),
+ SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN,
+ priv,
+ SD25G_LANE_LANE_0B(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0_SET
+ (params->cfg_txcal_shift_code_5_0),
+ SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0,
+ priv,
+ SD25G_LANE_LANE_0A(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0_SET
+ (params->cfg_txcal_valid_sel_3_0),
+ SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0,
+ priv,
+ SD25G_LANE_LANE_09(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0_SET(params->cfg_cdr_kf_2_0),
+ SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0,
+ priv,
+ SD25G_LANE_LANE_1A(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0_SET(params->cfg_cdr_m_7_0),
+ SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0,
+ priv,
+ SD25G_LANE_LANE_1B(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0_SET(params->cfg_pi_bw_3_0),
+ SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0,
+ priv,
+ SD25G_LANE_LANE_2B(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER_SET
+ (params->cfg_dis_2ndorder),
+ SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER,
+ priv,
+ SD25G_LANE_LANE_2C(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN_SET(params->cfg_ctle_rstn),
+ SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN,
+ priv,
+ SD25G_LANE_LANE_2E(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0_SET
+ (params->cfg_itx_ipcml_base_1_0),
+ SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0,
+ priv,
+ SD25G_LANE_LANE_00(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0_SET
+ (params->cfg_rx_reserve_7_0),
+ SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0,
+ priv,
+ SD25G_LANE_LANE_44(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8_SET
+ (params->cfg_rx_reserve_15_8),
+ SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8,
+ priv,
+ SD25G_LANE_LANE_45(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN_SET(params->cfg_dfeck_en) |
+ SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0_SET(params->cfg_rxterm_2_0),
+ SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN |
+ SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0,
+ priv,
+ SD25G_LANE_LANE_0D(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0_SET
+ (params->cfg_vga_ctrl_byp_4_0),
+ SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0,
+ priv,
+ SD25G_LANE_LANE_21(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0_SET
+ (params->cfg_eqr_force_3_0),
+ SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0,
+ priv,
+ SD25G_LANE_LANE_22(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0_SET
+ (params->cfg_eqc_force_3_0) |
+ SD25G_LANE_LANE_1C_LN_CFG_DFE_PD_SET(params->cfg_dfe_pd),
+ SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0 |
+ SD25G_LANE_LANE_1C_LN_CFG_DFE_PD,
+ priv,
+ SD25G_LANE_LANE_1C(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN_SET
+ (params->cfg_sum_setcm_en),
+ SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN,
+ priv,
+ SD25G_LANE_LANE_1E(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0_SET
+ (params->cfg_init_pos_iscan_6_0),
+ SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0,
+ priv,
+ SD25G_LANE_LANE_25(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0_SET
+ (params->cfg_init_pos_ipi_6_0),
+ SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0,
+ priv,
+ SD25G_LANE_LANE_26(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD_SET(params->cfg_erramp_pd),
+ SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD,
+ priv,
+ SD25G_LANE_LANE_18(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0_SET
+ (params->cfg_dfedig_m_2_0),
+ SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0,
+ priv,
+ SD25G_LANE_LANE_0E(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG_SET(params->cfg_en_dfedig),
+ SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG,
+ priv,
+ SD25G_LANE_LANE_0E(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_40_LN_R_TX_POL_INV_SET(params->r_tx_pol_inv) |
+ SD25G_LANE_LANE_40_LN_R_RX_POL_INV_SET(params->r_rx_pol_inv),
+ SD25G_LANE_LANE_40_LN_R_TX_POL_INV |
+ SD25G_LANE_LANE_40_LN_R_RX_POL_INV,
+ priv,
+ SD25G_LANE_LANE_40(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN_SET(params->cfg_rx2tx_lp_en) |
+ SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN_SET(params->cfg_tx2rx_lp_en),
+ SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN |
+ SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN,
+ priv,
+ SD25G_LANE_LANE_04(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN_SET(params->cfg_rxlb_en),
+ SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN,
+ priv,
+ SD25G_LANE_LANE_1E(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_19_LN_CFG_TXLB_EN_SET(params->cfg_txlb_en),
+ SD25G_LANE_LANE_19_LN_CFG_TXLB_EN,
+ priv,
+ SD25G_LANE_LANE_19(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG_SET(0),
+ SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG,
+ priv,
+ SD25G_LANE_LANE_2E(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG_SET(1),
+ SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG,
+ priv,
+ SD25G_LANE_LANE_2E(sd_index));
+
+ sdx5_rmw(SD_LANE_25G_SD_LANE_CFG_MACRO_RST_SET(0),
+ SD_LANE_25G_SD_LANE_CFG_MACRO_RST,
+ priv,
+ SD_LANE_25G_SD_LANE_CFG(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN_SET(0),
+ SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN,
+ priv,
+ SD25G_LANE_LANE_1C(sd_index));
+
+ usleep_range(1000, 2000);
+
+ sdx5_rmw(SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN_SET(1),
+ SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN,
+ priv,
+ SD25G_LANE_LANE_1C(sd_index));
+
+ usleep_range(10000, 20000);
+
+ sdx5_rmw(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_SET(0xff),
+ SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX,
+ priv,
+ SD25G_LANE_CMU_FF(sd_index));
+
+ value = readl(sdx5_addr(regs, SD25G_LANE_CMU_C0(sd_index)));
+ value = SD25G_LANE_CMU_C0_PLL_LOL_UDL_GET(value);
+
+ if (value) {
+ dev_err(dev, "25G PLL Loss of Lock: 0x%x\n", value);
+ return -EINVAL;
+ }
+
+ value = readl(sdx5_addr(regs, SD_LANE_25G_SD_LANE_STAT(sd_index)));
+ value = SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE_GET(value);
+
+ if (value != 0x1) {
+ dev_err(dev, "25G PMA Reset failed: 0x%x\n", value);
+ return -EINVAL;
+ }
+ sdx5_rmw(SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS_SET(0x1),
+ SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS,
+ priv,
+ SD25G_LANE_CMU_2A(sd_index));
+
+ sdx5_rmw(SD_LANE_25G_SD_SER_RST_SER_RST_SET(0x0),
+ SD_LANE_25G_SD_SER_RST_SER_RST,
+ priv,
+ SD_LANE_25G_SD_SER_RST(sd_index));
+
+ sdx5_rmw(SD_LANE_25G_SD_DES_RST_DES_RST_SET(0x0),
+ SD_LANE_25G_SD_DES_RST_DES_RST,
+ priv,
+ SD_LANE_25G_SD_DES_RST(sd_index));
+
+ sdx5_rmw(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_SET(0),
+ SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX,
+ priv,
+ SD25G_LANE_CMU_FF(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0_SET
+ (params->cfg_alos_thr_2_0),
+ SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0,
+ priv,
+ SD25G_LANE_LANE_2D(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ_SET(0),
+ SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ,
+ priv,
+ SD25G_LANE_LANE_2E(sd_index));
+
+ sdx5_rmw(SD25G_LANE_LANE_2E_LN_CFG_PD_SQ_SET(0),
+ SD25G_LANE_LANE_2E_LN_CFG_PD_SQ,
+ priv,
+ SD25G_LANE_LANE_2E(sd_index));
+
+ return 0;
+}
+
+static void sparx5_sd10g28_reset(void __iomem *regs[], u32 lane_index)
+{
+ /* Note: SerDes SD10G_LANE_1 is configured in 10G_LAN mode */
+ sdx5_rmw_addr(SD_LANE_SD_LANE_CFG_EXT_CFG_RST_SET(1),
+ SD_LANE_SD_LANE_CFG_EXT_CFG_RST,
+ sdx5_addr(regs, SD_LANE_SD_LANE_CFG(lane_index)));
+
+ usleep_range(1000, 2000);
+
+ sdx5_rmw_addr(SD_LANE_SD_LANE_CFG_EXT_CFG_RST_SET(0),
+ SD_LANE_SD_LANE_CFG_EXT_CFG_RST,
+ sdx5_addr(regs, SD_LANE_SD_LANE_CFG(lane_index)));
+}
+
+static int sparx5_sd10g28_apply_params(struct sparx5_serdes_macro *macro,
+ struct sparx5_sd10g28_params *params)
+{
+ struct sparx5_serdes_private *priv = macro->priv;
+ void __iomem **regs = priv->regs;
+ struct device *dev = priv->dev;
+ u32 lane_index = macro->sidx;
+ u32 sd_index = macro->stpidx;
+ void __iomem *sd_inst;
+ u32 value;
+
+ if (params->is_6g)
+ sd_inst = sdx5_inst_get(priv, TARGET_SD6G_LANE, sd_index);
+ else
+ sd_inst = sdx5_inst_get(priv, TARGET_SD10G_LANE, sd_index);
+
+ sdx5_rmw(SD_LANE_SD_LANE_CFG_MACRO_RST_SET(1),
+ SD_LANE_SD_LANE_CFG_MACRO_RST,
+ priv,
+ SD_LANE_SD_LANE_CFG(lane_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT_SET(0x0) |
+ SD10G_LANE_LANE_93_R_REG_MANUAL_SET(0x1) |
+ SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT_SET(0x1) |
+ SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT_SET(0x1) |
+ SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL_SET(0x0),
+ SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT |
+ SD10G_LANE_LANE_93_R_REG_MANUAL |
+ SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT |
+ SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT |
+ SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL,
+ sd_inst,
+ SD10G_LANE_LANE_93(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_94_R_ISCAN_REG_SET(0x1) |
+ SD10G_LANE_LANE_94_R_TXEQ_REG_SET(0x1) |
+ SD10G_LANE_LANE_94_R_MISC_REG_SET(0x1) |
+ SD10G_LANE_LANE_94_R_SWING_REG_SET(0x1),
+ SD10G_LANE_LANE_94_R_ISCAN_REG |
+ SD10G_LANE_LANE_94_R_TXEQ_REG |
+ SD10G_LANE_LANE_94_R_MISC_REG |
+ SD10G_LANE_LANE_94_R_SWING_REG,
+ sd_inst,
+ SD10G_LANE_LANE_94(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_9E_R_RXEQ_REG_SET(0x1),
+ SD10G_LANE_LANE_9E_R_RXEQ_REG,
+ sd_inst,
+ SD10G_LANE_LANE_9E(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_A1_R_SSC_FROM_HWT_SET(0x0) |
+ SD10G_LANE_LANE_A1_R_CDR_FROM_HWT_SET(0x0) |
+ SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT_SET(0x1),
+ SD10G_LANE_LANE_A1_R_SSC_FROM_HWT |
+ SD10G_LANE_LANE_A1_R_CDR_FROM_HWT |
+ SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT,
+ sd_inst,
+ SD10G_LANE_LANE_A1(sd_index));
+
+ sdx5_rmw(SD_LANE_SD_LANE_CFG_RX_REF_SEL_SET(params->cmu_sel) |
+ SD_LANE_SD_LANE_CFG_TX_REF_SEL_SET(params->cmu_sel),
+ SD_LANE_SD_LANE_CFG_RX_REF_SEL |
+ SD_LANE_SD_LANE_CFG_TX_REF_SEL,
+ priv,
+ SD_LANE_SD_LANE_CFG(lane_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0_SET
+ (params->cfg_lane_reserve_7_0),
+ SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0,
+ sd_inst,
+ SD10G_LANE_LANE_40(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL_SET
+ (params->cfg_ssc_rtl_clk_sel),
+ SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL,
+ sd_inst,
+ SD10G_LANE_LANE_50(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_35_CFG_TXRATE_1_0_SET
+ (params->cfg_txrate_1_0) |
+ SD10G_LANE_LANE_35_CFG_RXRATE_1_0_SET
+ (params->cfg_rxrate_1_0),
+ SD10G_LANE_LANE_35_CFG_TXRATE_1_0 |
+ SD10G_LANE_LANE_35_CFG_RXRATE_1_0,
+ sd_inst,
+ SD10G_LANE_LANE_35(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0_SET
+ (params->r_d_width_ctrl_2_0),
+ SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0,
+ sd_inst,
+ SD10G_LANE_LANE_94(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0_SET
+ (params->cfg_pma_tx_ck_bitwidth_2_0),
+ SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0,
+ sd_inst,
+ SD10G_LANE_LANE_01(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0_SET
+ (params->cfg_rxdiv_sel_2_0),
+ SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0,
+ sd_inst,
+ SD10G_LANE_LANE_30(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0_SET
+ (params->r_pcs2pma_phymode_4_0),
+ SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0,
+ sd_inst,
+ SD10G_LANE_LANE_A2(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_13_CFG_CDRCK_EN_SET(params->cfg_cdrck_en),
+ SD10G_LANE_LANE_13_CFG_CDRCK_EN,
+ sd_inst,
+ SD10G_LANE_LANE_13(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_23_CFG_DFECK_EN_SET
+ (params->cfg_dfeck_en) |
+ SD10G_LANE_LANE_23_CFG_DFE_PD_SET(params->cfg_dfe_pd) |
+ SD10G_LANE_LANE_23_CFG_ERRAMP_PD_SET
+ (params->cfg_erramp_pd),
+ SD10G_LANE_LANE_23_CFG_DFECK_EN |
+ SD10G_LANE_LANE_23_CFG_DFE_PD |
+ SD10G_LANE_LANE_23_CFG_ERRAMP_PD,
+ sd_inst,
+ SD10G_LANE_LANE_23(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1_SET
+ (params->cfg_dfetap_en_5_1),
+ SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1,
+ sd_inst,
+ SD10G_LANE_LANE_22(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_1A_CFG_PI_DFE_EN_SET
+ (params->cfg_pi_DFE_en),
+ SD10G_LANE_LANE_1A_CFG_PI_DFE_EN,
+ sd_inst,
+ SD10G_LANE_LANE_1A(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_02_CFG_EN_ADV_SET(params->cfg_en_adv) |
+ SD10G_LANE_LANE_02_CFG_EN_MAIN_SET(params->cfg_en_main) |
+ SD10G_LANE_LANE_02_CFG_EN_DLY_SET(params->cfg_en_dly) |
+ SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0_SET
+ (params->cfg_tap_adv_3_0),
+ SD10G_LANE_LANE_02_CFG_EN_ADV |
+ SD10G_LANE_LANE_02_CFG_EN_MAIN |
+ SD10G_LANE_LANE_02_CFG_EN_DLY |
+ SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0,
+ sd_inst,
+ SD10G_LANE_LANE_02(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_03_CFG_TAP_MAIN_SET(params->cfg_tap_main),
+ SD10G_LANE_LANE_03_CFG_TAP_MAIN,
+ sd_inst,
+ SD10G_LANE_LANE_03(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0_SET
+ (params->cfg_tap_dly_4_0),
+ SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0,
+ sd_inst,
+ SD10G_LANE_LANE_04(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0_SET
+ (params->cfg_vga_ctrl_3_0),
+ SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0,
+ sd_inst,
+ SD10G_LANE_LANE_2F(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0_SET
+ (params->cfg_vga_cp_2_0),
+ SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0,
+ sd_inst,
+ SD10G_LANE_LANE_2F(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0_SET
+ (params->cfg_eq_res_3_0),
+ SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0,
+ sd_inst,
+ SD10G_LANE_LANE_0B(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_0D_CFG_EQR_BYP_SET(params->cfg_eq_r_byp),
+ SD10G_LANE_LANE_0D_CFG_EQR_BYP,
+ sd_inst,
+ SD10G_LANE_LANE_0D(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0_SET
+ (params->cfg_eq_c_force_3_0) |
+ SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN_SET
+ (params->cfg_sum_setcm_en),
+ SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0 |
+ SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN,
+ sd_inst,
+ SD10G_LANE_LANE_0E(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_23_CFG_EN_DFEDIG_SET
+ (params->cfg_en_dfedig),
+ SD10G_LANE_LANE_23_CFG_EN_DFEDIG,
+ sd_inst,
+ SD10G_LANE_LANE_23(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_06_CFG_EN_PREEMPH_SET
+ (params->cfg_en_preemph),
+ SD10G_LANE_LANE_06_CFG_EN_PREEMPH,
+ sd_inst,
+ SD10G_LANE_LANE_06(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0_SET
+ (params->cfg_itx_ippreemp_base_1_0) |
+ SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0_SET
+ (params->cfg_itx_ipdriver_base_2_0),
+ SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0 |
+ SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0,
+ sd_inst,
+ SD10G_LANE_LANE_33(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0_SET
+ (params->cfg_ibias_tune_reserve_5_0),
+ SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0,
+ sd_inst,
+ SD10G_LANE_LANE_52(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_37_CFG_TXSWING_HALF_SET
+ (params->cfg_txswing_half),
+ SD10G_LANE_LANE_37_CFG_TXSWING_HALF,
+ sd_inst,
+ SD10G_LANE_LANE_37(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER_SET
+ (params->cfg_dis_2nd_order),
+ SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER,
+ sd_inst,
+ SD10G_LANE_LANE_3C(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_39_CFG_RX_SSC_LH_SET
+ (params->cfg_rx_ssc_lh),
+ SD10G_LANE_LANE_39_CFG_RX_SSC_LH,
+ sd_inst,
+ SD10G_LANE_LANE_39(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0_SET
+ (params->cfg_pi_floop_steps_1_0),
+ SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0,
+ sd_inst,
+ SD10G_LANE_LANE_1A(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16_SET
+ (params->cfg_pi_ext_dac_23_16),
+ SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16,
+ sd_inst,
+ SD10G_LANE_LANE_16(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8_SET
+ (params->cfg_pi_ext_dac_15_8),
+ SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8,
+ sd_inst,
+ SD10G_LANE_LANE_15(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0_SET
+ (params->cfg_iscan_ext_dac_7_0),
+ SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0,
+ sd_inst,
+ SD10G_LANE_LANE_26(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0_SET
+ (params->cfg_cdr_kf_gen1_2_0),
+ SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0,
+ sd_inst,
+ SD10G_LANE_LANE_42(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0_SET
+ (params->r_cdr_m_gen1_7_0),
+ SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0,
+ sd_inst,
+ SD10G_LANE_LANE_0F(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0_SET
+ (params->cfg_pi_bw_gen1_3_0),
+ SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0,
+ sd_inst,
+ SD10G_LANE_LANE_24(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0_SET
+ (params->cfg_pi_ext_dac_7_0),
+ SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0,
+ sd_inst,
+ SD10G_LANE_LANE_14(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_1A_CFG_PI_STEPS_SET(params->cfg_pi_steps),
+ SD10G_LANE_LANE_1A_CFG_PI_STEPS,
+ sd_inst,
+ SD10G_LANE_LANE_1A(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0_SET
+ (params->cfg_mp_max_3_0),
+ SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0,
+ sd_inst,
+ SD10G_LANE_LANE_3A(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG_SET
+ (params->cfg_rstn_dfedig),
+ SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG,
+ sd_inst,
+ SD10G_LANE_LANE_31(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0_SET
+ (params->cfg_alos_thr_3_0),
+ SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0,
+ sd_inst,
+ SD10G_LANE_LANE_48(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0_SET
+ (params->cfg_predrv_slewrate_1_0),
+ SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0,
+ sd_inst,
+ SD10G_LANE_LANE_36(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0_SET
+ (params->cfg_itx_ipcml_base_1_0),
+ SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0,
+ sd_inst,
+ SD10G_LANE_LANE_32(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0_SET
+ (params->cfg_ip_pre_base_1_0),
+ SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0,
+ sd_inst,
+ SD10G_LANE_LANE_37(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8_SET
+ (params->cfg_lane_reserve_15_8),
+ SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8,
+ sd_inst,
+ SD10G_LANE_LANE_41(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN_SET
+ (params->r_en_auto_cdr_rstn),
+ SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN,
+ sd_inst,
+ SD10G_LANE_LANE_9E(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_0C_CFG_OSCAL_AFE_SET
+ (params->cfg_oscal_afe) |
+ SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE_SET
+ (params->cfg_pd_osdac_afe),
+ SD10G_LANE_LANE_0C_CFG_OSCAL_AFE |
+ SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE,
+ sd_inst,
+ SD10G_LANE_LANE_0C(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE_SET
+ (params->cfg_resetb_oscal_afe[0]),
+ SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE,
+ sd_inst,
+ SD10G_LANE_LANE_0B(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE_SET
+ (params->cfg_resetb_oscal_afe[1]),
+ SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE,
+ sd_inst,
+ SD10G_LANE_LANE_0B(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_83_R_TX_POL_INV_SET
+ (params->r_tx_pol_inv) |
+ SD10G_LANE_LANE_83_R_RX_POL_INV_SET
+ (params->r_rx_pol_inv),
+ SD10G_LANE_LANE_83_R_TX_POL_INV |
+ SD10G_LANE_LANE_83_R_RX_POL_INV,
+ sd_inst,
+ SD10G_LANE_LANE_83(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN_SET
+ (params->cfg_rx2tx_lp_en) |
+ SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN_SET
+ (params->cfg_tx2rx_lp_en),
+ SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN |
+ SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN,
+ sd_inst,
+ SD10G_LANE_LANE_06(sd_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_0E_CFG_RXLB_EN_SET(params->cfg_rxlb_en) |
+ SD10G_LANE_LANE_0E_CFG_TXLB_EN_SET(params->cfg_txlb_en),
+ SD10G_LANE_LANE_0E_CFG_RXLB_EN |
+ SD10G_LANE_LANE_0E_CFG_TXLB_EN,
+ sd_inst,
+ SD10G_LANE_LANE_0E(sd_index));
+
+ sdx5_rmw(SD_LANE_SD_LANE_CFG_MACRO_RST_SET(0),
+ SD_LANE_SD_LANE_CFG_MACRO_RST,
+ priv,
+ SD_LANE_SD_LANE_CFG(lane_index));
+
+ sdx5_inst_rmw(SD10G_LANE_LANE_50_CFG_SSC_RESETB_SET(1),
+ SD10G_LANE_LANE_50_CFG_SSC_RESETB,
+ sd_inst,
+ SD10G_LANE_LANE_50(sd_index));
+
+ sdx5_rmw(SD10G_LANE_LANE_50_CFG_SSC_RESETB_SET(1),
+ SD10G_LANE_LANE_50_CFG_SSC_RESETB,
+ priv,
+ SD10G_LANE_LANE_50(sd_index));
+
+ sdx5_rmw(SD_LANE_MISC_SD_125_RST_DIS_SET(params->fx_100),
+ SD_LANE_MISC_SD_125_RST_DIS,
+ priv,
+ SD_LANE_MISC(lane_index));
+
+ sdx5_rmw(SD_LANE_MISC_RX_ENA_SET(params->fx_100),
+ SD_LANE_MISC_RX_ENA,
+ priv,
+ SD_LANE_MISC(lane_index));
+
+ sdx5_rmw(SD_LANE_MISC_MUX_ENA_SET(params->fx_100),
+ SD_LANE_MISC_MUX_ENA,
+ priv,
+ SD_LANE_MISC(lane_index));
+
+ usleep_range(3000, 6000);
+
+ value = readl(sdx5_addr(regs, SD_LANE_SD_LANE_STAT(lane_index)));
+ value = SD_LANE_SD_LANE_STAT_PMA_RST_DONE_GET(value);
+ if (value != 1) {
+ dev_err(dev, "10G PMA Reset failed: 0x%x\n", value);
+ return -EINVAL;
+ }
+
+ sdx5_rmw(SD_LANE_SD_SER_RST_SER_RST_SET(0x0),
+ SD_LANE_SD_SER_RST_SER_RST,
+ priv,
+ SD_LANE_SD_SER_RST(lane_index));
+
+ sdx5_rmw(SD_LANE_SD_DES_RST_DES_RST_SET(0x0),
+ SD_LANE_SD_DES_RST_DES_RST,
+ priv,
+ SD_LANE_SD_DES_RST(lane_index));
+
+ return 0;
+}
+
+static int sparx5_sd25g28_config(struct sparx5_serdes_macro *macro, bool reset)
+{
+ struct sparx5_sd25g28_media_preset media = media_presets_25g[macro->media];
+ struct sparx5_sd25g28_mode_preset mode;
+ struct sparx5_sd25g28_args args = {
+ .rxinvert = 1,
+ .txinvert = 0,
+ .txswing = 240,
+ .com_pll_reserve = 0xf,
+ .reg_rst = reset,
+ };
+ struct sparx5_sd25g28_params params;
+ int err;
+
+ err = sparx5_sd10g25_get_mode_preset(macro, &mode);
+ if (err)
+ return err;
+ sparx5_sd25g28_get_params(macro, &media, &mode, &args, &params);
+ sparx5_sd25g28_reset(macro->priv->regs, &params, macro->stpidx);
+ return sparx5_sd25g28_apply_params(macro, &params);
+}
+
+static int sparx5_sd10g28_config(struct sparx5_serdes_macro *macro, bool reset)
+{
+ struct sparx5_sd10g28_media_preset media = media_presets_10g[macro->media];
+ struct sparx5_sd10g28_mode_preset mode;
+ struct sparx5_sd10g28_params params;
+ struct sparx5_sd10g28_args args = {
+ .is_6g = (macro->serdestype == SPX5_SDT_6G),
+ .txinvert = 0,
+ .rxinvert = 1,
+ .txswing = 240,
+ .reg_rst = reset,
+ };
+ int err;
+
+ err = sparx5_sd10g28_get_mode_preset(macro, &mode, &args);
+ if (err)
+ return err;
+ sparx5_sd10g28_get_params(macro, &media, &mode, &args, &params);
+ sparx5_sd10g28_reset(macro->priv->regs, macro->sidx);
+ return sparx5_sd10g28_apply_params(macro, &params);
+}
+
+/* Power down serdes TX driver */
+static int sparx5_serdes_power_save(struct sparx5_serdes_macro *macro, u32 pwdn)
+{
+ struct sparx5_serdes_private *priv = macro->priv;
+ void __iomem *sd_inst;
+
+ if (macro->serdestype == SPX5_SDT_6G)
+ sd_inst = sdx5_inst_get(priv, TARGET_SD6G_LANE, macro->stpidx);
+ else if (macro->serdestype == SPX5_SDT_10G)
+ sd_inst = sdx5_inst_get(priv, TARGET_SD10G_LANE, macro->stpidx);
+ else
+ sd_inst = sdx5_inst_get(priv, TARGET_SD25G_LANE, macro->stpidx);
+
+ if (macro->serdestype == SPX5_SDT_25G) {
+ sdx5_inst_rmw(SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER_SET(pwdn),
+ SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER,
+ sd_inst,
+ SD25G_LANE_LANE_04(0));
+ } else {
+ /* 6G and 10G */
+ sdx5_inst_rmw(SD10G_LANE_LANE_06_CFG_PD_DRIVER_SET(pwdn),
+ SD10G_LANE_LANE_06_CFG_PD_DRIVER,
+ sd_inst,
+ SD10G_LANE_LANE_06(0));
+ }
+ return 0;
+}
+
+static int sparx5_serdes_clock_config(struct sparx5_serdes_macro *macro)
+{
+ struct sparx5_serdes_private *priv = macro->priv;
+
+ if (macro->serdesmode == SPX5_SD_MODE_100FX) {
+ u32 freq = priv->coreclock == 250000000 ? 2 :
+ priv->coreclock == 500000000 ? 1 : 0;
+
+ sdx5_rmw(SD_LANE_MISC_CORE_CLK_FREQ_SET(freq),
+ SD_LANE_MISC_CORE_CLK_FREQ,
+ priv,
+ SD_LANE_MISC(macro->sidx));
+ }
+ return 0;
+}
+
+static int sparx5_cmu_apply_cfg(struct sparx5_serdes_private *priv,
+ u32 cmu_idx,
+ void __iomem *cmu_tgt,
+ void __iomem *cmu_cfg_tgt,
+ u32 spd10g)
+{
+ void __iomem **regs = priv->regs;
+ struct device *dev = priv->dev;
+ int value;
+
+ cmu_tgt = sdx5_inst_get(priv, TARGET_SD_CMU, cmu_idx);
+ cmu_cfg_tgt = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, cmu_idx);
+
+ if (cmu_idx == 1 || cmu_idx == 4 || cmu_idx == 7 ||
+ cmu_idx == 10 || cmu_idx == 13) {
+ spd10g = 0;
+ }
+
+ sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(1),
+ SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST,
+ cmu_cfg_tgt,
+ SD_CMU_CFG_SD_CMU_CFG(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(0),
+ SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST,
+ cmu_cfg_tgt,
+ SD_CMU_CFG_SD_CMU_CFG(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(1),
+ SD_CMU_CFG_SD_CMU_CFG_CMU_RST,
+ cmu_cfg_tgt,
+ SD_CMU_CFG_SD_CMU_CFG(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT_SET(0x1) |
+ SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT_SET(0x1) |
+ SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT_SET(0x1) |
+ SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT_SET(0x1) |
+ SD_CMU_CMU_45_R_EN_RATECHG_CTRL_SET(0x0),
+ SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT |
+ SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT |
+ SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT |
+ SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT |
+ SD_CMU_CMU_45_R_EN_RATECHG_CTRL,
+ cmu_tgt,
+ SD_CMU_CMU_45(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0_SET(0),
+ SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0,
+ cmu_tgt,
+ SD_CMU_CMU_47(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_1B_CFG_RESERVE_7_0_SET(0),
+ SD_CMU_CMU_1B_CFG_RESERVE_7_0,
+ cmu_tgt,
+ SD_CMU_CMU_1B(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_JC_BYP_SET(0x1),
+ SD_CMU_CMU_0D_CFG_JC_BYP,
+ cmu_tgt,
+ SD_CMU_CMU_0D(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_1F_CFG_VTUNE_SEL_SET(1),
+ SD_CMU_CMU_1F_CFG_VTUNE_SEL,
+ cmu_tgt,
+ SD_CMU_CMU_1F(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0_SET(3),
+ SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0,
+ cmu_tgt,
+ SD_CMU_CMU_00(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_SET(3),
+ SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0,
+ cmu_tgt,
+ SD_CMU_CMU_05(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_30_R_PLL_DLOL_EN_SET(1),
+ SD_CMU_CMU_30_R_PLL_DLOL_EN,
+ cmu_tgt,
+ SD_CMU_CMU_30(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_09_CFG_SW_10G_SET(spd10g),
+ SD_CMU_CMU_09_CFG_SW_10G,
+ cmu_tgt,
+ SD_CMU_CMU_09(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(0),
+ SD_CMU_CFG_SD_CMU_CFG_CMU_RST,
+ cmu_cfg_tgt,
+ SD_CMU_CFG_SD_CMU_CFG(cmu_idx));
+
+ msleep(20);
+
+ sdx5_inst_rmw(SD_CMU_CMU_44_R_PLL_RSTN_SET(0),
+ SD_CMU_CMU_44_R_PLL_RSTN,
+ cmu_tgt,
+ SD_CMU_CMU_44(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_44_R_PLL_RSTN_SET(1),
+ SD_CMU_CMU_44_R_PLL_RSTN,
+ cmu_tgt,
+ SD_CMU_CMU_44(cmu_idx));
+
+ msleep(20);
+
+ value = readl(sdx5_addr(regs, SD_CMU_CMU_E0(cmu_idx)));
+ value = SD_CMU_CMU_E0_PLL_LOL_UDL_GET(value);
+
+ if (value) {
+ dev_err(dev, "CMU PLL Loss of Lock: 0x%x\n", value);
+ return -EINVAL;
+ }
+ sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD_SET(0),
+ SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD,
+ cmu_tgt,
+ SD_CMU_CMU_0D(cmu_idx));
+ return 0;
+}
+
+static int sparx5_cmu_cfg(struct sparx5_serdes_private *priv, u32 cmu_idx)
+{
+ void __iomem *cmu_tgt, *cmu_cfg_tgt;
+ u32 spd10g = 1;
+
+ if (cmu_idx == 1 || cmu_idx == 4 || cmu_idx == 7 ||
+ cmu_idx == 10 || cmu_idx == 13) {
+ spd10g = 0;
+ }
+
+ cmu_tgt = sdx5_inst_get(priv, TARGET_SD_CMU, cmu_idx);
+ cmu_cfg_tgt = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, cmu_idx);
+
+ return sparx5_cmu_apply_cfg(priv, cmu_idx, cmu_tgt, cmu_cfg_tgt, spd10g);
+}
+
+static int sparx5_serdes_cmu_enable(struct sparx5_serdes_private *priv)
+{
+ int idx, err = 0;
+
+ if (!priv->cmu_enabled) {
+ for (idx = 0; idx < SPX5_CMU_MAX; idx++) {
+ err = sparx5_cmu_cfg(priv, idx);
+ if (err) {
+ dev_err(priv->dev, "CMU %u, error: %d\n", idx, err);
+ goto leave;
+ }
+ }
+ priv->cmu_enabled = true;
+ }
+leave:
+ return err;
+}
+
+static int sparx5_serdes_get_serdesmode(phy_interface_t portmode, int speed)
+{
+ switch (portmode) {
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ if (speed == SPEED_2500)
+ return SPX5_SD_MODE_2G5;
+ if (speed == SPEED_100)
+ return SPX5_SD_MODE_100FX;
+ return SPX5_SD_MODE_1000BASEX;
+ case PHY_INTERFACE_MODE_SGMII:
+ /* The same Serdes mode is used for both SGMII and 1000BaseX */
+ return SPX5_SD_MODE_1000BASEX;
+ case PHY_INTERFACE_MODE_QSGMII:
+ return SPX5_SD_MODE_QSGMII;
+ case PHY_INTERFACE_MODE_10GBASER:
+ return SPX5_SD_MODE_SFI;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sparx5_serdes_config(struct sparx5_serdes_macro *macro)
+{
+ struct device *dev = macro->priv->dev;
+ int serdesmode;
+ int err;
+
+ err = sparx5_serdes_cmu_enable(macro->priv);
+ if (err)
+ return err;
+
+ serdesmode = sparx5_serdes_get_serdesmode(macro->portmode, macro->speed);
+ if (serdesmode < 0) {
+ dev_err(dev, "SerDes %u, interface not supported: %s\n",
+ macro->sidx,
+ phy_modes(macro->portmode));
+ return serdesmode;
+ }
+ macro->serdesmode = serdesmode;
+
+ sparx5_serdes_clock_config(macro);
+
+ if (macro->serdestype == SPX5_SDT_25G)
+ err = sparx5_sd25g28_config(macro, false);
+ else
+ err = sparx5_sd10g28_config(macro, false);
+ if (err) {
+ dev_err(dev, "SerDes %u, config error: %d\n",
+ macro->sidx, err);
+ }
+ return err;
+}
+
+static int sparx5_serdes_power_on(struct phy *phy)
+{
+ struct sparx5_serdes_macro *macro = phy_get_drvdata(phy);
+
+ return sparx5_serdes_power_save(macro, false);
+}
+
+static int sparx5_serdes_power_off(struct phy *phy)
+{
+ struct sparx5_serdes_macro *macro = phy_get_drvdata(phy);
+
+ return sparx5_serdes_power_save(macro, true);
+}
+
+static int sparx5_serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct sparx5_serdes_macro *macro;
+
+ if (mode != PHY_MODE_ETHERNET)
+ return -EINVAL;
+
+ switch (submode) {
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_10GBASER:
+ macro = phy_get_drvdata(phy);
+ macro->portmode = submode;
+ sparx5_serdes_config(macro);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sparx5_serdes_set_media(struct phy *phy, enum phy_media media)
+{
+ struct sparx5_serdes_macro *macro = phy_get_drvdata(phy);
+
+ if (media != macro->media) {
+ macro->media = media;
+ if (macro->serdesmode != SPX5_SD_MODE_NONE)
+ sparx5_serdes_config(macro);
+ }
+ return 0;
+}
+
+static int sparx5_serdes_set_speed(struct phy *phy, int speed)
+{
+ struct sparx5_serdes_macro *macro = phy_get_drvdata(phy);
+
+ if (macro->sidx < SPX5_SERDES_10G_START && speed > SPEED_5000)
+ return -EINVAL;
+ if (macro->sidx < SPX5_SERDES_25G_START && speed > SPEED_10000)
+ return -EINVAL;
+ if (speed != macro->speed) {
+ macro->speed = speed;
+ if (macro->serdesmode != SPX5_SD_MODE_NONE)
+ sparx5_serdes_config(macro);
+ }
+ return 0;
+}
+
+static int sparx5_serdes_reset(struct phy *phy)
+{
+ struct sparx5_serdes_macro *macro = phy_get_drvdata(phy);
+ int err;
+
+ err = sparx5_serdes_cmu_enable(macro->priv);
+ if (err)
+ return err;
+ if (macro->serdestype == SPX5_SDT_25G)
+ err = sparx5_sd25g28_config(macro, true);
+ else
+ err = sparx5_sd10g28_config(macro, true);
+ if (err) {
+ dev_err(&phy->dev, "SerDes %u, reset error: %d\n",
+ macro->sidx, err);
+ }
+ return err;
+}
+
+static int sparx5_serdes_validate(struct phy *phy, enum phy_mode mode,
+ int submode,
+ union phy_configure_opts *opts)
+{
+ struct sparx5_serdes_macro *macro = phy_get_drvdata(phy);
+
+ if (mode != PHY_MODE_ETHERNET)
+ return -EINVAL;
+
+ if (macro->speed == 0)
+ return -EINVAL;
+
+ if (macro->sidx < SPX5_SERDES_10G_START && macro->speed > SPEED_5000)
+ return -EINVAL;
+ if (macro->sidx < SPX5_SERDES_25G_START && macro->speed > SPEED_10000)
+ return -EINVAL;
+
+ switch (submode) {
+ case PHY_INTERFACE_MODE_1000BASEX:
+ if (macro->speed != SPEED_100 && /* This is for 100BASE-FX */
+ macro->speed != SPEED_1000)
+ return -EINVAL;
+ break;
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ case PHY_INTERFACE_MODE_QSGMII:
+ if (macro->speed >= SPEED_5000)
+ return -EINVAL;
+ break;
+ case PHY_INTERFACE_MODE_10GBASER:
+ if (macro->speed < SPEED_5000)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct phy_ops sparx5_serdes_ops = {
+ .power_on = sparx5_serdes_power_on,
+ .power_off = sparx5_serdes_power_off,
+ .set_mode = sparx5_serdes_set_mode,
+ .set_media = sparx5_serdes_set_media,
+ .set_speed = sparx5_serdes_set_speed,
+ .reset = sparx5_serdes_reset,
+ .validate = sparx5_serdes_validate,
+ .owner = THIS_MODULE,
+};
+
+static int sparx5_phy_create(struct sparx5_serdes_private *priv,
+ int idx, struct phy **phy)
+{
+ struct sparx5_serdes_macro *macro;
+
+ *phy = devm_phy_create(priv->dev, NULL, &sparx5_serdes_ops);
+ if (IS_ERR(*phy))
+ return PTR_ERR(*phy);
+
+ macro = devm_kzalloc(priv->dev, sizeof(*macro), GFP_KERNEL);
+ if (!macro)
+ return -ENOMEM;
+
+ macro->sidx = idx;
+ macro->priv = priv;
+ macro->speed = SPEED_UNKNOWN;
+ if (idx < SPX5_SERDES_10G_START) {
+ macro->serdestype = SPX5_SDT_6G;
+ macro->stpidx = macro->sidx;
+ } else if (idx < SPX5_SERDES_25G_START) {
+ macro->serdestype = SPX5_SDT_10G;
+ macro->stpidx = macro->sidx - SPX5_SERDES_10G_START;
+ } else {
+ macro->serdestype = SPX5_SDT_25G;
+ macro->stpidx = macro->sidx - SPX5_SERDES_25G_START;
+ }
+
+ phy_set_drvdata(*phy, macro);
+
+ return 0;
+}
+
+static struct sparx5_serdes_io_resource sparx5_serdes_iomap[] = {
+ { TARGET_SD_CMU, 0x0 }, /* 0x610808000: sd_cmu_0 */
+ { TARGET_SD_CMU + 1, 0x8000 }, /* 0x610810000: sd_cmu_1 */
+ { TARGET_SD_CMU + 2, 0x10000 }, /* 0x610818000: sd_cmu_2 */
+ { TARGET_SD_CMU + 3, 0x18000 }, /* 0x610820000: sd_cmu_3 */
+ { TARGET_SD_CMU + 4, 0x20000 }, /* 0x610828000: sd_cmu_4 */
+ { TARGET_SD_CMU + 5, 0x28000 }, /* 0x610830000: sd_cmu_5 */
+ { TARGET_SD_CMU + 6, 0x30000 }, /* 0x610838000: sd_cmu_6 */
+ { TARGET_SD_CMU + 7, 0x38000 }, /* 0x610840000: sd_cmu_7 */
+ { TARGET_SD_CMU + 8, 0x40000 }, /* 0x610848000: sd_cmu_8 */
+ { TARGET_SD_CMU_CFG, 0x48000 }, /* 0x610850000: sd_cmu_cfg_0 */
+ { TARGET_SD_CMU_CFG + 1, 0x50000 }, /* 0x610858000: sd_cmu_cfg_1 */
+ { TARGET_SD_CMU_CFG + 2, 0x58000 }, /* 0x610860000: sd_cmu_cfg_2 */
+ { TARGET_SD_CMU_CFG + 3, 0x60000 }, /* 0x610868000: sd_cmu_cfg_3 */
+ { TARGET_SD_CMU_CFG + 4, 0x68000 }, /* 0x610870000: sd_cmu_cfg_4 */
+ { TARGET_SD_CMU_CFG + 5, 0x70000 }, /* 0x610878000: sd_cmu_cfg_5 */
+ { TARGET_SD_CMU_CFG + 6, 0x78000 }, /* 0x610880000: sd_cmu_cfg_6 */
+ { TARGET_SD_CMU_CFG + 7, 0x80000 }, /* 0x610888000: sd_cmu_cfg_7 */
+ { TARGET_SD_CMU_CFG + 8, 0x88000 }, /* 0x610890000: sd_cmu_cfg_8 */
+ { TARGET_SD6G_LANE, 0x90000 }, /* 0x610898000: sd6g_lane_0 */
+ { TARGET_SD6G_LANE + 1, 0x98000 }, /* 0x6108a0000: sd6g_lane_1 */
+ { TARGET_SD6G_LANE + 2, 0xa0000 }, /* 0x6108a8000: sd6g_lane_2 */
+ { TARGET_SD6G_LANE + 3, 0xa8000 }, /* 0x6108b0000: sd6g_lane_3 */
+ { TARGET_SD6G_LANE + 4, 0xb0000 }, /* 0x6108b8000: sd6g_lane_4 */
+ { TARGET_SD6G_LANE + 5, 0xb8000 }, /* 0x6108c0000: sd6g_lane_5 */
+ { TARGET_SD6G_LANE + 6, 0xc0000 }, /* 0x6108c8000: sd6g_lane_6 */
+ { TARGET_SD6G_LANE + 7, 0xc8000 }, /* 0x6108d0000: sd6g_lane_7 */
+ { TARGET_SD6G_LANE + 8, 0xd0000 }, /* 0x6108d8000: sd6g_lane_8 */
+ { TARGET_SD6G_LANE + 9, 0xd8000 }, /* 0x6108e0000: sd6g_lane_9 */
+ { TARGET_SD6G_LANE + 10, 0xe0000 }, /* 0x6108e8000: sd6g_lane_10 */
+ { TARGET_SD6G_LANE + 11, 0xe8000 }, /* 0x6108f0000: sd6g_lane_11 */
+ { TARGET_SD6G_LANE + 12, 0xf0000 }, /* 0x6108f8000: sd6g_lane_12 */
+ { TARGET_SD10G_LANE, 0xf8000 }, /* 0x610900000: sd10g_lane_0 */
+ { TARGET_SD10G_LANE + 1, 0x100000 }, /* 0x610908000: sd10g_lane_1 */
+ { TARGET_SD10G_LANE + 2, 0x108000 }, /* 0x610910000: sd10g_lane_2 */
+ { TARGET_SD10G_LANE + 3, 0x110000 }, /* 0x610918000: sd10g_lane_3 */
+ { TARGET_SD_LANE, 0x1a0000 }, /* 0x6109a8000: sd_lane_0 */
+ { TARGET_SD_LANE + 1, 0x1a8000 }, /* 0x6109b0000: sd_lane_1 */
+ { TARGET_SD_LANE + 2, 0x1b0000 }, /* 0x6109b8000: sd_lane_2 */
+ { TARGET_SD_LANE + 3, 0x1b8000 }, /* 0x6109c0000: sd_lane_3 */
+ { TARGET_SD_LANE + 4, 0x1c0000 }, /* 0x6109c8000: sd_lane_4 */
+ { TARGET_SD_LANE + 5, 0x1c8000 }, /* 0x6109d0000: sd_lane_5 */
+ { TARGET_SD_LANE + 6, 0x1d0000 }, /* 0x6109d8000: sd_lane_6 */
+ { TARGET_SD_LANE + 7, 0x1d8000 }, /* 0x6109e0000: sd_lane_7 */
+ { TARGET_SD_LANE + 8, 0x1e0000 }, /* 0x6109e8000: sd_lane_8 */
+ { TARGET_SD_LANE + 9, 0x1e8000 }, /* 0x6109f0000: sd_lane_9 */
+ { TARGET_SD_LANE + 10, 0x1f0000 }, /* 0x6109f8000: sd_lane_10 */
+ { TARGET_SD_LANE + 11, 0x1f8000 }, /* 0x610a00000: sd_lane_11 */
+ { TARGET_SD_LANE + 12, 0x200000 }, /* 0x610a08000: sd_lane_12 */
+ { TARGET_SD_LANE + 13, 0x208000 }, /* 0x610a10000: sd_lane_13 */
+ { TARGET_SD_LANE + 14, 0x210000 }, /* 0x610a18000: sd_lane_14 */
+ { TARGET_SD_LANE + 15, 0x218000 }, /* 0x610a20000: sd_lane_15 */
+ { TARGET_SD_LANE + 16, 0x220000 }, /* 0x610a28000: sd_lane_16 */
+ { TARGET_SD_CMU + 9, 0x400000 }, /* 0x610c08000: sd_cmu_9 */
+ { TARGET_SD_CMU + 10, 0x408000 }, /* 0x610c10000: sd_cmu_10 */
+ { TARGET_SD_CMU + 11, 0x410000 }, /* 0x610c18000: sd_cmu_11 */
+ { TARGET_SD_CMU + 12, 0x418000 }, /* 0x610c20000: sd_cmu_12 */
+ { TARGET_SD_CMU + 13, 0x420000 }, /* 0x610c28000: sd_cmu_13 */
+ { TARGET_SD_CMU_CFG + 9, 0x428000 }, /* 0x610c30000: sd_cmu_cfg_9 */
+ { TARGET_SD_CMU_CFG + 10, 0x430000 }, /* 0x610c38000: sd_cmu_cfg_10 */
+ { TARGET_SD_CMU_CFG + 11, 0x438000 }, /* 0x610c40000: sd_cmu_cfg_11 */
+ { TARGET_SD_CMU_CFG + 12, 0x440000 }, /* 0x610c48000: sd_cmu_cfg_12 */
+ { TARGET_SD_CMU_CFG + 13, 0x448000 }, /* 0x610c50000: sd_cmu_cfg_13 */
+ { TARGET_SD10G_LANE + 4, 0x450000 }, /* 0x610c58000: sd10g_lane_4 */
+ { TARGET_SD10G_LANE + 5, 0x458000 }, /* 0x610c60000: sd10g_lane_5 */
+ { TARGET_SD10G_LANE + 6, 0x460000 }, /* 0x610c68000: sd10g_lane_6 */
+ { TARGET_SD10G_LANE + 7, 0x468000 }, /* 0x610c70000: sd10g_lane_7 */
+ { TARGET_SD10G_LANE + 8, 0x470000 }, /* 0x610c78000: sd10g_lane_8 */
+ { TARGET_SD10G_LANE + 9, 0x478000 }, /* 0x610c80000: sd10g_lane_9 */
+ { TARGET_SD10G_LANE + 10, 0x480000 }, /* 0x610c88000: sd10g_lane_10 */
+ { TARGET_SD10G_LANE + 11, 0x488000 }, /* 0x610c90000: sd10g_lane_11 */
+ { TARGET_SD25G_LANE, 0x490000 }, /* 0x610c98000: sd25g_lane_0 */
+ { TARGET_SD25G_LANE + 1, 0x498000 }, /* 0x610ca0000: sd25g_lane_1 */
+ { TARGET_SD25G_LANE + 2, 0x4a0000 }, /* 0x610ca8000: sd25g_lane_2 */
+ { TARGET_SD25G_LANE + 3, 0x4a8000 }, /* 0x610cb0000: sd25g_lane_3 */
+ { TARGET_SD25G_LANE + 4, 0x4b0000 }, /* 0x610cb8000: sd25g_lane_4 */
+ { TARGET_SD25G_LANE + 5, 0x4b8000 }, /* 0x610cc0000: sd25g_lane_5 */
+ { TARGET_SD25G_LANE + 6, 0x4c0000 }, /* 0x610cc8000: sd25g_lane_6 */
+ { TARGET_SD25G_LANE + 7, 0x4c8000 }, /* 0x610cd0000: sd25g_lane_7 */
+ { TARGET_SD_LANE + 17, 0x550000 }, /* 0x610d58000: sd_lane_17 */
+ { TARGET_SD_LANE + 18, 0x558000 }, /* 0x610d60000: sd_lane_18 */
+ { TARGET_SD_LANE + 19, 0x560000 }, /* 0x610d68000: sd_lane_19 */
+ { TARGET_SD_LANE + 20, 0x568000 }, /* 0x610d70000: sd_lane_20 */
+ { TARGET_SD_LANE + 21, 0x570000 }, /* 0x610d78000: sd_lane_21 */
+ { TARGET_SD_LANE + 22, 0x578000 }, /* 0x610d80000: sd_lane_22 */
+ { TARGET_SD_LANE + 23, 0x580000 }, /* 0x610d88000: sd_lane_23 */
+ { TARGET_SD_LANE + 24, 0x588000 }, /* 0x610d90000: sd_lane_24 */
+ { TARGET_SD_LANE_25G, 0x590000 }, /* 0x610d98000: sd_lane_25g_25 */
+ { TARGET_SD_LANE_25G + 1, 0x598000 }, /* 0x610da0000: sd_lane_25g_26 */
+ { TARGET_SD_LANE_25G + 2, 0x5a0000 }, /* 0x610da8000: sd_lane_25g_27 */
+ { TARGET_SD_LANE_25G + 3, 0x5a8000 }, /* 0x610db0000: sd_lane_25g_28 */
+ { TARGET_SD_LANE_25G + 4, 0x5b0000 }, /* 0x610db8000: sd_lane_25g_29 */
+ { TARGET_SD_LANE_25G + 5, 0x5b8000 }, /* 0x610dc0000: sd_lane_25g_30 */
+ { TARGET_SD_LANE_25G + 6, 0x5c0000 }, /* 0x610dc8000: sd_lane_25g_31 */
+ { TARGET_SD_LANE_25G + 7, 0x5c8000 }, /* 0x610dd0000: sd_lane_25g_32 */
+};
+
+/* Client lookup function, uses serdes index */
+static struct phy *sparx5_serdes_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct sparx5_serdes_private *priv = dev_get_drvdata(dev);
+ int idx;
+ unsigned int sidx;
+
+ if (args->args_count != 1)
+ return ERR_PTR(-EINVAL);
+
+ sidx = args->args[0];
+
+ /* Check validity: ERR_PTR(-ENODEV) if not valid */
+ for (idx = 0; idx < SPX5_SERDES_MAX; idx++) {
+ struct sparx5_serdes_macro *macro =
+ phy_get_drvdata(priv->phys[idx]);
+
+ if (sidx != macro->sidx)
+ continue;
+
+ return priv->phys[idx];
+ }
+ return ERR_PTR(-ENODEV);
+}
+
+static int sparx5_serdes_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct sparx5_serdes_private *priv;
+ struct phy_provider *provider;
+ struct resource *iores;
+ void __iomem *iomem;
+ unsigned long clock;
+ struct clk *clk;
+ int idx;
+ int err;
+
+ if (!np && !pdev->dev.platform_data)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+ priv->dev = &pdev->dev;
+
+ /* Get coreclock */
+ clk = devm_clk_get(priv->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(priv->dev, "Failed to get coreclock\n");
+ return PTR_ERR(clk);
+ }
+ clock = clk_get_rate(clk);
+ if (clock == 0) {
+ dev_err(priv->dev, "Invalid coreclock %lu\n", clock);
+ return -EINVAL;
+ }
+ priv->coreclock = clock;
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iores) {
+ dev_err(priv->dev, "Invalid resource\n");
+ return -EINVAL;
+ }
+ iomem = devm_ioremap(priv->dev, iores->start, resource_size(iores));
+ if (!iomem) {
+ dev_err(priv->dev, "Unable to get serdes registers: %s\n",
+ iores->name);
+ return -ENOMEM;
+ }
+ for (idx = 0; idx < ARRAY_SIZE(sparx5_serdes_iomap); idx++) {
+ struct sparx5_serdes_io_resource *iomap = &sparx5_serdes_iomap[idx];
+
+ priv->regs[iomap->id] = iomem + iomap->offset;
+ }
+ for (idx = 0; idx < SPX5_SERDES_MAX; idx++) {
+ err = sparx5_phy_create(priv, idx, &priv->phys[idx]);
+ if (err)
+ return err;
+ }
+
+ provider = devm_of_phy_provider_register(priv->dev, sparx5_serdes_xlate);
+
+ return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id sparx5_serdes_match[] = {
+ { .compatible = "microchip,sparx5-serdes" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sparx5_serdes_match);
+
+static struct platform_driver sparx5_serdes_driver = {
+ .probe = sparx5_serdes_probe,
+ .driver = {
+ .name = "sparx5-serdes",
+ .of_match_table = sparx5_serdes_match,
+ },
+};
+
+module_platform_driver(sparx5_serdes_driver);
+
+MODULE_DESCRIPTION("Microchip Sparx5 switch serdes driver");
+MODULE_AUTHOR("Steen Hegelund <steen.hegelund@microchip.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/microchip/sparx5_serdes.h b/drivers/phy/microchip/sparx5_serdes.h
new file mode 100644
index 000000000000..0a3e496e6210
--- /dev/null
+++ b/drivers/phy/microchip/sparx5_serdes.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ * Microchip Sparx5 SerDes driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc.
+ */
+
+#ifndef _SPARX5_SERDES_H_
+#define _SPARX5_SERDES_H_
+
+#include "sparx5_serdes_regs.h"
+
+#define SPX5_SERDES_MAX 33
+
+enum sparx5_serdes_type {
+ SPX5_SDT_6G = 6,
+ SPX5_SDT_10G = 10,
+ SPX5_SDT_25G = 25,
+};
+
+enum sparx5_serdes_mode {
+ SPX5_SD_MODE_NONE,
+ SPX5_SD_MODE_2G5,
+ SPX5_SD_MODE_QSGMII,
+ SPX5_SD_MODE_100FX,
+ SPX5_SD_MODE_1000BASEX,
+ SPX5_SD_MODE_SFI,
+};
+
+struct sparx5_serdes_private {
+ struct device *dev;
+ void __iomem *regs[NUM_TARGETS];
+ struct phy *phys[SPX5_SERDES_MAX];
+ bool cmu_enabled;
+ unsigned long coreclock;
+};
+
+struct sparx5_serdes_macro {
+ struct sparx5_serdes_private *priv;
+ u32 sidx;
+ u32 stpidx;
+ enum sparx5_serdes_type serdestype;
+ enum sparx5_serdes_mode serdesmode;
+ phy_interface_t portmode;
+ int speed;
+ enum phy_media media;
+};
+
+/* Read, Write and modify registers content.
+ * The register definition macros start at the id
+ */
+static inline void __iomem *sdx5_addr(void __iomem *base[],
+ int id, int tinst, int tcnt,
+ int gbase, int ginst,
+ int gcnt, int gwidth,
+ int raddr, int rinst,
+ int rcnt, int rwidth)
+{
+ WARN_ON((tinst) >= tcnt);
+ WARN_ON((ginst) >= gcnt);
+ WARN_ON((rinst) >= rcnt);
+ return base[id + (tinst)] +
+ gbase + ((ginst) * gwidth) +
+ raddr + ((rinst) * rwidth);
+}
+
+static inline void __iomem *sdx5_inst_baseaddr(void __iomem *base,
+ int gbase, int ginst,
+ int gcnt, int gwidth,
+ int raddr, int rinst,
+ int rcnt, int rwidth)
+{
+ WARN_ON((ginst) >= gcnt);
+ WARN_ON((rinst) >= rcnt);
+ return base +
+ gbase + ((ginst) * gwidth) +
+ raddr + ((rinst) * rwidth);
+}
+
+static inline void sdx5_rmw(u32 val, u32 mask, struct sparx5_serdes_private *priv,
+ int id, int tinst, int tcnt,
+ int gbase, int ginst, int gcnt, int gwidth,
+ int raddr, int rinst, int rcnt, int rwidth)
+{
+ u32 nval;
+ void __iomem *addr =
+ sdx5_addr(priv->regs, id, tinst, tcnt,
+ gbase, ginst, gcnt, gwidth,
+ raddr, rinst, rcnt, rwidth);
+ nval = readl(addr);
+ nval = (nval & ~mask) | (val & mask);
+ writel(nval, addr);
+}
+
+static inline void sdx5_inst_rmw(u32 val, u32 mask, void __iomem *iomem,
+ int id, int tinst, int tcnt,
+ int gbase, int ginst, int gcnt, int gwidth,
+ int raddr, int rinst, int rcnt, int rwidth)
+{
+ u32 nval;
+ void __iomem *addr =
+ sdx5_inst_baseaddr(iomem,
+ gbase, ginst, gcnt, gwidth,
+ raddr, rinst, rcnt, rwidth);
+ nval = readl(addr);
+ nval = (nval & ~mask) | (val & mask);
+ writel(nval, addr);
+}
+
+static inline void sdx5_rmw_addr(u32 val, u32 mask, void __iomem *addr)
+{
+ u32 nval;
+
+ nval = readl(addr);
+ nval = (nval & ~mask) | (val & mask);
+ writel(nval, addr);
+}
+
+static inline void __iomem *sdx5_inst_get(struct sparx5_serdes_private *priv,
+ int id, int tinst)
+{
+ return priv->regs[id + tinst];
+}
+
+static inline void __iomem *sdx5_inst_addr(void __iomem *iomem,
+ int id, int tinst, int tcnt,
+ int gbase,
+ int ginst, int gcnt, int gwidth,
+ int raddr,
+ int rinst, int rcnt, int rwidth)
+{
+ return sdx5_inst_baseaddr(iomem, gbase, ginst, gcnt, gwidth,
+ raddr, rinst, rcnt, rwidth);
+}
+
+
+#endif /* _SPARX5_SERDES_REGS_H_ */
diff --git a/drivers/phy/microchip/sparx5_serdes_regs.h b/drivers/phy/microchip/sparx5_serdes_regs.h
new file mode 100644
index 000000000000..b96386a4df5a
--- /dev/null
+++ b/drivers/phy/microchip/sparx5_serdes_regs.h
@@ -0,0 +1,2695 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ * Microchip Sparx5 SerDes driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc.
+ */
+
+/* This file is autogenerated by cml-utils 2020-11-16 13:11:27 +0100.
+ * Commit ID: 13bdf073131d8bf40c54901df6988ae4e9c8f29f
+ */
+
+#ifndef _SPARX5_SERDES_REGS_H_
+#define _SPARX5_SERDES_REGS_H_
+
+#include <linux/bitfield.h>
+#include <linux/types.h>
+#include <linux/bug.h>
+
+enum sparx5_serdes_target {
+ TARGET_SD10G_LANE = 200,
+ TARGET_SD25G_LANE = 212,
+ TARGET_SD6G_LANE = 233,
+ TARGET_SD_CMU = 248,
+ TARGET_SD_CMU_CFG = 262,
+ TARGET_SD_LANE = 276,
+ TARGET_SD_LANE_25G = 301,
+ NUM_TARGETS = 332
+};
+
+#define __REG(...) __VA_ARGS__
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_01 */
+#define SD10G_LANE_LANE_01(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 4, 0, 1, 4)
+
+#define SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0 GENMASK(2, 0)
+#define SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0, x)
+#define SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0, x)
+
+#define SD10G_LANE_LANE_01_CFG_RXDET_EN BIT(4)
+#define SD10G_LANE_LANE_01_CFG_RXDET_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_01_CFG_RXDET_EN, x)
+#define SD10G_LANE_LANE_01_CFG_RXDET_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_01_CFG_RXDET_EN, x)
+
+#define SD10G_LANE_LANE_01_CFG_RXDET_STR BIT(5)
+#define SD10G_LANE_LANE_01_CFG_RXDET_STR_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_01_CFG_RXDET_STR, x)
+#define SD10G_LANE_LANE_01_CFG_RXDET_STR_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_01_CFG_RXDET_STR, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_02 */
+#define SD10G_LANE_LANE_02(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 8, 0, 1, 4)
+
+#define SD10G_LANE_LANE_02_CFG_EN_ADV BIT(0)
+#define SD10G_LANE_LANE_02_CFG_EN_ADV_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_02_CFG_EN_ADV, x)
+#define SD10G_LANE_LANE_02_CFG_EN_ADV_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_02_CFG_EN_ADV, x)
+
+#define SD10G_LANE_LANE_02_CFG_EN_MAIN BIT(1)
+#define SD10G_LANE_LANE_02_CFG_EN_MAIN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_02_CFG_EN_MAIN, x)
+#define SD10G_LANE_LANE_02_CFG_EN_MAIN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_02_CFG_EN_MAIN, x)
+
+#define SD10G_LANE_LANE_02_CFG_EN_DLY BIT(2)
+#define SD10G_LANE_LANE_02_CFG_EN_DLY_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_02_CFG_EN_DLY, x)
+#define SD10G_LANE_LANE_02_CFG_EN_DLY_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_02_CFG_EN_DLY, x)
+
+#define SD10G_LANE_LANE_02_CFG_EN_DLY2 BIT(3)
+#define SD10G_LANE_LANE_02_CFG_EN_DLY2_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_02_CFG_EN_DLY2, x)
+#define SD10G_LANE_LANE_02_CFG_EN_DLY2_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_02_CFG_EN_DLY2, x)
+
+#define SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0 GENMASK(7, 4)
+#define SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0, x)
+#define SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_03 */
+#define SD10G_LANE_LANE_03(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 12, 0, 1, 4)
+
+#define SD10G_LANE_LANE_03_CFG_TAP_MAIN BIT(0)
+#define SD10G_LANE_LANE_03_CFG_TAP_MAIN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_03_CFG_TAP_MAIN, x)
+#define SD10G_LANE_LANE_03_CFG_TAP_MAIN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_03_CFG_TAP_MAIN, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_04 */
+#define SD10G_LANE_LANE_04(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 16, 0, 1, 4)
+
+#define SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0 GENMASK(4, 0)
+#define SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0, x)
+#define SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_06 */
+#define SD10G_LANE_LANE_06(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 24, 0, 1, 4)
+
+#define SD10G_LANE_LANE_06_CFG_PD_DRIVER BIT(0)
+#define SD10G_LANE_LANE_06_CFG_PD_DRIVER_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_06_CFG_PD_DRIVER, x)
+#define SD10G_LANE_LANE_06_CFG_PD_DRIVER_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_06_CFG_PD_DRIVER, x)
+
+#define SD10G_LANE_LANE_06_CFG_PD_CLK BIT(1)
+#define SD10G_LANE_LANE_06_CFG_PD_CLK_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_06_CFG_PD_CLK, x)
+#define SD10G_LANE_LANE_06_CFG_PD_CLK_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_06_CFG_PD_CLK, x)
+
+#define SD10G_LANE_LANE_06_CFG_PD_CML BIT(2)
+#define SD10G_LANE_LANE_06_CFG_PD_CML_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_06_CFG_PD_CML, x)
+#define SD10G_LANE_LANE_06_CFG_PD_CML_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_06_CFG_PD_CML, x)
+
+#define SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN BIT(3)
+#define SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN, x)
+#define SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN, x)
+
+#define SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN BIT(4)
+#define SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN, x)
+#define SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN, x)
+
+#define SD10G_LANE_LANE_06_CFG_EN_PREEMPH BIT(5)
+#define SD10G_LANE_LANE_06_CFG_EN_PREEMPH_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_06_CFG_EN_PREEMPH, x)
+#define SD10G_LANE_LANE_06_CFG_EN_PREEMPH_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_06_CFG_EN_PREEMPH, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0B */
+#define SD10G_LANE_LANE_0B(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 44, 0, 1, 4)
+
+#define SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0 GENMASK(3, 0)
+#define SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0, x)
+#define SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0, x)
+
+#define SD10G_LANE_LANE_0B_CFG_PD_CTLE BIT(4)
+#define SD10G_LANE_LANE_0B_CFG_PD_CTLE_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0B_CFG_PD_CTLE, x)
+#define SD10G_LANE_LANE_0B_CFG_PD_CTLE_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0B_CFG_PD_CTLE, x)
+
+#define SD10G_LANE_LANE_0B_CFG_CTLE_TP_EN BIT(5)
+#define SD10G_LANE_LANE_0B_CFG_CTLE_TP_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0B_CFG_CTLE_TP_EN, x)
+#define SD10G_LANE_LANE_0B_CFG_CTLE_TP_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0B_CFG_CTLE_TP_EN, x)
+
+#define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE BIT(6)
+#define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE, x)
+#define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE, x)
+
+#define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_SQ BIT(7)
+#define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_SQ_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_SQ, x)
+#define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_SQ_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_SQ, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0C */
+#define SD10G_LANE_LANE_0C(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 48, 0, 1, 4)
+
+#define SD10G_LANE_LANE_0C_CFG_OSCAL_AFE BIT(0)
+#define SD10G_LANE_LANE_0C_CFG_OSCAL_AFE_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0C_CFG_OSCAL_AFE, x)
+#define SD10G_LANE_LANE_0C_CFG_OSCAL_AFE_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0C_CFG_OSCAL_AFE, x)
+
+#define SD10G_LANE_LANE_0C_CFG_OSCAL_SQ BIT(1)
+#define SD10G_LANE_LANE_0C_CFG_OSCAL_SQ_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0C_CFG_OSCAL_SQ, x)
+#define SD10G_LANE_LANE_0C_CFG_OSCAL_SQ_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0C_CFG_OSCAL_SQ, x)
+
+#define SD10G_LANE_LANE_0C_CFG_OSDAC_2X_AFE BIT(2)
+#define SD10G_LANE_LANE_0C_CFG_OSDAC_2X_AFE_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0C_CFG_OSDAC_2X_AFE, x)
+#define SD10G_LANE_LANE_0C_CFG_OSDAC_2X_AFE_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0C_CFG_OSDAC_2X_AFE, x)
+
+#define SD10G_LANE_LANE_0C_CFG_OSDAC_2X_SQ BIT(3)
+#define SD10G_LANE_LANE_0C_CFG_OSDAC_2X_SQ_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0C_CFG_OSDAC_2X_SQ, x)
+#define SD10G_LANE_LANE_0C_CFG_OSDAC_2X_SQ_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0C_CFG_OSDAC_2X_SQ, x)
+
+#define SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE BIT(4)
+#define SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE, x)
+#define SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE, x)
+
+#define SD10G_LANE_LANE_0C_CFG_PD_OSDAC_SQ BIT(5)
+#define SD10G_LANE_LANE_0C_CFG_PD_OSDAC_SQ_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0C_CFG_PD_OSDAC_SQ, x)
+#define SD10G_LANE_LANE_0C_CFG_PD_OSDAC_SQ_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0C_CFG_PD_OSDAC_SQ, x)
+
+#define SD10G_LANE_LANE_0C_CFG_PD_RX_LS BIT(6)
+#define SD10G_LANE_LANE_0C_CFG_PD_RX_LS_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0C_CFG_PD_RX_LS, x)
+#define SD10G_LANE_LANE_0C_CFG_PD_RX_LS_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0C_CFG_PD_RX_LS, x)
+
+#define SD10G_LANE_LANE_0C_CFG_RX_PCIE_GEN12 BIT(7)
+#define SD10G_LANE_LANE_0C_CFG_RX_PCIE_GEN12_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0C_CFG_RX_PCIE_GEN12, x)
+#define SD10G_LANE_LANE_0C_CFG_RX_PCIE_GEN12_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0C_CFG_RX_PCIE_GEN12, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0D */
+#define SD10G_LANE_LANE_0D(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 52, 0, 1, 4)
+
+#define SD10G_LANE_LANE_0D_CFG_CTLE_M_THR_1_0 GENMASK(1, 0)
+#define SD10G_LANE_LANE_0D_CFG_CTLE_M_THR_1_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0D_CFG_CTLE_M_THR_1_0, x)
+#define SD10G_LANE_LANE_0D_CFG_CTLE_M_THR_1_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0D_CFG_CTLE_M_THR_1_0, x)
+
+#define SD10G_LANE_LANE_0D_CFG_EQR_BYP BIT(4)
+#define SD10G_LANE_LANE_0D_CFG_EQR_BYP_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0D_CFG_EQR_BYP, x)
+#define SD10G_LANE_LANE_0D_CFG_EQR_BYP_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0D_CFG_EQR_BYP, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0E */
+#define SD10G_LANE_LANE_0E(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 56, 0, 1, 4)
+
+#define SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0 GENMASK(3, 0)
+#define SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0, x)
+#define SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0, x)
+
+#define SD10G_LANE_LANE_0E_CFG_RXLB_EN BIT(4)
+#define SD10G_LANE_LANE_0E_CFG_RXLB_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0E_CFG_RXLB_EN, x)
+#define SD10G_LANE_LANE_0E_CFG_RXLB_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0E_CFG_RXLB_EN, x)
+
+#define SD10G_LANE_LANE_0E_CFG_TXLB_EN BIT(5)
+#define SD10G_LANE_LANE_0E_CFG_TXLB_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0E_CFG_TXLB_EN, x)
+#define SD10G_LANE_LANE_0E_CFG_TXLB_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0E_CFG_TXLB_EN, x)
+
+#define SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN BIT(6)
+#define SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN, x)
+#define SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0F */
+#define SD10G_LANE_LANE_0F(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 60, 0, 1, 4)
+
+#define SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0 GENMASK(7, 0)
+#define SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0, x)
+#define SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_13 */
+#define SD10G_LANE_LANE_13(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 76, 0, 1, 4)
+
+#define SD10G_LANE_LANE_13_CFG_DCDR_PD BIT(0)
+#define SD10G_LANE_LANE_13_CFG_DCDR_PD_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_13_CFG_DCDR_PD, x)
+#define SD10G_LANE_LANE_13_CFG_DCDR_PD_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_13_CFG_DCDR_PD, x)
+
+#define SD10G_LANE_LANE_13_CFG_PHID_1T BIT(1)
+#define SD10G_LANE_LANE_13_CFG_PHID_1T_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_13_CFG_PHID_1T, x)
+#define SD10G_LANE_LANE_13_CFG_PHID_1T_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_13_CFG_PHID_1T, x)
+
+#define SD10G_LANE_LANE_13_CFG_CDRCK_EN BIT(2)
+#define SD10G_LANE_LANE_13_CFG_CDRCK_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_13_CFG_CDRCK_EN, x)
+#define SD10G_LANE_LANE_13_CFG_CDRCK_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_13_CFG_CDRCK_EN, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_14 */
+#define SD10G_LANE_LANE_14(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 80, 0, 1, 4)
+
+#define SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0 GENMASK(7, 0)
+#define SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0, x)
+#define SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_15 */
+#define SD10G_LANE_LANE_15(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 84, 0, 1, 4)
+
+#define SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8 GENMASK(7, 0)
+#define SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8, x)
+#define SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_16 */
+#define SD10G_LANE_LANE_16(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 88, 0, 1, 4)
+
+#define SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16 GENMASK(7, 0)
+#define SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16, x)
+#define SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_1A */
+#define SD10G_LANE_LANE_1A(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 104, 0, 1, 4)
+
+#define SD10G_LANE_LANE_1A_CFG_PI_R_SCAN_EN BIT(0)
+#define SD10G_LANE_LANE_1A_CFG_PI_R_SCAN_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_1A_CFG_PI_R_SCAN_EN, x)
+#define SD10G_LANE_LANE_1A_CFG_PI_R_SCAN_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_1A_CFG_PI_R_SCAN_EN, x)
+
+#define SD10G_LANE_LANE_1A_CFG_PI_EN BIT(1)
+#define SD10G_LANE_LANE_1A_CFG_PI_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_1A_CFG_PI_EN, x)
+#define SD10G_LANE_LANE_1A_CFG_PI_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_1A_CFG_PI_EN, x)
+
+#define SD10G_LANE_LANE_1A_CFG_PI_DFE_EN BIT(2)
+#define SD10G_LANE_LANE_1A_CFG_PI_DFE_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_1A_CFG_PI_DFE_EN, x)
+#define SD10G_LANE_LANE_1A_CFG_PI_DFE_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_1A_CFG_PI_DFE_EN, x)
+
+#define SD10G_LANE_LANE_1A_CFG_PI_STEPS BIT(3)
+#define SD10G_LANE_LANE_1A_CFG_PI_STEPS_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_1A_CFG_PI_STEPS, x)
+#define SD10G_LANE_LANE_1A_CFG_PI_STEPS_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_1A_CFG_PI_STEPS, x)
+
+#define SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0 GENMASK(5, 4)
+#define SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0, x)
+#define SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_22 */
+#define SD10G_LANE_LANE_22(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 136, 0, 1, 4)
+
+#define SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1 GENMASK(4, 0)
+#define SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1, x)
+#define SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_23 */
+#define SD10G_LANE_LANE_23(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 140, 0, 1, 4)
+
+#define SD10G_LANE_LANE_23_CFG_DFE_PD BIT(0)
+#define SD10G_LANE_LANE_23_CFG_DFE_PD_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_23_CFG_DFE_PD, x)
+#define SD10G_LANE_LANE_23_CFG_DFE_PD_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_23_CFG_DFE_PD, x)
+
+#define SD10G_LANE_LANE_23_CFG_EN_DFEDIG BIT(1)
+#define SD10G_LANE_LANE_23_CFG_EN_DFEDIG_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_23_CFG_EN_DFEDIG, x)
+#define SD10G_LANE_LANE_23_CFG_EN_DFEDIG_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_23_CFG_EN_DFEDIG, x)
+
+#define SD10G_LANE_LANE_23_CFG_DFECK_EN BIT(2)
+#define SD10G_LANE_LANE_23_CFG_DFECK_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_23_CFG_DFECK_EN, x)
+#define SD10G_LANE_LANE_23_CFG_DFECK_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_23_CFG_DFECK_EN, x)
+
+#define SD10G_LANE_LANE_23_CFG_ERRAMP_PD BIT(3)
+#define SD10G_LANE_LANE_23_CFG_ERRAMP_PD_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_23_CFG_ERRAMP_PD, x)
+#define SD10G_LANE_LANE_23_CFG_ERRAMP_PD_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_23_CFG_ERRAMP_PD, x)
+
+#define SD10G_LANE_LANE_23_CFG_DFEDIG_M_2_0 GENMASK(6, 4)
+#define SD10G_LANE_LANE_23_CFG_DFEDIG_M_2_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_23_CFG_DFEDIG_M_2_0, x)
+#define SD10G_LANE_LANE_23_CFG_DFEDIG_M_2_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_23_CFG_DFEDIG_M_2_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_24 */
+#define SD10G_LANE_LANE_24(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 144, 0, 1, 4)
+
+#define SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0 GENMASK(3, 0)
+#define SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0, x)
+#define SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0, x)
+
+#define SD10G_LANE_LANE_24_CFG_PI_BW_GEN2_3_0 GENMASK(7, 4)
+#define SD10G_LANE_LANE_24_CFG_PI_BW_GEN2_3_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_24_CFG_PI_BW_GEN2_3_0, x)
+#define SD10G_LANE_LANE_24_CFG_PI_BW_GEN2_3_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_24_CFG_PI_BW_GEN2_3_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_26 */
+#define SD10G_LANE_LANE_26(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 152, 0, 1, 4)
+
+#define SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0 GENMASK(7, 0)
+#define SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0, x)
+#define SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_2F */
+#define SD10G_LANE_LANE_2F(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 188, 0, 1, 4)
+
+#define SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0 GENMASK(2, 0)
+#define SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0, x)
+#define SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0, x)
+
+#define SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0 GENMASK(7, 4)
+#define SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0, x)
+#define SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_30 */
+#define SD10G_LANE_LANE_30(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 192, 0, 1, 4)
+
+#define SD10G_LANE_LANE_30_CFG_SUMMER_EN BIT(0)
+#define SD10G_LANE_LANE_30_CFG_SUMMER_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_30_CFG_SUMMER_EN, x)
+#define SD10G_LANE_LANE_30_CFG_SUMMER_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_30_CFG_SUMMER_EN, x)
+
+#define SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0 GENMASK(6, 4)
+#define SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0, x)
+#define SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_31 */
+#define SD10G_LANE_LANE_31(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 196, 0, 1, 4)
+
+#define SD10G_LANE_LANE_31_CFG_PI_RSTN BIT(0)
+#define SD10G_LANE_LANE_31_CFG_PI_RSTN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_31_CFG_PI_RSTN, x)
+#define SD10G_LANE_LANE_31_CFG_PI_RSTN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_31_CFG_PI_RSTN, x)
+
+#define SD10G_LANE_LANE_31_CFG_CDR_RSTN BIT(1)
+#define SD10G_LANE_LANE_31_CFG_CDR_RSTN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_31_CFG_CDR_RSTN, x)
+#define SD10G_LANE_LANE_31_CFG_CDR_RSTN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_31_CFG_CDR_RSTN, x)
+
+#define SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG BIT(2)
+#define SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG, x)
+#define SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG, x)
+
+#define SD10G_LANE_LANE_31_CFG_CTLE_RSTN BIT(3)
+#define SD10G_LANE_LANE_31_CFG_CTLE_RSTN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_31_CFG_CTLE_RSTN, x)
+#define SD10G_LANE_LANE_31_CFG_CTLE_RSTN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_31_CFG_CTLE_RSTN, x)
+
+#define SD10G_LANE_LANE_31_CFG_RSTN_DIV5_8 BIT(4)
+#define SD10G_LANE_LANE_31_CFG_RSTN_DIV5_8_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_31_CFG_RSTN_DIV5_8, x)
+#define SD10G_LANE_LANE_31_CFG_RSTN_DIV5_8_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_31_CFG_RSTN_DIV5_8, x)
+
+#define SD10G_LANE_LANE_31_CFG_R50_EN BIT(5)
+#define SD10G_LANE_LANE_31_CFG_R50_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_31_CFG_R50_EN, x)
+#define SD10G_LANE_LANE_31_CFG_R50_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_31_CFG_R50_EN, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_32 */
+#define SD10G_LANE_LANE_32(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 200, 0, 1, 4)
+
+#define SD10G_LANE_LANE_32_CFG_ITX_IPCLK_BASE_1_0 GENMASK(1, 0)
+#define SD10G_LANE_LANE_32_CFG_ITX_IPCLK_BASE_1_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_32_CFG_ITX_IPCLK_BASE_1_0, x)
+#define SD10G_LANE_LANE_32_CFG_ITX_IPCLK_BASE_1_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_32_CFG_ITX_IPCLK_BASE_1_0, x)
+
+#define SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0 GENMASK(5, 4)
+#define SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0, x)
+#define SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_33 */
+#define SD10G_LANE_LANE_33(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 204, 0, 1, 4)
+
+#define SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0 GENMASK(2, 0)
+#define SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0, x)
+#define SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0, x)
+
+#define SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0 GENMASK(5, 4)
+#define SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0, x)
+#define SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_35 */
+#define SD10G_LANE_LANE_35(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 212, 0, 1, 4)
+
+#define SD10G_LANE_LANE_35_CFG_TXRATE_1_0 GENMASK(1, 0)
+#define SD10G_LANE_LANE_35_CFG_TXRATE_1_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_35_CFG_TXRATE_1_0, x)
+#define SD10G_LANE_LANE_35_CFG_TXRATE_1_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_35_CFG_TXRATE_1_0, x)
+
+#define SD10G_LANE_LANE_35_CFG_RXRATE_1_0 GENMASK(5, 4)
+#define SD10G_LANE_LANE_35_CFG_RXRATE_1_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_35_CFG_RXRATE_1_0, x)
+#define SD10G_LANE_LANE_35_CFG_RXRATE_1_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_35_CFG_RXRATE_1_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_36 */
+#define SD10G_LANE_LANE_36(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 216, 0, 1, 4)
+
+#define SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0 GENMASK(1, 0)
+#define SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0, x)
+#define SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0, x)
+
+#define SD10G_LANE_LANE_36_CFG_EID_LP BIT(4)
+#define SD10G_LANE_LANE_36_CFG_EID_LP_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_36_CFG_EID_LP, x)
+#define SD10G_LANE_LANE_36_CFG_EID_LP_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_36_CFG_EID_LP, x)
+
+#define SD10G_LANE_LANE_36_CFG_EN_PREDRV_EMPH BIT(5)
+#define SD10G_LANE_LANE_36_CFG_EN_PREDRV_EMPH_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_36_CFG_EN_PREDRV_EMPH, x)
+#define SD10G_LANE_LANE_36_CFG_EN_PREDRV_EMPH_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_36_CFG_EN_PREDRV_EMPH, x)
+
+#define SD10G_LANE_LANE_36_CFG_PRBS_SEL BIT(6)
+#define SD10G_LANE_LANE_36_CFG_PRBS_SEL_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_36_CFG_PRBS_SEL, x)
+#define SD10G_LANE_LANE_36_CFG_PRBS_SEL_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_36_CFG_PRBS_SEL, x)
+
+#define SD10G_LANE_LANE_36_CFG_PRBS_SETB BIT(7)
+#define SD10G_LANE_LANE_36_CFG_PRBS_SETB_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_36_CFG_PRBS_SETB, x)
+#define SD10G_LANE_LANE_36_CFG_PRBS_SETB_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_36_CFG_PRBS_SETB, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_37 */
+#define SD10G_LANE_LANE_37(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 220, 0, 1, 4)
+
+#define SD10G_LANE_LANE_37_CFG_RXDET_COMP_PD BIT(0)
+#define SD10G_LANE_LANE_37_CFG_RXDET_COMP_PD_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_37_CFG_RXDET_COMP_PD, x)
+#define SD10G_LANE_LANE_37_CFG_RXDET_COMP_PD_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_37_CFG_RXDET_COMP_PD, x)
+
+#define SD10G_LANE_LANE_37_CFG_PD_RX_CKTREE BIT(1)
+#define SD10G_LANE_LANE_37_CFG_PD_RX_CKTREE_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_37_CFG_PD_RX_CKTREE, x)
+#define SD10G_LANE_LANE_37_CFG_PD_RX_CKTREE_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_37_CFG_PD_RX_CKTREE, x)
+
+#define SD10G_LANE_LANE_37_CFG_TXSWING_HALF BIT(2)
+#define SD10G_LANE_LANE_37_CFG_TXSWING_HALF_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_37_CFG_TXSWING_HALF, x)
+#define SD10G_LANE_LANE_37_CFG_TXSWING_HALF_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_37_CFG_TXSWING_HALF, x)
+
+#define SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0 GENMASK(5, 4)
+#define SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0, x)
+#define SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_39 */
+#define SD10G_LANE_LANE_39(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 228, 0, 1, 4)
+
+#define SD10G_LANE_LANE_39_CFG_RXFILT_Y_2_0 GENMASK(2, 0)
+#define SD10G_LANE_LANE_39_CFG_RXFILT_Y_2_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_39_CFG_RXFILT_Y_2_0, x)
+#define SD10G_LANE_LANE_39_CFG_RXFILT_Y_2_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_39_CFG_RXFILT_Y_2_0, x)
+
+#define SD10G_LANE_LANE_39_CFG_RX_SSC_LH BIT(4)
+#define SD10G_LANE_LANE_39_CFG_RX_SSC_LH_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_39_CFG_RX_SSC_LH, x)
+#define SD10G_LANE_LANE_39_CFG_RX_SSC_LH_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_39_CFG_RX_SSC_LH, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_3A */
+#define SD10G_LANE_LANE_3A(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 232, 0, 1, 4)
+
+#define SD10G_LANE_LANE_3A_CFG_MP_MIN_3_0 GENMASK(3, 0)
+#define SD10G_LANE_LANE_3A_CFG_MP_MIN_3_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_3A_CFG_MP_MIN_3_0, x)
+#define SD10G_LANE_LANE_3A_CFG_MP_MIN_3_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_3A_CFG_MP_MIN_3_0, x)
+
+#define SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0 GENMASK(7, 4)
+#define SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0, x)
+#define SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_3C */
+#define SD10G_LANE_LANE_3C(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 240, 0, 1, 4)
+
+#define SD10G_LANE_LANE_3C_CFG_DIS_ACC BIT(0)
+#define SD10G_LANE_LANE_3C_CFG_DIS_ACC_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_3C_CFG_DIS_ACC, x)
+#define SD10G_LANE_LANE_3C_CFG_DIS_ACC_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_3C_CFG_DIS_ACC, x)
+
+#define SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER BIT(1)
+#define SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER, x)
+#define SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_40 */
+#define SD10G_LANE_LANE_40(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 256, 0, 1, 4)
+
+#define SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0 GENMASK(7, 0)
+#define SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0, x)
+#define SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_41 */
+#define SD10G_LANE_LANE_41(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 260, 0, 1, 4)
+
+#define SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8 GENMASK(7, 0)
+#define SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8, x)
+#define SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_42 */
+#define SD10G_LANE_LANE_42(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 264, 0, 1, 4)
+
+#define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0 GENMASK(2, 0)
+#define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0, x)
+#define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0, x)
+
+#define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN2_2_0 GENMASK(6, 4)
+#define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN2_2_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_42_CFG_CDR_KF_GEN2_2_0, x)
+#define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN2_2_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_42_CFG_CDR_KF_GEN2_2_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_1:LANE_48 */
+#define SD10G_LANE_LANE_48(t) __REG(TARGET_SD10G_LANE, t, 12, 288, 0, 1, 40, 0, 0, 1, 4)
+
+#define SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0 GENMASK(3, 0)
+#define SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0, x)
+#define SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0, x)
+
+#define SD10G_LANE_LANE_48_CFG_AUX_RXCK_SEL BIT(4)
+#define SD10G_LANE_LANE_48_CFG_AUX_RXCK_SEL_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_48_CFG_AUX_RXCK_SEL, x)
+#define SD10G_LANE_LANE_48_CFG_AUX_RXCK_SEL_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_48_CFG_AUX_RXCK_SEL, x)
+
+#define SD10G_LANE_LANE_48_CFG_CLK_ENQ BIT(5)
+#define SD10G_LANE_LANE_48_CFG_CLK_ENQ_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_48_CFG_CLK_ENQ, x)
+#define SD10G_LANE_LANE_48_CFG_CLK_ENQ_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_48_CFG_CLK_ENQ, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_1:LANE_50 */
+#define SD10G_LANE_LANE_50(t) __REG(TARGET_SD10G_LANE, t, 12, 288, 0, 1, 40, 32, 0, 1, 4)
+
+#define SD10G_LANE_LANE_50_CFG_SSC_PI_STEP_1_0 GENMASK(1, 0)
+#define SD10G_LANE_LANE_50_CFG_SSC_PI_STEP_1_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_50_CFG_SSC_PI_STEP_1_0, x)
+#define SD10G_LANE_LANE_50_CFG_SSC_PI_STEP_1_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_50_CFG_SSC_PI_STEP_1_0, x)
+
+#define SD10G_LANE_LANE_50_CFG_SSC_RESETB BIT(4)
+#define SD10G_LANE_LANE_50_CFG_SSC_RESETB_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_50_CFG_SSC_RESETB, x)
+#define SD10G_LANE_LANE_50_CFG_SSC_RESETB_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_50_CFG_SSC_RESETB, x)
+
+#define SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL BIT(5)
+#define SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL, x)
+#define SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL, x)
+
+#define SD10G_LANE_LANE_50_CFG_AUX_TXCK_SEL BIT(6)
+#define SD10G_LANE_LANE_50_CFG_AUX_TXCK_SEL_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_50_CFG_AUX_TXCK_SEL, x)
+#define SD10G_LANE_LANE_50_CFG_AUX_TXCK_SEL_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_50_CFG_AUX_TXCK_SEL, x)
+
+#define SD10G_LANE_LANE_50_CFG_JT_EN BIT(7)
+#define SD10G_LANE_LANE_50_CFG_JT_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_50_CFG_JT_EN, x)
+#define SD10G_LANE_LANE_50_CFG_JT_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_50_CFG_JT_EN, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_2:LANE_52 */
+#define SD10G_LANE_LANE_52(t) __REG(TARGET_SD10G_LANE, t, 12, 328, 0, 1, 24, 0, 0, 1, 4)
+
+#define SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0 GENMASK(5, 0)
+#define SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0, x)
+#define SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_4:LANE_83 */
+#define SD10G_LANE_LANE_83(t) __REG(TARGET_SD10G_LANE, t, 12, 464, 0, 1, 112, 60, 0, 1, 4)
+
+#define SD10G_LANE_LANE_83_R_TX_BIT_REVERSE BIT(0)
+#define SD10G_LANE_LANE_83_R_TX_BIT_REVERSE_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_83_R_TX_BIT_REVERSE, x)
+#define SD10G_LANE_LANE_83_R_TX_BIT_REVERSE_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_83_R_TX_BIT_REVERSE, x)
+
+#define SD10G_LANE_LANE_83_R_TX_POL_INV BIT(1)
+#define SD10G_LANE_LANE_83_R_TX_POL_INV_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_83_R_TX_POL_INV, x)
+#define SD10G_LANE_LANE_83_R_TX_POL_INV_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_83_R_TX_POL_INV, x)
+
+#define SD10G_LANE_LANE_83_R_RX_BIT_REVERSE BIT(2)
+#define SD10G_LANE_LANE_83_R_RX_BIT_REVERSE_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_83_R_RX_BIT_REVERSE, x)
+#define SD10G_LANE_LANE_83_R_RX_BIT_REVERSE_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_83_R_RX_BIT_REVERSE, x)
+
+#define SD10G_LANE_LANE_83_R_RX_POL_INV BIT(3)
+#define SD10G_LANE_LANE_83_R_RX_POL_INV_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_83_R_RX_POL_INV, x)
+#define SD10G_LANE_LANE_83_R_RX_POL_INV_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_83_R_RX_POL_INV, x)
+
+#define SD10G_LANE_LANE_83_R_DFE_RSTN BIT(4)
+#define SD10G_LANE_LANE_83_R_DFE_RSTN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_83_R_DFE_RSTN, x)
+#define SD10G_LANE_LANE_83_R_DFE_RSTN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_83_R_DFE_RSTN, x)
+
+#define SD10G_LANE_LANE_83_R_CDR_RSTN BIT(5)
+#define SD10G_LANE_LANE_83_R_CDR_RSTN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_83_R_CDR_RSTN, x)
+#define SD10G_LANE_LANE_83_R_CDR_RSTN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_83_R_CDR_RSTN, x)
+
+#define SD10G_LANE_LANE_83_R_CTLE_RSTN BIT(6)
+#define SD10G_LANE_LANE_83_R_CTLE_RSTN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_83_R_CTLE_RSTN, x)
+#define SD10G_LANE_LANE_83_R_CTLE_RSTN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_83_R_CTLE_RSTN, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_5:LANE_93 */
+#define SD10G_LANE_LANE_93(t) __REG(TARGET_SD10G_LANE, t, 12, 576, 0, 1, 64, 12, 0, 1, 4)
+
+#define SD10G_LANE_LANE_93_R_RXEI_FIFO_RST_EN BIT(0)
+#define SD10G_LANE_LANE_93_R_RXEI_FIFO_RST_EN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_93_R_RXEI_FIFO_RST_EN, x)
+#define SD10G_LANE_LANE_93_R_RXEI_FIFO_RST_EN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_93_R_RXEI_FIFO_RST_EN, x)
+
+#define SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT BIT(1)
+#define SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT, x)
+#define SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT, x)
+
+#define SD10G_LANE_LANE_93_R_DIS_RESTORE_DFE BIT(2)
+#define SD10G_LANE_LANE_93_R_DIS_RESTORE_DFE_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_93_R_DIS_RESTORE_DFE, x)
+#define SD10G_LANE_LANE_93_R_DIS_RESTORE_DFE_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_93_R_DIS_RESTORE_DFE, x)
+
+#define SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL BIT(3)
+#define SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL, x)
+#define SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL, x)
+
+#define SD10G_LANE_LANE_93_R_REG_MANUAL BIT(4)
+#define SD10G_LANE_LANE_93_R_REG_MANUAL_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_93_R_REG_MANUAL, x)
+#define SD10G_LANE_LANE_93_R_REG_MANUAL_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_93_R_REG_MANUAL, x)
+
+#define SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT BIT(5)
+#define SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT, x)
+#define SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT, x)
+
+#define SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT BIT(6)
+#define SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT, x)
+#define SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT, x)
+
+#define SD10G_LANE_LANE_93_R_RX_PCIE_GEN12_FROM_HWT BIT(7)
+#define SD10G_LANE_LANE_93_R_RX_PCIE_GEN12_FROM_HWT_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_93_R_RX_PCIE_GEN12_FROM_HWT, x)
+#define SD10G_LANE_LANE_93_R_RX_PCIE_GEN12_FROM_HWT_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_93_R_RX_PCIE_GEN12_FROM_HWT, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_5:LANE_94 */
+#define SD10G_LANE_LANE_94(t) __REG(TARGET_SD10G_LANE, t, 12, 576, 0, 1, 64, 16, 0, 1, 4)
+
+#define SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0 GENMASK(2, 0)
+#define SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0, x)
+#define SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0, x)
+
+#define SD10G_LANE_LANE_94_R_ISCAN_REG BIT(4)
+#define SD10G_LANE_LANE_94_R_ISCAN_REG_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_94_R_ISCAN_REG, x)
+#define SD10G_LANE_LANE_94_R_ISCAN_REG_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_94_R_ISCAN_REG, x)
+
+#define SD10G_LANE_LANE_94_R_TXEQ_REG BIT(5)
+#define SD10G_LANE_LANE_94_R_TXEQ_REG_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_94_R_TXEQ_REG, x)
+#define SD10G_LANE_LANE_94_R_TXEQ_REG_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_94_R_TXEQ_REG, x)
+
+#define SD10G_LANE_LANE_94_R_MISC_REG BIT(6)
+#define SD10G_LANE_LANE_94_R_MISC_REG_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_94_R_MISC_REG, x)
+#define SD10G_LANE_LANE_94_R_MISC_REG_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_94_R_MISC_REG, x)
+
+#define SD10G_LANE_LANE_94_R_SWING_REG BIT(7)
+#define SD10G_LANE_LANE_94_R_SWING_REG_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_94_R_SWING_REG, x)
+#define SD10G_LANE_LANE_94_R_SWING_REG_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_94_R_SWING_REG, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_5:LANE_9E */
+#define SD10G_LANE_LANE_9E(t) __REG(TARGET_SD10G_LANE, t, 12, 576, 0, 1, 64, 56, 0, 1, 4)
+
+#define SD10G_LANE_LANE_9E_R_RXEQ_REG BIT(0)
+#define SD10G_LANE_LANE_9E_R_RXEQ_REG_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_9E_R_RXEQ_REG, x)
+#define SD10G_LANE_LANE_9E_R_RXEQ_REG_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_9E_R_RXEQ_REG, x)
+
+#define SD10G_LANE_LANE_9E_R_AUTO_RST_TREE_PD_MAN BIT(1)
+#define SD10G_LANE_LANE_9E_R_AUTO_RST_TREE_PD_MAN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_9E_R_AUTO_RST_TREE_PD_MAN, x)
+#define SD10G_LANE_LANE_9E_R_AUTO_RST_TREE_PD_MAN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_9E_R_AUTO_RST_TREE_PD_MAN, x)
+
+#define SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN BIT(2)
+#define SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN, x)
+#define SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_6:LANE_A1 */
+#define SD10G_LANE_LANE_A1(t) __REG(TARGET_SD10G_LANE, t, 12, 640, 0, 1, 128, 4, 0, 1, 4)
+
+#define SD10G_LANE_LANE_A1_R_PMA_TXCK_DIV_SEL_1_0 GENMASK(1, 0)
+#define SD10G_LANE_LANE_A1_R_PMA_TXCK_DIV_SEL_1_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_A1_R_PMA_TXCK_DIV_SEL_1_0, x)
+#define SD10G_LANE_LANE_A1_R_PMA_TXCK_DIV_SEL_1_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_A1_R_PMA_TXCK_DIV_SEL_1_0, x)
+
+#define SD10G_LANE_LANE_A1_R_SSC_FROM_HWT BIT(4)
+#define SD10G_LANE_LANE_A1_R_SSC_FROM_HWT_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_A1_R_SSC_FROM_HWT, x)
+#define SD10G_LANE_LANE_A1_R_SSC_FROM_HWT_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_A1_R_SSC_FROM_HWT, x)
+
+#define SD10G_LANE_LANE_A1_R_CDR_FROM_HWT BIT(5)
+#define SD10G_LANE_LANE_A1_R_CDR_FROM_HWT_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_A1_R_CDR_FROM_HWT, x)
+#define SD10G_LANE_LANE_A1_R_CDR_FROM_HWT_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_A1_R_CDR_FROM_HWT, x)
+
+#define SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT BIT(6)
+#define SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT, x)
+#define SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT, x)
+
+#define SD10G_LANE_LANE_A1_R_PCLK_GATING BIT(7)
+#define SD10G_LANE_LANE_A1_R_PCLK_GATING_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_A1_R_PCLK_GATING, x)
+#define SD10G_LANE_LANE_A1_R_PCLK_GATING_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_A1_R_PCLK_GATING, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_6:LANE_A2 */
+#define SD10G_LANE_LANE_A2(t) __REG(TARGET_SD10G_LANE, t, 12, 640, 0, 1, 128, 8, 0, 1, 4)
+
+#define SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0 GENMASK(4, 0)
+#define SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0, x)
+#define SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_8:LANE_DF */
+#define SD10G_LANE_LANE_DF(t) __REG(TARGET_SD10G_LANE, t, 12, 832, 0, 1, 84, 60, 0, 1, 4)
+
+#define SD10G_LANE_LANE_DF_LOL_UDL BIT(0)
+#define SD10G_LANE_LANE_DF_LOL_UDL_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_DF_LOL_UDL, x)
+#define SD10G_LANE_LANE_DF_LOL_UDL_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_DF_LOL_UDL, x)
+
+#define SD10G_LANE_LANE_DF_LOL BIT(1)
+#define SD10G_LANE_LANE_DF_LOL_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_DF_LOL, x)
+#define SD10G_LANE_LANE_DF_LOL_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_DF_LOL, x)
+
+#define SD10G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED BIT(2)
+#define SD10G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED, x)
+#define SD10G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED, x)
+
+#define SD10G_LANE_LANE_DF_SQUELCH BIT(3)
+#define SD10G_LANE_LANE_DF_SQUELCH_SET(x)\
+ FIELD_PREP(SD10G_LANE_LANE_DF_SQUELCH, x)
+#define SD10G_LANE_LANE_DF_SQUELCH_GET(x)\
+ FIELD_GET(SD10G_LANE_LANE_DF_SQUELCH, x)
+
+/* SD25G_TARGET:CMU_GRP_0:CMU_09 */
+#define SD25G_LANE_CMU_09(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 36, 0, 1, 4)
+
+#define SD25G_LANE_CMU_09_CFG_REFCK_TERM_EN BIT(0)
+#define SD25G_LANE_CMU_09_CFG_REFCK_TERM_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_09_CFG_REFCK_TERM_EN, x)
+#define SD25G_LANE_CMU_09_CFG_REFCK_TERM_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_09_CFG_REFCK_TERM_EN, x)
+
+#define SD25G_LANE_CMU_09_CFG_EN_DUMMY BIT(1)
+#define SD25G_LANE_CMU_09_CFG_EN_DUMMY_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_09_CFG_EN_DUMMY, x)
+#define SD25G_LANE_CMU_09_CFG_EN_DUMMY_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_09_CFG_EN_DUMMY, x)
+
+#define SD25G_LANE_CMU_09_CFG_PLL_LOS_SET BIT(2)
+#define SD25G_LANE_CMU_09_CFG_PLL_LOS_SET_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_09_CFG_PLL_LOS_SET, x)
+#define SD25G_LANE_CMU_09_CFG_PLL_LOS_SET_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_09_CFG_PLL_LOS_SET, x)
+
+#define SD25G_LANE_CMU_09_CFG_CTRL_LOGIC_PD BIT(3)
+#define SD25G_LANE_CMU_09_CFG_CTRL_LOGIC_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_09_CFG_CTRL_LOGIC_PD, x)
+#define SD25G_LANE_CMU_09_CFG_CTRL_LOGIC_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_09_CFG_CTRL_LOGIC_PD, x)
+
+#define SD25G_LANE_CMU_09_CFG_PLL_TP_SEL_1_0 GENMASK(5, 4)
+#define SD25G_LANE_CMU_09_CFG_PLL_TP_SEL_1_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_09_CFG_PLL_TP_SEL_1_0, x)
+#define SD25G_LANE_CMU_09_CFG_PLL_TP_SEL_1_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_09_CFG_PLL_TP_SEL_1_0, x)
+
+/* SD25G_TARGET:CMU_GRP_0:CMU_0B */
+#define SD25G_LANE_CMU_0B(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 44, 0, 1, 4)
+
+#define SD25G_LANE_CMU_0B_CFG_FORCE_RX_FILT BIT(0)
+#define SD25G_LANE_CMU_0B_CFG_FORCE_RX_FILT_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0B_CFG_FORCE_RX_FILT, x)
+#define SD25G_LANE_CMU_0B_CFG_FORCE_RX_FILT_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0B_CFG_FORCE_RX_FILT, x)
+
+#define SD25G_LANE_CMU_0B_CFG_DISLOL BIT(1)
+#define SD25G_LANE_CMU_0B_CFG_DISLOL_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0B_CFG_DISLOL, x)
+#define SD25G_LANE_CMU_0B_CFG_DISLOL_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0B_CFG_DISLOL, x)
+
+#define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_EN BIT(2)
+#define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_EN, x)
+#define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_EN, x)
+
+#define SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN BIT(3)
+#define SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN, x)
+#define SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN, x)
+
+#define SD25G_LANE_CMU_0B_CFG_VFILT2PAD BIT(4)
+#define SD25G_LANE_CMU_0B_CFG_VFILT2PAD_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0B_CFG_VFILT2PAD, x)
+#define SD25G_LANE_CMU_0B_CFG_VFILT2PAD_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0B_CFG_VFILT2PAD, x)
+
+#define SD25G_LANE_CMU_0B_CFG_DISLOS BIT(5)
+#define SD25G_LANE_CMU_0B_CFG_DISLOS_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0B_CFG_DISLOS, x)
+#define SD25G_LANE_CMU_0B_CFG_DISLOS_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0B_CFG_DISLOS, x)
+
+#define SD25G_LANE_CMU_0B_CFG_DCLOL BIT(6)
+#define SD25G_LANE_CMU_0B_CFG_DCLOL_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0B_CFG_DCLOL, x)
+#define SD25G_LANE_CMU_0B_CFG_DCLOL_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0B_CFG_DCLOL, x)
+
+#define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN BIT(7)
+#define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN, x)
+#define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN, x)
+
+/* SD25G_TARGET:CMU_GRP_0:CMU_0C */
+#define SD25G_LANE_CMU_0C(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 48, 0, 1, 4)
+
+#define SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET BIT(0)
+#define SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET, x)
+#define SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET, x)
+
+#define SD25G_LANE_CMU_0C_CFG_EN_TX_CK_DN BIT(1)
+#define SD25G_LANE_CMU_0C_CFG_EN_TX_CK_DN_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0C_CFG_EN_TX_CK_DN, x)
+#define SD25G_LANE_CMU_0C_CFG_EN_TX_CK_DN_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0C_CFG_EN_TX_CK_DN, x)
+
+#define SD25G_LANE_CMU_0C_CFG_VCO_PD BIT(2)
+#define SD25G_LANE_CMU_0C_CFG_VCO_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0C_CFG_VCO_PD, x)
+#define SD25G_LANE_CMU_0C_CFG_VCO_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0C_CFG_VCO_PD, x)
+
+#define SD25G_LANE_CMU_0C_CFG_EN_TX_CK_UP BIT(3)
+#define SD25G_LANE_CMU_0C_CFG_EN_TX_CK_UP_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0C_CFG_EN_TX_CK_UP, x)
+#define SD25G_LANE_CMU_0C_CFG_EN_TX_CK_UP_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0C_CFG_EN_TX_CK_UP, x)
+
+#define SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0 GENMASK(5, 4)
+#define SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0, x)
+#define SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0, x)
+
+/* SD25G_TARGET:CMU_GRP_0:CMU_0D */
+#define SD25G_LANE_CMU_0D(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 52, 0, 1, 4)
+
+#define SD25G_LANE_CMU_0D_CFG_CK_TREE_PD BIT(0)
+#define SD25G_LANE_CMU_0D_CFG_CK_TREE_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0D_CFG_CK_TREE_PD, x)
+#define SD25G_LANE_CMU_0D_CFG_CK_TREE_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0D_CFG_CK_TREE_PD, x)
+
+#define SD25G_LANE_CMU_0D_CFG_EN_RX_CK_DN BIT(1)
+#define SD25G_LANE_CMU_0D_CFG_EN_RX_CK_DN_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0D_CFG_EN_RX_CK_DN, x)
+#define SD25G_LANE_CMU_0D_CFG_EN_RX_CK_DN_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0D_CFG_EN_RX_CK_DN, x)
+
+#define SD25G_LANE_CMU_0D_CFG_EN_RX_CK_UP BIT(2)
+#define SD25G_LANE_CMU_0D_CFG_EN_RX_CK_UP_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0D_CFG_EN_RX_CK_UP, x)
+#define SD25G_LANE_CMU_0D_CFG_EN_RX_CK_UP_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0D_CFG_EN_RX_CK_UP, x)
+
+#define SD25G_LANE_CMU_0D_CFG_VCO_CAL_BYP BIT(3)
+#define SD25G_LANE_CMU_0D_CFG_VCO_CAL_BYP_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0D_CFG_VCO_CAL_BYP, x)
+#define SD25G_LANE_CMU_0D_CFG_VCO_CAL_BYP_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0D_CFG_VCO_CAL_BYP, x)
+
+#define SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0 GENMASK(5, 4)
+#define SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0, x)
+#define SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0, x)
+
+/* SD25G_TARGET:CMU_GRP_0:CMU_0E */
+#define SD25G_LANE_CMU_0E(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 56, 0, 1, 4)
+
+#define SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0 GENMASK(3, 0)
+#define SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0, x)
+#define SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0, x)
+
+#define SD25G_LANE_CMU_0E_CFG_PMAA_CENTR_CK_PD BIT(4)
+#define SD25G_LANE_CMU_0E_CFG_PMAA_CENTR_CK_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_0E_CFG_PMAA_CENTR_CK_PD, x)
+#define SD25G_LANE_CMU_0E_CFG_PMAA_CENTR_CK_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_0E_CFG_PMAA_CENTR_CK_PD, x)
+
+/* SD25G_TARGET:CMU_GRP_0:CMU_13 */
+#define SD25G_LANE_CMU_13(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 76, 0, 1, 4)
+
+#define SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0 GENMASK(3, 0)
+#define SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0, x)
+#define SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0, x)
+
+#define SD25G_LANE_CMU_13_CFG_JT_EN BIT(4)
+#define SD25G_LANE_CMU_13_CFG_JT_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_13_CFG_JT_EN, x)
+#define SD25G_LANE_CMU_13_CFG_JT_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_13_CFG_JT_EN, x)
+
+/* SD25G_TARGET:CMU_GRP_0:CMU_18 */
+#define SD25G_LANE_CMU_18(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 96, 0, 1, 4)
+
+#define SD25G_LANE_CMU_18_R_PLL_RSTN BIT(0)
+#define SD25G_LANE_CMU_18_R_PLL_RSTN_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_18_R_PLL_RSTN, x)
+#define SD25G_LANE_CMU_18_R_PLL_RSTN_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_18_R_PLL_RSTN, x)
+
+#define SD25G_LANE_CMU_18_R_PLL_LOL_SET BIT(1)
+#define SD25G_LANE_CMU_18_R_PLL_LOL_SET_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_18_R_PLL_LOL_SET, x)
+#define SD25G_LANE_CMU_18_R_PLL_LOL_SET_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_18_R_PLL_LOL_SET, x)
+
+#define SD25G_LANE_CMU_18_R_PLL_LOS_SET BIT(2)
+#define SD25G_LANE_CMU_18_R_PLL_LOS_SET_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_18_R_PLL_LOS_SET, x)
+#define SD25G_LANE_CMU_18_R_PLL_LOS_SET_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_18_R_PLL_LOS_SET, x)
+
+#define SD25G_LANE_CMU_18_R_PLL_TP_SEL_1_0 GENMASK(5, 4)
+#define SD25G_LANE_CMU_18_R_PLL_TP_SEL_1_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_18_R_PLL_TP_SEL_1_0, x)
+#define SD25G_LANE_CMU_18_R_PLL_TP_SEL_1_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_18_R_PLL_TP_SEL_1_0, x)
+
+/* SD25G_TARGET:CMU_GRP_0:CMU_19 */
+#define SD25G_LANE_CMU_19(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 100, 0, 1, 4)
+
+#define SD25G_LANE_CMU_19_R_CK_RESETB BIT(0)
+#define SD25G_LANE_CMU_19_R_CK_RESETB_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_19_R_CK_RESETB, x)
+#define SD25G_LANE_CMU_19_R_CK_RESETB_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_19_R_CK_RESETB, x)
+
+#define SD25G_LANE_CMU_19_R_PLL_DLOL_EN BIT(1)
+#define SD25G_LANE_CMU_19_R_PLL_DLOL_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_19_R_PLL_DLOL_EN, x)
+#define SD25G_LANE_CMU_19_R_PLL_DLOL_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_19_R_PLL_DLOL_EN, x)
+
+/* SD25G_TARGET:CMU_GRP_0:CMU_1A */
+#define SD25G_LANE_CMU_1A(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 104, 0, 1, 4)
+
+#define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0 GENMASK(2, 0)
+#define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0, x)
+#define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0, x)
+
+#define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT BIT(4)
+#define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT, x)
+#define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT, x)
+
+#define SD25G_LANE_CMU_1A_R_MASK_EI_SOURCE BIT(5)
+#define SD25G_LANE_CMU_1A_R_MASK_EI_SOURCE_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_1A_R_MASK_EI_SOURCE, x)
+#define SD25G_LANE_CMU_1A_R_MASK_EI_SOURCE_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_1A_R_MASK_EI_SOURCE, x)
+
+#define SD25G_LANE_CMU_1A_R_REG_MANUAL BIT(6)
+#define SD25G_LANE_CMU_1A_R_REG_MANUAL_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_1A_R_REG_MANUAL, x)
+#define SD25G_LANE_CMU_1A_R_REG_MANUAL_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_1A_R_REG_MANUAL, x)
+
+/* SD25G_TARGET:CMU_GRP_1:CMU_2A */
+#define SD25G_LANE_CMU_2A(t) __REG(TARGET_SD25G_LANE, t, 8, 132, 0, 1, 124, 36, 0, 1, 4)
+
+#define SD25G_LANE_CMU_2A_R_DBG_SEL_1_0 GENMASK(1, 0)
+#define SD25G_LANE_CMU_2A_R_DBG_SEL_1_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_2A_R_DBG_SEL_1_0, x)
+#define SD25G_LANE_CMU_2A_R_DBG_SEL_1_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_2A_R_DBG_SEL_1_0, x)
+
+#define SD25G_LANE_CMU_2A_R_DBG_LINK_LANE BIT(4)
+#define SD25G_LANE_CMU_2A_R_DBG_LINK_LANE_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_2A_R_DBG_LINK_LANE, x)
+#define SD25G_LANE_CMU_2A_R_DBG_LINK_LANE_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_2A_R_DBG_LINK_LANE, x)
+
+#define SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS BIT(5)
+#define SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS, x)
+#define SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS, x)
+
+/* SD25G_TARGET:CMU_GRP_1:CMU_30 */
+#define SD25G_LANE_CMU_30(t) __REG(TARGET_SD25G_LANE, t, 8, 132, 0, 1, 124, 60, 0, 1, 4)
+
+#define SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0 GENMASK(2, 0)
+#define SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0, x)
+#define SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0, x)
+
+#define SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0 GENMASK(6, 4)
+#define SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0, x)
+#define SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0, x)
+
+/* SD25G_TARGET:CMU_GRP_1:CMU_31 */
+#define SD25G_LANE_CMU_31(t) __REG(TARGET_SD25G_LANE, t, 8, 132, 0, 1, 124, 64, 0, 1, 4)
+
+#define SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0 GENMASK(7, 0)
+#define SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0, x)
+#define SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0, x)
+
+/* SD25G_TARGET:CMU_GRP_2:CMU_40 */
+#define SD25G_LANE_CMU_40(t) __REG(TARGET_SD25G_LANE, t, 8, 256, 0, 1, 512, 0, 0, 1, 4)
+
+#define SD25G_LANE_CMU_40_L0_CFG_CKSKEW_CTRL BIT(0)
+#define SD25G_LANE_CMU_40_L0_CFG_CKSKEW_CTRL_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_40_L0_CFG_CKSKEW_CTRL, x)
+#define SD25G_LANE_CMU_40_L0_CFG_CKSKEW_CTRL_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_CKSKEW_CTRL, x)
+
+#define SD25G_LANE_CMU_40_L0_CFG_ISCAN_HOLD BIT(1)
+#define SD25G_LANE_CMU_40_L0_CFG_ISCAN_HOLD_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_40_L0_CFG_ISCAN_HOLD, x)
+#define SD25G_LANE_CMU_40_L0_CFG_ISCAN_HOLD_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_ISCAN_HOLD, x)
+
+#define SD25G_LANE_CMU_40_L0_CFG_PD_CLK BIT(2)
+#define SD25G_LANE_CMU_40_L0_CFG_PD_CLK_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_40_L0_CFG_PD_CLK, x)
+#define SD25G_LANE_CMU_40_L0_CFG_PD_CLK_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_PD_CLK, x)
+
+#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN BIT(3)
+#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN, x)
+#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN, x)
+
+#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_MAN_EN BIT(4)
+#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_MAN_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_40_L0_CFG_TXCAL_MAN_EN, x)
+#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_MAN_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_TXCAL_MAN_EN, x)
+
+#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_RST BIT(5)
+#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_RST_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_40_L0_CFG_TXCAL_RST, x)
+#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_RST_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_TXCAL_RST, x)
+
+/* SD25G_TARGET:CMU_GRP_2:CMU_45 */
+#define SD25G_LANE_CMU_45(t) __REG(TARGET_SD25G_LANE, t, 8, 256, 0, 1, 512, 20, 0, 1, 4)
+
+#define SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0 GENMASK(7, 0)
+#define SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0, x)
+#define SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0, x)
+
+/* SD25G_TARGET:CMU_GRP_2:CMU_46 */
+#define SD25G_LANE_CMU_46(t) __REG(TARGET_SD25G_LANE, t, 8, 256, 0, 1, 512, 24, 0, 1, 4)
+
+#define SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8 GENMASK(7, 0)
+#define SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8, x)
+#define SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8, x)
+
+/* SD25G_TARGET:CMU_GRP_3:CMU_C0 */
+#define SD25G_LANE_CMU_C0(t) __REG(TARGET_SD25G_LANE, t, 8, 768, 0, 1, 252, 0, 0, 1, 4)
+
+#define SD25G_LANE_CMU_C0_READ_VCO_CTUNE_3_0 GENMASK(3, 0)
+#define SD25G_LANE_CMU_C0_READ_VCO_CTUNE_3_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_C0_READ_VCO_CTUNE_3_0, x)
+#define SD25G_LANE_CMU_C0_READ_VCO_CTUNE_3_0_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_C0_READ_VCO_CTUNE_3_0, x)
+
+#define SD25G_LANE_CMU_C0_PLL_LOL_UDL BIT(4)
+#define SD25G_LANE_CMU_C0_PLL_LOL_UDL_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_C0_PLL_LOL_UDL, x)
+#define SD25G_LANE_CMU_C0_PLL_LOL_UDL_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_C0_PLL_LOL_UDL, x)
+
+/* SD25G_TARGET:CMU_GRP_4:CMU_FF */
+#define SD25G_LANE_CMU_FF(t) __REG(TARGET_SD25G_LANE, t, 8, 1020, 0, 1, 4, 0, 0, 1, 4)
+
+#define SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX GENMASK(7, 0)
+#define SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_SET(x)\
+ FIELD_PREP(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX, x)
+#define SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_GET(x)\
+ FIELD_GET(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_00 */
+#define SD25G_LANE_LANE_00(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 0, 0, 1, 4)
+
+#define SD25G_LANE_LANE_00_LN_CFG_ITX_VC_DRIVER_3_0 GENMASK(3, 0)
+#define SD25G_LANE_LANE_00_LN_CFG_ITX_VC_DRIVER_3_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_00_LN_CFG_ITX_VC_DRIVER_3_0, x)
+#define SD25G_LANE_LANE_00_LN_CFG_ITX_VC_DRIVER_3_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_00_LN_CFG_ITX_VC_DRIVER_3_0, x)
+
+#define SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0 GENMASK(5, 4)
+#define SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0, x)
+#define SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_01 */
+#define SD25G_LANE_LANE_01(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 4, 0, 1, 4)
+
+#define SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0 GENMASK(2, 0)
+#define SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0, x)
+#define SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0, x)
+
+#define SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0 GENMASK(5, 4)
+#define SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0, x)
+#define SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_03 */
+#define SD25G_LANE_LANE_03(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 12, 0, 1, 4)
+
+#define SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0 GENMASK(4, 0)
+#define SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0, x)
+#define SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_04 */
+#define SD25G_LANE_LANE_04(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 16, 0, 1, 4)
+
+#define SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN BIT(0)
+#define SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN, x)
+#define SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN, x)
+
+#define SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN BIT(1)
+#define SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN, x)
+#define SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN, x)
+
+#define SD25G_LANE_LANE_04_LN_CFG_PD_CML BIT(2)
+#define SD25G_LANE_LANE_04_LN_CFG_PD_CML_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_04_LN_CFG_PD_CML, x)
+#define SD25G_LANE_LANE_04_LN_CFG_PD_CML_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_PD_CML, x)
+
+#define SD25G_LANE_LANE_04_LN_CFG_PD_CLK BIT(3)
+#define SD25G_LANE_LANE_04_LN_CFG_PD_CLK_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_04_LN_CFG_PD_CLK, x)
+#define SD25G_LANE_LANE_04_LN_CFG_PD_CLK_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_PD_CLK, x)
+
+#define SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER BIT(4)
+#define SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER, x)
+#define SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER, x)
+
+#define SD25G_LANE_LANE_04_LN_CFG_TAP_MAIN BIT(5)
+#define SD25G_LANE_LANE_04_LN_CFG_TAP_MAIN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_04_LN_CFG_TAP_MAIN, x)
+#define SD25G_LANE_LANE_04_LN_CFG_TAP_MAIN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_TAP_MAIN, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_05 */
+#define SD25G_LANE_LANE_05(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 20, 0, 1, 4)
+
+#define SD25G_LANE_LANE_05_LN_CFG_TAP_DLY2_3_0 GENMASK(3, 0)
+#define SD25G_LANE_LANE_05_LN_CFG_TAP_DLY2_3_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_05_LN_CFG_TAP_DLY2_3_0, x)
+#define SD25G_LANE_LANE_05_LN_CFG_TAP_DLY2_3_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_05_LN_CFG_TAP_DLY2_3_0, x)
+
+#define SD25G_LANE_LANE_05_LN_CFG_BW_1_0 GENMASK(5, 4)
+#define SD25G_LANE_LANE_05_LN_CFG_BW_1_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_05_LN_CFG_BW_1_0, x)
+#define SD25G_LANE_LANE_05_LN_CFG_BW_1_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_05_LN_CFG_BW_1_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_06 */
+#define SD25G_LANE_LANE_06(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 24, 0, 1, 4)
+
+#define SD25G_LANE_LANE_06_LN_CFG_EN_MAIN BIT(0)
+#define SD25G_LANE_LANE_06_LN_CFG_EN_MAIN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_06_LN_CFG_EN_MAIN, x)
+#define SD25G_LANE_LANE_06_LN_CFG_EN_MAIN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_06_LN_CFG_EN_MAIN, x)
+
+#define SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0 GENMASK(7, 4)
+#define SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0, x)
+#define SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_07 */
+#define SD25G_LANE_LANE_07(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 28, 0, 1, 4)
+
+#define SD25G_LANE_LANE_07_LN_CFG_EN_ADV BIT(0)
+#define SD25G_LANE_LANE_07_LN_CFG_EN_ADV_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_07_LN_CFG_EN_ADV, x)
+#define SD25G_LANE_LANE_07_LN_CFG_EN_ADV_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_07_LN_CFG_EN_ADV, x)
+
+#define SD25G_LANE_LANE_07_LN_CFG_EN_DLY2 BIT(1)
+#define SD25G_LANE_LANE_07_LN_CFG_EN_DLY2_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_07_LN_CFG_EN_DLY2, x)
+#define SD25G_LANE_LANE_07_LN_CFG_EN_DLY2_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_07_LN_CFG_EN_DLY2, x)
+
+#define SD25G_LANE_LANE_07_LN_CFG_EN_DLY BIT(2)
+#define SD25G_LANE_LANE_07_LN_CFG_EN_DLY_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_07_LN_CFG_EN_DLY, x)
+#define SD25G_LANE_LANE_07_LN_CFG_EN_DLY_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_07_LN_CFG_EN_DLY, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_09 */
+#define SD25G_LANE_LANE_09(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 36, 0, 1, 4)
+
+#define SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0 GENMASK(3, 0)
+#define SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0, x)
+#define SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_0A */
+#define SD25G_LANE_LANE_0A(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 40, 0, 1, 4)
+
+#define SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0 GENMASK(5, 0)
+#define SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0, x)
+#define SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_0B */
+#define SD25G_LANE_LANE_0B(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 44, 0, 1, 4)
+
+#define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN BIT(0)
+#define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN, x)
+#define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN, x)
+
+#define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_RST BIT(1)
+#define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_RST_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0B_LN_CFG_TXCAL_RST, x)
+#define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_RST_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0B_LN_CFG_TXCAL_RST, x)
+
+#define SD25G_LANE_LANE_0B_LN_CFG_QUAD_MAN_1_0 GENMASK(5, 4)
+#define SD25G_LANE_LANE_0B_LN_CFG_QUAD_MAN_1_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0B_LN_CFG_QUAD_MAN_1_0, x)
+#define SD25G_LANE_LANE_0B_LN_CFG_QUAD_MAN_1_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0B_LN_CFG_QUAD_MAN_1_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_0C */
+#define SD25G_LANE_LANE_0C(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 48, 0, 1, 4)
+
+#define SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0 GENMASK(2, 0)
+#define SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0, x)
+#define SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0, x)
+
+#define SD25G_LANE_LANE_0C_LN_CFG_TXCAL_EN BIT(4)
+#define SD25G_LANE_LANE_0C_LN_CFG_TXCAL_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0C_LN_CFG_TXCAL_EN, x)
+#define SD25G_LANE_LANE_0C_LN_CFG_TXCAL_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0C_LN_CFG_TXCAL_EN, x)
+
+#define SD25G_LANE_LANE_0C_LN_CFG_RXTERM_PD BIT(5)
+#define SD25G_LANE_LANE_0C_LN_CFG_RXTERM_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0C_LN_CFG_RXTERM_PD, x)
+#define SD25G_LANE_LANE_0C_LN_CFG_RXTERM_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0C_LN_CFG_RXTERM_PD, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_0D */
+#define SD25G_LANE_LANE_0D(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 52, 0, 1, 4)
+
+#define SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0 GENMASK(2, 0)
+#define SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0, x)
+#define SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0, x)
+
+#define SD25G_LANE_LANE_0D_LN_CFG_RSTN_DIV5_8 BIT(4)
+#define SD25G_LANE_LANE_0D_LN_CFG_RSTN_DIV5_8_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0D_LN_CFG_RSTN_DIV5_8, x)
+#define SD25G_LANE_LANE_0D_LN_CFG_RSTN_DIV5_8_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0D_LN_CFG_RSTN_DIV5_8, x)
+
+#define SD25G_LANE_LANE_0D_LN_CFG_SUMMER_EN BIT(5)
+#define SD25G_LANE_LANE_0D_LN_CFG_SUMMER_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0D_LN_CFG_SUMMER_EN, x)
+#define SD25G_LANE_LANE_0D_LN_CFG_SUMMER_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0D_LN_CFG_SUMMER_EN, x)
+
+#define SD25G_LANE_LANE_0D_LN_CFG_DMUX_PD BIT(6)
+#define SD25G_LANE_LANE_0D_LN_CFG_DMUX_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0D_LN_CFG_DMUX_PD, x)
+#define SD25G_LANE_LANE_0D_LN_CFG_DMUX_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0D_LN_CFG_DMUX_PD, x)
+
+#define SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN BIT(7)
+#define SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN, x)
+#define SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_0E */
+#define SD25G_LANE_LANE_0E(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 56, 0, 1, 4)
+
+#define SD25G_LANE_LANE_0E_LN_CFG_ISCAN_EN BIT(0)
+#define SD25G_LANE_LANE_0E_LN_CFG_ISCAN_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0E_LN_CFG_ISCAN_EN, x)
+#define SD25G_LANE_LANE_0E_LN_CFG_ISCAN_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0E_LN_CFG_ISCAN_EN, x)
+
+#define SD25G_LANE_LANE_0E_LN_CFG_DMUX_CLK_PD BIT(1)
+#define SD25G_LANE_LANE_0E_LN_CFG_DMUX_CLK_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0E_LN_CFG_DMUX_CLK_PD, x)
+#define SD25G_LANE_LANE_0E_LN_CFG_DMUX_CLK_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0E_LN_CFG_DMUX_CLK_PD, x)
+
+#define SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG BIT(2)
+#define SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG, x)
+#define SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG, x)
+
+#define SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0 GENMASK(6, 4)
+#define SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0, x)
+#define SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_0F */
+#define SD25G_LANE_LANE_0F(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 60, 0, 1, 4)
+
+#define SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1 GENMASK(4, 0)
+#define SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1, x)
+#define SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_18 */
+#define SD25G_LANE_LANE_18(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 96, 0, 1, 4)
+
+#define SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN BIT(0)
+#define SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN, x)
+#define SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN, x)
+
+#define SD25G_LANE_LANE_18_LN_CFG_ADD_VOLT BIT(1)
+#define SD25G_LANE_LANE_18_LN_CFG_ADD_VOLT_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_18_LN_CFG_ADD_VOLT, x)
+#define SD25G_LANE_LANE_18_LN_CFG_ADD_VOLT_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_18_LN_CFG_ADD_VOLT, x)
+
+#define SD25G_LANE_LANE_18_LN_CFG_MAN_VOLT_EN BIT(2)
+#define SD25G_LANE_LANE_18_LN_CFG_MAN_VOLT_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_18_LN_CFG_MAN_VOLT_EN, x)
+#define SD25G_LANE_LANE_18_LN_CFG_MAN_VOLT_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_18_LN_CFG_MAN_VOLT_EN, x)
+
+#define SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD BIT(3)
+#define SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD, x)
+#define SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD, x)
+
+#define SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0 GENMASK(6, 4)
+#define SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0, x)
+#define SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_19 */
+#define SD25G_LANE_LANE_19(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 100, 0, 1, 4)
+
+#define SD25G_LANE_LANE_19_LN_CFG_DCDR_PD BIT(0)
+#define SD25G_LANE_LANE_19_LN_CFG_DCDR_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_DCDR_PD, x)
+#define SD25G_LANE_LANE_19_LN_CFG_DCDR_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_DCDR_PD, x)
+
+#define SD25G_LANE_LANE_19_LN_CFG_ECDR_PD BIT(1)
+#define SD25G_LANE_LANE_19_LN_CFG_ECDR_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_ECDR_PD, x)
+#define SD25G_LANE_LANE_19_LN_CFG_ECDR_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_ECDR_PD, x)
+
+#define SD25G_LANE_LANE_19_LN_CFG_ISCAN_SEL BIT(2)
+#define SD25G_LANE_LANE_19_LN_CFG_ISCAN_SEL_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_ISCAN_SEL, x)
+#define SD25G_LANE_LANE_19_LN_CFG_ISCAN_SEL_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_ISCAN_SEL, x)
+
+#define SD25G_LANE_LANE_19_LN_CFG_TXLB_EN BIT(3)
+#define SD25G_LANE_LANE_19_LN_CFG_TXLB_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_TXLB_EN, x)
+#define SD25G_LANE_LANE_19_LN_CFG_TXLB_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_TXLB_EN, x)
+
+#define SD25G_LANE_LANE_19_LN_CFG_RX_REG_PU BIT(4)
+#define SD25G_LANE_LANE_19_LN_CFG_RX_REG_PU_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_RX_REG_PU, x)
+#define SD25G_LANE_LANE_19_LN_CFG_RX_REG_PU_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_RX_REG_PU, x)
+
+#define SD25G_LANE_LANE_19_LN_CFG_RX_REG_BYP BIT(5)
+#define SD25G_LANE_LANE_19_LN_CFG_RX_REG_BYP_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_RX_REG_BYP, x)
+#define SD25G_LANE_LANE_19_LN_CFG_RX_REG_BYP_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_RX_REG_BYP, x)
+
+#define SD25G_LANE_LANE_19_LN_CFG_PD_RMS_DET BIT(6)
+#define SD25G_LANE_LANE_19_LN_CFG_PD_RMS_DET_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_PD_RMS_DET, x)
+#define SD25G_LANE_LANE_19_LN_CFG_PD_RMS_DET_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_PD_RMS_DET, x)
+
+#define SD25G_LANE_LANE_19_LN_CFG_PD_CTLE BIT(7)
+#define SD25G_LANE_LANE_19_LN_CFG_PD_CTLE_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_PD_CTLE, x)
+#define SD25G_LANE_LANE_19_LN_CFG_PD_CTLE_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_PD_CTLE, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_1A */
+#define SD25G_LANE_LANE_1A(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 104, 0, 1, 4)
+
+#define SD25G_LANE_LANE_1A_LN_CFG_CTLE_TP_EN BIT(0)
+#define SD25G_LANE_LANE_1A_LN_CFG_CTLE_TP_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1A_LN_CFG_CTLE_TP_EN, x)
+#define SD25G_LANE_LANE_1A_LN_CFG_CTLE_TP_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1A_LN_CFG_CTLE_TP_EN, x)
+
+#define SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0 GENMASK(6, 4)
+#define SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0, x)
+#define SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_1B */
+#define SD25G_LANE_LANE_1B(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 108, 0, 1, 4)
+
+#define SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0 GENMASK(7, 0)
+#define SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0, x)
+#define SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_1C */
+#define SD25G_LANE_LANE_1C(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 112, 0, 1, 4)
+
+#define SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN BIT(0)
+#define SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN, x)
+#define SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN, x)
+
+#define SD25G_LANE_LANE_1C_LN_CFG_DFE_PD BIT(1)
+#define SD25G_LANE_LANE_1C_LN_CFG_DFE_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1C_LN_CFG_DFE_PD, x)
+#define SD25G_LANE_LANE_1C_LN_CFG_DFE_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1C_LN_CFG_DFE_PD, x)
+
+#define SD25G_LANE_LANE_1C_LN_CFG_DFEDMX_PD BIT(2)
+#define SD25G_LANE_LANE_1C_LN_CFG_DFEDMX_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1C_LN_CFG_DFEDMX_PD, x)
+#define SD25G_LANE_LANE_1C_LN_CFG_DFEDMX_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1C_LN_CFG_DFEDMX_PD, x)
+
+#define SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0 GENMASK(7, 4)
+#define SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0, x)
+#define SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_1D */
+#define SD25G_LANE_LANE_1D(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 116, 0, 1, 4)
+
+#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_EXT_OVR BIT(0)
+#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_EXT_OVR_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_ISCAN_EXT_OVR, x)
+#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_EXT_OVR_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_ISCAN_EXT_OVR, x)
+
+#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_HOLD BIT(1)
+#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_HOLD_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_ISCAN_HOLD, x)
+#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_HOLD_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_ISCAN_HOLD, x)
+
+#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_RSTN BIT(2)
+#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_RSTN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_ISCAN_RSTN, x)
+#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_RSTN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_ISCAN_RSTN, x)
+
+#define SD25G_LANE_LANE_1D_LN_CFG_AGC_ADPT_BYP BIT(3)
+#define SD25G_LANE_LANE_1D_LN_CFG_AGC_ADPT_BYP_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_AGC_ADPT_BYP, x)
+#define SD25G_LANE_LANE_1D_LN_CFG_AGC_ADPT_BYP_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_AGC_ADPT_BYP, x)
+
+#define SD25G_LANE_LANE_1D_LN_CFG_PHID_1T BIT(4)
+#define SD25G_LANE_LANE_1D_LN_CFG_PHID_1T_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_PHID_1T, x)
+#define SD25G_LANE_LANE_1D_LN_CFG_PHID_1T_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_PHID_1T, x)
+
+#define SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN BIT(5)
+#define SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN, x)
+#define SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN, x)
+
+#define SD25G_LANE_LANE_1D_LN_CFG_PI_EXT_OVR BIT(6)
+#define SD25G_LANE_LANE_1D_LN_CFG_PI_EXT_OVR_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_PI_EXT_OVR, x)
+#define SD25G_LANE_LANE_1D_LN_CFG_PI_EXT_OVR_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_PI_EXT_OVR, x)
+
+#define SD25G_LANE_LANE_1D_LN_CFG_PI_HOLD BIT(7)
+#define SD25G_LANE_LANE_1D_LN_CFG_PI_HOLD_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_PI_HOLD, x)
+#define SD25G_LANE_LANE_1D_LN_CFG_PI_HOLD_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_PI_HOLD, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_1E */
+#define SD25G_LANE_LANE_1E(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 120, 0, 1, 4)
+
+#define SD25G_LANE_LANE_1E_LN_CFG_PI_STEPS_1_0 GENMASK(1, 0)
+#define SD25G_LANE_LANE_1E_LN_CFG_PI_STEPS_1_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1E_LN_CFG_PI_STEPS_1_0, x)
+#define SD25G_LANE_LANE_1E_LN_CFG_PI_STEPS_1_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1E_LN_CFG_PI_STEPS_1_0, x)
+
+#define SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN BIT(4)
+#define SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN, x)
+#define SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN, x)
+
+#define SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN BIT(5)
+#define SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN, x)
+#define SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN, x)
+
+#define SD25G_LANE_LANE_1E_LN_CFG_R_OFFSET_DIR BIT(6)
+#define SD25G_LANE_LANE_1E_LN_CFG_R_OFFSET_DIR_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1E_LN_CFG_R_OFFSET_DIR, x)
+#define SD25G_LANE_LANE_1E_LN_CFG_R_OFFSET_DIR_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1E_LN_CFG_R_OFFSET_DIR, x)
+
+#define SD25G_LANE_LANE_1E_LN_CFG_PMAD_CK_PD BIT(7)
+#define SD25G_LANE_LANE_1E_LN_CFG_PMAD_CK_PD_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_1E_LN_CFG_PMAD_CK_PD, x)
+#define SD25G_LANE_LANE_1E_LN_CFG_PMAD_CK_PD_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_1E_LN_CFG_PMAD_CK_PD, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_21 */
+#define SD25G_LANE_LANE_21(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 132, 0, 1, 4)
+
+#define SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0 GENMASK(4, 0)
+#define SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0, x)
+#define SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_22 */
+#define SD25G_LANE_LANE_22(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 136, 0, 1, 4)
+
+#define SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0 GENMASK(3, 0)
+#define SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0, x)
+#define SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_25 */
+#define SD25G_LANE_LANE_25(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 148, 0, 1, 4)
+
+#define SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0 GENMASK(6, 0)
+#define SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0, x)
+#define SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_26 */
+#define SD25G_LANE_LANE_26(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 152, 0, 1, 4)
+
+#define SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0 GENMASK(6, 0)
+#define SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0, x)
+#define SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_28 */
+#define SD25G_LANE_LANE_28(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 160, 0, 1, 4)
+
+#define SD25G_LANE_LANE_28_LN_CFG_ISCAN_MODE_EN BIT(0)
+#define SD25G_LANE_LANE_28_LN_CFG_ISCAN_MODE_EN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_28_LN_CFG_ISCAN_MODE_EN, x)
+#define SD25G_LANE_LANE_28_LN_CFG_ISCAN_MODE_EN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_28_LN_CFG_ISCAN_MODE_EN, x)
+
+#define SD25G_LANE_LANE_28_LN_CFG_RX_SSC_LH BIT(1)
+#define SD25G_LANE_LANE_28_LN_CFG_RX_SSC_LH_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_28_LN_CFG_RX_SSC_LH, x)
+#define SD25G_LANE_LANE_28_LN_CFG_RX_SSC_LH_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_28_LN_CFG_RX_SSC_LH, x)
+
+#define SD25G_LANE_LANE_28_LN_CFG_FIGMERIT_SEL BIT(2)
+#define SD25G_LANE_LANE_28_LN_CFG_FIGMERIT_SEL_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_28_LN_CFG_FIGMERIT_SEL, x)
+#define SD25G_LANE_LANE_28_LN_CFG_FIGMERIT_SEL_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_28_LN_CFG_FIGMERIT_SEL, x)
+
+#define SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0 GENMASK(6, 4)
+#define SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0, x)
+#define SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_2B */
+#define SD25G_LANE_LANE_2B(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 172, 0, 1, 4)
+
+#define SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0 GENMASK(3, 0)
+#define SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0, x)
+#define SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0, x)
+
+#define SD25G_LANE_LANE_2B_LN_CFG_RSTN_DMUX_SUBR BIT(4)
+#define SD25G_LANE_LANE_2B_LN_CFG_RSTN_DMUX_SUBR_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2B_LN_CFG_RSTN_DMUX_SUBR, x)
+#define SD25G_LANE_LANE_2B_LN_CFG_RSTN_DMUX_SUBR_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2B_LN_CFG_RSTN_DMUX_SUBR, x)
+
+#define SD25G_LANE_LANE_2B_LN_CFG_RSTN_TXDUPU BIT(5)
+#define SD25G_LANE_LANE_2B_LN_CFG_RSTN_TXDUPU_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2B_LN_CFG_RSTN_TXDUPU, x)
+#define SD25G_LANE_LANE_2B_LN_CFG_RSTN_TXDUPU_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2B_LN_CFG_RSTN_TXDUPU, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_2C */
+#define SD25G_LANE_LANE_2C(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 176, 0, 1, 4)
+
+#define SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0 GENMASK(2, 0)
+#define SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0, x)
+#define SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0, x)
+
+#define SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER BIT(4)
+#define SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER, x)
+#define SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_2D */
+#define SD25G_LANE_LANE_2D(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 180, 0, 1, 4)
+
+#define SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0 GENMASK(2, 0)
+#define SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0, x)
+#define SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0, x)
+
+#define SD25G_LANE_LANE_2D_LN_CFG_SAT_CNTSEL_2_0 GENMASK(6, 4)
+#define SD25G_LANE_LANE_2D_LN_CFG_SAT_CNTSEL_2_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2D_LN_CFG_SAT_CNTSEL_2_0, x)
+#define SD25G_LANE_LANE_2D_LN_CFG_SAT_CNTSEL_2_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2D_LN_CFG_SAT_CNTSEL_2_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_2E */
+#define SD25G_LANE_LANE_2E(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 184, 0, 1, 4)
+
+#define SD25G_LANE_LANE_2E_LN_CFG_EN_FAST_ISCAN BIT(0)
+#define SD25G_LANE_LANE_2E_LN_CFG_EN_FAST_ISCAN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_EN_FAST_ISCAN, x)
+#define SD25G_LANE_LANE_2E_LN_CFG_EN_FAST_ISCAN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_EN_FAST_ISCAN, x)
+
+#define SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ BIT(1)
+#define SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ, x)
+#define SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ, x)
+
+#define SD25G_LANE_LANE_2E_LN_CFG_PD_SQ BIT(2)
+#define SD25G_LANE_LANE_2E_LN_CFG_PD_SQ_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_PD_SQ, x)
+#define SD25G_LANE_LANE_2E_LN_CFG_PD_SQ_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_PD_SQ, x)
+
+#define SD25G_LANE_LANE_2E_LN_CFG_DIS_ALOS BIT(3)
+#define SD25G_LANE_LANE_2E_LN_CFG_DIS_ALOS_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_DIS_ALOS, x)
+#define SD25G_LANE_LANE_2E_LN_CFG_DIS_ALOS_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_DIS_ALOS, x)
+
+#define SD25G_LANE_LANE_2E_LN_CFG_RESETN_AGC BIT(4)
+#define SD25G_LANE_LANE_2E_LN_CFG_RESETN_AGC_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_RESETN_AGC, x)
+#define SD25G_LANE_LANE_2E_LN_CFG_RESETN_AGC_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_RESETN_AGC, x)
+
+#define SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG BIT(5)
+#define SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG, x)
+#define SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG, x)
+
+#define SD25G_LANE_LANE_2E_LN_CFG_PI_RSTN BIT(6)
+#define SD25G_LANE_LANE_2E_LN_CFG_PI_RSTN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_PI_RSTN, x)
+#define SD25G_LANE_LANE_2E_LN_CFG_PI_RSTN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_PI_RSTN, x)
+
+#define SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN BIT(7)
+#define SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN, x)
+#define SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_40 */
+#define SD25G_LANE_LANE_40(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 256, 0, 1, 4)
+
+#define SD25G_LANE_LANE_40_LN_R_TX_BIT_REVERSE BIT(0)
+#define SD25G_LANE_LANE_40_LN_R_TX_BIT_REVERSE_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_40_LN_R_TX_BIT_REVERSE, x)
+#define SD25G_LANE_LANE_40_LN_R_TX_BIT_REVERSE_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_40_LN_R_TX_BIT_REVERSE, x)
+
+#define SD25G_LANE_LANE_40_LN_R_TX_POL_INV BIT(1)
+#define SD25G_LANE_LANE_40_LN_R_TX_POL_INV_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_40_LN_R_TX_POL_INV, x)
+#define SD25G_LANE_LANE_40_LN_R_TX_POL_INV_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_40_LN_R_TX_POL_INV, x)
+
+#define SD25G_LANE_LANE_40_LN_R_RX_BIT_REVERSE BIT(2)
+#define SD25G_LANE_LANE_40_LN_R_RX_BIT_REVERSE_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_40_LN_R_RX_BIT_REVERSE, x)
+#define SD25G_LANE_LANE_40_LN_R_RX_BIT_REVERSE_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_40_LN_R_RX_BIT_REVERSE, x)
+
+#define SD25G_LANE_LANE_40_LN_R_RX_POL_INV BIT(3)
+#define SD25G_LANE_LANE_40_LN_R_RX_POL_INV_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_40_LN_R_RX_POL_INV, x)
+#define SD25G_LANE_LANE_40_LN_R_RX_POL_INV_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_40_LN_R_RX_POL_INV, x)
+
+#define SD25G_LANE_LANE_40_LN_R_CDR_RSTN BIT(4)
+#define SD25G_LANE_LANE_40_LN_R_CDR_RSTN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_40_LN_R_CDR_RSTN, x)
+#define SD25G_LANE_LANE_40_LN_R_CDR_RSTN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_40_LN_R_CDR_RSTN, x)
+
+#define SD25G_LANE_LANE_40_LN_R_DFE_RSTN BIT(5)
+#define SD25G_LANE_LANE_40_LN_R_DFE_RSTN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_40_LN_R_DFE_RSTN, x)
+#define SD25G_LANE_LANE_40_LN_R_DFE_RSTN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_40_LN_R_DFE_RSTN, x)
+
+#define SD25G_LANE_LANE_40_LN_R_CTLE_RSTN BIT(6)
+#define SD25G_LANE_LANE_40_LN_R_CTLE_RSTN_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_40_LN_R_CTLE_RSTN, x)
+#define SD25G_LANE_LANE_40_LN_R_CTLE_RSTN_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_40_LN_R_CTLE_RSTN, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_42 */
+#define SD25G_LANE_LANE_42(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 264, 0, 1, 4)
+
+#define SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0 GENMASK(7, 0)
+#define SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0, x)
+#define SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_43 */
+#define SD25G_LANE_LANE_43(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 268, 0, 1, 4)
+
+#define SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8 GENMASK(7, 0)
+#define SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8, x)
+#define SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_44 */
+#define SD25G_LANE_LANE_44(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 272, 0, 1, 4)
+
+#define SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0 GENMASK(7, 0)
+#define SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0, x)
+#define SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0, x)
+
+/* SD25G_TARGET:LANE_GRP_0:LANE_45 */
+#define SD25G_LANE_LANE_45(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 276, 0, 1, 4)
+
+#define SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8 GENMASK(7, 0)
+#define SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8, x)
+#define SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8, x)
+
+/* SD25G_TARGET:LANE_GRP_1:LANE_DE */
+#define SD25G_LANE_LANE_DE(t) __REG(TARGET_SD25G_LANE, t, 8, 1792, 0, 1, 128, 120, 0, 1, 4)
+
+#define SD25G_LANE_LANE_DE_LN_LOL_UDL BIT(0)
+#define SD25G_LANE_LANE_DE_LN_LOL_UDL_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_DE_LN_LOL_UDL, x)
+#define SD25G_LANE_LANE_DE_LN_LOL_UDL_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_DE_LN_LOL_UDL, x)
+
+#define SD25G_LANE_LANE_DE_LN_LOL BIT(1)
+#define SD25G_LANE_LANE_DE_LN_LOL_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_DE_LN_LOL, x)
+#define SD25G_LANE_LANE_DE_LN_LOL_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_DE_LN_LOL, x)
+
+#define SD25G_LANE_LANE_DE_LN_PMA2PCS_RXEI_FILTERED BIT(2)
+#define SD25G_LANE_LANE_DE_LN_PMA2PCS_RXEI_FILTERED_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_DE_LN_PMA2PCS_RXEI_FILTERED, x)
+#define SD25G_LANE_LANE_DE_LN_PMA2PCS_RXEI_FILTERED_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_DE_LN_PMA2PCS_RXEI_FILTERED, x)
+
+#define SD25G_LANE_LANE_DE_LN_PMA_RXEI BIT(3)
+#define SD25G_LANE_LANE_DE_LN_PMA_RXEI_SET(x)\
+ FIELD_PREP(SD25G_LANE_LANE_DE_LN_PMA_RXEI, x)
+#define SD25G_LANE_LANE_DE_LN_PMA_RXEI_GET(x)\
+ FIELD_GET(SD25G_LANE_LANE_DE_LN_PMA_RXEI, x)
+
+/* SD10G_LANE_TARGET:LANE_GRP_8:LANE_DF */
+#define SD6G_LANE_LANE_DF(t) __REG(TARGET_SD6G_LANE, t, 13, 832, 0, 1, 84, 60, 0, 1, 4)
+
+#define SD6G_LANE_LANE_DF_LOL_UDL BIT(0)
+#define SD6G_LANE_LANE_DF_LOL_UDL_SET(x)\
+ FIELD_PREP(SD6G_LANE_LANE_DF_LOL_UDL, x)
+#define SD6G_LANE_LANE_DF_LOL_UDL_GET(x)\
+ FIELD_GET(SD6G_LANE_LANE_DF_LOL_UDL, x)
+
+#define SD6G_LANE_LANE_DF_LOL BIT(1)
+#define SD6G_LANE_LANE_DF_LOL_SET(x)\
+ FIELD_PREP(SD6G_LANE_LANE_DF_LOL, x)
+#define SD6G_LANE_LANE_DF_LOL_GET(x)\
+ FIELD_GET(SD6G_LANE_LANE_DF_LOL, x)
+
+#define SD6G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED BIT(2)
+#define SD6G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED_SET(x)\
+ FIELD_PREP(SD6G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED, x)
+#define SD6G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED_GET(x)\
+ FIELD_GET(SD6G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED, x)
+
+#define SD6G_LANE_LANE_DF_SQUELCH BIT(3)
+#define SD6G_LANE_LANE_DF_SQUELCH_SET(x)\
+ FIELD_PREP(SD6G_LANE_LANE_DF_SQUELCH, x)
+#define SD6G_LANE_LANE_DF_SQUELCH_GET(x)\
+ FIELD_GET(SD6G_LANE_LANE_DF_SQUELCH, x)
+
+/* SD10G_CMU_TARGET:CMU_GRP_0:CMU_00 */
+#define SD_CMU_CMU_00(t) __REG(TARGET_SD_CMU, t, 14, 0, 0, 1, 20, 0, 0, 1, 4)
+
+#define SD_CMU_CMU_00_R_HWT_SIMULATION_MODE BIT(0)
+#define SD_CMU_CMU_00_R_HWT_SIMULATION_MODE_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_00_R_HWT_SIMULATION_MODE, x)
+#define SD_CMU_CMU_00_R_HWT_SIMULATION_MODE_GET(x)\
+ FIELD_GET(SD_CMU_CMU_00_R_HWT_SIMULATION_MODE, x)
+
+#define SD_CMU_CMU_00_CFG_PLL_LOL_SET BIT(1)
+#define SD_CMU_CMU_00_CFG_PLL_LOL_SET_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_00_CFG_PLL_LOL_SET, x)
+#define SD_CMU_CMU_00_CFG_PLL_LOL_SET_GET(x)\
+ FIELD_GET(SD_CMU_CMU_00_CFG_PLL_LOL_SET, x)
+
+#define SD_CMU_CMU_00_CFG_PLL_LOS_SET BIT(2)
+#define SD_CMU_CMU_00_CFG_PLL_LOS_SET_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_00_CFG_PLL_LOS_SET, x)
+#define SD_CMU_CMU_00_CFG_PLL_LOS_SET_GET(x)\
+ FIELD_GET(SD_CMU_CMU_00_CFG_PLL_LOS_SET, x)
+
+#define SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0 GENMASK(5, 4)
+#define SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0, x)
+#define SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0_GET(x)\
+ FIELD_GET(SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0, x)
+
+/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_05 */
+#define SD_CMU_CMU_05(t) __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 0, 0, 1, 4)
+
+#define SD_CMU_CMU_05_CFG_REFCK_TERM_EN BIT(0)
+#define SD_CMU_CMU_05_CFG_REFCK_TERM_EN_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_05_CFG_REFCK_TERM_EN, x)
+#define SD_CMU_CMU_05_CFG_REFCK_TERM_EN_GET(x)\
+ FIELD_GET(SD_CMU_CMU_05_CFG_REFCK_TERM_EN, x)
+
+#define SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0 GENMASK(5, 4)
+#define SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0, x)
+#define SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_GET(x)\
+ FIELD_GET(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0, x)
+
+/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_09 */
+#define SD_CMU_CMU_09(t) __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 16, 0, 1, 4)
+
+#define SD_CMU_CMU_09_CFG_EN_TX_CK_UP BIT(0)
+#define SD_CMU_CMU_09_CFG_EN_TX_CK_UP_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_09_CFG_EN_TX_CK_UP, x)
+#define SD_CMU_CMU_09_CFG_EN_TX_CK_UP_GET(x)\
+ FIELD_GET(SD_CMU_CMU_09_CFG_EN_TX_CK_UP, x)
+
+#define SD_CMU_CMU_09_CFG_EN_TX_CK_DN BIT(1)
+#define SD_CMU_CMU_09_CFG_EN_TX_CK_DN_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_09_CFG_EN_TX_CK_DN, x)
+#define SD_CMU_CMU_09_CFG_EN_TX_CK_DN_GET(x)\
+ FIELD_GET(SD_CMU_CMU_09_CFG_EN_TX_CK_DN, x)
+
+#define SD_CMU_CMU_09_CFG_SW_8G BIT(4)
+#define SD_CMU_CMU_09_CFG_SW_8G_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_09_CFG_SW_8G, x)
+#define SD_CMU_CMU_09_CFG_SW_8G_GET(x)\
+ FIELD_GET(SD_CMU_CMU_09_CFG_SW_8G, x)
+
+#define SD_CMU_CMU_09_CFG_SW_10G BIT(5)
+#define SD_CMU_CMU_09_CFG_SW_10G_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_09_CFG_SW_10G, x)
+#define SD_CMU_CMU_09_CFG_SW_10G_GET(x)\
+ FIELD_GET(SD_CMU_CMU_09_CFG_SW_10G, x)
+
+/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_0D */
+#define SD_CMU_CMU_0D(t) __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 32, 0, 1, 4)
+
+#define SD_CMU_CMU_0D_CFG_PD_DIV64 BIT(0)
+#define SD_CMU_CMU_0D_CFG_PD_DIV64_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_0D_CFG_PD_DIV64, x)
+#define SD_CMU_CMU_0D_CFG_PD_DIV64_GET(x)\
+ FIELD_GET(SD_CMU_CMU_0D_CFG_PD_DIV64, x)
+
+#define SD_CMU_CMU_0D_CFG_PD_DIV66 BIT(1)
+#define SD_CMU_CMU_0D_CFG_PD_DIV66_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_0D_CFG_PD_DIV66, x)
+#define SD_CMU_CMU_0D_CFG_PD_DIV66_GET(x)\
+ FIELD_GET(SD_CMU_CMU_0D_CFG_PD_DIV66, x)
+
+#define SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD BIT(2)
+#define SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD, x)
+#define SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD_GET(x)\
+ FIELD_GET(SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD, x)
+
+#define SD_CMU_CMU_0D_CFG_JC_BYP BIT(3)
+#define SD_CMU_CMU_0D_CFG_JC_BYP_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_0D_CFG_JC_BYP, x)
+#define SD_CMU_CMU_0D_CFG_JC_BYP_GET(x)\
+ FIELD_GET(SD_CMU_CMU_0D_CFG_JC_BYP, x)
+
+#define SD_CMU_CMU_0D_CFG_REFCK_PD BIT(4)
+#define SD_CMU_CMU_0D_CFG_REFCK_PD_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_0D_CFG_REFCK_PD, x)
+#define SD_CMU_CMU_0D_CFG_REFCK_PD_GET(x)\
+ FIELD_GET(SD_CMU_CMU_0D_CFG_REFCK_PD, x)
+
+/* SD10G_CMU_TARGET:CMU_GRP_3:CMU_1B */
+#define SD_CMU_CMU_1B(t) __REG(TARGET_SD_CMU, t, 14, 104, 0, 1, 20, 4, 0, 1, 4)
+
+#define SD_CMU_CMU_1B_CFG_RESERVE_7_0 GENMASK(7, 0)
+#define SD_CMU_CMU_1B_CFG_RESERVE_7_0_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_1B_CFG_RESERVE_7_0, x)
+#define SD_CMU_CMU_1B_CFG_RESERVE_7_0_GET(x)\
+ FIELD_GET(SD_CMU_CMU_1B_CFG_RESERVE_7_0, x)
+
+/* SD10G_CMU_TARGET:CMU_GRP_4:CMU_1F */
+#define SD_CMU_CMU_1F(t) __REG(TARGET_SD_CMU, t, 14, 124, 0, 1, 68, 0, 0, 1, 4)
+
+#define SD_CMU_CMU_1F_CFG_BIAS_DN_EN BIT(0)
+#define SD_CMU_CMU_1F_CFG_BIAS_DN_EN_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_1F_CFG_BIAS_DN_EN, x)
+#define SD_CMU_CMU_1F_CFG_BIAS_DN_EN_GET(x)\
+ FIELD_GET(SD_CMU_CMU_1F_CFG_BIAS_DN_EN, x)
+
+#define SD_CMU_CMU_1F_CFG_BIAS_UP_EN BIT(1)
+#define SD_CMU_CMU_1F_CFG_BIAS_UP_EN_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_1F_CFG_BIAS_UP_EN, x)
+#define SD_CMU_CMU_1F_CFG_BIAS_UP_EN_GET(x)\
+ FIELD_GET(SD_CMU_CMU_1F_CFG_BIAS_UP_EN, x)
+
+#define SD_CMU_CMU_1F_CFG_IC2IP_N BIT(2)
+#define SD_CMU_CMU_1F_CFG_IC2IP_N_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_1F_CFG_IC2IP_N, x)
+#define SD_CMU_CMU_1F_CFG_IC2IP_N_GET(x)\
+ FIELD_GET(SD_CMU_CMU_1F_CFG_IC2IP_N, x)
+
+#define SD_CMU_CMU_1F_CFG_VTUNE_SEL BIT(3)
+#define SD_CMU_CMU_1F_CFG_VTUNE_SEL_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_1F_CFG_VTUNE_SEL, x)
+#define SD_CMU_CMU_1F_CFG_VTUNE_SEL_GET(x)\
+ FIELD_GET(SD_CMU_CMU_1F_CFG_VTUNE_SEL, x)
+
+/* SD10G_CMU_TARGET:CMU_GRP_5:CMU_30 */
+#define SD_CMU_CMU_30(t) __REG(TARGET_SD_CMU, t, 14, 192, 0, 1, 72, 0, 0, 1, 4)
+
+#define SD_CMU_CMU_30_R_PLL_DLOL_EN BIT(0)
+#define SD_CMU_CMU_30_R_PLL_DLOL_EN_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_30_R_PLL_DLOL_EN, x)
+#define SD_CMU_CMU_30_R_PLL_DLOL_EN_GET(x)\
+ FIELD_GET(SD_CMU_CMU_30_R_PLL_DLOL_EN, x)
+
+/* SD10G_CMU_TARGET:CMU_GRP_6:CMU_44 */
+#define SD_CMU_CMU_44(t) __REG(TARGET_SD_CMU, t, 14, 264, 0, 1, 632, 8, 0, 1, 4)
+
+#define SD_CMU_CMU_44_R_PLL_RSTN BIT(0)
+#define SD_CMU_CMU_44_R_PLL_RSTN_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_44_R_PLL_RSTN, x)
+#define SD_CMU_CMU_44_R_PLL_RSTN_GET(x)\
+ FIELD_GET(SD_CMU_CMU_44_R_PLL_RSTN, x)
+
+#define SD_CMU_CMU_44_R_CK_RESETB BIT(1)
+#define SD_CMU_CMU_44_R_CK_RESETB_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_44_R_CK_RESETB, x)
+#define SD_CMU_CMU_44_R_CK_RESETB_GET(x)\
+ FIELD_GET(SD_CMU_CMU_44_R_CK_RESETB, x)
+
+/* SD10G_CMU_TARGET:CMU_GRP_6:CMU_45 */
+#define SD_CMU_CMU_45(t) __REG(TARGET_SD_CMU, t, 14, 264, 0, 1, 632, 12, 0, 1, 4)
+
+#define SD_CMU_CMU_45_R_EN_RATECHG_CTRL BIT(0)
+#define SD_CMU_CMU_45_R_EN_RATECHG_CTRL_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_45_R_EN_RATECHG_CTRL, x)
+#define SD_CMU_CMU_45_R_EN_RATECHG_CTRL_GET(x)\
+ FIELD_GET(SD_CMU_CMU_45_R_EN_RATECHG_CTRL, x)
+
+#define SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT BIT(1)
+#define SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT, x)
+#define SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT_GET(x)\
+ FIELD_GET(SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT, x)
+
+#define SD_CMU_CMU_45_RESERVED BIT(2)
+#define SD_CMU_CMU_45_RESERVED_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_45_RESERVED, x)
+#define SD_CMU_CMU_45_RESERVED_GET(x)\
+ FIELD_GET(SD_CMU_CMU_45_RESERVED, x)
+
+#define SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT BIT(3)
+#define SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT, x)
+#define SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT_GET(x)\
+ FIELD_GET(SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT, x)
+
+#define SD_CMU_CMU_45_RESERVED_2 BIT(4)
+#define SD_CMU_CMU_45_RESERVED_2_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_45_RESERVED_2, x)
+#define SD_CMU_CMU_45_RESERVED_2_GET(x)\
+ FIELD_GET(SD_CMU_CMU_45_RESERVED_2, x)
+
+#define SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT BIT(5)
+#define SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT, x)
+#define SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT_GET(x)\
+ FIELD_GET(SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT, x)
+
+#define SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT BIT(6)
+#define SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT, x)
+#define SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT_GET(x)\
+ FIELD_GET(SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT, x)
+
+#define SD_CMU_CMU_45_R_AUTO_RST_TREE_PD_MAN BIT(7)
+#define SD_CMU_CMU_45_R_AUTO_RST_TREE_PD_MAN_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_45_R_AUTO_RST_TREE_PD_MAN, x)
+#define SD_CMU_CMU_45_R_AUTO_RST_TREE_PD_MAN_GET(x)\
+ FIELD_GET(SD_CMU_CMU_45_R_AUTO_RST_TREE_PD_MAN, x)
+
+/* SD10G_CMU_TARGET:CMU_GRP_6:CMU_47 */
+#define SD_CMU_CMU_47(t) __REG(TARGET_SD_CMU, t, 14, 264, 0, 1, 632, 20, 0, 1, 4)
+
+#define SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0 GENMASK(4, 0)
+#define SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0, x)
+#define SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0_GET(x)\
+ FIELD_GET(SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0, x)
+
+/* SD10G_CMU_TARGET:CMU_GRP_7:CMU_E0 */
+#define SD_CMU_CMU_E0(t) __REG(TARGET_SD_CMU, t, 14, 896, 0, 1, 8, 0, 0, 1, 4)
+
+#define SD_CMU_CMU_E0_READ_VCO_CTUNE_3_0 GENMASK(3, 0)
+#define SD_CMU_CMU_E0_READ_VCO_CTUNE_3_0_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_E0_READ_VCO_CTUNE_3_0, x)
+#define SD_CMU_CMU_E0_READ_VCO_CTUNE_3_0_GET(x)\
+ FIELD_GET(SD_CMU_CMU_E0_READ_VCO_CTUNE_3_0, x)
+
+#define SD_CMU_CMU_E0_PLL_LOL_UDL BIT(4)
+#define SD_CMU_CMU_E0_PLL_LOL_UDL_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_E0_PLL_LOL_UDL, x)
+#define SD_CMU_CMU_E0_PLL_LOL_UDL_GET(x)\
+ FIELD_GET(SD_CMU_CMU_E0_PLL_LOL_UDL, x)
+
+/* SD_CMU_TARGET:SD_CMU_CFG:SD_CMU_CFG */
+#define SD_CMU_CFG_SD_CMU_CFG(t) __REG(TARGET_SD_CMU_CFG, t, 14, 0, 0, 1, 8, 0, 0, 1, 4)
+
+#define SD_CMU_CFG_SD_CMU_CFG_CMU_RST BIT(0)
+#define SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(x)\
+ FIELD_PREP(SD_CMU_CFG_SD_CMU_CFG_CMU_RST, x)
+#define SD_CMU_CFG_SD_CMU_CFG_CMU_RST_GET(x)\
+ FIELD_GET(SD_CMU_CFG_SD_CMU_CFG_CMU_RST, x)
+
+#define SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST BIT(1)
+#define SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(x)\
+ FIELD_PREP(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, x)
+#define SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_GET(x)\
+ FIELD_GET(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, x)
+
+/* SD_LANE_TARGET:SD_RESET:SD_SER_RST */
+#define SD_LANE_SD_SER_RST(t) __REG(TARGET_SD_LANE, t, 25, 0, 0, 1, 8, 0, 0, 1, 4)
+
+#define SD_LANE_SD_SER_RST_SER_RST BIT(0)
+#define SD_LANE_SD_SER_RST_SER_RST_SET(x)\
+ FIELD_PREP(SD_LANE_SD_SER_RST_SER_RST, x)
+#define SD_LANE_SD_SER_RST_SER_RST_GET(x)\
+ FIELD_GET(SD_LANE_SD_SER_RST_SER_RST, x)
+
+/* SD_LANE_TARGET:SD_RESET:SD_DES_RST */
+#define SD_LANE_SD_DES_RST(t) __REG(TARGET_SD_LANE, t, 25, 0, 0, 1, 8, 4, 0, 1, 4)
+
+#define SD_LANE_SD_DES_RST_DES_RST BIT(0)
+#define SD_LANE_SD_DES_RST_DES_RST_SET(x)\
+ FIELD_PREP(SD_LANE_SD_DES_RST_DES_RST, x)
+#define SD_LANE_SD_DES_RST_DES_RST_GET(x)\
+ FIELD_GET(SD_LANE_SD_DES_RST_DES_RST, x)
+
+/* SD_LANE_TARGET:SD_LANE_CFG_STAT:SD_LANE_CFG */
+#define SD_LANE_SD_LANE_CFG(t) __REG(TARGET_SD_LANE, t, 25, 8, 0, 1, 8, 0, 0, 1, 4)
+
+#define SD_LANE_SD_LANE_CFG_MACRO_RST BIT(0)
+#define SD_LANE_SD_LANE_CFG_MACRO_RST_SET(x)\
+ FIELD_PREP(SD_LANE_SD_LANE_CFG_MACRO_RST, x)
+#define SD_LANE_SD_LANE_CFG_MACRO_RST_GET(x)\
+ FIELD_GET(SD_LANE_SD_LANE_CFG_MACRO_RST, x)
+
+#define SD_LANE_SD_LANE_CFG_EXT_CFG_RST BIT(1)
+#define SD_LANE_SD_LANE_CFG_EXT_CFG_RST_SET(x)\
+ FIELD_PREP(SD_LANE_SD_LANE_CFG_EXT_CFG_RST, x)
+#define SD_LANE_SD_LANE_CFG_EXT_CFG_RST_GET(x)\
+ FIELD_GET(SD_LANE_SD_LANE_CFG_EXT_CFG_RST, x)
+
+#define SD_LANE_SD_LANE_CFG_TX_REF_SEL GENMASK(5, 4)
+#define SD_LANE_SD_LANE_CFG_TX_REF_SEL_SET(x)\
+ FIELD_PREP(SD_LANE_SD_LANE_CFG_TX_REF_SEL, x)
+#define SD_LANE_SD_LANE_CFG_TX_REF_SEL_GET(x)\
+ FIELD_GET(SD_LANE_SD_LANE_CFG_TX_REF_SEL, x)
+
+#define SD_LANE_SD_LANE_CFG_RX_REF_SEL GENMASK(7, 6)
+#define SD_LANE_SD_LANE_CFG_RX_REF_SEL_SET(x)\
+ FIELD_PREP(SD_LANE_SD_LANE_CFG_RX_REF_SEL, x)
+#define SD_LANE_SD_LANE_CFG_RX_REF_SEL_GET(x)\
+ FIELD_GET(SD_LANE_SD_LANE_CFG_RX_REF_SEL, x)
+
+#define SD_LANE_SD_LANE_CFG_LANE_RST BIT(8)
+#define SD_LANE_SD_LANE_CFG_LANE_RST_SET(x)\
+ FIELD_PREP(SD_LANE_SD_LANE_CFG_LANE_RST, x)
+#define SD_LANE_SD_LANE_CFG_LANE_RST_GET(x)\
+ FIELD_GET(SD_LANE_SD_LANE_CFG_LANE_RST, x)
+
+#define SD_LANE_SD_LANE_CFG_LANE_TX_RST BIT(9)
+#define SD_LANE_SD_LANE_CFG_LANE_TX_RST_SET(x)\
+ FIELD_PREP(SD_LANE_SD_LANE_CFG_LANE_TX_RST, x)
+#define SD_LANE_SD_LANE_CFG_LANE_TX_RST_GET(x)\
+ FIELD_GET(SD_LANE_SD_LANE_CFG_LANE_TX_RST, x)
+
+#define SD_LANE_SD_LANE_CFG_LANE_RX_RST BIT(10)
+#define SD_LANE_SD_LANE_CFG_LANE_RX_RST_SET(x)\
+ FIELD_PREP(SD_LANE_SD_LANE_CFG_LANE_RX_RST, x)
+#define SD_LANE_SD_LANE_CFG_LANE_RX_RST_GET(x)\
+ FIELD_GET(SD_LANE_SD_LANE_CFG_LANE_RX_RST, x)
+
+/* SD_LANE_TARGET:SD_LANE_CFG_STAT:SD_LANE_STAT */
+#define SD_LANE_SD_LANE_STAT(t) __REG(TARGET_SD_LANE, t, 25, 8, 0, 1, 8, 4, 0, 1, 4)
+
+#define SD_LANE_SD_LANE_STAT_PMA_RST_DONE BIT(0)
+#define SD_LANE_SD_LANE_STAT_PMA_RST_DONE_SET(x)\
+ FIELD_PREP(SD_LANE_SD_LANE_STAT_PMA_RST_DONE, x)
+#define SD_LANE_SD_LANE_STAT_PMA_RST_DONE_GET(x)\
+ FIELD_GET(SD_LANE_SD_LANE_STAT_PMA_RST_DONE, x)
+
+#define SD_LANE_SD_LANE_STAT_DFE_RST_DONE BIT(1)
+#define SD_LANE_SD_LANE_STAT_DFE_RST_DONE_SET(x)\
+ FIELD_PREP(SD_LANE_SD_LANE_STAT_DFE_RST_DONE, x)
+#define SD_LANE_SD_LANE_STAT_DFE_RST_DONE_GET(x)\
+ FIELD_GET(SD_LANE_SD_LANE_STAT_DFE_RST_DONE, x)
+
+#define SD_LANE_SD_LANE_STAT_DBG_OBS GENMASK(31, 16)
+#define SD_LANE_SD_LANE_STAT_DBG_OBS_SET(x)\
+ FIELD_PREP(SD_LANE_SD_LANE_STAT_DBG_OBS, x)
+#define SD_LANE_SD_LANE_STAT_DBG_OBS_GET(x)\
+ FIELD_GET(SD_LANE_SD_LANE_STAT_DBG_OBS, x)
+
+/* SD_LANE_TARGET:CFG_STAT_FX100:MISC */
+#define SD_LANE_MISC(t) __REG(TARGET_SD_LANE, t, 25, 56, 0, 1, 56, 0, 0, 1, 4)
+
+#define SD_LANE_MISC_SD_125_RST_DIS BIT(0)
+#define SD_LANE_MISC_SD_125_RST_DIS_SET(x)\
+ FIELD_PREP(SD_LANE_MISC_SD_125_RST_DIS, x)
+#define SD_LANE_MISC_SD_125_RST_DIS_GET(x)\
+ FIELD_GET(SD_LANE_MISC_SD_125_RST_DIS, x)
+
+#define SD_LANE_MISC_RX_ENA BIT(1)
+#define SD_LANE_MISC_RX_ENA_SET(x)\
+ FIELD_PREP(SD_LANE_MISC_RX_ENA, x)
+#define SD_LANE_MISC_RX_ENA_GET(x)\
+ FIELD_GET(SD_LANE_MISC_RX_ENA, x)
+
+#define SD_LANE_MISC_MUX_ENA BIT(2)
+#define SD_LANE_MISC_MUX_ENA_SET(x)\
+ FIELD_PREP(SD_LANE_MISC_MUX_ENA, x)
+#define SD_LANE_MISC_MUX_ENA_GET(x)\
+ FIELD_GET(SD_LANE_MISC_MUX_ENA, x)
+
+#define SD_LANE_MISC_CORE_CLK_FREQ GENMASK(5, 4)
+#define SD_LANE_MISC_CORE_CLK_FREQ_SET(x)\
+ FIELD_PREP(SD_LANE_MISC_CORE_CLK_FREQ, x)
+#define SD_LANE_MISC_CORE_CLK_FREQ_GET(x)\
+ FIELD_GET(SD_LANE_MISC_CORE_CLK_FREQ, x)
+
+/* SD_LANE_TARGET:CFG_STAT_FX100:M_STAT_MISC */
+#define SD_LANE_M_STAT_MISC(t) __REG(TARGET_SD_LANE, t, 25, 56, 0, 1, 56, 36, 0, 1, 4)
+
+#define SD_LANE_M_STAT_MISC_M_RIS_EDGE_PTR_ADJ_SUM GENMASK(21, 0)
+#define SD_LANE_M_STAT_MISC_M_RIS_EDGE_PTR_ADJ_SUM_SET(x)\
+ FIELD_PREP(SD_LANE_M_STAT_MISC_M_RIS_EDGE_PTR_ADJ_SUM, x)
+#define SD_LANE_M_STAT_MISC_M_RIS_EDGE_PTR_ADJ_SUM_GET(x)\
+ FIELD_GET(SD_LANE_M_STAT_MISC_M_RIS_EDGE_PTR_ADJ_SUM, x)
+
+#define SD_LANE_M_STAT_MISC_M_LOCK_CNT GENMASK(31, 24)
+#define SD_LANE_M_STAT_MISC_M_LOCK_CNT_SET(x)\
+ FIELD_PREP(SD_LANE_M_STAT_MISC_M_LOCK_CNT, x)
+#define SD_LANE_M_STAT_MISC_M_LOCK_CNT_GET(x)\
+ FIELD_GET(SD_LANE_M_STAT_MISC_M_LOCK_CNT, x)
+
+/* SD25G_CFG_TARGET:SD_RESET:SD_SER_RST */
+#define SD_LANE_25G_SD_SER_RST(t) __REG(TARGET_SD_LANE_25G, t, 8, 0, 0, 1, 8, 0, 0, 1, 4)
+
+#define SD_LANE_25G_SD_SER_RST_SER_RST BIT(0)
+#define SD_LANE_25G_SD_SER_RST_SER_RST_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_SER_RST_SER_RST, x)
+#define SD_LANE_25G_SD_SER_RST_SER_RST_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_SER_RST_SER_RST, x)
+
+/* SD25G_CFG_TARGET:SD_RESET:SD_DES_RST */
+#define SD_LANE_25G_SD_DES_RST(t) __REG(TARGET_SD_LANE_25G, t, 8, 0, 0, 1, 8, 4, 0, 1, 4)
+
+#define SD_LANE_25G_SD_DES_RST_DES_RST BIT(0)
+#define SD_LANE_25G_SD_DES_RST_DES_RST_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_DES_RST_DES_RST, x)
+#define SD_LANE_25G_SD_DES_RST_DES_RST_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_DES_RST_DES_RST, x)
+
+/* SD25G_CFG_TARGET:SD_LANE_CFG_STAT:SD_LANE_CFG */
+#define SD_LANE_25G_SD_LANE_CFG(t) __REG(TARGET_SD_LANE_25G, t, 8, 8, 0, 1, 12, 0, 0, 1, 4)
+
+#define SD_LANE_25G_SD_LANE_CFG_MACRO_RST BIT(0)
+#define SD_LANE_25G_SD_LANE_CFG_MACRO_RST_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_MACRO_RST, x)
+#define SD_LANE_25G_SD_LANE_CFG_MACRO_RST_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_MACRO_RST, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST BIT(1)
+#define SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST, x)
+#define SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_HWT_MULTI_LANE_MODE BIT(4)
+#define SD_LANE_25G_SD_LANE_CFG_HWT_MULTI_LANE_MODE_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_HWT_MULTI_LANE_MODE, x)
+#define SD_LANE_25G_SD_LANE_CFG_HWT_MULTI_LANE_MODE_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_HWT_MULTI_LANE_MODE, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_PHYMODE GENMASK(7, 5)
+#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_PHYMODE_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_PHYMODE, x)
+#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_PHYMODE_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_PHYMODE, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_LANE_RST BIT(8)
+#define SD_LANE_25G_SD_LANE_CFG_LANE_RST_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_LANE_RST, x)
+#define SD_LANE_25G_SD_LANE_CFG_LANE_RST_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_LANE_RST, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_ADV BIT(9)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_ADV_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_EN_ADV, x)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_ADV_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_EN_ADV, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_MAIN BIT(10)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_MAIN_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_EN_MAIN, x)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_MAIN_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_EN_MAIN, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_DLY BIT(11)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_DLY_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_EN_DLY, x)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_DLY_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_EN_DLY, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_ADV GENMASK(15, 12)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_ADV_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_TAP_ADV, x)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_ADV_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_TAP_ADV, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_MAIN BIT(16)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_MAIN_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_TAP_MAIN, x)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_MAIN_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_TAP_MAIN, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_DLY GENMASK(21, 17)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_DLY_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_TAP_DLY, x)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_DLY_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_TAP_DLY, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_PCS_ISCAN_EN BIT(22)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_ISCAN_EN_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_ISCAN_EN, x)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_ISCAN_EN_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_ISCAN_EN, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_FAST_ISCAN BIT(23)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_FAST_ISCAN_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_EN_FAST_ISCAN, x)
+#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_FAST_ISCAN_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_EN_FAST_ISCAN, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXSWING BIT(24)
+#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXSWING_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXSWING, x)
+#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXSWING_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXSWING, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXEI BIT(25)
+#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXEI_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXEI, x)
+#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXEI_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXEI, x)
+
+#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXMARGIN GENMASK(28, 26)
+#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXMARGIN_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXMARGIN, x)
+#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXMARGIN_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXMARGIN, x)
+
+/* SD25G_CFG_TARGET:SD_LANE_CFG_STAT:SD_LANE_CFG2 */
+#define SD_LANE_25G_SD_LANE_CFG2(t) __REG(TARGET_SD_LANE_25G, t, 8, 8, 0, 1, 12, 4, 0, 1, 4)
+
+#define SD_LANE_25G_SD_LANE_CFG2_DATA_WIDTH_SEL GENMASK(2, 0)
+#define SD_LANE_25G_SD_LANE_CFG2_DATA_WIDTH_SEL_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_DATA_WIDTH_SEL, x)
+#define SD_LANE_25G_SD_LANE_CFG2_DATA_WIDTH_SEL_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_DATA_WIDTH_SEL, x)
+
+#define SD_LANE_25G_SD_LANE_CFG2_PMA_TXCK_SEL GENMASK(5, 3)
+#define SD_LANE_25G_SD_LANE_CFG2_PMA_TXCK_SEL_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_PMA_TXCK_SEL, x)
+#define SD_LANE_25G_SD_LANE_CFG2_PMA_TXCK_SEL_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_PMA_TXCK_SEL, x)
+
+#define SD_LANE_25G_SD_LANE_CFG2_PMA_RXDIV_SEL GENMASK(8, 6)
+#define SD_LANE_25G_SD_LANE_CFG2_PMA_RXDIV_SEL_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_PMA_RXDIV_SEL, x)
+#define SD_LANE_25G_SD_LANE_CFG2_PMA_RXDIV_SEL_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_PMA_RXDIV_SEL, x)
+
+#define SD_LANE_25G_SD_LANE_CFG2_PCS2PMA_TX_SPEED GENMASK(10, 9)
+#define SD_LANE_25G_SD_LANE_CFG2_PCS2PMA_TX_SPEED_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_PCS2PMA_TX_SPEED, x)
+#define SD_LANE_25G_SD_LANE_CFG2_PCS2PMA_TX_SPEED_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_PCS2PMA_TX_SPEED, x)
+
+#define SD_LANE_25G_SD_LANE_CFG2_TXFIFO_CK_DIV GENMASK(13, 11)
+#define SD_LANE_25G_SD_LANE_CFG2_TXFIFO_CK_DIV_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_TXFIFO_CK_DIV, x)
+#define SD_LANE_25G_SD_LANE_CFG2_TXFIFO_CK_DIV_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_TXFIFO_CK_DIV, x)
+
+#define SD_LANE_25G_SD_LANE_CFG2_RXFIFO_CK_DIV GENMASK(16, 14)
+#define SD_LANE_25G_SD_LANE_CFG2_RXFIFO_CK_DIV_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_RXFIFO_CK_DIV, x)
+#define SD_LANE_25G_SD_LANE_CFG2_RXFIFO_CK_DIV_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_RXFIFO_CK_DIV, x)
+
+#define SD_LANE_25G_SD_LANE_CFG2_HWT_VCO_DIV_SEL GENMASK(19, 17)
+#define SD_LANE_25G_SD_LANE_CFG2_HWT_VCO_DIV_SEL_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_HWT_VCO_DIV_SEL, x)
+#define SD_LANE_25G_SD_LANE_CFG2_HWT_VCO_DIV_SEL_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_HWT_VCO_DIV_SEL, x)
+
+#define SD_LANE_25G_SD_LANE_CFG2_HWT_CFG_SEL_DIV GENMASK(23, 20)
+#define SD_LANE_25G_SD_LANE_CFG2_HWT_CFG_SEL_DIV_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_HWT_CFG_SEL_DIV, x)
+#define SD_LANE_25G_SD_LANE_CFG2_HWT_CFG_SEL_DIV_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_HWT_CFG_SEL_DIV, x)
+
+#define SD_LANE_25G_SD_LANE_CFG2_HWT_PRE_DIVSEL GENMASK(25, 24)
+#define SD_LANE_25G_SD_LANE_CFG2_HWT_PRE_DIVSEL_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_HWT_PRE_DIVSEL, x)
+#define SD_LANE_25G_SD_LANE_CFG2_HWT_PRE_DIVSEL_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_HWT_PRE_DIVSEL, x)
+
+#define SD_LANE_25G_SD_LANE_CFG2_TXRATE_SEL GENMASK(28, 26)
+#define SD_LANE_25G_SD_LANE_CFG2_TXRATE_SEL_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_TXRATE_SEL, x)
+#define SD_LANE_25G_SD_LANE_CFG2_TXRATE_SEL_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_TXRATE_SEL, x)
+
+#define SD_LANE_25G_SD_LANE_CFG2_RXRATE_SEL GENMASK(31, 29)
+#define SD_LANE_25G_SD_LANE_CFG2_RXRATE_SEL_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_RXRATE_SEL, x)
+#define SD_LANE_25G_SD_LANE_CFG2_RXRATE_SEL_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_RXRATE_SEL, x)
+
+/* SD25G_CFG_TARGET:SD_LANE_CFG_STAT:SD_LANE_STAT */
+#define SD_LANE_25G_SD_LANE_STAT(t) __REG(TARGET_SD_LANE_25G, t, 8, 8, 0, 1, 12, 8, 0, 1, 4)
+
+#define SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE BIT(0)
+#define SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE, x)
+#define SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE, x)
+
+#define SD_LANE_25G_SD_LANE_STAT_LANE_RST_DONE BIT(1)
+#define SD_LANE_25G_SD_LANE_STAT_LANE_RST_DONE_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_STAT_LANE_RST_DONE, x)
+#define SD_LANE_25G_SD_LANE_STAT_LANE_RST_DONE_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_STAT_LANE_RST_DONE, x)
+
+#define SD_LANE_25G_SD_LANE_STAT_DBG_OBS GENMASK(31, 16)
+#define SD_LANE_25G_SD_LANE_STAT_DBG_OBS_SET(x)\
+ FIELD_PREP(SD_LANE_25G_SD_LANE_STAT_DBG_OBS, x)
+#define SD_LANE_25G_SD_LANE_STAT_DBG_OBS_GET(x)\
+ FIELD_GET(SD_LANE_25G_SD_LANE_STAT_DBG_OBS, x)
+
+#endif /* _SPARX5_SERDES_REGS_H_ */
diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c
index 12e71a315a2c..2f8210167b77 100644
--- a/drivers/phy/motorola/phy-cpcap-usb.c
+++ b/drivers/phy/motorola/phy-cpcap-usb.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Motorola CPCAP PMIC USB PHY driver
* Copyright (C) 2017 Tony Lindgren <tony@atomide.com>
@@ -5,15 +6,6 @@
* Some parts based on earlier Motorola Linux kernel tree code in
* board-mapphone-usb.c and cpcap-usb-det.c:
* Copyright (C) 2007 - 2011 Motorola, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/atomic.h>
@@ -122,7 +114,6 @@ enum cpcap_gpio_mode {
struct cpcap_phy_ddata {
struct regmap *reg;
struct device *dev;
- struct clk *refclk;
struct usb_phy phy;
struct delayed_work detect_work;
struct pinctrl *pins;
@@ -144,7 +135,7 @@ static bool cpcap_usb_vbus_valid(struct cpcap_phy_ddata *ddata)
error = iio_read_channel_processed(ddata->vbus, &value);
if (error >= 0)
- return value > 3900 ? true : false;
+ return value > 3900;
dev_err(ddata->dev, "error reading VBUS: %i\n", error);
@@ -365,7 +356,8 @@ static int cpcap_usb_init_irq(struct platform_device *pdev,
error = devm_request_threaded_irq(ddata->dev, irq, NULL,
cpcap_phy_irq_thread,
- IRQF_SHARED,
+ IRQF_SHARED |
+ IRQF_ONESHOT,
name, ddata);
if (error) {
dev_err(ddata->dev, "could not get irq %s: %i\n",
@@ -662,35 +654,42 @@ static int cpcap_usb_phy_probe(struct platform_device *pdev)
generic_phy = devm_phy_create(ddata->dev, NULL, &ops);
if (IS_ERR(generic_phy)) {
error = PTR_ERR(generic_phy);
- return PTR_ERR(generic_phy);
+ goto out_reg_disable;
}
phy_set_drvdata(generic_phy, ddata);
phy_provider = devm_of_phy_provider_register(ddata->dev,
of_phy_simple_xlate);
- if (IS_ERR(phy_provider))
- return PTR_ERR(phy_provider);
+ if (IS_ERR(phy_provider)) {
+ error = PTR_ERR(phy_provider);
+ goto out_reg_disable;
+ }
error = cpcap_usb_init_optional_pins(ddata);
if (error)
- return error;
+ goto out_reg_disable;
cpcap_usb_init_optional_gpios(ddata);
error = cpcap_usb_init_iio(ddata);
if (error)
- return error;
+ goto out_reg_disable;
error = cpcap_usb_init_interrupts(pdev, ddata);
if (error)
- return error;
+ goto out_reg_disable;
usb_add_phy_dev(&ddata->phy);
atomic_set(&ddata->active, 1);
schedule_delayed_work(&ddata->detect_work, msecs_to_jiffies(1));
return 0;
+
+out_reg_disable:
+ regulator_disable(ddata->vusb);
+
+ return error;
}
static int cpcap_usb_phy_remove(struct platform_device *pdev)
@@ -707,7 +706,6 @@ static int cpcap_usb_phy_remove(struct platform_device *pdev)
usb_remove_phy(&ddata->phy);
cancel_delayed_work_sync(&ddata->detect_work);
- clk_unprepare(ddata->refclk);
regulator_disable(ddata->vusb);
return 0;
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index 94a34cf75eb3..3cd4d51c247c 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -178,6 +178,7 @@ static const struct phy_ops gpio_usb_ops = {
/**
* phy_mdm6600_cmd() - send a command request to mdm6600
* @ddata: device driver data
+ * @val: value of cmd to be set
*
* Configures the three command request GPIOs to the specified value.
*/
@@ -194,7 +195,7 @@ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
/**
* phy_mdm6600_status() - read mdm6600 status lines
- * @ddata: device driver data
+ * @work: work structure
*/
static void phy_mdm6600_status(struct work_struct *work)
{
@@ -628,7 +629,8 @@ idle:
cleanup:
if (error < 0)
phy_mdm6600_device_power_off(ddata);
-
+ pm_runtime_disable(ddata->dev);
+ pm_runtime_dont_use_autosuspend(ddata->dev);
return error;
}
diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c
new file mode 100644
index 000000000000..95c6dbb52da7
--- /dev/null
+++ b/drivers/phy/phy-can-transceiver.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * phy-can-transceiver.c - phy driver for CAN transceivers
+ *
+ * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com
+ *
+ */
+#include<linux/phy/phy.h>
+#include<linux/platform_device.h>
+#include<linux/module.h>
+#include<linux/gpio.h>
+#include<linux/gpio/consumer.h>
+#include <linux/mux/consumer.h>
+
+struct can_transceiver_data {
+ u32 flags;
+#define CAN_TRANSCEIVER_STB_PRESENT BIT(0)
+#define CAN_TRANSCEIVER_EN_PRESENT BIT(1)
+};
+
+struct can_transceiver_phy {
+ struct phy *generic_phy;
+ struct gpio_desc *standby_gpio;
+ struct gpio_desc *enable_gpio;
+ struct mux_state *mux_state;
+};
+
+/* Power on function */
+static int can_transceiver_phy_power_on(struct phy *phy)
+{
+ struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy);
+ int ret;
+
+ if (can_transceiver_phy->mux_state) {
+ ret = mux_state_select(can_transceiver_phy->mux_state);
+ if (ret) {
+ dev_err(&phy->dev, "Failed to select CAN mux: %d\n", ret);
+ return ret;
+ }
+ }
+ if (can_transceiver_phy->standby_gpio)
+ gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 0);
+ if (can_transceiver_phy->enable_gpio)
+ gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 1);
+
+ return 0;
+}
+
+/* Power off function */
+static int can_transceiver_phy_power_off(struct phy *phy)
+{
+ struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy);
+
+ if (can_transceiver_phy->standby_gpio)
+ gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 1);
+ if (can_transceiver_phy->enable_gpio)
+ gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 0);
+ if (can_transceiver_phy->mux_state)
+ mux_state_deselect(can_transceiver_phy->mux_state);
+
+ return 0;
+}
+
+static const struct phy_ops can_transceiver_phy_ops = {
+ .power_on = can_transceiver_phy_power_on,
+ .power_off = can_transceiver_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static const struct can_transceiver_data tcan1042_drvdata = {
+ .flags = CAN_TRANSCEIVER_STB_PRESENT,
+};
+
+static const struct can_transceiver_data tcan1043_drvdata = {
+ .flags = CAN_TRANSCEIVER_STB_PRESENT | CAN_TRANSCEIVER_EN_PRESENT,
+};
+
+static const struct of_device_id can_transceiver_phy_ids[] = {
+ {
+ .compatible = "ti,tcan1042",
+ .data = &tcan1042_drvdata
+ },
+ {
+ .compatible = "ti,tcan1043",
+ .data = &tcan1043_drvdata
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids);
+
+static int can_transceiver_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct can_transceiver_phy *can_transceiver_phy;
+ const struct can_transceiver_data *drvdata;
+ const struct of_device_id *match;
+ struct phy *phy;
+ struct gpio_desc *standby_gpio;
+ struct gpio_desc *enable_gpio;
+ u32 max_bitrate = 0;
+
+ can_transceiver_phy = devm_kzalloc(dev, sizeof(struct can_transceiver_phy), GFP_KERNEL);
+ if (!can_transceiver_phy)
+ return -ENOMEM;
+
+ match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node);
+ drvdata = match->data;
+
+ if (of_property_read_bool(dev->of_node, "mux-states")) {
+ struct mux_state *mux_state;
+
+ mux_state = devm_mux_state_get(dev, NULL);
+ if (IS_ERR(mux_state))
+ return dev_err_probe(&pdev->dev, PTR_ERR(mux_state),
+ "failed to get mux\n");
+ can_transceiver_phy->mux_state = mux_state;
+ }
+
+ phy = devm_phy_create(dev, dev->of_node,
+ &can_transceiver_phy_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create can transceiver phy\n");
+ return PTR_ERR(phy);
+ }
+
+ device_property_read_u32(dev, "max-bitrate", &max_bitrate);
+ if (!max_bitrate)
+ dev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit\n");
+ phy->attrs.max_link_rate = max_bitrate;
+
+ can_transceiver_phy->generic_phy = phy;
+
+ if (drvdata->flags & CAN_TRANSCEIVER_STB_PRESENT) {
+ 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_optional(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(enable_gpio))
+ return PTR_ERR(enable_gpio);
+ can_transceiver_phy->enable_gpio = enable_gpio;
+ }
+
+ phy_set_drvdata(can_transceiver_phy->generic_phy, can_transceiver_phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver can_transceiver_phy_driver = {
+ .probe = can_transceiver_phy_probe,
+ .driver = {
+ .name = "can-transceiver-phy",
+ .of_match_table = can_transceiver_phy_ids,
+ },
+};
+
+module_platform_driver(can_transceiver_phy_driver);
+
+MODULE_AUTHOR("Faiz Abbas <faiz_abbas@ti.com>");
+MODULE_AUTHOR("Aswath Govindraju <a-govindraju@ti.com>");
+MODULE_DESCRIPTION("CAN TRANSCEIVER PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c
index 14e0551cd319..929e86d6558e 100644
--- a/drivers/phy/phy-core-mipi-dphy.c
+++ b/drivers/phy/phy-core-mipi-dphy.c
@@ -12,12 +12,10 @@
#include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>
-#define PSEC_PER_SEC 1000000000000LL
-
/*
* Minimum D-PHY timings based on MIPI D-PHY specification. Derived
* from the valid ranges specified in Section 6.9, Table 14, Page 41
- * of the D-PHY specification (v2.1).
+ * of the D-PHY specification (v1.2).
*/
int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
unsigned int bpp,
@@ -38,7 +36,7 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
cfg->clk_miss = 0;
cfg->clk_post = 60000 + 52 * ui;
- cfg->clk_pre = 8000;
+ cfg->clk_pre = 8;
cfg->clk_prepare = 38000;
cfg->clk_settle = 95000;
cfg->clk_term_en = 0;
@@ -66,10 +64,10 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui);
cfg->init = 100;
- cfg->lpx = 60000;
+ cfg->lpx = 50000;
cfg->ta_get = 5 * cfg->lpx;
cfg->ta_go = 4 * cfg->lpx;
- cfg->ta_sure = 2 * cfg->lpx;
+ cfg->ta_sure = cfg->lpx;
cfg->wakeup = 1000;
cfg->hs_clk_rate = hs_clk_rate;
@@ -99,7 +97,7 @@ int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
if (cfg->clk_post < (60000 + 52 * ui))
return -EINVAL;
- if (cfg->clk_pre < 8000)
+ if (cfg->clk_pre < 8)
return -EINVAL;
if (cfg->clk_prepare < 38000 || cfg->clk_prepare > 95000)
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index a27b8d578d7f..d93ddf1262c5 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -229,6 +229,17 @@ void phy_pm_runtime_forbid(struct phy *phy)
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_forbid);
+/**
+ * phy_init - phy internal initialization before phy operation
+ * @phy: the phy returned by phy_get()
+ *
+ * Used to allow phy's driver to perform phy internal initialization,
+ * such as PLL block powering, clock initialization or anything that's
+ * is required by the phy to perform the start of operation.
+ * Must be called before phy_power_on().
+ *
+ * Return: %0 if successful, a negative error code otherwise
+ */
int phy_init(struct phy *phy)
{
int ret;
@@ -242,6 +253,9 @@ int phy_init(struct phy *phy)
ret = 0; /* Override possible ret == -ENOTSUPP */
mutex_lock(&phy->mutex);
+ if (phy->power_count > phy->init_count)
+ dev_warn(&phy->dev, "phy_power_on was called before phy_init\n");
+
if (phy->init_count == 0 && phy->ops->init) {
ret = phy->ops->init(phy);
if (ret < 0) {
@@ -258,6 +272,14 @@ out:
}
EXPORT_SYMBOL_GPL(phy_init);
+/**
+ * phy_exit - Phy internal un-initialization
+ * @phy: the phy returned by phy_get()
+ *
+ * Must be called after phy_power_off().
+ *
+ * Return: %0 if successful, a negative error code otherwise
+ */
int phy_exit(struct phy *phy)
{
int ret;
@@ -287,6 +309,14 @@ out:
}
EXPORT_SYMBOL_GPL(phy_exit);
+/**
+ * phy_power_on - Enable the phy and enter proper operation
+ * @phy: the phy returned by phy_get()
+ *
+ * Must be called after phy_init().
+ *
+ * Return: %0 if successful, a negative error code otherwise
+ */
int phy_power_on(struct phy *phy)
{
int ret = 0;
@@ -329,6 +359,14 @@ out:
}
EXPORT_SYMBOL_GPL(phy_power_on);
+/**
+ * phy_power_off - Disable the phy.
+ * @phy: the phy returned by phy_get()
+ *
+ * Must be called before phy_exit().
+ *
+ * Return: %0 if successful, a negative error code otherwise
+ */
int phy_power_off(struct phy *phy)
{
int ret;
@@ -373,6 +411,36 @@ int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode)
}
EXPORT_SYMBOL_GPL(phy_set_mode_ext);
+int phy_set_media(struct phy *phy, enum phy_media media)
+{
+ int ret;
+
+ if (!phy || !phy->ops->set_media)
+ return 0;
+
+ mutex_lock(&phy->mutex);
+ ret = phy->ops->set_media(phy, media);
+ mutex_unlock(&phy->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_set_media);
+
+int phy_set_speed(struct phy *phy, int speed)
+{
+ int ret;
+
+ if (!phy || !phy->ops->set_speed)
+ return 0;
+
+ mutex_lock(&phy->mutex);
+ ret = phy->ops->set_speed(phy, speed);
+ mutex_unlock(&phy->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_set_speed);
+
int phy_reset(struct phy *phy)
{
int ret;
@@ -402,7 +470,7 @@ EXPORT_SYMBOL_GPL(phy_reset);
* runtime, which are otherwise lost after host controller reset and cannot
* be applied in phy_init() or phy_power_on().
*
- * Returns: 0 if successful, an negative error code otherwise
+ * Return: %0 if successful, a negative error code otherwise
*/
int phy_calibrate(struct phy *phy)
{
@@ -428,7 +496,7 @@ EXPORT_SYMBOL_GPL(phy_calibrate);
* on the phy. The configuration will be applied on the current phy
* mode, that can be changed using phy_set_mode().
*
- * Returns: 0 if successful, an negative error code otherwise
+ * Return: %0 if successful, a negative error code otherwise
*/
int phy_configure(struct phy *phy, union phy_configure_opts *opts)
{
@@ -462,7 +530,7 @@ EXPORT_SYMBOL_GPL(phy_configure);
* PHY, so calling it as many times as deemed fit will have no side
* effect.
*
- * Returns: 0 if successful, an negative error code otherwise
+ * Return: %0 if successful, a negative error code otherwise
*/
int phy_validate(struct phy *phy, enum phy_mode mode, int submode,
union phy_configure_opts *opts)
@@ -667,16 +735,18 @@ struct phy *phy_get(struct device *dev, const char *string)
struct phy *phy;
struct device_link *link;
- if (string == NULL) {
- dev_WARN(dev, "missing string\n");
- return ERR_PTR(-EINVAL);
- }
-
if (dev->of_node) {
- index = of_property_match_string(dev->of_node, "phy-names",
- string);
+ if (string)
+ index = of_property_match_string(dev->of_node, "phy-names",
+ string);
+ else
+ index = 0;
phy = _of_phy_get(dev->of_node, index);
} else {
+ if (string == NULL) {
+ dev_WARN(dev, "missing string\n");
+ return ERR_PTR(-EINVAL);
+ }
phy = phy_find(dev, string);
}
if (IS_ERR(phy))
@@ -1062,6 +1132,7 @@ EXPORT_SYMBOL_GPL(__of_phy_provider_register);
* __devm_of_phy_provider_register() - create/register phy provider with the
* framework
* @dev: struct device of the phy provider
+ * @children: device node containing children (if different from dev->of_node)
* @owner: the module owner containing of_xlate
* @of_xlate: function pointer to obtain phy instance from phy provider
*
@@ -1117,12 +1188,14 @@ EXPORT_SYMBOL_GPL(of_phy_provider_unregister);
/**
* devm_of_phy_provider_unregister() - remove phy provider from the framework
* @dev: struct device of the phy provider
+ * @phy_provider: phy provider returned by of_phy_provider_register()
*
* destroys the devres associated with this phy provider and invokes
* of_phy_provider_unregister to unregister the phy provider.
*/
void devm_of_phy_provider_unregister(struct device *dev,
- struct phy_provider *phy_provider) {
+ struct phy_provider *phy_provider)
+{
int r;
r = devres_destroy(dev, devm_phy_provider_release, devm_phy_match,
diff --git a/drivers/phy/phy-lgm-usb.c b/drivers/phy/phy-lgm-usb.c
new file mode 100644
index 000000000000..309c8f0e0724
--- /dev/null
+++ b/drivers/phy/phy-lgm-usb.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel LGM USB PHY driver
+ *
+ * Copyright (C) 2020 Intel Corporation.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/usb/phy.h>
+#include <linux/workqueue.h>
+
+#define CTRL1_OFFSET 0x14
+#define SRAM_EXT_LD_DONE BIT(25)
+#define SRAM_INIT_DONE BIT(26)
+
+#define TCPC_OFFSET 0x1014
+#define TCPC_MUX_CTL GENMASK(1, 0)
+#define MUX_NC 0
+#define MUX_USB 1
+#define MUX_DP 2
+#define MUX_USBDP 3
+#define TCPC_FLIPPED BIT(2)
+#define TCPC_LOW_POWER_EN BIT(3)
+#define TCPC_VALID BIT(4)
+#define TCPC_CONN \
+ (TCPC_VALID | FIELD_PREP(TCPC_MUX_CTL, MUX_USB))
+#define TCPC_DISCONN \
+ (TCPC_VALID | FIELD_PREP(TCPC_MUX_CTL, MUX_NC) | TCPC_LOW_POWER_EN)
+
+static const char *const PHY_RESETS[] = { "phy31", "phy", };
+static const char *const CTL_RESETS[] = { "apb", "ctrl", };
+
+struct tca_apb {
+ struct reset_control *resets[ARRAY_SIZE(PHY_RESETS)];
+ struct regulator *vbus;
+ struct work_struct wk;
+ struct usb_phy phy;
+
+ bool regulator_enabled;
+ bool phy_initialized;
+ bool connected;
+};
+
+static int get_flipped(struct tca_apb *ta, bool *flipped)
+{
+ union extcon_property_value property;
+ int ret;
+
+ ret = extcon_get_property(ta->phy.edev, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_TYPEC_POLARITY, &property);
+ if (ret) {
+ dev_err(ta->phy.dev, "no polarity property from extcon\n");
+ return ret;
+ }
+
+ *flipped = property.intval;
+
+ return 0;
+}
+
+static int phy_init(struct usb_phy *phy)
+{
+ struct tca_apb *ta = container_of(phy, struct tca_apb, phy);
+ void __iomem *ctrl1 = phy->io_priv + CTRL1_OFFSET;
+ int val, ret, i;
+
+ if (ta->phy_initialized)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++)
+ reset_control_deassert(ta->resets[i]);
+
+ ret = readl_poll_timeout(ctrl1, val, val & SRAM_INIT_DONE, 10, 10 * 1000);
+ if (ret) {
+ dev_err(ta->phy.dev, "SRAM init failed, 0x%x\n", val);
+ return ret;
+ }
+
+ writel(readl(ctrl1) | SRAM_EXT_LD_DONE, ctrl1);
+
+ ta->phy_initialized = true;
+ if (!ta->phy.edev) {
+ writel(TCPC_CONN, ta->phy.io_priv + TCPC_OFFSET);
+ return phy->set_vbus(phy, true);
+ }
+
+ schedule_work(&ta->wk);
+
+ return ret;
+}
+
+static void phy_shutdown(struct usb_phy *phy)
+{
+ struct tca_apb *ta = container_of(phy, struct tca_apb, phy);
+ int i;
+
+ if (!ta->phy_initialized)
+ return;
+
+ ta->phy_initialized = false;
+ flush_work(&ta->wk);
+ ta->phy.set_vbus(&ta->phy, false);
+
+ ta->connected = false;
+ writel(TCPC_DISCONN, ta->phy.io_priv + TCPC_OFFSET);
+
+ for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++)
+ reset_control_assert(ta->resets[i]);
+}
+
+static int phy_set_vbus(struct usb_phy *phy, int on)
+{
+ struct tca_apb *ta = container_of(phy, struct tca_apb, phy);
+ int ret;
+
+ if (!!on == ta->regulator_enabled)
+ return 0;
+
+ if (on)
+ ret = regulator_enable(ta->vbus);
+ else
+ ret = regulator_disable(ta->vbus);
+
+ if (!ret)
+ ta->regulator_enabled = on;
+
+ dev_dbg(ta->phy.dev, "set vbus: %d\n", on);
+ return ret;
+}
+
+static void tca_work(struct work_struct *work)
+{
+ struct tca_apb *ta = container_of(work, struct tca_apb, wk);
+ bool connected;
+ bool flipped = false;
+ u32 val;
+ int ret;
+
+ ret = get_flipped(ta, &flipped);
+ if (ret)
+ return;
+
+ connected = extcon_get_state(ta->phy.edev, EXTCON_USB_HOST);
+ if (connected == ta->connected)
+ return;
+
+ ta->connected = connected;
+ if (connected) {
+ val = TCPC_CONN;
+ if (flipped)
+ val |= TCPC_FLIPPED;
+ dev_dbg(ta->phy.dev, "connected%s\n", flipped ? " flipped" : "");
+ } else {
+ val = TCPC_DISCONN;
+ dev_dbg(ta->phy.dev, "disconnected\n");
+ }
+
+ writel(val, ta->phy.io_priv + TCPC_OFFSET);
+
+ ret = ta->phy.set_vbus(&ta->phy, connected);
+ if (ret)
+ dev_err(ta->phy.dev, "failed to set VBUS\n");
+}
+
+static int id_notifier(struct notifier_block *nb, unsigned long event, void *ptr)
+{
+ struct tca_apb *ta = container_of(nb, struct tca_apb, phy.id_nb);
+
+ if (ta->phy_initialized)
+ schedule_work(&ta->wk);
+
+ return NOTIFY_DONE;
+}
+
+static int vbus_notifier(struct notifier_block *nb, unsigned long evnt, void *ptr)
+{
+ return NOTIFY_DONE;
+}
+
+static int phy_probe(struct platform_device *pdev)
+{
+ struct reset_control *resets[ARRAY_SIZE(CTL_RESETS)];
+ struct device *dev = &pdev->dev;
+ struct usb_phy *phy;
+ struct tca_apb *ta;
+ int i;
+
+ ta = devm_kzalloc(dev, sizeof(*ta), GFP_KERNEL);
+ if (!ta)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ta);
+ INIT_WORK(&ta->wk, tca_work);
+
+ phy = &ta->phy;
+ phy->dev = dev;
+ phy->label = dev_name(dev);
+ phy->type = USB_PHY_TYPE_USB3;
+ phy->init = phy_init;
+ phy->shutdown = phy_shutdown;
+ phy->set_vbus = phy_set_vbus;
+ phy->id_nb.notifier_call = id_notifier;
+ phy->vbus_nb.notifier_call = vbus_notifier;
+
+ phy->io_priv = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(phy->io_priv))
+ return PTR_ERR(phy->io_priv);
+
+ ta->vbus = devm_regulator_get(dev, "vbus");
+ if (IS_ERR(ta->vbus))
+ return PTR_ERR(ta->vbus);
+
+ for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++) {
+ resets[i] = devm_reset_control_get_exclusive(dev, CTL_RESETS[i]);
+ if (IS_ERR(resets[i])) {
+ dev_err(dev, "%s reset not found\n", CTL_RESETS[i]);
+ return PTR_ERR(resets[i]);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++) {
+ ta->resets[i] = devm_reset_control_get_exclusive(dev, PHY_RESETS[i]);
+ if (IS_ERR(ta->resets[i])) {
+ dev_err(dev, "%s reset not found\n", PHY_RESETS[i]);
+ return PTR_ERR(ta->resets[i]);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++)
+ reset_control_assert(resets[i]);
+
+ for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++)
+ reset_control_assert(ta->resets[i]);
+ /*
+ * Out-of-band reset of the controller after PHY reset will cause
+ * controller malfunctioning, so we should use in-band controller
+ * reset only and leave the controller de-asserted here.
+ */
+ for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++)
+ reset_control_deassert(resets[i]);
+
+ /* Need to wait at least 20us after de-assert the controller */
+ usleep_range(20, 100);
+
+ return usb_add_phy_dev(phy);
+}
+
+static int phy_remove(struct platform_device *pdev)
+{
+ struct tca_apb *ta = platform_get_drvdata(pdev);
+
+ usb_remove_phy(&ta->phy);
+
+ return 0;
+}
+
+static const struct of_device_id intel_usb_phy_dt_ids[] = {
+ { .compatible = "intel,lgm-usb-phy" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, intel_usb_phy_dt_ids);
+
+static struct platform_driver lgm_phy_driver = {
+ .driver = {
+ .name = "lgm-usb-phy",
+ .of_match_table = intel_usb_phy_dt_ids,
+ },
+ .probe = phy_probe,
+ .remove = phy_remove,
+};
+
+module_platform_driver(lgm_phy_driver);
+
+MODULE_DESCRIPTION("Intel LGM USB PHY driver");
+MODULE_AUTHOR("Li Yin <yin1.li@intel.com>");
+MODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c
index 7a33ec12f71b..d0f4546648f0 100644
--- a/drivers/phy/phy-xgene.c
+++ b/drivers/phy/phy-xgene.c
@@ -961,7 +961,8 @@ static void xgene_phy_sata_cfg_lanes(struct xgene_phy_ctx *ctx)
serdes_wr(ctx, lane, RXTX_REG1, val);
/* Latch VTT value based on the termination to ground and
- enable TX FIFO */
+ * enable TX FIFO
+ */
serdes_rd(ctx, lane, RXTX_REG2, &val);
val = RXTX_REG2_VTT_ENA_SET(val, 0x1);
val = RXTX_REG2_VTT_SEL_SET(val, 0x1);
@@ -1615,7 +1616,7 @@ static struct phy *xgene_phy_xlate(struct device *dev,
if (args->args_count <= 0)
return ERR_PTR(-EINVAL);
- if (args->args[0] < MODE_SATA || args->args[0] >= MODE_MAX)
+ if (args->args[0] >= MODE_MAX)
return ERR_PTR(-EINVAL);
ctx->mode = args->args[0];
@@ -1644,7 +1645,6 @@ static int xgene_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct xgene_phy_ctx *ctx;
- struct resource *res;
u32 default_spd[] = DEFAULT_SATA_SPD_SEL;
u32 default_txboost_gain[] = DEFAULT_SATA_TXBOOST_GAIN;
u32 default_txeye_direction[] = DEFAULT_SATA_TXEYEDIRECTION;
@@ -1661,8 +1661,7 @@ static int xgene_phy_probe(struct platform_device *pdev)
ctx->dev = &pdev->dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ctx->sds_base = devm_ioremap_resource(&pdev->dev, res);
+ ctx->sds_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ctx->sds_base))
return PTR_ERR(ctx->sds_base);
diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig
index e46824da29f6..5c98850f5a36 100644
--- a/drivers/phy/qualcomm/Kconfig
+++ b/drivers/phy/qualcomm/Kconfig
@@ -18,6 +18,23 @@ 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)
+ select GENERIC_PHY
+ help
+ Support for the USB PHY-s on Qualcomm IPQ40xx SoC-s.
+
config PHY_QCOM_IPQ806X_SATA
tristate "Qualcomm IPQ806x SATA SerDes/PHY driver"
depends on ARCH_QCOM
@@ -52,30 +69,6 @@ config PHY_QCOM_QUSB2
PHY which is usually paired with either the ChipIdea or Synopsys DWC3
USB IPs on MSM SOCs.
-config PHY_QCOM_UFS
- tristate "Qualcomm UFS PHY driver"
- depends on OF && ARCH_QCOM
- select GENERIC_PHY
- help
- Support for UFS PHY on QCOM chipsets.
-
-if PHY_QCOM_UFS
-
-config PHY_QCOM_UFS_14NM
- tristate
- default PHY_QCOM_UFS
- help
- Support for 14nm UFS QMP phy present on QCOM chipsets.
-
-config PHY_QCOM_UFS_20NM
- tristate
- default PHY_QCOM_UFS
- depends on BROKEN
- help
- Support for 20nm UFS QMP phy present on QCOM chipsets.
-
-endif
-
config PHY_QCOM_USB_HS
tristate "Qualcomm USB HS PHY module"
depends on USB_ULPI_BUS
@@ -85,9 +78,49 @@ config PHY_QCOM_USB_HS
Support for the USB high-speed ULPI compliant phy on Qualcomm
chipsets.
+config PHY_QCOM_USB_SNPS_FEMTO_V2
+ tristate "Qualcomm SNPS FEMTO USB HS PHY V2 module"
+ depends on OF && (ARCH_QCOM || COMPILE_TEST)
+ select GENERIC_PHY
+ help
+ Enable support for the USB high-speed SNPS Femto phy on Qualcomm
+ chipsets. This PHY has differences in the register map compared
+ to the V1 variants. The PHY is paired with a Synopsys DWC3 USB
+ controller on Qualcomm SOCs.
+
config PHY_QCOM_USB_HSIC
tristate "Qualcomm USB HSIC ULPI PHY module"
depends on USB_ULPI_BUS
select GENERIC_PHY
help
Support for the USB HSIC ULPI compliant PHY on QCOM chipsets.
+
+config PHY_QCOM_USB_HS_28NM
+ tristate "Qualcomm 28nm High-Speed PHY"
+ depends on OF && (ARCH_QCOM || COMPILE_TEST)
+ depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in
+ select GENERIC_PHY
+ help
+ Enable this to support the Qualcomm Synopsys DesignWare Core 28nm
+ High-Speed PHY driver. This driver supports the Hi-Speed PHY which
+ is usually paired with either the ChipIdea or Synopsys DWC3 USB
+ IPs on MSM SOCs.
+
+config PHY_QCOM_USB_SS
+ tristate "Qualcomm USB Super-Speed PHY driver"
+ depends on OF && (ARCH_QCOM || COMPILE_TEST)
+ depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in
+ select GENERIC_PHY
+ help
+ Enable this to support the Super-Speed USB transceiver on various
+ Qualcomm chipsets.
+
+config PHY_QCOM_IPQ806X_USB
+ tristate "Qualcomm IPQ806x DWC3 USB PHY driver"
+ depends on HAS_IOMEM
+ depends on OF && (ARCH_QCOM || COMPILE_TEST)
+ select GENERIC_PHY
+ help
+ This option enables support for the Synopsis PHYs present inside the
+ Qualcomm USB3.0 DWC3 controller on ipq806x SoC. This driver supports
+ both HS and SS PHY controllers.
diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile
index 283251d6a5d9..65f6c30a3e93 100644
--- a/drivers/phy/qualcomm/Makefile
+++ b/drivers/phy/qualcomm/Makefile
@@ -1,12 +1,21 @@
# 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
-obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o
+obj-$(CONFIG_PHY_QCOM_QMP) += \
+ phy-qcom-qmp-combo.o \
+ phy-qcom-qmp-pcie.o \
+ phy-qcom-qmp-pcie-msm8996.o \
+ phy-qcom-qmp-ufs.o \
+ phy-qcom-qmp-usb.o
+
obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o
-obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
-obj-$(CONFIG_PHY_QCOM_UFS_14NM) += phy-qcom-ufs-qmp-14nm.o
-obj-$(CONFIG_PHY_QCOM_UFS_20NM) += phy-qcom-ufs-qmp-20nm.o
obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o
obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o
+obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o
+obj-$(CONFIG_PHY_QCOM_USB_SS) += phy-qcom-usb-ss.o
+obj-$(CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2)+= phy-qcom-snps-femto-v2.o
+obj-$(CONFIG_PHY_QCOM_IPQ806X_USB) += phy-qcom-ipq806x-usb.o
diff --git a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c
index febe0aef68d4..d437a249cd73 100644
--- a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c
+++ b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c
@@ -4,6 +4,7 @@
*/
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -72,18 +73,12 @@ struct qcom_apq8064_sata_phy {
};
/* Helper function to do poll and timeout */
-static int read_poll_timeout(void __iomem *addr, u32 mask)
+static int poll_timeout(void __iomem *addr, u32 mask)
{
- unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS);
+ u32 val;
- do {
- if (readl_relaxed(addr) & mask)
- return 0;
-
- usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
- } while (!time_after(jiffies, timeout));
-
- return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT;
+ return readl_relaxed_poll_timeout(addr, val, (val & mask),
+ DELAY_INTERVAL_US, TIMEOUT_MS * 1000);
}
static int qcom_apq8064_sata_phy_init(struct phy *generic_phy)
@@ -137,21 +132,21 @@ static int qcom_apq8064_sata_phy_init(struct phy *generic_phy)
writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2);
/* PLL Lock wait */
- ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK);
+ ret = poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK);
if (ret) {
dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n");
return ret;
}
/* TX Calibration */
- ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL);
+ ret = poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL);
if (ret) {
dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n");
return ret;
}
/* RX Calibration */
- ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL);
+ ret = poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL);
if (ret) {
dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n");
return ret;
@@ -206,7 +201,6 @@ static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev)
{
struct qcom_apq8064_sata_phy *phy;
struct device *dev = &pdev->dev;
- struct resource *res;
struct phy_provider *phy_provider;
struct phy *generic_phy;
int ret;
@@ -215,8 +209,7 @@ static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev)
if (!phy)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- phy->mmio = devm_ioremap_resource(dev, res);
+ phy->mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(phy->mmio))
return PTR_ERR(phy->mmio);
diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c
new file mode 100644
index 000000000000..fc8ca0f3018d
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-edp.c
@@ -0,0 +1,864 @@
+// 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_cfg {
+ bool is_dp;
+
+ /* DP PHY swing and pre_emphasis tables */
+ const u8 (*swing_hbr_rbr)[4][4];
+ const u8 (*swing_hbr3_hbr2)[4][4];
+ const u8 (*pre_emphasis_hbr_rbr)[4][4];
+ const u8 (*pre_emphasis_hbr3_hbr2)[4][4];
+};
+
+struct qcom_edp {
+ struct device *dev;
+ const struct qcom_edp_cfg *cfg;
+
+ 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 const u8 dp_swing_hbr_rbr[4][4] = {
+ { 0x08, 0x0f, 0x16, 0x1f },
+ { 0x11, 0x1e, 0x1f, 0xff },
+ { 0x16, 0x1f, 0xff, 0xff },
+ { 0x1f, 0xff, 0xff, 0xff }
+};
+
+static const u8 dp_pre_emp_hbr_rbr[4][4] = {
+ { 0x00, 0x0d, 0x14, 0x1a },
+ { 0x00, 0x0e, 0x15, 0xff },
+ { 0x00, 0x0e, 0xff, 0xff },
+ { 0x03, 0xff, 0xff, 0xff }
+};
+
+static const u8 dp_swing_hbr2_hbr3[4][4] = {
+ { 0x02, 0x12, 0x16, 0x1a },
+ { 0x09, 0x19, 0x1f, 0xff },
+ { 0x10, 0x1f, 0xff, 0xff },
+ { 0x1f, 0xff, 0xff, 0xff }
+};
+
+static const u8 dp_pre_emp_hbr2_hbr3[4][4] = {
+ { 0x00, 0x0c, 0x15, 0x1b },
+ { 0x02, 0x0e, 0x16, 0xff },
+ { 0x02, 0x11, 0xff, 0xff },
+ { 0x04, 0xff, 0xff, 0xff }
+};
+
+static const struct qcom_edp_cfg dp_phy_cfg = {
+ .is_dp = true,
+ .swing_hbr_rbr = &dp_swing_hbr_rbr,
+ .swing_hbr3_hbr2 = &dp_swing_hbr2_hbr3,
+ .pre_emphasis_hbr_rbr = &dp_pre_emp_hbr_rbr,
+ .pre_emphasis_hbr3_hbr2 = &dp_pre_emp_hbr2_hbr3,
+};
+
+static const u8 edp_swing_hbr_rbr[4][4] = {
+ { 0x07, 0x0f, 0x16, 0x1f },
+ { 0x0d, 0x16, 0x1e, 0xff },
+ { 0x11, 0x1b, 0xff, 0xff },
+ { 0x16, 0xff, 0xff, 0xff }
+};
+
+static const u8 edp_pre_emp_hbr_rbr[4][4] = {
+ { 0x05, 0x12, 0x17, 0x1d },
+ { 0x05, 0x11, 0x18, 0xff },
+ { 0x06, 0x11, 0xff, 0xff },
+ { 0x00, 0xff, 0xff, 0xff }
+};
+
+static const u8 edp_swing_hbr2_hbr3[4][4] = {
+ { 0x0b, 0x11, 0x17, 0x1c },
+ { 0x10, 0x19, 0x1f, 0xff },
+ { 0x19, 0x1f, 0xff, 0xff },
+ { 0x1f, 0xff, 0xff, 0xff }
+};
+
+static const u8 edp_pre_emp_hbr2_hbr3[4][4] = {
+ { 0x08, 0x11, 0x17, 0x1b },
+ { 0x00, 0x0c, 0x13, 0xff },
+ { 0x05, 0x10, 0xff, 0xff },
+ { 0x00, 0xff, 0xff, 0xff }
+};
+
+static const struct qcom_edp_cfg edp_phy_cfg = {
+ .is_dp = false,
+ .swing_hbr_rbr = &edp_swing_hbr_rbr,
+ .swing_hbr3_hbr2 = &edp_swing_hbr2_hbr3,
+ .pre_emphasis_hbr_rbr = &edp_pre_emp_hbr_rbr,
+ .pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3,
+};
+
+static int qcom_edp_phy_init(struct phy *phy)
+{
+ struct qcom_edp *edp = phy_get_drvdata(phy);
+ const struct qcom_edp_cfg *cfg = edp->cfg;
+ int ret;
+ u8 cfg8;
+
+ 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);
+
+ if (cfg && cfg->is_dp)
+ cfg8 = 0xb7;
+ else
+ cfg8 = 0x37;
+
+ writel(0xfc, edp->edp + DP_PHY_MODE);
+
+ 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(cfg8, 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_set_voltages(struct qcom_edp *edp, const struct phy_configure_opts_dp *dp_opts)
+{
+ const struct qcom_edp_cfg *cfg = edp->cfg;
+ unsigned int v_level = 0;
+ unsigned int p_level = 0;
+ u8 ldo_config;
+ u8 swing;
+ u8 emph;
+ int i;
+
+ if (!cfg)
+ return 0;
+
+ for (i = 0; i < dp_opts->lanes; i++) {
+ v_level = max(v_level, dp_opts->voltage[i]);
+ p_level = max(p_level, dp_opts->pre[i]);
+ }
+
+ if (dp_opts->link_rate <= 2700) {
+ swing = (*cfg->swing_hbr_rbr)[v_level][p_level];
+ emph = (*cfg->pre_emphasis_hbr_rbr)[v_level][p_level];
+ } else {
+ swing = (*cfg->swing_hbr3_hbr2)[v_level][p_level];
+ emph = (*cfg->pre_emphasis_hbr3_hbr2)[v_level][p_level];
+ }
+
+ if (swing == 0xff || emph == 0xff)
+ return -EINVAL;
+
+ ldo_config = (cfg && cfg->is_dp) ? 0x1 : 0x0;
+
+ writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
+ writel(swing, edp->tx0 + TXn_TX_DRV_LVL);
+ writel(emph, edp->tx0 + TXn_TX_EMP_POST1_LVL);
+
+ writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG);
+ writel(swing, edp->tx1 + TXn_TX_DRV_LVL);
+ writel(emph, edp->tx1 + TXn_TX_EMP_POST1_LVL);
+
+ return 0;
+}
+
+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);
+ int ret = 0;
+
+ memcpy(&edp->dp_opts, dp_opts, sizeof(*dp_opts));
+
+ if (dp_opts->set_voltages)
+ ret = qcom_edp_set_voltages(edp, dp_opts);
+
+ return ret;
+}
+
+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, unsigned long *pixel_freq)
+{
+ const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
+ 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);
+
+ return 0;
+}
+
+static int qcom_edp_phy_power_on(struct phy *phy)
+{
+ const struct qcom_edp *edp = phy_get_drvdata(phy);
+ const struct qcom_edp_cfg *cfg = edp->cfg;
+ u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
+ unsigned long pixel_freq;
+ u8 ldo_config;
+ int timeout;
+ int ret;
+ u32 val;
+ u8 cfg1;
+
+ 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;
+
+
+ ldo_config = (cfg && cfg->is_dp) ? 0x1 : 0x0;
+
+ writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
+ writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG);
+ writel(0x00, edp->tx0 + TXn_LANE_MODE_1);
+ writel(0x00, edp->tx1 + TXn_LANE_MODE_1);
+
+ if (edp->dp_opts.ssc) {
+ 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, &pixel_freq);
+ 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);
+
+ if (edp->dp_opts.lanes == 1) {
+ bias0_en = 0x01;
+ bias1_en = 0x00;
+ drvr0_en = 0x06;
+ drvr1_en = 0x07;
+ cfg1 = 0x1;
+ } else if (edp->dp_opts.lanes == 2) {
+ bias0_en = 0x03;
+ bias1_en = 0x00;
+ drvr0_en = 0x04;
+ drvr1_en = 0x07;
+ cfg1 = 0x3;
+ } else {
+ bias0_en = 0x03;
+ bias1_en = 0x03;
+ drvr0_en = 0x04;
+ drvr1_en = 0x04;
+ cfg1 = 0xf;
+ }
+
+ writel(drvr0_en, edp->tx0 + TXn_HIGHZ_DRVR_EN);
+ writel(bias0_en, edp->tx0 + TXn_TRANSCEIVER_BIAS_EN);
+ writel(drvr1_en, edp->tx1 + TXn_HIGHZ_DRVR_EN);
+ writel(bias1_en, edp->tx1 + TXn_TRANSCEIVER_BIAS_EN);
+ writel(cfg1, edp->edp + DP_PHY_CFG_1);
+
+ writel(0x18, edp->edp + DP_PHY_CFG);
+ usleep_range(100, 1000);
+
+ writel(0x19, edp->edp + DP_PHY_CFG);
+
+ ret = readl_poll_timeout(edp->edp + DP_PHY_STATUS,
+ val, val & BIT(1), 500, 10000);
+ if (ret)
+ return ret;
+
+ clk_set_rate(edp->dp_link_hw.clk, edp->dp_opts.link_rate * 100000);
+ clk_set_rate(edp->dp_pixel_hw.clk, pixel_freq);
+
+ return 0;
+}
+
+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 = { };
+ char name[64];
+ int ret;
+
+ data = devm_kzalloc(edp->dev, struct_size(data, hws, 2), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ snprintf(name, sizeof(name), "%s::link_clk", dev_name(edp->dev));
+ init.ops = &qcom_edp_dp_link_clk_ops;
+ init.name = name;
+ edp->dp_link_hw.init = &init;
+ ret = devm_clk_hw_register(edp->dev, &edp->dp_link_hw);
+ if (ret)
+ return ret;
+
+ snprintf(name, sizeof(name), "%s::vco_div_clk", dev_name(edp->dev));
+ init.ops = &qcom_edp_dp_pixel_clk_ops;
+ init.name = name;
+ 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->cfg = of_device_get_match_data(&pdev->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 = regulator_set_load(edp->supplies[0].consumer, 21800); /* 1.2 V vdda-phy */
+ if (ret) {
+ dev_err(dev, "failed to set load at %s\n", edp->supplies[0].supply);
+ return ret;
+ }
+
+ ret = regulator_set_load(edp->supplies[1].consumer, 36000); /* 0.9 V vdda-pll */
+ if (ret) {
+ dev_err(dev, "failed to set load at %s\n", edp->supplies[1].supply);
+ 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,sc7280-edp-phy" },
+ { .compatible = "qcom,sc8180x-edp-phy" },
+ { .compatible = "qcom,sc8280xp-dp-phy", .data = &dp_phy_cfg },
+ { .compatible = "qcom,sc8280xp-edp-phy", .data = &edp_phy_cfg },
+ { }
+};
+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");
diff --git a/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c
new file mode 100644
index 000000000000..d3e7d5e1d1b6
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 John Crispin <john@phrozen.org>
+ *
+ * Based on code from
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+struct ipq4019_usb_phy {
+ struct device *dev;
+ struct phy *phy;
+ void __iomem *base;
+ struct reset_control *por_rst;
+ struct reset_control *srif_rst;
+};
+
+static int ipq4019_ss_phy_power_off(struct phy *_phy)
+{
+ struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy);
+
+ reset_control_assert(phy->por_rst);
+ msleep(10);
+
+ return 0;
+}
+
+static int ipq4019_ss_phy_power_on(struct phy *_phy)
+{
+ struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy);
+
+ ipq4019_ss_phy_power_off(_phy);
+
+ reset_control_deassert(phy->por_rst);
+
+ return 0;
+}
+
+static const struct phy_ops ipq4019_usb_ss_phy_ops = {
+ .power_on = ipq4019_ss_phy_power_on,
+ .power_off = ipq4019_ss_phy_power_off,
+};
+
+static int ipq4019_hs_phy_power_off(struct phy *_phy)
+{
+ struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy);
+
+ reset_control_assert(phy->por_rst);
+ msleep(10);
+
+ reset_control_assert(phy->srif_rst);
+ msleep(10);
+
+ return 0;
+}
+
+static int ipq4019_hs_phy_power_on(struct phy *_phy)
+{
+ struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy);
+
+ ipq4019_hs_phy_power_off(_phy);
+
+ reset_control_deassert(phy->srif_rst);
+ msleep(10);
+
+ reset_control_deassert(phy->por_rst);
+
+ return 0;
+}
+
+static const struct phy_ops ipq4019_usb_hs_phy_ops = {
+ .power_on = ipq4019_hs_phy_power_on,
+ .power_off = ipq4019_hs_phy_power_off,
+};
+
+static const struct of_device_id ipq4019_usb_phy_of_match[] = {
+ { .compatible = "qcom,usb-hs-ipq4019-phy", .data = &ipq4019_usb_hs_phy_ops},
+ { .compatible = "qcom,usb-ss-ipq4019-phy", .data = &ipq4019_usb_ss_phy_ops},
+ { },
+};
+MODULE_DEVICE_TABLE(of, ipq4019_usb_phy_of_match);
+
+static int ipq4019_usb_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *phy_provider;
+ struct ipq4019_usb_phy *phy;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ phy->dev = &pdev->dev;
+ phy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(phy->base)) {
+ dev_err(dev, "failed to remap register memory\n");
+ return PTR_ERR(phy->base);
+ }
+
+ phy->por_rst = devm_reset_control_get(phy->dev, "por_rst");
+ if (IS_ERR(phy->por_rst)) {
+ if (PTR_ERR(phy->por_rst) != -EPROBE_DEFER)
+ dev_err(dev, "POR reset is missing\n");
+ return PTR_ERR(phy->por_rst);
+ }
+
+ phy->srif_rst = devm_reset_control_get_optional(phy->dev, "srif_rst");
+ if (IS_ERR(phy->srif_rst))
+ return PTR_ERR(phy->srif_rst);
+
+ phy->phy = devm_phy_create(dev, NULL, of_device_get_match_data(dev));
+ if (IS_ERR(phy->phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(phy->phy);
+ }
+ phy_set_drvdata(phy->phy, phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver ipq4019_usb_phy_driver = {
+ .probe = ipq4019_usb_phy_probe,
+ .driver = {
+ .of_match_table = ipq4019_usb_phy_of_match,
+ .name = "ipq4019-usb-phy",
+ }
+};
+module_platform_driver(ipq4019_usb_phy_driver);
+
+MODULE_DESCRIPTION("QCOM/IPQ4019 USB phy driver");
+MODULE_AUTHOR("John Crispin <john@phrozen.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c
index 41a69f56b346..0fc2a1ed39b3 100644
--- a/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c
+++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c
@@ -128,7 +128,6 @@ static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
{
struct qcom_ipq806x_sata_phy *phy;
struct device *dev = &pdev->dev;
- struct resource *res;
struct phy_provider *phy_provider;
struct phy *generic_phy;
int ret;
@@ -137,8 +136,7 @@ static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
if (!phy)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- phy->mmio = devm_ioremap_resource(dev, res);
+ phy->mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(phy->mmio))
return PTR_ERR(phy->mmio);
diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c
new file mode 100644
index 000000000000..7bacc527fbad
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c
@@ -0,0 +1,566 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/bitfield.h>
+
+/* USB QSCRATCH Hardware registers */
+#define QSCRATCH_GENERAL_CFG (0x08)
+#define HSUSB_PHY_CTRL_REG (0x10)
+
+/* PHY_CTRL_REG */
+#define HSUSB_CTRL_DMSEHV_CLAMP BIT(24)
+#define HSUSB_CTRL_USB2_SUSPEND BIT(23)
+#define HSUSB_CTRL_UTMI_CLK_EN BIT(21)
+#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID BIT(20)
+#define HSUSB_CTRL_USE_CLKCORE BIT(18)
+#define HSUSB_CTRL_DPSEHV_CLAMP BIT(17)
+#define HSUSB_CTRL_COMMONONN BIT(11)
+#define HSUSB_CTRL_ID_HV_CLAMP BIT(9)
+#define HSUSB_CTRL_OTGSESSVLD_CLAMP BIT(8)
+#define HSUSB_CTRL_CLAMP_EN BIT(7)
+#define HSUSB_CTRL_RETENABLEN BIT(1)
+#define HSUSB_CTRL_POR BIT(0)
+
+/* QSCRATCH_GENERAL_CFG */
+#define HSUSB_GCFG_XHCI_REV BIT(2)
+
+/* USB QSCRATCH Hardware registers */
+#define SSUSB_PHY_CTRL_REG (0x00)
+#define SSUSB_PHY_PARAM_CTRL_1 (0x04)
+#define SSUSB_PHY_PARAM_CTRL_2 (0x08)
+#define CR_PROTOCOL_DATA_IN_REG (0x0c)
+#define CR_PROTOCOL_DATA_OUT_REG (0x10)
+#define CR_PROTOCOL_CAP_ADDR_REG (0x14)
+#define CR_PROTOCOL_CAP_DATA_REG (0x18)
+#define CR_PROTOCOL_READ_REG (0x1c)
+#define CR_PROTOCOL_WRITE_REG (0x20)
+
+/* PHY_CTRL_REG */
+#define SSUSB_CTRL_REF_USE_PAD BIT(28)
+#define SSUSB_CTRL_TEST_POWERDOWN BIT(27)
+#define SSUSB_CTRL_LANE0_PWR_PRESENT BIT(24)
+#define SSUSB_CTRL_SS_PHY_EN BIT(8)
+#define SSUSB_CTRL_SS_PHY_RESET BIT(7)
+
+/* SSPHY control registers - Does this need 0x30? */
+#define SSPHY_CTRL_RX_OVRD_IN_HI(lane) (0x1006 + 0x100 * (lane))
+#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane) (0x1002 + 0x100 * (lane))
+
+/* SSPHY SoC version specific values */
+#define SSPHY_RX_EQ_VALUE 4 /* Override value for rx_eq */
+/* Override value for transmit preemphasis */
+#define SSPHY_TX_DEEMPH_3_5DB 23
+/* Override value for mpll */
+#define SSPHY_MPLL_VALUE 0
+
+/* QSCRATCH PHY_PARAM_CTRL1 fields */
+#define PHY_PARAM_CTRL1_TX_FULL_SWING_MASK GENMASK(26, 19)
+#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK GENMASK(19, 13)
+#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK GENMASK(13, 7)
+#define PHY_PARAM_CTRL1_LOS_BIAS_MASK GENMASK(7, 2)
+
+#define PHY_PARAM_CTRL1_MASK \
+ (PHY_PARAM_CTRL1_TX_FULL_SWING_MASK | \
+ PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK | \
+ PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK | \
+ PHY_PARAM_CTRL1_LOS_BIAS_MASK)
+
+#define PHY_PARAM_CTRL1_TX_FULL_SWING(x) \
+ FIELD_PREP(PHY_PARAM_CTRL1_TX_FULL_SWING_MASK, (x))
+#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB(x) \
+ FIELD_PREP(PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK, (x))
+#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(x) \
+ FIELD_PREP(PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK, x)
+#define PHY_PARAM_CTRL1_LOS_BIAS(x) \
+ FIELD_PREP(PHY_PARAM_CTRL1_LOS_BIAS_MASK, (x))
+
+/* RX OVRD IN HI bits */
+#define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13)
+#define RX_OVRD_IN_HI_RX_RX_RESET BIT(12)
+#define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11)
+#define RX_OVRD_IN_HI_RX_EQ_MASK GENMASK(10, 7)
+#define RX_OVRD_IN_HI_RX_EQ(x) FIELD_PREP(RX_OVRD_IN_HI_RX_EQ_MASK, (x))
+#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7)
+#define RX_OVRD_IN_HI_RX_EQ_EN BIT(6)
+#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5)
+#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK GENMASK(4, 2)
+#define RX_OVRD_IN_HI_RX_RATE_OVRD BIT(2)
+#define RX_OVRD_IN_HI_RX_RATE_MASK GENMASK(2, 0)
+
+/* TX OVRD DRV LO register bits */
+#define TX_OVRD_DRV_LO_AMPLITUDE_MASK GENMASK(6, 0)
+#define TX_OVRD_DRV_LO_PREEMPH_MASK GENMASK(13, 6)
+#define TX_OVRD_DRV_LO_PREEMPH(x) ((x) << 7)
+#define TX_OVRD_DRV_LO_EN BIT(14)
+
+/* MPLL bits */
+#define SSPHY_MPLL_MASK GENMASK(8, 5)
+#define SSPHY_MPLL(x) ((x) << 5)
+
+/* SS CAP register bits */
+#define SS_CR_CAP_ADDR_REG BIT(0)
+#define SS_CR_CAP_DATA_REG BIT(0)
+#define SS_CR_READ_REG BIT(0)
+#define SS_CR_WRITE_REG BIT(0)
+
+#define LATCH_SLEEP 40
+#define LATCH_TIMEOUT 100
+
+struct usb_phy {
+ void __iomem *base;
+ struct device *dev;
+ struct clk *xo_clk;
+ struct clk *ref_clk;
+ u32 rx_eq;
+ u32 tx_deamp_3_5db;
+ u32 mpll;
+};
+
+struct phy_drvdata {
+ struct phy_ops ops;
+ u32 clk_rate;
+};
+
+/**
+ * usb_phy_write_readback() - Write register and read back masked value to
+ * confirm it is written
+ *
+ * @phy_dwc3: QCOM DWC3 phy context
+ * @offset: register offset.
+ * @mask: register bitmask specifying what should be updated
+ * @val: value to write.
+ */
+static inline void usb_phy_write_readback(struct usb_phy *phy_dwc3,
+ u32 offset,
+ const u32 mask, u32 val)
+{
+ u32 write_val, tmp = readl(phy_dwc3->base + offset);
+
+ tmp &= ~mask; /* retain other bits */
+ write_val = tmp | val;
+
+ writel(write_val, phy_dwc3->base + offset);
+
+ /* Read back to see if val was written */
+ tmp = readl(phy_dwc3->base + offset);
+ tmp &= mask; /* clear other bits */
+
+ if (tmp != val)
+ dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n", val, offset);
+}
+
+static int wait_for_latch(void __iomem *addr)
+{
+ u32 val;
+
+ return readl_poll_timeout(addr, val, !val, LATCH_SLEEP, LATCH_TIMEOUT);
+}
+
+/**
+ * usb_ss_write_phycreg() - Write SSPHY register
+ *
+ * @phy_dwc3: QCOM DWC3 phy context
+ * @addr: SSPHY address to write.
+ * @val: value to write.
+ */
+static int usb_ss_write_phycreg(struct usb_phy *phy_dwc3,
+ u32 addr, u32 val)
+{
+ int ret;
+
+ writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
+ writel(SS_CR_CAP_ADDR_REG,
+ phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
+
+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
+ if (ret)
+ goto err_wait;
+
+ writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
+ writel(SS_CR_CAP_DATA_REG,
+ phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);
+
+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);
+ if (ret)
+ goto err_wait;
+
+ writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG);
+
+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG);
+
+err_wait:
+ if (ret)
+ dev_err(phy_dwc3->dev, "timeout waiting for latch\n");
+ return ret;
+}
+
+/**
+ * usb_ss_read_phycreg() - Read SSPHY register.
+ *
+ * @phy_dwc3: QCOM DWC3 phy context
+ * @addr: SSPHY address to read.
+ * @val: pointer in which read is store.
+ */
+static int usb_ss_read_phycreg(struct usb_phy *phy_dwc3,
+ u32 addr, u32 *val)
+{
+ int ret;
+
+ writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
+ writel(SS_CR_CAP_ADDR_REG,
+ phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
+
+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
+ if (ret)
+ goto err_wait;
+
+ /*
+ * Due to hardware bug, first read of SSPHY register might be
+ * incorrect. Hence as workaround, SW should perform SSPHY register
+ * read twice, but use only second read and ignore first read.
+ */
+ writel(SS_CR_READ_REG, phy_dwc3->base + CR_PROTOCOL_READ_REG);
+
+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_READ_REG);
+ if (ret)
+ goto err_wait;
+
+ /* throwaway read */
+ readl(phy_dwc3->base + CR_PROTOCOL_DATA_OUT_REG);
+
+ writel(SS_CR_READ_REG, phy_dwc3->base + CR_PROTOCOL_READ_REG);
+
+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_READ_REG);
+ if (ret)
+ goto err_wait;
+
+ *val = readl(phy_dwc3->base + CR_PROTOCOL_DATA_OUT_REG);
+
+err_wait:
+ return ret;
+}
+
+static int qcom_ipq806x_usb_hs_phy_init(struct phy *phy)
+{
+ struct usb_phy *phy_dwc3 = phy_get_drvdata(phy);
+ int ret;
+ u32 val;
+
+ ret = clk_prepare_enable(phy_dwc3->xo_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(phy_dwc3->ref_clk);
+ if (ret) {
+ clk_disable_unprepare(phy_dwc3->xo_clk);
+ return ret;
+ }
+
+ /*
+ * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel
+ * enable clamping, and disable RETENTION (power-on default is ENABLED)
+ */
+ val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP |
+ HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN |
+ HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP |
+ HSUSB_CTRL_UTMI_OTG_VBUS_VALID | HSUSB_CTRL_UTMI_CLK_EN |
+ HSUSB_CTRL_CLAMP_EN | 0x70;
+
+ /* use core clock if external reference is not present */
+ if (!phy_dwc3->xo_clk)
+ val |= HSUSB_CTRL_USE_CLKCORE;
+
+ writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG);
+ usleep_range(2000, 2200);
+
+ /* Disable (bypass) VBUS and ID filters */
+ writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG);
+
+ return 0;
+}
+
+static int qcom_ipq806x_usb_hs_phy_exit(struct phy *phy)
+{
+ struct usb_phy *phy_dwc3 = phy_get_drvdata(phy);
+
+ clk_disable_unprepare(phy_dwc3->ref_clk);
+ clk_disable_unprepare(phy_dwc3->xo_clk);
+
+ return 0;
+}
+
+static int qcom_ipq806x_usb_ss_phy_init(struct phy *phy)
+{
+ struct usb_phy *phy_dwc3 = phy_get_drvdata(phy);
+ int ret;
+ u32 data;
+
+ ret = clk_prepare_enable(phy_dwc3->xo_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(phy_dwc3->ref_clk);
+ if (ret) {
+ clk_disable_unprepare(phy_dwc3->xo_clk);
+ return ret;
+ }
+
+ /* reset phy */
+ data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG);
+ writel(data | SSUSB_CTRL_SS_PHY_RESET,
+ phy_dwc3->base + SSUSB_PHY_CTRL_REG);
+ usleep_range(2000, 2200);
+ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
+
+ /* clear REF_PAD if we don't have XO clk */
+ if (!phy_dwc3->xo_clk)
+ data &= ~SSUSB_CTRL_REF_USE_PAD;
+ else
+ data |= SSUSB_CTRL_REF_USE_PAD;
+
+ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
+
+ /* wait for ref clk to become stable, this can take up to 30ms */
+ msleep(30);
+
+ data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT;
+ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
+
+ /*
+ * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates
+ * in HS mode instead of SS mode. Workaround it by asserting
+ * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode
+ */
+ ret = usb_ss_read_phycreg(phy_dwc3, 0x102D, &data);
+ if (ret)
+ goto err_phy_trans;
+
+ data |= (1 << 7);
+ ret = usb_ss_write_phycreg(phy_dwc3, 0x102D, data);
+ if (ret)
+ goto err_phy_trans;
+
+ ret = usb_ss_read_phycreg(phy_dwc3, 0x1010, &data);
+ if (ret)
+ goto err_phy_trans;
+
+ data &= ~0xff0;
+ data |= 0x20;
+ ret = usb_ss_write_phycreg(phy_dwc3, 0x1010, data);
+ if (ret)
+ goto err_phy_trans;
+
+ /*
+ * Fix RX Equalization setting as follows
+ * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
+ * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
+ * LANE0.RX_OVRD_IN_HI.RX_EQ set based on SoC version
+ * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
+ */
+ ret = usb_ss_read_phycreg(phy_dwc3, SSPHY_CTRL_RX_OVRD_IN_HI(0), &data);
+ if (ret)
+ goto err_phy_trans;
+
+ data &= ~RX_OVRD_IN_HI_RX_EQ_EN;
+ data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD;
+ data &= ~RX_OVRD_IN_HI_RX_EQ_MASK;
+ data |= RX_OVRD_IN_HI_RX_EQ(phy_dwc3->rx_eq);
+ data |= RX_OVRD_IN_HI_RX_EQ_OVRD;
+ ret = usb_ss_write_phycreg(phy_dwc3,
+ SSPHY_CTRL_RX_OVRD_IN_HI(0), data);
+ if (ret)
+ goto err_phy_trans;
+
+ /*
+ * Set EQ and TX launch amplitudes as follows
+ * LANE0.TX_OVRD_DRV_LO.PREEMPH set based on SoC version
+ * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 110
+ * LANE0.TX_OVRD_DRV_LO.EN set to 1.
+ */
+ ret = usb_ss_read_phycreg(phy_dwc3,
+ SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data);
+ if (ret)
+ goto err_phy_trans;
+
+ data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK;
+ data |= TX_OVRD_DRV_LO_PREEMPH(phy_dwc3->tx_deamp_3_5db);
+ data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK;
+ data |= 0x6E;
+ data |= TX_OVRD_DRV_LO_EN;
+ ret = usb_ss_write_phycreg(phy_dwc3,
+ SSPHY_CTRL_TX_OVRD_DRV_LO(0), data);
+ if (ret)
+ goto err_phy_trans;
+
+ data = 0;
+ data &= ~SSPHY_MPLL_MASK;
+ data |= SSPHY_MPLL(phy_dwc3->mpll);
+ usb_ss_write_phycreg(phy_dwc3, 0x30, data);
+
+ /*
+ * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
+ * TX_FULL_SWING [26:20] amplitude to 110
+ * TX_DEEMPH_6DB [19:14] to 32
+ * TX_DEEMPH_3_5DB [13:8] set based on SoC version
+ * LOS_BIAS [7:3] to 9
+ */
+ data = readl(phy_dwc3->base + SSUSB_PHY_PARAM_CTRL_1);
+
+ data &= ~PHY_PARAM_CTRL1_MASK;
+
+ data |= PHY_PARAM_CTRL1_TX_FULL_SWING(0x6e) |
+ PHY_PARAM_CTRL1_TX_DEEMPH_6DB(0x20) |
+ PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(phy_dwc3->tx_deamp_3_5db) |
+ PHY_PARAM_CTRL1_LOS_BIAS(0x9);
+
+ usb_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1,
+ PHY_PARAM_CTRL1_MASK, data);
+
+err_phy_trans:
+ return ret;
+}
+
+static int qcom_ipq806x_usb_ss_phy_exit(struct phy *phy)
+{
+ struct usb_phy *phy_dwc3 = phy_get_drvdata(phy);
+
+ /* Sequence to put SSPHY in low power state:
+ * 1. Clear REF_PHY_EN in PHY_CTRL_REG
+ * 2. Clear REF_USE_PAD in PHY_CTRL_REG
+ * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
+ */
+ usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
+ SSUSB_CTRL_SS_PHY_EN, 0x0);
+ usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
+ SSUSB_CTRL_REF_USE_PAD, 0x0);
+ usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
+ SSUSB_CTRL_TEST_POWERDOWN, 0x0);
+
+ clk_disable_unprepare(phy_dwc3->ref_clk);
+ clk_disable_unprepare(phy_dwc3->xo_clk);
+
+ return 0;
+}
+
+static const struct phy_drvdata qcom_ipq806x_usb_hs_drvdata = {
+ .ops = {
+ .init = qcom_ipq806x_usb_hs_phy_init,
+ .exit = qcom_ipq806x_usb_hs_phy_exit,
+ .owner = THIS_MODULE,
+ },
+ .clk_rate = 60000000,
+};
+
+static const struct phy_drvdata qcom_ipq806x_usb_ss_drvdata = {
+ .ops = {
+ .init = qcom_ipq806x_usb_ss_phy_init,
+ .exit = qcom_ipq806x_usb_ss_phy_exit,
+ .owner = THIS_MODULE,
+ },
+ .clk_rate = 125000000,
+};
+
+static const struct of_device_id qcom_ipq806x_usb_phy_table[] = {
+ { .compatible = "qcom,ipq806x-usb-phy-hs",
+ .data = &qcom_ipq806x_usb_hs_drvdata },
+ { .compatible = "qcom,ipq806x-usb-phy-ss",
+ .data = &qcom_ipq806x_usb_ss_drvdata },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, qcom_ipq806x_usb_phy_table);
+
+static int qcom_ipq806x_usb_phy_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ resource_size_t size;
+ struct phy *generic_phy;
+ struct usb_phy *phy_dwc3;
+ const struct phy_drvdata *data;
+ struct phy_provider *phy_provider;
+
+ phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL);
+ if (!phy_dwc3)
+ return -ENOMEM;
+
+ data = of_device_get_match_data(&pdev->dev);
+
+ phy_dwc3->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+ size = resource_size(res);
+ phy_dwc3->base = devm_ioremap(phy_dwc3->dev, res->start, size);
+
+ if (!phy_dwc3->base) {
+ dev_err(phy_dwc3->dev, "failed to map reg\n");
+ return -ENOMEM;
+ }
+
+ phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref");
+ if (IS_ERR(phy_dwc3->ref_clk)) {
+ dev_dbg(phy_dwc3->dev, "cannot get reference clock\n");
+ return PTR_ERR(phy_dwc3->ref_clk);
+ }
+
+ clk_set_rate(phy_dwc3->ref_clk, data->clk_rate);
+
+ phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo");
+ if (IS_ERR(phy_dwc3->xo_clk)) {
+ dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n");
+ phy_dwc3->xo_clk = NULL;
+ }
+
+ /* Parse device node to probe HSIO settings */
+ if (device_property_read_u32(&pdev->dev, "qcom,rx-eq",
+ &phy_dwc3->rx_eq))
+ phy_dwc3->rx_eq = SSPHY_RX_EQ_VALUE;
+
+ if (device_property_read_u32(&pdev->dev, "qcom,tx-deamp_3_5db",
+ &phy_dwc3->tx_deamp_3_5db))
+ phy_dwc3->tx_deamp_3_5db = SSPHY_TX_DEEMPH_3_5DB;
+
+ if (device_property_read_u32(&pdev->dev, "qcom,mpll", &phy_dwc3->mpll))
+ phy_dwc3->mpll = SSPHY_MPLL_VALUE;
+
+ generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node, &data->ops);
+
+ if (IS_ERR(generic_phy))
+ return PTR_ERR(generic_phy);
+
+ phy_set_drvdata(generic_phy, phy_dwc3);
+ platform_set_drvdata(pdev, phy_dwc3);
+
+ phy_provider = devm_of_phy_provider_register(phy_dwc3->dev,
+ of_phy_simple_xlate);
+
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ return 0;
+}
+
+static struct platform_driver qcom_ipq806x_usb_phy_driver = {
+ .probe = qcom_ipq806x_usb_phy_probe,
+ .driver = {
+ .name = "qcom-ipq806x-usb-phy",
+ .of_match_table = qcom_ipq806x_usb_phy_table,
+ },
+};
+
+module_platform_driver(qcom_ipq806x_usb_phy_driver);
+
+MODULE_ALIAS("platform:phy-qcom-ipq806x-usb");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
+MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver");
diff --git a/drivers/phy/qualcomm/phy-qcom-pcie2.c b/drivers/phy/qualcomm/phy-qcom-pcie2.c
index 9dba3594e6d9..5407e59bb185 100644
--- a/drivers/phy/qualcomm/phy-qcom-pcie2.c
+++ b/drivers/phy/qualcomm/phy-qcom-pcie2.c
@@ -250,7 +250,6 @@ static int qcom_pcie2_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct qcom_phy *qphy;
- struct resource *res;
struct device *dev = &pdev->dev;
struct phy *phy;
int ret;
@@ -260,9 +259,7 @@ static int qcom_pcie2_phy_probe(struct platform_device *pdev)
return -ENOMEM;
qphy->dev = dev;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- qphy->base = devm_ioremap_resource(dev, res);
+ qphy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(qphy->base))
return PTR_ERR(qphy->base);
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
new file mode 100644
index 000000000000..ba9d761ec49a
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
@@ -0,0 +1,2928 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#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"
+
+/* QPHY_SW_RESET bit */
+#define SW_RESET BIT(0)
+/* QPHY_POWER_DOWN_CONTROL */
+#define SW_PWRDN BIT(0)
+/* QPHY_START_CONTROL bits */
+#define SERDES_START BIT(0)
+#define PCS_START BIT(1)
+/* QPHY_PCS_STATUS bit */
+#define PHYSTATUS BIT(6)
+
+/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */
+/* DP PHY soft reset */
+#define SW_DPPHY_RESET BIT(0)
+/* mux to select DP PHY reset control, 0:HW control, 1: software reset */
+#define SW_DPPHY_RESET_MUX BIT(1)
+/* USB3 PHY soft reset */
+#define SW_USB3PHY_RESET BIT(2)
+/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */
+#define SW_USB3PHY_RESET_MUX BIT(3)
+
+/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */
+#define USB3_MODE BIT(0) /* enables USB3 mode */
+#define DP_MODE BIT(1) /* enables DP mode */
+
+/* QPHY_PCS_AUTONOMOUS_MODE_CTRL register bits */
+#define ARCVR_DTCT_EN BIT(0)
+#define ALFPS_DTCT_EN BIT(1)
+#define ARCVR_DTCT_EVENT_SEL BIT(4)
+
+/* QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR register bits */
+#define IRQ_CLEAR BIT(0)
+
+/* QPHY_PCS_LFPS_RXTERM_IRQ_STATUS register bits */
+#define RCVR_DETECT BIT(0)
+
+/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */
+#define CLAMP_EN BIT(0) /* enables i/o clamp_n */
+
+#define PHY_INIT_COMPLETE_TIMEOUT 10000
+#define POWER_DOWN_DELAY_US_MIN 10
+#define POWER_DOWN_DELAY_US_MAX 11
+
+struct qmp_phy_init_tbl {
+ unsigned int offset;
+ unsigned int val;
+ /*
+ * register part of layout ?
+ * if yes, then offset gives index in the reg-layout
+ */
+ bool in_layout;
+ /*
+ * mask of lanes for which this register is written
+ * for cases when second lane needs different values
+ */
+ u8 lane_mask;
+};
+
+#define QMP_PHY_INIT_CFG(o, v) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .lane_mask = 0xff, \
+ }
+
+#define QMP_PHY_INIT_CFG_L(o, v) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .in_layout = true, \
+ .lane_mask = 0xff, \
+ }
+
+#define QMP_PHY_INIT_CFG_LANE(o, v, l) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .lane_mask = l, \
+ }
+
+/* set of registers with offsets different per-PHY */
+enum qphy_reg_layout {
+ /* PCS registers */
+ QPHY_SW_RESET,
+ QPHY_START_CTRL,
+ QPHY_PCS_STATUS,
+ QPHY_PCS_AUTONOMOUS_MODE_CTRL,
+ QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR,
+ QPHY_PCS_LFPS_RXTERM_IRQ_STATUS,
+ QPHY_PCS_POWER_DOWN_CONTROL,
+ /* Keep last to ensure regs_layout arrays are properly initialized */
+ QPHY_LAYOUT_SIZE
+};
+
+static const unsigned int qmp_v3_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_START_CTRL] = 0x08,
+ [QPHY_PCS_STATUS] = 0x174,
+ [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d8,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0dc,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170,
+};
+
+static const unsigned int qmp_v4_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_START_CTRL] = 0x44,
+ [QPHY_PCS_STATUS] = 0x14,
+ [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40,
+
+ /* In PCS_USB */
+ [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x008,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x014,
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x37),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_CTRL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl_rbr[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x69),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x6f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x00),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl_hbr[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x69),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x00),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl_hbr2[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x8c),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x00),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl_hbr3[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x69),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x2a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x08),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_dp_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_TRANSCEIVER_BIAS_EN, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_VMODE_CTRL1, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_PRE_STALL_LDO_BOOST_EN, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_INTERFACE_SELECT, 0x3d),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_CLKBUF_ENABLE, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RESET_TSYNC_EN, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_TRAN_DRVR_EMP_EN, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_PARRATE_REC_DETECT_IDLE_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_INTERFACE_MODE, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_BAND, 0x4),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_POL_INV, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_DRV_LVL, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_EMP_POST1_LVL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x07),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_pcs_tbl[] = {
+ /* FLL settings */
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
+
+ /* Lock Det settings */
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x20),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xbf),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xbf),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x94),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb3),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_pcs_tbl[] = {
+ /* Lock Det settings */
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13),
+
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_pcs_usb_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x40, 1),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x54, 2),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff, 1),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f, 2),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f, 1),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff, 2),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x97),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_pcs_usb_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SVS_MODE_CLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_ENABLE1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_CONFIG, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BG_TIMER, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_CTRL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN, 0x17),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORE_CLK_EN, 0x1f),
+};
+
+static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_rbr[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x69),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x6f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04),
+};
+
+static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_hbr[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x69),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x08),
+};
+
+static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_hbr2[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x8c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x08),
+};
+
+static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_hbr3[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x69),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x2a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x08),
+};
+
+static const struct qmp_phy_init_tbl qmp_v4_dp_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_VMODE_CTRL1, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PRE_STALL_LDO_BOOST_EN, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_INTERFACE_SELECT, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_CLKBUF_ENABLE, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RESET_TSYNC_EN, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PARRATE_REC_DETECT_IDLE_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_INTERFACE_MODE, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_BAND, 0x4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_POL_INV, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_DRV_LVL, 0x2a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_EMP_POST1_LVL, 0x20),
+};
+
+static const struct qmp_phy_init_tbl qmp_v5_dp_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SVS_MODE_CLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_ENABLE1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_CONFIG, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BG_TIMER, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_CTRL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN, 0x17),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORE_CLK_EN, 0x1f),
+};
+
+static const struct qmp_phy_init_tbl qmp_v5_5nm_dp_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_LANE_MODE_3, 0x51),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_TRANSCEIVER_BIAS_EN, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_VMODE_CTRL1, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_PRE_STALL_LDO_BOOST_EN, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_INTERFACE_SELECT, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_CLKBUF_ENABLE, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_RESET_TSYNC_EN, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_TRAN_DRVR_EMP_EN, 0xf),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_PARRATE_REC_DETECT_IDLE_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_TX, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_RX, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_TX_BAND, 0x01),
+};
+
+static const struct qmp_phy_init_tbl sc8280xp_usb43dp_serdes_tbl[] = {
+ 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, 0xfd),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x0d),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0xfd),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x0d),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_BUF_ENABLE, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x02),
+ 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, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MSB_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MSB_MODE1, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE0, 0xd5),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE0, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE1, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE1, 0xd5),
+ 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_VCO_TUNE1_MODE0, 0xd4),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE2_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE1_MODE1, 0xd4),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE2_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x13),
+ 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_CORE_CLK_EN, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_CONFIG, 0x76),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_INTEGLOOP_GAIN0_MODE0, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_INTEGLOOP_GAIN0_MODE1, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_INITVAL2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAXVAL2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_SVS_MODE_CLK_SEL, 0x0a),
+};
+
+static const struct qmp_phy_init_tbl sc8280xp_usb43dp_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_LANE_MODE_1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_LANE_MODE_2, 0xc2),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_LANE_MODE_3, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_TX, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_RX, 0x0a),
+};
+
+static const struct qmp_phy_init_tbl sc8280xp_usb43dp_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_SIGDET_ENABLES, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B0, 0xd2),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B1, 0xd2),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B2, 0xdb),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B3, 0x21),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B4, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B5, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B6, 0x45),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B7, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B0, 0x6b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B1, 0x63),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B2, 0xb6),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B3, 0x23),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B4, 0x35),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B5, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B6, 0x8e),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B7, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_IVCM_CAL_CODE_OVERRIDE, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_IVCM_CAL_CTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_SUMMER_CAL_SPD_MODE, 0x1b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_UCDR_PI_CONTROLS, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_UCDR_SB2_GAIN2_RATE2, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_IVCM_POSTCAL_OFFSET, 0x7c),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_VGA_CAL_CNTRL1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_VGA_CAL_MAN_VAL, 0x0d),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_DFE_DAC_ENABLE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_DFE_3, 0x45),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_GM_CAL, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_UCDR_FO_GAIN_RATE2, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_UCDR_SO_GAIN_RATE2, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_Q_PI_INTRINSIC_BIAS_RATE32, 0x3f),
+};
+
+static const struct qmp_phy_init_tbl sc8280xp_usb43dp_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG1, 0xd0),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG3, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG6, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x21),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0xaa),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_CONFIG, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCS_TX_RX_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG5, 0x10),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
+};
+
+/* list of regulators */
+struct qmp_regulator_data {
+ const char *name;
+ unsigned int enable_load;
+};
+
+static struct qmp_regulator_data qmp_phy_vreg_l[] = {
+ { .name = "vdda-phy", .enable_load = 21800 },
+ { .name = "vdda-pll", .enable_load = 36000 },
+};
+
+static const u8 qmp_dp_v3_pre_emphasis_hbr3_hbr2[4][4] = {
+ { 0x00, 0x0c, 0x15, 0x1a },
+ { 0x02, 0x0e, 0x16, 0xff },
+ { 0x02, 0x11, 0xff, 0xff },
+ { 0x04, 0xff, 0xff, 0xff }
+};
+
+static const u8 qmp_dp_v3_voltage_swing_hbr3_hbr2[4][4] = {
+ { 0x02, 0x12, 0x16, 0x1a },
+ { 0x09, 0x19, 0x1f, 0xff },
+ { 0x10, 0x1f, 0xff, 0xff },
+ { 0x1f, 0xff, 0xff, 0xff }
+};
+
+static const u8 qmp_dp_v3_pre_emphasis_hbr_rbr[4][4] = {
+ { 0x00, 0x0c, 0x14, 0x19 },
+ { 0x00, 0x0b, 0x12, 0xff },
+ { 0x00, 0x0b, 0xff, 0xff },
+ { 0x04, 0xff, 0xff, 0xff }
+};
+
+static const u8 qmp_dp_v3_voltage_swing_hbr_rbr[4][4] = {
+ { 0x08, 0x0f, 0x16, 0x1f },
+ { 0x11, 0x1e, 0x1f, 0xff },
+ { 0x19, 0x1f, 0xff, 0xff },
+ { 0x1f, 0xff, 0xff, 0xff }
+};
+
+static const u8 qmp_dp_v5_pre_emphasis_hbr3_hbr2[4][4] = {
+ { 0x20, 0x2c, 0x35, 0x3b },
+ { 0x22, 0x2e, 0x36, 0xff },
+ { 0x22, 0x31, 0xff, 0xff },
+ { 0x24, 0xff, 0xff, 0xff }
+};
+
+static const u8 qmp_dp_v5_voltage_swing_hbr3_hbr2[4][4] = {
+ { 0x22, 0x32, 0x36, 0x3a },
+ { 0x29, 0x39, 0x3f, 0xff },
+ { 0x30, 0x3f, 0xff, 0xff },
+ { 0x3f, 0xff, 0xff, 0xff }
+};
+
+static const u8 qmp_dp_v5_pre_emphasis_hbr_rbr[4][4] = {
+ { 0x20, 0x2d, 0x34, 0x3a },
+ { 0x20, 0x2e, 0x35, 0xff },
+ { 0x20, 0x2e, 0xff, 0xff },
+ { 0x24, 0xff, 0xff, 0xff }
+};
+
+static const u8 qmp_dp_v5_voltage_swing_hbr_rbr[4][4] = {
+ { 0x28, 0x2f, 0x36, 0x3f },
+ { 0x31, 0x3e, 0x3f, 0xff },
+ { 0x36, 0x3f, 0xff, 0xff },
+ { 0x3f, 0xff, 0xff, 0xff }
+};
+
+struct qmp_phy;
+
+/* struct qmp_phy_cfg - per-PHY initialization config */
+struct qmp_phy_cfg {
+ /* phy-type - PCIE/UFS/USB */
+ unsigned int type;
+ int lanes;
+
+ /* Init sequence for PHY blocks - serdes, tx, rx, pcs */
+ const struct qmp_phy_init_tbl *serdes_tbl;
+ int serdes_tbl_num;
+ const struct qmp_phy_init_tbl *tx_tbl;
+ int tx_tbl_num;
+ const struct qmp_phy_init_tbl *rx_tbl;
+ int rx_tbl_num;
+ const struct qmp_phy_init_tbl *pcs_tbl;
+ int pcs_tbl_num;
+ const struct qmp_phy_init_tbl *pcs_usb_tbl;
+ int pcs_usb_tbl_num;
+
+ /* Init sequence for DP PHY block link rates */
+ const struct qmp_phy_init_tbl *serdes_tbl_rbr;
+ int serdes_tbl_rbr_num;
+ const struct qmp_phy_init_tbl *serdes_tbl_hbr;
+ int serdes_tbl_hbr_num;
+ const struct qmp_phy_init_tbl *serdes_tbl_hbr2;
+ int serdes_tbl_hbr2_num;
+ const struct qmp_phy_init_tbl *serdes_tbl_hbr3;
+ int serdes_tbl_hbr3_num;
+
+ /* DP PHY swing and pre_emphasis tables */
+ const u8 (*swing_hbr_rbr)[4][4];
+ const u8 (*swing_hbr3_hbr2)[4][4];
+ const u8 (*pre_emphasis_hbr_rbr)[4][4];
+ const u8 (*pre_emphasis_hbr3_hbr2)[4][4];
+
+ /* DP PHY callbacks */
+ int (*configure_dp_phy)(struct qmp_phy *qphy);
+ void (*configure_dp_tx)(struct qmp_phy *qphy);
+ int (*calibrate_dp_phy)(struct qmp_phy *qphy);
+ void (*dp_aux_init)(struct qmp_phy *qphy);
+
+ /* clock ids to be requested */
+ const char * const *clk_list;
+ int num_clks;
+ /* resets to be requested */
+ const char * const *reset_list;
+ int num_resets;
+ /* regulators to be requested */
+ const struct qmp_regulator_data *vreg_list;
+ int num_vregs;
+
+ /* array of registers with different offsets */
+ const unsigned int *regs;
+
+ unsigned int start_ctrl;
+ unsigned int pwrdn_ctrl;
+ /* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */
+ unsigned int phy_status;
+
+ /* true, if PHY needs delay after POWER_DOWN */
+ bool has_pwrdn_delay;
+ /* power_down delay in usec */
+ int pwrdn_delay_min;
+ int pwrdn_delay_max;
+
+ /* Offset from PCS to PCS_USB region */
+ unsigned int pcs_usb_offset;
+
+};
+
+struct qmp_phy_combo_cfg {
+ const struct qmp_phy_cfg *usb_cfg;
+ const struct qmp_phy_cfg *dp_cfg;
+};
+
+/**
+ * struct qmp_phy - per-lane phy descriptor
+ *
+ * @phy: generic phy
+ * @cfg: phy specific configuration
+ * @serdes: iomapped memory space for phy's serdes (i.e. PLL)
+ * @tx: iomapped memory space for lane's tx
+ * @rx: iomapped memory space for lane's rx
+ * @pcs: iomapped memory space for lane's pcs
+ * @tx2: iomapped memory space for second lane's tx (in dual lane PHYs)
+ * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs)
+ * @pcs_misc: iomapped memory space for lane's pcs_misc
+ * @pcs_usb: iomapped memory space for lane's pcs_usb
+ * @pipe_clk: pipe clock
+ * @qmp: QMP phy to which this lane belongs
+ * @mode: current PHY mode
+ * @dp_aux_cfg: Display port aux config
+ * @dp_opts: Display port optional config
+ * @dp_clks: Display port clocks
+ */
+struct qmp_phy {
+ struct phy *phy;
+ const struct qmp_phy_cfg *cfg;
+ void __iomem *serdes;
+ void __iomem *tx;
+ void __iomem *rx;
+ void __iomem *pcs;
+ void __iomem *tx2;
+ void __iomem *rx2;
+ void __iomem *pcs_misc;
+ void __iomem *pcs_usb;
+ struct clk *pipe_clk;
+ struct qcom_qmp *qmp;
+ enum phy_mode mode;
+ unsigned int dp_aux_cfg;
+ struct phy_configure_opts_dp dp_opts;
+ struct qmp_phy_dp_clks *dp_clks;
+};
+
+struct qmp_phy_dp_clks {
+ struct qmp_phy *qphy;
+ struct clk_hw dp_link_hw;
+ struct clk_hw dp_pixel_hw;
+};
+
+/**
+ * struct qcom_qmp - structure holding QMP phy block attributes
+ *
+ * @dev: device
+ * @dp_com: iomapped memory space for phy's dp_com control block
+ *
+ * @clks: array of clocks required by phy
+ * @resets: array of resets required by phy
+ * @vregs: regulator supplies bulk data
+ *
+ * @phys: array of per-lane phy descriptors
+ * @phy_mutex: mutex lock for PHY common block initialization
+ * @init_count: phy common block initialization count
+ * @ufs_reset: optional UFS PHY reset handle
+ */
+struct qcom_qmp {
+ struct device *dev;
+ void __iomem *dp_com;
+
+ struct clk_bulk_data *clks;
+ struct reset_control_bulk_data *resets;
+ struct regulator_bulk_data *vregs;
+
+ struct qmp_phy **phys;
+
+ struct mutex phy_mutex;
+ int init_count;
+
+ struct reset_control *ufs_reset;
+};
+
+static void qcom_qmp_v3_phy_dp_aux_init(struct qmp_phy *qphy);
+static void qcom_qmp_v3_phy_configure_dp_tx(struct qmp_phy *qphy);
+static int qcom_qmp_v3_phy_configure_dp_phy(struct qmp_phy *qphy);
+static int qcom_qmp_v3_dp_phy_calibrate(struct qmp_phy *qphy);
+
+static void qcom_qmp_v4_phy_dp_aux_init(struct qmp_phy *qphy);
+static void qcom_qmp_v4_phy_configure_dp_tx(struct qmp_phy *qphy);
+static int qcom_qmp_v4_phy_configure_dp_phy(struct qmp_phy *qphy);
+static int qcom_qmp_v4_dp_phy_calibrate(struct qmp_phy *qphy);
+
+static int qcom_qmp_v5_phy_configure_dp_phy(struct qmp_phy *qphy);
+
+static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg |= val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg &= ~val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+/* list of clocks required by phy */
+static const char * const qmp_v3_phy_clk_l[] = {
+ "aux", "cfg_ahb", "ref", "com_aux",
+};
+
+static const char * const qmp_v4_phy_clk_l[] = {
+ "aux", "ref_clk_src", "ref", "com_aux",
+};
+
+/* the primary usb3 phy on sm8250 doesn't have a ref clock */
+static const char * const qmp_v4_sm8250_usbphy_clk_l[] = {
+ "aux", "ref_clk_src", "com_aux"
+};
+
+/* list of resets */
+static const char * const msm8996_usb3phy_reset_l[] = {
+ "phy", "common",
+};
+
+static const char * const sc7180_usb3phy_reset_l[] = {
+ "phy",
+};
+
+static const struct qmp_phy_cfg sc7180_usb3phy_cfg = {
+ .type = PHY_TYPE_USB3,
+ .lanes = 2,
+
+ .serdes_tbl = qmp_v3_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl),
+ .tx_tbl = qmp_v3_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl),
+ .rx_tbl = qmp_v3_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl),
+ .pcs_tbl = qmp_v3_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl),
+ .clk_list = qmp_v3_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
+ .reset_list = sc7180_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v3_usb3phy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static const struct qmp_phy_cfg sc7180_dpphy_cfg = {
+ .type = PHY_TYPE_DP,
+ .lanes = 2,
+
+ .serdes_tbl = qmp_v3_dp_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl),
+ .tx_tbl = qmp_v3_dp_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(qmp_v3_dp_tx_tbl),
+
+ .serdes_tbl_rbr = qmp_v3_dp_serdes_tbl_rbr,
+ .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl_rbr),
+ .serdes_tbl_hbr = qmp_v3_dp_serdes_tbl_hbr,
+ .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl_hbr),
+ .serdes_tbl_hbr2 = qmp_v3_dp_serdes_tbl_hbr2,
+ .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl_hbr2),
+ .serdes_tbl_hbr3 = qmp_v3_dp_serdes_tbl_hbr3,
+ .serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl_hbr3),
+
+ .swing_hbr_rbr = &qmp_dp_v3_voltage_swing_hbr_rbr,
+ .pre_emphasis_hbr_rbr = &qmp_dp_v3_pre_emphasis_hbr_rbr,
+ .swing_hbr3_hbr2 = &qmp_dp_v3_voltage_swing_hbr3_hbr2,
+ .pre_emphasis_hbr3_hbr2 = &qmp_dp_v3_pre_emphasis_hbr3_hbr2,
+
+ .clk_list = qmp_v3_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
+ .reset_list = sc7180_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v3_usb3phy_regs_layout,
+
+ .dp_aux_init = qcom_qmp_v3_phy_dp_aux_init,
+ .configure_dp_tx = qcom_qmp_v3_phy_configure_dp_tx,
+ .configure_dp_phy = qcom_qmp_v3_phy_configure_dp_phy,
+ .calibrate_dp_phy = qcom_qmp_v3_dp_phy_calibrate,
+};
+
+static const struct qmp_phy_combo_cfg sc7180_usb3dpphy_cfg = {
+ .usb_cfg = &sc7180_usb3phy_cfg,
+ .dp_cfg = &sc7180_dpphy_cfg,
+};
+
+static const struct qmp_phy_cfg sdm845_usb3phy_cfg = {
+ .type = PHY_TYPE_USB3,
+ .lanes = 2,
+
+ .serdes_tbl = qmp_v3_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl),
+ .tx_tbl = qmp_v3_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl),
+ .rx_tbl = qmp_v3_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl),
+ .pcs_tbl = qmp_v3_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl),
+ .clk_list = qmp_v3_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v3_usb3phy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static const struct qmp_phy_combo_cfg sdm845_usb3dpphy_cfg = {
+ .usb_cfg = &sdm845_usb3phy_cfg,
+ .dp_cfg = &sc7180_dpphy_cfg,
+};
+
+static const struct qmp_phy_cfg sm8150_usb3phy_cfg = {
+ .type = PHY_TYPE_USB3,
+ .lanes = 2,
+
+ .serdes_tbl = sm8150_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl),
+ .tx_tbl = sm8150_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm8150_usb3_tx_tbl),
+ .rx_tbl = sm8150_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8150_usb3_rx_tbl),
+ .pcs_tbl = sm8150_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_tbl),
+ .pcs_usb_tbl = sm8150_usb3_pcs_usb_tbl,
+ .pcs_usb_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_usb_tbl),
+ .clk_list = qmp_v4_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+ .pcs_usb_offset = 0x300,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static const struct qmp_phy_cfg sc8180x_dpphy_cfg = {
+ .type = PHY_TYPE_DP,
+ .lanes = 2,
+
+ .serdes_tbl = qmp_v4_dp_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl),
+ .tx_tbl = qmp_v4_dp_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(qmp_v4_dp_tx_tbl),
+
+ .serdes_tbl_rbr = qmp_v4_dp_serdes_tbl_rbr,
+ .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_rbr),
+ .serdes_tbl_hbr = qmp_v4_dp_serdes_tbl_hbr,
+ .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr),
+ .serdes_tbl_hbr2 = qmp_v4_dp_serdes_tbl_hbr2,
+ .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr2),
+ .serdes_tbl_hbr3 = qmp_v4_dp_serdes_tbl_hbr3,
+ .serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr3),
+
+ .swing_hbr_rbr = &qmp_dp_v3_voltage_swing_hbr_rbr,
+ .pre_emphasis_hbr_rbr = &qmp_dp_v3_pre_emphasis_hbr_rbr,
+ .swing_hbr3_hbr2 = &qmp_dp_v3_voltage_swing_hbr3_hbr2,
+ .pre_emphasis_hbr3_hbr2 = &qmp_dp_v3_pre_emphasis_hbr3_hbr2,
+
+ .clk_list = qmp_v3_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
+ .reset_list = sc7180_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v3_usb3phy_regs_layout,
+
+ .dp_aux_init = qcom_qmp_v4_phy_dp_aux_init,
+ .configure_dp_tx = qcom_qmp_v4_phy_configure_dp_tx,
+ .configure_dp_phy = qcom_qmp_v4_phy_configure_dp_phy,
+ .calibrate_dp_phy = qcom_qmp_v4_dp_phy_calibrate,
+};
+
+static const struct qmp_phy_combo_cfg sc8180x_usb3dpphy_cfg = {
+ .usb_cfg = &sm8150_usb3phy_cfg,
+ .dp_cfg = &sc8180x_dpphy_cfg,
+};
+
+static const struct qmp_phy_cfg sc8280xp_usb43dp_usb_cfg = {
+ .type = PHY_TYPE_USB3,
+ .lanes = 2,
+
+ .serdes_tbl = sc8280xp_usb43dp_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sc8280xp_usb43dp_serdes_tbl),
+ .tx_tbl = sc8280xp_usb43dp_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sc8280xp_usb43dp_tx_tbl),
+ .rx_tbl = sc8280xp_usb43dp_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sc8280xp_usb43dp_rx_tbl),
+ .pcs_tbl = sc8280xp_usb43dp_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sc8280xp_usb43dp_pcs_tbl),
+ .clk_list = qmp_v4_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+ .pcs_usb_offset = 0x300,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static const struct qmp_phy_cfg sc8280xp_usb43dp_dp_cfg = {
+ .type = PHY_TYPE_DP,
+ .lanes = 2,
+
+ .serdes_tbl = qmp_v5_dp_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(qmp_v5_dp_serdes_tbl),
+ .tx_tbl = qmp_v5_5nm_dp_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(qmp_v5_5nm_dp_tx_tbl),
+
+ .serdes_tbl_rbr = qmp_v4_dp_serdes_tbl_rbr,
+ .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_rbr),
+ .serdes_tbl_hbr = qmp_v4_dp_serdes_tbl_hbr,
+ .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr),
+ .serdes_tbl_hbr2 = qmp_v4_dp_serdes_tbl_hbr2,
+ .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr2),
+ .serdes_tbl_hbr3 = qmp_v4_dp_serdes_tbl_hbr3,
+ .serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr3),
+
+ .swing_hbr_rbr = &qmp_dp_v5_voltage_swing_hbr_rbr,
+ .pre_emphasis_hbr_rbr = &qmp_dp_v5_pre_emphasis_hbr_rbr,
+ .swing_hbr3_hbr2 = &qmp_dp_v5_voltage_swing_hbr3_hbr2,
+ .pre_emphasis_hbr3_hbr2 = &qmp_dp_v5_pre_emphasis_hbr3_hbr2,
+
+ .clk_list = qmp_v4_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+
+ .dp_aux_init = qcom_qmp_v4_phy_dp_aux_init,
+ .configure_dp_tx = qcom_qmp_v4_phy_configure_dp_tx,
+ .configure_dp_phy = qcom_qmp_v5_phy_configure_dp_phy,
+ .calibrate_dp_phy = qcom_qmp_v4_dp_phy_calibrate,
+};
+
+static const struct qmp_phy_combo_cfg sc8280xp_usb43dpphy_combo_cfg = {
+ .usb_cfg = &sc8280xp_usb43dp_usb_cfg,
+ .dp_cfg = &sc8280xp_usb43dp_dp_cfg,
+};
+
+static const struct qmp_phy_cfg sm8250_usb3phy_cfg = {
+ .type = PHY_TYPE_USB3,
+ .lanes = 2,
+
+ .serdes_tbl = sm8150_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl),
+ .tx_tbl = sm8250_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_tx_tbl),
+ .rx_tbl = sm8250_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_rx_tbl),
+ .pcs_tbl = sm8250_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_tbl),
+ .pcs_usb_tbl = sm8250_usb3_pcs_usb_tbl,
+ .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_usb_tbl),
+ .clk_list = qmp_v4_sm8250_usbphy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+ .pcs_usb_offset = 0x300,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static const struct qmp_phy_cfg sm8250_dpphy_cfg = {
+ .type = PHY_TYPE_DP,
+ .lanes = 2,
+
+ .serdes_tbl = qmp_v4_dp_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl),
+ .tx_tbl = qmp_v4_dp_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(qmp_v4_dp_tx_tbl),
+
+ .serdes_tbl_rbr = qmp_v4_dp_serdes_tbl_rbr,
+ .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_rbr),
+ .serdes_tbl_hbr = qmp_v4_dp_serdes_tbl_hbr,
+ .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr),
+ .serdes_tbl_hbr2 = qmp_v4_dp_serdes_tbl_hbr2,
+ .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr2),
+ .serdes_tbl_hbr3 = qmp_v4_dp_serdes_tbl_hbr3,
+ .serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr3),
+
+ .swing_hbr_rbr = &qmp_dp_v3_voltage_swing_hbr_rbr,
+ .pre_emphasis_hbr_rbr = &qmp_dp_v3_pre_emphasis_hbr_rbr,
+ .swing_hbr3_hbr2 = &qmp_dp_v3_voltage_swing_hbr3_hbr2,
+ .pre_emphasis_hbr3_hbr2 = &qmp_dp_v3_pre_emphasis_hbr3_hbr2,
+
+ .clk_list = qmp_v4_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+
+ .dp_aux_init = qcom_qmp_v4_phy_dp_aux_init,
+ .configure_dp_tx = qcom_qmp_v4_phy_configure_dp_tx,
+ .configure_dp_phy = qcom_qmp_v4_phy_configure_dp_phy,
+ .calibrate_dp_phy = qcom_qmp_v4_dp_phy_calibrate,
+};
+
+static const struct qmp_phy_combo_cfg sm8250_usb3dpphy_cfg = {
+ .usb_cfg = &sm8250_usb3phy_cfg,
+ .dp_cfg = &sm8250_dpphy_cfg,
+};
+
+static void qmp_combo_configure_lane(void __iomem *base,
+ const unsigned int *regs,
+ const struct qmp_phy_init_tbl tbl[],
+ int num,
+ u8 lane_mask)
+{
+ int i;
+ const struct qmp_phy_init_tbl *t = tbl;
+
+ if (!t)
+ return;
+
+ for (i = 0; i < num; i++, t++) {
+ if (!(t->lane_mask & lane_mask))
+ continue;
+
+ if (t->in_layout)
+ writel(t->val, base + regs[t->offset]);
+ else
+ writel(t->val, base + t->offset);
+ }
+}
+
+static void qmp_combo_configure(void __iomem *base,
+ const unsigned int *regs,
+ const struct qmp_phy_init_tbl tbl[],
+ int num)
+{
+ qmp_combo_configure_lane(base, regs, tbl, num, 0xff);
+}
+
+static int qmp_combo_serdes_init(struct qmp_phy *qphy)
+{
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *serdes = qphy->serdes;
+ const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
+ const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl;
+ int serdes_tbl_num = cfg->serdes_tbl_num;
+
+ qmp_combo_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num);
+
+ if (cfg->type == PHY_TYPE_DP) {
+ switch (dp_opts->link_rate) {
+ case 1620:
+ qmp_combo_configure(serdes, cfg->regs,
+ cfg->serdes_tbl_rbr,
+ cfg->serdes_tbl_rbr_num);
+ break;
+ case 2700:
+ qmp_combo_configure(serdes, cfg->regs,
+ cfg->serdes_tbl_hbr,
+ cfg->serdes_tbl_hbr_num);
+ break;
+ case 5400:
+ qmp_combo_configure(serdes, cfg->regs,
+ cfg->serdes_tbl_hbr2,
+ cfg->serdes_tbl_hbr2_num);
+ break;
+ case 8100:
+ qmp_combo_configure(serdes, cfg->regs,
+ cfg->serdes_tbl_hbr3,
+ cfg->serdes_tbl_hbr3_num);
+ break;
+ default:
+ /* Other link rates aren't supported */
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void qcom_qmp_v3_phy_dp_aux_init(struct qmp_phy *qphy)
+{
+ writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
+ DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
+ qphy->pcs + QSERDES_DP_PHY_PD_CTL);
+
+ /* Turn on BIAS current for PHY/PLL */
+ writel(QSERDES_V3_COM_BIAS_EN | QSERDES_V3_COM_BIAS_EN_MUX |
+ QSERDES_V3_COM_CLKBUF_L_EN | QSERDES_V3_COM_EN_SYSCLK_TX_SEL,
+ qphy->serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN);
+
+ writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_DP_PHY_PD_CTL);
+
+ writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
+ DP_PHY_PD_CTL_LANE_0_1_PWRDN |
+ DP_PHY_PD_CTL_LANE_2_3_PWRDN | DP_PHY_PD_CTL_PLL_PWRDN |
+ DP_PHY_PD_CTL_DP_CLAMP_EN,
+ qphy->pcs + QSERDES_DP_PHY_PD_CTL);
+
+ writel(QSERDES_V3_COM_BIAS_EN |
+ QSERDES_V3_COM_BIAS_EN_MUX | QSERDES_V3_COM_CLKBUF_R_EN |
+ QSERDES_V3_COM_CLKBUF_L_EN | QSERDES_V3_COM_EN_SYSCLK_TX_SEL |
+ QSERDES_V3_COM_CLKBUF_RX_DRIVE_L,
+ qphy->serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN);
+
+ writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG0);
+ writel(0x13, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1);
+ writel(0x24, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2);
+ writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG3);
+ writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG4);
+ writel(0x26, qphy->pcs + QSERDES_DP_PHY_AUX_CFG5);
+ writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG6);
+ writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG7);
+ writel(0xbb, qphy->pcs + QSERDES_DP_PHY_AUX_CFG8);
+ writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG9);
+ qphy->dp_aux_cfg = 0;
+
+ writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK |
+ PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK |
+ PHY_AUX_REQ_ERR_MASK,
+ qphy->pcs + QSERDES_V3_DP_PHY_AUX_INTERRUPT_MASK);
+}
+
+static int qmp_combo_configure_dp_swing(struct qmp_phy *qphy,
+ unsigned int drv_lvl_reg, unsigned int emp_post_reg)
+{
+ const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ unsigned int v_level = 0, p_level = 0;
+ u8 voltage_swing_cfg, pre_emphasis_cfg;
+ int i;
+
+ for (i = 0; i < dp_opts->lanes; i++) {
+ v_level = max(v_level, dp_opts->voltage[i]);
+ p_level = max(p_level, dp_opts->pre[i]);
+ }
+
+ if (dp_opts->link_rate <= 2700) {
+ voltage_swing_cfg = (*cfg->swing_hbr_rbr)[v_level][p_level];
+ pre_emphasis_cfg = (*cfg->pre_emphasis_hbr_rbr)[v_level][p_level];
+ } else {
+ voltage_swing_cfg = (*cfg->swing_hbr3_hbr2)[v_level][p_level];
+ pre_emphasis_cfg = (*cfg->pre_emphasis_hbr3_hbr2)[v_level][p_level];
+ }
+
+ /* TODO: Move check to config check */
+ if (voltage_swing_cfg == 0xFF && pre_emphasis_cfg == 0xFF)
+ return -EINVAL;
+
+ /* Enable MUX to use Cursor values from these registers */
+ voltage_swing_cfg |= DP_PHY_TXn_TX_DRV_LVL_MUX_EN;
+ pre_emphasis_cfg |= DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN;
+
+ writel(voltage_swing_cfg, qphy->tx + drv_lvl_reg);
+ writel(pre_emphasis_cfg, qphy->tx + emp_post_reg);
+ writel(voltage_swing_cfg, qphy->tx2 + drv_lvl_reg);
+ writel(pre_emphasis_cfg, qphy->tx2 + emp_post_reg);
+
+ return 0;
+}
+
+static void qcom_qmp_v3_phy_configure_dp_tx(struct qmp_phy *qphy)
+{
+ const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
+ u32 bias_en, drvr_en;
+
+ if (qmp_combo_configure_dp_swing(qphy, QSERDES_V3_TX_TX_DRV_LVL,
+ QSERDES_V3_TX_TX_EMP_POST1_LVL) < 0)
+ return;
+
+ if (dp_opts->lanes == 1) {
+ bias_en = 0x3e;
+ drvr_en = 0x13;
+ } else {
+ bias_en = 0x3f;
+ drvr_en = 0x10;
+ }
+
+ writel(drvr_en, qphy->tx + QSERDES_V3_TX_HIGHZ_DRVR_EN);
+ writel(bias_en, qphy->tx + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN);
+ writel(drvr_en, qphy->tx2 + QSERDES_V3_TX_HIGHZ_DRVR_EN);
+ writel(bias_en, qphy->tx2 + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN);
+}
+
+static bool qmp_combo_configure_dp_mode(struct qmp_phy *qphy)
+{
+ u32 val;
+ bool reverse = false;
+
+ val = DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
+ DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN;
+
+ /*
+ * TODO: Assume orientation is CC1 for now and two lanes, need to
+ * use type-c connector to understand orientation and lanes.
+ *
+ * Otherwise val changes to be like below if this code understood
+ * the orientation of the type-c cable.
+ *
+ * if (lane_cnt == 4 || orientation == ORIENTATION_CC2)
+ * val |= DP_PHY_PD_CTL_LANE_0_1_PWRDN;
+ * if (lane_cnt == 4 || orientation == ORIENTATION_CC1)
+ * val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN;
+ * if (orientation == ORIENTATION_CC2)
+ * writel(0x4c, qphy->pcs + QSERDES_V3_DP_PHY_MODE);
+ */
+ val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN;
+ writel(val, qphy->pcs + QSERDES_DP_PHY_PD_CTL);
+
+ writel(0x5c, qphy->pcs + QSERDES_DP_PHY_MODE);
+
+ return reverse;
+}
+
+static int qcom_qmp_v3_phy_configure_dp_phy(struct qmp_phy *qphy)
+{
+ const struct qmp_phy_dp_clks *dp_clks = qphy->dp_clks;
+ const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
+ u32 phy_vco_div, status;
+ unsigned long pixel_freq;
+
+ qmp_combo_configure_dp_mode(qphy);
+
+ writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_TX0_TX1_LANE_CTL);
+ writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_TX2_TX3_LANE_CTL);
+
+ switch (dp_opts->link_rate) {
+ case 1620:
+ phy_vco_div = 0x1;
+ pixel_freq = 1620000000UL / 2;
+ break;
+ case 2700:
+ phy_vco_div = 0x1;
+ pixel_freq = 2700000000UL / 2;
+ break;
+ case 5400:
+ phy_vco_div = 0x2;
+ pixel_freq = 5400000000UL / 4;
+ break;
+ case 8100:
+ phy_vco_div = 0x0;
+ pixel_freq = 8100000000UL / 6;
+ break;
+ default:
+ /* Other link rates aren't supported */
+ return -EINVAL;
+ }
+ writel(phy_vco_div, qphy->pcs + QSERDES_V3_DP_PHY_VCO_DIV);
+
+ clk_set_rate(dp_clks->dp_link_hw.clk, dp_opts->link_rate * 100000);
+ clk_set_rate(dp_clks->dp_pixel_hw.clk, pixel_freq);
+
+ writel(0x04, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2);
+ writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG);
+ writel(0x05, qphy->pcs + QSERDES_DP_PHY_CFG);
+ writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG);
+ writel(0x09, qphy->pcs + QSERDES_DP_PHY_CFG);
+
+ writel(0x20, qphy->serdes + QSERDES_V3_COM_RESETSM_CNTRL);
+
+ if (readl_poll_timeout(qphy->serdes + QSERDES_V3_COM_C_READY_STATUS,
+ status,
+ ((status & BIT(0)) > 0),
+ 500,
+ 10000))
+ return -ETIMEDOUT;
+
+ writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG);
+
+ if (readl_poll_timeout(qphy->pcs + QSERDES_V3_DP_PHY_STATUS,
+ status,
+ ((status & BIT(1)) > 0),
+ 500,
+ 10000))
+ return -ETIMEDOUT;
+
+ writel(0x18, qphy->pcs + QSERDES_DP_PHY_CFG);
+ udelay(2000);
+ writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG);
+
+ return readl_poll_timeout(qphy->pcs + QSERDES_V3_DP_PHY_STATUS,
+ status,
+ ((status & BIT(1)) > 0),
+ 500,
+ 10000);
+}
+
+/*
+ * We need to calibrate the aux setting here as many times
+ * as the caller tries
+ */
+static int qcom_qmp_v3_dp_phy_calibrate(struct qmp_phy *qphy)
+{
+ static const u8 cfg1_settings[] = { 0x13, 0x23, 0x1d };
+ u8 val;
+
+ qphy->dp_aux_cfg++;
+ qphy->dp_aux_cfg %= ARRAY_SIZE(cfg1_settings);
+ val = cfg1_settings[qphy->dp_aux_cfg];
+
+ writel(val, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1);
+
+ return 0;
+}
+
+static void qcom_qmp_v4_phy_dp_aux_init(struct qmp_phy *qphy)
+{
+ writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_PSR_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
+ DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
+ qphy->pcs + QSERDES_DP_PHY_PD_CTL);
+
+ /* Turn on BIAS current for PHY/PLL */
+ writel(0x17, qphy->serdes + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN);
+
+ writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG0);
+ writel(0x13, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1);
+ writel(0xa4, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2);
+ writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG3);
+ writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG4);
+ writel(0x26, qphy->pcs + QSERDES_DP_PHY_AUX_CFG5);
+ writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG6);
+ writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG7);
+ writel(0xb7, qphy->pcs + QSERDES_DP_PHY_AUX_CFG8);
+ writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG9);
+ qphy->dp_aux_cfg = 0;
+
+ writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK |
+ PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK |
+ PHY_AUX_REQ_ERR_MASK,
+ qphy->pcs + QSERDES_V4_DP_PHY_AUX_INTERRUPT_MASK);
+}
+
+static void qcom_qmp_v4_phy_configure_dp_tx(struct qmp_phy *qphy)
+{
+ /* Program default values before writing proper values */
+ writel(0x27, qphy->tx + QSERDES_V4_TX_TX_DRV_LVL);
+ writel(0x27, qphy->tx2 + QSERDES_V4_TX_TX_DRV_LVL);
+
+ writel(0x20, qphy->tx + QSERDES_V4_TX_TX_EMP_POST1_LVL);
+ writel(0x20, qphy->tx2 + QSERDES_V4_TX_TX_EMP_POST1_LVL);
+
+ qmp_combo_configure_dp_swing(qphy, QSERDES_V4_TX_TX_DRV_LVL,
+ QSERDES_V4_TX_TX_EMP_POST1_LVL);
+}
+
+static int qcom_qmp_v45_phy_configure_dp_phy(struct qmp_phy *qphy)
+{
+ const struct qmp_phy_dp_clks *dp_clks = qphy->dp_clks;
+ const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
+ u32 phy_vco_div, status;
+ unsigned long pixel_freq;
+
+ writel(0x0f, qphy->pcs + QSERDES_V4_DP_PHY_CFG_1);
+
+ qmp_combo_configure_dp_mode(qphy);
+
+ writel(0x13, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1);
+ writel(0xa4, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2);
+
+ writel(0x05, qphy->pcs + QSERDES_V4_DP_PHY_TX0_TX1_LANE_CTL);
+ writel(0x05, qphy->pcs + QSERDES_V4_DP_PHY_TX2_TX3_LANE_CTL);
+
+ switch (dp_opts->link_rate) {
+ case 1620:
+ phy_vco_div = 0x1;
+ pixel_freq = 1620000000UL / 2;
+ break;
+ case 2700:
+ phy_vco_div = 0x1;
+ pixel_freq = 2700000000UL / 2;
+ break;
+ case 5400:
+ phy_vco_div = 0x2;
+ pixel_freq = 5400000000UL / 4;
+ break;
+ case 8100:
+ phy_vco_div = 0x0;
+ pixel_freq = 8100000000UL / 6;
+ break;
+ default:
+ /* Other link rates aren't supported */
+ return -EINVAL;
+ }
+ writel(phy_vco_div, qphy->pcs + QSERDES_V4_DP_PHY_VCO_DIV);
+
+ clk_set_rate(dp_clks->dp_link_hw.clk, dp_opts->link_rate * 100000);
+ clk_set_rate(dp_clks->dp_pixel_hw.clk, pixel_freq);
+
+ writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG);
+ writel(0x05, qphy->pcs + QSERDES_DP_PHY_CFG);
+ writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG);
+ writel(0x09, qphy->pcs + QSERDES_DP_PHY_CFG);
+
+ writel(0x20, qphy->serdes + QSERDES_V4_COM_RESETSM_CNTRL);
+
+ if (readl_poll_timeout(qphy->serdes + QSERDES_V4_COM_C_READY_STATUS,
+ status,
+ ((status & BIT(0)) > 0),
+ 500,
+ 10000))
+ return -ETIMEDOUT;
+
+ if (readl_poll_timeout(qphy->serdes + QSERDES_V4_COM_CMN_STATUS,
+ status,
+ ((status & BIT(0)) > 0),
+ 500,
+ 10000))
+ return -ETIMEDOUT;
+
+ if (readl_poll_timeout(qphy->serdes + QSERDES_V4_COM_CMN_STATUS,
+ status,
+ ((status & BIT(1)) > 0),
+ 500,
+ 10000))
+ return -ETIMEDOUT;
+
+ writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG);
+
+ if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS,
+ status,
+ ((status & BIT(0)) > 0),
+ 500,
+ 10000))
+ return -ETIMEDOUT;
+
+ if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS,
+ status,
+ ((status & BIT(1)) > 0),
+ 500,
+ 10000))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int qcom_qmp_v4_phy_configure_dp_phy(struct qmp_phy *qphy)
+{
+ const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
+ u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
+ bool reverse = false;
+ u32 status;
+ int ret;
+
+ ret = qcom_qmp_v45_phy_configure_dp_phy(qphy);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * At least for 7nm DP PHY this has to be done after enabling link
+ * clock.
+ */
+
+ if (dp_opts->lanes == 1) {
+ bias0_en = reverse ? 0x3e : 0x15;
+ bias1_en = reverse ? 0x15 : 0x3e;
+ drvr0_en = reverse ? 0x13 : 0x10;
+ drvr1_en = reverse ? 0x10 : 0x13;
+ } else if (dp_opts->lanes == 2) {
+ bias0_en = reverse ? 0x3f : 0x15;
+ bias1_en = reverse ? 0x15 : 0x3f;
+ drvr0_en = 0x10;
+ drvr1_en = 0x10;
+ } else {
+ bias0_en = 0x3f;
+ bias1_en = 0x3f;
+ drvr0_en = 0x10;
+ drvr1_en = 0x10;
+ }
+
+ writel(drvr0_en, qphy->tx + QSERDES_V4_TX_HIGHZ_DRVR_EN);
+ writel(bias0_en, qphy->tx + QSERDES_V4_TX_TRANSCEIVER_BIAS_EN);
+ writel(drvr1_en, qphy->tx2 + QSERDES_V4_TX_HIGHZ_DRVR_EN);
+ writel(bias1_en, qphy->tx2 + QSERDES_V4_TX_TRANSCEIVER_BIAS_EN);
+
+ writel(0x18, qphy->pcs + QSERDES_DP_PHY_CFG);
+ udelay(2000);
+ writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG);
+
+ if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS,
+ status,
+ ((status & BIT(1)) > 0),
+ 500,
+ 10000))
+ return -ETIMEDOUT;
+
+ writel(0x0a, qphy->tx + QSERDES_V4_TX_TX_POL_INV);
+ writel(0x0a, qphy->tx2 + QSERDES_V4_TX_TX_POL_INV);
+
+ writel(0x27, qphy->tx + QSERDES_V4_TX_TX_DRV_LVL);
+ writel(0x27, qphy->tx2 + QSERDES_V4_TX_TX_DRV_LVL);
+
+ writel(0x20, qphy->tx + QSERDES_V4_TX_TX_EMP_POST1_LVL);
+ writel(0x20, qphy->tx2 + QSERDES_V4_TX_TX_EMP_POST1_LVL);
+
+ return 0;
+}
+
+static int qcom_qmp_v5_phy_configure_dp_phy(struct qmp_phy *qphy)
+{
+ const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
+ u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
+ bool reverse = false;
+ u32 status;
+ int ret;
+
+ ret = qcom_qmp_v45_phy_configure_dp_phy(qphy);
+ if (ret < 0)
+ return ret;
+
+ if (dp_opts->lanes == 1) {
+ bias0_en = reverse ? 0x3e : 0x1a;
+ drvr0_en = reverse ? 0x13 : 0x10;
+ bias1_en = reverse ? 0x15 : 0x3e;
+ drvr1_en = reverse ? 0x10 : 0x13;
+ } else if (dp_opts->lanes == 2) {
+ bias0_en = reverse ? 0x3f : 0x15;
+ drvr0_en = 0x10;
+ bias1_en = reverse ? 0x15 : 0x3f;
+ drvr1_en = 0x10;
+ } else {
+ bias0_en = 0x3f;
+ bias1_en = 0x3f;
+ drvr0_en = 0x10;
+ drvr1_en = 0x10;
+ }
+
+ writel(drvr0_en, qphy->tx + QSERDES_V5_5NM_TX_HIGHZ_DRVR_EN);
+ writel(bias0_en, qphy->tx + QSERDES_V5_5NM_TX_TRANSCEIVER_BIAS_EN);
+ writel(drvr1_en, qphy->tx2 + QSERDES_V5_5NM_TX_HIGHZ_DRVR_EN);
+ writel(bias1_en, qphy->tx2 + QSERDES_V5_5NM_TX_TRANSCEIVER_BIAS_EN);
+
+ writel(0x18, qphy->pcs + QSERDES_DP_PHY_CFG);
+ udelay(2000);
+ writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG);
+
+ if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS,
+ status,
+ ((status & BIT(1)) > 0),
+ 500,
+ 10000))
+ return -ETIMEDOUT;
+
+ writel(0x0a, qphy->tx + QSERDES_V5_5NM_TX_TX_POL_INV);
+ writel(0x0a, qphy->tx2 + QSERDES_V5_5NM_TX_TX_POL_INV);
+
+ writel(0x27, qphy->tx + QSERDES_V5_5NM_TX_TX_DRV_LVL);
+ writel(0x27, qphy->tx2 + QSERDES_V5_5NM_TX_TX_DRV_LVL);
+
+ writel(0x20, qphy->tx + QSERDES_V5_5NM_TX_TX_EMP_POST1_LVL);
+ writel(0x20, qphy->tx2 + QSERDES_V5_5NM_TX_TX_EMP_POST1_LVL);
+
+ return 0;
+}
+
+/*
+ * We need to calibrate the aux setting here as many times
+ * as the caller tries
+ */
+static int qcom_qmp_v4_dp_phy_calibrate(struct qmp_phy *qphy)
+{
+ static const u8 cfg1_settings[] = { 0x20, 0x13, 0x23, 0x1d };
+ u8 val;
+
+ qphy->dp_aux_cfg++;
+ qphy->dp_aux_cfg %= ARRAY_SIZE(cfg1_settings);
+ val = cfg1_settings[qphy->dp_aux_cfg];
+
+ writel(val, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1);
+
+ return 0;
+}
+
+static int qcom_qmp_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+ const struct phy_configure_opts_dp *dp_opts = &opts->dp;
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ memcpy(&qphy->dp_opts, dp_opts, sizeof(*dp_opts));
+ if (qphy->dp_opts.set_voltages) {
+ cfg->configure_dp_tx(qphy);
+ qphy->dp_opts.set_voltages = 0;
+ }
+
+ return 0;
+}
+
+static int qcom_qmp_dp_phy_calibrate(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ if (cfg->calibrate_dp_phy)
+ return cfg->calibrate_dp_phy(qphy);
+
+ return 0;
+}
+
+static int qmp_combo_com_init(struct qmp_phy *qphy)
+{
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *pcs = qphy->pcs;
+ void __iomem *dp_com = qmp->dp_com;
+ int ret;
+
+ mutex_lock(&qmp->phy_mutex);
+ if (qmp->init_count++) {
+ mutex_unlock(&qmp->phy_mutex);
+ return 0;
+ }
+
+ /* turn on regulator supplies */
+ ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
+ if (ret) {
+ dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
+ goto err_unlock;
+ }
+
+ ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+ if (ret) {
+ dev_err(qmp->dev, "reset assert failed\n");
+ goto err_disable_regulators;
+ }
+
+ ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets);
+ if (ret) {
+ dev_err(qmp->dev, "reset deassert failed\n");
+ goto err_disable_regulators;
+ }
+
+ ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
+ if (ret)
+ goto err_assert_reset;
+
+ qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, SW_PWRDN);
+
+ /* override hardware control for reset of qmp phy */
+ qphy_setbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
+ SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
+ SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
+
+ /* Default type-c orientation, i.e CC1 */
+ qphy_setbits(dp_com, QPHY_V3_DP_COM_TYPEC_CTRL, 0x02);
+
+ qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL, USB3_MODE | DP_MODE);
+
+ /* bring both QMP USB and QMP DP PHYs PCS block out of reset */
+ qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
+ SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
+ SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
+
+ qphy_clrbits(dp_com, QPHY_V3_DP_COM_SWI_CTRL, 0x03);
+ qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
+
+ if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL])
+ qphy_setbits(pcs,
+ cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ cfg->pwrdn_ctrl);
+ else
+ qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
+ cfg->pwrdn_ctrl);
+
+ mutex_unlock(&qmp->phy_mutex);
+
+ return 0;
+
+err_assert_reset:
+ reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+err_disable_regulators:
+ regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+err_unlock:
+ mutex_unlock(&qmp->phy_mutex);
+
+ return ret;
+}
+
+static int qmp_combo_com_exit(struct qmp_phy *qphy)
+{
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ mutex_lock(&qmp->phy_mutex);
+ if (--qmp->init_count) {
+ mutex_unlock(&qmp->phy_mutex);
+ return 0;
+ }
+
+ reset_control_assert(qmp->ufs_reset);
+
+ reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+
+ regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+
+ mutex_unlock(&qmp->phy_mutex);
+
+ return 0;
+}
+
+static int qmp_combo_init(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ int ret;
+ dev_vdbg(qmp->dev, "Initializing QMP phy\n");
+
+ ret = qmp_combo_com_init(qphy);
+ if (ret)
+ return ret;
+
+ if (cfg->type == PHY_TYPE_DP)
+ cfg->dp_aux_init(qphy);
+
+ return 0;
+}
+
+static int qmp_combo_power_on(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *tx = qphy->tx;
+ void __iomem *rx = qphy->rx;
+ void __iomem *pcs = qphy->pcs;
+ void __iomem *status;
+ unsigned int mask, val, ready;
+ int ret;
+
+ qmp_combo_serdes_init(qphy);
+
+ ret = clk_prepare_enable(qphy->pipe_clk);
+ if (ret) {
+ dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret);
+ return ret;
+ }
+
+ /* Tx, Rx, and PCS configurations */
+ qmp_combo_configure_lane(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num, 1);
+
+ if (cfg->lanes >= 2) {
+ qmp_combo_configure_lane(qphy->tx2, cfg->regs, cfg->tx_tbl,
+ cfg->tx_tbl_num, 2);
+ }
+
+ /* Configure special DP tx tunings */
+ if (cfg->type == PHY_TYPE_DP)
+ cfg->configure_dp_tx(qphy);
+
+ qmp_combo_configure_lane(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 1);
+
+ if (cfg->lanes >= 2) {
+ qmp_combo_configure_lane(qphy->rx2, cfg->regs, cfg->rx_tbl,
+ cfg->rx_tbl_num, 2);
+ }
+
+ /* Configure link rate, swing, etc. */
+ if (cfg->type == PHY_TYPE_DP)
+ cfg->configure_dp_phy(qphy);
+ else
+ qmp_combo_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
+
+ ret = reset_control_deassert(qmp->ufs_reset);
+ if (ret)
+ goto err_disable_pipe_clk;
+
+ if (cfg->has_pwrdn_delay)
+ usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
+
+ if (cfg->type != PHY_TYPE_DP) {
+ /* Pull PHY out of reset state */
+ qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+ /* start SerDes and Phy-Coding-Sublayer */
+ qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
+
+ status = pcs + cfg->regs[QPHY_PCS_STATUS];
+ mask = cfg->phy_status;
+ ready = 0;
+
+ ret = readl_poll_timeout(status, val, (val & mask) == ready, 10,
+ PHY_INIT_COMPLETE_TIMEOUT);
+ if (ret) {
+ dev_err(qmp->dev, "phy initialization timed-out\n");
+ goto err_disable_pipe_clk;
+ }
+ }
+ return 0;
+
+err_disable_pipe_clk:
+ clk_disable_unprepare(qphy->pipe_clk);
+
+ return ret;
+}
+
+static int qmp_combo_power_off(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ clk_disable_unprepare(qphy->pipe_clk);
+
+ if (cfg->type == PHY_TYPE_DP) {
+ /* Assert DP PHY power down */
+ writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_DP_PHY_PD_CTL);
+ } else {
+ /* PHY reset */
+ qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+
+ /* stop SerDes and Phy-Coding-Sublayer */
+ qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
+
+ /* Put PHY into POWER DOWN state: active low */
+ if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) {
+ qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ cfg->pwrdn_ctrl);
+ } else {
+ qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
+ cfg->pwrdn_ctrl);
+ }
+ }
+
+ return 0;
+}
+
+static int qmp_combo_exit(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+
+ qmp_combo_com_exit(qphy);
+
+ return 0;
+}
+
+static int qmp_combo_enable(struct phy *phy)
+{
+ int ret;
+
+ ret = qmp_combo_init(phy);
+ if (ret)
+ return ret;
+
+ ret = qmp_combo_power_on(phy);
+ if (ret)
+ qmp_combo_exit(phy);
+
+ return ret;
+}
+
+static int qmp_combo_disable(struct phy *phy)
+{
+ int ret;
+
+ ret = qmp_combo_power_off(phy);
+ if (ret)
+ return ret;
+ return qmp_combo_exit(phy);
+}
+
+static int qmp_combo_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+
+ qphy->mode = mode;
+
+ return 0;
+}
+
+static void qmp_combo_enable_autonomous_mode(struct qmp_phy *qphy)
+{
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *pcs_usb = qphy->pcs_usb ?: qphy->pcs;
+ void __iomem *pcs_misc = qphy->pcs_misc;
+ u32 intr_mask;
+
+ if (qphy->mode == PHY_MODE_USB_HOST_SS ||
+ qphy->mode == PHY_MODE_USB_DEVICE_SS)
+ intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN;
+ else
+ intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL;
+
+ /* Clear any pending interrupts status */
+ qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+ /* Writing 1 followed by 0 clears the interrupt */
+ qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+
+ qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
+ ARCVR_DTCT_EN | ALFPS_DTCT_EN | ARCVR_DTCT_EVENT_SEL);
+
+ /* Enable required PHY autonomous mode interrupts */
+ qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], intr_mask);
+
+ /* Enable i/o clamp_n for autonomous mode */
+ if (pcs_misc)
+ qphy_clrbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN);
+}
+
+static void qmp_combo_disable_autonomous_mode(struct qmp_phy *qphy)
+{
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *pcs_usb = qphy->pcs_usb ?: qphy->pcs;
+ void __iomem *pcs_misc = qphy->pcs_misc;
+
+ /* Disable i/o clamp_n on resume for normal mode */
+ if (pcs_misc)
+ qphy_setbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN);
+
+ qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
+ ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL | ALFPS_DTCT_EN);
+
+ qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+ /* Writing 1 followed by 0 clears the interrupt */
+ qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+}
+
+static int __maybe_unused qmp_combo_runtime_suspend(struct device *dev)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ struct qmp_phy *qphy = qmp->phys[0];
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qphy->mode);
+
+ /* Supported only for USB3 PHY and luckily USB3 is the first phy */
+ if (cfg->type != PHY_TYPE_USB3)
+ return 0;
+
+ if (!qmp->init_count) {
+ dev_vdbg(dev, "PHY not initialized, bailing out\n");
+ return 0;
+ }
+
+ qmp_combo_enable_autonomous_mode(qphy);
+
+ clk_disable_unprepare(qphy->pipe_clk);
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+
+ return 0;
+}
+
+static int __maybe_unused qmp_combo_runtime_resume(struct device *dev)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ struct qmp_phy *qphy = qmp->phys[0];
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ int ret = 0;
+
+ dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qphy->mode);
+
+ /* Supported only for USB3 PHY and luckily USB3 is the first phy */
+ if (cfg->type != PHY_TYPE_USB3)
+ return 0;
+
+ if (!qmp->init_count) {
+ dev_vdbg(dev, "PHY not initialized, bailing out\n");
+ return 0;
+ }
+
+ ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(qphy->pipe_clk);
+ if (ret) {
+ dev_err(dev, "pipe_clk enable failed, err=%d\n", ret);
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+ return ret;
+ }
+
+ qmp_combo_disable_autonomous_mode(qphy);
+
+ return 0;
+}
+
+static int qmp_combo_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int num = cfg->num_vregs;
+ int ret, i;
+
+ qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL);
+ if (!qmp->vregs)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ qmp->vregs[i].supply = cfg->vreg_list[i].name;
+
+ ret = devm_regulator_bulk_get(dev, num, qmp->vregs);
+ if (ret) {
+ dev_err(dev, "failed at devm_regulator_bulk_get\n");
+ return ret;
+ }
+
+ for (i = 0; i < num; i++) {
+ ret = regulator_set_load(qmp->vregs[i].consumer,
+ cfg->vreg_list[i].enable_load);
+ if (ret) {
+ dev_err(dev, "failed to set load at %s\n",
+ qmp->vregs[i].supply);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int qmp_combo_reset_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int i;
+ int ret;
+
+ qmp->resets = devm_kcalloc(dev, cfg->num_resets,
+ sizeof(*qmp->resets), GFP_KERNEL);
+ if (!qmp->resets)
+ return -ENOMEM;
+
+ for (i = 0; i < cfg->num_resets; i++)
+ qmp->resets[i].id = cfg->reset_list[i];
+
+ ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets, qmp->resets);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get resets\n");
+
+ return 0;
+}
+
+static int qmp_combo_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int num = cfg->num_clks;
+ int i;
+
+ qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
+ if (!qmp->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ qmp->clks[i].id = cfg->clk_list[i];
+
+ return devm_clk_bulk_get(dev, num, qmp->clks);
+}
+
+static void phy_clk_release_provider(void *res)
+{
+ of_clk_del_provider(res);
+}
+
+/*
+ * Register a fixed rate pipe clock.
+ *
+ * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate
+ * controls it. The <s>_pipe_clk coming out of the GCC is requested
+ * by the PHY driver for its operations.
+ * We register the <s>_pipe_clksrc here. The gcc driver takes care
+ * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk.
+ * Below picture shows this relationship.
+ *
+ * +---------------+
+ * | PHY block |<<---------------------------------------+
+ * | | |
+ * | +-------+ | +-----+ |
+ * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+
+ * clk | +-------+ | +-----+
+ * +---------------+
+ */
+static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
+{
+ struct clk_fixed_rate *fixed;
+ struct clk_init_data init = { };
+ int ret;
+
+ ret = of_property_read_string(np, "clock-output-names", &init.name);
+ if (ret) {
+ dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np);
+ return ret;
+ }
+
+ fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL);
+ if (!fixed)
+ return -ENOMEM;
+
+ init.ops = &clk_fixed_rate_ops;
+
+ /* controllers using QMP phys use 125MHz pipe clock interface */
+ fixed->fixed_rate = 125000000;
+ fixed->hw.init = &init;
+
+ ret = devm_clk_hw_register(qmp->dev, &fixed->hw);
+ if (ret)
+ return ret;
+
+ ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw);
+ if (ret)
+ return ret;
+
+ /*
+ * Roll a devm action because the clock provider is the child node, but
+ * the child node is not actually a device.
+ */
+ return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np);
+}
+
+/*
+ * Display Port PLL driver block diagram for branch clocks
+ *
+ * +------------------------------+
+ * | DP_VCO_CLK |
+ * | |
+ * | +-------------------+ |
+ * | | (DP PLL/VCO) | |
+ * | +---------+---------+ |
+ * | v |
+ * | +----------+-----------+ |
+ * | | hsclk_divsel_clk_src | |
+ * | +----------+-----------+ |
+ * +------------------------------+
+ * |
+ * +---------<---------v------------>----------+
+ * | |
+ * +--------v----------------+ |
+ * | dp_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
+ * |
+ * +----------+-----------------+
+ * | dp_phy_pll_vco_div_clk |
+ * +---------+------------------+
+ * |
+ * v
+ * Input to DISPCC block
+ * for DP pixel clock
+ *
+ */
+static int qcom_qmp_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_qmp_dp_pixel_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ const struct qmp_phy_dp_clks *dp_clks;
+ const struct qmp_phy *qphy;
+ const struct phy_configure_opts_dp *dp_opts;
+
+ dp_clks = container_of(hw, struct qmp_phy_dp_clks, dp_pixel_hw);
+ qphy = dp_clks->qphy;
+ dp_opts = &qphy->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_qmp_dp_pixel_clk_ops = {
+ .determine_rate = qcom_qmp_dp_pixel_clk_determine_rate,
+ .recalc_rate = qcom_qmp_dp_pixel_clk_recalc_rate,
+};
+
+static int qcom_qmp_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_qmp_dp_link_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ const struct qmp_phy_dp_clks *dp_clks;
+ const struct qmp_phy *qphy;
+ const struct phy_configure_opts_dp *dp_opts;
+
+ dp_clks = container_of(hw, struct qmp_phy_dp_clks, dp_link_hw);
+ qphy = dp_clks->qphy;
+ dp_opts = &qphy->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_qmp_dp_link_clk_ops = {
+ .determine_rate = qcom_qmp_dp_link_clk_determine_rate,
+ .recalc_rate = qcom_qmp_dp_link_clk_recalc_rate,
+};
+
+static struct clk_hw *
+qcom_qmp_dp_clks_hw_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct qmp_phy_dp_clks *dp_clks = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= 2) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (idx == 0)
+ return &dp_clks->dp_link_hw;
+
+ return &dp_clks->dp_pixel_hw;
+}
+
+static int phy_dp_clks_register(struct qcom_qmp *qmp, struct qmp_phy *qphy,
+ struct device_node *np)
+{
+ struct clk_init_data init = { };
+ struct qmp_phy_dp_clks *dp_clks;
+ char name[64];
+ int ret;
+
+ dp_clks = devm_kzalloc(qmp->dev, sizeof(*dp_clks), GFP_KERNEL);
+ if (!dp_clks)
+ return -ENOMEM;
+
+ dp_clks->qphy = qphy;
+ qphy->dp_clks = dp_clks;
+
+ snprintf(name, sizeof(name), "%s::link_clk", dev_name(qmp->dev));
+ init.ops = &qcom_qmp_dp_link_clk_ops;
+ init.name = name;
+ dp_clks->dp_link_hw.init = &init;
+ ret = devm_clk_hw_register(qmp->dev, &dp_clks->dp_link_hw);
+ if (ret)
+ return ret;
+
+ snprintf(name, sizeof(name), "%s::vco_div_clk", dev_name(qmp->dev));
+ init.ops = &qcom_qmp_dp_pixel_clk_ops;
+ init.name = name;
+ dp_clks->dp_pixel_hw.init = &init;
+ ret = devm_clk_hw_register(qmp->dev, &dp_clks->dp_pixel_hw);
+ if (ret)
+ return ret;
+
+ ret = of_clk_add_hw_provider(np, qcom_qmp_dp_clks_hw_get, dp_clks);
+ if (ret)
+ return ret;
+
+ /*
+ * Roll a devm action because the clock provider is the child node, but
+ * the child node is not actually a device.
+ */
+ return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np);
+}
+
+static const struct phy_ops qmp_combo_usb_ops = {
+ .init = qmp_combo_enable,
+ .exit = qmp_combo_disable,
+ .set_mode = qmp_combo_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static const struct phy_ops qmp_combo_dp_ops = {
+ .init = qmp_combo_init,
+ .configure = qcom_qmp_dp_phy_configure,
+ .power_on = qmp_combo_power_on,
+ .calibrate = qcom_qmp_dp_phy_calibrate,
+ .power_off = qmp_combo_power_off,
+ .exit = qmp_combo_exit,
+ .set_mode = qmp_combo_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static int qmp_combo_create(struct device *dev, struct device_node *np, int id,
+ void __iomem *serdes, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ struct phy *generic_phy;
+ struct qmp_phy *qphy;
+ const struct phy_ops *ops;
+ int ret;
+
+ qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
+ if (!qphy)
+ return -ENOMEM;
+
+ qphy->cfg = cfg;
+ qphy->serdes = serdes;
+ /*
+ * Get memory resources for each phy lane:
+ * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
+ * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5
+ * For single lane PHYs: pcs_misc (optional) -> 3.
+ */
+ qphy->tx = devm_of_iomap(dev, np, 0, NULL);
+ if (IS_ERR(qphy->tx))
+ return PTR_ERR(qphy->tx);
+
+ qphy->rx = devm_of_iomap(dev, np, 1, NULL);
+ if (IS_ERR(qphy->rx))
+ return PTR_ERR(qphy->rx);
+
+ qphy->pcs = devm_of_iomap(dev, np, 2, NULL);
+ if (IS_ERR(qphy->pcs))
+ return PTR_ERR(qphy->pcs);
+
+ if (cfg->pcs_usb_offset)
+ qphy->pcs_usb = qphy->pcs + cfg->pcs_usb_offset;
+
+ if (cfg->lanes >= 2) {
+ qphy->tx2 = devm_of_iomap(dev, np, 3, NULL);
+ if (IS_ERR(qphy->tx2))
+ return PTR_ERR(qphy->tx2);
+
+ qphy->rx2 = devm_of_iomap(dev, np, 4, NULL);
+ if (IS_ERR(qphy->rx2))
+ return PTR_ERR(qphy->rx2);
+
+ qphy->pcs_misc = devm_of_iomap(dev, np, 5, NULL);
+ } else {
+ qphy->pcs_misc = devm_of_iomap(dev, np, 3, NULL);
+ }
+
+ if (IS_ERR(qphy->pcs_misc)) {
+ dev_vdbg(dev, "PHY pcs_misc-reg not used\n");
+ qphy->pcs_misc = NULL;
+ }
+
+ /*
+ * Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3
+ * based phys, so they essentially have pipe clock. So,
+ * we return error in case phy is USB3 or PIPE type.
+ * Otherwise, we initialize pipe clock to NULL for
+ * all phys that don't need this.
+ */
+ qphy->pipe_clk = devm_get_clk_from_child(dev, np, NULL);
+ if (IS_ERR(qphy->pipe_clk)) {
+ if (cfg->type == PHY_TYPE_USB3)
+ return dev_err_probe(dev, PTR_ERR(qphy->pipe_clk),
+ "failed to get lane%d pipe_clk\n",
+ id);
+ qphy->pipe_clk = NULL;
+ }
+
+ if (cfg->type == PHY_TYPE_DP)
+ ops = &qmp_combo_dp_ops;
+ else
+ ops = &qmp_combo_usb_ops;
+
+ generic_phy = devm_phy_create(dev, np, ops);
+ if (IS_ERR(generic_phy)) {
+ ret = PTR_ERR(generic_phy);
+ dev_err(dev, "failed to create qphy %d\n", ret);
+ return ret;
+ }
+
+ qphy->phy = generic_phy;
+ qphy->qmp = qmp;
+ qmp->phys[id] = qphy;
+ phy_set_drvdata(generic_phy, qphy);
+
+ return 0;
+}
+
+static const struct of_device_id qmp_combo_of_match_table[] = {
+ {
+ .compatible = "qcom,sc7180-qmp-usb3-dp-phy",
+ .data = &sc7180_usb3dpphy_cfg,
+ },
+ {
+ .compatible = "qcom,sdm845-qmp-usb3-dp-phy",
+ .data = &sdm845_usb3dpphy_cfg,
+ },
+ {
+ .compatible = "qcom,sm8250-qmp-usb3-dp-phy",
+ .data = &sm8250_usb3dpphy_cfg,
+ },
+ {
+ .compatible = "qcom,sc8180x-qmp-usb3-dp-phy",
+ .data = &sc8180x_usb3dpphy_cfg,
+ },
+ {
+ .compatible = "qcom,sc8280xp-qmp-usb43dp-phy",
+ .data = &sc8280xp_usb43dpphy_combo_cfg,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qmp_combo_of_match_table);
+
+static const struct dev_pm_ops qmp_combo_pm_ops = {
+ SET_RUNTIME_PM_OPS(qmp_combo_runtime_suspend,
+ qmp_combo_runtime_resume, NULL)
+};
+
+static int qmp_combo_probe(struct platform_device *pdev)
+{
+ struct qcom_qmp *qmp;
+ struct device *dev = &pdev->dev;
+ struct device_node *child;
+ struct phy_provider *phy_provider;
+ void __iomem *serdes;
+ void __iomem *usb_serdes;
+ void __iomem *dp_serdes = NULL;
+ const struct qmp_phy_combo_cfg *combo_cfg = NULL;
+ const struct qmp_phy_cfg *cfg = NULL;
+ const struct qmp_phy_cfg *usb_cfg = NULL;
+ const struct qmp_phy_cfg *dp_cfg = NULL;
+ int num, id, expected_phys;
+ int ret;
+
+ qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL);
+ if (!qmp)
+ return -ENOMEM;
+
+ qmp->dev = dev;
+ dev_set_drvdata(dev, qmp);
+
+ /* Get the specific init parameters of QMP phy */
+ combo_cfg = of_device_get_match_data(dev);
+ if (!combo_cfg)
+ return -EINVAL;
+
+ usb_cfg = combo_cfg->usb_cfg;
+ cfg = usb_cfg; /* Setup clks and regulators */
+
+ /* per PHY serdes; usually located at base address */
+ usb_serdes = serdes = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(serdes))
+ return PTR_ERR(serdes);
+
+ qmp->dp_com = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(qmp->dp_com))
+ return PTR_ERR(qmp->dp_com);
+
+ /* Only two serdes for combo PHY */
+ dp_serdes = devm_platform_ioremap_resource(pdev, 2);
+ if (IS_ERR(dp_serdes))
+ return PTR_ERR(dp_serdes);
+
+ dp_cfg = combo_cfg->dp_cfg;
+ expected_phys = 2;
+
+ mutex_init(&qmp->phy_mutex);
+
+ ret = qmp_combo_clk_init(dev, cfg);
+ if (ret)
+ return ret;
+
+ ret = qmp_combo_reset_init(dev, cfg);
+ if (ret)
+ return ret;
+
+ ret = qmp_combo_vreg_init(dev, cfg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get regulator supplies\n");
+
+ num = of_get_available_child_count(dev->of_node);
+ /* do we have a rogue child node ? */
+ if (num > expected_phys)
+ return -EINVAL;
+
+ qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL);
+ if (!qmp->phys)
+ return -ENOMEM;
+
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+ /*
+ * Prevent runtime pm from being ON by default. Users can enable
+ * it using power/control in sysfs.
+ */
+ pm_runtime_forbid(dev);
+
+ id = 0;
+ for_each_available_child_of_node(dev->of_node, child) {
+ if (of_node_name_eq(child, "dp-phy")) {
+ cfg = dp_cfg;
+ serdes = dp_serdes;
+
+ /* Create per-lane phy */
+ ret = qmp_combo_create(dev, child, id, serdes, cfg);
+ if (ret) {
+ dev_err(dev, "failed to create lane%d phy, %d\n",
+ id, ret);
+ goto err_node_put;
+ }
+
+ ret = phy_dp_clks_register(qmp, qmp->phys[id], child);
+ if (ret) {
+ dev_err(qmp->dev,
+ "failed to register DP clock source\n");
+ goto err_node_put;
+ }
+ } else if (of_node_name_eq(child, "usb3-phy")) {
+ cfg = usb_cfg;
+ serdes = usb_serdes;
+
+ /* Create per-lane phy */
+ ret = qmp_combo_create(dev, child, id, serdes, cfg);
+ if (ret) {
+ dev_err(dev, "failed to create lane%d phy, %d\n",
+ id, ret);
+ goto err_node_put;
+ }
+
+ /*
+ * Register the pipe clock provided by phy.
+ * See function description to see details of this pipe clock.
+ */
+ ret = phy_pipe_clk_register(qmp, child);
+ if (ret) {
+ dev_err(qmp->dev,
+ "failed to register pipe clock source\n");
+ goto err_node_put;
+ }
+ }
+
+ id++;
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+
+err_node_put:
+ of_node_put(child);
+ return ret;
+}
+
+static struct platform_driver qmp_combo_driver = {
+ .probe = qmp_combo_probe,
+ .driver = {
+ .name = "qcom-qmp-combo-phy",
+ .pm = &qmp_combo_pm_ops,
+ .of_match_table = qmp_combo_of_match_table,
+ },
+};
+
+module_platform_driver(qmp_combo_driver);
+
+MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm QMP USB+DP combo PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c
new file mode 100644
index 000000000000..461f0b5d464a
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c
@@ -0,0 +1,935 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#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"
+
+/* QPHY_SW_RESET bit */
+#define SW_RESET BIT(0)
+/* QPHY_POWER_DOWN_CONTROL */
+#define SW_PWRDN BIT(0)
+#define REFCLK_DRV_DSBL BIT(1)
+/* QPHY_START_CONTROL bits */
+#define SERDES_START BIT(0)
+#define PCS_START BIT(1)
+#define PLL_READY_GATE_EN BIT(3)
+/* QPHY_PCS_STATUS bit */
+#define PHYSTATUS BIT(6)
+#define PHYSTATUS_4_20 BIT(7)
+/* QPHY_COM_PCS_READY_STATUS bit */
+#define PCS_READY BIT(0)
+
+#define PHY_INIT_COMPLETE_TIMEOUT 10000
+#define POWER_DOWN_DELAY_US_MIN 10
+#define POWER_DOWN_DELAY_US_MAX 11
+
+struct qmp_phy_init_tbl {
+ unsigned int offset;
+ unsigned int val;
+ /*
+ * register part of layout ?
+ * if yes, then offset gives index in the reg-layout
+ */
+ bool in_layout;
+ /*
+ * mask of lanes for which this register is written
+ * for cases when second lane needs different values
+ */
+ u8 lane_mask;
+};
+
+#define QMP_PHY_INIT_CFG(o, v) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .lane_mask = 0xff, \
+ }
+
+#define QMP_PHY_INIT_CFG_L(o, v) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .in_layout = true, \
+ .lane_mask = 0xff, \
+ }
+
+#define QMP_PHY_INIT_CFG_LANE(o, v, l) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .lane_mask = l, \
+ }
+
+/* set of registers with offsets different per-PHY */
+enum qphy_reg_layout {
+ /* Common block control registers */
+ QPHY_COM_SW_RESET,
+ QPHY_COM_POWER_DOWN_CONTROL,
+ QPHY_COM_START_CONTROL,
+ QPHY_COM_PCS_READY_STATUS,
+ /* PCS registers */
+ QPHY_SW_RESET,
+ QPHY_START_CTRL,
+ QPHY_PCS_STATUS,
+ QPHY_PCS_POWER_DOWN_CONTROL,
+ /* Keep last to ensure regs_layout arrays are properly initialized */
+ QPHY_LAYOUT_SIZE
+};
+
+static const unsigned int pciephy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_COM_SW_RESET] = 0x400,
+ [QPHY_COM_POWER_DOWN_CONTROL] = 0x404,
+ [QPHY_COM_START_CONTROL] = 0x408,
+ [QPHY_COM_PCS_READY_STATUS] = 0x448,
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_START_CTRL] = 0x08,
+ [QPHY_PCS_STATUS] = 0x174,
+};
+
+static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19),
+ QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40),
+};
+
+static const struct qmp_phy_init_tbl msm8996_pcie_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
+ QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
+};
+
+static const struct qmp_phy_init_tbl msm8996_pcie_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19),
+};
+
+static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_IDLE_DTCT_CNTRL, 0x4c),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
+
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_PLL_LOCK_CHK_DLY_TIME, 0x05),
+
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_ENDPOINT_REFCLK_DRIVE, 0x05),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_DOWN_CONTROL, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG4, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG1, 0xa3),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_TXDEEMPH_M3P5DB_V0, 0x0e),
+};
+
+/* struct qmp_phy_cfg - per-PHY initialization config */
+struct qmp_phy_cfg {
+ /* number of PHYs provided by this block */
+ int num_phys;
+
+ /* Init sequence for PHY blocks - serdes, tx, rx, pcs */
+ const struct qmp_phy_init_tbl *serdes_tbl;
+ int serdes_tbl_num;
+ const struct qmp_phy_init_tbl *tx_tbl;
+ int tx_tbl_num;
+ const struct qmp_phy_init_tbl *rx_tbl;
+ int rx_tbl_num;
+ const struct qmp_phy_init_tbl *pcs_tbl;
+ int pcs_tbl_num;
+
+ /* clock ids to be requested */
+ const char * const *clk_list;
+ int num_clks;
+ /* resets to be requested */
+ const char * const *reset_list;
+ int num_resets;
+ /* regulators to be requested */
+ const char * const *vreg_list;
+ int num_vregs;
+
+ /* array of registers with different offsets */
+ const unsigned int *regs;
+
+ unsigned int start_ctrl;
+ unsigned int pwrdn_ctrl;
+ unsigned int mask_com_pcs_ready;
+ /* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */
+ unsigned int phy_status;
+
+ /* true, if PHY needs delay after POWER_DOWN */
+ bool has_pwrdn_delay;
+ /* power_down delay in usec */
+ int pwrdn_delay_min;
+ int pwrdn_delay_max;
+};
+
+/**
+ * struct qmp_phy - per-lane phy descriptor
+ *
+ * @phy: generic phy
+ * @cfg: phy specific configuration
+ * @serdes: iomapped memory space for phy's serdes (i.e. PLL)
+ * @tx: iomapped memory space for lane's tx
+ * @rx: iomapped memory space for lane's rx
+ * @pcs: iomapped memory space for lane's pcs
+ * @pipe_clk: pipe clock
+ * @index: lane index
+ * @qmp: QMP phy to which this lane belongs
+ * @lane_rst: lane's reset controller
+ */
+struct qmp_phy {
+ struct phy *phy;
+ const struct qmp_phy_cfg *cfg;
+ void __iomem *serdes;
+ void __iomem *tx;
+ void __iomem *rx;
+ void __iomem *pcs;
+ struct clk *pipe_clk;
+ unsigned int index;
+ struct qcom_qmp *qmp;
+ struct reset_control *lane_rst;
+};
+
+/**
+ * struct qcom_qmp - structure holding QMP phy block attributes
+ *
+ * @dev: device
+ *
+ * @clks: array of clocks required by phy
+ * @resets: array of resets required by phy
+ * @vregs: regulator supplies bulk data
+ *
+ * @phys: array of per-lane phy descriptors
+ * @phy_mutex: mutex lock for PHY common block initialization
+ * @init_count: phy common block initialization count
+ */
+struct qcom_qmp {
+ struct device *dev;
+
+ struct clk_bulk_data *clks;
+ struct reset_control_bulk_data *resets;
+ struct regulator_bulk_data *vregs;
+
+ struct qmp_phy **phys;
+
+ struct mutex phy_mutex;
+ int init_count;
+};
+
+static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg |= val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg &= ~val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+/* list of clocks required by phy */
+static const char * const msm8996_phy_clk_l[] = {
+ "aux", "cfg_ahb", "ref",
+};
+
+/* list of resets */
+static const char * const msm8996_pciephy_reset_l[] = {
+ "phy", "common", "cfg",
+};
+
+/* list of regulators */
+static const char * const qmp_phy_vreg_l[] = {
+ "vdda-phy", "vdda-pll",
+};
+
+static const struct qmp_phy_cfg msm8996_pciephy_cfg = {
+ .num_phys = 3,
+
+ .serdes_tbl = msm8996_pcie_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(msm8996_pcie_serdes_tbl),
+ .tx_tbl = msm8996_pcie_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(msm8996_pcie_tx_tbl),
+ .rx_tbl = msm8996_pcie_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(msm8996_pcie_rx_tbl),
+ .pcs_tbl = msm8996_pcie_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(msm8996_pcie_pcs_tbl),
+ .clk_list = msm8996_phy_clk_l,
+ .num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
+ .reset_list = msm8996_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_pciephy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = pciephy_regs_layout,
+
+ .start_ctrl = PCS_START | PLL_READY_GATE_EN,
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+ .mask_com_pcs_ready = PCS_READY,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static void qmp_pcie_msm8996_configure_lane(void __iomem *base,
+ const unsigned int *regs,
+ const struct qmp_phy_init_tbl tbl[],
+ int num,
+ u8 lane_mask)
+{
+ int i;
+ const struct qmp_phy_init_tbl *t = tbl;
+
+ if (!t)
+ return;
+
+ for (i = 0; i < num; i++, t++) {
+ if (!(t->lane_mask & lane_mask))
+ continue;
+
+ if (t->in_layout)
+ writel(t->val, base + regs[t->offset]);
+ else
+ writel(t->val, base + t->offset);
+ }
+}
+
+static void qmp_pcie_msm8996_configure(void __iomem *base,
+ const unsigned int *regs,
+ const struct qmp_phy_init_tbl tbl[],
+ int num)
+{
+ qmp_pcie_msm8996_configure_lane(base, regs, tbl, num, 0xff);
+}
+
+static int qmp_pcie_msm8996_serdes_init(struct qmp_phy *qphy)
+{
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *serdes = qphy->serdes;
+ const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl;
+ int serdes_tbl_num = cfg->serdes_tbl_num;
+ void __iomem *status;
+ unsigned int mask, val;
+ int ret;
+
+ qmp_pcie_msm8996_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num);
+
+ qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET);
+ qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
+ SERDES_START | PCS_START);
+
+ status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS];
+ mask = cfg->mask_com_pcs_ready;
+
+ ret = readl_poll_timeout(status, val, (val & mask), 10,
+ PHY_INIT_COMPLETE_TIMEOUT);
+ if (ret) {
+ dev_err(qmp->dev,
+ "phy common block init timed-out\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qmp_pcie_msm8996_com_init(struct qmp_phy *qphy)
+{
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *serdes = qphy->serdes;
+ int ret;
+
+ mutex_lock(&qmp->phy_mutex);
+ if (qmp->init_count++) {
+ mutex_unlock(&qmp->phy_mutex);
+ return 0;
+ }
+
+ /* turn on regulator supplies */
+ ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
+ if (ret) {
+ dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
+ goto err_unlock;
+ }
+
+ ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+ if (ret) {
+ dev_err(qmp->dev, "reset assert failed\n");
+ goto err_disable_regulators;
+ }
+
+ ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets);
+ if (ret) {
+ dev_err(qmp->dev, "reset deassert failed\n");
+ goto err_disable_regulators;
+ }
+
+ ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
+ if (ret)
+ goto err_assert_reset;
+
+ qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
+ SW_PWRDN);
+
+ mutex_unlock(&qmp->phy_mutex);
+
+ return 0;
+
+err_assert_reset:
+ reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+err_disable_regulators:
+ regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+err_unlock:
+ mutex_unlock(&qmp->phy_mutex);
+
+ return ret;
+}
+
+static int qmp_pcie_msm8996_com_exit(struct qmp_phy *qphy)
+{
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *serdes = qphy->serdes;
+
+ mutex_lock(&qmp->phy_mutex);
+ if (--qmp->init_count) {
+ mutex_unlock(&qmp->phy_mutex);
+ return 0;
+ }
+
+ qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
+ SERDES_START | PCS_START);
+ qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET],
+ SW_RESET);
+ qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
+ SW_PWRDN);
+
+ reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+
+ regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+
+ mutex_unlock(&qmp->phy_mutex);
+
+ return 0;
+}
+
+static int qmp_pcie_msm8996_init(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+ int ret;
+ dev_vdbg(qmp->dev, "Initializing QMP phy\n");
+
+ ret = qmp_pcie_msm8996_com_init(qphy);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int qmp_pcie_msm8996_power_on(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *tx = qphy->tx;
+ void __iomem *rx = qphy->rx;
+ void __iomem *pcs = qphy->pcs;
+ void __iomem *status;
+ unsigned int mask, val, ready;
+ int ret;
+
+ qmp_pcie_msm8996_serdes_init(qphy);
+
+ ret = reset_control_deassert(qphy->lane_rst);
+ if (ret) {
+ dev_err(qmp->dev, "lane%d reset deassert failed\n",
+ qphy->index);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(qphy->pipe_clk);
+ if (ret) {
+ dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret);
+ goto err_reset_lane;
+ }
+
+ /* Tx, Rx, and PCS configurations */
+ qmp_pcie_msm8996_configure_lane(tx, cfg->regs, cfg->tx_tbl,
+ cfg->tx_tbl_num, 1);
+
+ qmp_pcie_msm8996_configure_lane(rx, cfg->regs, cfg->rx_tbl,
+ cfg->rx_tbl_num, 1);
+
+ qmp_pcie_msm8996_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
+
+ /*
+ * Pull out PHY from POWER DOWN state.
+ * This is active low enable signal to power-down PHY.
+ */
+ qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
+
+ if (cfg->has_pwrdn_delay)
+ usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
+
+ /* Pull PHY out of reset state */
+ qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+
+ /* start SerDes and Phy-Coding-Sublayer */
+ qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
+
+ status = pcs + cfg->regs[QPHY_PCS_STATUS];
+ mask = cfg->phy_status;
+ ready = 0;
+
+ ret = readl_poll_timeout(status, val, (val & mask) == ready, 10,
+ PHY_INIT_COMPLETE_TIMEOUT);
+ if (ret) {
+ dev_err(qmp->dev, "phy initialization timed-out\n");
+ goto err_disable_pipe_clk;
+ }
+
+ return 0;
+
+err_disable_pipe_clk:
+ clk_disable_unprepare(qphy->pipe_clk);
+err_reset_lane:
+ reset_control_assert(qphy->lane_rst);
+
+ return ret;
+}
+
+static int qmp_pcie_msm8996_power_off(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ clk_disable_unprepare(qphy->pipe_clk);
+
+ /* PHY reset */
+ qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+
+ /* stop SerDes and Phy-Coding-Sublayer */
+ qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
+
+ /* Put PHY into POWER DOWN state: active low */
+ if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) {
+ qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ cfg->pwrdn_ctrl);
+ } else {
+ qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
+ cfg->pwrdn_ctrl);
+ }
+
+ return 0;
+}
+
+static int qmp_pcie_msm8996_exit(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+
+ reset_control_assert(qphy->lane_rst);
+
+ qmp_pcie_msm8996_com_exit(qphy);
+
+ return 0;
+}
+
+static int qmp_pcie_msm8996_enable(struct phy *phy)
+{
+ int ret;
+
+ ret = qmp_pcie_msm8996_init(phy);
+ if (ret)
+ return ret;
+
+ ret = qmp_pcie_msm8996_power_on(phy);
+ if (ret)
+ qmp_pcie_msm8996_exit(phy);
+
+ return ret;
+}
+
+static int qmp_pcie_msm8996_disable(struct phy *phy)
+{
+ int ret;
+
+ ret = qmp_pcie_msm8996_power_off(phy);
+ if (ret)
+ return ret;
+ return qmp_pcie_msm8996_exit(phy);
+}
+
+static int qmp_pcie_msm8996_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int num = cfg->num_vregs;
+ int i;
+
+ qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL);
+ if (!qmp->vregs)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ qmp->vregs[i].supply = cfg->vreg_list[i];
+
+ return devm_regulator_bulk_get(dev, num, qmp->vregs);
+}
+
+static int qmp_pcie_msm8996_reset_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int i;
+ int ret;
+
+ qmp->resets = devm_kcalloc(dev, cfg->num_resets,
+ sizeof(*qmp->resets), GFP_KERNEL);
+ if (!qmp->resets)
+ return -ENOMEM;
+
+ for (i = 0; i < cfg->num_resets; i++)
+ qmp->resets[i].id = cfg->reset_list[i];
+
+ ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets, qmp->resets);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get resets\n");
+
+ return 0;
+}
+
+static int qmp_pcie_msm8996_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int num = cfg->num_clks;
+ int i;
+
+ qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
+ if (!qmp->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ qmp->clks[i].id = cfg->clk_list[i];
+
+ return devm_clk_bulk_get(dev, num, qmp->clks);
+}
+
+static void phy_clk_release_provider(void *res)
+{
+ of_clk_del_provider(res);
+}
+
+/*
+ * Register a fixed rate pipe clock.
+ *
+ * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate
+ * controls it. The <s>_pipe_clk coming out of the GCC is requested
+ * by the PHY driver for its operations.
+ * We register the <s>_pipe_clksrc here. The gcc driver takes care
+ * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk.
+ * Below picture shows this relationship.
+ *
+ * +---------------+
+ * | PHY block |<<---------------------------------------+
+ * | | |
+ * | +-------+ | +-----+ |
+ * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+
+ * clk | +-------+ | +-----+
+ * +---------------+
+ */
+static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
+{
+ struct clk_fixed_rate *fixed;
+ struct clk_init_data init = { };
+ int ret;
+
+ ret = of_property_read_string(np, "clock-output-names", &init.name);
+ if (ret) {
+ dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np);
+ return ret;
+ }
+
+ fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL);
+ if (!fixed)
+ return -ENOMEM;
+
+ init.ops = &clk_fixed_rate_ops;
+
+ /* controllers using QMP phys use 125MHz pipe clock interface */
+ fixed->fixed_rate = 125000000;
+ fixed->hw.init = &init;
+
+ ret = devm_clk_hw_register(qmp->dev, &fixed->hw);
+ if (ret)
+ return ret;
+
+ ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw);
+ if (ret)
+ return ret;
+
+ /*
+ * Roll a devm action because the clock provider is the child node, but
+ * the child node is not actually a device.
+ */
+ return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np);
+}
+
+static const struct phy_ops qmp_pcie_msm8996_ops = {
+ .power_on = qmp_pcie_msm8996_enable,
+ .power_off = qmp_pcie_msm8996_disable,
+ .owner = THIS_MODULE,
+};
+
+static void qcom_qmp_reset_control_put(void *data)
+{
+ reset_control_put(data);
+}
+
+static int qmp_pcie_msm8996_create(struct device *dev, struct device_node *np, int id,
+ void __iomem *serdes, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ struct phy *generic_phy;
+ struct qmp_phy *qphy;
+ int ret;
+
+ qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
+ if (!qphy)
+ return -ENOMEM;
+
+ qphy->cfg = cfg;
+ qphy->serdes = serdes;
+ /*
+ * Get memory resources for each phy lane:
+ * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
+ */
+ qphy->tx = devm_of_iomap(dev, np, 0, NULL);
+ if (IS_ERR(qphy->tx))
+ return PTR_ERR(qphy->tx);
+
+ qphy->rx = devm_of_iomap(dev, np, 1, NULL);
+ if (IS_ERR(qphy->rx))
+ return PTR_ERR(qphy->rx);
+
+ qphy->pcs = devm_of_iomap(dev, np, 2, NULL);
+ if (IS_ERR(qphy->pcs))
+ return PTR_ERR(qphy->pcs);
+
+ qphy->pipe_clk = devm_get_clk_from_child(dev, np, NULL);
+ if (IS_ERR(qphy->pipe_clk)) {
+ return dev_err_probe(dev, PTR_ERR(qphy->pipe_clk),
+ "failed to get lane%d pipe clock\n", id);
+ }
+
+ qphy->lane_rst = of_reset_control_get_exclusive_by_index(np, 0);
+ if (IS_ERR(qphy->lane_rst)) {
+ dev_err(dev, "failed to get lane%d reset\n", id);
+ return PTR_ERR(qphy->lane_rst);
+ }
+ ret = devm_add_action_or_reset(dev, qcom_qmp_reset_control_put,
+ qphy->lane_rst);
+ if (ret)
+ return ret;
+
+ generic_phy = devm_phy_create(dev, np, &qmp_pcie_msm8996_ops);
+ if (IS_ERR(generic_phy)) {
+ ret = PTR_ERR(generic_phy);
+ dev_err(dev, "failed to create qphy %d\n", ret);
+ return ret;
+ }
+
+ qphy->phy = generic_phy;
+ qphy->index = id;
+ qphy->qmp = qmp;
+ qmp->phys[id] = qphy;
+ phy_set_drvdata(generic_phy, qphy);
+
+ return 0;
+}
+
+static const struct of_device_id qmp_pcie_msm8996_of_match_table[] = {
+ {
+ .compatible = "qcom,msm8996-qmp-pcie-phy",
+ .data = &msm8996_pciephy_cfg,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qmp_pcie_msm8996_of_match_table);
+
+static int qmp_pcie_msm8996_probe(struct platform_device *pdev)
+{
+ struct qcom_qmp *qmp;
+ struct device *dev = &pdev->dev;
+ struct device_node *child;
+ struct phy_provider *phy_provider;
+ void __iomem *serdes;
+ const struct qmp_phy_cfg *cfg = NULL;
+ int num, id, expected_phys;
+ int ret;
+
+ qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL);
+ if (!qmp)
+ return -ENOMEM;
+
+ qmp->dev = dev;
+ dev_set_drvdata(dev, qmp);
+
+ /* Get the specific init parameters of QMP phy */
+ cfg = of_device_get_match_data(dev);
+ if (!cfg)
+ return -EINVAL;
+
+ /* per PHY serdes; usually located at base address */
+ serdes = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(serdes))
+ return PTR_ERR(serdes);
+
+ expected_phys = cfg->num_phys;
+
+ mutex_init(&qmp->phy_mutex);
+
+ ret = qmp_pcie_msm8996_clk_init(dev, cfg);
+ if (ret)
+ return ret;
+
+ ret = qmp_pcie_msm8996_reset_init(dev, cfg);
+ if (ret)
+ return ret;
+
+ ret = qmp_pcie_msm8996_vreg_init(dev, cfg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get regulator supplies\n");
+
+ num = of_get_available_child_count(dev->of_node);
+ /* do we have a rogue child node ? */
+ if (num > expected_phys)
+ return -EINVAL;
+
+ qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL);
+ if (!qmp->phys)
+ return -ENOMEM;
+
+ id = 0;
+ for_each_available_child_of_node(dev->of_node, child) {
+ /* Create per-lane phy */
+ ret = qmp_pcie_msm8996_create(dev, child, id, serdes, cfg);
+ if (ret) {
+ dev_err(dev, "failed to create lane%d phy, %d\n",
+ id, ret);
+ goto err_node_put;
+ }
+
+ /*
+ * Register the pipe clock provided by phy.
+ * See function description to see details of this pipe clock.
+ */
+ ret = phy_pipe_clk_register(qmp, child);
+ if (ret) {
+ dev_err(qmp->dev,
+ "failed to register pipe clock source\n");
+ goto err_node_put;
+ }
+
+ id++;
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+
+err_node_put:
+ of_node_put(child);
+ return ret;
+}
+
+static struct platform_driver qmp_pcie_msm8996_driver = {
+ .probe = qmp_pcie_msm8996_probe,
+ .driver = {
+ .name = "qcom-qmp-msm8996-pcie-phy",
+ .of_match_table = qmp_pcie_msm8996_of_match_table,
+ },
+};
+
+module_platform_driver(qmp_pcie_msm8996_driver);
+
+MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm QMP MSM8996 PCIe PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-qhp.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-qhp.h
new file mode 100644
index 000000000000..e4a4d2cd85eb
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-qhp.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCIE_QHP_H_
+#define QCOM_PHY_QMP_PCIE_QHP_H_
+
+/* PCIE GEN3 COM registers */
+#define PCIE_GEN3_QHP_COM_SSC_EN_CENTER 0x14
+#define PCIE_GEN3_QHP_COM_SSC_PER1 0x20
+#define PCIE_GEN3_QHP_COM_SSC_PER2 0x24
+#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1 0x28
+#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2 0x2c
+#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1_MODE1 0x34
+#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2_MODE1 0x38
+#define PCIE_GEN3_QHP_COM_BIAS_EN_CKBUFLR_EN 0x54
+#define PCIE_GEN3_QHP_COM_CLK_ENABLE1 0x58
+#define PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE0 0x6c
+#define PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE0 0x70
+#define PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE1 0x78
+#define PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE1 0x7c
+#define PCIE_GEN3_QHP_COM_BGV_TRIM 0x98
+#define PCIE_GEN3_QHP_COM_CP_CTRL_MODE0 0xb4
+#define PCIE_GEN3_QHP_COM_CP_CTRL_MODE1 0xb8
+#define PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE0 0xc0
+#define PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE1 0xc4
+#define PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE0 0xcc
+#define PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE1 0xd0
+#define PCIE_GEN3_QHP_COM_SYSCLK_EN_SEL 0xdc
+#define PCIE_GEN3_QHP_COM_RESTRIM_CTRL2 0xf0
+#define PCIE_GEN3_QHP_COM_LOCK_CMP_EN 0xf8
+#define PCIE_GEN3_QHP_COM_DEC_START_MODE0 0x100
+#define PCIE_GEN3_QHP_COM_DEC_START_MODE1 0x108
+#define PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE0 0x11c
+#define PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE0 0x120
+#define PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE0 0x124
+#define PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE1 0x128
+#define PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE1 0x12c
+#define PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE1 0x130
+#define PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE0 0x150
+#define PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE1 0x158
+#define PCIE_GEN3_QHP_COM_VCO_TUNE_MAP 0x178
+#define PCIE_GEN3_QHP_COM_BG_CTRL 0x1c8
+#define PCIE_GEN3_QHP_COM_CLK_SELECT 0x1cc
+#define PCIE_GEN3_QHP_COM_HSCLK_SEL1 0x1d0
+#define PCIE_GEN3_QHP_COM_CORECLK_DIV 0x1e0
+#define PCIE_GEN3_QHP_COM_CORE_CLK_EN 0x1e8
+#define PCIE_GEN3_QHP_COM_CMN_CONFIG 0x1f0
+#define PCIE_GEN3_QHP_COM_SVS_MODE_CLK_SEL 0x1fc
+#define PCIE_GEN3_QHP_COM_CORECLK_DIV_MODE1 0x21c
+#define PCIE_GEN3_QHP_COM_CMN_MODE 0x224
+#define PCIE_GEN3_QHP_COM_VREGCLK_DIV1 0x228
+#define PCIE_GEN3_QHP_COM_VREGCLK_DIV2 0x22c
+
+/* PCIE GEN3 QHP Lane registers */
+#define PCIE_GEN3_QHP_L0_DRVR_CTRL0 0xc
+#define PCIE_GEN3_QHP_L0_DRVR_CTRL1 0x10
+#define PCIE_GEN3_QHP_L0_DRVR_CTRL2 0x14
+#define PCIE_GEN3_QHP_L0_DRVR_TAP_EN 0x18
+#define PCIE_GEN3_QHP_L0_TX_BAND_MODE 0x60
+#define PCIE_GEN3_QHP_L0_LANE_MODE 0x64
+#define PCIE_GEN3_QHP_L0_PARALLEL_RATE 0x7c
+#define PCIE_GEN3_QHP_L0_CML_CTRL_MODE0 0xc0
+#define PCIE_GEN3_QHP_L0_CML_CTRL_MODE1 0xc4
+#define PCIE_GEN3_QHP_L0_CML_CTRL_MODE2 0xc8
+#define PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE1 0xd0
+#define PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE2 0xd4
+#define PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE0 0xd8
+#define PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE1 0xdc
+#define PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE2 0xe0
+#define PCIE_GEN3_QHP_L0_CTLE_THRESH_DFE 0xfc
+#define PCIE_GEN3_QHP_L0_CGA_THRESH_DFE 0x100
+#define PCIE_GEN3_QHP_L0_RXENGINE_EN0 0x108
+#define PCIE_GEN3_QHP_L0_CTLE_TRAIN_TIME 0x114
+#define PCIE_GEN3_QHP_L0_CTLE_DFE_OVRLP_TIME 0x118
+#define PCIE_GEN3_QHP_L0_DFE_REFRESH_TIME 0x11c
+#define PCIE_GEN3_QHP_L0_DFE_ENABLE_TIME 0x120
+#define PCIE_GEN3_QHP_L0_VGA_GAIN 0x124
+#define PCIE_GEN3_QHP_L0_DFE_GAIN 0x128
+#define PCIE_GEN3_QHP_L0_EQ_GAIN 0x130
+#define PCIE_GEN3_QHP_L0_OFFSET_GAIN 0x134
+#define PCIE_GEN3_QHP_L0_PRE_GAIN 0x138
+#define PCIE_GEN3_QHP_L0_VGA_INITVAL 0x13c
+#define PCIE_GEN3_QHP_L0_EQ_INTVAL 0x154
+#define PCIE_GEN3_QHP_L0_EDAC_INITVAL 0x160
+#define PCIE_GEN3_QHP_L0_RXEQ_INITB0 0x168
+#define PCIE_GEN3_QHP_L0_RXEQ_INITB1 0x16c
+#define PCIE_GEN3_QHP_L0_RCVRDONE_THRESH1 0x178
+#define PCIE_GEN3_QHP_L0_RXEQ_CTRL 0x180
+#define PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE0 0x184
+#define PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE1 0x188
+#define PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE2 0x18c
+#define PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE0 0x190
+#define PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE1 0x194
+#define PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE2 0x198
+#define PCIE_GEN3_QHP_L0_UCDR_SO_CONFIG 0x19c
+#define PCIE_GEN3_QHP_L0_RX_BAND 0x1a4
+#define PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE0 0x1c0
+#define PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE1 0x1c4
+#define PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE2 0x1c8
+#define PCIE_GEN3_QHP_L0_SIGDET_ENABLES 0x230
+#define PCIE_GEN3_QHP_L0_SIGDET_CNTRL 0x234
+#define PCIE_GEN3_QHP_L0_SIGDET_DEGLITCH_CNTRL 0x238
+#define PCIE_GEN3_QHP_L0_DCC_GAIN 0x2a4
+#define PCIE_GEN3_QHP_L0_RSM_START 0x2a8
+#define PCIE_GEN3_QHP_L0_RX_EN_SIGNAL 0x2ac
+#define PCIE_GEN3_QHP_L0_PSM_RX_EN_CAL 0x2b0
+#define PCIE_GEN3_QHP_L0_RX_MISC_CNTRL0 0x2b8
+#define PCIE_GEN3_QHP_L0_TS0_TIMER 0x2c0
+#define PCIE_GEN3_QHP_L0_DLL_HIGHDATARATE 0x2c4
+#define PCIE_GEN3_QHP_L0_RX_RESETCODE_OFFSET 0x2cc
+
+/* PCIE GEN3 PCS registers */
+#define PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M3P5DB 0x2c
+#define PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M3P5DB 0x40
+#define PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M6DB 0x54
+#define PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M6DB 0x68
+#define PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG 0x15c
+#define PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG5 0x16c
+#define PCIE_GEN3_QHP_PHY_PCS_TX_RX_CONFIG 0x174
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
new file mode 100644
index 000000000000..5be5348fbb26
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
@@ -0,0 +1,2408 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#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"
+
+/* QPHY_SW_RESET bit */
+#define SW_RESET BIT(0)
+/* QPHY_POWER_DOWN_CONTROL */
+#define SW_PWRDN BIT(0)
+#define REFCLK_DRV_DSBL BIT(1)
+/* QPHY_START_CONTROL bits */
+#define SERDES_START BIT(0)
+#define PCS_START BIT(1)
+/* QPHY_PCS_STATUS bit */
+#define PHYSTATUS BIT(6)
+#define PHYSTATUS_4_20 BIT(7)
+
+#define PHY_INIT_COMPLETE_TIMEOUT 10000
+
+struct qmp_phy_init_tbl {
+ unsigned int offset;
+ unsigned int val;
+ /*
+ * register part of layout ?
+ * if yes, then offset gives index in the reg-layout
+ */
+ bool in_layout;
+ /*
+ * mask of lanes for which this register is written
+ * for cases when second lane needs different values
+ */
+ u8 lane_mask;
+};
+
+#define QMP_PHY_INIT_CFG(o, v) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .lane_mask = 0xff, \
+ }
+
+#define QMP_PHY_INIT_CFG_L(o, v) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .in_layout = true, \
+ .lane_mask = 0xff, \
+ }
+
+#define QMP_PHY_INIT_CFG_LANE(o, v, l) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .lane_mask = l, \
+ }
+
+/* set of registers with offsets different per-PHY */
+enum qphy_reg_layout {
+ /* Common block control registers */
+ QPHY_COM_SW_RESET,
+ QPHY_COM_POWER_DOWN_CONTROL,
+ QPHY_COM_START_CONTROL,
+ QPHY_COM_PCS_READY_STATUS,
+ /* PCS registers */
+ QPHY_SW_RESET,
+ QPHY_START_CTRL,
+ QPHY_PCS_STATUS,
+ QPHY_PCS_POWER_DOWN_CONTROL,
+ /* Keep last to ensure regs_layout arrays are properly initialized */
+ QPHY_LAYOUT_SIZE
+};
+
+static const unsigned int ipq_pciephy_gen3_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_START_CTRL] = 0x44,
+ [QPHY_PCS_STATUS] = 0x14,
+ [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40,
+};
+
+static const unsigned int pciephy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_COM_SW_RESET] = 0x400,
+ [QPHY_COM_POWER_DOWN_CONTROL] = 0x404,
+ [QPHY_COM_START_CONTROL] = 0x408,
+ [QPHY_COM_PCS_READY_STATUS] = 0x448,
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_START_CTRL] = 0x08,
+ [QPHY_PCS_STATUS] = 0x174,
+};
+
+static const unsigned int sdm845_qmp_pciephy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_START_CTRL] = 0x08,
+ [QPHY_PCS_STATUS] = 0x174,
+};
+
+static const unsigned int sdm845_qhp_pciephy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_START_CTRL] = 0x08,
+ [QPHY_PCS_STATUS] = 0x2ac,
+};
+
+static const unsigned int sm8250_pcie_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_START_CTRL] = 0x44,
+ [QPHY_PCS_STATUS] = 0x14,
+ [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40,
+};
+
+static const struct qmp_phy_init_tbl msm8998_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER2, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_EP_DIV, 0x19),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x90),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0d),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x33),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x7e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x15),
+};
+
+static const struct qmp_phy_init_tbl msm8998_pcie_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06),
+};
+
+static const struct qmp_phy_init_tbl msm8998_pcie_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN_HALF, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x71),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x40),
+};
+
+static const struct qmp_phy_init_tbl msm8998_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_OSC_DTCT_ACTIONS, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x99),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03),
+};
+
+static const struct qmp_phy_init_tbl ipq6018_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_PER1, 0x7d),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_STEP_SIZE1_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_STEP_SIZE2_MODE0, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_STEP_SIZE1_MODE1, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_STEP_SIZE2_MODE1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CLKBUFLR_EN, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_ENABLE1, 0x90),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_BUF_ENABLE, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE0, 0xd4),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE1, 0xaa),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE1, 0x29),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_BG_TRIM, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE0, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE1, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE0, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE1, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CTRL_BY_PSM, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_EN_SEL, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_RESETSM_CNTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP_EN, 0x42),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE0, 0x68),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE1, 0x53),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START2_MODE0, 0xaa),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START1_MODE1, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START2_MODE1, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START3_MODE1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE0, 0xa0),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE1, 0xa0),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE0, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE1, 0xb4),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE1, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_SELECT, 0x32),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_HSCLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CORECLK_DIV_MODE1, 0x08),
+};
+
+static const struct qmp_phy_init_tbl ipq6018_pcie_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+};
+
+static const struct qmp_phy_init_tbl ipq6018_pcie_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x70),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x61),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x73),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_ENABLES, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xf0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0xd3),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xc8),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb1),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+};
+
+static const struct qmp_phy_init_tbl ipq6018_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_CNTRL1, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_G12S1_TXDEEMPH_M3P5DB, 0x10),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_DCC_CAL_CONFIG, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x01),
+};
+
+static const struct qmp_phy_init_tbl ipq6018_pcie_pcs_misc_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG2, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG4, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG1, 0x11),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_PRE, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_POST, 0x58),
+};
+
+static const struct qmp_phy_init_tbl ipq8074_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0xf),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x1),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x6),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0xf),
+ QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x1),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0xa),
+ QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0xa),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0xa),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x3),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0xD),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xD04),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x2),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0xb),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CTRL_BY_PSM, 0x1),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x1),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x1),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x2),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19),
+};
+
+static const struct qmp_phy_init_tbl ipq8074_pcie_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
+ QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x6),
+ QMP_PHY_INIT_CFG(QSERDES_TX_RES_CODE_LANE_OFFSET, 0x2),
+ QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_TX_TX_EMP_POST1_LVL, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_TX_SLEW_CNTL, 0x0a),
+};
+
+static const struct qmp_phy_init_tbl ipq8074_pcie_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x1),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x4),
+};
+
+static const struct qmp_phy_init_tbl ipq8074_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_ENDPOINT_REFCLK_DRIVE, 0x4),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_OSC_DTCT_ACTIONS, 0x0),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x0),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB, 0x0),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_SIGDET_LVL, 0x99),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_TXDEEMPH_M6DB_V0, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_TXDEEMPH_M3P5DB_V0, 0xe),
+ QMP_PHY_INIT_CFG_L(QPHY_SW_RESET, 0x0),
+ QMP_PHY_INIT_CFG_L(QPHY_START_CTRL, 0x3),
+};
+
+static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CLKBUFLR_EN, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CTRL_BY_PSM, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_SELECT, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_BG_TRIM, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP_EN, 0x42),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_RESETSM_CNTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE_MAP, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE_TIMER1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE_TIMER2, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_HSCLK_SEL, 0x21),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START3_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START2_MODE0, 0x355),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START1_MODE0, 0x35555),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE0, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE0, 0x1a0a),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE0, 0xb),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE0, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN1_MODE0, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE0, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE0, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CORECLK_DIV, 0xa),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_SELECT, 0x32),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_BUF_ENABLE, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_EN_SEL, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_BG_TIMER, 0xa),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_HSCLK_SEL, 0x1),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE1, 0x68),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START3_MODE1, 0x2),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START2_MODE1, 0x2aa),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START1_MODE1, 0x2aaab),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_ENABLE1, 0x90),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE1, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE1, 0x3414),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE1, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE1, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN1_MODE1, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE1, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE1, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE1, 0xb4),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CORECLK_DIV_MODE1, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_EP_DIV_MODE0, 0x19),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_EP_DIV_MODE1, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_ENABLE1, 0x90),
+};
+
+static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_HIGHZ_DRVR_EN, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x06),
+};
+
+static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_ENABLES, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0xe),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x70),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x73),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xc8),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb1),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xf0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x2),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0xd3),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x02),
+};
+
+static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_CNTRL2, 0x83),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_CNT_VAL_L, 0x9),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_CNT_VAL_H_TOL, 0x42),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_MAN_CODE, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_CNTRL1, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_H, 0x0),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x1),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_ACTIONS, 0x0),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_H, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_H, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG1, 0x11),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG2, 0xb),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG4, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG2, 0x52),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG2, 0x50),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG4, 0x1a),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG5, 0x6),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_G12S1_TXDEEMPH_M3P5DB, 0x10),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_DCC_CAL_CONFIG, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x0d),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qmp_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x007),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER2, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_EP_DIV, 0x19),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x90),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0d),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_MODE, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x33),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x7e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x15),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qmp_pcie_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qmp_pcie_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN_HALF, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x71),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_01, 0x59),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x71),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x40),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qmp_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE, 0x04),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_OSC_DTCT_ACTIONS, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xbb),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG1, 0x0d),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG4, 0x00),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qmp_pcie_pcs_misc_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_CONFIG2, 0x52),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG2, 0x10),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4, 0x1a),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5, 0x06),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_PCIE_INT_AUX_CLK_CONFIG1, 0x00),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qhp_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SYSCLK_EN_SEL, 0x27),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1, 0xde),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2, 0x07),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1_MODE1, 0x4c),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_BIAS_EN_CKBUFLR_EN, 0x18),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CLK_ENABLE1, 0xb0),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE0, 0x8c),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE0, 0x20),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE1, 0x14),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE1, 0x34),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_RESTRIM_CTRL2, 0x05),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP_EN, 0x42),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DEC_START_MODE1, 0x68),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE1, 0xab),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE1, 0xaa),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE1, 0x3f),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_VCO_TUNE_MAP, 0x10),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CLK_SELECT, 0x04),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_HSCLK_SEL1, 0x30),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CORECLK_DIV, 0x04),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CORE_CLK_EN, 0x73),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CMN_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SVS_MODE_CLK_SEL, 0x15),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CORECLK_DIV_MODE1, 0x04),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CMN_MODE, 0x01),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_VREGCLK_DIV1, 0x22),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_VREGCLK_DIV2, 0x00),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_BGV_TRIM, 0x20),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_BG_CTRL, 0x07),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qhp_pcie_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_CTRL0, 0x00),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_TAP_EN, 0x0d),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_TX_BAND_MODE, 0x01),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_LANE_MODE, 0x1a),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PARALLEL_RATE, 0x2f),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CML_CTRL_MODE0, 0x09),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CML_CTRL_MODE1, 0x09),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CML_CTRL_MODE2, 0x1b),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE1, 0x01),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE2, 0x07),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE0, 0x31),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE1, 0x31),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE2, 0x03),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CTLE_THRESH_DFE, 0x02),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CGA_THRESH_DFE, 0x00),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXENGINE_EN0, 0x12),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CTLE_TRAIN_TIME, 0x25),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CTLE_DFE_OVRLP_TIME, 0x00),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DFE_REFRESH_TIME, 0x05),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DFE_ENABLE_TIME, 0x01),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_VGA_GAIN, 0x26),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DFE_GAIN, 0x12),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_EQ_GAIN, 0x04),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_OFFSET_GAIN, 0x04),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PRE_GAIN, 0x09),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_EQ_INTVAL, 0x15),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_EDAC_INITVAL, 0x28),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXEQ_INITB0, 0x7f),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXEQ_INITB1, 0x07),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RCVRDONE_THRESH1, 0x04),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXEQ_CTRL, 0x70),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE0, 0x8b),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE1, 0x08),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE2, 0x0a),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE1, 0x04),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE2, 0x04),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_BAND, 0x02),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE0, 0x5c),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE1, 0x3e),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE2, 0x3f),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_SIGDET_ENABLES, 0x01),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_SIGDET_CNTRL, 0xa0),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_SIGDET_DEGLITCH_CNTRL, 0x08),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DCC_GAIN, 0x01),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_EN_SIGNAL, 0xc3),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PSM_RX_EN_CAL, 0x00),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_MISC_CNTRL0, 0xbc),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_TS0_TIMER, 0x7f),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DLL_HIGHDATARATE, 0x15),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_CTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RESETCODE_OFFSET, 0x04),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_VGA_INITVAL, 0x20),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RSM_START, 0x01),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qhp_pcie_rx_tbl[] = {
+};
+
+static const struct qmp_phy_init_tbl sdm845_qhp_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG, 0x3f),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_PCS_TX_RX_CONFIG, 0x50),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M3P5DB, 0x19),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M3P5DB, 0x07),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M6DB, 0x17),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M6DB, 0x09),
+ QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG5, 0x9f),
+};
+
+static const struct qmp_phy_init_tbl sc8180x_qmp_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x42),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0xb4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x68),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xaa),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xa2),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0x4c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_ENABLE1, 0x90),
+};
+
+static const struct qmp_phy_init_tbl sc8180x_qmp_pcie_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x5),
+};
+
+static const struct qmp_phy_init_tbl sc8180x_qmp_pcie_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_ENABLES, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x6e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x6e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x70),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x37),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xd4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xdb),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x39),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xe4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xec),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x39),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0xdb),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x75),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RCLK_AUXDATA_SEL, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x03),
+};
+
+static const struct qmp_phy_init_tbl sc8180x_qmp_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RATE_SLEW_CNTRL1, 0x0b),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x01),
+};
+
+static const struct qmp_phy_init_tbl sc8180x_qmp_pcie_pcs_misc_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_INT_AUX_CLK_CONFIG1, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_PRE, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_POST, 0x58),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x42),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0xb4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x68),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xaa),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xa2),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0x4c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_ENABLE1, 0x90),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_gen3x1_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x07),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_pcie_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x35),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_pcie_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x70),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_ENABLES, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xd4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xdb),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xe4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xec),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0x36),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_gen3x1_pcie_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RCLK_AUXDATA_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x30),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0x77),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RATE_SLEW_CNTRL1, 0x0b),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_gen3x1_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x12),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_pcie_pcs_misc_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P6_P7_PRE, 0x33),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_PRE, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_POST, 0x58),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_gen3x1_pcie_pcs_misc_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_INT_AUX_CLK_CONFIG1, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG2, 0x0f),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_gen3x2_pcie_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x20),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_gen3x2_pcie_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xbf),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_gen3x2_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x05),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG2, 0x0f),
+};
+
+static const struct qmp_phy_init_tbl sm8250_qmp_gen3x2_pcie_pcs_misc_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG2, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG4, 0x07),
+};
+
+static const struct qmp_phy_init_tbl sdx55_qmp_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BG_TIMER, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYS_CLK_CTRL, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x19),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x19),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x46),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_CFG, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x4b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x50),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0, 0xfb),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE1, 0xfb),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE1, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE0, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_CONFIG, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_MISC1, 0x88),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_MISC2, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_MODE, 0x17),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_DC_LEVEL_CTRL, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x56),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1d),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0x4b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x22),
+};
+
+static const struct qmp_phy_init_tbl sdx55_qmp_pcie_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_LANE_MODE_1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_LANE_MODE_2, 0xf6),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_LANE_MODE_3, 0x13),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_VMODE_CTRL1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_PI_QEC_CTRL, 0x00),
+};
+
+static const struct qmp_phy_init_tbl sdx55_qmp_pcie_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_FO_GAIN_RATE2, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_UCDR_PI_CONTROLS, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_AUX_DATA_TCOARSE_TFINE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_DFE_3, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_DFE_DAC_ENABLE1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_DFE_DAC_ENABLE2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_VGA_CAL_CNTRL2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x27),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B1, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B2, 0x5a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B3, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B4, 0x37),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B0, 0xbd),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B1, 0xf9),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B2, 0xbf),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B3, 0xce),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B4, 0x62),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B0, 0xbf),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B1, 0x7d),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B2, 0xbf),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B3, 0xcf),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B4, 0xd6),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_PHPRE_CTRL, 0xa0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_MARG_COARSE_CTRL2, 0x12),
+};
+
+static const struct qmp_phy_init_tbl sdx55_qmp_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_RX_SIGDET_LVL, 0x77),
+ QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_EQ_CONFIG2, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_EQ_CONFIG4, 0x16),
+ QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_EQ_CONFIG5, 0x02),
+};
+
+static const struct qmp_phy_init_tbl sdx55_qmp_pcie_pcs_misc_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_EQ_CONFIG1, 0x17),
+ QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_G3_RXEQEVAL_TIME, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_G4_RXEQEVAL_TIME, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_G4_EQ_CONFIG2, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_G4_EQ_CONFIG5, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_LANE1_INSIG_SW_CTRL2, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_LANE1_INSIG_MX_CTRL2, 0x00),
+};
+
+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),
+};
+
+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_cfg - per-PHY initialization config */
+struct qmp_phy_cfg {
+ int lanes;
+
+ /* Init sequence for PHY blocks - serdes, tx, rx, pcs */
+ const struct qmp_phy_init_tbl *serdes_tbl;
+ int serdes_tbl_num;
+ const struct qmp_phy_init_tbl *serdes_tbl_sec;
+ int serdes_tbl_num_sec;
+ const struct qmp_phy_init_tbl *tx_tbl;
+ int tx_tbl_num;
+ const struct qmp_phy_init_tbl *tx_tbl_sec;
+ int tx_tbl_num_sec;
+ const struct qmp_phy_init_tbl *rx_tbl;
+ int rx_tbl_num;
+ const struct qmp_phy_init_tbl *rx_tbl_sec;
+ int rx_tbl_num_sec;
+ const struct qmp_phy_init_tbl *pcs_tbl;
+ int pcs_tbl_num;
+ const struct qmp_phy_init_tbl *pcs_tbl_sec;
+ int pcs_tbl_num_sec;
+ const struct qmp_phy_init_tbl *pcs_misc_tbl;
+ int pcs_misc_tbl_num;
+ const struct qmp_phy_init_tbl *pcs_misc_tbl_sec;
+ int pcs_misc_tbl_num_sec;
+
+ /* clock ids to be requested */
+ const char * const *clk_list;
+ int num_clks;
+ /* resets to be requested */
+ const char * const *reset_list;
+ int num_resets;
+ /* regulators to be requested */
+ const char * const *vreg_list;
+ int num_vregs;
+
+ /* array of registers with different offsets */
+ const unsigned int *regs;
+
+ unsigned int start_ctrl;
+ unsigned int pwrdn_ctrl;
+ /* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */
+ unsigned int phy_status;
+
+ /* true, if PHY needs delay after POWER_DOWN */
+ bool has_pwrdn_delay;
+ /* power_down delay in usec */
+ int pwrdn_delay_min;
+ int pwrdn_delay_max;
+
+ /* QMP PHY pipe clock interface rate */
+ unsigned long pipe_clock_rate;
+};
+
+/**
+ * struct qmp_phy - per-lane phy descriptor
+ *
+ * @phy: generic phy
+ * @cfg: phy specific configuration
+ * @serdes: iomapped memory space for phy's serdes (i.e. PLL)
+ * @tx: iomapped memory space for lane's tx
+ * @rx: iomapped memory space for lane's rx
+ * @pcs: iomapped memory space for lane's pcs
+ * @tx2: iomapped memory space for second lane's tx (in dual lane PHYs)
+ * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs)
+ * @pcs_misc: iomapped memory space for lane's pcs_misc
+ * @pipe_clk: pipe clock
+ * @qmp: QMP phy to which this lane belongs
+ */
+struct qmp_phy {
+ struct phy *phy;
+ const struct qmp_phy_cfg *cfg;
+ void __iomem *serdes;
+ void __iomem *tx;
+ void __iomem *rx;
+ void __iomem *pcs;
+ void __iomem *tx2;
+ void __iomem *rx2;
+ void __iomem *pcs_misc;
+ struct clk *pipe_clk;
+ struct qcom_qmp *qmp;
+};
+
+/**
+ * struct qcom_qmp - structure holding QMP phy block attributes
+ *
+ * @dev: device
+ *
+ * @clks: array of clocks required by phy
+ * @resets: array of resets required by phy
+ * @vregs: regulator supplies bulk data
+ *
+ * @phys: array of per-lane phy descriptors
+ */
+struct qcom_qmp {
+ struct device *dev;
+
+ struct clk_bulk_data *clks;
+ struct reset_control_bulk_data *resets;
+ struct regulator_bulk_data *vregs;
+
+ struct qmp_phy **phys;
+};
+
+static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg |= val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg &= ~val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+/* list of clocks required by phy */
+static const char * const msm8996_phy_clk_l[] = {
+ "aux", "cfg_ahb", "ref",
+};
+
+
+static const char * const sdm845_pciephy_clk_l[] = {
+ "aux", "cfg_ahb", "ref", "refgen",
+};
+
+/* list of regulators */
+static const char * const qmp_phy_vreg_l[] = {
+ "vdda-phy", "vdda-pll",
+};
+
+static const char * const ipq8074_pciephy_clk_l[] = {
+ "aux", "cfg_ahb",
+};
+
+/* list of resets */
+static const char * const ipq8074_pciephy_reset_l[] = {
+ "phy", "common",
+};
+
+static const char * const sdm845_pciephy_reset_l[] = {
+ "phy",
+};
+
+static const struct qmp_phy_cfg ipq8074_pciephy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = ipq8074_pcie_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(ipq8074_pcie_serdes_tbl),
+ .tx_tbl = ipq8074_pcie_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(ipq8074_pcie_tx_tbl),
+ .rx_tbl = ipq8074_pcie_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(ipq8074_pcie_rx_tbl),
+ .pcs_tbl = ipq8074_pcie_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(ipq8074_pcie_pcs_tbl),
+ .clk_list = ipq8074_pciephy_clk_l,
+ .num_clks = ARRAY_SIZE(ipq8074_pciephy_clk_l),
+ .reset_list = ipq8074_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l),
+ .vreg_list = NULL,
+ .num_vregs = 0,
+ .regs = pciephy_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 ipq8074_pciephy_gen3_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = ipq8074_pcie_gen3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(ipq8074_pcie_gen3_serdes_tbl),
+ .tx_tbl = ipq8074_pcie_gen3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(ipq8074_pcie_gen3_tx_tbl),
+ .rx_tbl = ipq8074_pcie_gen3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(ipq8074_pcie_gen3_rx_tbl),
+ .pcs_tbl = ipq8074_pcie_gen3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(ipq8074_pcie_gen3_pcs_tbl),
+ .clk_list = ipq8074_pciephy_clk_l,
+ .num_clks = ARRAY_SIZE(ipq8074_pciephy_clk_l),
+ .reset_list = ipq8074_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l),
+ .vreg_list = NULL,
+ .num_vregs = 0,
+ .regs = ipq_pciephy_gen3_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = 995, /* us */
+ .pwrdn_delay_max = 1005, /* us */
+
+ .pipe_clock_rate = 250000000,
+};
+
+static const struct qmp_phy_cfg ipq6018_pciephy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = ipq6018_pcie_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(ipq6018_pcie_serdes_tbl),
+ .tx_tbl = ipq6018_pcie_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(ipq6018_pcie_tx_tbl),
+ .rx_tbl = ipq6018_pcie_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(ipq6018_pcie_rx_tbl),
+ .pcs_tbl = ipq6018_pcie_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(ipq6018_pcie_pcs_tbl),
+ .pcs_misc_tbl = ipq6018_pcie_pcs_misc_tbl,
+ .pcs_misc_tbl_num = ARRAY_SIZE(ipq6018_pcie_pcs_misc_tbl),
+ .clk_list = ipq8074_pciephy_clk_l,
+ .num_clks = ARRAY_SIZE(ipq8074_pciephy_clk_l),
+ .reset_list = ipq8074_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l),
+ .vreg_list = NULL,
+ .num_vregs = 0,
+ .regs = ipq_pciephy_gen3_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = 995, /* us */
+ .pwrdn_delay_max = 1005, /* us */
+};
+
+static const struct qmp_phy_cfg sdm845_qmp_pciephy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = sdm845_qmp_pcie_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_serdes_tbl),
+ .tx_tbl = sdm845_qmp_pcie_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_tx_tbl),
+ .rx_tbl = sdm845_qmp_pcie_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_rx_tbl),
+ .pcs_tbl = sdm845_qmp_pcie_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_pcs_tbl),
+ .pcs_misc_tbl = sdm845_qmp_pcie_pcs_misc_tbl,
+ .pcs_misc_tbl_num = ARRAY_SIZE(sdm845_qmp_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 = sdm845_qmp_pciephy_regs_layout,
+
+ .start_ctrl = PCS_START | SERDES_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 sdm845_qhp_pciephy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = sdm845_qhp_pcie_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sdm845_qhp_pcie_serdes_tbl),
+ .tx_tbl = sdm845_qhp_pcie_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sdm845_qhp_pcie_tx_tbl),
+ .rx_tbl = sdm845_qhp_pcie_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sdm845_qhp_pcie_rx_tbl),
+ .pcs_tbl = sdm845_qhp_pcie_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sdm845_qhp_pcie_pcs_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 = sdm845_qhp_pciephy_regs_layout,
+
+ .start_ctrl = PCS_START | SERDES_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 sm8250_qmp_gen3x1_pciephy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = sm8250_qmp_pcie_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_serdes_tbl),
+ .serdes_tbl_sec = sm8250_qmp_gen3x1_pcie_serdes_tbl,
+ .serdes_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x1_pcie_serdes_tbl),
+ .tx_tbl = sm8250_qmp_pcie_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_tx_tbl),
+ .rx_tbl = sm8250_qmp_pcie_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_rx_tbl),
+ .rx_tbl_sec = sm8250_qmp_gen3x1_pcie_rx_tbl,
+ .rx_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x1_pcie_rx_tbl),
+ .pcs_tbl = sm8250_qmp_pcie_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_pcs_tbl),
+ .pcs_tbl_sec = sm8250_qmp_gen3x1_pcie_pcs_tbl,
+ .pcs_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x1_pcie_pcs_tbl),
+ .pcs_misc_tbl = sm8250_qmp_pcie_pcs_misc_tbl,
+ .pcs_misc_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_pcs_misc_tbl),
+ .pcs_misc_tbl_sec = sm8250_qmp_gen3x1_pcie_pcs_misc_tbl,
+ .pcs_misc_tbl_num_sec = ARRAY_SIZE(sm8250_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 = PCS_START | SERDES_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 sm8250_qmp_gen3x2_pciephy_cfg = {
+ .lanes = 2,
+
+ .serdes_tbl = sm8250_qmp_pcie_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_serdes_tbl),
+ .tx_tbl = sm8250_qmp_pcie_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_tx_tbl),
+ .tx_tbl_sec = sm8250_qmp_gen3x2_pcie_tx_tbl,
+ .tx_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x2_pcie_tx_tbl),
+ .rx_tbl = sm8250_qmp_pcie_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_rx_tbl),
+ .rx_tbl_sec = sm8250_qmp_gen3x2_pcie_rx_tbl,
+ .rx_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x2_pcie_rx_tbl),
+ .pcs_tbl = sm8250_qmp_pcie_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_pcs_tbl),
+ .pcs_tbl_sec = sm8250_qmp_gen3x2_pcie_pcs_tbl,
+ .pcs_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x2_pcie_pcs_tbl),
+ .pcs_misc_tbl = sm8250_qmp_pcie_pcs_misc_tbl,
+ .pcs_misc_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_pcs_misc_tbl),
+ .pcs_misc_tbl_sec = sm8250_qmp_gen3x2_pcie_pcs_misc_tbl,
+ .pcs_misc_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x2_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 = PCS_START | SERDES_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 msm8998_pciephy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = msm8998_pcie_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(msm8998_pcie_serdes_tbl),
+ .tx_tbl = msm8998_pcie_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(msm8998_pcie_tx_tbl),
+ .rx_tbl = msm8998_pcie_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(msm8998_pcie_rx_tbl),
+ .pcs_tbl = msm8998_pcie_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(msm8998_pcie_pcs_tbl),
+ .clk_list = msm8996_phy_clk_l,
+ .num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
+ .reset_list = ipq8074_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = pciephy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+ .phy_status = PHYSTATUS,
+};
+
+static const struct qmp_phy_cfg sc8180x_pciephy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = sc8180x_qmp_pcie_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_serdes_tbl),
+ .tx_tbl = sc8180x_qmp_pcie_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_tx_tbl),
+ .rx_tbl = sc8180x_qmp_pcie_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_rx_tbl),
+ .pcs_tbl = sc8180x_qmp_pcie_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_pcs_tbl),
+ .pcs_misc_tbl = sc8180x_qmp_pcie_pcs_misc_tbl,
+ .pcs_misc_tbl_num = ARRAY_SIZE(sc8180x_qmp_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 = PCS_START | SERDES_START,
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = 995, /* us */
+ .pwrdn_delay_max = 1005, /* us */
+};
+
+static const struct qmp_phy_cfg sdx55_qmp_pciephy_cfg = {
+ .lanes = 2,
+
+ .serdes_tbl = sdx55_qmp_pcie_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_serdes_tbl),
+ .tx_tbl = sdx55_qmp_pcie_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_tx_tbl),
+ .rx_tbl = sdx55_qmp_pcie_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_rx_tbl),
+ .pcs_tbl = sdx55_qmp_pcie_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_pcs_tbl),
+ .pcs_misc_tbl = sdx55_qmp_pcie_pcs_misc_tbl,
+ .pcs_misc_tbl_num = ARRAY_SIZE(sdx55_qmp_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 = PCS_START | SERDES_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS_4_20,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = 995, /* us */
+ .pwrdn_delay_max = 1005, /* us */
+};
+
+static const struct qmp_phy_cfg sm8450_qmp_gen3x1_pciephy_cfg = {
+ .lanes = 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 = {
+ .lanes = 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,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = 995, /* us */
+ .pwrdn_delay_max = 1005, /* us */
+};
+
+static void qmp_pcie_configure_lane(void __iomem *base,
+ const unsigned int *regs,
+ const struct qmp_phy_init_tbl tbl[],
+ int num,
+ u8 lane_mask)
+{
+ int i;
+ const struct qmp_phy_init_tbl *t = tbl;
+
+ if (!t)
+ return;
+
+ for (i = 0; i < num; i++, t++) {
+ if (!(t->lane_mask & lane_mask))
+ continue;
+
+ if (t->in_layout)
+ writel(t->val, base + regs[t->offset]);
+ else
+ writel(t->val, base + t->offset);
+ }
+}
+
+static void qmp_pcie_configure(void __iomem *base,
+ const unsigned int *regs,
+ const struct qmp_phy_init_tbl tbl[],
+ int num)
+{
+ qmp_pcie_configure_lane(base, regs, tbl, num, 0xff);
+}
+
+static int qmp_pcie_serdes_init(struct qmp_phy *qphy)
+{
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *serdes = qphy->serdes;
+ const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl;
+ int serdes_tbl_num = cfg->serdes_tbl_num;
+
+ qmp_pcie_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num);
+ qmp_pcie_configure(serdes, cfg->regs, cfg->serdes_tbl_sec, cfg->serdes_tbl_num_sec);
+
+ return 0;
+}
+
+static int qmp_pcie_init(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *pcs = qphy->pcs;
+ int ret;
+
+ /* turn on regulator supplies */
+ ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
+ if (ret) {
+ dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+ if (ret) {
+ dev_err(qmp->dev, "reset assert failed\n");
+ goto err_disable_regulators;
+ }
+
+ ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets);
+ if (ret) {
+ dev_err(qmp->dev, "reset deassert failed\n");
+ goto err_disable_regulators;
+ }
+
+ ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
+ if (ret)
+ goto err_assert_reset;
+
+ if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL])
+ qphy_setbits(pcs,
+ cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ cfg->pwrdn_ctrl);
+ else
+ qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
+ cfg->pwrdn_ctrl);
+
+ return 0;
+
+err_assert_reset:
+ reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+err_disable_regulators:
+ regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+
+ return ret;
+}
+
+static int qmp_pcie_exit(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+
+ regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+
+ return 0;
+}
+
+static int qmp_pcie_power_on(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *tx = qphy->tx;
+ void __iomem *rx = qphy->rx;
+ void __iomem *pcs = qphy->pcs;
+ void __iomem *pcs_misc = qphy->pcs_misc;
+ void __iomem *status;
+ unsigned int mask, val, ready;
+ int ret;
+
+ qmp_pcie_serdes_init(qphy);
+
+ ret = clk_prepare_enable(qphy->pipe_clk);
+ if (ret) {
+ dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret);
+ return ret;
+ }
+
+ /* Tx, Rx, and PCS configurations */
+ qmp_pcie_configure_lane(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num, 1);
+ qmp_pcie_configure_lane(tx, cfg->regs, cfg->tx_tbl_sec, cfg->tx_tbl_num_sec, 1);
+
+ if (cfg->lanes >= 2) {
+ qmp_pcie_configure_lane(qphy->tx2, cfg->regs, cfg->tx_tbl,
+ cfg->tx_tbl_num, 2);
+ qmp_pcie_configure_lane(qphy->tx2, cfg->regs, cfg->tx_tbl_sec,
+ cfg->tx_tbl_num_sec, 2);
+ }
+
+ qmp_pcie_configure_lane(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 1);
+ qmp_pcie_configure_lane(rx, cfg->regs, cfg->rx_tbl_sec, cfg->rx_tbl_num_sec, 1);
+
+ if (cfg->lanes >= 2) {
+ qmp_pcie_configure_lane(qphy->rx2, cfg->regs, cfg->rx_tbl,
+ cfg->rx_tbl_num, 2);
+ qmp_pcie_configure_lane(qphy->rx2, cfg->regs, cfg->rx_tbl_sec,
+ cfg->rx_tbl_num_sec, 2);
+ }
+
+ qmp_pcie_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
+ qmp_pcie_configure(pcs, cfg->regs, cfg->pcs_tbl_sec, cfg->pcs_tbl_num_sec);
+
+ qmp_pcie_configure(pcs_misc, cfg->regs, cfg->pcs_misc_tbl, cfg->pcs_misc_tbl_num);
+ qmp_pcie_configure(pcs_misc, cfg->regs, cfg->pcs_misc_tbl_sec, cfg->pcs_misc_tbl_num_sec);
+
+ /*
+ * Pull out PHY from POWER DOWN state.
+ * This is active low enable signal to power-down PHY.
+ */
+ qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
+
+ if (cfg->has_pwrdn_delay)
+ usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
+
+ /* Pull PHY out of reset state */
+ qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+
+ /* start SerDes and Phy-Coding-Sublayer */
+ qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
+
+ status = pcs + cfg->regs[QPHY_PCS_STATUS];
+ mask = cfg->phy_status;
+ ready = 0;
+
+ ret = readl_poll_timeout(status, val, (val & mask) == ready, 10,
+ PHY_INIT_COMPLETE_TIMEOUT);
+ if (ret) {
+ dev_err(qmp->dev, "phy initialization timed-out\n");
+ goto err_disable_pipe_clk;
+ }
+
+ return 0;
+
+err_disable_pipe_clk:
+ clk_disable_unprepare(qphy->pipe_clk);
+
+ return ret;
+}
+
+static int qmp_pcie_power_off(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ clk_disable_unprepare(qphy->pipe_clk);
+
+ /* PHY reset */
+ qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+
+ /* stop SerDes and Phy-Coding-Sublayer */
+ qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
+
+ /* Put PHY into POWER DOWN state: active low */
+ if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) {
+ qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ cfg->pwrdn_ctrl);
+ } else {
+ qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
+ cfg->pwrdn_ctrl);
+ }
+
+ return 0;
+}
+
+static int qmp_pcie_enable(struct phy *phy)
+{
+ int ret;
+
+ ret = qmp_pcie_init(phy);
+ if (ret)
+ return ret;
+
+ ret = qmp_pcie_power_on(phy);
+ if (ret)
+ qmp_pcie_exit(phy);
+
+ return ret;
+}
+
+static int qmp_pcie_disable(struct phy *phy)
+{
+ int ret;
+
+ ret = qmp_pcie_power_off(phy);
+ if (ret)
+ return ret;
+
+ return qmp_pcie_exit(phy);
+}
+
+static int qmp_pcie_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int num = cfg->num_vregs;
+ int i;
+
+ qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL);
+ if (!qmp->vregs)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ qmp->vregs[i].supply = cfg->vreg_list[i];
+
+ return devm_regulator_bulk_get(dev, num, qmp->vregs);
+}
+
+static int qmp_pcie_reset_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int i;
+ int ret;
+
+ qmp->resets = devm_kcalloc(dev, cfg->num_resets,
+ sizeof(*qmp->resets), GFP_KERNEL);
+ if (!qmp->resets)
+ return -ENOMEM;
+
+ for (i = 0; i < cfg->num_resets; i++)
+ qmp->resets[i].id = cfg->reset_list[i];
+
+ ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets, qmp->resets);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get resets\n");
+
+ return 0;
+}
+
+static int qmp_pcie_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int num = cfg->num_clks;
+ int i;
+
+ qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
+ if (!qmp->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ qmp->clks[i].id = cfg->clk_list[i];
+
+ return devm_clk_bulk_get(dev, num, qmp->clks);
+}
+
+static void phy_clk_release_provider(void *res)
+{
+ of_clk_del_provider(res);
+}
+
+/*
+ * Register a fixed rate pipe clock.
+ *
+ * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate
+ * controls it. The <s>_pipe_clk coming out of the GCC is requested
+ * by the PHY driver for its operations.
+ * We register the <s>_pipe_clksrc here. The gcc driver takes care
+ * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk.
+ * Below picture shows this relationship.
+ *
+ * +---------------+
+ * | PHY block |<<---------------------------------------+
+ * | | |
+ * | +-------+ | +-----+ |
+ * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+
+ * clk | +-------+ | +-----+
+ * +---------------+
+ */
+static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
+{
+ struct clk_fixed_rate *fixed;
+ struct clk_init_data init = { };
+ int ret;
+
+ ret = of_property_read_string(np, "clock-output-names", &init.name);
+ if (ret) {
+ dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np);
+ return ret;
+ }
+
+ fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL);
+ if (!fixed)
+ return -ENOMEM;
+
+ init.ops = &clk_fixed_rate_ops;
+
+ /*
+ * Controllers using QMP PHY-s use 125MHz pipe clock interface
+ * unless other frequency is specified in the PHY config.
+ */
+ if (qmp->phys[0]->cfg->pipe_clock_rate)
+ fixed->fixed_rate = qmp->phys[0]->cfg->pipe_clock_rate;
+ else
+ fixed->fixed_rate = 125000000;
+
+ fixed->hw.init = &init;
+
+ ret = devm_clk_hw_register(qmp->dev, &fixed->hw);
+ if (ret)
+ return ret;
+
+ ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw);
+ if (ret)
+ return ret;
+
+ /*
+ * Roll a devm action because the clock provider is the child node, but
+ * the child node is not actually a device.
+ */
+ return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np);
+}
+
+static const struct phy_ops qmp_pcie_ops = {
+ .power_on = qmp_pcie_enable,
+ .power_off = qmp_pcie_disable,
+ .owner = THIS_MODULE,
+};
+
+static int qmp_pcie_create(struct device *dev, struct device_node *np, int id,
+ void __iomem *serdes, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ struct phy *generic_phy;
+ struct qmp_phy *qphy;
+ int ret;
+
+ qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
+ if (!qphy)
+ return -ENOMEM;
+
+ qphy->cfg = cfg;
+ qphy->serdes = serdes;
+ /*
+ * Get memory resources for each phy lane:
+ * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
+ * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5
+ * For single lane PHYs: pcs_misc (optional) -> 3.
+ */
+ qphy->tx = devm_of_iomap(dev, np, 0, NULL);
+ if (IS_ERR(qphy->tx))
+ return PTR_ERR(qphy->tx);
+
+ if (of_device_is_compatible(dev->of_node, "qcom,sdm845-qhp-pcie-phy"))
+ qphy->rx = qphy->tx;
+ else
+ qphy->rx = devm_of_iomap(dev, np, 1, NULL);
+ if (IS_ERR(qphy->rx))
+ return PTR_ERR(qphy->rx);
+
+ qphy->pcs = devm_of_iomap(dev, np, 2, NULL);
+ if (IS_ERR(qphy->pcs))
+ return PTR_ERR(qphy->pcs);
+
+ if (cfg->lanes >= 2) {
+ qphy->tx2 = devm_of_iomap(dev, np, 3, NULL);
+ if (IS_ERR(qphy->tx2))
+ return PTR_ERR(qphy->tx2);
+
+ qphy->rx2 = devm_of_iomap(dev, np, 4, NULL);
+ if (IS_ERR(qphy->rx2))
+ return PTR_ERR(qphy->rx2);
+
+ qphy->pcs_misc = devm_of_iomap(dev, np, 5, NULL);
+ } else {
+ qphy->pcs_misc = devm_of_iomap(dev, np, 3, NULL);
+ }
+
+ if (IS_ERR(qphy->pcs_misc) &&
+ of_device_is_compatible(dev->of_node, "qcom,ipq6018-qmp-pcie-phy"))
+ qphy->pcs_misc = qphy->pcs + 0x400;
+
+ if (IS_ERR(qphy->pcs_misc)) {
+ if (cfg->pcs_misc_tbl || cfg->pcs_misc_tbl_sec)
+ return PTR_ERR(qphy->pcs_misc);
+ }
+
+ qphy->pipe_clk = devm_get_clk_from_child(dev, np, NULL);
+ if (IS_ERR(qphy->pipe_clk)) {
+ return dev_err_probe(dev, PTR_ERR(qphy->pipe_clk),
+ "failed to get lane%d pipe clock\n", id);
+ }
+
+ generic_phy = devm_phy_create(dev, np, &qmp_pcie_ops);
+ if (IS_ERR(generic_phy)) {
+ ret = PTR_ERR(generic_phy);
+ dev_err(dev, "failed to create qphy %d\n", ret);
+ return ret;
+ }
+
+ qphy->phy = generic_phy;
+ qphy->qmp = qmp;
+ qmp->phys[id] = qphy;
+ phy_set_drvdata(generic_phy, qphy);
+
+ return 0;
+}
+
+static const struct of_device_id qmp_pcie_of_match_table[] = {
+ {
+ .compatible = "qcom,msm8998-qmp-pcie-phy",
+ .data = &msm8998_pciephy_cfg,
+ }, {
+ .compatible = "qcom,ipq8074-qmp-pcie-phy",
+ .data = &ipq8074_pciephy_cfg,
+ }, {
+ .compatible = "qcom,ipq8074-qmp-gen3-pcie-phy",
+ .data = &ipq8074_pciephy_gen3_cfg,
+ }, {
+ .compatible = "qcom,ipq6018-qmp-pcie-phy",
+ .data = &ipq6018_pciephy_cfg,
+ }, {
+ .compatible = "qcom,sc8180x-qmp-pcie-phy",
+ .data = &sc8180x_pciephy_cfg,
+ }, {
+ .compatible = "qcom,sdm845-qhp-pcie-phy",
+ .data = &sdm845_qhp_pciephy_cfg,
+ }, {
+ .compatible = "qcom,sdm845-qmp-pcie-phy",
+ .data = &sdm845_qmp_pciephy_cfg,
+ }, {
+ .compatible = "qcom,sm8250-qmp-gen3x1-pcie-phy",
+ .data = &sm8250_qmp_gen3x1_pciephy_cfg,
+ }, {
+ .compatible = "qcom,sm8250-qmp-gen3x2-pcie-phy",
+ .data = &sm8250_qmp_gen3x2_pciephy_cfg,
+ }, {
+ .compatible = "qcom,sm8250-qmp-modem-pcie-phy",
+ .data = &sm8250_qmp_gen3x2_pciephy_cfg,
+ }, {
+ .compatible = "qcom,sdx55-qmp-pcie-phy",
+ .data = &sdx55_qmp_pciephy_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,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qmp_pcie_of_match_table);
+
+static int qmp_pcie_probe(struct platform_device *pdev)
+{
+ struct qcom_qmp *qmp;
+ struct device *dev = &pdev->dev;
+ struct device_node *child;
+ struct phy_provider *phy_provider;
+ void __iomem *serdes;
+ const struct qmp_phy_cfg *cfg = NULL;
+ int num, id;
+ int ret;
+
+ qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL);
+ if (!qmp)
+ return -ENOMEM;
+
+ qmp->dev = dev;
+ dev_set_drvdata(dev, qmp);
+
+ /* Get the specific init parameters of QMP phy */
+ cfg = of_device_get_match_data(dev);
+ if (!cfg)
+ return -EINVAL;
+
+ /* per PHY serdes; usually located at base address */
+ serdes = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(serdes))
+ return PTR_ERR(serdes);
+
+ ret = qmp_pcie_clk_init(dev, cfg);
+ if (ret)
+ return ret;
+
+ ret = qmp_pcie_reset_init(dev, cfg);
+ if (ret)
+ return ret;
+
+ ret = qmp_pcie_vreg_init(dev, cfg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get regulator supplies\n");
+
+ num = of_get_available_child_count(dev->of_node);
+ /* do we have a rogue child node ? */
+ if (num > 1)
+ return -EINVAL;
+
+ qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL);
+ if (!qmp->phys)
+ return -ENOMEM;
+
+ id = 0;
+ for_each_available_child_of_node(dev->of_node, child) {
+ /* Create per-lane phy */
+ ret = qmp_pcie_create(dev, child, id, serdes, cfg);
+ if (ret) {
+ dev_err(dev, "failed to create lane%d phy, %d\n",
+ id, ret);
+ goto err_node_put;
+ }
+
+ /*
+ * Register the pipe clock provided by phy.
+ * See function description to see details of this pipe clock.
+ */
+ ret = phy_pipe_clk_register(qmp, child);
+ if (ret) {
+ dev_err(qmp->dev,
+ "failed to register pipe clock source\n");
+ goto err_node_put;
+ }
+
+ id++;
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+
+err_node_put:
+ of_node_put(child);
+ return ret;
+}
+
+static struct platform_driver qmp_pcie_driver = {
+ .probe = qmp_pcie_probe,
+ .driver = {
+ .name = "qcom-qmp-pcie-phy",
+ .of_match_table = qmp_pcie_of_match_table,
+ },
+};
+
+module_platform_driver(qmp_pcie_driver);
+
+MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm QMP PCIe PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-misc-v3.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-misc-v3.h
new file mode 100644
index 000000000000..a45bd301bc9e
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-misc-v3.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_MISC_V3_H_
+#define QCOM_PHY_QMP_PCS_MISC_V3_H_
+
+/* Only for QMP V3 PHY - PCS_MISC registers */
+#define QPHY_V3_PCS_MISC_CLAMP_ENABLE 0x0c
+#define QPHY_V3_PCS_MISC_OSC_DTCT_CONFIG2 0x2c
+#define QPHY_V3_PCS_MISC_PCIE_INT_AUX_CLK_CONFIG1 0x44
+#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG2 0x54
+#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4 0x5c
+#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5 0x60
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4.h
new file mode 100644
index 000000000000..4cc02288d418
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_PCIE_V4_H_
+#define QCOM_PHY_QMP_PCS_PCIE_V4_H_
+
+/* Only for QMP V4 PHY - PCS_PCIE registers (same as PCS_MISC?) */
+#define QPHY_V4_PCS_PCIE_INT_AUX_CLK_STATUS 0x00
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_STATUS 0x04
+#define QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG1 0x08
+#define QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG2 0x0c
+#define QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG3 0x10
+#define QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG4 0x14
+#define QPHY_V4_PCS_PCIE_PCS_TX_RX_CONFIG 0x18
+#define QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x1c
+#define QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_CNTRL 0x20
+#define QPHY_V4_PCS_PCIE_EPCLK_PRE_PLL_LOCK_DLY_AUXCLK 0x24
+#define QPHY_V4_PCS_PCIE_EPCLK_DLY_COUNT_VAL_L 0x28
+#define QPHY_V4_PCS_PCIE_EPCLK_DLY_COUNT_VAL_H 0x2c
+#define QPHY_V4_PCS_PCIE_RX_IDLE_DTCT_CNTRL1 0x30
+#define QPHY_V4_PCS_PCIE_RX_IDLE_DTCT_CNTRL2 0x34
+#define QPHY_V4_PCS_PCIE_SIGDET_CNTRL 0x38
+#define QPHY_V4_PCS_PCIE_SIGDET_LOW_2_IDLE_TIME 0x3c
+#define QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L 0x40
+#define QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_H 0x44
+#define QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L 0x48
+#define QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_H 0x4c
+#define QPHY_V4_PCS_PCIE_INT_AUX_CLK_CONFIG1 0x50
+#define QPHY_V4_PCS_PCIE_INT_AUX_CLK_CONFIG2 0x54
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG1 0x58
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG2 0x5c
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG3 0x60
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG4 0x64
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG5 0x68
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG6 0x6c
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG7 0x70
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG1 0x74
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG2 0x78
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG3 0x7c
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG4 0x80
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG5 0x84
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG6 0x88
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG7 0x8c
+#define QPHY_V4_PCS_PCIE_OSC_DTCT_ACTIONS 0x90
+#define QPHY_V4_PCS_PCIE_LOCAL_FS 0x94
+#define QPHY_V4_PCS_PCIE_LOCAL_LF 0x98
+#define QPHY_V4_PCS_PCIE_LOCAL_FS_RS 0x9c
+#define QPHY_V4_PCS_PCIE_EQ_CONFIG1 0xa0
+#define QPHY_V4_PCS_PCIE_EQ_CONFIG2 0xa4
+#define QPHY_V4_PCS_PCIE_PRESET_P0_P1_PRE 0xa8
+#define QPHY_V4_PCS_PCIE_PRESET_P2_P3_PRE 0xac
+#define QPHY_V4_PCS_PCIE_PRESET_P4_P5_PRE 0xb0
+#define QPHY_V4_PCS_PCIE_PRESET_P6_P7_PRE 0xb4
+#define QPHY_V4_PCS_PCIE_PRESET_P8_P9_PRE 0xb8
+#define QPHY_V4_PCS_PCIE_PRESET_P10_PRE 0xbc
+#define QPHY_V4_PCS_PCIE_PRESET_P1_P3_PRE_RS 0xc0
+#define QPHY_V4_PCS_PCIE_PRESET_P4_P5_PRE_RS 0xc4
+#define QPHY_V4_PCS_PCIE_PRESET_P6_P9_PRE_RS 0xc8
+#define QPHY_V4_PCS_PCIE_PRESET_P0_P1_POST 0xcc
+#define QPHY_V4_PCS_PCIE_PRESET_P2_P3_POST 0xd0
+#define QPHY_V4_PCS_PCIE_PRESET_P4_P5_POST 0xd4
+#define QPHY_V4_PCS_PCIE_PRESET_P6_P7_POST 0xd8
+#define QPHY_V4_PCS_PCIE_PRESET_P8_P9_POST 0xdc
+#define QPHY_V4_PCS_PCIE_PRESET_P10_POST 0xe0
+#define QPHY_V4_PCS_PCIE_PRESET_P1_P3_POST_RS 0xe4
+#define QPHY_V4_PCS_PCIE_PRESET_P4_P5_POST_RS 0xe8
+#define QPHY_V4_PCS_PCIE_PRESET_P6_P9_POST_RS 0xec
+#define QPHY_V4_PCS_PCIE_RXEQEVAL_TIME 0xf0
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4_20.h
new file mode 100644
index 000000000000..af273602998e
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4_20.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_PCIE_V4_20_H_
+#define QCOM_PHY_QMP_PCS_PCIE_V4_20_H_
+
+#define QPHY_V4_20_PCS_PCIE_EQ_CONFIG1 0x0a0
+#define QPHY_V4_20_PCS_PCIE_G3_RXEQEVAL_TIME 0x0f0
+#define QPHY_V4_20_PCS_PCIE_G4_RXEQEVAL_TIME 0x0f4
+#define QPHY_V4_20_PCS_PCIE_G4_EQ_CONFIG2 0x0fc
+#define QPHY_V4_20_PCS_PCIE_G4_EQ_CONFIG5 0x108
+#define QPHY_V4_20_PCS_LANE1_INSIG_SW_CTRL2 0x824
+#define QPHY_V4_20_PCS_LANE1_INSIG_MX_CTRL2 0x828
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5.h
new file mode 100644
index 000000000000..2e19fb3f051e
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5.h
@@ -0,0 +1,16 @@
+/* Only for QMP V5 PHY - PCS_PCIE registers */
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_PCIE_V5_H_
+#define QCOM_PHY_QMP_PCS_PCIE_V5_H_
+
+/* 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
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5_20.h
new file mode 100644
index 000000000000..1eedf50cf9cb
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5_20.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_PCIE_V5_20_H_
+#define QCOM_PHY_QMP_PCS_PCIE_V5_20_H_
+
+/* 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
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v3.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v3.h
new file mode 100644
index 000000000000..ba1ea29d2884
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v3.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_UFS_V3_H_
+#define QCOM_PHY_QMP_PCS_UFS_V3_H_
+
+#define QPHY_V3_PCS_UFS_TX_LARGE_AMP_DRV_LVL 0x02c
+#define QPHY_V3_PCS_UFS_TX_SMALL_AMP_DRV_LVL 0x034
+#define QPHY_V3_PCS_UFS_RX_SYM_RESYNC_CTRL 0x134
+#define QPHY_V3_PCS_UFS_RX_MIN_HIBERN8_TIME 0x138
+#define QPHY_V3_PCS_UFS_RX_SIGDET_CTRL1 0x13c
+#define QPHY_V3_PCS_UFS_RX_SIGDET_CTRL2 0x140
+#define QPHY_V3_PCS_UFS_TX_MID_TERM_CTRL1 0x1bc
+#define QPHY_V3_PCS_UFS_MULTI_LANE_CTRL1 0x1c4
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v4.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v4.h
new file mode 100644
index 000000000000..a1c7d3d17150
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v4.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_UFS_V4_H_
+#define QCOM_PHY_QMP_PCS_UFS_V4_H_
+
+/* Only for QMP V4 PHY - UFS PCS registers */
+#define QPHY_V4_PCS_UFS_PHY_START 0x000
+#define QPHY_V4_PCS_UFS_POWER_DOWN_CONTROL 0x004
+#define QPHY_V4_PCS_UFS_SW_RESET 0x008
+#define QPHY_V4_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB 0x00c
+#define QPHY_V4_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB 0x010
+#define QPHY_V4_PCS_UFS_PLL_CNTL 0x02c
+#define QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL 0x030
+#define QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL 0x038
+#define QPHY_V4_PCS_UFS_BIST_FIXED_PAT_CTRL 0x060
+#define QPHY_V4_PCS_UFS_TX_HSGEAR_CAPABILITY 0x074
+#define QPHY_V4_PCS_UFS_RX_HSGEAR_CAPABILITY 0x0b4
+#define QPHY_V4_PCS_UFS_DEBUG_BUS_CLKSEL 0x124
+#define QPHY_V4_PCS_UFS_LINECFG_DISABLE 0x148
+#define QPHY_V4_PCS_UFS_RX_MIN_HIBERN8_TIME 0x150
+#define QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2 0x158
+#define QPHY_V4_PCS_UFS_TX_PWM_GEAR_BAND 0x160
+#define QPHY_V4_PCS_UFS_TX_HS_GEAR_BAND 0x168
+#define QPHY_V4_PCS_UFS_READY_STATUS 0x180
+#define QPHY_V4_PCS_UFS_TX_MID_TERM_CTRL1 0x1d8
+#define QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1 0x1e0
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v5.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v5.h
new file mode 100644
index 000000000000..bcca23493b7e
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v5.h
@@ -0,0 +1,27 @@
+/* Only for QMP V5 PHY - UFS PCS registers */
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_UFS_V5_H_
+#define QCOM_PHY_QMP_PCS_UFS_V5_H_
+
+/* 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
+#define QPHY_V5_PCS_UFS_PLL_CNTL 0x02c
+#define QPHY_V5_PCS_UFS_TX_LARGE_AMP_DRV_LVL 0x030
+#define QPHY_V5_PCS_UFS_TX_SMALL_AMP_DRV_LVL 0x038
+#define QPHY_V5_PCS_UFS_TX_HSGEAR_CAPABILITY 0x074
+#define QPHY_V5_PCS_UFS_RX_HSGEAR_CAPABILITY 0x0b4
+#define QPHY_V5_PCS_UFS_DEBUG_BUS_CLKSEL 0x124
+#define QPHY_V5_PCS_UFS_RX_MIN_HIBERN8_TIME 0x150
+#define QPHY_V5_PCS_UFS_RX_SIGDET_CTRL1 0x154
+#define QPHY_V5_PCS_UFS_RX_SIGDET_CTRL2 0x158
+#define QPHY_V5_PCS_UFS_TX_PWM_GEAR_BAND 0x160
+#define QPHY_V5_PCS_UFS_TX_HS_GEAR_BAND 0x168
+#define QPHY_V5_PCS_UFS_TX_MID_TERM_CTRL1 0x1d8
+#define QPHY_V5_PCS_UFS_MULTI_LANE_CTRL1 0x1e0
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v4.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v4.h
new file mode 100644
index 000000000000..d7fd4ac0fc55
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v4.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_USB_V4_H_
+#define QCOM_PHY_QMP_PCS_USB_V4_H_
+
+/* Only for QMP V4 PHY - USB3 PCS registers */
+#define QPHY_V4_PCS_USB3_POWER_STATE_CONFIG1 0x000
+#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_STATUS 0x004
+#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL 0x008
+#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL2 0x00c
+#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x010
+#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR 0x014
+#define QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL 0x018
+#define QPHY_V4_PCS_USB3_LFPS_TX_ECSTART 0x01c
+#define QPHY_V4_PCS_USB3_LFPS_PER_TIMER_VAL 0x020
+#define QPHY_V4_PCS_USB3_LFPS_TX_END_CNT_U3_START 0x024
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_LOCK_TIME 0x028
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME 0x02c
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_CTLE_TIME 0x030
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME_S2 0x034
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2 0x038
+#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_L 0x03c
+#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_H 0x040
+#define QPHY_V4_PCS_USB3_ARCVR_DTCT_EN_PERIOD 0x044
+#define QPHY_V4_PCS_USB3_ARCVR_DTCT_CM_DLY 0x048
+#define QPHY_V4_PCS_USB3_TXONESZEROS_RUN_LENGTH 0x04c
+#define QPHY_V4_PCS_USB3_ALFPS_DEGLITCH_VAL 0x050
+#define QPHY_V4_PCS_USB3_SIGDET_STARTUP_TIMER_VAL 0x054
+#define QPHY_V4_PCS_USB3_TEST_CONTROL 0x058
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v5.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v5.h
new file mode 100644
index 000000000000..73de626223ed
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v5.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_USB_V5_H_
+#define QCOM_PHY_QMP_PCS_USB_V5_H_
+
+/* Only for QMP V5 PHY - USB3 have different offsets than V4 */
+#define QPHY_V5_PCS_USB3_POWER_STATE_CONFIG1 0x000
+#define QPHY_V5_PCS_USB3_AUTONOMOUS_MODE_STATUS 0x004
+#define QPHY_V5_PCS_USB3_AUTONOMOUS_MODE_CTRL 0x008
+#define QPHY_V5_PCS_USB3_AUTONOMOUS_MODE_CTRL2 0x00c
+#define QPHY_V5_PCS_USB3_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x010
+#define QPHY_V5_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR 0x014
+#define QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL 0x018
+#define QPHY_V5_PCS_USB3_LFPS_TX_ECSTART 0x01c
+#define QPHY_V5_PCS_USB3_LFPS_PER_TIMER_VAL 0x020
+#define QPHY_V5_PCS_USB3_LFPS_TX_END_CNT_U3_START 0x024
+#define QPHY_V5_PCS_USB3_LFPS_CONFIG1 0x028
+#define QPHY_V5_PCS_USB3_RXEQTRAINING_LOCK_TIME 0x02c
+#define QPHY_V5_PCS_USB3_RXEQTRAINING_WAIT_TIME 0x030
+#define QPHY_V5_PCS_USB3_RXEQTRAINING_CTLE_TIME 0x034
+#define QPHY_V5_PCS_USB3_RXEQTRAINING_WAIT_TIME_S2 0x038
+#define QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2 0x03c
+#define QPHY_V5_PCS_USB3_RCVR_DTCT_DLY_U3_L 0x040
+#define QPHY_V5_PCS_USB3_RCVR_DTCT_DLY_U3_H 0x044
+#define QPHY_V5_PCS_USB3_ARCVR_DTCT_EN_PERIOD 0x048
+#define QPHY_V5_PCS_USB3_ARCVR_DTCT_CM_DLY 0x04c
+#define QPHY_V5_PCS_USB3_TXONESZEROS_RUN_LENGTH 0x050
+#define QPHY_V5_PCS_USB3_ALFPS_DEGLITCH_VAL 0x054
+#define QPHY_V5_PCS_USB3_SIGDET_STARTUP_TIMER_VAL 0x058
+#define QPHY_V5_PCS_USB3_TEST_CONTROL 0x05c
+#define QPHY_V5_PCS_USB3_RXTERMINATION_DLY_SEL 0x060
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v2.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v2.h
new file mode 100644
index 000000000000..c8515f506872
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v2.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_V2_H_
+#define QCOM_PHY_QMP_PCS_V2_H_
+
+/* Only for QMP V2 PHY - PCS registers */
+#define QPHY_V2_PCS_POWER_DOWN_CONTROL 0x004
+#define QPHY_V2_PCS_TXDEEMPH_M6DB_V0 0x024
+#define QPHY_V2_PCS_TXDEEMPH_M3P5DB_V0 0x028
+#define QPHY_V2_PCS_TX_LARGE_AMP_DRV_LVL 0x034
+#define QPHY_V2_PCS_TX_LARGE_AMP_POST_EMP_LVL 0x038
+#define QPHY_V2_PCS_TX_SMALL_AMP_DRV_LVL 0x03c
+#define QPHY_V2_PCS_TX_SMALL_AMP_POST_EMP_LVL 0x040
+#define QPHY_V2_PCS_ENDPOINT_REFCLK_DRIVE 0x054
+#define QPHY_V2_PCS_RX_IDLE_DTCT_CNTRL 0x058
+#define QPHY_V2_PCS_POWER_STATE_CONFIG1 0x060
+#define QPHY_V2_PCS_POWER_STATE_CONFIG2 0x064
+#define QPHY_V2_PCS_POWER_STATE_CONFIG4 0x06c
+#define QPHY_V2_PCS_LOCK_DETECT_CONFIG1 0x080
+#define QPHY_V2_PCS_LOCK_DETECT_CONFIG2 0x084
+#define QPHY_V2_PCS_LOCK_DETECT_CONFIG3 0x088
+#define QPHY_V2_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x0a0
+#define QPHY_V2_PCS_LP_WAKEUP_DLY_TIME_AUXCLK 0x0a4
+#define QPHY_V2_PCS_PLL_LOCK_CHK_DLY_TIME 0x0a8
+#define QPHY_V2_PCS_FLL_CNTRL1 0x0c0
+#define QPHY_V2_PCS_FLL_CNTRL2 0x0c4
+#define QPHY_V2_PCS_FLL_CNT_VAL_L 0x0c8
+#define QPHY_V2_PCS_FLL_CNT_VAL_H_TOL 0x0cc
+#define QPHY_V2_PCS_FLL_MAN_CODE 0x0d0
+
+/* UFS only ? */
+#define QPHY_V2_PCS_RX_MIN_STALL_NOCONFIG_TIME_CAP 0x0cc
+#define QPHY_V2_PCS_RX_SYM_RESYNC_CTRL 0x13c
+#define QPHY_V2_PCS_RX_MIN_HIBERN8_TIME 0x140
+#define QPHY_V2_PCS_RX_SIGDET_CTRL2 0x148
+#define QPHY_V2_PCS_RX_PWM_GEAR_BAND 0x154
+#define QPHY_V2_PCS_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB 0x1a8
+#define QPHY_V2_PCS_OSC_DTCT_ACTIONS 0x1ac
+#define QPHY_V2_PCS_RX_SIGDET_LVL 0x1d8
+#define QPHY_V2_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1dc
+#define QPHY_V2_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1e0
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v3.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v3.h
new file mode 100644
index 000000000000..10dbbb006201
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v3.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_V3_H_
+#define QCOM_PHY_QMP_PCS_V3_H_
+
+/* Only for QMP V3 PHY - PCS registers */
+#define QPHY_V3_PCS_SW_RESET 0x000
+#define QPHY_V3_PCS_POWER_DOWN_CONTROL 0x004
+#define QPHY_V3_PCS_START_CONTROL 0x008
+#define QPHY_V3_PCS_TXMGN_V0 0x00c
+#define QPHY_V3_PCS_TXMGN_V1 0x010
+#define QPHY_V3_PCS_TXMGN_V2 0x014
+#define QPHY_V3_PCS_TXMGN_V3 0x018
+#define QPHY_V3_PCS_TXMGN_V4 0x01c
+#define QPHY_V3_PCS_TXMGN_LS 0x020
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V0 0x024
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0 0x028
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V1 0x02c
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1 0x030
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V2 0x034
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2 0x038
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V3 0x03c
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3 0x040
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V4 0x044
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4 0x048
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_LS 0x04c
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS 0x050
+#define QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE 0x054
+#define QPHY_V3_PCS_RX_IDLE_DTCT_CNTRL 0x058
+#define QPHY_V3_PCS_RATE_SLEW_CNTRL 0x05c
+#define QPHY_V3_PCS_POWER_STATE_CONFIG1 0x060
+#define QPHY_V3_PCS_POWER_STATE_CONFIG2 0x064
+#define QPHY_V3_PCS_POWER_STATE_CONFIG3 0x068
+#define QPHY_V3_PCS_POWER_STATE_CONFIG4 0x06c
+#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L 0x070
+#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H 0x074
+#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L 0x078
+#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H 0x07c
+#define QPHY_V3_PCS_LOCK_DETECT_CONFIG1 0x080
+#define QPHY_V3_PCS_LOCK_DETECT_CONFIG2 0x084
+#define QPHY_V3_PCS_LOCK_DETECT_CONFIG3 0x088
+#define QPHY_V3_PCS_TSYNC_RSYNC_TIME 0x08c
+#define QPHY_V3_PCS_SIGDET_LOW_2_IDLE_TIME 0x090
+#define QPHY_V3_PCS_BEACON_2_IDLE_TIME_L 0x094
+#define QPHY_V3_PCS_BEACON_2_IDLE_TIME_H 0x098
+#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_SYSCLK 0x09c
+#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x0a0
+#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK 0x0a4
+#define QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME 0x0a8
+#define QPHY_V3_PCS_LFPS_DET_HIGH_COUNT_VAL 0x0ac
+#define QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK 0x0b0
+#define QPHY_V3_PCS_LFPS_TX_END_CNT_P2U3_START 0x0b4
+#define QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME 0x0b8
+#define QPHY_V3_PCS_RXEQTRAINING_RUN_TIME 0x0bc
+#define QPHY_V3_PCS_TXONESZEROS_RUN_LENGTH 0x0c0
+#define QPHY_V3_PCS_FLL_CNTRL1 0x0c4
+#define QPHY_V3_PCS_FLL_CNTRL2 0x0c8
+#define QPHY_V3_PCS_FLL_CNT_VAL_L 0x0cc
+#define QPHY_V3_PCS_FLL_CNT_VAL_H_TOL 0x0d0
+#define QPHY_V3_PCS_FLL_MAN_CODE 0x0d4
+#define QPHY_V3_PCS_AUTONOMOUS_MODE_CTRL 0x0d8
+#define QPHY_V3_PCS_LFPS_RXTERM_IRQ_CLEAR 0x0dc
+#define QPHY_V3_PCS_ARCVR_DTCT_EN_PERIOD 0x0e0
+#define QPHY_V3_PCS_ARCVR_DTCT_CM_DLY 0x0e4
+#define QPHY_V3_PCS_ALFPS_DEGLITCH_VAL 0x0e8
+#define QPHY_V3_PCS_INSIG_SW_CTRL1 0x0ec
+#define QPHY_V3_PCS_INSIG_SW_CTRL2 0x0f0
+#define QPHY_V3_PCS_INSIG_SW_CTRL3 0x0f4
+#define QPHY_V3_PCS_INSIG_MX_CTRL1 0x0f8
+#define QPHY_V3_PCS_INSIG_MX_CTRL2 0x0fc
+#define QPHY_V3_PCS_INSIG_MX_CTRL3 0x100
+#define QPHY_V3_PCS_OUTSIG_SW_CTRL1 0x104
+#define QPHY_V3_PCS_OUTSIG_MX_CTRL1 0x108
+#define QPHY_V3_PCS_CLK_DEBUG_BYPASS_CTRL 0x10c
+#define QPHY_V3_PCS_TEST_CONTROL 0x110
+#define QPHY_V3_PCS_TEST_CONTROL2 0x114
+#define QPHY_V3_PCS_TEST_CONTROL3 0x118
+#define QPHY_V3_PCS_TEST_CONTROL4 0x11c
+#define QPHY_V3_PCS_TEST_CONTROL5 0x120
+#define QPHY_V3_PCS_TEST_CONTROL6 0x124
+#define QPHY_V3_PCS_TEST_CONTROL7 0x128
+#define QPHY_V3_PCS_COM_RESET_CONTROL 0x12c
+#define QPHY_V3_PCS_BIST_CTRL 0x130
+#define QPHY_V3_PCS_PRBS_POLY0 0x134
+#define QPHY_V3_PCS_PRBS_POLY1 0x138
+#define QPHY_V3_PCS_PRBS_SEED0 0x13c
+#define QPHY_V3_PCS_PRBS_SEED1 0x140
+#define QPHY_V3_PCS_FIXED_PAT_CTRL 0x144
+#define QPHY_V3_PCS_FIXED_PAT0 0x148
+#define QPHY_V3_PCS_FIXED_PAT1 0x14c
+#define QPHY_V3_PCS_FIXED_PAT2 0x150
+#define QPHY_V3_PCS_FIXED_PAT3 0x154
+#define QPHY_V3_PCS_COM_CLK_SWITCH_CTRL 0x158
+#define QPHY_V3_PCS_ELECIDLE_DLY_SEL 0x15c
+#define QPHY_V3_PCS_SPARE1 0x160
+#define QPHY_V3_PCS_BIST_CHK_ERR_CNT_L_STATUS 0x164
+#define QPHY_V3_PCS_BIST_CHK_ERR_CNT_H_STATUS 0x168
+#define QPHY_V3_PCS_BIST_CHK_STATUS 0x16c
+#define QPHY_V3_PCS_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x170
+#define QPHY_V3_PCS_PCS_STATUS 0x174
+#define QPHY_V3_PCS_PCS_STATUS2 0x178
+#define QPHY_V3_PCS_PCS_STATUS3 0x17c
+#define QPHY_V3_PCS_COM_RESET_STATUS 0x180
+#define QPHY_V3_PCS_OSC_DTCT_STATUS 0x184
+#define QPHY_V3_PCS_REVISION_ID0 0x188
+#define QPHY_V3_PCS_REVISION_ID1 0x18c
+#define QPHY_V3_PCS_REVISION_ID2 0x190
+#define QPHY_V3_PCS_REVISION_ID3 0x194
+#define QPHY_V3_PCS_DEBUG_BUS_0_STATUS 0x198
+#define QPHY_V3_PCS_DEBUG_BUS_1_STATUS 0x19c
+#define QPHY_V3_PCS_DEBUG_BUS_2_STATUS 0x1a0
+#define QPHY_V3_PCS_DEBUG_BUS_3_STATUS 0x1a4
+#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1a8
+#define QPHY_V3_PCS_OSC_DTCT_ACTIONS 0x1ac
+#define QPHY_V3_PCS_SIGDET_CNTRL 0x1b0
+#define QPHY_V3_PCS_IDAC_CAL_CNTRL 0x1b4
+#define QPHY_V3_PCS_CMN_ACK_OUT_SEL 0x1b8
+#define QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME_SYSCLK 0x1bc
+#define QPHY_V3_PCS_AUTONOMOUS_MODE_STATUS 0x1c0
+#define QPHY_V3_PCS_ENDPOINT_REFCLK_CNTRL 0x1c4
+#define QPHY_V3_PCS_EPCLK_PRE_PLL_LOCK_DLY_SYSCLK 0x1c8
+#define QPHY_V3_PCS_EPCLK_PRE_PLL_LOCK_DLY_AUXCLK 0x1cc
+#define QPHY_V3_PCS_EPCLK_DLY_COUNT_VAL_L 0x1d0
+#define QPHY_V3_PCS_EPCLK_DLY_COUNT_VAL_H 0x1d4
+#define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8
+#define QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1dc
+#define QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1e0
+#define QPHY_V3_PCS_AUTONOMOUS_MODE_CTRL2 0x1e4
+#define QPHY_V3_PCS_RXTERMINATION_DLY_SEL 0x1e8
+#define QPHY_V3_PCS_LFPS_PER_TIMER_VAL 0x1ec
+#define QPHY_V3_PCS_SIGDET_STARTUP_TIMER_VAL 0x1f0
+#define QPHY_V3_PCS_LOCK_DETECT_CONFIG4 0x1f4
+#define QPHY_V3_PCS_RX_SIGDET_DTCT_CNTRL 0x1f8
+#define QPHY_V3_PCS_PCS_STATUS4 0x1fc
+#define QPHY_V3_PCS_PCS_STATUS4_CLEAR 0x200
+#define QPHY_V3_PCS_DEC_ERROR_COUNT_STATUS 0x204
+#define QPHY_V3_PCS_COMMA_POS_STATUS 0x208
+#define QPHY_V3_PCS_REFGEN_REQ_CONFIG1 0x20c
+#define QPHY_V3_PCS_REFGEN_REQ_CONFIG2 0x210
+#define QPHY_V3_PCS_REFGEN_REQ_CONFIG3 0x214
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4.h
new file mode 100644
index 000000000000..a2c1eba2b693
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4.h
@@ -0,0 +1,135 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_V4_H_
+#define QCOM_PHY_QMP_PCS_V4_H_
+
+/* Only for QMP V4 PHY - USB/PCIe PCS registers */
+#define QPHY_V4_PCS_SW_RESET 0x000
+#define QPHY_V4_PCS_REVISION_ID0 0x004
+#define QPHY_V4_PCS_REVISION_ID1 0x008
+#define QPHY_V4_PCS_REVISION_ID2 0x00c
+#define QPHY_V4_PCS_REVISION_ID3 0x010
+#define QPHY_V4_PCS_PCS_STATUS1 0x014
+#define QPHY_V4_PCS_PCS_STATUS2 0x018
+#define QPHY_V4_PCS_PCS_STATUS3 0x01c
+#define QPHY_V4_PCS_PCS_STATUS4 0x020
+#define QPHY_V4_PCS_PCS_STATUS5 0x024
+#define QPHY_V4_PCS_PCS_STATUS6 0x028
+#define QPHY_V4_PCS_PCS_STATUS7 0x02c
+#define QPHY_V4_PCS_DEBUG_BUS_0_STATUS 0x030
+#define QPHY_V4_PCS_DEBUG_BUS_1_STATUS 0x034
+#define QPHY_V4_PCS_DEBUG_BUS_2_STATUS 0x038
+#define QPHY_V4_PCS_DEBUG_BUS_3_STATUS 0x03c
+#define QPHY_V4_PCS_POWER_DOWN_CONTROL 0x040
+#define QPHY_V4_PCS_START_CONTROL 0x044
+#define QPHY_V4_PCS_INSIG_SW_CTRL1 0x048
+#define QPHY_V4_PCS_INSIG_SW_CTRL2 0x04c
+#define QPHY_V4_PCS_INSIG_SW_CTRL3 0x050
+#define QPHY_V4_PCS_INSIG_SW_CTRL4 0x054
+#define QPHY_V4_PCS_INSIG_SW_CTRL5 0x058
+#define QPHY_V4_PCS_INSIG_SW_CTRL6 0x05c
+#define QPHY_V4_PCS_INSIG_SW_CTRL7 0x060
+#define QPHY_V4_PCS_INSIG_SW_CTRL8 0x064
+#define QPHY_V4_PCS_INSIG_MX_CTRL1 0x068
+#define QPHY_V4_PCS_INSIG_MX_CTRL2 0x06c
+#define QPHY_V4_PCS_INSIG_MX_CTRL3 0x070
+#define QPHY_V4_PCS_INSIG_MX_CTRL4 0x074
+#define QPHY_V4_PCS_INSIG_MX_CTRL5 0x078
+#define QPHY_V4_PCS_INSIG_MX_CTRL7 0x07c
+#define QPHY_V4_PCS_INSIG_MX_CTRL8 0x080
+#define QPHY_V4_PCS_OUTSIG_SW_CTRL1 0x084
+#define QPHY_V4_PCS_OUTSIG_MX_CTRL1 0x088
+#define QPHY_V4_PCS_CLAMP_ENABLE 0x08c
+#define QPHY_V4_PCS_POWER_STATE_CONFIG1 0x090
+#define QPHY_V4_PCS_POWER_STATE_CONFIG2 0x094
+#define QPHY_V4_PCS_FLL_CNTRL1 0x098
+#define QPHY_V4_PCS_FLL_CNTRL2 0x09c
+#define QPHY_V4_PCS_FLL_CNT_VAL_L 0x0a0
+#define QPHY_V4_PCS_FLL_CNT_VAL_H_TOL 0x0a4
+#define QPHY_V4_PCS_FLL_MAN_CODE 0x0a8
+#define QPHY_V4_PCS_TEST_CONTROL1 0x0ac
+#define QPHY_V4_PCS_TEST_CONTROL2 0x0b0
+#define QPHY_V4_PCS_TEST_CONTROL3 0x0b4
+#define QPHY_V4_PCS_TEST_CONTROL4 0x0b8
+#define QPHY_V4_PCS_TEST_CONTROL5 0x0bc
+#define QPHY_V4_PCS_TEST_CONTROL6 0x0c0
+#define QPHY_V4_PCS_LOCK_DETECT_CONFIG1 0x0c4
+#define QPHY_V4_PCS_LOCK_DETECT_CONFIG2 0x0c8
+#define QPHY_V4_PCS_LOCK_DETECT_CONFIG3 0x0cc
+#define QPHY_V4_PCS_LOCK_DETECT_CONFIG4 0x0d0
+#define QPHY_V4_PCS_LOCK_DETECT_CONFIG5 0x0d4
+#define QPHY_V4_PCS_LOCK_DETECT_CONFIG6 0x0d8
+#define QPHY_V4_PCS_REFGEN_REQ_CONFIG1 0x0dc
+#define QPHY_V4_PCS_REFGEN_REQ_CONFIG2 0x0e0
+#define QPHY_V4_PCS_REFGEN_REQ_CONFIG3 0x0e4
+#define QPHY_V4_PCS_BIST_CTRL 0x0e8
+#define QPHY_V4_PCS_PRBS_POLY0 0x0ec
+#define QPHY_V4_PCS_PRBS_POLY1 0x0f0
+#define QPHY_V4_PCS_FIXED_PAT0 0x0f4
+#define QPHY_V4_PCS_FIXED_PAT1 0x0f8
+#define QPHY_V4_PCS_FIXED_PAT2 0x0fc
+#define QPHY_V4_PCS_FIXED_PAT3 0x100
+#define QPHY_V4_PCS_FIXED_PAT4 0x104
+#define QPHY_V4_PCS_FIXED_PAT5 0x108
+#define QPHY_V4_PCS_FIXED_PAT6 0x10c
+#define QPHY_V4_PCS_FIXED_PAT7 0x110
+#define QPHY_V4_PCS_FIXED_PAT8 0x114
+#define QPHY_V4_PCS_FIXED_PAT9 0x118
+#define QPHY_V4_PCS_FIXED_PAT10 0x11c
+#define QPHY_V4_PCS_FIXED_PAT11 0x120
+#define QPHY_V4_PCS_FIXED_PAT12 0x124
+#define QPHY_V4_PCS_FIXED_PAT13 0x128
+#define QPHY_V4_PCS_FIXED_PAT14 0x12c
+#define QPHY_V4_PCS_FIXED_PAT15 0x130
+#define QPHY_V4_PCS_TXMGN_CONFIG 0x134
+#define QPHY_V4_PCS_G12S1_TXMGN_V0 0x138
+#define QPHY_V4_PCS_G12S1_TXMGN_V1 0x13c
+#define QPHY_V4_PCS_G12S1_TXMGN_V2 0x140
+#define QPHY_V4_PCS_G12S1_TXMGN_V3 0x144
+#define QPHY_V4_PCS_G12S1_TXMGN_V4 0x148
+#define QPHY_V4_PCS_G12S1_TXMGN_V0_RS 0x14c
+#define QPHY_V4_PCS_G12S1_TXMGN_V1_RS 0x150
+#define QPHY_V4_PCS_G12S1_TXMGN_V2_RS 0x154
+#define QPHY_V4_PCS_G12S1_TXMGN_V3_RS 0x158
+#define QPHY_V4_PCS_G12S1_TXMGN_V4_RS 0x15c
+#define QPHY_V4_PCS_G3S2_TXMGN_MAIN 0x160
+#define QPHY_V4_PCS_G3S2_TXMGN_MAIN_RS 0x164
+#define QPHY_V4_PCS_G12S1_TXDEEMPH_M6DB 0x168
+#define QPHY_V4_PCS_G12S1_TXDEEMPH_M3P5DB 0x16c
+#define QPHY_V4_PCS_G3S2_PRE_GAIN 0x170
+#define QPHY_V4_PCS_G3S2_POST_GAIN 0x174
+#define QPHY_V4_PCS_G3S2_PRE_POST_OFFSET 0x178
+#define QPHY_V4_PCS_G3S2_PRE_GAIN_RS 0x17c
+#define QPHY_V4_PCS_G3S2_POST_GAIN_RS 0x180
+#define QPHY_V4_PCS_G3S2_PRE_POST_OFFSET_RS 0x184
+#define QPHY_V4_PCS_RX_SIGDET_LVL 0x188
+#define QPHY_V4_PCS_RX_SIGDET_DTCT_CNTRL 0x18c
+#define QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L 0x190
+#define QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H 0x194
+#define QPHY_V4_PCS_RATE_SLEW_CNTRL1 0x198
+#define QPHY_V4_PCS_RATE_SLEW_CNTRL2 0x19c
+#define QPHY_V4_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x1a0
+#define QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L 0x1a4
+#define QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_H 0x1a8
+#define QPHY_V4_PCS_TSYNC_RSYNC_TIME 0x1ac
+#define QPHY_V4_PCS_CDR_RESET_TIME 0x1b0
+#define QPHY_V4_PCS_TSYNC_DLY_TIME 0x1b4
+#define QPHY_V4_PCS_ELECIDLE_DLY_SEL 0x1b8
+#define QPHY_V4_PCS_CMN_ACK_OUT_SEL 0x1bc
+#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG1 0x1c0
+#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG2 0x1c4
+#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG3 0x1c8
+#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG4 0x1cc
+#define QPHY_V4_PCS_PCS_TX_RX_CONFIG 0x1d0
+#define QPHY_V4_PCS_RX_IDLE_DTCT_CNTRL 0x1d4
+#define QPHY_V4_PCS_RX_DCC_CAL_CONFIG 0x1d8
+#define QPHY_V4_PCS_EQ_CONFIG1 0x1dc
+#define QPHY_V4_PCS_EQ_CONFIG2 0x1e0
+#define QPHY_V4_PCS_EQ_CONFIG3 0x1e4
+#define QPHY_V4_PCS_EQ_CONFIG4 0x1e8
+#define QPHY_V4_PCS_EQ_CONFIG5 0x1ec
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4_20.h
new file mode 100644
index 000000000000..08c3dd115488
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4_20.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_V4_20_H_
+#define QCOM_PHY_QMP_PCS_V4_20_H_
+
+/* Only for QMP V4_20 PHY - USB/PCIe PCS registers */
+#define QPHY_V4_20_PCS_RX_SIGDET_LVL 0x188
+#define QPHY_V4_20_PCS_EQ_CONFIG2 0x1d8
+#define QPHY_V4_20_PCS_EQ_CONFIG4 0x1e0
+#define QPHY_V4_20_PCS_EQ_CONFIG5 0x1e4
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v5.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v5.h
new file mode 100644
index 000000000000..04f260711ea1
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v5.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_V5_H_
+#define QCOM_PHY_QMP_PCS_V5_H_
+
+/* Only for QMP V5 PHY - USB/PCIe PCS registers */
+#define QPHY_V5_PCS_LOCK_DETECT_CONFIG1 0x0c4
+#define QPHY_V5_PCS_LOCK_DETECT_CONFIG2 0x0c8
+#define QPHY_V5_PCS_LOCK_DETECT_CONFIG3 0x0cc
+#define QPHY_V5_PCS_LOCK_DETECT_CONFIG6 0x0d8
+#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_RCVR_DTCT_DLY_P1U2_L 0x190
+#define QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_H 0x194
+#define QPHY_V5_PCS_RATE_SLEW_CNTRL1 0x198
+#define QPHY_V5_PCS_CDR_RESET_TIME 0x1b0
+#define QPHY_V5_PCS_RX_CONFIG 0x1b0
+#define QPHY_V5_PCS_ALIGN_DETECT_CONFIG1 0x1c0
+#define QPHY_V5_PCS_ALIGN_DETECT_CONFIG2 0x1c4
+#define QPHY_V5_PCS_PCS_TX_RX_CONFIG 0x1d0
+#define QPHY_V5_PCS_EQ_CONFIG1 0x1dc
+#define QPHY_V5_PCS_EQ_CONFIG2 0x1e0
+#define QPHY_V5_PCS_EQ_CONFIG3 0x1e4
+#define QPHY_V5_PCS_EQ_CONFIG5 0x1ec
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v3.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v3.h
new file mode 100644
index 000000000000..c0bd54e0e7b6
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v3.h
@@ -0,0 +1,111 @@
+
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_COM_V3_H_
+#define QCOM_PHY_QMP_QSERDES_COM_V3_H_
+
+/* Only for QMP V3 PHY - QSERDES COM registers */
+#define QSERDES_V3_COM_ATB_SEL1 0x000
+#define QSERDES_V3_COM_ATB_SEL2 0x004
+#define QSERDES_V3_COM_FREQ_UPDATE 0x008
+#define QSERDES_V3_COM_BG_TIMER 0x00c
+#define QSERDES_V3_COM_SSC_EN_CENTER 0x010
+#define QSERDES_V3_COM_SSC_ADJ_PER1 0x014
+#define QSERDES_V3_COM_SSC_ADJ_PER2 0x018
+#define QSERDES_V3_COM_SSC_PER1 0x01c
+#define QSERDES_V3_COM_SSC_PER2 0x020
+#define QSERDES_V3_COM_SSC_STEP_SIZE1 0x024
+#define QSERDES_V3_COM_SSC_STEP_SIZE2 0x028
+#define QSERDES_V3_COM_POST_DIV 0x02c
+#define QSERDES_V3_COM_POST_DIV_MUX 0x030
+#define QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN 0x034
+#define QSERDES_V3_COM_CLK_ENABLE1 0x038
+#define QSERDES_V3_COM_SYS_CLK_CTRL 0x03c
+#define QSERDES_V3_COM_SYSCLK_BUF_ENABLE 0x040
+#define QSERDES_V3_COM_PLL_EN 0x044
+#define QSERDES_V3_COM_PLL_IVCO 0x048
+#define QSERDES_V3_COM_CMN_IETRIM 0x04c
+#define QSERDES_V3_COM_CMN_IPTRIM 0x050
+#define QSERDES_V3_COM_EP_CLOCK_DETECT_CTR 0x054
+#define QSERDES_V3_COM_SYSCLK_DET_COMP_STATUS 0x058
+#define QSERDES_V3_COM_CLK_EP_DIV 0x05c
+#define QSERDES_V3_COM_CP_CTRL_MODE0 0x060
+#define QSERDES_V3_COM_CP_CTRL_MODE1 0x064
+#define QSERDES_V3_COM_PLL_RCTRL_MODE0 0x068
+#define QSERDES_V3_COM_PLL_RCTRL_MODE1 0x06c
+#define QSERDES_V3_COM_PLL_CCTRL_MODE0 0x070
+#define QSERDES_V3_COM_PLL_CCTRL_MODE1 0x074
+#define QSERDES_V3_COM_PLL_CNTRL 0x078
+#define QSERDES_V3_COM_BIAS_EN_CTRL_BY_PSM 0x07c
+#define QSERDES_V3_COM_SYSCLK_EN_SEL 0x080
+#define QSERDES_V3_COM_CML_SYSCLK_SEL 0x084
+#define QSERDES_V3_COM_RESETSM_CNTRL 0x088
+#define QSERDES_V3_COM_RESETSM_CNTRL2 0x08c
+#define QSERDES_V3_COM_LOCK_CMP_EN 0x090
+#define QSERDES_V3_COM_LOCK_CMP_CFG 0x094
+#define QSERDES_V3_COM_LOCK_CMP1_MODE0 0x098
+#define QSERDES_V3_COM_LOCK_CMP2_MODE0 0x09c
+#define QSERDES_V3_COM_LOCK_CMP3_MODE0 0x0a0
+#define QSERDES_V3_COM_LOCK_CMP1_MODE1 0x0a4
+#define QSERDES_V3_COM_LOCK_CMP2_MODE1 0x0a8
+#define QSERDES_V3_COM_LOCK_CMP3_MODE1 0x0ac
+#define QSERDES_V3_COM_DEC_START_MODE0 0x0b0
+#define QSERDES_V3_COM_DEC_START_MODE1 0x0b4
+#define QSERDES_V3_COM_DIV_FRAC_START1_MODE0 0x0b8
+#define QSERDES_V3_COM_DIV_FRAC_START2_MODE0 0x0bc
+#define QSERDES_V3_COM_DIV_FRAC_START3_MODE0 0x0c0
+#define QSERDES_V3_COM_DIV_FRAC_START1_MODE1 0x0c4
+#define QSERDES_V3_COM_DIV_FRAC_START2_MODE1 0x0c8
+#define QSERDES_V3_COM_DIV_FRAC_START3_MODE1 0x0cc
+#define QSERDES_V3_COM_INTEGLOOP_INITVAL 0x0d0
+#define QSERDES_V3_COM_INTEGLOOP_EN 0x0d4
+#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0 0x0d8
+#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0 0x0dc
+#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1 0x0e0
+#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1 0x0e4
+#define QSERDES_V3_COM_VCOCAL_DEADMAN_CTRL 0x0e8
+#define QSERDES_V3_COM_VCO_TUNE_CTRL 0x0ec
+#define QSERDES_V3_COM_VCO_TUNE_MAP 0x0f0
+#define QSERDES_V3_COM_VCO_TUNE1_MODE0 0x0f4
+#define QSERDES_V3_COM_VCO_TUNE2_MODE0 0x0f8
+#define QSERDES_V3_COM_VCO_TUNE1_MODE1 0x0fc
+#define QSERDES_V3_COM_VCO_TUNE2_MODE1 0x100
+#define QSERDES_V3_COM_VCO_TUNE_INITVAL1 0x104
+#define QSERDES_V3_COM_VCO_TUNE_INITVAL2 0x108
+#define QSERDES_V3_COM_VCO_TUNE_MINVAL1 0x10c
+#define QSERDES_V3_COM_VCO_TUNE_MINVAL2 0x110
+#define QSERDES_V3_COM_VCO_TUNE_MAXVAL1 0x114
+#define QSERDES_V3_COM_VCO_TUNE_MAXVAL2 0x118
+#define QSERDES_V3_COM_VCO_TUNE_TIMER1 0x11c
+#define QSERDES_V3_COM_VCO_TUNE_TIMER2 0x120
+#define QSERDES_V3_COM_CMN_STATUS 0x124
+#define QSERDES_V3_COM_RESET_SM_STATUS 0x128
+#define QSERDES_V3_COM_RESTRIM_CODE_STATUS 0x12c
+#define QSERDES_V3_COM_PLLCAL_CODE1_STATUS 0x130
+#define QSERDES_V3_COM_PLLCAL_CODE2_STATUS 0x134
+#define QSERDES_V3_COM_CLK_SELECT 0x138
+#define QSERDES_V3_COM_HSCLK_SEL 0x13c
+#define QSERDES_V3_COM_INTEGLOOP_BINCODE_STATUS 0x140
+#define QSERDES_V3_COM_PLL_ANALOG 0x144
+#define QSERDES_V3_COM_CORECLK_DIV_MODE0 0x148
+#define QSERDES_V3_COM_CORECLK_DIV_MODE1 0x14c
+#define QSERDES_V3_COM_SW_RESET 0x150
+#define QSERDES_V3_COM_CORE_CLK_EN 0x154
+#define QSERDES_V3_COM_C_READY_STATUS 0x158
+#define QSERDES_V3_COM_CMN_CONFIG 0x15c
+#define QSERDES_V3_COM_CMN_RATE_OVERRIDE 0x160
+#define QSERDES_V3_COM_SVS_MODE_CLK_SEL 0x164
+#define QSERDES_V3_COM_DEBUG_BUS0 0x168
+#define QSERDES_V3_COM_DEBUG_BUS1 0x16c
+#define QSERDES_V3_COM_DEBUG_BUS2 0x170
+#define QSERDES_V3_COM_DEBUG_BUS3 0x174
+#define QSERDES_V3_COM_DEBUG_BUS_SEL 0x178
+#define QSERDES_V3_COM_CMN_MISC1 0x17c
+#define QSERDES_V3_COM_CMN_MISC2 0x180
+#define QSERDES_V3_COM_CMN_MODE 0x184
+#define QSERDES_V3_COM_CMN_VREG_SEL 0x188
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v4.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v4.h
new file mode 100644
index 000000000000..b0e3298d990d
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v4.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_COM_V4_H_
+#define QCOM_PHY_QMP_QSERDES_COM_V4_H_
+
+/* Only for QMP V4 PHY - QSERDES COM registers */
+#define QSERDES_V4_COM_ATB_SEL1 0x000
+#define QSERDES_V4_COM_ATB_SEL2 0x004
+#define QSERDES_V4_COM_FREQ_UPDATE 0x008
+#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_ADJ_PER2 0x018
+#define QSERDES_V4_COM_SSC_PER1 0x01c
+#define QSERDES_V4_COM_SSC_PER2 0x020
+#define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0 0x024
+#define QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0 0x028
+#define QSERDES_V4_COM_SSC_STEP_SIZE3_MODE0 0x02c
+#define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1 0x030
+#define QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1 0x034
+#define QSERDES_V4_COM_SSC_STEP_SIZE3_MODE1 0x038
+#define QSERDES_V4_COM_POST_DIV 0x03c
+#define QSERDES_V4_COM_POST_DIV_MUX 0x040
+#define QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN 0x044
+#define QSERDES_V4_COM_CLK_ENABLE1 0x048
+#define QSERDES_V4_COM_SYS_CLK_CTRL 0x04c
+#define QSERDES_V4_COM_SYSCLK_BUF_ENABLE 0x050
+#define QSERDES_V4_COM_PLL_EN 0x054
+#define QSERDES_V4_COM_PLL_IVCO 0x058
+#define QSERDES_V4_COM_CMN_IETRIM 0x05c
+#define QSERDES_V4_COM_CMN_IPTRIM 0x060
+#define QSERDES_V4_COM_EP_CLOCK_DETECT_CTRL 0x064
+#define QSERDES_V4_COM_SYSCLK_DET_COMP_STATUS 0x068
+#define QSERDES_V4_COM_CLK_EP_DIV_MODE0 0x06c
+#define QSERDES_V4_COM_CLK_EP_DIV_MODE1 0x070
+#define QSERDES_V4_COM_CP_CTRL_MODE0 0x074
+#define QSERDES_V4_COM_CP_CTRL_MODE1 0x078
+#define QSERDES_V4_COM_PLL_RCTRL_MODE0 0x07c
+#define QSERDES_V4_COM_PLL_RCTRL_MODE1 0x080
+#define QSERDES_V4_COM_PLL_CCTRL_MODE0 0x084
+#define QSERDES_V4_COM_PLL_CCTRL_MODE1 0x088
+#define QSERDES_V4_COM_PLL_CNTRL 0x08c
+#define QSERDES_V4_COM_BIAS_EN_CTRL_BY_PSM 0x090
+#define QSERDES_V4_COM_SYSCLK_EN_SEL 0x094
+#define QSERDES_V4_COM_CML_SYSCLK_SEL 0x098
+#define QSERDES_V4_COM_RESETSM_CNTRL 0x09c
+#define QSERDES_V4_COM_RESETSM_CNTRL2 0x0a0
+#define QSERDES_V4_COM_LOCK_CMP_EN 0x0a4
+#define QSERDES_V4_COM_LOCK_CMP_CFG 0x0a8
+#define QSERDES_V4_COM_LOCK_CMP1_MODE0 0x0ac
+#define QSERDES_V4_COM_LOCK_CMP2_MODE0 0x0b0
+#define QSERDES_V4_COM_LOCK_CMP1_MODE1 0x0b4
+#define QSERDES_V4_COM_LOCK_CMP2_MODE1 0x0b8
+#define QSERDES_V4_COM_DEC_START_MODE0 0x0bc
+#define QSERDES_V4_COM_DEC_START_MSB_MODE0 0x0c0
+#define QSERDES_V4_COM_DEC_START_MODE1 0x0c4
+#define QSERDES_V4_COM_DEC_START_MSB_MODE1 0x0c8
+#define QSERDES_V4_COM_DIV_FRAC_START1_MODE0 0x0cc
+#define QSERDES_V4_COM_DIV_FRAC_START2_MODE0 0x0d0
+#define QSERDES_V4_COM_DIV_FRAC_START3_MODE0 0x0d4
+#define QSERDES_V4_COM_DIV_FRAC_START1_MODE1 0x0d8
+#define QSERDES_V4_COM_DIV_FRAC_START2_MODE1 0x0dc
+#define QSERDES_V4_COM_DIV_FRAC_START3_MODE1 0x0e0
+#define QSERDES_V4_COM_INTEGLOOP_INITVAL 0x0e4
+#define QSERDES_V4_COM_INTEGLOOP_EN 0x0e8
+#define QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0 0x0ec
+#define QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0 0x0f0
+#define QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE1 0x0f4
+#define QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE1 0x0f8
+#define QSERDES_V4_COM_INTEGLOOP_P_PATH_GAIN0 0x0fc
+#define QSERDES_V4_COM_INTEGLOOP_P_PATH_GAIN1 0x100
+#define QSERDES_V4_COM_VCOCAL_DEADMAN_CTRL 0x104
+#define QSERDES_V4_COM_VCO_TUNE_CTRL 0x108
+#define QSERDES_V4_COM_VCO_TUNE_MAP 0x10c
+#define QSERDES_V4_COM_VCO_TUNE1_MODE0 0x110
+#define QSERDES_V4_COM_VCO_TUNE2_MODE0 0x114
+#define QSERDES_V4_COM_VCO_TUNE1_MODE1 0x118
+#define QSERDES_V4_COM_VCO_TUNE2_MODE1 0x11c
+#define QSERDES_V4_COM_VCO_TUNE_INITVAL1 0x120
+#define QSERDES_V4_COM_VCO_TUNE_INITVAL2 0x124
+#define QSERDES_V4_COM_VCO_TUNE_MINVAL1 0x128
+#define QSERDES_V4_COM_VCO_TUNE_MINVAL2 0x12c
+#define QSERDES_V4_COM_VCO_TUNE_MAXVAL1 0x130
+#define QSERDES_V4_COM_VCO_TUNE_MAXVAL2 0x134
+#define QSERDES_V4_COM_VCO_TUNE_TIMER1 0x138
+#define QSERDES_V4_COM_VCO_TUNE_TIMER2 0x13c
+#define QSERDES_V4_COM_CMN_STATUS 0x140
+#define QSERDES_V4_COM_RESET_SM_STATUS 0x144
+#define QSERDES_V4_COM_RESTRIM_CODE_STATUS 0x148
+#define QSERDES_V4_COM_PLLCAL_CODE1_STATUS 0x14c
+#define QSERDES_V4_COM_PLLCAL_CODE2_STATUS 0x150
+#define QSERDES_V4_COM_CLK_SELECT 0x154
+#define QSERDES_V4_COM_HSCLK_SEL 0x158
+#define QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL 0x15c
+#define QSERDES_V4_COM_INTEGLOOP_BINCODE_STATUS 0x160
+#define QSERDES_V4_COM_PLL_ANALOG 0x164
+#define QSERDES_V4_COM_CORECLK_DIV_MODE0 0x168
+#define QSERDES_V4_COM_CORECLK_DIV_MODE1 0x16c
+#define QSERDES_V4_COM_SW_RESET 0x170
+#define QSERDES_V4_COM_CORE_CLK_EN 0x174
+#define QSERDES_V4_COM_C_READY_STATUS 0x178
+#define QSERDES_V4_COM_CMN_CONFIG 0x17c
+#define QSERDES_V4_COM_CMN_RATE_OVERRIDE 0x180
+#define QSERDES_V4_COM_SVS_MODE_CLK_SEL 0x184
+#define QSERDES_V4_COM_DEBUG_BUS0 0x188
+#define QSERDES_V4_COM_DEBUG_BUS1 0x18c
+#define QSERDES_V4_COM_DEBUG_BUS2 0x190
+#define QSERDES_V4_COM_DEBUG_BUS3 0x194
+#define QSERDES_V4_COM_DEBUG_BUS_SEL 0x198
+#define QSERDES_V4_COM_CMN_MISC1 0x19c
+#define QSERDES_V4_COM_CMN_MISC2 0x1a0
+#define QSERDES_V4_COM_CMN_MODE 0x1a4
+#define QSERDES_V4_COM_VCO_DC_LEVEL_CTRL 0x1a8
+#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x1ac
+#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1b0
+#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x1b4
+#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x1b8
+#define QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL 0x1bc
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v5.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v5.h
new file mode 100644
index 000000000000..c8afdf7bc1ee
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v5.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_COM_V5_H_
+#define QCOM_PHY_QMP_QSERDES_COM_V5_H_
+
+/* Only for QMP V5 PHY - QSERDES COM registers */
+#define QSERDES_V5_COM_ATB_SEL1 0x000
+#define QSERDES_V5_COM_ATB_SEL2 0x004
+#define QSERDES_V5_COM_FREQ_UPDATE 0x008
+#define QSERDES_V5_COM_BG_TIMER 0x00c
+#define QSERDES_V5_COM_SSC_EN_CENTER 0x010
+#define QSERDES_V5_COM_SSC_ADJ_PER1 0x014
+#define QSERDES_V5_COM_SSC_ADJ_PER2 0x018
+#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_SIZE3_MODE0 0x02c
+#define QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1 0x030
+#define QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1 0x034
+#define QSERDES_V5_COM_SSC_STEP_SIZE3_MODE1 0x038
+#define QSERDES_V5_COM_POST_DIV 0x03c
+#define QSERDES_V5_COM_POST_DIV_MUX 0x040
+#define QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN 0x044
+#define QSERDES_V5_COM_CLK_ENABLE1 0x048
+#define QSERDES_V5_COM_SYS_CLK_CTRL 0x04c
+#define QSERDES_V5_COM_SYSCLK_BUF_ENABLE 0x050
+#define QSERDES_V5_COM_PLL_EN 0x054
+#define QSERDES_V5_COM_PLL_IVCO 0x058
+#define QSERDES_V5_COM_CMN_IETRIM 0x05c
+#define QSERDES_V5_COM_CMN_IPTRIM 0x060
+#define QSERDES_V5_COM_EP_CLOCK_DETECT_CTRL 0x064
+#define QSERDES_V5_COM_SYSCLK_DET_COMP_STATUS 0x068
+#define QSERDES_V5_COM_CLK_EP_DIV_MODE0 0x06c
+#define QSERDES_V5_COM_CLK_EP_DIV_MODE1 0x070
+#define QSERDES_V5_COM_CP_CTRL_MODE0 0x074
+#define QSERDES_V5_COM_CP_CTRL_MODE1 0x078
+#define QSERDES_V5_COM_PLL_RCTRL_MODE0 0x07c
+#define QSERDES_V5_COM_PLL_RCTRL_MODE1 0x080
+#define QSERDES_V5_COM_PLL_CCTRL_MODE0 0x084
+#define QSERDES_V5_COM_PLL_CCTRL_MODE1 0x088
+#define QSERDES_V5_COM_PLL_CNTRL 0x08c
+#define QSERDES_V5_COM_BIAS_EN_CTRL_BY_PSM 0x090
+#define QSERDES_V5_COM_SYSCLK_EN_SEL 0x094
+#define QSERDES_V5_COM_CML_SYSCLK_SEL 0x098
+#define QSERDES_V5_COM_RESETSM_CNTRL 0x09c
+#define QSERDES_V5_COM_RESETSM_CNTRL2 0x0a0
+#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_LOCK_CMP2_MODE1 0x0b8
+#define QSERDES_V5_COM_DEC_START_MODE0 0x0bc
+#define QSERDES_V5_COM_DEC_START_MSB_MODE0 0x0c0
+#define QSERDES_V5_COM_DEC_START_MODE1 0x0c4
+#define QSERDES_V5_COM_DEC_START_MSB_MODE1 0x0c8
+#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_INTEGLOOP_INITVAL 0x0e4
+#define QSERDES_V5_COM_INTEGLOOP_EN 0x0e8
+#define QSERDES_V5_COM_INTEGLOOP_GAIN0_MODE0 0x0ec
+#define QSERDES_V5_COM_INTEGLOOP_GAIN1_MODE0 0x0f0
+#define QSERDES_V5_COM_INTEGLOOP_GAIN0_MODE1 0x0f4
+#define QSERDES_V5_COM_INTEGLOOP_GAIN1_MODE1 0x0f8
+#define QSERDES_V5_COM_INTEGLOOP_P_PATH_GAIN0 0x0fc
+#define QSERDES_V5_COM_INTEGLOOP_P_PATH_GAIN1 0x100
+#define QSERDES_V5_COM_VCOCAL_DEADMAN_CTRL 0x104
+#define QSERDES_V5_COM_VCO_TUNE_CTRL 0x108
+#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_INITVAL1 0x120
+#define QSERDES_V5_COM_VCO_TUNE_INITVAL2 0x124
+#define QSERDES_V5_COM_VCO_TUNE_MINVAL1 0x128
+#define QSERDES_V5_COM_VCO_TUNE_MINVAL2 0x12c
+#define QSERDES_V5_COM_VCO_TUNE_MAXVAL1 0x130
+#define QSERDES_V5_COM_VCO_TUNE_MAXVAL2 0x134
+#define QSERDES_V5_COM_VCO_TUNE_TIMER1 0x138
+#define QSERDES_V5_COM_VCO_TUNE_TIMER2 0x13c
+#define QSERDES_V5_COM_CMN_STATUS 0x140
+#define QSERDES_V5_COM_RESET_SM_STATUS 0x144
+#define QSERDES_V5_COM_RESTRIM_CODE_STATUS 0x148
+#define QSERDES_V5_COM_PLLCAL_CODE1_STATUS 0x14c
+#define QSERDES_V5_COM_PLLCAL_CODE2_STATUS 0x150
+#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_INTEGLOOP_BINCODE_STATUS 0x160
+#define QSERDES_V5_COM_PLL_ANALOG 0x164
+#define QSERDES_V5_COM_CORECLK_DIV_MODE0 0x168
+#define QSERDES_V5_COM_CORECLK_DIV_MODE1 0x16c
+#define QSERDES_V5_COM_SW_RESET 0x170
+#define QSERDES_V5_COM_CORE_CLK_EN 0x174
+#define QSERDES_V5_COM_C_READY_STATUS 0x178
+#define QSERDES_V5_COM_CMN_CONFIG 0x17c
+#define QSERDES_V5_COM_CMN_RATE_OVERRIDE 0x180
+#define QSERDES_V5_COM_SVS_MODE_CLK_SEL 0x184
+#define QSERDES_V5_COM_DEBUG_BUS0 0x188
+#define QSERDES_V5_COM_DEBUG_BUS1 0x18c
+#define QSERDES_V5_COM_DEBUG_BUS2 0x190
+#define QSERDES_V5_COM_DEBUG_BUS3 0x194
+#define QSERDES_V5_COM_DEBUG_BUS_SEL 0x198
+#define QSERDES_V5_COM_CMN_MISC1 0x19c
+#define QSERDES_V5_COM_CMN_MODE 0x1a0
+#define QSERDES_V5_COM_CMN_MODE_CONTD 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
+#define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x1b8
+#define QSERDES_V5_COM_BIN_VCOCAL_HSCLK_SEL 0x1bc
+#define QSERDES_V5_COM_RESERVED_1 0x1c0
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com.h
new file mode 100644
index 000000000000..fbaf6ef467f8
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com.h
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_COM_H_
+#define QCOM_PHY_QMP_QSERDES_COM_H_
+
+/* Only for QMP V2 PHY - QSERDES COM registers */
+#define QSERDES_COM_ATB_SEL1 0x000
+#define QSERDES_COM_ATB_SEL2 0x004
+#define QSERDES_COM_FREQ_UPDATE 0x008
+#define QSERDES_COM_BG_TIMER 0x00c
+#define QSERDES_COM_SSC_EN_CENTER 0x010
+#define QSERDES_COM_SSC_ADJ_PER1 0x014
+#define QSERDES_COM_SSC_ADJ_PER2 0x018
+#define QSERDES_COM_SSC_PER1 0x01c
+#define QSERDES_COM_SSC_PER2 0x020
+#define QSERDES_COM_SSC_STEP_SIZE1 0x024
+#define QSERDES_COM_SSC_STEP_SIZE2 0x028
+#define QSERDES_COM_POST_DIV 0x02c
+#define QSERDES_COM_POST_DIV_MUX 0x030
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034
+#define QSERDES_COM_CLK_ENABLE1 0x038
+#define QSERDES_COM_SYS_CLK_CTRL 0x03c
+#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040
+#define QSERDES_COM_PLL_EN 0x044
+#define QSERDES_COM_PLL_IVCO 0x048
+#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c
+#define QSERDES_COM_LOCK_CMP2_MODE0 0x050
+#define QSERDES_COM_LOCK_CMP3_MODE0 0x054
+#define QSERDES_COM_LOCK_CMP1_MODE1 0x058
+#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c
+#define QSERDES_COM_LOCK_CMP3_MODE1 0x060
+#define QSERDES_COM_LOCK_CMP1_MODE2 0x064
+#define QSERDES_COM_CMN_RSVD0 0x064
+#define QSERDES_COM_LOCK_CMP2_MODE2 0x068
+#define QSERDES_COM_EP_CLOCK_DETECT_CTRL 0x068
+#define QSERDES_COM_LOCK_CMP3_MODE2 0x06c
+#define QSERDES_COM_SYSCLK_DET_COMP_STATUS 0x06c
+#define QSERDES_COM_BG_TRIM 0x070
+#define QSERDES_COM_CLK_EP_DIV 0x074
+#define QSERDES_COM_CP_CTRL_MODE0 0x078
+#define QSERDES_COM_CP_CTRL_MODE1 0x07c
+#define QSERDES_COM_CP_CTRL_MODE2 0x080
+#define QSERDES_COM_CMN_RSVD1 0x080
+#define QSERDES_COM_PLL_RCTRL_MODE0 0x084
+#define QSERDES_COM_PLL_RCTRL_MODE1 0x088
+#define QSERDES_COM_PLL_RCTRL_MODE2 0x08c
+#define QSERDES_COM_CMN_RSVD2 0x08c
+#define QSERDES_COM_PLL_CCTRL_MODE0 0x090
+#define QSERDES_COM_PLL_CCTRL_MODE1 0x094
+#define QSERDES_COM_PLL_CCTRL_MODE2 0x098
+#define QSERDES_COM_CMN_RSVD3 0x098
+#define QSERDES_COM_PLL_CNTRL 0x09c
+#define QSERDES_COM_PHASE_SEL_CTRL 0x0a0
+#define QSERDES_COM_PHASE_SEL_DC 0x0a4
+#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL 0x0a8
+#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x0a8
+#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac
+#define QSERDES_COM_CML_SYSCLK_SEL 0x0b0
+#define QSERDES_COM_RESETSM_CNTRL 0x0b4
+#define QSERDES_COM_RESETSM_CNTRL2 0x0b8
+#define QSERDES_COM_RESTRIM_CTRL 0x0bc
+#define QSERDES_COM_RESTRIM_CTRL2 0x0c0
+#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4
+#define QSERDES_COM_LOCK_CMP_EN 0x0c8
+#define QSERDES_COM_LOCK_CMP_CFG 0x0cc
+#define QSERDES_COM_DEC_START_MODE0 0x0d0
+#define QSERDES_COM_DEC_START_MODE1 0x0d4
+#define QSERDES_COM_DEC_START_MODE2 0x0d8
+#define QSERDES_COM_VCOCAL_DEADMAN_CTRL 0x0d8
+#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc
+#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0
+#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4
+#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8
+#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec
+#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0
+#define QSERDES_COM_DIV_FRAC_START1_MODE2 0x0f4
+#define QSERDES_COM_VCO_TUNE_MINVAL1 0x0f4
+#define QSERDES_COM_DIV_FRAC_START2_MODE2 0x0f8
+#define QSERDES_COM_VCO_TUNE_MINVAL2 0x0f8
+#define QSERDES_COM_DIV_FRAC_START3_MODE2 0x0fc
+#define QSERDES_COM_CMN_RSVD4 0x0fc
+#define QSERDES_COM_INTEGLOOP_INITVAL 0x100
+#define QSERDES_COM_INTEGLOOP_EN 0x104
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE2 0x118
+#define QSERDES_COM_VCO_TUNE_MAXVAL1 0x118
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE2 0x11c
+#define QSERDES_COM_VCO_TUNE_MAXVAL2 0x11c
+#define QSERDES_COM_RES_TRIM_CONTROL2 0x120
+#define QSERDES_COM_VCO_TUNE_CTRL 0x124
+#define QSERDES_COM_VCO_TUNE_MAP 0x128
+#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c
+#define QSERDES_COM_VCO_TUNE2_MODE0 0x130
+#define QSERDES_COM_VCO_TUNE1_MODE1 0x134
+#define QSERDES_COM_VCO_TUNE2_MODE1 0x138
+#define QSERDES_COM_VCO_TUNE1_MODE2 0x13c
+#define QSERDES_COM_VCO_TUNE_INITVAL1 0x13c
+#define QSERDES_COM_VCO_TUNE2_MODE2 0x140
+#define QSERDES_COM_VCO_TUNE_INITVAL2 0x140
+#define QSERDES_COM_VCO_TUNE_TIMER1 0x144
+#define QSERDES_COM_VCO_TUNE_TIMER2 0x148
+#define QSERDES_COM_SAR 0x14c
+#define QSERDES_COM_SAR_CLK 0x150
+#define QSERDES_COM_SAR_CODE_OUT_STATUS 0x154
+#define QSERDES_COM_SAR_CODE_READY_STATUS 0x158
+#define QSERDES_COM_CMN_STATUS 0x15c
+#define QSERDES_COM_RESET_SM_STATUS 0x160
+#define QSERDES_COM_RESTRIM_CODE_STATUS 0x164
+#define QSERDES_COM_PLLCAL_CODE1_STATUS 0x168
+#define QSERDES_COM_PLLCAL_CODE2_STATUS 0x16c
+#define QSERDES_COM_BG_CTRL 0x170
+#define QSERDES_COM_CLK_SELECT 0x174
+#define QSERDES_COM_HSCLK_SEL 0x178
+#define QSERDES_COM_INTEGLOOP_BINCODE_STATUS 0x17c
+#define QSERDES_COM_PLL_ANALOG 0x180
+#define QSERDES_COM_CORECLK_DIV 0x184
+#define QSERDES_COM_SW_RESET 0x188
+#define QSERDES_COM_CORE_CLK_EN 0x18c
+#define QSERDES_COM_C_READY_STATUS 0x190
+#define QSERDES_COM_CMN_CONFIG 0x194
+#define QSERDES_COM_CMN_RATE_OVERRIDE 0x198
+#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c
+#define QSERDES_COM_DEBUG_BUS0 0x1a0
+#define QSERDES_COM_DEBUG_BUS1 0x1a4
+#define QSERDES_COM_DEBUG_BUS2 0x1a8
+#define QSERDES_COM_DEBUG_BUS3 0x1ac
+#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0
+#define QSERDES_COM_CMN_MISC1 0x1b4
+#define QSERDES_COM_CMN_MISC2 0x1b8
+#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc
+#define QSERDES_COM_CORECLK_DIV_MODE2 0x1c0
+#define QSERDES_COM_CMN_RSVD5 0x1c0
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-pll.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-pll.h
new file mode 100644
index 000000000000..ad326e301a3a
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-pll.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_PLL_H_
+#define QCOM_PHY_QMP_QSERDES_PLL_H_
+
+/* QMP V2 PHY for PCIE gen3 ports - QSERDES PLL registers */
+#define QSERDES_PLL_BG_TIMER 0x00c
+#define QSERDES_PLL_SSC_PER1 0x01c
+#define QSERDES_PLL_SSC_PER2 0x020
+#define QSERDES_PLL_SSC_STEP_SIZE1_MODE0 0x024
+#define QSERDES_PLL_SSC_STEP_SIZE2_MODE0 0x028
+#define QSERDES_PLL_SSC_STEP_SIZE1_MODE1 0x02c
+#define QSERDES_PLL_SSC_STEP_SIZE2_MODE1 0x030
+#define QSERDES_PLL_BIAS_EN_CLKBUFLR_EN 0x03c
+#define QSERDES_PLL_CLK_ENABLE1 0x040
+#define QSERDES_PLL_SYS_CLK_CTRL 0x044
+#define QSERDES_PLL_SYSCLK_BUF_ENABLE 0x048
+#define QSERDES_PLL_PLL_IVCO 0x050
+#define QSERDES_PLL_LOCK_CMP1_MODE0 0x054
+#define QSERDES_PLL_LOCK_CMP2_MODE0 0x058
+#define QSERDES_PLL_LOCK_CMP1_MODE1 0x060
+#define QSERDES_PLL_LOCK_CMP2_MODE1 0x064
+#define QSERDES_PLL_BG_TRIM 0x074
+#define QSERDES_PLL_CLK_EP_DIV_MODE0 0x078
+#define QSERDES_PLL_CLK_EP_DIV_MODE1 0x07c
+#define QSERDES_PLL_CP_CTRL_MODE0 0x080
+#define QSERDES_PLL_CP_CTRL_MODE1 0x084
+#define QSERDES_PLL_PLL_RCTRL_MODE0 0x088
+#define QSERDES_PLL_PLL_RCTRL_MODE1 0x08c
+#define QSERDES_PLL_PLL_CCTRL_MODE0 0x090
+#define QSERDES_PLL_PLL_CCTRL_MODE1 0x094
+#define QSERDES_PLL_BIAS_EN_CTRL_BY_PSM 0x0a4
+#define QSERDES_PLL_SYSCLK_EN_SEL 0x0a8
+#define QSERDES_PLL_RESETSM_CNTRL 0x0b0
+#define QSERDES_PLL_LOCK_CMP_EN 0x0c4
+#define QSERDES_PLL_DEC_START_MODE0 0x0cc
+#define QSERDES_PLL_DEC_START_MODE1 0x0d0
+#define QSERDES_PLL_DIV_FRAC_START1_MODE0 0x0d8
+#define QSERDES_PLL_DIV_FRAC_START2_MODE0 0x0dc
+#define QSERDES_PLL_DIV_FRAC_START3_MODE0 0x0e0
+#define QSERDES_PLL_DIV_FRAC_START1_MODE1 0x0e4
+#define QSERDES_PLL_DIV_FRAC_START2_MODE1 0x0e8
+#define QSERDES_PLL_DIV_FRAC_START3_MODE1 0x0ec
+#define QSERDES_PLL_INTEGLOOP_GAIN0_MODE0 0x100
+#define QSERDES_PLL_INTEGLOOP_GAIN1_MODE0 0x104
+#define QSERDES_PLL_INTEGLOOP_GAIN0_MODE1 0x108
+#define QSERDES_PLL_INTEGLOOP_GAIN1_MODE1 0x10c
+#define QSERDES_PLL_VCO_TUNE_MAP 0x120
+#define QSERDES_PLL_VCO_TUNE1_MODE0 0x124
+#define QSERDES_PLL_VCO_TUNE2_MODE0 0x128
+#define QSERDES_PLL_VCO_TUNE1_MODE1 0x12c
+#define QSERDES_PLL_VCO_TUNE2_MODE1 0x130
+#define QSERDES_PLL_VCO_TUNE_TIMER1 0x13c
+#define QSERDES_PLL_VCO_TUNE_TIMER2 0x140
+#define QSERDES_PLL_CLK_SELECT 0x16c
+#define QSERDES_PLL_HSCLK_SEL 0x170
+#define QSERDES_PLL_CORECLK_DIV 0x17c
+#define QSERDES_PLL_CORE_CLK_EN 0x184
+#define QSERDES_PLL_CMN_CONFIG 0x18c
+#define QSERDES_PLL_SVS_MODE_CLK_SEL 0x194
+#define QSERDES_PLL_CORECLK_DIV_MODE1 0x1b4
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v3.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v3.h
new file mode 100644
index 000000000000..161e6df30ea8
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v3.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V3_H_
+#define QCOM_PHY_QMP_QSERDES_TXRX_V3_H_
+
+/* Only for QMP V3 PHY - TX registers */
+#define QSERDES_V3_TX_BIST_MODE_LANENO 0x000
+#define QSERDES_V3_TX_CLKBUF_ENABLE 0x008
+#define QSERDES_V3_TX_TX_EMP_POST1_LVL 0x00c
+#define QSERDES_V3_TX_TX_DRV_LVL 0x01c
+#define QSERDES_V3_TX_RESET_TSYNC_EN 0x024
+#define QSERDES_V3_TX_PRE_STALL_LDO_BOOST_EN 0x028
+#define QSERDES_V3_TX_TX_BAND 0x02c
+#define QSERDES_V3_TX_SLEW_CNTL 0x030
+#define QSERDES_V3_TX_INTERFACE_SELECT 0x034
+#define QSERDES_V3_TX_RES_CODE_LANE_TX 0x03c
+#define QSERDES_V3_TX_RES_CODE_LANE_RX 0x040
+#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX 0x044
+#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX 0x048
+#define QSERDES_V3_TX_DEBUG_BUS_SEL 0x058
+#define QSERDES_V3_TX_TRANSCEIVER_BIAS_EN 0x05c
+#define QSERDES_V3_TX_HIGHZ_DRVR_EN 0x060
+#define QSERDES_V3_TX_TX_POL_INV 0x064
+#define QSERDES_V3_TX_PARRATE_REC_DETECT_IDLE_EN 0x068
+#define QSERDES_V3_TX_LANE_MODE_1 0x08c
+#define QSERDES_V3_TX_LANE_MODE_2 0x090
+#define QSERDES_V3_TX_LANE_MODE_3 0x094
+#define QSERDES_V3_TX_RCV_DETECT_LVL_2 0x0a4
+#define QSERDES_V3_TX_TRAN_DRVR_EMP_EN 0x0c0
+#define QSERDES_V3_TX_TX_INTERFACE_MODE 0x0c4
+#define QSERDES_V3_TX_VMODE_CTRL1 0x0f0
+
+/* Only for QMP V3 PHY - RX registers */
+#define QSERDES_V3_RX_UCDR_FO_GAIN 0x008
+#define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c
+#define QSERDES_V3_RX_UCDR_SO_GAIN 0x014
+#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF 0x024
+#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER 0x028
+#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN 0x02c
+#define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030
+#define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034
+#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c
+#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040
+#define QSERDES_V3_RX_UCDR_PI_CONTROLS 0x044
+#define QSERDES_V3_RX_RX_TERM_BW 0x07c
+#define QSERDES_V3_RX_VGA_CAL_CNTRL1 0x0bc
+#define QSERDES_V3_RX_VGA_CAL_CNTRL2 0x0c0
+#define QSERDES_V3_RX_RX_EQ_GAIN2_LSB 0x0c8
+#define QSERDES_V3_RX_RX_EQ_GAIN2_MSB 0x0cc
+#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL1 0x0d0
+#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d4
+#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3 0x0d8
+#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4 0x0dc
+#define QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x0f8
+#define QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x0fc
+#define QSERDES_V3_RX_SIGDET_ENABLES 0x100
+#define QSERDES_V3_RX_SIGDET_CNTRL 0x104
+#define QSERDES_V3_RX_SIGDET_LVL 0x108
+#define QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL 0x10c
+#define QSERDES_V3_RX_RX_BAND 0x110
+#define QSERDES_V3_RX_RX_INTERFACE_MODE 0x11c
+#define QSERDES_V3_RX_RX_MODE_00 0x164
+#define QSERDES_V3_RX_RX_MODE_01 0x168
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4.h
new file mode 100644
index 000000000000..6ee3bec9ac4a
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4.h
@@ -0,0 +1,233 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V4_H_
+#define QCOM_PHY_QMP_QSERDES_TXRX_V4_H_
+
+/* Only for QMP V4 PHY - TX registers */
+#define QSERDES_V4_TX_BIST_MODE_LANENO 0x000
+#define QSERDES_V4_TX_BIST_INVERT 0x004
+#define QSERDES_V4_TX_CLKBUF_ENABLE 0x008
+#define QSERDES_V4_TX_TX_EMP_POST1_LVL 0x00c
+#define QSERDES_V4_TX_TX_IDLE_LVL_LARGE_AMP 0x010
+#define QSERDES_V4_TX_TX_DRV_LVL 0x014
+#define QSERDES_V4_TX_TX_DRV_LVL_OFFSET 0x018
+#define QSERDES_V4_TX_RESET_TSYNC_EN 0x01c
+#define QSERDES_V4_TX_PRE_STALL_LDO_BOOST_EN 0x020
+#define QSERDES_V4_TX_TX_BAND 0x024
+#define QSERDES_V4_TX_SLEW_CNTL 0x028
+#define QSERDES_V4_TX_INTERFACE_SELECT 0x02c
+#define QSERDES_V4_TX_LPB_EN 0x030
+#define QSERDES_V4_TX_RES_CODE_LANE_TX 0x034
+#define QSERDES_V4_TX_RES_CODE_LANE_RX 0x038
+#define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX 0x03c
+#define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX 0x040
+#define QSERDES_V4_TX_PERL_LENGTH1 0x044
+#define QSERDES_V4_TX_PERL_LENGTH2 0x048
+#define QSERDES_V4_TX_SERDES_BYP_EN_OUT 0x04c
+#define QSERDES_V4_TX_DEBUG_BUS_SEL 0x050
+#define QSERDES_V4_TX_TRANSCEIVER_BIAS_EN 0x054
+#define QSERDES_V4_TX_HIGHZ_DRVR_EN 0x058
+#define QSERDES_V4_TX_TX_POL_INV 0x05c
+#define QSERDES_V4_TX_PARRATE_REC_DETECT_IDLE_EN 0x060
+#define QSERDES_V4_TX_BIST_PATTERN1 0x064
+#define QSERDES_V4_TX_BIST_PATTERN2 0x068
+#define QSERDES_V4_TX_BIST_PATTERN3 0x06c
+#define QSERDES_V4_TX_BIST_PATTERN4 0x070
+#define QSERDES_V4_TX_BIST_PATTERN5 0x074
+#define QSERDES_V4_TX_BIST_PATTERN6 0x078
+#define QSERDES_V4_TX_BIST_PATTERN7 0x07c
+#define QSERDES_V4_TX_BIST_PATTERN8 0x080
+#define QSERDES_V4_TX_LANE_MODE_1 0x084
+#define QSERDES_V4_TX_LANE_MODE_2 0x088
+#define QSERDES_V4_TX_LANE_MODE_3 0x08c
+#define QSERDES_V4_TX_ATB_SEL1 0x090
+#define QSERDES_V4_TX_ATB_SEL2 0x094
+#define QSERDES_V4_TX_RCV_DETECT_LVL 0x098
+#define QSERDES_V4_TX_RCV_DETECT_LVL_2 0x09c
+#define QSERDES_V4_TX_PRBS_SEED1 0x0a0
+#define QSERDES_V4_TX_PRBS_SEED2 0x0a4
+#define QSERDES_V4_TX_PRBS_SEED3 0x0a8
+#define QSERDES_V4_TX_PRBS_SEED4 0x0ac
+#define QSERDES_V4_TX_RESET_GEN 0x0b0
+#define QSERDES_V4_TX_RESET_GEN_MUXES 0x0b4
+#define QSERDES_V4_TX_TRAN_DRVR_EMP_EN 0x0b8
+#define QSERDES_V4_TX_TX_INTERFACE_MODE 0x0bc
+#define QSERDES_V4_TX_PWM_CTRL 0x0c0
+#define QSERDES_V4_TX_PWM_ENCODED_OR_DATA 0x0c4
+#define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND2 0x0c8
+#define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND2 0x0cc
+#define QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND2 0x0d0
+#define QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND2 0x0d4
+#define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0x0d8
+#define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0x0dc
+#define QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0x0e0
+#define QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0x0e4
+#define QSERDES_V4_TX_VMODE_CTRL1 0x0e8
+#define QSERDES_V4_TX_ALOG_OBSV_BUS_CTRL_1 0x0ec
+#define QSERDES_V4_TX_BIST_STATUS 0x0f0
+#define QSERDES_V4_TX_BIST_ERROR_COUNT1 0x0f4
+#define QSERDES_V4_TX_BIST_ERROR_COUNT2 0x0f8
+#define QSERDES_V4_TX_ALOG_OBSV_BUS_STATUS_1 0x0fc
+#define QSERDES_V4_TX_LANE_DIG_CONFIG 0x100
+#define QSERDES_V4_TX_PI_QEC_CTRL 0x104
+#define QSERDES_V4_TX_PRE_EMPH 0x108
+#define QSERDES_V4_TX_SW_RESET 0x10c
+#define QSERDES_V4_TX_DCC_OFFSET 0x110
+#define QSERDES_V4_TX_DIG_BKUP_CTRL 0x114
+#define QSERDES_V4_TX_DEBUG_BUS0 0x118
+#define QSERDES_V4_TX_DEBUG_BUS1 0x11c
+#define QSERDES_V4_TX_DEBUG_BUS2 0x120
+#define QSERDES_V4_TX_DEBUG_BUS3 0x124
+#define QSERDES_V4_TX_READ_EQCODE 0x128
+#define QSERDES_V4_TX_READ_OFFSETCODE 0x12c
+#define QSERDES_V4_TX_IA_ERROR_COUNTER_LOW 0x130
+#define QSERDES_V4_TX_IA_ERROR_COUNTER_HIGH 0x134
+#define QSERDES_V4_TX_VGA_READ_CODE 0x138
+#define QSERDES_V4_TX_VTH_READ_CODE 0x13c
+#define QSERDES_V4_TX_DFE_TAP1_READ_CODE 0x140
+#define QSERDES_V4_TX_DFE_TAP2_READ_CODE 0x144
+#define QSERDES_V4_TX_IDAC_STATUS_I 0x148
+#define QSERDES_V4_TX_IDAC_STATUS_IBAR 0x14c
+#define QSERDES_V4_TX_IDAC_STATUS_Q 0x150
+#define QSERDES_V4_TX_IDAC_STATUS_QBAR 0x154
+#define QSERDES_V4_TX_IDAC_STATUS_A 0x158
+#define QSERDES_V4_TX_IDAC_STATUS_ABAR 0x15c
+#define QSERDES_V4_TX_IDAC_STATUS_SM_ON 0x160
+#define QSERDES_V4_TX_IDAC_STATUS_CAL_DONE 0x164
+#define QSERDES_V4_TX_IDAC_STATUS_SIGNERROR 0x168
+#define QSERDES_V4_TX_DCC_CAL_STATUS 0x16c
+
+/* Only for QMP V4 PHY - RX registers */
+#define QSERDES_V4_RX_UCDR_FO_GAIN_HALF 0x000
+#define QSERDES_V4_RX_UCDR_FO_GAIN_QUARTER 0x004
+#define QSERDES_V4_RX_UCDR_FO_GAIN 0x008
+#define QSERDES_V4_RX_UCDR_SO_GAIN_HALF 0x00c
+#define QSERDES_V4_RX_UCDR_SO_GAIN_QUARTER 0x010
+#define QSERDES_V4_RX_UCDR_SO_GAIN 0x014
+#define QSERDES_V4_RX_UCDR_SVS_FO_GAIN_HALF 0x018
+#define QSERDES_V4_RX_UCDR_SVS_FO_GAIN_QUARTER 0x01c
+#define QSERDES_V4_RX_UCDR_SVS_FO_GAIN 0x020
+#define QSERDES_V4_RX_UCDR_SVS_SO_GAIN_HALF 0x024
+#define QSERDES_V4_RX_UCDR_SVS_SO_GAIN_QUARTER 0x028
+#define QSERDES_V4_RX_UCDR_SVS_SO_GAIN 0x02c
+#define QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN 0x030
+#define QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034
+#define QSERDES_V4_RX_UCDR_FO_TO_SO_DELAY 0x038
+#define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c
+#define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040
+#define QSERDES_V4_RX_UCDR_PI_CONTROLS 0x044
+#define QSERDES_V4_RX_UCDR_PI_CTRL2 0x048
+#define QSERDES_V4_RX_UCDR_SB2_THRESH1 0x04c
+#define QSERDES_V4_RX_UCDR_SB2_THRESH2 0x050
+#define QSERDES_V4_RX_UCDR_SB2_GAIN1 0x054
+#define QSERDES_V4_RX_UCDR_SB2_GAIN2 0x058
+#define QSERDES_V4_RX_AUX_CONTROL 0x05c
+#define QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE 0x060
+#define QSERDES_V4_RX_RCLK_AUXDATA_SEL 0x064
+#define QSERDES_V4_RX_AC_JTAG_ENABLE 0x068
+#define QSERDES_V4_RX_AC_JTAG_INITP 0x06c
+#define QSERDES_V4_RX_AC_JTAG_INITN 0x070
+#define QSERDES_V4_RX_AC_JTAG_LVL 0x074
+#define QSERDES_V4_RX_AC_JTAG_MODE 0x078
+#define QSERDES_V4_RX_AC_JTAG_RESET 0x07c
+#define QSERDES_V4_RX_RX_TERM_BW 0x080
+#define QSERDES_V4_RX_RX_RCVR_IQ_EN 0x084
+#define QSERDES_V4_RX_RX_IDAC_I_DC_OFFSETS 0x088
+#define QSERDES_V4_RX_RX_IDAC_IBAR_DC_OFFSETS 0x08c
+#define QSERDES_V4_RX_RX_IDAC_Q_DC_OFFSETS 0x090
+#define QSERDES_V4_RX_RX_IDAC_QBAR_DC_OFFSETS 0x094
+#define QSERDES_V4_RX_RX_IDAC_A_DC_OFFSETS 0x098
+#define QSERDES_V4_RX_RX_IDAC_ABAR_DC_OFFSETS 0x09c
+#define QSERDES_V4_RX_RX_IDAC_EN 0x0a0
+#define QSERDES_V4_RX_RX_IDAC_ENABLES 0x0a4
+#define QSERDES_V4_RX_RX_IDAC_SIGN 0x0a8
+#define QSERDES_V4_RX_RX_HIGHZ_HIGHRATE 0x0ac
+#define QSERDES_V4_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x0b0
+#define QSERDES_V4_RX_DFE_1 0x0b4
+#define QSERDES_V4_RX_DFE_2 0x0b8
+#define QSERDES_V4_RX_DFE_3 0x0bc
+#define QSERDES_V4_RX_DFE_4 0x0c0
+#define QSERDES_V4_RX_TX_ADAPT_PRE_THRESH1 0x0c4
+#define QSERDES_V4_RX_TX_ADAPT_PRE_THRESH2 0x0c8
+#define QSERDES_V4_RX_TX_ADAPT_POST_THRESH 0x0cc
+#define QSERDES_V4_RX_TX_ADAPT_MAIN_THRESH 0x0d0
+#define QSERDES_V4_RX_VGA_CAL_CNTRL1 0x0d4
+#define QSERDES_V4_RX_VGA_CAL_CNTRL2 0x0d8
+#define QSERDES_V4_RX_GM_CAL 0x0dc
+#define QSERDES_V4_RX_RX_VGA_GAIN2_LSB 0x0e0
+#define QSERDES_V4_RX_RX_VGA_GAIN2_MSB 0x0e4
+#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1 0x0e8
+#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2 0x0ec
+#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3 0x0f0
+#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4 0x0f4
+#define QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW 0x0f8
+#define QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH 0x0fc
+#define QSERDES_V4_RX_RX_IDAC_MEASURE_TIME 0x100
+#define QSERDES_V4_RX_RX_IDAC_ACCUMULATOR 0x104
+#define QSERDES_V4_RX_RX_EQ_OFFSET_LSB 0x108
+#define QSERDES_V4_RX_RX_EQ_OFFSET_MSB 0x10c
+#define QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x110
+#define QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x114
+#define QSERDES_V4_RX_SIGDET_ENABLES 0x118
+#define QSERDES_V4_RX_SIGDET_CNTRL 0x11c
+#define QSERDES_V4_RX_SIGDET_LVL 0x120
+#define QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL 0x124
+#define QSERDES_V4_RX_RX_BAND 0x128
+#define QSERDES_V4_RX_CDR_FREEZE_UP_DN 0x12c
+#define QSERDES_V4_RX_CDR_RESET_OVERRIDE 0x130
+#define QSERDES_V4_RX_RX_INTERFACE_MODE 0x134
+#define QSERDES_V4_RX_JITTER_GEN_MODE 0x138
+#define QSERDES_V4_RX_SJ_AMP1 0x13c
+#define QSERDES_V4_RX_SJ_AMP2 0x140
+#define QSERDES_V4_RX_SJ_PER1 0x144
+#define QSERDES_V4_RX_SJ_PER2 0x148
+#define QSERDES_V4_RX_PPM_OFFSET1 0x14c
+#define QSERDES_V4_RX_PPM_OFFSET2 0x150
+#define QSERDES_V4_RX_SIGN_PPM_PERIOD1 0x154
+#define QSERDES_V4_RX_SIGN_PPM_PERIOD2 0x158
+#define QSERDES_V4_RX_RX_PWM_ENABLE_AND_DATA 0x15c
+#define QSERDES_V4_RX_RX_PWM_GEAR1_TIMEOUT_COUNT 0x160
+#define QSERDES_V4_RX_RX_PWM_GEAR2_TIMEOUT_COUNT 0x164
+#define QSERDES_V4_RX_RX_PWM_GEAR3_TIMEOUT_COUNT 0x168
+#define QSERDES_V4_RX_RX_PWM_GEAR4_TIMEOUT_COUNT 0x16c
+#define QSERDES_V4_RX_RX_MODE_00_LOW 0x170
+#define QSERDES_V4_RX_RX_MODE_00_HIGH 0x174
+#define QSERDES_V4_RX_RX_MODE_00_HIGH2 0x178
+#define QSERDES_V4_RX_RX_MODE_00_HIGH3 0x17c
+#define QSERDES_V4_RX_RX_MODE_00_HIGH4 0x180
+#define QSERDES_V4_RX_RX_MODE_01_LOW 0x184
+#define QSERDES_V4_RX_RX_MODE_01_HIGH 0x188
+#define QSERDES_V4_RX_RX_MODE_01_HIGH2 0x18c
+#define QSERDES_V4_RX_RX_MODE_01_HIGH3 0x190
+#define QSERDES_V4_RX_RX_MODE_01_HIGH4 0x194
+#define QSERDES_V4_RX_RX_MODE_10_LOW 0x198
+#define QSERDES_V4_RX_RX_MODE_10_HIGH 0x19c
+#define QSERDES_V4_RX_RX_MODE_10_HIGH2 0x1a0
+#define QSERDES_V4_RX_RX_MODE_10_HIGH3 0x1a4
+#define QSERDES_V4_RX_RX_MODE_10_HIGH4 0x1a8
+#define QSERDES_V4_RX_PHPRE_CTRL 0x1ac
+#define QSERDES_V4_RX_PHPRE_INITVAL 0x1b0
+#define QSERDES_V4_RX_DFE_EN_TIMER 0x1b4
+#define QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET 0x1b8
+#define QSERDES_V4_RX_DCC_CTRL1 0x1bc
+#define QSERDES_V4_RX_DCC_CTRL2 0x1c0
+#define QSERDES_V4_RX_VTH_CODE 0x1c4
+#define QSERDES_V4_RX_VTH_MIN_THRESH 0x1c8
+#define QSERDES_V4_RX_VTH_MAX_THRESH 0x1cc
+#define QSERDES_V4_RX_ALOG_OBSV_BUS_CTRL_1 0x1d0
+#define QSERDES_V4_RX_PI_CTRL1 0x1d4
+#define QSERDES_V4_RX_PI_CTRL2 0x1d8
+#define QSERDES_V4_RX_PI_QUAD 0x1dc
+#define QSERDES_V4_RX_IDATA1 0x1e0
+#define QSERDES_V4_RX_IDATA2 0x1e4
+#define QSERDES_V4_RX_AUX_DATA1 0x1e8
+#define QSERDES_V4_RX_AUX_DATA2 0x1ec
+#define QSERDES_V4_RX_AC_JTAG_OUTP 0x1f0
+#define QSERDES_V4_RX_AC_JTAG_OUTN 0x1f4
+#define QSERDES_V4_RX_RX_SIGDET 0x1f8
+#define QSERDES_V4_RX_ALOG_OBSV_BUS_STATUS_1 0x1fc
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4_20.h
new file mode 100644
index 000000000000..114570f3017f
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4_20.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V4_20_H_
+#define QCOM_PHY_QMP_QSERDES_TXRX_V4_20_H_
+
+/* Only for QMP V4_20 PHY - TX registers */
+#define QSERDES_V4_20_TX_LANE_MODE_1 0x88
+#define QSERDES_V4_20_TX_LANE_MODE_2 0x8c
+#define QSERDES_V4_20_TX_LANE_MODE_3 0x90
+#define QSERDES_V4_20_TX_VMODE_CTRL1 0xc4
+#define QSERDES_V4_20_TX_PI_QEC_CTRL 0xe0
+
+/* Only for QMP V4_20 PHY - RX registers */
+#define QSERDES_V4_20_RX_FO_GAIN_RATE2 0x008
+#define QSERDES_V4_20_RX_UCDR_PI_CONTROLS 0x058
+#define QSERDES_V4_20_RX_AUX_DATA_TCOARSE_TFINE 0x0ac
+#define QSERDES_V4_20_RX_DFE_3 0x110
+#define QSERDES_V4_20_RX_DFE_DAC_ENABLE1 0x134
+#define QSERDES_V4_20_RX_DFE_DAC_ENABLE2 0x138
+#define QSERDES_V4_20_RX_VGA_CAL_CNTRL2 0x150
+#define QSERDES_V4_20_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x178
+#define QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B1 0x1c8
+#define QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B2 0x1cc
+#define QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B3 0x1d0
+#define QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B4 0x1d4
+#define QSERDES_V4_20_RX_RX_MODE_RATE2_B0 0x1d8
+#define QSERDES_V4_20_RX_RX_MODE_RATE2_B1 0x1dc
+#define QSERDES_V4_20_RX_RX_MODE_RATE2_B2 0x1e0
+#define QSERDES_V4_20_RX_RX_MODE_RATE2_B3 0x1e4
+#define QSERDES_V4_20_RX_RX_MODE_RATE2_B4 0x1e8
+#define QSERDES_V4_20_RX_RX_MODE_RATE3_B0 0x1ec
+#define QSERDES_V4_20_RX_RX_MODE_RATE3_B1 0x1f0
+#define QSERDES_V4_20_RX_RX_MODE_RATE3_B2 0x1f4
+#define QSERDES_V4_20_RX_RX_MODE_RATE3_B3 0x1f8
+#define QSERDES_V4_20_RX_RX_MODE_RATE3_B4 0x1fc
+#define QSERDES_V4_20_RX_PHPRE_CTRL 0x200
+#define QSERDES_V4_20_RX_DFE_CTLE_POST_CAL_OFFSET 0x20c
+#define QSERDES_V4_20_RX_MARG_COARSE_CTRL2 0x23c
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5.h
new file mode 100644
index 000000000000..fe8f3e330d09
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5.h
@@ -0,0 +1,231 @@
+
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V5_H_
+#define QCOM_PHY_QMP_QSERDES_TXRX_V5_H_
+
+/* Only for QMP V5 PHY - TX registers */
+#define QSERDES_V5_TX_BIST_MODE_LANENO 0x000
+#define QSERDES_V5_TX_BIST_INVERT 0x004
+#define QSERDES_V5_TX_CLKBUF_ENABLE 0x008
+#define QSERDES_V5_TX_TX_EMP_POST1_LVL 0x00c
+#define QSERDES_V5_TX_TX_IDLE_LVL_LARGE_AMP 0x010
+#define QSERDES_V5_TX_TX_DRV_LVL 0x014
+#define QSERDES_V5_TX_TX_DRV_LVL_OFFSET 0x018
+#define QSERDES_V5_TX_RESET_TSYNC_EN 0x01c
+#define QSERDES_V5_TX_PRE_STALL_LDO_BOOST_EN 0x020
+#define QSERDES_V5_TX_TX_BAND 0x024
+#define QSERDES_V5_TX_SLEW_CNTL 0x028
+#define QSERDES_V5_TX_INTERFACE_SELECT 0x02c
+#define QSERDES_V5_TX_LPB_EN 0x030
+#define QSERDES_V5_TX_RES_CODE_LANE_TX 0x034
+#define QSERDES_V5_TX_RES_CODE_LANE_RX 0x038
+#define QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX 0x03c
+#define QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX 0x040
+#define QSERDES_V5_TX_PERL_LENGTH1 0x044
+#define QSERDES_V5_TX_PERL_LENGTH2 0x048
+#define QSERDES_V5_TX_SERDES_BYP_EN_OUT 0x04c
+#define QSERDES_V5_TX_DEBUG_BUS_SEL 0x050
+#define QSERDES_V5_TX_TRANSCEIVER_BIAS_EN 0x054
+#define QSERDES_V5_TX_HIGHZ_DRVR_EN 0x058
+#define QSERDES_V5_TX_TX_POL_INV 0x05c
+#define QSERDES_V5_TX_PARRATE_REC_DETECT_IDLE_EN 0x060
+#define QSERDES_V5_TX_BIST_PATTERN1 0x064
+#define QSERDES_V5_TX_BIST_PATTERN2 0x068
+#define QSERDES_V5_TX_BIST_PATTERN3 0x06c
+#define QSERDES_V5_TX_BIST_PATTERN4 0x070
+#define QSERDES_V5_TX_BIST_PATTERN5 0x074
+#define QSERDES_V5_TX_BIST_PATTERN6 0x078
+#define QSERDES_V5_TX_BIST_PATTERN7 0x07c
+#define QSERDES_V5_TX_BIST_PATTERN8 0x080
+#define QSERDES_V5_TX_LANE_MODE_1 0x084
+#define QSERDES_V5_TX_LANE_MODE_2 0x088
+#define QSERDES_V5_TX_LANE_MODE_3 0x08c
+#define QSERDES_V5_TX_LANE_MODE_4 0x090
+#define QSERDES_V5_TX_LANE_MODE_5 0x094
+#define QSERDES_V5_TX_ATB_SEL1 0x098
+#define QSERDES_V5_TX_ATB_SEL2 0x09c
+#define QSERDES_V5_TX_RCV_DETECT_LVL 0x0a0
+#define QSERDES_V5_TX_RCV_DETECT_LVL_2 0x0a4
+#define QSERDES_V5_TX_PRBS_SEED1 0x0a8
+#define QSERDES_V5_TX_PRBS_SEED2 0x0ac
+#define QSERDES_V5_TX_PRBS_SEED3 0x0b0
+#define QSERDES_V5_TX_PRBS_SEED4 0x0b4
+#define QSERDES_V5_TX_RESET_GEN 0x0b8
+#define QSERDES_V5_TX_RESET_GEN_MUXES 0x0bc
+#define QSERDES_V5_TX_TRAN_DRVR_EMP_EN 0x0c0
+#define QSERDES_V5_TX_TX_INTERFACE_MODE 0x0c4
+#define QSERDES_V5_TX_VMODE_CTRL1 0x0c8
+#define QSERDES_V5_TX_ALOG_OBSV_BUS_CTRL_1 0x0cc
+#define QSERDES_V5_TX_BIST_STATUS 0x0d0
+#define QSERDES_V5_TX_BIST_ERROR_COUNT1 0x0d4
+#define QSERDES_V5_TX_BIST_ERROR_COUNT2 0x0d8
+#define QSERDES_V5_TX_ALOG_OBSV_BUS_STATUS_1 0x0dc
+#define QSERDES_V5_TX_LANE_DIG_CONFIG 0x0e0
+#define QSERDES_V5_TX_PI_QEC_CTRL 0x0e4
+#define QSERDES_V5_TX_PRE_EMPH 0x0e8
+#define QSERDES_V5_TX_SW_RESET 0x0ec
+#define QSERDES_V5_TX_DCC_OFFSET 0x0f0
+#define QSERDES_V5_TX_DCC_CMUX_POSTCAL_OFFSET 0x0f4
+#define QSERDES_V5_TX_DCC_CMUX_CAL_CTRL1 0x0f8
+#define QSERDES_V5_TX_DCC_CMUX_CAL_CTRL2 0x0fc
+#define QSERDES_V5_TX_DIG_BKUP_CTRL 0x100
+#define QSERDES_V5_TX_DEBUG_BUS0 0x104
+#define QSERDES_V5_TX_DEBUG_BUS1 0x108
+#define QSERDES_V5_TX_DEBUG_BUS2 0x10c
+#define QSERDES_V5_TX_DEBUG_BUS3 0x110
+#define QSERDES_V5_TX_READ_EQCODE 0x114
+#define QSERDES_V5_TX_READ_OFFSETCODE 0x118
+#define QSERDES_V5_TX_IA_ERROR_COUNTER_LOW 0x11c
+#define QSERDES_V5_TX_IA_ERROR_COUNTER_HIGH 0x120
+#define QSERDES_V5_TX_VGA_READ_CODE 0x124
+#define QSERDES_V5_TX_VTH_READ_CODE 0x128
+#define QSERDES_V5_TX_DFE_TAP1_READ_CODE 0x12c
+#define QSERDES_V5_TX_DFE_TAP2_READ_CODE 0x130
+#define QSERDES_V5_TX_IDAC_STATUS_I 0x134
+#define QSERDES_V5_TX_IDAC_STATUS_IBAR 0x138
+#define QSERDES_V5_TX_IDAC_STATUS_Q 0x13c
+#define QSERDES_V5_TX_IDAC_STATUS_QBAR 0x140
+#define QSERDES_V5_TX_IDAC_STATUS_A 0x144
+#define QSERDES_V5_TX_IDAC_STATUS_ABAR 0x148
+#define QSERDES_V5_TX_IDAC_STATUS_SM_ON 0x14c
+#define QSERDES_V5_TX_IDAC_STATUS_CAL_DONE 0x150
+#define QSERDES_V5_TX_IDAC_STATUS_SIGNERROR 0x154
+#define QSERDES_V5_TX_DCC_CAL_STATUS 0x158
+#define QSERDES_V5_TX_DCC_READ_CODE_STATUS 0x15c
+
+/* Only for QMP V5 PHY - RX registers */
+#define QSERDES_V5_RX_UCDR_FO_GAIN_HALF 0x000
+#define QSERDES_V5_RX_UCDR_FO_GAIN_QUARTER 0x004
+#define QSERDES_V5_RX_UCDR_FO_GAIN 0x008
+#define QSERDES_V5_RX_UCDR_SO_GAIN_HALF 0x00c
+#define QSERDES_V5_RX_UCDR_SO_GAIN_QUARTER 0x010
+#define QSERDES_V5_RX_UCDR_SO_GAIN 0x014
+#define QSERDES_V5_RX_UCDR_SVS_FO_GAIN_HALF 0x018
+#define QSERDES_V5_RX_UCDR_SVS_FO_GAIN_QUARTER 0x01c
+#define QSERDES_V5_RX_UCDR_SVS_FO_GAIN 0x020
+#define QSERDES_V5_RX_UCDR_SVS_SO_GAIN_HALF 0x024
+#define QSERDES_V5_RX_UCDR_SVS_SO_GAIN_QUARTER 0x028
+#define QSERDES_V5_RX_UCDR_SVS_SO_GAIN 0x02c
+#define QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN 0x030
+#define QSERDES_V5_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034
+#define QSERDES_V5_RX_UCDR_FO_TO_SO_DELAY 0x038
+#define QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c
+#define QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040
+#define QSERDES_V5_RX_UCDR_PI_CONTROLS 0x044
+#define QSERDES_V5_RX_UCDR_PI_CTRL2 0x048
+#define QSERDES_V5_RX_UCDR_SB2_THRESH1 0x04c
+#define QSERDES_V5_RX_UCDR_SB2_THRESH2 0x050
+#define QSERDES_V5_RX_UCDR_SB2_GAIN1 0x054
+#define QSERDES_V5_RX_UCDR_SB2_GAIN2 0x058
+#define QSERDES_V5_RX_AUX_CONTROL 0x05c
+#define QSERDES_V5_RX_AUX_DATA_TCOARSE_TFINE 0x060
+#define QSERDES_V5_RX_RCLK_AUXDATA_SEL 0x064
+#define QSERDES_V5_RX_AC_JTAG_ENABLE 0x068
+#define QSERDES_V5_RX_AC_JTAG_INITP 0x06c
+#define QSERDES_V5_RX_AC_JTAG_INITN 0x070
+#define QSERDES_V5_RX_AC_JTAG_LVL 0x074
+#define QSERDES_V5_RX_AC_JTAG_MODE 0x078
+#define QSERDES_V5_RX_AC_JTAG_RESET 0x07c
+#define QSERDES_V5_RX_RX_TERM_BW 0x080
+#define QSERDES_V5_RX_RX_RCVR_IQ_EN 0x084
+#define QSERDES_V5_RX_RX_IDAC_I_DC_OFFSETS 0x088
+#define QSERDES_V5_RX_RX_IDAC_IBAR_DC_OFFSETS 0x08c
+#define QSERDES_V5_RX_RX_IDAC_Q_DC_OFFSETS 0x090
+#define QSERDES_V5_RX_RX_IDAC_QBAR_DC_OFFSETS 0x094
+#define QSERDES_V5_RX_RX_IDAC_A_DC_OFFSETS 0x098
+#define QSERDES_V5_RX_RX_IDAC_ABAR_DC_OFFSETS 0x09c
+#define QSERDES_V5_RX_RX_IDAC_EN 0x0a0
+#define QSERDES_V5_RX_RX_IDAC_ENABLES 0x0a4
+#define QSERDES_V5_RX_RX_IDAC_SIGN 0x0a8
+#define QSERDES_V5_RX_RX_HIGHZ_HIGHRATE 0x0ac
+#define QSERDES_V5_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x0b0
+#define QSERDES_V5_RX_DFE_1 0x0b4
+#define QSERDES_V5_RX_DFE_2 0x0b8
+#define QSERDES_V5_RX_DFE_3 0x0bc
+#define QSERDES_V5_RX_DFE_4 0x0c0
+#define QSERDES_V5_RX_TX_ADAPT_PRE_THRESH1 0x0c4
+#define QSERDES_V5_RX_TX_ADAPT_PRE_THRESH2 0x0c8
+#define QSERDES_V5_RX_TX_ADAPT_POST_THRESH 0x0cc
+#define QSERDES_V5_RX_TX_ADAPT_MAIN_THRESH 0x0d0
+#define QSERDES_V5_RX_VGA_CAL_CNTRL1 0x0d4
+#define QSERDES_V5_RX_VGA_CAL_CNTRL2 0x0d8
+#define QSERDES_V5_RX_GM_CAL 0x0dc
+#define QSERDES_V5_RX_RX_VGA_GAIN2_LSB 0x0e0
+#define QSERDES_V5_RX_RX_VGA_GAIN2_MSB 0x0e4
+#define QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL1 0x0e8
+#define QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2 0x0ec
+#define QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL3 0x0f0
+#define QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4 0x0f4
+#define QSERDES_V5_RX_RX_IDAC_TSETTLE_LOW 0x0f8
+#define QSERDES_V5_RX_RX_IDAC_TSETTLE_HIGH 0x0fc
+#define QSERDES_V5_RX_RX_IDAC_MEASURE_TIME 0x100
+#define QSERDES_V5_RX_RX_IDAC_ACCUMULATOR 0x104
+#define QSERDES_V5_RX_RX_EQ_OFFSET_LSB 0x108
+#define QSERDES_V5_RX_RX_EQ_OFFSET_MSB 0x10c
+#define QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x110
+#define QSERDES_V5_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x114
+#define QSERDES_V5_RX_SIGDET_ENABLES 0x118
+#define QSERDES_V5_RX_SIGDET_CNTRL 0x11c
+#define QSERDES_V5_RX_SIGDET_LVL 0x120
+#define QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL 0x124
+#define QSERDES_V5_RX_RX_BAND 0x128
+#define QSERDES_V5_RX_CDR_FREEZE_UP_DN 0x12c
+#define QSERDES_V5_RX_CDR_RESET_OVERRIDE 0x130
+#define QSERDES_V5_RX_RX_INTERFACE_MODE 0x134
+#define QSERDES_V5_RX_JITTER_GEN_MODE 0x138
+#define QSERDES_V5_RX_SJ_AMP1 0x13c
+#define QSERDES_V5_RX_SJ_AMP2 0x140
+#define QSERDES_V5_RX_SJ_PER1 0x144
+#define QSERDES_V5_RX_SJ_PER2 0x148
+#define QSERDES_V5_RX_PPM_OFFSET1 0x14c
+#define QSERDES_V5_RX_PPM_OFFSET2 0x150
+#define QSERDES_V5_RX_SIGN_PPM_PERIOD1 0x154
+#define QSERDES_V5_RX_SIGN_PPM_PERIOD2 0x158
+#define QSERDES_V5_RX_RX_MODE_00_LOW 0x15c
+#define QSERDES_V5_RX_RX_MODE_00_HIGH 0x160
+#define QSERDES_V5_RX_RX_MODE_00_HIGH2 0x164
+#define QSERDES_V5_RX_RX_MODE_00_HIGH3 0x168
+#define QSERDES_V5_RX_RX_MODE_00_HIGH4 0x16c
+#define QSERDES_V5_RX_RX_MODE_01_LOW 0x170
+#define QSERDES_V5_RX_RX_MODE_01_HIGH 0x174
+#define QSERDES_V5_RX_RX_MODE_01_HIGH2 0x178
+#define QSERDES_V5_RX_RX_MODE_01_HIGH3 0x17c
+#define QSERDES_V5_RX_RX_MODE_01_HIGH4 0x180
+#define QSERDES_V5_RX_RX_MODE_10_LOW 0x184
+#define QSERDES_V5_RX_RX_MODE_10_HIGH 0x188
+#define QSERDES_V5_RX_RX_MODE_10_HIGH2 0x18c
+#define QSERDES_V5_RX_RX_MODE_10_HIGH3 0x190
+#define QSERDES_V5_RX_RX_MODE_10_HIGH4 0x194
+#define QSERDES_V5_RX_PHPRE_CTRL 0x198
+#define QSERDES_V5_RX_PHPRE_INITVAL 0x19c
+#define QSERDES_V5_RX_DFE_EN_TIMER 0x1a0
+#define QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET 0x1a4
+#define QSERDES_V5_RX_DCC_CTRL1 0x1a8
+#define QSERDES_V5_RX_DCC_CTRL2 0x1ac
+#define QSERDES_V5_RX_VTH_CODE 0x1b0
+#define QSERDES_V5_RX_VTH_MIN_THRESH 0x1b4
+#define QSERDES_V5_RX_VTH_MAX_THRESH 0x1b8
+#define QSERDES_V5_RX_ALOG_OBSV_BUS_CTRL_1 0x1bc
+#define QSERDES_V5_RX_PI_CTRL1 0x1c0
+#define QSERDES_V5_RX_PI_CTRL2 0x1c4
+#define QSERDES_V5_RX_PI_QUAD 0x1c8
+#define QSERDES_V5_RX_IDATA1 0x1cc
+#define QSERDES_V5_RX_IDATA2 0x1d0
+#define QSERDES_V5_RX_AUX_DATA1 0x1d4
+#define QSERDES_V5_RX_AUX_DATA2 0x1d8
+#define QSERDES_V5_RX_AC_JTAG_OUTP 0x1dc
+#define QSERDES_V5_RX_AC_JTAG_OUTN 0x1e0
+#define QSERDES_V5_RX_RX_SIGDET 0x1e4
+#define QSERDES_V5_RX_ALOG_OBSV_BUS_STATUS_1 0x1e8
+
+/* Only for QMP V5 UFS ? */
+#define QSERDES_V5_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0x178
+#define QSERDES_V5_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0x17c
+#define QSERDES_V5_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0x180
+#define QSERDES_V5_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0x184
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_20.h
new file mode 100644
index 000000000000..86c01104799e
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_20.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V5_20_H_
+#define QCOM_PHY_QMP_QSERDES_TXRX_V5_20_H_
+
+/* 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_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
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_5nm.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_5nm.h
new file mode 100644
index 000000000000..a1c088bd5158
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_5nm.h
@@ -0,0 +1,333 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V5_5NM_H_
+#define QCOM_PHY_QMP_QSERDES_TXRX_V5_5NM_H_
+
+/* Only for QMP V5 5NM PHY - TX registers */
+#define QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_TX 0x30
+#define QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_RX 0x34
+#define QSERDES_V5_5NM_TX_LANE_MODE_1 0x78
+#define QSERDES_V5_5NM_TX_LANE_MODE_2 0x7c
+#define QSERDES_V5_5NM_TX_LANE_MODE_3 0x80
+#define QSERDES_V5_5NM_TX_BIST_MODE_LANENO 0x00
+#define QSERDES_V5_5NM_TX_BIST_INVERT 0x04
+#define QSERDES_V5_5NM_TX_CLKBUF_ENABLE 0x08
+#define QSERDES_V5_5NM_TX_TX_EMP_POST1_LVL 0x0c
+#define QSERDES_V5_5NM_TX_TX_IDLE_LVL_LARGE_AMP 0x10
+#define QSERDES_V5_5NM_TX_TX_DRV_LVL 0x14
+#define QSERDES_V5_5NM_TX_TX_DRV_LVL_OFFSET 0x18
+#define QSERDES_V5_5NM_TX_RESET_TSYNC_EN 0x1c
+#define QSERDES_V5_5NM_TX_PRE_STALL_LDO_BOOST_EN 0x20
+#define QSERDES_V5_5NM_TX_LPB_EN 0x24
+#define QSERDES_V5_5NM_TX_RES_CODE_LANE_TX 0x28
+#define QSERDES_V5_5NM_TX_RES_CODE_LANE_RX 0x2c
+#define QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_TX 0x30
+#define QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_RX 0x34
+#define QSERDES_V5_5NM_TX_PERL_LENGTH1 0x38
+#define QSERDES_V5_5NM_TX_PERL_LENGTH2 0x3c
+#define QSERDES_V5_5NM_TX_SERDES_BYP_EN_OUT 0x40
+#define QSERDES_V5_5NM_TX_DEBUG_BUS_SEL 0x44
+#define QSERDES_V5_5NM_TX_TRANSCEIVER_BIAS_EN 0x48
+#define QSERDES_V5_5NM_TX_HIGHZ_DRVR_EN 0x4c
+#define QSERDES_V5_5NM_TX_TX_POL_INV 0x50
+#define QSERDES_V5_5NM_TX_PARRATE_REC_DETECT_IDLE_EN 0x54
+#define QSERDES_V5_5NM_TX_BIST_PATTERN1 0x58
+#define QSERDES_V5_5NM_TX_BIST_PATTERN2 0x5c
+#define QSERDES_V5_5NM_TX_BIST_PATTERN3 0x60
+#define QSERDES_V5_5NM_TX_BIST_PATTERN4 0x64
+#define QSERDES_V5_5NM_TX_BIST_PATTERN5 0x68
+#define QSERDES_V5_5NM_TX_BIST_PATTERN6 0x6c
+#define QSERDES_V5_5NM_TX_BIST_PATTERN7 0x70
+#define QSERDES_V5_5NM_TX_BIST_PATTERN8 0x74
+#define QSERDES_V5_5NM_TX_LANE_MODE_1 0x78
+#define QSERDES_V5_5NM_TX_LANE_MODE_2 0x7c
+#define QSERDES_V5_5NM_TX_LANE_MODE_3 0x80
+#define QSERDES_V5_5NM_TX_ATB_SEL1 0x84
+#define QSERDES_V5_5NM_TX_ATB_SEL2 0x88
+#define QSERDES_V5_5NM_TX_RCV_DETECT_LVL 0x8c
+#define QSERDES_V5_5NM_TX_RCV_DETECT_LVL_2 0x90
+#define QSERDES_V5_5NM_TX_PRBS_SEED1 0x94
+#define QSERDES_V5_5NM_TX_PRBS_SEED2 0x98
+#define QSERDES_V5_5NM_TX_PRBS_SEED3 0x9c
+#define QSERDES_V5_5NM_TX_PRBS_SEED4 0xa0
+#define QSERDES_V5_5NM_TX_RESET_GEN 0xa4
+#define QSERDES_V5_5NM_TX_RESET_GEN_MUXES 0xa8
+#define QSERDES_V5_5NM_TX_TRAN_DRVR_EMP_EN 0xac
+#define QSERDES_V5_5NM_TX_VMODE_CTRL1 0xb0
+#define QSERDES_V5_5NM_TX_ALOG_OBSV_BUS_CTRL_1 0xb4
+#define QSERDES_V5_5NM_TX_BIST_STATUS 0xb8
+#define QSERDES_V5_5NM_TX_BIST_ERROR_COUNT1 0xbc
+#define QSERDES_V5_5NM_TX_BIST_ERROR_COUNT2 0xc0
+#define QSERDES_V5_5NM_TX_ALOG_OBSV_BUS_STATUS_1 0xc4
+#define QSERDES_V5_5NM_TX_LANE_DIG_CONFIG 0xc8
+#define QSERDES_V5_5NM_TX_PI_QEC_CTRL 0xcc
+#define QSERDES_V5_5NM_TX_PRE_EMPH 0xd0
+#define QSERDES_V5_5NM_TX_SW_RESET 0xd4
+#define QSERDES_V5_5NM_TX_TX_BAND 0xd8
+#define QSERDES_V5_5NM_TX_SLEW_CNTL0 0xdc
+#define QSERDES_V5_5NM_TX_SLEW_CNTL1 0xe0
+#define QSERDES_V5_5NM_TX_INTERFACE_SELECT 0xe4
+#define QSERDES_V5_5NM_TX_DIG_BKUP_CTRL 0xe8
+#define QSERDES_V5_5NM_TX_DEBUG_BUS0 0xec
+#define QSERDES_V5_5NM_TX_DEBUG_BUS1 0xf0
+#define QSERDES_V5_5NM_TX_DEBUG_BUS2 0xf4
+#define QSERDES_V5_5NM_TX_DEBUG_BUS3 0xf8
+#define QSERDES_V5_5NM_TX_TX_BKUP_RO_BUS 0xfc
+
+/* Only for QMP V5 5NM PHY - RX registers */
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_FO_GAIN_RATE0 0x000
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_FO_GAIN_RATE1 0x004
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_FO_GAIN_RATE2 0x008
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_FO_GAIN_RATE3 0x00c
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_SO_GAIN_RATE0 0x010
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_SO_GAIN_RATE1 0x014
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_SO_GAIN_RATE2 0x018
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_SO_GAIN_RATE3 0x01c
+#define QSERDES_V5_5NM_RX_UCDR_SO_SATURATION 0x020
+#define QSERDES_V5_5NM_RX_UCDR_FO_TO_SO_DELAY 0x024
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_LOW_RATE0 0x028
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE0 0x02c
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_LOW_RATE1 0x030
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE1 0x034
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_LOW_RATE2 0x038
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE2 0x03c
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_LOW_RATE3 0x040
+#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE3 0x044
+#define QSERDES_V5_5NM_RX_UCDR_PI_CTRL1 0x048
+#define QSERDES_V5_5NM_RX_UCDR_PI_CTRL2 0x04c
+#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH1_RATE0 0x050
+#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH1_RATE1 0x054
+#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH1_RATE2 0x058
+#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH1_RATE3 0x05c
+#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH2_RATE0 0x060
+#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH2_RATE1 0x064
+#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH2_RATE2 0x068
+#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH2_RATE3 0x06c
+#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN1_RATE0 0x070
+#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN1_RATE1 0x074
+#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN1_RATE2 0x078
+#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN1_RATE3 0x07c
+#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN2_RATE0 0x080
+#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN2_RATE1 0x084
+#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN2_RATE2 0x088
+#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN2_RATE3 0x08c
+#define QSERDES_V5_5NM_RX_RXCLK_DIV2_CTRL 0x090
+#define QSERDES_V5_5NM_RX_RX_BAND 0x094
+#define QSERDES_V5_5NM_RX_RX_TERM_BW 0x098
+#define QSERDES_V5_5NM_RX_UCDR_FO_GAIN_RATE0 0x09c
+#define QSERDES_V5_5NM_RX_UCDR_FO_GAIN_RATE1 0x0a0
+#define QSERDES_V5_5NM_RX_UCDR_FO_GAIN_RATE2 0x0a4
+#define QSERDES_V5_5NM_RX_UCDR_FO_GAIN_RATE3 0x0a8
+#define QSERDES_V5_5NM_RX_UCDR_SO_GAIN_RATE0 0x0ac
+#define QSERDES_V5_5NM_RX_UCDR_SO_GAIN_RATE1 0x0b0
+#define QSERDES_V5_5NM_RX_UCDR_SO_GAIN_RATE2 0x0b4
+#define QSERDES_V5_5NM_RX_UCDR_SO_GAIN_RATE3 0x0b8
+#define QSERDES_V5_5NM_RX_UCDR_PI_CONTROLS 0x0bc
+#define QSERDES_V5_5NM_RX_UCDR_PD_DATA_FILTER_ENABLES 0x0c0
+#define QSERDES_V5_5NM_RX_UCDR_SO_ACC_DEFAULT_VAL_RATE0 0x0c4
+#define QSERDES_V5_5NM_RX_UCDR_SO_ACC_DEFAULT_VAL_RATE1 0x0c8
+#define QSERDES_V5_5NM_RX_UCDR_SO_ACC_DEFAULT_VAL_RATE2 0x0cc
+#define QSERDES_V5_5NM_RX_UCDR_SO_ACC_DEFAULT_VAL_RATE3 0x0d0
+#define QSERDES_V5_5NM_RX_AUX_CONTROL 0x0d4
+#define QSERDES_V5_5NM_RX_AUXDATA_TB 0x0d8
+#define QSERDES_V5_5NM_RX_RCLK_AUXDATA_SEL 0x0dc
+#define QSERDES_V5_5NM_RX_EOM_CTRL 0x0e0
+#define QSERDES_V5_5NM_RX_AC_JTAG_ENABLE 0x0e4
+#define QSERDES_V5_5NM_RX_AC_JTAG_INITP 0x0e8
+#define QSERDES_V5_5NM_RX_AC_JTAG_INITN 0x0ec
+#define QSERDES_V5_5NM_RX_AC_JTAG_LVL 0x0f0
+#define QSERDES_V5_5NM_RX_AC_JTAG_MODE 0x0f4
+#define QSERDES_V5_5NM_RX_AC_JTAG_RESET 0x0f8
+#define QSERDES_V5_5NM_RX_RX_RCVR_IQ_EN 0x0fc
+#define QSERDES_V5_5NM_RX_RX_Q_EN_RATES 0x100
+#define QSERDES_V5_5NM_RX_RX_IDAC_I0_DC_OFFSETS 0x104
+#define QSERDES_V5_5NM_RX_RX_IDAC_I0BAR_DC_OFFSETS 0x108
+#define QSERDES_V5_5NM_RX_RX_IDAC_I1_DC_OFFSETS 0x10c
+#define QSERDES_V5_5NM_RX_RX_IDAC_I1BAR_DC_OFFSETS 0x110
+#define QSERDES_V5_5NM_RX_RX_IDAC_Q_DC_OFFSETS 0x114
+#define QSERDES_V5_5NM_RX_RX_IDAC_QBAR_DC_OFFSETS 0x118
+#define QSERDES_V5_5NM_RX_RX_IDAC_A_DC_OFFSETS 0x11c
+#define QSERDES_V5_5NM_RX_RX_IDAC_ABAR_DC_OFFSETS 0x120
+#define QSERDES_V5_5NM_RX_RX_IDAC_EN 0x124
+#define QSERDES_V5_5NM_RX_RX_IDAC_ENABLES 0x128
+#define QSERDES_V5_5NM_RX_RX_IDAC_SIGN 0x12c
+#define QSERDES_V5_5NM_RX_RX_IVCM_CAL_CODE_OVERRIDE 0x130
+#define QSERDES_V5_5NM_RX_RX_IVCM_CAL_CTRL1 0x134
+#define QSERDES_V5_5NM_RX_RX_IVCM_CAL_CTRL2 0x138
+#define QSERDES_V5_5NM_RX_RX_IVCM_POSTCAL_OFFSET 0x13c
+#define QSERDES_V5_5NM_RX_RX_SUMMER_CAL_SPD_MODE 0x140
+#define QSERDES_V5_5NM_RX_RX_HIGHZ_PARRATE 0x144
+#define QSERDES_V5_5NM_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x148
+#define QSERDES_V5_5NM_RX_DFE_1 0x14c
+#define QSERDES_V5_5NM_RX_DFE_2 0x150
+#define QSERDES_V5_5NM_RX_DFE_3 0x154
+#define QSERDES_V5_5NM_RX_DFE_4 0x158
+#define QSERDES_V5_5NM_RX_DFE_TAP3_CTRL 0x15c
+#define QSERDES_V5_5NM_RX_DFE_TAP3_MANVAL_KTAP 0x160
+#define QSERDES_V5_5NM_RX_DFE_TAP4_CTRL 0x164
+#define QSERDES_V5_5NM_RX_DFE_TAP4_MANVAL_KTAP 0x168
+#define QSERDES_V5_5NM_RX_DFE_TAP5_CTRL 0x16c
+#define QSERDES_V5_5NM_RX_DFE_TAP5_MANVAL_KTAP 0x170
+#define QSERDES_V5_5NM_RX_TX_ADPT_CTRL 0x174
+#define QSERDES_V5_5NM_RX_DFE_DAC_ENABLE1 0x178
+#define QSERDES_V5_5NM_RX_DFE_DAC_ENABLE2 0x17c
+#define QSERDES_V5_5NM_RX_TX_ADAPT_PRE_THRESH1 0x180
+#define QSERDES_V5_5NM_RX_TX_ADAPT_PRE_THRESH2 0x184
+#define QSERDES_V5_5NM_RX_TX_ADAPT_POST_THRESH1 0x188
+#define QSERDES_V5_5NM_RX_TX_ADAPT_POST_THRESH2 0x18c
+#define QSERDES_V5_5NM_RX_TX_ADAPT_MAIN_THRESH1 0x190
+#define QSERDES_V5_5NM_RX_TX_ADAPT_MAIN_THRESH2 0x194
+#define QSERDES_V5_5NM_RX_VGA_CAL_CNTRL1 0x198
+#define QSERDES_V5_5NM_RX_VGA_CAL_CNTRL2 0x19c
+#define QSERDES_V5_5NM_RX_VGA_CAL_MAN_VAL 0x1a0
+#define QSERDES_V5_5NM_RX_VTHRESH_CAL_CNTRL1 0x1a4
+#define QSERDES_V5_5NM_RX_VTHRESH_CAL_CNTRL2 0x1a8
+#define QSERDES_V5_5NM_RX_VTHRESH_CAL_MAN_VAL_RATE0 0x1ac
+#define QSERDES_V5_5NM_RX_VTHRESH_CAL_MAN_VAL_RATE1 0x1b0
+#define QSERDES_V5_5NM_RX_VTHRESH_CAL_MAN_VAL_RATE2 0x1b4
+#define QSERDES_V5_5NM_RX_VTHRESH_CAL_MAN_VAL_RATE3 0x1b8
+#define QSERDES_V5_5NM_RX_GM_CAL 0x1bc
+#define QSERDES_V5_5NM_RX_RX_VGA_GAIN2_BLK1 0x1c0
+#define QSERDES_V5_5NM_RX_RX_VGA_GAIN2_BLK2 0x1c4
+#define QSERDES_V5_5NM_RX_RX_EQU_ADAPTOR_CNTRL2 0x1c8
+#define QSERDES_V5_5NM_RX_RX_EQU_ADAPTOR_CNTRL3 0x1cc
+#define QSERDES_V5_5NM_RX_RX_EQU_ADAPTOR_CNTRL4 0x1d0
+#define QSERDES_V5_5NM_RX_RX_IDAC_TSETTLE_LOW 0x1d4
+#define QSERDES_V5_5NM_RX_RX_EQ_OFFSET_LSB 0x1d8
+#define QSERDES_V5_5NM_RX_RX_EQ_OFFSET_MSB 0x1dc
+#define QSERDES_V5_5NM_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x1e0
+#define QSERDES_V5_5NM_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x1e4
+#define QSERDES_V5_5NM_RX_SIGDET_ENABLES 0x1e8
+#define QSERDES_V5_5NM_RX_SIGDET_CNTRL 0x1ec
+#define QSERDES_V5_5NM_RX_SIGDET_LVL 0x1f0
+#define QSERDES_V5_5NM_RX_SIGDET_DEGLITCH_CNTRL 0x1f4
+#define QSERDES_V5_5NM_RX_CDR_FREEZE_UP_DN 0x1f8
+#define QSERDES_V5_5NM_RX_CDR_RESET_OVERRIDE 0x1fc
+#define QSERDES_V5_5NM_RX_RX_INTERFACE_MODE 0x200
+#define QSERDES_V5_5NM_RX_JITTER_GEN_MODE 0x204
+#define QSERDES_V5_5NM_RX_SJ_AMP1 0x208
+#define QSERDES_V5_5NM_RX_SJ_AMP2 0x20c
+#define QSERDES_V5_5NM_RX_SJ_PER1 0x210
+#define QSERDES_V5_5NM_RX_SJ_PER2 0x214
+#define QSERDES_V5_5NM_RX_PPM_OFFSET1 0x218
+#define QSERDES_V5_5NM_RX_PPM_OFFSET2 0x21c
+#define QSERDES_V5_5NM_RX_SIGN_PPM_PERIOD1 0x220
+#define QSERDES_V5_5NM_RX_SIGN_PPM_PERIOD2 0x224
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B0 0x228
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B1 0x22c
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B2 0x230
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B3 0x234
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B4 0x238
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B5 0x23c
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B6 0x240
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B7 0x244
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B0 0x248
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B1 0x24c
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B2 0x250
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B3 0x254
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B4 0x258
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B5 0x25c
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B6 0x260
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B7 0x264
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B0 0x268
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B1 0x26c
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B2 0x270
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B3 0x274
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B4 0x278
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B5 0x27c
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B6 0x280
+#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B7 0x284
+#define QSERDES_V5_5NM_RX_PHPRE_CTRL 0x288
+#define QSERDES_V5_5NM_RX_PHPRE_INITVAL 0x28c
+#define QSERDES_V5_5NM_RX_DFE_EN_TIMER 0x290
+#define QSERDES_V5_5NM_RX_DFE_CTLE_POST_CAL_OFFSET 0x294
+#define QSERDES_V5_5NM_RX_DCC_CTRL1 0x298
+#define QSERDES_V5_5NM_RX_DCC_CTRL2 0x29c
+#define QSERDES_V5_5NM_RX_DCC_OFFSET 0x2a0
+#define QSERDES_V5_5NM_RX_DCC_CMUX_POSTCAL_OFFSET 0x2a4
+#define QSERDES_V5_5NM_RX_DCC_CMUX_CAL_CTRL1 0x2a8
+#define QSERDES_V5_5NM_RX_DCC_CMUX_CAL_CTRL2 0x2ac
+#define QSERDES_V5_5NM_RX_ALOG_OBSV_BUS_CTRL_1 0x2b0
+#define QSERDES_V5_5NM_RX_RX_MARG_CTRL1 0x2b4
+#define QSERDES_V5_5NM_RX_RX_MARG_CTRL2 0x2b8
+#define QSERDES_V5_5NM_RX_RX_MARG_CTRL3 0x2bc
+#define QSERDES_V5_5NM_RX_RX_MARG_CTRL_4 0x2c0
+#define QSERDES_V5_5NM_RX_RX_MARG_CFG_RATE_0_1 0x2c4
+#define QSERDES_V5_5NM_RX_RX_MARG_CFG_RATE_2_3 0x2c8
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_CTRL1 0x2cc
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_CTRL2 0x2d0
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH1_RATE210 0x2d4
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH1_RATE3 0x2d8
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH2_RATE210 0x2dc
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH2_RATE3 0x2e0
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH3_RATE210 0x2e4
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH3_RATE3 0x2e8
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH4_RATE210 0x2ec
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH4_RATE3 0x2f0
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH5_RATE210 0x2f4
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH5_RATE3 0x2f8
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH6_RATE210 0x2fc
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH6_RATE3 0x300
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH7_RATE210 0x304
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH7_RATE3 0x308
+#define QSERDES_V5_5NM_RX_Q_PI_INTRINSIC_BIAS_RATE10 0x30c
+#define QSERDES_V5_5NM_RX_Q_PI_INTRINSIC_BIAS_RATE32 0x310
+#define QSERDES_V5_5NM_RX_RX_MARG_VERTICAL_CTRL 0x314
+#define QSERDES_V5_5NM_RX_RX_MARG_VERTICAL_CODE 0x318
+#define QSERDES_V5_5NM_RX_RES_CODE_THRESH_HIGH_AND_BYP 0x31c
+#define QSERDES_V5_5NM_RX_RES_CODE_THRESH_LOW 0x320
+#define QSERDES_V5_5NM_RX_RX_BKUP_CTRL1 0x324
+#define QSERDES_V5_5NM_RX_RX_BKUP_CTRL2 0x328
+#define QSERDES_V5_5NM_RX_RX_BKUP_CTRL3 0x32c
+#define QSERDES_V5_5NM_RX_PI_CTRL1 0x330
+#define QSERDES_V5_5NM_RX_PI_CTRL2 0x334
+#define QSERDES_V5_5NM_RX_PI_QUAD 0x338
+#define QSERDES_V5_5NM_RX_QPI_CTRL1 0x33c
+#define QSERDES_V5_5NM_RX_QPI_CTRL2 0x340
+#define QSERDES_V5_5NM_RX_QPI_QUAD 0x344
+#define QSERDES_V5_5NM_RX_IDATA1 0x348
+#define QSERDES_V5_5NM_RX_IDATA2 0x34c
+#define QSERDES_V5_5NM_RX_IDATA3 0x350
+#define QSERDES_V5_5NM_RX_AC_JTAG_OUTP 0x354
+#define QSERDES_V5_5NM_RX_AC_JTAG_OUTN 0x358
+#define QSERDES_V5_5NM_RX_RX_SIGDET 0x35c
+#define QSERDES_V5_5NM_RX_ALOG_OBSV_BUS_STATUS_1 0x360
+#define QSERDES_V5_5NM_RX_READ_EQCODE 0x364
+#define QSERDES_V5_5NM_RX_READ_OFFSETCODE 0x368
+#define QSERDES_V5_5NM_RX_IA_ERROR_COUNTER_LOW 0x36c
+#define QSERDES_V5_5NM_RX_IA_ERROR_COUNTER_HIGH 0x370
+#define QSERDES_V5_5NM_RX_VGA_READ_CODE 0x374
+#define QSERDES_V5_5NM_RX_VTHRESH_READ_CODE 0x378
+#define QSERDES_V5_5NM_RX_DFE_TAP1_READ_CODE 0x37c
+#define QSERDES_V5_5NM_RX_DFE_TAP2_READ_CODE 0x380
+#define QSERDES_V5_5NM_RX_DFE_TAP3_READ_CODE 0x384
+#define QSERDES_V5_5NM_RX_DFE_TAP4_READ_CODE 0x388
+#define QSERDES_V5_5NM_RX_DFE_TAP5_READ_CODE 0x38c
+#define QSERDES_V5_5NM_RX_IDAC_STATUS_I0 0x390
+#define QSERDES_V5_5NM_RX_IDAC_STATUS_I0BAR 0x394
+#define QSERDES_V5_5NM_RX_IDAC_STATUS_I1 0x398
+#define QSERDES_V5_5NM_RX_IDAC_STATUS_I1BAR 0x39c
+#define QSERDES_V5_5NM_RX_IDAC_STATUS_Q 0x3a0
+#define QSERDES_V5_5NM_RX_IDAC_STATUS_QBAR 0x3a4
+#define QSERDES_V5_5NM_RX_IDAC_STATUS_A 0x3a8
+#define QSERDES_V5_5NM_RX_IDAC_STATUS_ABAR 0x3ac
+#define QSERDES_V5_5NM_RX_IDAC_STATUS_SM_ON 0x3b0
+#define QSERDES_V5_5NM_RX_IDAC_STATUS_SIGNERROR 0x3b4
+#define QSERDES_V5_5NM_RX_IVCM_CAL_STATUS 0x3b8
+#define QSERDES_V5_5NM_RX_IVCM_CAL_DEBUG_STATUS 0x3bc
+#define QSERDES_V5_5NM_RX_DCC_CAL_STATUS 0x3c0
+#define QSERDES_V5_5NM_RX_DCC_READ_CODE_STATUS 0x3c4
+#define QSERDES_V5_5NM_RX_RX_MARG_DEBUG1_STATUS 0x3c8
+#define QSERDES_V5_5NM_RX_RX_MARG_DEBUG2_STATUS 0x3cc
+#define QSERDES_V5_5NM_RX_RX_MARG_READ_CODE_STATUS 0x3d0
+#define QSERDES_V5_5NM_RX_EOM_ERR_CNT_LSB_STATUS 0x3d4
+#define QSERDES_V5_5NM_RX_EOM_ERR_CNT_MSB_STATUS 0x3d8
+#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_TUNE_STATUS 0x3dc
+#define QSERDES_V5_5NM_RX_RX_BKUP_READ_BUS1_STATUS 0x3e0
+#define QSERDES_V5_5NM_RX_RX_BKUP_READ_BUS2_STATUS 0x3e4
+#define QSERDES_V5_5NM_RX_RX_BKUP_READ_BUS3_STATUS 0x3e8
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx.h
new file mode 100644
index 000000000000..d20694513eb4
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx.h
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_TXRX_H_
+#define QCOM_PHY_QMP_QSERDES_TXRX_H_
+
+/* Only for QMP V2 PHY - TX registers */
+#define QSERDES_TX_BIST_MODE_LANENO 0x000
+#define QSERDES_TX_BIST_INVERT 0x004
+#define QSERDES_TX_CLKBUF_ENABLE 0x008
+#define QSERDES_TX_CMN_CONTROL_ONE 0x00c
+#define QSERDES_TX_CMN_CONTROL_TWO 0x010
+#define QSERDES_TX_CMN_CONTROL_THREE 0x014
+#define QSERDES_TX_TX_EMP_POST1_LVL 0x018
+#define QSERDES_TX_TX_POST2_EMPH 0x01c
+#define QSERDES_TX_TX_BOOST_LVL_UP_DN 0x020
+#define QSERDES_TX_HP_PD_ENABLES 0x024
+#define QSERDES_TX_TX_IDLE_LVL_LARGE_AMP 0x028
+#define QSERDES_TX_TX_DRV_LVL 0x02c
+#define QSERDES_TX_TX_DRV_LVL_OFFSET 0x030
+#define QSERDES_TX_RESET_TSYNC_EN 0x034
+#define QSERDES_TX_PRE_STALL_LDO_BOOST_EN 0x038
+#define QSERDES_TX_TX_BAND 0x03c
+#define QSERDES_TX_SLEW_CNTL 0x040
+#define QSERDES_TX_INTERFACE_SELECT 0x044
+#define QSERDES_TX_LPB_EN 0x048
+#define QSERDES_TX_RES_CODE_LANE_TX 0x04c
+#define QSERDES_TX_RES_CODE_LANE_RX 0x050
+#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054
+#define QSERDES_TX_PERL_LENGTH1 0x058
+#define QSERDES_TX_PERL_LENGTH2 0x05c
+#define QSERDES_TX_SERDES_BYP_EN_OUT 0x060
+#define QSERDES_TX_DEBUG_BUS_SEL 0x064
+#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068
+#define QSERDES_TX_TX_POL_INV 0x06c
+#define QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN 0x070
+#define QSERDES_TX_BIST_PATTERN1 0x074
+#define QSERDES_TX_BIST_PATTERN2 0x078
+#define QSERDES_TX_BIST_PATTERN3 0x07c
+#define QSERDES_TX_BIST_PATTERN4 0x080
+#define QSERDES_TX_BIST_PATTERN5 0x084
+#define QSERDES_TX_BIST_PATTERN6 0x088
+#define QSERDES_TX_BIST_PATTERN7 0x08c
+#define QSERDES_TX_BIST_PATTERN8 0x090
+#define QSERDES_TX_LANE_MODE 0x094
+#define QSERDES_TX_IDAC_CAL_LANE_MODE 0x098
+#define QSERDES_TX_IDAC_CAL_LANE_MODE_CONFIGURATION 0x09c
+#define QSERDES_TX_ATB_SEL1 0x0a0
+#define QSERDES_TX_ATB_SEL2 0x0a4
+#define QSERDES_TX_RCV_DETECT_LVL 0x0a8
+#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac
+#define QSERDES_TX_PRBS_SEED1 0x0b0
+#define QSERDES_TX_PRBS_SEED2 0x0b4
+#define QSERDES_TX_PRBS_SEED3 0x0b8
+#define QSERDES_TX_PRBS_SEED4 0x0bc
+#define QSERDES_TX_RESET_GEN 0x0c0
+#define QSERDES_TX_RESET_GEN_MUXES 0x0c4
+#define QSERDES_TX_TRAN_DRVR_EMP_EN 0x0c8
+#define QSERDES_TX_TX_INTERFACE_MODE 0x0cc
+#define QSERDES_TX_PWM_CTRL 0x0d0
+#define QSERDES_TX_PWM_ENCODED_OR_DATA 0x0d4
+#define QSERDES_TX_PWM_GEAR_1_DIVIDER_BAND2 0x0d8
+#define QSERDES_TX_PWM_GEAR_2_DIVIDER_BAND2 0x0dc
+#define QSERDES_TX_PWM_GEAR_3_DIVIDER_BAND2 0x0e0
+#define QSERDES_TX_PWM_GEAR_4_DIVIDER_BAND2 0x0e4
+#define QSERDES_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0x0e8
+#define QSERDES_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0x0ec
+#define QSERDES_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0x0f0
+#define QSERDES_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0x0f4
+#define QSERDES_TX_VMODE_CTRL1 0x0f8
+#define QSERDES_TX_VMODE_CTRL2 0x0fc
+#define QSERDES_TX_TX_ALOG_INTF_OBSV_CNTL 0x100
+#define QSERDES_TX_BIST_STATUS 0x104
+#define QSERDES_TX_BIST_ERROR_COUNT1 0x108
+#define QSERDES_TX_BIST_ERROR_COUNT2 0x10c
+#define QSERDES_TX_TX_ALOG_INTF_OBSV 0x110
+
+/* Only for QMP V2 PHY - RX registers */
+#define QSERDES_RX_UCDR_FO_GAIN_HALF 0x000
+#define QSERDES_RX_UCDR_FO_GAIN_QUARTER 0x004
+#define QSERDES_RX_UCDR_FO_GAIN_EIGHTH 0x008
+#define QSERDES_RX_UCDR_FO_GAIN 0x00c
+#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010
+#define QSERDES_RX_UCDR_SO_GAIN_QUARTER 0x014
+#define QSERDES_RX_UCDR_SO_GAIN_EIGHTH 0x018
+#define QSERDES_RX_UCDR_SO_GAIN 0x01c
+#define QSERDES_RX_UCDR_SVS_FO_GAIN_HALF 0x020
+#define QSERDES_RX_UCDR_SVS_FO_GAIN_QUARTER 0x024
+#define QSERDES_RX_UCDR_SVS_FO_GAIN_EIGHTH 0x028
+#define QSERDES_RX_UCDR_SVS_FO_GAIN 0x02c
+#define QSERDES_RX_UCDR_SVS_SO_GAIN_HALF 0x030
+#define QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER 0x034
+#define QSERDES_RX_UCDR_SVS_SO_GAIN_EIGHTH 0x038
+#define QSERDES_RX_UCDR_SVS_SO_GAIN 0x03c
+#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040
+#define QSERDES_RX_UCDR_FD_GAIN 0x044
+#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048
+#define QSERDES_RX_UCDR_FO_TO_SO_DELAY 0x04c
+#define QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW 0x050
+#define QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH 0x054
+#define QSERDES_RX_UCDR_MODULATE 0x058
+#define QSERDES_RX_UCDR_PI_CONTROLS 0x05c
+#define QSERDES_RX_RBIST_CONTROL 0x060
+#define QSERDES_RX_AUX_CONTROL 0x064
+#define QSERDES_RX_AUX_DATA_TCOARSE 0x068
+#define QSERDES_RX_AUX_DATA_TFINE_LSB 0x06c
+#define QSERDES_RX_AUX_DATA_TFINE_MSB 0x070
+#define QSERDES_RX_RCLK_AUXDATA_SEL 0x074
+#define QSERDES_RX_AC_JTAG_ENABLE 0x078
+#define QSERDES_RX_AC_JTAG_INITP 0x07c
+#define QSERDES_RX_AC_JTAG_INITN 0x080
+#define QSERDES_RX_AC_JTAG_LVL 0x084
+#define QSERDES_RX_AC_JTAG_MODE 0x088
+#define QSERDES_RX_AC_JTAG_RESET 0x08c
+#define QSERDES_RX_RX_TERM_BW 0x090
+#define QSERDES_RX_RX_RCVR_IQ_EN 0x094
+#define QSERDES_RX_RX_IDAC_I_DC_OFFSETS 0x098
+#define QSERDES_RX_RX_IDAC_IBAR_DC_OFFSETS 0x09c
+#define QSERDES_RX_RX_IDAC_Q_DC_OFFSETS 0x0a0
+#define QSERDES_RX_RX_IDAC_QBAR_DC_OFFSETS 0x0a4
+#define QSERDES_RX_RX_IDAC_A_DC_OFFSETS 0x0a8
+#define QSERDES_RX_RX_IDAC_ABAR_DC_OFFSETS 0x0ac
+#define QSERDES_RX_RX_IDAC_EN 0x0b0
+#define QSERDES_RX_RX_IDAC_ENABLES 0x0b4
+#define QSERDES_RX_RX_IDAC_SIGN 0x0b8
+#define QSERDES_RX_RX_HIGHZ_HIGHRATE 0x0bc
+#define QSERDES_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x0c0
+#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4
+#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8
+#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc
+#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1 0x0d4
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0
+#define QSERDES_RX_RX_IDAC_CAL_CONFIGURATION 0x0e4
+#define QSERDES_RX_RX_IDAC_TSETTLE_LOW 0x0e8
+#define QSERDES_RX_RX_IDAC_TSETTLE_HIGH 0x0ec
+#define QSERDES_RX_RX_IDAC_ENDSAMP_LOW 0x0f0
+#define QSERDES_RX_RX_IDAC_ENDSAMP_HIGH 0x0f4
+#define QSERDES_RX_RX_IDAC_MIDPOINT_LOW 0x0f8
+#define QSERDES_RX_RX_IDAC_MIDPOINT_HIGH 0x0fc
+#define QSERDES_RX_RX_EQ_OFFSET_LSB 0x100
+#define QSERDES_RX_RX_EQ_OFFSET_MSB 0x104
+#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108
+#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c
+#define QSERDES_RX_SIGDET_ENABLES 0x110
+#define QSERDES_RX_SIGDET_CNTRL 0x114
+#define QSERDES_RX_SIGDET_LVL 0x118
+#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c
+#define QSERDES_RX_RX_BAND 0x120
+#define QSERDES_RX_CDR_FREEZE_UP_DN 0x124
+#define QSERDES_RX_CDR_RESET_OVERRIDE 0x128
+#define QSERDES_RX_RX_INTERFACE_MODE 0x12c
+#define QSERDES_RX_JITTER_GEN_MODE 0x130
+#define QSERDES_RX_BUJ_AMP 0x134
+#define QSERDES_RX_SJ_AMP1 0x138
+#define QSERDES_RX_SJ_AMP2 0x13c
+#define QSERDES_RX_SJ_PER1 0x140
+#define QSERDES_RX_SJ_PER2 0x144
+#define QSERDES_RX_BUJ_STEP_FREQ1 0x148
+#define QSERDES_RX_BUJ_STEP_FREQ2 0x14c
+#define QSERDES_RX_PPM_OFFSET1 0x150
+#define QSERDES_RX_PPM_OFFSET2 0x154
+#define QSERDES_RX_SIGN_PPM_PERIOD1 0x158
+#define QSERDES_RX_SIGN_PPM_PERIOD2 0x15c
+#define QSERDES_RX_SSC_CTRL 0x160
+#define QSERDES_RX_SSC_COUNT1 0x164
+#define QSERDES_RX_SSC_COUNT2 0x168
+#define QSERDES_RX_RX_ALOG_INTF_OBSV_CNTL 0x16c
+#define QSERDES_RX_RX_PWM_ENABLE_AND_DATA 0x170
+#define QSERDES_RX_RX_PWM_GEAR1_TIMEOUT_COUNT 0x174
+#define QSERDES_RX_RX_PWM_GEAR2_TIMEOUT_COUNT 0x178
+#define QSERDES_RX_RX_PWM_GEAR3_TIMEOUT_COUNT 0x17c
+#define QSERDES_RX_RX_PWM_GEAR4_TIMEOUT_COUNT 0x180
+#define QSERDES_RX_PI_CTRL1 0x184
+#define QSERDES_RX_PI_CTRL2 0x188
+#define QSERDES_RX_PI_QUAD 0x18c
+#define QSERDES_RX_IDATA1 0x190
+#define QSERDES_RX_IDATA2 0x194
+#define QSERDES_RX_AUX_DATA1 0x198
+#define QSERDES_RX_AUX_DATA2 0x19c
+#define QSERDES_RX_AC_JTAG_OUTP 0x1a0
+#define QSERDES_RX_AC_JTAG_OUTN 0x1a4
+#define QSERDES_RX_RX_SIGDET 0x1a8
+#define QSERDES_RX_RX_VDCOFF 0x1ac
+#define QSERDES_RX_IDAC_CAL_ON 0x1b0
+#define QSERDES_RX_IDAC_STATUS_I 0x1b4
+#define QSERDES_RX_IDAC_STATUS_IBAR 0x1b8
+#define QSERDES_RX_IDAC_STATUS_Q 0x1bc
+#define QSERDES_RX_IDAC_STATUS_QBAR 0x1c0
+#define QSERDES_RX_IDAC_STATUS_A 0x1c4
+#define QSERDES_RX_IDAC_STATUS_ABAR 0x1c8
+#define QSERDES_RX_CALST_STATUS_I 0x1cc
+#define QSERDES_RX_CALST_STATUS_Q 0x1d0
+#define QSERDES_RX_CALST_STATUS_A 0x1d4
+#define QSERDES_RX_RX_ALOG_INTF_OBSV 0x1d8
+#define QSERDES_RX_READ_EQCODE 0x1dc
+#define QSERDES_RX_READ_OFFSETCODE 0x1e0
+#define QSERDES_RX_IA_ERROR_COUNTER_LOW 0x1e4
+#define QSERDES_RX_IA_ERROR_COUNTER_HIGH 0x1e8
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
new file mode 100644
index 000000000000..c08d34ad1313
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
@@ -0,0 +1,1265 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#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"
+
+/* QPHY_SW_RESET bit */
+#define SW_RESET BIT(0)
+/* QPHY_POWER_DOWN_CONTROL */
+#define SW_PWRDN BIT(0)
+/* QPHY_START_CONTROL bits */
+#define SERDES_START BIT(0)
+#define PCS_START BIT(1)
+/* QPHY_PCS_STATUS bit */
+#define PHYSTATUS BIT(6)
+/* QPHY_PCS_READY_STATUS bit */
+#define PCS_READY BIT(0)
+
+#define PHY_INIT_COMPLETE_TIMEOUT 10000
+
+struct qmp_phy_init_tbl {
+ unsigned int offset;
+ unsigned int val;
+ /*
+ * register part of layout ?
+ * if yes, then offset gives index in the reg-layout
+ */
+ bool in_layout;
+ /*
+ * mask of lanes for which this register is written
+ * for cases when second lane needs different values
+ */
+ u8 lane_mask;
+};
+
+#define QMP_PHY_INIT_CFG(o, v) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .lane_mask = 0xff, \
+ }
+
+#define QMP_PHY_INIT_CFG_L(o, v) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .in_layout = true, \
+ .lane_mask = 0xff, \
+ }
+
+#define QMP_PHY_INIT_CFG_LANE(o, v, l) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .lane_mask = l, \
+ }
+
+/* set of registers with offsets different per-PHY */
+enum qphy_reg_layout {
+ /* PCS registers */
+ QPHY_SW_RESET,
+ QPHY_START_CTRL,
+ QPHY_PCS_READY_STATUS,
+ QPHY_PCS_POWER_DOWN_CONTROL,
+ /* Keep last to ensure regs_layout arrays are properly initialized */
+ QPHY_LAYOUT_SIZE
+};
+
+static const unsigned int msm8996_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_START_CTRL] = 0x00,
+ [QPHY_PCS_READY_STATUS] = 0x168,
+};
+
+static const unsigned int sdm845_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_START_CTRL] = 0x00,
+ [QPHY_PCS_READY_STATUS] = 0x160,
+};
+
+static const unsigned int sm6115_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_START_CTRL] = 0x00,
+ [QPHY_PCS_READY_STATUS] = 0x168,
+};
+
+static const unsigned int sm8150_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_START_CTRL] = QPHY_V4_PCS_UFS_PHY_START,
+ [QPHY_PCS_READY_STATUS] = QPHY_V4_PCS_UFS_READY_STATUS,
+ [QPHY_SW_RESET] = QPHY_V4_PCS_UFS_SW_RESET,
+};
+
+static const struct qmp_phy_init_tbl msm8996_ufs_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0xd7),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE0, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE1, 0x98),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE1, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE1, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE1, 0x32),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE1, 0x00),
+};
+
+static const struct qmp_phy_init_tbl msm8996_ufs_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
+ QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x02),
+};
+
+static const struct qmp_phy_init_tbl msm8996_ufs_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_INTERFACE_MODE, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_TERM_BW, 0x5b),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E),
+};
+
+static const struct qmp_phy_init_tbl sm6115_ufsphy_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE0, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE1, 0x98),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE1, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE1, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE1, 0x32),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_INITVAL1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00),
+
+ /* Rate B */
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x44),
+};
+
+static const struct qmp_phy_init_tbl sm6115_ufsphy_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
+ QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
+};
+
+static const struct qmp_phy_init_tbl sm6115_ufsphy_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x0F),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_INTERFACE_MODE, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_TERM_BW, 0x5B),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x3F),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0D),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SVS_SO_GAIN_HALF, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SVS_SO_GAIN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5B),
+};
+
+static const struct qmp_phy_init_tbl sm6115_ufsphy_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_PWM_GEAR_BAND, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_SIGDET_CTRL2, 0x6d),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_TX_LARGE_AMP_DRV_LVL, 0x0f),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_TX_SMALL_AMP_DRV_LVL, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_MIN_STALL_NOCONFIG_TIME_CAP, 0x28),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_SYM_RESYNC_CTRL, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_TX_LARGE_AMP_POST_EMP_LVL, 0x12),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_TX_SMALL_AMP_POST_EMP_LVL, 0x0f),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_MIN_HIBERN8_TIME, 0x9a), /* 8 us */
+};
+
+static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0xd5),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_CTRL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xda),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE1, 0x98),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE1, 0xc1),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE1, 0x32),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE1, 0x0f),
+
+ /* Rate B */
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x44),
+};
+
+static const struct qmp_phy_init_tbl sdm845_ufsphy_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x07),
+};
+
+static const struct qmp_phy_init_tbl sdm845_ufsphy_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_LVL, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_TERM_BW, 0x5b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x81),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59),
+};
+
+static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_RX_SIGDET_CTRL2, 0x6e),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_RX_SYM_RESYNC_CTRL, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_TX_MID_TERM_CTRL1, 0x43),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_RX_SIGDET_CTRL1, 0x0f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_RX_MIN_HIBERN8_TIME, 0x9a),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
+};
+
+static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0xd9),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_INITVAL2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xac),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x98),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x32),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xdd),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23),
+
+ /* Rate B */
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x06),
+};
+
+static const struct qmp_phy_init_tbl sm8150_ufsphy_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0c),
+};
+
+static const struct qmp_phy_init_tbl sm8150_ufsphy_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_LVL, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_BAND, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0xf1),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_TERM_BW, 0x1b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1d),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_MEASURE_TIME, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xf6),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x3d),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xe0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xc8),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xc8),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb1),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xe0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0xc8),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1),
+
+};
+
+static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2, 0x6d),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_MID_TERM_CTRL1, 0x43),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_DEBUG_BUS_CLKSEL, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_MIN_HIBERN8_TIME, 0xff),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
+};
+
+static const struct qmp_phy_init_tbl sm8350_ufsphy_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0xd9),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x42),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_INITVAL2, 0x00),
+ 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_CP_CTRL_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE0, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x19),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xac),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0x98),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE1, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x65),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xdd),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23),
+
+ /* Rate B */
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x06),
+};
+
+static const struct qmp_phy_init_tbl sm8350_ufsphy_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_4_DIVIDER_BAND0_1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0xf5),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_TRAN_DRVR_EMP_EN, 0x0c),
+};
+
+static const struct qmp_phy_init_tbl sm8350_ufsphy_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_LVL, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_BAND, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0xf1),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_TERM_BW, 0x1b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_MEASURE_TIME, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0x6d),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x6d),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xed),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0x3c),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0xe0),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0xc8),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xc8),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xb7),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_LOW, 0xe0),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH, 0xc8),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH2, 0xc8),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH3, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH4, 0xb7),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_DCC_CTRL1, 0x0c),
+};
+
+static const struct qmp_phy_init_tbl sm8350_ufsphy_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_RX_SIGDET_CTRL2, 0x6d),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_MID_TERM_CTRL1, 0x43),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_DEBUG_BUS_CLKSEL, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_RX_MIN_HIBERN8_TIME, 0xff),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_PLL_CNTL, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB, 0x16),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB, 0xd8),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_PWM_GEAR_BAND, 0xaa),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_HS_GEAR_BAND, 0x06),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_RX_SIGDET_CTRL1, 0x0e),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
+};
+
+/* struct qmp_phy_cfg - per-PHY initialization config */
+struct qmp_phy_cfg {
+ int lanes;
+
+ /* Init sequence for PHY blocks - serdes, tx, rx, pcs */
+ const struct qmp_phy_init_tbl *serdes_tbl;
+ int serdes_tbl_num;
+ const struct qmp_phy_init_tbl *tx_tbl;
+ int tx_tbl_num;
+ const struct qmp_phy_init_tbl *rx_tbl;
+ int rx_tbl_num;
+ const struct qmp_phy_init_tbl *pcs_tbl;
+ int pcs_tbl_num;
+
+ /* clock ids to be requested */
+ const char * const *clk_list;
+ int num_clks;
+ /* regulators to be requested */
+ const char * const *vreg_list;
+ int num_vregs;
+
+ /* array of registers with different offsets */
+ const unsigned int *regs;
+
+ unsigned int start_ctrl;
+ unsigned int pwrdn_ctrl;
+ /* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */
+ unsigned int phy_status;
+
+ /* true, if PCS block has no separate SW_RESET register */
+ bool no_pcs_sw_reset;
+};
+
+/**
+ * struct qmp_phy - per-lane phy descriptor
+ *
+ * @phy: generic phy
+ * @cfg: phy specific configuration
+ * @serdes: iomapped memory space for phy's serdes (i.e. PLL)
+ * @tx: iomapped memory space for lane's tx
+ * @rx: iomapped memory space for lane's rx
+ * @pcs: iomapped memory space for lane's pcs
+ * @tx2: iomapped memory space for second lane's tx (in dual lane PHYs)
+ * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs)
+ * @pcs_misc: iomapped memory space for lane's pcs_misc
+ * @qmp: QMP phy to which this lane belongs
+ */
+struct qmp_phy {
+ struct phy *phy;
+ const struct qmp_phy_cfg *cfg;
+ void __iomem *serdes;
+ void __iomem *tx;
+ void __iomem *rx;
+ void __iomem *pcs;
+ void __iomem *tx2;
+ void __iomem *rx2;
+ void __iomem *pcs_misc;
+ struct qcom_qmp *qmp;
+};
+
+/**
+ * struct qcom_qmp - structure holding QMP phy block attributes
+ *
+ * @dev: device
+ *
+ * @clks: array of clocks required by phy
+ * @resets: array of resets required by phy
+ * @vregs: regulator supplies bulk data
+ *
+ * @phys: array of per-lane phy descriptors
+ * @ufs_reset: optional UFS PHY reset handle
+ */
+struct qcom_qmp {
+ struct device *dev;
+
+ struct clk_bulk_data *clks;
+ struct regulator_bulk_data *vregs;
+
+ struct qmp_phy **phys;
+
+ struct reset_control *ufs_reset;
+};
+
+static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg |= val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg &= ~val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+/* list of clocks required by phy */
+static const char * const msm8996_ufs_phy_clk_l[] = {
+ "ref",
+};
+
+/* the primary usb3 phy on sm8250 doesn't have a ref clock */
+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",
+};
+
+/* list of regulators */
+static const char * const qmp_phy_vreg_l[] = {
+ "vdda-phy", "vdda-pll",
+};
+
+static const struct qmp_phy_cfg msm8996_ufs_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = msm8996_ufs_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(msm8996_ufs_serdes_tbl),
+ .tx_tbl = msm8996_ufs_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(msm8996_ufs_tx_tbl),
+ .rx_tbl = msm8996_ufs_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(msm8996_ufs_rx_tbl),
+
+ .clk_list = msm8996_ufs_phy_clk_l,
+ .num_clks = ARRAY_SIZE(msm8996_ufs_phy_clk_l),
+
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+
+ .regs = msm8996_ufsphy_regs_layout,
+
+ .start_ctrl = SERDES_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .no_pcs_sw_reset = true,
+};
+
+static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
+ .lanes = 2,
+
+ .serdes_tbl = sdm845_ufsphy_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sdm845_ufsphy_serdes_tbl),
+ .tx_tbl = sdm845_ufsphy_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_tx_tbl),
+ .rx_tbl = sdm845_ufsphy_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_rx_tbl),
+ .pcs_tbl = sdm845_ufsphy_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sdm845_ufsphy_pcs_tbl),
+ .clk_list = sdm845_ufs_phy_clk_l,
+ .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = sdm845_ufsphy_regs_layout,
+
+ .start_ctrl = SERDES_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .no_pcs_sw_reset = true,
+};
+
+static const struct qmp_phy_cfg sm6115_ufsphy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = sm6115_ufsphy_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm6115_ufsphy_serdes_tbl),
+ .tx_tbl = sm6115_ufsphy_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm6115_ufsphy_tx_tbl),
+ .rx_tbl = sm6115_ufsphy_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm6115_ufsphy_rx_tbl),
+ .pcs_tbl = sm6115_ufsphy_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm6115_ufsphy_pcs_tbl),
+ .clk_list = sdm845_ufs_phy_clk_l,
+ .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = sm6115_ufsphy_regs_layout,
+
+ .start_ctrl = SERDES_START,
+ .pwrdn_ctrl = SW_PWRDN,
+
+ .no_pcs_sw_reset = true,
+};
+
+static const struct qmp_phy_cfg sm8150_ufsphy_cfg = {
+ .lanes = 2,
+
+ .serdes_tbl = sm8150_ufsphy_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8150_ufsphy_serdes_tbl),
+ .tx_tbl = sm8150_ufsphy_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_tx_tbl),
+ .rx_tbl = sm8150_ufsphy_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_rx_tbl),
+ .pcs_tbl = sm8150_ufsphy_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8150_ufsphy_pcs_tbl),
+ .clk_list = sdm845_ufs_phy_clk_l,
+ .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = sm8150_ufsphy_regs_layout,
+
+ .start_ctrl = SERDES_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+};
+
+static const struct qmp_phy_cfg sm8350_ufsphy_cfg = {
+ .lanes = 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 = sdm845_ufs_phy_clk_l,
+ .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = sm8150_ufsphy_regs_layout,
+
+ .start_ctrl = SERDES_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+};
+
+static const struct qmp_phy_cfg sm8450_ufsphy_cfg = {
+ .lanes = 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,
+};
+
+static void qmp_ufs_configure_lane(void __iomem *base,
+ const unsigned int *regs,
+ const struct qmp_phy_init_tbl tbl[],
+ int num,
+ u8 lane_mask)
+{
+ int i;
+ const struct qmp_phy_init_tbl *t = tbl;
+
+ if (!t)
+ return;
+
+ for (i = 0; i < num; i++, t++) {
+ if (!(t->lane_mask & lane_mask))
+ continue;
+
+ if (t->in_layout)
+ writel(t->val, base + regs[t->offset]);
+ else
+ writel(t->val, base + t->offset);
+ }
+}
+
+static void qmp_ufs_configure(void __iomem *base,
+ const unsigned int *regs,
+ const struct qmp_phy_init_tbl tbl[],
+ int num)
+{
+ qmp_ufs_configure_lane(base, regs, tbl, num, 0xff);
+}
+
+static int qmp_ufs_serdes_init(struct qmp_phy *qphy)
+{
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *serdes = qphy->serdes;
+ const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl;
+ int serdes_tbl_num = cfg->serdes_tbl_num;
+
+ qmp_ufs_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num);
+
+ return 0;
+}
+
+static int qmp_ufs_com_init(struct qmp_phy *qphy)
+{
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *pcs = qphy->pcs;
+ int ret;
+
+ /* turn on regulator supplies */
+ ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
+ if (ret) {
+ dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
+ return ret;
+ }
+
+ ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
+ if (ret)
+ goto err_disable_regulators;
+
+ if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL])
+ qphy_setbits(pcs,
+ cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ cfg->pwrdn_ctrl);
+ else
+ qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
+ cfg->pwrdn_ctrl);
+
+ return 0;
+
+err_disable_regulators:
+ regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+
+ return ret;
+}
+
+static int qmp_ufs_com_exit(struct qmp_phy *qphy)
+{
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ reset_control_assert(qmp->ufs_reset);
+
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+
+ regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+
+ return 0;
+}
+
+static int qmp_ufs_init(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ int ret;
+ dev_vdbg(qmp->dev, "Initializing QMP phy\n");
+
+ if (cfg->no_pcs_sw_reset) {
+ /*
+ * Get UFS reset, which is delayed until now to avoid a
+ * circular dependency where UFS needs its PHY, but the PHY
+ * needs this UFS reset.
+ */
+ if (!qmp->ufs_reset) {
+ qmp->ufs_reset =
+ devm_reset_control_get_exclusive(qmp->dev,
+ "ufsphy");
+
+ if (IS_ERR(qmp->ufs_reset)) {
+ ret = PTR_ERR(qmp->ufs_reset);
+ dev_err(qmp->dev,
+ "failed to get UFS reset: %d\n",
+ ret);
+
+ qmp->ufs_reset = NULL;
+ return ret;
+ }
+ }
+
+ ret = reset_control_assert(qmp->ufs_reset);
+ if (ret)
+ return ret;
+ }
+
+ ret = qmp_ufs_com_init(qphy);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int qmp_ufs_power_on(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *tx = qphy->tx;
+ void __iomem *rx = qphy->rx;
+ void __iomem *pcs = qphy->pcs;
+ void __iomem *status;
+ unsigned int mask, val, ready;
+ int ret;
+
+ qmp_ufs_serdes_init(qphy);
+
+ /* Tx, Rx, and PCS configurations */
+ qmp_ufs_configure_lane(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num, 1);
+
+ if (cfg->lanes >= 2) {
+ qmp_ufs_configure_lane(qphy->tx2, cfg->regs,
+ cfg->tx_tbl, cfg->tx_tbl_num, 2);
+ }
+
+ qmp_ufs_configure_lane(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 1);
+
+ if (cfg->lanes >= 2) {
+ qmp_ufs_configure_lane(qphy->rx2, cfg->regs,
+ cfg->rx_tbl, cfg->rx_tbl_num, 2);
+ }
+
+ qmp_ufs_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
+
+ ret = reset_control_deassert(qmp->ufs_reset);
+ if (ret)
+ return ret;
+
+ /* Pull PHY out of reset state */
+ if (!cfg->no_pcs_sw_reset)
+ qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+ /* start SerDes and Phy-Coding-Sublayer */
+ qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
+
+ status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
+ mask = PCS_READY;
+ ready = PCS_READY;
+
+ ret = readl_poll_timeout(status, val, (val & mask) == ready, 10,
+ PHY_INIT_COMPLETE_TIMEOUT);
+ if (ret) {
+ dev_err(qmp->dev, "phy initialization timed-out\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qmp_ufs_power_off(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ /* PHY reset */
+ if (!cfg->no_pcs_sw_reset)
+ qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+
+ /* stop SerDes and Phy-Coding-Sublayer */
+ qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
+
+ /* Put PHY into POWER DOWN state: active low */
+ if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) {
+ qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ cfg->pwrdn_ctrl);
+ } else {
+ qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
+ cfg->pwrdn_ctrl);
+ }
+
+ return 0;
+}
+
+static int qmp_ufs_exit(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+
+ qmp_ufs_com_exit(qphy);
+
+ return 0;
+}
+
+static int qmp_ufs_enable(struct phy *phy)
+{
+ int ret;
+
+ ret = qmp_ufs_init(phy);
+ if (ret)
+ return ret;
+
+ ret = qmp_ufs_power_on(phy);
+ if (ret)
+ qmp_ufs_exit(phy);
+
+ return ret;
+}
+
+static int qmp_ufs_disable(struct phy *phy)
+{
+ int ret;
+
+ ret = qmp_ufs_power_off(phy);
+ if (ret)
+ return ret;
+ return qmp_ufs_exit(phy);
+}
+
+static int qmp_ufs_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int num = cfg->num_vregs;
+ int i;
+
+ qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL);
+ if (!qmp->vregs)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ qmp->vregs[i].supply = cfg->vreg_list[i];
+
+ return devm_regulator_bulk_get(dev, num, qmp->vregs);
+}
+
+static int qmp_ufs_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int num = cfg->num_clks;
+ int i;
+
+ qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
+ if (!qmp->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ qmp->clks[i].id = cfg->clk_list[i];
+
+ return devm_clk_bulk_get(dev, num, qmp->clks);
+}
+
+static const struct phy_ops qcom_qmp_ufs_ops = {
+ .power_on = qmp_ufs_enable,
+ .power_off = qmp_ufs_disable,
+ .owner = THIS_MODULE,
+};
+
+static int qmp_ufs_create(struct device *dev, struct device_node *np, int id,
+ void __iomem *serdes, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ struct phy *generic_phy;
+ struct qmp_phy *qphy;
+ int ret;
+
+ qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
+ if (!qphy)
+ return -ENOMEM;
+
+ qphy->cfg = cfg;
+ qphy->serdes = serdes;
+ /*
+ * Get memory resources for each phy lane:
+ * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
+ * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5
+ * For single lane PHYs: pcs_misc (optional) -> 3.
+ */
+ qphy->tx = devm_of_iomap(dev, np, 0, NULL);
+ if (IS_ERR(qphy->tx))
+ return PTR_ERR(qphy->tx);
+
+ qphy->rx = devm_of_iomap(dev, np, 1, NULL);
+ if (IS_ERR(qphy->rx))
+ return PTR_ERR(qphy->rx);
+
+ qphy->pcs = devm_of_iomap(dev, np, 2, NULL);
+ if (IS_ERR(qphy->pcs))
+ return PTR_ERR(qphy->pcs);
+
+ if (cfg->lanes >= 2) {
+ qphy->tx2 = devm_of_iomap(dev, np, 3, NULL);
+ if (IS_ERR(qphy->tx2))
+ return PTR_ERR(qphy->tx2);
+
+ qphy->rx2 = devm_of_iomap(dev, np, 4, NULL);
+ if (IS_ERR(qphy->rx2))
+ return PTR_ERR(qphy->rx2);
+
+ qphy->pcs_misc = devm_of_iomap(dev, np, 5, NULL);
+ } else {
+ qphy->pcs_misc = devm_of_iomap(dev, np, 3, NULL);
+ }
+
+ if (IS_ERR(qphy->pcs_misc))
+ dev_vdbg(dev, "PHY pcs_misc-reg not used\n");
+
+ generic_phy = devm_phy_create(dev, np, &qcom_qmp_ufs_ops);
+ if (IS_ERR(generic_phy)) {
+ ret = PTR_ERR(generic_phy);
+ dev_err(dev, "failed to create qphy %d\n", ret);
+ return ret;
+ }
+
+ qphy->phy = generic_phy;
+ qphy->qmp = qmp;
+ qmp->phys[id] = qphy;
+ phy_set_drvdata(generic_phy, qphy);
+
+ return 0;
+}
+
+static const struct of_device_id qmp_ufs_of_match_table[] = {
+ {
+ .compatible = "qcom,msm8996-qmp-ufs-phy",
+ .data = &msm8996_ufs_cfg,
+ }, {
+ .compatible = "qcom,msm8998-qmp-ufs-phy",
+ .data = &sdm845_ufsphy_cfg,
+ }, {
+ .compatible = "qcom,sc8180x-qmp-ufs-phy",
+ .data = &sm8150_ufsphy_cfg,
+ }, {
+ .compatible = "qcom,sc8280xp-qmp-ufs-phy",
+ .data = &sm8350_ufsphy_cfg,
+ }, {
+ .compatible = "qcom,sdm845-qmp-ufs-phy",
+ .data = &sdm845_ufsphy_cfg,
+ }, {
+ .compatible = "qcom,sm6115-qmp-ufs-phy",
+ .data = &sm6115_ufsphy_cfg,
+ }, {
+ .compatible = "qcom,sm6350-qmp-ufs-phy",
+ .data = &sdm845_ufsphy_cfg,
+ }, {
+ .compatible = "qcom,sm8150-qmp-ufs-phy",
+ .data = &sm8150_ufsphy_cfg,
+ }, {
+ .compatible = "qcom,sm8250-qmp-ufs-phy",
+ .data = &sm8150_ufsphy_cfg,
+ }, {
+ .compatible = "qcom,sm8350-qmp-ufs-phy",
+ .data = &sm8350_ufsphy_cfg,
+ }, {
+ .compatible = "qcom,sm8450-qmp-ufs-phy",
+ .data = &sm8450_ufsphy_cfg,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qmp_ufs_of_match_table);
+
+static int qmp_ufs_probe(struct platform_device *pdev)
+{
+ struct qcom_qmp *qmp;
+ struct device *dev = &pdev->dev;
+ struct device_node *child;
+ struct phy_provider *phy_provider;
+ void __iomem *serdes;
+ const struct qmp_phy_cfg *cfg = NULL;
+ int num, id;
+ int ret;
+
+ qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL);
+ if (!qmp)
+ return -ENOMEM;
+
+ qmp->dev = dev;
+ dev_set_drvdata(dev, qmp);
+
+ /* Get the specific init parameters of QMP phy */
+ cfg = of_device_get_match_data(dev);
+ if (!cfg)
+ return -EINVAL;
+
+ /* per PHY serdes; usually located at base address */
+ serdes = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(serdes))
+ return PTR_ERR(serdes);
+
+ ret = qmp_ufs_clk_init(dev, cfg);
+ if (ret)
+ return ret;
+
+ ret = qmp_ufs_vreg_init(dev, cfg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get regulator supplies\n");
+
+ num = of_get_available_child_count(dev->of_node);
+ /* do we have a rogue child node ? */
+ if (num > 1)
+ return -EINVAL;
+
+ qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL);
+ if (!qmp->phys)
+ return -ENOMEM;
+
+ id = 0;
+ for_each_available_child_of_node(dev->of_node, child) {
+ /* Create per-lane phy */
+ ret = qmp_ufs_create(dev, child, id, serdes, cfg);
+ if (ret) {
+ dev_err(dev, "failed to create lane%d phy, %d\n",
+ id, ret);
+ goto err_node_put;
+ }
+
+ id++;
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+
+err_node_put:
+ of_node_put(child);
+ return ret;
+}
+
+static struct platform_driver qmp_ufs_driver = {
+ .probe = qmp_ufs_probe,
+ .driver = {
+ .name = "qcom-qmp-ufs-phy",
+ .of_match_table = qmp_ufs_of_match_table,
+ },
+};
+
+module_platform_driver(qmp_ufs_driver);
+
+MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm QMP UFS PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
new file mode 100644
index 000000000000..b84c0d4b5754
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
@@ -0,0 +1,2825 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#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"
+
+/* QPHY_SW_RESET bit */
+#define SW_RESET BIT(0)
+/* QPHY_POWER_DOWN_CONTROL */
+#define SW_PWRDN BIT(0)
+/* QPHY_START_CONTROL bits */
+#define SERDES_START BIT(0)
+#define PCS_START BIT(1)
+/* QPHY_PCS_STATUS bit */
+#define PHYSTATUS BIT(6)
+
+/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */
+/* DP PHY soft reset */
+#define SW_DPPHY_RESET BIT(0)
+/* mux to select DP PHY reset control, 0:HW control, 1: software reset */
+#define SW_DPPHY_RESET_MUX BIT(1)
+/* USB3 PHY soft reset */
+#define SW_USB3PHY_RESET BIT(2)
+/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */
+#define SW_USB3PHY_RESET_MUX BIT(3)
+
+/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */
+#define USB3_MODE BIT(0) /* enables USB3 mode */
+#define DP_MODE BIT(1) /* enables DP mode */
+
+/* QPHY_PCS_AUTONOMOUS_MODE_CTRL register bits */
+#define ARCVR_DTCT_EN BIT(0)
+#define ALFPS_DTCT_EN BIT(1)
+#define ARCVR_DTCT_EVENT_SEL BIT(4)
+
+/* QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR register bits */
+#define IRQ_CLEAR BIT(0)
+
+/* QPHY_PCS_LFPS_RXTERM_IRQ_STATUS register bits */
+#define RCVR_DETECT BIT(0)
+
+/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */
+#define CLAMP_EN BIT(0) /* enables i/o clamp_n */
+
+#define PHY_INIT_COMPLETE_TIMEOUT 10000
+#define POWER_DOWN_DELAY_US_MIN 10
+#define POWER_DOWN_DELAY_US_MAX 11
+
+struct qmp_phy_init_tbl {
+ unsigned int offset;
+ unsigned int val;
+ /*
+ * register part of layout ?
+ * if yes, then offset gives index in the reg-layout
+ */
+ bool in_layout;
+ /*
+ * mask of lanes for which this register is written
+ * for cases when second lane needs different values
+ */
+ u8 lane_mask;
+};
+
+#define QMP_PHY_INIT_CFG(o, v) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .lane_mask = 0xff, \
+ }
+
+#define QMP_PHY_INIT_CFG_L(o, v) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .in_layout = true, \
+ .lane_mask = 0xff, \
+ }
+
+#define QMP_PHY_INIT_CFG_LANE(o, v, l) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .lane_mask = l, \
+ }
+
+/* set of registers with offsets different per-PHY */
+enum qphy_reg_layout {
+ /* PCS registers */
+ QPHY_SW_RESET,
+ QPHY_START_CTRL,
+ QPHY_PCS_STATUS,
+ QPHY_PCS_AUTONOMOUS_MODE_CTRL,
+ QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR,
+ QPHY_PCS_LFPS_RXTERM_IRQ_STATUS,
+ QPHY_PCS_POWER_DOWN_CONTROL,
+ /* PCS_MISC registers */
+ QPHY_PCS_MISC_TYPEC_CTRL,
+ /* Keep last to ensure regs_layout arrays are properly initialized */
+ QPHY_LAYOUT_SIZE
+};
+
+static const unsigned int usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_START_CTRL] = 0x08,
+ [QPHY_PCS_STATUS] = 0x17c,
+ [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d4,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0d8,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x178,
+};
+
+static const unsigned int qmp_v3_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_START_CTRL] = 0x08,
+ [QPHY_PCS_STATUS] = 0x174,
+ [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d8,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0dc,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170,
+};
+
+static const unsigned int qmp_v4_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_START_CTRL] = 0x44,
+ [QPHY_PCS_STATUS] = 0x14,
+ [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40,
+
+ /* In PCS_USB */
+ [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x008,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x014,
+};
+
+static const unsigned int qcm2290_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_PCS_POWER_DOWN_CONTROL] = 0x04,
+ [QPHY_START_CTRL] = 0x08,
+ [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0xd8,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0xdc,
+ [QPHY_PCS_STATUS] = 0x174,
+ [QPHY_PCS_MISC_TYPEC_CTRL] = 0x00,
+};
+
+static const struct qmp_phy_init_tbl ipq8074_usb3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06),
+ /* PLL and Loop filter settings */
+ QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
+ /* SSC settings */
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl ipq8074_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xb8),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x0),
+};
+
+static const struct qmp_phy_init_tbl ipq8074_usb3_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0e),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x85),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x17),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0f),
+};
+
+static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04),
+ /* PLL and Loop filter settings */
+ QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
+ /* SSC settings */
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl msm8996_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
+ QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
+};
+
+static const struct qmp_phy_init_tbl msm8996_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
+};
+
+static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = {
+ /* FLL settings */
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_FLL_CNTRL2, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_FLL_CNTRL1, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_FLL_CNT_VAL_L, 0x09),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_FLL_CNT_VAL_H_TOL, 0x42),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_FLL_MAN_CODE, 0x85),
+
+ /* Lock Det settings */
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_LOCK_DETECT_CONFIG1, 0xd1),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_LOCK_DETECT_CONFIG2, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_LOCK_DETECT_CONFIG3, 0x47),
+ QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG2, 0x08),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_pcs_tbl[] = {
+ /* FLL settings */
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
+
+ /* Lock Det settings */
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0xc6),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x50),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_pcs_tbl[] = {
+ /* FLL settings */
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
+
+ /* Lock Det settings */
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb5),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4c),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x64),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6a),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG1, 0x21),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG2, 0x60),
+};
+
+static const struct qmp_phy_init_tbl msm8998_usb3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_INITVAL, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_MODE, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl msm8998_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x00),
+};
+
+static const struct qmp_phy_init_tbl msm8998_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x43),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FO_GAIN, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x05),
+};
+
+static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x8a),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x20),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xbf),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xbf),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x94),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb3),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_pcs_tbl[] = {
+ /* Lock Det settings */
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13),
+
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_pcs_usb_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x95),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x05),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0xb8),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x37),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xef),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb3),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0f),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_pcs_usb_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x40, 1),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x54, 2),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff, 1),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f, 2),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f, 1),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff, 2),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x97),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_pcs_usb_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_2, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0xb8),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xbf),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
+};
+
+static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_pcs_usb_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+};
+
+static const struct qmp_phy_init_tbl sdx55_usb3_uniphy_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x08),
+};
+
+static const struct qmp_phy_init_tbl sdx55_usb3_uniphy_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x26),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xbf),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x048),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f),
+};
+
+static const struct qmp_phy_init_tbl sdx65_usb3_uniphy_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0xa5),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_2, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x21),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x0b),
+};
+
+static const struct qmp_phy_init_tbl sdx65_usb3_uniphy_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xdb),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0xbd),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa9),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x7b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xe4),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0x64),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0x99),
+ 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_SB2_GAIN1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN2, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_ENABLES, 0x00),
+};
+
+static const struct qmp_phy_init_tbl sm8350_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_TX, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_RX, 0x00),
+ 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, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0x35),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_5, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x21),
+};
+
+static const struct qmp_phy_init_tbl sm8350_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0x99),
+ 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_SB2_GAIN1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN2, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0xbb),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x7b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xbb),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3d, 1),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3c, 2),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xdb),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0x64),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xd2),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x13),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa9),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_AUX_DATA_TCOARSE_TFINE, 0xa0),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_DCC_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_VTH_CODE, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8350_usb3_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8350_usb3_pcs_usb_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RCVR_DTCT_DLY_U3_L, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RCVR_DTCT_DLY_U3_H, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0xa5),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_2, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x21),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x0e),
+};
+
+static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0xbd),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa9),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x7b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xe4),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0x64),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0x99),
+ 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_SB2_GAIN1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN2, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_ENABLES, 0x00),
+};
+
+static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
+};
+
+static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_pcs_usb_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+};
+
+static const struct qmp_phy_init_tbl qcm2290_usb3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_INITVAL, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CTRL_BY_PSM, 0x01),
+};
+
+static const struct qmp_phy_init_tbl qcm2290_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0xc6),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x00),
+};
+
+static const struct qmp_phy_init_tbl qcm2290_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FO_GAIN, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x00),
+};
+
+static const struct qmp_phy_init_tbl qcm2290_usb3_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x17),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x85),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88),
+};
+
+static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE0, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
+ 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_VCO_TUNE1_MODE0, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_BUF_ENABLE, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE2_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE1_MODE1, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE1, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE1, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE1, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x34),
+ 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_CODE1_MODE1, 0xca),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e),
+ 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_MODE1, 0xde),
+ QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x07),
+ 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_VCO_TUNE_MAP, 0x02),
+};
+
+static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0xa5),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_2, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x21),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x0e),
+};
+
+static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0xbd),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa9),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x7b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xe4),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0x64),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0x99),
+ 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_SB2_GAIN1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN2, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL1, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_ENABLES, 0x00),
+};
+
+static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG1, 0xd0),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG3, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG6, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0xaa),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCS_TX_RX_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_CDR_RESET_TIME, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG5, 0x10),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x21),
+};
+
+/* struct qmp_phy_cfg - per-PHY initialization config */
+struct qmp_phy_cfg {
+ int lanes;
+
+ /* Init sequence for PHY blocks - serdes, tx, rx, pcs */
+ const struct qmp_phy_init_tbl *serdes_tbl;
+ int serdes_tbl_num;
+ const struct qmp_phy_init_tbl *tx_tbl;
+ int tx_tbl_num;
+ const struct qmp_phy_init_tbl *rx_tbl;
+ int rx_tbl_num;
+ const struct qmp_phy_init_tbl *pcs_tbl;
+ int pcs_tbl_num;
+ const struct qmp_phy_init_tbl *pcs_usb_tbl;
+ int pcs_usb_tbl_num;
+
+ /* clock ids to be requested */
+ const char * const *clk_list;
+ int num_clks;
+ /* resets to be requested */
+ const char * const *reset_list;
+ int num_resets;
+ /* regulators to be requested */
+ const char * const *vreg_list;
+ int num_vregs;
+
+ /* array of registers with different offsets */
+ const unsigned int *regs;
+
+ unsigned int start_ctrl;
+ unsigned int pwrdn_ctrl;
+ /* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */
+ unsigned int phy_status;
+
+ /* true, if PHY needs delay after POWER_DOWN */
+ bool has_pwrdn_delay;
+ /* power_down delay in usec */
+ int pwrdn_delay_min;
+ int pwrdn_delay_max;
+
+ /* true, if PHY has a separate DP_COM control block */
+ bool has_phy_dp_com_ctrl;
+
+ /* Offset from PCS to PCS_USB region */
+ unsigned int pcs_usb_offset;
+};
+
+/**
+ * struct qmp_phy - per-lane phy descriptor
+ *
+ * @phy: generic phy
+ * @cfg: phy specific configuration
+ * @serdes: iomapped memory space for phy's serdes (i.e. PLL)
+ * @tx: iomapped memory space for lane's tx
+ * @rx: iomapped memory space for lane's rx
+ * @pcs: iomapped memory space for lane's pcs
+ * @tx2: iomapped memory space for second lane's tx (in dual lane PHYs)
+ * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs)
+ * @pcs_misc: iomapped memory space for lane's pcs_misc
+ * @pcs_usb: iomapped memory space for lane's pcs_usb
+ * @pipe_clk: pipe clock
+ * @qmp: QMP phy to which this lane belongs
+ * @mode: current PHY mode
+ */
+struct qmp_phy {
+ struct phy *phy;
+ const struct qmp_phy_cfg *cfg;
+ void __iomem *serdes;
+ void __iomem *tx;
+ void __iomem *rx;
+ void __iomem *pcs;
+ void __iomem *tx2;
+ void __iomem *rx2;
+ void __iomem *pcs_misc;
+ void __iomem *pcs_usb;
+ struct clk *pipe_clk;
+ struct qcom_qmp *qmp;
+ enum phy_mode mode;
+};
+
+/**
+ * struct qcom_qmp - structure holding QMP phy block attributes
+ *
+ * @dev: device
+ * @dp_com: iomapped memory space for phy's dp_com control block
+ *
+ * @clks: array of clocks required by phy
+ * @resets: array of resets required by phy
+ * @vregs: regulator supplies bulk data
+ *
+ * @phys: array of per-lane phy descriptors
+ */
+struct qcom_qmp {
+ struct device *dev;
+ void __iomem *dp_com;
+
+ struct clk_bulk_data *clks;
+ struct reset_control_bulk_data *resets;
+ struct regulator_bulk_data *vregs;
+
+ struct qmp_phy **phys;
+};
+
+static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg |= val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg &= ~val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+/* list of clocks required by phy */
+static const char * const msm8996_phy_clk_l[] = {
+ "aux", "cfg_ahb", "ref",
+};
+
+static const char * const qmp_v3_phy_clk_l[] = {
+ "aux", "cfg_ahb", "ref", "com_aux",
+};
+
+static const char * const qmp_v4_phy_clk_l[] = {
+ "aux", "ref_clk_src", "ref", "com_aux",
+};
+
+/* the primary usb3 phy on sm8250 doesn't have a ref clock */
+static const char * const qmp_v4_sm8250_usbphy_clk_l[] = {
+ "aux", "ref_clk_src", "com_aux"
+};
+
+/* usb3 phy on sdx55 doesn't have com_aux clock */
+static const char * const qmp_v4_sdx55_usbphy_clk_l[] = {
+ "aux", "cfg_ahb", "ref"
+};
+
+static const char * const qcm2290_usb3phy_clk_l[] = {
+ "cfg_ahb", "ref", "com_aux",
+};
+
+/* list of resets */
+static const char * const msm8996_usb3phy_reset_l[] = {
+ "phy", "common",
+};
+
+static const char * const sc7180_usb3phy_reset_l[] = {
+ "phy",
+};
+
+static const char * const qcm2290_usb3phy_reset_l[] = {
+ "phy_phy", "phy",
+};
+
+/* list of regulators */
+static const char * const qmp_phy_vreg_l[] = {
+ "vdda-phy", "vdda-pll",
+};
+
+static const struct qmp_phy_cfg ipq8074_usb3phy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = ipq8074_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(ipq8074_usb3_serdes_tbl),
+ .tx_tbl = msm8996_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl),
+ .rx_tbl = ipq8074_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(ipq8074_usb3_rx_tbl),
+ .pcs_tbl = ipq8074_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(ipq8074_usb3_pcs_tbl),
+ .clk_list = msm8996_phy_clk_l,
+ .num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = usb3phy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+};
+
+static const struct qmp_phy_cfg msm8996_usb3phy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = msm8996_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(msm8996_usb3_serdes_tbl),
+ .tx_tbl = msm8996_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl),
+ .rx_tbl = msm8996_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(msm8996_usb3_rx_tbl),
+ .pcs_tbl = msm8996_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(msm8996_usb3_pcs_tbl),
+ .clk_list = msm8996_phy_clk_l,
+ .num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = usb3phy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+};
+
+static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
+ .lanes = 2,
+
+ .serdes_tbl = qmp_v3_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl),
+ .tx_tbl = qmp_v3_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl),
+ .rx_tbl = qmp_v3_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl),
+ .pcs_tbl = qmp_v3_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl),
+ .clk_list = qmp_v3_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v3_usb3phy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+
+ .has_phy_dp_com_ctrl = true,
+};
+
+static const struct qmp_phy_cfg sc7180_usb3phy_cfg = {
+ .lanes = 2,
+
+ .serdes_tbl = qmp_v3_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl),
+ .tx_tbl = qmp_v3_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl),
+ .rx_tbl = qmp_v3_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl),
+ .pcs_tbl = qmp_v3_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl),
+ .clk_list = qmp_v3_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
+ .reset_list = sc7180_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v3_usb3phy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+
+ .has_phy_dp_com_ctrl = true,
+};
+
+static const struct qmp_phy_cfg sc8280xp_usb3_uniphy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = sc8280xp_usb3_uniphy_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_serdes_tbl),
+ .tx_tbl = sc8280xp_usb3_uniphy_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_tx_tbl),
+ .rx_tbl = sc8280xp_usb3_uniphy_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_rx_tbl),
+ .pcs_tbl = sc8280xp_usb3_uniphy_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_pcs_tbl),
+ .clk_list = qmp_v4_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = qmp_v3_usb3_uniphy_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_serdes_tbl),
+ .tx_tbl = qmp_v3_usb3_uniphy_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_tx_tbl),
+ .rx_tbl = qmp_v3_usb3_uniphy_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_rx_tbl),
+ .pcs_tbl = qmp_v3_usb3_uniphy_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_pcs_tbl),
+ .clk_list = qmp_v3_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v3_usb3phy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static const struct qmp_phy_cfg msm8998_usb3phy_cfg = {
+ .lanes = 2,
+
+ .serdes_tbl = msm8998_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(msm8998_usb3_serdes_tbl),
+ .tx_tbl = msm8998_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(msm8998_usb3_tx_tbl),
+ .rx_tbl = msm8998_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(msm8998_usb3_rx_tbl),
+ .pcs_tbl = msm8998_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(msm8998_usb3_pcs_tbl),
+ .clk_list = msm8996_phy_clk_l,
+ .num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v3_usb3phy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+};
+
+static const struct qmp_phy_cfg sm8150_usb3phy_cfg = {
+ .lanes = 2,
+
+ .serdes_tbl = sm8150_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl),
+ .tx_tbl = sm8150_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm8150_usb3_tx_tbl),
+ .rx_tbl = sm8150_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8150_usb3_rx_tbl),
+ .pcs_tbl = sm8150_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_tbl),
+ .pcs_usb_tbl = sm8150_usb3_pcs_usb_tbl,
+ .pcs_usb_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_usb_tbl),
+ .clk_list = qmp_v4_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+ .pcs_usb_offset = 0x300,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+
+ .has_phy_dp_com_ctrl = true,
+};
+
+static const struct qmp_phy_cfg sm8150_usb3_uniphy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl),
+ .tx_tbl = sm8150_usb3_uniphy_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_tx_tbl),
+ .rx_tbl = sm8150_usb3_uniphy_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_rx_tbl),
+ .pcs_tbl = sm8150_usb3_uniphy_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_pcs_tbl),
+ .pcs_usb_tbl = sm8150_usb3_uniphy_pcs_usb_tbl,
+ .pcs_usb_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_pcs_usb_tbl),
+ .clk_list = qmp_v4_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+ .pcs_usb_offset = 0x600,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static const struct qmp_phy_cfg sm8250_usb3phy_cfg = {
+ .lanes = 2,
+
+ .serdes_tbl = sm8150_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl),
+ .tx_tbl = sm8250_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_tx_tbl),
+ .rx_tbl = sm8250_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_rx_tbl),
+ .pcs_tbl = sm8250_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_tbl),
+ .pcs_usb_tbl = sm8250_usb3_pcs_usb_tbl,
+ .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_usb_tbl),
+ .clk_list = qmp_v4_sm8250_usbphy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+ .pcs_usb_offset = 0x300,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+
+ .has_phy_dp_com_ctrl = true,
+};
+
+static const struct qmp_phy_cfg sm8250_usb3_uniphy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl),
+ .tx_tbl = sm8250_usb3_uniphy_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_tx_tbl),
+ .rx_tbl = sm8250_usb3_uniphy_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_rx_tbl),
+ .pcs_tbl = sm8250_usb3_uniphy_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_pcs_tbl),
+ .pcs_usb_tbl = sm8250_usb3_uniphy_pcs_usb_tbl,
+ .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_pcs_usb_tbl),
+ .clk_list = qmp_v4_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+ .pcs_usb_offset = 0x600,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static const struct qmp_phy_cfg sdx55_usb3_uniphy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl),
+ .tx_tbl = sdx55_usb3_uniphy_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sdx55_usb3_uniphy_tx_tbl),
+ .rx_tbl = sdx55_usb3_uniphy_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sdx55_usb3_uniphy_rx_tbl),
+ .pcs_tbl = sm8250_usb3_uniphy_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_pcs_tbl),
+ .pcs_usb_tbl = sm8250_usb3_uniphy_pcs_usb_tbl,
+ .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_pcs_usb_tbl),
+ .clk_list = qmp_v4_sdx55_usbphy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_sdx55_usbphy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+ .pcs_usb_offset = 0x600,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static const struct qmp_phy_cfg sdx65_usb3_uniphy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl),
+ .tx_tbl = sdx65_usb3_uniphy_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sdx65_usb3_uniphy_tx_tbl),
+ .rx_tbl = sdx65_usb3_uniphy_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sdx65_usb3_uniphy_rx_tbl),
+ .pcs_tbl = sm8350_usb3_uniphy_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_pcs_tbl),
+ .pcs_usb_tbl = sm8350_usb3_uniphy_pcs_usb_tbl,
+ .pcs_usb_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_pcs_usb_tbl),
+ .clk_list = qmp_v4_sdx55_usbphy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_sdx55_usbphy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+ .pcs_usb_offset = 0x1000,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static const struct qmp_phy_cfg sm8350_usb3phy_cfg = {
+ .lanes = 2,
+
+ .serdes_tbl = sm8150_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl),
+ .tx_tbl = sm8350_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm8350_usb3_tx_tbl),
+ .rx_tbl = sm8350_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8350_usb3_rx_tbl),
+ .pcs_tbl = sm8350_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8350_usb3_pcs_tbl),
+ .pcs_usb_tbl = sm8350_usb3_pcs_usb_tbl,
+ .pcs_usb_tbl_num = ARRAY_SIZE(sm8350_usb3_pcs_usb_tbl),
+ .clk_list = qmp_v4_sm8250_usbphy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+ .pcs_usb_offset = 0x300,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+
+ .has_phy_dp_com_ctrl = true,
+};
+
+static const struct qmp_phy_cfg sm8350_usb3_uniphy_cfg = {
+ .lanes = 1,
+
+ .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl),
+ .tx_tbl = sm8350_usb3_uniphy_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_tx_tbl),
+ .rx_tbl = sm8350_usb3_uniphy_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_rx_tbl),
+ .pcs_tbl = sm8350_usb3_uniphy_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_pcs_tbl),
+ .pcs_usb_tbl = sm8350_usb3_uniphy_pcs_usb_tbl,
+ .pcs_usb_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_pcs_usb_tbl),
+ .clk_list = qmp_v4_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v4_usb3phy_regs_layout,
+ .pcs_usb_offset = 0x1000,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+
+ .has_pwrdn_delay = true,
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+};
+
+static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = {
+ .lanes = 2,
+
+ .serdes_tbl = qcm2290_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(qcm2290_usb3_serdes_tbl),
+ .tx_tbl = qcm2290_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(qcm2290_usb3_tx_tbl),
+ .rx_tbl = qcm2290_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(qcm2290_usb3_rx_tbl),
+ .pcs_tbl = qcm2290_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(qcm2290_usb3_pcs_tbl),
+ .clk_list = qcm2290_usb3phy_clk_l,
+ .num_clks = ARRAY_SIZE(qcm2290_usb3phy_clk_l),
+ .reset_list = qcm2290_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(qcm2290_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qcm2290_usb3phy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+};
+
+static void qmp_usb_configure_lane(void __iomem *base,
+ const unsigned int *regs,
+ const struct qmp_phy_init_tbl tbl[],
+ int num,
+ u8 lane_mask)
+{
+ int i;
+ const struct qmp_phy_init_tbl *t = tbl;
+
+ if (!t)
+ return;
+
+ for (i = 0; i < num; i++, t++) {
+ if (!(t->lane_mask & lane_mask))
+ continue;
+
+ if (t->in_layout)
+ writel(t->val, base + regs[t->offset]);
+ else
+ writel(t->val, base + t->offset);
+ }
+}
+
+static void qmp_usb_configure(void __iomem *base,
+ const unsigned int *regs,
+ const struct qmp_phy_init_tbl tbl[],
+ int num)
+{
+ qmp_usb_configure_lane(base, regs, tbl, num, 0xff);
+}
+
+static int qmp_usb_serdes_init(struct qmp_phy *qphy)
+{
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *serdes = qphy->serdes;
+ const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl;
+ int serdes_tbl_num = cfg->serdes_tbl_num;
+
+ qmp_usb_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num);
+
+ return 0;
+}
+
+static int qmp_usb_init(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *pcs = qphy->pcs;
+ void __iomem *dp_com = qmp->dp_com;
+ int ret;
+
+ /* turn on regulator supplies */
+ ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
+ if (ret) {
+ dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+ if (ret) {
+ dev_err(qmp->dev, "reset assert failed\n");
+ goto err_disable_regulators;
+ }
+
+ ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets);
+ if (ret) {
+ dev_err(qmp->dev, "reset deassert failed\n");
+ goto err_disable_regulators;
+ }
+
+ ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
+ if (ret)
+ goto err_assert_reset;
+
+ if (cfg->has_phy_dp_com_ctrl) {
+ qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL,
+ SW_PWRDN);
+ /* override hardware control for reset of qmp phy */
+ qphy_setbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
+ SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
+ SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
+
+ /* Default type-c orientation, i.e CC1 */
+ qphy_setbits(dp_com, QPHY_V3_DP_COM_TYPEC_CTRL, 0x02);
+
+ qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL,
+ USB3_MODE | DP_MODE);
+
+ /* bring both QMP USB and QMP DP PHYs PCS block out of reset */
+ qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
+ SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
+ SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
+
+ qphy_clrbits(dp_com, QPHY_V3_DP_COM_SWI_CTRL, 0x03);
+ qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
+ }
+
+ if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL])
+ qphy_setbits(pcs,
+ cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ cfg->pwrdn_ctrl);
+ else
+ qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
+ cfg->pwrdn_ctrl);
+
+ return 0;
+
+err_assert_reset:
+ reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+err_disable_regulators:
+ regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+
+ return ret;
+}
+
+static int qmp_usb_exit(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+
+ regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+
+ return 0;
+}
+
+static int qmp_usb_power_on(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *tx = qphy->tx;
+ void __iomem *rx = qphy->rx;
+ void __iomem *pcs = qphy->pcs;
+ void __iomem *status;
+ unsigned int mask, val, ready;
+ int ret;
+
+ qmp_usb_serdes_init(qphy);
+
+ ret = clk_prepare_enable(qphy->pipe_clk);
+ if (ret) {
+ dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret);
+ return ret;
+ }
+
+ /* Tx, Rx, and PCS configurations */
+ qmp_usb_configure_lane(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num, 1);
+
+ if (cfg->lanes >= 2) {
+ qmp_usb_configure_lane(qphy->tx2, cfg->regs,
+ cfg->tx_tbl, cfg->tx_tbl_num, 2);
+ }
+
+ qmp_usb_configure_lane(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 1);
+
+ if (cfg->lanes >= 2) {
+ qmp_usb_configure_lane(qphy->rx2, cfg->regs,
+ cfg->rx_tbl, cfg->rx_tbl_num, 2);
+ }
+
+ /* Configure link rate, swing, etc. */
+ qmp_usb_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
+
+ if (cfg->has_pwrdn_delay)
+ usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
+
+ /* Pull PHY out of reset state */
+ qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+
+ /* start SerDes and Phy-Coding-Sublayer */
+ qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
+
+ status = pcs + cfg->regs[QPHY_PCS_STATUS];
+ mask = cfg->phy_status;
+ ready = 0;
+
+ ret = readl_poll_timeout(status, val, (val & mask) == ready, 10,
+ PHY_INIT_COMPLETE_TIMEOUT);
+ if (ret) {
+ dev_err(qmp->dev, "phy initialization timed-out\n");
+ goto err_disable_pipe_clk;
+ }
+
+ return 0;
+
+err_disable_pipe_clk:
+ clk_disable_unprepare(qphy->pipe_clk);
+
+ return ret;
+}
+
+static int qmp_usb_power_off(struct phy *phy)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ clk_disable_unprepare(qphy->pipe_clk);
+
+ /* PHY reset */
+ qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+
+ /* stop SerDes and Phy-Coding-Sublayer */
+ qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
+
+ /* Put PHY into POWER DOWN state: active low */
+ if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) {
+ qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ cfg->pwrdn_ctrl);
+ } else {
+ qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
+ cfg->pwrdn_ctrl);
+ }
+
+ return 0;
+}
+
+static int qmp_usb_enable(struct phy *phy)
+{
+ int ret;
+
+ ret = qmp_usb_init(phy);
+ if (ret)
+ return ret;
+
+ ret = qmp_usb_power_on(phy);
+ if (ret)
+ qmp_usb_exit(phy);
+
+ return ret;
+}
+
+static int qmp_usb_disable(struct phy *phy)
+{
+ int ret;
+
+ ret = qmp_usb_power_off(phy);
+ if (ret)
+ return ret;
+ return qmp_usb_exit(phy);
+}
+
+static int qmp_usb_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+
+ qphy->mode = mode;
+
+ return 0;
+}
+
+static void qmp_usb_enable_autonomous_mode(struct qmp_phy *qphy)
+{
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *pcs_usb = qphy->pcs_usb ?: qphy->pcs;
+ void __iomem *pcs_misc = qphy->pcs_misc;
+ u32 intr_mask;
+
+ if (qphy->mode == PHY_MODE_USB_HOST_SS ||
+ qphy->mode == PHY_MODE_USB_DEVICE_SS)
+ intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN;
+ else
+ intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL;
+
+ /* Clear any pending interrupts status */
+ qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+ /* Writing 1 followed by 0 clears the interrupt */
+ qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+
+ qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
+ ARCVR_DTCT_EN | ALFPS_DTCT_EN | ARCVR_DTCT_EVENT_SEL);
+
+ /* Enable required PHY autonomous mode interrupts */
+ qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], intr_mask);
+
+ /* Enable i/o clamp_n for autonomous mode */
+ if (pcs_misc)
+ qphy_clrbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN);
+}
+
+static void qmp_usb_disable_autonomous_mode(struct qmp_phy *qphy)
+{
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *pcs_usb = qphy->pcs_usb ?: qphy->pcs;
+ void __iomem *pcs_misc = qphy->pcs_misc;
+
+ /* Disable i/o clamp_n on resume for normal mode */
+ if (pcs_misc)
+ qphy_setbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN);
+
+ qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
+ ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL | ALFPS_DTCT_EN);
+
+ qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+ /* Writing 1 followed by 0 clears the interrupt */
+ qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+}
+
+static int __maybe_unused qmp_usb_runtime_suspend(struct device *dev)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ struct qmp_phy *qphy = qmp->phys[0];
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+
+ dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qphy->mode);
+
+ if (!qphy->phy->init_count) {
+ dev_vdbg(dev, "PHY not initialized, bailing out\n");
+ return 0;
+ }
+
+ qmp_usb_enable_autonomous_mode(qphy);
+
+ clk_disable_unprepare(qphy->pipe_clk);
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+
+ return 0;
+}
+
+static int __maybe_unused qmp_usb_runtime_resume(struct device *dev)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ struct qmp_phy *qphy = qmp->phys[0];
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ int ret = 0;
+
+ dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qphy->mode);
+
+ if (!qphy->phy->init_count) {
+ dev_vdbg(dev, "PHY not initialized, bailing out\n");
+ return 0;
+ }
+
+ ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(qphy->pipe_clk);
+ if (ret) {
+ dev_err(dev, "pipe_clk enable failed, err=%d\n", ret);
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+ return ret;
+ }
+
+ qmp_usb_disable_autonomous_mode(qphy);
+
+ return 0;
+}
+
+static int qmp_usb_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int num = cfg->num_vregs;
+ int i;
+
+ qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL);
+ if (!qmp->vregs)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ qmp->vregs[i].supply = cfg->vreg_list[i];
+
+ return devm_regulator_bulk_get(dev, num, qmp->vregs);
+}
+
+static int qmp_usb_reset_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int i;
+ int ret;
+
+ qmp->resets = devm_kcalloc(dev, cfg->num_resets,
+ sizeof(*qmp->resets), GFP_KERNEL);
+ if (!qmp->resets)
+ return -ENOMEM;
+
+ for (i = 0; i < cfg->num_resets; i++)
+ qmp->resets[i].id = cfg->reset_list[i];
+
+ ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets, qmp->resets);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get resets\n");
+
+ return 0;
+}
+
+static int qmp_usb_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ int num = cfg->num_clks;
+ int i;
+
+ qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
+ if (!qmp->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ qmp->clks[i].id = cfg->clk_list[i];
+
+ return devm_clk_bulk_get(dev, num, qmp->clks);
+}
+
+static void phy_clk_release_provider(void *res)
+{
+ of_clk_del_provider(res);
+}
+
+/*
+ * Register a fixed rate pipe clock.
+ *
+ * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate
+ * controls it. The <s>_pipe_clk coming out of the GCC is requested
+ * by the PHY driver for its operations.
+ * We register the <s>_pipe_clksrc here. The gcc driver takes care
+ * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk.
+ * Below picture shows this relationship.
+ *
+ * +---------------+
+ * | PHY block |<<---------------------------------------+
+ * | | |
+ * | +-------+ | +-----+ |
+ * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+
+ * clk | +-------+ | +-----+
+ * +---------------+
+ */
+static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
+{
+ struct clk_fixed_rate *fixed;
+ struct clk_init_data init = { };
+ int ret;
+
+ ret = of_property_read_string(np, "clock-output-names", &init.name);
+ if (ret) {
+ dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np);
+ return ret;
+ }
+
+ fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL);
+ if (!fixed)
+ return -ENOMEM;
+
+ init.ops = &clk_fixed_rate_ops;
+
+ /* controllers using QMP phys use 125MHz pipe clock interface */
+ fixed->fixed_rate = 125000000;
+ fixed->hw.init = &init;
+
+ ret = devm_clk_hw_register(qmp->dev, &fixed->hw);
+ if (ret)
+ return ret;
+
+ ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw);
+ if (ret)
+ return ret;
+
+ /*
+ * Roll a devm action because the clock provider is the child node, but
+ * the child node is not actually a device.
+ */
+ return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np);
+}
+
+static const struct phy_ops qmp_usb_ops = {
+ .init = qmp_usb_enable,
+ .exit = qmp_usb_disable,
+ .set_mode = qmp_usb_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static void __iomem *qmp_usb_iomap(struct device *dev, struct device_node *np,
+ int index, bool exclusive)
+{
+ struct resource res;
+
+ if (!exclusive) {
+ if (of_address_to_resource(np, index, &res))
+ return IOMEM_ERR_PTR(-EINVAL);
+
+ return devm_ioremap(dev, res.start, resource_size(&res));
+ }
+
+ return devm_of_iomap(dev, np, index, NULL);
+}
+
+static
+int qmp_usb_create(struct device *dev, struct device_node *np, int id,
+ void __iomem *serdes, const struct qmp_phy_cfg *cfg)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ struct phy *generic_phy;
+ struct qmp_phy *qphy;
+ bool exclusive = true;
+ int ret;
+
+ /*
+ * FIXME: These bindings should be fixed to not rely on overlapping
+ * mappings for PCS.
+ */
+ if (of_device_is_compatible(dev->of_node, "qcom,sdx65-qmp-usb3-uni-phy"))
+ exclusive = false;
+ if (of_device_is_compatible(dev->of_node, "qcom,sm8350-qmp-usb3-uni-phy"))
+ exclusive = false;
+
+ qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
+ if (!qphy)
+ return -ENOMEM;
+
+ qphy->cfg = cfg;
+ qphy->serdes = serdes;
+ /*
+ * Get memory resources for each phy lane:
+ * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
+ * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5
+ * For single lane PHYs: pcs_misc (optional) -> 3.
+ */
+ qphy->tx = devm_of_iomap(dev, np, 0, NULL);
+ if (IS_ERR(qphy->tx))
+ return PTR_ERR(qphy->tx);
+
+ qphy->rx = devm_of_iomap(dev, np, 1, NULL);
+ if (IS_ERR(qphy->rx))
+ return PTR_ERR(qphy->rx);
+
+ qphy->pcs = qmp_usb_iomap(dev, np, 2, exclusive);
+ if (IS_ERR(qphy->pcs))
+ return PTR_ERR(qphy->pcs);
+
+ if (cfg->pcs_usb_offset)
+ qphy->pcs_usb = qphy->pcs + cfg->pcs_usb_offset;
+
+ if (cfg->lanes >= 2) {
+ qphy->tx2 = devm_of_iomap(dev, np, 3, NULL);
+ if (IS_ERR(qphy->tx2))
+ return PTR_ERR(qphy->tx2);
+
+ qphy->rx2 = devm_of_iomap(dev, np, 4, NULL);
+ if (IS_ERR(qphy->rx2))
+ return PTR_ERR(qphy->rx2);
+
+ qphy->pcs_misc = devm_of_iomap(dev, np, 5, NULL);
+ } else {
+ qphy->pcs_misc = devm_of_iomap(dev, np, 3, NULL);
+ }
+
+ if (IS_ERR(qphy->pcs_misc)) {
+ dev_vdbg(dev, "PHY pcs_misc-reg not used\n");
+ qphy->pcs_misc = NULL;
+ }
+
+ qphy->pipe_clk = devm_get_clk_from_child(dev, np, NULL);
+ if (IS_ERR(qphy->pipe_clk)) {
+ return dev_err_probe(dev, PTR_ERR(qphy->pipe_clk),
+ "failed to get lane%d pipe clock\n", id);
+ }
+
+ generic_phy = devm_phy_create(dev, np, &qmp_usb_ops);
+ if (IS_ERR(generic_phy)) {
+ ret = PTR_ERR(generic_phy);
+ dev_err(dev, "failed to create qphy %d\n", ret);
+ return ret;
+ }
+
+ qphy->phy = generic_phy;
+ qphy->qmp = qmp;
+ qmp->phys[id] = qphy;
+ phy_set_drvdata(generic_phy, qphy);
+
+ return 0;
+}
+
+static const struct of_device_id qmp_usb_of_match_table[] = {
+ {
+ .compatible = "qcom,ipq8074-qmp-usb3-phy",
+ .data = &ipq8074_usb3phy_cfg,
+ }, {
+ .compatible = "qcom,msm8996-qmp-usb3-phy",
+ .data = &msm8996_usb3phy_cfg,
+ }, {
+ .compatible = "qcom,ipq6018-qmp-usb3-phy",
+ .data = &ipq8074_usb3phy_cfg,
+ }, {
+ .compatible = "qcom,sc7180-qmp-usb3-phy",
+ .data = &sc7180_usb3phy_cfg,
+ }, {
+ .compatible = "qcom,sc8180x-qmp-usb3-phy",
+ .data = &sm8150_usb3phy_cfg,
+ }, {
+ .compatible = "qcom,sc8280xp-qmp-usb3-uni-phy",
+ .data = &sc8280xp_usb3_uniphy_cfg,
+ }, {
+ .compatible = "qcom,sdm845-qmp-usb3-phy",
+ .data = &qmp_v3_usb3phy_cfg,
+ }, {
+ .compatible = "qcom,sdm845-qmp-usb3-uni-phy",
+ .data = &qmp_v3_usb3_uniphy_cfg,
+ }, {
+ .compatible = "qcom,msm8998-qmp-usb3-phy",
+ .data = &msm8998_usb3phy_cfg,
+ }, {
+ .compatible = "qcom,sm8150-qmp-usb3-phy",
+ .data = &sm8150_usb3phy_cfg,
+ }, {
+ .compatible = "qcom,sm8150-qmp-usb3-uni-phy",
+ .data = &sm8150_usb3_uniphy_cfg,
+ }, {
+ .compatible = "qcom,sm8250-qmp-usb3-phy",
+ .data = &sm8250_usb3phy_cfg,
+ }, {
+ .compatible = "qcom,sm8250-qmp-usb3-uni-phy",
+ .data = &sm8250_usb3_uniphy_cfg,
+ }, {
+ .compatible = "qcom,sdx55-qmp-usb3-uni-phy",
+ .data = &sdx55_usb3_uniphy_cfg,
+ }, {
+ .compatible = "qcom,sdx65-qmp-usb3-uni-phy",
+ .data = &sdx65_usb3_uniphy_cfg,
+ }, {
+ .compatible = "qcom,sm8350-qmp-usb3-phy",
+ .data = &sm8350_usb3phy_cfg,
+ }, {
+ .compatible = "qcom,sm8350-qmp-usb3-uni-phy",
+ .data = &sm8350_usb3_uniphy_cfg,
+ }, {
+ .compatible = "qcom,sm8450-qmp-usb3-phy",
+ .data = &sm8350_usb3phy_cfg,
+ }, {
+ .compatible = "qcom,qcm2290-qmp-usb3-phy",
+ .data = &qcm2290_usb3phy_cfg,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qmp_usb_of_match_table);
+
+static const struct dev_pm_ops qmp_usb_pm_ops = {
+ SET_RUNTIME_PM_OPS(qmp_usb_runtime_suspend,
+ qmp_usb_runtime_resume, NULL)
+};
+
+static int qmp_usb_probe(struct platform_device *pdev)
+{
+ struct qcom_qmp *qmp;
+ struct device *dev = &pdev->dev;
+ struct device_node *child;
+ struct phy_provider *phy_provider;
+ void __iomem *serdes;
+ const struct qmp_phy_cfg *cfg = NULL;
+ int num, id;
+ int ret;
+
+ qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL);
+ if (!qmp)
+ return -ENOMEM;
+
+ qmp->dev = dev;
+ dev_set_drvdata(dev, qmp);
+
+ /* Get the specific init parameters of QMP phy */
+ cfg = of_device_get_match_data(dev);
+ if (!cfg)
+ return -EINVAL;
+
+ /* per PHY serdes; usually located at base address */
+ serdes = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(serdes))
+ return PTR_ERR(serdes);
+
+ /* per PHY dp_com; if PHY has dp_com control block */
+ if (cfg->has_phy_dp_com_ctrl) {
+ qmp->dp_com = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(qmp->dp_com))
+ return PTR_ERR(qmp->dp_com);
+ }
+
+ ret = qmp_usb_clk_init(dev, cfg);
+ if (ret)
+ return ret;
+
+ ret = qmp_usb_reset_init(dev, cfg);
+ if (ret)
+ return ret;
+
+ ret = qmp_usb_vreg_init(dev, cfg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get regulator supplies\n");
+
+ num = of_get_available_child_count(dev->of_node);
+ /* do we have a rogue child node ? */
+ if (num > 1)
+ return -EINVAL;
+
+ qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL);
+ if (!qmp->phys)
+ return -ENOMEM;
+
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+ /*
+ * Prevent runtime pm from being ON by default. Users can enable
+ * it using power/control in sysfs.
+ */
+ pm_runtime_forbid(dev);
+
+ id = 0;
+ for_each_available_child_of_node(dev->of_node, child) {
+ /* Create per-lane phy */
+ ret = qmp_usb_create(dev, child, id, serdes, cfg);
+ if (ret) {
+ dev_err(dev, "failed to create lane%d phy, %d\n",
+ id, ret);
+ goto err_node_put;
+ }
+
+ /*
+ * Register the pipe clock provided by phy.
+ * See function description to see details of this pipe clock.
+ */
+ ret = phy_pipe_clk_register(qmp, child);
+ if (ret) {
+ dev_err(qmp->dev,
+ "failed to register pipe clock source\n");
+ goto err_node_put;
+ }
+
+ id++;
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+
+err_node_put:
+ of_node_put(child);
+ return ret;
+}
+
+static struct platform_driver qmp_usb_driver = {
+ .probe = qmp_usb_probe,
+ .driver = {
+ .name = "qcom-qmp-usb-phy",
+ .pm = &qmp_usb_pm_ops,
+ .of_match_table = qmp_usb_of_match_table,
+ },
+};
+
+module_platform_driver(qmp_usb_driver);
+
+MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm QMP USB PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c
deleted file mode 100644
index 7db2a94f7a99..000000000000
--- a/drivers/phy/qualcomm/phy-qcom-qmp.c
+++ /dev/null
@@ -1,2257 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
- */
-
-#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"
-
-/* QPHY_SW_RESET bit */
-#define SW_RESET BIT(0)
-/* QPHY_POWER_DOWN_CONTROL */
-#define SW_PWRDN BIT(0)
-#define REFCLK_DRV_DSBL BIT(1)
-/* QPHY_START_CONTROL bits */
-#define SERDES_START BIT(0)
-#define PCS_START BIT(1)
-#define PLL_READY_GATE_EN BIT(3)
-/* QPHY_PCS_STATUS bit */
-#define PHYSTATUS BIT(6)
-/* QPHY_PCS_READY_STATUS & QPHY_COM_PCS_READY_STATUS bit */
-#define PCS_READY BIT(0)
-
-/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */
-/* DP PHY soft reset */
-#define SW_DPPHY_RESET BIT(0)
-/* mux to select DP PHY reset control, 0:HW control, 1: software reset */
-#define SW_DPPHY_RESET_MUX BIT(1)
-/* USB3 PHY soft reset */
-#define SW_USB3PHY_RESET BIT(2)
-/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */
-#define SW_USB3PHY_RESET_MUX BIT(3)
-
-/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */
-#define USB3_MODE BIT(0) /* enables USB3 mode */
-#define DP_MODE BIT(1) /* enables DP mode */
-
-/* QPHY_PCS_AUTONOMOUS_MODE_CTRL register bits */
-#define ARCVR_DTCT_EN BIT(0)
-#define ALFPS_DTCT_EN BIT(1)
-#define ARCVR_DTCT_EVENT_SEL BIT(4)
-
-/* QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR register bits */
-#define IRQ_CLEAR BIT(0)
-
-/* QPHY_PCS_LFPS_RXTERM_IRQ_STATUS register bits */
-#define RCVR_DETECT BIT(0)
-
-/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */
-#define CLAMP_EN BIT(0) /* enables i/o clamp_n */
-
-#define PHY_INIT_COMPLETE_TIMEOUT 10000
-#define POWER_DOWN_DELAY_US_MIN 10
-#define POWER_DOWN_DELAY_US_MAX 11
-
-#define MAX_PROP_NAME 32
-
-/* Define the assumed distance between lanes for underspecified device trees. */
-#define QMP_PHY_LEGACY_LANE_STRIDE 0x400
-
-struct qmp_phy_init_tbl {
- unsigned int offset;
- unsigned int val;
- /*
- * register part of layout ?
- * if yes, then offset gives index in the reg-layout
- */
- int in_layout;
-};
-
-#define QMP_PHY_INIT_CFG(o, v) \
- { \
- .offset = o, \
- .val = v, \
- }
-
-#define QMP_PHY_INIT_CFG_L(o, v) \
- { \
- .offset = o, \
- .val = v, \
- .in_layout = 1, \
- }
-
-/* set of registers with offsets different per-PHY */
-enum qphy_reg_layout {
- /* Common block control registers */
- QPHY_COM_SW_RESET,
- QPHY_COM_POWER_DOWN_CONTROL,
- QPHY_COM_START_CONTROL,
- QPHY_COM_PCS_READY_STATUS,
- /* PCS registers */
- QPHY_PLL_LOCK_CHK_DLY_TIME,
- QPHY_FLL_CNTRL1,
- QPHY_FLL_CNTRL2,
- QPHY_FLL_CNT_VAL_L,
- QPHY_FLL_CNT_VAL_H_TOL,
- QPHY_FLL_MAN_CODE,
- QPHY_SW_RESET,
- QPHY_START_CTRL,
- QPHY_PCS_READY_STATUS,
- QPHY_PCS_STATUS,
- QPHY_PCS_AUTONOMOUS_MODE_CTRL,
- QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR,
- QPHY_PCS_LFPS_RXTERM_IRQ_STATUS,
-};
-
-static const unsigned int pciephy_regs_layout[] = {
- [QPHY_COM_SW_RESET] = 0x400,
- [QPHY_COM_POWER_DOWN_CONTROL] = 0x404,
- [QPHY_COM_START_CONTROL] = 0x408,
- [QPHY_COM_PCS_READY_STATUS] = 0x448,
- [QPHY_PLL_LOCK_CHK_DLY_TIME] = 0xa8,
- [QPHY_FLL_CNTRL1] = 0xc4,
- [QPHY_FLL_CNTRL2] = 0xc8,
- [QPHY_FLL_CNT_VAL_L] = 0xcc,
- [QPHY_FLL_CNT_VAL_H_TOL] = 0xd0,
- [QPHY_FLL_MAN_CODE] = 0xd4,
- [QPHY_SW_RESET] = 0x00,
- [QPHY_START_CTRL] = 0x08,
- [QPHY_PCS_STATUS] = 0x174,
-};
-
-static const unsigned int usb3phy_regs_layout[] = {
- [QPHY_FLL_CNTRL1] = 0xc0,
- [QPHY_FLL_CNTRL2] = 0xc4,
- [QPHY_FLL_CNT_VAL_L] = 0xc8,
- [QPHY_FLL_CNT_VAL_H_TOL] = 0xcc,
- [QPHY_FLL_MAN_CODE] = 0xd0,
- [QPHY_SW_RESET] = 0x00,
- [QPHY_START_CTRL] = 0x08,
- [QPHY_PCS_STATUS] = 0x17c,
- [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d4,
- [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0d8,
- [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x178,
-};
-
-static const unsigned int qmp_v3_usb3phy_regs_layout[] = {
- [QPHY_SW_RESET] = 0x00,
- [QPHY_START_CTRL] = 0x08,
- [QPHY_PCS_STATUS] = 0x174,
- [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d8,
- [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0dc,
- [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170,
-};
-
-static const unsigned int sdm845_ufsphy_regs_layout[] = {
- [QPHY_START_CTRL] = 0x00,
- [QPHY_PCS_READY_STATUS] = 0x160,
-};
-
-static const unsigned int sm8150_ufsphy_regs_layout[] = {
- [QPHY_START_CTRL] = QPHY_V4_PHY_START,
- [QPHY_PCS_READY_STATUS] = QPHY_V4_PCS_READY_STATUS,
- [QPHY_SW_RESET] = QPHY_V4_SW_RESET,
-};
-
-static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c),
- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
- QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42),
- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f),
- QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09),
- QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a),
- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
- QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f),
- QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
- QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19),
- QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15),
- QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19),
- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
- QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40),
-};
-
-static const struct qmp_phy_init_tbl msm8996_pcie_tx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
- QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
-};
-
-static const struct qmp_phy_init_tbl msm8996_pcie_rx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c),
- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb),
- QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18),
- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19),
-};
-
-static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = {
- QMP_PHY_INIT_CFG(QPHY_RX_IDLE_DTCT_CNTRL, 0x4c),
- QMP_PHY_INIT_CFG(QPHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00),
- QMP_PHY_INIT_CFG(QPHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
-
- QMP_PHY_INIT_CFG_L(QPHY_PLL_LOCK_CHK_DLY_TIME, 0x05),
-
- QMP_PHY_INIT_CFG(QPHY_ENDPOINT_REFCLK_DRIVE, 0x05),
- QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x02),
- QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG4, 0x00),
- QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG1, 0xa3),
- QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0x0e),
-};
-
-static const struct qmp_phy_init_tbl msm8998_pcie_serdes_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x0f),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER1, 0xff),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER2, 0x3f),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_EP_DIV, 0x19),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x90),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x03),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x55),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0x55),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0d),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x08),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x34),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x33),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x07),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x09),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x40),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x7e),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x15),
-};
-
-static const struct qmp_phy_init_tbl msm8998_pcie_tx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06),
-};
-
-static const struct qmp_phy_init_tbl msm8998_pcie_rx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x1c),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN_HALF, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x71),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x40),
-};
-
-static const struct qmp_phy_init_tbl msm8998_pcie_pcs_tbl[] = {
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE, 0x04),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_OSC_DTCT_ACTIONS, 0x00),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x01),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x20),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x99),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03),
-};
-
-static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
- QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
- QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
- QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04),
- /* PLL and Loop filter settings */
- QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
- QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
- QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15),
- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34),
- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
- /* SSC settings */
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07),
-};
-
-static const struct qmp_phy_init_tbl msm8996_usb3_tx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
- QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12),
- QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
-};
-
-static const struct qmp_phy_init_tbl msm8996_usb3_rx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c),
- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb),
- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
- QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03),
- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18),
- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
-};
-
-static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = {
- /* FLL settings */
- QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL2, 0x03),
- QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL1, 0x02),
- QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_L, 0x09),
- QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_H_TOL, 0x42),
- QMP_PHY_INIT_CFG_L(QPHY_FLL_MAN_CODE, 0x85),
-
- /* Lock Det settings */
- QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG1, 0xd1),
- QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG2, 0x1f),
- QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG3, 0x47),
- QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG2, 0x08),
-};
-
-static const struct qmp_phy_init_tbl ipq8074_pcie_serdes_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x18),
- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
- QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0xf),
- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x1),
- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x0),
- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0x1f),
- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f),
- QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x6),
- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0xf),
- QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x0),
- QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x1),
- QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x20),
- QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0xa),
- QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20),
- QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0xa),
- QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0xa),
- QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x3),
- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x0),
- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0xD),
- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xD04),
- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
- QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x2),
- QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f),
- QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0xb),
- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
- QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x0),
- QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CTRL_BY_PSM, 0x1),
- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0xa),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x1),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x1),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x2),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x0),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f),
- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19),
- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19),
- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x7),
-};
-
-static const struct qmp_phy_init_tbl ipq8074_pcie_tx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
- QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x6),
- QMP_PHY_INIT_CFG(QSERDES_TX_RES_CODE_LANE_OFFSET, 0x2),
- QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12),
-};
-
-static const struct qmp_phy_init_tbl ipq8074_pcie_rx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c),
- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x1),
- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x0),
- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb),
- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x4),
- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x4),
-};
-
-static const struct qmp_phy_init_tbl ipq8074_pcie_pcs_tbl[] = {
- QMP_PHY_INIT_CFG(QPHY_ENDPOINT_REFCLK_DRIVE, 0x4),
- QMP_PHY_INIT_CFG(QPHY_OSC_DTCT_ACTIONS, 0x0),
- QMP_PHY_INIT_CFG(QPHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x40),
- QMP_PHY_INIT_CFG(QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x0),
- QMP_PHY_INIT_CFG(QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x40),
- QMP_PHY_INIT_CFG(QPHY_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB, 0x0),
- QMP_PHY_INIT_CFG(QPHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x40),
- QMP_PHY_INIT_CFG_L(QPHY_PLL_LOCK_CHK_DLY_TIME, 0x73),
- QMP_PHY_INIT_CFG(QPHY_RX_SIGDET_LVL, 0x99),
- QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M6DB_V0, 0x15),
- QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0xe),
- QMP_PHY_INIT_CFG_L(QPHY_SW_RESET, 0x0),
- QMP_PHY_INIT_CFG_L(QPHY_START_CTRL, 0x3),
-};
-
-static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07),
-};
-
-static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x09),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06),
-};
-
-static const struct qmp_phy_init_tbl qmp_v3_usb3_rx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
-};
-
-static const struct qmp_phy_init_tbl qmp_v3_usb3_pcs_tbl[] = {
- /* FLL settings */
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
-
- /* Lock Det settings */
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
-
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d),
-
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
-};
-
-static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_serdes_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07),
-};
-
-static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_tx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0xc6),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06),
-};
-
-static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_rx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x0c),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x50),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1c),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
-};
-
-static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_pcs_tbl[] = {
- /* FLL settings */
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
-
- /* Lock Det settings */
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
-
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb5),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4c),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x64),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6a),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d),
-
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
-
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG1, 0x21),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG2, 0x60),
-};
-
-static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0xd5),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_CTRL, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x05),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL1, 0xff),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL2, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xda),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0xff),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0c),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE1, 0x98),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE1, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE1, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE1, 0x36),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1, 0x3f),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE1, 0xc1),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE1, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE1, 0x32),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE1, 0x0f),
-
- /* Rate B */
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x44),
-};
-
-static const struct qmp_phy_init_tbl sdm845_ufsphy_tx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x07),
-};
-
-static const struct qmp_phy_init_tbl sdm845_ufsphy_rx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_LVL, 0x24),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x0f),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_TERM_BW, 0x5b),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1b),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x81),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59),
-};
-
-static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = {
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_CTRL2, 0x6e),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL, 0x0a),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_SMALL_AMP_DRV_LVL, 0x02),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SYM_RESYNC_CTRL, 0x03),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_MID_TERM_CTRL1, 0x43),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_CTRL1, 0x0f),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_MIN_HIBERN8_TIME, 0x9a),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_MULTI_LANE_CTRL1, 0x02),
-};
-
-static const struct qmp_phy_init_tbl msm8998_usb3_serdes_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_INITVAL, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_MODE, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85),
- QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07),
-};
-
-static const struct qmp_phy_init_tbl msm8998_usb3_tx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x00),
-};
-
-static const struct qmp_phy_init_tbl msm8998_usb3_rx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x07),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x43),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1c),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FO_GAIN, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x03),
- QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x05),
-};
-
-static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = {
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x8a),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
- QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
-};
-
-static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0xd9),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x11),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_INITVAL2, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0xff),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x0c),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xac),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x98),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x32),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x0f),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xdd),
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23),
-
- /* Rate B */
- QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x06),
-};
-
-static const struct qmp_phy_init_tbl sm8150_ufsphy_tx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03),
- QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01),
- QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x05),
- QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0c),
-};
-
-static const struct qmp_phy_init_tbl sm8150_ufsphy_rx_tbl[] = {
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_LVL, 0x24),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x0f),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_BAND, 0x18),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x0a),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0xf1),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CTRL2, 0x80),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_TERM_BW, 0x1b),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1d),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_MEASURE_TIME, 0x10),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x36),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x36),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xf6),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x3b),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x3d),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xe0),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xc8),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xc8),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x3b),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb1),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xe0),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0xc8),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b),
- QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1),
-
-};
-
-static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs_tbl[] = {
- QMP_PHY_INIT_CFG(QPHY_V4_RX_SIGDET_CTRL2, 0x6d),
- QMP_PHY_INIT_CFG(QPHY_V4_TX_LARGE_AMP_DRV_LVL, 0x0a),
- QMP_PHY_INIT_CFG(QPHY_V4_TX_SMALL_AMP_DRV_LVL, 0x02),
- QMP_PHY_INIT_CFG(QPHY_V4_TX_MID_TERM_CTRL1, 0x43),
- QMP_PHY_INIT_CFG(QPHY_V4_DEBUG_BUS_CLKSEL, 0x1f),
- QMP_PHY_INIT_CFG(QPHY_V4_RX_MIN_HIBERN8_TIME, 0xff),
- QMP_PHY_INIT_CFG(QPHY_V4_MULTI_LANE_CTRL1, 0x02),
-};
-
-/* struct qmp_phy_cfg - per-PHY initialization config */
-struct qmp_phy_cfg {
- /* phy-type - PCIE/UFS/USB */
- unsigned int type;
- /* number of lanes provided by phy */
- int nlanes;
-
- /* Init sequence for PHY blocks - serdes, tx, rx, pcs */
- const struct qmp_phy_init_tbl *serdes_tbl;
- int serdes_tbl_num;
- const struct qmp_phy_init_tbl *tx_tbl;
- int tx_tbl_num;
- const struct qmp_phy_init_tbl *rx_tbl;
- int rx_tbl_num;
- const struct qmp_phy_init_tbl *pcs_tbl;
- int pcs_tbl_num;
-
- /* clock ids to be requested */
- const char * const *clk_list;
- int num_clks;
- /* resets to be requested */
- const char * const *reset_list;
- int num_resets;
- /* regulators to be requested */
- const char * const *vreg_list;
- int num_vregs;
-
- /* array of registers with different offsets */
- const unsigned int *regs;
-
- unsigned int start_ctrl;
- unsigned int pwrdn_ctrl;
- unsigned int mask_com_pcs_ready;
-
- /* true, if PHY has a separate PHY_COM control block */
- bool has_phy_com_ctrl;
- /* true, if PHY has a reset for individual lanes */
- bool has_lane_rst;
- /* true, if PHY needs delay after POWER_DOWN */
- bool has_pwrdn_delay;
- /* power_down delay in usec */
- int pwrdn_delay_min;
- int pwrdn_delay_max;
-
- /* true, if PHY has a separate DP_COM control block */
- bool has_phy_dp_com_ctrl;
- /* true, if PHY has secondary tx/rx lanes to be configured */
- bool is_dual_lane_phy;
-
- /* true, if PCS block has no separate SW_RESET register */
- bool no_pcs_sw_reset;
-};
-
-/**
- * struct qmp_phy - per-lane phy descriptor
- *
- * @phy: generic phy
- * @tx: iomapped memory space for lane's tx
- * @rx: iomapped memory space for lane's rx
- * @pcs: iomapped memory space for lane's pcs
- * @tx2: iomapped memory space for second lane's tx (in dual lane PHYs)
- * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs)
- * @pcs_misc: iomapped memory space for lane's pcs_misc
- * @pipe_clk: pipe lock
- * @index: lane index
- * @qmp: QMP phy to which this lane belongs
- * @lane_rst: lane's reset controller
- */
-struct qmp_phy {
- struct phy *phy;
- void __iomem *tx;
- void __iomem *rx;
- void __iomem *pcs;
- void __iomem *tx2;
- void __iomem *rx2;
- void __iomem *pcs_misc;
- struct clk *pipe_clk;
- unsigned int index;
- struct qcom_qmp *qmp;
- struct reset_control *lane_rst;
-};
-
-/**
- * struct qcom_qmp - structure holding QMP phy block attributes
- *
- * @dev: device
- * @serdes: iomapped memory space for phy's serdes
- * @dp_com: iomapped memory space for phy's dp_com control block
- *
- * @clks: array of clocks required by phy
- * @resets: array of resets required by phy
- * @vregs: regulator supplies bulk data
- *
- * @cfg: phy specific configuration
- * @phys: array of per-lane phy descriptors
- * @phy_mutex: mutex lock for PHY common block initialization
- * @init_count: phy common block initialization count
- * @phy_initialized: indicate if PHY has been initialized
- * @mode: current PHY mode
- * @ufs_reset: optional UFS PHY reset handle
- */
-struct qcom_qmp {
- struct device *dev;
- void __iomem *serdes;
- void __iomem *dp_com;
-
- struct clk_bulk_data *clks;
- struct reset_control **resets;
- struct regulator_bulk_data *vregs;
-
- const struct qmp_phy_cfg *cfg;
- struct qmp_phy **phys;
-
- struct mutex phy_mutex;
- int init_count;
- bool phy_initialized;
- enum phy_mode mode;
-
- struct reset_control *ufs_reset;
-};
-
-static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg |= val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
-static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg &= ~val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
-/* list of clocks required by phy */
-static const char * const msm8996_phy_clk_l[] = {
- "aux", "cfg_ahb", "ref",
-};
-
-static const char * const qmp_v3_phy_clk_l[] = {
- "aux", "cfg_ahb", "ref", "com_aux",
-};
-
-static const char * const sdm845_ufs_phy_clk_l[] = {
- "ref", "ref_aux",
-};
-
-/* list of resets */
-static const char * const msm8996_pciephy_reset_l[] = {
- "phy", "common", "cfg",
-};
-
-static const char * const msm8996_usb3phy_reset_l[] = {
- "phy", "common",
-};
-
-/* list of regulators */
-static const char * const qmp_phy_vreg_l[] = {
- "vdda-phy", "vdda-pll",
-};
-
-static const struct qmp_phy_cfg msm8996_pciephy_cfg = {
- .type = PHY_TYPE_PCIE,
- .nlanes = 3,
-
- .serdes_tbl = msm8996_pcie_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(msm8996_pcie_serdes_tbl),
- .tx_tbl = msm8996_pcie_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(msm8996_pcie_tx_tbl),
- .rx_tbl = msm8996_pcie_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(msm8996_pcie_rx_tbl),
- .pcs_tbl = msm8996_pcie_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(msm8996_pcie_pcs_tbl),
- .clk_list = msm8996_phy_clk_l,
- .num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
- .reset_list = msm8996_pciephy_reset_l,
- .num_resets = ARRAY_SIZE(msm8996_pciephy_reset_l),
- .vreg_list = qmp_phy_vreg_l,
- .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
- .regs = pciephy_regs_layout,
-
- .start_ctrl = PCS_START | PLL_READY_GATE_EN,
- .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
- .mask_com_pcs_ready = PCS_READY,
-
- .has_phy_com_ctrl = true,
- .has_lane_rst = true,
- .has_pwrdn_delay = true,
- .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
- .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
-};
-
-static const struct qmp_phy_cfg msm8996_usb3phy_cfg = {
- .type = PHY_TYPE_USB3,
- .nlanes = 1,
-
- .serdes_tbl = msm8996_usb3_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(msm8996_usb3_serdes_tbl),
- .tx_tbl = msm8996_usb3_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl),
- .rx_tbl = msm8996_usb3_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(msm8996_usb3_rx_tbl),
- .pcs_tbl = msm8996_usb3_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(msm8996_usb3_pcs_tbl),
- .clk_list = msm8996_phy_clk_l,
- .num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
- .reset_list = msm8996_usb3phy_reset_l,
- .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
- .vreg_list = qmp_phy_vreg_l,
- .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
- .regs = usb3phy_regs_layout,
-
- .start_ctrl = SERDES_START | PCS_START,
- .pwrdn_ctrl = SW_PWRDN,
-};
-
-/* list of resets */
-static const char * const ipq8074_pciephy_reset_l[] = {
- "phy", "common",
-};
-
-static const struct qmp_phy_cfg ipq8074_pciephy_cfg = {
- .type = PHY_TYPE_PCIE,
- .nlanes = 1,
-
- .serdes_tbl = ipq8074_pcie_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(ipq8074_pcie_serdes_tbl),
- .tx_tbl = ipq8074_pcie_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(ipq8074_pcie_tx_tbl),
- .rx_tbl = ipq8074_pcie_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(ipq8074_pcie_rx_tbl),
- .pcs_tbl = ipq8074_pcie_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(ipq8074_pcie_pcs_tbl),
- .clk_list = NULL,
- .num_clks = 0,
- .reset_list = ipq8074_pciephy_reset_l,
- .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l),
- .vreg_list = NULL,
- .num_vregs = 0,
- .regs = pciephy_regs_layout,
-
- .start_ctrl = SERDES_START | PCS_START,
- .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
-
- .has_phy_com_ctrl = false,
- .has_lane_rst = false,
- .has_pwrdn_delay = true,
- .pwrdn_delay_min = 995, /* us */
- .pwrdn_delay_max = 1005, /* us */
-};
-
-static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
- .type = PHY_TYPE_USB3,
- .nlanes = 1,
-
- .serdes_tbl = qmp_v3_usb3_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl),
- .tx_tbl = qmp_v3_usb3_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl),
- .rx_tbl = qmp_v3_usb3_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl),
- .pcs_tbl = qmp_v3_usb3_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl),
- .clk_list = qmp_v3_phy_clk_l,
- .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
- .reset_list = msm8996_usb3phy_reset_l,
- .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
- .vreg_list = qmp_phy_vreg_l,
- .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
- .regs = qmp_v3_usb3phy_regs_layout,
-
- .start_ctrl = SERDES_START | PCS_START,
- .pwrdn_ctrl = SW_PWRDN,
-
- .has_pwrdn_delay = true,
- .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
- .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
-
- .has_phy_dp_com_ctrl = true,
- .is_dual_lane_phy = true,
-};
-
-static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = {
- .type = PHY_TYPE_USB3,
- .nlanes = 1,
-
- .serdes_tbl = qmp_v3_usb3_uniphy_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_serdes_tbl),
- .tx_tbl = qmp_v3_usb3_uniphy_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_tx_tbl),
- .rx_tbl = qmp_v3_usb3_uniphy_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_rx_tbl),
- .pcs_tbl = qmp_v3_usb3_uniphy_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_pcs_tbl),
- .clk_list = qmp_v3_phy_clk_l,
- .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
- .reset_list = msm8996_usb3phy_reset_l,
- .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
- .vreg_list = qmp_phy_vreg_l,
- .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
- .regs = qmp_v3_usb3phy_regs_layout,
-
- .start_ctrl = SERDES_START | PCS_START,
- .pwrdn_ctrl = SW_PWRDN,
-
- .has_pwrdn_delay = true,
- .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
- .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
-};
-
-static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
- .type = PHY_TYPE_UFS,
- .nlanes = 2,
-
- .serdes_tbl = sdm845_ufsphy_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(sdm845_ufsphy_serdes_tbl),
- .tx_tbl = sdm845_ufsphy_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_tx_tbl),
- .rx_tbl = sdm845_ufsphy_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_rx_tbl),
- .pcs_tbl = sdm845_ufsphy_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(sdm845_ufsphy_pcs_tbl),
- .clk_list = sdm845_ufs_phy_clk_l,
- .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
- .vreg_list = qmp_phy_vreg_l,
- .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
- .regs = sdm845_ufsphy_regs_layout,
-
- .start_ctrl = SERDES_START,
- .pwrdn_ctrl = SW_PWRDN,
-
- .is_dual_lane_phy = true,
- .no_pcs_sw_reset = true,
-};
-
-static const struct qmp_phy_cfg msm8998_pciephy_cfg = {
- .type = PHY_TYPE_PCIE,
- .nlanes = 1,
-
- .serdes_tbl = msm8998_pcie_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(msm8998_pcie_serdes_tbl),
- .tx_tbl = msm8998_pcie_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(msm8998_pcie_tx_tbl),
- .rx_tbl = msm8998_pcie_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(msm8998_pcie_rx_tbl),
- .pcs_tbl = msm8998_pcie_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(msm8998_pcie_pcs_tbl),
- .clk_list = msm8996_phy_clk_l,
- .num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
- .reset_list = ipq8074_pciephy_reset_l,
- .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l),
- .vreg_list = qmp_phy_vreg_l,
- .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
- .regs = pciephy_regs_layout,
-
- .start_ctrl = SERDES_START | PCS_START,
- .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
-};
-
-static const struct qmp_phy_cfg msm8998_usb3phy_cfg = {
- .type = PHY_TYPE_USB3,
- .nlanes = 1,
-
- .serdes_tbl = msm8998_usb3_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(msm8998_usb3_serdes_tbl),
- .tx_tbl = msm8998_usb3_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(msm8998_usb3_tx_tbl),
- .rx_tbl = msm8998_usb3_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(msm8998_usb3_rx_tbl),
- .pcs_tbl = msm8998_usb3_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(msm8998_usb3_pcs_tbl),
- .clk_list = msm8996_phy_clk_l,
- .num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
- .reset_list = msm8996_usb3phy_reset_l,
- .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
- .vreg_list = qmp_phy_vreg_l,
- .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
- .regs = qmp_v3_usb3phy_regs_layout,
-
- .start_ctrl = SERDES_START | PCS_START,
- .pwrdn_ctrl = SW_PWRDN,
-
- .is_dual_lane_phy = true,
-};
-
-static const struct qmp_phy_cfg sm8150_ufsphy_cfg = {
- .type = PHY_TYPE_UFS,
- .nlanes = 2,
-
- .serdes_tbl = sm8150_ufsphy_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(sm8150_ufsphy_serdes_tbl),
- .tx_tbl = sm8150_ufsphy_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_tx_tbl),
- .rx_tbl = sm8150_ufsphy_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_rx_tbl),
- .pcs_tbl = sm8150_ufsphy_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(sm8150_ufsphy_pcs_tbl),
- .clk_list = sdm845_ufs_phy_clk_l,
- .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
- .vreg_list = qmp_phy_vreg_l,
- .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
- .regs = sm8150_ufsphy_regs_layout,
-
- .start_ctrl = SERDES_START,
- .pwrdn_ctrl = SW_PWRDN,
-
- .is_dual_lane_phy = true,
-};
-
-static void qcom_qmp_phy_configure(void __iomem *base,
- const unsigned int *regs,
- const struct qmp_phy_init_tbl tbl[],
- int num)
-{
- int i;
- const struct qmp_phy_init_tbl *t = tbl;
-
- if (!t)
- return;
-
- for (i = 0; i < num; i++, t++) {
- if (t->in_layout)
- writel(t->val, base + regs[t->offset]);
- else
- writel(t->val, base + t->offset);
- }
-}
-
-static int qcom_qmp_phy_com_init(struct qmp_phy *qphy)
-{
- struct qcom_qmp *qmp = qphy->qmp;
- const struct qmp_phy_cfg *cfg = qmp->cfg;
- void __iomem *serdes = qmp->serdes;
- void __iomem *pcs = qphy->pcs;
- void __iomem *dp_com = qmp->dp_com;
- int ret, i;
-
- mutex_lock(&qmp->phy_mutex);
- if (qmp->init_count++) {
- mutex_unlock(&qmp->phy_mutex);
- return 0;
- }
-
- /* turn on regulator supplies */
- ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
- if (ret) {
- dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
- goto err_reg_enable;
- }
-
- for (i = 0; i < cfg->num_resets; i++) {
- ret = reset_control_assert(qmp->resets[i]);
- if (ret) {
- dev_err(qmp->dev, "%s reset assert failed\n",
- cfg->reset_list[i]);
- goto err_rst_assert;
- }
- }
-
- for (i = cfg->num_resets - 1; i >= 0; i--) {
- ret = reset_control_deassert(qmp->resets[i]);
- if (ret) {
- dev_err(qmp->dev, "%s reset deassert failed\n",
- qmp->cfg->reset_list[i]);
- goto err_rst;
- }
- }
-
- ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
- if (ret) {
- dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret);
- goto err_rst;
- }
-
- if (cfg->has_phy_dp_com_ctrl) {
- qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL,
- SW_PWRDN);
- /* override hardware control for reset of qmp phy */
- qphy_setbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
- SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
- SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
-
- qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL,
- USB3_MODE | DP_MODE);
-
- /* bring both QMP USB and QMP DP PHYs PCS block out of reset */
- qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
- SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
- SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
- }
-
- if (cfg->has_phy_com_ctrl)
- qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
- SW_PWRDN);
- else
- qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
-
- /* Serdes configuration */
- qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl,
- cfg->serdes_tbl_num);
-
- if (cfg->has_phy_com_ctrl) {
- void __iomem *status;
- unsigned int mask, val;
-
- qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET);
- qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
- SERDES_START | PCS_START);
-
- status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS];
- mask = cfg->mask_com_pcs_ready;
-
- ret = readl_poll_timeout(status, val, (val & mask), 10,
- PHY_INIT_COMPLETE_TIMEOUT);
- if (ret) {
- dev_err(qmp->dev,
- "phy common block init timed-out\n");
- goto err_com_init;
- }
- }
-
- mutex_unlock(&qmp->phy_mutex);
-
- return 0;
-
-err_com_init:
- clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
-err_rst:
- while (++i < cfg->num_resets)
- reset_control_assert(qmp->resets[i]);
-err_rst_assert:
- regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
-err_reg_enable:
- mutex_unlock(&qmp->phy_mutex);
-
- return ret;
-}
-
-static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
-{
- const struct qmp_phy_cfg *cfg = qmp->cfg;
- void __iomem *serdes = qmp->serdes;
- int i = cfg->num_resets;
-
- mutex_lock(&qmp->phy_mutex);
- if (--qmp->init_count) {
- mutex_unlock(&qmp->phy_mutex);
- return 0;
- }
-
- reset_control_assert(qmp->ufs_reset);
- if (cfg->has_phy_com_ctrl) {
- qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
- SERDES_START | PCS_START);
- qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET],
- SW_RESET);
- qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
- SW_PWRDN);
- }
-
- while (--i >= 0)
- reset_control_assert(qmp->resets[i]);
-
- clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
-
- regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
-
- mutex_unlock(&qmp->phy_mutex);
-
- return 0;
-}
-
-static int qcom_qmp_phy_enable(struct phy *phy)
-{
- struct qmp_phy *qphy = phy_get_drvdata(phy);
- struct qcom_qmp *qmp = qphy->qmp;
- const struct qmp_phy_cfg *cfg = qmp->cfg;
- void __iomem *tx = qphy->tx;
- void __iomem *rx = qphy->rx;
- void __iomem *pcs = qphy->pcs;
- void __iomem *dp_com = qmp->dp_com;
- void __iomem *status;
- unsigned int mask, val, ready;
- int ret;
-
- dev_vdbg(qmp->dev, "Initializing QMP phy\n");
-
- if (cfg->no_pcs_sw_reset) {
- /*
- * Get UFS reset, which is delayed until now to avoid a
- * circular dependency where UFS needs its PHY, but the PHY
- * needs this UFS reset.
- */
- if (!qmp->ufs_reset) {
- qmp->ufs_reset =
- devm_reset_control_get_exclusive(qmp->dev,
- "ufsphy");
-
- if (IS_ERR(qmp->ufs_reset)) {
- ret = PTR_ERR(qmp->ufs_reset);
- dev_err(qmp->dev,
- "failed to get UFS reset: %d\n",
- ret);
-
- qmp->ufs_reset = NULL;
- return ret;
- }
- }
-
- ret = reset_control_assert(qmp->ufs_reset);
- if (ret)
- goto err_lane_rst;
- }
-
- ret = qcom_qmp_phy_com_init(qphy);
- if (ret)
- return ret;
-
- if (cfg->has_lane_rst) {
- ret = reset_control_deassert(qphy->lane_rst);
- if (ret) {
- dev_err(qmp->dev, "lane%d reset deassert failed\n",
- qphy->index);
- goto err_lane_rst;
- }
- }
-
- ret = clk_prepare_enable(qphy->pipe_clk);
- if (ret) {
- dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret);
- goto err_clk_enable;
- }
-
- /* Tx, Rx, and PCS configurations */
- qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num);
- /* Configuration for other LANE for USB-DP combo PHY */
- if (cfg->is_dual_lane_phy)
- qcom_qmp_phy_configure(qphy->tx2, cfg->regs,
- cfg->tx_tbl, cfg->tx_tbl_num);
-
- qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num);
- if (cfg->is_dual_lane_phy)
- qcom_qmp_phy_configure(qphy->rx2, cfg->regs,
- cfg->rx_tbl, cfg->rx_tbl_num);
-
- qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
- ret = reset_control_deassert(qmp->ufs_reset);
- if (ret)
- goto err_lane_rst;
-
- /*
- * Pull out PHY from POWER DOWN state.
- * This is active low enable signal to power-down PHY.
- */
- if(cfg->type == PHY_TYPE_PCIE)
- qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
-
- if (cfg->has_pwrdn_delay)
- usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
-
- /* Pull PHY out of reset state */
- if (!cfg->no_pcs_sw_reset)
- qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
-
- if (cfg->has_phy_dp_com_ctrl)
- qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
-
- /* start SerDes and Phy-Coding-Sublayer */
- qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
-
- if (cfg->type == PHY_TYPE_UFS) {
- status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
- mask = PCS_READY;
- ready = PCS_READY;
- } else {
- status = pcs + cfg->regs[QPHY_PCS_STATUS];
- mask = PHYSTATUS;
- ready = 0;
- }
-
- ret = readl_poll_timeout(status, val, (val & mask) == ready, 10,
- PHY_INIT_COMPLETE_TIMEOUT);
- if (ret) {
- dev_err(qmp->dev, "phy initialization timed-out\n");
- goto err_pcs_ready;
- }
- qmp->phy_initialized = true;
- return 0;
-
-err_pcs_ready:
- reset_control_assert(qmp->ufs_reset);
- clk_disable_unprepare(qphy->pipe_clk);
-err_clk_enable:
- if (cfg->has_lane_rst)
- reset_control_assert(qphy->lane_rst);
-err_lane_rst:
- qcom_qmp_phy_com_exit(qmp);
-
- return ret;
-}
-
-static int qcom_qmp_phy_disable(struct phy *phy)
-{
- struct qmp_phy *qphy = phy_get_drvdata(phy);
- struct qcom_qmp *qmp = qphy->qmp;
- const struct qmp_phy_cfg *cfg = qmp->cfg;
-
- clk_disable_unprepare(qphy->pipe_clk);
-
- /* PHY reset */
- if (!cfg->no_pcs_sw_reset)
- qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
-
- /* stop SerDes and Phy-Coding-Sublayer */
- qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
-
- /* Put PHY into POWER DOWN state: active low */
- qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
-
- if (cfg->has_lane_rst)
- reset_control_assert(qphy->lane_rst);
-
- qcom_qmp_phy_com_exit(qmp);
-
- qmp->phy_initialized = false;
-
- return 0;
-}
-
-static int qcom_qmp_phy_set_mode(struct phy *phy,
- enum phy_mode mode, int submode)
-{
- struct qmp_phy *qphy = phy_get_drvdata(phy);
- struct qcom_qmp *qmp = qphy->qmp;
-
- qmp->mode = mode;
-
- return 0;
-}
-
-static void qcom_qmp_phy_enable_autonomous_mode(struct qmp_phy *qphy)
-{
- struct qcom_qmp *qmp = qphy->qmp;
- const struct qmp_phy_cfg *cfg = qmp->cfg;
- void __iomem *pcs = qphy->pcs;
- void __iomem *pcs_misc = qphy->pcs_misc;
- u32 intr_mask;
-
- if (qmp->mode == PHY_MODE_USB_HOST_SS ||
- qmp->mode == PHY_MODE_USB_DEVICE_SS)
- intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN;
- else
- intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL;
-
- /* Clear any pending interrupts status */
- qphy_setbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
- /* Writing 1 followed by 0 clears the interrupt */
- qphy_clrbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
-
- qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
- ARCVR_DTCT_EN | ALFPS_DTCT_EN | ARCVR_DTCT_EVENT_SEL);
-
- /* Enable required PHY autonomous mode interrupts */
- qphy_setbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], intr_mask);
-
- /* Enable i/o clamp_n for autonomous mode */
- if (pcs_misc)
- qphy_clrbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN);
-}
-
-static void qcom_qmp_phy_disable_autonomous_mode(struct qmp_phy *qphy)
-{
- struct qcom_qmp *qmp = qphy->qmp;
- const struct qmp_phy_cfg *cfg = qmp->cfg;
- void __iomem *pcs = qphy->pcs;
- void __iomem *pcs_misc = qphy->pcs_misc;
-
- /* Disable i/o clamp_n on resume for normal mode */
- if (pcs_misc)
- qphy_setbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN);
-
- qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
- ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL | ALFPS_DTCT_EN);
-
- qphy_setbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
- /* Writing 1 followed by 0 clears the interrupt */
- qphy_clrbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
-}
-
-static int __maybe_unused qcom_qmp_phy_runtime_suspend(struct device *dev)
-{
- struct qcom_qmp *qmp = dev_get_drvdata(dev);
- struct qmp_phy *qphy = qmp->phys[0];
- const struct qmp_phy_cfg *cfg = qmp->cfg;
-
- dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode);
-
- /* Supported only for USB3 PHY */
- if (cfg->type != PHY_TYPE_USB3)
- return 0;
-
- if (!qmp->phy_initialized) {
- dev_vdbg(dev, "PHY not initialized, bailing out\n");
- return 0;
- }
-
- qcom_qmp_phy_enable_autonomous_mode(qphy);
-
- clk_disable_unprepare(qphy->pipe_clk);
- clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
-
- return 0;
-}
-
-static int __maybe_unused qcom_qmp_phy_runtime_resume(struct device *dev)
-{
- struct qcom_qmp *qmp = dev_get_drvdata(dev);
- struct qmp_phy *qphy = qmp->phys[0];
- const struct qmp_phy_cfg *cfg = qmp->cfg;
- int ret = 0;
-
- dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode);
-
- /* Supported only for USB3 PHY */
- if (cfg->type != PHY_TYPE_USB3)
- return 0;
-
- if (!qmp->phy_initialized) {
- dev_vdbg(dev, "PHY not initialized, bailing out\n");
- return 0;
- }
-
- ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
- if (ret) {
- dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(qphy->pipe_clk);
- if (ret) {
- dev_err(dev, "pipe_clk enable failed, err=%d\n", ret);
- clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
- return ret;
- }
-
- qcom_qmp_phy_disable_autonomous_mode(qphy);
-
- return 0;
-}
-
-static int qcom_qmp_phy_vreg_init(struct device *dev)
-{
- struct qcom_qmp *qmp = dev_get_drvdata(dev);
- int num = qmp->cfg->num_vregs;
- int i;
-
- qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL);
- if (!qmp->vregs)
- return -ENOMEM;
-
- for (i = 0; i < num; i++)
- qmp->vregs[i].supply = qmp->cfg->vreg_list[i];
-
- return devm_regulator_bulk_get(dev, num, qmp->vregs);
-}
-
-static int qcom_qmp_phy_reset_init(struct device *dev)
-{
- struct qcom_qmp *qmp = dev_get_drvdata(dev);
- int i;
-
- qmp->resets = devm_kcalloc(dev, qmp->cfg->num_resets,
- sizeof(*qmp->resets), GFP_KERNEL);
- if (!qmp->resets)
- return -ENOMEM;
-
- for (i = 0; i < qmp->cfg->num_resets; i++) {
- struct reset_control *rst;
- const char *name = qmp->cfg->reset_list[i];
-
- rst = devm_reset_control_get(dev, name);
- if (IS_ERR(rst)) {
- dev_err(dev, "failed to get %s reset\n", name);
- return PTR_ERR(rst);
- }
- qmp->resets[i] = rst;
- }
-
- return 0;
-}
-
-static int qcom_qmp_phy_clk_init(struct device *dev)
-{
- struct qcom_qmp *qmp = dev_get_drvdata(dev);
- int num = qmp->cfg->num_clks;
- int i;
-
- qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
- if (!qmp->clks)
- return -ENOMEM;
-
- for (i = 0; i < num; i++)
- qmp->clks[i].id = qmp->cfg->clk_list[i];
-
- return devm_clk_bulk_get(dev, num, qmp->clks);
-}
-
-static void phy_pipe_clk_release_provider(void *res)
-{
- of_clk_del_provider(res);
-}
-
-/*
- * Register a fixed rate pipe clock.
- *
- * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate
- * controls it. The <s>_pipe_clk coming out of the GCC is requested
- * by the PHY driver for its operations.
- * We register the <s>_pipe_clksrc here. The gcc driver takes care
- * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk.
- * Below picture shows this relationship.
- *
- * +---------------+
- * | PHY block |<<---------------------------------------+
- * | | |
- * | +-------+ | +-----+ |
- * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+
- * clk | +-------+ | +-----+
- * +---------------+
- */
-static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
-{
- struct clk_fixed_rate *fixed;
- struct clk_init_data init = { };
- int ret;
-
- if ((qmp->cfg->type != PHY_TYPE_USB3) &&
- (qmp->cfg->type != PHY_TYPE_PCIE)) {
- /* not all phys register pipe clocks, so return success */
- return 0;
- }
-
- ret = of_property_read_string(np, "clock-output-names", &init.name);
- if (ret) {
- dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np);
- return ret;
- }
-
- fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL);
- if (!fixed)
- return -ENOMEM;
-
- init.ops = &clk_fixed_rate_ops;
-
- /* controllers using QMP phys use 125MHz pipe clock interface */
- fixed->fixed_rate = 125000000;
- fixed->hw.init = &init;
-
- ret = devm_clk_hw_register(qmp->dev, &fixed->hw);
- if (ret)
- return ret;
-
- ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw);
- if (ret)
- return ret;
-
- /*
- * Roll a devm action because the clock provider is the child node, but
- * the child node is not actually a device.
- */
- ret = devm_add_action(qmp->dev, phy_pipe_clk_release_provider, np);
- if (ret)
- phy_pipe_clk_release_provider(np);
-
- return ret;
-}
-
-static const struct phy_ops qcom_qmp_phy_gen_ops = {
- .init = qcom_qmp_phy_enable,
- .exit = qcom_qmp_phy_disable,
- .set_mode = qcom_qmp_phy_set_mode,
- .owner = THIS_MODULE,
-};
-
-static const struct phy_ops qcom_qmp_ufs_ops = {
- .power_on = qcom_qmp_phy_enable,
- .power_off = qcom_qmp_phy_disable,
- .set_mode = qcom_qmp_phy_set_mode,
- .owner = THIS_MODULE,
-};
-
-static
-int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
-{
- struct qcom_qmp *qmp = dev_get_drvdata(dev);
- struct phy *generic_phy;
- struct qmp_phy *qphy;
- const struct phy_ops *ops = &qcom_qmp_phy_gen_ops;
- char prop_name[MAX_PROP_NAME];
- int ret;
-
- qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
- if (!qphy)
- return -ENOMEM;
-
- /*
- * Get memory resources for each phy lane:
- * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
- * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5
- * For single lane PHYs: pcs_misc (optional) -> 3.
- */
- qphy->tx = of_iomap(np, 0);
- if (!qphy->tx)
- return -ENOMEM;
-
- qphy->rx = of_iomap(np, 1);
- if (!qphy->rx)
- return -ENOMEM;
-
- qphy->pcs = of_iomap(np, 2);
- if (!qphy->pcs)
- return -ENOMEM;
-
- /*
- * If this is a dual-lane PHY, then there should be registers for the
- * second lane. Some old device trees did not specify this, so fall
- * back to old legacy behavior of assuming they can be reached at an
- * offset from the first lane.
- */
- if (qmp->cfg->is_dual_lane_phy) {
- qphy->tx2 = of_iomap(np, 3);
- qphy->rx2 = of_iomap(np, 4);
- if (!qphy->tx2 || !qphy->rx2) {
- dev_warn(dev,
- "Underspecified device tree, falling back to legacy register regions\n");
-
- /* In the old version, pcs_misc is at index 3. */
- qphy->pcs_misc = qphy->tx2;
- qphy->tx2 = qphy->tx + QMP_PHY_LEGACY_LANE_STRIDE;
- qphy->rx2 = qphy->rx + QMP_PHY_LEGACY_LANE_STRIDE;
-
- } else {
- qphy->pcs_misc = of_iomap(np, 5);
- }
-
- } else {
- qphy->pcs_misc = of_iomap(np, 3);
- }
-
- if (!qphy->pcs_misc)
- dev_vdbg(dev, "PHY pcs_misc-reg not used\n");
-
- /*
- * Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3
- * based phys, so they essentially have pipe clock. So,
- * we return error in case phy is USB3 or PIPE type.
- * Otherwise, we initialize pipe clock to NULL for
- * all phys that don't need this.
- */
- snprintf(prop_name, sizeof(prop_name), "pipe%d", id);
- qphy->pipe_clk = of_clk_get_by_name(np, prop_name);
- if (IS_ERR(qphy->pipe_clk)) {
- if (qmp->cfg->type == PHY_TYPE_PCIE ||
- qmp->cfg->type == PHY_TYPE_USB3) {
- ret = PTR_ERR(qphy->pipe_clk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev,
- "failed to get lane%d pipe_clk, %d\n",
- id, ret);
- return ret;
- }
- qphy->pipe_clk = NULL;
- }
-
- /* Get lane reset, if any */
- if (qmp->cfg->has_lane_rst) {
- snprintf(prop_name, sizeof(prop_name), "lane%d", id);
- qphy->lane_rst = of_reset_control_get(np, prop_name);
- if (IS_ERR(qphy->lane_rst)) {
- dev_err(dev, "failed to get lane%d reset\n", id);
- return PTR_ERR(qphy->lane_rst);
- }
- }
-
- if (qmp->cfg->type == PHY_TYPE_UFS)
- ops = &qcom_qmp_ufs_ops;
-
- generic_phy = devm_phy_create(dev, np, ops);
- if (IS_ERR(generic_phy)) {
- ret = PTR_ERR(generic_phy);
- dev_err(dev, "failed to create qphy %d\n", ret);
- return ret;
- }
-
- qphy->phy = generic_phy;
- qphy->index = id;
- qphy->qmp = qmp;
- qmp->phys[id] = qphy;
- phy_set_drvdata(generic_phy, qphy);
-
- return 0;
-}
-
-static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
- {
- .compatible = "qcom,msm8996-qmp-pcie-phy",
- .data = &msm8996_pciephy_cfg,
- }, {
- .compatible = "qcom,msm8996-qmp-usb3-phy",
- .data = &msm8996_usb3phy_cfg,
- }, {
- .compatible = "qcom,msm8998-qmp-pcie-phy",
- .data = &msm8998_pciephy_cfg,
- }, {
- .compatible = "qcom,msm8998-qmp-ufs-phy",
- .data = &sdm845_ufsphy_cfg,
- }, {
- .compatible = "qcom,ipq8074-qmp-pcie-phy",
- .data = &ipq8074_pciephy_cfg,
- }, {
- .compatible = "qcom,sdm845-qmp-usb3-phy",
- .data = &qmp_v3_usb3phy_cfg,
- }, {
- .compatible = "qcom,sdm845-qmp-usb3-uni-phy",
- .data = &qmp_v3_usb3_uniphy_cfg,
- }, {
- .compatible = "qcom,sdm845-qmp-ufs-phy",
- .data = &sdm845_ufsphy_cfg,
- }, {
- .compatible = "qcom,msm8998-qmp-usb3-phy",
- .data = &msm8998_usb3phy_cfg,
- }, {
- .compatible = "qcom,sm8150-qmp-ufs-phy",
- .data = &sm8150_ufsphy_cfg,
- },
- { },
-};
-MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table);
-
-static const struct dev_pm_ops qcom_qmp_phy_pm_ops = {
- SET_RUNTIME_PM_OPS(qcom_qmp_phy_runtime_suspend,
- qcom_qmp_phy_runtime_resume, NULL)
-};
-
-static int qcom_qmp_phy_probe(struct platform_device *pdev)
-{
- struct qcom_qmp *qmp;
- struct device *dev = &pdev->dev;
- struct resource *res;
- struct device_node *child;
- struct phy_provider *phy_provider;
- void __iomem *base;
- int num, id;
- int ret;
-
- qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL);
- if (!qmp)
- return -ENOMEM;
-
- qmp->dev = dev;
- dev_set_drvdata(dev, qmp);
-
- /* Get the specific init parameters of QMP phy */
- qmp->cfg = of_device_get_match_data(dev);
- if (!qmp->cfg)
- return -EINVAL;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- /* per PHY serdes; usually located at base address */
- qmp->serdes = base;
-
- /* per PHY dp_com; if PHY has dp_com control block */
- if (qmp->cfg->has_phy_dp_com_ctrl) {
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "dp_com");
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- qmp->dp_com = base;
- }
-
- mutex_init(&qmp->phy_mutex);
-
- ret = qcom_qmp_phy_clk_init(dev);
- if (ret)
- return ret;
-
- ret = qcom_qmp_phy_reset_init(dev);
- if (ret)
- return ret;
-
- ret = qcom_qmp_phy_vreg_init(dev);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get regulator supplies: %d\n",
- ret);
- return ret;
- }
-
- num = of_get_available_child_count(dev->of_node);
- /* do we have a rogue child node ? */
- if (num > qmp->cfg->nlanes)
- return -EINVAL;
-
- qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL);
- if (!qmp->phys)
- return -ENOMEM;
-
- id = 0;
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- /*
- * Prevent runtime pm from being ON by default. Users can enable
- * it using power/control in sysfs.
- */
- pm_runtime_forbid(dev);
-
- for_each_available_child_of_node(dev->of_node, child) {
- /* Create per-lane phy */
- ret = qcom_qmp_phy_create(dev, child, id);
- if (ret) {
- dev_err(dev, "failed to create lane%d phy, %d\n",
- id, ret);
- goto err_node_put;
- }
-
- /*
- * Register the pipe clock provided by phy.
- * See function description to see details of this pipe clock.
- */
- ret = phy_pipe_clk_register(qmp, child);
- if (ret) {
- dev_err(qmp->dev,
- "failed to register pipe clock source\n");
- goto err_node_put;
- }
- id++;
- }
-
- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
- if (!IS_ERR(phy_provider))
- dev_info(dev, "Registered Qcom-QMP phy\n");
- else
- pm_runtime_disable(dev);
-
- return PTR_ERR_OR_ZERO(phy_provider);
-
-err_node_put:
- pm_runtime_disable(dev);
- of_node_put(child);
- return ret;
-}
-
-static struct platform_driver qcom_qmp_phy_driver = {
- .probe = qcom_qmp_phy_probe,
- .driver = {
- .name = "qcom-qmp-phy",
- .pm = &qcom_qmp_phy_pm_ops,
- .of_match_table = qcom_qmp_phy_of_match_table,
- },
-};
-
-module_platform_driver(qcom_qmp_phy_driver);
-
-MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
-MODULE_DESCRIPTION("Qualcomm QMP PHY driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h
index 90f793c2293d..26274e3c0cf9 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp.h
+++ b/drivers/phy/qualcomm/phy-qcom-qmp.h
@@ -6,407 +6,134 @@
#ifndef QCOM_PHY_QMP_H_
#define QCOM_PHY_QMP_H_
-/* Only for QMP V2 PHY - QSERDES COM registers */
-#define QSERDES_COM_BG_TIMER 0x00c
-#define QSERDES_COM_SSC_EN_CENTER 0x010
-#define QSERDES_COM_SSC_ADJ_PER1 0x014
-#define QSERDES_COM_SSC_ADJ_PER2 0x018
-#define QSERDES_COM_SSC_PER1 0x01c
-#define QSERDES_COM_SSC_PER2 0x020
-#define QSERDES_COM_SSC_STEP_SIZE1 0x024
-#define QSERDES_COM_SSC_STEP_SIZE2 0x028
-#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034
-#define QSERDES_COM_CLK_ENABLE1 0x038
-#define QSERDES_COM_SYS_CLK_CTRL 0x03c
-#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040
-#define QSERDES_COM_PLL_IVCO 0x048
-#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c
-#define QSERDES_COM_LOCK_CMP2_MODE0 0x050
-#define QSERDES_COM_LOCK_CMP3_MODE0 0x054
-#define QSERDES_COM_LOCK_CMP1_MODE1 0x058
-#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c
-#define QSERDES_COM_LOCK_CMP3_MODE1 0x060
-#define QSERDES_COM_BG_TRIM 0x070
-#define QSERDES_COM_CLK_EP_DIV 0x074
-#define QSERDES_COM_CP_CTRL_MODE0 0x078
-#define QSERDES_COM_CP_CTRL_MODE1 0x07c
-#define QSERDES_COM_PLL_RCTRL_MODE0 0x084
-#define QSERDES_COM_PLL_RCTRL_MODE1 0x088
-#define QSERDES_COM_PLL_CCTRL_MODE0 0x090
-#define QSERDES_COM_PLL_CCTRL_MODE1 0x094
-#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x0a8
-#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac
-#define QSERDES_COM_RESETSM_CNTRL 0x0b4
-#define QSERDES_COM_RESTRIM_CTRL 0x0bc
-#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4
-#define QSERDES_COM_LOCK_CMP_EN 0x0c8
-#define QSERDES_COM_LOCK_CMP_CFG 0x0cc
-#define QSERDES_COM_DEC_START_MODE0 0x0d0
-#define QSERDES_COM_DEC_START_MODE1 0x0d4
-#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc
-#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0
-#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4
-#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8
-#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec
-#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0
-#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108
-#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c
-#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110
-#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114
-#define QSERDES_COM_VCO_TUNE_CTRL 0x124
-#define QSERDES_COM_VCO_TUNE_MAP 0x128
-#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c
-#define QSERDES_COM_VCO_TUNE2_MODE0 0x130
-#define QSERDES_COM_VCO_TUNE1_MODE1 0x134
-#define QSERDES_COM_VCO_TUNE2_MODE1 0x138
-#define QSERDES_COM_VCO_TUNE_TIMER1 0x144
-#define QSERDES_COM_VCO_TUNE_TIMER2 0x148
-#define QSERDES_COM_BG_CTRL 0x170
-#define QSERDES_COM_CLK_SELECT 0x174
-#define QSERDES_COM_HSCLK_SEL 0x178
-#define QSERDES_COM_CORECLK_DIV 0x184
-#define QSERDES_COM_CORE_CLK_EN 0x18c
-#define QSERDES_COM_C_READY_STATUS 0x190
-#define QSERDES_COM_CMN_CONFIG 0x194
-#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c
-#define QSERDES_COM_DEBUG_BUS0 0x1a0
-#define QSERDES_COM_DEBUG_BUS1 0x1a4
-#define QSERDES_COM_DEBUG_BUS2 0x1a8
-#define QSERDES_COM_DEBUG_BUS3 0x1ac
-#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0
-#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc
+#include "phy-qcom-qmp-qserdes-com.h"
+#include "phy-qcom-qmp-qserdes-txrx.h"
-/* Only for QMP V2 PHY - TX registers */
-#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054
-#define QSERDES_TX_DEBUG_BUS_SEL 0x064
-#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068
-#define QSERDES_TX_LANE_MODE 0x094
-#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac
+#include "phy-qcom-qmp-qserdes-com-v3.h"
+#include "phy-qcom-qmp-qserdes-txrx-v3.h"
-/* Only for QMP V2 PHY - RX registers */
-#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010
-#define QSERDES_RX_UCDR_SO_GAIN 0x01c
-#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040
-#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048
-#define QSERDES_RX_RX_TERM_BW 0x090
-#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4
-#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8
-#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc
-#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0
-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8
-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc
-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0
-#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108
-#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c
-#define QSERDES_RX_SIGDET_ENABLES 0x110
-#define QSERDES_RX_SIGDET_CNTRL 0x114
-#define QSERDES_RX_SIGDET_LVL 0x118
-#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c
-#define QSERDES_RX_RX_BAND 0x120
-#define QSERDES_RX_RX_INTERFACE_MODE 0x12c
+#include "phy-qcom-qmp-qserdes-com-v4.h"
+#include "phy-qcom-qmp-qserdes-txrx-v4.h"
+#include "phy-qcom-qmp-qserdes-txrx-v4_20.h"
-/* Only for QMP V2 PHY - PCS registers */
-#define QPHY_POWER_DOWN_CONTROL 0x04
-#define QPHY_TXDEEMPH_M6DB_V0 0x24
-#define QPHY_TXDEEMPH_M3P5DB_V0 0x28
-#define QPHY_ENDPOINT_REFCLK_DRIVE 0x54
-#define QPHY_RX_IDLE_DTCT_CNTRL 0x58
-#define QPHY_POWER_STATE_CONFIG1 0x60
-#define QPHY_POWER_STATE_CONFIG2 0x64
-#define QPHY_POWER_STATE_CONFIG4 0x6c
-#define QPHY_LOCK_DETECT_CONFIG1 0x80
-#define QPHY_LOCK_DETECT_CONFIG2 0x84
-#define QPHY_LOCK_DETECT_CONFIG3 0x88
-#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK 0xa0
-#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK 0xa4
-#define QPHY_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB 0x1A8
-#define QPHY_OSC_DTCT_ACTIONS 0x1AC
-#define QPHY_RX_SIGDET_LVL 0x1D8
-#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1DC
-#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1E0
+#include "phy-qcom-qmp-qserdes-com-v5.h"
+#include "phy-qcom-qmp-qserdes-txrx-v5.h"
+#include "phy-qcom-qmp-qserdes-txrx-v5_20.h"
+#include "phy-qcom-qmp-qserdes-txrx-v5_5nm.h"
-/* Only for QMP V3 PHY - DP COM registers */
-#define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00
-#define QPHY_V3_DP_COM_SW_RESET 0x04
-#define QPHY_V3_DP_COM_POWER_DOWN_CTRL 0x08
-#define QPHY_V3_DP_COM_SWI_CTRL 0x0c
-#define QPHY_V3_DP_COM_TYPEC_CTRL 0x10
-#define QPHY_V3_DP_COM_TYPEC_PWRDN_CTRL 0x14
-#define QPHY_V3_DP_COM_RESET_OVRD_CTRL 0x1c
+#include "phy-qcom-qmp-qserdes-pll.h"
-/* Only for QMP V3 PHY - QSERDES COM registers */
-#define QSERDES_V3_COM_BG_TIMER 0x00c
-#define QSERDES_V3_COM_SSC_EN_CENTER 0x010
-#define QSERDES_V3_COM_SSC_ADJ_PER1 0x014
-#define QSERDES_V3_COM_SSC_ADJ_PER2 0x018
-#define QSERDES_V3_COM_SSC_PER1 0x01c
-#define QSERDES_V3_COM_SSC_PER2 0x020
-#define QSERDES_V3_COM_SSC_STEP_SIZE1 0x024
-#define QSERDES_V3_COM_SSC_STEP_SIZE2 0x028
-#define QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN 0x034
-#define QSERDES_V3_COM_CLK_ENABLE1 0x038
-#define QSERDES_V3_COM_SYS_CLK_CTRL 0x03c
-#define QSERDES_V3_COM_SYSCLK_BUF_ENABLE 0x040
-#define QSERDES_V3_COM_PLL_IVCO 0x048
-#define QSERDES_V3_COM_LOCK_CMP1_MODE0 0x098
-#define QSERDES_V3_COM_LOCK_CMP2_MODE0 0x09c
-#define QSERDES_V3_COM_LOCK_CMP3_MODE0 0x0a0
-#define QSERDES_V3_COM_LOCK_CMP1_MODE1 0x0a4
-#define QSERDES_V3_COM_LOCK_CMP2_MODE1 0x0a8
-#define QSERDES_V3_COM_LOCK_CMP3_MODE1 0x0ac
-#define QSERDES_V3_COM_CLK_EP_DIV 0x05c
-#define QSERDES_V3_COM_CP_CTRL_MODE0 0x060
-#define QSERDES_V3_COM_CP_CTRL_MODE1 0x064
-#define QSERDES_V3_COM_PLL_RCTRL_MODE0 0x068
-#define QSERDES_V3_COM_PLL_RCTRL_MODE1 0x06c
-#define QSERDES_V3_COM_PLL_CCTRL_MODE0 0x070
-#define QSERDES_V3_COM_PLL_CCTRL_MODE1 0x074
-#define QSERDES_V3_COM_SYSCLK_EN_SEL 0x080
-#define QSERDES_V3_COM_RESETSM_CNTRL 0x088
-#define QSERDES_V3_COM_RESETSM_CNTRL2 0x08c
-#define QSERDES_V3_COM_LOCK_CMP_EN 0x090
-#define QSERDES_V3_COM_LOCK_CMP_CFG 0x094
-#define QSERDES_V3_COM_DEC_START_MODE0 0x0b0
-#define QSERDES_V3_COM_DEC_START_MODE1 0x0b4
-#define QSERDES_V3_COM_DIV_FRAC_START1_MODE0 0x0b8
-#define QSERDES_V3_COM_DIV_FRAC_START2_MODE0 0x0bc
-#define QSERDES_V3_COM_DIV_FRAC_START3_MODE0 0x0c0
-#define QSERDES_V3_COM_DIV_FRAC_START1_MODE1 0x0c4
-#define QSERDES_V3_COM_DIV_FRAC_START2_MODE1 0x0c8
-#define QSERDES_V3_COM_DIV_FRAC_START3_MODE1 0x0cc
-#define QSERDES_V3_COM_INTEGLOOP_INITVAL 0x0d0
-#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0 0x0d8
-#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0 0x0dc
-#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1 0x0e0
-#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1 0x0e4
-#define QSERDES_V3_COM_VCO_TUNE_CTRL 0x0ec
-#define QSERDES_V3_COM_VCO_TUNE_MAP 0x0f0
-#define QSERDES_V3_COM_VCO_TUNE1_MODE0 0x0f4
-#define QSERDES_V3_COM_VCO_TUNE2_MODE0 0x0f8
-#define QSERDES_V3_COM_VCO_TUNE1_MODE1 0x0fc
-#define QSERDES_V3_COM_VCO_TUNE2_MODE1 0x100
-#define QSERDES_V3_COM_VCO_TUNE_INITVAL1 0x104
-#define QSERDES_V3_COM_VCO_TUNE_INITVAL2 0x108
-#define QSERDES_V3_COM_VCO_TUNE_TIMER1 0x11c
-#define QSERDES_V3_COM_VCO_TUNE_TIMER2 0x120
-#define QSERDES_V3_COM_CLK_SELECT 0x138
-#define QSERDES_V3_COM_HSCLK_SEL 0x13c
-#define QSERDES_V3_COM_CORECLK_DIV_MODE0 0x148
-#define QSERDES_V3_COM_CORECLK_DIV_MODE1 0x14c
-#define QSERDES_V3_COM_CORE_CLK_EN 0x154
-#define QSERDES_V3_COM_C_READY_STATUS 0x158
-#define QSERDES_V3_COM_CMN_CONFIG 0x15c
-#define QSERDES_V3_COM_SVS_MODE_CLK_SEL 0x164
-#define QSERDES_V3_COM_DEBUG_BUS0 0x168
-#define QSERDES_V3_COM_DEBUG_BUS1 0x16c
-#define QSERDES_V3_COM_DEBUG_BUS2 0x170
-#define QSERDES_V3_COM_DEBUG_BUS3 0x174
-#define QSERDES_V3_COM_DEBUG_BUS_SEL 0x178
-#define QSERDES_V3_COM_CMN_MODE 0x184
+#include "phy-qcom-qmp-pcs-v2.h"
-/* Only for QMP V3 PHY - TX registers */
-#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX 0x044
-#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX 0x048
-#define QSERDES_V3_TX_DEBUG_BUS_SEL 0x058
-#define QSERDES_V3_TX_HIGHZ_DRVR_EN 0x060
-#define QSERDES_V3_TX_LANE_MODE_1 0x08c
-#define QSERDES_V3_TX_RCV_DETECT_LVL_2 0x0a4
+#include "phy-qcom-qmp-pcs-v3.h"
+#include "phy-qcom-qmp-pcs-misc-v3.h"
+#include "phy-qcom-qmp-pcs-ufs-v3.h"
-/* Only for QMP V3 PHY - RX registers */
-#define QSERDES_V3_RX_UCDR_FO_GAIN 0x008
-#define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c
-#define QSERDES_V3_RX_UCDR_SO_GAIN 0x014
-#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF 0x024
-#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER 0x028
-#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN 0x02c
-#define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030
-#define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034
-#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c
-#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040
-#define QSERDES_V3_RX_UCDR_PI_CONTROLS 0x044
-#define QSERDES_V3_RX_RX_TERM_BW 0x07c
-#define QSERDES_V3_RX_VGA_CAL_CNTRL1 0x0bc
-#define QSERDES_V3_RX_VGA_CAL_CNTRL2 0x0c0
-#define QSERDES_V3_RX_RX_EQ_GAIN2_LSB 0x0c8
-#define QSERDES_V3_RX_RX_EQ_GAIN2_MSB 0x0cc
-#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d4
-#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3 0x0d8
-#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4 0x0dc
-#define QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x0f8
-#define QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x0fc
-#define QSERDES_V3_RX_SIGDET_ENABLES 0x100
-#define QSERDES_V3_RX_SIGDET_CNTRL 0x104
-#define QSERDES_V3_RX_SIGDET_LVL 0x108
-#define QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL 0x10c
-#define QSERDES_V3_RX_RX_BAND 0x110
-#define QSERDES_V3_RX_RX_INTERFACE_MODE 0x11c
-#define QSERDES_V3_RX_RX_MODE_00 0x164
-#define QSERDES_V3_RX_RX_MODE_01 0x168
+#include "phy-qcom-qmp-pcs-v4.h"
+#include "phy-qcom-qmp-pcs-pcie-v4.h"
+#include "phy-qcom-qmp-pcs-usb-v4.h"
+#include "phy-qcom-qmp-pcs-ufs-v4.h"
-/* Only for QMP V3 PHY - PCS registers */
-#define QPHY_V3_PCS_POWER_DOWN_CONTROL 0x004
-#define QPHY_V3_PCS_TXMGN_V0 0x00c
-#define QPHY_V3_PCS_TXMGN_V1 0x010
-#define QPHY_V3_PCS_TXMGN_V2 0x014
-#define QPHY_V3_PCS_TXMGN_V3 0x018
-#define QPHY_V3_PCS_TXMGN_V4 0x01c
-#define QPHY_V3_PCS_TXMGN_LS 0x020
-#define QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL 0x02c
-#define QPHY_V3_PCS_TX_SMALL_AMP_DRV_LVL 0x034
-#define QPHY_V3_PCS_TXDEEMPH_M6DB_V0 0x024
-#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0 0x028
-#define QPHY_V3_PCS_TXDEEMPH_M6DB_V1 0x02c
-#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1 0x030
-#define QPHY_V3_PCS_TXDEEMPH_M6DB_V2 0x034
-#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2 0x038
-#define QPHY_V3_PCS_TXDEEMPH_M6DB_V3 0x03c
-#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3 0x040
-#define QPHY_V3_PCS_TXDEEMPH_M6DB_V4 0x044
-#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4 0x048
-#define QPHY_V3_PCS_TXDEEMPH_M6DB_LS 0x04c
-#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS 0x050
-#define QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE 0x054
-#define QPHY_V3_PCS_RX_IDLE_DTCT_CNTRL 0x058
-#define QPHY_V3_PCS_RATE_SLEW_CNTRL 0x05c
-#define QPHY_V3_PCS_POWER_STATE_CONFIG1 0x060
-#define QPHY_V3_PCS_POWER_STATE_CONFIG2 0x064
-#define QPHY_V3_PCS_POWER_STATE_CONFIG4 0x06c
-#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L 0x070
-#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H 0x074
-#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L 0x078
-#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H 0x07c
-#define QPHY_V3_PCS_LOCK_DETECT_CONFIG1 0x080
-#define QPHY_V3_PCS_LOCK_DETECT_CONFIG2 0x084
-#define QPHY_V3_PCS_LOCK_DETECT_CONFIG3 0x088
-#define QPHY_V3_PCS_TSYNC_RSYNC_TIME 0x08c
-#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x0a0
-#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK 0x0a4
-#define QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME 0x0a8
-#define QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK 0x0b0
-#define QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME 0x0b8
-#define QPHY_V3_PCS_RXEQTRAINING_RUN_TIME 0x0bc
-#define QPHY_V3_PCS_FLL_CNTRL1 0x0c4
-#define QPHY_V3_PCS_FLL_CNTRL2 0x0c8
-#define QPHY_V3_PCS_FLL_CNT_VAL_L 0x0cc
-#define QPHY_V3_PCS_FLL_CNT_VAL_H_TOL 0x0d0
-#define QPHY_V3_PCS_FLL_MAN_CODE 0x0d4
-#define QPHY_V3_PCS_RX_SYM_RESYNC_CTRL 0x134
-#define QPHY_V3_PCS_RX_MIN_HIBERN8_TIME 0x138
-#define QPHY_V3_PCS_RX_SIGDET_CTRL1 0x13c
-#define QPHY_V3_PCS_RX_SIGDET_CTRL2 0x140
-#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1a8
-#define QPHY_V3_PCS_OSC_DTCT_ACTIONS 0x1ac
-#define QPHY_V3_PCS_SIGDET_CNTRL 0x1b0
-#define QPHY_V3_PCS_TX_MID_TERM_CTRL1 0x1bc
-#define QPHY_V3_PCS_MULTI_LANE_CTRL1 0x1c4
-#define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8
-#define QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1dc
-#define QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1e0
-#define QPHY_V3_PCS_REFGEN_REQ_CONFIG1 0x20c
-#define QPHY_V3_PCS_REFGEN_REQ_CONFIG2 0x210
+#include "phy-qcom-qmp-pcs-v4_20.h"
+#include "phy-qcom-qmp-pcs-pcie-v4_20.h"
-/* Only for QMP V3 PHY - PCS_MISC registers */
-#define QPHY_V3_PCS_MISC_CLAMP_ENABLE 0x0c
-#define QPHY_V3_PCS_MISC_OSC_DTCT_CONFIG2 0x2c
-#define QPHY_V3_PCS_MISC_PCIE_INT_AUX_CLK_CONFIG1 0x44
-#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG2 0x54
-#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4 0x5c
-#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5 0x60
+#include "phy-qcom-qmp-pcs-v5.h"
+#include "phy-qcom-qmp-pcs-pcie-v5.h"
+#include "phy-qcom-qmp-pcs-usb-v5.h"
+#include "phy-qcom-qmp-pcs-ufs-v5.h"
-/* Only for QMP V4 PHY - QSERDES COM registers */
-#define QSERDES_V4_COM_PLL_IVCO 0x058
-#define QSERDES_V4_COM_CMN_IPTRIM 0x060
-#define QSERDES_V4_COM_CP_CTRL_MODE0 0x074
-#define QSERDES_V4_COM_CP_CTRL_MODE1 0x078
-#define QSERDES_V4_COM_PLL_RCTRL_MODE0 0x07c
-#define QSERDES_V4_COM_PLL_RCTRL_MODE1 0x080
-#define QSERDES_V4_COM_PLL_CCTRL_MODE0 0x084
-#define QSERDES_V4_COM_PLL_CCTRL_MODE1 0x088
-#define QSERDES_V4_COM_SYSCLK_EN_SEL 0x094
-#define QSERDES_V4_COM_LOCK_CMP_EN 0x0a4
-#define QSERDES_V4_COM_LOCK_CMP1_MODE0 0x0ac
-#define QSERDES_V4_COM_LOCK_CMP2_MODE0 0x0b0
-#define QSERDES_V4_COM_LOCK_CMP1_MODE1 0x0b4
-#define QSERDES_V4_COM_DEC_START_MODE0 0x0bc
-#define QSERDES_V4_COM_LOCK_CMP2_MODE1 0x0b8
-#define QSERDES_V4_COM_DEC_START_MODE1 0x0c4
-#define QSERDES_V4_COM_VCO_TUNE_MAP 0x10c
-#define QSERDES_V4_COM_VCO_TUNE_INITVAL2 0x124
-#define QSERDES_V4_COM_HSCLK_SEL 0x158
-#define QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL 0x15c
-#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x1ac
-#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1b0
-#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x1b4
-#define QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL 0x1bc
-#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x1b8
+#include "phy-qcom-qmp-pcs-pcie-v5_20.h"
-/* Only for QMP V4 PHY - TX registers */
-#define QSERDES_V4_TX_LANE_MODE_1 0x84
-#define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0xd8
-#define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0xdC
-#define QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0xe0
-#define QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0xe4
-#define QSERDES_V4_TX_TRAN_DRVR_EMP_EN 0xb8
+#include "phy-qcom-qmp-pcie-qhp.h"
-/* Only for QMP V4 PHY - RX registers */
-#define QSERDES_V4_RX_UCDR_FO_GAIN 0x008
-#define QSERDES_V4_RX_UCDR_SO_GAIN 0x014
-#define QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN 0x030
-#define QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034
-#define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c
-#define QSERDES_V4_RX_UCDR_PI_CONTROLS 0x044
-#define QSERDES_V4_RX_UCDR_PI_CTRL2 0x048
-#define QSERDES_V4_RX_AC_JTAG_ENABLE 0x068
-#define QSERDES_V4_RX_AC_JTAG_MODE 0x078
-#define QSERDES_V4_RX_RX_TERM_BW 0x080
-#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2 0x0ec
-#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3 0x0f0
-#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4 0x0f4
-#define QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW 0x0f8
-#define QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH 0x0fc
-#define QSERDES_V4_RX_RX_IDAC_MEASURE_TIME 0x100
-#define QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x114
-#define QSERDES_V4_RX_SIGDET_CNTRL 0x11c
-#define QSERDES_V4_RX_SIGDET_LVL 0x120
-#define QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL 0x124
-#define QSERDES_V4_RX_RX_BAND 0x128
-#define QSERDES_V4_RX_RX_MODE_00_LOW 0x170
-#define QSERDES_V4_RX_RX_MODE_00_HIGH 0x174
-#define QSERDES_V4_RX_RX_MODE_00_HIGH2 0x178
-#define QSERDES_V4_RX_RX_MODE_00_HIGH3 0x17c
-#define QSERDES_V4_RX_RX_MODE_00_HIGH4 0x180
-#define QSERDES_V4_RX_RX_MODE_01_LOW 0x184
-#define QSERDES_V4_RX_RX_MODE_01_HIGH 0x188
-#define QSERDES_V4_RX_RX_MODE_01_HIGH2 0x18c
-#define QSERDES_V4_RX_RX_MODE_01_HIGH3 0x190
-#define QSERDES_V4_RX_RX_MODE_01_HIGH4 0x194
-#define QSERDES_V4_RX_RX_MODE_10_LOW 0x198
-#define QSERDES_V4_RX_RX_MODE_10_HIGH 0x19c
-#define QSERDES_V4_RX_RX_MODE_10_HIGH2 0x1a0
-#define QSERDES_V4_RX_RX_MODE_10_HIGH3 0x1a4
-#define QSERDES_V4_RX_RX_MODE_10_HIGH4 0x1a8
-#define QSERDES_V4_RX_DCC_CTRL1 0x1bc
+/* Only for QMP V3 & V4 PHY - DP COM registers */
+#define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00
+#define QPHY_V3_DP_COM_SW_RESET 0x04
+#define QPHY_V3_DP_COM_POWER_DOWN_CTRL 0x08
+#define QPHY_V3_DP_COM_SWI_CTRL 0x0c
+#define QPHY_V3_DP_COM_TYPEC_CTRL 0x10
+#define QPHY_V3_DP_COM_TYPEC_PWRDN_CTRL 0x14
+#define QPHY_V3_DP_COM_RESET_OVRD_CTRL 0x1c
-/* Only for QMP V4 PHY - PCS registers */
-#define QPHY_V4_PHY_START 0x000
-#define QPHY_V4_POWER_DOWN_CONTROL 0x004
-#define QPHY_V4_SW_RESET 0x008
-#define QPHY_V4_TIMER_20US_CORECLK_STEPS_MSB 0x00c
-#define QPHY_V4_TIMER_20US_CORECLK_STEPS_LSB 0x010
-#define QPHY_V4_PLL_CNTL 0x02c
-#define QPHY_V4_TX_LARGE_AMP_DRV_LVL 0x030
-#define QPHY_V4_TX_SMALL_AMP_DRV_LVL 0x038
-#define QPHY_V4_BIST_FIXED_PAT_CTRL 0x060
-#define QPHY_V4_TX_HSGEAR_CAPABILITY 0x074
-#define QPHY_V4_RX_HSGEAR_CAPABILITY 0x0b4
-#define QPHY_V4_DEBUG_BUS_CLKSEL 0x124
-#define QPHY_V4_LINECFG_DISABLE 0x148
-#define QPHY_V4_RX_MIN_HIBERN8_TIME 0x150
-#define QPHY_V4_RX_SIGDET_CTRL2 0x158
-#define QPHY_V4_TX_PWM_GEAR_BAND 0x160
-#define QPHY_V4_TX_HS_GEAR_BAND 0x168
-#define QPHY_V4_PCS_READY_STATUS 0x180
-#define QPHY_V4_TX_MID_TERM_CTRL1 0x1d8
-#define QPHY_V4_MULTI_LANE_CTRL1 0x1e0
+/* QSERDES V3 COM bits */
+# define QSERDES_V3_COM_BIAS_EN 0x0001
+# define QSERDES_V3_COM_BIAS_EN_MUX 0x0002
+# define QSERDES_V3_COM_CLKBUF_R_EN 0x0004
+# define QSERDES_V3_COM_CLKBUF_L_EN 0x0008
+# define QSERDES_V3_COM_EN_SYSCLK_TX_SEL 0x0010
+# define QSERDES_V3_COM_CLKBUF_RX_DRIVE_L 0x0020
+# define QSERDES_V3_COM_CLKBUF_RX_DRIVE_R 0x0040
+
+/* QSERDES V3 TX bits */
+# define DP_PHY_TXn_TX_EMP_POST1_LVL_MASK 0x001f
+# define DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN 0x0020
+# define DP_PHY_TXn_TX_DRV_LVL_MASK 0x001f
+# define DP_PHY_TXn_TX_DRV_LVL_MUX_EN 0x0020
+
+/* QMP PHY - DP PHY registers */
+#define QSERDES_DP_PHY_REVISION_ID0 0x000
+#define QSERDES_DP_PHY_REVISION_ID1 0x004
+#define QSERDES_DP_PHY_REVISION_ID2 0x008
+#define QSERDES_DP_PHY_REVISION_ID3 0x00c
+#define QSERDES_DP_PHY_CFG 0x010
+#define QSERDES_DP_PHY_PD_CTL 0x018
+# define DP_PHY_PD_CTL_PWRDN 0x001
+# define DP_PHY_PD_CTL_PSR_PWRDN 0x002
+# define DP_PHY_PD_CTL_AUX_PWRDN 0x004
+# define DP_PHY_PD_CTL_LANE_0_1_PWRDN 0x008
+# define DP_PHY_PD_CTL_LANE_2_3_PWRDN 0x010
+# define DP_PHY_PD_CTL_PLL_PWRDN 0x020
+# define DP_PHY_PD_CTL_DP_CLAMP_EN 0x040
+#define QSERDES_DP_PHY_MODE 0x01c
+#define QSERDES_DP_PHY_AUX_CFG0 0x020
+#define QSERDES_DP_PHY_AUX_CFG1 0x024
+#define QSERDES_DP_PHY_AUX_CFG2 0x028
+#define QSERDES_DP_PHY_AUX_CFG3 0x02c
+#define QSERDES_DP_PHY_AUX_CFG4 0x030
+#define QSERDES_DP_PHY_AUX_CFG5 0x034
+#define QSERDES_DP_PHY_AUX_CFG6 0x038
+#define QSERDES_DP_PHY_AUX_CFG7 0x03c
+#define QSERDES_DP_PHY_AUX_CFG8 0x040
+#define QSERDES_DP_PHY_AUX_CFG9 0x044
+
+/* Only for QMP V3 PHY - DP PHY registers */
+#define QSERDES_V3_DP_PHY_AUX_INTERRUPT_MASK 0x048
+# define PHY_AUX_STOP_ERR_MASK 0x01
+# define PHY_AUX_DEC_ERR_MASK 0x02
+# define PHY_AUX_SYNC_ERR_MASK 0x04
+# define PHY_AUX_ALIGN_ERR_MASK 0x08
+# define PHY_AUX_REQ_ERR_MASK 0x10
+
+#define QSERDES_V3_DP_PHY_AUX_INTERRUPT_CLEAR 0x04c
+#define QSERDES_V3_DP_PHY_AUX_BIST_CFG 0x050
+
+#define QSERDES_V3_DP_PHY_VCO_DIV 0x064
+#define QSERDES_V3_DP_PHY_TX0_TX1_LANE_CTL 0x06c
+#define QSERDES_V3_DP_PHY_TX2_TX3_LANE_CTL 0x088
+
+#define QSERDES_V3_DP_PHY_SPARE0 0x0ac
+#define DP_PHY_SPARE0_MASK 0x0f
+#define DP_PHY_SPARE0_ORIENTATION_INFO_SHIFT 0x04(0x0004)
+
+#define QSERDES_V3_DP_PHY_STATUS 0x0c0
+
+/* Only for QMP V4 PHY - DP PHY registers */
+#define QSERDES_V4_DP_PHY_CFG_1 0x014
+#define QSERDES_V4_DP_PHY_AUX_INTERRUPT_MASK 0x054
+#define QSERDES_V4_DP_PHY_AUX_INTERRUPT_CLEAR 0x058
+#define QSERDES_V4_DP_PHY_VCO_DIV 0x070
+#define QSERDES_V4_DP_PHY_TX0_TX1_LANE_CTL 0x078
+#define QSERDES_V4_DP_PHY_TX2_TX3_LANE_CTL 0x09c
+#define QSERDES_V4_DP_PHY_SPARE0 0x0c8
+#define QSERDES_V4_DP_PHY_AUX_INTERRUPT_STATUS 0x0d8
+#define QSERDES_V4_DP_PHY_STATUS 0x0dc
+
+/* Only for QMP V4 PHY - PCS_MISC registers */
+#define QPHY_V4_PCS_MISC_TYPEC_CTRL 0x00
+#define QPHY_V4_PCS_MISC_TYPEC_PWRDN_CTRL 0x04
+#define QPHY_V4_PCS_MISC_PCS_MISC_CONFIG1 0x08
+#define QPHY_V4_PCS_MISC_CLAMP_ENABLE 0x0c
+#define QPHY_V4_PCS_MISC_TYPEC_STATUS 0x10
+#define QPHY_V4_PCS_MISC_PLACEHOLDER_STATUS 0x14
#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c
index bf94a52d3087..2ef638b32e8f 100644
--- a/drivers/phy/qualcomm/phy-qcom-qusb2.c
+++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017, 2019, The Linux Foundation. All rights reserved.
*/
#include <linux/clk.h>
@@ -22,6 +22,7 @@
#include <dt-bindings/phy/phy-qcom-qusb2.h>
+#define QUSB2PHY_PLL 0x0
#define QUSB2PHY_PLL_TEST 0x04
#define CLK_REF_SEL BIT(7)
@@ -66,6 +67,14 @@
#define IMP_RES_OFFSET_MASK GENMASK(5, 0)
#define IMP_RES_OFFSET_SHIFT 0x0
+/* QUSB2PHY_PLL_BIAS_CONTROL_2 register bits */
+#define BIAS_CTRL2_RES_OFFSET_MASK GENMASK(5, 0)
+#define BIAS_CTRL2_RES_OFFSET_SHIFT 0x0
+
+/* QUSB2PHY_CHG_CONTROL_2 register bits */
+#define CHG_CTRL2_OFFSET_MASK GENMASK(5, 4)
+#define CHG_CTRL2_OFFSET_SHIFT 0x4
+
/* QUSB2PHY_PORT_TUNE1 register bits */
#define HSTX_TRIM_MASK GENMASK(7, 4)
#define HSTX_TRIM_SHIFT 0x4
@@ -73,6 +82,10 @@
#define PREEMPHASIS_EN_MASK GENMASK(1, 0)
#define PREEMPHASIS_EN_SHIFT 0x0
+/* QUSB2PHY_PORT_TUNE2 register bits */
+#define HSDISC_TRIM_MASK GENMASK(1, 0)
+#define HSDISC_TRIM_SHIFT 0x0
+
#define QUSB2PHY_PLL_ANALOG_CONTROLS_TWO 0x04
#define QUSB2PHY_PLL_CLOCK_INVERTERS 0x18c
#define QUSB2PHY_PLL_CMODE 0x2c
@@ -123,6 +136,35 @@ enum qusb2phy_reg_layout {
QUSB2PHY_INTR_CTRL,
};
+static const struct qusb2_phy_init_tbl ipq6018_init_tbl[] = {
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL, 0x14),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xF8),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0xB3),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x83),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0xC0),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE5, 0x00),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TEST, 0x80),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9F),
+};
+
+static const unsigned int ipq6018_regs_layout[] = {
+ [QUSB2PHY_PLL_STATUS] = 0x38,
+ [QUSB2PHY_PORT_TUNE1] = 0x80,
+ [QUSB2PHY_PORT_TUNE2] = 0x84,
+ [QUSB2PHY_PORT_TUNE3] = 0x88,
+ [QUSB2PHY_PORT_TUNE4] = 0x8C,
+ [QUSB2PHY_PORT_TUNE5] = 0x90,
+ [QUSB2PHY_PORT_TEST1] = 0x98,
+ [QUSB2PHY_PORT_TEST2] = 0x9C,
+ [QUSB2PHY_PORT_POWERDOWN] = 0xB4,
+ [QUSB2PHY_INTR_CTRL] = 0xBC,
+};
+
static const unsigned int msm8996_regs_layout[] = {
[QUSB2PHY_PLL_STATUS] = 0x38,
[QUSB2PHY_PORT_TUNE1] = 0x80,
@@ -177,7 +219,23 @@ static const struct qusb2_phy_init_tbl msm8998_init_tbl[] = {
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19),
};
-static const unsigned int sdm845_regs_layout[] = {
+static const struct qusb2_phy_init_tbl sm6115_init_tbl[] = {
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x53),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x81),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0x17),
+
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
+
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14),
+
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
+};
+
+static const unsigned int qusb2_v2_regs_layout[] = {
[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
[QUSB2PHY_PLL_STATUS] = 0x1a0,
[QUSB2PHY_PORT_TUNE1] = 0x240,
@@ -191,7 +249,7 @@ static const unsigned int sdm845_regs_layout[] = {
[QUSB2PHY_INTR_CTRL] = 0x230,
};
-static const struct qusb2_phy_init_tbl sdm845_init_tbl[] = {
+static const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = {
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x03),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80),
@@ -233,6 +291,9 @@ struct qusb2_phy_cfg {
/* true if PHY has PLL_CORE_INPUT_OVERRIDE register to reset PLL */
bool has_pll_override;
+
+ /* true if PHY default clk scheme is single-ended */
+ bool se_clk_scheme_default;
};
static const struct qusb2_phy_cfg msm8996_phy_cfg = {
@@ -241,6 +302,7 @@ static const struct qusb2_phy_cfg msm8996_phy_cfg = {
.regs = msm8996_regs_layout,
.has_pll_test = true,
+ .se_clk_scheme_default = true,
.disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN),
.mask_core_ready = PLL_LOCKED,
.autoresume_en = BIT(3),
@@ -254,29 +316,94 @@ static const struct qusb2_phy_cfg msm8998_phy_cfg = {
.disable_ctrl = POWER_DOWN,
.mask_core_ready = CORE_READY_STATUS,
.has_pll_override = true,
+ .se_clk_scheme_default = true,
.autoresume_en = BIT(0),
.update_tune1_with_efuse = true,
};
-static const struct qusb2_phy_cfg sdm845_phy_cfg = {
- .tbl = sdm845_init_tbl,
- .tbl_num = ARRAY_SIZE(sdm845_init_tbl),
- .regs = sdm845_regs_layout,
+static const struct qusb2_phy_cfg ipq6018_phy_cfg = {
+ .tbl = ipq6018_init_tbl,
+ .tbl_num = ARRAY_SIZE(ipq6018_init_tbl),
+ .regs = ipq6018_regs_layout,
+
+ .disable_ctrl = POWER_DOWN,
+ .mask_core_ready = PLL_LOCKED,
+ /* autoresume not used */
+ .autoresume_en = BIT(0),
+};
+
+static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = {
+ .tbl = qusb2_v2_init_tbl,
+ .tbl_num = ARRAY_SIZE(qusb2_v2_init_tbl),
+ .regs = qusb2_v2_regs_layout,
.disable_ctrl = (PWR_CTRL1_VREF_SUPPLY_TRIM | PWR_CTRL1_CLAMP_N_EN |
POWER_DOWN),
.mask_core_ready = CORE_READY_STATUS,
.has_pll_override = true,
+ .se_clk_scheme_default = true,
.autoresume_en = BIT(0),
.update_tune1_with_efuse = true,
};
+static const struct qusb2_phy_cfg sdm660_phy_cfg = {
+ .tbl = msm8996_init_tbl,
+ .tbl_num = ARRAY_SIZE(msm8996_init_tbl),
+ .regs = msm8996_regs_layout,
+
+ .has_pll_test = true,
+ .se_clk_scheme_default = false,
+ .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN),
+ .mask_core_ready = PLL_LOCKED,
+ .autoresume_en = BIT(3),
+};
+
+static const struct qusb2_phy_cfg sm6115_phy_cfg = {
+ .tbl = sm6115_init_tbl,
+ .tbl_num = ARRAY_SIZE(sm6115_init_tbl),
+ .regs = msm8996_regs_layout,
+
+ .has_pll_test = true,
+ .se_clk_scheme_default = true,
+ .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN),
+ .mask_core_ready = PLL_LOCKED,
+ .autoresume_en = BIT(3),
+};
+
static const char * const qusb2_phy_vreg_names[] = {
- "vdda-pll", "vdda-phy-dpdm",
+ "vdd", "vdda-pll", "vdda-phy-dpdm",
};
#define QUSB2_NUM_VREGS ARRAY_SIZE(qusb2_phy_vreg_names)
+/* struct override_param - structure holding qusb2 v2 phy overriding param
+ * set override true if the device tree property exists and read and assign
+ * to value
+ */
+struct override_param {
+ bool override;
+ u8 value;
+};
+
+/*struct override_params - structure holding qusb2 v2 phy overriding params
+ * @imp_res_offset: rescode offset to be updated in IMP_CTRL1 register
+ * @hstx_trim: HSTX_TRIM to be updated in TUNE1 register
+ * @preemphasis: Amplitude Pre-Emphasis to be updated in TUNE1 register
+ * @preemphasis_width: half/full-width Pre-Emphasis updated via TUNE1
+ * @bias_ctrl: bias ctrl to be updated in BIAS_CONTROL_2 register
+ * @charge_ctrl: charge ctrl to be updated in CHG_CTRL2 register
+ * @hsdisc_trim: disconnect threshold to be updated in TUNE2 register
+ */
+struct override_params {
+ struct override_param imp_res_offset;
+ struct override_param hstx_trim;
+ struct override_param preemphasis;
+ struct override_param preemphasis_width;
+ struct override_param bias_ctrl;
+ struct override_param charge_ctrl;
+ struct override_param hsdisc_trim;
+};
+
/**
* struct qusb2_phy - structure holding qusb2 phy attributes
*
@@ -292,14 +419,7 @@ static const char * const qusb2_phy_vreg_names[] = {
* @tcsr: TCSR syscon register map
* @cell: nvmem cell containing phy tuning value
*
- * @override_imp_res_offset: PHY should use different rescode offset
- * @imp_res_offset_value: rescode offset to be updated in IMP_CTRL1 register
- * @override_hstx_trim: PHY should use different HSTX o/p current value
- * @hstx_trim_value: HSTX_TRIM value to be updated in TUNE1 register
- * @override_preemphasis: PHY should use different pre-amphasis amplitude
- * @preemphasis_level: Amplitude Pre-Emphasis to be updated in TUNE1 register
- * @override_preemphasis_width: PHY should use different pre-emphasis duration
- * @preemphasis_width: half/full-width Pre-Emphasis updated via TUNE1
+ * @overrides: pointer to structure for all overriding tuning params
*
* @cfg: phy config data
* @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme
@@ -319,14 +439,7 @@ struct qusb2_phy {
struct regmap *tcsr;
struct nvmem_cell *cell;
- bool override_imp_res_offset;
- u8 imp_res_offset_value;
- bool override_hstx_trim;
- u8 hstx_trim_value;
- bool override_preemphasis;
- u8 preemphasis_level;
- bool override_preemphasis_width;
- u8 preemphasis_width;
+ struct override_params overrides;
const struct qusb2_phy_cfg *cfg;
bool has_se_clk_scheme;
@@ -394,24 +507,35 @@ void qcom_qusb2_phy_configure(void __iomem *base,
static void qusb2_phy_override_phy_params(struct qusb2_phy *qphy)
{
const struct qusb2_phy_cfg *cfg = qphy->cfg;
+ struct override_params *or = &qphy->overrides;
- if (qphy->override_imp_res_offset)
+ if (or->imp_res_offset.override)
qusb2_write_mask(qphy->base, QUSB2PHY_IMP_CTRL1,
- qphy->imp_res_offset_value << IMP_RES_OFFSET_SHIFT,
+ or->imp_res_offset.value << IMP_RES_OFFSET_SHIFT,
IMP_RES_OFFSET_MASK);
- if (qphy->override_hstx_trim)
+ if (or->bias_ctrl.override)
+ qusb2_write_mask(qphy->base, QUSB2PHY_PLL_BIAS_CONTROL_2,
+ or->bias_ctrl.value << BIAS_CTRL2_RES_OFFSET_SHIFT,
+ BIAS_CTRL2_RES_OFFSET_MASK);
+
+ if (or->charge_ctrl.override)
+ qusb2_write_mask(qphy->base, QUSB2PHY_CHG_CTRL2,
+ or->charge_ctrl.value << CHG_CTRL2_OFFSET_SHIFT,
+ CHG_CTRL2_OFFSET_MASK);
+
+ if (or->hstx_trim.override)
qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1],
- qphy->hstx_trim_value << HSTX_TRIM_SHIFT,
+ or->hstx_trim.value << HSTX_TRIM_SHIFT,
HSTX_TRIM_MASK);
- if (qphy->override_preemphasis)
+ if (or->preemphasis.override)
qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1],
- qphy->preemphasis_level << PREEMPHASIS_EN_SHIFT,
+ or->preemphasis.value << PREEMPHASIS_EN_SHIFT,
PREEMPHASIS_EN_MASK);
- if (qphy->override_preemphasis_width) {
- if (qphy->preemphasis_width ==
+ if (or->preemphasis_width.override) {
+ if (or->preemphasis_width.value ==
QUSB2_V2_PREEMPHASIS_WIDTH_HALF_BIT)
qusb2_setbits(qphy->base,
cfg->regs[QUSB2PHY_PORT_TUNE1],
@@ -421,6 +545,11 @@ static void qusb2_phy_override_phy_params(struct qusb2_phy *qphy)
cfg->regs[QUSB2PHY_PORT_TUNE1],
PREEMPH_WIDTH_HALF_BIT);
}
+
+ if (or->hsdisc_trim.override)
+ qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2],
+ or->hsdisc_trim.value << HSDISC_TRIM_SHIFT,
+ HSDISC_TRIM_MASK);
}
/*
@@ -432,7 +561,7 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
{
struct device *dev = &qphy->phy->dev;
const struct qusb2_phy_cfg *cfg = qphy->cfg;
- u8 *val;
+ u8 *val, hstx_trim;
/* efuse register is optional */
if (!qphy->cell)
@@ -446,7 +575,13 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
* set while configuring the phy.
*/
val = nvmem_cell_read(qphy->cell, NULL);
- if (IS_ERR(val) || !val[0]) {
+ if (IS_ERR(val)) {
+ dev_dbg(dev, "failed to read a valid hs-tx trim value\n");
+ return;
+ }
+ hstx_trim = val[0];
+ kfree(val);
+ if (!hstx_trim) {
dev_dbg(dev, "failed to read a valid hs-tx trim value\n");
return;
}
@@ -454,12 +589,10 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
/* Fused TUNE1/2 value is the higher nibble only */
if (cfg->update_tune1_with_efuse)
qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1],
- val[0] << HSTX_TRIM_SHIFT,
- HSTX_TRIM_MASK);
+ hstx_trim << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK);
else
qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2],
- val[0] << HSTX_TRIM_SHIFT,
- HSTX_TRIM_MASK);
+ hstx_trim << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK);
}
static int qusb2_phy_set_mode(struct phy *phy,
@@ -659,8 +792,13 @@ static int qusb2_phy_init(struct phy *phy)
/* Required to get phy pll lock successfully */
usleep_range(150, 160);
- /* Default is single-ended clock on msm8996 */
- qphy->has_se_clk_scheme = true;
+ /*
+ * Not all the SoCs have got a readable TCSR_PHY_CLK_SCHEME
+ * register in the TCSR so, if there's none, use the default
+ * value hardcoded in the configuration.
+ */
+ qphy->has_se_clk_scheme = cfg->se_clk_scheme_default;
+
/*
* read TCSR_PHY_CLK_SCHEME register to check if single-ended
* clock scheme is selected. If yes, then disable differential
@@ -768,14 +906,42 @@ static const struct phy_ops qusb2_phy_gen_ops = {
static const struct of_device_id qusb2_phy_of_match_table[] = {
{
+ .compatible = "qcom,ipq6018-qusb2-phy",
+ .data = &ipq6018_phy_cfg,
+ }, {
+ .compatible = "qcom,ipq8074-qusb2-phy",
+ .data = &msm8996_phy_cfg,
+ }, {
+ .compatible = "qcom,msm8953-qusb2-phy",
+ .data = &msm8996_phy_cfg,
+ }, {
.compatible = "qcom,msm8996-qusb2-phy",
.data = &msm8996_phy_cfg,
}, {
.compatible = "qcom,msm8998-qusb2-phy",
.data = &msm8998_phy_cfg,
}, {
+ .compatible = "qcom,qcm2290-qusb2-phy",
+ .data = &sm6115_phy_cfg,
+ }, {
+ .compatible = "qcom,sdm660-qusb2-phy",
+ .data = &sdm660_phy_cfg,
+ }, {
+ .compatible = "qcom,sm4250-qusb2-phy",
+ .data = &sm6115_phy_cfg,
+ }, {
+ .compatible = "qcom,sm6115-qusb2-phy",
+ .data = &sm6115_phy_cfg,
+ }, {
+ /*
+ * Deprecated. Only here to support legacy device
+ * trees that didn't include "qcom,qusb2-v2-phy"
+ */
.compatible = "qcom,sdm845-qusb2-phy",
- .data = &sdm845_phy_cfg,
+ .data = &qusb2_v2_phy_cfg,
+ }, {
+ .compatible = "qcom,qusb2-v2-phy",
+ .data = &qusb2_v2_phy_cfg,
},
{ },
};
@@ -792,35 +958,29 @@ static int qusb2_phy_probe(struct platform_device *pdev)
struct qusb2_phy *qphy;
struct phy_provider *phy_provider;
struct phy *generic_phy;
- struct resource *res;
int ret, i;
int num;
u32 value;
+ struct override_params *or;
qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
if (!qphy)
return -ENOMEM;
+ or = &qphy->overrides;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- qphy->base = devm_ioremap_resource(dev, res);
+ qphy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(qphy->base))
return PTR_ERR(qphy->base);
qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb");
- if (IS_ERR(qphy->cfg_ahb_clk)) {
- ret = PTR_ERR(qphy->cfg_ahb_clk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get cfg ahb clk, %d\n", ret);
- return ret;
- }
+ if (IS_ERR(qphy->cfg_ahb_clk))
+ return dev_err_probe(dev, PTR_ERR(qphy->cfg_ahb_clk),
+ "failed to get cfg ahb clk\n");
qphy->ref_clk = devm_clk_get(dev, "ref");
- if (IS_ERR(qphy->ref_clk)) {
- ret = PTR_ERR(qphy->ref_clk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get ref clk, %d\n", ret);
- return ret;
- }
+ if (IS_ERR(qphy->ref_clk))
+ return dev_err_probe(dev, PTR_ERR(qphy->ref_clk),
+ "failed to get ref clk\n");
qphy->iface_clk = devm_clk_get_optional(dev, "iface");
if (IS_ERR(qphy->iface_clk))
@@ -837,12 +997,9 @@ static int qusb2_phy_probe(struct platform_device *pdev)
qphy->vregs[i].supply = qusb2_phy_vreg_names[i];
ret = devm_regulator_bulk_get(dev, num, qphy->vregs);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get regulator supplies: %d\n",
- ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get regulator supplies\n");
/* Get the specific init parameters of QMP phy */
qphy->cfg = of_device_get_match_data(dev);
@@ -864,26 +1021,44 @@ static int qusb2_phy_probe(struct platform_device *pdev)
if (!of_property_read_u32(dev->of_node, "qcom,imp-res-offset-value",
&value)) {
- qphy->imp_res_offset_value = (u8)value;
- qphy->override_imp_res_offset = true;
+ or->imp_res_offset.value = (u8)value;
+ or->imp_res_offset.override = true;
+ }
+
+ if (!of_property_read_u32(dev->of_node, "qcom,bias-ctrl-value",
+ &value)) {
+ or->bias_ctrl.value = (u8)value;
+ or->bias_ctrl.override = true;
+ }
+
+ if (!of_property_read_u32(dev->of_node, "qcom,charge-ctrl-value",
+ &value)) {
+ or->charge_ctrl.value = (u8)value;
+ or->charge_ctrl.override = true;
}
if (!of_property_read_u32(dev->of_node, "qcom,hstx-trim-value",
&value)) {
- qphy->hstx_trim_value = (u8)value;
- qphy->override_hstx_trim = true;
+ or->hstx_trim.value = (u8)value;
+ or->hstx_trim.override = true;
}
if (!of_property_read_u32(dev->of_node, "qcom,preemphasis-level",
&value)) {
- qphy->preemphasis_level = (u8)value;
- qphy->override_preemphasis = true;
+ or->preemphasis.value = (u8)value;
+ or->preemphasis.override = true;
}
if (!of_property_read_u32(dev->of_node, "qcom,preemphasis-width",
&value)) {
- qphy->preemphasis_width = (u8)value;
- qphy->override_preemphasis_width = true;
+ or->preemphasis_width.value = (u8)value;
+ or->preemphasis_width.override = true;
+ }
+
+ if (!of_property_read_u32(dev->of_node, "qcom,hsdisc-trim-value",
+ &value)) {
+ or->hsdisc_trim.value = (u8)value;
+ or->hsdisc_trim.override = true;
}
pm_runtime_set_active(dev);
diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
new file mode 100644
index 000000000000..a59063596214
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
@@ -0,0 +1,622 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#define USB2_PHY_USB_PHY_UTMI_CTRL0 (0x3c)
+#define SLEEPM BIT(0)
+#define OPMODE_MASK GENMASK(4, 3)
+#define OPMODE_NORMAL (0x00)
+#define OPMODE_NONDRIVING BIT(3)
+#define TERMSEL BIT(5)
+
+#define USB2_PHY_USB_PHY_UTMI_CTRL1 (0x40)
+#define XCVRSEL BIT(0)
+
+#define USB2_PHY_USB_PHY_UTMI_CTRL5 (0x50)
+#define POR BIT(1)
+
+#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 (0x54)
+#define SIDDQ BIT(2)
+#define RETENABLEN BIT(3)
+#define FSEL_MASK GENMASK(6, 4)
+#define FSEL_DEFAULT (0x3 << 4)
+
+#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1 (0x58)
+#define VBUSVLDEXTSEL0 BIT(4)
+#define PLLBTUNE BIT(5)
+
+#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2 (0x5c)
+#define VREGBYPASS BIT(0)
+
+#define USB2_PHY_USB_PHY_HS_PHY_CTRL1 (0x60)
+#define VBUSVLDEXT0 BIT(0)
+
+#define USB2_PHY_USB_PHY_HS_PHY_CTRL2 (0x64)
+#define USB2_AUTO_RESUME BIT(0)
+#define USB2_SUSPEND_N BIT(2)
+#define USB2_SUSPEND_N_SEL BIT(3)
+
+#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0 (0x6c)
+#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1 (0x70)
+#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2 (0x74)
+#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X3 (0x78)
+#define PARAM_OVRD_MASK 0xFF
+
+#define USB2_PHY_USB_PHY_CFG0 (0x94)
+#define UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN BIT(0)
+#define UTMI_PHY_CMN_CTRL_OVERRIDE_EN BIT(1)
+
+#define USB2_PHY_USB_PHY_REFCLK_CTRL (0xa0)
+#define REFCLK_SEL_MASK GENMASK(1, 0)
+#define REFCLK_SEL_DEFAULT (0x2 << 0)
+
+#define HS_DISCONNECT_MASK GENMASK(2, 0)
+#define SQUELCH_DETECTOR_MASK GENMASK(7, 5)
+
+#define HS_AMPLITUDE_MASK GENMASK(3, 0)
+#define PREEMPHASIS_DURATION_MASK BIT(5)
+#define PREEMPHASIS_AMPLITUDE_MASK GENMASK(7, 6)
+
+#define HS_RISE_FALL_MASK GENMASK(1, 0)
+#define HS_CROSSOVER_VOLTAGE_MASK GENMASK(3, 2)
+#define HS_OUTPUT_IMPEDANCE_MASK GENMASK(5, 4)
+
+#define LS_FS_OUTPUT_IMPEDANCE_MASK GENMASK(3, 0)
+
+static const char * const qcom_snps_hsphy_vreg_names[] = {
+ "vdda-pll", "vdda33", "vdda18",
+};
+
+#define SNPS_HS_NUM_VREGS ARRAY_SIZE(qcom_snps_hsphy_vreg_names)
+
+struct override_param {
+ s32 value;
+ u8 reg_val;
+};
+
+struct override_param_map {
+ const char *prop_name;
+ const struct override_param *param_table;
+ u8 table_size;
+ u8 reg_offset;
+ u8 param_mask;
+};
+
+struct phy_override_seq {
+ bool need_update;
+ u8 offset;
+ u8 value;
+ u8 mask;
+};
+
+#define NUM_HSPHY_TUNING_PARAMS (9)
+
+/**
+ * struct qcom_snps_hsphy - snps hs phy attributes
+ *
+ * @phy: generic phy
+ * @base: iomapped memory space for snps hs phy
+ *
+ * @cfg_ahb_clk: AHB2PHY interface clock
+ * @ref_clk: phy reference clock
+ * @iface_clk: phy interface clock
+ * @phy_reset: phy reset control
+ * @vregs: regulator supplies bulk data
+ * @phy_initialized: if PHY has been initialized correctly
+ * @mode: contains the current mode the PHY is in
+ */
+struct qcom_snps_hsphy {
+ struct phy *phy;
+ void __iomem *base;
+
+ struct clk *cfg_ahb_clk;
+ struct clk *ref_clk;
+ struct reset_control *phy_reset;
+ struct regulator_bulk_data vregs[SNPS_HS_NUM_VREGS];
+
+ bool phy_initialized;
+ enum phy_mode mode;
+ struct phy_override_seq update_seq_cfg[NUM_HSPHY_TUNING_PARAMS];
+};
+
+static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset,
+ u32 mask, u32 val)
+{
+ u32 reg;
+
+ reg = readl_relaxed(base + offset);
+ reg &= ~mask;
+ reg |= val & mask;
+ writel_relaxed(reg, base + offset);
+
+ /* Ensure above write is completed */
+ readl_relaxed(base + offset);
+}
+
+static int qcom_snps_hsphy_suspend(struct qcom_snps_hsphy *hsphy)
+{
+ dev_dbg(&hsphy->phy->dev, "Suspend QCOM SNPS PHY\n");
+
+ if (hsphy->mode == PHY_MODE_USB_HOST) {
+ /* Enable auto-resume to meet remote wakeup timing */
+ qcom_snps_hsphy_write_mask(hsphy->base,
+ USB2_PHY_USB_PHY_HS_PHY_CTRL2,
+ USB2_AUTO_RESUME,
+ USB2_AUTO_RESUME);
+ usleep_range(500, 1000);
+ qcom_snps_hsphy_write_mask(hsphy->base,
+ USB2_PHY_USB_PHY_HS_PHY_CTRL2,
+ 0, USB2_AUTO_RESUME);
+ }
+
+ clk_disable_unprepare(hsphy->cfg_ahb_clk);
+ return 0;
+}
+
+static int qcom_snps_hsphy_resume(struct qcom_snps_hsphy *hsphy)
+{
+ int ret;
+
+ dev_dbg(&hsphy->phy->dev, "Resume QCOM SNPS PHY, mode\n");
+
+ ret = clk_prepare_enable(hsphy->cfg_ahb_clk);
+ if (ret) {
+ dev_err(&hsphy->phy->dev, "failed to enable cfg ahb clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused qcom_snps_hsphy_runtime_suspend(struct device *dev)
+{
+ struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev);
+
+ if (!hsphy->phy_initialized)
+ return 0;
+
+ qcom_snps_hsphy_suspend(hsphy);
+ return 0;
+}
+
+static int __maybe_unused qcom_snps_hsphy_runtime_resume(struct device *dev)
+{
+ struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev);
+
+ if (!hsphy->phy_initialized)
+ return 0;
+
+ qcom_snps_hsphy_resume(hsphy);
+ return 0;
+}
+
+static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode,
+ int submode)
+{
+ struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
+
+ hsphy->mode = mode;
+ return 0;
+}
+
+static const struct override_param hs_disconnect_sc7280[] = {
+ { -272, 0 },
+ { 0, 1 },
+ { 317, 2 },
+ { 630, 3 },
+ { 973, 4 },
+ { 1332, 5 },
+ { 1743, 6 },
+ { 2156, 7 },
+};
+
+static const struct override_param squelch_det_threshold_sc7280[] = {
+ { -2090, 7 },
+ { -1560, 6 },
+ { -1030, 5 },
+ { -530, 4 },
+ { 0, 3 },
+ { 530, 2 },
+ { 1060, 1 },
+ { 1590, 0 },
+};
+
+static const struct override_param hs_amplitude_sc7280[] = {
+ { -660, 0 },
+ { -440, 1 },
+ { -220, 2 },
+ { 0, 3 },
+ { 230, 4 },
+ { 440, 5 },
+ { 650, 6 },
+ { 890, 7 },
+ { 1110, 8 },
+ { 1330, 9 },
+ { 1560, 10 },
+ { 1780, 11 },
+ { 2000, 12 },
+ { 2220, 13 },
+ { 2430, 14 },
+ { 2670, 15 },
+};
+
+static const struct override_param preemphasis_duration_sc7280[] = {
+ { 10000, 1 },
+ { 20000, 0 },
+};
+
+static const struct override_param preemphasis_amplitude_sc7280[] = {
+ { 10000, 1 },
+ { 20000, 2 },
+ { 30000, 3 },
+ { 40000, 0 },
+};
+
+static const struct override_param hs_rise_fall_time_sc7280[] = {
+ { -4100, 3 },
+ { 0, 2 },
+ { 2810, 1 },
+ { 5430, 0 },
+};
+
+static const struct override_param hs_crossover_voltage_sc7280[] = {
+ { -31000, 1 },
+ { 0, 3 },
+ { 28000, 2 },
+};
+
+static const struct override_param hs_output_impedance_sc7280[] = {
+ { -2300000, 3 },
+ { 0, 2 },
+ { 2600000, 1 },
+ { 6100000, 0 },
+};
+
+static const struct override_param ls_fs_output_impedance_sc7280[] = {
+ { -1053, 15 },
+ { -557, 7 },
+ { 0, 3 },
+ { 612, 1 },
+ { 1310, 0 },
+};
+
+static const struct override_param_map sc7280_snps_7nm_phy[] = {
+ {
+ "qcom,hs-disconnect-bp",
+ hs_disconnect_sc7280,
+ ARRAY_SIZE(hs_disconnect_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0,
+ HS_DISCONNECT_MASK
+ },
+ {
+ "qcom,squelch-detector-bp",
+ squelch_det_threshold_sc7280,
+ ARRAY_SIZE(squelch_det_threshold_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0,
+ SQUELCH_DETECTOR_MASK
+ },
+ {
+ "qcom,hs-amplitude-bp",
+ hs_amplitude_sc7280,
+ ARRAY_SIZE(hs_amplitude_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1,
+ HS_AMPLITUDE_MASK
+ },
+ {
+ "qcom,pre-emphasis-duration-bp",
+ preemphasis_duration_sc7280,
+ ARRAY_SIZE(preemphasis_duration_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1,
+ PREEMPHASIS_DURATION_MASK,
+ },
+ {
+ "qcom,pre-emphasis-amplitude-bp",
+ preemphasis_amplitude_sc7280,
+ ARRAY_SIZE(preemphasis_amplitude_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1,
+ PREEMPHASIS_AMPLITUDE_MASK,
+ },
+ {
+ "qcom,hs-rise-fall-time-bp",
+ hs_rise_fall_time_sc7280,
+ ARRAY_SIZE(hs_rise_fall_time_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2,
+ HS_RISE_FALL_MASK
+ },
+ {
+ "qcom,hs-crossover-voltage-microvolt",
+ hs_crossover_voltage_sc7280,
+ ARRAY_SIZE(hs_crossover_voltage_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2,
+ HS_CROSSOVER_VOLTAGE_MASK
+ },
+ {
+ "qcom,hs-output-impedance-micro-ohms",
+ hs_output_impedance_sc7280,
+ ARRAY_SIZE(hs_output_impedance_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2,
+ HS_OUTPUT_IMPEDANCE_MASK,
+ },
+ {
+ "qcom,ls-fs-output-impedance-bp",
+ ls_fs_output_impedance_sc7280,
+ ARRAY_SIZE(ls_fs_output_impedance_sc7280),
+ USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X3,
+ LS_FS_OUTPUT_IMPEDANCE_MASK,
+ },
+ {},
+};
+
+static int qcom_snps_hsphy_init(struct phy *phy)
+{
+ struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
+ int ret, i;
+
+ dev_vdbg(&phy->dev, "%s(): Initializing SNPS HS phy\n", __func__);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(hsphy->cfg_ahb_clk);
+ if (ret) {
+ dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
+ goto poweroff_phy;
+ }
+
+ ret = reset_control_assert(hsphy->phy_reset);
+ if (ret) {
+ dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret);
+ goto disable_ahb_clk;
+ }
+
+ usleep_range(100, 150);
+
+ ret = reset_control_deassert(hsphy->phy_reset);
+ if (ret) {
+ dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret);
+ goto disable_ahb_clk;
+ }
+
+ qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,
+ UTMI_PHY_CMN_CTRL_OVERRIDE_EN,
+ UTMI_PHY_CMN_CTRL_OVERRIDE_EN);
+ qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
+ POR, POR);
+ qcom_snps_hsphy_write_mask(hsphy->base,
+ USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0,
+ FSEL_MASK, 0);
+ qcom_snps_hsphy_write_mask(hsphy->base,
+ USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
+ PLLBTUNE, PLLBTUNE);
+ qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_REFCLK_CTRL,
+ REFCLK_SEL_DEFAULT, REFCLK_SEL_MASK);
+ qcom_snps_hsphy_write_mask(hsphy->base,
+ USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
+ VBUSVLDEXTSEL0, VBUSVLDEXTSEL0);
+ qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL1,
+ VBUSVLDEXT0, VBUSVLDEXT0);
+
+ for (i = 0; i < ARRAY_SIZE(hsphy->update_seq_cfg); i++) {
+ if (hsphy->update_seq_cfg[i].need_update)
+ qcom_snps_hsphy_write_mask(hsphy->base,
+ hsphy->update_seq_cfg[i].offset,
+ hsphy->update_seq_cfg[i].mask,
+ hsphy->update_seq_cfg[i].value);
+ }
+
+ qcom_snps_hsphy_write_mask(hsphy->base,
+ USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2,
+ VREGBYPASS, VREGBYPASS);
+
+ qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
+ USB2_SUSPEND_N_SEL | USB2_SUSPEND_N,
+ USB2_SUSPEND_N_SEL | USB2_SUSPEND_N);
+
+ qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL0,
+ SLEEPM, SLEEPM);
+
+ qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0,
+ SIDDQ, 0);
+
+ qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
+ POR, 0);
+
+ qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
+ USB2_SUSPEND_N_SEL, 0);
+
+ qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,
+ UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 0);
+
+ hsphy->phy_initialized = true;
+
+ return 0;
+
+disable_ahb_clk:
+ clk_disable_unprepare(hsphy->cfg_ahb_clk);
+poweroff_phy:
+ regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
+
+ return ret;
+}
+
+static int qcom_snps_hsphy_exit(struct phy *phy)
+{
+ struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
+
+ reset_control_assert(hsphy->phy_reset);
+ clk_disable_unprepare(hsphy->cfg_ahb_clk);
+ regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
+ hsphy->phy_initialized = false;
+
+ return 0;
+}
+
+static const struct phy_ops qcom_snps_hsphy_gen_ops = {
+ .init = qcom_snps_hsphy_init,
+ .exit = qcom_snps_hsphy_exit,
+ .set_mode = qcom_snps_hsphy_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static const struct of_device_id qcom_snps_hsphy_of_match_table[] = {
+ { .compatible = "qcom,sm8150-usb-hs-phy", },
+ { .compatible = "qcom,usb-snps-hs-5nm-phy", },
+ {
+ .compatible = "qcom,usb-snps-hs-7nm-phy",
+ .data = &sc7280_snps_7nm_phy,
+ },
+ { .compatible = "qcom,usb-snps-femto-v2-phy", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_of_match_table);
+
+static const struct dev_pm_ops qcom_snps_hsphy_pm_ops = {
+ SET_RUNTIME_PM_OPS(qcom_snps_hsphy_runtime_suspend,
+ qcom_snps_hsphy_runtime_resume, NULL)
+};
+
+static void qcom_snps_hsphy_override_param_update_val(
+ const struct override_param_map map,
+ s32 dt_val, struct phy_override_seq *seq_entry)
+{
+ int i;
+
+ /*
+ * Param table for each param is in increasing order
+ * of dt values. We need to iterate over the list to
+ * select the entry that matches the dt value and pick
+ * up the corresponding register value.
+ */
+ for (i = 0; i < map.table_size - 1; i++) {
+ if (map.param_table[i].value == dt_val)
+ break;
+ }
+
+ seq_entry->need_update = true;
+ seq_entry->offset = map.reg_offset;
+ seq_entry->mask = map.param_mask;
+ seq_entry->value = map.param_table[i].reg_val << __ffs(map.param_mask);
+}
+
+static void qcom_snps_hsphy_read_override_param_seq(struct device *dev)
+{
+ struct device_node *node = dev->of_node;
+ s32 val;
+ int ret, i;
+ struct qcom_snps_hsphy *hsphy;
+ const struct override_param_map *cfg = of_device_get_match_data(dev);
+
+ if (!cfg)
+ return;
+
+ hsphy = dev_get_drvdata(dev);
+
+ for (i = 0; cfg[i].prop_name != NULL; i++) {
+ ret = of_property_read_s32(node, cfg[i].prop_name, &val);
+ if (ret)
+ continue;
+
+ qcom_snps_hsphy_override_param_update_val(cfg[i], val,
+ &hsphy->update_seq_cfg[i]);
+ dev_dbg(&hsphy->phy->dev, "Read param: %s dt_val: %d reg_val: 0x%x\n",
+ cfg[i].prop_name, val, hsphy->update_seq_cfg[i].value);
+
+ }
+}
+
+static int qcom_snps_hsphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct qcom_snps_hsphy *hsphy;
+ struct phy_provider *phy_provider;
+ struct phy *generic_phy;
+ int ret, i;
+ int num;
+
+ hsphy = devm_kzalloc(dev, sizeof(*hsphy), GFP_KERNEL);
+ if (!hsphy)
+ return -ENOMEM;
+
+ hsphy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hsphy->base))
+ return PTR_ERR(hsphy->base);
+
+ hsphy->ref_clk = devm_clk_get(dev, "ref");
+ if (IS_ERR(hsphy->ref_clk))
+ return dev_err_probe(dev, PTR_ERR(hsphy->ref_clk),
+ "failed to get ref clk\n");
+
+ hsphy->phy_reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(hsphy->phy_reset)) {
+ dev_err(dev, "failed to get phy core reset\n");
+ return PTR_ERR(hsphy->phy_reset);
+ }
+
+ num = ARRAY_SIZE(hsphy->vregs);
+ for (i = 0; i < num; i++)
+ hsphy->vregs[i].supply = qcom_snps_hsphy_vreg_names[i];
+
+ ret = devm_regulator_bulk_get(dev, num, hsphy->vregs);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get regulator supplies\n");
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ /*
+ * Prevent runtime pm from being ON by default. Users can enable
+ * it using power/control in sysfs.
+ */
+ pm_runtime_forbid(dev);
+
+ generic_phy = devm_phy_create(dev, NULL, &qcom_snps_hsphy_gen_ops);
+ if (IS_ERR(generic_phy)) {
+ ret = PTR_ERR(generic_phy);
+ dev_err(dev, "failed to create phy, %d\n", ret);
+ return ret;
+ }
+ hsphy->phy = generic_phy;
+
+ dev_set_drvdata(dev, hsphy);
+ phy_set_drvdata(generic_phy, hsphy);
+ qcom_snps_hsphy_read_override_param_seq(dev);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (!IS_ERR(phy_provider))
+ dev_dbg(dev, "Registered Qcom-SNPS HS phy\n");
+ else
+ pm_runtime_disable(dev);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver qcom_snps_hsphy_driver = {
+ .probe = qcom_snps_hsphy_probe,
+ .driver = {
+ .name = "qcom-snps-hs-femto-v2-phy",
+ .pm = &qcom_snps_hsphy_pm_ops,
+ .of_match_table = qcom_snps_hsphy_of_match_table,
+ },
+};
+
+module_platform_driver(qcom_snps_hsphy_driver);
+
+MODULE_DESCRIPTION("Qualcomm SNPS FEMTO USB HS PHY V2 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h
deleted file mode 100644
index 9bf973a0d8c3..000000000000
--- a/drivers/phy/qualcomm/phy-qcom-ufs-i.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
- */
-
-#ifndef UFS_QCOM_PHY_I_H_
-#define UFS_QCOM_PHY_I_H_
-
-#include <linux/module.h>
-#include <linux/clk.h>
-#include <linux/phy/phy.h>
-#include <linux/regulator/consumer.h>
-#include <linux/reset.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/iopoll.h>
-
-#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \
- { \
- .reg_offset = reg, \
- .cfg_value = val, \
- }
-
-#define UFS_QCOM_PHY_NAME_LEN 30
-
-enum {
- MASK_SERDES_START = 0x1,
- MASK_PCS_READY = 0x1,
-};
-
-enum {
- OFFSET_SERDES_START = 0x0,
-};
-
-struct ufs_qcom_phy_stored_attributes {
- u32 att;
- u32 value;
-};
-
-
-struct ufs_qcom_phy_calibration {
- u32 reg_offset;
- u32 cfg_value;
-};
-
-struct ufs_qcom_phy_vreg {
- const char *name;
- struct regulator *reg;
- int max_uA;
- int min_uV;
- int max_uV;
- bool enabled;
-};
-
-struct ufs_qcom_phy {
- struct list_head list;
- struct device *dev;
- void __iomem *mmio;
- void __iomem *dev_ref_clk_ctrl_mmio;
- struct clk *tx_iface_clk;
- struct clk *rx_iface_clk;
- bool is_iface_clk_enabled;
- struct clk *ref_clk_src;
- struct clk *ref_clk_parent;
- struct clk *ref_clk;
- bool is_ref_clk_enabled;
- bool is_dev_ref_clk_enabled;
- struct ufs_qcom_phy_vreg vdda_pll;
- struct ufs_qcom_phy_vreg vdda_phy;
- struct ufs_qcom_phy_vreg vddp_ref_clk;
- unsigned int quirks;
-
- /*
- * If UFS link is put into Hibern8 and if UFS PHY analog hardware is
- * power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8
- * exit might fail even after powering on UFS PHY analog hardware.
- * Enabling this quirk will help to solve above issue by doing
- * custom PHY settings just before PHY analog power collapse.
- */
- #define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0)
-
- u8 host_ctrl_rev_major;
- u16 host_ctrl_rev_minor;
- u16 host_ctrl_rev_step;
-
- char name[UFS_QCOM_PHY_NAME_LEN];
- struct ufs_qcom_phy_calibration *cached_regs;
- int cached_regs_table_size;
- struct ufs_qcom_phy_specific_ops *phy_spec_ops;
-
- enum phy_mode mode;
- struct reset_control *ufs_reset;
-};
-
-/**
- * struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a
- * specific implementation per phy. Each UFS phy, should implement
- * those functions according to its spec and requirements
- * @start_serdes: pointer to a function that starts the serdes
- * @is_physical_coding_sublayer_ready: pointer to a function that
- * checks pcs readiness. returns 0 for success and non-zero for error.
- * @set_tx_lane_enable: pointer to a function that enable tx lanes
- * @power_control: pointer to a function that controls analog rail of phy
- * and writes to QSERDES_RX_SIGDET_CNTRL attribute
- */
-struct ufs_qcom_phy_specific_ops {
- int (*calibrate)(struct ufs_qcom_phy *ufs_qcom_phy, bool is_rate_B);
- void (*start_serdes)(struct ufs_qcom_phy *phy);
- int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy);
- void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val);
- void (*power_control)(struct ufs_qcom_phy *phy, bool val);
-};
-
-struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy);
-int ufs_qcom_phy_power_on(struct phy *generic_phy);
-int ufs_qcom_phy_power_off(struct phy *generic_phy);
-int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common);
-int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common);
-int ufs_qcom_phy_remove(struct phy *generic_phy,
- struct ufs_qcom_phy *ufs_qcom_phy);
-struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
- struct ufs_qcom_phy *common_cfg,
- const struct phy_ops *ufs_qcom_phy_gen_ops,
- struct ufs_qcom_phy_specific_ops *phy_spec_ops);
-int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
- struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A,
- struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B,
- bool is_rate_B);
-#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c
deleted file mode 100644
index 54b355bfc24c..000000000000
--- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c
+++ /dev/null
@@ -1,172 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
- */
-
-#include "phy-qcom-ufs-qmp-14nm.h"
-
-#define UFS_PHY_NAME "ufs_phy_qmp_14nm"
-#define UFS_PHY_VDDA_PHY_UV (925000)
-
-static
-int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
- bool is_rate_B)
-{
- int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A);
- int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
- int err;
-
- err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A,
- tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B);
-
- if (err)
- dev_err(ufs_qcom_phy->dev,
- "%s: ufs_qcom_phy_calibrate() failed %d\n",
- __func__, err);
- return err;
-}
-
-static
-void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
-{
- phy_common->quirks =
- UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
-}
-
-static
-int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy,
- enum phy_mode mode, int submode)
-{
- struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
-
- phy_common->mode = PHY_MODE_INVALID;
-
- if (mode > 0)
- phy_common->mode = mode;
-
- return 0;
-}
-
-static
-void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val)
-{
- writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
- /*
- * Before any transactions involving PHY, ensure PHY knows
- * that it's analog rail is powered ON (or OFF).
- */
- mb();
-}
-
-static inline
-void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
-{
- /*
- * 14nm PHY does not have TX_LANE_ENABLE register.
- * Implement this function so as not to propagate error to caller.
- */
-}
-
-static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy)
-{
- u32 tmp;
-
- tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
- tmp &= ~MASK_SERDES_START;
- tmp |= (1 << OFFSET_SERDES_START);
- writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
- /* Ensure register value is committed */
- mb();
-}
-
-static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
-{
- int err = 0;
- u32 val;
-
- err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS,
- val, (val & MASK_PCS_READY), 10, 1000000);
- if (err)
- dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
- __func__, err);
- return err;
-}
-
-static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
- .power_on = ufs_qcom_phy_power_on,
- .power_off = ufs_qcom_phy_power_off,
- .set_mode = ufs_qcom_phy_qmp_14nm_set_mode,
- .owner = THIS_MODULE,
-};
-
-static struct ufs_qcom_phy_specific_ops phy_14nm_ops = {
- .calibrate = ufs_qcom_phy_qmp_14nm_phy_calibrate,
- .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes,
- .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready,
- .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable,
- .power_control = ufs_qcom_phy_qmp_14nm_power_control,
-};
-
-static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct phy *generic_phy;
- struct ufs_qcom_phy_qmp_14nm *phy;
- struct ufs_qcom_phy *phy_common;
- int err = 0;
-
- phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
- if (!phy) {
- err = -ENOMEM;
- goto out;
- }
- phy_common = &phy->common_cfg;
-
- generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common,
- &ufs_qcom_phy_qmp_14nm_phy_ops, &phy_14nm_ops);
-
- if (!generic_phy) {
- err = -EIO;
- goto out;
- }
-
- err = ufs_qcom_phy_init_clks(phy_common);
- if (err)
- goto out;
-
- err = ufs_qcom_phy_init_vregulators(phy_common);
- if (err)
- goto out;
-
- phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV;
- phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV;
-
- ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common);
-
- phy_set_drvdata(generic_phy, phy);
-
- strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name));
-
-out:
- return err;
-}
-
-static const struct of_device_id ufs_qcom_phy_qmp_14nm_of_match[] = {
- {.compatible = "qcom,ufs-phy-qmp-14nm"},
- {.compatible = "qcom,msm8996-ufs-phy-qmp-14nm"},
- {},
-};
-MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_14nm_of_match);
-
-static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = {
- .probe = ufs_qcom_phy_qmp_14nm_probe,
- .driver = {
- .of_match_table = ufs_qcom_phy_qmp_14nm_of_match,
- .name = "ufs_qcom_phy_qmp_14nm",
- },
-};
-
-module_platform_driver(ufs_qcom_phy_qmp_14nm_driver);
-
-MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 14nm");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h
deleted file mode 100644
index ceca654b9338..000000000000
--- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h
+++ /dev/null
@@ -1,168 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
- */
-
-#ifndef UFS_QCOM_PHY_QMP_14NM_H_
-#define UFS_QCOM_PHY_QMP_14NM_H_
-
-#include "phy-qcom-ufs-i.h"
-
-/* QCOM UFS PHY control registers */
-#define COM_OFF(x) (0x000 + x)
-#define PHY_OFF(x) (0xC00 + x)
-#define TX_OFF(n, x) (0x400 + (0x400 * n) + x)
-#define RX_OFF(n, x) (0x600 + (0x400 * n) + x)
-
-/* UFS PHY QSERDES COM registers */
-#define QSERDES_COM_BG_TIMER COM_OFF(0x0C)
-#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34)
-#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C)
-#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0x4C)
-#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0x50)
-#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0x54)
-#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0x58)
-#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0x5C)
-#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0x60)
-#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x78)
-#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C)
-#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84)
-#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x88)
-#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x90)
-#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94)
-#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC)
-#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4)
-#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xC8)
-#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0xCC)
-#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xD0)
-#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xD4)
-#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xDC)
-#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xE0)
-#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xE4)
-#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xE8)
-#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xEC)
-#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xF0)
-#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0x108)
-#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0x10C)
-#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0x110)
-#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0x114)
-#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0x124)
-#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0x128)
-#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0x12C)
-#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0x130)
-#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0x134)
-#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x138)
-#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x144)
-#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148)
-#define QSERDES_COM_CLK_SELECT COM_OFF(0x174)
-#define QSERDES_COM_HSCLK_SEL COM_OFF(0x178)
-#define QSERDES_COM_CORECLK_DIV COM_OFF(0x184)
-#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C)
-#define QSERDES_COM_CMN_CONFIG COM_OFF(0x194)
-#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C)
-#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC)
-
-/* UFS PHY registers */
-#define UFS_PHY_PHY_START PHY_OFF(0x00)
-#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04)
-#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168)
-
-/* UFS PHY TX registers */
-#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN TX_OFF(0, 0x68)
-#define QSERDES_TX_LANE_MODE TX_OFF(0, 0x94)
-
-/* UFS PHY RX registers */
-#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x40)
-#define QSERDES_RX_RX_TERM_BW RX_OFF(0, 0x90)
-#define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0, 0xC4)
-#define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0, 0xC8)
-#define QSERDES_RX_RX_EQ_GAIN2_LSB RX_OFF(0, 0xCC)
-#define QSERDES_RX_RX_EQ_GAIN2_MSB RX_OFF(0, 0xD0)
-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xD8)
-#define QSERDES_RX_SIGDET_CNTRL RX_OFF(0, 0x114)
-#define QSERDES_RX_SIGDET_LVL RX_OFF(0, 0x118)
-#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x11C)
-#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0, 0x12C)
-
-/*
- * This structure represents the 14nm specific phy.
- * common_cfg MUST remain the first field in this structure
- * in case extra fields are added. This way, when calling
- * get_ufs_qcom_phy() of generic phy, we can extract the
- * common phy structure (struct ufs_qcom_phy) out of it
- * regardless of the relevant specific phy.
- */
-struct ufs_qcom_phy_qmp_14nm {
- struct ufs_qcom_phy common_cfg;
-};
-
-static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = {
- UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x10),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x14),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00),
-
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x02),
-
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0F),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E),
-};
-
-static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54),
-};
-
-#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c
deleted file mode 100644
index 3e9d8b71e995..000000000000
--- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c
+++ /dev/null
@@ -1,226 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
- */
-
-#include "phy-qcom-ufs-qmp-20nm.h"
-
-#define UFS_PHY_NAME "ufs_phy_qmp_20nm"
-
-static
-int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
- bool is_rate_B)
-{
- struct ufs_qcom_phy_calibration *tbl_A, *tbl_B;
- int tbl_size_A, tbl_size_B;
- u8 major = ufs_qcom_phy->host_ctrl_rev_major;
- u16 minor = ufs_qcom_phy->host_ctrl_rev_minor;
- u16 step = ufs_qcom_phy->host_ctrl_rev_step;
- int err;
-
- if ((major == 0x1) && (minor == 0x002) && (step == 0x0000)) {
- tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_2_0);
- tbl_A = phy_cal_table_rate_A_1_2_0;
- } else if ((major == 0x1) && (minor == 0x003) && (step == 0x0000)) {
- tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0);
- tbl_A = phy_cal_table_rate_A_1_3_0;
- } else {
- dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, no calibration values\n",
- __func__);
- err = -ENODEV;
- goto out;
- }
-
- tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
- tbl_B = phy_cal_table_rate_B;
-
- err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A,
- tbl_B, tbl_size_B, is_rate_B);
-
- if (err)
- dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n",
- __func__, err);
-
-out:
- return err;
-}
-
-static
-void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
-{
- phy_common->quirks =
- UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
-}
-
-static
-int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy,
- enum phy_mode mode, int submode)
-{
- struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
-
- phy_common->mode = PHY_MODE_INVALID;
-
- if (mode > 0)
- phy_common->mode = mode;
-
- return 0;
-}
-
-static
-void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val)
-{
- bool hibern8_exit_after_pwr_collapse = phy->quirks &
- UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
-
- if (val) {
- writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
- /*
- * Before any transactions involving PHY, ensure PHY knows
- * that it's analog rail is powered ON.
- */
- mb();
-
- if (hibern8_exit_after_pwr_collapse) {
- /*
- * Give atleast 1us delay after restoring PHY analog
- * power.
- */
- usleep_range(1, 2);
- writel_relaxed(0x0A, phy->mmio +
- QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
- writel_relaxed(0x08, phy->mmio +
- QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
- /*
- * Make sure workaround is deactivated before proceeding
- * with normal PHY operations.
- */
- mb();
- }
- } else {
- if (hibern8_exit_after_pwr_collapse) {
- writel_relaxed(0x0A, phy->mmio +
- QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
- writel_relaxed(0x02, phy->mmio +
- QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
- /*
- * Make sure that above workaround is activated before
- * PHY analog power collapse.
- */
- mb();
- }
-
- writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
- /*
- * ensure that PHY knows its PHY analog rail is going
- * to be powered down
- */
- mb();
- }
-}
-
-static
-void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
-{
- writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK,
- phy->mmio + UFS_PHY_TX_LANE_ENABLE);
- mb();
-}
-
-static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy *phy)
-{
- u32 tmp;
-
- tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
- tmp &= ~MASK_SERDES_START;
- tmp |= (1 << OFFSET_SERDES_START);
- writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
- mb();
-}
-
-static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
-{
- int err = 0;
- u32 val;
-
- err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS,
- val, (val & MASK_PCS_READY), 10, 1000000);
- if (err)
- dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
- __func__, err);
- return err;
-}
-
-static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
- .power_on = ufs_qcom_phy_power_on,
- .power_off = ufs_qcom_phy_power_off,
- .set_mode = ufs_qcom_phy_qmp_20nm_set_mode,
- .owner = THIS_MODULE,
-};
-
-static struct ufs_qcom_phy_specific_ops phy_20nm_ops = {
- .calibrate = ufs_qcom_phy_qmp_20nm_phy_calibrate,
- .start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes,
- .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready,
- .set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable,
- .power_control = ufs_qcom_phy_qmp_20nm_power_control,
-};
-
-static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct phy *generic_phy;
- struct ufs_qcom_phy_qmp_20nm *phy;
- struct ufs_qcom_phy *phy_common;
- int err = 0;
-
- phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
- if (!phy) {
- err = -ENOMEM;
- goto out;
- }
- phy_common = &phy->common_cfg;
-
- generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common,
- &ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops);
-
- if (!generic_phy) {
- err = -EIO;
- goto out;
- }
-
- err = ufs_qcom_phy_init_clks(phy_common);
- if (err)
- goto out;
-
- err = ufs_qcom_phy_init_vregulators(phy_common);
- if (err)
- goto out;
-
- ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common);
-
- phy_set_drvdata(generic_phy, phy);
-
- strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name));
-
-out:
- return err;
-}
-
-static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match[] = {
- {.compatible = "qcom,ufs-phy-qmp-20nm"},
- {},
-};
-MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_20nm_of_match);
-
-static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = {
- .probe = ufs_qcom_phy_qmp_20nm_probe,
- .driver = {
- .of_match_table = ufs_qcom_phy_qmp_20nm_of_match,
- .name = "ufs_qcom_phy_qmp_20nm",
- },
-};
-
-module_platform_driver(ufs_qcom_phy_qmp_20nm_driver);
-
-MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h
deleted file mode 100644
index 8ce79f524eed..000000000000
--- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h
+++ /dev/null
@@ -1,226 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
- */
-
-#ifndef UFS_QCOM_PHY_QMP_20NM_H_
-#define UFS_QCOM_PHY_QMP_20NM_H_
-
-#include "phy-qcom-ufs-i.h"
-
-/* QCOM UFS PHY control registers */
-
-#define COM_OFF(x) (0x000 + x)
-#define PHY_OFF(x) (0xC00 + x)
-#define TX_OFF(n, x) (0x400 + (0x400 * n) + x)
-#define RX_OFF(n, x) (0x600 + (0x400 * n) + x)
-
-/* UFS PHY PLL block registers */
-#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x0)
-#define QSERDES_COM_PLL_VCOTAIL_EN COM_OFF(0x04)
-#define QSERDES_COM_PLL_CNTRL COM_OFF(0x14)
-#define QSERDES_COM_PLL_IP_SETI COM_OFF(0x24)
-#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL COM_OFF(0x28)
-#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x30)
-#define QSERDES_COM_PLL_CP_SETI COM_OFF(0x34)
-#define QSERDES_COM_PLL_IP_SETP COM_OFF(0x38)
-#define QSERDES_COM_PLL_CP_SETP COM_OFF(0x3C)
-#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND COM_OFF(0x48)
-#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x4C)
-#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0x50)
-#define QSERDES_COM_PLLLOCK_CMP1 COM_OFF(0x90)
-#define QSERDES_COM_PLLLOCK_CMP2 COM_OFF(0x94)
-#define QSERDES_COM_PLLLOCK_CMP3 COM_OFF(0x98)
-#define QSERDES_COM_PLLLOCK_CMP_EN COM_OFF(0x9C)
-#define QSERDES_COM_BGTC COM_OFF(0xA0)
-#define QSERDES_COM_DEC_START1 COM_OFF(0xAC)
-#define QSERDES_COM_PLL_AMP_OS COM_OFF(0xB0)
-#define QSERDES_COM_RES_CODE_UP_OFFSET COM_OFF(0xD8)
-#define QSERDES_COM_RES_CODE_DN_OFFSET COM_OFF(0xDC)
-#define QSERDES_COM_DIV_FRAC_START1 COM_OFF(0x100)
-#define QSERDES_COM_DIV_FRAC_START2 COM_OFF(0x104)
-#define QSERDES_COM_DIV_FRAC_START3 COM_OFF(0x108)
-#define QSERDES_COM_DEC_START2 COM_OFF(0x10C)
-#define QSERDES_COM_PLL_RXTXEPCLK_EN COM_OFF(0x110)
-#define QSERDES_COM_PLL_CRCTRL COM_OFF(0x114)
-#define QSERDES_COM_PLL_CLKEPDIV COM_OFF(0x118)
-
-/* TX LANE n (0, 1) registers */
-#define QSERDES_TX_EMP_POST1_LVL(n) TX_OFF(n, 0x08)
-#define QSERDES_TX_DRV_LVL(n) TX_OFF(n, 0x0C)
-#define QSERDES_TX_LANE_MODE(n) TX_OFF(n, 0x54)
-
-/* RX LANE n (0, 1) registers */
-#define QSERDES_RX_CDR_CONTROL1(n) RX_OFF(n, 0x0)
-#define QSERDES_RX_CDR_CONTROL_HALF(n) RX_OFF(n, 0x8)
-#define QSERDES_RX_RX_EQ_GAIN1_LSB(n) RX_OFF(n, 0xA8)
-#define QSERDES_RX_RX_EQ_GAIN1_MSB(n) RX_OFF(n, 0xAC)
-#define QSERDES_RX_RX_EQ_GAIN2_LSB(n) RX_OFF(n, 0xB0)
-#define QSERDES_RX_RX_EQ_GAIN2_MSB(n) RX_OFF(n, 0xB4)
-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(n) RX_OFF(n, 0xBC)
-#define QSERDES_RX_CDR_CONTROL_QUARTER(n) RX_OFF(n, 0xC)
-#define QSERDES_RX_SIGDET_CNTRL(n) RX_OFF(n, 0x100)
-
-/* UFS PHY registers */
-#define UFS_PHY_PHY_START PHY_OFF(0x00)
-#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x4)
-#define UFS_PHY_TX_LANE_ENABLE PHY_OFF(0x44)
-#define UFS_PHY_PWM_G1_CLK_DIVIDER PHY_OFF(0x08)
-#define UFS_PHY_PWM_G2_CLK_DIVIDER PHY_OFF(0x0C)
-#define UFS_PHY_PWM_G3_CLK_DIVIDER PHY_OFF(0x10)
-#define UFS_PHY_PWM_G4_CLK_DIVIDER PHY_OFF(0x14)
-#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER PHY_OFF(0x34)
-#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER PHY_OFF(0x38)
-#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER PHY_OFF(0x3C)
-#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER PHY_OFF(0x40)
-#define UFS_PHY_OMC_STATUS_RDVAL PHY_OFF(0x68)
-#define UFS_PHY_LINE_RESET_TIME PHY_OFF(0x28)
-#define UFS_PHY_LINE_RESET_GRANULARITY PHY_OFF(0x2C)
-#define UFS_PHY_TSYNC_RSYNC_CNTL PHY_OFF(0x48)
-#define UFS_PHY_PLL_CNTL PHY_OFF(0x50)
-#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x54)
-#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x5C)
-#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL PHY_OFF(0x58)
-#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL PHY_OFF(0x60)
-#define UFS_PHY_CFG_CHANGE_CNT_VAL PHY_OFF(0x64)
-#define UFS_PHY_RX_SYNC_WAIT_TIME PHY_OFF(0x6C)
-#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB4)
-#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE0)
-#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB8)
-#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE4)
-#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xBC)
-#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xE8)
-#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC)
-#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY PHY_OFF(0x100)
-#define UFS_PHY_RX_SIGDET_CTRL3 PHY_OFF(0x14c)
-#define UFS_PHY_RMMI_ATTR_CTRL PHY_OFF(0x160)
-#define UFS_PHY_RMMI_RX_CFGUPDT_L1 (1 << 7)
-#define UFS_PHY_RMMI_TX_CFGUPDT_L1 (1 << 6)
-#define UFS_PHY_RMMI_CFGWR_L1 (1 << 5)
-#define UFS_PHY_RMMI_CFGRD_L1 (1 << 4)
-#define UFS_PHY_RMMI_RX_CFGUPDT_L0 (1 << 3)
-#define UFS_PHY_RMMI_TX_CFGUPDT_L0 (1 << 2)
-#define UFS_PHY_RMMI_CFGWR_L0 (1 << 1)
-#define UFS_PHY_RMMI_CFGRD_L0 (1 << 0)
-#define UFS_PHY_RMMI_ATTRID PHY_OFF(0x164)
-#define UFS_PHY_RMMI_ATTRWRVAL PHY_OFF(0x168)
-#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS PHY_OFF(0x16C)
-#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS PHY_OFF(0x170)
-#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x174)
-
-#define UFS_PHY_TX_LANE_ENABLE_MASK 0x3
-
-/*
- * This structure represents the 20nm specific phy.
- * common_cfg MUST remain the first field in this structure
- * in case extra fields are added. This way, when calling
- * get_ufs_qcom_phy() of generic phy, we can extract the
- * common phy structure (struct ufs_qcom_phy) out of it
- * regardless of the relevant specific phy.
- */
-struct ufs_qcom_phy_qmp_20nm {
- struct ufs_qcom_phy common_cfg;
-};
-
-static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_2_0[] = {
- UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
- UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(0), 0x2F),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(0), 0x20),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(1), 0x2F),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(1), 0x20),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3),
-};
-
-static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_3_0[] = {
- UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
- UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x2b),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x38),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x3c),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_UP_OFFSET, 0x02),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_DN_OFFSET, 0x02),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x40),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3),
-};
-
-static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1e),
-};
-
-#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c
deleted file mode 100644
index 763c8d396af1..000000000000
--- a/drivers/phy/qualcomm/phy-qcom-ufs.c
+++ /dev/null
@@ -1,648 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
- */
-
-#include "phy-qcom-ufs-i.h"
-
-#define MAX_PROP_NAME 32
-#define VDDA_PHY_MIN_UV 1000000
-#define VDDA_PHY_MAX_UV 1000000
-#define VDDA_PLL_MIN_UV 1800000
-#define VDDA_PLL_MAX_UV 1800000
-#define VDDP_REF_CLK_MIN_UV 1200000
-#define VDDP_REF_CLK_MAX_UV 1200000
-
-int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
- struct ufs_qcom_phy_calibration *tbl_A,
- int tbl_size_A,
- struct ufs_qcom_phy_calibration *tbl_B,
- int tbl_size_B, bool is_rate_B)
-{
- int i;
- int ret = 0;
-
- if (!tbl_A) {
- dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__);
- ret = EINVAL;
- goto out;
- }
-
- for (i = 0; i < tbl_size_A; i++)
- writel_relaxed(tbl_A[i].cfg_value,
- ufs_qcom_phy->mmio + tbl_A[i].reg_offset);
-
- /*
- * In case we would like to work in rate B, we need
- * to override a registers that were configured in rate A table
- * with registers of rate B table.
- * table.
- */
- if (is_rate_B) {
- if (!tbl_B) {
- dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL",
- __func__);
- ret = EINVAL;
- goto out;
- }
-
- for (i = 0; i < tbl_size_B; i++)
- writel_relaxed(tbl_B[i].cfg_value,
- ufs_qcom_phy->mmio + tbl_B[i].reg_offset);
- }
-
- /* flush buffered writes */
- mb();
-
-out:
- return ret;
-}
-EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate);
-
-/*
- * This assumes the embedded phy structure inside generic_phy is of type
- * struct ufs_qcom_phy. In order to function properly it's crucial
- * to keep the embedded struct "struct ufs_qcom_phy common_cfg"
- * as the first inside generic_phy.
- */
-struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy)
-{
- return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy);
-}
-EXPORT_SYMBOL_GPL(get_ufs_qcom_phy);
-
-static
-int ufs_qcom_phy_base_init(struct platform_device *pdev,
- struct ufs_qcom_phy *phy_common)
-{
- struct device *dev = &pdev->dev;
- struct resource *res;
- int err = 0;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem");
- phy_common->mmio = devm_ioremap_resource(dev, res);
- if (IS_ERR((void const *)phy_common->mmio)) {
- err = PTR_ERR((void const *)phy_common->mmio);
- phy_common->mmio = NULL;
- dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n",
- __func__, err);
- return err;
- }
-
- /* "dev_ref_clk_ctrl_mem" is optional resource */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "dev_ref_clk_ctrl_mem");
- phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res);
- if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio))
- phy_common->dev_ref_clk_ctrl_mmio = NULL;
-
- return 0;
-}
-
-struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
- struct ufs_qcom_phy *common_cfg,
- const struct phy_ops *ufs_qcom_phy_gen_ops,
- struct ufs_qcom_phy_specific_ops *phy_spec_ops)
-{
- int err;
- struct device *dev = &pdev->dev;
- struct phy *generic_phy = NULL;
- struct phy_provider *phy_provider;
-
- err = ufs_qcom_phy_base_init(pdev, common_cfg);
- if (err) {
- dev_err(dev, "%s: phy base init failed %d\n", __func__, err);
- goto out;
- }
-
- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
- if (IS_ERR(phy_provider)) {
- err = PTR_ERR(phy_provider);
- dev_err(dev, "%s: failed to register phy %d\n", __func__, err);
- goto out;
- }
-
- generic_phy = devm_phy_create(dev, NULL, ufs_qcom_phy_gen_ops);
- if (IS_ERR(generic_phy)) {
- err = PTR_ERR(generic_phy);
- dev_err(dev, "%s: failed to create phy %d\n", __func__, err);
- generic_phy = NULL;
- goto out;
- }
-
- common_cfg->phy_spec_ops = phy_spec_ops;
- common_cfg->dev = dev;
-
-out:
- return generic_phy;
-}
-EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe);
-
-static int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common)
-{
- struct reset_control *reset;
-
- if (phy_common->ufs_reset)
- return 0;
-
- reset = devm_reset_control_get_exclusive_by_index(phy_common->dev, 0);
- if (IS_ERR(reset))
- return PTR_ERR(reset);
-
- phy_common->ufs_reset = reset;
- return 0;
-}
-
-static int __ufs_qcom_phy_clk_get(struct device *dev,
- const char *name, struct clk **clk_out, bool err_print)
-{
- struct clk *clk;
- int err = 0;
-
- clk = devm_clk_get(dev, name);
- if (IS_ERR(clk)) {
- err = PTR_ERR(clk);
- if (err_print)
- dev_err(dev, "failed to get %s err %d", name, err);
- } else {
- *clk_out = clk;
- }
-
- return err;
-}
-
-static int ufs_qcom_phy_clk_get(struct device *dev,
- const char *name, struct clk **clk_out)
-{
- return __ufs_qcom_phy_clk_get(dev, name, clk_out, true);
-}
-
-int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common)
-{
- int err;
-
- if (of_device_is_compatible(phy_common->dev->of_node,
- "qcom,msm8996-ufs-phy-qmp-14nm"))
- goto skip_txrx_clk;
-
- err = ufs_qcom_phy_clk_get(phy_common->dev, "tx_iface_clk",
- &phy_common->tx_iface_clk);
- if (err)
- goto out;
-
- err = ufs_qcom_phy_clk_get(phy_common->dev, "rx_iface_clk",
- &phy_common->rx_iface_clk);
- if (err)
- goto out;
-
-skip_txrx_clk:
- err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_src",
- &phy_common->ref_clk_src);
- if (err)
- goto out;
-
- /*
- * "ref_clk_parent" is optional hence don't abort init if it's not
- * found.
- */
- __ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_parent",
- &phy_common->ref_clk_parent, false);
-
- err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk",
- &phy_common->ref_clk);
-
-out:
- return err;
-}
-EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks);
-
-static int ufs_qcom_phy_init_vreg(struct device *dev,
- struct ufs_qcom_phy_vreg *vreg,
- const char *name)
-{
- int err = 0;
-
- char prop_name[MAX_PROP_NAME];
-
- vreg->name = name;
- vreg->reg = devm_regulator_get(dev, name);
- if (IS_ERR(vreg->reg)) {
- err = PTR_ERR(vreg->reg);
- dev_err(dev, "failed to get %s, %d\n", name, err);
- goto out;
- }
-
- if (dev->of_node) {
- snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name);
- err = of_property_read_u32(dev->of_node,
- prop_name, &vreg->max_uA);
- if (err && err != -EINVAL) {
- dev_err(dev, "%s: failed to read %s\n",
- __func__, prop_name);
- goto out;
- } else if (err == -EINVAL || !vreg->max_uA) {
- if (regulator_count_voltages(vreg->reg) > 0) {
- dev_err(dev, "%s: %s is mandatory\n",
- __func__, prop_name);
- goto out;
- }
- err = 0;
- }
- }
-
- if (!strcmp(name, "vdda-pll")) {
- vreg->max_uV = VDDA_PLL_MAX_UV;
- vreg->min_uV = VDDA_PLL_MIN_UV;
- } else if (!strcmp(name, "vdda-phy")) {
- vreg->max_uV = VDDA_PHY_MAX_UV;
- vreg->min_uV = VDDA_PHY_MIN_UV;
- } else if (!strcmp(name, "vddp-ref-clk")) {
- vreg->max_uV = VDDP_REF_CLK_MAX_UV;
- vreg->min_uV = VDDP_REF_CLK_MIN_UV;
- }
-
-out:
- return err;
-}
-
-int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common)
-{
- int err;
-
- err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_pll,
- "vdda-pll");
- if (err)
- goto out;
-
- err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_phy,
- "vdda-phy");
-
- if (err)
- goto out;
-
- err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk,
- "vddp-ref-clk");
-
-out:
- return err;
-}
-EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_vregulators);
-
-static int ufs_qcom_phy_cfg_vreg(struct device *dev,
- struct ufs_qcom_phy_vreg *vreg, bool on)
-{
- int ret = 0;
- struct regulator *reg = vreg->reg;
- const char *name = vreg->name;
- int min_uV;
- int uA_load;
-
- if (regulator_count_voltages(reg) > 0) {
- min_uV = on ? vreg->min_uV : 0;
- ret = regulator_set_voltage(reg, min_uV, vreg->max_uV);
- if (ret) {
- dev_err(dev, "%s: %s set voltage failed, err=%d\n",
- __func__, name, ret);
- goto out;
- }
- uA_load = on ? vreg->max_uA : 0;
- ret = regulator_set_load(reg, uA_load);
- if (ret >= 0) {
- /*
- * regulator_set_load() returns new regulator
- * mode upon success.
- */
- ret = 0;
- } else {
- dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n",
- __func__, name, uA_load, ret);
- goto out;
- }
- }
-out:
- return ret;
-}
-
-static int ufs_qcom_phy_enable_vreg(struct device *dev,
- struct ufs_qcom_phy_vreg *vreg)
-{
- int ret = 0;
-
- if (!vreg || vreg->enabled)
- goto out;
-
- ret = ufs_qcom_phy_cfg_vreg(dev, vreg, true);
- if (ret) {
- dev_err(dev, "%s: ufs_qcom_phy_cfg_vreg() failed, err=%d\n",
- __func__, ret);
- goto out;
- }
-
- ret = regulator_enable(vreg->reg);
- if (ret) {
- dev_err(dev, "%s: enable failed, err=%d\n",
- __func__, ret);
- goto out;
- }
-
- vreg->enabled = true;
-out:
- return ret;
-}
-
-static int ufs_qcom_phy_enable_ref_clk(struct ufs_qcom_phy *phy)
-{
- int ret = 0;
-
- if (phy->is_ref_clk_enabled)
- goto out;
-
- /*
- * reference clock is propagated in a daisy-chained manner from
- * source to phy, so ungate them at each stage.
- */
- ret = clk_prepare_enable(phy->ref_clk_src);
- if (ret) {
- dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n",
- __func__, ret);
- goto out;
- }
-
- /*
- * "ref_clk_parent" is optional clock hence make sure that clk reference
- * is available before trying to enable the clock.
- */
- if (phy->ref_clk_parent) {
- ret = clk_prepare_enable(phy->ref_clk_parent);
- if (ret) {
- dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n",
- __func__, ret);
- goto out_disable_src;
- }
- }
-
- ret = clk_prepare_enable(phy->ref_clk);
- if (ret) {
- dev_err(phy->dev, "%s: ref_clk enable failed %d\n",
- __func__, ret);
- goto out_disable_parent;
- }
-
- phy->is_ref_clk_enabled = true;
- goto out;
-
-out_disable_parent:
- if (phy->ref_clk_parent)
- clk_disable_unprepare(phy->ref_clk_parent);
-out_disable_src:
- clk_disable_unprepare(phy->ref_clk_src);
-out:
- return ret;
-}
-
-static int ufs_qcom_phy_disable_vreg(struct device *dev,
- struct ufs_qcom_phy_vreg *vreg)
-{
- int ret = 0;
-
- if (!vreg || !vreg->enabled)
- goto out;
-
- ret = regulator_disable(vreg->reg);
-
- if (!ret) {
- /* ignore errors on applying disable config */
- ufs_qcom_phy_cfg_vreg(dev, vreg, false);
- vreg->enabled = false;
- } else {
- dev_err(dev, "%s: %s disable failed, err=%d\n",
- __func__, vreg->name, ret);
- }
-out:
- return ret;
-}
-
-static void ufs_qcom_phy_disable_ref_clk(struct ufs_qcom_phy *phy)
-{
- if (phy->is_ref_clk_enabled) {
- clk_disable_unprepare(phy->ref_clk);
- /*
- * "ref_clk_parent" is optional clock hence make sure that clk
- * reference is available before trying to disable the clock.
- */
- if (phy->ref_clk_parent)
- clk_disable_unprepare(phy->ref_clk_parent);
- clk_disable_unprepare(phy->ref_clk_src);
- phy->is_ref_clk_enabled = false;
- }
-}
-
-/* Turn ON M-PHY RMMI interface clocks */
-static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy)
-{
- int ret = 0;
-
- if (phy->is_iface_clk_enabled)
- goto out;
-
- ret = clk_prepare_enable(phy->tx_iface_clk);
- if (ret) {
- dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n",
- __func__, ret);
- goto out;
- }
- ret = clk_prepare_enable(phy->rx_iface_clk);
- if (ret) {
- clk_disable_unprepare(phy->tx_iface_clk);
- dev_err(phy->dev, "%s: rx_iface_clk enable failed %d. disabling also tx_iface_clk\n",
- __func__, ret);
- goto out;
- }
- phy->is_iface_clk_enabled = true;
-
-out:
- return ret;
-}
-
-/* Turn OFF M-PHY RMMI interface clocks */
-static void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy)
-{
- if (phy->is_iface_clk_enabled) {
- clk_disable_unprepare(phy->tx_iface_clk);
- clk_disable_unprepare(phy->rx_iface_clk);
- phy->is_iface_clk_enabled = false;
- }
-}
-
-static int ufs_qcom_phy_start_serdes(struct ufs_qcom_phy *ufs_qcom_phy)
-{
- int ret = 0;
-
- if (!ufs_qcom_phy->phy_spec_ops->start_serdes) {
- dev_err(ufs_qcom_phy->dev, "%s: start_serdes() callback is not supported\n",
- __func__);
- ret = -ENOTSUPP;
- } else {
- ufs_qcom_phy->phy_spec_ops->start_serdes(ufs_qcom_phy);
- }
-
- return ret;
-}
-
-int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes)
-{
- struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
- int ret = 0;
-
- if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) {
- dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n",
- __func__);
- ret = -ENOTSUPP;
- } else {
- ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy,
- tx_lanes);
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(ufs_qcom_phy_set_tx_lane_enable);
-
-void ufs_qcom_phy_save_controller_version(struct phy *generic_phy,
- u8 major, u16 minor, u16 step)
-{
- struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
-
- ufs_qcom_phy->host_ctrl_rev_major = major;
- ufs_qcom_phy->host_ctrl_rev_minor = minor;
- ufs_qcom_phy->host_ctrl_rev_step = step;
-}
-EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version);
-
-static int ufs_qcom_phy_is_pcs_ready(struct ufs_qcom_phy *ufs_qcom_phy)
-{
- if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) {
- dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n",
- __func__);
- return -ENOTSUPP;
- }
-
- return ufs_qcom_phy->phy_spec_ops->
- is_physical_coding_sublayer_ready(ufs_qcom_phy);
-}
-
-int ufs_qcom_phy_power_on(struct phy *generic_phy)
-{
- struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
- struct device *dev = phy_common->dev;
- bool is_rate_B = false;
- int err;
-
- err = ufs_qcom_phy_get_reset(phy_common);
- if (err)
- return err;
-
- err = reset_control_assert(phy_common->ufs_reset);
- if (err)
- return err;
-
- if (phy_common->mode == PHY_MODE_UFS_HS_B)
- is_rate_B = true;
-
- err = phy_common->phy_spec_ops->calibrate(phy_common, is_rate_B);
- if (err)
- return err;
-
- err = reset_control_deassert(phy_common->ufs_reset);
- if (err) {
- dev_err(dev, "Failed to assert UFS PHY reset");
- return err;
- }
-
- err = ufs_qcom_phy_start_serdes(phy_common);
- if (err)
- return err;
-
- err = ufs_qcom_phy_is_pcs_ready(phy_common);
- if (err)
- return err;
-
- err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy);
- if (err) {
- dev_err(dev, "%s enable vdda_phy failed, err=%d\n",
- __func__, err);
- goto out;
- }
-
- phy_common->phy_spec_ops->power_control(phy_common, true);
-
- /* vdda_pll also enables ref clock LDOs so enable it first */
- err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_pll);
- if (err) {
- dev_err(dev, "%s enable vdda_pll failed, err=%d\n",
- __func__, err);
- goto out_disable_phy;
- }
-
- err = ufs_qcom_phy_enable_iface_clk(phy_common);
- if (err) {
- dev_err(dev, "%s enable phy iface clock failed, err=%d\n",
- __func__, err);
- goto out_disable_pll;
- }
-
- err = ufs_qcom_phy_enable_ref_clk(phy_common);
- if (err) {
- dev_err(dev, "%s enable phy ref clock failed, err=%d\n",
- __func__, err);
- goto out_disable_iface_clk;
- }
-
- /* enable device PHY ref_clk pad rail */
- if (phy_common->vddp_ref_clk.reg) {
- err = ufs_qcom_phy_enable_vreg(dev,
- &phy_common->vddp_ref_clk);
- if (err) {
- dev_err(dev, "%s enable vddp_ref_clk failed, err=%d\n",
- __func__, err);
- goto out_disable_ref_clk;
- }
- }
-
- goto out;
-
-out_disable_ref_clk:
- ufs_qcom_phy_disable_ref_clk(phy_common);
-out_disable_iface_clk:
- ufs_qcom_phy_disable_iface_clk(phy_common);
-out_disable_pll:
- ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_pll);
-out_disable_phy:
- ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_phy);
-out:
- return err;
-}
-EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_on);
-
-int ufs_qcom_phy_power_off(struct phy *generic_phy)
-{
- struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
-
- phy_common->phy_spec_ops->power_control(phy_common, false);
-
- if (phy_common->vddp_ref_clk.reg)
- ufs_qcom_phy_disable_vreg(phy_common->dev,
- &phy_common->vddp_ref_clk);
- ufs_qcom_phy_disable_ref_clk(phy_common);
- ufs_qcom_phy_disable_iface_clk(phy_common);
-
- ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_pll);
- ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy);
- reset_control_assert(phy_common->ufs_reset);
- return 0;
-}
-EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off);
-
-MODULE_AUTHOR("Yaniv Gardi <ygardi@codeaurora.org>");
-MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
-MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c b/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c
new file mode 100644
index 000000000000..8807e59a1162
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2009-2018, Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, Linaro Limited
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+/* PHY register and bit definitions */
+#define PHY_CTRL_COMMON0 0x078
+#define SIDDQ BIT(2)
+#define PHY_IRQ_CMD 0x0d0
+#define PHY_INTR_MASK0 0x0d4
+#define PHY_INTR_CLEAR0 0x0dc
+#define DPDM_MASK 0x1e
+#define DP_1_0 BIT(4)
+#define DP_0_1 BIT(3)
+#define DM_1_0 BIT(2)
+#define DM_0_1 BIT(1)
+
+enum hsphy_voltage {
+ VOL_NONE,
+ VOL_MIN,
+ VOL_MAX,
+ VOL_NUM,
+};
+
+enum hsphy_vreg {
+ VDD,
+ VDDA_1P8,
+ VDDA_3P3,
+ VREG_NUM,
+};
+
+struct hsphy_init_seq {
+ int offset;
+ int val;
+ int delay;
+};
+
+struct hsphy_data {
+ const struct hsphy_init_seq *init_seq;
+ unsigned int init_seq_num;
+};
+
+struct hsphy_priv {
+ void __iomem *base;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ struct reset_control *phy_reset;
+ struct reset_control *por_reset;
+ struct regulator_bulk_data vregs[VREG_NUM];
+ const struct hsphy_data *data;
+ enum phy_mode mode;
+};
+
+static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode,
+ int submode)
+{
+ struct hsphy_priv *priv = phy_get_drvdata(phy);
+
+ priv->mode = PHY_MODE_INVALID;
+
+ if (mode > 0)
+ priv->mode = mode;
+
+ return 0;
+}
+
+static void qcom_snps_hsphy_enable_hv_interrupts(struct hsphy_priv *priv)
+{
+ u32 val;
+
+ /* Clear any existing interrupts before enabling the interrupts */
+ val = readb(priv->base + PHY_INTR_CLEAR0);
+ val |= DPDM_MASK;
+ writeb(val, priv->base + PHY_INTR_CLEAR0);
+
+ writeb(0x0, priv->base + PHY_IRQ_CMD);
+ usleep_range(200, 220);
+ writeb(0x1, priv->base + PHY_IRQ_CMD);
+
+ /* Make sure the interrupts are cleared */
+ usleep_range(200, 220);
+
+ val = readb(priv->base + PHY_INTR_MASK0);
+ switch (priv->mode) {
+ case PHY_MODE_USB_HOST_HS:
+ case PHY_MODE_USB_HOST_FS:
+ case PHY_MODE_USB_DEVICE_HS:
+ case PHY_MODE_USB_DEVICE_FS:
+ val |= DP_1_0 | DM_0_1;
+ break;
+ case PHY_MODE_USB_HOST_LS:
+ case PHY_MODE_USB_DEVICE_LS:
+ val |= DP_0_1 | DM_1_0;
+ break;
+ default:
+ /* No device connected */
+ val |= DP_0_1 | DM_0_1;
+ break;
+ }
+ writeb(val, priv->base + PHY_INTR_MASK0);
+}
+
+static void qcom_snps_hsphy_disable_hv_interrupts(struct hsphy_priv *priv)
+{
+ u32 val;
+
+ val = readb(priv->base + PHY_INTR_MASK0);
+ val &= ~DPDM_MASK;
+ writeb(val, priv->base + PHY_INTR_MASK0);
+
+ /* Clear any pending interrupts */
+ val = readb(priv->base + PHY_INTR_CLEAR0);
+ val |= DPDM_MASK;
+ writeb(val, priv->base + PHY_INTR_CLEAR0);
+
+ writeb(0x0, priv->base + PHY_IRQ_CMD);
+ usleep_range(200, 220);
+
+ writeb(0x1, priv->base + PHY_IRQ_CMD);
+ usleep_range(200, 220);
+}
+
+static void qcom_snps_hsphy_enter_retention(struct hsphy_priv *priv)
+{
+ u32 val;
+
+ val = readb(priv->base + PHY_CTRL_COMMON0);
+ val |= SIDDQ;
+ writeb(val, priv->base + PHY_CTRL_COMMON0);
+}
+
+static void qcom_snps_hsphy_exit_retention(struct hsphy_priv *priv)
+{
+ u32 val;
+
+ val = readb(priv->base + PHY_CTRL_COMMON0);
+ val &= ~SIDDQ;
+ writeb(val, priv->base + PHY_CTRL_COMMON0);
+}
+
+static int qcom_snps_hsphy_power_on(struct phy *phy)
+{
+ struct hsphy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = regulator_bulk_enable(VREG_NUM, priv->vregs);
+ if (ret)
+ return ret;
+
+ qcom_snps_hsphy_disable_hv_interrupts(priv);
+ qcom_snps_hsphy_exit_retention(priv);
+
+ return 0;
+}
+
+static int qcom_snps_hsphy_power_off(struct phy *phy)
+{
+ struct hsphy_priv *priv = phy_get_drvdata(phy);
+
+ qcom_snps_hsphy_enter_retention(priv);
+ qcom_snps_hsphy_enable_hv_interrupts(priv);
+ regulator_bulk_disable(VREG_NUM, priv->vregs);
+
+ return 0;
+}
+
+static int qcom_snps_hsphy_reset(struct hsphy_priv *priv)
+{
+ int ret;
+
+ ret = reset_control_assert(priv->phy_reset);
+ if (ret)
+ return ret;
+
+ usleep_range(10, 15);
+
+ ret = reset_control_deassert(priv->phy_reset);
+ if (ret)
+ return ret;
+
+ usleep_range(80, 100);
+
+ return 0;
+}
+
+static void qcom_snps_hsphy_init_sequence(struct hsphy_priv *priv)
+{
+ const struct hsphy_data *data = priv->data;
+ const struct hsphy_init_seq *seq;
+ int i;
+
+ /* Device match data is optional. */
+ if (!data)
+ return;
+
+ seq = data->init_seq;
+
+ for (i = 0; i < data->init_seq_num; i++, seq++) {
+ writeb(seq->val, priv->base + seq->offset);
+ if (seq->delay)
+ usleep_range(seq->delay, seq->delay + 10);
+ }
+}
+
+static int qcom_snps_hsphy_por_reset(struct hsphy_priv *priv)
+{
+ int ret;
+
+ ret = reset_control_assert(priv->por_reset);
+ if (ret)
+ return ret;
+
+ /*
+ * The Femto PHY is POR reset in the following scenarios.
+ *
+ * 1. After overriding the parameter registers.
+ * 2. Low power mode exit from PHY retention.
+ *
+ * Ensure that SIDDQ is cleared before bringing the PHY
+ * out of reset.
+ */
+ qcom_snps_hsphy_exit_retention(priv);
+
+ /*
+ * As per databook, 10 usec delay is required between
+ * PHY POR assert and de-assert.
+ */
+ usleep_range(10, 20);
+ ret = reset_control_deassert(priv->por_reset);
+ if (ret)
+ return ret;
+
+ /*
+ * As per databook, it takes 75 usec for PHY to stabilize
+ * after the reset.
+ */
+ usleep_range(80, 100);
+
+ return 0;
+}
+
+static int qcom_snps_hsphy_init(struct phy *phy)
+{
+ struct hsphy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
+ if (ret)
+ return ret;
+
+ ret = qcom_snps_hsphy_reset(priv);
+ if (ret)
+ goto disable_clocks;
+
+ qcom_snps_hsphy_init_sequence(priv);
+
+ ret = qcom_snps_hsphy_por_reset(priv);
+ if (ret)
+ goto disable_clocks;
+
+ return 0;
+
+disable_clocks:
+ clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+ return ret;
+}
+
+static int qcom_snps_hsphy_exit(struct phy *phy)
+{
+ struct hsphy_priv *priv = phy_get_drvdata(phy);
+
+ clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+
+ return 0;
+}
+
+static const struct phy_ops qcom_snps_hsphy_ops = {
+ .init = qcom_snps_hsphy_init,
+ .exit = qcom_snps_hsphy_exit,
+ .power_on = qcom_snps_hsphy_power_on,
+ .power_off = qcom_snps_hsphy_power_off,
+ .set_mode = qcom_snps_hsphy_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static const char * const qcom_snps_hsphy_clks[] = {
+ "ref",
+ "ahb",
+ "sleep",
+};
+
+static int qcom_snps_hsphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *provider;
+ struct hsphy_priv *priv;
+ struct phy *phy;
+ int ret;
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->num_clks = ARRAY_SIZE(qcom_snps_hsphy_clks);
+ priv->clks = devm_kcalloc(dev, priv->num_clks, sizeof(*priv->clks),
+ GFP_KERNEL);
+ if (!priv->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < priv->num_clks; i++)
+ priv->clks[i].id = qcom_snps_hsphy_clks[i];
+
+ ret = devm_clk_bulk_get(dev, priv->num_clks, priv->clks);
+ if (ret)
+ return ret;
+
+ priv->phy_reset = devm_reset_control_get_exclusive(dev, "phy");
+ if (IS_ERR(priv->phy_reset))
+ return PTR_ERR(priv->phy_reset);
+
+ priv->por_reset = devm_reset_control_get_exclusive(dev, "por");
+ if (IS_ERR(priv->por_reset))
+ return PTR_ERR(priv->por_reset);
+
+ priv->vregs[VDD].supply = "vdd";
+ priv->vregs[VDDA_1P8].supply = "vdda1p8";
+ priv->vregs[VDDA_3P3].supply = "vdda3p3";
+
+ ret = devm_regulator_bulk_get(dev, VREG_NUM, priv->vregs);
+ if (ret)
+ return ret;
+
+ /* Get device match data */
+ priv->data = device_get_match_data(dev);
+
+ phy = devm_phy_create(dev, dev->of_node, &qcom_snps_hsphy_ops);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ phy_set_drvdata(phy, priv);
+
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(provider))
+ return PTR_ERR(provider);
+
+ ret = regulator_set_load(priv->vregs[VDDA_1P8].consumer, 19000);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_set_load(priv->vregs[VDDA_3P3].consumer, 16000);
+ if (ret < 0)
+ goto unset_1p8_load;
+
+ return 0;
+
+unset_1p8_load:
+ regulator_set_load(priv->vregs[VDDA_1P8].consumer, 0);
+
+ return ret;
+}
+
+/*
+ * The macro is used to define an initialization sequence. Each tuple
+ * is meant to program 'value' into phy register at 'offset' with 'delay'
+ * in us followed.
+ */
+#define HSPHY_INIT_CFG(o, v, d) { .offset = o, .val = v, .delay = d, }
+
+static const struct hsphy_init_seq init_seq_femtophy[] = {
+ HSPHY_INIT_CFG(0xc0, 0x01, 0),
+ HSPHY_INIT_CFG(0xe8, 0x0d, 0),
+ HSPHY_INIT_CFG(0x74, 0x12, 0),
+ HSPHY_INIT_CFG(0x98, 0x63, 0),
+ HSPHY_INIT_CFG(0x9c, 0x03, 0),
+ HSPHY_INIT_CFG(0xa0, 0x1d, 0),
+ HSPHY_INIT_CFG(0xa4, 0x03, 0),
+ HSPHY_INIT_CFG(0x8c, 0x23, 0),
+ HSPHY_INIT_CFG(0x78, 0x08, 0),
+ HSPHY_INIT_CFG(0x7c, 0xdc, 0),
+ HSPHY_INIT_CFG(0x90, 0xe0, 20),
+ HSPHY_INIT_CFG(0x74, 0x10, 0),
+ HSPHY_INIT_CFG(0x90, 0x60, 0),
+};
+
+static const struct hsphy_init_seq init_seq_mdm9607[] = {
+ HSPHY_INIT_CFG(0x80, 0x44, 0),
+ HSPHY_INIT_CFG(0x81, 0x38, 0),
+ HSPHY_INIT_CFG(0x82, 0x24, 0),
+ HSPHY_INIT_CFG(0x83, 0x13, 0),
+};
+
+static const struct hsphy_data hsphy_data_femtophy = {
+ .init_seq = init_seq_femtophy,
+ .init_seq_num = ARRAY_SIZE(init_seq_femtophy),
+};
+
+static const struct hsphy_data hsphy_data_mdm9607 = {
+ .init_seq = init_seq_mdm9607,
+ .init_seq_num = ARRAY_SIZE(init_seq_mdm9607),
+};
+
+static const struct of_device_id qcom_snps_hsphy_match[] = {
+ { .compatible = "qcom,usb-hs-28nm-femtophy", .data = &hsphy_data_femtophy, },
+ { .compatible = "qcom,usb-hs-28nm-mdm9607", .data = &hsphy_data_mdm9607, },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_match);
+
+static struct platform_driver qcom_snps_hsphy_driver = {
+ .probe = qcom_snps_hsphy_probe,
+ .driver = {
+ .name = "qcom,usb-hs-28nm-phy",
+ .of_match_table = qcom_snps_hsphy_match,
+ },
+};
+module_platform_driver(qcom_snps_hsphy_driver);
+
+MODULE_DESCRIPTION("Qualcomm 28nm Hi-Speed USB PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs.c b/drivers/phy/qualcomm/phy-qcom-usb-hs.c
index 61054272a7c8..53e46c220a3a 100644
--- a/drivers/phy/qualcomm/phy-qcom-usb-hs.c
+++ b/drivers/phy/qualcomm/phy-qcom-usb-hs.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/**
+/*
* Copyright (C) 2016 Linaro Ltd
*/
#include <linux/module.h>
@@ -53,9 +53,10 @@ static int qcom_usb_hs_phy_set_mode(struct phy *phy,
case PHY_MODE_USB_OTG:
case PHY_MODE_USB_HOST:
val |= ULPI_INT_IDGRD;
- /* fall through */
+ fallthrough;
case PHY_MODE_USB_DEVICE:
val |= ULPI_INT_SESS_VALID;
+ break;
default:
break;
}
diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hsic.c b/drivers/phy/qualcomm/phy-qcom-usb-hsic.c
index 04d18d52f700..20f6dd37c7c1 100644
--- a/drivers/phy/qualcomm/phy-qcom-usb-hsic.c
+++ b/drivers/phy/qualcomm/phy-qcom-usb-hsic.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/**
+/*
* Copyright (C) 2016 Linaro Ltd
*/
#include <linux/module.h>
@@ -54,8 +54,10 @@ static int qcom_usb_hsic_phy_power_on(struct phy *phy)
/* Configure pins for HSIC functionality */
pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT);
- if (IS_ERR(pins_default))
- return PTR_ERR(pins_default);
+ if (IS_ERR(pins_default)) {
+ ret = PTR_ERR(pins_default);
+ goto err_ulpi;
+ }
ret = pinctrl_select_state(uphy->pctl, pins_default);
if (ret)
diff --git a/drivers/phy/qualcomm/phy-qcom-usb-ss.c b/drivers/phy/qualcomm/phy-qcom-usb-ss.c
new file mode 100644
index 000000000000..a3a6d3ce7ea1
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-usb-ss.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012-2014,2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, Linaro Limited
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#define PHY_CTRL0 0x6C
+#define PHY_CTRL1 0x70
+#define PHY_CTRL2 0x74
+#define PHY_CTRL4 0x7C
+
+/* PHY_CTRL bits */
+#define REF_PHY_EN BIT(0)
+#define LANE0_PWR_ON BIT(2)
+#define SWI_PCS_CLK_SEL BIT(4)
+#define TST_PWR_DOWN BIT(4)
+#define PHY_RESET BIT(7)
+
+#define NUM_BULK_CLKS 3
+#define NUM_BULK_REGS 2
+
+struct ssphy_priv {
+ void __iomem *base;
+ struct device *dev;
+ struct reset_control *reset_com;
+ struct reset_control *reset_phy;
+ struct regulator_bulk_data regs[NUM_BULK_REGS];
+ struct clk_bulk_data clks[NUM_BULK_CLKS];
+ enum phy_mode mode;
+};
+
+static inline void qcom_ssphy_updatel(void __iomem *addr, u32 mask, u32 val)
+{
+ writel((readl(addr) & ~mask) | val, addr);
+}
+
+static int qcom_ssphy_do_reset(struct ssphy_priv *priv)
+{
+ int ret;
+
+ if (!priv->reset_com) {
+ qcom_ssphy_updatel(priv->base + PHY_CTRL1, PHY_RESET,
+ PHY_RESET);
+ usleep_range(10, 20);
+ qcom_ssphy_updatel(priv->base + PHY_CTRL1, PHY_RESET, 0);
+ } else {
+ ret = reset_control_assert(priv->reset_com);
+ if (ret) {
+ dev_err(priv->dev, "Failed to assert reset com\n");
+ return ret;
+ }
+
+ ret = reset_control_assert(priv->reset_phy);
+ if (ret) {
+ dev_err(priv->dev, "Failed to assert reset phy\n");
+ return ret;
+ }
+
+ usleep_range(10, 20);
+
+ ret = reset_control_deassert(priv->reset_com);
+ if (ret) {
+ dev_err(priv->dev, "Failed to deassert reset com\n");
+ return ret;
+ }
+
+ ret = reset_control_deassert(priv->reset_phy);
+ if (ret) {
+ dev_err(priv->dev, "Failed to deassert reset phy\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int qcom_ssphy_power_on(struct phy *phy)
+{
+ struct ssphy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = regulator_bulk_enable(NUM_BULK_REGS, priv->regs);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(NUM_BULK_CLKS, priv->clks);
+ if (ret)
+ goto err_disable_regulator;
+
+ ret = qcom_ssphy_do_reset(priv);
+ if (ret)
+ goto err_disable_clock;
+
+ writeb(SWI_PCS_CLK_SEL, priv->base + PHY_CTRL0);
+ qcom_ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, LANE0_PWR_ON);
+ qcom_ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, REF_PHY_EN);
+ qcom_ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, 0);
+
+ return 0;
+err_disable_clock:
+ clk_bulk_disable_unprepare(NUM_BULK_CLKS, priv->clks);
+err_disable_regulator:
+ regulator_bulk_disable(NUM_BULK_REGS, priv->regs);
+
+ return ret;
+}
+
+static int qcom_ssphy_power_off(struct phy *phy)
+{
+ struct ssphy_priv *priv = phy_get_drvdata(phy);
+
+ qcom_ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, 0);
+ qcom_ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, 0);
+ qcom_ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, TST_PWR_DOWN);
+
+ clk_bulk_disable_unprepare(NUM_BULK_CLKS, priv->clks);
+ regulator_bulk_disable(NUM_BULK_REGS, priv->regs);
+
+ return 0;
+}
+
+static int qcom_ssphy_init_clock(struct ssphy_priv *priv)
+{
+ priv->clks[0].id = "ref";
+ priv->clks[1].id = "ahb";
+ priv->clks[2].id = "pipe";
+
+ return devm_clk_bulk_get(priv->dev, NUM_BULK_CLKS, priv->clks);
+}
+
+static int qcom_ssphy_init_regulator(struct ssphy_priv *priv)
+{
+ int ret;
+
+ priv->regs[0].supply = "vdd";
+ priv->regs[1].supply = "vdda1p8";
+ ret = devm_regulator_bulk_get(priv->dev, NUM_BULK_REGS, priv->regs);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(priv->dev, "Failed to get regulators\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int qcom_ssphy_init_reset(struct ssphy_priv *priv)
+{
+ priv->reset_com = devm_reset_control_get_optional_exclusive(priv->dev, "com");
+ if (IS_ERR(priv->reset_com)) {
+ dev_err(priv->dev, "Failed to get reset control com\n");
+ return PTR_ERR(priv->reset_com);
+ }
+
+ if (priv->reset_com) {
+ /* if reset_com is present, reset_phy is no longer optional */
+ priv->reset_phy = devm_reset_control_get_exclusive(priv->dev, "phy");
+ if (IS_ERR(priv->reset_phy)) {
+ dev_err(priv->dev, "Failed to get reset control phy\n");
+ return PTR_ERR(priv->reset_phy);
+ }
+ }
+
+ return 0;
+}
+
+static const struct phy_ops qcom_ssphy_ops = {
+ .power_off = qcom_ssphy_power_off,
+ .power_on = qcom_ssphy_power_on,
+ .owner = THIS_MODULE,
+};
+
+static int qcom_ssphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *provider;
+ struct ssphy_priv *priv;
+ struct phy *phy;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(struct ssphy_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->mode = PHY_MODE_INVALID;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ ret = qcom_ssphy_init_clock(priv);
+ if (ret)
+ return ret;
+
+ ret = qcom_ssphy_init_reset(priv);
+ if (ret)
+ return ret;
+
+ ret = qcom_ssphy_init_regulator(priv);
+ if (ret)
+ return ret;
+
+ phy = devm_phy_create(dev, dev->of_node, &qcom_ssphy_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "Failed to create the SS phy\n");
+ return PTR_ERR(phy);
+ }
+
+ phy_set_drvdata(phy, priv);
+
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id qcom_ssphy_match[] = {
+ { .compatible = "qcom,usb-ss-28nm-phy", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qcom_ssphy_match);
+
+static struct platform_driver qcom_ssphy_driver = {
+ .probe = qcom_ssphy_probe,
+ .driver = {
+ .name = "qcom-usb-ssphy",
+ .of_match_table = qcom_ssphy_match,
+ },
+};
+module_platform_driver(qcom_ssphy_driver);
+
+MODULE_DESCRIPTION("Qualcomm SuperSpeed USB PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/ralink/Kconfig b/drivers/phy/ralink/Kconfig
index da982c9cffb3..c2373b30b8a6 100644
--- a/drivers/phy/ralink/Kconfig
+++ b/drivers/phy/ralink/Kconfig
@@ -2,6 +2,14 @@
#
# PHY drivers for Ralink platforms.
#
+config PHY_MT7621_PCI
+ tristate "MediaTek MT7621 PCI PHY Driver"
+ depends on (RALINK && OF) || COMPILE_TEST
+ select GENERIC_PHY
+ select REGMAP_MMIO
+ help
+ Say 'Y' here to add support for MediaTek MT7621 PCI PHY driver,
+
config PHY_RALINK_USB
tristate "Ralink USB PHY driver"
depends on RALINK || COMPILE_TEST
diff --git a/drivers/phy/ralink/Makefile b/drivers/phy/ralink/Makefile
index d8d3ffcf0a15..cda2a4a7ca5e 100644
--- a/drivers/phy/ralink/Makefile
+++ b/drivers/phy/ralink/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PHY_MT7621_PCI) += phy-mt7621-pci.o
obj-$(CONFIG_PHY_RALINK_USB) += phy-ralink-usb.o
diff --git a/drivers/phy/ralink/phy-mt7621-pci.c b/drivers/phy/ralink/phy-mt7621-pci.c
new file mode 100644
index 000000000000..85888ab2d307
--- /dev/null
+++ b/drivers/phy/ralink/phy-mt7621-pci.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Mediatek MT7621 PCI PHY Driver
+ * Author: Sergio Paracuellos <sergio.paracuellos@gmail.com>
+ */
+
+#include <dt-bindings/phy/phy.h>
+#include <linux/clk.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/module.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 <linux/sys_soc.h>
+
+#define RG_PE1_PIPE_REG 0x02c
+#define RG_PE1_PIPE_RST BIT(12)
+#define RG_PE1_PIPE_CMD_FRC BIT(4)
+
+#define RG_P0_TO_P1_WIDTH 0x100
+#define RG_PE1_H_LCDDS_REG 0x49c
+#define RG_PE1_H_LCDDS_PCW GENMASK(30, 0)
+
+#define RG_PE1_FRC_H_XTAL_REG 0x400
+#define RG_PE1_FRC_H_XTAL_TYPE BIT(8)
+#define RG_PE1_H_XTAL_TYPE GENMASK(10, 9)
+
+#define RG_PE1_FRC_PHY_REG 0x000
+#define RG_PE1_FRC_PHY_EN BIT(4)
+#define RG_PE1_PHY_EN BIT(5)
+
+#define RG_PE1_H_PLL_REG 0x490
+#define RG_PE1_H_PLL_BC GENMASK(23, 22)
+#define RG_PE1_H_PLL_BP GENMASK(21, 18)
+#define RG_PE1_H_PLL_IR GENMASK(15, 12)
+#define RG_PE1_H_PLL_IC GENMASK(11, 8)
+#define RG_PE1_H_PLL_PREDIV GENMASK(7, 6)
+#define RG_PE1_PLL_DIVEN GENMASK(3, 1)
+
+#define RG_PE1_H_PLL_FBKSEL_REG 0x4bc
+#define RG_PE1_H_PLL_FBKSEL GENMASK(5, 4)
+
+#define RG_PE1_H_LCDDS_SSC_PRD_REG 0x4a4
+#define RG_PE1_H_LCDDS_SSC_PRD GENMASK(15, 0)
+
+#define RG_PE1_H_LCDDS_SSC_DELTA_REG 0x4a8
+#define RG_PE1_H_LCDDS_SSC_DELTA GENMASK(11, 0)
+#define RG_PE1_H_LCDDS_SSC_DELTA1 GENMASK(27, 16)
+
+#define RG_PE1_LCDDS_CLK_PH_INV_REG 0x4a0
+#define RG_PE1_LCDDS_CLK_PH_INV BIT(5)
+
+#define RG_PE1_H_PLL_BR_REG 0x4ac
+#define RG_PE1_H_PLL_BR GENMASK(18, 16)
+
+#define RG_PE1_MSTCKDIV_REG 0x414
+#define RG_PE1_MSTCKDIV GENMASK(7, 6)
+
+#define RG_PE1_FRC_MSTCKDIV BIT(5)
+
+#define MAX_PHYS 2
+
+/**
+ * struct mt7621_pci_phy - Mt7621 Pcie PHY core
+ * @dev: pointer to device
+ * @regmap: kernel regmap pointer
+ * @phy: pointer to the kernel PHY device
+ * @sys_clk: pointer to the system XTAL clock
+ * @port_base: base register
+ * @has_dual_port: if the phy has dual ports.
+ * @bypass_pipe_rst: mark if 'mt7621_bypass_pipe_rst'
+ * needs to be executed. Depends on chip revision.
+ */
+struct mt7621_pci_phy {
+ struct device *dev;
+ struct regmap *regmap;
+ struct phy *phy;
+ struct clk *sys_clk;
+ void __iomem *port_base;
+ bool has_dual_port;
+ bool bypass_pipe_rst;
+};
+
+static inline void mt7621_phy_rmw(struct mt7621_pci_phy *phy,
+ u32 reg, u32 clr, u32 set)
+{
+ u32 val;
+
+ /*
+ * We cannot use 'regmap_write_bits' here because internally
+ * 'set' is masked before is set to the value that will be
+ * written to the register. That way results in no reliable
+ * pci setup. Avoid to mask 'set' before set value to 'val'
+ * completely avoid the problem.
+ */
+ regmap_read(phy->regmap, reg, &val);
+ val &= ~clr;
+ val |= set;
+ regmap_write(phy->regmap, reg, val);
+}
+
+static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy)
+{
+ mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_RST);
+ mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_CMD_FRC);
+
+ if (phy->has_dual_port) {
+ mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH,
+ 0, RG_PE1_PIPE_RST);
+ mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH,
+ 0, RG_PE1_PIPE_CMD_FRC);
+ }
+}
+
+static int mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy)
+{
+ struct device *dev = phy->dev;
+ unsigned long clk_rate;
+
+ clk_rate = clk_get_rate(phy->sys_clk);
+ if (!clk_rate)
+ return -EINVAL;
+
+ /* Set PCIe Port PHY to disable SSC */
+ /* Debug Xtal Type */
+ mt7621_phy_rmw(phy, RG_PE1_FRC_H_XTAL_REG,
+ RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE,
+ RG_PE1_FRC_H_XTAL_TYPE |
+ FIELD_PREP(RG_PE1_H_XTAL_TYPE, 0x00));
+
+ /* disable port */
+ mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG, RG_PE1_PHY_EN,
+ RG_PE1_FRC_PHY_EN);
+
+ if (phy->has_dual_port) {
+ mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH,
+ RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);
+ }
+
+ if (clk_rate == 40000000) { /* 40MHz Xtal */
+ /* Set Pre-divider ratio (for host mode) */
+ mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV,
+ FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x01));
+
+ dev_dbg(dev, "Xtal is 40MHz\n");
+ } else if (clk_rate == 25000000) { /* 25MHz Xal */
+ mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV,
+ FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00));
+
+ /* Select feedback clock */
+ mt7621_phy_rmw(phy, RG_PE1_H_PLL_FBKSEL_REG,
+ RG_PE1_H_PLL_FBKSEL,
+ FIELD_PREP(RG_PE1_H_PLL_FBKSEL, 0x01));
+
+ /* DDS NCPO PCW (for host mode) */
+ mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG,
+ RG_PE1_H_LCDDS_SSC_PRD,
+ FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x00));
+
+ /* DDS SSC dither period control */
+ mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG,
+ RG_PE1_H_LCDDS_SSC_PRD,
+ FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x18d));
+
+ /* DDS SSC dither amplitude control */
+ mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_DELTA_REG,
+ RG_PE1_H_LCDDS_SSC_DELTA |
+ RG_PE1_H_LCDDS_SSC_DELTA1,
+ FIELD_PREP(RG_PE1_H_LCDDS_SSC_DELTA, 0x4a) |
+ FIELD_PREP(RG_PE1_H_LCDDS_SSC_DELTA1, 0x4a));
+
+ dev_dbg(dev, "Xtal is 25MHz\n");
+ } else { /* 20MHz Xtal */
+ mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV,
+ FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00));
+
+ dev_dbg(dev, "Xtal is 20MHz\n");
+ }
+
+ /* DDS clock inversion */
+ mt7621_phy_rmw(phy, RG_PE1_LCDDS_CLK_PH_INV_REG,
+ RG_PE1_LCDDS_CLK_PH_INV, RG_PE1_LCDDS_CLK_PH_INV);
+
+ /* Set PLL bits */
+ mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG,
+ RG_PE1_H_PLL_BC | RG_PE1_H_PLL_BP | RG_PE1_H_PLL_IR |
+ RG_PE1_H_PLL_IC | RG_PE1_PLL_DIVEN,
+ FIELD_PREP(RG_PE1_H_PLL_BC, 0x02) |
+ FIELD_PREP(RG_PE1_H_PLL_BP, 0x06) |
+ FIELD_PREP(RG_PE1_H_PLL_IR, 0x02) |
+ FIELD_PREP(RG_PE1_H_PLL_IC, 0x01) |
+ FIELD_PREP(RG_PE1_PLL_DIVEN, 0x02));
+
+ mt7621_phy_rmw(phy, RG_PE1_H_PLL_BR_REG, RG_PE1_H_PLL_BR,
+ FIELD_PREP(RG_PE1_H_PLL_BR, 0x00));
+
+ if (clk_rate == 40000000) { /* 40MHz Xtal */
+ /* set force mode enable of da_pe1_mstckdiv */
+ mt7621_phy_rmw(phy, RG_PE1_MSTCKDIV_REG,
+ RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV,
+ FIELD_PREP(RG_PE1_MSTCKDIV, 0x01) |
+ RG_PE1_FRC_MSTCKDIV);
+ }
+
+ return 0;
+}
+
+static int mt7621_pci_phy_init(struct phy *phy)
+{
+ struct mt7621_pci_phy *mphy = phy_get_drvdata(phy);
+
+ if (mphy->bypass_pipe_rst)
+ mt7621_bypass_pipe_rst(mphy);
+
+ return mt7621_set_phy_for_ssc(mphy);
+}
+
+static int mt7621_pci_phy_power_on(struct phy *phy)
+{
+ struct mt7621_pci_phy *mphy = phy_get_drvdata(phy);
+
+ /* Enable PHY and disable force mode */
+ mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG,
+ RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN);
+
+ if (mphy->has_dual_port) {
+ mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH,
+ RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN);
+ }
+
+ return 0;
+}
+
+static int mt7621_pci_phy_power_off(struct phy *phy)
+{
+ struct mt7621_pci_phy *mphy = phy_get_drvdata(phy);
+
+ /* Disable PHY */
+ mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG,
+ RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);
+
+ if (mphy->has_dual_port) {
+ mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH,
+ RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN);
+ }
+
+ return 0;
+}
+
+static int mt7621_pci_phy_exit(struct phy *phy)
+{
+ return 0;
+}
+
+static const struct phy_ops mt7621_pci_phy_ops = {
+ .init = mt7621_pci_phy_init,
+ .exit = mt7621_pci_phy_exit,
+ .power_on = mt7621_pci_phy_power_on,
+ .power_off = mt7621_pci_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static struct phy *mt7621_pcie_phy_of_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct mt7621_pci_phy *mt7621_phy = dev_get_drvdata(dev);
+
+ if (WARN_ON(args->args[0] >= MAX_PHYS))
+ return ERR_PTR(-ENODEV);
+
+ mt7621_phy->has_dual_port = args->args[0];
+
+ dev_dbg(dev, "PHY for 0x%px (dual port = %d)\n",
+ mt7621_phy->port_base, mt7621_phy->has_dual_port);
+
+ return mt7621_phy->phy;
+}
+
+static const struct soc_device_attribute mt7621_pci_quirks_match[] = {
+ { .soc_id = "mt7621", .revision = "E2" },
+ { /* sentinel */ }
+};
+
+static const struct regmap_config mt7621_pci_phy_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x700,
+};
+
+static int mt7621_pci_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct soc_device_attribute *attr;
+ struct phy_provider *provider;
+ struct mt7621_pci_phy *phy;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ attr = soc_device_match(mt7621_pci_quirks_match);
+ if (attr)
+ phy->bypass_pipe_rst = true;
+
+ phy->dev = dev;
+ platform_set_drvdata(pdev, phy);
+
+ phy->port_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(phy->port_base)) {
+ dev_err(dev, "failed to remap phy regs\n");
+ return PTR_ERR(phy->port_base);
+ }
+
+ phy->regmap = devm_regmap_init_mmio(phy->dev, phy->port_base,
+ &mt7621_pci_phy_regmap_config);
+ if (IS_ERR(phy->regmap))
+ return PTR_ERR(phy->regmap);
+
+ phy->phy = devm_phy_create(dev, dev->of_node, &mt7621_pci_phy_ops);
+ if (IS_ERR(phy->phy)) {
+ dev_err(dev, "failed to create phy\n");
+ return PTR_ERR(phy->phy);
+ }
+
+ phy->sys_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(phy->sys_clk)) {
+ dev_err(dev, "failed to get phy clock\n");
+ return PTR_ERR(phy->sys_clk);
+ }
+
+ phy_set_drvdata(phy->phy, phy);
+
+ provider = devm_of_phy_provider_register(dev, mt7621_pcie_phy_of_xlate);
+
+ return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id mt7621_pci_phy_ids[] = {
+ { .compatible = "mediatek,mt7621-pci-phy" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt7621_pci_phy_ids);
+
+static struct platform_driver mt7621_pci_phy_driver = {
+ .probe = mt7621_pci_phy_probe,
+ .driver = {
+ .name = "mt7621-pci-phy",
+ .of_match_table = mt7621_pci_phy_ids,
+ },
+};
+
+builtin_platform_driver(mt7621_pci_phy_driver);
+
+MODULE_AUTHOR("Sergio Paracuellos <sergio.paracuellos@gmail.com>");
+MODULE_DESCRIPTION("MediaTek MT7621 PCIe PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/ralink/phy-ralink-usb.c b/drivers/phy/ralink/phy-ralink-usb.c
index ba3c197fc5b0..2bd8ad2e76ed 100644
--- a/drivers/phy/ralink/phy-ralink-usb.c
+++ b/drivers/phy/ralink/phy-ralink-usb.c
@@ -142,7 +142,7 @@ static int ralink_usb_phy_power_off(struct phy *_phy)
return 0;
}
-static struct phy_ops ralink_usb_phy_ops = {
+static const struct phy_ops ralink_usb_phy_ops = {
.power_on = ralink_usb_phy_power_on,
.power_off = ralink_usb_phy_power_off,
.owner = THIS_MODULE,
@@ -170,7 +170,6 @@ MODULE_DEVICE_TABLE(of, ralink_usb_phy_of_match);
static int ralink_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct resource *res;
struct phy_provider *phy_provider;
const struct of_device_id *match;
struct ralink_usb_phy *phy;
@@ -194,8 +193,7 @@ static int ralink_usb_phy_probe(struct platform_device *pdev)
/* The MT7628 and MT7688 require extra setup of PHY registers. */
if (of_device_is_compatible(dev->of_node, "mediatek,mt7628-usbphy")) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- phy->base = devm_ioremap_resource(&pdev->dev, res);
+ phy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(phy->base)) {
dev_err(dev, "failed to remap register memory\n");
return PTR_ERR(phy->base);
diff --git a/drivers/phy/renesas/phy-rcar-gen2.c b/drivers/phy/renesas/phy-rcar-gen2.c
index 2e279ac0fa4d..c375a4676a3d 100644
--- a/drivers/phy/renesas/phy-rcar-gen2.c
+++ b/drivers/phy/renesas/phy-rcar-gen2.c
@@ -339,7 +339,6 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
struct rcar_gen2_phy_driver *drv;
struct phy_provider *provider;
struct device_node *np;
- struct resource *res;
void __iomem *base;
struct clk *clk;
const struct rcar_gen2_phy_data *data;
@@ -357,8 +356,7 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
return PTR_ERR(clk);
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/drivers/phy/renesas/phy-rcar-gen3-pcie.c b/drivers/phy/renesas/phy-rcar-gen3-pcie.c
index c4e4aa216936..4dc721eb9577 100644
--- a/drivers/phy/renesas/phy-rcar-gen3-pcie.c
+++ b/drivers/phy/renesas/phy-rcar-gen3-pcie.c
@@ -76,7 +76,6 @@ static int rcar_gen3_phy_pcie_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct phy_provider *provider;
struct rcar_gen3_phy *phy;
- struct resource *res;
void __iomem *base;
int error;
@@ -86,8 +85,7 @@ static int rcar_gen3_phy_pcie_probe(struct platform_device *pdev)
return -EINVAL;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
index bfb22f868857..9de617ca9daa 100644
--- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
@@ -64,6 +64,7 @@
/* VBCTRL */
#define USB2_VBCTRL_OCCLREN BIT(16)
#define USB2_VBCTRL_DRVVBUSSEL BIT(8)
+#define USB2_VBCTRL_VBOUT BIT(0)
/* LINECTRL1 */
#define USB2_LINECTRL1_DPRPD_EN BIT(19)
@@ -78,6 +79,10 @@
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
#define USB2_ADPCTRL_DRVVBUS BIT(4)
+/* RZ/G2L specific */
+#define USB2_OBINT_IDCHG_EN BIT(0)
+#define USB2_LINECTRL1_USB2_IDMON BIT(0)
+
#define NUM_OF_PHYS 4
enum rcar_gen3_phy_index {
PHY_INDEX_BOTH_HC,
@@ -111,9 +116,17 @@ struct rcar_gen3_chan {
struct work_struct work;
struct mutex lock; /* protects rphys[...].powered */
enum usb_dr_mode dr_mode;
+ int irq;
+ u32 obint_enable_bits;
bool extcon_host;
bool is_otg_channel;
bool uses_otg_pins;
+ bool soc_no_adp_ctrl;
+};
+
+struct rcar_gen3_phy_drv_data {
+ const struct phy_ops *phy_usb2_ops;
+ bool no_adp_ctrl;
};
/*
@@ -171,14 +184,22 @@ static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm)
static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus)
{
void __iomem *usb2_base = ch->base;
- u32 val = readl(usb2_base + USB2_ADPCTRL);
+ u32 vbus_ctrl_reg = USB2_ADPCTRL;
+ u32 vbus_ctrl_val = USB2_ADPCTRL_DRVVBUS;
+ u32 val;
dev_vdbg(ch->dev, "%s: %08x, %d\n", __func__, val, vbus);
+ if (ch->soc_no_adp_ctrl) {
+ vbus_ctrl_reg = USB2_VBCTRL;
+ vbus_ctrl_val = USB2_VBCTRL_VBOUT;
+ }
+
+ val = readl(usb2_base + vbus_ctrl_reg);
if (vbus)
- val |= USB2_ADPCTRL_DRVVBUS;
+ val |= vbus_ctrl_val;
else
- val &= ~USB2_ADPCTRL_DRVVBUS;
- writel(val, usb2_base + USB2_ADPCTRL);
+ val &= ~vbus_ctrl_val;
+ writel(val, usb2_base + vbus_ctrl_reg);
}
static void rcar_gen3_control_otg_irq(struct rcar_gen3_chan *ch, int enable)
@@ -187,9 +208,9 @@ static void rcar_gen3_control_otg_irq(struct rcar_gen3_chan *ch, int enable)
u32 val = readl(usb2_base + USB2_OBINTEN);
if (ch->uses_otg_pins && enable)
- val |= USB2_OBINT_BITS;
+ val |= ch->obint_enable_bits;
else
- val &= ~USB2_OBINT_BITS;
+ val &= ~ch->obint_enable_bits;
writel(val, usb2_base + USB2_OBINTEN);
}
@@ -251,6 +272,9 @@ static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch)
if (!ch->uses_otg_pins)
return (ch->dr_mode == USB_DR_MODE_HOST) ? false : true;
+ if (ch->soc_no_adp_ctrl)
+ return !!(readl(ch->base + USB2_LINECTRL1) & USB2_LINECTRL1_USB2_IDMON);
+
return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG);
}
@@ -375,26 +399,55 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
USB2_LINECTRL1_DMRPD_EN | USB2_LINECTRL1_DM_RPD;
writel(val, usb2_base + USB2_LINECTRL1);
- val = readl(usb2_base + USB2_VBCTRL);
- val &= ~USB2_VBCTRL_OCCLREN;
- writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL);
- val = readl(usb2_base + USB2_ADPCTRL);
- writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
-
+ if (!ch->soc_no_adp_ctrl) {
+ val = readl(usb2_base + USB2_VBCTRL);
+ val &= ~USB2_VBCTRL_OCCLREN;
+ writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL);
+ val = readl(usb2_base + USB2_ADPCTRL);
+ writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
+ }
msleep(20);
writel(0xffffffff, usb2_base + USB2_OBINTSTA);
- writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
+ writel(ch->obint_enable_bits, usb2_base + USB2_OBINTEN);
rcar_gen3_device_recognition(ch);
}
+static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
+{
+ struct rcar_gen3_chan *ch = _ch;
+ void __iomem *usb2_base = ch->base;
+ u32 status = readl(usb2_base + USB2_OBINTSTA);
+ irqreturn_t ret = IRQ_NONE;
+
+ if (status & ch->obint_enable_bits) {
+ dev_vdbg(ch->dev, "%s: %08x\n", __func__, status);
+ writel(ch->obint_enable_bits, usb2_base + USB2_OBINTSTA);
+ rcar_gen3_device_recognition(ch);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
static int rcar_gen3_phy_usb2_init(struct phy *p)
{
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
struct rcar_gen3_chan *channel = rphy->ch;
void __iomem *usb2_base = channel->base;
u32 val;
+ int ret;
+
+ if (!rcar_gen3_is_any_rphy_initialized(channel) && channel->irq >= 0) {
+ INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work);
+ ret = request_irq(channel->irq, rcar_gen3_phy_usb2_irq,
+ IRQF_SHARED, dev_name(channel->dev), channel);
+ if (ret < 0) {
+ dev_err(channel->dev, "No irq handler (%d)\n", channel->irq);
+ return ret;
+ }
+ }
/* Initialize USB2 part */
val = readl(usb2_base + USB2_INT_ENABLE);
@@ -433,6 +486,9 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p)
val &= ~USB2_INT_ENABLE_UCOM_INTEN;
writel(val, usb2_base + USB2_INT_ENABLE);
+ if (channel->irq >= 0 && !rcar_gen3_is_any_rphy_initialized(channel))
+ free_irq(channel->irq, channel);
+
return 0;
}
@@ -503,43 +559,45 @@ static const struct phy_ops rz_g1c_phy_usb2_ops = {
.owner = THIS_MODULE,
};
-static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
-{
- struct rcar_gen3_chan *ch = _ch;
- void __iomem *usb2_base = ch->base;
- u32 status = readl(usb2_base + USB2_OBINTSTA);
- irqreturn_t ret = IRQ_NONE;
+static const struct rcar_gen3_phy_drv_data rcar_gen3_phy_usb2_data = {
+ .phy_usb2_ops = &rcar_gen3_phy_usb2_ops,
+ .no_adp_ctrl = false,
+};
- if (status & USB2_OBINT_BITS) {
- dev_vdbg(ch->dev, "%s: %08x\n", __func__, status);
- writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
- rcar_gen3_device_recognition(ch);
- ret = IRQ_HANDLED;
- }
+static const struct rcar_gen3_phy_drv_data rz_g1c_phy_usb2_data = {
+ .phy_usb2_ops = &rz_g1c_phy_usb2_ops,
+ .no_adp_ctrl = false,
+};
- return ret;
-}
+static const struct rcar_gen3_phy_drv_data rz_g2l_phy_usb2_data = {
+ .phy_usb2_ops = &rcar_gen3_phy_usb2_ops,
+ .no_adp_ctrl = true,
+};
static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
{
.compatible = "renesas,usb2-phy-r8a77470",
- .data = &rz_g1c_phy_usb2_ops,
+ .data = &rz_g1c_phy_usb2_data,
},
{
.compatible = "renesas,usb2-phy-r8a7795",
- .data = &rcar_gen3_phy_usb2_ops,
+ .data = &rcar_gen3_phy_usb2_data,
},
{
.compatible = "renesas,usb2-phy-r8a7796",
- .data = &rcar_gen3_phy_usb2_ops,
+ .data = &rcar_gen3_phy_usb2_data,
},
{
.compatible = "renesas,usb2-phy-r8a77965",
- .data = &rcar_gen3_phy_usb2_ops,
+ .data = &rcar_gen3_phy_usb2_data,
+ },
+ {
+ .compatible = "renesas,rzg2l-usb2-phy",
+ .data = &rz_g2l_phy_usb2_data,
},
{
.compatible = "renesas,rcar-gen3-usb2-phy",
- .data = &rcar_gen3_phy_usb2_ops,
+ .data = &rcar_gen3_phy_usb2_data,
},
{ /* sentinel */ },
};
@@ -593,12 +651,11 @@ static enum usb_dr_mode rcar_gen3_get_dr_mode(struct device_node *np)
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
{
+ const struct rcar_gen3_phy_drv_data *phy_data;
struct device *dev = &pdev->dev;
struct rcar_gen3_chan *channel;
struct phy_provider *provider;
- struct resource *res;
- const struct phy_ops *phy_usb2_ops;
- int irq, ret = 0, i;
+ int ret = 0, i;
if (!dev->of_node) {
dev_err(dev, "This driver needs device tree\n");
@@ -609,21 +666,13 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
if (!channel)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- channel->base = devm_ioremap_resource(dev, res);
+ channel->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(channel->base))
return PTR_ERR(channel->base);
- /* call request_irq for OTG */
- irq = platform_get_irq_optional(pdev, 0);
- if (irq >= 0) {
- INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work);
- irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
- IRQF_SHARED, dev_name(dev), channel);
- if (irq < 0)
- dev_err(dev, "No irq handler (%d)\n", irq);
- }
-
+ channel->obint_enable_bits = USB2_OBINT_BITS;
+ /* get irq number here and request_irq for OTG in phy_init */
+ channel->irq = platform_get_irq_optional(pdev, 0);
channel->dr_mode = rcar_gen3_get_dr_mode(dev->of_node);
if (channel->dr_mode != USB_DR_MODE_UNKNOWN) {
int ret;
@@ -648,14 +697,21 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
* And then, phy-core will manage runtime pm for this device.
*/
pm_runtime_enable(dev);
- phy_usb2_ops = of_device_get_match_data(dev);
- if (!phy_usb2_ops)
- return -EINVAL;
+
+ phy_data = of_device_get_match_data(dev);
+ if (!phy_data) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ channel->soc_no_adp_ctrl = phy_data->no_adp_ctrl;
+ if (phy_data->no_adp_ctrl)
+ channel->obint_enable_bits = USB2_OBINT_IDCHG_EN;
mutex_init(&channel->lock);
for (i = 0; i < NUM_OF_PHYS; i++) {
channel->rphys[i].phy = devm_phy_create(dev, NULL,
- phy_usb2_ops);
+ phy_data->phy_usb2_ops);
if (IS_ERR(channel->rphys[i].phy)) {
dev_err(dev, "Failed to create USB2 PHY\n");
ret = PTR_ERR(channel->rphys[i].phy);
diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb3.c b/drivers/phy/renesas/phy-rcar-gen3-usb3.c
index 566b4cf4ff38..f27d6f471629 100644
--- a/drivers/phy/renesas/phy-rcar-gen3-usb3.c
+++ b/drivers/phy/renesas/phy-rcar-gen3-usb3.c
@@ -133,7 +133,6 @@ static int rcar_gen3_phy_usb3_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct rcar_gen3_usb3 *r;
struct phy_provider *provider;
- struct resource *res;
int ret = 0;
struct clk *clk;
@@ -146,8 +145,7 @@ static int rcar_gen3_phy_usb3_probe(struct platform_device *pdev)
if (!r)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- r->base = devm_ioremap_resource(dev, res);
+ r->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(r->base))
return PTR_ERR(r->base);
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index 0824b9dd5683..94360fc96a6f 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -9,6 +9,18 @@ config PHY_ROCKCHIP_DP
help
Enable this to support the Rockchip Display Port PHY.
+config PHY_ROCKCHIP_DPHY_RX0
+ tristate "Rockchip MIPI Synopsys DPHY RX0 driver"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select GENERIC_PHY_MIPI_DPHY
+ select GENERIC_PHY
+ help
+ Enable this to support the Rockchip MIPI Synopsys DPHY RX0
+ associated to the Rockchip ISP module present in RK3399 SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called phy-rockchip-dphy-rx0.
+
config PHY_ROCKCHIP_EMMC
tristate "Rockchip EMMC PHY Driver"
depends on ARCH_ROCKCHIP && OF
@@ -20,6 +32,7 @@ config PHY_ROCKCHIP_INNO_HDMI
tristate "Rockchip INNO HDMI PHY Driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
depends on COMMON_CLK
+ depends on HAS_IOMEM
select GENERIC_PHY
help
Enable this to support the Rockchip Innosilicon HDMI PHY.
@@ -35,6 +48,15 @@ config PHY_ROCKCHIP_INNO_USB2
help
Support for Rockchip USB2.0 PHY with Innosilicon IP block.
+config PHY_ROCKCHIP_INNO_CSIDPHY
+ tristate "Rockchip Innosilicon MIPI CSI PHY driver"
+ depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
+ select GENERIC_PHY
+ select GENERIC_PHY_MIPI_DPHY
+ help
+ Enable this to support the Rockchip MIPI CSI PHY with
+ Innosilicon IP block.
+
config PHY_ROCKCHIP_INNO_DSIDPHY
tristate "Rockchip Innosilicon MIPI/LVDS/TTL PHY driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
@@ -44,6 +66,14 @@ config PHY_ROCKCHIP_INNO_DSIDPHY
Enable this to support the Rockchip MIPI/LVDS/TTL PHY with
Innosilicon IP block.
+config PHY_ROCKCHIP_NANENG_COMBO_PHY
+ tristate "Rockchip NANENG COMBO PHY Driver"
+ depends on ARCH_ROCKCHIP && OF
+ select GENERIC_PHY
+ help
+ Enable this to support the Rockchip PCIe/USB3.0/SATA/QSGMII
+ combo PHY with NaNeng IP block.
+
config PHY_ROCKCHIP_PCIE
tristate "Rockchip PCIe PHY Driver"
depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
@@ -53,6 +83,15 @@ config PHY_ROCKCHIP_PCIE
help
Enable this to support the Rockchip PCIe PHY.
+config PHY_ROCKCHIP_SNPS_PCIE3
+ tristate "Rockchip Snps PCIe3 PHY Driver"
+ depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
+ depends on HAS_IOMEM
+ select GENERIC_PHY
+ select MFD_SYSCON
+ help
+ Enable this to support the Rockchip snps PCIe3 PHY.
+
config PHY_ROCKCHIP_TYPEC
tristate "Rockchip TYPEC PHY Driver"
depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST)
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
index 9f59a81e4e0d..7eab129230d1 100644
--- a/drivers/phy/rockchip/Makefile
+++ b/drivers/phy/rockchip/Makefile
@@ -1,9 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o
+obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0.o
obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
+obj-$(CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY) += phy-rockchip-inno-csidphy.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
+obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o
obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
+obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o
obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
diff --git a/drivers/phy/rockchip/phy-rockchip-dphy-rx0.c b/drivers/phy/rockchip/phy-rockchip-dphy-rx0.c
new file mode 100644
index 000000000000..639452f47869
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-dphy-rx0.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip MIPI Synopsys DPHY RX0 driver
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ *
+ * Based on:
+ *
+ * drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
+ * in https://chromium.googlesource.com/chromiumos/third_party/kernel,
+ * chromeos-4.4 branch.
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ * Jacob Chen <jacob2.chen@rock-chips.com>
+ * Shunqian Zheng <zhengsq@rock-chips.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define RK3399_GRF_SOC_CON9 0x6224
+#define RK3399_GRF_SOC_CON21 0x6254
+#define RK3399_GRF_SOC_CON22 0x6258
+#define RK3399_GRF_SOC_CON23 0x625c
+#define RK3399_GRF_SOC_CON24 0x6260
+#define RK3399_GRF_SOC_CON25 0x6264
+#define RK3399_GRF_SOC_STATUS1 0xe2a4
+
+#define CLOCK_LANE_HS_RX_CONTROL 0x34
+#define LANE0_HS_RX_CONTROL 0x44
+#define LANE1_HS_RX_CONTROL 0x54
+#define LANE2_HS_RX_CONTROL 0x84
+#define LANE3_HS_RX_CONTROL 0x94
+#define LANES_THS_SETTLE_CONTROL 0x75
+#define THS_SETTLE_COUNTER_THRESHOLD 0x04
+
+struct hsfreq_range {
+ u16 range_h;
+ u8 cfg_bit;
+};
+
+static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
+ { 89, 0x00 }, { 99, 0x10 }, { 109, 0x20 }, { 129, 0x01 },
+ { 139, 0x11 }, { 149, 0x21 }, { 169, 0x02 }, { 179, 0x12 },
+ { 199, 0x22 }, { 219, 0x03 }, { 239, 0x13 }, { 249, 0x23 },
+ { 269, 0x04 }, { 299, 0x14 }, { 329, 0x05 }, { 359, 0x15 },
+ { 399, 0x25 }, { 449, 0x06 }, { 499, 0x16 }, { 549, 0x07 },
+ { 599, 0x17 }, { 649, 0x08 }, { 699, 0x18 }, { 749, 0x09 },
+ { 799, 0x19 }, { 849, 0x29 }, { 899, 0x39 }, { 949, 0x0a },
+ { 999, 0x1a }, { 1049, 0x2a }, { 1099, 0x3a }, { 1149, 0x0b },
+ { 1199, 0x1b }, { 1249, 0x2b }, { 1299, 0x3b }, { 1349, 0x0c },
+ { 1399, 0x1c }, { 1449, 0x2c }, { 1500, 0x3c }
+};
+
+static const char * const rk3399_mipidphy_clks[] = {
+ "dphy-ref",
+ "dphy-cfg",
+ "grf",
+};
+
+enum dphy_reg_id {
+ GRF_DPHY_RX0_TURNDISABLE = 0,
+ GRF_DPHY_RX0_FORCERXMODE,
+ GRF_DPHY_RX0_FORCETXSTOPMODE,
+ GRF_DPHY_RX0_ENABLE,
+ GRF_DPHY_RX0_TESTCLR,
+ GRF_DPHY_RX0_TESTCLK,
+ GRF_DPHY_RX0_TESTEN,
+ GRF_DPHY_RX0_TESTDIN,
+ GRF_DPHY_RX0_TURNREQUEST,
+ GRF_DPHY_RX0_TESTDOUT,
+ GRF_DPHY_TX0_TURNDISABLE,
+ GRF_DPHY_TX0_FORCERXMODE,
+ GRF_DPHY_TX0_FORCETXSTOPMODE,
+ GRF_DPHY_TX0_TURNREQUEST,
+ GRF_DPHY_TX1RX1_TURNDISABLE,
+ GRF_DPHY_TX1RX1_FORCERXMODE,
+ GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
+ GRF_DPHY_TX1RX1_ENABLE,
+ GRF_DPHY_TX1RX1_MASTERSLAVEZ,
+ GRF_DPHY_TX1RX1_BASEDIR,
+ GRF_DPHY_TX1RX1_ENABLECLK,
+ GRF_DPHY_TX1RX1_TURNREQUEST,
+ GRF_DPHY_RX1_SRC_SEL,
+ /* rk3288 only */
+ GRF_CON_DISABLE_ISP,
+ GRF_CON_ISP_DPHY_SEL,
+ GRF_DSI_CSI_TESTBUS_SEL,
+ GRF_DVP_V18SEL,
+ /* below is for rk3399 only */
+ GRF_DPHY_RX0_CLK_INV_SEL,
+ GRF_DPHY_RX1_CLK_INV_SEL,
+};
+
+struct dphy_reg {
+ u16 offset;
+ u8 mask;
+ u8 shift;
+};
+
+#define PHY_REG(_offset, _width, _shift) \
+ { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
+
+static const struct dphy_reg rk3399_grf_dphy_regs[] = {
+ [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
+ [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
+ [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
+ [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
+ [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
+ [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
+ [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
+ [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
+ [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
+ [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
+ [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
+ [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
+ [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
+ [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
+ [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
+ [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
+ [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
+ [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
+ [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
+ [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
+ [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
+ [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
+ [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
+ [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
+ [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
+};
+
+struct rk_dphy_drv_data {
+ const char * const *clks;
+ unsigned int num_clks;
+ const struct hsfreq_range *hsfreq_ranges;
+ unsigned int num_hsfreq_ranges;
+ const struct dphy_reg *regs;
+};
+
+struct rk_dphy {
+ struct device *dev;
+ struct regmap *grf;
+ struct clk_bulk_data *clks;
+
+ const struct rk_dphy_drv_data *drv_data;
+ struct phy_configure_opts_mipi_dphy config;
+
+ u8 hsfreq;
+};
+
+static inline void rk_dphy_write_grf(struct rk_dphy *priv,
+ unsigned int index, u8 value)
+{
+ const struct dphy_reg *reg = &priv->drv_data->regs[index];
+ /* Update high word */
+ unsigned int val = (value << reg->shift) |
+ (reg->mask << (reg->shift + 16));
+
+ if (WARN_ON(!reg->offset))
+ return;
+ regmap_write(priv->grf, reg->offset, val);
+}
+
+static void rk_dphy_write(struct rk_dphy *priv, u8 test_code, u8 test_data)
+{
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTDIN, test_code);
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTEN, 1);
+ /*
+ * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
+ * is latched internally as the current test code. Test data is
+ * programmed internally by rising edge on TESTCLK.
+ * This code assumes that TESTCLK is already 1.
+ */
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLK, 0);
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTEN, 0);
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTDIN, test_data);
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLK, 1);
+}
+
+static void rk_dphy_enable(struct rk_dphy *priv)
+{
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
+
+ /* Disable lane turn around, which is ignored in receive mode */
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
+
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_ENABLE,
+ GENMASK(priv->config.lanes - 1, 0));
+
+ /* dphy start */
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLK, 1);
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLR, 1);
+ usleep_range(100, 150);
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLR, 0);
+ usleep_range(100, 150);
+
+ /* set clock lane */
+ /* HS hsfreq_range & lane 0 settle bypass */
+ rk_dphy_write(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
+ /* HS RX Control of lane0 */
+ rk_dphy_write(priv, LANE0_HS_RX_CONTROL, priv->hsfreq << 1);
+ /* HS RX Control of lane1 */
+ rk_dphy_write(priv, LANE1_HS_RX_CONTROL, priv->hsfreq << 1);
+ /* HS RX Control of lane2 */
+ rk_dphy_write(priv, LANE2_HS_RX_CONTROL, priv->hsfreq << 1);
+ /* HS RX Control of lane3 */
+ rk_dphy_write(priv, LANE3_HS_RX_CONTROL, priv->hsfreq << 1);
+ /* HS RX Data Lanes Settle State Time Control */
+ rk_dphy_write(priv, LANES_THS_SETTLE_CONTROL,
+ THS_SETTLE_COUNTER_THRESHOLD);
+
+ /* Normal operation */
+ rk_dphy_write(priv, 0x0, 0);
+}
+
+static int rk_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+ struct rk_dphy *priv = phy_get_drvdata(phy);
+ const struct rk_dphy_drv_data *drv_data = priv->drv_data;
+ struct phy_configure_opts_mipi_dphy *config = &opts->mipi_dphy;
+ unsigned int hsfreq = 0;
+ unsigned int i;
+ u64 data_rate_mbps;
+ int ret;
+
+ /* pass with phy_mipi_dphy_get_default_config (with pixel rate?) */
+ ret = phy_mipi_dphy_config_validate(config);
+ if (ret)
+ return ret;
+
+ data_rate_mbps = div_u64(config->hs_clk_rate, 1000 * 1000);
+
+ dev_dbg(priv->dev, "lanes %d - data_rate_mbps %llu\n",
+ config->lanes, data_rate_mbps);
+ for (i = 0; i < drv_data->num_hsfreq_ranges; i++) {
+ if (drv_data->hsfreq_ranges[i].range_h >= data_rate_mbps) {
+ hsfreq = drv_data->hsfreq_ranges[i].cfg_bit;
+ break;
+ }
+ }
+ if (!hsfreq)
+ return -EINVAL;
+
+ priv->hsfreq = hsfreq;
+ priv->config = *config;
+ return 0;
+}
+
+static int rk_dphy_power_on(struct phy *phy)
+{
+ struct rk_dphy *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = clk_bulk_enable(priv->drv_data->num_clks, priv->clks);
+ if (ret)
+ return ret;
+
+ rk_dphy_enable(priv);
+
+ return 0;
+}
+
+static int rk_dphy_power_off(struct phy *phy)
+{
+ struct rk_dphy *priv = phy_get_drvdata(phy);
+
+ rk_dphy_write_grf(priv, GRF_DPHY_RX0_ENABLE, 0);
+ clk_bulk_disable(priv->drv_data->num_clks, priv->clks);
+ return 0;
+}
+
+static int rk_dphy_init(struct phy *phy)
+{
+ struct rk_dphy *priv = phy_get_drvdata(phy);
+
+ return clk_bulk_prepare(priv->drv_data->num_clks, priv->clks);
+}
+
+static int rk_dphy_exit(struct phy *phy)
+{
+ struct rk_dphy *priv = phy_get_drvdata(phy);
+
+ clk_bulk_unprepare(priv->drv_data->num_clks, priv->clks);
+ return 0;
+}
+
+static const struct phy_ops rk_dphy_ops = {
+ .power_on = rk_dphy_power_on,
+ .power_off = rk_dphy_power_off,
+ .init = rk_dphy_init,
+ .exit = rk_dphy_exit,
+ .configure = rk_dphy_configure,
+ .owner = THIS_MODULE,
+};
+
+static const struct rk_dphy_drv_data rk3399_mipidphy_drv_data = {
+ .clks = rk3399_mipidphy_clks,
+ .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
+ .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
+ .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
+ .regs = rk3399_grf_dphy_regs,
+};
+
+static const struct of_device_id rk_dphy_dt_ids[] = {
+ {
+ .compatible = "rockchip,rk3399-mipi-dphy-rx0",
+ .data = &rk3399_mipidphy_drv_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rk_dphy_dt_ids);
+
+static int rk_dphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ const struct rk_dphy_drv_data *drv_data;
+ struct phy_provider *phy_provider;
+ struct rk_dphy *priv;
+ struct phy *phy;
+ unsigned int i;
+ int ret;
+
+ if (!dev->parent || !dev->parent->of_node)
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->dev = dev;
+
+ priv->grf = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(priv->grf)) {
+ dev_err(dev, "Can't find GRF syscon\n");
+ return -ENODEV;
+ }
+
+ drv_data = of_device_get_match_data(dev);
+ priv->drv_data = drv_data;
+ priv->clks = devm_kcalloc(&pdev->dev, drv_data->num_clks,
+ sizeof(*priv->clks), GFP_KERNEL);
+ if (!priv->clks)
+ return -ENOMEM;
+ for (i = 0; i < drv_data->num_clks; i++)
+ priv->clks[i].id = drv_data->clks[i];
+ ret = devm_clk_bulk_get(&pdev->dev, drv_data->num_clks, priv->clks);
+ if (ret)
+ return ret;
+
+ phy = devm_phy_create(dev, np, &rk_dphy_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create phy\n");
+ return PTR_ERR(phy);
+ }
+ phy_set_drvdata(phy, priv);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver rk_dphy_driver = {
+ .probe = rk_dphy_probe,
+ .driver = {
+ .name = "rockchip-mipi-dphy-rx0",
+ .of_match_table = rk_dphy_dt_ids,
+ },
+};
+module_platform_driver(rk_dphy_driver);
+
+MODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>");
+MODULE_DESCRIPTION("Rockchip MIPI Synopsys DPHY RX0 driver");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/phy/rockchip/phy-rockchip-emmc.c b/drivers/phy/rockchip/phy-rockchip-emmc.c
index 2dc19ddd120f..20023f6eb994 100644
--- a/drivers/phy/rockchip/phy-rockchip-emmc.c
+++ b/drivers/phy/rockchip/phy-rockchip-emmc.c
@@ -65,8 +65,14 @@
#define PHYCTRL_OTAPDLYENA 0x1
#define PHYCTRL_OTAPDLYENA_MASK 0x1
#define PHYCTRL_OTAPDLYENA_SHIFT 0xb
+#define PHYCTRL_OTAPDLYSEL_DEFAULT 0x4
+#define PHYCTRL_OTAPDLYSEL_MAXVALUE 0xf
#define PHYCTRL_OTAPDLYSEL_MASK 0xf
#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7
+#define PHYCTRL_REN_STRB_DISABLE 0x0
+#define PHYCTRL_REN_STRB_ENABLE 0x1
+#define PHYCTRL_REN_STRB_MASK 0x1
+#define PHYCTRL_REN_STRB_SHIFT 0x9
#define PHYCTRL_IS_CALDONE(x) \
((((x) >> PHYCTRL_CALDONE_SHIFT) & \
@@ -80,6 +86,8 @@ struct rockchip_emmc_phy {
struct regmap *reg_base;
struct clk *emmcclk;
unsigned int drive_impedance;
+ unsigned int enable_strobe_pulldown;
+ unsigned int output_tapdelay_select;
};
static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
@@ -240,15 +248,17 @@ static int rockchip_emmc_phy_init(struct phy *phy)
* - SDHCI driver to get the PHY
* - SDHCI driver to init the PHY
*
- * The clock is optional, so upon any error we just set to NULL.
+ * The clock is optional, using clk_get_optional() to get the clock
+ * and do error processing if the return value != NULL
*
* NOTE: we don't do anything special for EPROBE_DEFER here. Given the
* above expected use case, EPROBE_DEFER isn't sensible to expect, so
* it's just like any other error.
*/
- rk_phy->emmcclk = clk_get(&phy->dev, "emmcclk");
+ rk_phy->emmcclk = clk_get_optional(&phy->dev, "emmcclk");
if (IS_ERR(rk_phy->emmcclk)) {
- dev_dbg(&phy->dev, "Error getting emmcclk: %d\n", ret);
+ ret = PTR_ERR(rk_phy->emmcclk);
+ dev_err(&phy->dev, "Error getting emmcclk: %d\n", ret);
rk_phy->emmcclk = NULL;
}
@@ -291,10 +301,17 @@ static int rockchip_emmc_phy_power_on(struct phy *phy)
/* Output tap delay */
regmap_write(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_CON0,
- HIWORD_UPDATE(4,
+ HIWORD_UPDATE(rk_phy->output_tapdelay_select,
PHYCTRL_OTAPDLYSEL_MASK,
PHYCTRL_OTAPDLYSEL_SHIFT));
+ /* Internal pull-down for strobe line */
+ regmap_write(rk_phy->reg_base,
+ rk_phy->reg_offset + GRF_EMMCPHY_CON2,
+ HIWORD_UPDATE(rk_phy->enable_strobe_pulldown,
+ PHYCTRL_REN_STRB_MASK,
+ PHYCTRL_REN_STRB_SHIFT));
+
/* Power up emmc phy analog blocks */
return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON);
}
@@ -359,10 +376,22 @@ static int rockchip_emmc_phy_probe(struct platform_device *pdev)
rk_phy->reg_offset = reg_offset;
rk_phy->reg_base = grf;
rk_phy->drive_impedance = PHYCTRL_DR_50OHM;
+ rk_phy->enable_strobe_pulldown = PHYCTRL_REN_STRB_DISABLE;
+ rk_phy->output_tapdelay_select = PHYCTRL_OTAPDLYSEL_DEFAULT;
if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val))
rk_phy->drive_impedance = convert_drive_impedance_ohm(pdev, val);
+ if (of_property_read_bool(dev->of_node, "rockchip,enable-strobe-pulldown"))
+ rk_phy->enable_strobe_pulldown = PHYCTRL_REN_STRB_ENABLE;
+
+ if (!of_property_read_u32(dev->of_node, "rockchip,output-tapdelay-select", &val)) {
+ if (val <= PHYCTRL_OTAPDLYSEL_MAXVALUE)
+ rk_phy->output_tapdelay_select = val;
+ else
+ dev_err(dev, "output-tapdelay-select exceeds limit, apply default\n");
+ }
+
generic_phy = devm_phy_create(dev, dev->of_node, &ops);
if (IS_ERR(generic_phy)) {
dev_err(dev, "failed to create PHY\n");
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c
new file mode 100644
index 000000000000..75f948bdea6a
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip MIPI RX Innosilicon DPHY driver
+ *
+ * Copyright (C) 2021 Fuzhou Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+/* GRF */
+#define RK1808_GRF_PD_VI_CON_OFFSET 0x0430
+
+#define RK3326_GRF_PD_VI_CON_OFFSET 0x0430
+
+#define RK3368_GRF_SOC_CON6_OFFSET 0x0418
+
+#define RK3568_GRF_VI_CON0 0x0340
+#define RK3568_GRF_VI_CON1 0x0344
+
+/* PHY */
+#define CSIDPHY_CTRL_LANE_ENABLE 0x00
+#define CSIDPHY_CTRL_LANE_ENABLE_CK BIT(6)
+#define CSIDPHY_CTRL_LANE_ENABLE_MASK GENMASK(5, 2)
+#define CSIDPHY_CTRL_LANE_ENABLE_UNDEFINED BIT(0)
+
+/* not present on all variants */
+#define CSIDPHY_CTRL_PWRCTL 0x04
+#define CSIDPHY_CTRL_PWRCTL_UNDEFINED GENMASK(7, 5)
+#define CSIDPHY_CTRL_PWRCTL_SYNCRST BIT(2)
+#define CSIDPHY_CTRL_PWRCTL_LDO_PD BIT(1)
+#define CSIDPHY_CTRL_PWRCTL_PLL_PD BIT(0)
+
+#define CSIDPHY_CTRL_DIG_RST 0x80
+#define CSIDPHY_CTRL_DIG_RST_UNDEFINED 0x1e
+#define CSIDPHY_CTRL_DIG_RST_RESET BIT(0)
+
+/* offset after ths_settle_offset */
+#define CSIDPHY_CLK_THS_SETTLE 0
+#define CSIDPHY_LANE_THS_SETTLE(n) (((n) + 1) * 0x80)
+#define CSIDPHY_THS_SETTLE_MASK GENMASK(6, 0)
+
+/* offset after calib_offset */
+#define CSIDPHY_CLK_CALIB_EN 0
+#define CSIDPHY_LANE_CALIB_EN(n) (((n) + 1) * 0x80)
+#define CSIDPHY_CALIB_EN BIT(7)
+
+/* Configure the count time of the THS-SETTLE by protocol. */
+#define RK1808_CSIDPHY_CLK_WR_THS_SETTLE 0x160
+#define RK3326_CSIDPHY_CLK_WR_THS_SETTLE 0x100
+#define RK3368_CSIDPHY_CLK_WR_THS_SETTLE 0x100
+#define RK3568_CSIDPHY_CLK_WR_THS_SETTLE 0x160
+
+/* Calibration reception enable */
+#define RK1808_CSIDPHY_CLK_CALIB_EN 0x168
+#define RK3568_CSIDPHY_CLK_CALIB_EN 0x168
+
+/*
+ * The higher 16-bit of this register is used for write protection
+ * only if BIT(x + 16) set to 1 the BIT(x) can be written.
+ */
+#define HIWORD_UPDATE(val, mask, shift) \
+ ((val) << (shift) | (mask) << ((shift) + 16))
+
+#define HZ_TO_MHZ(freq) div_u64(freq, 1000 * 1000)
+
+enum dphy_reg_id {
+ /* rk1808 & rk3326 */
+ GRF_DPHY_CSIPHY_FORCERXMODE,
+ GRF_DPHY_CSIPHY_CLKLANE_EN,
+ GRF_DPHY_CSIPHY_DATALANE_EN,
+};
+
+struct dphy_reg {
+ u32 offset;
+ u32 mask;
+ u32 shift;
+};
+
+#define PHY_REG(_offset, _width, _shift) \
+ { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
+
+static const struct dphy_reg rk1808_grf_dphy_regs[] = {
+ [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 4, 0),
+ [GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 1, 8),
+ [GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 4, 4),
+};
+
+static const struct dphy_reg rk3326_grf_dphy_regs[] = {
+ [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3326_GRF_PD_VI_CON_OFFSET, 4, 0),
+ [GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK3326_GRF_PD_VI_CON_OFFSET, 1, 8),
+ [GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK3326_GRF_PD_VI_CON_OFFSET, 4, 4),
+};
+
+static const struct dphy_reg rk3368_grf_dphy_regs[] = {
+ [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3368_GRF_SOC_CON6_OFFSET, 4, 8),
+};
+
+static const struct dphy_reg rk3568_grf_dphy_regs[] = {
+ [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3568_GRF_VI_CON0, 4, 0),
+ [GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK3568_GRF_VI_CON0, 4, 4),
+ [GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK3568_GRF_VI_CON0, 1, 8),
+};
+
+struct hsfreq_range {
+ u32 range_h;
+ u8 cfg_bit;
+};
+
+struct dphy_drv_data {
+ int pwrctl_offset;
+ int ths_settle_offset;
+ int calib_offset;
+ const struct hsfreq_range *hsfreq_ranges;
+ int num_hsfreq_ranges;
+ const struct dphy_reg *grf_regs;
+};
+
+struct rockchip_inno_csidphy {
+ struct device *dev;
+ void __iomem *phy_base;
+ struct clk *pclk;
+ struct regmap *grf;
+ struct reset_control *rst;
+ const struct dphy_drv_data *drv_data;
+ struct phy_configure_opts_mipi_dphy config;
+ u8 hsfreq;
+};
+
+static inline void write_grf_reg(struct rockchip_inno_csidphy *priv,
+ int index, u8 value)
+{
+ const struct dphy_drv_data *drv_data = priv->drv_data;
+ const struct dphy_reg *reg = &drv_data->grf_regs[index];
+
+ if (reg->offset)
+ regmap_write(priv->grf, reg->offset,
+ HIWORD_UPDATE(value, reg->mask, reg->shift));
+}
+
+/* These tables must be sorted by .range_h ascending. */
+static const struct hsfreq_range rk1808_mipidphy_hsfreq_ranges[] = {
+ { 109, 0x02}, { 149, 0x03}, { 199, 0x06}, { 249, 0x06},
+ { 299, 0x06}, { 399, 0x08}, { 499, 0x0b}, { 599, 0x0e},
+ { 699, 0x10}, { 799, 0x12}, { 999, 0x16}, {1199, 0x1e},
+ {1399, 0x23}, {1599, 0x2d}, {1799, 0x32}, {1999, 0x37},
+ {2199, 0x3c}, {2399, 0x41}, {2499, 0x46}
+};
+
+static const struct hsfreq_range rk3326_mipidphy_hsfreq_ranges[] = {
+ { 109, 0x00}, { 149, 0x01}, { 199, 0x02}, { 249, 0x03},
+ { 299, 0x04}, { 399, 0x05}, { 499, 0x06}, { 599, 0x07},
+ { 699, 0x08}, { 799, 0x09}, { 899, 0x0a}, {1099, 0x0b},
+ {1249, 0x0c}, {1349, 0x0d}, {1500, 0x0e}
+};
+
+static const struct hsfreq_range rk3368_mipidphy_hsfreq_ranges[] = {
+ { 109, 0x00}, { 149, 0x01}, { 199, 0x02}, { 249, 0x03},
+ { 299, 0x04}, { 399, 0x05}, { 499, 0x06}, { 599, 0x07},
+ { 699, 0x08}, { 799, 0x09}, { 899, 0x0a}, {1099, 0x0b},
+ {1249, 0x0c}, {1349, 0x0d}, {1500, 0x0e}
+};
+
+static void rockchip_inno_csidphy_ths_settle(struct rockchip_inno_csidphy *priv,
+ int hsfreq, int offset)
+{
+ const struct dphy_drv_data *drv_data = priv->drv_data;
+ u32 val;
+
+ val = readl(priv->phy_base + drv_data->ths_settle_offset + offset);
+ val &= ~CSIDPHY_THS_SETTLE_MASK;
+ val |= hsfreq;
+ writel(val, priv->phy_base + drv_data->ths_settle_offset + offset);
+}
+
+static int rockchip_inno_csidphy_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy);
+ const struct dphy_drv_data *drv_data = priv->drv_data;
+ struct phy_configure_opts_mipi_dphy *config = &opts->mipi_dphy;
+ unsigned int hsfreq = 0;
+ unsigned int i;
+ u64 data_rate_mbps;
+ int ret;
+
+ /* pass with phy_mipi_dphy_get_default_config (with pixel rate?) */
+ ret = phy_mipi_dphy_config_validate(config);
+ if (ret)
+ return ret;
+
+ data_rate_mbps = HZ_TO_MHZ(config->hs_clk_rate);
+
+ dev_dbg(priv->dev, "lanes %d - data_rate_mbps %llu\n",
+ config->lanes, data_rate_mbps);
+ for (i = 0; i < drv_data->num_hsfreq_ranges; i++) {
+ if (drv_data->hsfreq_ranges[i].range_h >= data_rate_mbps) {
+ hsfreq = drv_data->hsfreq_ranges[i].cfg_bit;
+ break;
+ }
+ }
+ if (!hsfreq)
+ return -EINVAL;
+
+ priv->hsfreq = hsfreq;
+ priv->config = *config;
+ return 0;
+}
+
+static int rockchip_inno_csidphy_power_on(struct phy *phy)
+{
+ struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy);
+ const struct dphy_drv_data *drv_data = priv->drv_data;
+ u64 data_rate_mbps = HZ_TO_MHZ(priv->config.hs_clk_rate);
+ u32 val;
+ int ret, i;
+
+ ret = clk_enable(priv->pclk);
+ if (ret < 0)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret < 0) {
+ clk_disable(priv->pclk);
+ return ret;
+ }
+
+ /* phy start */
+ if (drv_data->pwrctl_offset >= 0)
+ writel(CSIDPHY_CTRL_PWRCTL_UNDEFINED |
+ CSIDPHY_CTRL_PWRCTL_SYNCRST,
+ priv->phy_base + drv_data->pwrctl_offset);
+
+ /* set data lane num and enable clock lane */
+ val = FIELD_PREP(CSIDPHY_CTRL_LANE_ENABLE_MASK, GENMASK(priv->config.lanes - 1, 0)) |
+ FIELD_PREP(CSIDPHY_CTRL_LANE_ENABLE_CK, 1) |
+ FIELD_PREP(CSIDPHY_CTRL_LANE_ENABLE_UNDEFINED, 1);
+ writel(val, priv->phy_base + CSIDPHY_CTRL_LANE_ENABLE);
+
+ /* Reset dphy analog part */
+ if (drv_data->pwrctl_offset >= 0)
+ writel(CSIDPHY_CTRL_PWRCTL_UNDEFINED,
+ priv->phy_base + drv_data->pwrctl_offset);
+ usleep_range(500, 1000);
+
+ /* Reset dphy digital part */
+ writel(CSIDPHY_CTRL_DIG_RST_UNDEFINED,
+ priv->phy_base + CSIDPHY_CTRL_DIG_RST);
+ writel(CSIDPHY_CTRL_DIG_RST_UNDEFINED + CSIDPHY_CTRL_DIG_RST_RESET,
+ priv->phy_base + CSIDPHY_CTRL_DIG_RST);
+
+ /* not into receive mode/wait stopstate */
+ write_grf_reg(priv, GRF_DPHY_CSIPHY_FORCERXMODE, 0x0);
+
+ /* enable calibration */
+ if (data_rate_mbps > 1500 && drv_data->calib_offset >= 0) {
+ writel(CSIDPHY_CALIB_EN,
+ priv->phy_base + drv_data->calib_offset +
+ CSIDPHY_CLK_CALIB_EN);
+ for (i = 0; i < priv->config.lanes; i++)
+ writel(CSIDPHY_CALIB_EN,
+ priv->phy_base + drv_data->calib_offset +
+ CSIDPHY_LANE_CALIB_EN(i));
+ }
+
+ rockchip_inno_csidphy_ths_settle(priv, priv->hsfreq,
+ CSIDPHY_CLK_THS_SETTLE);
+ for (i = 0; i < priv->config.lanes; i++)
+ rockchip_inno_csidphy_ths_settle(priv, priv->hsfreq,
+ CSIDPHY_LANE_THS_SETTLE(i));
+
+ write_grf_reg(priv, GRF_DPHY_CSIPHY_CLKLANE_EN, 0x1);
+ write_grf_reg(priv, GRF_DPHY_CSIPHY_DATALANE_EN,
+ GENMASK(priv->config.lanes - 1, 0));
+
+ return 0;
+}
+
+static int rockchip_inno_csidphy_power_off(struct phy *phy)
+{
+ struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy);
+ const struct dphy_drv_data *drv_data = priv->drv_data;
+
+ /* disable all lanes */
+ writel(CSIDPHY_CTRL_LANE_ENABLE_UNDEFINED,
+ priv->phy_base + CSIDPHY_CTRL_LANE_ENABLE);
+
+ /* disable pll and ldo */
+ if (drv_data->pwrctl_offset >= 0)
+ writel(CSIDPHY_CTRL_PWRCTL_UNDEFINED |
+ CSIDPHY_CTRL_PWRCTL_LDO_PD |
+ CSIDPHY_CTRL_PWRCTL_PLL_PD,
+ priv->phy_base + drv_data->pwrctl_offset);
+ usleep_range(500, 1000);
+
+ pm_runtime_put(priv->dev);
+ clk_disable(priv->pclk);
+
+ return 0;
+}
+
+static int rockchip_inno_csidphy_init(struct phy *phy)
+{
+ struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy);
+
+ return clk_prepare(priv->pclk);
+}
+
+static int rockchip_inno_csidphy_exit(struct phy *phy)
+{
+ struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy);
+
+ clk_unprepare(priv->pclk);
+
+ return 0;
+}
+
+static const struct phy_ops rockchip_inno_csidphy_ops = {
+ .power_on = rockchip_inno_csidphy_power_on,
+ .power_off = rockchip_inno_csidphy_power_off,
+ .init = rockchip_inno_csidphy_init,
+ .exit = rockchip_inno_csidphy_exit,
+ .configure = rockchip_inno_csidphy_configure,
+ .owner = THIS_MODULE,
+};
+
+static const struct dphy_drv_data rk1808_mipidphy_drv_data = {
+ .pwrctl_offset = -1,
+ .ths_settle_offset = RK1808_CSIDPHY_CLK_WR_THS_SETTLE,
+ .calib_offset = RK1808_CSIDPHY_CLK_CALIB_EN,
+ .hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges,
+ .num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges),
+ .grf_regs = rk1808_grf_dphy_regs,
+};
+
+static const struct dphy_drv_data rk3326_mipidphy_drv_data = {
+ .pwrctl_offset = CSIDPHY_CTRL_PWRCTL,
+ .ths_settle_offset = RK3326_CSIDPHY_CLK_WR_THS_SETTLE,
+ .calib_offset = -1,
+ .hsfreq_ranges = rk3326_mipidphy_hsfreq_ranges,
+ .num_hsfreq_ranges = ARRAY_SIZE(rk3326_mipidphy_hsfreq_ranges),
+ .grf_regs = rk3326_grf_dphy_regs,
+};
+
+static const struct dphy_drv_data rk3368_mipidphy_drv_data = {
+ .pwrctl_offset = CSIDPHY_CTRL_PWRCTL,
+ .ths_settle_offset = RK3368_CSIDPHY_CLK_WR_THS_SETTLE,
+ .calib_offset = -1,
+ .hsfreq_ranges = rk3368_mipidphy_hsfreq_ranges,
+ .num_hsfreq_ranges = ARRAY_SIZE(rk3368_mipidphy_hsfreq_ranges),
+ .grf_regs = rk3368_grf_dphy_regs,
+};
+
+static const struct dphy_drv_data rk3568_mipidphy_drv_data = {
+ .pwrctl_offset = -1,
+ .ths_settle_offset = RK3568_CSIDPHY_CLK_WR_THS_SETTLE,
+ .calib_offset = RK3568_CSIDPHY_CLK_CALIB_EN,
+ .hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges,
+ .num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges),
+ .grf_regs = rk3568_grf_dphy_regs,
+};
+
+static const struct of_device_id rockchip_inno_csidphy_match_id[] = {
+ {
+ .compatible = "rockchip,px30-csi-dphy",
+ .data = &rk3326_mipidphy_drv_data,
+ },
+ {
+ .compatible = "rockchip,rk1808-csi-dphy",
+ .data = &rk1808_mipidphy_drv_data,
+ },
+ {
+ .compatible = "rockchip,rk3326-csi-dphy",
+ .data = &rk3326_mipidphy_drv_data,
+ },
+ {
+ .compatible = "rockchip,rk3368-csi-dphy",
+ .data = &rk3368_mipidphy_drv_data,
+ },
+ {
+ .compatible = "rockchip,rk3568-csi-dphy",
+ .data = &rk3568_mipidphy_drv_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rockchip_inno_csidphy_match_id);
+
+static int rockchip_inno_csidphy_probe(struct platform_device *pdev)
+{
+ struct rockchip_inno_csidphy *priv;
+ struct device *dev = &pdev->dev;
+ struct phy_provider *phy_provider;
+ struct phy *phy;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ platform_set_drvdata(pdev, priv);
+
+ priv->drv_data = of_device_get_match_data(dev);
+ if (!priv->drv_data) {
+ dev_err(dev, "Can't find device data\n");
+ return -ENODEV;
+ }
+
+ priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(priv->grf)) {
+ dev_err(dev, "Can't find GRF syscon\n");
+ return PTR_ERR(priv->grf);
+ }
+
+ priv->phy_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->phy_base))
+ return PTR_ERR(priv->phy_base);
+
+ priv->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(priv->pclk)) {
+ dev_err(dev, "failed to get pclk\n");
+ return PTR_ERR(priv->pclk);
+ }
+
+ priv->rst = devm_reset_control_get(dev, "apb");
+ if (IS_ERR(priv->rst)) {
+ dev_err(dev, "failed to get system reset control\n");
+ return PTR_ERR(priv->rst);
+ }
+
+ phy = devm_phy_create(dev, NULL, &rockchip_inno_csidphy_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create phy\n");
+ return PTR_ERR(phy);
+ }
+
+ phy_set_drvdata(phy, priv);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider)) {
+ dev_err(dev, "failed to register phy provider\n");
+ return PTR_ERR(phy_provider);
+ }
+
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static int rockchip_inno_csidphy_remove(struct platform_device *pdev)
+{
+ struct rockchip_inno_csidphy *priv = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(priv->dev);
+
+ return 0;
+}
+
+static struct platform_driver rockchip_inno_csidphy_driver = {
+ .driver = {
+ .name = "rockchip-inno-csidphy",
+ .of_match_table = rockchip_inno_csidphy_match_id,
+ },
+ .probe = rockchip_inno_csidphy_probe,
+ .remove = rockchip_inno_csidphy_remove,
+};
+
+module_platform_driver(rockchip_inno_csidphy_driver);
+MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
+MODULE_DESCRIPTION("Rockchip MIPI Innosilicon CSI-DPHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c
index a7c6c940a3a8..2c5847faff63 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c
@@ -5,22 +5,23 @@
* Author: Wyon Bi <bivvy.bi@rock-chips.com>
*/
+#include <linux/bits.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/reset.h>
+#include <linux/time64.h>
+
#include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>
-#include <linux/pm_runtime.h>
-#include <linux/mfd/syscon.h>
-
-#define PSEC_PER_SEC 1000000000000LL
#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
@@ -83,9 +84,25 @@
#define DATA_LANE_0_SKEW_PHASE_MASK GENMASK(2, 0)
#define DATA_LANE_0_SKEW_PHASE(x) UPDATE(x, 2, 0)
/* Analog Register Part: reg08 */
+#define PLL_POST_DIV_ENABLE_MASK BIT(5)
+#define PLL_POST_DIV_ENABLE BIT(5)
#define SAMPLE_CLOCK_DIRECTION_MASK BIT(4)
#define SAMPLE_CLOCK_DIRECTION_REVERSE BIT(4)
#define SAMPLE_CLOCK_DIRECTION_FORWARD 0
+#define LOWFRE_EN_MASK BIT(5)
+#define PLL_OUTPUT_FREQUENCY_DIV_BY_1 0
+#define PLL_OUTPUT_FREQUENCY_DIV_BY_2 1
+/* Analog Register Part: reg0b */
+#define CLOCK_LANE_VOD_RANGE_SET_MASK GENMASK(3, 0)
+#define CLOCK_LANE_VOD_RANGE_SET(x) UPDATE(x, 3, 0)
+#define VOD_MIN_RANGE 0x1
+#define VOD_MID_RANGE 0x3
+#define VOD_BIG_RANGE 0x7
+#define VOD_MAX_RANGE 0xf
+/* Analog Register Part: reg1E */
+#define PLL_MODE_SEL_MASK GENMASK(6, 5)
+#define PLL_MODE_SEL_LVDS_MODE 0
+#define PLL_MODE_SEL_MIPI_MODE BIT(5)
/* Digital Register Part: reg00 */
#define REG_DIG_RSTN_MASK BIT(0)
#define REG_DIG_RSTN_NORMAL BIT(0)
@@ -101,20 +118,22 @@
#define T_LPX_CNT_MASK GENMASK(5, 0)
#define T_LPX_CNT(x) UPDATE(x, 5, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg06 */
+#define T_HS_ZERO_CNT_HI_MASK BIT(7)
+#define T_HS_ZERO_CNT_HI(x) UPDATE(x, 7, 7)
#define T_HS_PREPARE_CNT_MASK GENMASK(6, 0)
#define T_HS_PREPARE_CNT(x) UPDATE(x, 6, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg07 */
-#define T_HS_ZERO_CNT_MASK GENMASK(5, 0)
-#define T_HS_ZERO_CNT(x) UPDATE(x, 5, 0)
+#define T_HS_ZERO_CNT_LO_MASK GENMASK(5, 0)
+#define T_HS_ZERO_CNT_LO(x) UPDATE(x, 5, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg08 */
#define T_HS_TRAIL_CNT_MASK GENMASK(6, 0)
#define T_HS_TRAIL_CNT(x) UPDATE(x, 6, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg09 */
-#define T_HS_EXIT_CNT_MASK GENMASK(4, 0)
-#define T_HS_EXIT_CNT(x) UPDATE(x, 4, 0)
+#define T_HS_EXIT_CNT_LO_MASK GENMASK(4, 0)
+#define T_HS_EXIT_CNT_LO(x) UPDATE(x, 4, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0a */
-#define T_CLK_POST_CNT_MASK GENMASK(3, 0)
-#define T_CLK_POST_CNT(x) UPDATE(x, 3, 0)
+#define T_CLK_POST_CNT_LO_MASK GENMASK(3, 0)
+#define T_CLK_POST_CNT_LO(x) UPDATE(x, 3, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0c */
#define LPDT_TX_PPI_SYNC_MASK BIT(2)
#define LPDT_TX_PPI_SYNC_ENABLE BIT(2)
@@ -128,9 +147,13 @@
#define T_CLK_PRE_CNT_MASK GENMASK(3, 0)
#define T_CLK_PRE_CNT(x) UPDATE(x, 3, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg10 */
+#define T_CLK_POST_CNT_HI_MASK GENMASK(7, 6)
+#define T_CLK_POST_CNT_HI(x) UPDATE(x, 7, 6)
#define T_TA_GO_CNT_MASK GENMASK(5, 0)
#define T_TA_GO_CNT(x) UPDATE(x, 5, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg11 */
+#define T_HS_EXIT_CNT_HI_MASK BIT(6)
+#define T_HS_EXIT_CNT_HI(x) UPDATE(x, 6, 6)
#define T_TA_SURE_CNT_MASK GENMASK(5, 0)
#define T_TA_SURE_CNT(x) UPDATE(x, 5, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg12 */
@@ -168,11 +191,23 @@
#define DSI_PHY_STATUS 0xb0
#define PHY_LOCK BIT(0)
+enum phy_max_rate {
+ MAX_1GHZ,
+ MAX_2_5GHZ,
+};
+
+struct inno_video_phy_plat_data {
+ const struct inno_mipi_dphy_timing *inno_mipi_dphy_timing_table;
+ const unsigned int num_timings;
+ enum phy_max_rate max_rate;
+};
+
struct inno_dsidphy {
struct device *dev;
struct clk *ref_clk;
struct clk *pclk_phy;
struct clk *pclk_host;
+ const struct inno_video_phy_plat_data *pdata;
void __iomem *phy_base;
void __iomem *host_base;
struct reset_control *rst;
@@ -199,6 +234,53 @@ enum {
REGISTER_PART_LVDS,
};
+struct inno_mipi_dphy_timing {
+ unsigned long rate;
+ u8 lpx;
+ u8 hs_prepare;
+ u8 clk_lane_hs_zero;
+ u8 data_lane_hs_zero;
+ u8 hs_trail;
+};
+
+static const
+struct inno_mipi_dphy_timing inno_mipi_dphy_timing_table_max_1ghz[] = {
+ { 110000000, 0x0, 0x20, 0x16, 0x02, 0x22},
+ { 150000000, 0x0, 0x06, 0x16, 0x03, 0x45},
+ { 200000000, 0x0, 0x18, 0x17, 0x04, 0x0b},
+ { 250000000, 0x0, 0x05, 0x17, 0x05, 0x16},
+ { 300000000, 0x0, 0x51, 0x18, 0x06, 0x2c},
+ { 400000000, 0x0, 0x64, 0x19, 0x07, 0x33},
+ { 500000000, 0x0, 0x20, 0x1b, 0x07, 0x4e},
+ { 600000000, 0x0, 0x6a, 0x1d, 0x08, 0x3a},
+ { 700000000, 0x0, 0x3e, 0x1e, 0x08, 0x6a},
+ { 800000000, 0x0, 0x21, 0x1f, 0x09, 0x29},
+ {1000000000, 0x0, 0x09, 0x20, 0x09, 0x27},
+};
+
+static const
+struct inno_mipi_dphy_timing inno_mipi_dphy_timing_table_max_2_5ghz[] = {
+ { 110000000, 0x02, 0x7f, 0x16, 0x02, 0x02},
+ { 150000000, 0x02, 0x7f, 0x16, 0x03, 0x02},
+ { 200000000, 0x02, 0x7f, 0x17, 0x04, 0x02},
+ { 250000000, 0x02, 0x7f, 0x17, 0x05, 0x04},
+ { 300000000, 0x02, 0x7f, 0x18, 0x06, 0x04},
+ { 400000000, 0x03, 0x7e, 0x19, 0x07, 0x04},
+ { 500000000, 0x03, 0x7c, 0x1b, 0x07, 0x08},
+ { 600000000, 0x03, 0x70, 0x1d, 0x08, 0x10},
+ { 700000000, 0x05, 0x40, 0x1e, 0x08, 0x30},
+ { 800000000, 0x05, 0x02, 0x1f, 0x09, 0x30},
+ {1000000000, 0x05, 0x08, 0x20, 0x09, 0x30},
+ {1200000000, 0x06, 0x03, 0x32, 0x14, 0x0f},
+ {1400000000, 0x09, 0x03, 0x32, 0x14, 0x0f},
+ {1600000000, 0x0d, 0x42, 0x36, 0x0e, 0x0f},
+ {1800000000, 0x0e, 0x47, 0x7a, 0x0e, 0x0f},
+ {2000000000, 0x11, 0x64, 0x7a, 0x0e, 0x0b},
+ {2200000000, 0x13, 0x64, 0x7e, 0x15, 0x0b},
+ {2400000000, 0x13, 0x33, 0x7f, 0x15, 0x6a},
+ {2500000000, 0x15, 0x54, 0x7f, 0x15, 0x6a},
+};
+
static inline struct inno_dsidphy *hw_to_inno(struct clk_hw *hw)
{
return container_of(hw, struct inno_dsidphy, pll.hw);
@@ -289,31 +371,15 @@ static unsigned long inno_dsidphy_pll_calc_rate(struct inno_dsidphy *inno,
static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
{
struct phy_configure_opts_mipi_dphy *cfg = &inno->dphy_cfg;
- const struct {
- unsigned long rate;
- u8 hs_prepare;
- u8 clk_lane_hs_zero;
- u8 data_lane_hs_zero;
- u8 hs_trail;
- } timings[] = {
- { 110000000, 0x20, 0x16, 0x02, 0x22},
- { 150000000, 0x06, 0x16, 0x03, 0x45},
- { 200000000, 0x18, 0x17, 0x04, 0x0b},
- { 250000000, 0x05, 0x17, 0x05, 0x16},
- { 300000000, 0x51, 0x18, 0x06, 0x2c},
- { 400000000, 0x64, 0x19, 0x07, 0x33},
- { 500000000, 0x20, 0x1b, 0x07, 0x4e},
- { 600000000, 0x6a, 0x1d, 0x08, 0x3a},
- { 700000000, 0x3e, 0x1e, 0x08, 0x6a},
- { 800000000, 0x21, 0x1f, 0x09, 0x29},
- {1000000000, 0x09, 0x20, 0x09, 0x27},
- };
+ const struct inno_mipi_dphy_timing *timings;
u32 t_txbyteclkhs, t_txclkesc;
u32 txbyteclkhs, txclkesc, esc_clk_div;
u32 hs_exit, clk_post, clk_pre, wakeup, lpx, ta_go, ta_sure, ta_wait;
u32 hs_prepare, hs_trail, hs_zero, clk_lane_hs_zero, data_lane_hs_zero;
unsigned int i;
+ timings = inno->pdata->inno_mipi_dphy_timing_table;
+
inno_dsidphy_pll_calc_rate(inno, cfg->hs_clk_rate);
/* Select MIPI mode */
@@ -326,6 +392,13 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
REG_FBDIV_HI_MASK, REG_FBDIV_HI(inno->pll.fbdiv));
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x04,
REG_FBDIV_LO_MASK, REG_FBDIV_LO(inno->pll.fbdiv));
+ if (inno->pdata->max_rate == MAX_2_5GHZ) {
+ phy_update_bits(inno, REGISTER_PART_ANALOG, 0x08,
+ PLL_POST_DIV_ENABLE_MASK, PLL_POST_DIV_ENABLE);
+ phy_update_bits(inno, REGISTER_PART_ANALOG, 0x0b,
+ CLOCK_LANE_VOD_RANGE_SET_MASK,
+ CLOCK_LANE_VOD_RANGE_SET(VOD_MAX_RANGE));
+ }
/* Enable PLL and LDO */
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01,
REG_LDOPD_MASK | REG_PLLPD_MASK,
@@ -364,15 +437,7 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
* The value of counter for HS Tclk-pre
* Tclk-pre = Tpin_txbyteclkhs * value
*/
- clk_pre = DIV_ROUND_UP(cfg->clk_pre, t_txbyteclkhs);
-
- /*
- * The value of counter for HS Tlpx Time
- * Tlpx = Tpin_txbyteclkhs * (2 + value)
- */
- lpx = DIV_ROUND_UP(cfg->lpx, t_txbyteclkhs);
- if (lpx >= 2)
- lpx -= 2;
+ clk_pre = DIV_ROUND_UP(cfg->clk_pre, BITS_PER_BYTE);
/*
* The value of counter for HS Tta-go
@@ -393,13 +458,24 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
*/
ta_wait = DIV_ROUND_UP(cfg->ta_get, t_txclkesc);
- for (i = 0; i < ARRAY_SIZE(timings); i++)
+ for (i = 0; i < inno->pdata->num_timings; i++)
if (inno->pll.rate <= timings[i].rate)
break;
- if (i == ARRAY_SIZE(timings))
+ if (i == inno->pdata->num_timings)
--i;
+ /*
+ * The value of counter for HS Tlpx Time
+ * Tlpx = Tpin_txbyteclkhs * (2 + value)
+ */
+ if (inno->pdata->max_rate == MAX_1GHZ) {
+ lpx = DIV_ROUND_UP(cfg->lpx, t_txbyteclkhs);
+ if (lpx >= 2)
+ lpx -= 2;
+ } else
+ lpx = timings[i].lpx;
+
hs_prepare = timings[i].hs_prepare;
hs_trail = timings[i].hs_trail;
clk_lane_hs_zero = timings[i].clk_lane_hs_zero;
@@ -416,14 +492,23 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
T_LPX_CNT(lpx));
phy_update_bits(inno, i, 0x06, T_HS_PREPARE_CNT_MASK,
T_HS_PREPARE_CNT(hs_prepare));
- phy_update_bits(inno, i, 0x07, T_HS_ZERO_CNT_MASK,
- T_HS_ZERO_CNT(hs_zero));
+ if (inno->pdata->max_rate == MAX_2_5GHZ)
+ phy_update_bits(inno, i, 0x06, T_HS_ZERO_CNT_HI_MASK,
+ T_HS_ZERO_CNT_HI(hs_zero >> 6));
+ phy_update_bits(inno, i, 0x07, T_HS_ZERO_CNT_LO_MASK,
+ T_HS_ZERO_CNT_LO(hs_zero));
phy_update_bits(inno, i, 0x08, T_HS_TRAIL_CNT_MASK,
T_HS_TRAIL_CNT(hs_trail));
- phy_update_bits(inno, i, 0x09, T_HS_EXIT_CNT_MASK,
- T_HS_EXIT_CNT(hs_exit));
- phy_update_bits(inno, i, 0x0a, T_CLK_POST_CNT_MASK,
- T_CLK_POST_CNT(clk_post));
+ if (inno->pdata->max_rate == MAX_2_5GHZ)
+ phy_update_bits(inno, i, 0x11, T_HS_EXIT_CNT_HI_MASK,
+ T_HS_EXIT_CNT_HI(hs_exit >> 5));
+ phy_update_bits(inno, i, 0x09, T_HS_EXIT_CNT_LO_MASK,
+ T_HS_EXIT_CNT_LO(hs_exit));
+ if (inno->pdata->max_rate == MAX_2_5GHZ)
+ phy_update_bits(inno, i, 0x10, T_CLK_POST_CNT_HI_MASK,
+ T_CLK_POST_CNT_HI(clk_post >> 4));
+ phy_update_bits(inno, i, 0x0a, T_CLK_POST_CNT_LO_MASK,
+ T_CLK_POST_CNT_LO(clk_post));
phy_update_bits(inno, i, 0x0e, T_CLK_PRE_CNT_MASK,
T_CLK_PRE_CNT(clk_pre));
phy_update_bits(inno, i, 0x0c, T_WAKEUP_CNT_HI_MASK,
@@ -451,8 +536,9 @@ static void inno_dsidphy_lvds_mode_enable(struct inno_dsidphy *inno)
/* Sample clock reverse direction */
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x08,
- SAMPLE_CLOCK_DIRECTION_MASK,
- SAMPLE_CLOCK_DIRECTION_REVERSE);
+ SAMPLE_CLOCK_DIRECTION_MASK | LOWFRE_EN_MASK,
+ SAMPLE_CLOCK_DIRECTION_REVERSE |
+ PLL_OUTPUT_FREQUENCY_DIV_BY_1);
/* Select LVDS mode */
phy_update_bits(inno, REGISTER_PART_LVDS, 0x03,
@@ -472,6 +558,10 @@ static void inno_dsidphy_lvds_mode_enable(struct inno_dsidphy *inno)
msleep(20);
+ /* Select PLL mode */
+ phy_update_bits(inno, REGISTER_PART_ANALOG, 0x1e,
+ PLL_MODE_SEL_MASK, PLL_MODE_SEL_LVDS_MODE);
+
/* Reset LVDS digital logic */
phy_update_bits(inno, REGISTER_PART_LVDS, 0x00,
LVDS_DIGITAL_INTERNAL_RESET_MASK,
@@ -591,6 +681,18 @@ static const struct phy_ops inno_dsidphy_ops = {
.owner = THIS_MODULE,
};
+static const struct inno_video_phy_plat_data max_1ghz_video_phy_plat_data = {
+ .inno_mipi_dphy_timing_table = inno_mipi_dphy_timing_table_max_1ghz,
+ .num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table_max_1ghz),
+ .max_rate = MAX_1GHZ,
+};
+
+static const struct inno_video_phy_plat_data max_2_5ghz_video_phy_plat_data = {
+ .inno_mipi_dphy_timing_table = inno_mipi_dphy_timing_table_max_2_5ghz,
+ .num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table_max_2_5ghz),
+ .max_rate = MAX_2_5GHZ,
+};
+
static int inno_dsidphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -604,11 +706,12 @@ static int inno_dsidphy_probe(struct platform_device *pdev)
return -ENOMEM;
inno->dev = dev;
+ inno->pdata = of_device_get_match_data(inno->dev);
platform_set_drvdata(pdev, inno);
inno->phy_base = devm_platform_ioremap_resource(pdev, 0);
- if (!inno->phy_base)
- return -ENOMEM;
+ if (IS_ERR(inno->phy_base))
+ return PTR_ERR(inno->phy_base);
inno->ref_clk = devm_clk_get(dev, "ref");
if (IS_ERR(inno->ref_clk)) {
@@ -662,9 +765,19 @@ static int inno_dsidphy_remove(struct platform_device *pdev)
}
static const struct of_device_id inno_dsidphy_of_match[] = {
- { .compatible = "rockchip,px30-dsi-dphy", },
- { .compatible = "rockchip,rk3128-dsi-dphy", },
- { .compatible = "rockchip,rk3368-dsi-dphy", },
+ {
+ .compatible = "rockchip,px30-dsi-dphy",
+ .data = &max_1ghz_video_phy_plat_data,
+ }, {
+ .compatible = "rockchip,rk3128-dsi-dphy",
+ .data = &max_1ghz_video_phy_plat_data,
+ }, {
+ .compatible = "rockchip,rk3368-dsi-dphy",
+ .data = &max_1ghz_video_phy_plat_data,
+ }, {
+ .compatible = "rockchip,rk3568-dsi-dphy",
+ .data = &max_2_5ghz_video_phy_plat_data,
+ },
{}
};
MODULE_DEVICE_TABLE(of, inno_dsidphy_of_match);
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
index 9ca20c947283..80acca4e9e14 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -620,7 +620,7 @@ static int inno_hdmi_phy_rk3228_clk_set_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw);
- const struct pre_pll_config *cfg = pre_pll_cfg_table;
+ const struct pre_pll_config *cfg;
unsigned long tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
u32 v;
int ret;
@@ -774,7 +774,7 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw);
- const struct pre_pll_config *cfg = pre_pll_cfg_table;
+ const struct pre_pll_config *cfg;
unsigned long tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
u32 val;
int ret;
@@ -1144,7 +1144,6 @@ static int inno_hdmi_phy_probe(struct platform_device *pdev)
{
struct inno_hdmi_phy *inno;
struct phy_provider *phy_provider;
- struct resource *res;
void __iomem *regs;
int ret;
@@ -1158,8 +1157,7 @@ static int inno_hdmi_phy_probe(struct platform_device *pdev)
if (!inno->plat_data || !inno->plat_data->ops)
return -EINVAL;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(inno->dev, res);
+ regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 680cc0c8825c..e6ededc51523 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -116,11 +116,15 @@ struct rockchip_chg_det_reg {
* @bvalid_det_en: vbus valid rise detection enable register.
* @bvalid_det_st: vbus valid rise detection status register.
* @bvalid_det_clr: vbus valid rise detection clear register.
+ * @id_det_en: id detection enable register.
+ * @id_det_st: id detection state register.
+ * @id_det_clr: id detection clear register.
* @ls_det_en: linestate detection enable register.
* @ls_det_st: linestate detection state register.
* @ls_det_clr: linestate detection clear register.
* @utmi_avalid: utmi vbus avalid status register.
* @utmi_bvalid: utmi vbus bvalid status register.
+ * @utmi_id: utmi id state register.
* @utmi_ls: utmi linestate state register.
* @utmi_hstdet: utmi host disconnect register.
*/
@@ -129,11 +133,15 @@ struct rockchip_usb2phy_port_cfg {
struct usb2phy_reg bvalid_det_en;
struct usb2phy_reg bvalid_det_st;
struct usb2phy_reg bvalid_det_clr;
+ struct usb2phy_reg id_det_en;
+ struct usb2phy_reg id_det_st;
+ struct usb2phy_reg id_det_clr;
struct usb2phy_reg ls_det_en;
struct usb2phy_reg ls_det_st;
struct usb2phy_reg ls_det_clr;
struct usb2phy_reg utmi_avalid;
struct usb2phy_reg utmi_bvalid;
+ struct usb2phy_reg utmi_id;
struct usb2phy_reg utmi_ls;
struct usb2phy_reg utmi_hstdet;
};
@@ -161,6 +169,7 @@ struct rockchip_usb2phy_cfg {
* @suspended: phy suspended flag.
* @vbus_attached: otg device vbus status.
* @bvalid_irq: IRQ number assigned for vbus valid rise detection.
+ * @id_irq: IRQ number assigned for ID pin detection.
* @ls_irq: IRQ number assigned for linestate detection.
* @otg_mux_irq: IRQ number which multiplex otg-id/otg-bvalid/linestate
* irqs to one irq in otg-port.
@@ -179,6 +188,7 @@ struct rockchip_usb2phy_port {
bool suspended;
bool vbus_attached;
int bvalid_irq;
+ int id_irq;
int ls_irq;
int otg_mux_irq;
struct mutex mutex;
@@ -204,6 +214,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 +229,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];
};
@@ -251,7 +263,7 @@ static inline bool property_enabled(struct regmap *base,
return false;
tmp = (orig & mask) >> reg->bitstart;
- return tmp == reg->enable;
+ return tmp != reg->disable;
}
static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw)
@@ -321,7 +333,7 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
struct device_node *node = rphy->dev->of_node;
struct clk_init_data init;
const char *clk_name;
- int ret;
+ int ret = 0;
init.flags = 0;
init.name = "clk_usbphy_480m";
@@ -352,15 +364,8 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
if (ret < 0)
goto err_clk_provider;
- ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister,
- rphy);
- if (ret < 0)
- goto err_unreg_action;
-
- return 0;
+ return devm_add_action_or_reset(rphy->dev, rockchip_usb2phy_clk480m_unregister, rphy);
-err_unreg_action:
- of_clk_del_provider(node);
err_clk_provider:
clk_unregister(rphy->clk480m);
err_ret:
@@ -424,6 +429,19 @@ static int rockchip_usb2phy_init(struct phy *phy)
if (ret)
goto out;
+ /* clear id status and enable id detect irq */
+ ret = property_enable(rphy->grf,
+ &rport->port_cfg->id_det_clr,
+ true);
+ if (ret)
+ goto out;
+
+ ret = property_enable(rphy->grf,
+ &rport->port_cfg->id_det_en,
+ true);
+ if (ret)
+ goto out;
+
schedule_delayed_work(&rport->otg_sm_work,
OTG_SCHEDULE_DELAY * 3);
} else {
@@ -546,7 +564,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
rport->state = OTG_STATE_B_IDLE;
if (!vbus_attach)
rockchip_usb2phy_power_off(rport->phy);
- /* fall through */
+ fallthrough;
case OTG_STATE_B_IDLE:
if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0) {
dev_dbg(&rport->phy->dev, "usb otg host connect\n");
@@ -754,16 +772,15 @@ static void rockchip_chg_detect_work(struct work_struct *work)
rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP;
else
rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP;
- /* fall through */
+ fallthrough;
case USB_CHG_STATE_SECONDARY_DONE:
rphy->chg_state = USB_CHG_STATE_DETECTED;
- delay = 0;
- /* fall through */
+ fallthrough;
case USB_CHG_STATE_DETECTED:
/* put the controller in normal mode */
property_enable(base, &rphy->phy_cfg->chg_det.opmode, true);
rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work);
- dev_info(&rport->phy->dev, "charger = %s\n",
+ dev_dbg(&rport->phy->dev, "charger = %s\n",
chg_to_string(rphy->chg_type));
return;
default:
@@ -835,7 +852,7 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work)
dev_dbg(&rport->phy->dev, "FS/LS online\n");
break;
}
- /* fall through */
+ fallthrough;
case PHY_STATE_CONNECT:
if (rport->suspended) {
dev_dbg(&rport->phy->dev, "Connected\n");
@@ -911,27 +928,163 @@ static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data)
if (!property_enabled(rphy->grf, &rport->port_cfg->bvalid_det_st))
return IRQ_NONE;
- mutex_lock(&rport->mutex);
-
/* clear bvalid detect irq pending status */
property_enable(rphy->grf, &rport->port_cfg->bvalid_det_clr, true);
- mutex_unlock(&rport->mutex);
-
rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work);
return IRQ_HANDLED;
}
-static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data)
+static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data)
{
struct rockchip_usb2phy_port *rport = data;
struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+ bool id;
- if (property_enabled(rphy->grf, &rport->port_cfg->bvalid_det_st))
- return rockchip_usb2phy_bvalid_irq(irq, data);
- else
+ if (!property_enabled(rphy->grf, &rport->port_cfg->id_det_st))
return IRQ_NONE;
+
+ /* clear id detect irq pending status */
+ property_enable(rphy->grf, &rport->port_cfg->id_det_clr, true);
+
+ id = property_enabled(rphy->grf, &rport->port_cfg->utmi_id);
+ extcon_set_state_sync(rphy->edev, EXTCON_USB_HOST, !id);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data)
+{
+ irqreturn_t ret = IRQ_NONE;
+
+ ret |= rockchip_usb2phy_bvalid_irq(irq, data);
+ ret |= rockchip_usb2phy_id_irq(irq, data);
+
+ return ret;
+}
+
+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;
+
+ switch (rport->port_id) {
+ case USB2PHY_PORT_OTG:
+ if (rport->mode != USB_DR_MODE_HOST &&
+ rport->mode != USB_DR_MODE_UNKNOWN)
+ ret |= rockchip_usb2phy_otg_mux_irq(irq, rport);
+ break;
+ case USB2PHY_PORT_HOST:
+ ret |= rockchip_usb2phy_linestate_irq(irq, rport);
+ break;
+ }
+ }
+
+ 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;
+ }
+
+ rport->id_irq = of_irq_get_byname(child_np, "otg-id");
+ if (rport->id_irq < 0) {
+ dev_err(rphy->dev, "no otg-id irq provided\n");
+ ret = rport->id_irq;
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(rphy->dev, rport->id_irq,
+ NULL,
+ rockchip_usb2phy_id_irq,
+ IRQF_ONESHOT,
+ "rockchip_usb2phy_id",
+ rport);
+ if (ret) {
+ dev_err(rphy->dev,
+ "failed to request otg-id irq handle\n");
+ return ret;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
}
static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
@@ -947,18 +1100,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;
}
@@ -980,7 +1124,7 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
struct rockchip_usb2phy_port *rport,
struct device_node *child_np)
{
- int ret;
+ int ret, id;
rport->port_id = USB2PHY_PORT_OTG;
rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG];
@@ -1007,43 +1151,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)) {
@@ -1051,8 +1162,16 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
ret = devm_extcon_register_notifier(rphy->dev, rphy->edev,
EXTCON_USB_HOST, &rport->event_nb);
- if (ret)
+ if (ret) {
dev_err(rphy->dev, "register USB HOST notifier failed\n");
+ goto out;
+ }
+
+ if (!of_property_read_bool(rphy->dev->of_node, "extcon")) {
+ /* do initial sync of usb state */
+ id = property_enabled(rphy->grf, &rport->port_cfg->utmi_id);
+ extcon_set_state_sync(rphy->edev, EXTCON_USB_HOST, !id);
+ }
}
out:
@@ -1081,12 +1200,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 =
@@ -1098,16 +1224,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);
@@ -1157,7 +1293,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
if (IS_ERR(phy)) {
- dev_err(dev, "failed to create phy\n");
+ dev_err_probe(dev, PTR_ERR(phy), "failed to create phy\n");
ret = PTR_ERR(phy);
goto put_child;
}
@@ -1180,11 +1316,27 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
next_child:
/* to prevent out of boundary */
- if (++index >= rphy->phy_cfg->num_ports)
+ if (++index >= rphy->phy_cfg->num_ports) {
+ of_node_put(child_np);
break;
+ }
}
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:
@@ -1208,10 +1360,14 @@ static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = {
.bvalid_det_en = { 0x0680, 3, 3, 0, 1 },
.bvalid_det_st = { 0x0690, 3, 3, 0, 1 },
.bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 },
+ .id_det_en = { 0x0680, 6, 5, 0, 3 },
+ .id_det_st = { 0x0690, 6, 5, 0, 3 },
+ .id_det_clr = { 0x06a0, 6, 5, 0, 3 },
.ls_det_en = { 0x0680, 2, 2, 0, 1 },
.ls_det_st = { 0x0690, 2, 2, 0, 1 },
.ls_det_clr = { 0x06a0, 2, 2, 0, 1 },
.utmi_bvalid = { 0x0480, 4, 4, 0, 1 },
+ .utmi_id = { 0x0480, 1, 1, 0, 1 },
.utmi_ls = { 0x0480, 3, 2, 0, 1 },
},
[USB2PHY_PORT_HOST] = {
@@ -1256,6 +1412,53 @@ static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = {
{ /* sentinel */ }
};
+static const struct rockchip_usb2phy_cfg rk3308_phy_cfgs[] = {
+ {
+ .reg = 0x100,
+ .num_ports = 2,
+ .clkout_ctl = { 0x108, 4, 4, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0x0100, 8, 0, 0, 0x1d1 },
+ .bvalid_det_en = { 0x3020, 3, 2, 0, 3 },
+ .bvalid_det_st = { 0x3024, 3, 2, 0, 3 },
+ .bvalid_det_clr = { 0x3028, 3, 2, 0, 3 },
+ .id_det_en = { 0x3020, 5, 4, 0, 3 },
+ .id_det_st = { 0x3024, 5, 4, 0, 3 },
+ .id_det_clr = { 0x3028, 5, 4, 0, 3 },
+ .ls_det_en = { 0x3020, 0, 0, 0, 1 },
+ .ls_det_st = { 0x3024, 0, 0, 0, 1 },
+ .ls_det_clr = { 0x3028, 0, 0, 0, 1 },
+ .utmi_avalid = { 0x0120, 10, 10, 0, 1 },
+ .utmi_bvalid = { 0x0120, 9, 9, 0, 1 },
+ .utmi_id = { 0x0120, 6, 6, 0, 1 },
+ .utmi_ls = { 0x0120, 5, 4, 0, 1 },
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0x0104, 8, 0, 0, 0x1d1 },
+ .ls_det_en = { 0x3020, 1, 1, 0, 1 },
+ .ls_det_st = { 0x3024, 1, 1, 0, 1 },
+ .ls_det_clr = { 0x3028, 1, 1, 0, 1 },
+ .utmi_ls = { 0x0120, 17, 16, 0, 1 },
+ .utmi_hstdet = { 0x0120, 19, 19, 0, 1 }
+ }
+ },
+ .chg_det = {
+ .opmode = { 0x0100, 3, 0, 5, 1 },
+ .cp_det = { 0x0120, 24, 24, 0, 1 },
+ .dcp_det = { 0x0120, 23, 23, 0, 1 },
+ .dp_det = { 0x0120, 25, 25, 0, 1 },
+ .idm_sink_en = { 0x0108, 8, 8, 0, 1 },
+ .idp_sink_en = { 0x0108, 7, 7, 0, 1 },
+ .idp_src_en = { 0x0108, 9, 9, 0, 1 },
+ .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 },
+ .vdm_src_en = { 0x0108, 12, 12, 0, 1 },
+ .vdp_src_en = { 0x0108, 11, 11, 0, 1 },
+ },
+ },
+ { /* sentinel */ }
+};
+
static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = {
{
.reg = 0x100,
@@ -1264,14 +1467,18 @@ static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = {
.port_cfgs = {
[USB2PHY_PORT_OTG] = {
.phy_sus = { 0x0100, 15, 0, 0, 0x1d1 },
- .bvalid_det_en = { 0x0110, 2, 2, 0, 1 },
- .bvalid_det_st = { 0x0114, 2, 2, 0, 1 },
- .bvalid_det_clr = { 0x0118, 2, 2, 0, 1 },
+ .bvalid_det_en = { 0x0110, 3, 2, 0, 3 },
+ .bvalid_det_st = { 0x0114, 3, 2, 0, 3 },
+ .bvalid_det_clr = { 0x0118, 3, 2, 0, 3 },
+ .id_det_en = { 0x0110, 5, 4, 0, 3 },
+ .id_det_st = { 0x0114, 5, 4, 0, 3 },
+ .id_det_clr = { 0x0118, 5, 4, 0, 3 },
.ls_det_en = { 0x0110, 0, 0, 0, 1 },
.ls_det_st = { 0x0114, 0, 0, 0, 1 },
.ls_det_clr = { 0x0118, 0, 0, 0, 1 },
.utmi_avalid = { 0x0120, 10, 10, 0, 1 },
.utmi_bvalid = { 0x0120, 9, 9, 0, 1 },
+ .utmi_id = { 0x0120, 6, 6, 0, 1 },
.utmi_ls = { 0x0120, 5, 4, 0, 1 },
},
[USB2PHY_PORT_HOST] = {
@@ -1329,8 +1536,12 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
.bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 },
.bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 },
.bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 },
+ .id_det_en = { 0xe3c0, 5, 4, 0, 3 },
+ .id_det_st = { 0xe3e0, 5, 4, 0, 3 },
+ .id_det_clr = { 0xe3d0, 5, 4, 0, 3 },
.utmi_avalid = { 0xe2ac, 7, 7, 0, 1 },
.utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 },
+ .utmi_id = { 0xe2ac, 8, 8, 0, 1 },
},
[USB2PHY_PORT_HOST] = {
.phy_sus = { 0xe458, 1, 0, 0x2, 0x1 },
@@ -1364,8 +1575,12 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
.bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 },
.bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 },
.bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 },
+ .id_det_en = { 0xe3c0, 10, 9, 0, 3 },
+ .id_det_st = { 0xe3e0, 10, 9, 0, 3 },
+ .id_det_clr = { 0xe3d0, 10, 9, 0, 3 },
.utmi_avalid = { 0xe2ac, 10, 10, 0, 1 },
.utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 },
+ .utmi_id = { 0xe2ac, 11, 11, 0, 1 },
},
[USB2PHY_PORT_HOST] = {
.phy_sus = { 0xe468, 1, 0, 0x2, 0x1 },
@@ -1380,6 +1595,73 @@ 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, 3, 2, 0, 3 },
+ .bvalid_det_st = { 0x0084, 3, 2, 0, 3 },
+ .bvalid_det_clr = { 0x0088, 3, 2, 0, 3 },
+ .id_det_en = { 0x0080, 5, 4, 0, 3 },
+ .id_det_st = { 0x0084, 5, 4, 0, 3 },
+ .id_det_clr = { 0x0088, 5, 4, 0, 3 },
+ .utmi_avalid = { 0x00c0, 10, 10, 0, 1 },
+ .utmi_bvalid = { 0x00c0, 9, 9, 0, 1 },
+ .utmi_id = { 0x00c0, 6, 6, 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,
@@ -1425,9 +1707,11 @@ static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = {
static const struct of_device_id rockchip_usb2phy_dt_match[] = {
{ .compatible = "rockchip,px30-usb2phy", .data = &rk3328_phy_cfgs },
{ .compatible = "rockchip,rk3228-usb2phy", .data = &rk3228_phy_cfgs },
+ { .compatible = "rockchip,rk3308-usb2phy", .data = &rk3308_phy_cfgs },
{ .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 },
{}
};
diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
new file mode 100644
index 000000000000..7b213825fb5d
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip PIPE USB3.0 PCIE SATA Combo Phy driver
+ *
+ * Copyright (C) 2021 Rockchip Electronics Co., Ltd.
+ */
+
+#include <dt-bindings/phy/phy.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/units.h>
+
+#define BIT_WRITEABLE_SHIFT 16
+#define REF_CLOCK_24MHz (24 * HZ_PER_MHZ)
+#define REF_CLOCK_25MHz (25 * HZ_PER_MHZ)
+#define REF_CLOCK_100MHz (100 * HZ_PER_MHZ)
+
+/* COMBO PHY REG */
+#define PHYREG6 0x14
+#define PHYREG6_PLL_DIV_MASK GENMASK(7, 6)
+#define PHYREG6_PLL_DIV_SHIFT 6
+#define PHYREG6_PLL_DIV_2 1
+
+#define PHYREG7 0x18
+#define PHYREG7_TX_RTERM_MASK GENMASK(7, 4)
+#define PHYREG7_TX_RTERM_SHIFT 4
+#define PHYREG7_TX_RTERM_50OHM 8
+#define PHYREG7_RX_RTERM_MASK GENMASK(3, 0)
+#define PHYREG7_RX_RTERM_SHIFT 0
+#define PHYREG7_RX_RTERM_44OHM 15
+
+#define PHYREG8 0x1C
+#define PHYREG8_SSC_EN BIT(4)
+
+#define PHYREG11 0x28
+#define PHYREG11_SU_TRIM_0_7 0xF0
+
+#define PHYREG12 0x2C
+#define PHYREG12_PLL_LPF_ADJ_VALUE 4
+
+#define PHYREG13 0x30
+#define PHYREG13_RESISTER_MASK GENMASK(5, 4)
+#define PHYREG13_RESISTER_SHIFT 0x4
+#define PHYREG13_RESISTER_HIGH_Z 3
+#define PHYREG13_CKRCV_AMP0 BIT(7)
+
+#define PHYREG14 0x34
+#define PHYREG14_CKRCV_AMP1 BIT(0)
+
+#define PHYREG15 0x38
+#define PHYREG15_CTLE_EN BIT(0)
+#define PHYREG15_SSC_CNT_MASK GENMASK(7, 6)
+#define PHYREG15_SSC_CNT_SHIFT 6
+#define PHYREG15_SSC_CNT_VALUE 1
+
+#define PHYREG16 0x3C
+#define PHYREG16_SSC_CNT_VALUE 0x5f
+
+#define PHYREG18 0x44
+#define PHYREG18_PLL_LOOP 0x32
+
+#define PHYREG32 0x7C
+#define PHYREG32_SSC_MASK GENMASK(7, 4)
+#define PHYREG32_SSC_DIR_SHIFT 4
+#define PHYREG32_SSC_UPWARD 0
+#define PHYREG32_SSC_DOWNWARD 1
+#define PHYREG32_SSC_OFFSET_SHIFT 6
+#define PHYREG32_SSC_OFFSET_500PPM 1
+
+#define PHYREG33 0x80
+#define PHYREG33_PLL_KVCO_MASK GENMASK(4, 2)
+#define PHYREG33_PLL_KVCO_SHIFT 2
+#define PHYREG33_PLL_KVCO_VALUE 2
+
+struct rockchip_combphy_priv;
+
+struct combphy_reg {
+ u16 offset;
+ u16 bitend;
+ u16 bitstart;
+ u16 disable;
+ u16 enable;
+};
+
+struct rockchip_combphy_grfcfg {
+ struct combphy_reg pcie_mode_set;
+ struct combphy_reg usb_mode_set;
+ struct combphy_reg sgmii_mode_set;
+ struct combphy_reg qsgmii_mode_set;
+ struct combphy_reg pipe_rxterm_set;
+ struct combphy_reg pipe_txelec_set;
+ struct combphy_reg pipe_txcomp_set;
+ struct combphy_reg pipe_clk_25m;
+ struct combphy_reg pipe_clk_100m;
+ struct combphy_reg pipe_phymode_sel;
+ struct combphy_reg pipe_rate_sel;
+ struct combphy_reg pipe_rxterm_sel;
+ struct combphy_reg pipe_txelec_sel;
+ struct combphy_reg pipe_txcomp_sel;
+ struct combphy_reg pipe_clk_ext;
+ struct combphy_reg pipe_sel_usb;
+ struct combphy_reg pipe_sel_qsgmii;
+ struct combphy_reg pipe_phy_status;
+ struct combphy_reg con0_for_pcie;
+ struct combphy_reg con1_for_pcie;
+ struct combphy_reg con2_for_pcie;
+ struct combphy_reg con3_for_pcie;
+ struct combphy_reg con0_for_sata;
+ struct combphy_reg con1_for_sata;
+ struct combphy_reg con2_for_sata;
+ struct combphy_reg con3_for_sata;
+ struct combphy_reg pipe_con0_for_sata;
+ struct combphy_reg pipe_xpcs_phy_ready;
+};
+
+struct rockchip_combphy_cfg {
+ const struct rockchip_combphy_grfcfg *grfcfg;
+ int (*combphy_cfg)(struct rockchip_combphy_priv *priv);
+};
+
+struct rockchip_combphy_priv {
+ u8 type;
+ void __iomem *mmio;
+ int num_clks;
+ struct clk_bulk_data *clks;
+ struct device *dev;
+ struct regmap *pipe_grf;
+ struct regmap *phy_grf;
+ struct phy *phy;
+ struct reset_control *phy_rst;
+ const struct rockchip_combphy_cfg *cfg;
+ bool enable_ssc;
+ bool ext_refclk;
+ struct clk *refclk;
+};
+
+static void rockchip_combphy_updatel(struct rockchip_combphy_priv *priv,
+ int mask, int val, int reg)
+{
+ unsigned int temp;
+
+ temp = readl(priv->mmio + reg);
+ temp = (temp & ~(mask)) | val;
+ writel(temp, priv->mmio + reg);
+}
+
+static int rockchip_combphy_param_write(struct regmap *base,
+ const struct combphy_reg *reg, bool en)
+{
+ u32 val, mask, tmp;
+
+ tmp = en ? reg->enable : reg->disable;
+ mask = GENMASK(reg->bitend, reg->bitstart);
+ val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
+
+ return regmap_write(base, reg->offset, val);
+}
+
+static u32 rockchip_combphy_is_ready(struct rockchip_combphy_priv *priv)
+{
+ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+ u32 mask, val;
+
+ mask = GENMASK(cfg->pipe_phy_status.bitend,
+ cfg->pipe_phy_status.bitstart);
+
+ regmap_read(priv->phy_grf, cfg->pipe_phy_status.offset, &val);
+ val = (val & mask) >> cfg->pipe_phy_status.bitstart;
+
+ return val;
+}
+
+static int rockchip_combphy_init(struct phy *phy)
+{
+ struct rockchip_combphy_priv *priv = phy_get_drvdata(phy);
+ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+ u32 val;
+ int ret;
+
+ ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
+ if (ret) {
+ dev_err(priv->dev, "failed to enable clks\n");
+ return ret;
+ }
+
+ switch (priv->type) {
+ case PHY_TYPE_PCIE:
+ case PHY_TYPE_USB3:
+ case PHY_TYPE_SATA:
+ case PHY_TYPE_SGMII:
+ case PHY_TYPE_QSGMII:
+ if (priv->cfg->combphy_cfg)
+ ret = priv->cfg->combphy_cfg(priv);
+ break;
+ default:
+ dev_err(priv->dev, "incompatible PHY type\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret) {
+ dev_err(priv->dev, "failed to init phy for phy type %x\n", priv->type);
+ goto err_clk;
+ }
+
+ ret = reset_control_deassert(priv->phy_rst);
+ if (ret)
+ goto err_clk;
+
+ if (priv->type == PHY_TYPE_USB3) {
+ ret = readx_poll_timeout_atomic(rockchip_combphy_is_ready,
+ priv, val,
+ val == cfg->pipe_phy_status.enable,
+ 10, 1000);
+ if (ret)
+ dev_warn(priv->dev, "wait phy status ready timeout\n");
+ }
+
+ return 0;
+
+err_clk:
+ clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+
+ return ret;
+}
+
+static int rockchip_combphy_exit(struct phy *phy)
+{
+ struct rockchip_combphy_priv *priv = phy_get_drvdata(phy);
+
+ clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+ reset_control_assert(priv->phy_rst);
+
+ return 0;
+}
+
+static const struct phy_ops rochchip_combphy_ops = {
+ .init = rockchip_combphy_init,
+ .exit = rockchip_combphy_exit,
+ .owner = THIS_MODULE,
+};
+
+static struct phy *rockchip_combphy_xlate(struct device *dev, struct of_phandle_args *args)
+{
+ struct rockchip_combphy_priv *priv = dev_get_drvdata(dev);
+
+ if (args->args_count != 1) {
+ dev_err(dev, "invalid number of arguments\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (priv->type != PHY_NONE && priv->type != args->args[0])
+ dev_warn(dev, "phy type select %d overwriting type %d\n",
+ args->args[0], priv->type);
+
+ priv->type = args->args[0];
+
+ return priv->phy;
+}
+
+static int rockchip_combphy_parse_dt(struct device *dev, struct rockchip_combphy_priv *priv)
+{
+ int i;
+
+ priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks);
+ if (priv->num_clks < 1)
+ return -EINVAL;
+
+ priv->refclk = NULL;
+ for (i = 0; i < priv->num_clks; i++) {
+ if (!strncmp(priv->clks[i].id, "ref", 3)) {
+ priv->refclk = priv->clks[i].clk;
+ break;
+ }
+ }
+
+ if (!priv->refclk) {
+ dev_err(dev, "no refclk found\n");
+ return -EINVAL;
+ }
+
+ priv->pipe_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-grf");
+ if (IS_ERR(priv->pipe_grf)) {
+ dev_err(dev, "failed to find peri_ctrl pipe-grf regmap\n");
+ return PTR_ERR(priv->pipe_grf);
+ }
+
+ priv->phy_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-phy-grf");
+ if (IS_ERR(priv->phy_grf)) {
+ dev_err(dev, "failed to find peri_ctrl pipe-phy-grf regmap\n");
+ return PTR_ERR(priv->phy_grf);
+ }
+
+ priv->enable_ssc = device_property_present(dev, "rockchip,enable-ssc");
+
+ priv->ext_refclk = device_property_present(dev, "rockchip,ext-refclk");
+
+ priv->phy_rst = devm_reset_control_array_get_exclusive(dev);
+ if (IS_ERR(priv->phy_rst))
+ return dev_err_probe(dev, PTR_ERR(priv->phy_rst), "failed to get phy reset\n");
+
+ return 0;
+}
+
+static int rockchip_combphy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct rockchip_combphy_priv *priv;
+ const struct rockchip_combphy_cfg *phy_cfg;
+ struct resource *res;
+ int ret;
+
+ phy_cfg = of_device_get_match_data(dev);
+ if (!phy_cfg) {
+ dev_err(dev, "no OF match data provided\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(priv->mmio)) {
+ ret = PTR_ERR(priv->mmio);
+ return ret;
+ }
+
+ priv->dev = dev;
+ priv->type = PHY_NONE;
+ priv->cfg = phy_cfg;
+
+ ret = rockchip_combphy_parse_dt(dev, priv);
+ if (ret)
+ return ret;
+
+ ret = reset_control_assert(priv->phy_rst);
+ if (ret) {
+ dev_err(dev, "failed to reset phy\n");
+ return ret;
+ }
+
+ priv->phy = devm_phy_create(dev, NULL, &rochchip_combphy_ops);
+ if (IS_ERR(priv->phy)) {
+ dev_err(dev, "failed to create combphy\n");
+ return PTR_ERR(priv->phy);
+ }
+
+ dev_set_drvdata(dev, priv);
+ phy_set_drvdata(priv->phy, priv);
+
+ phy_provider = devm_of_phy_provider_register(dev, rockchip_combphy_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv)
+{
+ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+ unsigned long rate;
+ u32 val;
+
+ switch (priv->type) {
+ case PHY_TYPE_PCIE:
+ /* Set SSC downward spread spectrum. */
+ rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK,
+ PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT,
+ PHYREG32);
+
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_pcie, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_pcie, true);
+ break;
+
+ case PHY_TYPE_USB3:
+ /* Set SSC downward spread spectrum. */
+ rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK,
+ PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT,
+ PHYREG32);
+
+ /* Enable adaptive CTLE for USB3.0 Rx. */
+ val = readl(priv->mmio + PHYREG15);
+ val |= PHYREG15_CTLE_EN;
+ writel(val, priv->mmio + PHYREG15);
+
+ /* Set PLL KVCO fine tuning signals. */
+ rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK,
+ PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT,
+ PHYREG33);
+
+ /* Enable controlling random jitter. */
+ writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12);
+
+ /* Set PLL input clock divider 1/2. */
+ rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK,
+ PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT,
+ PHYREG6);
+
+ writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18);
+ writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11);
+
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_usb, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->usb_mode_set, true);
+ break;
+
+ case PHY_TYPE_SATA:
+ /* Enable adaptive CTLE for SATA Rx. */
+ val = readl(priv->mmio + PHYREG15);
+ val |= PHYREG15_CTLE_EN;
+ writel(val, priv->mmio + PHYREG15);
+ /*
+ * Set tx_rterm=50ohm and rx_rterm=44ohm for SATA.
+ * 0: 60ohm, 8: 50ohm 15: 44ohm (by step abort 1ohm)
+ */
+ val = PHYREG7_TX_RTERM_50OHM << PHYREG7_TX_RTERM_SHIFT;
+ val |= PHYREG7_RX_RTERM_44OHM << PHYREG7_RX_RTERM_SHIFT;
+ writel(val, priv->mmio + PHYREG7);
+
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_sata, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_sata, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_sata, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_sata, true);
+ rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_con0_for_sata, true);
+ break;
+
+ case PHY_TYPE_SGMII:
+ rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->sgmii_mode_set, true);
+ break;
+
+ case PHY_TYPE_QSGMII:
+ rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_rate_sel, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->qsgmii_mode_set, true);
+ break;
+
+ default:
+ dev_err(priv->dev, "incompatible PHY type\n");
+ return -EINVAL;
+ }
+
+ rate = clk_get_rate(priv->refclk);
+
+ switch (rate) {
+ case REF_CLOCK_24MHz:
+ if (priv->type == PHY_TYPE_USB3 || priv->type == PHY_TYPE_SATA) {
+ /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz. */
+ val = PHYREG15_SSC_CNT_VALUE << PHYREG15_SSC_CNT_SHIFT;
+ rockchip_combphy_updatel(priv, PHYREG15_SSC_CNT_MASK,
+ val, PHYREG15);
+
+ writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16);
+ }
+ break;
+
+ case REF_CLOCK_25MHz:
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_25m, true);
+ break;
+
+ case REF_CLOCK_100MHz:
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true);
+ if (priv->type == PHY_TYPE_PCIE) {
+ /* PLL KVCO fine tuning. */
+ val = PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT;
+ rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK,
+ val, PHYREG33);
+
+ /* Enable controlling random jitter. */
+ writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12);
+
+ val = PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT;
+ rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK,
+ val, PHYREG6);
+
+ writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18);
+ writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11);
+ } else if (priv->type == PHY_TYPE_SATA) {
+ /* downward spread spectrum +500ppm */
+ val = PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT;
+ val |= PHYREG32_SSC_OFFSET_500PPM << PHYREG32_SSC_OFFSET_SHIFT;
+ rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32);
+ }
+ break;
+
+ default:
+ dev_err(priv->dev, "unsupported rate: %lu\n", rate);
+ return -EINVAL;
+ }
+
+ if (priv->ext_refclk) {
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_ext, true);
+ if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) {
+ val = PHYREG13_RESISTER_HIGH_Z << PHYREG13_RESISTER_SHIFT;
+ val |= PHYREG13_CKRCV_AMP0;
+ rockchip_combphy_updatel(priv, PHYREG13_RESISTER_MASK, val, PHYREG13);
+
+ val = readl(priv->mmio + PHYREG14);
+ val |= PHYREG14_CKRCV_AMP1;
+ writel(val, priv->mmio + PHYREG14);
+ }
+ }
+
+ if (priv->enable_ssc) {
+ val = readl(priv->mmio + PHYREG8);
+ val |= PHYREG8_SSC_EN;
+ writel(val, priv->mmio + PHYREG8);
+ }
+
+ return 0;
+}
+
+static const struct rockchip_combphy_grfcfg rk3568_combphy_grfcfgs = {
+ /* pipe-phy-grf */
+ .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x11 },
+ .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x04 },
+ .sgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x01 },
+ .qsgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x21 },
+ .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x01 },
+ .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x01 },
+ .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x01 },
+ .pipe_clk_25m = { 0x0004, 14, 13, 0x00, 0x01 },
+ .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x02 },
+ .pipe_phymode_sel = { 0x0008, 1, 1, 0x00, 0x01 },
+ .pipe_rate_sel = { 0x0008, 2, 2, 0x00, 0x01 },
+ .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x01 },
+ .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x01 },
+ .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x01 },
+ .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x01 },
+ .pipe_sel_usb = { 0x000c, 14, 13, 0x00, 0x01 },
+ .pipe_sel_qsgmii = { 0x000c, 15, 13, 0x00, 0x07 },
+ .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x00 },
+ .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x1000 },
+ .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x0000 },
+ .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x0101 },
+ .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 },
+ .con0_for_sata = { 0x0000, 15, 0, 0x00, 0x0119 },
+ .con1_for_sata = { 0x0004, 15, 0, 0x00, 0x0040 },
+ .con2_for_sata = { 0x0008, 15, 0, 0x00, 0x80c3 },
+ .con3_for_sata = { 0x000c, 15, 0, 0x00, 0x4407 },
+ /* pipe-grf */
+ .pipe_con0_for_sata = { 0x0000, 15, 0, 0x00, 0x2220 },
+ .pipe_xpcs_phy_ready = { 0x0040, 2, 2, 0x00, 0x01 },
+};
+
+static const struct rockchip_combphy_cfg rk3568_combphy_cfgs = {
+ .grfcfg = &rk3568_combphy_grfcfgs,
+ .combphy_cfg = rk3568_combphy_cfg,
+};
+
+static const struct of_device_id rockchip_combphy_of_match[] = {
+ {
+ .compatible = "rockchip,rk3568-naneng-combphy",
+ .data = &rk3568_combphy_cfgs,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rockchip_combphy_of_match);
+
+static struct platform_driver rockchip_combphy_driver = {
+ .probe = rockchip_combphy_probe,
+ .driver = {
+ .name = "rockchip-naneng-combphy",
+ .of_match_table = rockchip_combphy_of_match,
+ },
+};
+module_platform_driver(rockchip_combphy_driver);
+
+MODULE_DESCRIPTION("Rockchip NANENG COMBPHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c
new file mode 100644
index 000000000000..1d355b32ba55
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip PCIE3.0 phy driver
+ *
+ * Copyright (C) 2022 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/pcie.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+/* Register for RK3568 */
+#define GRF_PCIE30PHY_CON1 0x4
+#define GRF_PCIE30PHY_CON6 0x18
+#define GRF_PCIE30PHY_CON9 0x24
+#define GRF_PCIE30PHY_DA_OCM (BIT(15) | BIT(31))
+#define GRF_PCIE30PHY_STATUS0 0x80
+#define GRF_PCIE30PHY_WR_EN (0xf << 16)
+#define SRAM_INIT_DONE(reg) (reg & BIT(14))
+
+#define RK3568_BIFURCATION_LANE_0_1 BIT(0)
+
+/* Register for RK3588 */
+#define PHP_GRF_PCIESEL_CON 0x100
+#define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0
+#define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904
+#define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04
+#define RK3588_SRAM_INIT_DONE(reg) (reg & BIT(0))
+
+#define RK3588_BIFURCATION_LANE_0_1 BIT(0)
+#define RK3588_BIFURCATION_LANE_2_3 BIT(1)
+#define RK3588_LANE_AGGREGATION BIT(2)
+
+struct rockchip_p3phy_ops;
+
+struct rockchip_p3phy_priv {
+ const struct rockchip_p3phy_ops *ops;
+ void __iomem *mmio;
+ /* mode: RC, EP */
+ int mode;
+ /* pcie30_phymode: Aggregation, Bifurcation */
+ int pcie30_phymode;
+ struct regmap *phy_grf;
+ struct regmap *pipe_grf;
+ struct reset_control *p30phy;
+ struct phy *phy;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ int num_lanes;
+ u32 lanes[4];
+};
+
+struct rockchip_p3phy_ops {
+ int (*phy_init)(struct rockchip_p3phy_priv *priv);
+};
+
+static int rockchip_p3phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy);
+
+ /* Actually We don't care EP/RC mode, but just record it */
+ switch (submode) {
+ case PHY_MODE_PCIE_RC:
+ priv->mode = PHY_MODE_PCIE_RC;
+ break;
+ case PHY_MODE_PCIE_EP:
+ priv->mode = PHY_MODE_PCIE_EP;
+ break;
+ default:
+ dev_err(&phy->dev, "%s, invalid mode\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rockchip_p3phy_rk3568_init(struct rockchip_p3phy_priv *priv)
+{
+ struct phy *phy = priv->phy;
+ bool bifurcation = false;
+ int ret;
+ u32 reg;
+
+ /* Deassert PCIe PMA output clamp mode */
+ regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, GRF_PCIE30PHY_DA_OCM);
+
+ for (int i = 0; i < priv->num_lanes; i++) {
+ dev_info(&phy->dev, "lane number %d, val %d\n", i, priv->lanes[i]);
+ if (priv->lanes[i] > 1)
+ bifurcation = true;
+ }
+
+ /* Set bifurcation if needed, and it doesn't care RC/EP */
+ if (bifurcation) {
+ dev_info(&phy->dev, "bifurcation enabled\n");
+ regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6,
+ GRF_PCIE30PHY_WR_EN | RK3568_BIFURCATION_LANE_0_1);
+ regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON1,
+ GRF_PCIE30PHY_DA_OCM);
+ } else {
+ dev_dbg(&phy->dev, "bifurcation disabled\n");
+ regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6,
+ GRF_PCIE30PHY_WR_EN & ~RK3568_BIFURCATION_LANE_0_1);
+ }
+
+ reset_control_deassert(priv->p30phy);
+
+ ret = regmap_read_poll_timeout(priv->phy_grf,
+ GRF_PCIE30PHY_STATUS0,
+ reg, SRAM_INIT_DONE(reg),
+ 0, 500);
+ if (ret)
+ dev_err(&priv->phy->dev, "%s: lock failed 0x%x, check input refclk and power supply\n",
+ __func__, reg);
+ return ret;
+}
+
+static const struct rockchip_p3phy_ops rk3568_ops = {
+ .phy_init = rockchip_p3phy_rk3568_init,
+};
+
+static int rockchip_p3phy_rk3588_init(struct rockchip_p3phy_priv *priv)
+{
+ u32 reg = 0;
+ u8 mode = 0;
+ int ret;
+
+ /* Deassert PCIe PMA output clamp mode */
+ regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, BIT(8) | BIT(24));
+
+ /* Set bifurcation if needed */
+ for (int i = 0; i < priv->num_lanes; i++) {
+ if (!priv->lanes[i])
+ mode |= (BIT(i) << 3);
+
+ if (priv->lanes[i] > 1)
+ mode |= (BIT(i) >> 1);
+ }
+
+ if (!mode)
+ reg = RK3588_LANE_AGGREGATION;
+ else {
+ if (mode & (BIT(0) | BIT(1)))
+ reg |= RK3588_BIFURCATION_LANE_0_1;
+
+ if (mode & (BIT(2) | BIT(3)))
+ reg |= RK3588_BIFURCATION_LANE_2_3;
+ }
+
+ regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, (0x7<<16) | reg);
+
+ /* Set pcie1ln_sel in PHP_GRF_PCIESEL_CON */
+ if (!IS_ERR(priv->pipe_grf)) {
+ reg = (mode & (BIT(6) | BIT(7))) >> 6;
+ if (reg)
+ regmap_write(priv->pipe_grf, PHP_GRF_PCIESEL_CON,
+ (reg << 16) | reg);
+ }
+
+ reset_control_deassert(priv->p30phy);
+
+ ret = regmap_read_poll_timeout(priv->phy_grf,
+ RK3588_PCIE3PHY_GRF_PHY0_STATUS1,
+ reg, RK3588_SRAM_INIT_DONE(reg),
+ 0, 500);
+ ret |= regmap_read_poll_timeout(priv->phy_grf,
+ RK3588_PCIE3PHY_GRF_PHY1_STATUS1,
+ reg, RK3588_SRAM_INIT_DONE(reg),
+ 0, 500);
+ if (ret)
+ dev_err(&priv->phy->dev, "lock failed 0x%x, check input refclk and power supply\n",
+ reg);
+ return ret;
+}
+
+static const struct rockchip_p3phy_ops rk3588_ops = {
+ .phy_init = rockchip_p3phy_rk3588_init,
+};
+
+static int rochchip_p3phy_init(struct phy *phy)
+{
+ struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
+ if (ret) {
+ dev_err(&priv->phy->dev, "failed to enable PCIe bulk clks %d\n", ret);
+ return ret;
+ }
+
+ reset_control_assert(priv->p30phy);
+ udelay(1);
+
+ if (priv->ops->phy_init) {
+ ret = priv->ops->phy_init(priv);
+ if (ret)
+ clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+ }
+
+ return ret;
+}
+
+static int rochchip_p3phy_exit(struct phy *phy)
+{
+ struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy);
+
+ clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+ reset_control_assert(priv->p30phy);
+ return 0;
+}
+
+static const struct phy_ops rochchip_p3phy_ops = {
+ .init = rochchip_p3phy_init,
+ .exit = rochchip_p3phy_exit,
+ .set_mode = rockchip_p3phy_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static int rockchip_p3phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct rockchip_p3phy_priv *priv;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(priv->mmio)) {
+ ret = PTR_ERR(priv->mmio);
+ return ret;
+ }
+
+ priv->ops = of_device_get_match_data(&pdev->dev);
+ if (!priv->ops) {
+ dev_err(dev, "no of match data provided\n");
+ return -EINVAL;
+ }
+
+ priv->phy_grf = syscon_regmap_lookup_by_phandle(np, "rockchip,phy-grf");
+ if (IS_ERR(priv->phy_grf)) {
+ dev_err(dev, "failed to find rockchip,phy_grf regmap\n");
+ return PTR_ERR(priv->phy_grf);
+ }
+
+ if (of_device_is_compatible(np, "rockchip,rk3588-pcie3-phy")) {
+ priv->pipe_grf =
+ syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,pipe-grf");
+ if (IS_ERR(priv->pipe_grf))
+ dev_info(dev, "failed to find rockchip,pipe_grf regmap\n");
+ } else {
+ priv->pipe_grf = NULL;
+ }
+
+ priv->num_lanes = of_property_read_variable_u32_array(dev->of_node, "data-lanes",
+ priv->lanes, 2,
+ ARRAY_SIZE(priv->lanes));
+
+ /* if no data-lanes assume aggregation */
+ if (priv->num_lanes == -EINVAL) {
+ dev_dbg(dev, "no data-lanes property found\n");
+ priv->num_lanes = 1;
+ priv->lanes[0] = 1;
+ } else if (priv->num_lanes < 0) {
+ dev_err(dev, "failed to read data-lanes property %d\n", priv->num_lanes);
+ return priv->num_lanes;
+ }
+
+ priv->phy = devm_phy_create(dev, NULL, &rochchip_p3phy_ops);
+ if (IS_ERR(priv->phy)) {
+ dev_err(dev, "failed to create combphy\n");
+ return PTR_ERR(priv->phy);
+ }
+
+ priv->p30phy = devm_reset_control_get_optional_exclusive(dev, "phy");
+ if (IS_ERR(priv->p30phy)) {
+ return dev_err_probe(dev, PTR_ERR(priv->p30phy),
+ "failed to get phy reset control\n");
+ }
+ if (!priv->p30phy)
+ dev_info(dev, "no phy reset control specified\n");
+
+ priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks);
+ if (priv->num_clks < 1)
+ return -ENODEV;
+
+ dev_set_drvdata(dev, priv);
+ phy_set_drvdata(priv->phy, priv);
+ 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 rockchip_p3phy_of_match[] = {
+ { .compatible = "rockchip,rk3568-pcie3-phy", .data = &rk3568_ops },
+ { .compatible = "rockchip,rk3588-pcie3-phy", .data = &rk3588_ops },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rockchip_p3phy_of_match);
+
+static struct platform_driver rockchip_p3phy_driver = {
+ .probe = rockchip_p3phy_probe,
+ .driver = {
+ .name = "rockchip-snps-pcie3-phy",
+ .of_match_table = rockchip_p3phy_of_match,
+ },
+};
+module_platform_driver(rockchip_p3phy_driver);
+MODULE_DESCRIPTION("Rockchip Synopsys PCIe 3.0 PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
index 24563160197f..d76440ae10ff 100644
--- a/drivers/phy/rockchip/phy-rockchip-typec.c
+++ b/drivers/phy/rockchip/phy-rockchip-typec.c
@@ -347,7 +347,7 @@ struct usb3phy_reg {
};
/**
- * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
+ * struct rockchip_usb3phy_port_cfg - usb3-phy port configuration.
* @reg: the base address for usb3-phy config.
* @typec_conn_dir: the register of type-c connector direction.
* @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
@@ -1105,15 +1105,14 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
struct phy_provider *phy_provider;
struct resource *res;
const struct rockchip_usb3phy_port_cfg *phy_cfgs;
- const struct of_device_id *match;
int index, ret;
tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
if (!tcphy)
return -ENOMEM;
- match = of_match_device(dev->driver->of_match_table, dev);
- if (!match || !match->data) {
+ phy_cfgs = of_device_get_match_data(dev);
+ if (!phy_cfgs) {
dev_err(dev, "phy configs are not assigned!\n");
return -EINVAL;
}
@@ -1123,7 +1122,6 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
if (IS_ERR(tcphy->base))
return PTR_ERR(tcphy->base);
- phy_cfgs = match->data;
/* find out a proper config which can be matched with dt. */
index = 0;
while (phy_cfgs[index].reg) {
@@ -1180,6 +1178,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
dev_err(dev, "failed to create phy: %pOFn\n",
child_np);
pm_runtime_disable(dev);
+ of_node_put(child_np);
return PTR_ERR(phy);
}
diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig
index 9e483d1fdaf2..3ccaabf2850a 100644
--- a/drivers/phy/samsung/Kconfig
+++ b/drivers/phy/samsung/Kconfig
@@ -3,23 +3,23 @@
# Phy drivers for Samsung platforms
#
config PHY_EXYNOS_DP_VIDEO
- tristate "EXYNOS SoC series Display Port PHY driver"
+ tristate "Exynos SoC series Display Port PHY driver"
depends on OF
depends on ARCH_EXYNOS || COMPILE_TEST
default ARCH_EXYNOS
select GENERIC_PHY
help
- Support for Display Port PHY found on Samsung EXYNOS SoCs.
+ Support for Display Port PHY found on Samsung Exynos SoCs.
config PHY_EXYNOS_MIPI_VIDEO
- tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
+ tristate "S5P/Exynos SoC series MIPI CSI-2/DSI PHY driver"
depends on HAS_IOMEM
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
select GENERIC_PHY
default y if ARCH_S5PV210 || ARCH_EXYNOS
help
Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
- and EXYNOS SoCs.
+ and Exynos SoCs.
config PHY_EXYNOS_PCIE
bool "Exynos PCIe PHY driver"
@@ -29,8 +29,17 @@ config PHY_EXYNOS_PCIE
Enable PCIe PHY support for Exynos SoC series.
This driver provides PHY interface for Exynos PCIe controller.
+config PHY_SAMSUNG_UFS
+ tristate "Exynos SoC series UFS PHY driver"
+ depends on OF && (ARCH_EXYNOS || COMPILE_TEST)
+ select GENERIC_PHY
+ help
+ Enable this to support the Samsung Exynos SoC UFS PHY driver for
+ Samsung Exynos SoCs. This driver provides the interface for UFS host
+ controller to do PHY related programming.
+
config PHY_SAMSUNG_USB2
- tristate "Samsung USB 2.0 PHY driver"
+ tristate "S5P/Exynos SoC series USB 2.0 PHY driver"
depends on HAS_IOMEM
depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 || COMPILE_TEST
select GENERIC_PHY
@@ -38,9 +47,9 @@ config PHY_SAMSUNG_USB2
default ARCH_EXYNOS
help
Enable this to support the Samsung USB 2.0 PHY driver for Samsung
- SoCs. This driver provides the interface for USB 2.0 PHY. Support
- for particular PHYs will be enabled based on the SoC type in addition
- to this driver.
+ S5Pv210 and Exynos SoCs. This driver provides the interface for USB
+ 2.0 PHY. Support for particular PHYs will be enabled based on the SoC
+ type in addition to this driver.
config PHY_EXYNOS4210_USB2
bool
diff --git a/drivers/phy/samsung/Makefile b/drivers/phy/samsung/Makefile
index db9b1aa0de6e..afb34a153e34 100644
--- a/drivers/phy/samsung/Makefile
+++ b/drivers/phy/samsung/Makefile
@@ -2,6 +2,11 @@
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
obj-$(CONFIG_PHY_EXYNOS_PCIE) += phy-exynos-pcie.o
+obj-$(CONFIG_PHY_SAMSUNG_UFS) += phy-exynos-ufs.o
+phy-exynos-ufs-y += phy-samsung-ufs.o
+phy-exynos-ufs-y += phy-exynos7-ufs.o
+phy-exynos-ufs-y += phy-exynosautov9-ufs.o
+phy-exynos-ufs-y += phy-fsd-ufs.o
obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o
phy-exynos-usb2-y += phy-samsung-usb2.o
phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o
diff --git a/drivers/phy/samsung/phy-exynos-dp-video.c b/drivers/phy/samsung/phy-exynos-dp-video.c
index 6c607df1dc9a..2b670ef91deb 100644
--- a/drivers/phy/samsung/phy-exynos-dp-video.c
+++ b/drivers/phy/samsung/phy-exynos-dp-video.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Samsung EXYNOS SoC series Display Port PHY driver
+ * Samsung Exynos SoC series Display Port PHY driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Jingoo Han <jg1.han@samsung.com>
@@ -115,5 +115,5 @@ static struct platform_driver exynos_dp_video_phy_driver = {
module_platform_driver(exynos_dp_video_phy_driver);
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
-MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver");
+MODULE_DESCRIPTION("Samsung Exynos SoC DP PHY driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/samsung/phy-exynos-mipi-video.c b/drivers/phy/samsung/phy-exynos-mipi-video.c
index bb51195f189f..c1df1ef3ee3c 100644
--- a/drivers/phy/samsung/phy-exynos-mipi-video.c
+++ b/drivers/phy/samsung/phy-exynos-mipi-video.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
+ * Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver
*
* Copyright (C) 2013,2016 Samsung Electronics Co., Ltd.
* Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
@@ -364,6 +364,6 @@ static struct platform_driver exynos_mipi_video_phy_driver = {
};
module_platform_driver(exynos_mipi_video_phy_driver);
-MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver");
+MODULE_DESCRIPTION("Samsung S5P/Exynos SoC MIPI CSI-2/DSI PHY driver");
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/samsung/phy-exynos-pcie.c b/drivers/phy/samsung/phy-exynos-pcie.c
index 659e7ae0a6cf..53c9230c2907 100644
--- a/drivers/phy/samsung/phy-exynos-pcie.c
+++ b/drivers/phy/samsung/phy-exynos-pcie.c
@@ -1,73 +1,44 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Samsung EXYNOS SoC series PCIe PHY driver
+ * Samsung Exynos SoC series PCIe PHY driver
*
* Phy provider for PCIe controller on Exynos SoC series
*
- * Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (C) 2017-2020 Samsung Electronics Co., Ltd.
* Jaehoon Chung <jh80.chung@samsung.com>
*/
-#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/iopoll.h>
-#include <linux/init.h>
#include <linux/mfd/syscon.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
-/* PCIe Purple registers */
-#define PCIE_PHY_GLOBAL_RESET 0x000
-#define PCIE_PHY_COMMON_RESET 0x004
-#define PCIE_PHY_CMN_REG 0x008
-#define PCIE_PHY_MAC_RESET 0x00c
-#define PCIE_PHY_PLL_LOCKED 0x010
-#define PCIE_PHY_TRSVREG_RESET 0x020
-#define PCIE_PHY_TRSV_RESET 0x024
-
-/* PCIe PHY registers */
-#define PCIE_PHY_IMPEDANCE 0x004
-#define PCIE_PHY_PLL_DIV_0 0x008
-#define PCIE_PHY_PLL_BIAS 0x00c
-#define PCIE_PHY_DCC_FEEDBACK 0x014
-#define PCIE_PHY_PLL_DIV_1 0x05c
-#define PCIE_PHY_COMMON_POWER 0x064
-#define PCIE_PHY_COMMON_PD_CMN BIT(3)
-#define PCIE_PHY_TRSV0_EMP_LVL 0x084
-#define PCIE_PHY_TRSV0_DRV_LVL 0x088
-#define PCIE_PHY_TRSV0_RXCDR 0x0ac
-#define PCIE_PHY_TRSV0_POWER 0x0c4
-#define PCIE_PHY_TRSV0_PD_TSV BIT(7)
-#define PCIE_PHY_TRSV0_LVCC 0x0dc
-#define PCIE_PHY_TRSV1_EMP_LVL 0x144
-#define PCIE_PHY_TRSV1_RXCDR 0x16c
-#define PCIE_PHY_TRSV1_POWER 0x184
-#define PCIE_PHY_TRSV1_PD_TSV BIT(7)
-#define PCIE_PHY_TRSV1_LVCC 0x19c
-#define PCIE_PHY_TRSV2_EMP_LVL 0x204
-#define PCIE_PHY_TRSV2_RXCDR 0x22c
-#define PCIE_PHY_TRSV2_POWER 0x244
-#define PCIE_PHY_TRSV2_PD_TSV BIT(7)
-#define PCIE_PHY_TRSV2_LVCC 0x25c
-#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4
-#define PCIE_PHY_TRSV3_RXCDR 0x2ec
-#define PCIE_PHY_TRSV3_POWER 0x304
-#define PCIE_PHY_TRSV3_PD_TSV BIT(7)
-#define PCIE_PHY_TRSV3_LVCC 0x31c
-
-struct exynos_pcie_phy_data {
- const struct phy_ops *ops;
-};
+#define PCIE_PHY_OFFSET(x) ((x) * 0x4)
+
+/* Sysreg FSYS register offsets and bits for Exynos5433 */
+#define PCIE_EXYNOS5433_PHY_MAC_RESET 0x0208
+#define PCIE_MAC_RESET_MASK 0xFF
+#define PCIE_MAC_RESET BIT(4)
+#define PCIE_EXYNOS5433_PHY_L1SUB_CM_CON 0x1010
+#define PCIE_REFCLK_GATING_EN BIT(0)
+#define PCIE_EXYNOS5433_PHY_COMMON_RESET 0x1020
+#define PCIE_PHY_RESET BIT(0)
+#define PCIE_EXYNOS5433_PHY_GLOBAL_RESET 0x1040
+#define PCIE_GLOBAL_RESET BIT(0)
+#define PCIE_REFCLK BIT(1)
+#define PCIE_REFCLK_MASK 0x16
+#define PCIE_APP_REQ_EXIT_L1_MODE BIT(5)
+
+/* PMU PCIE PHY isolation control */
+#define EXYNOS5433_PMU_PCIE_PHY_OFFSET 0x730
/* For Exynos pcie phy */
struct exynos_pcie_phy {
- const struct exynos_pcie_phy_data *drv_data;
- void __iomem *phy_base;
- void __iomem *blk_base; /* For exynos5440 */
+ void __iomem *base;
+ struct regmap *pmureg;
+ struct regmap *fsysreg;
};
static void exynos_pcie_phy_writel(void __iomem *base, u32 val, u32 offset)
@@ -75,153 +46,96 @@ static void exynos_pcie_phy_writel(void __iomem *base, u32 val, u32 offset)
writel(val, base + offset);
}
-static u32 exynos_pcie_phy_readl(void __iomem *base, u32 offset)
-{
- return readl(base + offset);
-}
-
-/* For Exynos5440 specific functions */
-static int exynos5440_pcie_phy_init(struct phy *phy)
+/* Exynos5433 specific functions */
+static int exynos5433_pcie_phy_init(struct phy *phy)
{
struct exynos_pcie_phy *ep = phy_get_drvdata(phy);
- /* DCC feedback control off */
- exynos_pcie_phy_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
-
- /* set TX/RX impedance */
- exynos_pcie_phy_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
-
- /* set 50Mhz PHY clock */
- exynos_pcie_phy_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
- exynos_pcie_phy_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
-
- /* set TX Differential output for lane 0 */
- exynos_pcie_phy_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
-
- /* set TX Pre-emphasis Level Control for lane 0 to minimum */
- exynos_pcie_phy_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
-
- /* set RX clock and data recovery bandwidth */
- exynos_pcie_phy_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
- exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
- exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
- exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
- exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
-
- /* change TX Pre-emphasis Level Control for lanes */
- exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
- exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
- exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
- exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
-
- /* set LVCC */
- exynos_pcie_phy_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
- exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
- exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
- exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
-
- /* pulse for common reset */
- exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_COMMON_RESET);
- udelay(500);
- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET);
-
+ regmap_update_bits(ep->pmureg, EXYNOS5433_PMU_PCIE_PHY_OFFSET,
+ BIT(0), 1);
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_GLOBAL_RESET,
+ PCIE_APP_REQ_EXIT_L1_MODE, 0);
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_L1SUB_CM_CON,
+ PCIE_REFCLK_GATING_EN, 0);
+
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_COMMON_RESET,
+ PCIE_PHY_RESET, 1);
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_MAC_RESET,
+ PCIE_MAC_RESET, 0);
+
+ /* PHY refclk 24MHz */
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_GLOBAL_RESET,
+ PCIE_REFCLK_MASK, PCIE_REFCLK);
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_GLOBAL_RESET,
+ PCIE_GLOBAL_RESET, 0);
+
+
+ exynos_pcie_phy_writel(ep->base, 0x11, PCIE_PHY_OFFSET(0x3));
+
+ /* band gap reference on */
+ exynos_pcie_phy_writel(ep->base, 0, PCIE_PHY_OFFSET(0x20));
+ exynos_pcie_phy_writel(ep->base, 0, PCIE_PHY_OFFSET(0x4b));
+
+ /* jitter tuning */
+ exynos_pcie_phy_writel(ep->base, 0x34, PCIE_PHY_OFFSET(0x4));
+ exynos_pcie_phy_writel(ep->base, 0x02, PCIE_PHY_OFFSET(0x7));
+ exynos_pcie_phy_writel(ep->base, 0x41, PCIE_PHY_OFFSET(0x21));
+ exynos_pcie_phy_writel(ep->base, 0x7F, PCIE_PHY_OFFSET(0x14));
+ exynos_pcie_phy_writel(ep->base, 0xC0, PCIE_PHY_OFFSET(0x15));
+ exynos_pcie_phy_writel(ep->base, 0x61, PCIE_PHY_OFFSET(0x36));
+
+ /* D0 uninit.. */
+ exynos_pcie_phy_writel(ep->base, 0x44, PCIE_PHY_OFFSET(0x3D));
+
+ /* 24MHz */
+ exynos_pcie_phy_writel(ep->base, 0x94, PCIE_PHY_OFFSET(0x8));
+ exynos_pcie_phy_writel(ep->base, 0xA7, PCIE_PHY_OFFSET(0x9));
+ exynos_pcie_phy_writel(ep->base, 0x93, PCIE_PHY_OFFSET(0xA));
+ exynos_pcie_phy_writel(ep->base, 0x6B, PCIE_PHY_OFFSET(0xC));
+ exynos_pcie_phy_writel(ep->base, 0xA5, PCIE_PHY_OFFSET(0xF));
+ exynos_pcie_phy_writel(ep->base, 0x34, PCIE_PHY_OFFSET(0x16));
+ exynos_pcie_phy_writel(ep->base, 0xA3, PCIE_PHY_OFFSET(0x17));
+ exynos_pcie_phy_writel(ep->base, 0xA7, PCIE_PHY_OFFSET(0x1A));
+ exynos_pcie_phy_writel(ep->base, 0x71, PCIE_PHY_OFFSET(0x23));
+ exynos_pcie_phy_writel(ep->base, 0x4C, PCIE_PHY_OFFSET(0x24));
+
+ exynos_pcie_phy_writel(ep->base, 0x0E, PCIE_PHY_OFFSET(0x26));
+ exynos_pcie_phy_writel(ep->base, 0x14, PCIE_PHY_OFFSET(0x7));
+ exynos_pcie_phy_writel(ep->base, 0x48, PCIE_PHY_OFFSET(0x43));
+ exynos_pcie_phy_writel(ep->base, 0x44, PCIE_PHY_OFFSET(0x44));
+ exynos_pcie_phy_writel(ep->base, 0x03, PCIE_PHY_OFFSET(0x45));
+ exynos_pcie_phy_writel(ep->base, 0xA7, PCIE_PHY_OFFSET(0x48));
+ exynos_pcie_phy_writel(ep->base, 0x13, PCIE_PHY_OFFSET(0x54));
+ exynos_pcie_phy_writel(ep->base, 0x04, PCIE_PHY_OFFSET(0x31));
+ exynos_pcie_phy_writel(ep->base, 0, PCIE_PHY_OFFSET(0x32));
+
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_COMMON_RESET,
+ PCIE_PHY_RESET, 0);
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_MAC_RESET,
+ PCIE_MAC_RESET_MASK, PCIE_MAC_RESET);
return 0;
}
-static int exynos5440_pcie_phy_power_on(struct phy *phy)
+static int exynos5433_pcie_phy_exit(struct phy *phy)
{
struct exynos_pcie_phy *ep = phy_get_drvdata(phy);
- u32 val;
-
- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET);
- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_CMN_REG);
- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSVREG_RESET);
- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSV_RESET);
-
- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
- val &= ~PCIE_PHY_COMMON_PD_CMN;
- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
-
- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
- val &= ~PCIE_PHY_TRSV0_PD_TSV;
- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
-
- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
- val &= ~PCIE_PHY_TRSV1_PD_TSV;
- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
-
- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
- val &= ~PCIE_PHY_TRSV2_PD_TSV;
- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
-
- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
- val &= ~PCIE_PHY_TRSV3_PD_TSV;
- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_L1SUB_CM_CON,
+ PCIE_REFCLK_GATING_EN, PCIE_REFCLK_GATING_EN);
+ regmap_update_bits(ep->pmureg, EXYNOS5433_PMU_PCIE_PHY_OFFSET,
+ BIT(0), 0);
return 0;
}
-static int exynos5440_pcie_phy_power_off(struct phy *phy)
-{
- struct exynos_pcie_phy *ep = phy_get_drvdata(phy);
- u32 val;
-
- if (readl_poll_timeout(ep->phy_base + PCIE_PHY_PLL_LOCKED, val,
- (val != 0), 1, 500)) {
- dev_err(&phy->dev, "PLL Locked: 0x%x\n", val);
- return -ETIMEDOUT;
- }
-
- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
- val |= PCIE_PHY_COMMON_PD_CMN;
- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
-
- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
- val |= PCIE_PHY_TRSV0_PD_TSV;
- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
-
- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
- val |= PCIE_PHY_TRSV1_PD_TSV;
- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
-
- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
- val |= PCIE_PHY_TRSV2_PD_TSV;
- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
-
- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
- val |= PCIE_PHY_TRSV3_PD_TSV;
- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
-
- return 0;
-}
-
-static int exynos5440_pcie_phy_reset(struct phy *phy)
-{
- struct exynos_pcie_phy *ep = phy_get_drvdata(phy);
-
- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_MAC_RESET);
- exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_GLOBAL_RESET);
- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_GLOBAL_RESET);
-
- return 0;
-}
-
-static const struct phy_ops exynos5440_phy_ops = {
- .init = exynos5440_pcie_phy_init,
- .power_on = exynos5440_pcie_phy_power_on,
- .power_off = exynos5440_pcie_phy_power_off,
- .reset = exynos5440_pcie_phy_reset,
+static const struct phy_ops exynos5433_phy_ops = {
+ .init = exynos5433_pcie_phy_init,
+ .exit = exynos5433_pcie_phy_exit,
.owner = THIS_MODULE,
};
-static const struct exynos_pcie_phy_data exynos5440_pcie_phy_data = {
- .ops = &exynos5440_phy_ops,
-};
-
static const struct of_device_id exynos_pcie_phy_match[] = {
{
- .compatible = "samsung,exynos5440-pcie-phy",
- .data = &exynos5440_pcie_phy_data,
+ .compatible = "samsung,exynos5433-pcie-phy",
},
{},
};
@@ -232,30 +146,30 @@ static int exynos_pcie_phy_probe(struct platform_device *pdev)
struct exynos_pcie_phy *exynos_phy;
struct phy *generic_phy;
struct phy_provider *phy_provider;
- struct resource *res;
- const struct exynos_pcie_phy_data *drv_data;
-
- drv_data = of_device_get_match_data(dev);
- if (!drv_data)
- return -ENODEV;
exynos_phy = devm_kzalloc(dev, sizeof(*exynos_phy), GFP_KERNEL);
if (!exynos_phy)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- exynos_phy->phy_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(exynos_phy->phy_base))
- return PTR_ERR(exynos_phy->phy_base);
+ exynos_phy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(exynos_phy->base))
+ return PTR_ERR(exynos_phy->base);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- exynos_phy->blk_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(exynos_phy->blk_base))
- return PTR_ERR(exynos_phy->blk_base);
+ exynos_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,pmu-syscon");
+ if (IS_ERR(exynos_phy->pmureg)) {
+ dev_err(&pdev->dev, "PMU regmap lookup failed.\n");
+ return PTR_ERR(exynos_phy->pmureg);
+ }
- exynos_phy->drv_data = drv_data;
+ exynos_phy->fsysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,fsys-sysreg");
+ if (IS_ERR(exynos_phy->fsysreg)) {
+ dev_err(&pdev->dev, "FSYS sysreg regmap lookup failed.\n");
+ return PTR_ERR(exynos_phy->fsysreg);
+ }
- generic_phy = devm_phy_create(dev, dev->of_node, drv_data->ops);
+ generic_phy = devm_phy_create(dev, dev->of_node, &exynos5433_phy_ops);
if (IS_ERR(generic_phy)) {
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(generic_phy);
@@ -275,5 +189,4 @@ static struct platform_driver exynos_pcie_phy_driver = {
.suppress_bind_attrs = true,
}
};
-
builtin_platform_driver(exynos_pcie_phy_driver);
diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c
index e510732afb8b..ee0848fe8432 100644
--- a/drivers/phy/samsung/phy-exynos5-usbdrd.c
+++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Samsung EXYNOS5 SoC series USB DRD PHY driver
+ * Samsung Exynos5 SoC series USB DRD PHY driver
*
* Phy provider for USB 3.0 DRD controller on Exynos5 SoC series
*
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/iopoll.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
@@ -33,7 +34,7 @@
#define EXYNOS5_FSEL_24MHZ 0x5
#define EXYNOS5_FSEL_50MHZ 0x7
-/* EXYNOS5: USB 3.0 DRD PHY registers */
+/* Exynos5: USB 3.0 DRD PHY registers */
#define EXYNOS5_DRD_LINKSYSTEM 0x04
#define LINKSYSTEM_FLADJ_MASK (0x3f << 1)
@@ -180,14 +181,14 @@ struct exynos5_usbdrd_phy_drvdata {
* @utmiclk: clock for utmi+ phy
* @itpclk: clock for ITP generation
* @drv_data: pointer to SoC level driver data structure
- * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
+ * @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
* instances each with its 'phy' and 'phy_cfg'.
* @extrefclk: frequency select settings when using 'separate
* reference clocks' for SS and HS operations
* @ref_clk: reference clock to PHY block from which PHY's
* operational clocks are derived
- * vbus: VBUS regulator for phy
- * vbus_boost: Boost regulator for VBUS present on few Exynos boards
+ * @vbus: VBUS regulator for phy
+ * @vbus_boost: Boost regulator for VBUS present on few Exynos boards
*/
struct exynos5_usbdrd_phy {
struct device *dev;
@@ -556,41 +557,25 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy)
static int crport_handshake(struct exynos5_usbdrd_phy *phy_drd,
u32 val, u32 cmd)
{
- u32 usec = 100;
unsigned int result;
+ int err;
writel(val | cmd, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
- do {
- result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
- if (result & PHYREG1_CR_ACK)
- break;
-
- udelay(1);
- } while (usec-- > 0);
-
- if (!usec) {
- dev_err(phy_drd->dev,
- "CRPORT handshake timeout1 (0x%08x)\n", val);
- return -ETIME;
+ err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1,
+ result, (result & PHYREG1_CR_ACK), 1, 100);
+ if (err == -ETIMEDOUT) {
+ dev_err(phy_drd->dev, "CRPORT handshake timeout1 (0x%08x)\n", val);
+ return err;
}
- usec = 100;
-
writel(val, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
- do {
- result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
- if (!(result & PHYREG1_CR_ACK))
- break;
-
- udelay(1);
- } while (usec-- > 0);
-
- if (!usec) {
- dev_err(phy_drd->dev,
- "CRPORT handshake timeout2 (0x%08x)\n", val);
- return -ETIME;
+ err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1,
+ result, !(result & PHYREG1_CR_ACK), 1, 100);
+ if (err == -ETIMEDOUT) {
+ dev_err(phy_drd->dev, "CRPORT handshake timeout2 (0x%08x)\n", val);
+ return err;
}
return 0;
@@ -714,7 +699,9 @@ static int exynos5_usbdrd_phy_calibrate(struct phy *phy)
struct phy_usb_instance *inst = phy_get_drvdata(phy);
struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
- return exynos5420_usbdrd_phy_calibrate(phy_drd);
+ if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI)
+ return exynos5420_usbdrd_phy_calibrate(phy_drd);
+ return 0;
}
static const struct phy_ops exynos5_usbdrd_phy_ops = {
@@ -842,7 +829,6 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct exynos5_usbdrd_phy *phy_drd;
struct phy_provider *phy_provider;
- struct resource *res;
const struct exynos5_usbdrd_phy_drvdata *drv_data;
struct regmap *reg_pmu;
u32 pmu_offset;
@@ -856,8 +842,7 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
dev_set_drvdata(dev, phy_drd);
phy_drd->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- phy_drd->reg_phy = devm_ioremap_resource(dev, res);
+ phy_drd->reg_phy = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(phy_drd->reg_phy))
return PTR_ERR(phy_drd->reg_phy);
@@ -958,7 +943,7 @@ static struct platform_driver exynos5_usb3drd_phy = {
};
module_platform_driver(exynos5_usb3drd_phy);
-MODULE_DESCRIPTION("Samsung EXYNOS5 SoCs USB 3.0 DRD controller PHY driver");
+MODULE_DESCRIPTION("Samsung Exynos5 SoCs USB 3.0 DRD controller PHY driver");
MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:exynos5_usb3drd_phy");
diff --git a/drivers/phy/samsung/phy-exynos5250-sata.c b/drivers/phy/samsung/phy-exynos5250-sata.c
index 4dd7324d91b2..595adba5fb8f 100644
--- a/drivers/phy/samsung/phy-exynos5250-sata.c
+++ b/drivers/phy/samsung/phy-exynos5250-sata.c
@@ -162,7 +162,6 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
{
struct exynos_sata_phy *sata_phy;
struct device *dev = &pdev->dev;
- struct resource *res;
struct phy_provider *phy_provider;
struct device_node *node;
int ret = 0;
@@ -171,9 +170,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
if (!sata_phy)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- sata_phy->regs = devm_ioremap_resource(dev, res);
+ sata_phy->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(sata_phy->regs))
return PTR_ERR(sata_phy->regs);
@@ -190,6 +187,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
return -EINVAL;
sata_phy->client = of_find_i2c_device_by_node(node);
+ of_node_put(node);
if (!sata_phy->client)
return -EPROBE_DEFER;
@@ -198,20 +196,21 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl");
if (IS_ERR(sata_phy->phyclk)) {
dev_err(dev, "failed to get clk for PHY\n");
- return PTR_ERR(sata_phy->phyclk);
+ ret = PTR_ERR(sata_phy->phyclk);
+ goto put_dev;
}
ret = clk_prepare_enable(sata_phy->phyclk);
if (ret < 0) {
dev_err(dev, "failed to enable source clk\n");
- return ret;
+ goto put_dev;
}
sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops);
if (IS_ERR(sata_phy->phy)) {
- clk_disable_unprepare(sata_phy->phyclk);
dev_err(dev, "failed to create PHY\n");
- return PTR_ERR(sata_phy->phy);
+ ret = PTR_ERR(sata_phy->phy);
+ goto clk_disable;
}
phy_set_drvdata(sata_phy->phy, sata_phy);
@@ -219,11 +218,18 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
phy_provider = devm_of_phy_provider_register(dev,
of_phy_simple_xlate);
if (IS_ERR(phy_provider)) {
- clk_disable_unprepare(sata_phy->phyclk);
- return PTR_ERR(phy_provider);
+ ret = PTR_ERR(phy_provider);
+ goto clk_disable;
}
return 0;
+
+clk_disable:
+ clk_disable_unprepare(sata_phy->phyclk);
+put_dev:
+ put_device(&sata_phy->client->dev);
+
+ return ret;
}
static const struct of_device_id exynos_sata_phy_of_match[] = {
diff --git a/drivers/phy/samsung/phy-exynos5250-usb2.c b/drivers/phy/samsung/phy-exynos5250-usb2.c
index 4f53b711fd6f..e198010e1bfd 100644
--- a/drivers/phy/samsung/phy-exynos5250-usb2.c
+++ b/drivers/phy/samsung/phy-exynos5250-usb2.c
@@ -117,9 +117,9 @@
/* Isolation, configured in the power management unit */
#define EXYNOS_5250_USB_ISOL_OTG_OFFSET 0x704
-#define EXYNOS_5250_USB_ISOL_OTG BIT(0)
#define EXYNOS_5250_USB_ISOL_HOST_OFFSET 0x708
-#define EXYNOS_5250_USB_ISOL_HOST BIT(0)
+#define EXYNOS_5420_USB_ISOL_HOST_OFFSET 0x70C
+#define EXYNOS_5250_USB_ISOL_ENABLE BIT(0)
/* Mode swtich register */
#define EXYNOS_5250_MODE_SWITCH_OFFSET 0x230
@@ -132,7 +132,6 @@ enum exynos4x12_phy_id {
EXYNOS5250_HOST,
EXYNOS5250_HSIC0,
EXYNOS5250_HSIC1,
- EXYNOS5250_NUM_PHYS,
};
/*
@@ -176,20 +175,19 @@ static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 offset;
- u32 mask;
+ u32 mask = EXYNOS_5250_USB_ISOL_ENABLE;
- switch (inst->cfg->id) {
- case EXYNOS5250_DEVICE:
+ if (drv->cfg == &exynos5250_usb2_phy_config &&
+ inst->cfg->id == EXYNOS5250_DEVICE)
offset = EXYNOS_5250_USB_ISOL_OTG_OFFSET;
- mask = EXYNOS_5250_USB_ISOL_OTG;
- break;
- case EXYNOS5250_HOST:
+ else if (drv->cfg == &exynos5250_usb2_phy_config &&
+ inst->cfg->id == EXYNOS5250_HOST)
offset = EXYNOS_5250_USB_ISOL_HOST_OFFSET;
- mask = EXYNOS_5250_USB_ISOL_HOST;
- break;
- default:
+ else if (drv->cfg == &exynos5420_usb2_phy_config &&
+ inst->cfg->id == EXYNOS5250_HOST)
+ offset = EXYNOS_5420_USB_ISOL_HOST_OFFSET;
+ else
return;
- }
regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
}
@@ -390,9 +388,31 @@ static const struct samsung_usb2_common_phy exynos5250_phys[] = {
},
};
+static const struct samsung_usb2_common_phy exynos5420_phys[] = {
+ {
+ .label = "host",
+ .id = EXYNOS5250_HOST,
+ .power_on = exynos5250_power_on,
+ .power_off = exynos5250_power_off,
+ },
+ {
+ .label = "hsic",
+ .id = EXYNOS5250_HSIC0,
+ .power_on = exynos5250_power_on,
+ .power_off = exynos5250_power_off,
+ },
+};
+
const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = {
.has_mode_switch = 1,
- .num_phys = EXYNOS5250_NUM_PHYS,
+ .num_phys = ARRAY_SIZE(exynos5250_phys),
.phys = exynos5250_phys,
.rate_to_clk = exynos5250_rate_to_clk,
};
+
+const struct samsung_usb2_phy_config exynos5420_usb2_phy_config = {
+ .has_mode_switch = 1,
+ .num_phys = ARRAY_SIZE(exynos5420_phys),
+ .phys = exynos5420_phys,
+ .rate_to_clk = exynos5250_rate_to_clk,
+};
diff --git a/drivers/phy/samsung/phy-exynos7-ufs.c b/drivers/phy/samsung/phy-exynos7-ufs.c
new file mode 100644
index 000000000000..a982e7c128c5
--- /dev/null
+++ b/drivers/phy/samsung/phy-exynos7-ufs.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UFS PHY driver data for Samsung EXYNOS7 SoC
+ *
+ * Copyright (C) 2020 Samsung Electronics Co., Ltd.
+ */
+
+#include "phy-samsung-ufs.h"
+
+#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL 0x720
+#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1
+#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0)
+
+#define EXYNOS7_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x5e
+
+/* Calibration for phy initialization */
+static const struct samsung_ufs_phy_cfg exynos7_pre_init_cfg[] = {
+ PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x017, 0x84, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x035, 0x58, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x037, 0x40, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x03b, 0x83, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x04c, 0x5b, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x05c, 0x14, PWR_MODE_ANY),
+ END_UFS_PHY_CFG
+};
+
+/* Calibration for HS mode series A/B */
+static const struct samsung_ufs_phy_cfg exynos7_pre_pwr_hs_cfg[] = {
+ PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_HS_ANY),
+ PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_HS_ANY),
+ PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_HS_ANY),
+ /* Setting order: 1st(0x16, 2nd(0x15) */
+ PHY_COMN_REG_CFG(0x016, 0xff, PWR_MODE_HS_ANY),
+ PHY_COMN_REG_CFG(0x015, 0x80, PWR_MODE_HS_ANY),
+ PHY_COMN_REG_CFG(0x017, 0x94, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x037, 0x43, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x038, 0x3f, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_HS_G2_SER_A),
+ PHY_TRSV_REG_CFG(0x042, 0xbb, PWR_MODE_HS_G2_SER_B),
+ PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x034, 0x35, PWR_MODE_HS_G2_SER_A),
+ PHY_TRSV_REG_CFG(0x034, 0x36, PWR_MODE_HS_G2_SER_B),
+ PHY_TRSV_REG_CFG(0x035, 0x5b, PWR_MODE_HS_G2_SER_A),
+ PHY_TRSV_REG_CFG(0x035, 0x5c, PWR_MODE_HS_G2_SER_B),
+ END_UFS_PHY_CFG
+};
+
+/* Calibration for HS mode series A/B atfer PMC */
+static const struct samsung_ufs_phy_cfg exynos7_post_pwr_hs_cfg[] = {
+ PHY_COMN_REG_CFG(0x015, 0x00, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_HS_ANY),
+ END_UFS_PHY_CFG
+};
+
+static const struct samsung_ufs_phy_cfg *exynos7_ufs_phy_cfgs[CFG_TAG_MAX] = {
+ [CFG_PRE_INIT] = exynos7_pre_init_cfg,
+ [CFG_PRE_PWR_HS] = exynos7_pre_pwr_hs_cfg,
+ [CFG_POST_PWR_HS] = exynos7_post_pwr_hs_cfg,
+};
+
+static const char * const exynos7_ufs_phy_clks[] = {
+ "tx0_symbol_clk", "rx0_symbol_clk", "rx1_symbol_clk", "ref_clk",
+};
+
+const struct samsung_ufs_phy_drvdata exynos7_ufs_phy = {
+ .cfgs = exynos7_ufs_phy_cfgs,
+ .isol = {
+ .offset = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL,
+ .mask = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_MASK,
+ .en = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_EN,
+ },
+ .clk_list = exynos7_ufs_phy_clks,
+ .num_clks = ARRAY_SIZE(exynos7_ufs_phy_clks),
+ .cdr_lock_status_offset = EXYNOS7_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS,
+};
diff --git a/drivers/phy/samsung/phy-exynosautov9-ufs.c b/drivers/phy/samsung/phy-exynosautov9-ufs.c
new file mode 100644
index 000000000000..49e2bcbef0b4
--- /dev/null
+++ b/drivers/phy/samsung/phy-exynosautov9-ufs.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UFS PHY driver data for Samsung EXYNOSAUTO v9 SoC
+ *
+ * Copyright (C) 2021 Samsung Electronics Co., Ltd.
+ */
+
+#include "phy-samsung-ufs.h"
+
+#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL 0x728
+#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1
+#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0)
+#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x5e
+
+#define PHY_TRSV_REG_CFG_AUTOV9(o, v, d) \
+ PHY_TRSV_REG_CFG_OFFSET(o, v, d, 0x50)
+
+/* Calibration for phy initialization */
+static const struct samsung_ufs_phy_cfg exynosautov9_pre_init_cfg[] = {
+ PHY_COMN_REG_CFG(0x023, 0x80, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x01d, 0x10, PWR_MODE_ANY),
+
+ PHY_TRSV_REG_CFG_AUTOV9(0x044, 0xb5, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x04d, 0x43, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x05b, 0x20, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x05e, 0xc0, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x038, 0x12, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x059, 0x58, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x06c, 0x18, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x06d, 0x02, PWR_MODE_ANY),
+
+ PHY_COMN_REG_CFG(0x023, 0xc0, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x023, 0x00, PWR_MODE_ANY),
+
+ PHY_TRSV_REG_CFG_AUTOV9(0x042, 0x5d, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x043, 0x80, PWR_MODE_ANY),
+
+ END_UFS_PHY_CFG,
+};
+
+/* Calibration for HS mode series A/B */
+static const struct samsung_ufs_phy_cfg exynosautov9_pre_pwr_hs_cfg[] = {
+ PHY_TRSV_REG_CFG_AUTOV9(0x032, 0xbc, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x03c, 0x7f, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x048, 0xc0, PWR_MODE_HS_ANY),
+
+ PHY_TRSV_REG_CFG_AUTOV9(0x04a, 0x00, PWR_MODE_HS_G3_SER_B),
+ PHY_TRSV_REG_CFG_AUTOV9(0x04b, 0x10, PWR_MODE_HS_G1_SER_B |
+ PWR_MODE_HS_G3_SER_B),
+ PHY_TRSV_REG_CFG_AUTOV9(0x04d, 0x63, PWR_MODE_HS_G3_SER_B),
+
+ END_UFS_PHY_CFG,
+};
+
+static const struct samsung_ufs_phy_cfg *exynosautov9_ufs_phy_cfgs[CFG_TAG_MAX] = {
+ [CFG_PRE_INIT] = exynosautov9_pre_init_cfg,
+ [CFG_PRE_PWR_HS] = exynosautov9_pre_pwr_hs_cfg,
+};
+
+static const char * const exynosautov9_ufs_phy_clks[] = {
+ "ref_clk",
+};
+
+const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy = {
+ .cfgs = exynosautov9_ufs_phy_cfgs,
+ .isol = {
+ .offset = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL,
+ .mask = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_MASK,
+ .en = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_EN,
+ },
+ .clk_list = exynosautov9_ufs_phy_clks,
+ .num_clks = ARRAY_SIZE(exynosautov9_ufs_phy_clks),
+ .cdr_lock_status_offset = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS,
+};
diff --git a/drivers/phy/samsung/phy-fsd-ufs.c b/drivers/phy/samsung/phy-fsd-ufs.c
new file mode 100644
index 000000000000..d36cabd53434
--- /dev/null
+++ b/drivers/phy/samsung/phy-fsd-ufs.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UFS PHY driver data for FSD SoC
+ *
+ * Copyright (C) 2022 Samsung Electronics Co., Ltd.
+ *
+ */
+#include "phy-samsung-ufs.h"
+
+#define FSD_EMBEDDED_COMBO_PHY_CTRL 0x724
+#define FSD_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1
+#define FSD_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0)
+#define FSD_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x6e
+
+static const struct samsung_ufs_phy_cfg fsd_pre_init_cfg[] = {
+ PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x017, 0x94, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x035, 0x58, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x037, 0x40, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x03b, 0x83, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x04c, 0x5b, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x05c, 0x14, PWR_MODE_ANY),
+ END_UFS_PHY_CFG
+};
+
+/* Calibration for HS mode series A/B */
+static const struct samsung_ufs_phy_cfg fsd_pre_pwr_hs_cfg[] = {
+ END_UFS_PHY_CFG
+};
+
+/* Calibration for HS mode series A/B atfer PMC */
+static const struct samsung_ufs_phy_cfg fsd_post_pwr_hs_cfg[] = {
+ END_UFS_PHY_CFG
+};
+
+static const struct samsung_ufs_phy_cfg *fsd_ufs_phy_cfgs[CFG_TAG_MAX] = {
+ [CFG_PRE_INIT] = fsd_pre_init_cfg,
+ [CFG_PRE_PWR_HS] = fsd_pre_pwr_hs_cfg,
+ [CFG_POST_PWR_HS] = fsd_post_pwr_hs_cfg,
+};
+
+static const char * const fsd_ufs_phy_clks[] = {
+ "ref_clk",
+};
+
+const struct samsung_ufs_phy_drvdata fsd_ufs_phy = {
+ .cfgs = fsd_ufs_phy_cfgs,
+ .isol = {
+ .offset = FSD_EMBEDDED_COMBO_PHY_CTRL,
+ .mask = FSD_EMBEDDED_COMBO_PHY_CTRL_MASK,
+ .en = FSD_EMBEDDED_COMBO_PHY_CTRL_EN,
+ },
+ .clk_list = fsd_ufs_phy_clks,
+ .num_clks = ARRAY_SIZE(fsd_ufs_phy_clks),
+ .cdr_lock_status_offset = FSD_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS,
+};
diff --git a/drivers/phy/samsung/phy-s5pv210-usb2.c b/drivers/phy/samsung/phy-s5pv210-usb2.c
index 56a5083fe6f9..32be62e49804 100644
--- a/drivers/phy/samsung/phy-s5pv210-usb2.c
+++ b/drivers/phy/samsung/phy-s5pv210-usb2.c
@@ -139,6 +139,10 @@ static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
udelay(10);
rst &= ~rstbits;
writel(rst, drv->reg_phy + S5PV210_UPHYRST);
+ /* The following delay is necessary for the reset sequence to be
+ * completed
+ */
+ udelay(80);
} else {
pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
pwr |= phypwr;
diff --git a/drivers/phy/samsung/phy-samsung-ufs.c b/drivers/phy/samsung/phy-samsung-ufs.c
new file mode 100644
index 000000000000..183c88e3d1ec
--- /dev/null
+++ b/drivers/phy/samsung/phy-samsung-ufs.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UFS PHY driver for Samsung SoC
+ *
+ * Copyright (C) 2020 Samsung Electronics Co., Ltd.
+ * Author: Seungwon Jeon <essuuj@gmail.com>
+ * Author: Alim Akhtar <alim.akhtar@samsung.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "phy-samsung-ufs.h"
+
+#define for_each_phy_lane(phy, i) \
+ for (i = 0; i < (phy)->lane_cnt; i++)
+#define for_each_phy_cfg(cfg) \
+ for (; (cfg)->id; (cfg)++)
+
+#define PHY_DEF_LANE_CNT 1
+
+static void samsung_ufs_phy_config(struct samsung_ufs_phy *phy,
+ const struct samsung_ufs_phy_cfg *cfg,
+ u8 lane)
+{
+ enum {LANE_0, LANE_1}; /* lane index */
+
+ switch (lane) {
+ case LANE_0:
+ writel(cfg->val, (phy)->reg_pma + cfg->off_0);
+ break;
+ case LANE_1:
+ if (cfg->id == PHY_TRSV_BLK)
+ writel(cfg->val, (phy)->reg_pma + cfg->off_1);
+ break;
+ }
+}
+
+static int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy)
+{
+ struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
+ const unsigned int timeout_us = 100000;
+ const unsigned int sleep_us = 10;
+ u32 val;
+ int err;
+
+ err = readl_poll_timeout(
+ ufs_phy->reg_pma + PHY_APB_ADDR(PHY_PLL_LOCK_STATUS),
+ val, (val & PHY_PLL_LOCK_BIT), sleep_us, timeout_us);
+ if (err) {
+ dev_err(ufs_phy->dev,
+ "failed to get phy pll lock acquisition %d\n", err);
+ goto out;
+ }
+
+ err = readl_poll_timeout(
+ ufs_phy->reg_pma +
+ PHY_APB_ADDR(ufs_phy->drvdata->cdr_lock_status_offset),
+ val, (val & PHY_CDR_LOCK_BIT), sleep_us, timeout_us);
+ if (err)
+ dev_err(ufs_phy->dev,
+ "failed to get phy cdr lock acquisition %d\n", err);
+out:
+ return err;
+}
+
+static int samsung_ufs_phy_calibrate(struct phy *phy)
+{
+ struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
+ const struct samsung_ufs_phy_cfg * const *cfgs = ufs_phy->cfgs;
+ const struct samsung_ufs_phy_cfg *cfg;
+ int err = 0;
+ int i;
+
+ if (unlikely(ufs_phy->ufs_phy_state < CFG_PRE_INIT ||
+ ufs_phy->ufs_phy_state >= CFG_TAG_MAX)) {
+ dev_err(ufs_phy->dev, "invalid phy config index %d\n", ufs_phy->ufs_phy_state);
+ return -EINVAL;
+ }
+
+ cfg = cfgs[ufs_phy->ufs_phy_state];
+ if (!cfg)
+ goto out;
+
+ for_each_phy_cfg(cfg) {
+ for_each_phy_lane(ufs_phy, i) {
+ samsung_ufs_phy_config(ufs_phy, cfg, i);
+ }
+ }
+
+ if (ufs_phy->ufs_phy_state == CFG_POST_PWR_HS)
+ err = samsung_ufs_phy_wait_for_lock_acq(phy);
+
+ /**
+ * In Samsung ufshci, PHY need to be calibrated at different
+ * stages / state mainly before Linkstartup, after Linkstartup,
+ * before power mode change and after power mode change.
+ * Below state machine to make sure to calibrate PHY in each
+ * state. Here after configuring PHY in a given state, will
+ * change the state to next state so that next state phy
+ * calibration value can be programed
+ */
+out:
+ switch (ufs_phy->ufs_phy_state) {
+ case CFG_PRE_INIT:
+ ufs_phy->ufs_phy_state = CFG_POST_INIT;
+ break;
+ case CFG_POST_INIT:
+ ufs_phy->ufs_phy_state = CFG_PRE_PWR_HS;
+ break;
+ case CFG_PRE_PWR_HS:
+ ufs_phy->ufs_phy_state = CFG_POST_PWR_HS;
+ break;
+ case CFG_POST_PWR_HS:
+ /* Change back to INIT state */
+ ufs_phy->ufs_phy_state = CFG_PRE_INIT;
+ break;
+ default:
+ dev_err(ufs_phy->dev, "wrong state for phy calibration\n");
+ }
+
+ return err;
+}
+
+static int samsung_ufs_phy_clks_init(struct samsung_ufs_phy *phy)
+{
+ int i;
+ const struct samsung_ufs_phy_drvdata *drvdata = phy->drvdata;
+ int num_clks = drvdata->num_clks;
+
+ phy->clks = devm_kcalloc(phy->dev, num_clks, sizeof(*phy->clks),
+ GFP_KERNEL);
+ if (!phy->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < num_clks; i++)
+ phy->clks[i].id = drvdata->clk_list[i];
+
+ return devm_clk_bulk_get(phy->dev, num_clks, phy->clks);
+}
+
+static int samsung_ufs_phy_init(struct phy *phy)
+{
+ struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
+
+ ss_phy->lane_cnt = phy->attrs.bus_width;
+ ss_phy->ufs_phy_state = CFG_PRE_INIT;
+
+ return 0;
+}
+
+static int samsung_ufs_phy_power_on(struct phy *phy)
+{
+ struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
+ int ret;
+
+ samsung_ufs_phy_ctrl_isol(ss_phy, false);
+
+ ret = clk_bulk_prepare_enable(ss_phy->drvdata->num_clks, ss_phy->clks);
+ if (ret) {
+ dev_err(ss_phy->dev, "failed to enable ufs phy clocks\n");
+ return ret;
+ }
+
+ if (ss_phy->ufs_phy_state == CFG_PRE_INIT) {
+ ret = samsung_ufs_phy_calibrate(phy);
+ if (ret)
+ dev_err(ss_phy->dev, "ufs phy calibration failed\n");
+ }
+
+ return ret;
+}
+
+static int samsung_ufs_phy_power_off(struct phy *phy)
+{
+ struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
+
+ clk_bulk_disable_unprepare(ss_phy->drvdata->num_clks, ss_phy->clks);
+
+ samsung_ufs_phy_ctrl_isol(ss_phy, true);
+
+ return 0;
+}
+
+static int samsung_ufs_phy_set_mode(struct phy *generic_phy,
+ enum phy_mode mode, int submode)
+{
+ struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(generic_phy);
+
+ ss_phy->mode = PHY_MODE_INVALID;
+
+ if (mode > 0)
+ ss_phy->mode = mode;
+
+ return 0;
+}
+
+static int samsung_ufs_phy_exit(struct phy *phy)
+{
+ struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
+
+ ss_phy->ufs_phy_state = CFG_TAG_MAX;
+
+ return 0;
+}
+
+static const struct phy_ops samsung_ufs_phy_ops = {
+ .init = samsung_ufs_phy_init,
+ .exit = samsung_ufs_phy_exit,
+ .power_on = samsung_ufs_phy_power_on,
+ .power_off = samsung_ufs_phy_power_off,
+ .calibrate = samsung_ufs_phy_calibrate,
+ .set_mode = samsung_ufs_phy_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static const struct of_device_id samsung_ufs_phy_match[];
+
+static int samsung_ufs_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *match;
+ struct samsung_ufs_phy *phy;
+ struct phy *gen_phy;
+ struct phy_provider *phy_provider;
+ const struct samsung_ufs_phy_drvdata *drvdata;
+ u32 isol_offset;
+ int err = 0;
+
+ match = of_match_node(samsung_ufs_phy_match, dev->of_node);
+ if (!match) {
+ err = -EINVAL;
+ dev_err(dev, "failed to get match_node\n");
+ goto out;
+ }
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ phy->reg_pma = devm_platform_ioremap_resource_byname(pdev, "phy-pma");
+ if (IS_ERR(phy->reg_pma)) {
+ err = PTR_ERR(phy->reg_pma);
+ goto out;
+ }
+
+ phy->reg_pmu = syscon_regmap_lookup_by_phandle(
+ dev->of_node, "samsung,pmu-syscon");
+ if (IS_ERR(phy->reg_pmu)) {
+ err = PTR_ERR(phy->reg_pmu);
+ dev_err(dev, "failed syscon remap for pmu\n");
+ goto out;
+ }
+
+ gen_phy = devm_phy_create(dev, NULL, &samsung_ufs_phy_ops);
+ if (IS_ERR(gen_phy)) {
+ err = PTR_ERR(gen_phy);
+ dev_err(dev, "failed to create PHY for ufs-phy\n");
+ goto out;
+ }
+
+ drvdata = match->data;
+ phy->dev = dev;
+ phy->drvdata = drvdata;
+ phy->cfgs = drvdata->cfgs;
+ memcpy(&phy->isol, &drvdata->isol, sizeof(phy->isol));
+
+ if (!of_property_read_u32_index(dev->of_node, "samsung,pmu-syscon", 1,
+ &isol_offset))
+ phy->isol.offset = isol_offset;
+
+ phy->lane_cnt = PHY_DEF_LANE_CNT;
+
+ err = samsung_ufs_phy_clks_init(phy);
+ if (err) {
+ dev_err(dev, "failed to get phy clocks\n");
+ goto out;
+ }
+
+ phy_set_drvdata(gen_phy, phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider)) {
+ err = PTR_ERR(phy_provider);
+ dev_err(dev, "failed to register phy-provider\n");
+ goto out;
+ }
+out:
+ return err;
+}
+
+static const struct of_device_id samsung_ufs_phy_match[] = {
+ {
+ .compatible = "samsung,exynos7-ufs-phy",
+ .data = &exynos7_ufs_phy,
+ }, {
+ .compatible = "samsung,exynosautov9-ufs-phy",
+ .data = &exynosautov9_ufs_phy,
+ }, {
+ .compatible = "tesla,fsd-ufs-phy",
+ .data = &fsd_ufs_phy,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, samsung_ufs_phy_match);
+
+static struct platform_driver samsung_ufs_phy_driver = {
+ .probe = samsung_ufs_phy_probe,
+ .driver = {
+ .name = "samsung-ufs-phy",
+ .of_match_table = samsung_ufs_phy_match,
+ },
+};
+module_platform_driver(samsung_ufs_phy_driver);
+MODULE_DESCRIPTION("Samsung SoC UFS PHY Driver");
+MODULE_AUTHOR("Seungwon Jeon <essuuj@gmail.com>");
+MODULE_AUTHOR("Alim Akhtar <alim.akhtar@samsung.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/samsung/phy-samsung-ufs.h b/drivers/phy/samsung/phy-samsung-ufs.h
new file mode 100644
index 000000000000..e122960cfee8
--- /dev/null
+++ b/drivers/phy/samsung/phy-samsung-ufs.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * UFS PHY driver for Samsung EXYNOS SoC
+ *
+ * Copyright (C) 2020 Samsung Electronics Co., Ltd.
+ * Author: Seungwon Jeon <essuuj@gmail.com>
+ * Author: Alim Akhtar <alim.akhtar@samsung.com>
+ *
+ */
+#ifndef _PHY_SAMSUNG_UFS_
+#define _PHY_SAMSUNG_UFS_
+
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+
+#define PHY_COMN_BLK 1
+#define PHY_TRSV_BLK 2
+#define END_UFS_PHY_CFG { 0 }
+#define PHY_TRSV_CH_OFFSET 0x30
+#define PHY_APB_ADDR(off) ((off) << 2)
+
+#define PHY_COMN_REG_CFG(o, v, d) { \
+ .off_0 = PHY_APB_ADDR((o)), \
+ .off_1 = 0, \
+ .val = (v), \
+ .desc = (d), \
+ .id = PHY_COMN_BLK, \
+}
+
+#define PHY_TRSV_REG_CFG_OFFSET(o, v, d, c) { \
+ .off_0 = PHY_APB_ADDR((o)), \
+ .off_1 = PHY_APB_ADDR((o) + (c)), \
+ .val = (v), \
+ .desc = (d), \
+ .id = PHY_TRSV_BLK, \
+}
+
+#define PHY_TRSV_REG_CFG(o, v, d) \
+ PHY_TRSV_REG_CFG_OFFSET(o, v, d, PHY_TRSV_CH_OFFSET)
+
+/* UFS PHY registers */
+#define PHY_PLL_LOCK_STATUS 0x1e
+
+#define PHY_PLL_LOCK_BIT BIT(5)
+#define PHY_CDR_LOCK_BIT BIT(4)
+
+/* description for PHY calibration */
+enum {
+ /* applicable to any */
+ PWR_DESC_ANY = 0,
+ /* mode */
+ PWR_DESC_PWM = 1,
+ PWR_DESC_HS = 2,
+ /* series */
+ PWR_DESC_SER_A = 1,
+ PWR_DESC_SER_B = 2,
+ /* gear */
+ PWR_DESC_G1 = 1,
+ PWR_DESC_G2 = 2,
+ PWR_DESC_G3 = 3,
+ /* field mask */
+ MD_MASK = 0x3,
+ SR_MASK = 0x3,
+ GR_MASK = 0x7,
+};
+
+#define PWR_MODE_HS_G1_ANY PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_ANY)
+#define PWR_MODE_HS_G1_SER_A PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_A)
+#define PWR_MODE_HS_G1_SER_B PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_B)
+#define PWR_MODE_HS_G2_ANY PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_ANY)
+#define PWR_MODE_HS_G2_SER_A PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_A)
+#define PWR_MODE_HS_G2_SER_B PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_B)
+#define PWR_MODE_HS_G3_ANY PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_ANY)
+#define PWR_MODE_HS_G3_SER_A PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_A)
+#define PWR_MODE_HS_G3_SER_B PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_B)
+#define PWR_MODE(g, s, m) ((((g) & GR_MASK) << 4) |\
+ (((s) & SR_MASK) << 2) | ((m) & MD_MASK))
+#define PWR_MODE_PWM_ANY PWR_MODE(PWR_DESC_ANY,\
+ PWR_DESC_ANY, PWR_DESC_PWM)
+#define PWR_MODE_HS(g, s) ((((g) & GR_MASK) << 4) |\
+ (((s) & SR_MASK) << 2) | PWR_DESC_HS)
+#define PWR_MODE_HS_ANY PWR_MODE(PWR_DESC_ANY,\
+ PWR_DESC_ANY, PWR_DESC_HS)
+#define PWR_MODE_ANY PWR_MODE(PWR_DESC_ANY,\
+ PWR_DESC_ANY, PWR_DESC_ANY)
+/* PHY calibration point/state */
+enum {
+ CFG_PRE_INIT,
+ CFG_POST_INIT,
+ CFG_PRE_PWR_HS,
+ CFG_POST_PWR_HS,
+ CFG_TAG_MAX,
+};
+
+struct samsung_ufs_phy_cfg {
+ u32 off_0;
+ u32 off_1;
+ u32 val;
+ u8 desc;
+ u8 id;
+};
+
+struct samsung_ufs_phy_pmu_isol {
+ u32 offset;
+ u32 mask;
+ u32 en;
+};
+
+struct samsung_ufs_phy_drvdata {
+ const struct samsung_ufs_phy_cfg **cfgs;
+ struct samsung_ufs_phy_pmu_isol isol;
+ const char * const *clk_list;
+ int num_clks;
+ u32 cdr_lock_status_offset;
+};
+
+struct samsung_ufs_phy {
+ struct device *dev;
+ void __iomem *reg_pma;
+ struct regmap *reg_pmu;
+ struct clk_bulk_data *clks;
+ const struct samsung_ufs_phy_drvdata *drvdata;
+ const struct samsung_ufs_phy_cfg * const *cfgs;
+ struct samsung_ufs_phy_pmu_isol isol;
+ u8 lane_cnt;
+ int ufs_phy_state;
+ enum phy_mode mode;
+};
+
+static inline struct samsung_ufs_phy *get_samsung_ufs_phy(struct phy *phy)
+{
+ return (struct samsung_ufs_phy *)phy_get_drvdata(phy);
+}
+
+static inline void samsung_ufs_phy_ctrl_isol(
+ struct samsung_ufs_phy *phy, u32 isol)
+{
+ regmap_update_bits(phy->reg_pmu, phy->isol.offset,
+ phy->isol.mask, isol ? 0 : phy->isol.en);
+}
+
+extern const struct samsung_ufs_phy_drvdata exynos7_ufs_phy;
+extern const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy;
+extern const struct samsung_ufs_phy_drvdata fsd_ufs_phy;
+
+#endif /* _PHY_SAMSUNG_UFS_ */
diff --git a/drivers/phy/samsung/phy-samsung-usb2.c b/drivers/phy/samsung/phy-samsung-usb2.c
index 090aa02e02de..ec2befabeea6 100644
--- a/drivers/phy/samsung/phy-samsung-usb2.c
+++ b/drivers/phy/samsung/phy-samsung-usb2.c
@@ -127,6 +127,10 @@ static const struct of_device_id samsung_usb2_phy_of_match[] = {
.compatible = "samsung,exynos5250-usb2-phy",
.data = &exynos5250_usb2_phy_config,
},
+ {
+ .compatible = "samsung,exynos5420-usb2-phy",
+ .data = &exynos5420_usb2_phy_config,
+ },
#endif
#ifdef CONFIG_PHY_S5PV210_USB2
{
@@ -143,7 +147,6 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev)
const struct samsung_usb2_phy_config *cfg;
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
- struct resource *mem;
struct samsung_usb2_phy_driver *drv;
int i, ret;
@@ -167,8 +170,7 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev)
drv->cfg = cfg;
drv->dev = dev;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- drv->reg_phy = devm_ioremap_resource(dev, mem);
+ drv->reg_phy = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(drv->reg_phy)) {
dev_err(dev, "Failed to map register memory (phy)\n");
return PTR_ERR(drv->reg_phy);
@@ -255,7 +257,7 @@ static struct platform_driver samsung_usb2_phy_driver = {
};
module_platform_driver(samsung_usb2_phy_driver);
-MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
+MODULE_DESCRIPTION("Samsung S5P/Exynos SoC USB PHY driver");
MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:samsung-usb2-phy");
diff --git a/drivers/phy/samsung/phy-samsung-usb2.h b/drivers/phy/samsung/phy-samsung-usb2.h
index 2c1a7d71142b..ebaf43bfc5a2 100644
--- a/drivers/phy/samsung/phy-samsung-usb2.h
+++ b/drivers/phy/samsung/phy-samsung-usb2.h
@@ -43,7 +43,7 @@ struct samsung_usb2_phy_driver {
struct regmap *reg_pmu;
struct regmap *reg_sys;
spinlock_t lock;
- struct samsung_usb2_phy_instance instances[0];
+ struct samsung_usb2_phy_instance instances[];
};
struct samsung_usb2_common_phy {
@@ -66,5 +66,6 @@ extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config;
extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config;
+extern const struct samsung_usb2_phy_config exynos5420_usb2_phy_config;
extern const struct samsung_usb2_phy_config s5pv210_usb2_phy_config;
#endif
diff --git a/drivers/phy/socionext/Kconfig b/drivers/phy/socionext/Kconfig
index 8c9d7c37536a..8ae644756352 100644
--- a/drivers/phy/socionext/Kconfig
+++ b/drivers/phy/socionext/Kconfig
@@ -34,3 +34,13 @@ config PHY_UNIPHIER_PCIE
help
Enable this to support PHY implemented in PCIe controller
on UniPhier SoCs. This driver supports LD20 and PXs3 SoCs.
+
+config PHY_UNIPHIER_AHCI
+ tristate "UniPhier AHCI PHY driver"
+ depends on ARCH_UNIPHIER || COMPILE_TEST
+ depends on OF && HAS_IOMEM
+ default SATA_AHCI_PLATFORM
+ select GENERIC_PHY
+ help
+ Enable this to support PHY implemented in AHCI controller
+ on UniPhier SoCs. This driver supports Pro4, PXs2 and PXs3 SoCs.
diff --git a/drivers/phy/socionext/Makefile b/drivers/phy/socionext/Makefile
index 7dc9095b5bb7..e67c2da6675c 100644
--- a/drivers/phy/socionext/Makefile
+++ b/drivers/phy/socionext/Makefile
@@ -6,3 +6,4 @@
obj-$(CONFIG_PHY_UNIPHIER_USB2) += phy-uniphier-usb2.o
obj-$(CONFIG_PHY_UNIPHIER_USB3) += phy-uniphier-usb3hs.o phy-uniphier-usb3ss.o
obj-$(CONFIG_PHY_UNIPHIER_PCIE) += phy-uniphier-pcie.o
+obj-$(CONFIG_PHY_UNIPHIER_AHCI) += phy-uniphier-ahci.o
diff --git a/drivers/phy/socionext/phy-uniphier-ahci.c b/drivers/phy/socionext/phy-uniphier-ahci.c
new file mode 100644
index 000000000000..28cf3efe0695
--- /dev/null
+++ b/drivers/phy/socionext/phy-uniphier-ahci.c
@@ -0,0 +1,514 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * phy-uniphier-ahci.c - PHY driver for UniPhier AHCI controller
+ * Copyright 2016-2020, Socionext Inc.
+ * Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+struct uniphier_ahciphy_priv {
+ struct device *dev;
+ void __iomem *base;
+ 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;
+};
+
+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)
+#define CKCTRL_P0_RESET BIT(10)
+#define CKCTRL_REF_SSP_EN BIT(9)
+#define TXCTRL0 0x4
+#define TXCTRL0_AMP_G3_MASK GENMASK(22, 16)
+#define TXCTRL0_AMP_G2_MASK GENMASK(14, 8)
+#define TXCTRL0_AMP_G1_MASK GENMASK(6, 0)
+#define TXCTRL1 0x8
+#define TXCTRL1_DEEMPH_G3_MASK GENMASK(21, 16)
+#define TXCTRL1_DEEMPH_G2_MASK GENMASK(13, 8)
+#define TXCTRL1_DEEMPH_G1_MASK GENMASK(5, 0)
+#define RXCTRL 0xc
+#define RXCTRL_LOS_LVL_MASK GENMASK(20, 16)
+#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)
+{
+ u32 val;
+
+ val = readl(priv->base + CKCTRL);
+
+ if (enable) {
+ val |= CKCTRL_REF_SSP_EN;
+ writel(val, priv->base + CKCTRL);
+ val &= ~CKCTRL_P0_RESET;
+ writel(val, priv->base + CKCTRL);
+ } else {
+ val |= CKCTRL_P0_RESET;
+ writel(val, priv->base + CKCTRL);
+ val &= ~CKCTRL_REF_SSP_EN;
+ writel(val, priv->base + CKCTRL);
+ }
+}
+
+static int uniphier_ahciphy_pxs2_power_on(struct uniphier_ahciphy_priv *priv)
+{
+ int ret;
+ u32 val;
+
+ uniphier_ahciphy_pxs2_enable(priv, true);
+
+ /* wait until PLL is ready */
+ if (priv->data->is_ready_high)
+ ret = readl_poll_timeout(priv->base + CKCTRL, val,
+ (val & CKCTRL_P0_READY), 200, 400);
+ else
+ ret = readl_poll_timeout(priv->base + CKCTRL, val,
+ !(val & CKCTRL_P0_READY), 200, 400);
+ if (ret) {
+ dev_err(priv->dev, "Failed to check whether PHY PLL is ready\n");
+ uniphier_ahciphy_pxs2_enable(priv, false);
+ }
+
+ return ret;
+}
+
+static int uniphier_ahciphy_pxs2_power_off(struct uniphier_ahciphy_priv *priv)
+{
+ uniphier_ahciphy_pxs2_enable(priv, false);
+
+ return 0;
+}
+
+static int uniphier_ahciphy_pxs3_init(struct uniphier_ahciphy_priv *priv)
+{
+ int i;
+ u32 val;
+
+ /* setup port parameter */
+ val = readl(priv->base + TXCTRL0);
+ val &= ~TXCTRL0_AMP_G3_MASK;
+ val |= FIELD_PREP(TXCTRL0_AMP_G3_MASK, 0x73);
+ val &= ~TXCTRL0_AMP_G2_MASK;
+ val |= FIELD_PREP(TXCTRL0_AMP_G2_MASK, 0x46);
+ val &= ~TXCTRL0_AMP_G1_MASK;
+ val |= FIELD_PREP(TXCTRL0_AMP_G1_MASK, 0x42);
+ writel(val, priv->base + TXCTRL0);
+
+ val = readl(priv->base + TXCTRL1);
+ val &= ~TXCTRL1_DEEMPH_G3_MASK;
+ val |= FIELD_PREP(TXCTRL1_DEEMPH_G3_MASK, 0x23);
+ val &= ~TXCTRL1_DEEMPH_G2_MASK;
+ val |= FIELD_PREP(TXCTRL1_DEEMPH_G2_MASK, 0x05);
+ val &= ~TXCTRL1_DEEMPH_G1_MASK;
+ val |= FIELD_PREP(TXCTRL1_DEEMPH_G1_MASK, 0x05);
+
+ val = readl(priv->base + RXCTRL);
+ val &= ~RXCTRL_LOS_LVL_MASK;
+ val |= FIELD_PREP(RXCTRL_LOS_LVL_MASK, 0x9);
+ val &= ~RXCTRL_LOS_BIAS_MASK;
+ val |= FIELD_PREP(RXCTRL_LOS_BIAS_MASK, 0x2);
+ val &= ~RXCTRL_RX_EQ_MASK;
+ val |= FIELD_PREP(RXCTRL_RX_EQ_MASK, 0x1);
+
+ /* dummy read 25 times to make a wait time for the phy to stabilize */
+ for (i = 0; i < 25; i++)
+ readl(priv->base + CKCTRL);
+
+ return 0;
+}
+
+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_gio);
+ if (ret)
+ return ret;
+
+ 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)
+ goto out_rst_assert;
+ }
+
+ return 0;
+
+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;
+}
+
+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;
+}
+
+static int uniphier_ahciphy_power_on(struct phy *phy)
+{
+ struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);
+ int ret = 0;
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(priv->rst);
+ if (ret)
+ goto out_clk_disable;
+
+ if (priv->data->power_on) {
+ ret = priv->data->power_on(priv);
+ if (ret)
+ goto out_reset_assert;
+ }
+
+ return 0;
+
+out_reset_assert:
+ reset_control_assert(priv->rst);
+out_clk_disable:
+ clk_disable_unprepare(priv->clk);
+
+ return ret;
+}
+
+static int uniphier_ahciphy_power_off(struct phy *phy)
+{
+ struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);
+ int ret = 0;
+
+ if (priv->data->power_off)
+ ret = priv->data->power_off(priv);
+
+ reset_control_assert(priv->rst);
+ clk_disable_unprepare(priv->clk);
+
+ return ret;
+}
+
+static const struct phy_ops uniphier_ahciphy_ops = {
+ .init = uniphier_ahciphy_init,
+ .exit = uniphier_ahciphy_exit,
+ .power_on = uniphier_ahciphy_power_on,
+ .power_off = uniphier_ahciphy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int uniphier_ahciphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct uniphier_ahciphy_priv *priv;
+ struct phy *phy;
+ struct phy_provider *phy_provider;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->data = of_device_get_match_data(dev);
+ if (WARN_ON(!priv->data))
+ return -EINVAL;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->clk_parent = devm_clk_get(dev, "link");
+ if (IS_ERR(priv->clk_parent))
+ return PTR_ERR(priv->clk_parent);
+
+ if (priv->data->is_phy_clk) {
+ priv->clk = devm_clk_get(dev, "phy");
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+ }
+
+ priv->rst_parent = devm_reset_control_get_shared(dev, "link");
+ if (IS_ERR(priv->rst_parent))
+ return PTR_ERR(priv->rst_parent);
+
+ priv->rst = devm_reset_control_get_shared(dev, "phy");
+ 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");
+ return PTR_ERR(phy);
+ }
+
+ phy_set_drvdata(phy, priv);
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ 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,
+};
+
+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,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs3-ahci-phy",
+ .data = &uniphier_pxs3_data,
+ },
+ { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, uniphier_ahciphy_match);
+
+static struct platform_driver uniphier_ahciphy_driver = {
+ .probe = uniphier_ahciphy_probe,
+ .driver = {
+ .name = "uniphier-ahci-phy",
+ .of_match_table = uniphier_ahciphy_match,
+ },
+};
+module_platform_driver(uniphier_ahciphy_driver);
+
+MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
+MODULE_DESCRIPTION("UniPhier PHY driver for AHCI controller");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/socionext/phy-uniphier-pcie.c b/drivers/phy/socionext/phy-uniphier-pcie.c
index 93ffbd2940fa..ebca296ef123 100644
--- a/drivers/phy/socionext/phy-uniphier-pcie.c
+++ b/drivers/phy/socionext/phy-uniphier-pcie.c
@@ -19,11 +19,18 @@
#include <linux/resource.h>
/* PHY */
+#define PCL_PHY_CLKCTRL 0x0000
+#define PORT_SEL_MASK GENMASK(11, 9)
+#define PORT_SEL_1 FIELD_PREP(PORT_SEL_MASK, 1)
+
#define PCL_PHY_TEST_I 0x2000
-#define PCL_PHY_TEST_O 0x2004
#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)
#define PCL_PHY_RESET 0x200c
#define PCL_PHY_RESET_N_MNMODE BIT(8) /* =1:manual */
@@ -33,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
@@ -41,52 +52,71 @@
#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;
struct device *dev;
- struct clk *clk;
- struct reset_control *rst;
+ struct clk *clk, *clk_gio;
+ struct reset_control *rst, *rst_gio;
const struct uniphier_pciephy_soc_data *data;
};
struct uniphier_pciephy_soc_data {
- bool has_syscon;
+ 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);
+ uniphier_pciephy_testio_write(priv, id, val);
+ val = uniphier_pciephy_testio_read(priv, id);
/* update value */
- val &= ~FIELD_PREP(TESTI_DAT_MASK, mask);
- val = FIELD_PREP(TESTI_DAT_MASK, mask & param);
+ 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)
@@ -111,22 +141,45 @@ static void uniphier_pciephy_deassert(struct uniphier_pciephy_priv *priv)
static int uniphier_pciephy_init(struct phy *phy)
{
struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);
- int ret;
+ u32 val;
+ int ret, id;
ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
- ret = reset_control_deassert(priv->rst);
+ ret = clk_prepare_enable(priv->clk_gio);
if (ret)
goto out_clk_disable;
- uniphier_pciephy_set_param(priv, PCL_PHY_R00,
+ ret = reset_control_deassert(priv->rst);
+ if (ret)
+ goto out_clk_gio_disable;
+
+ ret = reset_control_deassert(priv->rst_gio);
+ if (ret)
+ goto out_rst_assert;
+
+ /* support only 1 port */
+ val = readl(priv->base + PCL_PHY_CLKCTRL);
+ val &= ~PORT_SEL_MASK;
+ val |= PORT_SEL_1;
+ writel(val, priv->base + PCL_PHY_CLKCTRL);
+
+ /* legacy controller doesn't have phy_reset and parameters */
+ if (priv->data->is_legacy)
+ return 0;
+
+ 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);
@@ -134,6 +187,10 @@ static int uniphier_pciephy_init(struct phy *phy)
return 0;
+out_rst_assert:
+ reset_control_assert(priv->rst);
+out_clk_gio_disable:
+ clk_disable_unprepare(priv->clk_gio);
out_clk_disable:
clk_disable_unprepare(priv->clk);
@@ -144,8 +201,11 @@ static int uniphier_pciephy_exit(struct phy *phy)
{
struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);
- uniphier_pciephy_assert(priv);
+ if (!priv->data->is_legacy)
+ uniphier_pciephy_assert(priv);
+ reset_control_assert(priv->rst_gio);
reset_control_assert(priv->rst);
+ clk_disable_unprepare(priv->clk_gio);
clk_disable_unprepare(priv->clk);
return 0;
@@ -163,7 +223,6 @@ static int uniphier_pciephy_probe(struct platform_device *pdev)
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct regmap *regmap;
- struct resource *res;
struct phy *phy;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -176,18 +235,36 @@ static int uniphier_pciephy_probe(struct platform_device *pdev)
priv->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(dev, res);
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
- priv->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(priv->clk))
- return PTR_ERR(priv->clk);
-
- priv->rst = devm_reset_control_get_shared(dev, NULL);
- if (IS_ERR(priv->rst))
- return PTR_ERR(priv->rst);
+ if (priv->data->is_legacy) {
+ priv->clk_gio = devm_clk_get(dev, "gio");
+ if (IS_ERR(priv->clk_gio))
+ return PTR_ERR(priv->clk_gio);
+
+ priv->rst_gio =
+ devm_reset_control_get_shared(dev, "gio");
+ if (IS_ERR(priv->rst_gio))
+ return PTR_ERR(priv->rst_gio);
+
+ priv->clk = devm_clk_get(dev, "link");
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ priv->rst = devm_reset_control_get_shared(dev, "link");
+ if (IS_ERR(priv->rst))
+ return PTR_ERR(priv->rst);
+ } else {
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ priv->rst = devm_reset_control_get_shared(dev, NULL);
+ if (IS_ERR(priv->rst))
+ return PTR_ERR(priv->rst);
+ }
phy = devm_phy_create(dev, dev->of_node, &uniphier_pciephy_ops);
if (IS_ERR(phy))
@@ -195,9 +272,8 @@ static int uniphier_pciephy_probe(struct platform_device *pdev)
regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"socionext,syscon");
- if (!IS_ERR(regmap) && priv->data->has_syscon)
- regmap_update_bits(regmap, SG_USBPCIESEL,
- SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE);
+ if (!IS_ERR(regmap) && priv->data->set_phymode)
+ priv->data->set_phymode(regmap);
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
@@ -205,16 +281,45 @@ static int uniphier_pciephy_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(phy_provider);
}
+static void uniphier_pciephy_ld20_setmode(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, SG_USBPCIESEL,
+ 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 = {
- .has_syscon = true,
+ .is_legacy = false,
+ .is_dual_phy = false,
+ .set_phymode = uniphier_pciephy_ld20_setmode,
};
static const struct uniphier_pciephy_soc_data uniphier_pxs3_data = {
- .has_syscon = false,
+ .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[] = {
{
+ .compatible = "socionext,uniphier-pro5-pcie-phy",
+ .data = &uniphier_pro5_data,
+ },
+ {
.compatible = "socionext,uniphier-ld20-pcie-phy",
.data = &uniphier_ld20_data,
},
@@ -222,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);
diff --git a/drivers/phy/socionext/phy-uniphier-usb3hs.c b/drivers/phy/socionext/phy-uniphier-usb3hs.c
index 50f379fc4e06..8c8673df0084 100644
--- a/drivers/phy/socionext/phy-uniphier-usb3hs.c
+++ b/drivers/phy/socionext/phy-uniphier-usb3hs.c
@@ -41,10 +41,12 @@
#define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) }
+#define RX_CHK_SYNC PHY_F(0, 5, 5) /* RX sync mode */
+#define RX_SYNC_SEL PHY_F(1, 1, 0) /* RX sync length */
#define LS_SLEW PHY_F(10, 6, 6) /* LS mode slew rate */
#define FS_LS_DRV PHY_F(10, 5, 5) /* FS/LS slew rate */
-#define MAX_PHY_PARAMS 2
+#define MAX_PHY_PARAMS 4
struct uniphier_u3hsphy_param {
struct {
@@ -66,13 +68,14 @@ struct uniphier_u3hsphy_trim_param {
struct uniphier_u3hsphy_priv {
struct device *dev;
void __iomem *base;
- struct clk *clk, *clk_parent, *clk_ext;
- struct reset_control *rst, *rst_parent;
+ struct clk *clk, *clk_parent, *clk_ext, *clk_parent_gio;
+ struct reset_control *rst, *rst_parent, *rst_parent_gio;
struct regulator *vbus;
const struct uniphier_u3hsphy_soc_data *data;
};
struct uniphier_u3hsphy_soc_data {
+ bool is_legacy;
int nparams;
const struct uniphier_u3hsphy_param param[MAX_PHY_PARAMS];
u32 config0;
@@ -256,11 +259,20 @@ static int uniphier_u3hsphy_init(struct phy *phy)
if (ret)
return ret;
- ret = reset_control_deassert(priv->rst_parent);
+ ret = clk_prepare_enable(priv->clk_parent_gio);
if (ret)
goto out_clk_disable;
- if (!priv->data->config0 && !priv->data->config1)
+ ret = reset_control_deassert(priv->rst_parent);
+ if (ret)
+ goto out_clk_gio_disable;
+
+ ret = reset_control_deassert(priv->rst_parent_gio);
+ if (ret)
+ goto out_rst_assert;
+
+ if ((priv->data->is_legacy)
+ || (!priv->data->config0 && !priv->data->config1))
return 0;
config0 = priv->data->config0;
@@ -280,6 +292,8 @@ static int uniphier_u3hsphy_init(struct phy *phy)
out_rst_assert:
reset_control_assert(priv->rst_parent);
+out_clk_gio_disable:
+ clk_disable_unprepare(priv->clk_parent_gio);
out_clk_disable:
clk_disable_unprepare(priv->clk_parent);
@@ -290,7 +304,9 @@ static int uniphier_u3hsphy_exit(struct phy *phy)
{
struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
+ reset_control_assert(priv->rst_parent_gio);
reset_control_assert(priv->rst_parent);
+ clk_disable_unprepare(priv->clk_parent_gio);
clk_disable_unprepare(priv->clk_parent);
return 0;
@@ -309,7 +325,6 @@ static int uniphier_u3hsphy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct uniphier_u3hsphy_priv *priv;
struct phy_provider *phy_provider;
- struct resource *res;
struct phy *phy;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -322,27 +337,38 @@ static int uniphier_u3hsphy_probe(struct platform_device *pdev)
priv->data->nparams > MAX_PHY_PARAMS))
return -EINVAL;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(dev, res);
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
- priv->clk = devm_clk_get(dev, "phy");
- if (IS_ERR(priv->clk))
- return PTR_ERR(priv->clk);
+ if (!priv->data->is_legacy) {
+ priv->clk = devm_clk_get(dev, "phy");
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ priv->clk_ext = devm_clk_get_optional(dev, "phy-ext");
+ if (IS_ERR(priv->clk_ext))
+ return PTR_ERR(priv->clk_ext);
+
+ priv->rst = devm_reset_control_get_shared(dev, "phy");
+ if (IS_ERR(priv->rst))
+ return PTR_ERR(priv->rst);
+
+ } else {
+ 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->clk_parent = devm_clk_get(dev, "link");
if (IS_ERR(priv->clk_parent))
return PTR_ERR(priv->clk_parent);
- priv->clk_ext = devm_clk_get_optional(dev, "phy-ext");
- if (IS_ERR(priv->clk_ext))
- return PTR_ERR(priv->clk_ext);
-
- priv->rst = devm_reset_control_get_shared(dev, "phy");
- if (IS_ERR(priv->rst))
- return PTR_ERR(priv->rst);
-
priv->rst_parent = devm_reset_control_get_shared(dev, "link");
if (IS_ERR(priv->rst_parent))
return PTR_ERR(priv->rst_parent);
@@ -364,13 +390,26 @@ static int uniphier_u3hsphy_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(phy_provider);
}
-static const struct uniphier_u3hsphy_soc_data uniphier_pxs2_data = {
+static const struct uniphier_u3hsphy_soc_data uniphier_pro5_data = {
+ .is_legacy = true,
.nparams = 0,
};
-static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = {
+static const struct uniphier_u3hsphy_soc_data uniphier_pxs2_data = {
+ .is_legacy = false,
.nparams = 2,
.param = {
+ { RX_CHK_SYNC, 1 },
+ { RX_SYNC_SEL, 1 },
+ },
+};
+
+static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = {
+ .is_legacy = false,
+ .nparams = 4,
+ .param = {
+ { RX_CHK_SYNC, 1 },
+ { RX_SYNC_SEL, 1 },
{ LS_SLEW, 1 },
{ FS_LS_DRV, 1 },
},
@@ -380,7 +419,12 @@ static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = {
};
static const struct uniphier_u3hsphy_soc_data uniphier_pxs3_data = {
- .nparams = 0,
+ .is_legacy = false,
+ .nparams = 2,
+ .param = {
+ { RX_CHK_SYNC, 1 },
+ { RX_SYNC_SEL, 1 },
+ },
.trim_func = uniphier_u3hsphy_trim_ld20,
.config0 = 0x92316680,
.config1 = 0x00000106,
@@ -388,6 +432,10 @@ static const struct uniphier_u3hsphy_soc_data uniphier_pxs3_data = {
static const struct of_device_id uniphier_u3hsphy_match[] = {
{
+ .compatible = "socionext,uniphier-pro5-usb3-hsphy",
+ .data = &uniphier_pro5_data,
+ },
+ {
.compatible = "socionext,uniphier-pxs2-usb3-hsphy",
.data = &uniphier_pxs2_data,
},
@@ -399,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);
diff --git a/drivers/phy/socionext/phy-uniphier-usb3ss.c b/drivers/phy/socionext/phy-uniphier-usb3ss.c
index ec231e40ef2a..f402ed8732fd 100644
--- a/drivers/phy/socionext/phy-uniphier-usb3ss.c
+++ b/drivers/phy/socionext/phy-uniphier-usb3ss.c
@@ -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);
@@ -215,7 +217,6 @@ static int uniphier_u3ssphy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct uniphier_u3ssphy_priv *priv;
struct phy_provider *phy_provider;
- struct resource *res;
struct phy *phy;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -228,8 +229,7 @@ static int uniphier_u3ssphy_probe(struct platform_device *pdev)
priv->data->nparams > MAX_PHY_PARAMS))
return -EINVAL;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(dev, res);
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
@@ -315,6 +315,10 @@ static const struct of_device_id uniphier_u3ssphy_match[] = {
.data = &uniphier_pro4_data,
},
{
+ .compatible = "socionext,uniphier-pro5-usb3-ssphy",
+ .data = &uniphier_pro4_data,
+ },
+ {
.compatible = "socionext,uniphier-pxs2-usb3-ssphy",
.data = &uniphier_pxs2_data,
},
@@ -326,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);
diff --git a/drivers/phy/st/Kconfig b/drivers/phy/st/Kconfig
index b32f44ff9033..3fc3d0781fb8 100644
--- a/drivers/phy/st/Kconfig
+++ b/drivers/phy/st/Kconfig
@@ -36,6 +36,7 @@ config PHY_STIH407_USB
config PHY_STM32_USBPHYC
tristate "STMicroelectronics STM32 USB HS PHY Controller driver"
depends on ARCH_STM32 || COMPILE_TEST
+ depends on COMMON_CLK
select GENERIC_PHY
help
Enable this to support the High-Speed USB transceivers that are part
diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c
index 56bdea4b0bd9..5bb9647b078f 100644
--- a/drivers/phy/st/phy-stm32-usbphyc.c
+++ b/drivers/phy/st/phy-stm32-usbphyc.c
@@ -7,16 +7,20 @@
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/delay.h>
-#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/reset.h>
+#include <linux/units.h>
#define STM32_USBPHYC_PLL 0x0
#define STM32_USBPHYC_MISC 0x8
+#define STM32_USBPHYC_MONITOR(X) (0x108 + ((X) * 0x100))
+#define STM32_USBPHYC_TUNE(X) (0x10C + ((X) * 0x100))
#define STM32_USBPHYC_VERSION 0x3F4
/* STM32_USBPHYC_PLL bit fields */
@@ -32,23 +36,96 @@
/* STM32_USBPHYC_MISC bit fields */
#define SWITHOST BIT(0)
-/* STM32_USBPHYC_VERSION bit fields */
-#define MINREV GENMASK(3, 0)
-#define MAJREV GENMASK(7, 4)
+/* STM32_USBPHYC_MONITOR bit fields */
+#define STM32_USBPHYC_MON_OUT GENMASK(3, 0)
+#define STM32_USBPHYC_MON_SEL GENMASK(8, 4)
+#define STM32_USBPHYC_MON_SEL_LOCKP 0x1F
+#define STM32_USBPHYC_MON_OUT_LOCKP BIT(3)
+
+/* STM32_USBPHYC_TUNE bit fields */
+#define INCURREN BIT(0)
+#define INCURRINT BIT(1)
+#define LFSCAPEN BIT(2)
+#define HSDRVSLEW BIT(3)
+#define HSDRVDCCUR BIT(4)
+#define HSDRVDCLEV BIT(5)
+#define HSDRVCURINCR BIT(6)
+#define FSDRVRFADJ BIT(7)
+#define HSDRVRFRED BIT(8)
+#define HSDRVCHKITRM GENMASK(12, 9)
+#define HSDRVCHKZTRM GENMASK(14, 13)
+#define OTPCOMP GENMASK(19, 15)
+#define SQLCHCTL GENMASK(21, 20)
+#define HDRXGNEQEN BIT(22)
+#define HSRXOFF GENMASK(24, 23)
+#define HSFALLPREEM BIT(25)
+#define SHTCCTCTLPROT BIT(26)
+#define STAGSEL BIT(27)
+
+enum boosting_vals {
+ BOOST_1000_UA = 1000,
+ BOOST_2000_UA = 2000,
+};
+
+enum dc_level_vals {
+ DC_NOMINAL,
+ DC_PLUS_5_TO_7_MV,
+ DC_PLUS_10_TO_14_MV,
+ DC_MINUS_5_TO_7_MV,
+ DC_MAX,
+};
-static const char * const supplies_names[] = {
- "vdda1v1", /* 1V1 */
- "vdda1v8", /* 1V8 */
+enum current_trim {
+ CUR_NOMINAL,
+ CUR_PLUS_1_56_PCT,
+ CUR_PLUS_3_12_PCT,
+ CUR_PLUS_4_68_PCT,
+ CUR_PLUS_6_24_PCT,
+ CUR_PLUS_7_8_PCT,
+ CUR_PLUS_9_36_PCT,
+ CUR_PLUS_10_92_PCT,
+ CUR_PLUS_12_48_PCT,
+ CUR_PLUS_14_04_PCT,
+ CUR_PLUS_15_6_PCT,
+ CUR_PLUS_17_16_PCT,
+ CUR_PLUS_19_01_PCT,
+ CUR_PLUS_20_58_PCT,
+ CUR_PLUS_22_16_PCT,
+ CUR_PLUS_23_73_PCT,
+ CUR_MAX,
};
-#define NUM_SUPPLIES ARRAY_SIZE(supplies_names)
+enum impedance_trim {
+ IMP_NOMINAL,
+ IMP_MINUS_2_OHMS,
+ IMP_MINUS_4_OMHS,
+ IMP_MINUS_6_OHMS,
+ IMP_MAX,
+};
+
+enum squelch_level {
+ SQLCH_NOMINAL,
+ SQLCH_PLUS_7_MV,
+ SQLCH_MINUS_5_MV,
+ SQLCH_PLUS_14_MV,
+ SQLCH_MAX,
+};
+
+enum rx_offset {
+ NO_RX_OFFSET,
+ RX_OFFSET_PLUS_5_MV,
+ RX_OFFSET_PLUS_10_MV,
+ RX_OFFSET_MINUS_5_MV,
+ RX_OFFSET_MAX,
+};
+
+/* STM32_USBPHYC_VERSION bit fields */
+#define MINREV GENMASK(3, 0)
+#define MAJREV GENMASK(7, 4)
-#define PLL_LOCK_TIME_US 100
-#define PLL_PWR_DOWN_TIME_US 5
#define PLL_FVCO_MHZ 2880
#define PLL_INFF_MIN_RATE_HZ 19200000
#define PLL_INFF_MAX_RATE_HZ 38400000
-#define HZ_PER_MHZ 1000000L
struct pll_params {
u8 ndiv;
@@ -58,9 +135,10 @@ struct pll_params {
struct stm32_usbphyc_phy {
struct phy *phy;
struct stm32_usbphyc *usbphyc;
- struct regulator_bulk_data supplies[NUM_SUPPLIES];
+ struct regulator *vbus;
u32 index;
bool active;
+ u32 tune;
};
struct stm32_usbphyc {
@@ -70,6 +148,10 @@ struct stm32_usbphyc {
struct reset_control *rst;
struct stm32_usbphyc_phy **phys;
int nphys;
+ struct regulator *vdda1v1;
+ struct regulator *vdda1v8;
+ atomic_t n_pll_cons;
+ struct clk_hw clk48_hw;
int switch_setup;
};
@@ -83,6 +165,41 @@ static inline void stm32_usbphyc_clr_bits(void __iomem *reg, u32 bits)
writel_relaxed(readl_relaxed(reg) & ~bits, reg);
}
+static int stm32_usbphyc_regulators_enable(struct stm32_usbphyc *usbphyc)
+{
+ int ret;
+
+ ret = regulator_enable(usbphyc->vdda1v1);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(usbphyc->vdda1v8);
+ if (ret)
+ goto vdda1v1_disable;
+
+ return 0;
+
+vdda1v1_disable:
+ regulator_disable(usbphyc->vdda1v1);
+
+ return ret;
+}
+
+static int stm32_usbphyc_regulators_disable(struct stm32_usbphyc *usbphyc)
+{
+ int ret;
+
+ ret = regulator_disable(usbphyc->vdda1v8);
+ if (ret)
+ return ret;
+
+ ret = regulator_disable(usbphyc->vdda1v1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static void stm32_usbphyc_get_pll_params(u32 clk_rate,
struct pll_params *pll_params)
{
@@ -142,83 +259,108 @@ static int stm32_usbphyc_pll_init(struct stm32_usbphyc *usbphyc)
return 0;
}
-static bool stm32_usbphyc_has_one_phy_active(struct stm32_usbphyc *usbphyc)
+static int __stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
{
- int i;
+ void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
+ u32 pllen;
+
+ stm32_usbphyc_clr_bits(pll_reg, PLLEN);
- for (i = 0; i < usbphyc->nphys; i++)
- if (usbphyc->phys[i]->active)
- return true;
+ /* Wait for minimum width of powerdown pulse (ENABLE = Low) */
+ if (readl_relaxed_poll_timeout(pll_reg, pllen, !(pllen & PLLEN), 5, 50))
+ dev_err(usbphyc->dev, "PLL not reset\n");
- return false;
+ return stm32_usbphyc_regulators_disable(usbphyc);
+}
+
+static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
+{
+ /* Check if a phy port is still active or clk48 in use */
+ if (atomic_dec_return(&usbphyc->n_pll_cons) > 0)
+ return 0;
+
+ return __stm32_usbphyc_pll_disable(usbphyc);
}
static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc)
{
void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
- bool pllen = (readl_relaxed(pll_reg) & PLLEN);
+ bool pllen = readl_relaxed(pll_reg) & PLLEN;
int ret;
- /* Check if one phy port has already configured the pll */
- if (pllen && stm32_usbphyc_has_one_phy_active(usbphyc))
+ /*
+ * Check if a phy port or clk48 prepare has configured the pll
+ * and ensure the PLL is enabled
+ */
+ if (atomic_inc_return(&usbphyc->n_pll_cons) > 1 && pllen)
return 0;
if (pllen) {
- stm32_usbphyc_clr_bits(pll_reg, PLLEN);
- /* Wait for minimum width of powerdown pulse (ENABLE = Low) */
- udelay(PLL_PWR_DOWN_TIME_US);
+ /*
+ * PLL shouldn't be enabled without known consumer,
+ * disable it and reinit n_pll_cons
+ */
+ dev_warn(usbphyc->dev, "PLL enabled without known consumers\n");
+
+ ret = __stm32_usbphyc_pll_disable(usbphyc);
+ if (ret)
+ goto dec_n_pll_cons;
}
+ ret = stm32_usbphyc_regulators_enable(usbphyc);
+ if (ret)
+ goto dec_n_pll_cons;
+
ret = stm32_usbphyc_pll_init(usbphyc);
if (ret)
- return ret;
+ goto reg_disable;
stm32_usbphyc_set_bits(pll_reg, PLLEN);
- /* Wait for maximum lock time */
- udelay(PLL_LOCK_TIME_US);
-
- if (!(readl_relaxed(pll_reg) & PLLEN)) {
- dev_err(usbphyc->dev, "PLLEN not set\n");
- return -EIO;
- }
-
return 0;
-}
-static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
-{
- void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
+reg_disable:
+ stm32_usbphyc_regulators_disable(usbphyc);
- /* Check if other phy port active */
- if (stm32_usbphyc_has_one_phy_active(usbphyc))
- return 0;
+dec_n_pll_cons:
+ atomic_dec(&usbphyc->n_pll_cons);
- stm32_usbphyc_clr_bits(pll_reg, PLLEN);
- /* Wait for minimum width of powerdown pulse (ENABLE = Low) */
- udelay(PLL_PWR_DOWN_TIME_US);
-
- if (readl_relaxed(pll_reg) & PLLEN) {
- dev_err(usbphyc->dev, "PLL not reset\n");
- return -EIO;
- }
-
- return 0;
+ return ret;
}
static int stm32_usbphyc_phy_init(struct phy *phy)
{
struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc;
+ u32 reg_mon = STM32_USBPHYC_MONITOR(usbphyc_phy->index);
+ u32 monsel = FIELD_PREP(STM32_USBPHYC_MON_SEL,
+ STM32_USBPHYC_MON_SEL_LOCKP);
+ u32 monout;
int ret;
ret = stm32_usbphyc_pll_enable(usbphyc);
if (ret)
return ret;
+ /* Check that PLL Lock input to PHY is High */
+ writel_relaxed(monsel, usbphyc->base + reg_mon);
+ ret = readl_relaxed_poll_timeout(usbphyc->base + reg_mon, monout,
+ (monout & STM32_USBPHYC_MON_OUT_LOCKP),
+ 100, 1000);
+ if (ret) {
+ dev_err(usbphyc->dev, "PLL Lock input to PHY is Low (val=%x)\n",
+ (u32)(monout & STM32_USBPHYC_MON_OUT));
+ goto pll_disable;
+ }
+
usbphyc_phy->active = true;
return 0;
+
+pll_disable:
+ stm32_usbphyc_pll_disable(usbphyc);
+
+ return ret;
}
static int stm32_usbphyc_phy_exit(struct phy *phy)
@@ -235,14 +377,20 @@ static int stm32_usbphyc_phy_power_on(struct phy *phy)
{
struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
- return regulator_bulk_enable(NUM_SUPPLIES, usbphyc_phy->supplies);
+ if (usbphyc_phy->vbus)
+ return regulator_enable(usbphyc_phy->vbus);
+
+ return 0;
}
static int stm32_usbphyc_phy_power_off(struct phy *phy)
{
struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
- return regulator_bulk_disable(NUM_SUPPLIES, usbphyc_phy->supplies);
+ if (usbphyc_phy->vbus)
+ return regulator_disable(usbphyc_phy->vbus);
+
+ return 0;
}
static const struct phy_ops stm32_usbphyc_phy_ops = {
@@ -253,6 +401,162 @@ static const struct phy_ops stm32_usbphyc_phy_ops = {
.owner = THIS_MODULE,
};
+static int stm32_usbphyc_clk48_prepare(struct clk_hw *hw)
+{
+ struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, clk48_hw);
+
+ return stm32_usbphyc_pll_enable(usbphyc);
+}
+
+static void stm32_usbphyc_clk48_unprepare(struct clk_hw *hw)
+{
+ struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, clk48_hw);
+
+ stm32_usbphyc_pll_disable(usbphyc);
+}
+
+static unsigned long stm32_usbphyc_clk48_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ return 48000000;
+}
+
+static const struct clk_ops usbphyc_clk48_ops = {
+ .prepare = stm32_usbphyc_clk48_prepare,
+ .unprepare = stm32_usbphyc_clk48_unprepare,
+ .recalc_rate = stm32_usbphyc_clk48_recalc_rate,
+};
+
+static void stm32_usbphyc_clk48_unregister(void *data)
+{
+ struct stm32_usbphyc *usbphyc = data;
+
+ of_clk_del_provider(usbphyc->dev->of_node);
+ clk_hw_unregister(&usbphyc->clk48_hw);
+}
+
+static int stm32_usbphyc_clk48_register(struct stm32_usbphyc *usbphyc)
+{
+ struct device_node *node = usbphyc->dev->of_node;
+ struct clk_init_data init = { };
+ int ret = 0;
+
+ init.name = "ck_usbo_48m";
+ init.ops = &usbphyc_clk48_ops;
+
+ usbphyc->clk48_hw.init = &init;
+
+ ret = clk_hw_register(usbphyc->dev, &usbphyc->clk48_hw);
+ if (ret)
+ return ret;
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &usbphyc->clk48_hw);
+ if (ret)
+ clk_hw_unregister(&usbphyc->clk48_hw);
+
+ return ret;
+}
+
+static void stm32_usbphyc_phy_tuning(struct stm32_usbphyc *usbphyc,
+ struct device_node *np, u32 index)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = usbphyc->phys[index];
+ u32 reg = STM32_USBPHYC_TUNE(index);
+ u32 otpcomp, val;
+ int ret;
+
+ /* Backup OTP compensation code */
+ otpcomp = FIELD_GET(OTPCOMP, readl_relaxed(usbphyc->base + reg));
+
+ ret = of_property_read_u32(np, "st,current-boost-microamp", &val);
+ if (ret != -EINVAL) {
+ if (!ret && (val == BOOST_1000_UA || val == BOOST_2000_UA)) {
+ val = (val == BOOST_2000_UA) ? 1 : 0;
+ usbphyc_phy->tune |= INCURREN | FIELD_PREP(INCURRINT, val);
+ } else {
+ dev_warn(usbphyc->dev, "phy%d: invalid st,current-boost-microamp\n", index);
+ }
+ }
+
+ if (!of_property_read_bool(np, "st,no-lsfs-fb-cap"))
+ usbphyc_phy->tune |= LFSCAPEN;
+
+ if (of_property_read_bool(np, "st,decrease-hs-slew-rate"))
+ usbphyc_phy->tune |= HSDRVSLEW;
+
+ ret = of_property_read_u32(np, "st,tune-hs-dc-level", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < DC_MAX) {
+ if (val == DC_MINUS_5_TO_7_MV) {/* Decreases HS driver DC level */
+ usbphyc_phy->tune |= HSDRVDCCUR;
+ } else if (val > 0) { /* Increases HS driver DC level */
+ val = (val == DC_PLUS_10_TO_14_MV) ? 1 : 0;
+ usbphyc_phy->tune |= HSDRVCURINCR | FIELD_PREP(HSDRVDCLEV, val);
+ }
+ } else {
+ dev_warn(usbphyc->dev, "phy%d: invalid st,tune-hs-dc-level\n", index);
+ }
+ }
+
+ if (of_property_read_bool(np, "st,enable-fs-rftime-tuning"))
+ usbphyc_phy->tune |= FSDRVRFADJ;
+
+ if (of_property_read_bool(np, "st,enable-hs-rftime-reduction"))
+ usbphyc_phy->tune |= HSDRVRFRED;
+
+ ret = of_property_read_u32(np, "st,trim-hs-current", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < CUR_MAX)
+ usbphyc_phy->tune |= FIELD_PREP(HSDRVCHKITRM, val);
+ else
+ dev_warn(usbphyc->dev, "phy%d: invalid st,trim-hs-current\n", index);
+ }
+
+ ret = of_property_read_u32(np, "st,trim-hs-impedance", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < IMP_MAX)
+ usbphyc_phy->tune |= FIELD_PREP(HSDRVCHKZTRM, val);
+ else
+ dev_warn(usbphyc->dev, "phy%d: invalid st,trim-hs-impedance\n", index);
+ }
+
+ ret = of_property_read_u32(np, "st,tune-squelch-level", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < SQLCH_MAX)
+ usbphyc_phy->tune |= FIELD_PREP(SQLCHCTL, val);
+ else
+ dev_warn(usbphyc->dev, "phy%d: invalid st,tune-squelch\n", index);
+ }
+
+ if (of_property_read_bool(np, "st,enable-hs-rx-gain-eq"))
+ usbphyc_phy->tune |= HDRXGNEQEN;
+
+ ret = of_property_read_u32(np, "st,tune-hs-rx-offset", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < RX_OFFSET_MAX)
+ usbphyc_phy->tune |= FIELD_PREP(HSRXOFF, val);
+ else
+ dev_warn(usbphyc->dev, "phy%d: invalid st,tune-hs-rx-offset\n", index);
+ }
+
+ if (of_property_read_bool(np, "st,no-hs-ftime-ctrl"))
+ usbphyc_phy->tune |= HSFALLPREEM;
+
+ if (!of_property_read_bool(np, "st,no-lsfs-sc"))
+ usbphyc_phy->tune |= SHTCCTCTLPROT;
+
+ if (of_property_read_bool(np, "st,enable-hs-tx-staggering"))
+ usbphyc_phy->tune |= STAGSEL;
+
+ /* Restore OTP compensation code */
+ usbphyc_phy->tune |= FIELD_PREP(OTPCOMP, otpcomp);
+
+ /*
+ * By default, if no st,xxx tuning property is used, usbphyc_phy->tune is equal to
+ * STM32_USBPHYC_TUNE reset value (LFSCAPEN | SHTCCTCTLPROT | OTPCOMP).
+ */
+ writel_relaxed(usbphyc_phy->tune, usbphyc->base + reg);
+}
+
static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc,
u32 utmi_switch)
{
@@ -311,9 +615,8 @@ static int stm32_usbphyc_probe(struct platform_device *pdev)
struct stm32_usbphyc *usbphyc;
struct device *dev = &pdev->dev;
struct device_node *child, *np = dev->of_node;
- struct resource *res;
struct phy_provider *phy_provider;
- u32 version;
+ u32 pllen, version;
int ret, port = 0;
usbphyc = devm_kzalloc(dev, sizeof(*usbphyc), GFP_KERNEL);
@@ -322,17 +625,13 @@ static int stm32_usbphyc_probe(struct platform_device *pdev)
usbphyc->dev = dev;
dev_set_drvdata(dev, usbphyc);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- usbphyc->base = devm_ioremap_resource(dev, res);
+ usbphyc->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(usbphyc->base))
return PTR_ERR(usbphyc->base);
- usbphyc->clk = devm_clk_get(dev, 0);
- if (IS_ERR(usbphyc->clk)) {
- ret = PTR_ERR(usbphyc->clk);
- dev_err(dev, "clk get failed: %d\n", ret);
- return ret;
- }
+ usbphyc->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(usbphyc->clk))
+ return dev_err_probe(dev, PTR_ERR(usbphyc->clk), "clk get_failed\n");
ret = clk_prepare_enable(usbphyc->clk);
if (ret) {
@@ -340,11 +639,28 @@ static int stm32_usbphyc_probe(struct platform_device *pdev)
return ret;
}
- usbphyc->rst = devm_reset_control_get(dev, 0);
+ usbphyc->rst = devm_reset_control_get(dev, NULL);
if (!IS_ERR(usbphyc->rst)) {
reset_control_assert(usbphyc->rst);
udelay(2);
reset_control_deassert(usbphyc->rst);
+ } else {
+ ret = PTR_ERR(usbphyc->rst);
+ if (ret == -EPROBE_DEFER)
+ goto clk_disable;
+
+ stm32_usbphyc_clr_bits(usbphyc->base + STM32_USBPHYC_PLL, PLLEN);
+ }
+
+ /*
+ * Wait for minimum width of powerdown pulse (ENABLE = Low):
+ * we have to ensure the PLL is disabled before phys initialization.
+ */
+ if (readl_relaxed_poll_timeout(usbphyc->base + STM32_USBPHYC_PLL,
+ pllen, !(pllen & PLLEN), 5, 50)) {
+ dev_warn(usbphyc->dev, "PLL not reset\n");
+ ret = -EPROBE_DEFER;
+ goto clk_disable;
}
usbphyc->switch_setup = -EINVAL;
@@ -356,11 +672,24 @@ static int stm32_usbphyc_probe(struct platform_device *pdev)
goto clk_disable;
}
+ usbphyc->vdda1v1 = devm_regulator_get(dev, "vdda1v1");
+ if (IS_ERR(usbphyc->vdda1v1)) {
+ 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 = dev_err_probe(dev, PTR_ERR(usbphyc->vdda1v8),
+ "failed to get vdda1v8 supply\n");
+ goto clk_disable;
+ }
+
for_each_child_of_node(np, child) {
struct stm32_usbphyc_phy *usbphyc_phy;
struct phy *phy;
u32 index;
- int i;
phy = devm_phy_create(dev, child, &stm32_usbphyc_phy_ops);
if (IS_ERR(phy)) {
@@ -378,21 +707,11 @@ static int stm32_usbphyc_probe(struct platform_device *pdev)
goto put_child;
}
- for (i = 0; i < NUM_SUPPLIES; i++)
- usbphyc_phy->supplies[i].supply = supplies_names[i];
-
- ret = devm_regulator_bulk_get(&phy->dev, NUM_SUPPLIES,
- usbphyc_phy->supplies);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&phy->dev,
- "failed to get regulators: %d\n", ret);
- goto put_child;
- }
-
ret = of_property_read_u32(child, "reg", &index);
if (ret || index > usbphyc->nphys) {
dev_err(&phy->dev, "invalid reg property: %d\n", ret);
+ if (!ret)
+ ret = -EINVAL;
goto put_child;
}
@@ -405,6 +724,17 @@ static int stm32_usbphyc_probe(struct platform_device *pdev)
usbphyc->phys[port]->index = index;
usbphyc->phys[port]->active = false;
+ usbphyc->phys[port]->vbus = devm_regulator_get_optional(&phy->dev, "vbus");
+ if (IS_ERR(usbphyc->phys[port]->vbus)) {
+ ret = PTR_ERR(usbphyc->phys[port]->vbus);
+ if (ret == -EPROBE_DEFER)
+ goto put_child;
+ usbphyc->phys[port]->vbus = NULL;
+ }
+
+ /* Configure phy tuning */
+ stm32_usbphyc_phy_tuning(usbphyc, child, index);
+
port++;
}
@@ -416,6 +746,12 @@ static int stm32_usbphyc_probe(struct platform_device *pdev)
goto clk_disable;
}
+ ret = stm32_usbphyc_clk48_register(usbphyc);
+ if (ret) {
+ dev_err(dev, "failed to register ck_usbo_48m clock: %d\n", ret);
+ goto clk_disable;
+ }
+
version = readl_relaxed(usbphyc->base + STM32_USBPHYC_VERSION);
dev_info(dev, "registered rev:%lu.%lu\n",
FIELD_GET(MAJREV, version), FIELD_GET(MINREV, version));
@@ -433,12 +769,39 @@ clk_disable:
static int stm32_usbphyc_remove(struct platform_device *pdev)
{
struct stm32_usbphyc *usbphyc = dev_get_drvdata(&pdev->dev);
+ int port;
+
+ /* Ensure PHYs are not active, to allow PLL disabling */
+ for (port = 0; port < usbphyc->nphys; port++)
+ if (usbphyc->phys[port]->active)
+ stm32_usbphyc_phy_exit(usbphyc->phys[port]->phy);
+
+ stm32_usbphyc_clk48_unregister(usbphyc);
clk_disable_unprepare(usbphyc->clk);
return 0;
}
+static int __maybe_unused stm32_usbphyc_resume(struct device *dev)
+{
+ struct stm32_usbphyc *usbphyc = dev_get_drvdata(dev);
+ struct stm32_usbphyc_phy *usbphyc_phy;
+ int port;
+
+ if (usbphyc->switch_setup >= 0)
+ stm32_usbphyc_switch_setup(usbphyc, usbphyc->switch_setup);
+
+ for (port = 0; port < usbphyc->nphys; port++) {
+ usbphyc_phy = usbphyc->phys[port];
+ writel_relaxed(usbphyc_phy->tune, usbphyc->base + STM32_USBPHYC_TUNE(port));
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stm32_usbphyc_pm_ops, NULL, stm32_usbphyc_resume);
+
static const struct of_device_id stm32_usbphyc_of_match[] = {
{ .compatible = "st,stm32mp1-usbphyc", },
{ },
@@ -451,6 +814,7 @@ static struct platform_driver stm32_usbphyc_driver = {
.driver = {
.of_match_table = stm32_usbphyc_of_match,
.name = "stm32-usbphyc",
+ .pm = &stm32_usbphyc_pm_ops,
}
};
module_platform_driver(stm32_usbphyc_driver);
diff --git a/drivers/phy/sunplus/Kconfig b/drivers/phy/sunplus/Kconfig
new file mode 100644
index 000000000000..3bd3cfb53a63
--- /dev/null
+++ b/drivers/phy/sunplus/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config PHY_SUNPLUS_USB
+ tristate "Sunplus SP7021 USB 2.0 PHY driver"
+ depends on OF && (SOC_SP7021 || COMPILE_TEST)
+ select GENERIC_PHY
+ help
+ Enable this to support the USB 2.0 PHY on Sunplus SP7021
+ SoC. The USB 2.0 PHY controller supports battery charger
+ and synchronous signals, various power down modes including
+ operating, partial and suspend modes, and high-speed,
+ full-speed and low-speed data transfer.
diff --git a/drivers/phy/sunplus/Makefile b/drivers/phy/sunplus/Makefile
new file mode 100644
index 000000000000..71754d5cb545
--- /dev/null
+++ b/drivers/phy/sunplus/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PHY_SUNPLUS_USB) += phy-sunplus-usb2.o
diff --git a/drivers/phy/sunplus/phy-sunplus-usb2.c b/drivers/phy/sunplus/phy-sunplus-usb2.c
new file mode 100644
index 000000000000..e827b79f6d49
--- /dev/null
+++ b/drivers/phy/sunplus/phy-sunplus-usb2.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Sunplus SP7021 USB 2.0 phy driver
+ *
+ * Copyright (C) 2022 Sunplus Technology Inc., All rights reserved.
+ *
+ * Note 1 : non-posted write command for the registers accesses of
+ * Sunplus SP7021.
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#define HIGH_MASK_BITS GENMASK(31, 16)
+#define LOW_MASK_BITS GENMASK(15, 0)
+#define OTP_DISC_LEVEL_DEFAULT 0xd
+
+/* GROUP UPHY */
+#define CONFIG1 0x4
+#define J_HS_TX_PWRSAV BIT(5)
+#define CONFIG3 0xc
+#define J_FORCE_DISC_ON BIT(5)
+#define J_DEBUG_CTRL_ADDR_MACRO BIT(0)
+#define CONFIG7 0x1c
+#define J_DISC 0X1f
+#define CONFIG9 0x24
+#define J_ECO_PATH BIT(6)
+#define CONFIG16 0x40
+#define J_TBCWAIT_MASK GENMASK(6, 5)
+#define J_TBCWAIT_1P1_MS FIELD_PREP(J_TBCWAIT_MASK, 0)
+#define J_TVDM_SRC_DIS_MASK GENMASK(4, 3)
+#define J_TVDM_SRC_DIS_8P2_MS FIELD_PREP(J_TVDM_SRC_DIS_MASK, 3)
+#define J_TVDM_SRC_EN_MASK GENMASK(2, 1)
+#define J_TVDM_SRC_EN_1P6_MS FIELD_PREP(J_TVDM_SRC_EN_MASK, 0)
+#define J_BC_EN BIT(0)
+#define CONFIG17 0x44
+#define IBG_TRIM0_MASK GENMASK(7, 5)
+#define IBG_TRIM0_SSLVHT FIELD_PREP(IBG_TRIM0_MASK, 4)
+#define J_VDATREE_TRIM_MASK GENMASK(4, 1)
+#define J_VDATREE_TRIM_DEFAULT FIELD_PREP(J_VDATREE_TRIM_MASK, 9)
+#define CONFIG23 0x5c
+#define PROB_MASK GENMASK(5, 3)
+#define PROB FIELD_PREP(PROB_MASK, 7)
+
+/* GROUP MOON4 */
+#define UPHY_CONTROL0 0x0
+#define UPHY_CONTROL1 0x4
+#define UPHY_CONTROL2 0x8
+#define MO1_UPHY_RX_CLK_SEL BIT(6)
+#define MASK_MO1_UPHY_RX_CLK_SEL BIT(6 + 16)
+#define UPHY_CONTROL3 0xc
+#define MO1_UPHY_PLL_POWER_OFF_SEL BIT(7)
+#define MASK_MO1_UPHY_PLL_POWER_OFF_SEL BIT(7 + 16)
+#define MO1_UPHY_PLL_POWER_OFF BIT(3)
+#define MASK_UPHY_PLL_POWER_OFF BIT(3 + 16)
+
+struct sp_usbphy {
+ struct device *dev;
+ struct resource *phy_res_mem;
+ struct resource *moon4_res_mem;
+ struct reset_control *rstc;
+ struct clk *phy_clk;
+ void __iomem *phy_regs;
+ void __iomem *moon4_regs;
+ u32 disc_vol_addr_off;
+};
+
+static int update_disc_vol(struct sp_usbphy *usbphy)
+{
+ struct nvmem_cell *cell;
+ char *disc_name = "disc_vol";
+ ssize_t otp_l = 0;
+ char *otp_v;
+ u32 val, set;
+
+ cell = nvmem_cell_get(usbphy->dev, disc_name);
+ if (IS_ERR_OR_NULL(cell)) {
+ if (PTR_ERR(cell) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ }
+
+ otp_v = nvmem_cell_read(cell, &otp_l);
+ nvmem_cell_put(cell);
+
+ if (!IS_ERR(otp_v)) {
+ set = *(otp_v + 1);
+ set = (set << (sizeof(char) * 8)) | *otp_v;
+ set = (set >> usbphy->disc_vol_addr_off) & J_DISC;
+ }
+
+ if (IS_ERR(otp_v) || set == 0)
+ set = OTP_DISC_LEVEL_DEFAULT;
+
+ val = readl(usbphy->phy_regs + CONFIG7);
+ val = (val & ~J_DISC) | set;
+ writel(val, usbphy->phy_regs + CONFIG7);
+
+ return 0;
+}
+
+static int sp_uphy_init(struct phy *phy)
+{
+ struct sp_usbphy *usbphy = phy_get_drvdata(phy);
+ u32 val;
+ int ret;
+
+ ret = clk_prepare_enable(usbphy->phy_clk);
+ if (ret)
+ goto err_clk;
+
+ ret = reset_control_deassert(usbphy->rstc);
+ if (ret)
+ goto err_reset;
+
+ /* Default value modification */
+ writel(HIGH_MASK_BITS | 0x4002, usbphy->moon4_regs + UPHY_CONTROL0);
+ writel(HIGH_MASK_BITS | 0x8747, usbphy->moon4_regs + UPHY_CONTROL1);
+
+ /* disconnect voltage */
+ ret = update_disc_vol(usbphy);
+ if (ret < 0)
+ return ret;
+
+ /* board uphy 0 internal register modification for tid certification */
+ val = readl(usbphy->phy_regs + CONFIG9);
+ val &= ~(J_ECO_PATH);
+ writel(val, usbphy->phy_regs + CONFIG9);
+
+ val = readl(usbphy->phy_regs + CONFIG1);
+ val &= ~(J_HS_TX_PWRSAV);
+ writel(val, usbphy->phy_regs + CONFIG1);
+
+ val = readl(usbphy->phy_regs + CONFIG23);
+ val = (val & ~PROB) | PROB;
+ writel(val, usbphy->phy_regs + CONFIG23);
+
+ /* port 0 uphy clk fix */
+ writel(MASK_MO1_UPHY_RX_CLK_SEL | MO1_UPHY_RX_CLK_SEL,
+ usbphy->moon4_regs + UPHY_CONTROL2);
+
+ /* battery charger */
+ writel(J_TBCWAIT_1P1_MS | J_TVDM_SRC_DIS_8P2_MS | J_TVDM_SRC_EN_1P6_MS | J_BC_EN,
+ usbphy->phy_regs + CONFIG16);
+ writel(IBG_TRIM0_SSLVHT | J_VDATREE_TRIM_DEFAULT, usbphy->phy_regs + CONFIG17);
+
+ /* chirp mode */
+ writel(J_FORCE_DISC_ON | J_DEBUG_CTRL_ADDR_MACRO, usbphy->phy_regs + CONFIG3);
+
+ return 0;
+
+err_reset:
+ reset_control_assert(usbphy->rstc);
+err_clk:
+ clk_disable_unprepare(usbphy->phy_clk);
+
+ return ret;
+}
+
+static int sp_uphy_power_on(struct phy *phy)
+{
+ struct sp_usbphy *usbphy = phy_get_drvdata(phy);
+ u32 pll_pwr_on, pll_pwr_off;
+
+ /* PLL power off/on twice */
+ pll_pwr_off = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS)
+ | MO1_UPHY_PLL_POWER_OFF_SEL | MO1_UPHY_PLL_POWER_OFF;
+ pll_pwr_on = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS)
+ | MO1_UPHY_PLL_POWER_OFF_SEL;
+
+ writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off,
+ usbphy->moon4_regs + UPHY_CONTROL3);
+ mdelay(1);
+ writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_on,
+ usbphy->moon4_regs + UPHY_CONTROL3);
+ mdelay(1);
+ writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off,
+ usbphy->moon4_regs + UPHY_CONTROL3);
+ mdelay(1);
+ writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_on,
+ usbphy->moon4_regs + UPHY_CONTROL3);
+ mdelay(1);
+ writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | 0x0,
+ usbphy->moon4_regs + UPHY_CONTROL3);
+
+ return 0;
+}
+
+static int sp_uphy_power_off(struct phy *phy)
+{
+ struct sp_usbphy *usbphy = phy_get_drvdata(phy);
+ u32 pll_pwr_off;
+
+ pll_pwr_off = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS)
+ | MO1_UPHY_PLL_POWER_OFF_SEL | MO1_UPHY_PLL_POWER_OFF;
+
+ writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off,
+ usbphy->moon4_regs + UPHY_CONTROL3);
+ mdelay(1);
+ writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | 0x0,
+ usbphy->moon4_regs + UPHY_CONTROL3);
+
+ return 0;
+}
+
+static int sp_uphy_exit(struct phy *phy)
+{
+ struct sp_usbphy *usbphy = phy_get_drvdata(phy);
+
+ reset_control_assert(usbphy->rstc);
+ clk_disable_unprepare(usbphy->phy_clk);
+
+ return 0;
+}
+
+static const struct phy_ops sp_uphy_ops = {
+ .init = sp_uphy_init,
+ .power_on = sp_uphy_power_on,
+ .power_off = sp_uphy_power_off,
+ .exit = sp_uphy_exit,
+};
+
+static const struct of_device_id sp_uphy_dt_ids[] = {
+ {.compatible = "sunplus,sp7021-usb2-phy", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sp_uphy_dt_ids);
+
+static int sp_usb_phy_probe(struct platform_device *pdev)
+{
+ struct sp_usbphy *usbphy;
+ struct phy_provider *phy_provider;
+ struct phy *phy;
+ int ret;
+
+ usbphy = devm_kzalloc(&pdev->dev, sizeof(*usbphy), GFP_KERNEL);
+ if (!usbphy)
+ return -ENOMEM;
+
+ usbphy->dev = &pdev->dev;
+
+ usbphy->phy_res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
+ usbphy->phy_regs = devm_ioremap_resource(&pdev->dev, usbphy->phy_res_mem);
+ if (IS_ERR(usbphy->phy_regs))
+ return PTR_ERR(usbphy->phy_regs);
+
+ usbphy->moon4_res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "moon4");
+ usbphy->moon4_regs = devm_ioremap(&pdev->dev, usbphy->moon4_res_mem->start,
+ resource_size(usbphy->moon4_res_mem));
+ if (!usbphy->moon4_regs)
+ return -ENOMEM;
+
+ usbphy->phy_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(usbphy->phy_clk))
+ return PTR_ERR(usbphy->phy_clk);
+
+ usbphy->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(usbphy->rstc))
+ return PTR_ERR(usbphy->rstc);
+
+ of_property_read_u32(pdev->dev.of_node, "sunplus,disc-vol-addr-off",
+ &usbphy->disc_vol_addr_off);
+
+ phy = devm_phy_create(&pdev->dev, NULL, &sp_uphy_ops);
+ if (IS_ERR(phy)) {
+ ret = -PTR_ERR(phy);
+ return ret;
+ }
+
+ phy_set_drvdata(phy, usbphy);
+ phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver sunplus_usb_phy_driver = {
+ .probe = sp_usb_phy_probe,
+ .driver = {
+ .name = "sunplus-usb2-phy",
+ .of_match_table = sp_uphy_dt_ids,
+ },
+};
+module_platform_driver(sunplus_usb_phy_driver);
+
+MODULE_AUTHOR("Vincent Shih <vincent.shih@sunplus.com>");
+MODULE_DESCRIPTION("Sunplus USB 2.0 phy driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/tegra/Kconfig b/drivers/phy/tegra/Kconfig
index f9817c3ae85f..c591c958f1eb 100644
--- a/drivers/phy/tegra/Kconfig
+++ b/drivers/phy/tegra/Kconfig
@@ -1,7 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
config PHY_TEGRA_XUSB
tristate "NVIDIA Tegra XUSB pad controller driver"
- depends on ARCH_TEGRA
+ depends on ARCH_TEGRA && USB_SUPPORT
+ select USB_COMMON
+ select USB_CONN_GPIO
+ select USB_PHY
help
Choose this option if you have an NVIDIA Tegra SoC.
diff --git a/drivers/phy/tegra/Makefile b/drivers/phy/tegra/Makefile
index 320dd389f34d..89b84067cb4c 100644
--- a/drivers/phy/tegra/Makefile
+++ b/drivers/phy/tegra/Makefile
@@ -6,4 +6,5 @@ phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_124_SOC) += xusb-tegra124.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_SOC) += xusb-tegra124.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_210_SOC) += xusb-tegra210.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_186_SOC) += xusb-tegra186.o
+phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_194_SOC) += xusb-tegra186.o
obj-$(CONFIG_PHY_TEGRA194_P2U) += phy-tegra194-p2u.o
diff --git a/drivers/phy/tegra/phy-tegra194-p2u.c b/drivers/phy/tegra/phy-tegra194-p2u.c
index 7042bed9feaa..1415ca71de38 100644
--- a/drivers/phy/tegra/phy-tegra194-p2u.c
+++ b/drivers/phy/tegra/phy-tegra194-p2u.c
@@ -2,7 +2,7 @@
/*
* P2U (PIPE to UPHY) driver for Tegra T194 SoC
*
- * Copyright (C) 2019 NVIDIA Corporation.
+ * Copyright (C) 2019-2022 NVIDIA Corporation.
*
* Author: Vidya Sagar <vidyas@nvidia.com>
*/
@@ -14,6 +14,9 @@
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
+#define P2U_CONTROL_CMN 0x74
+#define P2U_CONTROL_CMN_SKP_SIZE_PROTECTION_EN BIT(20)
+
#define P2U_PERIODIC_EQ_CTRL_GEN3 0xc0
#define P2U_PERIODIC_EQ_CTRL_GEN3_PERIODIC_EQ_EN BIT(0)
#define P2U_PERIODIC_EQ_CTRL_GEN3_INIT_PRESET_EQ_TRAIN_EN BIT(1)
@@ -24,8 +27,17 @@
#define P2U_RX_DEBOUNCE_TIME_DEBOUNCE_TIMER_MASK 0xffff
#define P2U_RX_DEBOUNCE_TIME_DEBOUNCE_TIMER_VAL 160
+#define P2U_DIR_SEARCH_CTRL 0xd4
+#define P2U_DIR_SEARCH_CTRL_GEN4_FINE_GRAIN_SEARCH_TWICE BIT(18)
+
+struct tegra_p2u_of_data {
+ bool one_dir_search;
+};
+
struct tegra_p2u {
void __iomem *base;
+ bool skip_sz_protection_en; /* Needed to support two retimers */
+ struct tegra_p2u_of_data *of_data;
};
static inline void p2u_writel(struct tegra_p2u *phy, const u32 value,
@@ -44,6 +56,12 @@ static int tegra_p2u_power_on(struct phy *x)
struct tegra_p2u *phy = phy_get_drvdata(x);
u32 val;
+ if (phy->skip_sz_protection_en) {
+ val = p2u_readl(phy, P2U_CONTROL_CMN);
+ val |= P2U_CONTROL_CMN_SKP_SIZE_PROTECTION_EN;
+ p2u_writel(phy, val, P2U_CONTROL_CMN);
+ }
+
val = p2u_readl(phy, P2U_PERIODIC_EQ_CTRL_GEN3);
val &= ~P2U_PERIODIC_EQ_CTRL_GEN3_PERIODIC_EQ_EN;
val |= P2U_PERIODIC_EQ_CTRL_GEN3_INIT_PRESET_EQ_TRAIN_EN;
@@ -58,6 +76,12 @@ static int tegra_p2u_power_on(struct phy *x)
val |= P2U_RX_DEBOUNCE_TIME_DEBOUNCE_TIMER_VAL;
p2u_writel(phy, val, P2U_RX_DEBOUNCE_TIME);
+ if (phy->of_data->one_dir_search) {
+ val = p2u_readl(phy, P2U_DIR_SEARCH_CTRL);
+ val &= ~P2U_DIR_SEARCH_CTRL_GEN4_FINE_GRAIN_SEARCH_TWICE;
+ p2u_writel(phy, val, P2U_DIR_SEARCH_CTRL);
+ }
+
return 0;
}
@@ -72,17 +96,24 @@ static int tegra_p2u_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct phy *generic_phy;
struct tegra_p2u *phy;
- struct resource *res;
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
return -ENOMEM;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctl");
- phy->base = devm_ioremap_resource(dev, res);
+ phy->of_data =
+ (struct tegra_p2u_of_data *)of_device_get_match_data(dev);
+ if (!phy->of_data)
+ return -EINVAL;
+
+ phy->base = devm_platform_ioremap_resource_byname(pdev, "ctl");
if (IS_ERR(phy->base))
return PTR_ERR(phy->base);
+ phy->skip_sz_protection_en =
+ of_property_read_bool(dev->of_node,
+ "nvidia,skip-sz-protect-en");
+
platform_set_drvdata(pdev, phy);
generic_phy = devm_phy_create(dev, NULL, &ops);
@@ -98,9 +129,22 @@ static int tegra_p2u_probe(struct platform_device *pdev)
return 0;
}
+static const struct tegra_p2u_of_data tegra194_p2u_of_data = {
+ .one_dir_search = false,
+};
+
+static const struct tegra_p2u_of_data tegra234_p2u_of_data = {
+ .one_dir_search = true,
+};
+
static const struct of_device_id tegra_p2u_id_table[] = {
{
.compatible = "nvidia,tegra194-p2u",
+ .data = &tegra194_p2u_of_data,
+ },
+ {
+ .compatible = "nvidia,tegra234-p2u",
+ .data = &tegra234_p2u_of_data,
},
{}
};
diff --git a/drivers/phy/tegra/xusb-tegra124.c b/drivers/phy/tegra/xusb-tegra124.c
index 98d84920c676..db56c7fbe60b 100644
--- a/drivers/phy/tegra/xusb-tegra124.c
+++ b/drivers/phy/tegra/xusb-tegra124.c
@@ -1422,6 +1422,8 @@ tegra124_usb2_port_map(struct tegra_xusb_port *port)
}
static const struct tegra_xusb_port_ops tegra124_usb2_port_ops = {
+ .release = tegra_xusb_usb2_port_release,
+ .remove = tegra_xusb_usb2_port_remove,
.enable = tegra124_usb2_port_enable,
.disable = tegra124_usb2_port_disable,
.map = tegra124_usb2_port_map,
@@ -1443,6 +1445,7 @@ tegra124_ulpi_port_map(struct tegra_xusb_port *port)
}
static const struct tegra_xusb_port_ops tegra124_ulpi_port_ops = {
+ .release = tegra_xusb_ulpi_port_release,
.enable = tegra124_ulpi_port_enable,
.disable = tegra124_ulpi_port_disable,
.map = tegra124_ulpi_port_map,
@@ -1464,6 +1467,7 @@ tegra124_hsic_port_map(struct tegra_xusb_port *port)
}
static const struct tegra_xusb_port_ops tegra124_hsic_port_ops = {
+ .release = tegra_xusb_hsic_port_release,
.enable = tegra124_hsic_port_enable,
.disable = tegra124_hsic_port_disable,
.map = tegra124_hsic_port_map,
@@ -1647,6 +1651,8 @@ tegra124_usb3_port_map(struct tegra_xusb_port *port)
}
static const struct tegra_xusb_port_ops tegra124_usb3_port_ops = {
+ .release = tegra_xusb_usb3_port_release,
+ .remove = tegra_xusb_usb3_port_remove,
.enable = tegra124_usb3_port_enable,
.disable = tegra124_usb3_port_disable,
.map = tegra124_usb3_port_map,
diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c
index 84c27394c181..0996ede63387 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2016-2022, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/delay.h>
@@ -63,6 +63,10 @@
#define SSPX_ELPG_CLAMP_EN(x) BIT(0 + (x) * 3)
#define SSPX_ELPG_CLAMP_EN_EARLY(x) BIT(1 + (x) * 3)
#define SSPX_ELPG_VCORE_DOWN(x) BIT(2 + (x) * 3)
+#define XUSB_PADCTL_SS_PORT_CFG 0x2c
+#define PORTX_SPEED_SUPPORT_SHIFT(x) ((x) * 4)
+#define PORTX_SPEED_SUPPORT_MASK (0x3)
+#define PORT_SPEED_SUPPORT_GEN1 (0x0)
#define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x88 + (x) * 0x40)
#define HS_CURR_LEVEL(x) ((x) & 0x3f)
@@ -109,6 +113,117 @@
#define ID_OVERRIDE_FLOATING ID_OVERRIDE(8)
#define ID_OVERRIDE_GROUNDED ID_OVERRIDE(0)
+/* XUSB AO registers */
+#define XUSB_AO_USB_DEBOUNCE_DEL (0x4)
+#define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 4)
+#define UTMIP_LINE_DEB_CNT(x) ((x) & 0xf)
+
+#define XUSB_AO_UTMIP_TRIGGERS(x) (0x40 + (x) * 4)
+#define CLR_WALK_PTR BIT(0)
+#define CAP_CFG BIT(1)
+#define CLR_WAKE_ALARM BIT(3)
+
+#define XUSB_AO_UHSIC_TRIGGERS(x) (0x60 + (x) * 4)
+#define HSIC_CLR_WALK_PTR BIT(0)
+#define HSIC_CLR_WAKE_ALARM BIT(3)
+#define HSIC_CAP_CFG BIT(4)
+
+#define XUSB_AO_UTMIP_SAVED_STATE(x) (0x70 + (x) * 4)
+#define SPEED(x) ((x) & 0x3)
+#define UTMI_HS SPEED(0)
+#define UTMI_FS SPEED(1)
+#define UTMI_LS SPEED(2)
+#define UTMI_RST SPEED(3)
+
+#define XUSB_AO_UHSIC_SAVED_STATE(x) (0x90 + (x) * 4)
+#define MODE(x) ((x) & 0x1)
+#define MODE_HS MODE(0)
+#define MODE_RST MODE(1)
+
+#define XUSB_AO_UTMIP_SLEEPWALK_CFG(x) (0xd0 + (x) * 4)
+#define XUSB_AO_UHSIC_SLEEPWALK_CFG(x) (0xf0 + (x) * 4)
+#define FAKE_USBOP_VAL BIT(0)
+#define FAKE_USBON_VAL BIT(1)
+#define FAKE_USBOP_EN BIT(2)
+#define FAKE_USBON_EN BIT(3)
+#define FAKE_STROBE_VAL BIT(0)
+#define FAKE_DATA_VAL BIT(1)
+#define FAKE_STROBE_EN BIT(2)
+#define FAKE_DATA_EN BIT(3)
+#define WAKE_WALK_EN BIT(14)
+#define MASTER_ENABLE BIT(15)
+#define LINEVAL_WALK_EN BIT(16)
+#define WAKE_VAL(x) (((x) & 0xf) << 17)
+#define WAKE_VAL_NONE WAKE_VAL(12)
+#define WAKE_VAL_ANY WAKE_VAL(15)
+#define WAKE_VAL_DS10 WAKE_VAL(2)
+#define LINE_WAKEUP_EN BIT(21)
+#define MASTER_CFG_SEL BIT(22)
+
+#define XUSB_AO_UTMIP_SLEEPWALK(x) (0x100 + (x) * 4)
+/* phase A */
+#define USBOP_RPD_A BIT(0)
+#define USBON_RPD_A BIT(1)
+#define AP_A BIT(4)
+#define AN_A BIT(5)
+#define HIGHZ_A BIT(6)
+/* phase B */
+#define USBOP_RPD_B BIT(8)
+#define USBON_RPD_B BIT(9)
+#define AP_B BIT(12)
+#define AN_B BIT(13)
+#define HIGHZ_B BIT(14)
+/* phase C */
+#define USBOP_RPD_C BIT(16)
+#define USBON_RPD_C BIT(17)
+#define AP_C BIT(20)
+#define AN_C BIT(21)
+#define HIGHZ_C BIT(22)
+/* phase D */
+#define USBOP_RPD_D BIT(24)
+#define USBON_RPD_D BIT(25)
+#define AP_D BIT(28)
+#define AN_D BIT(29)
+#define HIGHZ_D BIT(30)
+
+#define XUSB_AO_UHSIC_SLEEPWALK(x) (0x120 + (x) * 4)
+/* phase A */
+#define RPD_STROBE_A BIT(0)
+#define RPD_DATA0_A BIT(1)
+#define RPU_STROBE_A BIT(2)
+#define RPU_DATA0_A BIT(3)
+/* phase B */
+#define RPD_STROBE_B BIT(8)
+#define RPD_DATA0_B BIT(9)
+#define RPU_STROBE_B BIT(10)
+#define RPU_DATA0_B BIT(11)
+/* phase C */
+#define RPD_STROBE_C BIT(16)
+#define RPD_DATA0_C BIT(17)
+#define RPU_STROBE_C BIT(18)
+#define RPU_DATA0_C BIT(19)
+/* phase D */
+#define RPD_STROBE_D BIT(24)
+#define RPD_DATA0_D BIT(25)
+#define RPU_STROBE_D BIT(26)
+#define RPU_DATA0_D BIT(27)
+
+#define XUSB_AO_UTMIP_PAD_CFG(x) (0x130 + (x) * 4)
+#define FSLS_USE_XUSB_AO BIT(3)
+#define TRK_CTRL_USE_XUSB_AO BIT(4)
+#define RPD_CTRL_USE_XUSB_AO BIT(5)
+#define RPU_USE_XUSB_AO BIT(6)
+#define VREG_USE_XUSB_AO BIT(7)
+#define USBOP_VAL_PD BIT(8)
+#define USBON_VAL_PD BIT(9)
+#define E_DPD_OVRD_EN BIT(10)
+#define E_DPD_OVRD_VAL BIT(11)
+
+#define XUSB_AO_UHSIC_PAD_CFG(x) (0x150 + (x) * 4)
+#define STROBE_VAL_PD BIT(0)
+#define DATA0_VAL_PD BIT(1)
+#define USE_XUSB_AO BIT(4)
+
#define TEGRA186_LANE(_name, _offset, _shift, _mask, _type) \
{ \
.name = _name, \
@@ -126,16 +241,37 @@ struct tegra_xusb_fuse_calibration {
u32 rpd_ctrl;
};
+struct tegra186_xusb_padctl_context {
+ u32 vbus_id;
+ u32 usb2_pad_mux;
+ u32 usb2_port_cap;
+ u32 ss_port_cap;
+};
+
struct tegra186_xusb_padctl {
struct tegra_xusb_padctl base;
+ void __iomem *ao_regs;
struct tegra_xusb_fuse_calibration calib;
/* UTMI bias and tracking */
struct clk *usb2_trk_clk;
unsigned int bias_pad_enable;
+
+ /* padctl context */
+ struct tegra186_xusb_padctl_context context;
};
+static inline void ao_writel(struct tegra186_xusb_padctl *priv, u32 value, unsigned int offset)
+{
+ writel(value, priv->ao_regs + offset);
+}
+
+static inline u32 ao_readl(struct tegra186_xusb_padctl *priv, unsigned int offset)
+{
+ return readl(priv->ao_regs + offset);
+}
+
static inline struct tegra186_xusb_padctl *
to_tegra186_xusb_padctl(struct tegra_xusb_padctl *padctl)
{
@@ -176,9 +312,264 @@ static void tegra186_usb2_lane_remove(struct tegra_xusb_lane *lane)
kfree(usb2);
}
+static int tegra186_utmi_enable_phy_sleepwalk(struct tegra_xusb_lane *lane,
+ enum usb_device_speed speed)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ /* ensure sleepwalk logic is disabled */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~MASTER_ENABLE;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* ensure sleepwalk logics are in low power mode */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value |= MASTER_CFG_SEL;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* set debounce time */
+ value = ao_readl(priv, XUSB_AO_USB_DEBOUNCE_DEL);
+ value &= ~UTMIP_LINE_DEB_CNT(~0);
+ value |= UTMIP_LINE_DEB_CNT(1);
+ ao_writel(priv, value, XUSB_AO_USB_DEBOUNCE_DEL);
+
+ /* ensure fake events of sleepwalk logic are desiabled */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~(FAKE_USBOP_VAL | FAKE_USBON_VAL |
+ FAKE_USBOP_EN | FAKE_USBON_EN);
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* ensure wake events of sleepwalk logic are not latched */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~LINE_WAKEUP_EN;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* disable wake event triggers of sleepwalk logic */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~WAKE_VAL(~0);
+ value |= WAKE_VAL_NONE;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* power down the line state detectors of the pad */
+ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index));
+ value |= (USBOP_VAL_PD | USBON_VAL_PD);
+ ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index));
+
+ /* save state per speed */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SAVED_STATE(index));
+ value &= ~SPEED(~0);
+
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ value |= UTMI_HS;
+ break;
+
+ case USB_SPEED_FULL:
+ value |= UTMI_FS;
+ break;
+
+ case USB_SPEED_LOW:
+ value |= UTMI_LS;
+ break;
+
+ default:
+ value |= UTMI_RST;
+ break;
+ }
+
+ ao_writel(priv, value, XUSB_AO_UTMIP_SAVED_STATE(index));
+
+ /* enable the trigger of the sleepwalk logic */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value |= LINEVAL_WALK_EN;
+ value &= ~WAKE_WALK_EN;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* reset the walk pointer and clear the alarm of the sleepwalk logic,
+ * as well as capture the configuration of the USB2.0 pad
+ */
+ value = ao_readl(priv, XUSB_AO_UTMIP_TRIGGERS(index));
+ value |= (CLR_WALK_PTR | CLR_WAKE_ALARM | CAP_CFG);
+ ao_writel(priv, value, XUSB_AO_UTMIP_TRIGGERS(index));
+
+ /* setup the pull-ups and pull-downs of the signals during the four
+ * stages of sleepwalk.
+ * if device is connected, program sleepwalk logic to maintain a J and
+ * keep driving K upon seeing remote wake.
+ */
+ value = USBOP_RPD_A | USBOP_RPD_B | USBOP_RPD_C | USBOP_RPD_D;
+ value |= USBON_RPD_A | USBON_RPD_B | USBON_RPD_C | USBON_RPD_D;
+
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ case USB_SPEED_FULL:
+ /* J state: D+/D- = high/low, K state: D+/D- = low/high */
+ value |= HIGHZ_A;
+ value |= AP_A;
+ value |= AN_B | AN_C | AN_D;
+ break;
+
+ case USB_SPEED_LOW:
+ /* J state: D+/D- = low/high, K state: D+/D- = high/low */
+ value |= HIGHZ_A;
+ value |= AN_A;
+ value |= AP_B | AP_C | AP_D;
+ break;
+
+ default:
+ value |= HIGHZ_A | HIGHZ_B | HIGHZ_C | HIGHZ_D;
+ break;
+ }
+
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK(index));
+
+ /* power up the line state detectors of the pad */
+ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index));
+ value &= ~(USBOP_VAL_PD | USBON_VAL_PD);
+ ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index));
+
+ usleep_range(150, 200);
+
+ /* switch the electric control of the USB2.0 pad to XUSB_AO */
+ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index));
+ value |= FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | RPD_CTRL_USE_XUSB_AO |
+ RPU_USE_XUSB_AO | VREG_USE_XUSB_AO;
+ ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index));
+
+ /* set the wake signaling trigger events */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~WAKE_VAL(~0);
+ value |= WAKE_VAL_ANY;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* enable the wake detection */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value |= MASTER_ENABLE | LINE_WAKEUP_EN;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra186_utmi_disable_phy_sleepwalk(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ /* disable the wake detection */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~(MASTER_ENABLE | LINE_WAKEUP_EN);
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* switch the electric control of the USB2.0 pad to XUSB vcore logic */
+ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index));
+ value &= ~(FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | RPD_CTRL_USE_XUSB_AO |
+ RPU_USE_XUSB_AO | VREG_USE_XUSB_AO);
+ ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index));
+
+ /* disable wake event triggers of sleepwalk logic */
+ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+ value &= ~WAKE_VAL(~0);
+ value |= WAKE_VAL_NONE;
+ ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index));
+
+ /* power down the line state detectors of the port */
+ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index));
+ value |= USBOP_VAL_PD | USBON_VAL_PD;
+ ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index));
+
+ /* clear alarm of the sleepwalk logic */
+ value = ao_readl(priv, XUSB_AO_UTMIP_TRIGGERS(index));
+ value |= CLR_WAKE_ALARM;
+ ao_writel(priv, value, XUSB_AO_UTMIP_TRIGGERS(index));
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra186_utmi_enable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra186_utmi_disable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static bool tegra186_utmi_phy_remote_wake_detected(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(index)) &&
+ (value & USB2_PORT_WAKEUP_EVENT(index)))
+ return true;
+
+ return false;
+}
+
static const struct tegra_xusb_lane_ops tegra186_usb2_lane_ops = {
.probe = tegra186_usb2_lane_probe,
.remove = tegra186_usb2_lane_remove,
+ .enable_phy_sleepwalk = tegra186_utmi_enable_phy_sleepwalk,
+ .disable_phy_sleepwalk = tegra186_utmi_disable_phy_sleepwalk,
+ .enable_phy_wake = tegra186_utmi_enable_phy_wake,
+ .disable_phy_wake = tegra186_utmi_disable_phy_wake,
+ .remote_wake_detected = tegra186_utmi_phy_remote_wake_detected,
};
static void tegra186_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl)
@@ -247,7 +638,7 @@ static void tegra186_utmi_bias_pad_power_off(struct tegra_xusb_padctl *padctl)
mutex_unlock(&padctl->lock);
}
-static void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy)
+static void tegra186_utmi_pad_power_on(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
@@ -265,6 +656,8 @@ static void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy)
return;
}
+ dev_dbg(dev, "power on UTMI pad %u\n", index);
+
tegra186_utmi_bias_pad_power_on(padctl);
udelay(2);
@@ -278,7 +671,7 @@ static void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy)
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
}
-static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
+static void tegra186_utmi_pad_power_down(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
@@ -288,6 +681,8 @@ static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
if (!phy)
return;
+ dev_dbg(padctl->dev, "power down UTMI pad %u\n", index);
+
value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
value |= USB2_OTG_PD;
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
@@ -301,6 +696,97 @@ static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
tegra186_utmi_bias_pad_power_off(padctl);
}
+static int tegra186_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl,
+ bool status)
+{
+ u32 value;
+
+ dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear");
+
+ value = padctl_readl(padctl, USB2_VBUS_ID);
+
+ if (status) {
+ value |= VBUS_OVERRIDE;
+ value &= ~ID_OVERRIDE(~0);
+ value |= ID_OVERRIDE_FLOATING;
+ } else {
+ value &= ~VBUS_OVERRIDE;
+ }
+
+ padctl_writel(padctl, value, USB2_VBUS_ID);
+
+ return 0;
+}
+
+static int tegra186_xusb_padctl_id_override(struct tegra_xusb_padctl *padctl,
+ bool status)
+{
+ u32 value;
+
+ dev_dbg(padctl->dev, "%s id override\n", status ? "set" : "clear");
+
+ value = padctl_readl(padctl, USB2_VBUS_ID);
+
+ if (status) {
+ if (value & VBUS_OVERRIDE) {
+ value &= ~VBUS_OVERRIDE;
+ padctl_writel(padctl, value, USB2_VBUS_ID);
+ usleep_range(1000, 2000);
+
+ value = padctl_readl(padctl, USB2_VBUS_ID);
+ }
+
+ value &= ~ID_OVERRIDE(~0);
+ value |= ID_OVERRIDE_GROUNDED;
+ } else {
+ value &= ~ID_OVERRIDE(~0);
+ value |= ID_OVERRIDE_FLOATING;
+ }
+
+ padctl_writel(padctl, value, USB2_VBUS_ID);
+
+ return 0;
+}
+
+static int tegra186_utmi_phy_set_mode(struct phy *phy, enum phy_mode mode,
+ int submode)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra_xusb_usb2_port *port = tegra_xusb_find_usb2_port(padctl,
+ lane->index);
+ int err = 0;
+
+ mutex_lock(&padctl->lock);
+
+ dev_dbg(&port->base.dev, "%s: mode %d", __func__, mode);
+
+ if (mode == PHY_MODE_USB_OTG) {
+ if (submode == USB_ROLE_HOST) {
+ tegra186_xusb_padctl_id_override(padctl, true);
+
+ err = regulator_enable(port->supply);
+ } else if (submode == USB_ROLE_DEVICE) {
+ tegra186_xusb_padctl_vbus_override(padctl, true);
+ } else if (submode == USB_ROLE_NONE) {
+ /*
+ * When port is peripheral only or role transitions to
+ * USB_ROLE_NONE from USB_ROLE_DEVICE, regulator is not
+ * enabled.
+ */
+ if (regulator_is_enabled(port->supply))
+ regulator_disable(port->supply);
+
+ tegra186_xusb_padctl_id_override(padctl, false);
+ tegra186_xusb_padctl_vbus_override(padctl, false);
+ }
+ }
+
+ mutex_unlock(&padctl->lock);
+
+ return err;
+}
+
static int tegra186_utmi_phy_power_on(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
@@ -367,15 +853,14 @@ static int tegra186_utmi_phy_power_on(struct phy *phy)
value |= RPD_CTRL(priv->calib.rpd_ctrl);
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
- /* TODO: pad power saving */
- tegra_phy_xusb_utmi_pad_power_on(phy);
+ tegra186_utmi_pad_power_on(phy);
+
return 0;
}
static int tegra186_utmi_phy_power_off(struct phy *phy)
{
- /* TODO: pad power saving */
- tegra_phy_xusb_utmi_pad_power_down(phy);
+ tegra186_utmi_pad_power_down(phy);
return 0;
}
@@ -439,6 +924,7 @@ static const struct phy_ops utmi_phy_ops = {
.exit = tegra186_utmi_phy_exit,
.power_on = tegra186_utmi_phy_power_on,
.power_off = tegra186_utmi_phy_power_off,
+ .set_mode = tegra186_utmi_phy_set_mode,
.owner = THIS_MODULE,
};
@@ -503,19 +989,6 @@ static const char * const tegra186_usb2_functions[] = {
"xusb",
};
-static const struct tegra_xusb_lane_soc tegra186_usb2_lanes[] = {
- TEGRA186_LANE("usb2-0", 0, 0, 0, usb2),
- TEGRA186_LANE("usb2-1", 0, 0, 0, usb2),
- TEGRA186_LANE("usb2-2", 0, 0, 0, usb2),
-};
-
-static const struct tegra_xusb_pad_soc tegra186_usb2_pad = {
- .name = "usb2",
- .num_lanes = ARRAY_SIZE(tegra186_usb2_lanes),
- .lanes = tegra186_usb2_lanes,
- .ops = &tegra186_usb2_pad_ops,
-};
-
static int tegra186_usb2_port_enable(struct tegra_xusb_port *port)
{
return 0;
@@ -532,6 +1005,8 @@ tegra186_usb2_port_map(struct tegra_xusb_port *port)
}
static const struct tegra_xusb_port_ops tegra186_usb2_port_ops = {
+ .release = tegra_xusb_usb2_port_release,
+ .remove = tegra_xusb_usb2_port_remove,
.enable = tegra186_usb2_port_enable,
.disable = tegra186_usb2_port_disable,
.map = tegra186_usb2_port_map,
@@ -571,10 +1046,128 @@ static void tegra186_usb3_lane_remove(struct tegra_xusb_lane *lane)
kfree(usb3);
}
+static int tegra186_usb3_enable_phy_sleepwalk(struct tegra_xusb_lane *lane,
+ enum usb_device_speed speed)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+ value |= SSPX_ELPG_CLAMP_EN_EARLY(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+ value |= SSPX_ELPG_CLAMP_EN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+ usleep_range(250, 350);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra186_usb3_disable_phy_sleepwalk(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+ value &= ~SSPX_ELPG_CLAMP_EN_EARLY(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
+ value &= ~SSPX_ELPG_CLAMP_EN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra186_usb3_enable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= SS_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= SS_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra186_usb3_disable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= SS_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static bool tegra186_usb3_phy_remote_wake_detected(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(index)) && (value & SS_PORT_WAKEUP_EVENT(index)))
+ return true;
+
+ return false;
+}
+
static const struct tegra_xusb_lane_ops tegra186_usb3_lane_ops = {
.probe = tegra186_usb3_lane_probe,
.remove = tegra186_usb3_lane_remove,
+ .enable_phy_sleepwalk = tegra186_usb3_enable_phy_sleepwalk,
+ .disable_phy_sleepwalk = tegra186_usb3_disable_phy_sleepwalk,
+ .enable_phy_wake = tegra186_usb3_enable_phy_wake,
+ .disable_phy_wake = tegra186_usb3_disable_phy_wake,
+ .remote_wake_detected = tegra186_usb3_phy_remote_wake_detected,
};
+
static int tegra186_usb3_port_enable(struct tegra_xusb_port *port)
{
return 0;
@@ -591,6 +1184,8 @@ tegra186_usb3_port_map(struct tegra_xusb_port *port)
}
static const struct tegra_xusb_port_ops tegra186_usb3_port_ops = {
+ .release = tegra_xusb_usb3_port_release,
+ .remove = tegra_xusb_usb3_port_remove,
.enable = tegra186_usb3_port_enable,
.disable = tegra186_usb3_port_disable,
.map = tegra186_usb3_port_map,
@@ -635,6 +1230,15 @@ static int tegra186_usb3_phy_power_on(struct phy *phy)
padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_CAP);
+ if (padctl->soc->supports_gen2 && port->disable_gen2) {
+ value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CFG);
+ value &= ~(PORTX_SPEED_SUPPORT_MASK <<
+ PORTX_SPEED_SUPPORT_SHIFT(index));
+ value |= (PORT_SPEED_SUPPORT_GEN1 <<
+ PORTX_SPEED_SUPPORT_SHIFT(index));
+ padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_CFG);
+ }
+
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
value &= ~SSPX_ELPG_VCORE_DOWN(index);
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
@@ -765,27 +1369,6 @@ static const char * const tegra186_usb3_functions[] = {
"xusb",
};
-static const struct tegra_xusb_lane_soc tegra186_usb3_lanes[] = {
- TEGRA186_LANE("usb3-0", 0, 0, 0, usb3),
- TEGRA186_LANE("usb3-1", 0, 0, 0, usb3),
- TEGRA186_LANE("usb3-2", 0, 0, 0, usb3),
-};
-
-static const struct tegra_xusb_pad_soc tegra186_usb3_pad = {
- .name = "usb3",
- .num_lanes = ARRAY_SIZE(tegra186_usb3_lanes),
- .lanes = tegra186_usb3_lanes,
- .ops = &tegra186_usb3_pad_ops,
-};
-
-static const struct tegra_xusb_pad_soc * const tegra186_pads[] = {
- &tegra186_usb2_pad,
- &tegra186_usb3_pad,
-#if 0 /* TODO implement */
- &tegra186_hsic_pad,
-#endif
-};
-
static int
tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
{
@@ -801,10 +1384,9 @@ tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
return -ENOMEM;
err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
- if (err) {
- dev_err(dev, "failed to read calibration fuse: %d\n", err);
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "failed to read calibration fuse\n");
dev_dbg(dev, "FUSE_USB_CALIB_0 %#x\n", value);
@@ -836,7 +1418,9 @@ static struct tegra_xusb_padctl *
tegra186_xusb_padctl_probe(struct device *dev,
const struct tegra_xusb_padctl_soc *soc)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct tegra186_xusb_padctl *priv;
+ struct resource *res;
int err;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -846,6 +1430,11 @@ tegra186_xusb_padctl_probe(struct device *dev,
priv->base.dev = dev;
priv->base.soc = soc;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ao");
+ priv->ao_regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->ao_regs))
+ return ERR_CAST(priv->ao_regs);
+
err = tegra186_xusb_read_fuse_calibration(priv);
if (err < 0)
return ERR_PTR(err);
@@ -853,38 +1442,55 @@ tegra186_xusb_padctl_probe(struct device *dev,
return &priv->base;
}
-static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl)
+static void tegra186_xusb_padctl_save(struct tegra_xusb_padctl *padctl)
{
+ struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
+
+ priv->context.vbus_id = padctl_readl(padctl, USB2_VBUS_ID);
+ priv->context.usb2_pad_mux = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
+ priv->context.usb2_port_cap = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
+ priv->context.ss_port_cap = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CAP);
}
-static int tegra186_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl,
- bool status)
+static void tegra186_xusb_padctl_restore(struct tegra_xusb_padctl *padctl)
{
- u32 value;
+ struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
- dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear");
+ padctl_writel(padctl, priv->context.usb2_pad_mux, XUSB_PADCTL_USB2_PAD_MUX);
+ padctl_writel(padctl, priv->context.usb2_port_cap, XUSB_PADCTL_USB2_PORT_CAP);
+ padctl_writel(padctl, priv->context.ss_port_cap, XUSB_PADCTL_SS_PORT_CAP);
+ padctl_writel(padctl, priv->context.vbus_id, USB2_VBUS_ID);
+}
- value = padctl_readl(padctl, USB2_VBUS_ID);
+static int tegra186_xusb_padctl_suspend_noirq(struct tegra_xusb_padctl *padctl)
+{
+ tegra186_xusb_padctl_save(padctl);
- if (status) {
- value |= VBUS_OVERRIDE;
- value &= ~ID_OVERRIDE(~0);
- value |= ID_OVERRIDE_FLOATING;
- } else {
- value &= ~VBUS_OVERRIDE;
- }
+ return 0;
+}
- padctl_writel(padctl, value, USB2_VBUS_ID);
+static int tegra186_xusb_padctl_resume_noirq(struct tegra_xusb_padctl *padctl)
+{
+ tegra186_xusb_padctl_restore(padctl);
return 0;
}
+static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl)
+{
+}
+
static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = {
.probe = tegra186_xusb_padctl_probe,
.remove = tegra186_xusb_padctl_remove,
+ .suspend_noirq = tegra186_xusb_padctl_suspend_noirq,
+ .resume_noirq = tegra186_xusb_padctl_resume_noirq,
.vbus_override = tegra186_xusb_padctl_vbus_override,
+ .utmi_pad_power_on = tegra186_utmi_pad_power_on,
+ .utmi_pad_power_down = tegra186_utmi_pad_power_down,
};
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
static const char * const tegra186_xusb_padctl_supply_names[] = {
"avdd-pll-erefeut",
"avdd-usb",
@@ -892,6 +1498,40 @@ static const char * const tegra186_xusb_padctl_supply_names[] = {
"vddio-hsic",
};
+static const struct tegra_xusb_lane_soc tegra186_usb2_lanes[] = {
+ TEGRA186_LANE("usb2-0", 0, 0, 0, usb2),
+ TEGRA186_LANE("usb2-1", 0, 0, 0, usb2),
+ TEGRA186_LANE("usb2-2", 0, 0, 0, usb2),
+};
+
+static const struct tegra_xusb_pad_soc tegra186_usb2_pad = {
+ .name = "usb2",
+ .num_lanes = ARRAY_SIZE(tegra186_usb2_lanes),
+ .lanes = tegra186_usb2_lanes,
+ .ops = &tegra186_usb2_pad_ops,
+};
+
+static const struct tegra_xusb_lane_soc tegra186_usb3_lanes[] = {
+ TEGRA186_LANE("usb3-0", 0, 0, 0, usb3),
+ TEGRA186_LANE("usb3-1", 0, 0, 0, usb3),
+ TEGRA186_LANE("usb3-2", 0, 0, 0, usb3),
+};
+
+static const struct tegra_xusb_pad_soc tegra186_usb3_pad = {
+ .name = "usb3",
+ .num_lanes = ARRAY_SIZE(tegra186_usb3_lanes),
+ .lanes = tegra186_usb3_lanes,
+ .ops = &tegra186_usb3_pad_ops,
+};
+
+static const struct tegra_xusb_pad_soc * const tegra186_pads[] = {
+ &tegra186_usb2_pad,
+ &tegra186_usb3_pad,
+#if 0 /* TODO implement */
+ &tegra186_hsic_pad,
+#endif
+};
+
const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = {
.num_pads = ARRAY_SIZE(tegra186_pads),
.pads = tegra186_pads,
@@ -916,6 +1556,67 @@ const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = {
.num_supplies = ARRAY_SIZE(tegra186_xusb_padctl_supply_names),
};
EXPORT_SYMBOL_GPL(tegra186_xusb_padctl_soc);
+#endif
+
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC)
+static const char * const tegra194_xusb_padctl_supply_names[] = {
+ "avdd-usb",
+ "vclamp-usb",
+};
+
+static const struct tegra_xusb_lane_soc tegra194_usb2_lanes[] = {
+ TEGRA186_LANE("usb2-0", 0, 0, 0, usb2),
+ TEGRA186_LANE("usb2-1", 0, 0, 0, usb2),
+ TEGRA186_LANE("usb2-2", 0, 0, 0, usb2),
+ TEGRA186_LANE("usb2-3", 0, 0, 0, usb2),
+};
+
+static const struct tegra_xusb_pad_soc tegra194_usb2_pad = {
+ .name = "usb2",
+ .num_lanes = ARRAY_SIZE(tegra194_usb2_lanes),
+ .lanes = tegra194_usb2_lanes,
+ .ops = &tegra186_usb2_pad_ops,
+};
+
+static const struct tegra_xusb_lane_soc tegra194_usb3_lanes[] = {
+ TEGRA186_LANE("usb3-0", 0, 0, 0, usb3),
+ TEGRA186_LANE("usb3-1", 0, 0, 0, usb3),
+ TEGRA186_LANE("usb3-2", 0, 0, 0, usb3),
+ TEGRA186_LANE("usb3-3", 0, 0, 0, usb3),
+};
+
+static const struct tegra_xusb_pad_soc tegra194_usb3_pad = {
+ .name = "usb3",
+ .num_lanes = ARRAY_SIZE(tegra194_usb3_lanes),
+ .lanes = tegra194_usb3_lanes,
+ .ops = &tegra186_usb3_pad_ops,
+};
+
+static const struct tegra_xusb_pad_soc * const tegra194_pads[] = {
+ &tegra194_usb2_pad,
+ &tegra194_usb3_pad,
+};
+
+const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc = {
+ .num_pads = ARRAY_SIZE(tegra194_pads),
+ .pads = tegra194_pads,
+ .ports = {
+ .usb2 = {
+ .ops = &tegra186_usb2_port_ops,
+ .count = 4,
+ },
+ .usb3 = {
+ .ops = &tegra186_usb3_port_ops,
+ .count = 4,
+ },
+ },
+ .ops = &tegra186_xusb_padctl_ops,
+ .supply_names = tegra194_xusb_padctl_supply_names,
+ .num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
+ .supports_gen2 = true,
+};
+EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc);
+#endif
MODULE_AUTHOR("JC Kuo <jckuo@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra186 XUSB Pad Controller driver");
diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c
index 394913bb2f20..eedfc7c2cc05 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
* Copyright (C) 2015 Google, Inc.
*/
@@ -11,8 +11,10 @@
#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
@@ -52,6 +54,20 @@
#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5))
#define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7
+#define XUSB_PADCTL_ELPG_PROGRAM_0 0x20
+#define USB2_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x))
+#define USB2_PORT_WAKEUP_EVENT(x) BIT((x) + 7)
+#define SS_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 14)
+#define SS_PORT_WAKEUP_EVENT(x) BIT((x) + 21)
+#define USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 28)
+#define USB2_HSIC_PORT_WAKEUP_EVENT(x) BIT((x) + 30)
+#define ALL_WAKE_EVENTS ( \
+ USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \
+ USB2_PORT_WAKEUP_EVENT(2) | USB2_PORT_WAKEUP_EVENT(3) | \
+ SS_PORT_WAKEUP_EVENT(0) | SS_PORT_WAKEUP_EVENT(1) | \
+ SS_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(3) | \
+ USB2_HSIC_PORT_WAKEUP_EVENT(0))
+
#define XUSB_PADCTL_ELPG_PROGRAM1 0x024
#define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31)
#define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30)
@@ -90,6 +106,8 @@
#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1)
#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0)
+#define RPD_CTRL(x) (((x) & 0x1f) << 26)
+#define RPD_CTRL_VALUE(x) (((x) >> 26) & 0x1f)
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11)
@@ -108,6 +126,8 @@
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e
+#define TCTRL_VALUE(x) (((x) & 0x3f) >> 0)
+#define PCTRL_VALUE(x) (((x) >> 6) & 0x3f)
#define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20)
#define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18)
@@ -198,6 +218,18 @@
#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_TERM_EN BIT(18)
#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_MODE_OVRD BIT(13)
+#define XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(x) (0x464 + (x) * 0x40)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ BIT(0)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD BIT(1)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK GENMASK(5, 4)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL GENMASK(5, 4)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD BIT(24)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ BIT(8)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD BIT(9)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK GENMASK(13, 12)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL GENMASK(13, 12)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD BIT(25)
+
#define XUSB_PADCTL_UPHY_PLL_S0_CTL1 0x860
#define XUSB_PADCTL_UPHY_PLL_S0_CTL2 0x864
@@ -209,6 +241,7 @@
#define XUSB_PADCTL_UPHY_PLL_S0_CTL8 0x87c
#define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL1 0x960
+#define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL2 0x964
#define XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(x) (0xa60 + (x) * 0x40)
#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT 16
@@ -236,6 +269,143 @@
#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT 18
#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK 0xf
#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8
+#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0
+
+/* USB2 SLEEPWALK registers */
+#define UTMIP(_port, _offset1, _offset2) \
+ (((_port) <= 2) ? (_offset1) : (_offset2))
+
+#define PMC_UTMIP_UHSIC_SLEEP_CFG(x) UTMIP(x, 0x1fc, 0x4d0)
+#define UTMIP_MASTER_ENABLE(x) UTMIP(x, BIT(8 * (x)), BIT(0))
+#define UTMIP_FSLS_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 1), \
+ BIT(1))
+#define UTMIP_PCTRL_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 2), \
+ BIT(2))
+#define UTMIP_TCTRL_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 3), \
+ BIT(3))
+#define UTMIP_WAKE_VAL(_port, _value) (((_value) & 0xf) << \
+ (UTMIP(_port, 8 * (_port) + 4, 4)))
+#define UTMIP_WAKE_VAL_NONE(_port) UTMIP_WAKE_VAL(_port, 12)
+#define UTMIP_WAKE_VAL_ANY(_port) UTMIP_WAKE_VAL(_port, 15)
+
+#define PMC_UTMIP_UHSIC_SLEEP_CFG1 (0x4d0)
+#define UTMIP_RPU_SWITC_LOW_USE_PMC_PX(x) BIT((x) + 8)
+#define UTMIP_RPD_CTRL_USE_PMC_PX(x) BIT((x) + 16)
+
+#define PMC_UTMIP_MASTER_CONFIG (0x274)
+#define UTMIP_PWR(x) UTMIP(x, BIT(x), BIT(4))
+#define UHSIC_PWR BIT(3)
+
+#define PMC_USB_DEBOUNCE_DEL (0xec)
+#define DEBOUNCE_VAL(x) (((x) & 0xffff) << 0)
+#define UTMIP_LINE_DEB_CNT(x) (((x) & 0xf) << 16)
+#define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 20)
+
+#define PMC_UTMIP_UHSIC_FAKE(x) UTMIP(x, 0x218, 0x294)
+#define UTMIP_FAKE_USBOP_VAL(x) UTMIP(x, BIT(4 * (x)), BIT(8))
+#define UTMIP_FAKE_USBON_VAL(x) UTMIP(x, BIT(4 * (x) + 1), \
+ BIT(9))
+#define UTMIP_FAKE_USBOP_EN(x) UTMIP(x, BIT(4 * (x) + 2), \
+ BIT(10))
+#define UTMIP_FAKE_USBON_EN(x) UTMIP(x, BIT(4 * (x) + 3), \
+ BIT(11))
+
+#define PMC_UTMIP_UHSIC_SLEEPWALK_CFG(x) UTMIP(x, 0x200, 0x288)
+#define UTMIP_LINEVAL_WALK_EN(x) UTMIP(x, BIT(8 * (x) + 7), \
+ BIT(15))
+
+#define PMC_USB_AO (0xf0)
+#define USBOP_VAL_PD(x) UTMIP(x, BIT(4 * (x)), BIT(20))
+#define USBON_VAL_PD(x) UTMIP(x, BIT(4 * (x) + 1), \
+ BIT(21))
+#define STROBE_VAL_PD BIT(12)
+#define DATA0_VAL_PD BIT(13)
+#define DATA1_VAL_PD BIT(24)
+
+#define PMC_UTMIP_UHSIC_SAVED_STATE(x) UTMIP(x, 0x1f0, 0x280)
+#define SPEED(_port, _value) (((_value) & 0x3) << \
+ (UTMIP(_port, 8 * (_port), 8)))
+#define UTMI_HS(_port) SPEED(_port, 0)
+#define UTMI_FS(_port) SPEED(_port, 1)
+#define UTMI_LS(_port) SPEED(_port, 2)
+#define UTMI_RST(_port) SPEED(_port, 3)
+
+#define PMC_UTMIP_UHSIC_TRIGGERS (0x1ec)
+#define UTMIP_CLR_WALK_PTR(x) UTMIP(x, BIT(x), BIT(16))
+#define UTMIP_CAP_CFG(x) UTMIP(x, BIT((x) + 4), BIT(17))
+#define UTMIP_CLR_WAKE_ALARM(x) UTMIP(x, BIT((x) + 12), \
+ BIT(19))
+#define UHSIC_CLR_WALK_PTR BIT(3)
+#define UHSIC_CLR_WAKE_ALARM BIT(15)
+
+#define PMC_UTMIP_SLEEPWALK_PX(x) UTMIP(x, 0x204 + (4 * (x)), \
+ 0x4e0)
+/* phase A */
+#define UTMIP_USBOP_RPD_A BIT(0)
+#define UTMIP_USBON_RPD_A BIT(1)
+#define UTMIP_AP_A BIT(4)
+#define UTMIP_AN_A BIT(5)
+#define UTMIP_HIGHZ_A BIT(6)
+/* phase B */
+#define UTMIP_USBOP_RPD_B BIT(8)
+#define UTMIP_USBON_RPD_B BIT(9)
+#define UTMIP_AP_B BIT(12)
+#define UTMIP_AN_B BIT(13)
+#define UTMIP_HIGHZ_B BIT(14)
+/* phase C */
+#define UTMIP_USBOP_RPD_C BIT(16)
+#define UTMIP_USBON_RPD_C BIT(17)
+#define UTMIP_AP_C BIT(20)
+#define UTMIP_AN_C BIT(21)
+#define UTMIP_HIGHZ_C BIT(22)
+/* phase D */
+#define UTMIP_USBOP_RPD_D BIT(24)
+#define UTMIP_USBON_RPD_D BIT(25)
+#define UTMIP_AP_D BIT(28)
+#define UTMIP_AN_D BIT(29)
+#define UTMIP_HIGHZ_D BIT(30)
+
+#define PMC_UTMIP_UHSIC_LINE_WAKEUP (0x26c)
+#define UTMIP_LINE_WAKEUP_EN(x) UTMIP(x, BIT(x), BIT(4))
+#define UHSIC_LINE_WAKEUP_EN BIT(3)
+
+#define PMC_UTMIP_TERM_PAD_CFG (0x1f8)
+#define PCTRL_VAL(x) (((x) & 0x3f) << 1)
+#define TCTRL_VAL(x) (((x) & 0x3f) << 7)
+
+#define PMC_UTMIP_PAD_CFGX(x) (0x4c0 + (4 * (x)))
+#define RPD_CTRL_PX(x) (((x) & 0x1f) << 22)
+
+#define PMC_UHSIC_SLEEP_CFG PMC_UTMIP_UHSIC_SLEEP_CFG(0)
+#define UHSIC_MASTER_ENABLE BIT(24)
+#define UHSIC_WAKE_VAL(_value) (((_value) & 0xf) << 28)
+#define UHSIC_WAKE_VAL_SD10 UHSIC_WAKE_VAL(2)
+#define UHSIC_WAKE_VAL_NONE UHSIC_WAKE_VAL(12)
+
+#define PMC_UHSIC_FAKE PMC_UTMIP_UHSIC_FAKE(0)
+#define UHSIC_FAKE_STROBE_VAL BIT(12)
+#define UHSIC_FAKE_DATA_VAL BIT(13)
+#define UHSIC_FAKE_STROBE_EN BIT(14)
+#define UHSIC_FAKE_DATA_EN BIT(15)
+
+#define PMC_UHSIC_SAVED_STATE PMC_UTMIP_UHSIC_SAVED_STATE(0)
+#define UHSIC_MODE(_value) (((_value) & 0x1) << 24)
+#define UHSIC_HS UHSIC_MODE(0)
+#define UHSIC_RST UHSIC_MODE(1)
+
+#define PMC_UHSIC_SLEEPWALK_CFG PMC_UTMIP_UHSIC_SLEEPWALK_CFG(0)
+#define UHSIC_WAKE_WALK_EN BIT(30)
+#define UHSIC_LINEVAL_WALK_EN BIT(31)
+
+#define PMC_UHSIC_SLEEPWALK_P0 (0x210)
+#define UHSIC_DATA0_RPD_A BIT(1)
+#define UHSIC_DATA0_RPU_B BIT(11)
+#define UHSIC_DATA0_RPU_C BIT(19)
+#define UHSIC_DATA0_RPU_D BIT(27)
+#define UHSIC_STROBE_RPU_A BIT(2)
+#define UHSIC_STROBE_RPD_B BIT(8)
+#define UHSIC_STROBE_RPD_C BIT(16)
+#define UHSIC_STROBE_RPD_D BIT(24)
struct tegra210_xusb_fuse_calibration {
u32 hs_curr_level[4];
@@ -243,10 +413,19 @@ struct tegra210_xusb_fuse_calibration {
u32 rpd_ctrl;
};
+struct tegra210_xusb_padctl_context {
+ u32 usb2_pad_mux;
+ u32 usb2_port_cap;
+ u32 ss_port_map;
+ u32 usb3_pad_mux;
+};
+
struct tegra210_xusb_padctl {
struct tegra_xusb_padctl base;
+ struct regmap *regmap;
struct tegra210_xusb_fuse_calibration fuse;
+ struct tegra210_xusb_padctl_context context;
};
static inline struct tegra210_xusb_padctl *
@@ -255,23 +434,51 @@ to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl)
return container_of(padctl, struct tegra210_xusb_padctl, base);
}
+static const struct tegra_xusb_lane_map tegra210_usb3_map[] = {
+ { 0, "pcie", 6 },
+ { 1, "pcie", 5 },
+ { 2, "pcie", 0 },
+ { 2, "pcie", 3 },
+ { 3, "pcie", 4 },
+ { 3, "sata", 0 },
+ { 0, NULL, 0 }
+};
+
+static int tegra210_usb3_lane_map(struct tegra_xusb_lane *lane)
+{
+ const struct tegra_xusb_lane_map *map;
+
+ for (map = tegra210_usb3_map; map->type; map++) {
+ if (map->index == lane->index &&
+ strcmp(map->type, lane->pad->soc->name) == 0) {
+ dev_dbg(lane->pad->padctl->dev, "lane = %s map to port = usb3-%d\n",
+ lane->pad->soc->lanes[lane->index].name, map->port);
+ return map->port;
+ }
+ }
+
+ return -EINVAL;
+}
+
/* must be called under padctl->lock */
static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
unsigned long timeout;
u32 value;
+ unsigned int i;
int err;
- if (pcie->enable > 0) {
- pcie->enable++;
+ if (pcie->enable)
return 0;
- }
err = clk_prepare_enable(pcie->pll);
if (err < 0)
return err;
+ if (tegra210_plle_hw_sequence_is_enabled())
+ goto skip_pll_init;
+
err = reset_control_deassert(pcie->rst);
if (err < 0)
goto disable;
@@ -454,7 +661,14 @@ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
tegra210_xusb_pll_hw_sequence_start();
- pcie->enable++;
+skip_pll_init:
+ pcie->enable = true;
+
+ for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ }
return 0;
@@ -468,34 +682,44 @@ disable:
static void tegra210_pex_uphy_disable(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
+ u32 value;
+ unsigned int i;
- mutex_lock(&padctl->lock);
+ if (WARN_ON(!pcie->enable))
+ return;
- if (WARN_ON(pcie->enable == 0))
- goto unlock;
+ pcie->enable = false;
- if (--pcie->enable > 0)
- goto unlock;
+ for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ }
- reset_control_assert(pcie->rst);
clk_disable_unprepare(pcie->pll);
-
-unlock:
- mutex_unlock(&padctl->lock);
}
/* must be called under padctl->lock */
-static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb)
+static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
+ struct tegra_xusb_lane *lane = tegra_xusb_find_lane(padctl, "sata", 0);
unsigned long timeout;
u32 value;
+ unsigned int i;
int err;
+ bool usb;
- if (sata->enable > 0) {
- sata->enable++;
+ if (sata->enable)
return 0;
- }
+
+ if (IS_ERR(lane))
+ return 0;
+
+ if (tegra210_plle_hw_sequence_is_enabled())
+ goto skip_pll_init;
+
+ usb = tegra_xusb_lane_check(lane, "usb3-ss");
err = clk_prepare_enable(sata->pll);
if (err < 0)
@@ -696,7 +920,14 @@ static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb)
tegra210_sata_pll_hw_sequence_start();
- sata->enable++;
+skip_pll_init:
+ sata->enable = true;
+
+ for (i = 0; i < padctl->sata->soc->num_lanes; i++) {
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(i);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ }
return 0;
@@ -710,31 +941,27 @@ disable:
static void tegra210_sata_uphy_disable(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
+ u32 value;
+ unsigned int i;
- mutex_lock(&padctl->lock);
+ if (WARN_ON(!sata->enable))
+ return;
- if (WARN_ON(sata->enable == 0))
- goto unlock;
+ sata->enable = false;
- if (--sata->enable > 0)
- goto unlock;
+ for (i = 0; i < padctl->sata->soc->num_lanes; i++) {
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(i);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ }
- reset_control_assert(sata->rst);
clk_disable_unprepare(sata->pll);
-
-unlock:
- mutex_unlock(&padctl->lock);
}
-static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
+static void tegra210_aux_mux_lp0_clamp_disable(struct tegra_xusb_padctl *padctl)
{
u32 value;
- mutex_lock(&padctl->lock);
-
- if (padctl->enable++ > 0)
- goto out;
-
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN;
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
@@ -750,24 +977,12 @@ static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN;
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
-out:
- mutex_unlock(&padctl->lock);
- return 0;
}
-static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl)
+static void tegra210_aux_mux_lp0_clamp_enable(struct tegra_xusb_padctl *padctl)
{
u32 value;
- mutex_lock(&padctl->lock);
-
- if (WARN_ON(padctl->enable == 0))
- goto out;
-
- if (--padctl->enable > 0)
- goto out;
-
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN;
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
@@ -783,12 +998,38 @@ static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl)
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN;
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+}
+
+static int tegra210_uphy_init(struct tegra_xusb_padctl *padctl)
+{
+ if (padctl->pcie)
+ tegra210_pex_uphy_enable(padctl);
+
+ if (padctl->sata)
+ tegra210_sata_uphy_enable(padctl);
+
+ if (!tegra210_plle_hw_sequence_is_enabled())
+ tegra210_plle_hw_sequence_start();
+ else
+ dev_dbg(padctl->dev, "PLLE is already in HW control\n");
+
+ tegra210_aux_mux_lp0_clamp_disable(padctl);
-out:
- mutex_unlock(&padctl->lock);
return 0;
}
+static void __maybe_unused
+tegra210_uphy_deinit(struct tegra_xusb_padctl *padctl)
+{
+ tegra210_aux_mux_lp0_clamp_enable(padctl);
+
+ if (padctl->sata)
+ tegra210_sata_uphy_disable(padctl);
+
+ if (padctl->pcie)
+ tegra210_pex_uphy_disable(padctl);
+}
+
static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl,
unsigned int index, bool idle)
{
@@ -814,6 +1055,643 @@ static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl,
return 0;
}
+static int tegra210_usb3_enable_phy_sleepwalk(struct tegra_xusb_lane *lane,
+ enum usb_device_speed speed)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ int port = tegra210_usb3_lane_map(lane);
+ struct device *dev = padctl->dev;
+ u32 value;
+
+ if (port < 0) {
+ dev_err(dev, "invalid usb3 port number\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(250, 350);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra210_usb3_disable_phy_sleepwalk(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ int port = tegra210_usb3_lane_map(lane);
+ struct device *dev = padctl->dev;
+ u32 value;
+
+ if (port < 0) {
+ dev_err(dev, "invalid usb3 port number\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra210_usb3_enable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ int port = tegra210_usb3_lane_map(lane);
+ struct device *dev = padctl->dev;
+ u32 value;
+
+ if (port < 0) {
+ dev_err(dev, "invalid usb3 port number\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= SS_PORT_WAKEUP_EVENT(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= SS_PORT_WAKE_INTERRUPT_ENABLE(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra210_usb3_disable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ int port = tegra210_usb3_lane_map(lane);
+ struct device *dev = padctl->dev;
+ u32 value;
+
+ if (port < 0) {
+ dev_err(dev, "invalid usb3 port number\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= SS_PORT_WAKEUP_EVENT(port);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static bool tegra210_usb3_phy_remote_wake_detected(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ int index = tegra210_usb3_lane_map(lane);
+ u32 value;
+
+ if (index < 0)
+ return false;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(index)) && (value & SS_PORT_WAKEUP_EVENT(index)))
+ return true;
+
+ return false;
+}
+
+static int tegra210_utmi_enable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra210_utmi_disable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static bool tegra210_utmi_phy_remote_wake_detected(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(index)) &&
+ (value & USB2_PORT_WAKEUP_EVENT(index)))
+ return true;
+
+ return false;
+}
+
+static int tegra210_hsic_enable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_HSIC_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static int tegra210_hsic_disable_phy_wake(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value &= ~USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ value &= ~ALL_WAKE_EVENTS;
+ value |= USB2_HSIC_PORT_WAKEUP_EVENT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0);
+
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+}
+
+static bool tegra210_hsic_phy_remote_wake_detected(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0);
+ if ((value & USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index)) &&
+ (value & USB2_HSIC_PORT_WAKEUP_EVENT(index)))
+ return true;
+
+ return false;
+}
+
+#define padctl_pmc_readl(_priv, _offset) \
+({ \
+ u32 value; \
+ WARN(regmap_read(_priv->regmap, _offset, &value), "read %s failed\n", #_offset);\
+ value; \
+})
+
+#define padctl_pmc_writel(_priv, _value, _offset) \
+ WARN(regmap_write(_priv->regmap, _offset, _value), "write %s failed\n", #_offset)
+
+static int tegra210_pmc_utmi_enable_phy_sleepwalk(struct tegra_xusb_lane *lane,
+ enum usb_device_speed speed)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
+ unsigned int port = lane->index;
+ u32 value, tctrl, pctrl, rpd_ctrl;
+
+ if (!priv->regmap)
+ return -EOPNOTSUPP;
+
+ if (speed > USB_SPEED_HIGH)
+ return -EINVAL;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+ tctrl = TCTRL_VALUE(value);
+ pctrl = PCTRL_VALUE(value);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port));
+ rpd_ctrl = RPD_CTRL_VALUE(value);
+
+ /* ensure sleepwalk logic is disabled */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value &= ~UTMIP_MASTER_ENABLE(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ /* ensure sleepwalk logics are in low power mode */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG);
+ value |= UTMIP_PWR(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_MASTER_CONFIG);
+
+ /* set debounce time */
+ value = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL);
+ value &= ~UTMIP_LINE_DEB_CNT(~0);
+ value |= UTMIP_LINE_DEB_CNT(0x1);
+ padctl_pmc_writel(priv, value, PMC_USB_DEBOUNCE_DEL);
+
+ /* ensure fake events of sleepwalk logic are desiabled */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_FAKE(port));
+ value &= ~(UTMIP_FAKE_USBOP_VAL(port) | UTMIP_FAKE_USBON_VAL(port) |
+ UTMIP_FAKE_USBOP_EN(port) | UTMIP_FAKE_USBON_EN(port));
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_FAKE(port));
+
+ /* ensure wake events of sleepwalk logic are not latched */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+ value &= ~UTMIP_LINE_WAKEUP_EN(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+ /* disable wake event triggers of sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value &= ~UTMIP_WAKE_VAL(port, ~0);
+ value |= UTMIP_WAKE_VAL_NONE(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ /* power down the line state detectors of the pad */
+ value = padctl_pmc_readl(priv, PMC_USB_AO);
+ value |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
+ padctl_pmc_writel(priv, value, PMC_USB_AO);
+
+ /* save state per speed */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SAVED_STATE(port));
+ value &= ~SPEED(port, ~0);
+
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ value |= UTMI_HS(port);
+ break;
+
+ case USB_SPEED_FULL:
+ value |= UTMI_FS(port);
+ break;
+
+ case USB_SPEED_LOW:
+ value |= UTMI_LS(port);
+ break;
+
+ default:
+ value |= UTMI_RST(port);
+ break;
+ }
+
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SAVED_STATE(port));
+
+ /* enable the trigger of the sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port));
+ value |= UTMIP_LINEVAL_WALK_EN(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port));
+
+ /*
+ * Reset the walk pointer and clear the alarm of the sleepwalk logic,
+ * as well as capture the configuration of the USB2.0 pad.
+ */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS);
+ value |= UTMIP_CLR_WALK_PTR(port) | UTMIP_CLR_WAKE_ALARM(port) | UTMIP_CAP_CFG(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS);
+
+ /* program electrical parameters read from XUSB PADCTL */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_TERM_PAD_CFG);
+ value &= ~(TCTRL_VAL(~0) | PCTRL_VAL(~0));
+ value |= (TCTRL_VAL(tctrl) | PCTRL_VAL(pctrl));
+ padctl_pmc_writel(priv, value, PMC_UTMIP_TERM_PAD_CFG);
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_PAD_CFGX(port));
+ value &= ~RPD_CTRL_PX(~0);
+ value |= RPD_CTRL_PX(rpd_ctrl);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_PAD_CFGX(port));
+
+ /*
+ * Set up the pull-ups and pull-downs of the signals during the four
+ * stages of sleepwalk. If a device is connected, program sleepwalk
+ * logic to maintain a J and keep driving K upon seeing remote wake.
+ */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_SLEEPWALK_PX(port));
+ value = UTMIP_USBOP_RPD_A | UTMIP_USBOP_RPD_B | UTMIP_USBOP_RPD_C | UTMIP_USBOP_RPD_D;
+ value |= UTMIP_USBON_RPD_A | UTMIP_USBON_RPD_B | UTMIP_USBON_RPD_C | UTMIP_USBON_RPD_D;
+
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ case USB_SPEED_FULL:
+ /* J state: D+/D- = high/low, K state: D+/D- = low/high */
+ value |= UTMIP_HIGHZ_A;
+ value |= UTMIP_AP_A;
+ value |= UTMIP_AN_B | UTMIP_AN_C | UTMIP_AN_D;
+ break;
+
+ case USB_SPEED_LOW:
+ /* J state: D+/D- = low/high, K state: D+/D- = high/low */
+ value |= UTMIP_HIGHZ_A;
+ value |= UTMIP_AN_A;
+ value |= UTMIP_AP_B | UTMIP_AP_C | UTMIP_AP_D;
+ break;
+
+ default:
+ value |= UTMIP_HIGHZ_A | UTMIP_HIGHZ_B | UTMIP_HIGHZ_C | UTMIP_HIGHZ_D;
+ break;
+ }
+
+ padctl_pmc_writel(priv, value, PMC_UTMIP_SLEEPWALK_PX(port));
+
+ /* power up the line state detectors of the pad */
+ value = padctl_pmc_readl(priv, PMC_USB_AO);
+ value &= ~(USBOP_VAL_PD(port) | USBON_VAL_PD(port));
+ padctl_pmc_writel(priv, value, PMC_USB_AO);
+
+ usleep_range(50, 100);
+
+ /* switch the electric control of the USB2.0 pad to PMC */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value |= UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) | UTMIP_TCTRL_USE_PMC(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1);
+ value |= UTMIP_RPD_CTRL_USE_PMC_PX(port) | UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG1);
+
+ /* set the wake signaling trigger events */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value &= ~UTMIP_WAKE_VAL(port, ~0);
+ value |= UTMIP_WAKE_VAL_ANY(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ /* enable the wake detection */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value |= UTMIP_MASTER_ENABLE(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+ value |= UTMIP_LINE_WAKEUP_EN(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+ return 0;
+}
+
+static int tegra210_pmc_utmi_disable_phy_sleepwalk(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
+ unsigned int port = lane->index;
+ u32 value;
+
+ if (!priv->regmap)
+ return -EOPNOTSUPP;
+
+ /* disable the wake detection */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value &= ~UTMIP_MASTER_ENABLE(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+ value &= ~UTMIP_LINE_WAKEUP_EN(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+ /* switch the electric control of the USB2.0 pad to XUSB or USB2 */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value &= ~(UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) |
+ UTMIP_TCTRL_USE_PMC(port));
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1);
+ value &= ~(UTMIP_RPD_CTRL_USE_PMC_PX(port) | UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port));
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG1);
+
+ /* disable wake event triggers of sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+ value &= ~UTMIP_WAKE_VAL(port, ~0);
+ value |= UTMIP_WAKE_VAL_NONE(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
+
+ /* power down the line state detectors of the port */
+ value = padctl_pmc_readl(priv, PMC_USB_AO);
+ value |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
+ padctl_pmc_writel(priv, value, PMC_USB_AO);
+
+ /* clear alarm of the sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS);
+ value |= UTMIP_CLR_WAKE_ALARM(port);
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS);
+
+ return 0;
+}
+
+static int tegra210_pmc_hsic_enable_phy_sleepwalk(struct tegra_xusb_lane *lane,
+ enum usb_device_speed speed)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
+ u32 value;
+
+ if (!priv->regmap)
+ return -EOPNOTSUPP;
+
+ /* ensure sleepwalk logic is disabled */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG);
+ value &= ~UHSIC_MASTER_ENABLE;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG);
+
+ /* ensure sleepwalk logics are in low power mode */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG);
+ value |= UHSIC_PWR;
+ padctl_pmc_writel(priv, value, PMC_UTMIP_MASTER_CONFIG);
+
+ /* set debounce time */
+ value = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL);
+ value &= ~UHSIC_LINE_DEB_CNT(~0);
+ value |= UHSIC_LINE_DEB_CNT(0x1);
+ padctl_pmc_writel(priv, value, PMC_USB_DEBOUNCE_DEL);
+
+ /* ensure fake events of sleepwalk logic are desiabled */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_FAKE);
+ value &= ~(UHSIC_FAKE_STROBE_VAL | UHSIC_FAKE_DATA_VAL |
+ UHSIC_FAKE_STROBE_EN | UHSIC_FAKE_DATA_EN);
+ padctl_pmc_writel(priv, value, PMC_UHSIC_FAKE);
+
+ /* ensure wake events of sleepwalk logic are not latched */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+ value &= ~UHSIC_LINE_WAKEUP_EN;
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+ /* disable wake event triggers of sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG);
+ value &= ~UHSIC_WAKE_VAL(~0);
+ value |= UHSIC_WAKE_VAL_NONE;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG);
+
+ /* power down the line state detectors of the port */
+ value = padctl_pmc_readl(priv, PMC_USB_AO);
+ value |= STROBE_VAL_PD | DATA0_VAL_PD | DATA1_VAL_PD;
+ padctl_pmc_writel(priv, value, PMC_USB_AO);
+
+ /* save state, HSIC always comes up as HS */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SAVED_STATE);
+ value &= ~UHSIC_MODE(~0);
+ value |= UHSIC_HS;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SAVED_STATE);
+
+ /* enable the trigger of the sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEPWALK_CFG);
+ value |= UHSIC_WAKE_WALK_EN | UHSIC_LINEVAL_WALK_EN;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEPWALK_CFG);
+
+ /*
+ * Reset the walk pointer and clear the alarm of the sleepwalk logic,
+ * as well as capture the configuration of the USB2.0 port.
+ */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS);
+ value |= UHSIC_CLR_WALK_PTR | UHSIC_CLR_WAKE_ALARM;
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS);
+
+ /*
+ * Set up the pull-ups and pull-downs of the signals during the four
+ * stages of sleepwalk. Maintain a HSIC IDLE and keep driving HSIC
+ * RESUME upon remote wake.
+ */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEPWALK_P0);
+ value = UHSIC_DATA0_RPD_A | UHSIC_DATA0_RPU_B | UHSIC_DATA0_RPU_C | UHSIC_DATA0_RPU_D |
+ UHSIC_STROBE_RPU_A | UHSIC_STROBE_RPD_B | UHSIC_STROBE_RPD_C | UHSIC_STROBE_RPD_D;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEPWALK_P0);
+
+ /* power up the line state detectors of the port */
+ value = padctl_pmc_readl(priv, PMC_USB_AO);
+ value &= ~(STROBE_VAL_PD | DATA0_VAL_PD | DATA1_VAL_PD);
+ padctl_pmc_writel(priv, value, PMC_USB_AO);
+
+ usleep_range(50, 100);
+
+ /* set the wake signaling trigger events */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG);
+ value &= ~UHSIC_WAKE_VAL(~0);
+ value |= UHSIC_WAKE_VAL_SD10;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG);
+
+ /* enable the wake detection */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG);
+ value |= UHSIC_MASTER_ENABLE;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG);
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+ value |= UHSIC_LINE_WAKEUP_EN;
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+ return 0;
+}
+
+static int tegra210_pmc_hsic_disable_phy_sleepwalk(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
+ u32 value;
+
+ if (!priv->regmap)
+ return -EOPNOTSUPP;
+
+ /* disable the wake detection */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG);
+ value &= ~UHSIC_MASTER_ENABLE;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG);
+
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+ value &= ~UHSIC_LINE_WAKEUP_EN;
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
+
+ /* disable wake event triggers of sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG);
+ value &= ~UHSIC_WAKE_VAL(~0);
+ value |= UHSIC_WAKE_VAL_NONE;
+ padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG);
+
+ /* power down the line state detectors of the port */
+ value = padctl_pmc_readl(priv, PMC_USB_AO);
+ value |= STROBE_VAL_PD | DATA0_VAL_PD | DATA1_VAL_PD;
+ padctl_pmc_writel(priv, value, PMC_USB_AO);
+
+ /* clear alarm of the sleepwalk logic */
+ value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS);
+ value |= UHSIC_CLR_WAKE_ALARM;
+ padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS);
+
+ return 0;
+}
+
static int tegra210_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
unsigned int index, bool enable)
{
@@ -910,14 +1788,36 @@ static void tegra210_usb2_lane_remove(struct tegra_xusb_lane *lane)
static const struct tegra_xusb_lane_ops tegra210_usb2_lane_ops = {
.probe = tegra210_usb2_lane_probe,
.remove = tegra210_usb2_lane_remove,
+ .enable_phy_sleepwalk = tegra210_pmc_utmi_enable_phy_sleepwalk,
+ .disable_phy_sleepwalk = tegra210_pmc_utmi_disable_phy_sleepwalk,
+ .enable_phy_wake = tegra210_utmi_enable_phy_wake,
+ .disable_phy_wake = tegra210_utmi_disable_phy_wake,
+ .remote_wake_detected = tegra210_utmi_phy_remote_wake_detected,
};
static int tegra210_usb2_phy_init(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ struct tegra_xusb_usb2_port *port;
+ int err;
u32 value;
+ port = tegra_xusb_find_usb2_port(padctl, index);
+ if (!port) {
+ dev_err(&phy->dev, "no port found for USB2 lane %u\n", index);
+ return -ENODEV;
+ }
+
+ if (port->supply && port->mode == USB_DR_MODE_HOST) {
+ err = regulator_enable(port->supply);
+ if (err)
+ return err;
+ }
+
+ mutex_lock(&padctl->lock);
+
value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK <<
XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT);
@@ -925,14 +1825,128 @@ static int tegra210_usb2_phy_init(struct phy *phy)
XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT;
padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
- return tegra210_xusb_padctl_enable(padctl);
+ mutex_unlock(&padctl->lock);
+
+ return 0;
}
static int tegra210_usb2_phy_exit(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra_xusb_usb2_port *port;
+ int err;
+
+ port = tegra_xusb_find_usb2_port(padctl, lane->index);
+ if (!port) {
+ dev_err(&phy->dev, "no port found for USB2 lane %u\n", lane->index);
+ return -ENODEV;
+ }
+
+ if (port->supply && port->mode == USB_DR_MODE_HOST) {
+ err = regulator_disable(port->supply);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl,
+ bool status)
+{
+ u32 value;
- return tegra210_xusb_padctl_disable(lane->pad->padctl);
+ dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear");
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
+
+ if (status) {
+ value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
+ value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
+ XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
+ value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING <<
+ XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
+ } else {
+ value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
+ }
+
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID);
+
+ return 0;
+}
+
+static int tegra210_xusb_padctl_id_override(struct tegra_xusb_padctl *padctl,
+ bool status)
+{
+ u32 value;
+
+ dev_dbg(padctl->dev, "%s id override\n", status ? "set" : "clear");
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
+
+ if (status) {
+ if (value & XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON) {
+ value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID);
+ usleep_range(1000, 2000);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
+ }
+
+ value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
+ XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
+ value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED <<
+ XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
+ } else {
+ value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
+ XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
+ value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING <<
+ XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
+ }
+
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID);
+
+ return 0;
+}
+
+static int tegra210_usb2_phy_set_mode(struct phy *phy, enum phy_mode mode,
+ int submode)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra_xusb_usb2_port *port = tegra_xusb_find_usb2_port(padctl,
+ lane->index);
+ int err = 0;
+
+ mutex_lock(&padctl->lock);
+
+ dev_dbg(&port->base.dev, "%s: mode %d", __func__, mode);
+
+ if (mode == PHY_MODE_USB_OTG) {
+ if (submode == USB_ROLE_HOST) {
+ tegra210_xusb_padctl_id_override(padctl, true);
+
+ err = regulator_enable(port->supply);
+ } else if (submode == USB_ROLE_DEVICE) {
+ tegra210_xusb_padctl_vbus_override(padctl, true);
+ } else if (submode == USB_ROLE_NONE) {
+ /*
+ * When port is peripheral only or role transitions to
+ * USB_ROLE_NONE from USB_ROLE_DEVICE, regulator is not
+ * be enabled.
+ */
+ if (regulator_is_enabled(port->supply))
+ regulator_disable(port->supply);
+
+ tegra210_xusb_padctl_id_override(padctl, false);
+ tegra210_xusb_padctl_vbus_override(padctl, false);
+ }
+ }
+
+ mutex_unlock(&padctl->lock);
+
+ return err;
}
static int tegra210_usb2_phy_power_on(struct phy *phy)
@@ -955,6 +1969,8 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
priv = to_tegra210_xusb_padctl(padctl);
+ mutex_lock(&padctl->lock);
+
if (port->usb3_port_fake != -1) {
value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(
@@ -1048,12 +2064,6 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
padctl_writel(padctl, value,
XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
- err = regulator_enable(port->supply);
- if (err)
- return err;
-
- mutex_lock(&padctl->lock);
-
if (pad->enable > 0) {
pad->enable++;
mutex_unlock(&padctl->lock);
@@ -1062,7 +2072,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
err = clk_prepare_enable(pad->clk);
if (err)
- goto disable_regulator;
+ goto out;
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK <<
@@ -1094,8 +2104,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
return 0;
-disable_regulator:
- regulator_disable(port->supply);
+out:
mutex_unlock(&padctl->lock);
return err;
}
@@ -1154,7 +2163,6 @@ static int tegra210_usb2_phy_power_off(struct phy *phy)
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
out:
- regulator_disable(port->supply);
mutex_unlock(&padctl->lock);
return 0;
}
@@ -1164,6 +2172,7 @@ static const struct phy_ops tegra210_usb2_phy_ops = {
.exit = tegra210_usb2_phy_exit,
.power_on = tegra210_usb2_phy_power_on,
.power_off = tegra210_usb2_phy_power_off,
+ .set_mode = tegra210_usb2_phy_set_mode,
.owner = THIS_MODULE,
};
@@ -1275,6 +2284,11 @@ static void tegra210_hsic_lane_remove(struct tegra_xusb_lane *lane)
static const struct tegra_xusb_lane_ops tegra210_hsic_lane_ops = {
.probe = tegra210_hsic_lane_probe,
.remove = tegra210_hsic_lane_remove,
+ .enable_phy_sleepwalk = tegra210_pmc_hsic_enable_phy_sleepwalk,
+ .disable_phy_sleepwalk = tegra210_pmc_hsic_disable_phy_sleepwalk,
+ .enable_phy_wake = tegra210_hsic_enable_phy_wake,
+ .disable_phy_wake = tegra210_hsic_disable_phy_wake,
+ .remote_wake_detected = tegra210_hsic_phy_remote_wake_detected,
};
static int tegra210_hsic_phy_init(struct phy *phy)
@@ -1290,14 +2304,12 @@ static int tegra210_hsic_phy_init(struct phy *phy)
XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_SHIFT;
padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
- return tegra210_xusb_padctl_enable(padctl);
+ return 0;
}
static int tegra210_hsic_phy_exit(struct phy *phy)
{
- struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
-
- return tegra210_xusb_padctl_disable(lane->pad->padctl);
+ return 0;
}
static int tegra210_hsic_phy_power_on(struct phy *phy)
@@ -1481,6 +2493,55 @@ static const struct tegra_xusb_pad_soc tegra210_hsic_pad = {
.ops = &tegra210_hsic_ops,
};
+static void tegra210_uphy_lane_iddq_enable(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ u32 value;
+
+ value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+ padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+static void tegra210_uphy_lane_iddq_disable(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ u32 value;
+
+ value = padctl_readl(padctl, lane->soc->regs.misc_ctl2);
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ;
+ value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK;
+ value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL;
+ padctl_writel(padctl, value, lane->soc->regs.misc_ctl2);
+}
+
+#define TEGRA210_UPHY_LANE(_name, _offset, _shift, _mask, _type, _misc) \
+ { \
+ .name = _name, \
+ .offset = _offset, \
+ .shift = _shift, \
+ .mask = _mask, \
+ .num_funcs = ARRAY_SIZE(tegra210_##_type##_functions), \
+ .funcs = tegra210_##_type##_functions, \
+ .regs.misc_ctl2 = _misc, \
+ }
+
static const char *tegra210_pcie_functions[] = {
"pcie-x1",
"usb3-ss",
@@ -1489,15 +2550,137 @@ static const char *tegra210_pcie_functions[] = {
};
static const struct tegra_xusb_lane_soc tegra210_pcie_lanes[] = {
- TEGRA210_LANE("pcie-0", 0x028, 12, 0x3, pcie),
- TEGRA210_LANE("pcie-1", 0x028, 14, 0x3, pcie),
- TEGRA210_LANE("pcie-2", 0x028, 16, 0x3, pcie),
- TEGRA210_LANE("pcie-3", 0x028, 18, 0x3, pcie),
- TEGRA210_LANE("pcie-4", 0x028, 20, 0x3, pcie),
- TEGRA210_LANE("pcie-5", 0x028, 22, 0x3, pcie),
- TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, pcie),
+ TEGRA210_UPHY_LANE("pcie-0", 0x028, 12, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(0)),
+ TEGRA210_UPHY_LANE("pcie-1", 0x028, 14, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(1)),
+ TEGRA210_UPHY_LANE("pcie-2", 0x028, 16, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(2)),
+ TEGRA210_UPHY_LANE("pcie-3", 0x028, 18, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(3)),
+ TEGRA210_UPHY_LANE("pcie-4", 0x028, 20, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(4)),
+ TEGRA210_UPHY_LANE("pcie-5", 0x028, 22, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(5)),
+ TEGRA210_UPHY_LANE("pcie-6", 0x028, 24, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(6)),
};
+static struct tegra_xusb_usb3_port *
+tegra210_lane_to_usb3_port(struct tegra_xusb_lane *lane)
+{
+ int port;
+
+ if (!lane || !lane->pad || !lane->pad->padctl)
+ return NULL;
+
+ port = tegra210_usb3_lane_map(lane);
+ if (port < 0)
+ return NULL;
+
+ return tegra_xusb_find_usb3_port(lane->pad->padctl, port);
+}
+
+static int tegra210_usb3_phy_power_on(struct phy *phy)
+{
+ struct device *dev = &phy->dev;
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane);
+ unsigned int index;
+ u32 value;
+
+ if (!usb3) {
+ dev_err(dev, "no USB3 port found for lane %u\n", lane->index);
+ return -ENODEV;
+ }
+
+ index = usb3->base.index;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+
+ if (!usb3->internal)
+ value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+ else
+ value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+
+ value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index);
+ value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port);
+ padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));
+ value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT);
+ value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));
+ value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT);
+ value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));
+
+ padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL,
+ XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));
+ value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT);
+ value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));
+
+ padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL,
+ XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ return 0;
+}
+
+static int tegra210_usb3_phy_power_off(struct phy *phy)
+{
+ struct device *dev = &phy->dev;
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane);
+ unsigned int index;
+ u32 value;
+
+ if (!usb3) {
+ dev_err(dev, "no USB3 port found for lane %u\n", lane->index);
+ return -ENODEV;
+ }
+
+ index = usb3->base.index;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(250, 350);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ return 0;
+}
static struct tegra_xusb_lane *
tegra210_pcie_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
unsigned int index)
@@ -1534,40 +2717,40 @@ static void tegra210_pcie_lane_remove(struct tegra_xusb_lane *lane)
static const struct tegra_xusb_lane_ops tegra210_pcie_lane_ops = {
.probe = tegra210_pcie_lane_probe,
.remove = tegra210_pcie_lane_remove,
+ .iddq_enable = tegra210_uphy_lane_iddq_enable,
+ .iddq_disable = tegra210_uphy_lane_iddq_disable,
+ .enable_phy_sleepwalk = tegra210_usb3_enable_phy_sleepwalk,
+ .disable_phy_sleepwalk = tegra210_usb3_disable_phy_sleepwalk,
+ .enable_phy_wake = tegra210_usb3_enable_phy_wake,
+ .disable_phy_wake = tegra210_usb3_disable_phy_wake,
+ .remote_wake_detected = tegra210_usb3_phy_remote_wake_detected,
};
static int tegra210_pcie_phy_init(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
- return tegra210_xusb_padctl_enable(lane->pad->padctl);
-}
+ mutex_lock(&padctl->lock);
-static int tegra210_pcie_phy_exit(struct phy *phy)
-{
- struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ tegra210_uphy_init(padctl);
+
+ mutex_unlock(&padctl->lock);
- return tegra210_xusb_padctl_disable(lane->pad->padctl);
+ return 0;
}
static int tegra210_pcie_phy_power_on(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
- u32 value;
- int err;
+ int err = 0;
mutex_lock(&padctl->lock);
- err = tegra210_pex_uphy_enable(padctl);
- if (err < 0)
- goto unlock;
-
- value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
- value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index);
- padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ if (tegra_xusb_lane_check(lane, "usb3-ss"))
+ err = tegra210_usb3_phy_power_on(phy);
-unlock:
mutex_unlock(&padctl->lock);
return err;
}
@@ -1576,20 +2759,19 @@ static int tegra210_pcie_phy_power_off(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
- u32 value;
+ int err = 0;
- value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
- value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index);
- padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ mutex_lock(&padctl->lock);
- tegra210_pex_uphy_disable(padctl);
+ if (tegra_xusb_lane_check(lane, "usb3-ss"))
+ err = tegra210_usb3_phy_power_off(phy);
- return 0;
+ mutex_unlock(&padctl->lock);
+ return err;
}
static const struct phy_ops tegra210_pcie_phy_ops = {
.init = tegra210_pcie_phy_init,
- .exit = tegra210_pcie_phy_exit,
.power_on = tegra210_pcie_phy_power_on,
.power_off = tegra210_pcie_phy_power_off,
.owner = THIS_MODULE,
@@ -1666,7 +2848,7 @@ static const struct tegra_xusb_pad_soc tegra210_pcie_pad = {
};
static const struct tegra_xusb_lane_soc tegra210_sata_lanes[] = {
- TEGRA210_LANE("sata-0", 0x028, 30, 0x3, pcie),
+ TEGRA210_UPHY_LANE("sata-0", 0x028, 30, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL2),
};
static struct tegra_xusb_lane *
@@ -1705,40 +2887,39 @@ static void tegra210_sata_lane_remove(struct tegra_xusb_lane *lane)
static const struct tegra_xusb_lane_ops tegra210_sata_lane_ops = {
.probe = tegra210_sata_lane_probe,
.remove = tegra210_sata_lane_remove,
+ .iddq_enable = tegra210_uphy_lane_iddq_enable,
+ .iddq_disable = tegra210_uphy_lane_iddq_disable,
+ .enable_phy_sleepwalk = tegra210_usb3_enable_phy_sleepwalk,
+ .disable_phy_sleepwalk = tegra210_usb3_disable_phy_sleepwalk,
+ .enable_phy_wake = tegra210_usb3_enable_phy_wake,
+ .disable_phy_wake = tegra210_usb3_disable_phy_wake,
+ .remote_wake_detected = tegra210_usb3_phy_remote_wake_detected,
};
static int tegra210_sata_phy_init(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
- return tegra210_xusb_padctl_enable(lane->pad->padctl);
-}
+ mutex_lock(&padctl->lock);
-static int tegra210_sata_phy_exit(struct phy *phy)
-{
- struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ tegra210_uphy_init(padctl);
- return tegra210_xusb_padctl_disable(lane->pad->padctl);
+ mutex_unlock(&padctl->lock);
+ return 0;
}
static int tegra210_sata_phy_power_on(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
- u32 value;
- int err;
+ int err = 0;
mutex_lock(&padctl->lock);
- err = tegra210_sata_uphy_enable(padctl, false);
- if (err < 0)
- goto unlock;
-
- value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
- value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index);
- padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ if (tegra_xusb_lane_check(lane, "usb3-ss"))
+ err = tegra210_usb3_phy_power_on(phy);
-unlock:
mutex_unlock(&padctl->lock);
return err;
}
@@ -1747,20 +2928,19 @@ static int tegra210_sata_phy_power_off(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
- u32 value;
+ int err = 0;
- value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
- value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index);
- padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ mutex_lock(&padctl->lock);
- tegra210_sata_uphy_disable(lane->pad->padctl);
+ if (tegra_xusb_lane_check(lane, "usb3-ss"))
+ err = tegra210_usb3_phy_power_off(phy);
- return 0;
+ mutex_unlock(&padctl->lock);
+ return err;
}
static const struct phy_ops tegra210_sata_phy_ops = {
.init = tegra210_sata_phy_init,
- .exit = tegra210_sata_phy_exit,
.power_on = tegra210_sata_phy_power_on,
.power_off = tegra210_sata_phy_power_off,
.owner = THIS_MODULE,
@@ -1852,6 +3032,8 @@ tegra210_usb2_port_map(struct tegra_xusb_port *port)
}
static const struct tegra_xusb_port_ops tegra210_usb2_port_ops = {
+ .release = tegra_xusb_usb2_port_release,
+ .remove = tegra_xusb_usb2_port_remove,
.enable = tegra210_usb2_port_enable,
.disable = tegra210_usb2_port_disable,
.map = tegra210_usb2_port_map,
@@ -1873,6 +3055,7 @@ tegra210_hsic_port_map(struct tegra_xusb_port *port)
}
static const struct tegra_xusb_port_ops tegra210_hsic_port_ops = {
+ .release = tegra_xusb_hsic_port_release,
.enable = tegra210_hsic_port_enable,
.disable = tegra210_hsic_port_disable,
.map = tegra210_hsic_port_map,
@@ -1880,137 +3063,13 @@ static const struct tegra_xusb_port_ops tegra210_hsic_port_ops = {
static int tegra210_usb3_port_enable(struct tegra_xusb_port *port)
{
- struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
- struct tegra_xusb_padctl *padctl = port->padctl;
- struct tegra_xusb_lane *lane = usb3->base.lane;
- unsigned int index = port->index;
- u32 value;
- int err;
-
- value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
-
- if (!usb3->internal)
- value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
- else
- value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
-
- value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index);
- value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port);
- padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
-
- /*
- * TODO: move this code into the PCIe/SATA PHY ->power_on() callbacks
- * and conditionalize based on mux function? This seems to work, but
- * might not be the exact proper sequence.
- */
- err = regulator_enable(usb3->supply);
- if (err < 0)
- return err;
-
- value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));
- value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK <<
- XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT);
- value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL <<
- XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT;
- padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));
-
- value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));
- value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK <<
- XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT);
- value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL <<
- XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT;
- padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));
-
- padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL,
- XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(index));
-
- value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));
- value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK <<
- XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT);
- value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL <<
- XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT;
- padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));
-
- padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL,
- XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(index));
-
- if (lane->pad == padctl->sata)
- err = tegra210_sata_uphy_enable(padctl, true);
- else
- err = tegra210_pex_uphy_enable(padctl);
-
- if (err) {
- dev_err(&port->dev, "%s: failed to enable UPHY: %d\n",
- __func__, err);
- return err;
- }
-
- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
- value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index);
- padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
- usleep_range(100, 200);
-
- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
- value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index);
- padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
- usleep_range(100, 200);
-
- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
- value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index);
- padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
return 0;
}
static void tegra210_usb3_port_disable(struct tegra_xusb_port *port)
{
- struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
- struct tegra_xusb_padctl *padctl = port->padctl;
- struct tegra_xusb_lane *lane = port->lane;
- unsigned int index = port->index;
- u32 value;
-
- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
- value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index);
- padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
- usleep_range(100, 200);
-
- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
- value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index);
- padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
- usleep_range(250, 350);
-
- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
- value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index);
- padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
-
- if (lane->pad == padctl->sata)
- tegra210_sata_uphy_disable(padctl);
- else
- tegra210_pex_uphy_disable(padctl);
-
- regulator_disable(usb3->supply);
-
- value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
- value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index);
- value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, 0x7);
- padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
}
-static const struct tegra_xusb_lane_map tegra210_usb3_map[] = {
- { 0, "pcie", 6 },
- { 1, "pcie", 5 },
- { 2, "pcie", 0 },
- { 2, "pcie", 3 },
- { 3, "pcie", 4 },
- { 3, "pcie", 4 },
- { 0, NULL, 0 }
-};
-
static struct tegra_xusb_lane *
tegra210_usb3_port_map(struct tegra_xusb_port *port)
{
@@ -2018,35 +3077,13 @@ tegra210_usb3_port_map(struct tegra_xusb_port *port)
}
static const struct tegra_xusb_port_ops tegra210_usb3_port_ops = {
+ .release = tegra_xusb_usb3_port_release,
+ .remove = tegra_xusb_usb3_port_remove,
.enable = tegra210_usb3_port_enable,
.disable = tegra210_usb3_port_disable,
.map = tegra210_usb3_port_map,
};
-static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl,
- bool status)
-{
- u32 value;
-
- dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear");
-
- value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
-
- if (status) {
- value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
- value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
- XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
- value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING <<
- XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
- } else {
- value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
- }
-
- padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID);
-
- return 0;
-}
-
static int tegra210_utmi_port_reset(struct phy *phy)
{
struct tegra_xusb_padctl *padctl;
@@ -2106,6 +3143,8 @@ tegra210_xusb_padctl_probe(struct device *dev,
const struct tegra_xusb_padctl_soc *soc)
{
struct tegra210_xusb_padctl *padctl;
+ struct platform_device *pdev;
+ struct device_node *np;
int err;
padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL);
@@ -2119,6 +3158,26 @@ tegra210_xusb_padctl_probe(struct device *dev,
if (err < 0)
return ERR_PTR(err);
+ np = of_parse_phandle(dev->of_node, "nvidia,pmc", 0);
+ if (!np) {
+ dev_warn(dev, "nvidia,pmc property is missing\n");
+ goto out;
+ }
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev) {
+ dev_warn(dev, "PMC device is not available\n");
+ goto out;
+ }
+
+ if (!platform_get_drvdata(pdev))
+ return ERR_PTR(-EPROBE_DEFER);
+
+ padctl->regmap = dev_get_regmap(&pdev->dev, "usb_sleepwalk");
+ if (!padctl->regmap)
+ dev_info(dev, "failed to find PMC regmap\n");
+
+out:
return &padctl->base;
}
@@ -2126,9 +3185,75 @@ static void tegra210_xusb_padctl_remove(struct tegra_xusb_padctl *padctl)
{
}
+static void tegra210_xusb_padctl_save(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
+
+ priv->context.usb2_pad_mux =
+ padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
+ priv->context.usb2_port_cap =
+ padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
+ priv->context.ss_port_map =
+ padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+ priv->context.usb3_pad_mux =
+ padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+}
+
+static void tegra210_xusb_padctl_restore(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
+ struct tegra_xusb_lane *lane;
+
+ padctl_writel(padctl, priv->context.usb2_pad_mux,
+ XUSB_PADCTL_USB2_PAD_MUX);
+ padctl_writel(padctl, priv->context.usb2_port_cap,
+ XUSB_PADCTL_USB2_PORT_CAP);
+ padctl_writel(padctl, priv->context.ss_port_map,
+ XUSB_PADCTL_SS_PORT_MAP);
+
+ list_for_each_entry(lane, &padctl->lanes, list) {
+ if (lane->pad->ops->iddq_enable)
+ tegra210_uphy_lane_iddq_enable(lane);
+ }
+
+ padctl_writel(padctl, priv->context.usb3_pad_mux,
+ XUSB_PADCTL_USB3_PAD_MUX);
+
+ list_for_each_entry(lane, &padctl->lanes, list) {
+ if (lane->pad->ops->iddq_disable)
+ tegra210_uphy_lane_iddq_disable(lane);
+ }
+}
+
+static int tegra210_xusb_padctl_suspend_noirq(struct tegra_xusb_padctl *padctl)
+{
+ mutex_lock(&padctl->lock);
+
+ tegra210_uphy_deinit(padctl);
+
+ tegra210_xusb_padctl_save(padctl);
+
+ mutex_unlock(&padctl->lock);
+ return 0;
+}
+
+static int tegra210_xusb_padctl_resume_noirq(struct tegra_xusb_padctl *padctl)
+{
+ mutex_lock(&padctl->lock);
+
+ tegra210_xusb_padctl_restore(padctl);
+
+ tegra210_uphy_init(padctl);
+
+ mutex_unlock(&padctl->lock);
+ return 0;
+}
+
static const struct tegra_xusb_padctl_ops tegra210_xusb_padctl_ops = {
.probe = tegra210_xusb_padctl_probe,
.remove = tegra210_xusb_padctl_remove,
+ .suspend_noirq = tegra210_xusb_padctl_suspend_noirq,
+ .resume_noirq = tegra210_xusb_padctl_resume_noirq,
.usb3_set_lfps_detect = tegra210_usb3_set_lfps_detect,
.hsic_set_idle = tegra210_hsic_set_idle,
.vbus_override = tegra210_xusb_padctl_vbus_override,
diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index f98ec3922c02..dce45fbbd699 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/delay.h>
@@ -66,6 +66,12 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
.data = &tegra186_xusb_padctl_soc,
},
#endif
+#if defined(CONFIG_ARCH_TEGRA_194_SOC)
+ {
+ .compatible = "nvidia,tegra194-xusb-padctl",
+ .data = &tegra194_xusb_padctl_soc,
+ },
+#endif
{ }
};
MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match);
@@ -140,7 +146,7 @@ static void tegra_xusb_pad_release(struct device *dev)
pad->soc->ops->remove(pad);
}
-static struct device_type tegra_xusb_pad_type = {
+static const struct device_type tegra_xusb_pad_type = {
.release = tegra_xusb_pad_release,
};
@@ -315,11 +321,17 @@ static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane)
if (soc->num_funcs < 2)
return;
+ if (lane->pad->ops->iddq_enable)
+ lane->pad->ops->iddq_enable(lane);
+
/* choose function */
value = padctl_readl(padctl, soc->offset);
value &= ~(soc->mask << soc->shift);
value |= lane->function << soc->shift;
padctl_writel(padctl, value, soc->offset);
+
+ if (lane->pad->ops->iddq_disable)
+ lane->pad->ops->iddq_disable(lane);
}
static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad)
@@ -370,7 +382,7 @@ static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl)
return 0;
}
-static bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane,
+bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane,
const char *function)
{
const char *func = lane->soc->funcs[lane->function];
@@ -443,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);
@@ -501,9 +513,13 @@ tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl, unsigned int index)
static void tegra_xusb_port_release(struct device *dev)
{
+ struct tegra_xusb_port *port = to_tegra_xusb_port(dev);
+
+ if (port->ops->release)
+ port->ops->release(port);
}
-static struct device_type tegra_xusb_port_type = {
+static const struct device_type tegra_xusb_port_type = {
.release = tegra_xusb_port_release,
};
@@ -541,6 +557,16 @@ unregister:
static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
{
+ if (!IS_ERR_OR_NULL(port->usb_role_sw)) {
+ of_platform_depopulate(&port->dev);
+ usb_role_switch_unregister(port->usb_role_sw);
+ cancel_work_sync(&port->usb_phy_work);
+ usb_remove_phy(&port->usb_phy);
+ }
+
+ if (port->ops->remove)
+ port->ops->remove(port);
+
device_unregister(&port->dev);
}
@@ -551,11 +577,147 @@ static const char *const modes[] = {
[USB_DR_MODE_OTG] = "otg",
};
+static const char * const usb_roles[] = {
+ [USB_ROLE_NONE] = "none",
+ [USB_ROLE_HOST] = "host",
+ [USB_ROLE_DEVICE] = "device",
+};
+
+static enum usb_phy_events to_usb_phy_event(enum usb_role role)
+{
+ switch (role) {
+ case USB_ROLE_DEVICE:
+ return USB_EVENT_VBUS;
+
+ case USB_ROLE_HOST:
+ return USB_EVENT_ID;
+
+ default:
+ return USB_EVENT_NONE;
+ }
+}
+
+static void tegra_xusb_usb_phy_work(struct work_struct *work)
+{
+ struct tegra_xusb_port *port = container_of(work,
+ struct tegra_xusb_port,
+ usb_phy_work);
+ enum usb_role role = usb_role_switch_get_role(port->usb_role_sw);
+
+ usb_phy_set_event(&port->usb_phy, to_usb_phy_event(role));
+
+ dev_dbg(&port->dev, "%s(): calling notifier for role %s\n", __func__,
+ usb_roles[role]);
+
+ atomic_notifier_call_chain(&port->usb_phy.notifier, 0, &port->usb_phy);
+}
+
+static int tegra_xusb_role_sw_set(struct usb_role_switch *sw,
+ enum usb_role role)
+{
+ struct tegra_xusb_port *port = usb_role_switch_get_drvdata(sw);
+
+ dev_dbg(&port->dev, "%s(): role %s\n", __func__, usb_roles[role]);
+
+ schedule_work(&port->usb_phy_work);
+
+ return 0;
+}
+
+static int tegra_xusb_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *gadget)
+{
+ struct tegra_xusb_port *port = container_of(otg->usb_phy,
+ struct tegra_xusb_port,
+ usb_phy);
+
+ if (gadget != NULL)
+ schedule_work(&port->usb_phy_work);
+
+ return 0;
+}
+
+static int tegra_xusb_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+ struct tegra_xusb_port *port = container_of(otg->usb_phy,
+ struct tegra_xusb_port,
+ usb_phy);
+
+ if (host != NULL)
+ schedule_work(&port->usb_phy_work);
+
+ return 0;
+}
+
+
+static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
+{
+ struct tegra_xusb_lane *lane;
+ struct usb_role_switch_desc role_sx_desc = {
+ .fwnode = dev_fwnode(&port->dev),
+ .set = tegra_xusb_role_sw_set,
+ .allow_userspace_control = true,
+ };
+ int err = 0;
+
+ /*
+ * USB role switch driver needs parent driver owner info. This is a
+ * suboptimal solution. TODO: Need to revisit this in a follow-up patch
+ * where an optimal solution is possible with changes to USB role
+ * switch driver.
+ */
+ port->dev.driver = devm_kzalloc(&port->dev,
+ sizeof(struct device_driver),
+ GFP_KERNEL);
+ port->dev.driver->owner = THIS_MODULE;
+
+ port->usb_role_sw = usb_role_switch_register(&port->dev,
+ &role_sx_desc);
+ if (IS_ERR(port->usb_role_sw)) {
+ err = PTR_ERR(port->usb_role_sw);
+ dev_err(&port->dev, "failed to register USB role switch: %d",
+ err);
+ return err;
+ }
+
+ INIT_WORK(&port->usb_phy_work, tegra_xusb_usb_phy_work);
+ usb_role_switch_set_drvdata(port->usb_role_sw, port);
+
+ port->usb_phy.otg = devm_kzalloc(&port->dev, sizeof(struct usb_otg),
+ GFP_KERNEL);
+ if (!port->usb_phy.otg)
+ return -ENOMEM;
+
+ lane = tegra_xusb_find_lane(port->padctl, "usb2", port->index);
+
+ /*
+ * Assign phy dev to usb-phy dev. Host/device drivers can use phy
+ * reference to retrieve usb-phy details.
+ */
+ port->usb_phy.dev = &lane->pad->lanes[port->index]->dev;
+ port->usb_phy.dev->driver = port->dev.driver;
+ port->usb_phy.otg->usb_phy = &port->usb_phy;
+ port->usb_phy.otg->set_peripheral = tegra_xusb_set_peripheral;
+ port->usb_phy.otg->set_host = tegra_xusb_set_host;
+
+ err = usb_add_phy_dev(&port->usb_phy);
+ if (err < 0) {
+ dev_err(&port->dev, "Failed to add USB PHY: %d\n", err);
+ return err;
+ }
+
+ /* populate connector entry */
+ of_platform_populate(port->dev.of_node, NULL, NULL, &port->dev);
+
+ return err;
+}
+
static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
{
struct tegra_xusb_port *port = &usb2->base;
struct device_node *np = port->dev.of_node;
const char *mode;
+ int err;
usb2->internal = of_property_read_bool(np, "nvidia,internal");
@@ -572,7 +734,21 @@ static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
usb2->mode = USB_DR_MODE_HOST;
}
- usb2->supply = devm_regulator_get(&port->dev, "vbus");
+ /* usb-role-switch property is mandatory for OTG/Peripheral modes */
+ if (usb2->mode == USB_DR_MODE_PERIPHERAL ||
+ usb2->mode == USB_DR_MODE_OTG) {
+ if (of_property_read_bool(np, "usb-role-switch")) {
+ err = tegra_xusb_setup_usb_role_switch(port);
+ if (err < 0)
+ return err;
+ } else {
+ dev_err(&port->dev, "usb-role-switch not found for %s mode",
+ modes[usb2->mode]);
+ return -EINVAL;
+ }
+ }
+
+ usb2->supply = regulator_get(&port->dev, "vbus");
return PTR_ERR_OR_ZERO(usb2->supply);
}
@@ -591,7 +767,7 @@ static int tegra_xusb_add_usb2_port(struct tegra_xusb_padctl *padctl,
if (!np || !of_device_is_available(np))
goto out;
- usb2 = devm_kzalloc(padctl->dev, sizeof(*usb2), GFP_KERNEL);
+ usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
if (!usb2) {
err = -ENOMEM;
goto out;
@@ -622,6 +798,20 @@ out:
return err;
}
+void tegra_xusb_usb2_port_release(struct tegra_xusb_port *port)
+{
+ struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port);
+
+ kfree(usb2);
+}
+
+void tegra_xusb_usb2_port_remove(struct tegra_xusb_port *port)
+{
+ struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port);
+
+ regulator_put(usb2->supply);
+}
+
static int tegra_xusb_ulpi_port_parse_dt(struct tegra_xusb_ulpi_port *ulpi)
{
struct tegra_xusb_port *port = &ulpi->base;
@@ -643,7 +833,7 @@ static int tegra_xusb_add_ulpi_port(struct tegra_xusb_padctl *padctl,
if (!np || !of_device_is_available(np))
goto out;
- ulpi = devm_kzalloc(padctl->dev, sizeof(*ulpi), GFP_KERNEL);
+ ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
if (!ulpi) {
err = -ENOMEM;
goto out;
@@ -674,6 +864,13 @@ out:
return err;
}
+void tegra_xusb_ulpi_port_release(struct tegra_xusb_port *port)
+{
+ struct tegra_xusb_ulpi_port *ulpi = to_ulpi_port(port);
+
+ kfree(ulpi);
+}
+
static int tegra_xusb_hsic_port_parse_dt(struct tegra_xusb_hsic_port *hsic)
{
/* XXX */
@@ -691,7 +888,7 @@ static int tegra_xusb_add_hsic_port(struct tegra_xusb_padctl *padctl,
if (!np || !of_device_is_available(np))
goto out;
- hsic = devm_kzalloc(padctl->dev, sizeof(*hsic), GFP_KERNEL);
+ hsic = kzalloc(sizeof(*hsic), GFP_KERNEL);
if (!hsic) {
err = -ENOMEM;
goto out;
@@ -722,10 +919,18 @@ out:
return err;
}
+void tegra_xusb_hsic_port_release(struct tegra_xusb_port *port)
+{
+ struct tegra_xusb_hsic_port *hsic = to_hsic_port(port);
+
+ kfree(hsic);
+}
+
static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3)
{
struct tegra_xusb_port *port = &usb3->base;
struct device_node *np = port->dev.of_node;
+ enum usb_device_speed maximum_speed;
u32 value;
int err;
@@ -739,7 +944,17 @@ static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3)
usb3->internal = of_property_read_bool(np, "nvidia,internal");
- usb3->supply = devm_regulator_get(&port->dev, "vbus");
+ if (device_property_present(&port->dev, "maximum-speed")) {
+ maximum_speed = usb_get_maximum_speed(&port->dev);
+ if (maximum_speed == USB_SPEED_SUPER)
+ usb3->disable_gen2 = true;
+ else if (maximum_speed == USB_SPEED_SUPER_PLUS)
+ usb3->disable_gen2 = false;
+ else
+ return -EINVAL;
+ }
+
+ usb3->supply = regulator_get(&port->dev, "vbus");
return PTR_ERR_OR_ZERO(usb3->supply);
}
@@ -759,7 +974,7 @@ static int tegra_xusb_add_usb3_port(struct tegra_xusb_padctl *padctl,
if (!np || !of_device_is_available(np))
goto out;
- usb3 = devm_kzalloc(padctl->dev, sizeof(*usb3), GFP_KERNEL);
+ usb3 = kzalloc(sizeof(*usb3), GFP_KERNEL);
if (!usb3) {
err = -ENOMEM;
goto out;
@@ -790,6 +1005,20 @@ out:
return err;
}
+void tegra_xusb_usb3_port_release(struct tegra_xusb_port *port)
+{
+ struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
+
+ kfree(usb3);
+}
+
+void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port)
+{
+ struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
+
+ regulator_put(usb3->supply);
+}
+
static void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_port *port, *tmp;
@@ -926,7 +1155,6 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
const struct tegra_xusb_padctl_soc *soc;
struct tegra_xusb_padctl *padctl;
const struct of_device_id *match;
- struct resource *res;
int err;
/* for backwards compatibility with old device trees */
@@ -951,8 +1179,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&padctl->pads);
mutex_init(&padctl->lock);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- padctl->regs = devm_ioremap_resource(&pdev->dev, res);
+ padctl->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(padctl->regs)) {
err = PTR_ERR(padctl->regs);
goto remove;
@@ -978,7 +1205,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
err = devm_regulator_bulk_get(&pdev->dev, padctl->soc->num_supplies,
padctl->supplies);
if (err < 0) {
- dev_err(&pdev->dev, "failed to get regulators: %d\n", err);
+ dev_err_probe(&pdev->dev, err, "failed to get regulators\n");
goto remove;
}
@@ -1001,7 +1228,13 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
err = tegra_xusb_setup_ports(padctl);
if (err) {
- dev_err(&pdev->dev, "failed to setup XUSB ports: %d\n", err);
+ const char *level = KERN_ERR;
+
+ if (err == -EPROBE_DEFER)
+ level = KERN_DEBUG;
+
+ dev_printk(level, &pdev->dev,
+ dev_fmt("failed to setup XUSB ports: %d\n"), err);
goto remove_pads;
}
@@ -1014,6 +1247,7 @@ power_down:
reset:
reset_control_assert(padctl->rst);
remove:
+ platform_set_drvdata(pdev, NULL);
soc->ops->remove(padctl);
return err;
}
@@ -1037,13 +1271,39 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev)
padctl->soc->ops->remove(padctl);
- return err;
+ return 0;
}
+static __maybe_unused int tegra_xusb_padctl_suspend_noirq(struct device *dev)
+{
+ struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+ if (padctl->soc && padctl->soc->ops && padctl->soc->ops->suspend_noirq)
+ return padctl->soc->ops->suspend_noirq(padctl);
+
+ return 0;
+}
+
+static __maybe_unused int tegra_xusb_padctl_resume_noirq(struct device *dev)
+{
+ struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
+
+ if (padctl->soc && padctl->soc->ops && padctl->soc->ops->resume_noirq)
+ return padctl->soc->ops->resume_noirq(padctl);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra_xusb_padctl_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_xusb_padctl_suspend_noirq,
+ tegra_xusb_padctl_resume_noirq)
+};
+
static struct platform_driver tegra_xusb_padctl_driver = {
.driver = {
.name = "tegra-xusb-padctl",
.of_match_table = tegra_xusb_padctl_of_match,
+ .pm = &tegra_xusb_padctl_pm_ops,
},
.probe = tegra_xusb_padctl_probe,
.remove = tegra_xusb_padctl_remove,
@@ -1110,6 +1370,62 @@ int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl,
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle);
+int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy,
+ enum usb_device_speed speed)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ if (lane->pad->ops->enable_phy_sleepwalk)
+ return lane->pad->ops->enable_phy_sleepwalk(lane, speed);
+
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk);
+
+int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ if (lane->pad->ops->disable_phy_sleepwalk)
+ return lane->pad->ops->disable_phy_sleepwalk(lane);
+
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk);
+
+int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ if (lane->pad->ops->enable_phy_wake)
+ return lane->pad->ops->enable_phy_wake(lane);
+
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake);
+
+int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ if (lane->pad->ops->disable_phy_wake)
+ return lane->pad->ops->disable_phy_wake(lane);
+
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake);
+
+bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ if (lane->pad->ops->remote_wake_detected)
+ return lane->pad->ops->remote_wake_detected(lane);
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_remote_wake_detected);
+
int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
unsigned int port, bool enable)
{
@@ -1143,6 +1459,59 @@ int tegra_phy_xusb_utmi_port_reset(struct phy *phy)
}
EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_port_reset);
+void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane;
+ struct tegra_xusb_padctl *padctl;
+
+ if (!phy)
+ return;
+
+ lane = phy_get_drvdata(phy);
+ padctl = lane->pad->padctl;
+
+ if (padctl->soc->ops->utmi_pad_power_on)
+ padctl->soc->ops->utmi_pad_power_on(phy);
+}
+EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_on);
+
+void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane;
+ struct tegra_xusb_padctl *padctl;
+
+ if (!phy)
+ return;
+
+ lane = phy_get_drvdata(phy);
+ padctl = lane->pad->padctl;
+
+ if (padctl->soc->ops->utmi_pad_power_down)
+ padctl->soc->ops->utmi_pad_power_down(phy);
+}
+EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_down);
+
+int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl,
+ unsigned int port)
+{
+ struct tegra_xusb_usb2_port *usb2;
+ struct tegra_xusb_usb3_port *usb3;
+ int i;
+
+ usb2 = tegra_xusb_find_usb2_port(padctl, port);
+ if (!usb2)
+ return -EINVAL;
+
+ for (i = 0; i < padctl->soc->ports.usb3.count; i++) {
+ usb3 = tegra_xusb_find_usb3_port(padctl, i);
+ if (usb3 && usb3->port == usb2->base.index)
+ return usb3->base.index;
+ }
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_get_usb3_companion);
+
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index da94fcce6307..8cfbbdbd6e0c 100644
--- a/drivers/phy/tegra/xusb.h
+++ b/drivers/phy/tegra/xusb.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2015, Google Inc.
*/
@@ -11,7 +11,9 @@
#include <linux/mutex.h>
#include <linux/workqueue.h>
+#include <linux/usb/ch9.h>
#include <linux/usb/otg.h>
+#include <linux/usb/role.h>
/* legacy entry points for backwards-compatibility */
int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev);
@@ -34,6 +36,10 @@ struct tegra_xusb_lane_soc {
const char * const *funcs;
unsigned int num_funcs;
+
+ struct {
+ unsigned int misc_ctl2;
+ } regs;
};
struct tegra_xusb_lane {
@@ -125,8 +131,17 @@ struct tegra_xusb_lane_ops {
struct device_node *np,
unsigned int index);
void (*remove)(struct tegra_xusb_lane *lane);
+ void (*iddq_enable)(struct tegra_xusb_lane *lane);
+ void (*iddq_disable)(struct tegra_xusb_lane *lane);
+ int (*enable_phy_sleepwalk)(struct tegra_xusb_lane *lane, enum usb_device_speed speed);
+ int (*disable_phy_sleepwalk)(struct tegra_xusb_lane *lane);
+ int (*enable_phy_wake)(struct tegra_xusb_lane *lane);
+ int (*disable_phy_wake)(struct tegra_xusb_lane *lane);
+ bool (*remote_wake_detected)(struct tegra_xusb_lane *lane);
};
+bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function);
+
/*
* pads
*/
@@ -229,7 +244,7 @@ struct tegra_xusb_pcie_pad {
struct reset_control *rst;
struct clk *pll;
- unsigned int enable;
+ bool enable;
};
static inline struct tegra_xusb_pcie_pad *
@@ -244,7 +259,7 @@ struct tegra_xusb_sata_pad {
struct reset_control *rst;
struct clk *pll;
- unsigned int enable;
+ bool enable;
};
static inline struct tegra_xusb_sata_pad *
@@ -266,9 +281,18 @@ struct tegra_xusb_port {
struct list_head list;
struct device dev;
+ struct usb_role_switch *usb_role_sw;
+ struct work_struct usb_phy_work;
+ struct usb_phy usb_phy;
+
const struct tegra_xusb_port_ops *ops;
};
+static inline struct tegra_xusb_port *to_tegra_xusb_port(struct device *dev)
+{
+ return container_of(dev, struct tegra_xusb_port, dev);
+}
+
struct tegra_xusb_lane_map {
unsigned int port;
const char *type;
@@ -303,6 +327,8 @@ to_usb2_port(struct tegra_xusb_port *port)
struct tegra_xusb_usb2_port *
tegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl,
unsigned int index);
+void tegra_xusb_usb2_port_release(struct tegra_xusb_port *port);
+void tegra_xusb_usb2_port_remove(struct tegra_xusb_port *port);
struct tegra_xusb_ulpi_port {
struct tegra_xusb_port base;
@@ -317,6 +343,8 @@ to_ulpi_port(struct tegra_xusb_port *port)
return container_of(port, struct tegra_xusb_ulpi_port, base);
}
+void tegra_xusb_ulpi_port_release(struct tegra_xusb_port *port);
+
struct tegra_xusb_hsic_port {
struct tegra_xusb_port base;
};
@@ -327,12 +355,15 @@ to_hsic_port(struct tegra_xusb_port *port)
return container_of(port, struct tegra_xusb_hsic_port, base);
}
+void tegra_xusb_hsic_port_release(struct tegra_xusb_port *port);
+
struct tegra_xusb_usb3_port {
struct tegra_xusb_port base;
struct regulator *supply;
bool context_saved;
unsigned int port;
bool internal;
+ bool disable_gen2;
u32 tap1;
u32 amp;
@@ -349,8 +380,12 @@ to_usb3_port(struct tegra_xusb_port *port)
struct tegra_xusb_usb3_port *
tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl,
unsigned int index);
+void tegra_xusb_usb3_port_release(struct tegra_xusb_port *port);
+void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port);
struct tegra_xusb_port_ops {
+ void (*release)(struct tegra_xusb_port *port);
+ void (*remove)(struct tegra_xusb_port *port);
int (*enable)(struct tegra_xusb_port *port);
void (*disable)(struct tegra_xusb_port *port);
struct tegra_xusb_lane *(*map)(struct tegra_xusb_port *port);
@@ -367,6 +402,8 @@ struct tegra_xusb_padctl_ops {
const struct tegra_xusb_padctl_soc *soc);
void (*remove)(struct tegra_xusb_padctl *padctl);
+ int (*suspend_noirq)(struct tegra_xusb_padctl *padctl);
+ int (*resume_noirq)(struct tegra_xusb_padctl *padctl);
int (*usb3_save_context)(struct tegra_xusb_padctl *padctl,
unsigned int index);
int (*hsic_set_idle)(struct tegra_xusb_padctl *padctl,
@@ -375,6 +412,8 @@ struct tegra_xusb_padctl_ops {
unsigned int index, bool enable);
int (*vbus_override)(struct tegra_xusb_padctl *padctl, bool set);
int (*utmi_port_reset)(struct phy *phy);
+ void (*utmi_pad_power_on)(struct phy *phy);
+ void (*utmi_pad_power_down)(struct phy *phy);
};
struct tegra_xusb_padctl_soc {
@@ -392,6 +431,7 @@ struct tegra_xusb_padctl_soc {
const char * const *supply_names;
unsigned int num_supplies;
+ bool supports_gen2;
bool need_fake_usb3_port;
};
@@ -448,5 +488,8 @@ extern const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc;
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
extern const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc;
#endif
+#if defined(CONFIG_ARCH_TEGRA_194_SOC)
+extern const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc;
+#endif
#endif /* __PHY_TEGRA_XUSB_H */
diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig
index 6dbe9d0b9ff3..15a3bcf32308 100644
--- a/drivers/phy/ti/Kconfig
+++ b/drivers/phy/ti/Kconfig
@@ -106,11 +106,8 @@ config TWL4030_USB
config PHY_TI_GMII_SEL
tristate
- default y if TI_CPSW=y || TI_CPSW_SWITCHDEV=y
- depends on TI_CPSW || TI_CPSW_SWITCHDEV || COMPILE_TEST
select GENERIC_PHY
select REGMAP
- default m
help
This driver supports configuring of the TI CPSW Port mode depending on
the Ethernet PHY connected to the CPSW Port.
diff --git a/drivers/phy/ti/phy-am654-serdes.c b/drivers/phy/ti/phy-am654-serdes.c
index 88a047b9fa6f..0be727bb9f79 100644
--- a/drivers/phy/ti/phy-am654-serdes.c
+++ b/drivers/phy/ti/phy-am654-serdes.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* PCIe SERDES driver for AM654x SoC
*
* Copyright (C) 2018 - 2019 Texas Instruments Incorporated - http://www.ti.com/
@@ -19,15 +19,38 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+#define CMU_R004 0x4
+#define CMU_R060 0x60
#define CMU_R07C 0x7c
-
+#define CMU_R088 0x88
+#define CMU_R0D0 0xd0
+#define CMU_R0E8 0xe8
+
+#define LANE_R048 0x248
+#define LANE_R058 0x258
+#define LANE_R06c 0x26c
+#define LANE_R070 0x270
+#define LANE_R070 0x270
+#define LANE_R19C 0x39c
+
+#define COMLANE_R004 0xa04
#define COMLANE_R138 0xb38
-#define VERSION 0x70
+#define VERSION_VAL 0x70
#define COMLANE_R190 0xb90
-
#define COMLANE_R194 0xb94
+#define COMRXEQ_R004 0x1404
+#define COMRXEQ_R008 0x1408
+#define COMRXEQ_R00C 0x140c
+#define COMRXEQ_R014 0x1414
+#define COMRXEQ_R018 0x1418
+#define COMRXEQ_R01C 0x141c
+#define COMRXEQ_R04C 0x144c
+#define COMRXEQ_R088 0x1488
+#define COMRXEQ_R094 0x1494
+#define COMRXEQ_R098 0x1498
+
#define SERDES_CTRL 0x1fd0
#define WIZ_LANEXCTL_STS 0x1fe0
@@ -72,34 +95,144 @@ struct serdes_am654_clk_mux {
#define to_serdes_am654_clk_mux(_hw) \
container_of(_hw, struct serdes_am654_clk_mux, hw)
-static struct regmap_config serdes_am654_regmap_config = {
+static const struct regmap_config serdes_am654_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.fast_io = true,
+ .max_register = 0x1ffc,
};
-static const struct reg_field cmu_master_cdn_o = REG_FIELD(CMU_R07C, 24, 24);
-static const struct reg_field config_version = REG_FIELD(COMLANE_R138, 16, 23);
-static const struct reg_field l1_master_cdn_o = REG_FIELD(COMLANE_R190, 9, 9);
-static const struct reg_field cmu_ok_i_0 = REG_FIELD(COMLANE_R194, 19, 19);
-static const struct reg_field por_en = REG_FIELD(SERDES_CTRL, 29, 29);
-static const struct reg_field tx0_enable = REG_FIELD(WIZ_LANEXCTL_STS, 29, 31);
-static const struct reg_field rx0_enable = REG_FIELD(WIZ_LANEXCTL_STS, 13, 15);
-static const struct reg_field pll_enable = REG_FIELD(WIZ_PLL_CTRL, 29, 31);
-static const struct reg_field pll_ok = REG_FIELD(WIZ_PLL_CTRL, 28, 28);
+enum serdes_am654_fields {
+ /* CMU PLL Control */
+ CMU_PLL_CTRL,
+
+ LANE_PLL_CTRL_RXEQ_RXIDLE,
+
+ /* CMU VCO bias current and VREG setting */
+ AHB_PMA_CM_VCO_VBIAS_VREG,
+ AHB_PMA_CM_VCO_BIAS_VREG,
+
+ AHB_PMA_CM_SR,
+ AHB_SSC_GEN_Z_O_20_13,
+
+ /* AHB PMA Lane Configuration */
+ AHB_PMA_LN_AGC_THSEL_VREGH,
+
+ /* AGC and Signal detect threshold for Gen3 */
+ AHB_PMA_LN_GEN3_AGC_SD_THSEL,
+
+ AHB_PMA_LN_RX_SELR_GEN3,
+ AHB_PMA_LN_TX_DRV,
+
+ /* CMU Master Reset */
+ CMU_MASTER_CDN,
+
+ /* P2S ring buffer initial startup pointer difference */
+ P2S_RBUF_PTR_DIFF,
+
+ CONFIG_VERSION,
+
+ /* Lane 1 Master Reset */
+ L1_MASTER_CDN,
+
+ /* CMU OK Status */
+ CMU_OK_I_0,
+
+ /* Mid-speed initial calibration control */
+ COMRXEQ_MS_INIT_CTRL_7_0,
+
+ /* High-speed initial calibration control */
+ COMRXEQ_HS_INIT_CAL_7_0,
+
+ /* Mid-speed recalibration control */
+ COMRXEQ_MS_RECAL_CTRL_7_0,
+
+ /* High-speed recalibration control */
+ COMRXEQ_HS_RECAL_CTRL_7_0,
+
+ /* ATT configuration */
+ COMRXEQ_CSR_ATT_CONFIG,
+
+ /* Edge based boost adaptation window length */
+ COMRXEQ_CSR_EBSTADAPT_WIN_LEN,
+
+ /* COMRXEQ control 3 & 4 */
+ COMRXEQ_CTRL_3_4,
+
+ /* COMRXEQ control 14, 15 and 16*/
+ COMRXEQ_CTRL_14_15_16,
+
+ /* Threshold for errors in pattern data */
+ COMRXEQ_CSR_DLEV_ERR_THRESH,
+
+ /* COMRXEQ control 25 */
+ COMRXEQ_CTRL_25,
+
+ /* Mid-speed rate change calibration control */
+ CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O,
+
+ /* High-speed rate change calibration control */
+ COMRXEQ_HS_RCHANGE_CTRL_7_0,
+
+ /* Serdes reset */
+ POR_EN,
+
+ /* Tx Enable Value */
+ TX0_ENABLE,
+
+ /* Rx Enable Value */
+ RX0_ENABLE,
+
+ /* PLL Enable Value */
+ PLL_ENABLE,
+
+ /* PLL ready for use */
+ PLL_OK,
+
+ /* sentinel */
+ MAX_FIELDS
+
+};
+
+static const struct reg_field serdes_am654_reg_fields[] = {
+ [CMU_PLL_CTRL] = REG_FIELD(CMU_R004, 8, 15),
+ [AHB_PMA_CM_VCO_VBIAS_VREG] = REG_FIELD(CMU_R060, 8, 15),
+ [CMU_MASTER_CDN] = REG_FIELD(CMU_R07C, 24, 31),
+ [AHB_PMA_CM_VCO_BIAS_VREG] = REG_FIELD(CMU_R088, 24, 31),
+ [AHB_PMA_CM_SR] = REG_FIELD(CMU_R0D0, 24, 31),
+ [AHB_SSC_GEN_Z_O_20_13] = REG_FIELD(CMU_R0E8, 8, 15),
+ [LANE_PLL_CTRL_RXEQ_RXIDLE] = REG_FIELD(LANE_R048, 8, 15),
+ [AHB_PMA_LN_AGC_THSEL_VREGH] = REG_FIELD(LANE_R058, 16, 23),
+ [AHB_PMA_LN_GEN3_AGC_SD_THSEL] = REG_FIELD(LANE_R06c, 0, 7),
+ [AHB_PMA_LN_RX_SELR_GEN3] = REG_FIELD(LANE_R070, 16, 23),
+ [AHB_PMA_LN_TX_DRV] = REG_FIELD(LANE_R19C, 16, 23),
+ [P2S_RBUF_PTR_DIFF] = REG_FIELD(COMLANE_R004, 0, 7),
+ [CONFIG_VERSION] = REG_FIELD(COMLANE_R138, 16, 23),
+ [L1_MASTER_CDN] = REG_FIELD(COMLANE_R190, 8, 15),
+ [CMU_OK_I_0] = REG_FIELD(COMLANE_R194, 19, 19),
+ [COMRXEQ_MS_INIT_CTRL_7_0] = REG_FIELD(COMRXEQ_R004, 24, 31),
+ [COMRXEQ_HS_INIT_CAL_7_0] = REG_FIELD(COMRXEQ_R008, 0, 7),
+ [COMRXEQ_MS_RECAL_CTRL_7_0] = REG_FIELD(COMRXEQ_R00C, 8, 15),
+ [COMRXEQ_HS_RECAL_CTRL_7_0] = REG_FIELD(COMRXEQ_R00C, 16, 23),
+ [COMRXEQ_CSR_ATT_CONFIG] = REG_FIELD(COMRXEQ_R014, 16, 23),
+ [COMRXEQ_CSR_EBSTADAPT_WIN_LEN] = REG_FIELD(COMRXEQ_R018, 16, 23),
+ [COMRXEQ_CTRL_3_4] = REG_FIELD(COMRXEQ_R01C, 8, 15),
+ [COMRXEQ_CTRL_14_15_16] = REG_FIELD(COMRXEQ_R04C, 0, 7),
+ [COMRXEQ_CSR_DLEV_ERR_THRESH] = REG_FIELD(COMRXEQ_R088, 16, 23),
+ [COMRXEQ_CTRL_25] = REG_FIELD(COMRXEQ_R094, 24, 31),
+ [CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O] = REG_FIELD(COMRXEQ_R098, 8, 15),
+ [COMRXEQ_HS_RCHANGE_CTRL_7_0] = REG_FIELD(COMRXEQ_R098, 16, 23),
+ [POR_EN] = REG_FIELD(SERDES_CTRL, 29, 29),
+ [TX0_ENABLE] = REG_FIELD(WIZ_LANEXCTL_STS, 29, 31),
+ [RX0_ENABLE] = REG_FIELD(WIZ_LANEXCTL_STS, 13, 15),
+ [PLL_ENABLE] = REG_FIELD(WIZ_PLL_CTRL, 29, 31),
+ [PLL_OK] = REG_FIELD(WIZ_PLL_CTRL, 28, 28),
+};
struct serdes_am654 {
struct regmap *regmap;
- struct regmap_field *cmu_master_cdn_o;
- struct regmap_field *config_version;
- struct regmap_field *l1_master_cdn_o;
- struct regmap_field *cmu_ok_i_0;
- struct regmap_field *por_en;
- struct regmap_field *tx0_enable;
- struct regmap_field *rx0_enable;
- struct regmap_field *pll_enable;
- struct regmap_field *pll_ok;
+ struct regmap_field *fields[MAX_FIELDS];
struct device *dev;
struct mux_control *control;
@@ -115,12 +248,12 @@ static int serdes_am654_enable_pll(struct serdes_am654 *phy)
int ret;
u32 val;
- ret = regmap_field_write(phy->pll_enable, PLL_ENABLE_STATE);
+ ret = regmap_field_write(phy->fields[PLL_ENABLE], PLL_ENABLE_STATE);
if (ret)
return ret;
- return regmap_field_read_poll_timeout(phy->pll_ok, val, val, 1000,
- PLL_LOCK_TIME);
+ return regmap_field_read_poll_timeout(phy->fields[PLL_OK], val, val,
+ 1000, PLL_LOCK_TIME);
}
static void serdes_am654_disable_pll(struct serdes_am654 *phy)
@@ -128,41 +261,39 @@ static void serdes_am654_disable_pll(struct serdes_am654 *phy)
struct device *dev = phy->dev;
int ret;
- ret = regmap_field_write(phy->pll_enable, PLL_DISABLE_STATE);
+ ret = regmap_field_write(phy->fields[PLL_ENABLE], PLL_DISABLE_STATE);
if (ret)
dev_err(dev, "Failed to disable PLL\n");
}
static int serdes_am654_enable_txrx(struct serdes_am654 *phy)
{
- int ret;
+ int ret = 0;
/* Enable TX */
- ret = regmap_field_write(phy->tx0_enable, TX0_ENABLE_STATE);
- if (ret)
- return ret;
+ ret |= regmap_field_write(phy->fields[TX0_ENABLE], TX0_ENABLE_STATE);
/* Enable RX */
- ret = regmap_field_write(phy->rx0_enable, RX0_ENABLE_STATE);
+ ret |= regmap_field_write(phy->fields[RX0_ENABLE], RX0_ENABLE_STATE);
+
if (ret)
- return ret;
+ return -EIO;
return 0;
}
static int serdes_am654_disable_txrx(struct serdes_am654 *phy)
{
- int ret;
+ int ret = 0;
/* Disable TX */
- ret = regmap_field_write(phy->tx0_enable, TX0_DISABLE_STATE);
- if (ret)
- return ret;
+ ret |= regmap_field_write(phy->fields[TX0_ENABLE], TX0_DISABLE_STATE);
/* Disable RX */
- ret = regmap_field_write(phy->rx0_enable, RX0_DISABLE_STATE);
+ ret |= regmap_field_write(phy->fields[RX0_ENABLE], RX0_DISABLE_STATE);
+
if (ret)
- return ret;
+ return -EIO;
return 0;
}
@@ -186,8 +317,8 @@ static int serdes_am654_power_on(struct phy *x)
return ret;
}
- return regmap_field_read_poll_timeout(phy->cmu_ok_i_0, val, val,
- SLEEP_TIME, PLL_LOCK_TIME);
+ return regmap_field_read_poll_timeout(phy->fields[CMU_OK_I_0], val,
+ val, SLEEP_TIME, PLL_LOCK_TIME);
}
static int serdes_am654_power_off(struct phy *x)
@@ -200,40 +331,156 @@ static int serdes_am654_power_off(struct phy *x)
return 0;
}
-static int serdes_am654_init(struct phy *x)
+#define SERDES_AM654_CFG(offset, a, b, val) \
+ regmap_update_bits(phy->regmap, (offset),\
+ GENMASK((a), (b)), (val) << (b))
+
+static int serdes_am654_usb3_init(struct serdes_am654 *phy)
{
- struct serdes_am654 *phy = phy_get_drvdata(x);
- int ret;
+ SERDES_AM654_CFG(0x0000, 31, 24, 0x17);
+ SERDES_AM654_CFG(0x0004, 15, 8, 0x02);
+ SERDES_AM654_CFG(0x0004, 7, 0, 0x0e);
+ SERDES_AM654_CFG(0x0008, 23, 16, 0x2e);
+ SERDES_AM654_CFG(0x0008, 31, 24, 0x2e);
+ SERDES_AM654_CFG(0x0060, 7, 0, 0x4b);
+ SERDES_AM654_CFG(0x0060, 15, 8, 0x98);
+ SERDES_AM654_CFG(0x0060, 23, 16, 0x60);
+ SERDES_AM654_CFG(0x00d0, 31, 24, 0x45);
+ SERDES_AM654_CFG(0x00e8, 15, 8, 0x0e);
+ SERDES_AM654_CFG(0x0220, 7, 0, 0x34);
+ SERDES_AM654_CFG(0x0220, 15, 8, 0x34);
+ SERDES_AM654_CFG(0x0220, 31, 24, 0x37);
+ SERDES_AM654_CFG(0x0224, 7, 0, 0x37);
+ SERDES_AM654_CFG(0x0224, 15, 8, 0x37);
+ SERDES_AM654_CFG(0x0228, 23, 16, 0x37);
+ SERDES_AM654_CFG(0x0228, 31, 24, 0x37);
+ SERDES_AM654_CFG(0x022c, 7, 0, 0x37);
+ SERDES_AM654_CFG(0x022c, 15, 8, 0x37);
+ SERDES_AM654_CFG(0x0230, 15, 8, 0x2a);
+ SERDES_AM654_CFG(0x0230, 23, 16, 0x2a);
+ SERDES_AM654_CFG(0x0240, 23, 16, 0x10);
+ SERDES_AM654_CFG(0x0240, 31, 24, 0x34);
+ SERDES_AM654_CFG(0x0244, 7, 0, 0x40);
+ SERDES_AM654_CFG(0x0244, 23, 16, 0x34);
+ SERDES_AM654_CFG(0x0248, 15, 8, 0x0d);
+ SERDES_AM654_CFG(0x0258, 15, 8, 0x16);
+ SERDES_AM654_CFG(0x0258, 23, 16, 0x84);
+ SERDES_AM654_CFG(0x0258, 31, 24, 0xf2);
+ SERDES_AM654_CFG(0x025c, 7, 0, 0x21);
+ SERDES_AM654_CFG(0x0260, 7, 0, 0x27);
+ SERDES_AM654_CFG(0x0260, 15, 8, 0x04);
+ SERDES_AM654_CFG(0x0268, 15, 8, 0x04);
+ SERDES_AM654_CFG(0x0288, 15, 8, 0x2c);
+ SERDES_AM654_CFG(0x0330, 31, 24, 0xa0);
+ SERDES_AM654_CFG(0x0338, 23, 16, 0x03);
+ SERDES_AM654_CFG(0x0338, 31, 24, 0x00);
+ SERDES_AM654_CFG(0x033c, 7, 0, 0x00);
+ SERDES_AM654_CFG(0x0344, 31, 24, 0x18);
+ SERDES_AM654_CFG(0x034c, 7, 0, 0x18);
+ SERDES_AM654_CFG(0x039c, 23, 16, 0x3b);
+ SERDES_AM654_CFG(0x0a04, 7, 0, 0x03);
+ SERDES_AM654_CFG(0x0a14, 31, 24, 0x3c);
+ SERDES_AM654_CFG(0x0a18, 15, 8, 0x3c);
+ SERDES_AM654_CFG(0x0a38, 7, 0, 0x3e);
+ SERDES_AM654_CFG(0x0a38, 15, 8, 0x3e);
+ SERDES_AM654_CFG(0x0ae0, 7, 0, 0x07);
+ SERDES_AM654_CFG(0x0b6c, 23, 16, 0xcd);
+ SERDES_AM654_CFG(0x0b6c, 31, 24, 0x04);
+ SERDES_AM654_CFG(0x0b98, 23, 16, 0x03);
+ SERDES_AM654_CFG(0x1400, 7, 0, 0x3f);
+ SERDES_AM654_CFG(0x1404, 23, 16, 0x6f);
+ SERDES_AM654_CFG(0x1404, 31, 24, 0x6f);
+ SERDES_AM654_CFG(0x140c, 7, 0, 0x6f);
+ SERDES_AM654_CFG(0x140c, 15, 8, 0x6f);
+ SERDES_AM654_CFG(0x1410, 15, 8, 0x27);
+ SERDES_AM654_CFG(0x1414, 7, 0, 0x0c);
+ SERDES_AM654_CFG(0x1414, 23, 16, 0x07);
+ SERDES_AM654_CFG(0x1418, 23, 16, 0x40);
+ SERDES_AM654_CFG(0x141c, 7, 0, 0x00);
+ SERDES_AM654_CFG(0x141c, 15, 8, 0x1f);
+ SERDES_AM654_CFG(0x1428, 31, 24, 0x08);
+ SERDES_AM654_CFG(0x1434, 31, 24, 0x00);
+ SERDES_AM654_CFG(0x1444, 7, 0, 0x94);
+ SERDES_AM654_CFG(0x1460, 31, 24, 0x7f);
+ SERDES_AM654_CFG(0x1464, 7, 0, 0x43);
+ SERDES_AM654_CFG(0x1464, 23, 16, 0x6f);
+ SERDES_AM654_CFG(0x1464, 31, 24, 0x43);
+ SERDES_AM654_CFG(0x1484, 23, 16, 0x8f);
+ SERDES_AM654_CFG(0x1498, 7, 0, 0x4f);
+ SERDES_AM654_CFG(0x1498, 23, 16, 0x4f);
+ SERDES_AM654_CFG(0x007c, 31, 24, 0x0d);
+ SERDES_AM654_CFG(0x0b90, 15, 8, 0x0f);
- ret = regmap_field_write(phy->config_version, VERSION);
- if (ret)
- return ret;
+ return 0;
+}
- ret = regmap_field_write(phy->cmu_master_cdn_o, 0x1);
- if (ret)
- return ret;
+static int serdes_am654_pcie_init(struct serdes_am654 *phy)
+{
+ int ret = 0;
+
+ ret |= regmap_field_write(phy->fields[CMU_PLL_CTRL], 0x2);
+ ret |= regmap_field_write(phy->fields[AHB_PMA_CM_VCO_VBIAS_VREG], 0x98);
+ ret |= regmap_field_write(phy->fields[AHB_PMA_CM_VCO_BIAS_VREG], 0x98);
+ ret |= regmap_field_write(phy->fields[AHB_PMA_CM_SR], 0x45);
+ ret |= regmap_field_write(phy->fields[AHB_SSC_GEN_Z_O_20_13], 0xe);
+ ret |= regmap_field_write(phy->fields[LANE_PLL_CTRL_RXEQ_RXIDLE], 0x5);
+ ret |= regmap_field_write(phy->fields[AHB_PMA_LN_AGC_THSEL_VREGH], 0x83);
+ ret |= regmap_field_write(phy->fields[AHB_PMA_LN_GEN3_AGC_SD_THSEL], 0x83);
+ ret |= regmap_field_write(phy->fields[AHB_PMA_LN_RX_SELR_GEN3], 0x81);
+ ret |= regmap_field_write(phy->fields[AHB_PMA_LN_TX_DRV], 0x3b);
+ ret |= regmap_field_write(phy->fields[P2S_RBUF_PTR_DIFF], 0x3);
+ ret |= regmap_field_write(phy->fields[CONFIG_VERSION], VERSION_VAL);
+ ret |= regmap_field_write(phy->fields[COMRXEQ_MS_INIT_CTRL_7_0], 0xf);
+ ret |= regmap_field_write(phy->fields[COMRXEQ_HS_INIT_CAL_7_0], 0x4f);
+ ret |= regmap_field_write(phy->fields[COMRXEQ_MS_RECAL_CTRL_7_0], 0xf);
+ ret |= regmap_field_write(phy->fields[COMRXEQ_HS_RECAL_CTRL_7_0], 0x4f);
+ ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_ATT_CONFIG], 0x7);
+ ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_EBSTADAPT_WIN_LEN], 0x7f);
+ ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_3_4], 0xf);
+ ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_14_15_16], 0x9a);
+ ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_DLEV_ERR_THRESH], 0x32);
+ ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_25], 0x80);
+ ret |= regmap_field_write(phy->fields[CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O], 0xf);
+ ret |= regmap_field_write(phy->fields[COMRXEQ_HS_RCHANGE_CTRL_7_0], 0x4f);
+ ret |= regmap_field_write(phy->fields[CMU_MASTER_CDN], 0x1);
+ ret |= regmap_field_write(phy->fields[L1_MASTER_CDN], 0x2);
- ret = regmap_field_write(phy->l1_master_cdn_o, 0x1);
if (ret)
- return ret;
+ return -EIO;
return 0;
}
+static int serdes_am654_init(struct phy *x)
+{
+ struct serdes_am654 *phy = phy_get_drvdata(x);
+
+ switch (phy->type) {
+ case PHY_TYPE_PCIE:
+ return serdes_am654_pcie_init(phy);
+ case PHY_TYPE_USB3:
+ return serdes_am654_usb3_init(phy);
+ default:
+ return -EINVAL;
+ }
+}
+
static int serdes_am654_reset(struct phy *x)
{
struct serdes_am654 *phy = phy_get_drvdata(x);
- int ret;
+ int ret = 0;
- ret = regmap_field_write(phy->por_en, 0x1);
- if (ret)
- return ret;
+ serdes_am654_disable_pll(phy);
+ serdes_am654_disable_txrx(phy);
+
+ ret |= regmap_field_write(phy->fields[POR_EN], 0x1);
mdelay(1);
- ret = regmap_field_write(phy->por_en, 0x0);
+ ret |= regmap_field_write(phy->fields[POR_EN], 0x0);
+
if (ret)
- return ret;
+ return -EIO;
return 0;
}
@@ -487,66 +734,16 @@ static int serdes_am654_regfield_init(struct serdes_am654 *am654_phy)
{
struct regmap *regmap = am654_phy->regmap;
struct device *dev = am654_phy->dev;
+ int i;
- am654_phy->cmu_master_cdn_o = devm_regmap_field_alloc(dev, regmap,
- cmu_master_cdn_o);
- if (IS_ERR(am654_phy->cmu_master_cdn_o)) {
- dev_err(dev, "CMU_MASTER_CDN_O reg field init failed\n");
- return PTR_ERR(am654_phy->cmu_master_cdn_o);
- }
-
- am654_phy->config_version = devm_regmap_field_alloc(dev, regmap,
- config_version);
- if (IS_ERR(am654_phy->config_version)) {
- dev_err(dev, "CONFIG_VERSION reg field init failed\n");
- return PTR_ERR(am654_phy->config_version);
- }
-
- am654_phy->l1_master_cdn_o = devm_regmap_field_alloc(dev, regmap,
- l1_master_cdn_o);
- if (IS_ERR(am654_phy->l1_master_cdn_o)) {
- dev_err(dev, "L1_MASTER_CDN_O reg field init failed\n");
- return PTR_ERR(am654_phy->l1_master_cdn_o);
- }
-
- am654_phy->cmu_ok_i_0 = devm_regmap_field_alloc(dev, regmap,
- cmu_ok_i_0);
- if (IS_ERR(am654_phy->cmu_ok_i_0)) {
- dev_err(dev, "CMU_OK_I_0 reg field init failed\n");
- return PTR_ERR(am654_phy->cmu_ok_i_0);
- }
-
- am654_phy->por_en = devm_regmap_field_alloc(dev, regmap, por_en);
- if (IS_ERR(am654_phy->por_en)) {
- dev_err(dev, "POR_EN reg field init failed\n");
- return PTR_ERR(am654_phy->por_en);
- }
-
- am654_phy->tx0_enable = devm_regmap_field_alloc(dev, regmap,
- tx0_enable);
- if (IS_ERR(am654_phy->tx0_enable)) {
- dev_err(dev, "TX0_ENABLE reg field init failed\n");
- return PTR_ERR(am654_phy->tx0_enable);
- }
-
- am654_phy->rx0_enable = devm_regmap_field_alloc(dev, regmap,
- rx0_enable);
- if (IS_ERR(am654_phy->rx0_enable)) {
- dev_err(dev, "RX0_ENABLE reg field init failed\n");
- return PTR_ERR(am654_phy->rx0_enable);
- }
-
- am654_phy->pll_enable = devm_regmap_field_alloc(dev, regmap,
- pll_enable);
- if (IS_ERR(am654_phy->pll_enable)) {
- dev_err(dev, "PLL_ENABLE reg field init failed\n");
- return PTR_ERR(am654_phy->pll_enable);
- }
-
- am654_phy->pll_ok = devm_regmap_field_alloc(dev, regmap, pll_ok);
- if (IS_ERR(am654_phy->pll_ok)) {
- dev_err(dev, "PLL_OK reg field init failed\n");
- return PTR_ERR(am654_phy->pll_ok);
+ for (i = 0; i < MAX_FIELDS; i++) {
+ am654_phy->fields[i] = devm_regmap_field_alloc(dev,
+ regmap,
+ serdes_am654_reg_fields[i]);
+ if (IS_ERR(am654_phy->fields[i])) {
+ dev_err(dev, "Unable to allocate regmap field %d\n", i);
+ return PTR_ERR(am654_phy->fields[i]);
+ }
}
return 0;
@@ -625,8 +822,10 @@ static int serdes_am654_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
phy = devm_phy_create(dev, NULL, &ops);
- if (IS_ERR(phy))
- return PTR_ERR(phy);
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ goto clk_err;
+ }
phy_set_drvdata(phy, am654_phy);
phy_provider = devm_of_phy_provider_register(dev, serdes_am654_xlate);
@@ -639,7 +838,7 @@ static int serdes_am654_probe(struct platform_device *pdev)
clk_err:
of_clk_del_provider(node);
-
+ pm_runtime_disable(dev);
return ret;
}
diff --git a/drivers/phy/ti/phy-dm816x-usb.c b/drivers/phy/ti/phy-dm816x-usb.c
index 26f194779064..fb619908f912 100644
--- a/drivers/phy/ti/phy-dm816x-usb.c
+++ b/drivers/phy/ti/phy-dm816x-usb.c
@@ -1,13 +1,4 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -82,17 +73,16 @@ static int dm816x_usb_phy_init(struct phy *x)
{
struct dm816x_usb_phy *phy = phy_get_drvdata(x);
unsigned int val;
- int error;
if (clk_get_rate(phy->refclk) != 24000000)
dev_warn(phy->dev, "nonstandard phy refclk\n");
/* Set PLL ref clock and put phys to sleep */
- error = regmap_update_bits(phy->syscon, phy->usb_ctrl,
- DM816X_USB_CTRL_PHYCLKSRC |
- DM816X_USB_CTRL_PHYSLEEP1 |
- DM816X_USB_CTRL_PHYSLEEP0,
- 0);
+ regmap_update_bits(phy->syscon, phy->usb_ctrl,
+ DM816X_USB_CTRL_PHYCLKSRC |
+ DM816X_USB_CTRL_PHYSLEEP1 |
+ DM816X_USB_CTRL_PHYSLEEP0,
+ 0);
regmap_read(phy->syscon, phy->usb_ctrl, &val);
if ((val & 3) != 0)
dev_info(phy->dev,
@@ -243,19 +233,28 @@ static int dm816x_usb_phy_probe(struct platform_device *pdev)
pm_runtime_enable(phy->dev);
generic_phy = devm_phy_create(phy->dev, NULL, &ops);
- if (IS_ERR(generic_phy))
- return PTR_ERR(generic_phy);
+ if (IS_ERR(generic_phy)) {
+ error = PTR_ERR(generic_phy);
+ goto clk_unprepare;
+ }
phy_set_drvdata(generic_phy, phy);
phy_provider = devm_of_phy_provider_register(phy->dev,
of_phy_simple_xlate);
- if (IS_ERR(phy_provider))
- return PTR_ERR(phy_provider);
+ if (IS_ERR(phy_provider)) {
+ error = PTR_ERR(phy_provider);
+ goto clk_unprepare;
+ }
usb_add_phy_dev(&phy->phy);
return 0;
+
+clk_unprepare:
+ pm_runtime_disable(phy->dev);
+ clk_unprepare(phy->refclk);
+ return error;
}
static int dm816x_usb_phy_remove(struct platform_device *pdev)
diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c
index 1c536fc03c83..0bcfd6d96b4d 100644
--- a/drivers/phy/ti/phy-gmii-sel.c
+++ b/drivers/phy/ti/phy-gmii-sel.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/phy/phy.h>
@@ -21,8 +22,14 @@
#define AM33XX_GMII_SEL_MODE_RMII 1
#define AM33XX_GMII_SEL_MODE_RGMII 2
+/* J72xx SoC specific definitions for the CONTROL port */
+#define J72XX_GMII_SEL_MODE_QSGMII 4
+#define J72XX_GMII_SEL_MODE_QSGMII_SUB 6
+
+#define PHY_GMII_PORT(n) BIT((n) - 1)
+
enum {
- PHY_GMII_SEL_PORT_MODE,
+ PHY_GMII_SEL_PORT_MODE = 0,
PHY_GMII_SEL_RGMII_ID_MODE,
PHY_GMII_SEL_RMII_IO_CLK_EN,
PHY_GMII_SEL_LAST,
@@ -41,6 +48,8 @@ struct phy_gmii_sel_soc_data {
u32 num_ports;
u32 features;
const struct reg_field (*regfields)[PHY_GMII_SEL_LAST];
+ bool use_of_data;
+ u64 extra_modes;
};
struct phy_gmii_sel_priv {
@@ -49,6 +58,9 @@ struct phy_gmii_sel_priv {
struct regmap *regmap;
struct phy_provider *phy_provider;
struct phy_gmii_sel_phy_priv *if_phys;
+ u32 num_ports;
+ u32 reg_offset;
+ u32 qsgmii_main_ports;
};
static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode)
@@ -84,10 +96,17 @@ static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode)
gmii_sel_mode = AM33XX_GMII_SEL_MODE_MII;
break;
+ case PHY_INTERFACE_MODE_QSGMII:
+ if (!(soc_data->extra_modes & BIT(PHY_INTERFACE_MODE_QSGMII)))
+ goto unsupported;
+ if (if_phy->priv->qsgmii_main_ports & BIT(if_phy->id - 1))
+ gmii_sel_mode = J72XX_GMII_SEL_MODE_QSGMII;
+ else
+ gmii_sel_mode = J72XX_GMII_SEL_MODE_QSGMII_SUB;
+ break;
+
default:
- dev_warn(dev, "port%u: unsupported mode: \"%s\"\n",
- if_phy->id, phy_modes(submode));
- return -EINVAL;
+ goto unsupported;
}
if_phy->phy_if_mode = submode;
@@ -119,6 +138,11 @@ static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode)
}
return 0;
+
+unsupported:
+ dev_warn(dev, "port%u: unsupported mode: \"%s\"\n",
+ if_phy->id, phy_modes(submode));
+ return -EINVAL;
}
static const
@@ -147,13 +171,9 @@ static const
struct reg_field phy_gmii_sel_fields_dra7[][PHY_GMII_SEL_LAST] = {
{
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 0, 1),
- [PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
- [PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
},
{
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 4, 5),
- [PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
- [PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
},
};
@@ -170,6 +190,31 @@ struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dm814 = {
.regfields = phy_gmii_sel_fields_am33xx,
};
+static const
+struct reg_field phy_gmii_sel_fields_am654[][PHY_GMII_SEL_LAST] = {
+ { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x0, 0, 2), },
+ { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x4, 0, 2), },
+ { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x8, 0, 2), },
+ { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0xC, 0, 2), },
+ { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x10, 0, 2), },
+ { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x14, 0, 2), },
+ { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x18, 0, 2), },
+ { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x1C, 0, 2), },
+};
+
+static const
+struct phy_gmii_sel_soc_data phy_gmii_sel_soc_am654 = {
+ .use_of_data = true,
+ .regfields = phy_gmii_sel_fields_am654,
+};
+
+static const
+struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw5g_soc_j7200 = {
+ .use_of_data = true,
+ .regfields = phy_gmii_sel_fields_am654,
+ .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII),
+};
+
static const struct of_device_id phy_gmii_sel_id_table[] = {
{
.compatible = "ti,am3352-phy-gmii-sel",
@@ -187,6 +232,14 @@ static const struct of_device_id phy_gmii_sel_id_table[] = {
.compatible = "ti,dm814-phy-gmii-sel",
.data = &phy_gmii_sel_soc_dm814,
},
+ {
+ .compatible = "ti,am654-phy-gmii-sel",
+ .data = &phy_gmii_sel_soc_am654,
+ },
+ {
+ .compatible = "ti,j7200-cpsw5g-phy-gmii-sel",
+ .data = &phy_gmii_sel_cpsw5g_soc_j7200,
+ },
{}
};
MODULE_DEVICE_TABLE(of, phy_gmii_sel_id_table);
@@ -209,7 +262,7 @@ static struct phy *phy_gmii_sel_of_xlate(struct device *dev,
if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) &&
args->args_count < 2)
return ERR_PTR(-EINVAL);
- if (phy_id > priv->soc_data->num_ports)
+ if (phy_id > priv->num_ports)
return ERR_PTR(-EINVAL);
if (phy_id != priv->if_phys[phy_id - 1].id)
return ERR_PTR(-EINVAL);
@@ -223,68 +276,99 @@ static struct phy *phy_gmii_sel_of_xlate(struct device *dev,
return priv->if_phys[phy_id].if_phy;
}
-static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv)
+static int phy_gmii_init_phy(struct phy_gmii_sel_priv *priv, int port,
+ struct phy_gmii_sel_phy_priv *if_phy)
{
const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data;
struct device *dev = priv->dev;
- struct phy_gmii_sel_phy_priv *if_phys;
- int i, num_ports, ret;
+ const struct reg_field *fields;
+ struct regmap_field *regfield;
+ struct reg_field field;
+ int ret;
- num_ports = priv->soc_data->num_ports;
+ if_phy->id = port;
+ if_phy->priv = priv;
+
+ fields = soc_data->regfields[port - 1];
+ field = *fields++;
+ field.reg += priv->reg_offset;
+ dev_dbg(dev, "%s field %x %d %d\n", __func__,
+ field.reg, field.msb, field.lsb);
+
+ regfield = devm_regmap_field_alloc(dev, priv->regmap, field);
+ if (IS_ERR(regfield))
+ return PTR_ERR(regfield);
+ if_phy->fields[PHY_GMII_SEL_PORT_MODE] = regfield;
+
+ field = *fields++;
+ field.reg += priv->reg_offset;
+ if (soc_data->features & BIT(PHY_GMII_SEL_RGMII_ID_MODE)) {
+ regfield = devm_regmap_field_alloc(dev,
+ priv->regmap,
+ field);
+ if (IS_ERR(regfield))
+ return PTR_ERR(regfield);
+ if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE] = regfield;
+ dev_dbg(dev, "%s field %x %d %d\n", __func__,
+ field.reg, field.msb, field.lsb);
+ }
- if_phys = devm_kcalloc(priv->dev, num_ports,
- sizeof(*if_phys), GFP_KERNEL);
- if (!if_phys)
- return -ENOMEM;
- dev_dbg(dev, "%s %d\n", __func__, num_ports);
+ field = *fields;
+ field.reg += priv->reg_offset;
+ if (soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN)) {
+ regfield = devm_regmap_field_alloc(dev,
+ priv->regmap,
+ field);
+ if (IS_ERR(regfield))
+ return PTR_ERR(regfield);
+ if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN] = regfield;
+ dev_dbg(dev, "%s field %x %d %d\n", __func__,
+ field.reg, field.msb, field.lsb);
+ }
- for (i = 0; i < num_ports; i++) {
- const struct reg_field *field;
- struct regmap_field *regfield;
+ if_phy->if_phy = devm_phy_create(dev,
+ priv->dev->of_node,
+ &phy_gmii_sel_ops);
+ if (IS_ERR(if_phy->if_phy)) {
+ ret = PTR_ERR(if_phy->if_phy);
+ dev_err(dev, "Failed to create phy%d %d\n", port, ret);
+ return ret;
+ }
+ phy_set_drvdata(if_phy->if_phy, if_phy);
- if_phys[i].id = i + 1;
- if_phys[i].priv = priv;
+ return 0;
+}
- field = &soc_data->regfields[i][PHY_GMII_SEL_PORT_MODE];
- dev_dbg(dev, "%s field %x %d %d\n", __func__,
- field->reg, field->msb, field->lsb);
+static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv)
+{
+ const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data;
+ struct phy_gmii_sel_phy_priv *if_phys;
+ struct device *dev = priv->dev;
+ int i, ret;
+
+ if (soc_data->use_of_data) {
+ const __be32 *offset;
+ u64 size;
+
+ offset = of_get_address(dev->of_node, 0, &size, NULL);
+ if (!offset)
+ return -EINVAL;
+ priv->num_ports = size / sizeof(u32);
+ if (!priv->num_ports)
+ return -EINVAL;
+ priv->reg_offset = __be32_to_cpu(*offset);
+ }
- regfield = devm_regmap_field_alloc(dev, priv->regmap, *field);
- if (IS_ERR(regfield))
- return PTR_ERR(regfield);
- if_phys[i].fields[PHY_GMII_SEL_PORT_MODE] = regfield;
-
- field = &soc_data->regfields[i][PHY_GMII_SEL_RGMII_ID_MODE];
- if (field->reg != (~0)) {
- regfield = devm_regmap_field_alloc(dev,
- priv->regmap,
- *field);
- if (IS_ERR(regfield))
- return PTR_ERR(regfield);
- if_phys[i].fields[PHY_GMII_SEL_RGMII_ID_MODE] =
- regfield;
- }
-
- field = &soc_data->regfields[i][PHY_GMII_SEL_RMII_IO_CLK_EN];
- if (field->reg != (~0)) {
- regfield = devm_regmap_field_alloc(dev,
- priv->regmap,
- *field);
- if (IS_ERR(regfield))
- return PTR_ERR(regfield);
- if_phys[i].fields[PHY_GMII_SEL_RMII_IO_CLK_EN] =
- regfield;
- }
-
- if_phys[i].if_phy = devm_phy_create(dev,
- priv->dev->of_node,
- &phy_gmii_sel_ops);
- if (IS_ERR(if_phys[i].if_phy)) {
- ret = PTR_ERR(if_phys[i].if_phy);
- dev_err(dev, "Failed to create phy%d %d\n", i, ret);
+ if_phys = devm_kcalloc(dev, priv->num_ports,
+ sizeof(*if_phys), GFP_KERNEL);
+ if (!if_phys)
+ return -ENOMEM;
+ dev_dbg(dev, "%s %d\n", __func__, priv->num_ports);
+
+ for (i = 0; i < priv->num_ports; i++) {
+ ret = phy_gmii_init_phy(priv, i + 1, &if_phys[i]);
+ if (ret)
return ret;
- }
- phy_set_drvdata(if_phys[i].if_phy, &if_phys[i]);
}
priv->if_phys = if_phys;
@@ -297,6 +381,7 @@ static int phy_gmii_sel_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
const struct of_device_id *of_id;
struct phy_gmii_sel_priv *priv;
+ u32 main_ports = 1;
int ret;
of_id = of_match_node(phy_gmii_sel_id_table, pdev->dev.of_node);
@@ -309,6 +394,16 @@ static int phy_gmii_sel_probe(struct platform_device *pdev)
priv->dev = &pdev->dev;
priv->soc_data = of_id->data;
+ priv->num_ports = priv->soc_data->num_ports;
+ of_property_read_u32(node, "ti,qsgmii-main-ports", &main_ports);
+ /*
+ * Ensure that main_ports is within bounds. If the property
+ * ti,qsgmii-main-ports is not mentioned, or the value mentioned
+ * is out of bounds, default to 1.
+ */
+ if (main_ports < 1 || main_ports > 4)
+ main_ports = 1;
+ priv->qsgmii_main_ports = PHY_GMII_PORT(main_ports);
priv->regmap = syscon_node_to_regmap(node->parent);
if (IS_ERR(priv->regmap)) {
diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c
index 7b51045df783..41725c6bcdf6 100644
--- a/drivers/phy/ti/phy-j721e-wiz.c
+++ b/drivers/phy/ti/phy-j721e-wiz.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* Wrapper driver for SERDES used in J721E
*
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
@@ -7,12 +7,15 @@
*/
#include <dt-bindings/phy/phy.h>
+#include <dt-bindings/phy/phy-ti.h>
+#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/mfd/syscon.h>
#include <linux/mux/consumer.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
@@ -21,11 +24,25 @@
#include <linux/regmap.h>
#include <linux/reset-controller.h>
+#define REF_CLK_19_2MHZ 19200000
+#define REF_CLK_25MHZ 25000000
+#define REF_CLK_100MHZ 100000000
+#define REF_CLK_156_25MHZ 156250000
+
+/* SCM offsets */
+#define SERDES_SUP_CTRL 0x4400
+
+/* SERDES offsets */
#define WIZ_SERDES_CTRL 0x404
#define WIZ_SERDES_TOP_CTRL 0x408
#define WIZ_SERDES_RST 0x40c
#define WIZ_SERDES_TYPEC 0x410
#define WIZ_LANECTL(n) (0x480 + (0x40 * (n)))
+#define WIZ_LANEDIV(n) (0x484 + (0x40 * (n)))
+
+#define WIZ_MAX_INPUT_CLOCKS 4
+/* To include mux clocks, divider clocks and gate clocks */
+#define WIZ_MAX_OUTPUT_CLOCKS 32
#define WIZ_MAX_LANES 4
#define WIZ_MUX_NUM_CLOCKS 3
@@ -52,8 +69,16 @@ enum wiz_refclk_div_sel {
CMN_REFCLK1_DIG_DIV,
};
+enum wiz_clock_input {
+ WIZ_CORE_REFCLK,
+ WIZ_EXT_REFCLK,
+ WIZ_CORE_REFCLK1,
+ WIZ_EXT_REFCLK1,
+};
+
static const struct reg_field por_en = REG_FIELD(WIZ_SERDES_CTRL, 31, 31);
static const struct reg_field phy_reset_n = REG_FIELD(WIZ_SERDES_RST, 31, 31);
+static const struct reg_field phy_en_refclk = REG_FIELD(WIZ_SERDES_RST, 30, 30);
static const struct reg_field pll1_refclk_mux_sel =
REG_FIELD(WIZ_SERDES_RST, 29, 29);
static const struct reg_field pll0_refclk_mux_sel =
@@ -71,6 +96,24 @@ static const struct reg_field pma_cmn_refclk_dig_div =
static const struct reg_field pma_cmn_refclk1_dig_div =
REG_FIELD(WIZ_SERDES_TOP_CTRL, 24, 25);
+static const struct reg_field sup_pll0_refclk_mux_sel =
+ REG_FIELD(SERDES_SUP_CTRL, 0, 1);
+static const struct reg_field sup_pll1_refclk_mux_sel =
+ REG_FIELD(SERDES_SUP_CTRL, 2, 3);
+static const struct reg_field sup_pma_cmn_refclk1_int_mode =
+ REG_FIELD(SERDES_SUP_CTRL, 4, 5);
+static const struct reg_field sup_refclk_dig_sel_10g =
+ REG_FIELD(SERDES_SUP_CTRL, 6, 7);
+static const struct reg_field sup_legacy_clk_override =
+ REG_FIELD(SERDES_SUP_CTRL, 8, 8);
+
+static const char * const output_clk_names[] = {
+ [TI_WIZ_PLL0_REFCLK] = "pll0-refclk",
+ [TI_WIZ_PLL1_REFCLK] = "pll1-refclk",
+ [TI_WIZ_REFCLK_DIG] = "refclk-dig",
+ [TI_WIZ_PHY_EN_REFCLK] = "phy-en-refclk",
+};
+
static const struct reg_field p_enable[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(0), 30, 31),
REG_FIELD(WIZ_LANECTL(1), 30, 31),
@@ -78,6 +121,8 @@ static const struct reg_field p_enable[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(3), 30, 31),
};
+enum p_enable { P_ENABLE = 2, P_ENABLE_FORCE = 1, P_ENABLE_DISABLE = 0 };
+
static const struct reg_field p_align[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(0), 29, 29),
REG_FIELD(WIZ_LANECTL(1), 29, 29),
@@ -99,13 +144,54 @@ static const struct reg_field p_standard_mode[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(3), 24, 25),
};
+static const struct reg_field p0_fullrt_div[WIZ_MAX_LANES] = {
+ REG_FIELD(WIZ_LANECTL(0), 22, 23),
+ REG_FIELD(WIZ_LANECTL(1), 22, 23),
+ REG_FIELD(WIZ_LANECTL(2), 22, 23),
+ REG_FIELD(WIZ_LANECTL(3), 22, 23),
+};
+
+static const struct reg_field p0_mac_src_sel[WIZ_MAX_LANES] = {
+ REG_FIELD(WIZ_LANECTL(0), 20, 21),
+ REG_FIELD(WIZ_LANECTL(1), 20, 21),
+ REG_FIELD(WIZ_LANECTL(2), 20, 21),
+ REG_FIELD(WIZ_LANECTL(3), 20, 21),
+};
+
+static const struct reg_field p0_rxfclk_sel[WIZ_MAX_LANES] = {
+ REG_FIELD(WIZ_LANECTL(0), 6, 7),
+ REG_FIELD(WIZ_LANECTL(1), 6, 7),
+ REG_FIELD(WIZ_LANECTL(2), 6, 7),
+ REG_FIELD(WIZ_LANECTL(3), 6, 7),
+};
+
+static const struct reg_field p0_refclk_sel[WIZ_MAX_LANES] = {
+ REG_FIELD(WIZ_LANECTL(0), 18, 19),
+ REG_FIELD(WIZ_LANECTL(1), 18, 19),
+ REG_FIELD(WIZ_LANECTL(2), 18, 19),
+ REG_FIELD(WIZ_LANECTL(3), 18, 19),
+};
+static const struct reg_field p_mac_div_sel0[WIZ_MAX_LANES] = {
+ REG_FIELD(WIZ_LANEDIV(0), 16, 22),
+ REG_FIELD(WIZ_LANEDIV(1), 16, 22),
+ REG_FIELD(WIZ_LANEDIV(2), 16, 22),
+ REG_FIELD(WIZ_LANEDIV(3), 16, 22),
+};
+
+static const struct reg_field p_mac_div_sel1[WIZ_MAX_LANES] = {
+ REG_FIELD(WIZ_LANEDIV(0), 0, 8),
+ REG_FIELD(WIZ_LANEDIV(1), 0, 8),
+ REG_FIELD(WIZ_LANEDIV(2), 0, 8),
+ REG_FIELD(WIZ_LANEDIV(3), 0, 8),
+};
+
static const struct reg_field typec_ln10_swap =
REG_FIELD(WIZ_SERDES_TYPEC, 30, 30);
struct wiz_clk_mux {
struct clk_hw hw;
struct regmap_field *field;
- u32 *table;
+ const u32 *table;
struct clk_init_data clk_data;
};
@@ -114,25 +200,33 @@ struct wiz_clk_mux {
struct wiz_clk_divider {
struct clk_hw hw;
struct regmap_field *field;
- struct clk_div_table *table;
+ const struct clk_div_table *table;
struct clk_init_data clk_data;
};
#define to_wiz_clk_div(_hw) container_of(_hw, struct wiz_clk_divider, hw)
struct wiz_clk_mux_sel {
- struct regmap_field *field;
- u32 table[4];
+ u32 table[WIZ_MAX_INPUT_CLOCKS];
const char *node_name;
+ u32 num_parents;
+ u32 parents[WIZ_MAX_INPUT_CLOCKS];
};
struct wiz_clk_div_sel {
- struct regmap_field *field;
- struct clk_div_table *table;
+ const struct clk_div_table *table;
const char *node_name;
};
-static struct wiz_clk_mux_sel clk_mux_sel_16g[] = {
+struct wiz_phy_en_refclk {
+ struct clk_hw hw;
+ struct regmap_field *phy_en_refclk;
+ struct clk_init_data clk_data;
+};
+
+#define to_wiz_phy_en_refclk(_hw) container_of(_hw, struct wiz_phy_en_refclk, hw)
+
+static const struct wiz_clk_mux_sel clk_mux_sel_16g[] = {
{
/*
* Mux value to be configured for each of the input clocks
@@ -151,33 +245,61 @@ static struct wiz_clk_mux_sel clk_mux_sel_16g[] = {
},
};
-static struct wiz_clk_mux_sel clk_mux_sel_10g[] = {
+static const struct wiz_clk_mux_sel clk_mux_sel_10g[] = {
{
/*
* Mux value to be configured for each of the input clocks
* in the order populated in device tree
*/
+ .num_parents = 2,
+ .parents = { WIZ_CORE_REFCLK, WIZ_EXT_REFCLK },
.table = { 1, 0 },
.node_name = "pll0-refclk",
},
{
+ .num_parents = 2,
+ .parents = { WIZ_CORE_REFCLK, WIZ_EXT_REFCLK },
.table = { 1, 0 },
.node_name = "pll1-refclk",
},
{
+ .num_parents = 2,
+ .parents = { WIZ_CORE_REFCLK, WIZ_EXT_REFCLK },
.table = { 1, 0 },
.node_name = "refclk-dig",
},
};
-static struct clk_div_table clk_div_table[] = {
+static const struct wiz_clk_mux_sel clk_mux_sel_10g_2_refclk[] = {
+ {
+ .num_parents = 3,
+ .parents = { WIZ_CORE_REFCLK, WIZ_CORE_REFCLK1, WIZ_EXT_REFCLK },
+ .table = { 2, 3, 0 },
+ .node_name = "pll0-refclk",
+ },
+ {
+ .num_parents = 3,
+ .parents = { WIZ_CORE_REFCLK, WIZ_CORE_REFCLK1, WIZ_EXT_REFCLK },
+ .table = { 2, 3, 0 },
+ .node_name = "pll1-refclk",
+ },
+ {
+ .num_parents = 3,
+ .parents = { WIZ_CORE_REFCLK, WIZ_CORE_REFCLK1, WIZ_EXT_REFCLK },
+ .table = { 2, 3, 0 },
+ .node_name = "refclk-dig",
+ },
+};
+
+static const struct clk_div_table clk_div_table[] = {
{ .val = 0, .div = 1, },
{ .val = 1, .div = 2, },
{ .val = 2, .div = 4, },
{ .val = 3, .div = 8, },
+ { /* sentinel */ },
};
-static struct wiz_clk_div_sel clk_div_sel[] = {
+static const struct wiz_clk_div_sel clk_div_sel[] = {
{
.table = clk_div_table,
.node_name = "cmn-refclk-dig-div",
@@ -190,7 +312,20 @@ static struct wiz_clk_div_sel clk_div_sel[] = {
enum wiz_type {
J721E_WIZ_16G,
- J721E_WIZ_10G,
+ J721E_WIZ_10G, /* Also for J7200 SR1.0 */
+ AM64_WIZ_10G,
+ J7200_WIZ_10G, /* J7200 SR2.0 */
+};
+
+struct wiz_data {
+ enum wiz_type type;
+ const struct reg_field *pll0_refclk_mux_sel;
+ const struct reg_field *pll1_refclk_mux_sel;
+ const struct reg_field *refclk_dig_sel;
+ const struct reg_field *pma_cmn_refclk1_dig_div;
+ const struct reg_field *pma_cmn_refclk1_int_mode;
+ const struct wiz_clk_mux_sel *clk_mux_sel;
+ unsigned int clk_div_sel_num;
};
#define WIZ_TYPEC_DIR_DEBOUNCE_MIN 100 /* ms */
@@ -198,21 +333,33 @@ enum wiz_type {
struct wiz {
struct regmap *regmap;
+ struct regmap *scm_regmap;
enum wiz_type type;
- struct wiz_clk_mux_sel *clk_mux_sel;
- struct wiz_clk_div_sel *clk_div_sel;
+ const struct wiz_clk_mux_sel *clk_mux_sel;
+ const struct wiz_clk_div_sel *clk_div_sel;
unsigned int clk_div_sel_num;
struct regmap_field *por_en;
struct regmap_field *phy_reset_n;
+ struct regmap_field *phy_en_refclk;
struct regmap_field *p_enable[WIZ_MAX_LANES];
struct regmap_field *p_align[WIZ_MAX_LANES];
struct regmap_field *p_raw_auto_start[WIZ_MAX_LANES];
struct regmap_field *p_standard_mode[WIZ_MAX_LANES];
+ struct regmap_field *p_mac_div_sel0[WIZ_MAX_LANES];
+ struct regmap_field *p_mac_div_sel1[WIZ_MAX_LANES];
+ struct regmap_field *p0_fullrt_div[WIZ_MAX_LANES];
+ struct regmap_field *p0_mac_src_sel[WIZ_MAX_LANES];
+ struct regmap_field *p0_rxfclk_sel[WIZ_MAX_LANES];
+ struct regmap_field *p0_refclk_sel[WIZ_MAX_LANES];
struct regmap_field *pma_cmn_refclk_int_mode;
+ struct regmap_field *pma_cmn_refclk1_int_mode;
struct regmap_field *pma_cmn_refclk_mode;
struct regmap_field *pma_cmn_refclk_dig_div;
struct regmap_field *pma_cmn_refclk1_dig_div;
+ struct regmap_field *mux_sel_field[WIZ_MUX_NUM_CLOCKS];
+ struct regmap_field *div_sel_field[WIZ_DIV_NUM_CLOCKS_16G];
struct regmap_field *typec_ln10_swap;
+ struct regmap_field *sup_legacy_clk_override;
struct device *dev;
u32 num_lanes;
@@ -220,6 +367,11 @@ struct wiz {
struct reset_controller_dev wiz_phy_reset_dev;
struct gpio_desc *gpio_typec_dir;
int typec_dir_delay;
+ u32 lane_phy_type[WIZ_MAX_LANES];
+ struct clk *input_clks[WIZ_MAX_INPUT_CLOCKS];
+ struct clk *output_clks[WIZ_MAX_OUTPUT_CLOCKS];
+ struct clk_onecell_data clk_data;
+ const struct wiz_data *data;
};
static int wiz_reset(struct wiz *wiz)
@@ -239,15 +391,52 @@ static int wiz_reset(struct wiz *wiz)
return 0;
}
+static int wiz_p_mac_div_sel(struct wiz *wiz)
+{
+ u32 num_lanes = wiz->num_lanes;
+ int ret;
+ int i;
+
+ for (i = 0; i < num_lanes; i++) {
+ if (wiz->lane_phy_type[i] == PHY_TYPE_SGMII ||
+ wiz->lane_phy_type[i] == PHY_TYPE_QSGMII ||
+ wiz->lane_phy_type[i] == PHY_TYPE_USXGMII) {
+ ret = regmap_field_write(wiz->p_mac_div_sel0[i], 1);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(wiz->p_mac_div_sel1[i], 2);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int wiz_mode_select(struct wiz *wiz)
{
u32 num_lanes = wiz->num_lanes;
+ enum wiz_lane_standard_mode mode;
int ret;
int i;
for (i = 0; i < num_lanes; i++) {
- ret = regmap_field_write(wiz->p_standard_mode[i],
- LANE_MODE_GEN4);
+ if (wiz->lane_phy_type[i] == PHY_TYPE_DP)
+ mode = LANE_MODE_GEN1;
+ else if (wiz->lane_phy_type[i] == PHY_TYPE_QSGMII)
+ mode = LANE_MODE_GEN2;
+ else
+ continue;
+
+ if (wiz->lane_phy_type[i] == PHY_TYPE_USXGMII) {
+ ret = regmap_field_write(wiz->p0_mac_src_sel[i], 0x3);
+ ret = regmap_field_write(wiz->p0_rxfclk_sel[i], 0x3);
+ ret = regmap_field_write(wiz->p0_refclk_sel[i], 0x3);
+ mode = LANE_MODE_GEN1;
+ }
+
+ ret = regmap_field_write(wiz->p_standard_mode[i], mode);
if (ret)
return ret;
}
@@ -291,6 +480,12 @@ static int wiz_init(struct wiz *wiz)
return ret;
}
+ ret = wiz_p_mac_div_sel(wiz);
+ if (ret) {
+ dev_err(dev, "Configuring P0 MAC DIV SEL failed\n");
+ return ret;
+ }
+
ret = wiz_init_raw_interface(wiz, true);
if (ret) {
dev_err(dev, "WIZ interface initialization failed\n");
@@ -302,11 +497,11 @@ static int wiz_init(struct wiz *wiz)
static int wiz_regfield_init(struct wiz *wiz)
{
- struct wiz_clk_mux_sel *clk_mux_sel;
- struct wiz_clk_div_sel *clk_div_sel;
struct regmap *regmap = wiz->regmap;
+ struct regmap *scm_regmap = wiz->regmap; /* updated later to scm_regmap if applicable */
int num_lanes = wiz->num_lanes;
struct device *dev = wiz->dev;
+ const struct wiz_data *data = wiz->data;
int i;
wiz->por_en = devm_regmap_field_alloc(dev, regmap, por_en);
@@ -336,54 +531,61 @@ static int wiz_regfield_init(struct wiz *wiz)
return PTR_ERR(wiz->pma_cmn_refclk_mode);
}
- clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK_DIG_DIV];
- clk_div_sel->field = devm_regmap_field_alloc(dev, regmap,
- pma_cmn_refclk_dig_div);
- if (IS_ERR(clk_div_sel->field)) {
+ wiz->div_sel_field[CMN_REFCLK_DIG_DIV] =
+ devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk_dig_div);
+ if (IS_ERR(wiz->div_sel_field[CMN_REFCLK_DIG_DIV])) {
dev_err(dev, "PMA_CMN_REFCLK_DIG_DIV reg field init failed\n");
- return PTR_ERR(clk_div_sel->field);
+ return PTR_ERR(wiz->div_sel_field[CMN_REFCLK_DIG_DIV]);
}
- if (wiz->type == J721E_WIZ_16G) {
- clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK1_DIG_DIV];
- clk_div_sel->field =
+ if (data->pma_cmn_refclk1_dig_div) {
+ wiz->div_sel_field[CMN_REFCLK1_DIG_DIV] =
devm_regmap_field_alloc(dev, regmap,
- pma_cmn_refclk1_dig_div);
- if (IS_ERR(clk_div_sel->field)) {
+ *data->pma_cmn_refclk1_dig_div);
+ if (IS_ERR(wiz->div_sel_field[CMN_REFCLK1_DIG_DIV])) {
dev_err(dev, "PMA_CMN_REFCLK1_DIG_DIV reg field init failed\n");
- return PTR_ERR(clk_div_sel->field);
+ return PTR_ERR(wiz->div_sel_field[CMN_REFCLK1_DIG_DIV]);
}
}
- clk_mux_sel = &wiz->clk_mux_sel[PLL0_REFCLK];
- clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap,
- pll0_refclk_mux_sel);
- if (IS_ERR(clk_mux_sel->field)) {
+ if (wiz->scm_regmap) {
+ scm_regmap = wiz->scm_regmap;
+ wiz->sup_legacy_clk_override =
+ devm_regmap_field_alloc(dev, scm_regmap, sup_legacy_clk_override);
+ if (IS_ERR(wiz->sup_legacy_clk_override)) {
+ dev_err(dev, "SUP_LEGACY_CLK_OVERRIDE reg field init failed\n");
+ return PTR_ERR(wiz->sup_legacy_clk_override);
+ }
+ }
+
+ wiz->mux_sel_field[PLL0_REFCLK] =
+ devm_regmap_field_alloc(dev, scm_regmap, *data->pll0_refclk_mux_sel);
+ if (IS_ERR(wiz->mux_sel_field[PLL0_REFCLK])) {
dev_err(dev, "PLL0_REFCLK_SEL reg field init failed\n");
- return PTR_ERR(clk_mux_sel->field);
+ return PTR_ERR(wiz->mux_sel_field[PLL0_REFCLK]);
}
- clk_mux_sel = &wiz->clk_mux_sel[PLL1_REFCLK];
- clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap,
- pll1_refclk_mux_sel);
- if (IS_ERR(clk_mux_sel->field)) {
+ wiz->mux_sel_field[PLL1_REFCLK] =
+ devm_regmap_field_alloc(dev, scm_regmap, *data->pll1_refclk_mux_sel);
+ if (IS_ERR(wiz->mux_sel_field[PLL1_REFCLK])) {
dev_err(dev, "PLL1_REFCLK_SEL reg field init failed\n");
- return PTR_ERR(clk_mux_sel->field);
+ return PTR_ERR(wiz->mux_sel_field[PLL1_REFCLK]);
}
- clk_mux_sel = &wiz->clk_mux_sel[REFCLK_DIG];
- if (wiz->type == J721E_WIZ_10G)
- clk_mux_sel->field =
- devm_regmap_field_alloc(dev, regmap,
- refclk_dig_sel_10g);
- else
- clk_mux_sel->field =
- devm_regmap_field_alloc(dev, regmap,
- refclk_dig_sel_16g);
-
- if (IS_ERR(clk_mux_sel->field)) {
+ wiz->mux_sel_field[REFCLK_DIG] = devm_regmap_field_alloc(dev, scm_regmap,
+ *data->refclk_dig_sel);
+ if (IS_ERR(wiz->mux_sel_field[REFCLK_DIG])) {
dev_err(dev, "REFCLK_DIG_SEL reg field init failed\n");
- return PTR_ERR(clk_mux_sel->field);
+ return PTR_ERR(wiz->mux_sel_field[REFCLK_DIG]);
+ }
+
+ if (data->pma_cmn_refclk1_int_mode) {
+ wiz->pma_cmn_refclk1_int_mode =
+ devm_regmap_field_alloc(dev, scm_regmap, *data->pma_cmn_refclk1_int_mode);
+ if (IS_ERR(wiz->pma_cmn_refclk1_int_mode)) {
+ dev_err(dev, "PMA_CMN_REFCLK1_INT_MODE reg field init failed\n");
+ return PTR_ERR(wiz->pma_cmn_refclk1_int_mode);
+ }
}
for (i = 0; i < num_lanes; i++) {
@@ -416,6 +618,46 @@ static int wiz_regfield_init(struct wiz *wiz)
i);
return PTR_ERR(wiz->p_standard_mode[i]);
}
+
+ wiz->p0_fullrt_div[i] = devm_regmap_field_alloc(dev, regmap, p0_fullrt_div[i]);
+ if (IS_ERR(wiz->p0_fullrt_div[i])) {
+ dev_err(dev, "P%d_FULLRT_DIV reg field init failed\n", i);
+ return PTR_ERR(wiz->p0_fullrt_div[i]);
+ }
+
+ wiz->p0_mac_src_sel[i] = devm_regmap_field_alloc(dev, regmap, p0_mac_src_sel[i]);
+ if (IS_ERR(wiz->p0_mac_src_sel[i])) {
+ dev_err(dev, "P%d_MAC_SRC_SEL reg field init failed\n", i);
+ return PTR_ERR(wiz->p0_mac_src_sel[i]);
+ }
+
+ wiz->p0_rxfclk_sel[i] = devm_regmap_field_alloc(dev, regmap, p0_rxfclk_sel[i]);
+ if (IS_ERR(wiz->p0_rxfclk_sel[i])) {
+ dev_err(dev, "P%d_RXFCLK_SEL reg field init failed\n", i);
+ return PTR_ERR(wiz->p0_rxfclk_sel[i]);
+ }
+
+ wiz->p0_refclk_sel[i] = devm_regmap_field_alloc(dev, regmap, p0_refclk_sel[i]);
+ if (IS_ERR(wiz->p0_refclk_sel[i])) {
+ dev_err(dev, "P%d_REFCLK_SEL reg field init failed\n", i);
+ return PTR_ERR(wiz->p0_refclk_sel[i]);
+ }
+
+ wiz->p_mac_div_sel0[i] =
+ devm_regmap_field_alloc(dev, regmap, p_mac_div_sel0[i]);
+ if (IS_ERR(wiz->p_mac_div_sel0[i])) {
+ dev_err(dev, "P%d_MAC_DIV_SEL0 reg field init fail\n",
+ i);
+ return PTR_ERR(wiz->p_mac_div_sel0[i]);
+ }
+
+ wiz->p_mac_div_sel1[i] =
+ devm_regmap_field_alloc(dev, regmap, p_mac_div_sel1[i]);
+ if (IS_ERR(wiz->p_mac_div_sel1[i])) {
+ dev_err(dev, "P%d_MAC_DIV_SEL1 reg field init fail\n",
+ i);
+ return PTR_ERR(wiz->p_mac_div_sel1[i]);
+ }
}
wiz->typec_ln10_swap = devm_regmap_field_alloc(dev, regmap,
@@ -425,6 +667,89 @@ static int wiz_regfield_init(struct wiz *wiz)
return PTR_ERR(wiz->typec_ln10_swap);
}
+ wiz->phy_en_refclk = devm_regmap_field_alloc(dev, regmap, phy_en_refclk);
+ if (IS_ERR(wiz->phy_en_refclk)) {
+ dev_err(dev, "PHY_EN_REFCLK reg field init failed\n");
+ return PTR_ERR(wiz->phy_en_refclk);
+ }
+
+ return 0;
+}
+
+static int wiz_phy_en_refclk_enable(struct clk_hw *hw)
+{
+ struct wiz_phy_en_refclk *wiz_phy_en_refclk = to_wiz_phy_en_refclk(hw);
+ struct regmap_field *phy_en_refclk = wiz_phy_en_refclk->phy_en_refclk;
+
+ regmap_field_write(phy_en_refclk, 1);
+
+ return 0;
+}
+
+static void wiz_phy_en_refclk_disable(struct clk_hw *hw)
+{
+ struct wiz_phy_en_refclk *wiz_phy_en_refclk = to_wiz_phy_en_refclk(hw);
+ struct regmap_field *phy_en_refclk = wiz_phy_en_refclk->phy_en_refclk;
+
+ regmap_field_write(phy_en_refclk, 0);
+}
+
+static int wiz_phy_en_refclk_is_enabled(struct clk_hw *hw)
+{
+ struct wiz_phy_en_refclk *wiz_phy_en_refclk = to_wiz_phy_en_refclk(hw);
+ struct regmap_field *phy_en_refclk = wiz_phy_en_refclk->phy_en_refclk;
+ int val;
+
+ regmap_field_read(phy_en_refclk, &val);
+
+ return !!val;
+}
+
+static const struct clk_ops wiz_phy_en_refclk_ops = {
+ .enable = wiz_phy_en_refclk_enable,
+ .disable = wiz_phy_en_refclk_disable,
+ .is_enabled = wiz_phy_en_refclk_is_enabled,
+};
+
+static int wiz_phy_en_refclk_register(struct wiz *wiz)
+{
+ struct wiz_phy_en_refclk *wiz_phy_en_refclk;
+ struct device *dev = wiz->dev;
+ struct clk_init_data *init;
+ struct clk *clk;
+ char *clk_name;
+ unsigned int sz;
+
+ wiz_phy_en_refclk = devm_kzalloc(dev, sizeof(*wiz_phy_en_refclk), GFP_KERNEL);
+ if (!wiz_phy_en_refclk)
+ return -ENOMEM;
+
+ init = &wiz_phy_en_refclk->clk_data;
+
+ init->ops = &wiz_phy_en_refclk_ops;
+ init->flags = 0;
+
+ sz = strlen(dev_name(dev)) + strlen(output_clk_names[TI_WIZ_PHY_EN_REFCLK]) + 2;
+
+ clk_name = kzalloc(sz, GFP_KERNEL);
+ if (!clk_name)
+ return -ENOMEM;
+
+ snprintf(clk_name, sz, "%s_%s", dev_name(dev), output_clk_names[TI_WIZ_PHY_EN_REFCLK]);
+ init->name = clk_name;
+
+ wiz_phy_en_refclk->phy_en_refclk = wiz->phy_en_refclk;
+ wiz_phy_en_refclk->hw.init = init;
+
+ clk = devm_clk_register(dev, &wiz_phy_en_refclk->hw);
+
+ kfree(clk_name);
+
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ wiz->output_clks[TI_WIZ_PHY_EN_REFCLK] = clk;
+
return 0;
}
@@ -435,7 +760,7 @@ static u8 wiz_clk_mux_get_parent(struct clk_hw *hw)
unsigned int val;
regmap_field_read(field, &val);
- return clk_mux_val_to_index(hw, mux->table, 0, val);
+ return clk_mux_val_to_index(hw, (u32 *)mux->table, 0, val);
}
static int wiz_clk_mux_set_parent(struct clk_hw *hw, u8 index)
@@ -453,8 +778,69 @@ static const struct clk_ops wiz_clk_mux_ops = {
.get_parent = wiz_clk_mux_get_parent,
};
-static int wiz_mux_clk_register(struct wiz *wiz, struct device_node *node,
- struct regmap_field *field, u32 *table)
+static int wiz_mux_clk_register(struct wiz *wiz, struct regmap_field *field,
+ const struct wiz_clk_mux_sel *mux_sel, int clk_index)
+{
+ struct device *dev = wiz->dev;
+ struct clk_init_data *init;
+ const char **parent_names;
+ unsigned int num_parents;
+ struct wiz_clk_mux *mux;
+ char clk_name[100];
+ struct clk *clk;
+ int ret = 0, i;
+
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return -ENOMEM;
+
+ num_parents = mux_sel->num_parents;
+
+ parent_names = kzalloc((sizeof(char *) * num_parents), GFP_KERNEL);
+ if (!parent_names)
+ return -ENOMEM;
+
+ for (i = 0; i < num_parents; i++) {
+ clk = wiz->input_clks[mux_sel->parents[i]];
+ if (IS_ERR_OR_NULL(clk)) {
+ dev_err(dev, "Failed to get parent clk for %s\n",
+ output_clk_names[clk_index]);
+ ret = -EINVAL;
+ goto err;
+ }
+ parent_names[i] = __clk_get_name(clk);
+ }
+
+ snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), output_clk_names[clk_index]);
+
+ init = &mux->clk_data;
+
+ init->ops = &wiz_clk_mux_ops;
+ init->flags = CLK_SET_RATE_NO_REPARENT;
+ init->parent_names = parent_names;
+ init->num_parents = num_parents;
+ init->name = clk_name;
+
+ mux->field = field;
+ mux->table = mux_sel->table;
+ mux->hw.init = init;
+
+ clk = devm_clk_register(dev, &mux->hw);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto err;
+ }
+
+ wiz->output_clks[clk_index] = clk;
+
+err:
+ kfree(parent_names);
+
+ return ret;
+}
+
+static int wiz_mux_of_clk_register(struct wiz *wiz, struct device_node *node,
+ struct regmap_field *field, const u32 *table)
{
struct device *dev = wiz->dev;
struct clk_init_data *init;
@@ -550,7 +936,7 @@ static const struct clk_ops wiz_clk_div_ops = {
static int wiz_div_clk_register(struct wiz *wiz, struct device_node *node,
struct regmap_field *field,
- struct clk_div_table *table)
+ const struct clk_div_table *table)
{
struct device *dev = wiz->dev;
struct wiz_clk_divider *div;
@@ -598,20 +984,71 @@ static int wiz_div_clk_register(struct wiz *wiz, struct device_node *node,
static void wiz_clock_cleanup(struct wiz *wiz, struct device_node *node)
{
- struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
+ const struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
+ struct device *dev = wiz->dev;
struct device_node *clk_node;
int i;
+ switch (wiz->type) {
+ case AM64_WIZ_10G:
+ case J7200_WIZ_10G:
+ of_clk_del_provider(dev->of_node);
+ return;
+ default:
+ break;
+ }
+
for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) {
clk_node = of_get_child_by_name(node, clk_mux_sel[i].node_name);
of_clk_del_provider(clk_node);
of_node_put(clk_node);
}
+
+ for (i = 0; i < wiz->clk_div_sel_num; i++) {
+ clk_node = of_get_child_by_name(node, clk_div_sel[i].node_name);
+ of_clk_del_provider(clk_node);
+ of_node_put(clk_node);
+ }
+
+ of_clk_del_provider(wiz->dev->of_node);
+}
+
+static int wiz_clock_register(struct wiz *wiz)
+{
+ const struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
+ struct device *dev = wiz->dev;
+ struct device_node *node = dev->of_node;
+ int clk_index;
+ int ret;
+ int i;
+
+ clk_index = TI_WIZ_PLL0_REFCLK;
+ for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++, clk_index++) {
+ ret = wiz_mux_clk_register(wiz, wiz->mux_sel_field[i], &clk_mux_sel[i], clk_index);
+ if (ret) {
+ dev_err(dev, "Failed to register clk: %s\n", output_clk_names[clk_index]);
+ return ret;
+ }
+ }
+
+ ret = wiz_phy_en_refclk_register(wiz);
+ if (ret) {
+ dev_err(dev, "Failed to add phy-en-refclk\n");
+ return ret;
+ }
+
+ wiz->clk_data.clks = wiz->output_clks;
+ wiz->clk_data.clk_num = WIZ_MAX_OUTPUT_CLOCKS;
+ ret = of_clk_add_provider(node, of_clk_src_onecell_get, &wiz->clk_data);
+ if (ret)
+ dev_err(dev, "Failed to add clock provider: %s\n", node->name);
+
+ return ret;
}
static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
{
- struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
+ const struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
struct device *dev = wiz->dev;
struct device_node *clk_node;
const char *node_name;
@@ -626,6 +1063,7 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
ret = PTR_ERR(clk);
return ret;
}
+ wiz->input_clks[WIZ_CORE_REFCLK] = clk;
rate = clk_get_rate(clk);
if (rate >= 100000000)
@@ -633,12 +1071,48 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
else
regmap_field_write(wiz->pma_cmn_refclk_int_mode, 0x3);
+ switch (wiz->type) {
+ case AM64_WIZ_10G:
+ case J7200_WIZ_10G:
+ switch (rate) {
+ case REF_CLK_100MHZ:
+ regmap_field_write(wiz->div_sel_field[CMN_REFCLK_DIG_DIV], 0x2);
+ break;
+ case REF_CLK_156_25MHZ:
+ regmap_field_write(wiz->div_sel_field[CMN_REFCLK_DIG_DIV], 0x3);
+ break;
+ default:
+ regmap_field_write(wiz->div_sel_field[CMN_REFCLK_DIG_DIV], 0);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (wiz->data->pma_cmn_refclk1_int_mode) {
+ clk = devm_clk_get(dev, "core_ref1_clk");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "core_ref1_clk clock not found\n");
+ ret = PTR_ERR(clk);
+ return ret;
+ }
+ wiz->input_clks[WIZ_CORE_REFCLK1] = clk;
+
+ rate = clk_get_rate(clk);
+ if (rate >= 100000000)
+ regmap_field_write(wiz->pma_cmn_refclk1_int_mode, 0x1);
+ else
+ regmap_field_write(wiz->pma_cmn_refclk1_int_mode, 0x3);
+ }
+
clk = devm_clk_get(dev, "ext_ref_clk");
if (IS_ERR(clk)) {
dev_err(dev, "ext_ref_clk clock not found\n");
ret = PTR_ERR(clk);
return ret;
}
+ wiz->input_clks[WIZ_EXT_REFCLK] = clk;
rate = clk_get_rate(clk);
if (rate >= 100000000)
@@ -646,6 +1120,17 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
else
regmap_field_write(wiz->pma_cmn_refclk_mode, 0x2);
+ switch (wiz->type) {
+ case AM64_WIZ_10G:
+ case J7200_WIZ_10G:
+ ret = wiz_clock_register(wiz);
+ if (ret)
+ dev_err(dev, "Failed to register wiz clocks\n");
+ return ret;
+ default:
+ break;
+ }
+
for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) {
node_name = clk_mux_sel[i].node_name;
clk_node = of_get_child_by_name(node, node_name);
@@ -655,8 +1140,8 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
goto err;
}
- ret = wiz_mux_clk_register(wiz, clk_node, clk_mux_sel[i].field,
- clk_mux_sel[i].table);
+ ret = wiz_mux_of_clk_register(wiz, clk_node, wiz->mux_sel_field[i],
+ clk_mux_sel[i].table);
if (ret) {
dev_err(dev, "Failed to register %s clock\n",
node_name);
@@ -676,7 +1161,7 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
goto err;
}
- ret = wiz_div_clk_register(wiz, clk_node, clk_div_sel[i].field,
+ ret = wiz_div_clk_register(wiz, clk_node, wiz->div_sel_field[i],
clk_div_sel[i].table);
if (ret) {
dev_err(dev, "Failed to register %s clock\n",
@@ -707,10 +1192,28 @@ static int wiz_phy_reset_assert(struct reset_controller_dev *rcdev,
return ret;
}
- ret = regmap_field_write(wiz->p_enable[id - 1], false);
+ ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE_DISABLE);
return ret;
}
+static int wiz_phy_fullrt_div(struct wiz *wiz, int lane)
+{
+ switch (wiz->type) {
+ case AM64_WIZ_10G:
+ if (wiz->lane_phy_type[lane] == PHY_TYPE_PCIE)
+ return regmap_field_write(wiz->p0_fullrt_div[lane], 0x1);
+ break;
+ case J721E_WIZ_10G:
+ case J7200_WIZ_10G:
+ if (wiz->lane_phy_type[lane] == PHY_TYPE_SGMII)
+ return regmap_field_write(wiz->p0_fullrt_div[lane], 0x2);
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
@@ -734,7 +1237,15 @@ static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev,
return ret;
}
- ret = regmap_field_write(wiz->p_enable[id - 1], true);
+ ret = wiz_phy_fullrt_div(wiz, id - 1);
+ if (ret)
+ return ret;
+
+ if (wiz->lane_phy_type[id - 1] == PHY_TYPE_DP)
+ ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE);
+ else
+ ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE_FORCE);
+
return ret;
}
@@ -743,43 +1254,135 @@ static const struct reset_control_ops wiz_phy_reset_ops = {
.deassert = wiz_phy_reset_deassert,
};
-static struct regmap_config wiz_regmap_config = {
+static const struct regmap_config wiz_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.fast_io = true,
};
+static struct wiz_data j721e_16g_data = {
+ .type = J721E_WIZ_16G,
+ .pll0_refclk_mux_sel = &pll0_refclk_mux_sel,
+ .pll1_refclk_mux_sel = &pll1_refclk_mux_sel,
+ .refclk_dig_sel = &refclk_dig_sel_16g,
+ .pma_cmn_refclk1_dig_div = &pma_cmn_refclk1_dig_div,
+ .clk_mux_sel = clk_mux_sel_16g,
+ .clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_16G,
+};
+
+static struct wiz_data j721e_10g_data = {
+ .type = J721E_WIZ_10G,
+ .pll0_refclk_mux_sel = &pll0_refclk_mux_sel,
+ .pll1_refclk_mux_sel = &pll1_refclk_mux_sel,
+ .refclk_dig_sel = &refclk_dig_sel_10g,
+ .clk_mux_sel = clk_mux_sel_10g,
+ .clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G,
+};
+
+static struct wiz_data am64_10g_data = {
+ .type = AM64_WIZ_10G,
+ .pll0_refclk_mux_sel = &pll0_refclk_mux_sel,
+ .pll1_refclk_mux_sel = &pll1_refclk_mux_sel,
+ .refclk_dig_sel = &refclk_dig_sel_10g,
+ .clk_mux_sel = clk_mux_sel_10g,
+ .clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G,
+};
+
+static struct wiz_data j7200_pg2_10g_data = {
+ .type = J7200_WIZ_10G,
+ .pll0_refclk_mux_sel = &sup_pll0_refclk_mux_sel,
+ .pll1_refclk_mux_sel = &sup_pll1_refclk_mux_sel,
+ .refclk_dig_sel = &sup_refclk_dig_sel_10g,
+ .pma_cmn_refclk1_int_mode = &sup_pma_cmn_refclk1_int_mode,
+ .clk_mux_sel = clk_mux_sel_10g_2_refclk,
+ .clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G,
+};
+
static const struct of_device_id wiz_id_table[] = {
{
- .compatible = "ti,j721e-wiz-16g", .data = (void *)J721E_WIZ_16G
+ .compatible = "ti,j721e-wiz-16g", .data = &j721e_16g_data,
+ },
+ {
+ .compatible = "ti,j721e-wiz-10g", .data = &j721e_10g_data,
+ },
+ {
+ .compatible = "ti,am64-wiz-10g", .data = &am64_10g_data,
},
{
- .compatible = "ti,j721e-wiz-10g", .data = (void *)J721E_WIZ_10G
+ .compatible = "ti,j7200-wiz-10g", .data = &j7200_pg2_10g_data,
},
{}
};
MODULE_DEVICE_TABLE(of, wiz_id_table);
+static int wiz_get_lane_phy_types(struct device *dev, struct wiz *wiz)
+{
+ struct device_node *serdes, *subnode;
+
+ serdes = of_get_child_by_name(dev->of_node, "serdes");
+ if (!serdes) {
+ dev_err(dev, "%s: Getting \"serdes\"-node failed\n", __func__);
+ return -EINVAL;
+ }
+
+ for_each_child_of_node(serdes, subnode) {
+ u32 reg, num_lanes = 1, phy_type = PHY_NONE;
+ int ret, i;
+
+ if (!(of_node_name_eq(subnode, "phy") ||
+ of_node_name_eq(subnode, "link")))
+ continue;
+
+ ret = of_property_read_u32(subnode, "reg", &reg);
+ if (ret) {
+ of_node_put(subnode);
+ dev_err(dev,
+ "%s: Reading \"reg\" from \"%s\" failed: %d\n",
+ __func__, subnode->name, ret);
+ return ret;
+ }
+ of_property_read_u32(subnode, "cdns,num-lanes", &num_lanes);
+ of_property_read_u32(subnode, "cdns,phy-type", &phy_type);
+
+ dev_dbg(dev, "%s: Lanes %u-%u have phy-type %u\n", __func__,
+ reg, reg + num_lanes - 1, phy_type);
+
+ for (i = reg; i < reg + num_lanes; i++)
+ wiz->lane_phy_type[i] = phy_type;
+ }
+
+ return 0;
+}
+
static int wiz_probe(struct platform_device *pdev)
{
struct reset_controller_dev *phy_reset_dev;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct platform_device *serdes_pdev;
+ bool already_configured = false;
struct device_node *child_node;
struct regmap *regmap;
struct resource res;
void __iomem *base;
struct wiz *wiz;
+ int ret, val, i;
u32 num_lanes;
- int ret;
+ const struct wiz_data *data;
wiz = devm_kzalloc(dev, sizeof(*wiz), GFP_KERNEL);
if (!wiz)
return -ENOMEM;
- wiz->type = (enum wiz_type)of_device_get_match_data(dev);
+ data = of_device_get_match_data(dev);
+ if (!data) {
+ dev_err(dev, "NULL device data\n");
+ return -EINVAL;
+ }
+
+ wiz->data = data;
+ wiz->type = data->type;
child_node = of_get_child_by_name(node, "serdes");
if (!child_node) {
@@ -794,8 +1397,10 @@ static int wiz_probe(struct platform_device *pdev)
}
base = devm_ioremap(dev, res.start, resource_size(&res));
- if (!base)
+ if (!base) {
+ ret = -ENOMEM;
goto err_addr_to_resource;
+ }
regmap = devm_regmap_init_mmio(dev, base, &wiz_regmap_config);
if (IS_ERR(regmap)) {
@@ -804,6 +1409,17 @@ static int wiz_probe(struct platform_device *pdev)
goto err_addr_to_resource;
}
+ wiz->scm_regmap = syscon_regmap_lookup_by_phandle(node, "ti,scm");
+ if (IS_ERR(wiz->scm_regmap)) {
+ if (wiz->type == J7200_WIZ_10G) {
+ dev_err(dev, "Couldn't get ti,scm regmap\n");
+ ret = -ENODEV;
+ goto err_addr_to_resource;
+ }
+
+ wiz->scm_regmap = NULL;
+ }
+
ret = of_property_read_u32(node, "num-lanes", &num_lanes);
if (ret) {
dev_err(dev, "Failed to read num-lanes property\n");
@@ -812,6 +1428,7 @@ static int wiz_probe(struct platform_device *pdev)
if (num_lanes > WIZ_MAX_LANES) {
dev_err(dev, "Cannot support %d lanes\n", num_lanes);
+ ret = -ENODEV;
goto err_addr_to_resource;
}
@@ -839,25 +1456,22 @@ static int wiz_probe(struct platform_device *pdev)
if (wiz->typec_dir_delay < WIZ_TYPEC_DIR_DEBOUNCE_MIN ||
wiz->typec_dir_delay > WIZ_TYPEC_DIR_DEBOUNCE_MAX) {
+ ret = -EINVAL;
dev_err(dev, "Invalid typec-dir-debounce property\n");
goto err_addr_to_resource;
}
}
+ ret = wiz_get_lane_phy_types(dev, wiz);
+ if (ret)
+ goto err_addr_to_resource;
+
wiz->dev = dev;
wiz->regmap = regmap;
wiz->num_lanes = num_lanes;
- if (wiz->type == J721E_WIZ_10G)
- wiz->clk_mux_sel = clk_mux_sel_10g;
- else
- wiz->clk_mux_sel = clk_mux_sel_16g;
-
+ wiz->clk_mux_sel = data->clk_mux_sel;
wiz->clk_div_sel = clk_div_sel;
-
- if (wiz->type == J721E_WIZ_10G)
- wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G;
- else
- wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_16G;
+ wiz->clk_div_sel_num = data->clk_div_sel_num;
platform_set_drvdata(pdev, wiz);
@@ -867,6 +1481,10 @@ static int wiz_probe(struct platform_device *pdev)
goto err_addr_to_resource;
}
+ /* Enable supplemental Control override if available */
+ if (wiz->scm_regmap)
+ regmap_field_write(wiz->sup_legacy_clk_override, 1);
+
phy_reset_dev = &wiz->wiz_phy_reset_dev;
phy_reset_dev->dev = dev;
phy_reset_dev->ops = &wiz_phy_reset_ops,
@@ -894,26 +1512,34 @@ static int wiz_probe(struct platform_device *pdev)
goto err_get_sync;
}
+ for (i = 0; i < wiz->num_lanes; i++) {
+ regmap_field_read(wiz->p_enable[i], &val);
+ if (val & (P_ENABLE | P_ENABLE_FORCE)) {
+ already_configured = true;
+ break;
+ }
+ }
+
+ if (!already_configured) {
+ ret = wiz_init(wiz);
+ if (ret) {
+ dev_err(dev, "WIZ initialization failed\n");
+ goto err_wiz_init;
+ }
+ }
+
serdes_pdev = of_platform_device_create(child_node, NULL, dev);
if (!serdes_pdev) {
dev_WARN(dev, "Unable to create SERDES platform device\n");
- goto err_pdev_create;
- }
- wiz->serdes_pdev = serdes_pdev;
-
- ret = wiz_init(wiz);
- if (ret) {
- dev_err(dev, "WIZ initialization failed\n");
+ ret = -ENOMEM;
goto err_wiz_init;
}
+ wiz->serdes_pdev = serdes_pdev;
of_node_put(child_node);
return 0;
err_wiz_init:
- of_platform_device_destroy(&serdes_pdev->dev, NULL);
-
-err_pdev_create:
wiz_clock_cleanup(wiz, node);
err_get_sync:
diff --git a/drivers/phy/ti/phy-omap-control.c b/drivers/phy/ti/phy-omap-control.c
index ccd0e4e00451..76c5595f0859 100644
--- a/drivers/phy/ti/phy-omap-control.c
+++ b/drivers/phy/ti/phy-omap-control.c
@@ -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);
@@ -268,7 +268,6 @@ MODULE_DEVICE_TABLE(of, omap_control_phy_id_table);
static int omap_control_phy_probe(struct platform_device *pdev)
{
- struct resource *res;
const struct of_device_id *of_id;
struct omap_control_phy *control_phy;
@@ -285,16 +284,13 @@ static int omap_control_phy_probe(struct platform_device *pdev)
control_phy->type = *(enum omap_control_phy_type *)of_id->data;
if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) {
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "otghs_control");
- control_phy->otghs_control = devm_ioremap_resource(
- &pdev->dev, res);
+ control_phy->otghs_control =
+ devm_platform_ioremap_resource_byname(pdev, "otghs_control");
if (IS_ERR(control_phy->otghs_control))
return PTR_ERR(control_phy->otghs_control);
} else {
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "power");
- control_phy->power = devm_ioremap_resource(&pdev->dev, res);
+ control_phy->power =
+ devm_platform_ioremap_resource_byname(pdev, "power");
if (IS_ERR(control_phy->power)) {
dev_err(&pdev->dev, "Couldn't get power register\n");
return PTR_ERR(control_phy->power);
@@ -312,9 +308,8 @@ static int omap_control_phy_probe(struct platform_device *pdev)
}
if (control_phy->type == OMAP_CTRL_TYPE_PCIE) {
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "pcie_pcs");
- control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res);
+ control_phy->pcie_pcs =
+ devm_platform_ioremap_resource_byname(pdev, "pcie_pcs");
if (IS_ERR(control_phy->pcie_pcs))
return PTR_ERR(control_phy->pcie_pcs);
}
diff --git a/drivers/phy/ti/phy-omap-usb2.c b/drivers/phy/ti/phy-omap-usb2.c
index 3d74629d7423..31a775877f6e 100644
--- a/drivers/phy/ti/phy-omap-usb2.c
+++ b/drivers/phy/ti/phy-omap-usb2.c
@@ -1,39 +1,97 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * omap-usb2.c - USB PHY, talking to musb controller in OMAP.
+ * omap-usb2.c - USB PHY, talking to USB controller on TI SoCs.
*
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2012-2020 Texas Instruments Incorporated - http://www.ti.com
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/io.h>
-#include <linux/phy/omap_usb.h>
-#include <linux/usb/phy_companion.h>
#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/pm_runtime.h>
#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/phy/omap_control_phy.h>
+#include <linux/phy/omap_usb.h>
#include <linux/phy/phy.h>
-#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
-#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+#include <linux/usb/phy_companion.h>
+
+#define USB2PHY_ANA_CONFIG1 0x4c
+#define USB2PHY_DISCON_BYP_LATCH BIT(31)
-#define USB2PHY_DISCON_BYP_LATCH (1 << 31)
-#define USB2PHY_ANA_CONFIG1 0x4c
+#define USB2PHY_CHRG_DET 0x14
+#define USB2PHY_CHRG_DET_USE_CHG_DET_REG BIT(29)
+#define USB2PHY_CHRG_DET_DIS_CHG_DET BIT(28)
+/* SoC Specific USB2_OTG register definitions */
#define AM654_USB2_OTG_PD BIT(8)
#define AM654_USB2_VBUS_DET_EN BIT(5)
#define AM654_USB2_VBUSVALID_DET_EN BIT(4)
+#define OMAP_DEV_PHY_PD BIT(0)
+#define OMAP_USB2_PHY_PD BIT(28)
+
+#define AM437X_USB2_PHY_PD BIT(0)
+#define AM437X_USB2_OTG_PD BIT(1)
+#define AM437X_USB2_OTGVDET_EN BIT(19)
+#define AM437X_USB2_OTGSESSEND_EN BIT(20)
+
+/* Driver Flags */
+#define OMAP_USB2_HAS_START_SRP BIT(0)
+#define OMAP_USB2_HAS_SET_VBUS BIT(1)
+#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT BIT(2)
+#define OMAP_USB2_DISABLE_CHRG_DET BIT(3)
+
+struct omap_usb {
+ struct usb_phy phy;
+ struct phy_companion *comparator;
+ void __iomem *pll_ctrl_base;
+ void __iomem *phy_base;
+ struct device *dev;
+ struct device *control_dev;
+ struct clk *wkupclk;
+ struct clk *optclk;
+ u8 flags;
+ struct regmap *syscon_phy_power; /* ctrl. reg. acces */
+ unsigned int power_reg; /* power reg. index within syscon */
+ u32 mask;
+ u32 power_on;
+ u32 power_off;
+};
+
+#define phy_to_omapusb(x) container_of((x), struct omap_usb, phy)
+
+struct usb_phy_data {
+ const char *label;
+ u8 flags;
+ u32 mask;
+ u32 power_on;
+ u32 power_off;
+};
+
+static inline u32 omap_usb_readl(void __iomem *addr, unsigned int offset)
+{
+ return __raw_readl(addr + offset);
+}
+
+static inline void omap_usb_writel(void __iomem *addr, unsigned int offset,
+ u32 data)
+{
+ __raw_writel(data, addr + offset);
+}
+
/**
- * omap_usb2_set_comparator - links the comparator present in the sytem with
- * this phy
- * @comparator - the companion phy(comparator) for this phy
+ * omap_usb2_set_comparator() - links the comparator present in the system with this phy
+ *
+ * @comparator: the companion phy(comparator) for this phy
*
* The phy companion driver should call this API passing the phy_companion
* filled with set_vbus and start_srp to be used by usb phy.
@@ -84,7 +142,7 @@ static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
}
static int omap_usb_set_peripheral(struct usb_otg *otg,
- struct usb_gadget *gadget)
+ struct usb_gadget *gadget)
{
otg->gadget = gadget;
if (!gadget)
@@ -157,7 +215,7 @@ static int omap_usb2_enable_clocks(struct omap_usb *phy)
return 0;
err1:
- clk_disable(phy->wkupclk);
+ clk_disable_unprepare(phy->wkupclk);
err0:
return ret;
@@ -184,6 +242,13 @@ static int omap_usb_init(struct phy *x)
omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val);
}
+ if (phy->flags & OMAP_USB2_DISABLE_CHRG_DET) {
+ val = omap_usb_readl(phy->phy_base, USB2PHY_CHRG_DET);
+ val |= USB2PHY_CHRG_DET_USE_CHG_DET_REG |
+ USB2PHY_CHRG_DET_DIS_CHG_DET;
+ omap_usb_writel(phy->phy_base, USB2PHY_CHRG_DET, val);
+ }
+
return 0;
}
@@ -277,11 +342,30 @@ static const struct of_device_id omap_usb2_id_table[] = {
};
MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
+static void omap_usb2_init_errata(struct omap_usb *phy)
+{
+ static const struct soc_device_attribute am65x_sr10_soc_devices[] = {
+ { .family = "AM65X", .revision = "SR1.0" },
+ { /* sentinel */ }
+ };
+
+ /*
+ * Errata i2075: USB2PHY: USB2PHY Charger Detect is Enabled by
+ * Default Without VBUS Presence.
+ *
+ * AM654x SR1.0 has a silicon bug due to which D+ is pulled high after
+ * POR, which could cause enumeration failure with some USB hubs.
+ * Disabling the USB2_PHY Charger Detect function will put D+
+ * into the normal state.
+ */
+ if (soc_device_match(am65x_sr10_soc_devices))
+ phy->flags |= OMAP_USB2_DISABLE_CHRG_DET;
+}
+
static int omap_usb2_probe(struct platform_device *pdev)
{
struct omap_usb *phy;
struct phy *generic_phy;
- struct resource *res;
struct phy_provider *phy_provider;
struct usb_otg *otg;
struct device_node *node = pdev->dev.of_node;
@@ -314,17 +398,16 @@ static int omap_usb2_probe(struct platform_device *pdev)
phy->mask = phy_data->mask;
phy->power_on = phy_data->power_on;
phy->power_off = phy_data->power_off;
+ phy->flags = phy_data->flags;
- if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- phy->phy_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(phy->phy_base))
- return PTR_ERR(phy->phy_base);
- phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
- }
+ omap_usb2_init_errata(phy);
+
+ phy->phy_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(phy->phy_base))
+ return PTR_ERR(phy->phy_base);
phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node,
- "syscon-phy-power");
+ "syscon-phy-power");
if (IS_ERR(phy->syscon_phy_power)) {
dev_dbg(&pdev->dev,
"can't get syscon-phy-power, using control device\n");
@@ -353,7 +436,6 @@ static int omap_usb2_probe(struct platform_device *pdev)
}
}
-
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
if (IS_ERR(phy->wkupclk)) {
if (PTR_ERR(phy->wkupclk) == -EPROBE_DEFER)
@@ -367,10 +449,10 @@ static int omap_usb2_probe(struct platform_device *pdev)
if (PTR_ERR(phy->wkupclk) != -EPROBE_DEFER)
dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
return PTR_ERR(phy->wkupclk);
- } else {
- dev_warn(&pdev->dev,
- "found usb_phy_cm_clk32k, please fix DTS\n");
}
+
+ dev_warn(&pdev->dev,
+ "found usb_phy_cm_clk32k, please fix DTS\n");
}
phy->optclk = devm_clk_get(phy->dev, "refclk");
@@ -419,7 +501,6 @@ static int omap_usb2_probe(struct platform_device *pdev)
return PTR_ERR(phy_provider);
}
-
usb_add_phy_dev(&phy->phy);
return 0;
diff --git a/drivers/phy/ti/phy-ti-pipe3.c b/drivers/phy/ti/phy-ti-pipe3.c
index a87946589eb7..f502c36f3be5 100644
--- a/drivers/phy/ti/phy-ti-pipe3.c
+++ b/drivers/phy/ti/phy-ti-pipe3.c
@@ -337,7 +337,6 @@ static int ti_pipe3_power_on(struct phy *x)
{
u32 val;
u32 mask;
- int ret;
unsigned long rate;
struct ti_pipe3 *phy = phy_get_drvdata(x);
bool rx_pending = false;
@@ -355,8 +354,8 @@ static int ti_pipe3_power_on(struct phy *x)
rate = rate / 1000000;
mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK;
val = rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
- ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
- mask, val);
+ regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
+ mask, val);
/*
* For PCIe, TX and RX must be powered on simultaneously.
* For USB and SATA, TX must be powered on before RX
@@ -697,6 +696,7 @@ static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy)
}
control_pdev = of_find_device_by_node(control_node);
+ of_node_put(control_node);
if (!control_pdev) {
dev_err(dev, "Failed to get control device\n");
return -EINVAL;
@@ -746,35 +746,28 @@ static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy)
static int ti_pipe3_get_tx_rx_base(struct ti_pipe3 *phy)
{
- struct resource *res;
struct device *dev = phy->dev;
struct platform_device *pdev = to_platform_device(dev);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "phy_rx");
- phy->phy_rx = devm_ioremap_resource(dev, res);
+ phy->phy_rx = devm_platform_ioremap_resource_byname(pdev, "phy_rx");
if (IS_ERR(phy->phy_rx))
return PTR_ERR(phy->phy_rx);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "phy_tx");
- phy->phy_tx = devm_ioremap_resource(dev, res);
+ phy->phy_tx = devm_platform_ioremap_resource_byname(pdev, "phy_tx");
return PTR_ERR_OR_ZERO(phy->phy_tx);
}
static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy)
{
- struct resource *res;
struct device *dev = phy->dev;
struct platform_device *pdev = to_platform_device(dev);
if (phy->mode == PIPE3_MODE_PCIE)
return 0;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "pll_ctrl");
- phy->pll_ctrl_base = devm_ioremap_resource(dev, res);
+ phy->pll_ctrl_base =
+ devm_platform_ioremap_resource_byname(pdev, "pll_ctrl");
return PTR_ERR_OR_ZERO(phy->pll_ctrl_base);
}
diff --git a/drivers/phy/ti/phy-tusb1210.c b/drivers/phy/ti/phy-tusb1210.c
index d8d0cc11d187..669c13d6e402 100644
--- a/drivers/phy/ti/phy-tusb1210.c
+++ b/drivers/phy/ti/phy-tusb1210.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/**
+/*
* tusb1210.c - TUSB1210 USB ULPI PHY driver
*
* Copyright (C) 2015 Intel Corporation
@@ -7,24 +7,94 @@
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
*/
#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
#include <linux/ulpi/driver.h>
#include <linux/ulpi/regs.h>
#include <linux/gpio/consumer.h>
#include <linux/phy/ulpi_phy.h>
-
-#define TUSB1210_VENDOR_SPECIFIC2 0x80
-#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0
-#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4
-#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6
+#include <linux/power_supply.h>
+#include <linux/workqueue.h>
+
+#define TUSB1211_POWER_CONTROL 0x3d
+#define TUSB1211_POWER_CONTROL_SET 0x3e
+#define TUSB1211_POWER_CONTROL_CLEAR 0x3f
+#define TUSB1211_POWER_CONTROL_SW_CONTROL BIT(0)
+#define TUSB1211_POWER_CONTROL_DET_COMP BIT(1)
+#define TUSB1211_POWER_CONTROL_DP_VSRC_EN BIT(6)
+
+#define TUSB1210_VENDOR_SPECIFIC2 0x80
+#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK GENMASK(3, 0)
+#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK GENMASK(5, 4)
+#define TUSB1210_VENDOR_SPECIFIC2_DP_MASK BIT(6)
+
+#define TUSB1211_VENDOR_SPECIFIC3 0x85
+#define TUSB1211_VENDOR_SPECIFIC3_SET 0x86
+#define TUSB1211_VENDOR_SPECIFIC3_CLEAR 0x87
+#define TUSB1211_VENDOR_SPECIFIC3_SW_USB_DET BIT(4)
+#define TUSB1211_VENDOR_SPECIFIC3_CHGD_IDP_SRC_EN BIT(6)
+
+#define TUSB1210_RESET_TIME_MS 50
+
+#define TUSB1210_CHG_DET_MAX_RETRIES 5
+
+/* TUSB1210 charger detection work states */
+enum tusb1210_chg_det_state {
+ TUSB1210_CHG_DET_CONNECTING,
+ TUSB1210_CHG_DET_START_DET,
+ TUSB1210_CHG_DET_READ_DET,
+ TUSB1210_CHG_DET_FINISH_DET,
+ TUSB1210_CHG_DET_CONNECTED,
+ TUSB1210_CHG_DET_DISCONNECTING,
+ TUSB1210_CHG_DET_DISCONNECTING_DONE,
+ TUSB1210_CHG_DET_DISCONNECTED,
+};
struct tusb1210 {
struct ulpi *ulpi;
struct phy *phy;
struct gpio_desc *gpio_reset;
struct gpio_desc *gpio_cs;
+ u8 otg_ctrl;
u8 vendor_specific2;
+#ifdef CONFIG_POWER_SUPPLY
+ enum power_supply_usb_type chg_type;
+ enum tusb1210_chg_det_state chg_det_state;
+ int chg_det_retries;
+ struct delayed_work chg_det_work;
+ struct notifier_block psy_nb;
+ struct power_supply *psy;
+ struct power_supply *charger;
+#endif
};
+static int tusb1210_ulpi_write(struct tusb1210 *tusb, u8 reg, u8 val)
+{
+ int ret;
+
+ ret = ulpi_write(tusb->ulpi, reg, val);
+ if (ret)
+ dev_err(&tusb->ulpi->dev, "error %d writing val 0x%02x to reg 0x%02x\n",
+ ret, val, reg);
+
+ return ret;
+}
+
+static int tusb1210_ulpi_read(struct tusb1210 *tusb, u8 reg, u8 *val)
+{
+ int ret;
+
+ ret = ulpi_read(tusb->ulpi, reg);
+ if (ret >= 0) {
+ *val = ret;
+ ret = 0;
+ } else {
+ dev_err(&tusb->ulpi->dev, "error %d reading reg 0x%02x\n", ret, reg);
+ }
+
+ return ret;
+}
+
static int tusb1210_power_on(struct phy *phy)
{
struct tusb1210 *tusb = phy_get_drvdata(phy);
@@ -32,10 +102,10 @@ static int tusb1210_power_on(struct phy *phy)
gpiod_set_value_cansleep(tusb->gpio_reset, 1);
gpiod_set_value_cansleep(tusb->gpio_cs, 1);
+ msleep(TUSB1210_RESET_TIME_MS);
+
/* Restore the optional eye diagram optimization value */
- if (tusb->vendor_specific2)
- ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2,
- tusb->vendor_specific2);
+ tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, tusb->vendor_specific2);
return 0;
}
@@ -54,35 +124,357 @@ static int tusb1210_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
struct tusb1210 *tusb = phy_get_drvdata(phy);
int ret;
+ u8 reg;
- ret = ulpi_read(tusb->ulpi, ULPI_OTG_CTRL);
+ ret = tusb1210_ulpi_read(tusb, ULPI_OTG_CTRL, &reg);
if (ret < 0)
return ret;
switch (mode) {
case PHY_MODE_USB_HOST:
- ret |= (ULPI_OTG_CTRL_DRVVBUS_EXT
+ reg |= (ULPI_OTG_CTRL_DRVVBUS_EXT
| ULPI_OTG_CTRL_ID_PULLUP
| ULPI_OTG_CTRL_DP_PULLDOWN
| ULPI_OTG_CTRL_DM_PULLDOWN);
- ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret);
- ret |= ULPI_OTG_CTRL_DRVVBUS;
+ tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg);
+ reg |= ULPI_OTG_CTRL_DRVVBUS;
break;
case PHY_MODE_USB_DEVICE:
- ret &= ~(ULPI_OTG_CTRL_DRVVBUS
+ reg &= ~(ULPI_OTG_CTRL_DRVVBUS
| ULPI_OTG_CTRL_DP_PULLDOWN
| ULPI_OTG_CTRL_DM_PULLDOWN);
- ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret);
- ret &= ~ULPI_OTG_CTRL_DRVVBUS_EXT;
+ tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg);
+ reg &= ~ULPI_OTG_CTRL_DRVVBUS_EXT;
break;
default:
/* nothing */
return 0;
}
- return ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret);
+ tusb->otg_ctrl = reg;
+ return tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg);
+}
+
+#ifdef CONFIG_POWER_SUPPLY
+static const char * const tusb1210_chg_det_states[] = {
+ "CHG_DET_CONNECTING",
+ "CHG_DET_START_DET",
+ "CHG_DET_READ_DET",
+ "CHG_DET_FINISH_DET",
+ "CHG_DET_CONNECTED",
+ "CHG_DET_DISCONNECTING",
+ "CHG_DET_DISCONNECTING_DONE",
+ "CHG_DET_DISCONNECTED",
+};
+
+static void tusb1210_reset(struct tusb1210 *tusb)
+{
+ gpiod_set_value_cansleep(tusb->gpio_reset, 0);
+ usleep_range(200, 500);
+ gpiod_set_value_cansleep(tusb->gpio_reset, 1);
+}
+
+static void tusb1210_chg_det_set_type(struct tusb1210 *tusb,
+ enum power_supply_usb_type type)
+{
+ dev_dbg(&tusb->ulpi->dev, "charger type: %d\n", type);
+ tusb->chg_type = type;
+ tusb->chg_det_retries = 0;
+ power_supply_changed(tusb->psy);
+}
+
+static void tusb1210_chg_det_set_state(struct tusb1210 *tusb,
+ enum tusb1210_chg_det_state new_state,
+ int delay_ms)
+{
+ if (delay_ms)
+ dev_dbg(&tusb->ulpi->dev, "chg_det new state %s in %d ms\n",
+ tusb1210_chg_det_states[new_state], delay_ms);
+
+ tusb->chg_det_state = new_state;
+ mod_delayed_work(system_long_wq, &tusb->chg_det_work,
+ msecs_to_jiffies(delay_ms));
+}
+
+static void tusb1210_chg_det_handle_ulpi_error(struct tusb1210 *tusb)
+{
+ tusb1210_reset(tusb);
+ if (tusb->chg_det_retries < TUSB1210_CHG_DET_MAX_RETRIES) {
+ tusb->chg_det_retries++;
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_START_DET,
+ TUSB1210_RESET_TIME_MS);
+ } else {
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_FINISH_DET,
+ TUSB1210_RESET_TIME_MS);
+ }
+}
+
+/*
+ * Boards using a TUSB121x for charger-detection have 3 power_supply class devs:
+ *
+ * tusb1211-charger-detect(1) -> charger -> fuel-gauge
+ *
+ * To determine if an USB charger is connected to the board, the online prop of
+ * the charger psy needs to be read. Since the tusb1211-charger-detect psy is
+ * the start of the supplier -> supplied-to chain, power_supply_am_i_supplied()
+ * cannot be used here.
+ *
+ * Instead, below is a list of the power_supply names of known chargers for
+ * these boards and the charger psy is looked up by name from this list.
+ *
+ * (1) modelling the external USB charger
+ */
+static const char * const tusb1210_chargers[] = {
+ "bq24190-charger",
+};
+
+static bool tusb1210_get_online(struct tusb1210 *tusb)
+{
+ union power_supply_propval val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tusb1210_chargers) && !tusb->charger; i++)
+ tusb->charger = power_supply_get_by_name(tusb1210_chargers[i]);
+
+ if (!tusb->charger)
+ return false;
+
+ if (power_supply_get_property(tusb->charger, POWER_SUPPLY_PROP_ONLINE, &val))
+ return false;
+
+ return val.intval;
+}
+
+static void tusb1210_chg_det_work(struct work_struct *work)
+{
+ struct tusb1210 *tusb = container_of(work, struct tusb1210, chg_det_work.work);
+ bool vbus_present = tusb1210_get_online(tusb);
+ int ret;
+ u8 val;
+
+ dev_dbg(&tusb->ulpi->dev, "chg_det state %s vbus_present %d\n",
+ tusb1210_chg_det_states[tusb->chg_det_state], vbus_present);
+
+ switch (tusb->chg_det_state) {
+ case TUSB1210_CHG_DET_CONNECTING:
+ tusb->chg_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
+ tusb->chg_det_retries = 0;
+ /* Power on USB controller for ulpi_read()/_write() */
+ ret = pm_runtime_resume_and_get(tusb->ulpi->dev.parent);
+ if (ret < 0) {
+ dev_err(&tusb->ulpi->dev, "error %d runtime-resuming\n", ret);
+ /* Should never happen, skip charger detection */
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTED, 0);
+ return;
+ }
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_START_DET, 0);
+ break;
+ case TUSB1210_CHG_DET_START_DET:
+ /*
+ * Use the builtin charger detection FSM to keep things simple.
+ * This only detects DCP / SDP. This is good enough for the few
+ * boards which actually rely on the phy for charger detection.
+ */
+ mutex_lock(&tusb->phy->mutex);
+ ret = tusb1210_ulpi_write(tusb, TUSB1211_VENDOR_SPECIFIC3_SET,
+ TUSB1211_VENDOR_SPECIFIC3_SW_USB_DET);
+ mutex_unlock(&tusb->phy->mutex);
+ if (ret) {
+ tusb1210_chg_det_handle_ulpi_error(tusb);
+ break;
+ }
+
+ /* Wait 400 ms for the charger detection FSM to finish */
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_READ_DET, 400);
+ break;
+ case TUSB1210_CHG_DET_READ_DET:
+ mutex_lock(&tusb->phy->mutex);
+ ret = tusb1210_ulpi_read(tusb, TUSB1211_POWER_CONTROL, &val);
+ mutex_unlock(&tusb->phy->mutex);
+ if (ret) {
+ tusb1210_chg_det_handle_ulpi_error(tusb);
+ break;
+ }
+
+ if (val & TUSB1211_POWER_CONTROL_DET_COMP)
+ tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_DCP);
+ else
+ tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_SDP);
+
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_FINISH_DET, 0);
+ break;
+ case TUSB1210_CHG_DET_FINISH_DET:
+ mutex_lock(&tusb->phy->mutex);
+
+ /* Set SW_CONTROL to stop the charger-det FSM */
+ ret = tusb1210_ulpi_write(tusb, TUSB1211_POWER_CONTROL_SET,
+ TUSB1211_POWER_CONTROL_SW_CONTROL);
+
+ /* Clear DP_VSRC_EN which may have been enabled by the charger-det FSM */
+ ret |= tusb1210_ulpi_write(tusb, TUSB1211_POWER_CONTROL_CLEAR,
+ TUSB1211_POWER_CONTROL_DP_VSRC_EN);
+
+ /* Clear CHGD_IDP_SRC_EN (may have been enabled by the charger-det FSM) */
+ ret |= tusb1210_ulpi_write(tusb, TUSB1211_VENDOR_SPECIFIC3_CLEAR,
+ TUSB1211_VENDOR_SPECIFIC3_CHGD_IDP_SRC_EN);
+
+ /* If any of the above fails reset the phy */
+ if (ret) {
+ tusb1210_reset(tusb);
+ msleep(TUSB1210_RESET_TIME_MS);
+ }
+
+ /* Restore phy-parameters and OTG_CTRL register */
+ tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, tusb->otg_ctrl);
+ tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2,
+ tusb->vendor_specific2);
+
+ mutex_unlock(&tusb->phy->mutex);
+
+ pm_runtime_put(tusb->ulpi->dev.parent);
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTED, 0);
+ break;
+ case TUSB1210_CHG_DET_CONNECTED:
+ if (!vbus_present)
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTING, 0);
+ break;
+ case TUSB1210_CHG_DET_DISCONNECTING:
+ /*
+ * The phy seems to take approx. 600ms longer then the charger
+ * chip (which is used to get vbus_present) to determine Vbus
+ * session end. Wait 800ms to ensure the phy has detected and
+ * signalled Vbus session end.
+ */
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTING_DONE, 800);
+ break;
+ case TUSB1210_CHG_DET_DISCONNECTING_DONE:
+ /*
+ * The phy often stops reacting to ulpi_read()/_write requests
+ * after a Vbus-session end. Reset it to work around this.
+ */
+ tusb1210_reset(tusb);
+ tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_UNKNOWN);
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTED, 0);
+ break;
+ case TUSB1210_CHG_DET_DISCONNECTED:
+ if (vbus_present)
+ tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTING, 0);
+ break;
+ }
+}
+
+static int tusb1210_psy_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct tusb1210 *tusb = container_of(nb, struct tusb1210, psy_nb);
+ struct power_supply *psy = ptr;
+
+ if (psy != tusb->psy && psy->desc->type == POWER_SUPPLY_TYPE_USB)
+ queue_delayed_work(system_long_wq, &tusb->chg_det_work, 0);
+
+ return NOTIFY_OK;
}
+static int tusb1210_psy_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct tusb1210 *tusb = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = tusb1210_get_online(tusb);
+ break;
+ case POWER_SUPPLY_PROP_USB_TYPE:
+ val->intval = tusb->chg_type;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ if (tusb->chg_type == POWER_SUPPLY_USB_TYPE_DCP)
+ val->intval = 2000000;
+ else
+ val->intval = 500000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const enum power_supply_usb_type tusb1210_psy_usb_types[] = {
+ POWER_SUPPLY_USB_TYPE_SDP,
+ POWER_SUPPLY_USB_TYPE_DCP,
+ POWER_SUPPLY_USB_TYPE_UNKNOWN,
+};
+
+static const enum power_supply_property tusb1210_psy_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_USB_TYPE,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static const struct power_supply_desc tusb1210_psy_desc = {
+ .name = "tusb1211-charger-detect",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .usb_types = tusb1210_psy_usb_types,
+ .num_usb_types = ARRAY_SIZE(tusb1210_psy_usb_types),
+ .properties = tusb1210_psy_props,
+ .num_properties = ARRAY_SIZE(tusb1210_psy_props),
+ .get_property = tusb1210_psy_get_prop,
+};
+
+/* Setup charger detection if requested, on errors continue without chg-det */
+static void tusb1210_probe_charger_detect(struct tusb1210 *tusb)
+{
+ struct power_supply_config psy_cfg = { .drv_data = tusb };
+ struct device *dev = &tusb->ulpi->dev;
+ int ret;
+
+ if (!device_property_read_bool(dev->parent, "linux,phy_charger_detect"))
+ return;
+
+ if (tusb->ulpi->id.product != 0x1508) {
+ dev_err(dev, "error charger detection is only supported on the TUSB1211\n");
+ return;
+ }
+
+ ret = tusb1210_ulpi_read(tusb, ULPI_OTG_CTRL, &tusb->otg_ctrl);
+ if (ret)
+ return;
+
+ tusb->psy = power_supply_register(dev, &tusb1210_psy_desc, &psy_cfg);
+ if (IS_ERR(tusb->psy))
+ return;
+
+ /*
+ * Delay initial run by 2 seconds to allow the charger driver,
+ * which is used to determine vbus_present, to load.
+ */
+ tusb->chg_det_state = TUSB1210_CHG_DET_DISCONNECTED;
+ INIT_DELAYED_WORK(&tusb->chg_det_work, tusb1210_chg_det_work);
+ queue_delayed_work(system_long_wq, &tusb->chg_det_work, 2 * HZ);
+
+ tusb->psy_nb.notifier_call = tusb1210_psy_notifier;
+ power_supply_reg_notifier(&tusb->psy_nb);
+}
+
+static void tusb1210_remove_charger_detect(struct tusb1210 *tusb)
+{
+
+ if (!IS_ERR_OR_NULL(tusb->psy)) {
+ power_supply_unreg_notifier(&tusb->psy_nb);
+ cancel_delayed_work_sync(&tusb->chg_det_work);
+ power_supply_unregister(tusb->psy);
+ }
+
+ if (tusb->charger)
+ power_supply_put(tusb->charger);
+}
+#else
+static void tusb1210_probe_charger_detect(struct tusb1210 *tusb) { }
+static void tusb1210_remove_charger_detect(struct tusb1210 *tusb) { }
+#endif
+
static const struct phy_ops phy_ops = {
.power_on = tusb1210_power_on,
.power_off = tusb1210_power_off,
@@ -94,11 +486,14 @@ static int tusb1210_probe(struct ulpi *ulpi)
{
struct tusb1210 *tusb;
u8 val, reg;
+ int ret;
tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL);
if (!tusb)
return -ENOMEM;
+ tusb->ulpi = ulpi;
+
tusb->gpio_reset = devm_gpiod_get_optional(&ulpi->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(tusb->gpio_reset))
@@ -118,32 +513,43 @@ static int tusb1210_probe(struct ulpi *ulpi)
* diagram optimization and DP/DM swap.
*/
+ ret = tusb1210_ulpi_read(tusb, TUSB1210_VENDOR_SPECIFIC2, &reg);
+ if (ret)
+ return ret;
+
/* High speed output drive strength configuration */
- device_property_read_u8(&ulpi->dev, "ihstx", &val);
- reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT;
+ if (!device_property_read_u8(&ulpi->dev, "ihstx", &val))
+ u8p_replace_bits(&reg, val, (u8)TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK);
/* High speed output impedance configuration */
- device_property_read_u8(&ulpi->dev, "zhsdrv", &val);
- reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT;
+ if (!device_property_read_u8(&ulpi->dev, "zhsdrv", &val))
+ u8p_replace_bits(&reg, val, (u8)TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK);
/* DP/DM swap control */
- device_property_read_u8(&ulpi->dev, "datapolarity", &val);
- reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT;
+ if (!device_property_read_u8(&ulpi->dev, "datapolarity", &val))
+ u8p_replace_bits(&reg, val, (u8)TUSB1210_VENDOR_SPECIFIC2_DP_MASK);
- if (reg) {
- ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg);
- tusb->vendor_specific2 = reg;
- }
+ ret = tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, reg);
+ if (ret)
+ return ret;
- tusb->phy = ulpi_phy_create(ulpi, &phy_ops);
- if (IS_ERR(tusb->phy))
- return PTR_ERR(tusb->phy);
+ tusb->vendor_specific2 = reg;
- tusb->ulpi = ulpi;
+ tusb1210_probe_charger_detect(tusb);
+
+ tusb->phy = ulpi_phy_create(ulpi, &phy_ops);
+ if (IS_ERR(tusb->phy)) {
+ ret = PTR_ERR(tusb->phy);
+ goto err_remove_charger;
+ }
phy_set_drvdata(tusb->phy, tusb);
ulpi_set_drvdata(ulpi, tusb);
return 0;
+
+err_remove_charger:
+ tusb1210_remove_charger_detect(tusb);
+ return ret;
}
static void tusb1210_remove(struct ulpi *ulpi)
@@ -151,6 +557,7 @@ static void tusb1210_remove(struct ulpi *ulpi)
struct tusb1210 *tusb = ulpi_get_drvdata(ulpi);
ulpi_phy_destroy(ulpi, tusb->phy);
+ tusb1210_remove_charger_detect(tusb);
}
#define TI_VENDOR_ID 0x0451
diff --git a/drivers/phy/ti/phy-twl4030-usb.c b/drivers/phy/ti/phy-twl4030-usb.c
index 9887f908f540..ac71017a0bc1 100644
--- a/drivers/phy/ti/phy-twl4030-usb.c
+++ b/drivers/phy/ti/phy-twl4030-usb.c
@@ -162,6 +162,8 @@ struct twl4030_usb {
atomic_t connected;
bool vbus_supplied;
bool musb_mailbox_pending;
+ unsigned long runtime_suspended:1;
+ unsigned long needs_resume:1;
struct delayed_work id_workaround_work;
};
@@ -384,6 +386,9 @@ static void __twl4030_phy_power(struct twl4030_usb *twl, int on)
WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
}
+static int twl4030_usb_runtime_suspend(struct device *dev);
+static int twl4030_usb_runtime_resume(struct device *dev);
+
static int __maybe_unused twl4030_usb_suspend(struct device *dev)
{
struct twl4030_usb *twl = dev_get_drvdata(dev);
@@ -395,6 +400,10 @@ static int __maybe_unused twl4030_usb_suspend(struct device *dev)
*/
dev_dbg(twl->dev, "%s\n", __func__);
disable_irq(twl->irq);
+ if (!twl->runtime_suspended && !atomic_read(&twl->connected)) {
+ twl4030_usb_runtime_suspend(dev);
+ twl->needs_resume = 1;
+ }
return 0;
}
@@ -405,9 +414,13 @@ static int __maybe_unused twl4030_usb_resume(struct device *dev)
dev_dbg(twl->dev, "%s\n", __func__);
enable_irq(twl->irq);
+ if (twl->needs_resume)
+ twl4030_usb_runtime_resume(dev);
/* check whether cable status changed */
twl4030_usb_irq(0, twl);
+ twl->runtime_suspended = 0;
+
return 0;
}
@@ -422,6 +435,8 @@ static int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev)
regulator_disable(twl->usb1v8);
regulator_disable(twl->usb3v1);
+ twl->runtime_suspended = 1;
+
return 0;
}
@@ -544,8 +559,8 @@ static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
return 0;
}
-static ssize_t twl4030_usb_vbus_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t vbus_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct twl4030_usb *twl = dev_get_drvdata(dev);
int ret = -EINVAL;
@@ -557,7 +572,7 @@ static ssize_t twl4030_usb_vbus_show(struct device *dev,
return ret;
}
-static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
+static DEVICE_ATTR_RO(vbus);
static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
{
@@ -779,7 +794,7 @@ static int twl4030_usb_remove(struct platform_device *pdev)
usb_remove_phy(&twl->phy);
pm_runtime_get_sync(twl->dev);
- cancel_delayed_work(&twl->id_workaround_work);
+ cancel_delayed_work_sync(&twl->id_workaround_work);
device_remove_file(twl->dev, &dev_attr_vbus);
/* set transceiver mode to power on defaults */
diff --git a/drivers/phy/xilinx/Kconfig b/drivers/phy/xilinx/Kconfig
new file mode 100644
index 000000000000..d8b0d46b2b4d
--- /dev/null
+++ b/drivers/phy/xilinx/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+#
+# PHY drivers for Xilinx platforms
+#
+
+config PHY_XILINX_ZYNQMP
+ tristate "Xilinx ZynqMP PHY driver"
+ depends on ARCH_ZYNQMP || COMPILE_TEST
+ select GENERIC_PHY
+ help
+ Enable this to support ZynqMP High Speed Gigabit Transceiver
+ that is part of ZynqMP SoC.
diff --git a/drivers/phy/xilinx/Makefile b/drivers/phy/xilinx/Makefile
new file mode 100644
index 000000000000..3f1f6a2a9b45
--- /dev/null
+++ b/drivers/phy/xilinx/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_PHY_XILINX_ZYNQMP) += phy-zynqmp.o
diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
new file mode 100644
index 000000000000..9be9535ad7ab
--- /dev/null
+++ b/drivers/phy/xilinx/phy-zynqmp.c
@@ -0,0 +1,1038 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * phy-zynqmp.c - PHY driver for Xilinx ZynqMP GT.
+ *
+ * Copyright (C) 2018-2020 Xilinx Inc.
+ *
+ * Author: Anurag Kumar Vulisha <anuragku@xilinx.com>
+ * Author: Subbaraya Sundeep <sundeep.lkml@gmail.com>
+ * Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This driver is tested for USB, SATA and Display Port currently.
+ * Other controllers PCIe and SGMII should also work but that is
+ * experimental as of now.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/phy/phy.h>
+
+/*
+ * Lane Registers
+ */
+
+/* TX De-emphasis parameters */
+#define L0_TX_ANA_TM_18 0x0048
+#define L0_TX_ANA_TM_118 0x01d8
+#define L0_TX_ANA_TM_118_FORCE_17_0 BIT(0)
+
+/* DN Resistor calibration code parameters */
+#define L0_TXPMA_ST_3 0x0b0c
+#define L0_DN_CALIB_CODE 0x3f
+
+/* PMA control parameters */
+#define L0_TXPMD_TM_45 0x0cb4
+#define L0_TXPMD_TM_48 0x0cc0
+#define L0_TXPMD_TM_45_OVER_DP_MAIN BIT(0)
+#define L0_TXPMD_TM_45_ENABLE_DP_MAIN BIT(1)
+#define L0_TXPMD_TM_45_OVER_DP_POST1 BIT(2)
+#define L0_TXPMD_TM_45_ENABLE_DP_POST1 BIT(3)
+#define L0_TXPMD_TM_45_OVER_DP_POST2 BIT(4)
+#define L0_TXPMD_TM_45_ENABLE_DP_POST2 BIT(5)
+
+/* PCS control parameters */
+#define L0_TM_DIG_6 0x106c
+#define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f
+#define L0_TX_DIG_61 0x00f4
+#define L0_TM_DISABLE_SCRAMBLE_ENCODER 0x0f
+
+/* PLL Test Mode register parameters */
+#define L0_TM_PLL_DIG_37 0x2094
+#define L0_TM_COARSE_CODE_LIMIT 0x10
+
+/* PLL SSC step size offsets */
+#define L0_PLL_SS_STEPS_0_LSB 0x2368
+#define L0_PLL_SS_STEPS_1_MSB 0x236c
+#define L0_PLL_SS_STEP_SIZE_0_LSB 0x2370
+#define L0_PLL_SS_STEP_SIZE_1 0x2374
+#define L0_PLL_SS_STEP_SIZE_2 0x2378
+#define L0_PLL_SS_STEP_SIZE_3_MSB 0x237c
+#define L0_PLL_STATUS_READ_1 0x23e4
+
+/* SSC step size parameters */
+#define STEP_SIZE_0_MASK 0xff
+#define STEP_SIZE_1_MASK 0xff
+#define STEP_SIZE_2_MASK 0xff
+#define STEP_SIZE_3_MASK 0x3
+#define STEP_SIZE_SHIFT 8
+#define FORCE_STEP_SIZE 0x10
+#define FORCE_STEPS 0x20
+#define STEPS_0_MASK 0xff
+#define STEPS_1_MASK 0x07
+
+/* Reference clock selection parameters */
+#define L0_Ln_REF_CLK_SEL(n) (0x2860 + (n) * 4)
+#define L0_REF_CLK_SEL_MASK 0x8f
+
+/* Calibration digital logic parameters */
+#define L3_TM_CALIB_DIG19 0xec4c
+#define L3_CALIB_DONE_STATUS 0xef14
+#define L3_TM_CALIB_DIG18 0xec48
+#define L3_TM_CALIB_DIG19_NSW 0x07
+#define L3_TM_CALIB_DIG18_NSW 0xe0
+#define L3_TM_OVERRIDE_NSW_CODE 0x20
+#define L3_CALIB_DONE 0x02
+#define L3_NSW_SHIFT 5
+#define L3_NSW_PIPE_SHIFT 4
+#define L3_NSW_CALIB_SHIFT 3
+
+#define PHY_REG_OFFSET 0x4000
+
+/*
+ * Global Registers
+ */
+
+/* Refclk selection parameters */
+#define PLL_REF_SEL(n) (0x10000 + (n) * 4)
+#define PLL_FREQ_MASK 0x1f
+#define PLL_STATUS_LOCKED 0x10
+
+/* Inter Connect Matrix parameters */
+#define ICM_CFG0 0x10010
+#define ICM_CFG1 0x10014
+#define ICM_CFG0_L0_MASK 0x07
+#define ICM_CFG0_L1_MASK 0x70
+#define ICM_CFG1_L2_MASK 0x07
+#define ICM_CFG2_L3_MASK 0x70
+#define ICM_CFG_SHIFT 4
+
+/* Inter Connect Matrix allowed protocols */
+#define ICM_PROTOCOL_PD 0x0
+#define ICM_PROTOCOL_PCIE 0x1
+#define ICM_PROTOCOL_SATA 0x2
+#define ICM_PROTOCOL_USB 0x3
+#define ICM_PROTOCOL_DP 0x4
+#define ICM_PROTOCOL_SGMII 0x5
+
+/* Test Mode common reset control parameters */
+#define TM_CMN_RST 0x10018
+#define TM_CMN_RST_EN 0x1
+#define TM_CMN_RST_SET 0x2
+#define TM_CMN_RST_MASK 0x3
+
+/* Bus width parameters */
+#define TX_PROT_BUS_WIDTH 0x10040
+#define RX_PROT_BUS_WIDTH 0x10044
+#define PROT_BUS_WIDTH_10 0x0
+#define PROT_BUS_WIDTH_20 0x1
+#define PROT_BUS_WIDTH_40 0x2
+#define PROT_BUS_WIDTH_SHIFT(n) ((n) * 2)
+#define PROT_BUS_WIDTH_MASK(n) GENMASK((n) * 2 + 1, (n) * 2)
+
+/* Number of GT lanes */
+#define NUM_LANES 4
+
+/* SIOU SATA control register */
+#define SATA_CONTROL_OFFSET 0x0100
+
+/* Total number of controllers */
+#define CONTROLLERS_PER_LANE 5
+
+/* Protocol Type parameters */
+#define XPSGTR_TYPE_USB0 0 /* USB controller 0 */
+#define XPSGTR_TYPE_USB1 1 /* USB controller 1 */
+#define XPSGTR_TYPE_SATA_0 2 /* SATA controller lane 0 */
+#define XPSGTR_TYPE_SATA_1 3 /* SATA controller lane 1 */
+#define XPSGTR_TYPE_PCIE_0 4 /* PCIe controller lane 0 */
+#define XPSGTR_TYPE_PCIE_1 5 /* PCIe controller lane 1 */
+#define XPSGTR_TYPE_PCIE_2 6 /* PCIe controller lane 2 */
+#define XPSGTR_TYPE_PCIE_3 7 /* PCIe controller lane 3 */
+#define XPSGTR_TYPE_DP_0 8 /* Display Port controller lane 0 */
+#define XPSGTR_TYPE_DP_1 9 /* Display Port controller lane 1 */
+#define XPSGTR_TYPE_SGMII0 10 /* Ethernet SGMII controller 0 */
+#define XPSGTR_TYPE_SGMII1 11 /* Ethernet SGMII controller 1 */
+#define XPSGTR_TYPE_SGMII2 12 /* Ethernet SGMII controller 2 */
+#define XPSGTR_TYPE_SGMII3 13 /* Ethernet SGMII controller 3 */
+
+/* Timeout values */
+#define TIMEOUT_US 1000
+
+struct xpsgtr_dev;
+
+/**
+ * struct xpsgtr_ssc - structure to hold SSC settings for a lane
+ * @refclk_rate: PLL reference clock frequency
+ * @pll_ref_clk: value to be written to register for corresponding ref clk rate
+ * @steps: number of steps of SSC (Spread Spectrum Clock)
+ * @step_size: step size of each step
+ */
+struct xpsgtr_ssc {
+ u32 refclk_rate;
+ u8 pll_ref_clk;
+ u32 steps;
+ u32 step_size;
+};
+
+/**
+ * struct xpsgtr_phy - representation of a lane
+ * @phy: pointer to the kernel PHY device
+ * @type: controller which uses this lane
+ * @lane: lane number
+ * @protocol: protocol in which the lane operates
+ * @skip_phy_init: skip phy_init() if true
+ * @dev: pointer to the xpsgtr_dev instance
+ * @refclk: reference clock index
+ */
+struct xpsgtr_phy {
+ struct phy *phy;
+ u8 type;
+ u8 lane;
+ u8 protocol;
+ bool skip_phy_init;
+ struct xpsgtr_dev *dev;
+ unsigned int refclk;
+};
+
+/**
+ * struct xpsgtr_dev - representation of a ZynMP GT device
+ * @dev: pointer to device
+ * @serdes: serdes base address
+ * @siou: siou base address
+ * @gtr_mutex: mutex for locking
+ * @phys: PHY lanes
+ * @refclk_sscs: spread spectrum settings for the reference clocks
+ * @clk: reference clocks
+ * @tx_term_fix: fix for GT issue
+ * @saved_icm_cfg0: stored value of ICM CFG0 register
+ * @saved_icm_cfg1: stored value of ICM CFG1 register
+ */
+struct xpsgtr_dev {
+ struct device *dev;
+ void __iomem *serdes;
+ void __iomem *siou;
+ struct mutex gtr_mutex; /* mutex for locking */
+ struct xpsgtr_phy phys[NUM_LANES];
+ const struct xpsgtr_ssc *refclk_sscs[NUM_LANES];
+ struct clk *clk[NUM_LANES];
+ bool tx_term_fix;
+ unsigned int saved_icm_cfg0;
+ unsigned int saved_icm_cfg1;
+};
+
+/*
+ * Configuration Data
+ */
+
+/* lookup table to hold all settings needed for a ref clock frequency */
+static const struct xpsgtr_ssc ssc_lookup[] = {
+ { 19200000, 0x05, 608, 264020 },
+ { 20000000, 0x06, 634, 243454 },
+ { 24000000, 0x07, 760, 168973 },
+ { 26000000, 0x08, 824, 143860 },
+ { 27000000, 0x09, 856, 86551 },
+ { 38400000, 0x0a, 1218, 65896 },
+ { 40000000, 0x0b, 634, 243454 },
+ { 52000000, 0x0c, 824, 143860 },
+ { 100000000, 0x0d, 1058, 87533 },
+ { 108000000, 0x0e, 856, 86551 },
+ { 125000000, 0x0f, 992, 119497 },
+ { 135000000, 0x10, 1070, 55393 },
+ { 150000000, 0x11, 792, 187091 }
+};
+
+/*
+ * I/O Accessors
+ */
+
+static inline u32 xpsgtr_read(struct xpsgtr_dev *gtr_dev, u32 reg)
+{
+ return readl(gtr_dev->serdes + reg);
+}
+
+static inline void xpsgtr_write(struct xpsgtr_dev *gtr_dev, u32 reg, u32 value)
+{
+ writel(value, gtr_dev->serdes + reg);
+}
+
+static inline void xpsgtr_clr_set(struct xpsgtr_dev *gtr_dev, u32 reg,
+ u32 clr, u32 set)
+{
+ u32 value = xpsgtr_read(gtr_dev, reg);
+
+ value &= ~clr;
+ value |= set;
+ xpsgtr_write(gtr_dev, reg, value);
+}
+
+static inline u32 xpsgtr_read_phy(struct xpsgtr_phy *gtr_phy, u32 reg)
+{
+ void __iomem *addr = gtr_phy->dev->serdes
+ + gtr_phy->lane * PHY_REG_OFFSET + reg;
+
+ return readl(addr);
+}
+
+static inline void xpsgtr_write_phy(struct xpsgtr_phy *gtr_phy,
+ u32 reg, u32 value)
+{
+ void __iomem *addr = gtr_phy->dev->serdes
+ + gtr_phy->lane * PHY_REG_OFFSET + reg;
+
+ writel(value, addr);
+}
+
+static inline void xpsgtr_clr_set_phy(struct xpsgtr_phy *gtr_phy,
+ u32 reg, u32 clr, u32 set)
+{
+ void __iomem *addr = gtr_phy->dev->serdes
+ + gtr_phy->lane * PHY_REG_OFFSET + reg;
+
+ writel((readl(addr) & ~clr) | set, addr);
+}
+
+/*
+ * Hardware Configuration
+ */
+
+/* Wait for the PLL to lock (with a timeout). */
+static int xpsgtr_wait_pll_lock(struct phy *phy)
+{
+ struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy);
+ struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+ unsigned int timeout = TIMEOUT_US;
+ int ret;
+
+ dev_dbg(gtr_dev->dev, "Waiting for PLL lock\n");
+
+ while (1) {
+ u32 reg = xpsgtr_read_phy(gtr_phy, L0_PLL_STATUS_READ_1);
+
+ if ((reg & PLL_STATUS_LOCKED) == PLL_STATUS_LOCKED) {
+ ret = 0;
+ break;
+ }
+
+ if (--timeout == 0) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ udelay(1);
+ }
+
+ if (ret == -ETIMEDOUT)
+ dev_err(gtr_dev->dev,
+ "lane %u (type %u, protocol %u): PLL lock timeout\n",
+ gtr_phy->lane, gtr_phy->type, gtr_phy->protocol);
+
+ return ret;
+}
+
+/* Configure PLL and spread-sprectrum clock. */
+static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)
+{
+ const struct xpsgtr_ssc *ssc;
+ u32 step_size;
+
+ ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk];
+ step_size = ssc->step_size;
+
+ xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane),
+ PLL_FREQ_MASK, ssc->pll_ref_clk);
+
+ /* Enable lane clock sharing, if required */
+ if (gtr_phy->refclk != gtr_phy->lane) {
+ /* Lane3 Ref Clock Selection Register */
+ xpsgtr_clr_set(gtr_phy->dev, L0_Ln_REF_CLK_SEL(gtr_phy->lane),
+ L0_REF_CLK_SEL_MASK, 1 << gtr_phy->refclk);
+ }
+
+ /* SSC step size [7:0] */
+ xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_0_LSB,
+ STEP_SIZE_0_MASK, step_size & STEP_SIZE_0_MASK);
+
+ /* SSC step size [15:8] */
+ step_size >>= STEP_SIZE_SHIFT;
+ xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_1,
+ STEP_SIZE_1_MASK, step_size & STEP_SIZE_1_MASK);
+
+ /* SSC step size [23:16] */
+ step_size >>= STEP_SIZE_SHIFT;
+ xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_2,
+ STEP_SIZE_2_MASK, step_size & STEP_SIZE_2_MASK);
+
+ /* SSC steps [7:0] */
+ xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEPS_0_LSB,
+ STEPS_0_MASK, ssc->steps & STEPS_0_MASK);
+
+ /* SSC steps [10:8] */
+ xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEPS_1_MSB,
+ STEPS_1_MASK,
+ (ssc->steps >> STEP_SIZE_SHIFT) & STEPS_1_MASK);
+
+ /* SSC step size [24:25] */
+ step_size >>= STEP_SIZE_SHIFT;
+ xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB,
+ STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) |
+ FORCE_STEP_SIZE | FORCE_STEPS);
+}
+
+/* Configure the lane protocol. */
+static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy)
+{
+ struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+ u8 protocol = gtr_phy->protocol;
+
+ switch (gtr_phy->lane) {
+ case 0:
+ xpsgtr_clr_set(gtr_dev, ICM_CFG0, ICM_CFG0_L0_MASK, protocol);
+ break;
+ case 1:
+ xpsgtr_clr_set(gtr_dev, ICM_CFG0, ICM_CFG0_L1_MASK,
+ protocol << ICM_CFG_SHIFT);
+ break;
+ case 2:
+ xpsgtr_clr_set(gtr_dev, ICM_CFG1, ICM_CFG0_L0_MASK, protocol);
+ break;
+ case 3:
+ xpsgtr_clr_set(gtr_dev, ICM_CFG1, ICM_CFG0_L1_MASK,
+ protocol << ICM_CFG_SHIFT);
+ break;
+ default:
+ /* We already checked 0 <= lane <= 3 */
+ break;
+ }
+}
+
+/* Bypass (de)scrambler and 8b/10b decoder and encoder. */
+static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy)
+{
+ xpsgtr_write_phy(gtr_phy, L0_TM_DIG_6, L0_TM_DIS_DESCRAMBLE_DECODER);
+ xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER);
+}
+
+/* DP-specific initialization. */
+static void xpsgtr_phy_init_dp(struct xpsgtr_phy *gtr_phy)
+{
+ xpsgtr_write_phy(gtr_phy, L0_TXPMD_TM_45,
+ L0_TXPMD_TM_45_OVER_DP_MAIN |
+ L0_TXPMD_TM_45_ENABLE_DP_MAIN |
+ L0_TXPMD_TM_45_OVER_DP_POST1 |
+ L0_TXPMD_TM_45_OVER_DP_POST2 |
+ L0_TXPMD_TM_45_ENABLE_DP_POST2);
+ xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_118,
+ L0_TX_ANA_TM_118_FORCE_17_0);
+}
+
+/* SATA-specific initialization. */
+static void xpsgtr_phy_init_sata(struct xpsgtr_phy *gtr_phy)
+{
+ struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+
+ xpsgtr_bypass_scrambler_8b10b(gtr_phy);
+
+ writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET);
+}
+
+/* SGMII-specific initialization. */
+static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy)
+{
+ struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+ u32 mask = PROT_BUS_WIDTH_MASK(gtr_phy->lane);
+ u32 val = PROT_BUS_WIDTH_10 << PROT_BUS_WIDTH_SHIFT(gtr_phy->lane);
+
+ /* Set SGMII protocol TX and RX bus width to 10 bits. */
+ xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val);
+ xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val);
+
+ xpsgtr_bypass_scrambler_8b10b(gtr_phy);
+}
+
+/* Configure TX de-emphasis and margining for DP. */
+static void xpsgtr_phy_configure_dp(struct xpsgtr_phy *gtr_phy, unsigned int pre,
+ unsigned int voltage)
+{
+ static const u8 voltage_swing[4][4] = {
+ { 0x2a, 0x27, 0x24, 0x20 },
+ { 0x27, 0x23, 0x20, 0xff },
+ { 0x24, 0x20, 0xff, 0xff },
+ { 0xff, 0xff, 0xff, 0xff }
+ };
+ static const u8 pre_emphasis[4][4] = {
+ { 0x02, 0x02, 0x02, 0x02 },
+ { 0x01, 0x01, 0x01, 0xff },
+ { 0x00, 0x00, 0xff, 0xff },
+ { 0xff, 0xff, 0xff, 0xff }
+ };
+
+ xpsgtr_write_phy(gtr_phy, L0_TXPMD_TM_48, voltage_swing[pre][voltage]);
+ xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_18, pre_emphasis[pre][voltage]);
+}
+
+/*
+ * PHY Operations
+ */
+
+static bool xpsgtr_phy_init_required(struct xpsgtr_phy *gtr_phy)
+{
+ /*
+ * As USB may save the snapshot of the states during hibernation, doing
+ * phy_init() will put the USB controller into reset, resulting in the
+ * losing of the saved snapshot. So try to avoid phy_init() for USB
+ * except when gtr_phy->skip_phy_init is false (this happens when FPD is
+ * shutdown during suspend or when gt lane is changed from current one)
+ */
+ if (gtr_phy->protocol == ICM_PROTOCOL_USB && gtr_phy->skip_phy_init)
+ return false;
+ else
+ return true;
+}
+
+/*
+ * There is a functional issue in the GT. The TX termination resistance can be
+ * out of spec due to a issue in the calibration logic. This is the workaround
+ * to fix it, required for XCZU9EG silicon.
+ */
+static int xpsgtr_phy_tx_term_fix(struct xpsgtr_phy *gtr_phy)
+{
+ struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+ u32 timeout = TIMEOUT_US;
+ u32 nsw;
+
+ /* Enabling Test Mode control for CMN Rest */
+ xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET);
+
+ /* Set Test Mode reset */
+ xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_EN);
+
+ xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG18, 0x00);
+ xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG19, L3_TM_OVERRIDE_NSW_CODE);
+
+ /*
+ * As a part of work around sequence for PMOS calibration fix,
+ * we need to configure any lane ICM_CFG to valid protocol. This
+ * will deassert the CMN_Resetn signal.
+ */
+ xpsgtr_lane_set_protocol(gtr_phy);
+
+ /* Clear Test Mode reset */
+ xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET);
+
+ dev_dbg(gtr_dev->dev, "calibrating...\n");
+
+ do {
+ u32 reg = xpsgtr_read(gtr_dev, L3_CALIB_DONE_STATUS);
+
+ if ((reg & L3_CALIB_DONE) == L3_CALIB_DONE)
+ break;
+
+ if (!--timeout) {
+ dev_err(gtr_dev->dev, "calibration time out\n");
+ return -ETIMEDOUT;
+ }
+
+ udelay(1);
+ } while (timeout > 0);
+
+ dev_dbg(gtr_dev->dev, "calibration done\n");
+
+ /* Reading NMOS Register Code */
+ nsw = xpsgtr_read(gtr_dev, L0_TXPMA_ST_3) & L0_DN_CALIB_CODE;
+
+ /* Set Test Mode reset */
+ xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_EN);
+
+ /* Writing NMOS register values back [5:3] */
+ xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG19, nsw >> L3_NSW_CALIB_SHIFT);
+
+ /* Writing NMOS register value [2:0] */
+ xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG18,
+ ((nsw & L3_TM_CALIB_DIG19_NSW) << L3_NSW_SHIFT) |
+ (1 << L3_NSW_PIPE_SHIFT));
+
+ /* Clear Test Mode reset */
+ xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET);
+
+ return 0;
+}
+
+static int xpsgtr_phy_init(struct phy *phy)
+{
+ struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy);
+ struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+ int ret = 0;
+
+ mutex_lock(&gtr_dev->gtr_mutex);
+
+ /* Skip initialization if not required. */
+ if (!xpsgtr_phy_init_required(gtr_phy))
+ goto out;
+
+ if (gtr_dev->tx_term_fix) {
+ ret = xpsgtr_phy_tx_term_fix(gtr_phy);
+ if (ret < 0)
+ goto out;
+
+ gtr_dev->tx_term_fix = false;
+ }
+
+ /* Enable coarse code saturation limiting logic. */
+ xpsgtr_write_phy(gtr_phy, L0_TM_PLL_DIG_37, L0_TM_COARSE_CODE_LIMIT);
+
+ /*
+ * Configure the PLL, the lane protocol, and perform protocol-specific
+ * initialization.
+ */
+ xpsgtr_configure_pll(gtr_phy);
+ xpsgtr_lane_set_protocol(gtr_phy);
+
+ switch (gtr_phy->protocol) {
+ case ICM_PROTOCOL_DP:
+ xpsgtr_phy_init_dp(gtr_phy);
+ break;
+
+ case ICM_PROTOCOL_SATA:
+ xpsgtr_phy_init_sata(gtr_phy);
+ break;
+
+ case ICM_PROTOCOL_SGMII:
+ xpsgtr_phy_init_sgmii(gtr_phy);
+ break;
+ }
+
+out:
+ mutex_unlock(&gtr_dev->gtr_mutex);
+ return ret;
+}
+
+static int xpsgtr_phy_exit(struct phy *phy)
+{
+ struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy);
+
+ gtr_phy->skip_phy_init = false;
+
+ return 0;
+}
+
+static int xpsgtr_phy_power_on(struct phy *phy)
+{
+ struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy);
+ int ret = 0;
+
+ /* Skip initialization if not required. */
+ if (!xpsgtr_phy_init_required(gtr_phy))
+ return ret;
+ /*
+ * Wait for the PLL to lock. For DP, only wait on DP0 to avoid
+ * cumulating waits for both lanes. The user is expected to initialize
+ * lane 0 last.
+ */
+ if (gtr_phy->protocol != ICM_PROTOCOL_DP ||
+ gtr_phy->type == XPSGTR_TYPE_DP_0)
+ ret = xpsgtr_wait_pll_lock(phy);
+
+ return ret;
+}
+
+static int xpsgtr_phy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+ struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy);
+
+ if (gtr_phy->protocol != ICM_PROTOCOL_DP)
+ return 0;
+
+ xpsgtr_phy_configure_dp(gtr_phy, opts->dp.pre[0], opts->dp.voltage[0]);
+
+ return 0;
+}
+
+static const struct phy_ops xpsgtr_phyops = {
+ .init = xpsgtr_phy_init,
+ .exit = xpsgtr_phy_exit,
+ .power_on = xpsgtr_phy_power_on,
+ .configure = xpsgtr_phy_configure,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * OF Xlate Support
+ */
+
+/* Set the lane type and protocol based on the PHY type and instance number. */
+static int xpsgtr_set_lane_type(struct xpsgtr_phy *gtr_phy, u8 phy_type,
+ unsigned int phy_instance)
+{
+ unsigned int num_phy_types;
+ const int *phy_types;
+
+ switch (phy_type) {
+ case PHY_TYPE_SATA: {
+ static const int types[] = {
+ XPSGTR_TYPE_SATA_0,
+ XPSGTR_TYPE_SATA_1,
+ };
+
+ phy_types = types;
+ num_phy_types = ARRAY_SIZE(types);
+ gtr_phy->protocol = ICM_PROTOCOL_SATA;
+ break;
+ }
+ case PHY_TYPE_USB3: {
+ static const int types[] = {
+ XPSGTR_TYPE_USB0,
+ XPSGTR_TYPE_USB1,
+ };
+
+ phy_types = types;
+ num_phy_types = ARRAY_SIZE(types);
+ gtr_phy->protocol = ICM_PROTOCOL_USB;
+ break;
+ }
+ case PHY_TYPE_DP: {
+ static const int types[] = {
+ XPSGTR_TYPE_DP_0,
+ XPSGTR_TYPE_DP_1,
+ };
+
+ phy_types = types;
+ num_phy_types = ARRAY_SIZE(types);
+ gtr_phy->protocol = ICM_PROTOCOL_DP;
+ break;
+ }
+ case PHY_TYPE_PCIE: {
+ static const int types[] = {
+ XPSGTR_TYPE_PCIE_0,
+ XPSGTR_TYPE_PCIE_1,
+ XPSGTR_TYPE_PCIE_2,
+ XPSGTR_TYPE_PCIE_3,
+ };
+
+ phy_types = types;
+ num_phy_types = ARRAY_SIZE(types);
+ gtr_phy->protocol = ICM_PROTOCOL_PCIE;
+ break;
+ }
+ case PHY_TYPE_SGMII: {
+ static const int types[] = {
+ XPSGTR_TYPE_SGMII0,
+ XPSGTR_TYPE_SGMII1,
+ XPSGTR_TYPE_SGMII2,
+ XPSGTR_TYPE_SGMII3,
+ };
+
+ phy_types = types;
+ num_phy_types = ARRAY_SIZE(types);
+ gtr_phy->protocol = ICM_PROTOCOL_SGMII;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ if (phy_instance >= num_phy_types)
+ return -EINVAL;
+
+ gtr_phy->type = phy_types[phy_instance];
+ return 0;
+}
+
+/*
+ * Valid combinations of controllers and lanes (Interconnect Matrix).
+ */
+static const unsigned int icm_matrix[NUM_LANES][CONTROLLERS_PER_LANE] = {
+ { XPSGTR_TYPE_PCIE_0, XPSGTR_TYPE_SATA_0, XPSGTR_TYPE_USB0,
+ XPSGTR_TYPE_DP_1, XPSGTR_TYPE_SGMII0 },
+ { XPSGTR_TYPE_PCIE_1, XPSGTR_TYPE_SATA_1, XPSGTR_TYPE_USB0,
+ XPSGTR_TYPE_DP_0, XPSGTR_TYPE_SGMII1 },
+ { XPSGTR_TYPE_PCIE_2, XPSGTR_TYPE_SATA_0, XPSGTR_TYPE_USB0,
+ XPSGTR_TYPE_DP_1, XPSGTR_TYPE_SGMII2 },
+ { XPSGTR_TYPE_PCIE_3, XPSGTR_TYPE_SATA_1, XPSGTR_TYPE_USB1,
+ XPSGTR_TYPE_DP_0, XPSGTR_TYPE_SGMII3 }
+};
+
+/* Translate OF phandle and args to PHY instance. */
+static struct phy *xpsgtr_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev);
+ struct xpsgtr_phy *gtr_phy;
+ unsigned int phy_instance;
+ unsigned int phy_lane;
+ unsigned int phy_type;
+ unsigned int refclk;
+ unsigned int i;
+ int ret;
+
+ if (args->args_count != 4) {
+ dev_err(dev, "Invalid number of cells in 'phy' property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ /*
+ * Get the PHY parameters from the OF arguments and derive the lane
+ * type.
+ */
+ phy_lane = args->args[0];
+ if (phy_lane >= ARRAY_SIZE(gtr_dev->phys)) {
+ dev_err(dev, "Invalid lane number %u\n", phy_lane);
+ return ERR_PTR(-ENODEV);
+ }
+
+ gtr_phy = &gtr_dev->phys[phy_lane];
+ phy_type = args->args[1];
+ phy_instance = args->args[2];
+
+ ret = xpsgtr_set_lane_type(gtr_phy, phy_type, phy_instance);
+ if (ret < 0) {
+ dev_err(gtr_dev->dev, "Invalid PHY type and/or instance\n");
+ return ERR_PTR(ret);
+ }
+
+ refclk = args->args[3];
+ if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) ||
+ !gtr_dev->refclk_sscs[refclk]) {
+ dev_err(dev, "Invalid reference clock number %u\n", refclk);
+ return ERR_PTR(-EINVAL);
+ }
+
+ gtr_phy->refclk = refclk;
+
+ /*
+ * Ensure that the Interconnect Matrix is obeyed, i.e a given lane type
+ * is allowed to operate on the lane.
+ */
+ for (i = 0; i < CONTROLLERS_PER_LANE; i++) {
+ if (icm_matrix[phy_lane][i] == gtr_phy->type)
+ return gtr_phy->phy;
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Power Management
+ */
+
+static int __maybe_unused xpsgtr_suspend(struct device *dev)
+{
+ struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev);
+ unsigned int i;
+
+ /* Save the snapshot ICM_CFG registers. */
+ gtr_dev->saved_icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0);
+ gtr_dev->saved_icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1);
+
+ for (i = 0; i < ARRAY_SIZE(gtr_dev->clk); i++)
+ clk_disable_unprepare(gtr_dev->clk[i]);
+
+ return 0;
+}
+
+static int __maybe_unused xpsgtr_resume(struct device *dev)
+{
+ struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev);
+ unsigned int icm_cfg0, icm_cfg1;
+ unsigned int i;
+ bool skip_phy_init;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(gtr_dev->clk); i++) {
+ err = clk_prepare_enable(gtr_dev->clk[i]);
+ if (err)
+ goto err_clk_put;
+ }
+
+ icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0);
+ icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1);
+
+ /* Return if no GT lanes got configured before suspend. */
+ if (!gtr_dev->saved_icm_cfg0 && !gtr_dev->saved_icm_cfg1)
+ return 0;
+
+ /* Check if the ICM configurations changed after suspend. */
+ if (icm_cfg0 == gtr_dev->saved_icm_cfg0 &&
+ icm_cfg1 == gtr_dev->saved_icm_cfg1)
+ skip_phy_init = true;
+ else
+ skip_phy_init = false;
+
+ /* Update the skip_phy_init for all gtr_phy instances. */
+ for (i = 0; i < ARRAY_SIZE(gtr_dev->phys); i++)
+ gtr_dev->phys[i].skip_phy_init = skip_phy_init;
+
+ return 0;
+
+err_clk_put:
+ while (i--)
+ clk_disable_unprepare(gtr_dev->clk[i]);
+
+ return err;
+}
+
+static const struct dev_pm_ops xpsgtr_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(xpsgtr_suspend, xpsgtr_resume)
+};
+
+/*
+ * Probe & Platform Driver
+ */
+
+static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)
+{
+ unsigned int refclk;
+ int ret;
+
+ for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) {
+ unsigned long rate;
+ unsigned int i;
+ struct clk *clk;
+ char name[8];
+
+ snprintf(name, sizeof(name), "ref%u", refclk);
+ clk = devm_clk_get_optional(gtr_dev->dev, name);
+ if (IS_ERR(clk)) {
+ ret = dev_err_probe(gtr_dev->dev, PTR_ERR(clk),
+ "Failed to get reference clock %u\n",
+ refclk);
+ goto err_clk_put;
+ }
+
+ if (!clk)
+ continue;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto err_clk_put;
+
+ gtr_dev->clk[refclk] = clk;
+
+ /*
+ * Get the spread spectrum (SSC) settings for the reference
+ * clock rate.
+ */
+ rate = clk_get_rate(clk);
+
+ for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) {
+ if (rate == ssc_lookup[i].refclk_rate) {
+ gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i];
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(ssc_lookup)) {
+ dev_err(gtr_dev->dev,
+ "Invalid rate %lu for reference clock %u\n",
+ rate, refclk);
+ ret = -EINVAL;
+ goto err_clk_put;
+ }
+ }
+
+ return 0;
+
+err_clk_put:
+ while (refclk--)
+ clk_disable_unprepare(gtr_dev->clk[refclk]);
+
+ return ret;
+}
+
+static int xpsgtr_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct xpsgtr_dev *gtr_dev;
+ struct phy_provider *provider;
+ unsigned int port;
+ unsigned int i;
+ int ret;
+
+ gtr_dev = devm_kzalloc(&pdev->dev, sizeof(*gtr_dev), GFP_KERNEL);
+ if (!gtr_dev)
+ return -ENOMEM;
+
+ gtr_dev->dev = &pdev->dev;
+ platform_set_drvdata(pdev, gtr_dev);
+
+ mutex_init(&gtr_dev->gtr_mutex);
+
+ if (of_device_is_compatible(np, "xlnx,zynqmp-psgtr"))
+ gtr_dev->tx_term_fix =
+ of_property_read_bool(np, "xlnx,tx-termination-fix");
+
+ /* Acquire resources. */
+ gtr_dev->serdes = devm_platform_ioremap_resource_byname(pdev, "serdes");
+ if (IS_ERR(gtr_dev->serdes))
+ return PTR_ERR(gtr_dev->serdes);
+
+ gtr_dev->siou = devm_platform_ioremap_resource_byname(pdev, "siou");
+ if (IS_ERR(gtr_dev->siou))
+ return PTR_ERR(gtr_dev->siou);
+
+ ret = xpsgtr_get_ref_clocks(gtr_dev);
+ if (ret)
+ return ret;
+
+ /* Create PHYs. */
+ for (port = 0; port < ARRAY_SIZE(gtr_dev->phys); ++port) {
+ struct xpsgtr_phy *gtr_phy = &gtr_dev->phys[port];
+ struct phy *phy;
+
+ gtr_phy->lane = port;
+ gtr_phy->dev = gtr_dev;
+
+ phy = devm_phy_create(&pdev->dev, np, &xpsgtr_phyops);
+ if (IS_ERR(phy)) {
+ dev_err(&pdev->dev, "failed to create PHY\n");
+ ret = PTR_ERR(phy);
+ goto err_clk_put;
+ }
+
+ gtr_phy->phy = phy;
+ phy_set_drvdata(phy, gtr_phy);
+ }
+
+ /* Register the PHY provider. */
+ provider = devm_of_phy_provider_register(&pdev->dev, xpsgtr_xlate);
+ if (IS_ERR(provider)) {
+ dev_err(&pdev->dev, "registering provider failed\n");
+ ret = PTR_ERR(provider);
+ goto err_clk_put;
+ }
+ return 0;
+
+err_clk_put:
+ for (i = 0; i < ARRAY_SIZE(gtr_dev->clk); i++)
+ clk_disable_unprepare(gtr_dev->clk[i]);
+
+ return ret;
+}
+
+static const struct of_device_id xpsgtr_of_match[] = {
+ { .compatible = "xlnx,zynqmp-psgtr", },
+ { .compatible = "xlnx,zynqmp-psgtr-v1.1", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xpsgtr_of_match);
+
+static struct platform_driver xpsgtr_driver = {
+ .probe = xpsgtr_probe,
+ .driver = {
+ .name = "xilinx-psgtr",
+ .of_match_table = xpsgtr_of_match,
+ .pm = &xpsgtr_pm_ops,
+ },
+};
+
+module_platform_driver(xpsgtr_driver);
+
+MODULE_AUTHOR("Xilinx Inc.");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Xilinx ZynqMP High speed Gigabit Transceiver");