summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-28 07:21:13 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-28 07:21:13 -1000
commite191b05f321b064a973399b7cdc003c0de89e5b9 (patch)
tree3d39ae34f3c0662113ee33edb6f4f539499ddb78 /drivers
parent1cb6cfab0916d18f88d44e4a23806b6c4cb355f8 (diff)
parent8f56fdf3d8cb9f1dbdc93bf9ac816698aa6f70b7 (diff)
Merge tag 'phy-for-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy
Pull phy updates from Vinod Koul: "As usual bunch of new device and driver support and updates to existing drivers: New Support: - Rockchip rk3576 csi dphy support - Mediatek MT8195, MT8196 DisplayPort PHYs. MT8196 PCI-Express Gen4 S-PHY, MT8189 tphy support - Qualcomm Nord eUSB2, QMP USB PHY, QMP PCIe and Gen5x16 PHY support, M31 eUSB2 PHY, Maili QMP UFS & PCIe PHY, Shikra PCIe phy - New driver for Qualcomm CSI2 C-PHY/DPHY support and new APIs in core to parse phy nodes - Nvidia Tegra238 XUSB pad controller support, Tegra264 MPHY driver, Tegra264 xusb support - Thunderbolt phy support in core and Qualcomm driver support for USB4 for X1E platforms - Google Tensor SoC USB PHY driver - ASPEED AST2700 USB3 PHY driver - Microchip MCP2542 can phy support Updates: - FRL TxFFE config support and Samsumg driver support and runtime PM for dcphy driver - Mediatek optional repeater support for USB2 ports - Refactoring and improvements on rockchip usbdp driver - Apple USB4 pipehandler state support - Rockchip single-lane DP support" * tag 'phy-for-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy: (114 commits) phy: tegra: Add support for Tegra238 XUSB pad controller phy: tegra: xusb: Use dev_err_probe() dt-bindings: phy: tegra194: Add compatible for Tegra238 MAINTAINERS: Add ASPEED USB3 PHY driver phy: aspeed: Add AST2700 USB3.2 PHY driver dt-bindings: phy: aspeed: Document AST2700 USB3.2 PHY phy: rockchip: phy-rockchip-inno-csidphy: add support for rk3576 variant dt-bindings: phy: rockchip-inno-csi-dphy: add rk3576 variant phy: mediatek: Add support for PCI-Express Gen4 S-PHY dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY phy: tegra: Add Tegra264 MPHY driver dt-bindings: phy: Add nvidia,tegra264-mphy phy: rockchip: samsung-hdptx: Add support for FRL TxFFE level control phy: rockchip: samsung-hdptx: Handle PHY config after module reload phy: hdmi: Add optional FRL TxFFE config options phy: tegra: xusb: Add Tegra264 support phy: tegra: xusb: Increase timeout for USB2_TRK_COMPLETED polling phy: tegra: xusb: Use devm_clk_get_optional to fetch USB2 tracking clock dt-bindings: phy: tegra-xusb: Add support for Tegra264 phy: freescale: fsl-samsung-hdmi: initialize default rate ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/phy/Kconfig3
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/apple/atc.c93
-rw-r--r--drivers/phy/aspeed/Kconfig15
-rw-r--r--drivers/phy/aspeed/Makefile2
-rw-r--r--drivers/phy/aspeed/phy-aspeed-usb3.c236
-rw-r--r--drivers/phy/broadcom/phy-brcm-sata.c2
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb.c29
-rw-r--r--drivers/phy/cadence/phy-cadence-sierra.c11
-rw-r--r--drivers/phy/freescale/phy-fsl-samsung-hdmi.c61
-rw-r--r--drivers/phy/hisilicon/phy-hi3670-pcie.c4
-rw-r--r--drivers/phy/mediatek/Kconfig12
-rw-r--r--drivers/phy/mediatek/Makefile1
-rw-r--r--drivers/phy/mediatek/phy-mtk-dp.c830
-rw-r--r--drivers/phy/mediatek/phy-mtk-pcie-sphy.c561
-rw-r--r--drivers/phy/mediatek/phy-mtk-xsphy.c39
-rw-r--r--drivers/phy/phy-google-usb.c447
-rw-r--r--drivers/phy/phy-xgene.c2
-rw-r--r--drivers/phy/qualcomm/Kconfig1
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-combo.c506
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-common.h32
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c24
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c963
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcie.c105
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6_40.h55
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6_40.h14
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v8_50.h4
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-pcie-v6_40.h94
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrxz-pcie-v6_40.h194
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-ufs.c24
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-usb.c24
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-usbc.c24
-rw-r--r--drivers/phy/realtek/phy-rtk-usb2.c3
-rw-r--r--drivers/phy/realtek/phy-rtk-usb3.c3
-rw-r--r--drivers/phy/rockchip/Kconfig2
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-csidphy.c15
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-hdmi.c2
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-usb2.c2
-rw-r--r--drivers/phy/rockchip/phy-rockchip-naneng-combphy.c19
-rw-r--r--drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c8
-rw-r--r--drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c82
-rw-r--r--drivers/phy/rockchip/phy-rockchip-usb.c2
-rw-r--r--drivers/phy/rockchip/phy-rockchip-usbdp.c441
-rw-r--r--drivers/phy/samsung/phy-exynos5-usbdrd.c78
-rw-r--r--drivers/phy/socionext/phy-uniphier-usb3hs.c3
-rw-r--r--drivers/phy/spacemit/Kconfig3
-rw-r--r--drivers/phy/starfive/phy-jh7110-dphy-tx.c16
-rw-r--r--drivers/phy/tegra/Kconfig11
-rw-r--r--drivers/phy/tegra/Makefile3
-rw-r--r--drivers/phy/tegra/phy-tegra-mphy.c459
-rw-r--r--drivers/phy/tegra/xusb-tegra186.c56
-rw-r--r--drivers/phy/tegra/xusb.c20
-rw-r--r--drivers/phy/tegra/xusb.h6
53 files changed, 4830 insertions, 817 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 19f3b7d12b7d..105fbe408aa2 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -100,7 +100,7 @@ config PHY_GOOGLE_USB
the G5 generation (Laguna). This driver provides the PHY interfaces
to interact with the SNPS eUSB2 and USB 3.2/DisplayPort Combo PHY,
both of which are integrated with the DWC3 USB DRD controller.
- This driver currently supports USB high-speed.
+ This driver currently supports USB high-speed and SuperSpeed.
config USB_LGM_PHY
tristate "INTEL Lightning Mountain USB PHY Driver"
@@ -171,6 +171,7 @@ config PHY_XGENE
source "drivers/phy/allwinner/Kconfig"
source "drivers/phy/amlogic/Kconfig"
source "drivers/phy/apple/Kconfig"
+source "drivers/phy/aspeed/Kconfig"
source "drivers/phy/axiado/Kconfig"
source "drivers/phy/broadcom/Kconfig"
source "drivers/phy/cadence/Kconfig"
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index d7aa516bcc49..c6dd02003bbe 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
obj-$(CONFIG_GENERIC_PHY) += allwinner/ \
amlogic/ \
apple/ \
+ aspeed/ \
axiado/ \
broadcom/ \
cadence/ \
diff --git a/drivers/phy/apple/atc.c b/drivers/phy/apple/atc.c
index 4156fabad742..45f1280a6faa 100644
--- a/drivers/phy/apple/atc.c
+++ b/drivers/phy/apple/atc.c
@@ -585,8 +585,6 @@ struct atcphy_mode_configuration {
* @phys.dp: DisplayPort PHY instance
* @phy_provider: PHY provider instance
* @rcdev: Reset controller device
- * @sw: Type-C switch instance
- * @mux: Type-C mux instance
* @lock: Mutex for synchronizing register access across PHY, Type-C switch/mux and reset controller
*/
struct apple_atcphy {
@@ -972,6 +970,19 @@ static int atcphy_pipehandler_check(struct apple_atcphy *atcphy)
return 0;
}
+static void atcphy_pipehandler_set_mux(struct apple_atcphy *atcphy, u32 data, u32 clk)
+{
+ mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
+ FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_OFF));
+ udelay(10);
+ mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_DATA,
+ FIELD_PREP(PIPEHANDLER_MUX_CTRL_DATA, data));
+ udelay(10);
+ mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
+ FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, clk));
+ udelay(10);
+}
+
static int atcphy_configure_pipehandler_usb3(struct apple_atcphy *atcphy, bool host)
{
int ret;
@@ -1054,15 +1065,8 @@ static int atcphy_configure_pipehandler_usb3(struct apple_atcphy *atcphy, bool h
}
/* Configure PIPE mux to USB3 PHY */
- mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
- FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_OFF));
- udelay(10);
- mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_DATA,
- FIELD_PREP(PIPEHANDLER_MUX_CTRL_DATA, PIPEHANDLER_MUX_CTRL_DATA_USB3));
- udelay(10);
- mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
- FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_USB3));
- udelay(10);
+ atcphy_pipehandler_set_mux(atcphy, PIPEHANDLER_MUX_CTRL_DATA_USB3,
+ PIPEHANDLER_MUX_CTRL_CLK_USB3);
/* Remove link detection override */
clear32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXVALID);
@@ -1078,6 +1082,41 @@ static int atcphy_configure_pipehandler_usb3(struct apple_atcphy *atcphy, bool h
return 0;
}
+static int atcphy_configure_pipehandler_usb4(struct apple_atcphy *atcphy)
+{
+ int ret;
+
+ ret = atcphy_pipehandler_check(atcphy);
+ if (ret)
+ return ret;
+
+ /* Force disable link detection */
+ clear32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE_VALUES,
+ PIPEHANDLER_OVERRIDE_VAL_RXDETECT0 | PIPEHANDLER_OVERRIDE_VAL_RXDETECT1);
+ set32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXVALID);
+ set32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXDETECT);
+
+ ret = atcphy_pipehandler_lock(atcphy);
+ if (ret) {
+ dev_err(atcphy->dev, "Failed to lock pipehandler\n");
+ return ret;
+ }
+
+ /* Configure PIPE mux to the USB4/Thunderbolt controller */
+ atcphy_pipehandler_set_mux(atcphy, PIPEHANDLER_MUX_CTRL_DATA_USB4,
+ PIPEHANDLER_MUX_CTRL_CLK_USB4);
+
+ /* Remove link detection override */
+ clear32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXVALID);
+ clear32(atcphy->regs.pipehandler + PIPEHANDLER_OVERRIDE, PIPEHANDLER_OVERRIDE_RXDETECT);
+
+ ret = atcphy_pipehandler_unlock(atcphy);
+ if (ret)
+ dev_warn(atcphy->dev, "Failed to unlock pipehandler\n");
+
+ return 0;
+}
+
static int atcphy_configure_pipehandler_dummy(struct apple_atcphy *atcphy)
{
int ret;
@@ -1097,15 +1136,8 @@ static int atcphy_configure_pipehandler_dummy(struct apple_atcphy *atcphy)
dev_warn(atcphy->dev, "Failed to lock pipehandler");
/* Switch to dummy PHY */
- mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
- FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_OFF));
- udelay(10);
- mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_DATA,
- FIELD_PREP(PIPEHANDLER_MUX_CTRL_DATA, PIPEHANDLER_MUX_CTRL_DATA_DUMMY));
- udelay(10);
- mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
- FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_DUMMY));
- udelay(10);
+ atcphy_pipehandler_set_mux(atcphy, PIPEHANDLER_MUX_CTRL_DATA_DUMMY,
+ PIPEHANDLER_MUX_CTRL_CLK_DUMMY);
ret = atcphy_pipehandler_unlock(atcphy);
if (ret)
@@ -1121,7 +1153,7 @@ static int atcphy_configure_pipehandler_dummy(struct apple_atcphy *atcphy)
static int atcphy_configure_pipehandler(struct apple_atcphy *atcphy, bool host)
{
- int ret;
+ int ret = -EINVAL;
lockdep_assert_held(&atcphy->lock);
@@ -1131,13 +1163,13 @@ static int atcphy_configure_pipehandler(struct apple_atcphy *atcphy, bool host)
atcphy->pipehandler_up = true;
break;
case ATCPHY_PIPEHANDLER_STATE_USB4:
- dev_warn(atcphy->dev,
- "ATCPHY_PIPEHANDLER_STATE_USB4 not implemented; falling back to USB2\n");
+ ret = atcphy_configure_pipehandler_usb4(atcphy);
+ atcphy->pipehandler_up = true;
+ break;
+ case ATCPHY_PIPEHANDLER_STATE_DUMMY:
ret = atcphy_configure_pipehandler_dummy(atcphy);
atcphy->pipehandler_up = false;
break;
- default:
- ret = -EINVAL;
}
return ret;
@@ -1147,15 +1179,8 @@ static void atcphy_setup_pipehandler(struct apple_atcphy *atcphy)
{
lockdep_assert_held(&atcphy->lock);
- mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
- FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_OFF));
- udelay(10);
- mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_DATA,
- FIELD_PREP(PIPEHANDLER_MUX_CTRL_DATA, PIPEHANDLER_MUX_CTRL_DATA_DUMMY));
- udelay(10);
- mask32(atcphy->regs.pipehandler + PIPEHANDLER_MUX_CTRL, PIPEHANDLER_MUX_CTRL_CLK,
- FIELD_PREP(PIPEHANDLER_MUX_CTRL_CLK, PIPEHANDLER_MUX_CTRL_CLK_DUMMY));
- udelay(10);
+ atcphy_pipehandler_set_mux(atcphy, PIPEHANDLER_MUX_CTRL_DATA_DUMMY,
+ PIPEHANDLER_MUX_CTRL_CLK_DUMMY);
}
static void atcphy_configure_lanes(struct apple_atcphy *atcphy, enum atcphy_mode mode)
diff --git a/drivers/phy/aspeed/Kconfig b/drivers/phy/aspeed/Kconfig
new file mode 100644
index 000000000000..7b5f48db2be8
--- /dev/null
+++ b/drivers/phy/aspeed/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+#
+# PHY drivers for ASPEED
+#
+
+config PHY_ASPEED_USB3
+ tristate "ASPEED USB3 PHY driver"
+ select GENERIC_PHY
+ depends on (ARCH_ASPEED || COMPILE_TEST)
+ help
+ Enable this to support the USB 3.2 PHY on the Aspeed AST2700 SoC.
+ It supports SuperSpeedPlus Gen2x1 (10 Gbps), SuperSpeed (5 Gbps),
+ High Speed (480 Mbps), Full Speed (12 Mbps) and Low Speed
+ (1.5 Mbps), and is paired with the DWC3 USB controller.
diff --git a/drivers/phy/aspeed/Makefile b/drivers/phy/aspeed/Makefile
new file mode 100644
index 000000000000..d96d9d73a009
--- /dev/null
+++ b/drivers/phy/aspeed/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PHY_ASPEED_USB3) += phy-aspeed-usb3.o
diff --git a/drivers/phy/aspeed/phy-aspeed-usb3.c b/drivers/phy/aspeed/phy-aspeed-usb3.c
new file mode 100644
index 000000000000..6f371cae02f3
--- /dev/null
+++ b/drivers/phy/aspeed/phy-aspeed-usb3.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Aspeed Technology Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#define PHY3S00 0x00
+#define PHY3S00_INIT_DONE BIT(15)
+#define PHY3S00_SRAM_BYPASS BIT(7)
+#define PHY3S00_SRAM_EXT_LOAD BIT(6)
+#define PHY3S04 0x04
+#define PHY3C00 0x08
+#define PHY3C04 0x0c
+#define PHY3P00 0x10
+#define PHY3P00_RX_ADAPT_AFE_EN_G1 BIT(0)
+#define PHY3P00_RX_ADAPT_AFE_EN_G2 BIT(1)
+#define PHY3P00_RX_ADAPT_DFE_EN_G1 BIT(2)
+#define PHY3P00_RX_ADAPT_DFE_EN_G2 BIT(3)
+#define PHY3P00_RX_CDR_VCO_LOWFREQ_G1 BIT(4)
+#define PHY3P00_RX_CDR_VCO_LOWFREQ_G2 BIT(5)
+#define PHY3P00_RX_EQ_AFE_GAIN_G1 GENMASK(9, 6)
+#define PHY3P00_RX_EQ_AFE_GAIN_G2 GENMASK(13, 10)
+#define PHY3P00_RX_EQ_ATT_LVL_G1 GENMASK(16, 14)
+#define PHY3P00_RX_EQ_ATT_LVL_G2 GENMASK(19, 17)
+#define PHY3P00_RX_EQ_CTLE_BOOST_G1 GENMASK(24, 20)
+#define PHY3P00_RX_EQ_CTLE_BOOST_G2 GENMASK(29, 25)
+#define PHY3P00_RX_EQ_DELTA_IQ_G1_LO GENMASK(31, 30)
+
+#define PHY3P04 0x14
+#define PHY3P04_RX_EQ_DELTA_IQ_G1_HI GENMASK(1, 0)
+#define PHY3P04_RX_EQ_DELTA_IQ_G2 GENMASK(5, 2)
+#define PHY3P04_RX_EQ_DFE_TAP1_G1 GENMASK(13, 6)
+#define PHY3P04_RX_EQ_DFE_TAP1_G2 GENMASK(21, 14)
+#define PHY3P04_RX_LOS_LFPS_EN BIT(22)
+#define PHY3P04_RX_LOS_THRESHOLD GENMASK(25, 23)
+#define PHY3P04_RX_TERM_CTRL GENMASK(28, 26)
+#define PHY3P04_TX_EQ_MAIN_G1_LO GENMASK(31, 29)
+
+#define PHY3P08 0x18
+#define PHY3P08_TX_EQ_MAIN_G1_HI GENMASK(1, 0)
+#define PHY3P08_TX_EQ_MAIN_G2 GENMASK(6, 2)
+#define PHY3P08_TX_EQ_OVRD BIT(7)
+#define PHY3P08_TX_EQ_POST_G1 GENMASK(12, 9)
+#define PHY3P08_TX_EQ_POST_G2 GENMASK(16, 13)
+#define PHY3P08_TX_EQ_PRE_G1 GENMASK(20, 17)
+#define PHY3P08_TX_EQ_PRE_G2 GENMASK(24, 21)
+#define PHY3P08_TX_IBOOST_LVL GENMASK(28, 25)
+#define PHY3P08_TX_TERM_CTRL GENMASK(31, 29)
+
+#define PHY3P0C 0x1c
+#define PHY3P0C_TX_VBOOST_EN BIT(0)
+
+#define PHY3CMD 0x40
+
+#define PHY3P_RX_EQ_CTLE_BOOST_G1_DEFAULT 0x7
+#define PHY3P_RX_EQ_CTLE_BOOST_G2_DEFAULT 0x7
+#define PHY3P_RX_EQ_DELTA_IQ_G1_DEFAULT 0x3
+#define PHY3P_RX_EQ_DELTA_IQ_G2_DEFAULT 0x5
+#define PHY3P_RX_LOS_THRESHOLD_DEFAULT 0x3
+#define PHY3P_RX_TERM_CTRL_DEFAULT 0x2
+#define PHY3P_TX_EQ_MAIN_G1_DEFAULT 0xa
+#define PHY3P_TX_EQ_MAIN_G2_DEFAULT 0x9
+#define PHY3P_TX_EQ_POST_G1_DEFAULT 0x4
+#define PHY3P_TX_EQ_POST_G2_DEFAULT 0x3
+#define PHY3P_TX_EQ_PRE_G2_DEFAULT 0x2
+#define PHY3P_TX_IBOOST_LVL_DEFAULT 0xf
+#define PHY3P_TX_TERM_CTRL_DEFAULT 0x2
+
+#define PHY3P00_DEFAULT ( \
+ PHY3P00_RX_ADAPT_AFE_EN_G1 | \
+ PHY3P00_RX_ADAPT_AFE_EN_G2 | \
+ PHY3P00_RX_ADAPT_DFE_EN_G1 | \
+ PHY3P00_RX_ADAPT_DFE_EN_G2 | \
+ FIELD_PREP(PHY3P00_RX_EQ_CTLE_BOOST_G1, PHY3P_RX_EQ_CTLE_BOOST_G1_DEFAULT) | \
+ FIELD_PREP(PHY3P00_RX_EQ_CTLE_BOOST_G2, PHY3P_RX_EQ_CTLE_BOOST_G2_DEFAULT) | \
+ FIELD_PREP(PHY3P00_RX_EQ_DELTA_IQ_G1_LO, \
+ PHY3P_RX_EQ_DELTA_IQ_G1_DEFAULT & 0x3) \
+)
+
+#define PHY3P04_DEFAULT ( \
+ FIELD_PREP(PHY3P04_RX_EQ_DELTA_IQ_G1_HI, \
+ PHY3P_RX_EQ_DELTA_IQ_G1_DEFAULT >> 2) | \
+ FIELD_PREP(PHY3P04_RX_EQ_DELTA_IQ_G2, PHY3P_RX_EQ_DELTA_IQ_G2_DEFAULT) | \
+ PHY3P04_RX_LOS_LFPS_EN | \
+ FIELD_PREP(PHY3P04_RX_LOS_THRESHOLD, PHY3P_RX_LOS_THRESHOLD_DEFAULT) | \
+ FIELD_PREP(PHY3P04_RX_TERM_CTRL, PHY3P_RX_TERM_CTRL_DEFAULT) | \
+ FIELD_PREP(PHY3P04_TX_EQ_MAIN_G1_LO, \
+ PHY3P_TX_EQ_MAIN_G1_DEFAULT & 0x7) \
+)
+
+#define PHY3P08_DEFAULT ( \
+ FIELD_PREP(PHY3P08_TX_EQ_MAIN_G1_HI, PHY3P_TX_EQ_MAIN_G1_DEFAULT >> 3) | \
+ FIELD_PREP(PHY3P08_TX_EQ_MAIN_G2, PHY3P_TX_EQ_MAIN_G2_DEFAULT) | \
+ FIELD_PREP(PHY3P08_TX_EQ_POST_G1, PHY3P_TX_EQ_POST_G1_DEFAULT) | \
+ FIELD_PREP(PHY3P08_TX_EQ_POST_G2, PHY3P_TX_EQ_POST_G2_DEFAULT) | \
+ FIELD_PREP(PHY3P08_TX_EQ_PRE_G2, PHY3P_TX_EQ_PRE_G2_DEFAULT) | \
+ FIELD_PREP(PHY3P08_TX_IBOOST_LVL, PHY3P_TX_IBOOST_LVL_DEFAULT) | \
+ FIELD_PREP(PHY3P08_TX_TERM_CTRL, PHY3P_TX_TERM_CTRL_DEFAULT) \
+)
+
+#define PHY3P0C_DEFAULT \
+ PHY3P0C_TX_VBOOST_EN
+
+struct aspeed_usb3_phy {
+ void __iomem *regs;
+ struct reset_control *rst;
+ struct device *dev;
+ struct clk *clk;
+};
+
+static int aspeed_usb3_phy_init(struct phy *phy)
+{
+ struct aspeed_usb3_phy *aspeed_phy = phy_get_drvdata(phy);
+ u32 val;
+ int ret;
+
+ ret = clk_prepare_enable(aspeed_phy->clk);
+ if (ret) {
+ dev_err(aspeed_phy->dev, "Failed to enable clock %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_deassert(aspeed_phy->rst);
+ if (ret)
+ goto err_disable_clk;
+
+ /* Wait for USB3 PHY internal SRAM initialization done */
+ ret = readl_poll_timeout(aspeed_phy->regs + PHY3S00, val,
+ val & PHY3S00_INIT_DONE,
+ USEC_PER_MSEC, 10 * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(aspeed_phy->dev, "SRAM init timeout\n");
+ goto err_assert_reset;
+ }
+
+ val = readl(aspeed_phy->regs + PHY3S00);
+ val |= PHY3S00_SRAM_EXT_LOAD;
+ writel(val, aspeed_phy->regs + PHY3S00);
+
+ /* Set protocol1_ext signals as default PHY3 settings based on SNPS documents.
+ * Including PCFGI[54]: protocol1_ext_rx_los_lfps_en for better compatibility
+ */
+ writel(PHY3P00_DEFAULT, aspeed_phy->regs + PHY3P00);
+ writel(PHY3P04_DEFAULT, aspeed_phy->regs + PHY3P04);
+ writel(PHY3P08_DEFAULT, aspeed_phy->regs + PHY3P08);
+ writel(PHY3P0C_DEFAULT, aspeed_phy->regs + PHY3P0C);
+
+ return 0;
+
+err_assert_reset:
+ reset_control_assert(aspeed_phy->rst);
+err_disable_clk:
+ clk_disable_unprepare(aspeed_phy->clk);
+ return ret;
+}
+
+static int aspeed_usb3_phy_exit(struct phy *phy)
+{
+ struct aspeed_usb3_phy *aspeed_phy = phy_get_drvdata(phy);
+
+ reset_control_assert(aspeed_phy->rst);
+ clk_disable_unprepare(aspeed_phy->clk);
+
+ return 0;
+}
+
+static const struct phy_ops aspeed_usb3_phy_ops = {
+ .init = aspeed_usb3_phy_init,
+ .exit = aspeed_usb3_phy_exit,
+ .owner = THIS_MODULE,
+};
+
+static int aspeed_usb3_phy_probe(struct platform_device *pdev)
+{
+ struct aspeed_usb3_phy *aspeed_phy;
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct phy *phy;
+
+ aspeed_phy = devm_kzalloc(dev, sizeof(*aspeed_phy), GFP_KERNEL);
+ if (!aspeed_phy)
+ return -ENOMEM;
+
+ aspeed_phy->dev = dev;
+
+ aspeed_phy->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(aspeed_phy->clk))
+ return PTR_ERR(aspeed_phy->clk);
+
+ aspeed_phy->rst = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(aspeed_phy->rst))
+ return PTR_ERR(aspeed_phy->rst);
+
+ aspeed_phy->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(aspeed_phy->regs))
+ return PTR_ERR(aspeed_phy->regs);
+
+ phy = devm_phy_create(dev, NULL, &aspeed_usb3_phy_ops);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ phy_set_drvdata(phy, aspeed_phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id aspeed_usb3_phy_match_table[] = {
+ {
+ .compatible = "aspeed,ast2700-usb3-phy",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aspeed_usb3_phy_match_table);
+
+static struct platform_driver aspeed_usb3_phy_driver = {
+ .probe = aspeed_usb3_phy_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = aspeed_usb3_phy_match_table,
+ },
+};
+module_platform_driver(aspeed_usb3_phy_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ASPEED USB3.2 PHY Driver");
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
diff --git a/drivers/phy/broadcom/phy-brcm-sata.c b/drivers/phy/broadcom/phy-brcm-sata.c
index fb69e21a0292..43b287a25e7a 100644
--- a/drivers/phy/broadcom/phy-brcm-sata.c
+++ b/drivers/phy/broadcom/phy-brcm-sata.c
@@ -334,7 +334,7 @@ static int brcm_stb_sata_16nm_ssc_init(struct brcm_sata_port *port)
brcm_sata_phy_wr(port, RXPMD_REG_BANK, RXPMD_RX_CDR_CONTROL1,
~RXPMD_RX_PPM_VAL_MASK, value);
- /* Set proportional loop bandwith Gen1/2/3 */
+ /* Set proportional loop bandwidth Gen1/2/3 */
tmp = RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G1_CDR_PROP_BW_SHIFT |
RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G2_CDR_PROP_BW_SHIFT |
RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G3_CDR_PROB_BW_SHIFT;
diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c
index 59d756a10d6c..e606db489698 100644
--- a/drivers/phy/broadcom/phy-brcm-usb.c
+++ b/drivers/phy/broadcom/phy-brcm-usb.c
@@ -71,6 +71,8 @@ struct brcm_usb_phy_data {
int wake_irq;
struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX];
struct notifier_block pm_notifier;
+ bool pm_notifier_registered;
+ bool sysfs_group_created;
bool pm_active;
};
@@ -545,7 +547,11 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
return err;
priv->pm_notifier.notifier_call = brcm_pm_notifier;
- register_pm_notifier(&priv->pm_notifier);
+ err = register_pm_notifier(&priv->pm_notifier);
+ if (err)
+ dev_warn(dev, "Error registering PM notifier\n");
+ else
+ priv->pm_notifier_registered = true;
mutex_init(&priv->mutex);
@@ -561,6 +567,8 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
err = sysfs_create_group(&dev->kobj, &brcm_usb_phy_group);
if (err)
dev_warn(dev, "Error creating sysfs attributes\n");
+ else
+ priv->sysfs_group_created = true;
/* Get piarbctl syscon if it exists */
rmap = syscon_regmap_lookup_by_phandle(dev->of_node,
@@ -581,16 +589,29 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
clk_disable_unprepare(priv->usb_30_clk);
phy_provider = devm_of_phy_provider_register(dev, brcm_usb_phy_xlate);
+ err = PTR_ERR_OR_ZERO(phy_provider);
+ if (err)
+ goto err_remove_debug;
+
+ return 0;
+
+err_remove_debug:
+ if (priv->sysfs_group_created)
+ sysfs_remove_group(&dev->kobj, &brcm_usb_phy_group);
+ if (priv->pm_notifier_registered)
+ unregister_pm_notifier(&priv->pm_notifier);
- return PTR_ERR_OR_ZERO(phy_provider);
+ return err;
}
static void brcm_usb_phy_remove(struct platform_device *pdev)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(&pdev->dev);
- sysfs_remove_group(&pdev->dev.kobj, &brcm_usb_phy_group);
- unregister_pm_notifier(&priv->pm_notifier);
+ if (priv->sysfs_group_created)
+ sysfs_remove_group(&pdev->dev.kobj, &brcm_usb_phy_group);
+ if (priv->pm_notifier_registered)
+ unregister_pm_notifier(&priv->pm_notifier);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c
index 92ab1a31646a..dbeda7f01cbb 100644
--- a/drivers/phy/cadence/phy-cadence-sierra.c
+++ b/drivers/phy/cadence/phy-cadence-sierra.c
@@ -698,23 +698,16 @@ static const struct phy_ops noop_ops = {
static u8 cdns_sierra_pll_mux_get_parent(struct clk_hw *hw)
{
struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw);
- struct regmap_field *plllc1en_field = mux->plllc1en_field;
- struct regmap_field *termen_field = mux->termen_field;
struct regmap_field *field = mux->pfdclk_sel_preg;
unsigned int val;
int index;
regmap_field_read(field, &val);
- if (strstr(clk_hw_get_name(hw), clk_names[CDNS_SIERRA_PLL_CMNLC1])) {
+ if (strstr(clk_hw_get_name(hw), clk_names[CDNS_SIERRA_PLL_CMNLC1]))
index = clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table[CMN_PLLLC1], 0, val);
- if (index == 1) {
- regmap_field_write(plllc1en_field, 1);
- regmap_field_write(termen_field, 1);
- }
- } else {
+ else
index = clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table[CMN_PLLLC], 0, val);
- }
return index;
}
diff --git a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
index 0f25d81de61b..02539ad0b0fe 100644
--- a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
+++ b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
@@ -44,6 +44,8 @@
#define MHZ (1000UL * 1000UL)
#endif
+#define PHY_DEFAULT_RATE 74250000
+
#define PHY_PLL_DIV_REGS_NUM 7
struct phy_config {
@@ -489,17 +491,6 @@ static int fsl_samsung_hdmi_phy_configure(struct fsl_samsung_hdmi_phy *phy,
return ret;
}
-static unsigned long phy_clk_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw);
-
- if (!phy->cur_cfg)
- return 74250000;
-
- return phy->cur_cfg->pixclk;
-}
-
/* Helper function to lookup the available fractional-divider rate */
static const struct phy_config *fsl_samsung_hdmi_phy_lookup_rate(unsigned long rate)
{
@@ -520,6 +511,14 @@ static const struct phy_config *fsl_samsung_hdmi_phy_lookup_rate(unsigned long r
&phy_pll_cfg[i] : &phy_pll_cfg[i+1]);
}
+static unsigned long phy_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw);
+
+ return phy->cur_cfg->pixclk;
+}
+
static void fsl_samsung_hdmi_calculate_phy(struct phy_config *cal_phy, unsigned long rate,
u8 p, u16 m, u8 s)
{
@@ -610,7 +609,7 @@ static int phy_clk_register(struct fsl_samsung_hdmi_phy *phy)
{
struct device *dev = phy->dev;
struct device_node *np = dev->of_node;
- struct clk_init_data init;
+ struct clk_init_data init = {};
const char *parent_name;
struct clk *phyclk;
int ret;
@@ -640,6 +639,7 @@ static int phy_clk_register(struct fsl_samsung_hdmi_phy *phy)
static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
{
+ const struct phy_config *default_cfg;
struct fsl_samsung_hdmi_phy *phy;
int ret;
@@ -664,6 +664,23 @@ static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
return dev_err_probe(phy->dev, PTR_ERR(phy->refclk),
"failed to get ref clk\n");
+ /*
+ * Establish the rate reported by recalc_rate() before registering the
+ * clock. Otherwise CCF may skip the first set_rate() when it requests
+ * the assumed default rate, leaving the PHY unconfigured.
+ */
+ ret = clk_prepare_enable(phy->refclk);
+ if (ret)
+ return dev_err_probe(phy->dev, ret,
+ "failed to enable ref clk\n");
+
+ default_cfg = fsl_samsung_hdmi_phy_lookup_rate(PHY_DEFAULT_RATE);
+ ret = fsl_samsung_hdmi_phy_configure(phy, default_cfg);
+ clk_disable_unprepare(phy->refclk);
+ if (ret)
+ return dev_err_probe(phy->dev, ret,
+ "failed to configure default rate\n");
+
pm_runtime_get_noresume(phy->dev);
pm_runtime_set_active(phy->dev);
pm_runtime_enable(phy->dev);
@@ -702,7 +719,7 @@ static int __maybe_unused fsl_samsung_hdmi_phy_suspend(struct device *dev)
static int __maybe_unused fsl_samsung_hdmi_phy_resume(struct device *dev)
{
struct fsl_samsung_hdmi_phy *phy = dev_get_drvdata(dev);
- int ret = 0;
+ int ret;
ret = clk_prepare_enable(phy->apbclk);
if (ret) {
@@ -710,11 +727,23 @@ static int __maybe_unused fsl_samsung_hdmi_phy_resume(struct device *dev)
return ret;
}
- if (phy->cur_cfg)
- ret = fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
+ if (!phy->cur_cfg)
+ return 0;
- return ret;
+ ret = clk_prepare_enable(phy->refclk);
+ if (ret) {
+ dev_err(phy->dev, "failed to enable ref clk\n");
+ goto disable_apbclk;
+ }
+
+ ret = fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
+ clk_disable_unprepare(phy->refclk);
+ if (!ret)
+ return 0;
+disable_apbclk:
+ clk_disable_unprepare(phy->apbclk);
+ return ret;
}
static DEFINE_RUNTIME_DEV_PM_OPS(fsl_samsung_hdmi_phy_pm_ops,
diff --git a/drivers/phy/hisilicon/phy-hi3670-pcie.c b/drivers/phy/hisilicon/phy-hi3670-pcie.c
index 7396c601d874..148763ed2c62 100644
--- a/drivers/phy/hisilicon/phy-hi3670-pcie.c
+++ b/drivers/phy/hisilicon/phy-hi3670-pcie.c
@@ -480,7 +480,7 @@ static int hi3670_pcie_allclk_ctrl(struct hi3670_pcie_phy *phy, bool clk_on)
ret = hi3670_pcie_pll_ctrl(phy, true);
if (ret) {
dev_err(dev, "Failed to enable pll\n");
- return -EINVAL;
+ goto close_clocks;
}
hi3670_pcie_hp_debounce_gt(phy, true);
hi3670_pcie_phyref_gt(phy, true);
@@ -569,6 +569,7 @@ static int hi3670_pcie_get_resources_from_pcie(struct hi3670_pcie_phy *phy)
}
pcie_dev = bus_find_device_by_of_node(&platform_bus_type, pcie_port);
+ of_node_put(pcie_port);
if (!pcie_dev) {
dev_err(dev, "Didn't find pcie device\n");
return -ENODEV;
@@ -583,6 +584,7 @@ static int hi3670_pcie_get_resources_from_pcie(struct hi3670_pcie_phy *phy)
* right regmap. So, let's use the named version.
*/
phy->apb = dev_get_regmap(pcie_dev, "kirin_pcie_apb");
+ put_device(pcie_dev);
if (!phy->apb) {
dev_err(dev, "Failed to get APB regmap\n");
return -ENODEV;
diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig
index ba6461350951..9beffd1fbeb3 100644
--- a/drivers/phy/mediatek/Kconfig
+++ b/drivers/phy/mediatek/Kconfig
@@ -13,6 +13,18 @@ config PHY_MTK_PCIE
callback for PCIe GEN3 port, it supports software efuse
initialization.
+config PHY_MTK_PCIE_SPHY
+ tristate "MediaTek PCIe S-PHY Driver"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on OF
+ select GENERIC_PHY
+ help
+ Enable this to add support for MediaTek PCIe S-PHYs used with the
+ PCI-Express Gen4 controllers as found in MT6991, MT8196 and others.
+
+ To compile this driver as module, say 'M' here: the module will be
+ called phy-mtk-pcie-sphy.
+
config PHY_MTK_XFI_TPHY
tristate "MediaTek 10GE SerDes XFI T-PHY driver"
depends on ARCH_MEDIATEK || COMPILE_TEST
diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile
index ed0da708759b..7e984c7cfea3 100644
--- a/drivers/phy/mediatek/Makefile
+++ b/drivers/phy/mediatek/Makefile
@@ -5,6 +5,7 @@
obj-$(CONFIG_PHY_MTK_DP) += phy-mtk-dp.o
obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-pcie.o
+obj-$(CONFIG_PHY_MTK_PCIE_SPHY) += phy-mtk-pcie-sphy.o
obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o
obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o
diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
index d7024a144335..dbef4a4bd60a 100644
--- a/drivers/phy/mediatek/phy-mtk-dp.c
+++ b/drivers/phy/mediatek/phy-mtk-dp.c
@@ -4,104 +4,350 @@
*
* Copyright (c) 2022, BayLibre Inc.
* Copyright (c) 2022, MediaTek Inc.
+ *
+ * Major refactoring and new SoCs support
+ * Copyright (c) 2026, Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
+#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
+#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
-#define PHY_OFFSET 0x1000
+#define MTK_DP_PHY_MAX_LANES 4
+
+/* DP_PHYA_GLB_BIAS_GEN_0 (PHYA - Analog) */
+#define XTP_GLB_BIAS_INT_R_CTRL GENMASK(20, 16)
+
+/* DP_PHYA_GLB_FORCE_CTRL_1 */
+#define CKM_CKTX0_EN_FORCE_MODE BIT(10)
+
+/* DP_PHYA_GLB_DPAUX_TX */
+#define CKM_PT0_CKTX_IMPSEL GENMASK(23, 20)
-#define MTK_DP_PHY_DIG_PLL_CTL_1 (PHY_OFFSET + 0x14)
+/* DP_PHYA_LAN_LANE_TX_0 */
+#define XTP_LN_TX_IMPSEL_PMOS GENMASK(15, 12)
+#define XTP_LN_TX_IMPSEL_NMOS GENMASK(19, 16)
+
+/* DP_PHYA_GLB_FORCE_CTRL_1 */
+#define CKM_CKTX0_EN_FORCE_MODE BIT(10)
+
+/* DP_PHYD_PLL_CTL_1 */
#define TPLL_SSC_EN BIT(3)
-#define MTK_DP_PHY_DIG_BIT_RATE (PHY_OFFSET + 0x3C)
-#define BIT_RATE_RBR 0
-#define BIT_RATE_HBR 1
-#define BIT_RATE_HBR2 2
-#define BIT_RATE_HBR3 3
-
-#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)
+/* DP_PHYD_BIT_RATE */
+#define PHYD_DIG_RG_BIT_RATE_V2 GENMASK(3, 0)
+#define PHYD_DIG_RG_BIT_RATE GENMASK(1, 0)
+
+/* DP_PHYD_SW_RST */
+#define PHYD_DIG_GLB_SW_RST_B GENMASK(7, 0)
+# define DP_GLB_SW_RST_PHYD BIT(0)
+# define DP_GLB_SW_RST_TFIFO_ANA BIT(1)
+# define DP_GLB_SW_RST_XTAL_CLK BIT(2)
+# define DP_GLB_SW_RST_MAIN_LINK BIT(3)
+
+/* DP_PHYD_AUX_RX_CTL */
+#define PHYD_DIG_DPAUX_RX_EN BIT(0)
+#define PHYD_DIG_XTP_GLB_CKDET_EN BIT(1)
+#define PHYD_DIG_DPAUX_RX_DEGLITCH_EN BIT(2)
+
+#define DRIVING_PARAM_0_DEFAULT 0x0
+#define DRIVING_PARAM_1_DEFAULT 0x0
+#define DRIVING_PARAM_2_DEFAULT 0x0
+
+/* DP_PHYD_TX_CTL_0 */
+#define PHYD_TX_LN_EN GENMASK(7, 4)
+#define PHYD_TX_LN_EN_V2 GENMASK(3, 0)
+
+/* DP_PHYD_DRIVING_FORCE */
+#define PHYD_DP_TX_FORCE_VOLT_SWING_EN BIT(0)
+#define PHYD_DP_TX_FORCE_VOLT_SWING_VAL GENMASK(2, 1)
+#define PHYD_DP_TX_FORCE_PRE_EMPH_VAL GENMASK(4, 3)
+
+/*
+ * DRIVING_PARAM_X (PHYD - Digital)
+ *
+ * Driving param registers are split in three sets, all containing settings
+ * for Voltage Swing and Pre-Emphasis for each lane's differential pair.
+ *
+ * All three sets share the same layout, but for different physical signals;
+ * In particular:
+ * [0-2]: LC TX CM (Minus / Negative Edge)
+ * [3-5]: LC TX C (Logic State Change Point)
+ * [6-8]: LC TX CP (Plus / Positive Edge)
+ *
+ * And they contain values for:
+ * [0,3,6]: Swing 0 Pre[0-3]
+ * [1,4,7]: Swing 1 Pre[0-2] and Swing 2 Pre0
+ * [2,5,8]: Swing 2 Pre1 and Swing 3 Pre0
+ */
+#define PHYD_DIG_NUM_DRV_PARA_REGS 9
+#define XTP_LN_TX_LCTXC_SW0_PRE0 GENMASK(5, 0)
+#define XTP_LN_TX_LCTXC_SW0_PRE1 GENMASK(13, 8)
+#define XTP_LN_TX_LCTXC_SW0_PRE2 GENMASK(21, 16)
+#define XTP_LN_TX_LCTXC_SW0_PRE3 GENMASK(29, 24)
+
+#define XTP_LN_TX_LCTXC_SW1_PRE0 GENMASK(5, 0)
+#define XTP_LN_TX_LCTXC_SW1_PRE1 GENMASK(13, 8)
+#define XTP_LN_TX_LCTXC_SW1_PRE2 GENMASK(21, 16)
+#define XTP_LN_TX_LCTXC_SW2_PRE0 GENMASK(29, 24)
+
+#define XTP_LN_TX_LCTXC_SW2_PRE1 GENMASK(5, 0)
+#define XTP_LN_TX_LCTXC_SW3_PRE0 GENMASK(13, 8)
+
+#define BUILD_DRIVING_PARAM_0(sw0_pre0, sw0_pre1, sw0_pre2, sw0_pre3) ( \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE0, sw0_pre0) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE1, sw0_pre1) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE2, sw0_pre2) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW0_PRE3, sw0_pre3) \
+)
+
+#define BUILD_DRIVING_PARAM_12(sw1_pre0, sw1_pre1, sw1_pre2, sw2_pre0) (\
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW1_PRE0, sw1_pre0) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW1_PRE1, sw1_pre1) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW1_PRE2, sw1_pre2) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW2_PRE0, sw2_pre0) \
+)
+
+#define BUILD_DRIVING_PARAM_23(sw2_pre1, sw3_pre0) ( \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW2_PRE1, sw2_pre1) | \
+ FIELD_PREP_CONST(XTP_LN_TX_LCTXC_SW3_PRE0, sw3_pre0) \
+)
+
+/* MT8195: Logic State Change Point (LC TX C) */
+#define MT8195_DRIVING_PARAM_3_DEFAULT BUILD_DRIVING_PARAM_0( 16, 20, 24, 32)
+#define MT8195_DRIVING_PARAM_4_DEFAULT BUILD_DRIVING_PARAM_12(24, 30, 36, 32)
+#define MT8195_DRIVING_PARAM_5_DEFAULT BUILD_DRIVING_PARAM_23(40, 48)
+
+/* MT8195: Positive Edge (LC TX CP) */
+#define MT8195_DRIVING_PARAM_6_DEFAULT BUILD_DRIVING_PARAM_0( 0, 4, 8, 16)
+#define MT8195_DRIVING_PARAM_7_DEFAULT BUILD_DRIVING_PARAM_12(0, 6, 12, 0)
+#define MT8195_DRIVING_PARAM_8_DEFAULT BUILD_DRIVING_PARAM_23(8, 0)
+
+/* MT8196/MT6991: Logic State Change Point (LC TX C) */
+#define MT8196_DRIVING_PARAM_3_DEFAULT BUILD_DRIVING_PARAM_0( 10, 12, 14, 17)
+#define MT8196_DRIVING_PARAM_4_DEFAULT BUILD_DRIVING_PARAM_12(14, 17, 18, 18)
+#define MT8196_DRIVING_PARAM_5_DEFAULT BUILD_DRIVING_PARAM_23(21, 24)
+
+/* MT8196/MT6991: Positive Edge (LC TX CP) */
+#define MT8196_DRIVING_PARAM_6_DEFAULT BUILD_DRIVING_PARAM_0( 0, 2, 4, 7)
+#define MT8196_DRIVING_PARAM_7_DEFAULT BUILD_DRIVING_PARAM_12(0, 3, 6, 0)
+#define MT8196_DRIVING_PARAM_8_DEFAULT BUILD_DRIVING_PARAM_23(3, 0)
+
+enum mtk_dp_phya_ana_glb_regidx {
+ DP_PHYA_GLB_BIAS_GEN_0,
+ DP_PHYA_GLB_BIAS_GEN_1,
+ DP_PHYA_GLB_DPAUX_TX,
+ DP_PHYA_GLB_FORCE_CTRL_0,
+ DP_PHYA_GLB_FORCE_CTRL_1,
+ DP_PHYA_GLOBAL_MAX
+};
+
+enum mtk_dp_phya_ana_lane_regidx {
+ DP_PHYA_LAN_LANE_TX_0,
+ DP_PHYA_LAN_MAX
+};
+
+enum mtk_dp_phyd_dig_lane_regidx {
+ DP_PHYD_LAN_DRIVING_FORCE,
+ DP_PHYD_LAN_DRIVING_PARAM_0,
+ DP_PHYD_LAN_MAX
+};
+
+enum mtk_dp_phyd_dig_glb_regidx {
+ DP_PHYD_PLL_CTL_0,
+ DP_PHYD_PLL_CTL_1,
+ DP_PHYD_SW_RST,
+ DP_PHYD_BIT_RATE,
+ DP_PHYD_AUX_RX_CTL,
+ DP_PHYD_TX_CTL_0,
+ DP_PHYD_GLOBAL_MAX
+};
+
+enum mtk_dp_phyd_bit_rate_regval {
+ DP_PHYD_BIT_RATE_RBR,
+ DP_PHYD_BIT_RATE_HBR,
+ DP_PHYD_BIT_RATE_HBR2,
+ DP_PHYD_BIT_RATE_HBR3,
+ DP_PHYD_BIT_RATE_MAX,
+};
+
+static const u8 mt8195_phy_ana_glb_regs[DP_PHYA_GLOBAL_MAX] = {
+ [DP_PHYA_GLB_BIAS_GEN_0] = 0x0,
+ [DP_PHYA_GLB_BIAS_GEN_1] = 0x4,
+ [DP_PHYA_GLB_DPAUX_TX] = 0x8,
+ [DP_PHYA_GLB_FORCE_CTRL_0] = 0x30,
+ [DP_PHYA_GLB_FORCE_CTRL_1] = 0x34,
+};
+
+static const u8 mt8195_phy_ana_lane_regs[DP_PHYA_LAN_MAX] = {
+ [DP_PHYA_LAN_LANE_TX_0] = 0x4,
+};
+
+static const u8 mt8195_phy_dig_lane_regs[DP_PHYD_LAN_MAX] = {
+ [DP_PHYD_LAN_DRIVING_FORCE] = 0x18,
+ [DP_PHYD_LAN_DRIVING_PARAM_0] = 0x2c,
+};
+
+static const u8 mt8196_phy_dig_lane_regs[DP_PHYD_LAN_MAX] = {
+ [DP_PHYD_LAN_DRIVING_FORCE] = 0x30,
+ [DP_PHYD_LAN_DRIVING_PARAM_0] = 0x34,
+};
+
+static const u8 mt8195_phy_dig_glb_regs[DP_PHYD_GLOBAL_MAX] = {
+ [DP_PHYD_PLL_CTL_0] = 0x10,
+ [DP_PHYD_PLL_CTL_1] = 0x14,
+ [DP_PHYD_SW_RST] = 0x38,
+ [DP_PHYD_BIT_RATE] = 0x3c,
+ [DP_PHYD_AUX_RX_CTL] = 0x40,
+ [DP_PHYD_TX_CTL_0] = 0x44,
+};
+
+static const u8 mt8196_phy_dig_glb_regs[DP_PHYD_GLOBAL_MAX] = {
+ [DP_PHYD_PLL_CTL_0] = 0x10,
+ [DP_PHYD_PLL_CTL_1] = 0x14,
+ [DP_PHYD_SW_RST] = 0x38,
+ [DP_PHYD_BIT_RATE] = 0x3c,
+ [DP_PHYD_AUX_RX_CTL] = 0x40,
+ [DP_PHYD_TX_CTL_0] = 0x74,
+};
+
+static const u8 mt8195_phy_dig_bitrate_val[DP_PHYD_BIT_RATE_MAX] = {
+ [DP_PHYD_BIT_RATE_RBR] = 0,
+ [DP_PHYD_BIT_RATE_HBR] = 1,
+ [DP_PHYD_BIT_RATE_HBR2] = 2,
+ [DP_PHYD_BIT_RATE_HBR3] = 3
+};
+
+static const u8 mt8196_edp_phy_dig_bitrate_val[DP_PHYD_BIT_RATE_MAX] = {
+ [DP_PHYD_BIT_RATE_RBR] = 1,
+ [DP_PHYD_BIT_RATE_HBR] = 4,
+ [DP_PHYD_BIT_RATE_HBR2] = 7,
+ [DP_PHYD_BIT_RATE_HBR3] = 9
+};
+
+/**
+ * struct mtk_dp_phya_imp_sel - Per-Lane Impedance Selection
+ * @pmos: Impedance selection for P-Channel MOSFET
+ * @nmos: Impedance selection for N-Channel MOSFET
+ */
+struct mtk_dp_phya_imp_sel {
+ u8 pmos : 4;
+ u8 nmos : 4;
+};
+
+/**
+ * struct mtk_dp_phy_pdata - Platform data and defaults for MediaTek DP/eDP PHY
+ * @off_ana_glb: Base offset for dptx_phyd_sifslv_ana_glb
+ * @off_ana_lane: Base offsets for dptx_phyd_sifslv_ana_lan (for each lane)
+ * @off_dig_glb: Base offset for dptx_phyd_sifslv_dig_glb
+ * @off_dig_lane: Base offsets for dptx_phyd_sifslv_dig_lan (for each lane)
+ * @regs_ana_glb: Register (layout) offsets for ana_glb
+ * @regs_ana_lane: Register (layout) offsets for ana_lan
+ * @regs_dig_glb: Register (layout) offsets for dig_glb
+ * @regs_dig_lane: Register (layout) offsets for dig_lan
+ * @mask_dig_tx_ln: Register mask for PHYD_TX_LN_EN field
+ * @val_dig_bitrate:IP Version specific register values for Bit Rate setting
+ * @ana_bias_r: Internal resistance "R" Selection Settings (global)
+ * @ana_cktx_imp: TX Clock Impedance Selection Settings (global)
+ * @ana_lanes_imp: TX Impedance Selection Settings (for all lanes)
+ * @driving_params: Voltage Swing and Pre-Emphasis settings (for all lanes)
+ */
+struct mtk_dp_phy_pdata {
+ /* Register offsets */
+ u16 off_ana_glb;
+ u16 off_ana_lane[MTK_DP_PHY_MAX_LANES];
+ u16 off_dig_glb;
+ u16 off_dig_lane[MTK_DP_PHY_MAX_LANES];
+
+ /* Register maps */
+ const u8 *regs_ana_glb;
+ const u8 *regs_ana_lane;
+ const u8 *regs_dig_glb;
+ const u8 *regs_dig_lane;
+
+ /* Register masks */
+ u32 mask_dig_tx_ln;
+
+ /* IP-Version specific register value arrays */
+ const u8 *val_dig_bitrate;
+
+ /* Calibration defaults */
+ u8 ana_bias_r;
+ u8 ana_cktx_imp;
+ struct mtk_dp_phya_imp_sel ana_lanes_imp;
+ u32 driving_params[PHYD_DIG_NUM_DRV_PARA_REGS];
+};
struct mtk_dp_phy {
- struct regmap *regs;
+ struct device *dev;
+ struct regmap *regmap;
+ const struct mtk_dp_phy_pdata *pdata;
+
+ u8 ana_bias_r;
+ u8 ana_cktx_imp;
+ struct mtk_dp_phya_imp_sel ana_impsel[MTK_DP_PHY_MAX_LANES];
};
+static void mtk_dp_phy_set_analog_calibration_params(struct mtk_dp_phy *dp_phy)
+{
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ const u8 *regs_ana_glb = pdata->regs_ana_glb;
+ const u8 *regs_ana_lane = pdata->regs_ana_lane;
+ int i;
+
+ regmap_update_bits(dp_phy->regmap,
+ pdata->off_ana_glb + regs_ana_glb[DP_PHYA_GLB_BIAS_GEN_0],
+ XTP_GLB_BIAS_INT_R_CTRL,
+ FIELD_PREP(XTP_GLB_BIAS_INT_R_CTRL, dp_phy->ana_bias_r));
+
+ regmap_update_bits(dp_phy->regmap,
+ pdata->off_ana_glb + regs_ana_glb[DP_PHYA_GLB_DPAUX_TX],
+ CKM_PT0_CKTX_IMPSEL,
+ FIELD_PREP(CKM_PT0_CKTX_IMPSEL, dp_phy->ana_cktx_imp));
+
+ for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
+ struct mtk_dp_phya_imp_sel *ana_imp = &dp_phy->ana_impsel[i];
+ u32 val = FIELD_PREP(XTP_LN_TX_IMPSEL_PMOS, ana_imp->pmos) |
+ FIELD_PREP(XTP_LN_TX_IMPSEL_NMOS, ana_imp->nmos);
+ u32 off_ana_lane = pdata->off_ana_lane[i];
+
+ regmap_update_bits(dp_phy->regmap,
+ off_ana_lane + regs_ana_lane[DP_PHYA_LAN_LANE_TX_0],
+ XTP_LN_TX_IMPSEL_PMOS | XTP_LN_TX_IMPSEL_NMOS, val);
+ }
+}
+
+static void mtk_dp_phy_set_digital_drv_params(struct mtk_dp_phy *dp_phy)
+{
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ const u32 reg = pdata->regs_dig_lane[DP_PHYD_LAN_DRIVING_PARAM_0];
+ int i;
+
+ /*
+ * Assume that all lanes need the same driving parameters: this
+ * will bulk write from DRIVING_PARAM_0 to DRIVING_PARAM_8 on
+ * all lanes (a grand total of [9 * num_lanes] 32-bit writes)
+ */
+ for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++)
+ regmap_bulk_write(dp_phy->regmap, pdata->off_dig_lane[i] + reg,
+ pdata->driving_params,
+ ARRAY_SIZE(pdata->driving_params));
+}
+
static int mtk_dp_phy_init(struct phy *phy)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
- static const u32 driving_params[] = {
- DRIVING_PARAM_3_DEFAULT,
- DRIVING_PARAM_4_DEFAULT,
- DRIVING_PARAM_5_DEFAULT,
- DRIVING_PARAM_6_DEFAULT,
- DRIVING_PARAM_7_DEFAULT,
- DRIVING_PARAM_8_DEFAULT
- };
- regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3,
- driving_params, ARRAY_SIZE(driving_params));
- regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3,
- driving_params, ARRAY_SIZE(driving_params));
- regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3,
- driving_params, ARRAY_SIZE(driving_params));
- regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3,
- driving_params, ARRAY_SIZE(driving_params));
+ mtk_dp_phy_set_digital_drv_params(dp_phy);
+ mtk_dp_phy_set_analog_calibration_params(dp_phy);
return 0;
}
@@ -109,9 +355,14 @@ static int mtk_dp_phy_init(struct phy *phy)
static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
u32 val;
+ int i;
if (opts->dp.set_rate) {
+ const u32 reg_bit_rate = pdata->regs_dig_glb[DP_PHYD_BIT_RATE];
+ enum mtk_dp_phyd_bit_rate_regval regval_idx;
+
switch (opts->dp.link_rate) {
default:
dev_err(&phy->dev,
@@ -119,84 +370,469 @@ static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
opts->dp.link_rate);
return -EINVAL;
case 1620:
- val = BIT_RATE_RBR;
+ regval_idx = DP_PHYD_BIT_RATE_RBR;
break;
case 2700:
- val = BIT_RATE_HBR;
+ regval_idx = DP_PHYD_BIT_RATE_HBR;
break;
case 5400:
- val = BIT_RATE_HBR2;
+ regval_idx = DP_PHYD_BIT_RATE_HBR2;
break;
case 8100:
- val = BIT_RATE_HBR3;
+ regval_idx = DP_PHYD_BIT_RATE_HBR3;
break;
}
- regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE, val);
+ regmap_write(dp_phy->regmap, pdata->off_dig_glb + reg_bit_rate,
+ pdata->val_dig_bitrate[regval_idx]);
+ }
+
+ if (opts->dp.set_lanes) {
+ const u32 reg_dig_tx_ctl = pdata->regs_dig_glb[DP_PHYD_TX_CTL_0];
+
+ val = 0;
+ for (i = 0; i < opts->dp.lanes; i++)
+ val |= field_prep(pdata->mask_dig_tx_ln, BIT(i));
+
+ regmap_update_bits(dp_phy->regmap, pdata->off_dig_glb + reg_dig_tx_ctl,
+ pdata->mask_dig_tx_ln, val);
+ }
+
+ if (opts->dp.set_voltages) {
+ const u32 reg_drv_force = pdata->regs_dig_lane[DP_PHYD_LAN_DRIVING_FORCE];
+
+ if (opts->dp.lanes > 4) {
+ dev_err(&phy->dev, "Wrong lanes config %u\n", opts->dp.lanes);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < opts->dp.lanes; i++) {
+ const u32 off_dig_lane = pdata->off_dig_lane[i];
+ u32 val;
+
+ val = FIELD_PREP(PHYD_DP_TX_FORCE_VOLT_SWING_VAL, opts->dp.voltage[i]);
+ val |= FIELD_PREP(PHYD_DP_TX_FORCE_PRE_EMPH_VAL, opts->dp.pre[i]);
+ val |= PHYD_DP_TX_FORCE_VOLT_SWING_EN;
+
+ regmap_update_bits(dp_phy->regmap, off_dig_lane + reg_drv_force,
+ PHYD_DP_TX_FORCE_VOLT_SWING_EN |
+ PHYD_DP_TX_FORCE_VOLT_SWING_VAL |
+ PHYD_DP_TX_FORCE_PRE_EMPH_VAL,
+ val);
+ }
}
- regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1,
+ regmap_update_bits(dp_phy->regmap,
+ pdata->off_dig_glb + pdata->regs_dig_glb[DP_PHYD_PLL_CTL_1],
TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0);
return 0;
}
+static int mtk_dp_phy_power_on(struct phy *phy)
+{
+ struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ const u8 *regs_dig = pdata->regs_dig_glb;
+ const u8 *regs_ana = pdata->regs_ana_glb;
+
+ /* Enable AUX Channel with RX De-Glitch and input clock detection */
+ regmap_write(dp_phy->regmap,
+ pdata->off_dig_glb + regs_dig[DP_PHYD_AUX_RX_CTL],
+ PHYD_DIG_DPAUX_RX_EN |
+ PHYD_DIG_XTP_GLB_CKDET_EN |
+ PHYD_DIG_DPAUX_RX_DEGLITCH_EN);
+
+ regmap_clear_bits(dp_phy->regmap,
+ pdata->off_ana_glb + regs_ana[DP_PHYA_GLB_FORCE_CTRL_1],
+ CKM_CKTX0_EN_FORCE_MODE);
+
+ return 0;
+}
+
+static int mtk_dp_phy_disable_all_lanes(struct mtk_dp_phy *dp_phy)
+{
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ const u8 *regs = pdata->regs_dig_glb;
+ u32 val;
+
+ /* Get mask of currently enabled lane */
+ regmap_read(dp_phy->regmap, pdata->off_dig_glb + regs[DP_PHYD_TX_CTL_0], &val);
+ val = field_get(pdata->mask_dig_tx_ln, val);
+ if (val == 0)
+ return 0;
+
+ /* Disable all lanes (needs to be done one by one, from last to first) */
+ do {
+ u32 lane_num = fls(val) - 1;
+ val &= ~BIT(lane_num);
+
+ regmap_clear_bits(dp_phy->regmap,
+ pdata->off_dig_glb + regs[DP_PHYD_TX_CTL_0],
+ field_prep(pdata->mask_dig_tx_ln, BIT(lane_num)));
+ } while (val);
+
+ return 0;
+}
+
+static int mtk_dp_phy_power_off(struct phy *phy)
+{
+ struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ const u8 *regs_dig = pdata->regs_dig_glb;
+ const u8 *regs_ana = pdata->regs_ana_glb;
+ int ret;
+
+ regmap_set_bits(dp_phy->regmap,
+ pdata->off_ana_glb + regs_ana[DP_PHYA_GLB_FORCE_CTRL_1],
+ CKM_CKTX0_EN_FORCE_MODE);
+
+ /* Disable RX unconditionally */
+ regmap_write(dp_phy->regmap,
+ pdata->off_dig_glb + regs_dig[DP_PHYD_AUX_RX_CTL], 0);
+
+ ret = mtk_dp_phy_disable_all_lanes(dp_phy);
+ if (ret) {
+ dev_err(dp_phy->dev, "Could not disable lanes for poweroff!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static int mtk_dp_phy_reset(struct phy *phy)
{
struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ const u32 reg_rst = pdata->regs_dig_glb[DP_PHYD_SW_RST];
+ const u32 reg_drv_force = pdata->regs_dig_lane[DP_PHYD_LAN_DRIVING_FORCE];
+ int i, ret;
+
+ /* Clearing bits sets reset state */
+ regmap_clear_bits(dp_phy->regmap, pdata->off_dig_glb + reg_rst, DP_GLB_SW_RST_PHYD);
- regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
- DP_GLB_SW_RST_PHYD, 0);
+ /* PHYD needs 50uS to guarantee reset done */
usleep_range(50, 200);
- regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST,
- DP_GLB_SW_RST_PHYD, 1);
+
+ /* Setting bits means go out of reset */
+ regmap_set_bits(dp_phy->regmap, pdata->off_dig_glb + reg_rst, DP_GLB_SW_RST_PHYD);
+
+ /* Disable all lanes and continue reset even if this fails, but notify */
+ ret = mtk_dp_phy_disable_all_lanes(dp_phy);
+ if (ret)
+ dev_err(dp_phy->dev, "Could not disable lanes during reset!\n");
+
+ /* Reset Voltage Swing and Preemphasis values */
+ for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
+ const u32 off_dig_lane = pdata->off_dig_lane[i];
+
+ regmap_clear_bits(dp_phy->regmap, off_dig_lane + reg_drv_force,
+ PHYD_DP_TX_FORCE_VOLT_SWING_EN |
+ PHYD_DP_TX_FORCE_VOLT_SWING_VAL |
+ PHYD_DP_TX_FORCE_PRE_EMPH_VAL);
+ }
return 0;
}
static const struct phy_ops mtk_dp_phy_dev_ops = {
.init = mtk_dp_phy_init,
+ .power_on = mtk_dp_phy_power_on,
+ .power_off = mtk_dp_phy_power_off,
.configure = mtk_dp_phy_configure,
.reset = mtk_dp_phy_reset,
.owner = THIS_MODULE,
};
+static void mtk_dp_phy_get_default_cal_data(struct mtk_dp_phy *dp_phy)
+{
+ const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
+ int i;
+
+ dp_phy->ana_bias_r = pdata->ana_bias_r;
+ dp_phy->ana_cktx_imp = pdata->ana_cktx_imp;
+
+ /* Copy the default lane impedance settings to all lanes */
+ for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++)
+ memcpy(&dp_phy->ana_impsel[i], &pdata->ana_lanes_imp,
+ sizeof(dp_phy->ana_impsel[0]));
+
+ return;
+}
+
+static int mtk_dp_phy_get_one_cal_para(struct device *dev, const char *name, u8 max_val)
+{
+ u8 buf_byte;
+ u16 buf;
+ int ret;
+
+ /*
+ * All of the calibrations are always max 8 bits long, but some may
+ * be split between two different 8-bits cells: handle this corner
+ * case by retrying reading as u16.
+ */
+ ret = nvmem_cell_read_u8(dev, name, &buf_byte);
+ if (ret)
+ ret = nvmem_cell_read_u16(dev, name, &buf);
+ else
+ buf = buf_byte;
+
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot get calibration data for %s\n", name);
+
+ if (buf == 0) {
+ dev_warn(dev, "No calibration for %s. Using defaults\n", name);
+ return -ENOENT;
+ }
+
+ if (buf > max_val)
+ return dev_err_probe(dev, -ERANGE, "Bad value %u retrieved for %s\n", buf, name);
+
+ return buf;
+}
+
+static int mtk_dp_phy_get_calibration_data(struct mtk_dp_phy *dp_phy)
+{
+ char mtk_dp_cal_lane_imp_name[] = "impedance-laneXM";
+ struct device *dev = dp_phy->dev;
+ int i, ret;
+
+ ret = mtk_dp_phy_get_one_cal_para(dev, "rbias-trim", FIELD_MAX(XTP_GLB_BIAS_INT_R_CTRL));
+ if (ret < 0)
+ goto end;
+ dp_phy->ana_bias_r = ret;
+
+ ret = mtk_dp_phy_get_one_cal_para(dev, "impedance-txclk", FIELD_MAX(CKM_PT0_CKTX_IMPSEL));
+ if (ret < 0)
+ goto end;
+ dp_phy->ana_cktx_imp = ret;
+
+ /* Get impedance params for each lane */
+ for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
+ /* P-MOSFET first */
+ snprintf(mtk_dp_cal_lane_imp_name, ARRAY_SIZE(mtk_dp_cal_lane_imp_name),
+ "impedance-lane%dp", i);
+ ret = mtk_dp_phy_get_one_cal_para(dev, mtk_dp_cal_lane_imp_name,
+ FIELD_MAX(XTP_LN_TX_IMPSEL_PMOS));
+ if (ret < 0)
+ goto end;
+ dp_phy->ana_impsel[i].pmos = ret;
+
+ /* ...and then N-MOSFET too */
+ snprintf(mtk_dp_cal_lane_imp_name, ARRAY_SIZE(mtk_dp_cal_lane_imp_name),
+ "impedance-lane%dn", i);
+ ret = mtk_dp_phy_get_one_cal_para(dev, mtk_dp_cal_lane_imp_name,
+ FIELD_MAX(XTP_LN_TX_IMPSEL_NMOS));
+ if (ret < 0)
+ goto end;
+ dp_phy->ana_impsel[i].nmos = ret;
+ }
+end:
+ if (ret < 0) {
+ /*
+ * If any of the calibration values is missing, or if there
+ * is no calibration at all in the eFuses, copy the default
+ * one entirely (as partial values shall not be mixed!)
+ */
+ if (ret == -ENOENT) {
+ dev_info(dev, "Using calibration default values\n");
+ mtk_dp_phy_get_default_cal_data(dp_phy);
+ return 0;
+ }
+ return ret;
+ };
+
+ return 0;
+}
+
+static void mtk_dp_phy_legacy_remove_lookup(void *data)
+{
+ struct phy *phy = data;
+ struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
+
+ phy_remove_lookup(phy, "dp", dev_name(dp_phy->dev));
+}
+
+static const struct mtk_dp_phy_pdata mt8195_dp_phy_data;
+
+static int mtk_dp_phy_legacy_probe(struct platform_device *pdev, struct mtk_dp_phy *dp_phy)
+{
+ struct device *dev = &pdev->dev;
+ struct phy *phy;
+ int ret;
+
+ /*
+ * If legacy platform driver probe, assume this is MT8195 or compatible
+ * with a devicetree that was not migrated to the new, proper bindings.
+ */
+ dp_phy->pdata = &mt8195_dp_phy_data;
+ dp_phy->regmap = *(struct regmap **)dev->platform_data;
+ if (!dp_phy->regmap)
+ return dev_err_probe(dev, -EINVAL, "No platform data available\n");
+
+ 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);
+
+ /*
+ * Set default calibration data before exposing the PHY.
+ * For legacy probe, mtk_dp will set calibrations from eFuse, if found.
+ */
+ mtk_dp_phy_get_default_cal_data(dp_phy);
+
+ ret = phy_create_lookup(phy, "dp", dev_name(dev));
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, mtk_dp_phy_legacy_remove_lookup, phy);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct regmap_config mtk_dp_phy_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .disable_locking = true,
+};
+
static int mtk_dp_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct phy_provider *provider;
struct mtk_dp_phy *dp_phy;
+ void __iomem *base;
struct phy *phy;
- struct regmap *regs;
-
- regs = *(struct regmap **)dev->platform_data;
- if (!regs)
- return dev_err_probe(dev, -EINVAL,
- "No data passed, requires struct regmap**\n");
+ int ret;
dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL);
if (!dp_phy)
return -ENOMEM;
- dp_phy->regs = regs;
+ dp_phy->dev = dev;
+
+ /* If there's no devicetree, go for legacy pdev probe */
+ if (!dev->of_node)
+ return mtk_dp_phy_legacy_probe(pdev, dp_phy);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ dp_phy->regmap = devm_regmap_init_mmio(dev, base, &mtk_dp_phy_regmap_cfg);
+ if (IS_ERR(dp_phy->regmap))
+ return PTR_ERR(dp_phy->regmap);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ dp_phy->pdata = device_get_match_data(dev);
+
+ if (IS_REACHABLE(CONFIG_NVMEM)) {
+ ret = mtk_dp_phy_get_calibration_data(dp_phy);
+ if (ret)
+ return ret;
+ } else {
+ /* Use default calibration data */
+ mtk_dp_phy_get_default_cal_data(dp_phy);
+ }
+
phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops);
if (IS_ERR(phy))
return dev_err_probe(dev, PTR_ERR(phy),
"Failed to create DP PHY\n");
phy_set_drvdata(phy, dp_phy);
- if (!dev->of_node)
- phy_create_lookup(phy, "dp", dev_name(dev));
+
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(provider))
+ return PTR_ERR(provider);
return 0;
}
+static const struct mtk_dp_phy_pdata mt8195_dp_phy_data = {
+ .off_ana_glb = 0,
+ .off_ana_lane = { 0x100, 0x200, 0x300, 0x400 },
+ .off_dig_glb = 0x1000,
+ .off_dig_lane = { 0x1100, 0x1200, 0x1300, 0x1400 },
+ .regs_ana_glb = mt8195_phy_ana_glb_regs,
+ .regs_ana_lane = mt8195_phy_ana_lane_regs,
+ .regs_dig_glb = mt8195_phy_dig_glb_regs,
+ .regs_dig_lane = mt8195_phy_dig_lane_regs,
+ .mask_dig_tx_ln = PHYD_TX_LN_EN,
+ .val_dig_bitrate = mt8195_phy_dig_bitrate_val,
+ .ana_bias_r = 15,
+ .ana_cktx_imp = 8,
+ .ana_lanes_imp = {
+ .pmos = 8,
+ .nmos = 8,
+ },
+ .driving_params = {
+ [0] = 0,
+ [1] = 0,
+ [2] = 0,
+ [3] = MT8195_DRIVING_PARAM_3_DEFAULT,
+ [4] = MT8195_DRIVING_PARAM_4_DEFAULT,
+ [5] = MT8195_DRIVING_PARAM_5_DEFAULT,
+ [6] = MT8195_DRIVING_PARAM_6_DEFAULT,
+ [7] = MT8195_DRIVING_PARAM_7_DEFAULT,
+ [8] = MT8195_DRIVING_PARAM_8_DEFAULT
+ },
+};
+
+static const struct mtk_dp_phy_pdata mt8196_edp_phy_data = {
+ .off_ana_glb = 0x400,
+ .off_ana_lane = { 0x0, 0x100, 0x200, 0x300 },
+ .off_dig_glb = 0x1400,
+ .off_dig_lane = { 0x1000, 0x1100, 0x1200, 0x1300 },
+ .regs_ana_glb = mt8195_phy_ana_glb_regs,
+ .regs_ana_lane = mt8195_phy_ana_lane_regs,
+ .regs_dig_glb = mt8196_phy_dig_glb_regs,
+ .regs_dig_lane = mt8196_phy_dig_lane_regs,
+ .mask_dig_tx_ln = PHYD_TX_LN_EN_V2,
+ .val_dig_bitrate = mt8196_edp_phy_dig_bitrate_val,
+ .ana_bias_r = 15,
+ .ana_cktx_imp = 8,
+ .ana_lanes_imp = {
+ .pmos = 8,
+ .nmos = 8,
+ },
+ .driving_params = {
+ [0] = 0,
+ [1] = 0,
+ [2] = 0,
+ [3] = MT8196_DRIVING_PARAM_3_DEFAULT,
+ [4] = MT8196_DRIVING_PARAM_4_DEFAULT,
+ [5] = MT8196_DRIVING_PARAM_5_DEFAULT,
+ [6] = MT8196_DRIVING_PARAM_6_DEFAULT,
+ [7] = MT8196_DRIVING_PARAM_7_DEFAULT,
+ [8] = MT8196_DRIVING_PARAM_8_DEFAULT
+ },
+};
+
+static const struct of_device_id mtk_dp_phy_of_match[] = {
+ { .compatible = "mediatek,mt8195-dp-phy", .data = &mt8195_dp_phy_data },
+ { .compatible = "mediatek,mt8196-edp-phy", .data = &mt8196_edp_phy_data },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mtk_dp_phy_of_match);
+
static struct platform_driver mtk_dp_phy_driver = {
.probe = mtk_dp_phy_probe,
.driver = {
.name = "mediatek-dp-phy",
+ .of_match_table = mtk_dp_phy_of_match,
},
};
module_platform_driver(mtk_dp_phy_driver);
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
-MODULE_DESCRIPTION("MediaTek DP PHY Driver");
+MODULE_DESCRIPTION("MediaTek DisplayPort PHY Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/phy/mediatek/phy-mtk-pcie-sphy.c b/drivers/phy/mediatek/phy-mtk-pcie-sphy.c
new file mode 100644
index 000000000000..2bf10346ea97
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-pcie-sphy.c
@@ -0,0 +1,561 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Copyright (c) 2026 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include "phy-mtk-io.h"
+
+/* PHY System Interface (SIF) registers */
+#define PEXTP_DIG_GLB_TOP 0x20
+# define RG_XTP_BYPASS_PIPE_RST_RC BIT(17)
+#define PEXTP_DIG_GLB_CKBG0 0x30
+# define RG_XTP_CKBG_XTAL_STABLE_TIME_SEL GENMASK(25, 16)
+#define PEXTP_DIG_GLB_TPLL_CTL0 0x38
+# define RG_XTP_TPLL_SET_STABLE_TIME_SEL GENMASK(7, 2)
+# define RG_XTP_TPLL_PWE_ON_STABLE_TIME_SEL GENMASK(9, 8)
+#define PEXTP_DIG_GLB_CLKREQ_CTL 0x50
+# define RG_XTP_CKM_EN_L1S0 BIT(13)
+# define RG_XTP_CKM_EN_L1S1 BIT(14)
+#define PEXTP_DIG_GLB_TPLL_CTL2 0xf4
+# define RG_XTP_TPLL_ISO_EN_STABLE_TIME_SEL GENMASK(13, 12)
+
+/* PHY System Interface Digital registers */
+#define PEXTP_DIG_LN_TRX_PIPE_IF_17 0x30e8
+# define RG_XTP_LN_RX_LF_CTLE_CSEL_GEN4 GENMASK(14, 12)
+#define PEXTP_DIG_LN_RX_F0 0x50f0
+# define RG_XTP_LN_RX_GEN1_CTLE1_CSEL GENMASK(3, 0)
+# define RG_XTP_LN_RX_GEN2_CTLE1_CSEL GENMASK(7, 4)
+# define RG_XTP_LN_RX_GEN3_CTLE1_CSEL GENMASK(11, 8)
+# define RG_XTP_LN_RX_GEN4_CTLE1_CSEL GENMASK(15, 12)
+#define PEXTP_DIG_LN_RX2_AEQ_EDGE_0 0x6004
+# define RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN3 GENMASK(21, 16)
+# define RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN4 GENMASK(29, 24)
+
+/* PHY System Interface Analog registers */
+#define PEXTP_ANA_GLB_TPLL1_RSVD 0x902c
+# define RG_XTP_GLB_TPLL1_P_PATH_GAIN GENMASK(2, 0)
+#define PEXTP_ANA_GLB_BIAS_0 0x9060
+# define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(5, 0)
+#define PEXTP_ANA_GLB_BIAS_1 0x90c0
+# define RG_XTP_GLB_BIAS_V2V_VTRIM GENMASK(9, 6)
+#define PEXTP_ANA_LN_TRX_0C 0xa00c
+# define RG_XTP_LN_TX_RSWN_IMPSEL GENMASK(20, 16)
+#define PEXTP_ANA_LN_TRX_34 0xa034
+# define RG_XTP_LN_RX_FE BIT(15)
+#define PEXTP_ANA_LN_TRX_6C 0xa06c
+# define RG_XTP_LN_RX_AEQ_CTLE_ERR_TYPE GENMASK(14, 13)
+# define AEQ_CTLE_SEARCH_ERR_TYPE_H1P5 0
+# define AEQ_CTLE_SEARCH_ERR_TYPE_H1P5_H2P5 1
+# define AEQ_CTLE_SEARCH_ERR_TYPE_P1P5_H2P5_H3P5 2
+#define PEXTP_ANA_LN_TRX_A0 0xa0a0
+# define RG_XTP_LN_TX_IMPSEL_PMOS GENMASK(4, 0)
+# define RG_XTP_LN_TX_IMPSEL_NMOS GENMASK(11, 7)
+# define RG_XTP_LN_RX_IMPSEL GENMASK(15, 12)
+#define PEXTP_ANA_LN_TRX_A8 0xa0a8
+# define RG_XTP_LN_RX_LEQ_RL_CTLE_CAL GENMASK(6, 2)
+# define RG_XTP_LN_RX_LEQ_RL_VGA_CAL GENMASK(11, 7)
+# define RG_XTP_LN_RX_LEQ_RL_DFE_CAL GENMASK(23, 19)
+#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4 0xb004
+#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8 0xb008
+#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_C 0xb00c
+#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10 0xb010
+#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14 0xb014
+#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18 0xb018
+# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1 GENMASK(5, 0)
+# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0 GENMASK(13, 8)
+# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1 GENMASK(21, 16)
+#define PEXTP_REG_LANE(x) ((x) * 0x100)
+
+/* PHY Clock Management (CKM) registers */
+#define XTP_CKM_FORCE_6 0x38
+# define RG_CKM_BIAS_WAIT_PRD_US GENMASK(21, 16)
+#define XTP_CKM_REG_SPLL_FBKDIV_5 0xd4
+# define RG_CKM_CKTX_IMPSEL_PMOS GENMASK(19, 16)
+# define RG_CKM_CKTX_IMPSEL_NMOS GENMASK(23, 20)
+# define RG_CKM_CKTX_IMPSEL_SW GENMASK(27, 24)
+
+/* Calibration data from eFuses */
+#define MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES 2
+#define MTK_PCIE_SPHY_CALIBRATION_LAST_QUIRK_VER 4
+
+/**
+ * struct mtk_pcie_sphy_imp_sel - Impedance Selection parameters
+ * @pmos: Impedance selection for P-Channel MOSFET
+ * @nmos: Impedance selection for N-Channel MOSFET
+ */
+struct mtk_pcie_sphy_imp_sel {
+ u8 pmos;
+ u8 nmos;
+};
+
+/**
+ * struct mtk_pcie_sphy_efuse - eFuse calibration data for S-PHY
+ * @cktx_impsel: SPLL CKTX Impedance Selection (P and N MOSFET)
+ * @tx_impsel: TX Impedance Selection (P and N MOSFET)
+ * @int_r_ctrl: Internal resistor selection of TX Bias Current
+ * @xtp_vtrim: XTP Bias V2V voltage calibration
+ * @cktx_r_mid: SPLL CKTX Intermediate Transition Impedance (Rmid)
+ * @rx_leq_rl_ctle: RX Front-End Return Loss Continuous Time Linear Equalization value
+ * @rx_leq_rl_vga: RX Front-End Return Loss Variable Gain Amplifier value
+ * @rx_leq_rl_dfe: RX Front-End Return Loss Decision Feedback Equalization value
+ * @rx_impsel: RX Impedance Selection
+ * @tx_rswn_impsel: TX RSWn (Switch Resistance) impedance selection
+ * @supported: eFuse calibration data is supported
+ */
+struct mtk_pcie_sphy_efuse {
+ struct mtk_pcie_sphy_imp_sel cktx_impsel;
+ struct mtk_pcie_sphy_imp_sel tx_impsel;
+ u8 int_r_ctrl;
+ u8 xtp_vtrim;
+ u8 cktx_r_mid;
+ u8 rx_leq_rl_ctle;
+ u8 rx_leq_rl_vga;
+ u8 rx_leq_rl_dfe;
+ u8 rx_impsel;
+ u8 tx_rswn_impsel[MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES];
+ bool supported;
+};
+
+/**
+ * struct mtk_pcie_sphy - PCI-Express S-PHY driver main structure
+ * @dev: Pointer to device structure
+ * @phy: Pointer to generic phy structure
+ * @sif_base: IO mapped register base address of system interface
+ * @ckm_base: IO mapped register base address of clock management interface
+ * @num_lanes: Number of lanes
+ * @calibration: eFuse calibration data for S-PHY
+ */
+struct mtk_pcie_sphy {
+ struct device *dev;
+ struct phy *phy;
+ void __iomem *sif_base;
+ void __iomem *ckm_base;
+ u8 num_lanes;
+ struct mtk_pcie_sphy_efuse calibration;
+};
+
+static void mtk_pcie_sphy_apply_calibration(struct mtk_pcie_sphy *pcie_sphy)
+{
+ struct mtk_pcie_sphy_efuse *cal = &pcie_sphy->calibration;
+ int i;
+
+ mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_BIAS_0,
+ RG_XTP_GLB_BIAS_INTR_CTRL, cal->int_r_ctrl);
+
+ mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_BIAS_1,
+ RG_XTP_GLB_BIAS_V2V_VTRIM, cal->xtp_vtrim);
+
+ mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5,
+ RG_CKM_CKTX_IMPSEL_PMOS, cal->cktx_impsel.pmos);
+
+ mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5,
+ RG_CKM_CKTX_IMPSEL_NMOS, cal->cktx_impsel.nmos);
+
+ mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5,
+ RG_CKM_CKTX_IMPSEL_SW, cal->cktx_r_mid);
+
+ for (i = 0; i < pcie_sphy->num_lanes; i++) {
+ void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_0C,
+ RG_XTP_LN_TX_RSWN_IMPSEL, cal->tx_rswn_impsel[i]);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8,
+ RG_XTP_LN_RX_LEQ_RL_CTLE_CAL, cal->rx_leq_rl_ctle);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8,
+ RG_XTP_LN_RX_LEQ_RL_VGA_CAL, cal->rx_leq_rl_vga);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8,
+ RG_XTP_LN_RX_LEQ_RL_DFE_CAL, cal->rx_leq_rl_dfe);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0,
+ RG_XTP_LN_RX_IMPSEL, cal->rx_impsel);
+ }
+}
+
+/**
+ * mtk_pcie_sphy_init() - Initialize the PCI-Express S-PHY
+ * @phy: the phy to be initialized
+ *
+ * The hardware settings will be reset during suspend, it should be
+ * reinitialized when the consumer calls phy_init() again on resume.
+ */
+static int mtk_pcie_sphy_init(struct phy *phy)
+{
+ struct mtk_pcie_sphy *pcie_sphy = phy_get_drvdata(phy);
+ struct mtk_pcie_sphy_imp_sel tx_impsel;
+ int i;
+
+ /* Set CKM Bias wait time to 4 microseconds */
+ mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_FORCE_6,
+ RG_CKM_BIAS_WAIT_PRD_US, 4);
+
+ /* TPLL needs 63 ref_ck ticks to stabilize when setting frequency */
+ mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL0,
+ RG_XTP_TPLL_SET_STABLE_TIME_SEL, 63);
+
+ /* TPLL needs 3 ref_ck ticks to stabilize when powering on... */
+ mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL0,
+ RG_XTP_TPLL_PWE_ON_STABLE_TIME_SEL, 3);
+
+ /* ...and the same goes for setting isolation */
+ mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL2,
+ RG_XTP_TPLL_ISO_EN_STABLE_TIME_SEL, 3);
+
+ /* XTAL doesn't need any stabilization time */
+ mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_CKBG0,
+ RG_XTP_CKBG_XTAL_STABLE_TIME_SEL, 0);
+
+ /* Keep pextp_ckm enabled when in L1SS_L1S1 state */
+ mtk_phy_clear_bits(pcie_sphy->sif_base + PEXTP_DIG_GLB_CLKREQ_CTL, RG_XTP_CKM_EN_L1S1);
+
+ /* Set PIPE to reset TPLL */
+ mtk_phy_clear_bits(pcie_sphy->sif_base + PEXTP_DIG_GLB_TOP, RG_XTP_BYPASS_PIPE_RST_RC);
+
+ /* Set TPLL P-Path gain compensation to 1 */
+ mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_TPLL1_RSVD,
+ RG_XTP_GLB_TPLL1_P_PATH_GAIN, 1);
+
+ for (i = 0; i < pcie_sphy->num_lanes; i++) {
+ void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i);
+
+ /* Set RX Lane AEQ CTRL-E Search Error type to h1.5 + h2.5 */
+ mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_6C,
+ RG_XTP_LN_RX_AEQ_CTLE_ERR_TYPE,
+ AEQ_CTLE_SEARCH_ERR_TYPE_H1P5_H2P5);
+
+ mtk_phy_set_bits(sif_lane_base + PEXTP_ANA_LN_TRX_34, RG_XTP_LN_RX_FE);
+
+ /* TRX: Select CTLE1 for RX Lane AutoEQ CTRL-E Setting on Gen4 */
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TRX_PIPE_IF_17,
+ RG_XTP_LN_RX_LF_CTLE_CSEL_GEN4, 1);
+
+ /* Set RX Lane AutoEQ CTRL-E for PCI-Express Gen1 to Gen 4 */
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0,
+ RG_XTP_LN_RX_GEN1_CTLE1_CSEL, 13);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0,
+ RG_XTP_LN_RX_GEN2_CTLE1_CSEL, 13);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0,
+ RG_XTP_LN_RX_GEN3_CTLE1_CSEL, 13);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0,
+ RG_XTP_LN_RX_GEN4_CTLE1_CSEL, 0);
+
+ /* Set RX Lane AutoEQ's Edge EQ Ratio to 22 * 0.0625 = 1.375 */
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX2_AEQ_EDGE_0,
+ RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN3, 22);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX2_AEQ_EDGE_0,
+ RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN4, 22);
+
+ /* Setup Digital lane TX Link Characteristics Table */
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4,
+ RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 10);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4,
+ RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1, 2);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8,
+ RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 11);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8,
+ RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1, 1);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_C,
+ RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 12);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10,
+ RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 13);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10,
+ RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 1);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14,
+ RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 11);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14,
+ RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 1);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18,
+ RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 10);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18,
+ RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 2);
+ }
+
+ if (pcie_sphy->calibration.supported) {
+ mtk_pcie_sphy_apply_calibration(pcie_sphy);
+
+ tx_impsel.pmos = pcie_sphy->calibration.tx_impsel.pmos;
+ tx_impsel.nmos = pcie_sphy->calibration.tx_impsel.nmos;
+ } else {
+ /* Set P=10, N=9 to prevent EMI if no calibration present */
+ tx_impsel.pmos = 10;
+ tx_impsel.nmos = 9;
+ }
+
+ /* Select TX Impedance on N and P MOSFETs */
+ for (i = 0; i < pcie_sphy->num_lanes; i++) {
+ void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0,
+ RG_XTP_LN_TX_IMPSEL_PMOS, tx_impsel.pmos);
+
+ mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0,
+ RG_XTP_LN_TX_IMPSEL_NMOS, tx_impsel.nmos);
+ }
+
+ return 0;
+}
+
+static const struct phy_ops mtk_pcie_sphy_ops = {
+ .init = mtk_pcie_sphy_init,
+ .owner = THIS_MODULE,
+};
+
+static int mtk_pcie_sphy_get_one_cal_para(struct device *dev, const char *name, u8 max_val)
+{
+ u16 buf;
+ u8 tmp;
+ int ret;
+
+ /*
+ * All of the calibrations are always max 8 bits long, but some may
+ * be split between two different 8-bits cells: handle this corner
+ * case by retrying reading as u16.
+ */
+ ret = nvmem_cell_read_u8(dev, name, &tmp);
+ if (ret == 0)
+ buf = tmp;
+ else
+ ret = nvmem_cell_read_u16(dev, name, &buf);
+
+ if (ret == -ENOENT) {
+ dev_info(dev, "No calibration for %s. Using defaults\n", name);
+ return -ENOENT;
+ } else if (ret)
+ return dev_err_probe(dev, ret,
+ "Cannot get calibration data for %s\n", name);
+
+ if (buf > max_val)
+ return dev_err_probe(dev, -ERANGE,
+ "Bad value %u retrieved for %s.\n", buf, name);
+
+ return buf;
+}
+
+static int mtk_pcie_sphy_get_calibration_data(struct mtk_pcie_sphy *pcie_sphy)
+{
+ struct mtk_pcie_sphy_efuse *cal = &pcie_sphy->calibration;
+ struct device *dev = pcie_sphy->dev;
+ u8 version;
+ int ret;
+
+ if (pcie_sphy->num_lanes > MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES) {
+ dev_info(dev, "Skipping PHY calibration for more than %u lanes.\n",
+ MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES);
+ ret = -EOPNOTSUPP;
+ goto end;
+ }
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "int-r",
+ FIELD_MAX(RG_XTP_GLB_BIAS_INTR_CTRL));
+ if (ret < 0)
+ goto end;
+ cal->int_r_ctrl = ret;
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "xtp-vtrim",
+ FIELD_MAX(RG_XTP_GLB_BIAS_V2V_VTRIM));
+ if (ret < 0)
+ goto end;
+ cal->xtp_vtrim = ret;
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-pmos",
+ FIELD_MAX(RG_CKM_CKTX_IMPSEL_PMOS));
+ if (ret < 0)
+ goto end;
+ cal->cktx_impsel.pmos = ret;
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-nmos",
+ FIELD_MAX(RG_CKM_CKTX_IMPSEL_NMOS));
+ if (ret < 0)
+ goto end;
+ cal->cktx_impsel.nmos = ret;
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-r-mid",
+ FIELD_MAX(RG_CKM_CKTX_IMPSEL_SW));
+ if (ret < 0)
+ goto end;
+ cal->cktx_r_mid = ret;
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-ctle",
+ FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_CTLE_CAL));
+ if (ret < 0)
+ goto end;
+ cal->rx_leq_rl_ctle = ret;
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-vga",
+ FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_VGA_CAL));
+ if (ret < 0)
+ goto end;
+ cal->rx_leq_rl_vga = ret;
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-dfe",
+ FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_DFE_CAL));
+ if (ret < 0)
+ goto end;
+ cal->rx_leq_rl_dfe = ret;
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "rx-lanes-imp",
+ FIELD_MAX(RG_XTP_LN_RX_IMPSEL));
+ if (ret < 0)
+ goto end;
+ cal->rx_impsel = ret;
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-lanes-pmos",
+ FIELD_MAX(RG_XTP_LN_TX_IMPSEL_PMOS));
+ if (ret < 0)
+ goto end;
+ cal->tx_impsel.pmos = ret;
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-lanes-nmos",
+ FIELD_MAX(RG_XTP_LN_TX_IMPSEL_NMOS));
+ if (ret < 0)
+ goto end;
+ cal->tx_impsel.nmos = ret;
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln0-rswn",
+ FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL));
+ if (ret < 0)
+ goto end;
+ cal->tx_rswn_impsel[0] = ret;
+
+ if (pcie_sphy->num_lanes == 2) {
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln1-rswn",
+ FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL));
+ if (ret < 0)
+ goto end;
+ cal->tx_rswn_impsel[1] = ret;
+ }
+
+ ret = mtk_pcie_sphy_get_one_cal_para(dev, "cal-version", 15);
+ if (ret < 0)
+ goto end;
+ version = ret;
+
+ /* Quirk for eFuse calibration table versions 0 to 4 */
+ if ((version <= MTK_PCIE_SPHY_CALIBRATION_LAST_QUIRK_VER) &&
+ cal->rx_leq_rl_ctle == 10) {
+ cal->rx_leq_rl_vga = cal->rx_leq_rl_ctle;
+ cal->rx_leq_rl_dfe = cal->rx_leq_rl_ctle;
+ }
+
+end:
+ if (ret < 0) {
+ /*
+ * If any of the calibration values is missing, or if there is
+ * no calibration at all in the eFuses, this is not a problem,
+ * as the PHY doesn't require one to actually work.
+ *
+ * Note that if even just one of the calibration values is
+ * missing it would not be safe to trust the correctness of
+ * the others as this may be a different eFuse layout.
+ */
+ if (ret == -ENOENT || ret == -EOPNOTSUPP) {
+ cal->supported = false;
+ return 0;
+ }
+ return ret;
+ };
+ cal->supported = true;
+
+ return 0;
+}
+
+static int mtk_pcie_sphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *provider;
+ struct mtk_pcie_sphy *pcie_sphy;
+ u32 num_lanes;
+ int ret;
+
+ pcie_sphy = devm_kzalloc(dev, sizeof(*pcie_sphy), GFP_KERNEL);
+ if (!pcie_sphy)
+ return -ENOMEM;
+
+ pcie_sphy->sif_base = devm_platform_ioremap_resource_byname(pdev, "sif");
+ if (IS_ERR(pcie_sphy->sif_base))
+ return dev_err_probe(dev, PTR_ERR(pcie_sphy->sif_base),
+ "Failed to map phy-sif base\n");
+
+ pcie_sphy->ckm_base = devm_platform_ioremap_resource_byname(pdev, "ckm");
+ if (IS_ERR(pcie_sphy->ckm_base))
+ return dev_err_probe(dev, PTR_ERR(pcie_sphy->ckm_base),
+ "Failed to map phy-ckm base\n");
+
+ pcie_sphy->phy = devm_phy_create(dev, dev->of_node, &mtk_pcie_sphy_ops);
+ if (IS_ERR(pcie_sphy->phy))
+ return dev_err_probe(dev, PTR_ERR(pcie_sphy->phy),
+ "Failed to create PCIe phy\n");
+
+ ret = of_property_read_u32(dev->of_node, "num-lanes", &num_lanes);
+ if (ret)
+ num_lanes = 1;
+ else if (num_lanes > 4)
+ return dev_err_probe(dev, -EINVAL, "Invalid number of lanes.\n");
+
+ pcie_sphy->num_lanes = num_lanes;
+ pcie_sphy->dev = dev;
+
+ ret = mtk_pcie_sphy_get_calibration_data(pcie_sphy);
+ if (ret)
+ return ret;
+
+ phy_set_drvdata(pcie_sphy->phy, pcie_sphy);
+
+ pm_runtime_no_callbacks(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(provider))
+ return dev_err_probe(dev, PTR_ERR(provider),
+ "Could not register PCI-Express S-PHY\n");
+
+ return 0;
+}
+
+static const struct of_device_id mtk_pcie_sphy_of_match[] = {
+ { .compatible = "mediatek,mt8196-pcie-sphy" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mtk_pcie_sphy_of_match);
+
+static struct platform_driver mtk_pcie_sphy_driver = {
+ .probe = mtk_pcie_sphy_probe,
+ .driver = {
+ .name = "mtk-pcie-sphy",
+ .of_match_table = mtk_pcie_sphy_of_match,
+ },
+};
+module_platform_driver(mtk_pcie_sphy_driver);
+
+MODULE_DESCRIPTION("MediaTek PCIe SPHY driver");
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/mediatek/phy-mtk-xsphy.c b/drivers/phy/mediatek/phy-mtk-xsphy.c
index cc1d66954212..597186de48c5 100644
--- a/drivers/phy/mediatek/phy-mtk-xsphy.c
+++ b/drivers/phy/mediatek/phy-mtk-xsphy.c
@@ -62,6 +62,14 @@
#define P2A6_RG_BC11_SW_EN BIT(23)
#define P2A6_RG_OTG_VBUSCMP_EN BIT(20)
+#define XSP_U2PHYDTM0 ((SSUSB_SIFSLV_U2PHY_COM) + 0x068)
+#define P2D_FORCE_DATAIN BIT(23)
+#define P2D_FORCE_DM_PULLDOWN BIT(21)
+#define P2D_FORCE_DP_PULLDOWN BIT(20)
+#define P2D_FORCE_XCVRSEL BIT(19)
+#define P2D_FORCE_SUSPENDM BIT(18)
+#define P2D_FORCE_TERMSEL BIT(17)
+
#define XSP_U2PHYDTM1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x06C)
#define P2D_FORCE_IDDIG BIT(9)
#define P2D_RG_VBUSVALID BIT(5)
@@ -94,6 +102,7 @@ struct xsphy_instance {
struct phy *phy;
void __iomem *port_base;
struct clk *ref_clk; /* reference clock of anolog phy */
+ struct phy *repeater;
u32 index;
u32 type;
struct regmap *type_sw;
@@ -181,6 +190,11 @@ static void u2_phy_instance_init(struct mtk_xsphy *xsphy,
{
void __iomem *pbase = inst->port_base;
+ mtk_phy_clear_bits(pbase + XSP_U2PHYDTM0,
+ P2D_FORCE_DATAIN | P2D_FORCE_DM_PULLDOWN |
+ P2D_FORCE_DP_PULLDOWN | P2D_FORCE_XCVRSEL |
+ P2D_FORCE_SUSPENDM | P2D_FORCE_TERMSEL);
+
/* DP/DM BC1.1 path Disable */
mtk_phy_clear_bits(pbase + XSP_USBPHYACR6, P2A6_RG_BC11_SW_EN);
@@ -391,6 +405,11 @@ static int mtk_phy_init(struct phy *phy)
switch (inst->type) {
case PHY_TYPE_USB2:
+ ret = phy_init(inst->repeater);
+ if (ret) {
+ clk_disable_unprepare(inst->ref_clk);
+ return ret;
+ }
u2_phy_instance_init(xsphy, inst);
u2_phy_props_set(xsphy, inst);
break;
@@ -438,7 +457,11 @@ static int mtk_phy_exit(struct phy *phy)
{
struct xsphy_instance *inst = phy_get_drvdata(phy);
+ if (inst->type == PHY_TYPE_USB2)
+ phy_exit(inst->repeater);
+
clk_disable_unprepare(inst->ref_clk);
+
return 0;
}
@@ -446,9 +469,16 @@ static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
struct xsphy_instance *inst = phy_get_drvdata(phy);
struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent);
+ int ret;
- if (inst->type == PHY_TYPE_USB2)
- u2_phy_instance_set_mode(xsphy, inst, mode);
+ if (inst->type != PHY_TYPE_USB2)
+ return 0;
+
+ ret = phy_set_mode_ext(inst->repeater, mode, submode);
+ if (ret)
+ return ret;
+
+ u2_phy_instance_set_mode(xsphy, inst, mode);
return 0;
}
@@ -590,6 +620,11 @@ static int mtk_xsphy_probe(struct platform_device *pdev)
retval = phy_type_syscon_get(inst, child_np);
if (retval)
return retval;
+
+ inst->repeater = devm_of_phy_optional_get(dev, child_np, NULL);
+ if (IS_ERR(inst->repeater))
+ return dev_err_probe(dev, PTR_ERR(inst->repeater),
+ "failed to get repeater\n");
}
provider = devm_of_phy_provider_register(dev, mtk_phy_xlate);
diff --git a/drivers/phy/phy-google-usb.c b/drivers/phy/phy-google-usb.c
index ab20bc20f19e..70d3a3833948 100644
--- a/drivers/phy/phy-google-usb.c
+++ b/drivers/phy/phy-google-usb.c
@@ -9,6 +9,7 @@
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
@@ -16,10 +17,12 @@
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/usb/typec_mux.h>
+/* USB_CFG_CSR */
#define USBCS_USB2PHY_CFG19_OFFSET 0x0
#define USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV GENMASK(19, 8)
@@ -28,11 +31,44 @@
#define USBCS_USB2PHY_CFG21_REF_FREQ_SEL GENMASK(15, 13)
#define USBCS_USB2PHY_CFG21_PHY_TX_DIG_BYPASS_SEL BIT(19)
+/* USBDP_TOP */
#define USBCS_PHY_CFG1_OFFSET 0x28
+#define USBCS_PHY_CFG1_PHY0_MPLLA_SSC_EN BIT(1)
+#define USBCS_PHY_CFG1_PHY0_SRAM_BYPASS_MODE GENMASK(11, 10)
+#define SRAM_BYPASS_MODE_BYPASS_FIRMWARE BIT(0)
+#define SRAM_BYPASS_MODE_BYPASS_CONTEXT BIT(1)
#define USBCS_PHY_CFG1_SYS_VBUSVALID BIT(17)
+#define USBDP_TOP_CFG_REG_OFFSET 0x44
+#define USBDP_TOP_CFG_REG_PMGT_REF_CLK_REQ_N BIT(0)
+
+#define PHY_POWER_CONFIG_REG1_OFFSET 0x48
+#define PHY_POWER_CONFIG_REG1_PG_MODE_EN BIT(1)
+#define PHY_POWER_CONFIG_REG1_UPCS_PIPE_CONFIG GENMASK(31, 14)
+#define UPCS_PIPE_CONFIG_ISO_CPM BIT(5)
+#define UPCS_PIPE_CONFIG_PG_MODE_STATIC BIT(6)
+#define UPCS_PIPE_CONFIG_LANE_RESET_NO_PG_EXIT BIT(9)
+
+/* USB3_TCA */
+#define TCA_INTR_STS_OFFSET 0x8
+#define TCA_INTR_STS_XA_ACT_EVT BIT(0)
+#define TCA_TCPC_OFFSET 0x14
+#define TCA_TCPC_MUX_CONTROL GENMASK(2, 0)
+#define TCA_TCPC_MUX_CONTROL_USB_ONLY 0x1
+#define TCA_TCPC_CONNECTOR_ORIENTATION BIT(3)
+#define TCA_TCPC_VALID BIT(4)
+#define TCA_CTRLSYNCMODE_CFG1_OFFSET 0x24
+#define TCA_CTRLSYNCMODE_CFG1_XA_TIMEOUT_VAL GENMASK(19, 0)
+#define TCA_CTRLSYNCMODE_CFG1_XA_TIMEOUT_VAL_100MS 0x1e85
+#define TCA_PSTATE_0_OFFSET 0x50
+#define TCA_PSTATE_0_UPCS_LANE0_PHYSTATUS BIT(8)
+
+#define GPHY_TCA_DELAY_US 10
+#define GPHY_TCA_TIMEOUT_US 110000
+
enum google_usb_phy_id {
GOOGLE_USB2_PHY,
+ GOOGLE_USB3_PHY,
GOOGLE_USB_PHY_NUM,
};
@@ -46,53 +82,205 @@ struct google_usb_phy_instance {
struct reset_control_bulk_data *rsts;
};
+struct google_usb_phy_config {
+ const char * const *clk_names;
+ unsigned int num_clks;
+ const char * const *rst_names;
+ unsigned int num_rsts;
+};
+
+static const char * const u2phy_clk_names[] = {
+ "usb2",
+ "usb2_apb",
+};
+
+static const char * const u3phy_clk_names[] = {
+ "usb3"
+};
+
+static const char * const u2phy_rst_names[] = {
+ "usb2",
+ "usb2_apb",
+};
+
+static const char * const u3phy_rst_names[] = {
+ "usb3"
+};
+
+static const struct google_usb_phy_config phy_configs[GOOGLE_USB_PHY_NUM] = {
+ [GOOGLE_USB2_PHY] = {
+ .clk_names = u2phy_clk_names,
+ .num_clks = ARRAY_SIZE(u2phy_clk_names),
+ .rst_names = u2phy_rst_names,
+ .num_rsts = ARRAY_SIZE(u2phy_rst_names),
+ },
+ [GOOGLE_USB3_PHY] = {
+ .clk_names = u3phy_clk_names,
+ .num_clks = ARRAY_SIZE(u3phy_clk_names),
+ .rst_names = u3phy_rst_names,
+ .num_rsts = ARRAY_SIZE(u3phy_rst_names),
+ },
+};
+
+static inline void google_usb_phy_clk_disable(struct google_usb_phy_instance *inst)
+{
+ clk_bulk_disable_unprepare(inst->num_clks, inst->clks);
+}
+
+DEFINE_FREE(inst_clk_disable, struct google_usb_phy_instance *,
+ if (_T) google_usb_phy_clk_disable(_T))
+
+static inline void google_usb_phy_rst_disable(struct google_usb_phy_instance *inst)
+{
+ reset_control_bulk_assert(inst->num_rsts, inst->rsts);
+}
+
+DEFINE_FREE(inst_rst_disable, struct google_usb_phy_instance *,
+ if (_T) google_usb_phy_rst_disable(_T))
+
+/*
+ * combo_phy_state
+ * COMBO_PHY_IDLE: The ComboPHY has been torn down and USB3 has not completed
+ * bringup
+ * COMBO_PHY_INIT_DONE: The ComboPHY bringup sequence is complete.
+ * COMBO_PHY_TCA_READY: The PoR => NC transition is complete, and the TCA can be
+ * moved into USB.
+ */
+enum combo_phy_state {
+ COMBO_PHY_IDLE,
+ COMBO_PHY_INIT_DONE,
+ COMBO_PHY_TCA_READY,
+};
+
struct google_usb_phy {
struct device *dev;
struct regmap *usb_cfg_regmap;
unsigned int usb2_cfg_offset;
void __iomem *usbdp_top_base;
+ void __iomem *usb3_tca_base;
struct google_usb_phy_instance *insts;
/*
* Protect phy registers from concurrent access, specifically via
- * google_usb_set_orientation callback.
+ * google_usb_set_orientation callback. phy_mutex also protects
+ * concurrent access to phy_state.
*/
struct mutex phy_mutex;
struct typec_switch_dev *sw;
enum typec_orientation orientation;
+ enum combo_phy_state phy_state;
};
static void set_vbus_valid(struct google_usb_phy *gphy)
{
u32 reg;
- if (gphy->orientation == TYPEC_ORIENTATION_NONE) {
- reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
+ reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
+ if (gphy->orientation == TYPEC_ORIENTATION_NONE)
reg &= ~USBCS_PHY_CFG1_SYS_VBUSVALID;
- writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
- } else {
- reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
+ else
reg |= USBCS_PHY_CFG1_SYS_VBUSVALID;
- writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
- }
+ writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
+}
+
+static void set_sram_bypass(struct google_usb_phy *gphy, u32 bypass)
+{
+ u32 reg;
+
+ reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
+ reg &= ~USBCS_PHY_CFG1_PHY0_SRAM_BYPASS_MODE;
+ reg |= FIELD_PREP(USBCS_PHY_CFG1_PHY0_SRAM_BYPASS_MODE, bypass);
+ writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
+}
+
+static void set_pmgt_ref_clk_req_n(struct google_usb_phy *gphy, bool resume)
+{
+ u32 reg;
+
+ reg = readl(gphy->usbdp_top_base + USBDP_TOP_CFG_REG_OFFSET);
+ if (resume)
+ reg |= USBDP_TOP_CFG_REG_PMGT_REF_CLK_REQ_N;
+ else
+ reg &= ~USBDP_TOP_CFG_REG_PMGT_REF_CLK_REQ_N;
+ writel(reg, gphy->usbdp_top_base + USBDP_TOP_CFG_REG_OFFSET);
+}
+
+static inline void disable_pmgt_ref_clk_req_n(struct google_usb_phy *gphy)
+{
+ set_pmgt_ref_clk_req_n(gphy, false);
+}
+
+DEFINE_FREE(pmgt_ref_clk_req_n, struct google_usb_phy *, if (_T) disable_pmgt_ref_clk_req_n(_T))
+
+static int wait_tca_xa_ack(struct google_usb_phy *gphy)
+{
+ int ret;
+ u32 reg;
+
+ ret = readl_poll_timeout(gphy->usb3_tca_base + TCA_INTR_STS_OFFSET,
+ reg, !!(reg & TCA_INTR_STS_XA_ACT_EVT),
+ GPHY_TCA_DELAY_US, GPHY_TCA_TIMEOUT_US);
+ if (ret)
+ dev_err(gphy->dev, "tca xa_ack timeout, ret=%d\n", ret);
+
+ return ret;
+}
+
+static int program_tca_locked(struct google_usb_phy *gphy)
+ __must_hold(&gphy->phy_mutex)
+{
+ int ret;
+ u32 reg;
+
+ reg = readl(gphy->usb3_tca_base + TCA_INTR_STS_OFFSET);
+ writel(reg, gphy->usb3_tca_base + TCA_INTR_STS_OFFSET);
+
+ reg = readl(gphy->usb3_tca_base + TCA_TCPC_OFFSET);
+ reg &= ~TCA_TCPC_MUX_CONTROL;
+ reg |= FIELD_PREP(TCA_TCPC_MUX_CONTROL, TCA_TCPC_MUX_CONTROL_USB_ONLY);
+ if (gphy->orientation == TYPEC_ORIENTATION_REVERSE)
+ reg |= TCA_TCPC_CONNECTOR_ORIENTATION;
+ else
+ reg &= ~TCA_TCPC_CONNECTOR_ORIENTATION;
+ reg |= TCA_TCPC_VALID;
+ writel(reg, gphy->usb3_tca_base + TCA_TCPC_OFFSET);
+
+ ret = wait_tca_xa_ack(gphy);
+ dev_dbg(gphy->dev, "TCA switch %s, mux %lu, orientation %s\n",
+ ret ? "failed" : "success",
+ FIELD_GET(TCA_TCPC_MUX_CONTROL, reg),
+ FIELD_GET(TCA_TCPC_CONNECTOR_ORIENTATION, reg) ? "reverse" : "normal");
+
+ reg = readl(gphy->usb3_tca_base + TCA_INTR_STS_OFFSET);
+ writel(reg, gphy->usb3_tca_base + TCA_INTR_STS_OFFSET);
+
+ return ret;
}
static int google_usb_set_orientation(struct typec_switch_dev *sw,
enum typec_orientation orientation)
{
struct google_usb_phy *gphy = typec_switch_get_drvdata(sw);
+ int ret = 0;
dev_dbg(gphy->dev, "set orientation %d\n", orientation);
- gphy->orientation = orientation;
+ guard(mutex)(&gphy->phy_mutex);
- if (pm_runtime_suspended(gphy->dev))
- return 0;
+ gphy->orientation = orientation;
- guard(mutex)(&gphy->phy_mutex);
+ if (IS_ENABLED(CONFIG_PM)) {
+ if (pm_runtime_get_if_active(gphy->dev) <= 0)
+ return 0;
+ }
set_vbus_valid(gphy);
- return 0;
+ if (gphy->phy_state == COMBO_PHY_TCA_READY && orientation != TYPEC_ORIENTATION_NONE)
+ ret = program_tca_locked(gphy);
+
+ pm_runtime_put(gphy->dev);
+
+ return ret;
}
static int google_usb2_phy_init(struct phy *_phy)
@@ -122,17 +310,18 @@ static int google_usb2_phy_init(struct phy *_phy)
ret = clk_bulk_prepare_enable(inst->num_clks, inst->clks);
if (ret)
return ret;
+ struct google_usb_phy_instance *clk_dev __free(inst_clk_disable) = inst;
ret = reset_control_bulk_deassert(inst->num_rsts, inst->rsts);
- if (ret) {
- clk_bulk_disable_unprepare(inst->num_clks, inst->clks);
+ if (ret)
return ret;
- }
regmap_read(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, &reg);
reg |= USBCS_USB2PHY_CFG21_PHY_ENABLE;
regmap_write(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, reg);
+ retain_and_null_ptr(clk_dev);
+
return 0;
}
@@ -161,6 +350,128 @@ static const struct phy_ops google_usb2_phy_ops = {
.exit = google_usb2_phy_exit,
};
+static int google_usb3_phy_init(struct phy *_phy)
+{
+ struct google_usb_phy_instance *inst = phy_get_drvdata(_phy);
+ struct google_usb_phy *gphy = inst->parent;
+ int ret = 0;
+ u32 reg;
+
+ dev_dbg(gphy->dev, "initializing usb3 phy\n");
+
+ guard(mutex)(&gphy->phy_mutex);
+
+ if (gphy->phy_state != COMBO_PHY_IDLE) {
+ dev_warn(gphy->dev, "usb3 phy init called when combo phy state is not idle\n");
+ return 0;
+ }
+
+ reg = readl(gphy->usb3_tca_base + TCA_CTRLSYNCMODE_CFG1_OFFSET);
+ reg &= ~TCA_CTRLSYNCMODE_CFG1_XA_TIMEOUT_VAL;
+ reg |= FIELD_PREP(TCA_CTRLSYNCMODE_CFG1_XA_TIMEOUT_VAL,
+ TCA_CTRLSYNCMODE_CFG1_XA_TIMEOUT_VAL_100MS);
+ writel(reg, gphy->usb3_tca_base + TCA_CTRLSYNCMODE_CFG1_OFFSET);
+
+ reg = readl(gphy->usbdp_top_base + PHY_POWER_CONFIG_REG1_OFFSET);
+ reg |= PHY_POWER_CONFIG_REG1_PG_MODE_EN;
+ reg &= ~PHY_POWER_CONFIG_REG1_UPCS_PIPE_CONFIG;
+ reg |= FIELD_PREP(PHY_POWER_CONFIG_REG1_UPCS_PIPE_CONFIG,
+ (UPCS_PIPE_CONFIG_ISO_CPM |
+ UPCS_PIPE_CONFIG_PG_MODE_STATIC |
+ UPCS_PIPE_CONFIG_LANE_RESET_NO_PG_EXIT));
+ writel(reg, gphy->usbdp_top_base + PHY_POWER_CONFIG_REG1_OFFSET);
+
+ set_vbus_valid(gphy);
+
+ reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
+ reg |= USBCS_PHY_CFG1_PHY0_MPLLA_SSC_EN;
+ writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
+
+ set_sram_bypass(gphy, SRAM_BYPASS_MODE_BYPASS_FIRMWARE |
+ SRAM_BYPASS_MODE_BYPASS_CONTEXT);
+ set_pmgt_ref_clk_req_n(gphy, true);
+ struct google_usb_phy *pmgt_ref_clk_req_dev __free(pmgt_ref_clk_req_n) = gphy;
+
+ ret = clk_bulk_prepare_enable(inst->num_clks, inst->clks);
+ if (ret)
+ return ret;
+ struct google_usb_phy_instance *clk_dev __free(inst_clk_disable) = inst;
+
+ ret = reset_control_bulk_deassert(inst->num_rsts, inst->rsts);
+ if (ret)
+ return ret;
+ struct google_usb_phy_instance *rst_dev __free(inst_rst_disable) = inst;
+
+ ret = readl_poll_timeout(gphy->usb3_tca_base + TCA_PSTATE_0_OFFSET,
+ reg, !(reg & TCA_PSTATE_0_UPCS_LANE0_PHYSTATUS),
+ GPHY_TCA_DELAY_US, GPHY_TCA_TIMEOUT_US);
+ if (ret) {
+ dev_err(gphy->dev, "wait for lane0 phystatus timed out\n");
+ return ret;
+ }
+
+ gphy->phy_state = COMBO_PHY_INIT_DONE;
+
+ retain_and_null_ptr(rst_dev);
+ retain_and_null_ptr(clk_dev);
+ retain_and_null_ptr(pmgt_ref_clk_req_dev);
+
+ return 0;
+}
+
+static int google_usb3_phy_exit(struct phy *_phy)
+{
+ struct google_usb_phy_instance *inst = phy_get_drvdata(_phy);
+ struct google_usb_phy *gphy = inst->parent;
+
+ dev_dbg(gphy->dev, "exiting usb3 phy\n");
+
+ guard(mutex)(&gphy->phy_mutex);
+
+ reset_control_bulk_assert(inst->num_rsts, inst->rsts);
+ clk_bulk_disable_unprepare(inst->num_clks, inst->clks);
+ set_pmgt_ref_clk_req_n(gphy, false);
+
+ gphy->phy_state = COMBO_PHY_IDLE;
+
+ return 0;
+}
+
+static int google_usb3_phy_power_on(struct phy *_phy)
+{
+ struct google_usb_phy_instance *inst = phy_get_drvdata(_phy);
+ struct google_usb_phy *gphy = inst->parent;
+ int ret;
+
+ dev_dbg(gphy->dev, "power on usb3 phy\n");
+
+ guard(mutex)(&gphy->phy_mutex);
+
+ if (gphy->phy_state == COMBO_PHY_IDLE)
+ return -EINVAL;
+
+ if (gphy->phy_state == COMBO_PHY_INIT_DONE) {
+ /* Wait for PoR -> NC transitions*/
+ ret = wait_tca_xa_ack(gphy);
+ if (ret) {
+ dev_err(gphy->dev, "PoR->NC transition timeout\n");
+ return ret;
+ }
+ gphy->phy_state = COMBO_PHY_TCA_READY;
+ }
+
+ if (gphy->orientation != TYPEC_ORIENTATION_NONE)
+ return program_tca_locked(gphy);
+
+ return 0;
+}
+
+static const struct phy_ops google_usb3_phy_ops = {
+ .init = google_usb3_phy_init,
+ .exit = google_usb3_phy_exit,
+ .power_on = google_usb3_phy_power_on,
+};
+
static struct phy *google_usb_phy_xlate(struct device *dev,
const struct of_phandle_args *args)
{
@@ -173,14 +484,61 @@ static struct phy *google_usb_phy_xlate(struct device *dev,
return gphy->insts[args->args[0]].phy;
}
+static int google_usb_phy_parse_clocks(struct google_usb_phy *gphy)
+{
+ struct device *dev = gphy->dev;
+ int id, i, ret;
+
+ for (id = 0; id < GOOGLE_USB_PHY_NUM; id++) {
+ const struct google_usb_phy_config *cfg = &phy_configs[id];
+ struct google_usb_phy_instance *inst = &gphy->insts[id];
+
+ inst->num_clks = cfg->num_clks;
+ inst->clks = devm_kcalloc(dev, inst->num_clks, sizeof(*inst->clks), GFP_KERNEL);
+ if (!inst->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < inst->num_clks; i++)
+ inst->clks[i].id = cfg->clk_names[i];
+
+ ret = devm_clk_bulk_get(dev, inst->num_clks, inst->clks);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get phy%d clks\n", id);
+ }
+
+ return 0;
+}
+
+static int google_usb_phy_parse_resets(struct google_usb_phy *gphy)
+{
+ struct device *dev = gphy->dev;
+ int id, i, ret;
+
+ for (id = 0; id < GOOGLE_USB_PHY_NUM; id++) {
+ const struct google_usb_phy_config *cfg = &phy_configs[id];
+ struct google_usb_phy_instance *inst = &gphy->insts[id];
+
+ inst->num_rsts = cfg->num_rsts;
+ inst->rsts = devm_kcalloc(dev, inst->num_rsts, sizeof(*inst->rsts), GFP_KERNEL);
+ if (!inst->rsts)
+ return -ENOMEM;
+
+ for (i = 0; i < inst->num_rsts; i++)
+ inst->rsts[i].id = cfg->rst_names[i];
+ ret = devm_reset_control_bulk_get_exclusive(dev, inst->num_rsts, inst->rsts);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get phy%d resets\n", id);
+ }
+
+ return 0;
+}
+
static int google_usb_phy_probe(struct platform_device *pdev)
{
struct typec_switch_desc sw_desc = { };
- struct google_usb_phy_instance *inst;
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct google_usb_phy *gphy;
- struct phy *phy;
u32 args[1];
int ret;
@@ -212,46 +570,48 @@ static int google_usb_phy_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(gphy->usbdp_top_base),
"invalid usbdp top\n");
+ gphy->usb3_tca_base = devm_platform_ioremap_resource_byname(pdev,
+ "usb3_tca");
+ if (IS_ERR(gphy->usb3_tca_base))
+ return dev_err_probe(dev, PTR_ERR(gphy->usb3_tca_base),
+ "invalid usb3 tca\n");
+
gphy->insts = devm_kcalloc(dev, GOOGLE_USB_PHY_NUM, sizeof(*gphy->insts), GFP_KERNEL);
if (!gphy->insts)
return -ENOMEM;
- inst = &gphy->insts[GOOGLE_USB2_PHY];
- inst->parent = gphy;
- inst->index = GOOGLE_USB2_PHY;
- phy = devm_phy_create(dev, NULL, &google_usb2_phy_ops);
- if (IS_ERR(phy))
- return dev_err_probe(dev, PTR_ERR(phy),
+ gphy->insts[GOOGLE_USB2_PHY].phy = devm_phy_create(dev, NULL, &google_usb2_phy_ops);
+ gphy->insts[GOOGLE_USB2_PHY].index = GOOGLE_USB2_PHY;
+ gphy->insts[GOOGLE_USB2_PHY].parent = gphy;
+ if (IS_ERR(gphy->insts[GOOGLE_USB2_PHY].phy))
+ return dev_err_probe(dev, PTR_ERR(gphy->insts[GOOGLE_USB2_PHY].phy),
"failed to create usb2 phy instance\n");
- inst->phy = phy;
- phy_set_drvdata(phy, inst);
+ phy_set_drvdata(gphy->insts[GOOGLE_USB2_PHY].phy, &gphy->insts[GOOGLE_USB2_PHY]);
- inst->num_clks = 2;
- inst->clks = devm_kcalloc(dev, inst->num_clks, sizeof(*inst->clks), GFP_KERNEL);
- if (!inst->clks)
- return -ENOMEM;
- inst->clks[0].id = "usb2";
- inst->clks[1].id = "usb2_apb";
- ret = devm_clk_bulk_get(dev, inst->num_clks, inst->clks);
+ gphy->insts[GOOGLE_USB3_PHY].phy = devm_phy_create(dev, NULL, &google_usb3_phy_ops);
+ gphy->insts[GOOGLE_USB3_PHY].index = GOOGLE_USB3_PHY;
+ gphy->insts[GOOGLE_USB3_PHY].parent = gphy;
+ if (IS_ERR(gphy->insts[GOOGLE_USB3_PHY].phy))
+ return dev_err_probe(dev, PTR_ERR(gphy->insts[GOOGLE_USB3_PHY].phy),
+ "failed to create usb3 phy instance\n");
+ phy_set_drvdata(gphy->insts[GOOGLE_USB3_PHY].phy, &gphy->insts[GOOGLE_USB3_PHY]);
+
+ ret = google_usb_phy_parse_clocks(gphy);
if (ret)
- return dev_err_probe(dev, ret, "failed to get u2 phy clks\n");
+ return ret;
- inst->num_rsts = 2;
- inst->rsts = devm_kcalloc(dev, inst->num_rsts, sizeof(*inst->rsts), GFP_KERNEL);
- if (!inst->rsts)
- return -ENOMEM;
- inst->rsts[0].id = "usb2";
- inst->rsts[1].id = "usb2_apb";
- ret = devm_reset_control_bulk_get_exclusive(dev, inst->num_rsts, inst->rsts);
+ ret = google_usb_phy_parse_resets(gphy);
if (ret)
- return dev_err_probe(dev, ret, "failed to get u2 phy resets\n");
+ return ret;
phy_provider = devm_of_phy_provider_register(dev, google_usb_phy_xlate);
if (IS_ERR(phy_provider))
return dev_err_probe(dev, PTR_ERR(phy_provider),
"failed to register phy provider\n");
- pm_runtime_enable(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
sw_desc.fwnode = dev_fwnode(dev);
sw_desc.drvdata = gphy;
@@ -271,7 +631,6 @@ static void google_usb_phy_remove(struct platform_device *pdev)
struct google_usb_phy *gphy = dev_get_drvdata(&pdev->dev);
typec_switch_unregister(gphy->sw);
- pm_runtime_disable(&pdev->dev);
}
static const struct of_device_id google_usb_phy_of_match[] = {
diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c
index 5007dc7a357c..59f18e16bbbb 100644
--- a/drivers/phy/phy-xgene.c
+++ b/drivers/phy/phy-xgene.c
@@ -1669,7 +1669,7 @@ static int xgene_phy_probe(struct platform_device *pdev)
/* Retrieve optional clock */
ctx->clk = clk_get(&pdev->dev, NULL);
- /* Load override paramaters */
+ /* Load override parameters */
xgene_phy_get_param(pdev, "apm,tx-eye-tuning",
ctx->sata_param.txeyetuning, 6, default_txeye_tuning, 1);
xgene_phy_get_param(pdev, "apm,tx-eye-direction",
diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig
index 81ccf49d00d9..44a805361e35 100644
--- a/drivers/phy/qualcomm/Kconfig
+++ b/drivers/phy/qualcomm/Kconfig
@@ -96,6 +96,7 @@ config PHY_QCOM_QMP_PCIE_MULTIPHY
tristate "Qualcomm QMP PCIe Multi PHY Driver"
depends on PCI || COMPILE_TEST
select GENERIC_PHY
+ select MFD_SYSCON
help
Enable this to support the QMP PCIe PHY transceiver that is used
with PCIe controllers on Qualcomm chips. This PHY is a single
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
index c39ced168d03..187f0cd11f6c 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
@@ -22,6 +22,7 @@
#include <linux/usb/typec.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_tbt.h>
#include <drm/bridge/aux-bridge.h>
@@ -66,10 +67,14 @@
#define SW_USB3PHY_RESET BIT(2)
/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */
#define SW_USB3PHY_RESET_MUX BIT(3)
+#define SW_USB4PHY_RESET BIT(4)
+#define SW_USB4PHY_RESET_MUX BIT(5)
/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */
#define USB3_MODE BIT(0) /* enables USB3 mode */
#define DP_MODE BIT(1) /* enables DP mode */
+#define USB4_MODE BIT(2) /* mutually exclusive with the above */
+#define DP_TUNNELING_CLOCK_GEN_EN BIT(3)
/* QPHY_V3_DP_COM_TYPEC_CTRL register bits */
#define SW_PORTSELECT_VAL BIT(0)
@@ -82,6 +87,8 @@ enum qmpphy_mode {
QMPPHY_MODE_USB3DP = 0,
QMPPHY_MODE_DP_ONLY,
QMPPHY_MODE_USB3_ONLY,
+ /* USB4 QMPPHY mode refers to both USB4 and TBT3 */
+ QMPPHY_MODE_USB4,
};
/* set of registers with offsets different per-PHY */
@@ -94,6 +101,7 @@ enum qphy_reg_layout {
QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR,
QPHY_PCS_POWER_DOWN_CONTROL,
QPHY_PCS_CLAMP_ENABLE,
+ QPHY_PCS_USB4_CLAMP_ENABLE,
QPHY_COM_RESETSM_CNTRL,
QPHY_COM_C_READY_STATUS,
@@ -2348,6 +2356,20 @@ static const struct qmp_phy_init_tbl hawi_usb3_pcs_usb_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V10_PCS_USB3_RCVR_DTCT_DLY_U3_H, 0x00),
};
+static const struct qmp_phy_init_tbl nord_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_TX, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_RX, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_OFFSET_TX, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_OFFSET_RX, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_1, 0xf5),
+ QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_3, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_4, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_5, 0x5f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_TX_RCV_DETECT_LVL_2, 0x14),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V6_TX_PI_QEC_CTRL, 0x21, 1),
+ QMP_PHY_INIT_CFG_LANE(QSERDES_V6_TX_PI_QEC_CTRL, 0x05, 2),
+};
+
/* list of regulators */
static struct regulator_bulk_data qmp_phy_vreg_l[] = {
{ .supply = "vdda-phy", .init_load_uA = 21800, },
@@ -2489,6 +2511,8 @@ struct qmp_combo_offsets {
u16 dp_txb;
u16 dp_dp_phy;
u16 aon_toggle;
+ u16 usb4_serdes;
+ u16 usb4_pcs;
};
struct qmp_phy_cfg {
@@ -2536,6 +2560,18 @@ struct qmp_phy_cfg {
int (*calibrate_dp_phy)(struct qmp_combo *qmp);
void (*dp_aux_init)(struct qmp_combo *qmp);
+ /* USB4 specifics */
+ const struct qmp_phy_init_tbl *usb4_serdes_tbl;
+ int usb4_serdes_tbl_num;
+ const struct qmp_phy_init_tbl *usb4_serdes_tb3_ovrd_tbl;
+ int usb4_serdes_tb3_ovrd_num;
+ const struct qmp_phy_init_tbl *usb4_tx_tbl;
+ int usb4_tx_tbl_num;
+ const struct qmp_phy_init_tbl *usb4_rx_tbl;
+ int usb4_rx_tbl_num;
+ const struct qmp_phy_init_tbl *usb4_pcs_tbl;
+ int usb4_pcs_tbl_num;
+
/* resets to be requested */
const char * const *reset_list;
int num_resets;
@@ -2578,8 +2614,12 @@ struct qmp_combo {
void __iomem *dp_tx2;
void __iomem *dp_dp_phy;
+ void __iomem *usb4_serdes;
+ void __iomem *usb4_pcs;
+
struct clk *pipe_clk;
struct clk_bulk_data *clks;
+ struct clk *p2rr2p_pipe_clk;
int num_clks;
struct reset_control_bulk_data *resets;
struct regulator_bulk_data *vregs;
@@ -2598,6 +2638,10 @@ struct qmp_combo {
unsigned int dp_init_count;
bool dp_powered_on;
+ struct phy *usb4_phy;
+ enum tbt_phy_submode usb4_phy_submode;
+ unsigned int usb4_init_count;
+
struct clk_fixed_rate pipe_clk_fixed;
struct clk_hw dp_link_hw;
struct clk_hw dp_pixel_hw;
@@ -2622,30 +2666,6 @@ static void qmp_v8_dp_aux_init(struct qmp_combo *qmp);
static int qmp_v8_configure_dp_clocks(struct qmp_combo *qmp);
static int qmp_v8_configure_dp_phy(struct qmp_combo *qmp);
-static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg |= val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
-static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg &= ~val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
/* list of clocks required by phy */
static const char * const qmp_combo_phy_clk_l[] = {
"aux", "cfg_ahb", "ref", "com_aux",
@@ -2690,6 +2710,23 @@ static const struct qmp_combo_offsets qmp_combo_offsets_v5 = {
.dp_dp_phy = 0x2200,
};
+static const struct qmp_combo_offsets qmp_combo_offsets_v6 = {
+ .com = 0x0000,
+ .txa = 0x1200,
+ .rxa = 0x1400,
+ .txb = 0x1600,
+ .rxb = 0x1800,
+ .usb3_serdes = 0x1000,
+ .usb3_pcs_misc = 0x1a00,
+ .usb3_pcs = 0x1c00,
+ .usb3_pcs_aon = 0x1e00,
+ .usb3_pcs_usb = 0x1f00,
+ .dp_serdes = 0x2000,
+ .dp_txa = 0x2200,
+ .dp_txb = 0x2600,
+ .dp_dp_phy = 0x2a00,
+};
+
static const struct qmp_combo_offsets qmp_combo_offsets_v8 = {
.com = 0x0000,
.txa = 0x1400,
@@ -3394,6 +3431,51 @@ static const struct qmp_phy_cfg glymur_usb3dpphy_cfg = {
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_refgen),
};
+static const struct qmp_phy_cfg nord_usb3dpphy_cfg = {
+ .offsets = &qmp_combo_offsets_v6,
+
+ .serdes_tbl = sm8550_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sm8550_usb3_serdes_tbl),
+ .tx_tbl = nord_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(nord_usb3_tx_tbl),
+ .rx_tbl = sm8550_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sm8550_usb3_rx_tbl),
+ .pcs_tbl = sm8550_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sm8550_usb3_pcs_tbl),
+ .pcs_usb_tbl = sm8550_usb3_pcs_usb_tbl,
+ .pcs_usb_tbl_num = ARRAY_SIZE(sm8550_usb3_pcs_usb_tbl),
+
+ .dp_serdes_tbl = qmp_v6_dp_serdes_tbl,
+ .dp_serdes_tbl_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl),
+ .dp_tx_tbl = qmp_v6_dp_tx_tbl,
+ .dp_tx_tbl_num = ARRAY_SIZE(qmp_v6_dp_tx_tbl),
+
+ .serdes_tbl_rbr = qmp_v6_dp_serdes_tbl_rbr,
+ .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_rbr),
+ .serdes_tbl_hbr = qmp_v6_dp_serdes_tbl_hbr,
+ .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr),
+ .serdes_tbl_hbr2 = qmp_v6_dp_serdes_tbl_hbr2,
+ .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr2),
+ .serdes_tbl_hbr3 = qmp_v6_dp_serdes_tbl_hbr3,
+ .serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr3),
+
+ .swing_hbr_rbr = &qmp_dp_v5_voltage_swing_hbr_rbr,
+ .pre_emphasis_hbr_rbr = &qmp_dp_v6_pre_emphasis_hbr_rbr,
+ .swing_hbr3_hbr2 = &qmp_dp_v5_voltage_swing_hbr3_hbr2,
+ .pre_emphasis_hbr3_hbr2 = &qmp_dp_v5_pre_emphasis_hbr3_hbr2,
+
+ .dp_aux_init = qmp_v4_dp_aux_init,
+ .configure_dp_tx = qmp_v4_configure_dp_tx,
+ .configure_dp_clocks = qmp_v3_configure_dp_clocks,
+ .configure_dp_phy = qmp_v4_configure_dp_phy,
+
+ .regs = qmp_v6_usb3phy_regs_layout,
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+};
+
static int qmp_combo_dp_serdes_init(struct qmp_combo *qmp)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
@@ -4006,6 +4088,9 @@ static int qmp_combo_dp_calibrate(struct phy *phy)
static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
+ u32 dp_reset_val = SW_DPPHY_RESET_MUX | SW_DPPHY_RESET;
+ void __iomem *pcs_aon = qmp->pcs_aon;
+ void __iomem *pcs = qmp->pcs;
void __iomem *com = qmp->com;
void __iomem *aon_toggle;
int ret;
@@ -4019,6 +4104,16 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
else
aon_toggle = qmp->pcs_aon;
+ if (qmp->qmpphy_mode == QMPPHY_MODE_USB4) {
+ pcs = qmp->usb4_pcs;
+
+ qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_USB4_CLAMP_ENABLE], CLAMP_EN);
+
+ /* Do not disturb the DP PLL in case there's an active DP tunnel */
+ if (readl(com + QPHY_V3_DP_COM_RESET_OVRD_CTRL) & DP_TUNNELING_CLOCK_GEN_EN)
+ dp_reset_val &= ~SW_DPPHY_RESET_MUX;
+ }
+
ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
if (ret) {
dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
@@ -4052,8 +4147,9 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
/* override hardware control for reset of qmp phy */
qphy_setbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
- SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
- SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
+ dp_reset_val |
+ SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET |
+ SW_USB4PHY_RESET_MUX | SW_USB4PHY_RESET);
/* override hardware control for reset of qmp phy */
if (aon_toggle && cfg->regs[QPHY_AON_TOGGLE_ENABLE]) {
@@ -4069,6 +4165,10 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
if (cfg->invert_cc_polarity)
val |= INVERT_CC_POLARITY;
+ /*
+ * Note that in USB4 mode, the router controls pin assignments instead
+ * and the values of the PORTSELECT registers are ignored.
+ */
writel(val, com + QPHY_V3_DP_COM_TYPEC_CTRL);
switch (qmp->qmpphy_mode) {
@@ -4096,12 +4196,24 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
break;
+ case QMPPHY_MODE_USB4:
+ writel(USB4_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL);
+
+ /* bring both QMP USB and QMP DP PHYs PCS block out of reset */
+ /*
+ * TODO: disable the DP PLL to save power if there are no
+ * active DP tunnels after router setup has concluded
+ */
+ qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
+ SW_USB4PHY_RESET_MUX | SW_USB4PHY_RESET |
+ SW_DPPHY_RESET_MUX | SW_DPPHY_RESET);
+ break;
}
qphy_clrbits(com, QPHY_V3_DP_COM_SWI_CTRL, 0x03);
qphy_clrbits(com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
- qphy_setbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
SW_PWRDN);
return 0;
@@ -4219,6 +4331,10 @@ static int qmp_combo_usb_power_on(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *serdes = qmp->serdes;
+ const struct qmp_phy_init_tbl *pcs_tbl;
+ const struct qmp_phy_init_tbl *rx_tbl;
+ const struct qmp_phy_init_tbl *serdes_tbl;
+ const struct qmp_phy_init_tbl *tx_tbl;
void __iomem *tx = qmp->tx;
void __iomem *rx = qmp->rx;
void __iomem *tx2 = qmp->tx2;
@@ -4226,23 +4342,54 @@ static int qmp_combo_usb_power_on(struct phy *phy)
void __iomem *pcs = qmp->pcs;
void __iomem *pcs_usb = qmp->pcs_usb;
void __iomem *status;
+ int pcs_tbl_num, rx_tbl_num, serdes_tbl_num, tx_tbl_num;
unsigned int val;
int ret;
- qmp_configure(qmp->dev, serdes, cfg->serdes_tbl, cfg->serdes_tbl_num);
+ if (qmp->qmpphy_mode == QMPPHY_MODE_USB4) {
+ pcs = qmp->usb4_pcs;
+ serdes = qmp->usb4_serdes;
+ pcs_tbl = cfg->usb4_pcs_tbl;
+ pcs_tbl_num = cfg->usb4_pcs_tbl_num;
+
+ serdes_tbl = cfg->usb4_serdes_tbl;
+ serdes_tbl_num = cfg->usb4_serdes_tbl_num;
+
+ rx_tbl = cfg->usb4_rx_tbl;
+ rx_tbl_num = cfg->usb4_rx_tbl_num;
+
+ tx_tbl = cfg->usb4_tx_tbl;
+ tx_tbl_num = cfg->usb4_tx_tbl_num;
+ } else {
+ pcs_tbl = cfg->pcs_tbl;
+ pcs_tbl_num = cfg->pcs_tbl_num;
+
+ serdes_tbl = cfg->serdes_tbl;
+ serdes_tbl_num = cfg->serdes_tbl_num;
+
+ rx_tbl = cfg->rx_tbl;
+ rx_tbl_num = cfg->rx_tbl_num;
+
+ tx_tbl = cfg->tx_tbl;
+ tx_tbl_num = cfg->tx_tbl_num;
+ }
+
+ qmp_configure(qmp->dev, serdes, serdes_tbl, serdes_tbl_num);
/* Tx, Rx, and PCS configurations */
- qmp_configure_lane(qmp->dev, tx, cfg->tx_tbl, cfg->tx_tbl_num, 1);
- qmp_configure_lane(qmp->dev, tx2, cfg->tx_tbl, cfg->tx_tbl_num, 2);
+ qmp_configure_lane(qmp->dev, tx, tx_tbl, tx_tbl_num, 1);
+ qmp_configure_lane(qmp->dev, tx2, tx_tbl, tx_tbl_num, 2);
- qmp_configure_lane(qmp->dev, rx, cfg->rx_tbl, cfg->rx_tbl_num, 1);
- qmp_configure_lane(qmp->dev, rx2, cfg->rx_tbl, cfg->rx_tbl_num, 2);
+ qmp_configure_lane(qmp->dev, rx, rx_tbl, rx_tbl_num, 1);
+ qmp_configure_lane(qmp->dev, rx2, rx_tbl, rx_tbl_num, 2);
- qmp_configure(qmp->dev, pcs, cfg->pcs_tbl, cfg->pcs_tbl_num);
+ qmp_configure(qmp->dev, pcs, pcs_tbl, pcs_tbl_num);
qmp_configure(qmp->dev, qmp->pcs_misc, cfg->pcs_misc_tbl, cfg->pcs_misc_tbl_num);
-
- if (pcs_usb)
+ if (qmp->qmpphy_mode == QMPPHY_MODE_USB4 && qmp->usb4_phy_submode == PHY_SUBMODE_TBT3)
+ qmp_configure(qmp->dev, serdes, cfg->usb4_serdes_tb3_ovrd_tbl,
+ cfg->usb4_serdes_tb3_ovrd_num);
+ else if (qmp->qmpphy_mode != QMPPHY_MODE_USB4 && pcs_usb)
qmp_configure(qmp->dev, pcs_usb, cfg->pcs_usb_tbl,
cfg->pcs_usb_tbl_num);
@@ -4275,16 +4422,20 @@ static int qmp_combo_usb_power_off(struct phy *phy)
{
struct qmp_combo *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
+ void __iomem *pcs = qmp->pcs;
+
+ if (qmp->usb4_init_count)
+ pcs = qmp->usb4_pcs;
/* PHY reset */
- qphy_setbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+ qphy_setbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
/* stop SerDes and Phy-Coding-Sublayer */
- qphy_clrbits(qmp->pcs, cfg->regs[QPHY_START_CTRL],
+ qphy_clrbits(pcs, cfg->regs[QPHY_START_CTRL],
SERDES_START | PCS_START);
/* Put PHY into POWER DOWN state: active low */
- qphy_clrbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ qphy_clrbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
SW_PWRDN);
return 0;
@@ -4295,21 +4446,27 @@ static int qmp_combo_usb_init(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
int ret;
- mutex_lock(&qmp->phy_mutex);
+ guard(mutex)(&qmp->phy_mutex);
+
+ /* USB4 mode takes precedence, do not reprogram the PHY in that case */
+ if (qmp->usb4_init_count) {
+ qmp->init_count++;
+ qmp->usb_init_count++;
+ return 0;
+ }
+
ret = qmp_combo_com_init(qmp, false);
if (ret)
- goto out_unlock;
+ return ret;
ret = qmp_combo_usb_power_on(phy);
if (ret) {
qmp_combo_com_exit(qmp, false);
- goto out_unlock;
+ return ret;
}
qmp->usb_init_count++;
-out_unlock:
- mutex_unlock(&qmp->phy_mutex);
return ret;
}
@@ -4318,20 +4475,26 @@ static int qmp_combo_usb_exit(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
int ret;
- mutex_lock(&qmp->phy_mutex);
+ guard(mutex)(&qmp->phy_mutex);
+
+ /* USB4 mode takes precedence, do not reprogram the PHY in that case */
+ if (qmp->usb4_init_count) {
+ qmp->init_count--;
+ qmp->usb_init_count--;
+ return 0;
+ }
+
ret = qmp_combo_usb_power_off(phy);
if (ret)
- goto out_unlock;
+ return ret;
ret = qmp_combo_com_exit(qmp, false);
if (ret)
- goto out_unlock;
+ return ret;
qmp->usb_init_count--;
-out_unlock:
- mutex_unlock(&qmp->phy_mutex);
- return ret;
+ return 0;
}
static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submode)
@@ -4343,6 +4506,125 @@ static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submo
return 0;
}
+static int qmp_combo_reconfigure_phy(struct qmp_combo *qmp, enum qmpphy_mode new_mode)
+{
+ dev_dbg(qmp->dev, "qmp_combo_reconfigure_phy: switching from qmpphy mode %d to %d\n",
+ qmp->qmpphy_mode, new_mode);
+
+ if (qmp->usb_init_count || qmp->usb4_init_count)
+ qmp_combo_usb_power_off(qmp->usb_phy);
+
+ if (qmp->dp_init_count)
+ writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);
+
+ qmp_combo_com_exit(qmp, true);
+
+ qmp->qmpphy_mode = new_mode;
+
+ /* Now everything's powered down, power up the right PHYs */
+ qmp_combo_com_init(qmp, true);
+
+ if ((qmp->usb_init_count || qmp->usb4_init_count) &&
+ new_mode != QMPPHY_MODE_DP_ONLY)
+ qmp_combo_usb_power_on(qmp->usb_phy);
+
+ if ((new_mode == QMPPHY_MODE_USB3DP || new_mode == QMPPHY_MODE_DP_ONLY) &&
+ qmp->dp_init_count)
+ qmp->cfg->dp_aux_init(qmp);
+
+ return 0;
+}
+
+static int qmp_combo_usb4_init(struct phy *phy)
+{
+ struct qmp_combo *qmp = phy_get_drvdata(phy);
+ int ret;
+
+ guard(mutex)(&qmp->phy_mutex);
+
+ if (!qmp->p2rr2p_pipe_clk) {
+ dev_err(qmp->dev, "missing p2rr2p_pipe clock handle. Update your Device Tree.\n");
+ return -EINVAL;
+ }
+
+ ret = clk_prepare_enable(qmp->p2rr2p_pipe_clk);
+ if (ret) {
+ dev_err(qmp->dev, "p2rr2p_pipe enable failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = qmp_combo_com_init(qmp, false);
+ if (ret) {
+ clk_disable_unprepare(qmp->p2rr2p_pipe_clk);
+ return ret;
+ }
+
+ /* USB4 mode takes precedence to USB3(+DP), force reconfigure the PHY */
+ ret = qmp_combo_reconfigure_phy(qmp, QMPPHY_MODE_USB4);
+ if (ret) {
+ clk_disable_unprepare(qmp->p2rr2p_pipe_clk);
+ qmp_combo_com_exit(qmp, false);
+ return ret;
+ }
+
+ ret = qmp_combo_usb_power_on(phy);
+ if (ret) {
+ clk_disable_unprepare(qmp->p2rr2p_pipe_clk);
+ qmp_combo_com_exit(qmp, false);
+ return ret;
+ }
+
+ /*
+ * Due to the SoC design, the PHY only has a single valid consumer and
+ * preventing it from having sole ownership of the PHY's power state
+ * makes suspending/resuming the router impossible.
+ */
+ WARN_ON(qmp->usb4_init_count++);
+
+ return 0;
+}
+
+static int qmp_combo_usb4_exit(struct phy *phy)
+{
+ struct qmp_combo *qmp = phy_get_drvdata(phy);
+ int ret;
+
+ guard(mutex)(&qmp->phy_mutex);
+
+ ret = qmp_combo_usb_power_off(qmp->usb_phy);
+ if (ret)
+ return ret;
+
+ ret = qmp_combo_com_exit(qmp, false);
+ if (ret)
+ return ret;
+
+ /*
+ * Mark the USB4 PHY uninitialized and wait for a mux_set event to determine the correct
+ * setting. This will always be possible because USB4 requires Type-C.
+ */
+ qmp->usb4_init_count--;
+
+ clk_disable_unprepare(qmp->p2rr2p_pipe_clk);
+
+ return 0;
+}
+
+static int qmp_combo_usb4_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct qmp_combo *qmp = phy_get_drvdata(phy);
+
+ if (mode != PHY_MODE_TBT)
+ return -EINVAL;
+
+ if (submode != PHY_SUBMODE_USB4 && submode != PHY_SUBMODE_TBT3)
+ return -EINVAL;
+
+ qmp->usb4_phy_submode = submode;
+
+ return 0;
+}
+
static const struct phy_ops qmp_combo_usb_phy_ops = {
.init = qmp_combo_usb_init,
.exit = qmp_combo_usb_exit,
@@ -4350,6 +4632,13 @@ static const struct phy_ops qmp_combo_usb_phy_ops = {
.owner = THIS_MODULE,
};
+static const struct phy_ops qmp_combo_usb4_phy_ops = {
+ .init = qmp_combo_usb4_init,
+ .exit = qmp_combo_usb4_exit,
+ .set_mode = qmp_combo_usb4_set_mode,
+ .owner = THIS_MODULE,
+};
+
static const struct phy_ops qmp_combo_dp_phy_ops = {
.init = qmp_combo_dp_init,
.configure = qmp_combo_dp_configure,
@@ -4389,9 +4678,12 @@ static void qmp_combo_enable_autonomous_mode(struct qmp_combo *qmp)
* Enable i/o clamp_n for autonomous mode
* V6 and later versions use pcs aon clamp register
*/
- if (pcs_aon)
- qphy_clrbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
- else if (pcs_misc)
+ if (pcs_aon) {
+ if (qmp->qmpphy_mode == QMPPHY_MODE_USB4)
+ qphy_clrbits(pcs_aon, cfg->regs[QPHY_PCS_USB4_CLAMP_ENABLE], CLAMP_EN);
+ else
+ qphy_clrbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
+ } else if (pcs_misc)
qphy_clrbits(pcs_misc, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
}
@@ -4403,9 +4695,12 @@ static void qmp_combo_disable_autonomous_mode(struct qmp_combo *qmp)
void __iomem *pcs_aon = qmp->pcs_aon;
/* Disable i/o clamp_n on resume for normal mode */
- if (pcs_aon)
- qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
- else if (pcs_misc)
+ if (pcs_aon) {
+ if (qmp->qmpphy_mode == QMPPHY_MODE_USB4)
+ qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_USB4_CLAMP_ENABLE], CLAMP_EN);
+ else
+ qphy_setbits(pcs_aon, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
+ } else if (pcs_misc)
qphy_setbits(pcs_misc, cfg->regs[QPHY_PCS_CLAMP_ENABLE], CLAMP_EN);
qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
@@ -4779,24 +5074,29 @@ static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw,
struct qmp_combo *qmp = typec_switch_get_drvdata(sw);
const struct qmp_phy_cfg *cfg = qmp->cfg;
+ if (qmp->qmpphy_mode == QMPPHY_MODE_USB4) {
+ /* QMPPHY has no orientation handling in USB4 mode, don't cache the setting */
+ qmp->orientation = TYPEC_ORIENTATION_NONE;
+ return 0;
+ }
+
if (orientation == qmp->orientation || orientation == TYPEC_ORIENTATION_NONE)
return 0;
- mutex_lock(&qmp->phy_mutex);
+ guard(mutex)(&qmp->phy_mutex);
qmp->orientation = orientation;
if (qmp->init_count) {
- if (qmp->usb_init_count)
+ if (qmp->usb_init_count || qmp->usb4_init_count)
qmp_combo_usb_power_off(qmp->usb_phy);
qmp_combo_com_exit(qmp, true);
qmp_combo_com_init(qmp, true);
- if (qmp->usb_init_count)
+ if (qmp->usb_init_count || qmp->usb4_init_count)
qmp_combo_usb_power_on(qmp->usb_phy);
if (qmp->dp_init_count)
cfg->dp_aux_init(qmp);
}
- mutex_unlock(&qmp->phy_mutex);
return 0;
}
@@ -4804,7 +5104,6 @@ static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw,
static int qmp_combo_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
{
struct qmp_combo *qmp = typec_mux_get_drvdata(mux);
- const struct qmp_phy_cfg *cfg = qmp->cfg;
enum qmpphy_mode new_mode;
unsigned int svid;
@@ -4815,6 +5114,29 @@ static int qmp_combo_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_s
else
svid = 0;
+ /*
+ * The USB4 router driver must excercise fine-grained control over the timing of
+ * USB4 QMPPHY mode entry/exit, which is difficult to otherwise ensure within Linux's
+ * Type-C framework if the PHY acts as a self-decisive mode mux in parallel.
+ *
+ * Keep the hardware in QMPPHY_MODE_USB4 at all times between .init and .exit of the
+ * (Linux) USB4 PHY and wait for the router driver to turn it off at its discretion.
+ * Once that happens, fall back to the usual USB3/DP/Combo mode logic.
+ *
+ * After the QMPPHY has been turned off through phy_exit(usb4_phy), the next mux_set
+ * will initialize it in the right mode.
+ */
+ if (qmp->usb4_init_count)
+ return 0;
+
+ /*
+ * Explicitly ignore TBT/USB4 mode requests that may come if the USB4 PHY hasn't been
+ * initialized, either due to the USB4 drivers being disabled or due to this PHY instance
+ * lacking USB4 support.
+ */
+ if (svid == USB_TYPEC_TBT_SID || (!state->alt && state->mode == TYPEC_MODE_USB4))
+ return 0;
+
if (svid == USB_TYPEC_DP_SID) {
switch (state->mode) {
/* DP Only */
@@ -4847,41 +5169,11 @@ static int qmp_combo_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_s
return 0;
}
- dev_dbg(qmp->dev, "typec_mux_set: switching from qmpphy mode %d to %d\n",
- qmp->qmpphy_mode, new_mode);
-
- qmp->qmpphy_mode = new_mode;
-
- if (qmp->init_count) {
- if (qmp->usb_init_count)
- qmp_combo_usb_power_off(qmp->usb_phy);
-
- if (qmp->dp_init_count)
- writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);
-
- qmp_combo_com_exit(qmp, true);
-
- /* Now everything's powered down, power up the right PHYs */
- qmp_combo_com_init(qmp, true);
-
- if (new_mode == QMPPHY_MODE_DP_ONLY) {
- if (qmp->usb_init_count)
- qmp->usb_init_count--;
- }
-
- if (new_mode == QMPPHY_MODE_USB3DP || new_mode == QMPPHY_MODE_USB3_ONLY) {
- qmp_combo_usb_power_on(qmp->usb_phy);
- if (!qmp->usb_init_count)
- qmp->usb_init_count++;
- }
-
- if (new_mode == QMPPHY_MODE_DP_ONLY || new_mode == QMPPHY_MODE_USB3DP) {
- if (qmp->dp_init_count)
- cfg->dp_aux_init(qmp);
- }
- }
+ /* The mux still receives Type-C events, even if all PHYs are uninitialized */
+ if (!qmp->init_count)
+ return 0;
- return 0;
+ return qmp_combo_reconfigure_phy(qmp, new_mode);
}
static void qmp_combo_typec_switch_unregister(void *data)
@@ -5088,6 +5380,9 @@ static int qmp_combo_parse_dt(struct qmp_combo *qmp)
if (offs->aon_toggle)
qmp->aon_toggle = base + offs->aon_toggle;
+ qmp->usb4_serdes = base + offs->usb4_serdes;
+ qmp->usb4_pcs = base + offs->usb4_pcs;
+
ret = qmp_combo_clk_init(qmp);
if (ret)
return ret;
@@ -5098,6 +5393,12 @@ static int qmp_combo_parse_dt(struct qmp_combo *qmp)
"failed to get usb3_pipe clock\n");
}
+ qmp->p2rr2p_pipe_clk = devm_clk_get_optional(dev, "p2rr2p_pipe");
+ if (IS_ERR(qmp->p2rr2p_pipe_clk)) {
+ return dev_err_probe(dev, PTR_ERR(qmp->p2rr2p_pipe_clk),
+ "failed to get p2rr2p_pipe clock\n");
+ }
+
return 0;
}
@@ -5113,6 +5414,8 @@ static struct phy *qmp_combo_phy_xlate(struct device *dev, const struct of_phand
return qmp->usb_phy;
case QMP_USB43DP_DP_PHY:
return qmp->dp_phy;
+ case QMP_USB43DP_USB4_PHY:
+ return qmp->usb4_phy ?: ERR_PTR(-EINVAL);
}
return ERR_PTR(-EINVAL);
@@ -5325,6 +5628,17 @@ static int qmp_combo_probe(struct platform_device *pdev)
phy_set_drvdata(qmp->dp_phy, qmp);
+ if (qmp->cfg->usb4_serdes_tbl) {
+ qmp->usb4_phy = devm_phy_create(dev, dev->of_node, &qmp_combo_usb4_phy_ops);
+ if (IS_ERR(qmp->usb4_phy)) {
+ ret = PTR_ERR(qmp->usb4_phy);
+ dev_err(dev, "failed to create USB4 PHY: %d\n", ret);
+ goto err_node_put;
+ }
+
+ phy_set_drvdata(qmp->usb4_phy, qmp);
+ }
+
if (usb_np == dev->of_node)
phy_provider = devm_of_phy_provider_register(dev, qmp_combo_phy_xlate);
else
@@ -5360,6 +5674,10 @@ static const struct of_device_id qmp_combo_of_match_table[] = {
.data = &hawi_usb3dpphy_cfg,
},
{
+ .compatible = "qcom,nord-qmp-usb3-dp-phy",
+ .data = &nord_usb3dpphy_cfg,
+ },
+ {
.compatible = "qcom,sar2130p-qmp-usb3-dp-phy",
.data = &sar2130p_usb3dpphy_cfg,
},
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-common.h b/drivers/phy/qualcomm/phy-qcom-qmp-common.h
index b945fc14cece..3c0e47f4af75 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-common.h
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-common.h
@@ -59,4 +59,36 @@ static inline void qmp_configure(struct device *dev, void __iomem *base,
qmp_configure_lane(dev, base, tbl, num, 0xff);
}
+static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg |= val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg &= ~val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+static inline bool qphy_checkbits(const void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ return (reg & val) == val;
+}
+
#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c
index 37e96493b722..ed22b7a256ef 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c
@@ -218,30 +218,6 @@ struct qcom_qmp {
int init_count;
};
-static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg |= val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
-static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg &= ~val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
/* list of clocks required by phy */
static const char * const msm8996_phy_clk_l[] = {
"aux", "cfg_ahb", "ref",
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c
index e93cba4369fb..3d225c0bb80e 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c
@@ -3,6 +3,7 @@
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
@@ -22,6 +23,11 @@
#include <linux/slab.h>
#include "phy-qcom-qmp.h"
+#include "phy-qcom-qmp-common.h"
+#include "phy-qcom-qmp-pcs-v6_40.h"
+#include "phy-qcom-qmp-pcs-pcie-v6_40.h"
+#include "phy-qcom-qmp-qserdes-com-pcie-v6_40.h"
+#include "phy-qcom-qmp-qserdes-txrxz-pcie-v6_40.h"
#define PHY_INIT_COMPLETE_TIMEOUT_US 10000
@@ -30,8 +36,19 @@ enum qmp_pcie_glymur_link_mode {
QMP_PCIE_GLYMUR_MODE_X4X4,
};
+enum qmp_pcie_nord_link_mode {
+ QMP_PCIE_NORD_MODE_X16,
+ QMP_PCIE_NORD_MODE_X8_X8,
+ QMP_PCIE_NORD_MODE_X8_X4_X4,
+ QMP_PCIE_NORD_MODE_X8_X4_X2_X2
+};
+
enum qphy_reg_layout {
QPHY_PCS_STATUS,
+ QPHY_PCS_START_CONTROL,
+ QPHY_PCS_SW_RESET,
+ QPHY_PCS_POWER_DOWN_CONTROL,
+ QPHY_COM_BIAS_EN_CLKBUFLR_EN,
QPHY_LAYOUT_SIZE
};
@@ -39,16 +56,48 @@ static const unsigned int pciephy_v8_50_regs_layout[QPHY_LAYOUT_SIZE] = {
[QPHY_PCS_STATUS] = QPHY_V8_50_PCS_STATUS1,
};
+static const unsigned int nord_pciephy_v6_40_regs_layout[QPHY_LAYOUT_SIZE] = {
+ [QPHY_PCS_STATUS] = QPHY_V6_40_PCS_STATUS1,
+ [QPHY_PCS_START_CONTROL] = QPHY_V6_40_PCS_START_CONTROL,
+ [QPHY_PCS_SW_RESET] = QPHY_V6_40_PCS_SW_RESET,
+ [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_40_PCS_POWER_DOWN_CONTROL,
+ [QPHY_COM_BIAS_EN_CLKBUFLR_EN] = QSERDES_PCIE_V6_40_COM_BIAS_EN_CLKBUFLR_EN,
+};
+
struct qmp_pcie_offsets {
u16 pcs;
+ u16 pll;
+ u16 txrxz;
+ u16 pcs_lanez;
+};
+
+struct qmp_phy_cfg_tbls {
+ const struct qmp_phy_init_tbl *pll_common;
+ int pll_common_num;
+ const struct qmp_phy_init_tbl *txrxz;
+ int txrxz_num;
+ const struct qmp_phy_init_tbl *pcs;
+ int pcs_num;
+ const struct qmp_phy_init_tbl *pcs_lanez;
+ int pcs_lanez_num;
+ const struct qmp_phy_init_tbl *port_split_pre_pwrup;
+ int port_split_pre_pwrup_num;
+ const struct qmp_phy_init_tbl *port_split_post_cfg;
+ int port_split_post_cfg_num;
};
struct qmp_phy_cfg {
const struct qmp_pcie_offsets *offsets;
+ const struct qmp_phy_cfg_tbls tbls;
+ const struct qmp_phy_init_tbl * const *pll_tbls;
+ const int *pll_nums;
+ bool port_split;
const char * const *reg_names;
int num_regs;
const char * const *pd_names;
int num_pds;
+ const char * const *reset_list;
+ int num_resets;
const char * const *nocsr_reset_list;
int num_nocsr_resets;
const char * const *vreg_list;
@@ -64,9 +113,11 @@ struct qmp_phy_cfg {
struct qmp_pcie {
struct device *dev;
const struct qmp_phy_cfg *cfg;
+ bool skip_init;
void __iomem **base;
struct clk_bulk_data *clks;
struct clk_bulk_data *pipe_clks;
+ struct reset_control_bulk_data *resets;
struct reset_control_bulk_data *nocsr_resets;
struct regulator_bulk_data *vregs;
struct device **pd_devs;
@@ -89,6 +140,10 @@ struct qmp_pcie_match_data {
u32 num_modes;
};
+/* QSERDES_PCIE_V6_40_COM_BIAS_EN_CLKBUFLR_EN bits */
+#define COM_BIAS_EN BIT(0)
+#define COM_BIAS_EN_MUX BIT(1)
+
static const char * const glymur_pciephy_clk_l[] = {
"aux", "cfg_ahb", "ref", "rchng", "phy_b_aux",
};
@@ -113,31 +168,19 @@ static const char * const glymur_vreg_l[] = {
"vdda-phy", "vdda-pll", "vdda-refgen0p9", "vdda-refgen1p2",
};
-static const char * const glymur_pciephy_a_reg_l[] = {
+static const char * const pciephy_port_a_reg_l[] = {
"port_a",
};
-static const char * const glymur_pciephy_b_reg_l[] = {
+static const char * const pciephy_port_b_reg_l[] = {
"port_b",
};
-static const char * const glymur_pciephy_reg_l[] = {
+static const char * const pciephy_port_ab_reg_l[] = {
"port_a", "port_b",
};
-static const char * const glymur_pciephy_a_pd_l[] = {
- "port_a",
-};
-
-static const char * const glymur_pciephy_b_pd_l[] = {
- "port_b",
-};
-
-static const char * const glymur_pciephy_pd_l[] = {
- "port_a", "port_b",
-};
-
-static const char * const glymur_pciephy_a_nocsr_reset_l[] = {
+static const char * const pciephy_port_a_nocsr_reset_l[] = {
"port_a_nocsr",
};
@@ -145,7 +188,7 @@ static const char * const glymur_pciephy_nocsr_reset_l[] = {
"port_a_nocsr", "port_b_nocsr",
};
-static const char * const glymur_pciephy_b_nocsr_reset_l[] = {
+static const char * const pciephy_port_b_nocsr_reset_l[] = {
"port_b_nocsr",
};
@@ -155,12 +198,12 @@ static const struct qmp_pcie_offsets glymur_pcie_offsets_v8_50 = {
static const struct qmp_phy_cfg glymur_qmp_gen5x4_pciephy_a_cfg = {
.offsets = &glymur_pcie_offsets_v8_50,
- .reg_names = glymur_pciephy_a_reg_l,
- .num_regs = ARRAY_SIZE(glymur_pciephy_a_reg_l),
- .pd_names = glymur_pciephy_a_pd_l,
- .num_pds = ARRAY_SIZE(glymur_pciephy_a_pd_l),
- .nocsr_reset_list = glymur_pciephy_a_nocsr_reset_l,
- .num_nocsr_resets = ARRAY_SIZE(glymur_pciephy_a_nocsr_reset_l),
+ .reg_names = pciephy_port_a_reg_l,
+ .num_regs = ARRAY_SIZE(pciephy_port_a_reg_l),
+ .pd_names = pciephy_port_a_reg_l,
+ .num_pds = ARRAY_SIZE(pciephy_port_a_reg_l),
+ .nocsr_reset_list = pciephy_port_a_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(pciephy_port_a_nocsr_reset_l),
.vreg_list = glymur_vreg_l,
.num_vregs = ARRAY_SIZE(glymur_vreg_l),
.regs = pciephy_v8_50_regs_layout,
@@ -173,12 +216,12 @@ static const struct qmp_phy_cfg glymur_qmp_gen5x4_pciephy_a_cfg = {
static const struct qmp_phy_cfg glymur_qmp_gen5x4_pciephy_b_cfg = {
.offsets = &glymur_pcie_offsets_v8_50,
- .reg_names = glymur_pciephy_b_reg_l,
- .num_regs = ARRAY_SIZE(glymur_pciephy_b_reg_l),
- .pd_names = glymur_pciephy_b_pd_l,
- .num_pds = ARRAY_SIZE(glymur_pciephy_b_pd_l),
- .nocsr_reset_list = glymur_pciephy_b_nocsr_reset_l,
- .num_nocsr_resets = ARRAY_SIZE(glymur_pciephy_b_nocsr_reset_l),
+ .reg_names = pciephy_port_b_reg_l,
+ .num_regs = ARRAY_SIZE(pciephy_port_b_reg_l),
+ .pd_names = pciephy_port_b_reg_l,
+ .num_pds = ARRAY_SIZE(pciephy_port_b_reg_l),
+ .nocsr_reset_list = pciephy_port_b_nocsr_reset_l,
+ .num_nocsr_resets = ARRAY_SIZE(pciephy_port_b_nocsr_reset_l),
.vreg_list = glymur_vreg_l,
.num_vregs = ARRAY_SIZE(glymur_vreg_l),
.regs = pciephy_v8_50_regs_layout,
@@ -191,10 +234,10 @@ static const struct qmp_phy_cfg glymur_qmp_gen5x4_pciephy_b_cfg = {
static const struct qmp_phy_cfg glymur_qmp_gen5x8_pciephy_cfg = {
.offsets = &glymur_pcie_offsets_v8_50,
- .reg_names = glymur_pciephy_reg_l,
- .num_regs = ARRAY_SIZE(glymur_pciephy_reg_l),
- .pd_names = glymur_pciephy_pd_l,
- .num_pds = ARRAY_SIZE(glymur_pciephy_pd_l),
+ .reg_names = pciephy_port_ab_reg_l,
+ .num_regs = ARRAY_SIZE(pciephy_port_ab_reg_l),
+ .pd_names = pciephy_port_ab_reg_l,
+ .num_pds = ARRAY_SIZE(pciephy_port_ab_reg_l),
.nocsr_reset_list = glymur_pciephy_nocsr_reset_l,
.num_nocsr_resets = ARRAY_SIZE(glymur_pciephy_nocsr_reset_l),
.vreg_list = glymur_vreg_l,
@@ -232,6 +275,694 @@ static const struct qmp_pcie_match_data glymur_qmp_gen5x8_match_data = {
.num_modes = ARRAY_SIZE(glymur_qmp_gen5x8_mode_cfgs),
};
+static const char * const nord_pciephy_port_a_vreg_l[] = {
+ "vdda-phy-a", "vdda-pll-a",
+};
+
+static const char * const nord_pciephy_port_b_vreg_l[] = {
+ "vdda-phy-b", "vdda-pll-b",
+};
+
+static const char * const nord_pciephy_port_c_vreg_l[] = {
+ "vdda-phy-c", "vdda-pll-c",
+};
+
+static const char * const nord_pciephy_port_d_vreg_l[] = {
+ "vdda-phy-d", "vdda-pll-d",
+};
+
+static const char * const nord_pciephy_port_cd_vreg_l[] = {
+ "vdda-phy-c", "vdda-pll-c", "vdda-phy-d", "vdda-pll-d",
+};
+
+static const char * const nord_pciephy_port_bcd_vreg_l[] = {
+ "vdda-phy-b", "vdda-pll-b", "vdda-phy-c", "vdda-pll-c",
+ "vdda-phy-d", "vdda-pll-d",
+};
+
+static const char * const nord_pciephy_port_abcd_vreg_l[] = {
+ "vdda-phy-a", "vdda-pll-a", "vdda-phy-b", "vdda-pll-b",
+ "vdda-phy-c", "vdda-pll-c", "vdda-phy-d", "vdda-pll-d",
+};
+
+static const struct qmp_pcie_offsets nord_pcie_offsets_v6_40 = {
+ .pll = 0x8000,
+ .pcs = 0x9000,
+ .txrxz = 0xd000,
+ .pcs_lanez = 0xe800,
+};
+
+static const struct qmp_phy_init_tbl nord_qmp_pcie_pll_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CLK_FWD_CONFIG_1, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE1_MODE2, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE2_MODE2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CP_CTRL_MODE2, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_PLL_RCTRL_MODE2, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_PLL_CCTRL_MODE2, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CORECLK_DIV_MODE2, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_LOCK_CMP1_MODE2, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_LOCK_CMP2_MODE2, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DEC_START_MODE2, 0x68),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DIV_FRAC_START1_MODE2, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DIV_FRAC_START2_MODE2, 0xaa),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DIV_FRAC_START3_MODE2, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE1_MODE2, 0xa4),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE2_MODE2, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE1_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE2_MODE1, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CORECLK_DIV_MODE1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_LOCK_CMP1_MODE1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_LOCK_CMP2_MODE1, 0x0d),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DEC_START_MODE1, 0x68),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DIV_FRAC_START1_MODE1, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DIV_FRAC_START2_MODE1, 0xaa),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DIV_FRAC_START3_MODE1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0x52),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_HSCLK_SEL_1, 0x3c),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_HSCLK_SEL_2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_HSCLK_SEL_1, 0x3c),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_HSCLK_SEL_2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE1_MODE0, 0xe0),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE2_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CORECLK_DIV_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_LOCK_CMP1_MODE0, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_LOCK_CMP2_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DEC_START_MODE0, 0x41),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DIV_FRAC_START2_MODE0, 0xaa),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DIV_FRAC_START3_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xd4),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_HSCLK_HS_SWITCH_SEL_1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_HSCLK_HS_SWITCH_SEL_2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_BG_TIMER, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_VCO_TUNE_CTRL, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_SSC_PER1, 0x62),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_SSC_PER2, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_POST_DIV_MUX, 0xc0),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_BIAS_EN_CLKBUFLR_EN, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CLK_ENABLE1, 0x90),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_SYS_CLK_CTRL, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_PLL_IVCO, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_PLL_IVCO_MODE1, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_PLL_IVCO_MODE2, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_SYSCLK_EN_SEL, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_LOCK_CMP_EN, 0x46),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_LOCK_CMP_CFG, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_VCO_TUNE_MAP, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CLK_SELECT, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CORE_CLK_EN, 0xe0),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CMN_CONFIG_1, 0x86),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CMN_MISC1, 0x88),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CMN_MODE, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_VCO_DC_LEVEL_CTRL, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CMN_CONFIG_2, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CLK_EP_DIV_MODE1, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CLK_EP_DIV_MODE0, 0x32),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_PSM_CAL_EN, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_PLL_SPARE_FOR_ECO, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DCC_CAL_1, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DCC_CAL_2, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DCC_CAL_3, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DCC_CAL_8, 0x32),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_IP_CTRL_AND_DP_SEL, 0xaf),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_CMN_IETRIM, 0x0d),
+};
+
+static const struct qmp_phy_init_tbl nord_qmp_pcie_pll_a_ovl_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DCC_CAL_7, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DCC_CAL_9, 0x31),
+};
+
+static const struct qmp_phy_init_tbl nord_qmp_pcie_pll_b_ovl_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DCC_CAL_7, 0x32),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DCC_CAL_9, 0x22),
+};
+
+static const struct qmp_phy_init_tbl nord_qmp_pcie_pll_c_ovl_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DCC_CAL_7, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DCC_CAL_9, 0x31),
+};
+
+static const struct qmp_phy_init_tbl nord_qmp_pcie_pll_d_ovl_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DCC_CAL_7, 0x22),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_COM_DCC_CAL_9, 0x22),
+};
+
+static const struct qmp_phy_init_tbl nord_qmp_pcie_txrxz_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_SIGDET_ENABLES, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B0, 0xcd),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B1, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B2, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B3, 0x50),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B4, 0xac),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B5, 0xdb),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B6, 0x52),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B7, 0x3e),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B0, 0xce),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B1, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B2, 0x6d),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B3, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B4, 0x8c),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B5, 0x9d),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B6, 0x6d),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B7, 0x26),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B0, 0xfb),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B1, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B2, 0xe8),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B3, 0x70),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B4, 0x9c),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B5, 0xf4),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B6, 0x6d),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B7, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_SUMMER_CAL_SPD_MODE_RATE_0123, 0x6f),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_TX_ADAPT_POST_THRESH1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_TX_ADAPT_POST_THRESH2, 0x0d),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_PHPRE_CTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_TX_ADPT_CTRL, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B0, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B1, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B2, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B3, 0x50),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B4, 0x9a),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B5, 0x47),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B6, 0x52),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B7, 0x1d),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_LANE_MODE_1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_LANE_MODE_2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_LANE_MODE_3, 0x41),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_LANE_MODE_4, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_TOP_LDO_CODE_CTRL1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_TOP_LDO_CODE_CTRL2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_TOP_LDO_CODE_CTRL3, 0x81),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_TOP_LDO_CODE_CTRL4, 0x8d),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOCK_CTRL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_DFE_TAP1_DAC_ENABLE, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_DFE_TAP2_DAC_ENABLE, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_DFE_TAP345_DAC_ENABLE, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_DFE_TAP67_DAC_ENABLE, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE0, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE1, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE2, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE3, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE4, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE3, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE4, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_FLL_DIV_RATIO_RATE_0123, 0x94),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_CCODE_RATE_01, 0x66),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_CCODE_RATE_23, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_CCODE_RATE4, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_IQTUNE_CTRL, 0x58),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_IQTUNE_MAN_INDEX, 0x0d),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_IQTUNE_ANA_CTRL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_FUNC_CTRL, 0xd0),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE0, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE2, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE3, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE4, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE0, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE1, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE2, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE3, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE4, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KP_CODE_OVRD_RATE_2_3, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_IVCM_CAL_CTRL2, 0x83),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_IVCM_CAL_CTRL3, 0x44),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_IVCM_CAL_CTRL4, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_PLL_RATE_0_1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_PLL_RATE_2_3, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_PLL_RATE4, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FLL_RATE_0_1, 0x33),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FLL_RATE_2_3, 0x33),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FLL_RATE4, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FAST_RATE_0_1, 0x22),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FAST_RATE_2_3, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FAST_RATE4, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_IQTUNE_DIV2_CTRL_RATE0123, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE0, 0x13),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE0, 0x13),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE1, 0x26),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE1, 0x26),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE2, 0x9c),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE2, 0x3d),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE3, 0x9c),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE3, 0x3d),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE4, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE4, 0x3d),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_EQU_ADAPTOR_CNTRL3, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_EQU_ADAPTOR_CNTRL4, 0xaa),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RX_EQU_ADAPTOR_CNTRL5, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_VGA_CAL_CNTRL1, 0xe1),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_VGA_CAL_MAN_VAL_RATE0_1, 0xdd),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_VGA_CAL_MAN_VAL_RATE2_3, 0xdc),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_VGA_CAL_MAN_VAL_RATE4, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CAP_CODE_OVRD_MUXES, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CAP_CODE_RATE_0123, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CAP_CODE_RATE4, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE0, 0x4b),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF2_RATE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE1, 0x4b),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF2_RATE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE2, 0x35),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF2_RATE2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE3, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE4, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_INIT_RATE_0_1, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_INIT_RATE_2_3, 0x22),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_INIT_RATE_4, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_CODE_OVRD_RATE0, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_CODE_OVRD_RATE1, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_KVCO_CODE_OVRD_RATE2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE0, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE2, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE3, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE4, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCTRL_RATE_0_1, 0x43),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCTRL_RATE_2_3, 0x44),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_TYPE_CONFIG, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CTLE_POST_CAL_OFFSET_RATE_0_1_2, 0x67),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_EN_LOWFREQ, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RES_CODE_LANE_OFFSET_RX, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_GM_CAL_RES_RATE0_1, 0x88),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_GM_CAL_RES_RATE2_3, 0x88),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_GM_CAL_RES_RATE4, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_VTHRESH_CAL_MAN_VAL_RATE4, 0x56),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_CLKBUF_ENABLE, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE0, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE1, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE2, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE3, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE4, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_SIGDET_CNTRL, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_SIGDET_LVL, 0x84),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_SIGDET_CAL_CTRL1, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_DIG_BKUP_CTRL15, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_DIG_BKUP_CTRL16, 0x52),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RESTRIM_CAL_CTRL, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RESTRIM_POST_CAL_OFFSET, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_RESTRIM_VREF_SEL, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_BLW_CTRL, 0x3c),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_BLW_MAN_VAL_RATE3, 0x17),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_BLW_MAN_VAL_RATE4, 0x17),
+};
+
+static const struct qmp_phy_init_tbl nord_qmp_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_POWER_STATE_CONFIG2, 0x3d),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_POWER_STATE_CONFIG5, 0xe4),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_POWER_STATE_CONFIG6, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_POWER_STATE_CONFIG7, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_MULTIPHY_CONFIG2, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG2, 0xe4),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG3, 0x30),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG5, 0x05),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG6, 0x08),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG7, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG8, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG10, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_DIVTX_CLK_DCC_CAL_CONFIG2, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_DIVTX_CLK_DCC_CAL_CONFIG3, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_DIVTX_CLK_DCC_CAL_CONFIG4, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_ENDPOINT_REFCLK_DRIVE, 0xc1),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_RX_SIGDET_LVL, 0x66),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_OSC_DTCT_ACTIONS, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_LOCK_DETECT_CONFIG1, 0xff),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG1, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG2, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG3, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG7, 0x35),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG8, 0x3e),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_EQ_CONFIG1, 0x06),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_EQ_CONFIG2, 0x1c),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_G345_EQ_CONFIG1, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_G345_EQ_CONFIG3, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_G3_EQ_CONFIG1, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_G3_EQ_CONFIG5, 0x90),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_G3_EQ_CONFIG7, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_G4_EQ_CONFIG1, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_G4_EQ_CONFIG5, 0x90),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_G4_EQ_CONFIG7, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_G5_EQ_CONFIG1, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_G5_EQ_CONFIG5, 0x90),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_G5_EQ_CONFIG7, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_COM_TEST_CONTROL1, 0x08),
+};
+
+static const struct qmp_phy_init_tbl nord_qmp_pcie_pcs_lanez_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_LANEZ_OUTSIG_MX_CTRL2, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_PCIE_V6_40_PCS_LANEZ_OUTSIG_MX_CTRL5, 0x00),
+};
+
+static const struct qmp_phy_init_tbl nord_qmp_pcie_port_split_pre_pwrup_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_PRE_STALL_LDO_BOOST_EN, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_LDO_TIMER_CTRL, 0x91),
+};
+
+static const struct qmp_phy_init_tbl nord_qmp_pcie_port_split_post_cfg_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_LDO_TIMER_CTRL, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_PCIE_V6_40_TXRXZ_PRE_STALL_LDO_BOOST_EN, 0x00),
+};
+
+static const struct qmp_phy_cfg_tbls nord_qmp_pcie_tbls = {
+ .pll_common = nord_qmp_pcie_pll_tbl,
+ .pll_common_num = ARRAY_SIZE(nord_qmp_pcie_pll_tbl),
+ .txrxz = nord_qmp_pcie_txrxz_tbl,
+ .txrxz_num = ARRAY_SIZE(nord_qmp_pcie_txrxz_tbl),
+ .pcs = nord_qmp_pcie_pcs_tbl,
+ .pcs_num = ARRAY_SIZE(nord_qmp_pcie_pcs_tbl),
+ .pcs_lanez = nord_qmp_pcie_pcs_lanez_tbl,
+ .pcs_lanez_num = ARRAY_SIZE(nord_qmp_pcie_pcs_lanez_tbl),
+ .port_split_pre_pwrup = nord_qmp_pcie_port_split_pre_pwrup_tbl,
+ .port_split_pre_pwrup_num = ARRAY_SIZE(nord_qmp_pcie_port_split_pre_pwrup_tbl),
+ .port_split_post_cfg = nord_qmp_pcie_port_split_post_cfg_tbl,
+ .port_split_post_cfg_num = ARRAY_SIZE(nord_qmp_pcie_port_split_post_cfg_tbl),
+};
+
+static const struct qmp_phy_init_tbl * const nord_qmp_pcie_pll_a[] = {
+ nord_qmp_pcie_pll_a_ovl_tbl,
+};
+
+static const int nord_qmp_pcie_pll_a_nums[] = {
+ ARRAY_SIZE(nord_qmp_pcie_pll_a_ovl_tbl),
+};
+
+static const struct qmp_phy_init_tbl * const nord_qmp_pcie_pll_b[] = {
+ nord_qmp_pcie_pll_b_ovl_tbl,
+};
+
+static const int nord_qmp_pcie_pll_b_nums[] = {
+ ARRAY_SIZE(nord_qmp_pcie_pll_b_ovl_tbl),
+};
+
+static const struct qmp_phy_init_tbl * const nord_qmp_pcie_pll_c[] = {
+ nord_qmp_pcie_pll_c_ovl_tbl,
+};
+
+static const int nord_qmp_pcie_pll_c_nums[] = {
+ ARRAY_SIZE(nord_qmp_pcie_pll_c_ovl_tbl),
+};
+
+static const struct qmp_phy_init_tbl * const nord_qmp_pcie_pll_d[] = {
+ nord_qmp_pcie_pll_d_ovl_tbl,
+};
+
+static const int nord_qmp_pcie_pll_d_nums[] = {
+ ARRAY_SIZE(nord_qmp_pcie_pll_d_ovl_tbl),
+};
+
+static const struct qmp_phy_init_tbl * const nord_qmp_pcie_pll_cd[] = {
+ nord_qmp_pcie_pll_c_ovl_tbl, nord_qmp_pcie_pll_d_ovl_tbl,
+};
+
+static const int nord_qmp_pcie_pll_cd_nums[] = {
+ ARRAY_SIZE(nord_qmp_pcie_pll_c_ovl_tbl), ARRAY_SIZE(nord_qmp_pcie_pll_d_ovl_tbl),
+};
+
+static const struct qmp_phy_init_tbl * const nord_qmp_pcie_pll_bcd[] = {
+ nord_qmp_pcie_pll_b_ovl_tbl, nord_qmp_pcie_pll_c_ovl_tbl, nord_qmp_pcie_pll_d_ovl_tbl,
+};
+
+static const int nord_qmp_pcie_pll_bcd_nums[] = {
+ ARRAY_SIZE(nord_qmp_pcie_pll_b_ovl_tbl), ARRAY_SIZE(nord_qmp_pcie_pll_c_ovl_tbl),
+ ARRAY_SIZE(nord_qmp_pcie_pll_d_ovl_tbl),
+};
+
+static const struct qmp_phy_init_tbl * const nord_qmp_pcie_pll_abcd[] = {
+ nord_qmp_pcie_pll_a_ovl_tbl, nord_qmp_pcie_pll_b_ovl_tbl,
+ nord_qmp_pcie_pll_c_ovl_tbl, nord_qmp_pcie_pll_d_ovl_tbl,
+};
+
+static const int nord_qmp_pcie_pll_abcd_nums[] = {
+ ARRAY_SIZE(nord_qmp_pcie_pll_a_ovl_tbl), ARRAY_SIZE(nord_qmp_pcie_pll_b_ovl_tbl),
+ ARRAY_SIZE(nord_qmp_pcie_pll_c_ovl_tbl), ARRAY_SIZE(nord_qmp_pcie_pll_d_ovl_tbl),
+};
+
+static const char * const nord_pciephy_port_c_reg_l[] = { "port_c" };
+
+static const char * const nord_pciephy_port_d_reg_l[] = { "port_d" };
+
+static const char * const nord_pciephy_port_cd_reg_l[] = { "port_c", "port_d" };
+
+static const char * const nord_pciephy_port_bcd_reg_l[] = {
+ "port_b", "port_c", "port_d",
+};
+
+static const char * const nord_pciephy_port_abcd_reg_l[] = {
+ "port_a", "port_b", "port_c", "port_d",
+};
+
+static const char * const nord_pciephy_port_a_reset_l[] = { "port_a" };
+
+static const char * const nord_pciephy_port_b_reset_l[] = { "port_b" };
+
+static const char * const nord_pciephy_port_c_reset_l[] = { "port_c" };
+
+static const char * const nord_pciephy_port_d_reset_l[] = { "port_d" };
+
+static const char * const nord_pciephy_port_cd_reset_l[] = {
+ "port_c", "port_d",
+};
+
+static const char * const nord_pciephy_port_bcd_reset_l[] = {
+ "port_b", "port_c", "port_d",
+};
+
+static const char * const nord_pciephy_port_abcd_reset_l[] = {
+ "port_a", "port_b", "port_c", "port_d",
+};
+
+static const char * const nord_pciephy_port_c_nocsr_reset_l[] = {
+ "port_c_nocsr",
+};
+
+static const char * const nord_pciephy_port_d_nocsr_reset_l[] = {
+ "port_d_nocsr",
+};
+
+static const char * const nord_pciephy_port_cd_nocsr_reset_l[] = {
+ "port_c_nocsr", "port_d_nocsr",
+};
+
+static const char * const nord_pciephy_port_bcd_nocsr_reset_l[] = {
+ "port_b_nocsr", "port_c_nocsr", "port_d_nocsr",
+};
+
+static const char * const nord_pciephy_port_abcd_nocsr_reset_l[] = {
+ "port_a_nocsr", "port_b_nocsr", "port_c_nocsr", "port_d_nocsr",
+};
+
+static const char * const nord_pciephy_port_a_pd_l[] = { "port_a" };
+
+static const char * const nord_pciephy_port_b_pd_l[] = {
+ "port_a", "port_b",
+};
+
+static const char * const nord_pciephy_port_c_pd_l[] = {
+ "port_a", "port_c",
+};
+
+static const char * const nord_pciephy_port_d_pd_l[] = {
+ "port_a", "port_d",
+};
+
+static const char * const nord_pciephy_port_cd_pd_l[] = {
+ "port_a", "port_c", "port_d",
+};
+
+static const char * const nord_pciephy_port_bcd_pd_l[] = {
+ "port_a", "port_b", "port_c", "port_d",
+};
+
+static const char * const nord_pciephy_port_abcd_pd_l[] = {
+ "port_a", "port_b", "port_c", "port_d",
+};
+
+static const char * const nord_pciephy_port_a_clk_l[] = {
+ "aux_a", "cfg_ahb_a", "rchng_a", "ref",
+};
+
+static const char * const nord_pciephy_port_b_clk_l[] = {
+ "aux_a", "aux_b", "cfg_ahb_b", "rchng_b", "ref",
+};
+
+static const char * const nord_pciephy_port_c_clk_l[] = {
+ "aux_a", "aux_c", "cfg_ahb_c", "rchng_c", "ref",
+};
+
+static const char * const nord_pciephy_port_d_clk_l[] = {
+ "aux_a", "aux_d", "cfg_ahb_d", "rchng_d", "ref",
+};
+
+static const char * const nord_pciephy_port_cd_clk_l[] = {
+ "aux_a", "aux_c", "cfg_ahb_c", "rchng_c",
+ "aux_d", "cfg_ahb_d", "rchng_d", "ref",
+};
+
+static const char * const nord_pciephy_port_bcd_clk_l[] = {
+ "aux_a", "aux_b", "cfg_ahb_b", "rchng_b",
+ "aux_c", "cfg_ahb_c", "rchng_c",
+ "aux_d", "cfg_ahb_d", "rchng_d", "ref",
+};
+
+static const char * const nord_pciephy_port_abcd_clk_l[] = {
+ "aux_a", "cfg_ahb_a", "rchng_a",
+ "aux_b", "cfg_ahb_b", "rchng_b",
+ "aux_c", "cfg_ahb_c", "rchng_c",
+ "aux_d", "cfg_ahb_d", "rchng_d", "ref",
+};
+
+static const char * const nord_pciephy_port_a_pipeclk_l[] = { "pipe_a" };
+
+static const char * const nord_pciephy_port_b_pipeclk_l[] = { "pipe_b" };
+
+static const char * const nord_pciephy_port_c_pipeclk_l[] = { "pipe_c" };
+
+static const char * const nord_pciephy_port_d_pipeclk_l[] = { "pipe_d" };
+
+#define NORD_QMP_PCIE_PORT_CFG(_port, _reg_l, _pd_l, _reset_l, _nocsr_reset_l, _pipeclk_l, \
+ _port_split) \
+static const struct qmp_phy_cfg nord_qmp_pcie_port_##_port##_cfg = { \
+ .offsets = &nord_pcie_offsets_v6_40, \
+ .tbls = nord_qmp_pcie_tbls, \
+ .pll_tbls = nord_qmp_pcie_pll_##_port, \
+ .pll_nums = nord_qmp_pcie_pll_##_port##_nums, \
+ .port_split = _port_split, \
+ .reg_names = _reg_l, \
+ .num_regs = ARRAY_SIZE(_reg_l), \
+ .pd_names = _pd_l, \
+ .num_pds = ARRAY_SIZE(_pd_l), \
+ .reset_list = _reset_l, \
+ .num_resets = ARRAY_SIZE(_reset_l), \
+ .nocsr_reset_list = _nocsr_reset_l, \
+ .num_nocsr_resets = ARRAY_SIZE(_nocsr_reset_l), \
+ .vreg_list = nord_pciephy_port_##_port##_vreg_l, \
+ .num_vregs = ARRAY_SIZE(nord_pciephy_port_##_port##_vreg_l), \
+ .regs = nord_pciephy_v6_40_regs_layout, \
+ .phy_status = PHYSTATUS_4_20, \
+ .clk_list = nord_pciephy_port_##_port##_clk_l, \
+ .num_clks = ARRAY_SIZE(nord_pciephy_port_##_port##_clk_l), \
+ .pipe_clk_list = _pipeclk_l, \
+ .num_pipe_clks = 1, \
+}
+
+NORD_QMP_PCIE_PORT_CFG(a, pciephy_port_a_reg_l, nord_pciephy_port_a_pd_l,
+ nord_pciephy_port_a_reset_l, pciephy_port_a_nocsr_reset_l,
+ nord_pciephy_port_a_pipeclk_l, true);
+NORD_QMP_PCIE_PORT_CFG(b, pciephy_port_b_reg_l, nord_pciephy_port_b_pd_l,
+ nord_pciephy_port_b_reset_l, pciephy_port_b_nocsr_reset_l,
+ nord_pciephy_port_b_pipeclk_l, true);
+NORD_QMP_PCIE_PORT_CFG(c, nord_pciephy_port_c_reg_l, nord_pciephy_port_c_pd_l,
+ nord_pciephy_port_c_reset_l, nord_pciephy_port_c_nocsr_reset_l,
+ nord_pciephy_port_c_pipeclk_l, true);
+NORD_QMP_PCIE_PORT_CFG(d, nord_pciephy_port_d_reg_l, nord_pciephy_port_d_pd_l,
+ nord_pciephy_port_d_reset_l, nord_pciephy_port_d_nocsr_reset_l,
+ nord_pciephy_port_d_pipeclk_l, true);
+NORD_QMP_PCIE_PORT_CFG(cd, nord_pciephy_port_cd_reg_l, nord_pciephy_port_cd_pd_l,
+ nord_pciephy_port_cd_reset_l, nord_pciephy_port_cd_nocsr_reset_l,
+ nord_pciephy_port_c_pipeclk_l, true);
+NORD_QMP_PCIE_PORT_CFG(bcd, nord_pciephy_port_bcd_reg_l, nord_pciephy_port_bcd_pd_l,
+ nord_pciephy_port_bcd_reset_l, nord_pciephy_port_bcd_nocsr_reset_l,
+ nord_pciephy_port_b_pipeclk_l, true);
+NORD_QMP_PCIE_PORT_CFG(abcd, nord_pciephy_port_abcd_reg_l, nord_pciephy_port_abcd_pd_l,
+ nord_pciephy_port_abcd_reset_l, nord_pciephy_port_abcd_nocsr_reset_l,
+ nord_pciephy_port_a_pipeclk_l, false);
+
+static const struct qmp_phy_cfg * const nord_qmp_gen5x16_mode_x16_cfgs[] = {
+ &nord_qmp_pcie_port_abcd_cfg,
+};
+
+static const struct qmp_phy_cfg * const nord_qmp_gen5x16_mode_x8x8_cfgs[] = {
+ &nord_qmp_pcie_port_a_cfg,
+ &nord_qmp_pcie_port_bcd_cfg,
+};
+
+static const struct qmp_phy_cfg * const nord_qmp_gen5x16_mode_x8x4x4_cfgs[] = {
+ &nord_qmp_pcie_port_a_cfg,
+ &nord_qmp_pcie_port_b_cfg,
+ &nord_qmp_pcie_port_cd_cfg,
+};
+
+static const struct qmp_phy_cfg * const nord_qmp_gen5x16_mode_x8x4x2x2_cfgs[] = {
+ &nord_qmp_pcie_port_a_cfg,
+ &nord_qmp_pcie_port_b_cfg,
+ &nord_qmp_pcie_port_c_cfg,
+ &nord_qmp_pcie_port_d_cfg,
+};
+
+static const struct qmp_pcie_link_mode_cfg nord_qmp_gen5x16_mode_cfgs[] = {
+ [QMP_PCIE_NORD_MODE_X16] = {
+ .cfgs = nord_qmp_gen5x16_mode_x16_cfgs,
+ .num_phys = ARRAY_SIZE(nord_qmp_gen5x16_mode_x16_cfgs),
+ },
+ [QMP_PCIE_NORD_MODE_X8_X8] = {
+ .cfgs = nord_qmp_gen5x16_mode_x8x8_cfgs,
+ .num_phys = ARRAY_SIZE(nord_qmp_gen5x16_mode_x8x8_cfgs),
+ },
+ [QMP_PCIE_NORD_MODE_X8_X4_X4] = {
+ .cfgs = nord_qmp_gen5x16_mode_x8x4x4_cfgs,
+ .num_phys = ARRAY_SIZE(nord_qmp_gen5x16_mode_x8x4x4_cfgs),
+ },
+ [QMP_PCIE_NORD_MODE_X8_X4_X2_X2] = {
+ .cfgs = nord_qmp_gen5x16_mode_x8x4x2x2_cfgs,
+ .num_phys = ARRAY_SIZE(nord_qmp_gen5x16_mode_x8x4x2x2_cfgs),
+ },
+};
+
+static const struct qmp_pcie_match_data nord_qmp_gen5x16_match_data = {
+ .mode_cfgs = nord_qmp_gen5x16_mode_cfgs,
+ .num_modes = ARRAY_SIZE(nord_qmp_gen5x16_mode_cfgs),
+};
+
+static void qmp_pcie_init_port_registers(struct qmp_pcie *qmp)
+{
+ const struct qmp_phy_cfg *cfg = qmp->cfg;
+ const struct qmp_pcie_offsets *offs = cfg->offsets;
+ const struct qmp_phy_cfg_tbls *tbls = &cfg->tbls;
+ int i;
+
+ for (i = 0; i < cfg->num_regs; i++) {
+ void __iomem *port = qmp->base[i];
+
+ if (cfg->port_split)
+ qmp_configure(qmp->dev, port + offs->txrxz,
+ tbls->port_split_pre_pwrup, tbls->port_split_pre_pwrup_num);
+
+ writel(SW_PWRDN | REFCLK_DRV_DSBL,
+ port + offs->pcs + cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]);
+
+ if (cfg->port_split)
+ qphy_setbits(port + offs->pll, cfg->regs[QPHY_COM_BIAS_EN_CLKBUFLR_EN],
+ COM_BIAS_EN_MUX | COM_BIAS_EN);
+
+ qmp_configure(qmp->dev, port + offs->pll,
+ tbls->pll_common, tbls->pll_common_num);
+ qmp_configure(qmp->dev, port + offs->pll,
+ cfg->pll_tbls[i], cfg->pll_nums[i]);
+ qmp_configure(qmp->dev, port + offs->txrxz,
+ tbls->txrxz, tbls->txrxz_num);
+ qmp_configure(qmp->dev, port + offs->pcs,
+ tbls->pcs, tbls->pcs_num);
+ qmp_configure(qmp->dev, port + offs->pcs_lanez,
+ tbls->pcs_lanez, tbls->pcs_lanez_num);
+
+ if (cfg->port_split)
+ qmp_configure(qmp->dev, port + offs->txrxz,
+ tbls->port_split_post_cfg, tbls->port_split_post_cfg_num);
+ }
+}
+
static int qmp_pcie_pd_power_on(struct qmp_pcie *qmp)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
@@ -268,32 +999,76 @@ static int qmp_pcie_init(struct phy *phy)
{
struct qmp_pcie *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
- int ret;
+ const struct qmp_pcie_offsets *offs = cfg->offsets;
+ int i, ret;
ret = qmp_pcie_pd_power_on(qmp);
if (ret)
return ret;
+ /*
+ * We can skip PHY initialization if all of the following conditions
+ * are met:
+ * 1. The PHY supports the nocsr_reset that preserves the PHY config.
+ * 2. The PHY was started (and not powered down again) by the
+ * bootloader, with all of the expected bits set correctly.
+ * In this case, we can continue without having the init sequence
+ * defined in the driver.
+ */
+ qmp->skip_init = qmp->cfg->num_nocsr_resets;
+ for (i = 0; qmp->skip_init && i < cfg->num_regs; i++) {
+ void __iomem *port = qmp->base[i];
+
+ if (!qphy_checkbits(port + offs->pcs, cfg->regs[QPHY_PCS_START_CONTROL],
+ SERDES_START | PCS_START) ||
+ !qphy_checkbits(port + offs->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ SW_PWRDN | REFCLK_DRV_DSBL))
+ qmp->skip_init = false;
+ }
+
ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
if (ret) {
dev_err(qmp->dev, "failed to enable regulators: %d\n", ret);
goto err_pd_power_off;
}
+ /*
+ * Toggle BCR reset for a PHY that doesn't support the nocsr_reset,
+ * or has not been initialized yet.
+ */
+ if (!qmp->skip_init) {
+ ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+ if (ret) {
+ dev_err(qmp->dev, "reset assert failed: %d\n", ret);
+ goto err_disable_regulators;
+ }
+ }
+
ret = reset_control_bulk_assert(qmp->cfg->num_nocsr_resets, qmp->nocsr_resets);
if (ret) {
dev_err(qmp->dev, "no-csr reset assert failed: %d\n", ret);
- goto err_disable_regulators;
+ goto err_assert_reset;
}
usleep_range(200, 300);
+ if (!qmp->skip_init) {
+ ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets);
+ if (ret) {
+ dev_err(qmp->dev, "reset deassert failed: %d\n", ret);
+ goto err_assert_reset;
+ }
+ }
+
ret = clk_bulk_prepare_enable(qmp->cfg->num_clks, qmp->clks);
if (ret)
- goto err_disable_regulators;
+ goto err_assert_reset;
return 0;
+err_assert_reset:
+ if (!qmp->skip_init)
+ reset_control_bulk_assert(cfg->num_resets, qmp->resets);
err_disable_regulators:
regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
err_pd_power_off:
@@ -307,7 +1082,8 @@ static int qmp_pcie_exit(struct phy *phy)
struct qmp_pcie *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
- reset_control_bulk_assert(qmp->cfg->num_nocsr_resets, qmp->nocsr_resets);
+ if (qmp->nocsr_resets)
+ reset_control_bulk_assert(qmp->cfg->num_nocsr_resets, qmp->nocsr_resets);
clk_bulk_disable_unprepare(qmp->cfg->num_clks, qmp->clks);
regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
@@ -335,6 +1111,20 @@ static int qmp_pcie_power_on(struct phy *phy)
goto err_disable_pipe_clk;
}
+ if (cfg->pll_tbls && !qmp->skip_init) {
+ qmp_pcie_init_port_registers(qmp);
+
+ for (i = 0; i < cfg->num_regs; i++) {
+ qphy_clrbits(qmp->base[i] + offs->pcs,
+ cfg->regs[QPHY_PCS_SW_RESET], SW_RESET);
+ qphy_setbits(qmp->base[i] + offs->pcs,
+ cfg->regs[QPHY_PCS_START_CONTROL],
+ SERDES_START | PCS_START);
+ }
+
+ usleep_range(2000, 2500);
+ }
+
for (i = 0; i < cfg->num_regs; i++) {
status = qmp->base[i] + offs->pcs + cfg->regs[QPHY_PCS_STATUS];
ret = readl_poll_timeout(status, val, !(val & cfg->phy_status), 200,
@@ -398,45 +1188,39 @@ static const struct phy_ops qmp_pcie_phy_ops = {
static void qmp_pcie_pd_detach(void *data)
{
struct qmp_pcie *qmp = data;
- const struct qmp_phy_cfg *cfg = qmp->cfg;
int i;
- for (i = 0; i < cfg->num_pds; i++) {
+ for (i = 0; i < qmp->cfg->num_pds; i++) {
if (!IS_ERR_OR_NULL(qmp->pd_devs[i]))
dev_pm_domain_detach(qmp->pd_devs[i], true);
}
}
-static int qmp_pcie_pd_init(struct qmp_pcie *qmp)
+static int qmp_pcie_pd_init(struct qmp_pcie *qmp, struct phy *phy)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
- struct device *dev = qmp->dev;
+ struct device *pd_dev = &phy->dev;
int i, ret;
if (!cfg->num_pds)
return 0;
- qmp->pd_devs = devm_kcalloc(dev, cfg->num_pds, sizeof(*qmp->pd_devs),
+ qmp->pd_devs = devm_kcalloc(pd_dev, cfg->num_pds, sizeof(*qmp->pd_devs),
GFP_KERNEL);
if (!qmp->pd_devs)
return -ENOMEM;
+ ret = devm_add_action_or_reset(pd_dev, qmp_pcie_pd_detach, qmp);
+ if (ret)
+ return ret;
+
for (i = 0; i < cfg->num_pds; i++) {
- qmp->pd_devs[i] = dev_pm_domain_attach_by_name(dev,
- cfg->pd_names[i]);
- if (IS_ERR_OR_NULL(qmp->pd_devs[i])) {
- ret = PTR_ERR(qmp->pd_devs[i]) ? : -ENODATA;
- goto err_detach;
- }
+ qmp->pd_devs[i] = dev_pm_domain_attach_by_name(pd_dev, cfg->pd_names[i]);
+ if (IS_ERR_OR_NULL(qmp->pd_devs[i]))
+ return PTR_ERR_OR_ZERO(qmp->pd_devs[i]) ? : -ENODATA;
}
- return devm_add_action_or_reset(dev, qmp_pcie_pd_detach, qmp);
-
-err_detach:
- while (--i >= 0)
- dev_pm_domain_detach(qmp->pd_devs[i], false);
-
- return ret;
+ return 0;
}
static int qmp_pcie_vreg_init(struct qmp_pcie *qmp)
@@ -462,20 +1246,37 @@ static int qmp_pcie_reset_init(struct qmp_pcie *qmp)
struct device *dev = qmp->dev;
int i, ret;
- qmp->nocsr_resets = devm_kcalloc(dev, cfg->num_nocsr_resets,
- sizeof(*qmp->nocsr_resets),
- GFP_KERNEL);
- if (!qmp->nocsr_resets)
- return -ENOMEM;
+ if (cfg->num_resets) {
+ qmp->resets = devm_kcalloc(dev, cfg->num_resets,
+ sizeof(*qmp->resets), GFP_KERNEL);
+ if (!qmp->resets)
+ return -ENOMEM;
- for (i = 0; i < cfg->num_nocsr_resets; i++)
- qmp->nocsr_resets[i].id = cfg->nocsr_reset_list[i];
+ 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_nocsr_resets,
- qmp->nocsr_resets);
- if (ret)
- return dev_err_probe(dev, ret, "failed to get no-csr resets\n");
+ 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");
+ }
+
+ if (cfg->num_nocsr_resets) {
+ qmp->nocsr_resets = devm_kcalloc(dev, cfg->num_nocsr_resets,
+ sizeof(*qmp->nocsr_resets),
+ GFP_KERNEL);
+ if (!qmp->nocsr_resets)
+ return -ENOMEM;
+
+ for (i = 0; i < cfg->num_nocsr_resets; i++)
+ qmp->nocsr_resets[i].id = cfg->nocsr_reset_list[i];
+
+ ret = devm_reset_control_bulk_get_exclusive(dev,
+ cfg->num_nocsr_resets,
+ qmp->nocsr_resets);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get no-csr resets\n");
+ }
return 0;
}
@@ -555,6 +1356,9 @@ static int qmp_pcie_multiphy_register_clocks(struct device *dev,
int i, ret;
num_pipe_outputs = of_property_count_strings(np, "clock-output-names");
+ if (num_pipe_outputs < 0)
+ return dev_err_probe(dev, num_pipe_outputs,
+ "failed to read clock-output-names\n");
qmp_data->num_pipe_outputs = num_pipe_outputs;
qmp_data->pipe_out_clks = devm_kcalloc(dev, num_pipe_outputs,
@@ -630,6 +1434,7 @@ static struct phy *qmp_pcie_multiphy_xlate(struct device *dev,
static int qmp_pcie_probe_phy(struct qmp_pcie *qmp, struct device_node *np,
struct phy **out_phy)
{
+ struct phy *phy;
int ret;
ret = qmp_pcie_get_mmio(qmp);
@@ -648,15 +1453,16 @@ static int qmp_pcie_probe_phy(struct qmp_pcie *qmp, struct device_node *np,
if (ret)
return ret;
- ret = qmp_pcie_pd_init(qmp);
+ phy = devm_phy_create(qmp->dev, np, &qmp_pcie_phy_ops);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ ret = qmp_pcie_pd_init(qmp, phy);
if (ret)
return ret;
- *out_phy = devm_phy_create(qmp->dev, np, &qmp_pcie_phy_ops);
- if (IS_ERR(*out_phy))
- return PTR_ERR(*out_phy);
-
- phy_set_drvdata(*out_phy, qmp);
+ phy_set_drvdata(phy, qmp);
+ *out_phy = phy;
return 0;
}
@@ -674,14 +1480,13 @@ static int qmp_pcie_multiphy_probe(struct platform_device *pdev)
int ret;
qmp_data = devm_kzalloc(dev, sizeof(*qmp_data), GFP_KERNEL);
+ if (!qmp_data)
+ return -ENOMEM;
match_data = of_device_get_match_data(dev);
if (!match_data)
return -EINVAL;
- if (!qmp_data)
- return -ENOMEM;
-
ret = qmp_pcie_read_link_mode(dev, &link_mode);
if (ret)
return dev_err_probe(dev, ret, "failed to read qcom,link-mode\n");
@@ -725,6 +1530,10 @@ static const struct of_device_id qmp_pcie_multiphy_of_match_table[] = {
.compatible = "qcom,glymur-qmp-gen5x8-pcie-phy",
.data = &glymur_qmp_gen5x8_match_data,
},
+ {
+ .compatible = "qcom,nord-qmp-gen5x16-pcie-phy",
+ .data = &nord_qmp_gen5x16_match_data,
+ },
{ }
};
MODULE_DEVICE_TABLE(of, qmp_pcie_multiphy_of_match_table);
@@ -739,5 +1548,5 @@ static struct platform_driver qmp_pcie_multiphy_driver = {
module_platform_driver(qmp_pcie_multiphy_driver);
MODULE_AUTHOR("Qiang Yu <qiang.yu@oss.qualcomm.com>");
-MODULE_DESCRIPTION("Qualcomm QMP PCIe Multi-PHY driver for Glymur");
+MODULE_DESCRIPTION("Qualcomm QMP PCIe Multi-PHY driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
index da83358c97ea..9cfabf474fd6 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
@@ -2417,6 +2417,50 @@ static const struct qmp_phy_init_tbl sdx65_qmp_pcie_pcs_lane1_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_LANE1_INSIG_MX_CTRL2, 0x00),
};
+static const struct qmp_phy_init_tbl shikra_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_ENABLE1, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TRIM, 0xf),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_EN, 0x1),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER2, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x6),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_IVCO, 0xf),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x1),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORE_CLK_EN, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORECLK_DIV, 0xa),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_RESETSM_CNTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0x9),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0xa),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x3),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0xd),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x35),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYS_CLK_CTRL, 0x2),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_BUF_ENABLE, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE0, 0x4),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE0, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN1_MODE0, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CTRL_BY_PSM, 0x1),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0xa),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_EN_CENTER, 0x1),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER2, 0x1),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER1, 0x2),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER2, 0x0),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE1, 0x2f),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE2, 0x19),
+ QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_EP_DIV, 0x19),
+};
+
static const struct qmp_phy_init_tbl sm8450_qmp_gen3_pcie_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_SELECT, 0x34),
@@ -4044,38 +4088,6 @@ struct qmp_pcie {
struct clk_fixed_rate aux_clk_fixed;
};
-static bool qphy_checkbits(const void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- return (reg & val) == val;
-}
-
-static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg |= val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
-static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg &= ~val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
/* list of clocks required by phy */
static const char * const qmp_pciephy_clk_l[] = {
"aux", "cfg_ahb", "ref", "refgen", "rchng", "phy_aux",
@@ -4643,6 +4655,32 @@ static const struct qmp_phy_cfg sdm845_qhp_pciephy_cfg = {
.phy_status = PHYSTATUS,
};
+static const struct qmp_phy_cfg shikra_pciephy_cfg = {
+ .lanes = 1,
+
+ .offsets = &qmp_pcie_offsets_v2,
+
+ .tbls = {
+ .serdes = shikra_pcie_serdes_tbl,
+ .serdes_num = ARRAY_SIZE(shikra_pcie_serdes_tbl),
+ .tx = qcs615_pcie_tx_tbl,
+ .tx_num = ARRAY_SIZE(qcs615_pcie_tx_tbl),
+ .rx = qcs615_pcie_rx_tbl,
+ .rx_num = ARRAY_SIZE(qcs615_pcie_rx_tbl),
+ .pcs = qcs615_pcie_pcs_tbl,
+ .pcs_num = ARRAY_SIZE(qcs615_pcie_pcs_tbl),
+ },
+ .reset_list = sdm845_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = pciephy_v2_regs_layout,
+
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+ .phy_status = PHYSTATUS,
+};
+
+
static const struct qmp_phy_cfg sm8250_qmp_gen3x1_pciephy_cfg = {
.lanes = 1,
@@ -6445,6 +6483,9 @@ static const struct of_device_id qmp_pcie_of_match_table[] = {
.compatible = "qcom,sdx65-qmp-gen4x2-pcie-phy",
.data = &sdx65_qmp_pciephy_cfg,
}, {
+ .compatible = "qcom,shikra-qmp-gen2x1-pcie-phy",
+ .data = &shikra_pciephy_cfg,
+ }, {
.compatible = "qcom,sm8150-qmp-gen3x1-pcie-phy",
.data = &sm8250_qmp_gen3x1_pciephy_cfg,
}, {
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6_40.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6_40.h
new file mode 100644
index 000000000000..479f6d39d878
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6_40.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_PCIE_V6_40_H_
+#define QCOM_PHY_QMP_PCS_PCIE_V6_40_H_
+
+/* PCS COM — power, start, clocks */
+#define QPHY_PCIE_V6_40_PCS_COM_POWER_STATE_CONFIG2 0x06c
+#define QPHY_PCIE_V6_40_PCS_COM_POWER_STATE_CONFIG5 0x078
+#define QPHY_PCIE_V6_40_PCS_COM_POWER_STATE_CONFIG6 0x07c
+#define QPHY_PCIE_V6_40_PCS_COM_POWER_STATE_CONFIG7 0x080
+#define QPHY_PCIE_V6_40_PCS_COM_MULTIPHY_CONFIG2 0x088
+#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG2 0x0a0
+#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG3 0x0a4
+#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG5 0x0ac
+#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG6 0x0b0
+#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG7 0x0b4
+#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG8 0x0b8
+#define QPHY_PCIE_V6_40_PCS_COM_PCS_TX_RX_CONFIG10 0x0c0
+#define QPHY_PCIE_V6_40_PCS_COM_ENDPOINT_REFCLK_DRIVE 0x0c8
+#define QPHY_PCIE_V6_40_PCS_COM_RX_SIGDET_LVL 0x0ec
+#define QPHY_PCIE_V6_40_PCS_COM_DIVTX_CLK_DCC_CAL_CONFIG2 0x0f8
+#define QPHY_PCIE_V6_40_PCS_COM_DIVTX_CLK_DCC_CAL_CONFIG3 0x0fc
+#define QPHY_PCIE_V6_40_PCS_COM_DIVTX_CLK_DCC_CAL_CONFIG4 0x100
+#define QPHY_PCIE_V6_40_PCS_COM_OSC_DTCT_ACTIONS 0x194
+#define QPHY_PCIE_V6_40_PCS_COM_LOCK_DETECT_CONFIG1 0x1bc
+#define QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG1 0x1dc
+#define QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG2 0x1e0
+#define QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG3 0x1e4
+#define QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG7 0x1f4
+#define QPHY_PCIE_V6_40_PCS_COM_ALIGN_DETECT_CONFIG8 0x1f8
+
+/* PCS COM — EQ config */
+#define QPHY_PCIE_V6_40_PCS_COM_EQ_CONFIG1 0x380
+#define QPHY_PCIE_V6_40_PCS_COM_EQ_CONFIG2 0x384
+#define QPHY_PCIE_V6_40_PCS_COM_G345_EQ_CONFIG1 0x38c
+#define QPHY_PCIE_V6_40_PCS_COM_G345_EQ_CONFIG3 0x394
+#define QPHY_PCIE_V6_40_PCS_COM_G3_EQ_CONFIG1 0x3a4
+#define QPHY_PCIE_V6_40_PCS_COM_G3_EQ_CONFIG5 0x3b4
+#define QPHY_PCIE_V6_40_PCS_COM_G3_EQ_CONFIG7 0x3bc
+#define QPHY_PCIE_V6_40_PCS_COM_G4_EQ_CONFIG1 0x3c8
+#define QPHY_PCIE_V6_40_PCS_COM_G4_EQ_CONFIG5 0x3d8
+#define QPHY_PCIE_V6_40_PCS_COM_G4_EQ_CONFIG7 0x3e0
+#define QPHY_PCIE_V6_40_PCS_COM_G5_EQ_CONFIG1 0x3ec
+#define QPHY_PCIE_V6_40_PCS_COM_G5_EQ_CONFIG5 0x3fc
+#define QPHY_PCIE_V6_40_PCS_COM_G5_EQ_CONFIG7 0x404
+#define QPHY_PCIE_V6_40_PCS_COM_TEST_CONTROL1 0x468
+
+/* PCS LANEZ — output signal mux control */
+#define QPHY_PCIE_V6_40_PCS_LANEZ_OUTSIG_MX_CTRL2 0x0e0
+#define QPHY_PCIE_V6_40_PCS_LANEZ_OUTSIG_MX_CTRL5 0x0f8
+
+#endif /* QCOM_PHY_QMP_PCS_PCIE_V6_40_H_ */
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6_40.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6_40.h
new file mode 100644
index 000000000000..c7ac7b3bd97e
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6_40.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef QCOM_PHY_QMP_PCS_V6_40_H_
+#define QCOM_PHY_QMP_PCS_V6_40_H_
+
+#define QPHY_V6_40_PCS_STATUS1 0x010
+#define QPHY_V6_40_PCS_START_CONTROL 0x05c
+#define QPHY_V6_40_PCS_SW_RESET 0x060
+#define QPHY_V6_40_PCS_POWER_DOWN_CONTROL 0x064
+
+#endif /* QCOM_PHY_QMP_PCS_V6_40_H_ */
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v8_50.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v8_50.h
index 325c127e8eb7..f094517ae3c1 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v8_50.h
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v8_50.h
@@ -7,7 +7,7 @@
#define QCOM_PHY_QMP_PCS_V8_50_H_
#define QPHY_V8_50_PCS_STATUS1 0x010
-#define QPHY_V8_50_PCS_START_CONTROL 0x05c
-#define QPHY_V8_50_PCS_POWER_DOWN_CONTROL 0x64
+#define QPHY_V8_50_PCS_START_CONTROL 0x05c
+#define QPHY_V8_50_PCS_POWER_DOWN_CONTROL 0x64
#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-pcie-v6_40.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-pcie-v6_40.h
new file mode 100644
index 000000000000..0eeab0693e9b
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-pcie-v6_40.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_COM_PCIE_V6_40_H_
+#define QCOM_PHY_QMP_QSERDES_COM_PCIE_V6_40_H_
+
+#define QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE1_MODE1 0x000
+#define QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE2_MODE1 0x004
+#define QSERDES_PCIE_V6_40_COM_CLK_EP_DIV_MODE1 0x00c
+#define QSERDES_PCIE_V6_40_COM_CP_CTRL_MODE1 0x010
+#define QSERDES_PCIE_V6_40_COM_PLL_RCTRL_MODE1 0x014
+#define QSERDES_PCIE_V6_40_COM_PLL_CCTRL_MODE1 0x018
+#define QSERDES_PCIE_V6_40_COM_CORECLK_DIV_MODE1 0x01c
+#define QSERDES_PCIE_V6_40_COM_LOCK_CMP1_MODE1 0x020
+#define QSERDES_PCIE_V6_40_COM_LOCK_CMP2_MODE1 0x024
+#define QSERDES_PCIE_V6_40_COM_DEC_START_MODE1 0x028
+#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START1_MODE1 0x030
+#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START2_MODE1 0x034
+#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START3_MODE1 0x038
+#define QSERDES_PCIE_V6_40_COM_HSCLK_SEL_1 0x03c
+#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x050
+#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x054
+#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x058
+#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x05c
+#define QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE1_MODE0 0x060
+#define QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE2_MODE0 0x064
+#define QSERDES_PCIE_V6_40_COM_CLK_EP_DIV_MODE0 0x06c
+#define QSERDES_PCIE_V6_40_COM_CP_CTRL_MODE0 0x070
+#define QSERDES_PCIE_V6_40_COM_PLL_RCTRL_MODE0 0x074
+#define QSERDES_PCIE_V6_40_COM_PLL_CCTRL_MODE0 0x078
+#define QSERDES_PCIE_V6_40_COM_CORECLK_DIV_MODE0 0x07c
+#define QSERDES_PCIE_V6_40_COM_LOCK_CMP1_MODE0 0x080
+#define QSERDES_PCIE_V6_40_COM_LOCK_CMP2_MODE0 0x084
+#define QSERDES_PCIE_V6_40_COM_DEC_START_MODE0 0x088
+#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START1_MODE0 0x090
+#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START2_MODE0 0x094
+#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START3_MODE0 0x098
+#define QSERDES_PCIE_V6_40_COM_HSCLK_HS_SWITCH_SEL_1 0x09c
+#define QSERDES_PCIE_V6_40_COM_BG_TIMER 0x0bc
+#define QSERDES_PCIE_V6_40_COM_SSC_EN_CENTER 0x0c0
+#define QSERDES_PCIE_V6_40_COM_SSC_PER1 0x0cc
+#define QSERDES_PCIE_V6_40_COM_SSC_PER2 0x0d0
+#define QSERDES_PCIE_V6_40_COM_POST_DIV_MUX 0x0d8
+#define QSERDES_PCIE_V6_40_COM_BIAS_EN_CLKBUFLR_EN 0x0dc
+#define QSERDES_PCIE_V6_40_COM_CLK_ENABLE1 0x0e0
+#define QSERDES_PCIE_V6_40_COM_SYS_CLK_CTRL 0x0e4
+#define QSERDES_PCIE_V6_40_COM_PLL_IVCO 0x0f4
+#define QSERDES_PCIE_V6_40_COM_CMN_IETRIM 0x0fc
+#define QSERDES_PCIE_V6_40_COM_SYSCLK_EN_SEL 0x110
+#define QSERDES_PCIE_V6_40_COM_LOCK_CMP_EN 0x120
+#define QSERDES_PCIE_V6_40_COM_LOCK_CMP_CFG 0x124
+#define QSERDES_PCIE_V6_40_COM_VCO_TUNE_CTRL 0x13c
+#define QSERDES_PCIE_V6_40_COM_VCO_TUNE_MAP 0x140
+#define QSERDES_PCIE_V6_40_COM_CLK_SELECT 0x164
+#define QSERDES_PCIE_V6_40_COM_CORE_CLK_EN 0x170
+#define QSERDES_PCIE_V6_40_COM_CMN_CONFIG_1 0x174
+#define QSERDES_PCIE_V6_40_COM_CMN_MISC1 0x188
+#define QSERDES_PCIE_V6_40_COM_CMN_MODE 0x18c
+#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_HSCLK_SEL_1 0x1a0
+#define QSERDES_PCIE_V6_40_COM_VCO_DC_LEVEL_CTRL 0x19c
+#define QSERDES_PCIE_V6_40_COM_HSCLK_SEL_2 0x21c
+#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE1_MODE2 0x220
+#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_CMP_CODE2_MODE2 0x224
+#define QSERDES_PCIE_V6_40_COM_HSCLK_HS_SWITCH_SEL_2 0x228
+#define QSERDES_PCIE_V6_40_COM_BIN_VCOCAL_HSCLK_SEL_2 0x230
+#define QSERDES_PCIE_V6_40_COM_CMN_CONFIG_2 0x22c
+#define QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE1_MODE2 0x1cc
+#define QSERDES_PCIE_V6_40_COM_SSC_STEP_SIZE2_MODE2 0x1d0
+#define QSERDES_PCIE_V6_40_COM_CP_CTRL_MODE2 0x1dc
+#define QSERDES_PCIE_V6_40_COM_PLL_RCTRL_MODE2 0x1e0
+#define QSERDES_PCIE_V6_40_COM_PLL_CCTRL_MODE2 0x1e4
+#define QSERDES_PCIE_V6_40_COM_CORECLK_DIV_MODE2 0x1e8
+#define QSERDES_PCIE_V6_40_COM_LOCK_CMP1_MODE2 0x1ec
+#define QSERDES_PCIE_V6_40_COM_LOCK_CMP2_MODE2 0x1f0
+#define QSERDES_PCIE_V6_40_COM_DEC_START_MODE2 0x1f4
+#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START1_MODE2 0x1fc
+#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START2_MODE2 0x200
+#define QSERDES_PCIE_V6_40_COM_DIV_FRAC_START3_MODE2 0x204
+#define QSERDES_PCIE_V6_40_COM_DCC_CAL_1 0x2d8
+#define QSERDES_PCIE_V6_40_COM_DCC_CAL_2 0x2dc
+#define QSERDES_PCIE_V6_40_COM_DCC_CAL_3 0x2e0
+#define QSERDES_PCIE_V6_40_COM_PSM_CAL_EN 0x2f0
+#define QSERDES_PCIE_V6_40_COM_CLK_FWD_CONFIG_1 0x2f4
+#define QSERDES_PCIE_V6_40_COM_IP_CTRL_AND_DP_SEL 0x2fc
+#define QSERDES_PCIE_V6_40_COM_PLL_SPARE_FOR_ECO 0x2b8
+#define QSERDES_PCIE_V6_40_COM_PLL_IVCO_MODE1 0x0f8
+#define QSERDES_PCIE_V6_40_COM_PLL_IVCO_MODE2 0x218
+#define QSERDES_PCIE_V6_40_COM_DCC_CAL_7 0x300
+#define QSERDES_PCIE_V6_40_COM_DCC_CAL_8 0x304
+#define QSERDES_PCIE_V6_40_COM_DCC_CAL_9 0x308
+
+#endif /* QCOM_PHY_QMP_QSERDES_COM_PCIE_V6_40_H_ */
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrxz-pcie-v6_40.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrxz-pcie-v6_40.h
new file mode 100644
index 000000000000..1de976a4d787
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrxz-pcie-v6_40.h
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_TXRXZ_PCIE_V6_40_H_
+#define QCOM_PHY_QMP_QSERDES_TXRXZ_PCIE_V6_40_H_
+
+/* Lane mode / LDO */
+#define QSERDES_PCIE_V6_40_TXRXZ_TOP_LDO_CODE_CTRL1 0x070
+#define QSERDES_PCIE_V6_40_TXRXZ_TOP_LDO_CODE_CTRL2 0x074
+#define QSERDES_PCIE_V6_40_TXRXZ_TOP_LDO_CODE_CTRL3 0x078
+#define QSERDES_PCIE_V6_40_TXRXZ_TOP_LDO_CODE_CTRL4 0x07c
+#define QSERDES_PCIE_V6_40_TXRXZ_LANE_MODE_1 0x094
+#define QSERDES_PCIE_V6_40_TXRXZ_LANE_MODE_2 0x098
+#define QSERDES_PCIE_V6_40_TXRXZ_LANE_MODE_3 0x09c
+#define QSERDES_PCIE_V6_40_TXRXZ_LANE_MODE_4 0x0a0
+#define QSERDES_PCIE_V6_40_TXRXZ_RESTRIM_VREF_SEL 0x0bc
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B0 0x0f4
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B1 0x0f8
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B2 0x0fc
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B3 0x100
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B4 0x104
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B5 0x108
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B6 0x10c
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE_0_1_B7 0x110
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B0 0x114
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B1 0x118
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B2 0x11c
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B3 0x120
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B4 0x124
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B5 0x128
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B6 0x12c
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE2_B7 0x130
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B0 0x134
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B1 0x138
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B2 0x13c
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B3 0x140
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B4 0x144
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B5 0x148
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B6 0x14c
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE3_B7 0x150
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B0 0x154
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B1 0x158
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B2 0x15c
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B3 0x160
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B4 0x164
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B5 0x168
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B6 0x16c
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_MODE_RATE4_B7 0x170
+
+/* VCO / KVCO calibration */
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE0 0x1b4
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE0 0x1b8
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE1 0x1bc
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE1 0x1c0
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE2 0x1c4
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE2 0x1c8
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE3 0x1cc
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE3 0x1d0
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT1_RATE4 0x1d4
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CTUNE_MEAS_CNT2_RATE4 0x1d8
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCTRL_RATE_0_1 0x1dc
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCTRL_RATE_2_3 0x1e0
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_INIT_RATE_0_1 0x1e8
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_INIT_RATE_2_3 0x1ec
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_INIT_RATE_4 0x1f0
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_CODE_OVRD_RATE0 0x1f4
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_CODE_OVRD_RATE1 0x1f8
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_CODE_OVRD_RATE2 0x1fc
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE0 0x220
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF2_RATE0 0x224
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE1 0x228
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF2_RATE1 0x22c
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE2 0x230
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF2_RATE2 0x234
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE3 0x238
+#define QSERDES_PCIE_V6_40_TXRXZ_KVCO_IDEAL_FREQ_DIFF1_RATE4 0x240
+#define QSERDES_PCIE_V6_40_TXRXZ_KP_CODE_OVRD_RATE_2_3 0x250
+#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE0 0x258
+#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE1 0x260
+#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE2 0x268
+#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE3 0x270
+#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_UPPER_FREQ_DIFF_BND1_RATE4 0x278
+#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE0 0x280
+#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE1 0x284
+#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE2 0x288
+#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE3 0x28c
+#define QSERDES_PCIE_V6_40_TXRXZ_KP_CAL_LOWER_FREQ_DIFF_BND_RATE4 0x290
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_SUMMER_CAL_SPD_MODE_RATE_0123 0x2a8
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_IVCM_CAL_CTRL2 0x2c8
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_IVCM_CAL_CTRL3 0x2cc
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_IVCM_CAL_CTRL4 0x2d0
+
+/* Signal detect */
+#define QSERDES_PCIE_V6_40_TXRXZ_SIGDET_ENABLES 0x31c
+#define QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE0 0x33c
+#define QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE1 0x340
+#define QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE2 0x344
+#define QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE3 0x348
+#define QSERDES_PCIE_V6_40_TXRXZ_FREQ_LOCK_DET_DLY_RATE4 0x34c
+
+/* CDR */
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOCK_CTRL 0x364
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE0 0x368
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE1 0x36c
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE2 0x370
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE3 0x374
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_FLL_RATE4 0x378
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE0 0x37c
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE1 0x380
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE2 0x384
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE3 0x388
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_CP_CUR_PLL_RATE4 0x38c
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_FLL_DIV_RATIO_RATE_0123 0x390
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_CCODE_RATE_01 0x398
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_CCODE_RATE_23 0x39c
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_CCODE_RATE4 0x3a0
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FAST_RATE_0_1 0x3a4
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FAST_RATE_2_3 0x3a8
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FAST_RATE4 0x3ac
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FLL_RATE_0_1 0x3b0
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FLL_RATE_2_3 0x3b4
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_FLL_RATE4 0x3b8
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_PLL_RATE_0_1 0x3bc
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_PLL_RATE_2_3 0x3c0
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_RCODE_PLL_RATE4 0x3c4
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CAP_CODE_RATE_0123 0x3c8
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_TYPE_CONFIG 0x3d0
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_EN_LOWFREQ 0x3d4
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_LOOP_FUNC_CTRL 0x3dc
+
+/* Rescode / residuals */
+#define QSERDES_PCIE_V6_40_TXRXZ_RES_CODE_LANE_OFFSET_RX 0x3f4
+
+/* VGA / GM calibration */
+#define QSERDES_PCIE_V6_40_TXRXZ_GM_CAL_RES_RATE0_1 0x404
+#define QSERDES_PCIE_V6_40_TXRXZ_GM_CAL_RES_RATE2_3 0x408
+#define QSERDES_PCIE_V6_40_TXRXZ_GM_CAL_RES_RATE4 0x40c
+
+/* RX equalizer */
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_EQU_ADAPTOR_CNTRL3 0x454
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_EQU_ADAPTOR_CNTRL4 0x458
+#define QSERDES_PCIE_V6_40_TXRXZ_RX_EQU_ADAPTOR_CNTRL5 0x45c
+#define QSERDES_PCIE_V6_40_TXRXZ_CTLE_POST_CAL_OFFSET_RATE_0_1_2 0x478
+#define QSERDES_PCIE_V6_40_TXRXZ_VGA_CAL_MAN_VAL_RATE0_1 0x490
+#define QSERDES_PCIE_V6_40_TXRXZ_VGA_CAL_MAN_VAL_RATE2_3 0x494
+#define QSERDES_PCIE_V6_40_TXRXZ_VGA_CAL_MAN_VAL_RATE4 0x498
+#define QSERDES_PCIE_V6_40_TXRXZ_VTHRESH_CAL_MAN_VAL_RATE4 0x4bc
+
+/* DFE */
+#define QSERDES_PCIE_V6_40_TXRXZ_DFE_TAP1_DAC_ENABLE 0x50c
+#define QSERDES_PCIE_V6_40_TXRXZ_DFE_TAP2_DAC_ENABLE 0x510
+#define QSERDES_PCIE_V6_40_TXRXZ_DFE_TAP345_DAC_ENABLE 0x514
+#define QSERDES_PCIE_V6_40_TXRXZ_DFE_TAP67_DAC_ENABLE 0x518
+#define QSERDES_PCIE_V6_40_TXRXZ_PHPRE_CTRL 0x51c
+#define QSERDES_PCIE_V6_40_TXRXZ_TX_ADAPT_POST_THRESH1 0x52c
+#define QSERDES_PCIE_V6_40_TXRXZ_TX_ADAPT_POST_THRESH2 0x530
+#define QSERDES_PCIE_V6_40_TXRXZ_TX_ADPT_CTRL 0x53c
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_IQTUNE_CTRL 0x548
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_IQTUNE_MAN_INDEX 0x550
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_IQTUNE_ANA_CTRL 0x584
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_IQTUNE_DIV2_CTRL_RATE0123 0x58c
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CAP_CODE_OVRD_MUXES 0x5ac
+#define QSERDES_PCIE_V6_40_TXRXZ_CDR_VCO_CAP_CODE_RATE4 0x3cc
+
+/* Clock buffer / LDO stall control (used by bifurcated pre/post-pwrup sequences) */
+#define QSERDES_PCIE_V6_40_TXRXZ_CLKBUF_ENABLE 0x04c
+#define QSERDES_PCIE_V6_40_TXRXZ_PRE_STALL_LDO_BOOST_EN 0x06c
+#define QSERDES_PCIE_V6_40_TXRXZ_LDO_TIMER_CTRL 0x0d8
+
+/* Restrim / VGA / signal-detect (v2-only) */
+#define QSERDES_PCIE_V6_40_TXRXZ_RESTRIM_CAL_CTRL 0x0b0
+#define QSERDES_PCIE_V6_40_TXRXZ_RESTRIM_POST_CAL_OFFSET 0x0b8
+#define QSERDES_PCIE_V6_40_TXRXZ_SIGDET_CNTRL 0x320
+#define QSERDES_PCIE_V6_40_TXRXZ_SIGDET_LVL 0x324
+#define QSERDES_PCIE_V6_40_TXRXZ_SIGDET_CAL_CTRL1 0x32c
+#define QSERDES_PCIE_V6_40_TXRXZ_VGA_CAL_CNTRL1 0x488
+
+/* BLW / VCO coarse-tune bands (v2-only) */
+#define QSERDES_PCIE_V6_40_TXRXZ_BLW_CTRL 0x594
+#define QSERDES_PCIE_V6_40_TXRXZ_BLW_MAN_VAL_RATE3 0x5a4
+#define QSERDES_PCIE_V6_40_TXRXZ_BLW_MAN_VAL_RATE4 0x5a8
+#define QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE0 0x5c4
+#define QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE1 0x5c8
+#define QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE2 0x5cc
+#define QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE3 0x5d0
+#define QSERDES_PCIE_V6_40_TXRXZ_VCO_CTUNE_UPPER_BND_RATE4 0x5d4
+
+/* Analog backup control (v2-only) */
+#define QSERDES_PCIE_V6_40_TXRXZ_DIG_BKUP_CTRL15 0x7e8
+#define QSERDES_PCIE_V6_40_TXRXZ_DIG_BKUP_CTRL16 0x7ec
+
+#endif /* QCOM_PHY_QMP_QSERDES_TXRXZ_PCIE_V6_40_H_ */
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
index d4aca22c181e..d15db9ab9a90 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
@@ -1211,30 +1211,6 @@ struct qmp_ufs {
u32 submode;
};
-static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg |= val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
-static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg &= ~val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
/* Regulator bulk data with load values for specific configurations */
static const struct regulator_bulk_data milos_ufsphy_vreg_l[] = {
{ .supply = "vdda-phy", .init_load_uA = 140120 },
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
index b0790bcf0bc8..6a9faa0ce3bb 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
@@ -1441,30 +1441,6 @@ struct qmp_usb {
struct clk_fixed_rate pipe_clk_fixed;
};
-static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg |= val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
-static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg &= ~val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
/* list of clocks required by phy */
static const char * const qmp_usb_phy_clk_l[] = {
"aux", "cfg_ahb", "ref", "com_aux",
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c
index e3465ab7c80e..548ec97373ac 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c
@@ -474,30 +474,6 @@ struct qmp_usbc {
enum typec_orientation orientation;
};
-static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg |= val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
-static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
-{
- u32 reg;
-
- reg = readl(base + offset);
- reg &= ~val;
- writel(reg, base + offset);
-
- /* ensure that above write is through */
- readl(base + offset);
-}
-
/* list of clocks required by phy */
static const char * const qmp_usbc_phy_clk_l[] = {
"aux", "cfg_ahb", "ref", "com_aux",
diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c
index 248550ef98ca..1f08b151d90a 100644
--- a/drivers/phy/realtek/phy-rtk-usb2.c
+++ b/drivers/phy/realtek/phy-rtk-usb2.c
@@ -1022,7 +1022,8 @@ static int rtk_usb2phy_probe(struct platform_device *pdev)
return -ENOMEM;
rtk_phy->dev = &pdev->dev;
- rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL);
+ rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*rtk_phy->phy_cfg),
+ GFP_KERNEL);
if (!rtk_phy->phy_cfg)
return -ENOMEM;
diff --git a/drivers/phy/realtek/phy-rtk-usb3.c b/drivers/phy/realtek/phy-rtk-usb3.c
index cce453686db2..4231a8aa0ba5 100644
--- a/drivers/phy/realtek/phy-rtk-usb3.c
+++ b/drivers/phy/realtek/phy-rtk-usb3.c
@@ -576,7 +576,8 @@ static int rtk_usb3phy_probe(struct platform_device *pdev)
return -ENOMEM;
rtk_phy->dev = &pdev->dev;
- rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL);
+ rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*rtk_phy->phy_cfg),
+ GFP_KERNEL);
if (!rtk_phy->phy_cfg)
return -ENOMEM;
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index 14698571b607..39759bb2fa1d 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -136,8 +136,10 @@ config PHY_ROCKCHIP_USBDP
tristate "Rockchip USBDP COMBO PHY Driver"
depends on ARCH_ROCKCHIP && OF
depends on TYPEC
+ depends on DRM || DRM=n
select GENERIC_PHY
select USB_COMMON
+ select DRM_AUX_BRIDGE if DRM_BRIDGE
help
Enable this to support the Rockchip USB3.0/DP combo PHY with
Samsung IP block. This is required for USB3 support on RK3588.
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c
index d989f1649b0e..b4f64ebfbcf7 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c
@@ -415,6 +415,17 @@ static const struct dphy_drv_data rk3568_mipidphy_drv_data = {
.resets_num = ARRAY_SIZE(rk3368_reset_names),
};
+static const struct dphy_drv_data rk3576_mipidphy_drv_data = {
+ .pwrctl_offset = -1,
+ .ths_settle_offset = RK3568_CSIDPHY_CLK_WR_THS_SETTLE,
+ .calib_offset = RK3568_CSIDPHY_CLK_CALIB_EN,
+ .hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges,
+ .num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges),
+ .grf_regs = rk3588_grf_dphy_regs,
+ .resets = rk3368_reset_names,
+ .resets_num = ARRAY_SIZE(rk3368_reset_names),
+};
+
static const struct dphy_drv_data rk3588_mipidphy_drv_data = {
.pwrctl_offset = -1,
.ths_settle_offset = RK3568_CSIDPHY_CLK_WR_THS_SETTLE,
@@ -448,6 +459,10 @@ static const struct of_device_id rockchip_inno_csidphy_match_id[] = {
.data = &rk3568_mipidphy_drv_data,
},
{
+ .compatible = "rockchip,rk3576-csi-dphy",
+ .data = &rk3576_mipidphy_drv_data,
+ },
+ {
.compatible = "rockchip,rk3588-csi-dphy",
.data = &rk3588_mipidphy_drv_data,
},
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
index c3d257a0f4a6..d2c2b17df5f3 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -1044,7 +1044,7 @@ static int inno_hdmi_phy_clk_register(struct inno_hdmi_phy *inno)
{
struct device *dev = inno->dev;
struct device_node *np = dev->of_node;
- struct clk_init_data init;
+ struct clk_init_data init = {};
const char *parent_name;
int ret;
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 7d8a533f24ae..e47b89015586 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -400,7 +400,7 @@ static int
rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
{
struct device_node *node = rphy->dev->of_node;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk *refclk = NULL;
const char *clk_name;
int i;
diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
index 7843356a4dd4..3102a99128f6 100644
--- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
@@ -106,6 +106,9 @@
#define RK3568_PHYREG18 0x44
#define RK3568_PHYREG18_PLL_LOOP 0x32
+#define RK3568_PHYREG26 0x64
+#define RK3568_PHYREG26_FORCE_RTERM_DET_RDY BIT(5)
+
#define RK3568_PHYREG30 0x74
#define RK3568_PHYREG30_GATE_TX_PCK_SEL BIT(7)
#define RK3568_PHYREG30_GATE_TX_PCK_DLY_PLL_OFF BIT(7)
@@ -194,6 +197,7 @@ struct rockchip_combphy_cfg {
unsigned int num_phys;
unsigned int phy_ids[3];
const struct rockchip_combphy_grfcfg *grfcfg;
+ bool force_rxterm_det_rdy;
int (*combphy_cfg)(struct rockchip_combphy_priv *priv);
};
@@ -265,6 +269,17 @@ static int rockchip_combphy_init(struct phy *phy)
switch (priv->type) {
case PHY_TYPE_PCIE:
+ /*
+ * Hardware Errata: TX fails to detect peer RX termination.
+ * Some PHY revisions may fail to detect remote RX's RTERM
+ * (receiver termination resistor) under certain critical
+ * temperature conditions. Set force rterm detect ready to
+ * fix it.
+ */
+ if (priv->cfg->force_rxterm_det_rdy)
+ rockchip_combphy_updatel(priv, RK3568_PHYREG26_FORCE_RTERM_DET_RDY,
+ RK3568_PHYREG26_FORCE_RTERM_DET_RDY, RK3568_PHYREG26);
+ fallthrough;
case PHY_TYPE_USB3:
case PHY_TYPE_SATA:
case PHY_TYPE_SGMII:
@@ -801,6 +816,7 @@ static const struct rockchip_combphy_cfg rk3562_combphy_cfgs = {
},
.grfcfg = &rk3562_combphy_grfcfgs,
.combphy_cfg = rk3562_combphy_cfg,
+ .force_rxterm_det_rdy = true,
};
static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv)
@@ -998,6 +1014,7 @@ static const struct rockchip_combphy_cfg rk3568_combphy_cfgs = {
},
.grfcfg = &rk3568_combphy_grfcfgs,
.combphy_cfg = rk3568_combphy_cfg,
+ .force_rxterm_det_rdy = true,
};
static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv)
@@ -1219,6 +1236,7 @@ static const struct rockchip_combphy_cfg rk3576_combphy_cfgs = {
},
.grfcfg = &rk3576_combphy_grfcfgs,
.combphy_cfg = rk3576_combphy_cfg,
+ .force_rxterm_det_rdy = true,
};
static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv)
@@ -1391,6 +1409,7 @@ static const struct rockchip_combphy_cfg rk3588_combphy_cfgs = {
},
.grfcfg = &rk3588_combphy_grfcfgs,
.combphy_cfg = rk3588_combphy_cfg,
+ .force_rxterm_det_rdy = true,
};
static const struct of_device_id rockchip_combphy_of_match[] = {
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
index 18f5f582d1d7..7382043e1f31 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
@@ -1610,16 +1610,16 @@ static int samsung_mipi_dcphy_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(samsung->grf_apb_rst),
"Failed to get system grf_apb_rst control\n");
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
+
samsung->phy = devm_phy_create(dev, NULL, &samsung_mipi_dcphy_ops);
if (IS_ERR(samsung->phy))
return dev_err_probe(dev, PTR_ERR(samsung->phy), "Failed to create MIPI DC-PHY\n");
phy_set_drvdata(samsung->phy, samsung);
- ret = devm_pm_runtime_enable(dev);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
-
phy_provider = devm_of_phy_provider_register(dev, samsung_mipi_dcphy_xlate);
if (IS_ERR(phy_provider))
return dev_err_probe(dev, PTR_ERR(phy_provider),
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 24ed0d7eb6f8..6879eecc5a9f 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -333,6 +333,7 @@
#define FRL_3G3L_RATE 900000000
#define FRL_6G3L_RATE 1800000000
#define FRL_8G4L_RATE 3200000000
+#define FRL_FFE_MAX_LEVEL 3
enum dp_link_rate {
DP_BW_RBR,
@@ -466,6 +467,16 @@ static const struct ropll_config rk_hdptx_tmds_ropll_cfg[] = {
{ 25175000ULL, 84, 84, 1, 1, 15, 1, 168, 1, 16, 4, 1, 1, },
};
+static const struct ffe_config {
+ u8 pre_shoot;
+ u8 de_emphasis;
+} rk_hdptx_frl_ffe_cfg[FRL_FFE_MAX_LEVEL + 1] = {
+ { 0x3, 0x4 },
+ { 0x3, 0x6 },
+ { 0x3, 0x8 },
+ { 0x3, 0x9 },
+};
+
static const struct reg_sequence rk_hdptx_common_cmn_init_seq[] = {
REG_SEQ0(CMN_REG(0009), 0x0c),
REG_SEQ0(CMN_REG(000a), 0x83),
@@ -1321,6 +1332,45 @@ static int rk_hdptx_tmds_ropll_mode_config(struct rk_hdptx_phy *hdptx)
return rk_hdptx_post_enable_lane(hdptx);
}
+static int rk_hdptx_frl_ffe_config(struct rk_hdptx_phy *hdptx, u8 ffe_level)
+{
+ u8 val;
+
+ if (ffe_level > FRL_FFE_MAX_LEVEL)
+ return -EINVAL;
+
+ val = rk_hdptx_frl_ffe_cfg[ffe_level].pre_shoot;
+
+ regmap_update_bits(hdptx->regmap, LANE_REG(0305),
+ LN_TX_DRV_PRE_LVL_CTRL_MASK,
+ FIELD_PREP(LN_TX_DRV_PRE_LVL_CTRL_MASK, val));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0405),
+ LN_TX_DRV_PRE_LVL_CTRL_MASK,
+ FIELD_PREP(LN_TX_DRV_PRE_LVL_CTRL_MASK, val));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0505),
+ LN_TX_DRV_PRE_LVL_CTRL_MASK,
+ FIELD_PREP(LN_TX_DRV_PRE_LVL_CTRL_MASK, val));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0605),
+ LN_TX_DRV_PRE_LVL_CTRL_MASK,
+ FIELD_PREP(LN_TX_DRV_PRE_LVL_CTRL_MASK, val));
+
+ val = rk_hdptx_frl_ffe_cfg[ffe_level].de_emphasis;
+
+ regmap_update_bits(hdptx->regmap, LANE_REG(0304),
+ LN_TX_DRV_POST_LVL_CTRL_MASK,
+ FIELD_PREP(LN_TX_DRV_POST_LVL_CTRL_MASK, val));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0404),
+ LN_TX_DRV_POST_LVL_CTRL_MASK,
+ FIELD_PREP(LN_TX_DRV_POST_LVL_CTRL_MASK, val));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0504),
+ LN_TX_DRV_POST_LVL_CTRL_MASK,
+ FIELD_PREP(LN_TX_DRV_POST_LVL_CTRL_MASK, val));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0604),
+ LN_TX_DRV_POST_LVL_CTRL_MASK,
+ FIELD_PREP(LN_TX_DRV_POST_LVL_CTRL_MASK, val));
+ return 0;
+}
+
static void rk_hdptx_dp_reset(struct rk_hdptx_phy *hdptx)
{
reset_control_assert(hdptx->rsts[RST_LANE].rstc);
@@ -1735,6 +1785,13 @@ static int rk_hdptx_phy_verify_hdmi_config(struct rk_hdptx_phy *hdptx,
unsigned long long frl_rate = 100000000ULL * hdmi_in->frl.lanes *
hdmi_in->frl.rate_per_lane;
+ if (hdmi_in->frl.set_ffe_level) {
+ if (hdmi_in->frl.ffe_level > FRL_FFE_MAX_LEVEL)
+ return -EINVAL;
+
+ return 0;
+ }
+
switch (hdmi_in->frl.rate_per_lane) {
case 3:
case 6:
@@ -2081,11 +2138,18 @@ static int rk_hdptx_phy_configure(struct phy *phy, union phy_configure_opts *opt
if (ret) {
dev_err(hdptx->dev, "invalid hdmi params for phy configure\n");
} else {
- hdptx->pll_config_dirty = true;
-
- dev_dbg(hdptx->dev, "%s %s rate=%llu bpc=%u\n", __func__,
- hdptx->hdmi_cfg.mode ? "FRL" : "TMDS",
- hdptx->hdmi_cfg.rate, hdptx->hdmi_cfg.bpc);
+ if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL &&
+ opts->hdmi.frl.set_ffe_level) {
+ dev_dbg(hdptx->dev, "%s ffe_level=%u\n", __func__,
+ opts->hdmi.frl.ffe_level);
+ ret = rk_hdptx_frl_ffe_config(hdptx, opts->hdmi.frl.ffe_level);
+ } else {
+ hdptx->pll_config_dirty = true;
+
+ dev_dbg(hdptx->dev, "%s %s rate=%llu bpc=%u\n", __func__,
+ hdptx->hdmi_cfg.mode ? "FRL" : "TMDS",
+ hdptx->hdmi_cfg.rate, hdptx->hdmi_cfg.bpc);
+ }
}
return ret;
@@ -2346,14 +2410,16 @@ static int rk_hdptx_phy_clk_determine_rate(struct clk_hw *hw,
* to ensure rk_hdptx_phy_clk_set_rate() will be always invoked.
* Otherwise, restrict the rate according to the PHY link setup.
*/
- if (hdptx->pll_config_dirty)
- req->rate = 0;
- else if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
+
+ if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
req->rate = hdptx->hdmi_cfg.rate;
else
req->rate = DIV_ROUND_CLOSEST_ULL(hdptx->hdmi_cfg.rate * 8,
hdptx->hdmi_cfg.bpc);
+ if (hdptx->pll_config_dirty && req->rate == clk_hw_get_rate(hw))
+ req->rate = 0;
+
return 0;
}
diff --git a/drivers/phy/rockchip/phy-rockchip-usb.c b/drivers/phy/rockchip/phy-rockchip-usb.c
index cef96739cf3f..c5a2853a63d3 100644
--- a/drivers/phy/rockchip/phy-rockchip-usb.c
+++ b/drivers/phy/rockchip/phy-rockchip-usb.c
@@ -200,9 +200,9 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
struct device_node *child)
{
struct rockchip_usb_phy *rk_phy;
+ struct clk_init_data init = {};
unsigned int reg_offset;
const char *clk_name;
- struct clk_init_data init;
int err, i;
rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL);
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 07a839e41db7..e748acdd2d31 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -6,12 +6,15 @@
* Copyright (C) 2024 Collabora Ltd
*/
+#include <drm/bridge/aux-bridge.h>
#include <dt-bindings/phy/phy.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
+#include <linux/hw_bitfield.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -20,6 +23,7 @@
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/reset.h>
+#include <linux/string_choices.h>
#include <linux/usb/ch9.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
@@ -74,7 +78,6 @@
#define TRSV_LN2_MON_RX_CDR_DONE_OFFSET 0x1b84 /* trsv_reg06E1 */
#define TRSV_LN2_MON_RX_CDR_LOCK_DONE BIT(0)
-#define BIT_WRITEABLE_SHIFT 16
#define PHY_AUX_DP_DATA_POL_NORMAL 0
#define PHY_AUX_DP_DATA_POL_INVERT 1
#define PHY_LANE_MUX_USB 0
@@ -103,8 +106,8 @@ struct rk_udphy_grf_reg {
#define _RK_UDPHY_GEN_GRF_REG(offset, mask, disable, enable) \
{\
offset, \
- FIELD_PREP_CONST(mask, disable) | (mask << BIT_WRITEABLE_SHIFT), \
- FIELD_PREP_CONST(mask, enable) | (mask << BIT_WRITEABLE_SHIFT), \
+ FIELD_PREP_WM16_CONST(mask, disable), \
+ FIELD_PREP_WM16_CONST(mask, enable), \
}
#define RK_UDPHY_GEN_GRF_REG(offset, bitend, bitstart, disable, enable) \
@@ -126,7 +129,6 @@ struct rk_udphy_grf_cfg {
struct rk_udphy_vogrf_cfg {
/* vo-grf */
- struct rk_udphy_grf_reg hpd_trigger;
u32 dp_lane_reg;
};
@@ -170,7 +172,7 @@ struct rk_udphy {
/* PHY status management */
bool flip;
- bool mode_change;
+ bool phy_needs_reinit;
u8 mode;
u8 status;
@@ -184,14 +186,12 @@ struct rk_udphy {
u32 dp_lane_sel[4];
u32 dp_aux_dout_sel;
u32 dp_aux_din_sel;
- bool dp_sink_hpd_sel;
- bool dp_sink_hpd_cfg;
unsigned int link_rate;
unsigned int lanes;
u8 bw;
int id;
- bool dp_in_use;
+ int dp_lanes;
/* PHY const config */
const struct rk_udphy_cfg *cfgs;
@@ -349,7 +349,8 @@ static const struct reg_sequence rk_udphy_24m_refclk_cfg[] = {
{0x0a64, 0xa8}, {0x1a3c, 0xd0},
{0x1a44, 0xd0}, {0x1a48, 0x01},
{0x1a4c, 0x0d}, {0x1a54, 0xe0},
- {0x1a5c, 0xe0}, {0x1a64, 0xa8}
+ {0x1a5c, 0xe0}, {0x1a64, 0xa8},
+ {0x00d4, 0x30}
};
static const struct reg_sequence rk_udphy_26m_refclk_cfg[] = {
@@ -376,7 +377,7 @@ static const struct reg_sequence rk_udphy_26m_refclk_cfg[] = {
{0x0c30, 0x0e}, {0x0c48, 0x06},
{0x1c30, 0x0e}, {0x1c48, 0x06},
{0x028c, 0x18}, {0x0af0, 0x00},
- {0x1af0, 0x00}
+ {0x1af0, 0x00}, {0x00d4, 0x33}
};
static const struct reg_sequence rk_udphy_init_sequence[] = {
@@ -411,8 +412,8 @@ static const struct reg_sequence rk_udphy_init_sequence[] = {
{0x0070, 0x7d}, {0x0074, 0x68},
{0x0af4, 0x1a}, {0x1af4, 0x1a},
{0x0440, 0x3f}, {0x10d4, 0x08},
- {0x20d4, 0x08}, {0x00d4, 0x30},
- {0x0024, 0x6e},
+ {0x20d4, 0x08}, {0x0024, 0x6e},
+ {0x09c0, 0x0a}, {0x19c0, 0x0a}
};
static inline int rk_udphy_grfreg_write(struct regmap *base,
@@ -426,11 +427,14 @@ static int rk_udphy_clk_init(struct rk_udphy *udphy, struct device *dev)
int i;
udphy->num_clks = devm_clk_bulk_get_all(dev, &udphy->clks);
- if (udphy->num_clks < 1)
- return -ENODEV;
+ if (udphy->num_clks < 0)
+ return udphy->num_clks;
/* used for configure phy reference clock frequency */
for (i = 0; i < udphy->num_clks; i++) {
+ if (!udphy->clks[i].id)
+ continue;
+
if (!strncmp(udphy->clks[i].id, "refclk", 6)) {
udphy->refclk = udphy->clks[i].clk;
break;
@@ -448,11 +452,6 @@ static int rk_udphy_reset_assert_all(struct rk_udphy *udphy)
return reset_control_bulk_assert(udphy->num_rsts, udphy->rsts);
}
-static int rk_udphy_reset_deassert_all(struct rk_udphy *udphy)
-{
- return reset_control_bulk_deassert(udphy->num_rsts, udphy->rsts);
-}
-
static int rk_udphy_reset_deassert(struct rk_udphy *udphy, char *name)
{
struct reset_control_bulk_data *list = udphy->rsts;
@@ -463,6 +462,8 @@ static int rk_udphy_reset_deassert(struct rk_udphy *udphy, char *name)
return reset_control_deassert(list[idx].rstc);
}
+ dev_err(udphy->dev, "failed to de-assert missing reset line: %s\n", name);
+
return -EINVAL;
}
@@ -489,6 +490,8 @@ static void rk_udphy_u3_port_disable(struct rk_udphy *udphy, u8 disable)
const struct rk_udphy_cfg *cfg = udphy->cfgs;
const struct rk_udphy_grf_reg *preg;
+ dev_dbg(udphy->dev, "USB3 port %s\n", str_on_off(!disable));
+
preg = udphy->id ? &cfg->grfcfg.usb3otg1_cfg : &cfg->grfcfg.usb3otg0_cfg;
rk_udphy_grfreg_write(udphy->usbgrf, preg, disable);
}
@@ -535,60 +538,31 @@ static void rk_udphy_usb_bvalid_enable(struct rk_udphy *udphy, u8 enable)
* <0 1> dpln0 dpln1 usbrx usbtx
* <2 3> usbrx usbtx dpln0 dpln1
* ---------------------------------------------------------------------------
+ * if 1 lane for dp function, 2 lane for usb function, define rockchip,dp-lane-mux = <x>;
+ * sample as follow:
+ * ---------------------------------------------------------------------------
+ * B11-B10 A2-A3 A11-A10 B2-B3
+ * rockchip,dp-lane-mux ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx)
+ * <0> dpln0 \ usbrx usbtx
+ * ---------------------------------------------------------------------------
*/
-static void rk_udphy_dplane_select(struct rk_udphy *udphy)
+static void rk_udphy_dp_lane_select(struct rk_udphy *udphy)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
- u32 value = 0;
-
- switch (udphy->mode) {
- case UDPHY_MODE_DP:
- value |= 2 << udphy->dp_lane_sel[2] * 2;
- value |= 3 << udphy->dp_lane_sel[3] * 2;
- fallthrough;
-
- case UDPHY_MODE_DP_USB:
- value |= 0 << udphy->dp_lane_sel[0] * 2;
- value |= 1 << udphy->dp_lane_sel[1] * 2;
- break;
-
- case UDPHY_MODE_USB:
- break;
-
- default:
- break;
- }
-
- regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg,
- ((DP_AUX_DIN_SEL | DP_AUX_DOUT_SEL | DP_LANE_SEL_ALL) << 16) |
- FIELD_PREP(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel) |
- FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value);
-}
-
-static int rk_udphy_dplane_get(struct rk_udphy *udphy)
-{
- int dp_lanes;
-
- switch (udphy->mode) {
- case UDPHY_MODE_DP:
- dp_lanes = 4;
- break;
+ u32 value = FIELD_PREP_WM16(DP_LANE_SEL_ALL, 0);
+ int i;
- case UDPHY_MODE_DP_USB:
- dp_lanes = 2;
- break;
+ for (i = 0; i < udphy->dp_lanes; i++)
+ value |= field_prep(DP_LANE_SEL_N(udphy->dp_lane_sel[i]), i);
- case UDPHY_MODE_USB:
- default:
- dp_lanes = 0;
- break;
- }
+ value |= FIELD_PREP_WM16(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel);
+ value |= FIELD_PREP_WM16(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel);
- return dp_lanes;
+ regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg, value);
}
-static void rk_udphy_dplane_enable(struct rk_udphy *udphy, int dp_lanes)
+static void rk_udphy_dp_lane_enable(struct rk_udphy *udphy, int dp_lanes)
{
u32 val = 0;
int i;
@@ -604,17 +578,49 @@ static void rk_udphy_dplane_enable(struct rk_udphy *udphy, int dp_lanes)
CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
}
-static void rk_udphy_dp_hpd_event_trigger(struct rk_udphy *udphy, bool hpd)
+static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
{
- const struct rk_udphy_cfg *cfg = udphy->cfgs;
+ if (udphy->mode == mode)
+ return;
- udphy->dp_sink_hpd_sel = true;
- udphy->dp_sink_hpd_cfg = hpd;
+ udphy->phy_needs_reinit = true;
+ udphy->mode = mode;
+}
- if (!udphy->dp_in_use)
- return;
+static void rk_udphy_set_typec_state(struct rk_udphy *udphy, unsigned long state)
+{
+ u8 mode;
+
+ switch (state) {
+ case TYPEC_DP_STATE_C:
+ case TYPEC_DP_STATE_E:
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
+ mode = UDPHY_MODE_DP;
+ udphy->dp_lanes = 4;
+ break;
- rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, hpd);
+ case TYPEC_DP_STATE_D:
+ default:
+ if (udphy->flip) {
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
+ } else {
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
+ }
+ mode = UDPHY_MODE_DP_USB;
+ udphy->dp_lanes = 2;
+ break;
+ }
+
+ rk_udphy_mode_set(udphy, mode);
}
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
@@ -624,10 +630,6 @@ static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
udphy->dp_lane_sel[1] = 1;
udphy->dp_lane_sel[2] = 3;
udphy->dp_lane_sel[3] = 2;
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_INVERT;
udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_INVERT;
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 1);
@@ -637,40 +639,42 @@ static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
udphy->dp_lane_sel[1] = 3;
udphy->dp_lane_sel[2] = 1;
udphy->dp_lane_sel[3] = 0;
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_NORMAL;
udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_NORMAL;
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 1);
}
- udphy->mode = UDPHY_MODE_DP_USB;
+ /* default to USB3 + DP as 4 lane USB is not supported */
+ rk_udphy_set_typec_state(udphy, TYPEC_DP_STATE_D);
}
static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
enum typec_orientation orien)
{
struct rk_udphy *udphy = typec_switch_get_drvdata(sw);
+ bool flipped = orien == TYPEC_ORIENTATION_REVERSE;
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
if (orien == TYPEC_ORIENTATION_NONE) {
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0);
/* unattached */
rk_udphy_usb_bvalid_enable(udphy, false);
- goto unlock_ret;
+
+ return 0;
+ }
+
+ if (udphy->flip != flipped) {
+ dev_dbg(udphy->dev, "cable orientation changed, PHY re-init required.\n");
+ udphy->phy_needs_reinit = true;
}
- udphy->flip = orien == TYPEC_ORIENTATION_REVERSE;
+ udphy->flip = flipped;
rk_udphy_set_typec_default_mapping(udphy);
rk_udphy_usb_bvalid_enable(udphy, true);
-unlock_ret:
- mutex_unlock(&udphy->mutex);
return 0;
}
@@ -783,6 +787,11 @@ static int rk_udphy_init(struct rk_udphy *udphy)
const struct rk_udphy_cfg *cfg = udphy->cfgs;
int ret;
+ dev_dbg(udphy->dev, "reinit PHY with USB3=%s and DP=%s (%u lanes) flipped=%s\n",
+ str_on_off(udphy->mode & UDPHY_MODE_USB),
+ str_on_off(udphy->mode & UDPHY_MODE_DP),
+ udphy->dp_lanes, str_yes_no(udphy->flip));
+
rk_udphy_reset_assert_all(udphy);
usleep_range(10000, 11000);
@@ -793,8 +802,12 @@ static int rk_udphy_init(struct rk_udphy *udphy)
/* Step 1: power on pma and deassert apb rstn */
rk_udphy_grfreg_write(udphy->udphygrf, &cfg->grfcfg.low_pwrn, true);
- rk_udphy_reset_deassert(udphy, "pma_apb");
- rk_udphy_reset_deassert(udphy, "pcs_apb");
+ ret = rk_udphy_reset_deassert(udphy, "pma_apb");
+ if (ret)
+ goto assert_resets;
+ ret = rk_udphy_reset_deassert(udphy, "pcs_apb");
+ if (ret)
+ goto assert_resets;
/* Step 2: set init sequence and phy refclk */
ret = regmap_multi_reg_write(udphy->pma_regmap, rk_udphy_init_sequence,
@@ -820,8 +833,11 @@ static int rk_udphy_init(struct rk_udphy *udphy)
FIELD_PREP(CMN_DP_LANE_EN_ALL, 0));
/* Step 4: deassert init rstn and wait for 200ns from datasheet */
- if (udphy->mode & UDPHY_MODE_USB)
- rk_udphy_reset_deassert(udphy, "init");
+ if (udphy->mode & UDPHY_MODE_USB) {
+ ret = rk_udphy_reset_deassert(udphy, "init");
+ if (ret)
+ goto assert_resets;
+ }
if (udphy->mode & UDPHY_MODE_DP) {
regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
@@ -833,8 +849,14 @@ static int rk_udphy_init(struct rk_udphy *udphy)
/* Step 5: deassert cmn/lane rstn */
if (udphy->mode & UDPHY_MODE_USB) {
- rk_udphy_reset_deassert(udphy, "cmn");
- rk_udphy_reset_deassert(udphy, "lane");
+ ret = rk_udphy_reset_deassert(udphy, "cmn");
+ if (ret)
+ goto assert_resets;
+
+ ret = rk_udphy_reset_deassert(udphy, "lane");
+ if (ret)
+ goto assert_resets;
+
}
/* Step 6: wait for lock done of pll */
@@ -853,6 +875,8 @@ static int rk_udphy_setup(struct rk_udphy *udphy)
{
int ret;
+ dev_dbg(udphy->dev, "enable PHY\n");
+
ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
if (ret) {
dev_err(udphy->dev, "failed to enable clk\n");
@@ -871,6 +895,7 @@ static int rk_udphy_setup(struct rk_udphy *udphy)
static void rk_udphy_disable(struct rk_udphy *udphy)
{
+ dev_dbg(udphy->dev, "disable PHY\n");
clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks);
rk_udphy_reset_assert_all(udphy);
}
@@ -886,7 +911,7 @@ static int rk_udphy_parse_lane_mux_data(struct rk_udphy *udphy)
return 0;
}
- if (num_lanes != 2 && num_lanes != 4)
+ if (num_lanes != 1 && num_lanes != 2 && num_lanes != 4)
return dev_err_probe(udphy->dev, -EINVAL,
"invalid number of lane mux\n");
@@ -912,33 +937,13 @@ static int rk_udphy_parse_lane_mux_data(struct rk_udphy *udphy)
}
udphy->mode = UDPHY_MODE_DP;
- if (num_lanes == 2) {
+ udphy->dp_lanes = num_lanes;
+ if (num_lanes == 1 || num_lanes == 2) {
udphy->mode |= UDPHY_MODE_USB;
- udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP);
- }
-
- return 0;
-}
-
-static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
-{
- int ret;
- u32 value;
-
- ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
- if (ret) {
- dev_err(udphy->dev, "failed to enable clk\n");
- return ret;
+ udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP) ||
+ (udphy->lane_mux_sel[1] == PHY_LANE_MUX_DP);
}
- rk_udphy_reset_deassert_all(udphy);
-
- regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value);
- if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value))
- udphy->status = UDPHY_MODE_DP;
- else
- rk_udphy_disable(udphy);
-
return 0;
}
@@ -999,23 +1004,24 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
}
if (udphy->status == UDPHY_MODE_NONE) {
- udphy->mode_change = false;
ret = rk_udphy_setup(udphy);
if (ret)
return ret;
- if (udphy->mode & UDPHY_MODE_USB)
+ if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
- } else if (udphy->mode_change) {
- udphy->mode_change = false;
- udphy->status = UDPHY_MODE_NONE;
- if (udphy->mode == UDPHY_MODE_DP)
- rk_udphy_u3_port_disable(udphy, true);
+ udphy->phy_needs_reinit = false;
+ } else if (udphy->phy_needs_reinit) {
+ rk_udphy_u3_port_disable(udphy, true);
+ udelay(10);
- rk_udphy_disable(udphy);
- ret = rk_udphy_setup(udphy);
+ ret = rk_udphy_init(udphy);
if (ret)
return ret;
+
+ if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
+ rk_udphy_u3_port_disable(udphy, false);
+ udphy->phy_needs_reinit = false;
}
udphy->status |= mode;
@@ -1039,55 +1045,30 @@ static void rk_udphy_power_off(struct rk_udphy *udphy, u8 mode)
rk_udphy_disable(udphy);
}
-static int rk_udphy_dp_phy_init(struct phy *phy)
-{
- struct rk_udphy *udphy = phy_get_drvdata(phy);
-
- mutex_lock(&udphy->mutex);
-
- udphy->dp_in_use = true;
-
- mutex_unlock(&udphy->mutex);
-
- return 0;
-}
-
-static int rk_udphy_dp_phy_exit(struct phy *phy)
-{
- struct rk_udphy *udphy = phy_get_drvdata(phy);
-
- mutex_lock(&udphy->mutex);
- udphy->dp_in_use = false;
- mutex_unlock(&udphy->mutex);
- return 0;
-}
-
static int rk_udphy_dp_phy_power_on(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
- int ret, dp_lanes;
-
- mutex_lock(&udphy->mutex);
+ int ret;
- dp_lanes = rk_udphy_dplane_get(udphy);
- phy_set_bus_width(phy, dp_lanes);
+ scoped_guard(mutex, &udphy->mutex) {
+ phy_set_bus_width(phy, udphy->dp_lanes);
- ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
- if (ret)
- goto unlock;
+ ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
+ if (ret)
+ return ret;
- rk_udphy_dplane_enable(udphy, dp_lanes);
+ rk_udphy_dp_lane_enable(udphy, udphy->dp_lanes);
- rk_udphy_dplane_select(udphy);
+ rk_udphy_dp_lane_select(udphy);
+ }
-unlock:
- mutex_unlock(&udphy->mutex);
/*
* If data send by aux channel too fast after phy power on,
* the aux may be not ready which will cause aux error. Adding
* delay to avoid this issue.
*/
usleep_range(10000, 11000);
+
return ret;
}
@@ -1095,10 +1076,10 @@ static int rk_udphy_dp_phy_power_off(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
- mutex_lock(&udphy->mutex);
- rk_udphy_dplane_enable(udphy, 0);
+ guard(mutex)(&udphy->mutex);
+
+ rk_udphy_dp_lane_enable(udphy, 0);
rk_udphy_power_off(udphy, UDPHY_MODE_DP);
- mutex_unlock(&udphy->mutex);
return 0;
}
@@ -1126,6 +1107,9 @@ static int rk_udphy_dp_phy_verify_link_rate(struct rk_udphy *udphy,
static int rk_udphy_dp_phy_verify_lanes(struct rk_udphy *udphy,
struct phy_configure_opts_dp *dp)
{
+ if (dp->lanes > udphy->dp_lanes)
+ return -EINVAL;
+
switch (dp->lanes) {
case 1:
case 2:
@@ -1200,6 +1184,8 @@ static int rk_udphy_dp_phy_configure(struct phy *phy,
u32 i, val, lane;
int ret;
+ guard(mutex)(&udphy->mutex);
+
if (dp->set_rate) {
ret = rk_udphy_dp_phy_verify_link_rate(udphy, dp);
if (ret)
@@ -1291,8 +1277,6 @@ static int rk_udphy_dp_phy_configure(struct phy *phy,
}
static const struct phy_ops rk_udphy_dp_phy_ops = {
- .init = rk_udphy_dp_phy_init,
- .exit = rk_udphy_dp_phy_exit,
.power_on = rk_udphy_dp_phy_power_on,
.power_off = rk_udphy_dp_phy_power_off,
.configure = rk_udphy_dp_phy_configure,
@@ -1302,35 +1286,30 @@ static const struct phy_ops rk_udphy_dp_phy_ops = {
static int rk_udphy_usb3_phy_init(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
- int ret = 0;
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
+
/* DP only or high-speed, disable U3 port */
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
rk_udphy_u3_port_disable(udphy, true);
- goto unlock;
+ return 0;
}
- ret = rk_udphy_power_on(udphy, UDPHY_MODE_USB);
-
-unlock:
- mutex_unlock(&udphy->mutex);
- return ret;
+ return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
}
static int rk_udphy_usb3_phy_exit(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
- mutex_lock(&udphy->mutex);
+ guard(mutex)(&udphy->mutex);
+
/* DP only or high-speed */
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
- goto unlock;
+ return 0;
rk_udphy_power_off(udphy, UDPHY_MODE_USB);
-unlock:
- mutex_unlock(&udphy->mutex);
return 0;
}
@@ -1340,62 +1319,36 @@ static const struct phy_ops rk_udphy_usb3_phy_ops = {
.owner = THIS_MODULE,
};
+static bool rk_udphy_is_supported_mode(struct typec_mux_state *state)
+{
+ /* Handle Safe State and USB State */
+ if (state->mode < TYPEC_STATE_MODAL)
+ return true;
+
+ /* Handle DP AltMode */
+ if (state->alt && state->alt->svid == USB_TYPEC_DP_SID)
+ return true;
+
+ return false;
+}
+
static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
struct typec_mux_state *state)
{
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
- u8 mode;
- mutex_lock(&udphy->mutex);
+ /* Ignore mux events not involving USB or DP */
+ if (!rk_udphy_is_supported_mode(state)) {
+ dev_dbg(udphy->dev, "ignore mux event with mode=%lu\n", state->mode);
+ return 0;
+ }
- switch (state->mode) {
- case TYPEC_DP_STATE_C:
- case TYPEC_DP_STATE_E:
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
- mode = UDPHY_MODE_DP;
- break;
+ dev_dbg(udphy->dev, "new mode: %lu\n", state->mode);
- case TYPEC_DP_STATE_D:
- default:
- if (udphy->flip) {
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
- } else {
- udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
- udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
- udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
- }
- mode = UDPHY_MODE_DP_USB;
- break;
- }
+ guard(mutex)(&udphy->mutex);
- if (state->alt && state->alt->svid == USB_TYPEC_DP_SID) {
- struct typec_displayport_data *data = state->data;
-
- if (!data) {
- rk_udphy_dp_hpd_event_trigger(udphy, false);
- } else if (data->status & DP_STATUS_IRQ_HPD) {
- rk_udphy_dp_hpd_event_trigger(udphy, false);
- usleep_range(750, 800);
- rk_udphy_dp_hpd_event_trigger(udphy, true);
- } else if (data->status & DP_STATUS_HPD_STATE) {
- if (udphy->mode != mode) {
- udphy->mode = mode;
- udphy->mode_change = true;
- }
- rk_udphy_dp_hpd_event_trigger(udphy, true);
- } else {
- rk_udphy_dp_hpd_event_trigger(udphy, false);
- }
- }
+ rk_udphy_set_typec_state(udphy, state->mode);
- mutex_unlock(&udphy->mutex);
return 0;
}
@@ -1453,6 +1406,7 @@ static int rk_udphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
+ struct fwnode_handle *dp_aux_ep;
struct resource *res;
struct rk_udphy *udphy;
void __iomem *base;
@@ -1492,10 +1446,6 @@ static int rk_udphy_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = rk_udphy_get_initial_status(udphy);
- if (ret)
- return ret;
-
mutex_init(&udphy->mutex);
platform_set_drvdata(pdev, udphy);
@@ -1511,6 +1461,21 @@ static int rk_udphy_probe(struct platform_device *pdev)
return ret;
}
+ /*
+ * Only register the DRM bridge, if the DP aux channel is connected.
+ * Some boards use the USBDP PHY only for its USB3 capabilities. The
+ * aux bridge itself will be registered using port 0, endpoint 0, which
+ * is fine as that is the actual superspeed data connection shared by
+ * USB3 and DP based on the mux config.
+ */
+ dp_aux_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 3, 0, 0);
+ if (dp_aux_ep) {
+ ret = drm_aux_bridge_register(dev);
+ fwnode_handle_put(dp_aux_ep);
+ if (ret)
+ return ret;
+ }
+
udphy->phy_u3 = devm_phy_create(dev, dev->of_node, &rk_udphy_usb3_phy_ops);
if (IS_ERR(udphy->phy_u3)) {
ret = PTR_ERR(udphy->phy_u3);
@@ -1523,7 +1488,7 @@ static int rk_udphy_probe(struct platform_device *pdev)
ret = PTR_ERR(udphy->phy_dp);
return dev_err_probe(dev, ret, "failed to create DP phy\n");
}
- phy_set_bus_width(udphy->phy_dp, rk_udphy_dplane_get(udphy));
+ phy_set_bus_width(udphy->phy_dp, udphy->dp_lanes);
udphy->phy_dp->attrs.max_link_rate = 8100;
phy_set_drvdata(udphy->phy_dp, udphy);
@@ -1536,20 +1501,6 @@ static int rk_udphy_probe(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused rk_udphy_resume(struct device *dev)
-{
- struct rk_udphy *udphy = dev_get_drvdata(dev);
-
- if (udphy->dp_sink_hpd_sel)
- rk_udphy_dp_hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg);
-
- return 0;
-}
-
-static const struct dev_pm_ops rk_udphy_pm_ops = {
- SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, rk_udphy_resume)
-};
-
static const char * const rk_udphy_rst_list[] = {
"init", "cmn", "lane", "pcs_apb", "pma_apb"
};
@@ -1573,7 +1524,6 @@ static const struct rk_udphy_cfg rk3576_udphy_cfgs = {
},
.vogrfcfg = {
{
- .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3),
.dp_lane_reg = 0x0000,
},
},
@@ -1614,11 +1564,9 @@ static const struct rk_udphy_cfg rk3588_udphy_cfgs = {
},
.vogrfcfg = {
{
- .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3),
.dp_lane_reg = 0x0000,
},
{
- .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0008, 11, 10, 1, 3),
.dp_lane_reg = 0x0008,
},
},
@@ -1654,7 +1602,6 @@ static struct platform_driver rk_udphy_driver = {
.driver = {
.name = "rockchip-usbdp-phy",
.of_match_table = rk_udphy_dt_match,
- .pm = &rk_udphy_pm_ops,
},
};
module_platform_driver(rk_udphy_driver);
diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c
index 8711a3b62c8e..087d552eb511 100644
--- a/drivers/phy/samsung/phy-exynos5-usbdrd.c
+++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c
@@ -471,7 +471,7 @@ struct exynos5_usbdrd_phy;
struct exynos5_usbdrd_phy_config {
u32 id;
void (*phy_isol)(struct phy_usb_instance *inst, bool isolate);
- void (*phy_init)(struct exynos5_usbdrd_phy *phy_drd);
+ int (*phy_init)(struct exynos5_usbdrd_phy *phy_drd);
unsigned int (*set_refclk)(struct phy_usb_instance *inst);
};
@@ -708,7 +708,7 @@ exynos5_usbdrd_apply_phy_tunes(struct exynos5_usbdrd_phy *phy_drd,
}
}
-static void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
+static int exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
{
u32 reg;
@@ -721,6 +721,8 @@ static void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
reg &= ~PHYTEST_POWERDOWN_SSP;
writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
+
+ return 0;
}
static void
@@ -831,7 +833,7 @@ exynos5_usbdrd_usbdp_g2_v4_pma_check_pll_lock(struct exynos5_usbdrd_phy *phy_drd
return err;
}
-static void
+static int
exynos5_usbdrd_usbdp_g2_v4_pma_check_cdr_lock(struct exynos5_usbdrd_phy *phy_drd)
{
static const unsigned int timeout_us = 40000;
@@ -857,9 +859,11 @@ exynos5_usbdrd_usbdp_g2_v4_pma_check_cdr_lock(struct exynos5_usbdrd_phy *phy_drd
((phy_drd->orientation == TYPEC_ORIENTATION_NORMAL)
? 0
: 2), reg);
+
+ return err;
}
-static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
+static int exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
{
u32 reg;
@@ -881,6 +885,8 @@ static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
reg &= ~PHYTEST_POWERDOWN_HSP;
writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
+
+ return 0;
}
static int exynos5_usbdrd_phy_init(struct phy *phy)
@@ -917,7 +923,9 @@ static int exynos5_usbdrd_phy_init(struct phy *phy)
writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL);
/* UTMI or PIPE3 specific init */
- inst->phy_cfg->phy_init(phy_drd);
+ ret = inst->phy_cfg->phy_init(phy_drd);
+ if (ret)
+ goto disable_clks;
/* reference clock settings */
reg = inst->phy_cfg->set_refclk(inst);
@@ -940,9 +948,10 @@ static int exynos5_usbdrd_phy_init(struct phy *phy)
reg &= ~PHYCLKRST_PORTRESET;
writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
+disable_clks:
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
- return 0;
+ return ret;
}
static int exynos5_usbdrd_phy_exit(struct phy *phy)
@@ -1203,7 +1212,7 @@ static void exynos7870_usbdrd_phy_isol(struct phy_usb_instance *inst,
EXYNOS7870_USB2PHY_ENABLE, val);
}
-static void exynos7870_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
+static int exynos7870_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
{
u32 reg;
@@ -1291,6 +1300,8 @@ static void exynos7870_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
if (phy_drd->drv_data->phy_tunes)
exynos5_usbdrd_apply_phy_tunes(phy_drd,
PTS_UTMI_POSTINIT);
+
+ return 0;
}
static int exynos7870_usbdrd_phy_init(struct phy *phy)
@@ -1304,11 +1315,11 @@ static int exynos7870_usbdrd_phy_init(struct phy *phy)
return ret;
/* UTMI or PIPE3 specific init */
- inst->phy_cfg->phy_init(phy_drd);
+ ret = inst->phy_cfg->phy_init(phy_drd);
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
- return 0;
+ return ret;
}
static int exynos7870_usbdrd_phy_exit(struct phy *phy)
@@ -1355,10 +1366,12 @@ static const struct phy_ops exynos7870_usbdrd_phy_ops = {
.owner = THIS_MODULE,
};
-static void exynos2200_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
+static int exynos2200_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
{
/* Configure non-Samsung IP PHY, responsible for UTMI */
phy_init(phy_drd->hs_phy);
+
+ return 0;
}
static void exynos2200_usbdrd_link_init(struct exynos5_usbdrd_phy *phy_drd)
@@ -1458,11 +1471,11 @@ static int exynos2200_usbdrd_phy_init(struct phy *phy)
exynos2200_usbdrd_link_attach_detach_pipe3_phy(inst);
/* UTMI or PIPE3 specific init */
- inst->phy_cfg->phy_init(phy_drd);
+ ret = inst->phy_cfg->phy_init(phy_drd);
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
- return 0;
+ return ret;
}
static int exynos2200_usbdrd_phy_exit(struct phy *phy)
@@ -1516,7 +1529,7 @@ exynos5_usbdrd_usb_v3p1_pipe_override(struct exynos5_usbdrd_phy *phy_drd)
writel(reg, regs_base + EXYNOS850_DRD_SECPMACTL);
}
-static void exynos850_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
+static int exynos850_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
{
void __iomem *regs_base = phy_drd->reg_phy;
u32 reg;
@@ -1622,6 +1635,8 @@ static void exynos850_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
if (ss_ports)
exynos5_usbdrd_usb_v3p1_pipe_override(phy_drd);
+
+ return 0;
}
static int exynos850_usbdrd_phy_init(struct phy *phy)
@@ -1635,12 +1650,13 @@ static int exynos850_usbdrd_phy_init(struct phy *phy)
return ret;
/* UTMI or PIPE3 specific init */
+ ret = 0;
scoped_guard(mutex, &phy_drd->phy_mutex)
- inst->phy_cfg->phy_init(phy_drd);
+ ret = inst->phy_cfg->phy_init(phy_drd);
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
- return 0;
+ return ret;
}
static int exynos850_usbdrd_phy_exit(struct phy *phy)
@@ -1689,11 +1705,12 @@ static const struct phy_ops exynos850_usbdrd_phy_ops = {
.owner = THIS_MODULE,
};
-static void exynos5_usbdrd_gs101_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
+static int exynos5_usbdrd_gs101_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
{
void __iomem *regs_pma = phy_drd->reg_pma;
void __iomem *regs_phy = phy_drd->reg_phy;
u32 reg;
+ int ret;
exynos5_usbdrd_usbdp_g2_v4_ctrl_pma_ready(phy_drd);
@@ -1715,8 +1732,11 @@ static void exynos5_usbdrd_gs101_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
SECPMACTL_PMA_INIT_SW_RST);
writel(reg, regs_phy + EXYNOS850_DRD_SECPMACTL);
- if (!exynos5_usbdrd_usbdp_g2_v4_pma_check_pll_lock(phy_drd))
- exynos5_usbdrd_usbdp_g2_v4_pma_check_cdr_lock(phy_drd);
+ ret = exynos5_usbdrd_usbdp_g2_v4_pma_check_pll_lock(phy_drd);
+ if (ret)
+ return ret;
+
+ return exynos5_usbdrd_usbdp_g2_v4_pma_check_cdr_lock(phy_drd);
}
static int exynos5_usbdrd_gs101_phy_init(struct phy *phy)
@@ -1741,7 +1761,15 @@ static int exynos5_usbdrd_gs101_phy_init(struct phy *phy)
*/
exynos5_usbdrd_phy_isol(inst, false);
- return exynos850_usbdrd_phy_init(phy);
+ ret = exynos850_usbdrd_phy_init(phy);
+ if (ret) {
+ exynos5_usbdrd_phy_isol(inst, true);
+ if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI)
+ regulator_bulk_disable(phy_drd->drv_data->n_regulators,
+ phy_drd->regulators);
+ }
+
+ return ret;
}
static int exynos5_usbdrd_gs101_phy_exit(struct phy *phy)
@@ -2277,7 +2305,7 @@ exynosautov920_usb31drd_lane0_reset(struct exynos5_usbdrd_phy *phy_drd, int val)
writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_RST_CTRL);
}
-static void
+static int
exynosautov920_usb31drd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
{
void __iomem *reg_phy = phy_drd->reg_phy;
@@ -2335,6 +2363,8 @@ exynosautov920_usb31drd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
/* override values for level settings */
exynosautov920_usb31drd_cr_write(phy_drd, 0x22, 0x00F5);
+
+ return 0;
}
static void
@@ -2353,7 +2383,7 @@ exynosautov920_usb31drd_ssphy_disable(struct exynos5_usbdrd_phy *phy_drd)
writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CONFIG7);
}
-static void
+static int
exynosautov920_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
{
void __iomem *reg_phy = phy_drd->reg_phy;
@@ -2457,6 +2487,8 @@ exynosautov920_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
reg = readl(reg_phy + EXYNOS2200_DRD_CLKRST);
reg |= EXYNOS2200_CLKRST_LINK_PCLK_SEL;
writel(reg, reg_phy + EXYNOS2200_DRD_CLKRST);
+
+ return 0;
}
static void
@@ -2504,11 +2536,11 @@ static int exynosautov920_usbdrd_phy_init(struct phy *phy)
inst->phy_cfg->phy_isol(inst, false);
/* UTMI or PIPE3 specific init */
- inst->phy_cfg->phy_init(phy_drd);
+ ret = inst->phy_cfg->phy_init(phy_drd);
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
- return 0;
+ return ret;
}
static int exynosautov920_usbdrd_phy_exit(struct phy *phy)
diff --git a/drivers/phy/socionext/phy-uniphier-usb3hs.c b/drivers/phy/socionext/phy-uniphier-usb3hs.c
index 8c8673df0084..c8971eee3640 100644
--- a/drivers/phy/socionext/phy-uniphier-usb3hs.c
+++ b/drivers/phy/socionext/phy-uniphier-usb3hs.c
@@ -104,11 +104,12 @@ static int uniphier_u3hsphy_get_nvparam(struct uniphier_u3hsphy_priv *priv,
struct nvmem_cell *cell;
u8 *buf;
- cell = devm_nvmem_cell_get(priv->dev, name);
+ cell = nvmem_cell_get(priv->dev, name);
if (IS_ERR(cell))
return PTR_ERR(cell);
buf = nvmem_cell_read(cell, NULL);
+ nvmem_cell_put(cell);
if (IS_ERR(buf))
return PTR_ERR(buf);
diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig
index 5fdf18fce499..b5f36621b207 100644
--- a/drivers/phy/spacemit/Kconfig
+++ b/drivers/phy/spacemit/Kconfig
@@ -18,8 +18,11 @@ config PHY_SPACEMIT_K1_USB2
tristate "SpacemiT K1 USB 2.0 PHY support"
depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF
depends on COMMON_CLK
+ depends on HAS_IOMEM
depends on USB_COMMON
select GENERIC_PHY
+ select REGMAP_MMIO
+ default ARCH_SPACEMIT
help
Enable this to support K1 USB 2.0 PHY driver. This driver takes care of
enabling and clock setup and will be used by K1 udc/ehci/otg/xhci driver.
diff --git a/drivers/phy/starfive/phy-jh7110-dphy-tx.c b/drivers/phy/starfive/phy-jh7110-dphy-tx.c
index 181491a93807..e9ffd0f811d2 100644
--- a/drivers/phy/starfive/phy-jh7110-dphy-tx.c
+++ b/drivers/phy/starfive/phy-jh7110-dphy-tx.c
@@ -209,7 +209,7 @@ static u32 stf_dphy_get_config_index(u32 bitrate)
return 0;
}
-static void stf_dphy_hw_reset(struct stf_dphy *dphy, int assert)
+static int stf_dphy_hw_reset(struct stf_dphy *dphy, int assert)
{
int rc;
u32 status = 0;
@@ -224,8 +224,12 @@ static void stf_dphy_hw_reset(struct stf_dphy *dphy, int assert)
!(FIELD_GET(STF_DPHY_RGS_CDTX_PLL_UNLOCK, status)),
STF_DPHY_HW_DELAY_US, STF_DPHY_HW_TIMEOUT_US);
if (rc)
- dev_err(dphy->dev, "MIPI dphy-tx # PLL Locked\n");
+ dev_err(dphy->dev, "MIPI dphy-tx PLL failed to lock\n");
+
+ return rc;
}
+
+ return 0;
}
static int stf_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
@@ -313,7 +317,9 @@ static int stf_dphy_init(struct phy *phy)
struct stf_dphy *dphy = phy_get_drvdata(phy);
int ret;
- stf_dphy_hw_reset(dphy, 1);
+ ret = stf_dphy_hw_reset(dphy, 1);
+ if (ret)
+ return ret;
writel(FIELD_PREP(STF_DPHY_SCFG_PPI_C_READY_SEL, 0) |
FIELD_PREP(STF_DPHY_SCFG_DSI_TXREADY_ESC_SEL, 0),
@@ -350,7 +356,9 @@ static int stf_dphy_exit(struct phy *phy)
clk_disable_unprepare(dphy->txesc_clk);
- stf_dphy_hw_reset(dphy, 0);
+ ret = stf_dphy_hw_reset(dphy, 0);
+ if (ret)
+ return ret;
return 0;
}
diff --git a/drivers/phy/tegra/Kconfig b/drivers/phy/tegra/Kconfig
index f0734415fc94..b5ed5bc0c8ef 100644
--- a/drivers/phy/tegra/Kconfig
+++ b/drivers/phy/tegra/Kconfig
@@ -18,3 +18,14 @@ config PHY_TEGRA194_P2U
help
Enable this to support the P2U (PIPE to UPHY) that is part of Tegra 19x
and 234 SOCs.
+
+config PHY_TEGRA_MPHY
+ tristate "NVIDIA Tegra264 MPHY driver"
+ depends on ARCH_TEGRA_264_SOC || COMPILE_TEST
+ select GENERIC_PHY
+ help
+ Enable this to support the MIPI M-PHY on NVIDIA Tegra264 SoCs.
+ The M-PHY provides the physical layer for the on-die UFS host
+ controller. The driver manages the shared clock-control block
+ and per-lane resets, exposing four sub-phys (L0-TX, L0-RX,
+ L1-TX, L1-RX) consumed by the UFS host driver.
diff --git a/drivers/phy/tegra/Makefile b/drivers/phy/tegra/Makefile
index eeeea72de117..018a4cab2b40 100644
--- a/drivers/phy/tegra/Makefile
+++ b/drivers/phy/tegra/Makefile
@@ -8,4 +8,7 @@ phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_210_SOC) += xusb-tegra210.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_186_SOC) += xusb-tegra186.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_194_SOC) += xusb-tegra186.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_234_SOC) += xusb-tegra186.o
+phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_238_SOC) += xusb-tegra186.o
+phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_264_SOC) += xusb-tegra186.o
obj-$(CONFIG_PHY_TEGRA194_P2U) += phy-tegra194-p2u.o
+obj-$(CONFIG_PHY_TEGRA_MPHY) += phy-tegra-mphy.o
diff --git a/drivers/phy/tegra/phy-tegra-mphy.c b/drivers/phy/tegra/phy-tegra-mphy.c
new file mode 100644
index 000000000000..01cafe1c7477
--- /dev/null
+++ b/drivers/phy/tegra/phy-tegra-mphy.c
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+// NVIDIA Tegra264 MPHY driver.
+
+#include <dt-bindings/phy/nvidia,tegra264-mphy.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#define MPHY_GO_BIT BIT(0)
+
+#define MPHY_RX_APB_CAPABILITY_88_8B 0x88
+#define RX_HS_G1_SYNC_LENGTH_CAPABILITY(x) (((x) & 0x3f) << 24)
+
+#define MPHY_RX_APB_CAPABILITY_94_97 0x94
+#define RX_HS_G3_SYNC_LENGTH_CAPABILITY(x) (((x) & 0x3f) << 8)
+#define RX_HS_G2_SYNC_LENGTH_CAPABILITY(x) (((x) & 0x3f) << 0)
+
+#define MPHY_TX_APB_VENDOR0 0x100
+#define MPHY_TX_APB_VENDOR2 0x108
+#define TX_CAL_DONE BIT(19)
+#define TX_CAL_EN BIT(15)
+
+#define MPHY_RX_APB_VENDOR2 0x184
+#define RX_CAL_DONE BIT(19)
+#define RX_CAL_EN BIT(15)
+
+#define MPHY_RX_APB_VENDOR3 0x188
+#define RX_MPHY2UPHY_IF_OVR_CTRL BIT(26)
+
+#define MPHY_RX_APB_VENDOR3B 0x220
+#define MPHY_RX_APB_VENDOR49 0x254
+
+#define MPHY_EQ_TIMEOUT 0xffffffff
+#define MPHY_PWR_CHANGE_CLK_BOOST 0x0017
+
+#define MPHY_TX_OFFSET 0x1000
+#define MPHY_RX_OFFSET 0x2000
+
+struct tegra_mphy_lane {
+ void __iomem *regs;
+ void __iomem *tx_regs;
+ void __iomem *rx_regs;
+
+ struct reset_control *rst_rx;
+ struct reset_control *rst_tx;
+
+ struct phy *tx;
+ struct phy *rx;
+};
+
+struct tegra_mphy {
+ struct device *dev;
+
+ struct tegra_mphy_lane l0;
+ struct tegra_mphy_lane l1;
+
+ unsigned int num_clks;
+ struct clk_bulk_data *clks;
+
+ struct reset_control *rst_clk_ctl;
+
+ unsigned int power_count;
+};
+
+static int tegra_mphy_get_clocks(struct tegra_mphy *mphy)
+{
+ struct device *dev = mphy->dev;
+ int num_clks;
+
+ num_clks = devm_clk_bulk_get_all(dev, &mphy->clks);
+ if (num_clks < 0)
+ return dev_err_probe(dev, num_clks, "failed to get clocks\n");
+
+ mphy->num_clks = num_clks;
+
+ return 0;
+}
+
+static int tegra_mphy_get_resets(struct tegra_mphy *mphy)
+{
+ struct device *dev = mphy->dev;
+
+ mphy->rst_clk_ctl = devm_reset_control_get_exclusive(dev, "clk-ctl");
+ if (IS_ERR(mphy->rst_clk_ctl))
+ return dev_err_probe(dev, PTR_ERR(mphy->rst_clk_ctl),
+ "failed to get clk-ctl reset\n");
+
+ mphy->l0.rst_rx = devm_reset_control_get_exclusive(dev, "l0-rx");
+ if (IS_ERR(mphy->l0.rst_rx))
+ return dev_err_probe(dev, PTR_ERR(mphy->l0.rst_rx), "failed to get l0-rx reset\n");
+
+ mphy->l0.rst_tx = devm_reset_control_get_exclusive(dev, "l0-tx");
+ if (IS_ERR(mphy->l0.rst_tx))
+ return dev_err_probe(dev, PTR_ERR(mphy->l0.rst_tx), "failed to get l0-tx reset\n");
+
+ mphy->l1.rst_rx = devm_reset_control_get_exclusive(dev, "l1-rx");
+ if (IS_ERR(mphy->l1.rst_rx))
+ return dev_err_probe(dev, PTR_ERR(mphy->l1.rst_rx), "failed to get l1-rx reset\n");
+
+ mphy->l1.rst_tx = devm_reset_control_get_exclusive(dev, "l1-tx");
+ if (IS_ERR(mphy->l1.rst_tx))
+ return dev_err_probe(dev, PTR_ERR(mphy->l1.rst_tx), "failed to get l1-tx reset\n");
+
+ return 0;
+}
+
+static int tegra_mphy_rx_write_kick_go(struct tegra_mphy_lane *lane, u32 offset, u32 value)
+{
+ u32 v;
+
+ writel(value, lane->rx_regs + offset);
+
+ v = readl(lane->rx_regs + MPHY_RX_APB_VENDOR2);
+ v |= MPHY_GO_BIT;
+ writel(v, lane->rx_regs + MPHY_RX_APB_VENDOR2);
+
+ return readl_poll_timeout(lane->rx_regs + MPHY_RX_APB_VENDOR2, v,
+ (v & MPHY_GO_BIT) == 0, 25, 5000);
+}
+
+static int tegra_mphy_rx_power_on(struct phy *phy)
+{
+ struct tegra_mphy *mphy = dev_get_drvdata(phy->dev.parent);
+ struct tegra_mphy_lane *lane = phy_get_drvdata(phy);
+ u32 value;
+ int err;
+
+ if (mphy->power_count++ == 0) {
+ err = clk_bulk_prepare_enable(mphy->num_clks, mphy->clks);
+ if (err) {
+ mphy->power_count--;
+ dev_err(&phy->dev, "failed to enable clocks: %d\n", err);
+ return err;
+ }
+ reset_control_deassert(mphy->rst_clk_ctl);
+ }
+
+ reset_control_deassert(lane->rst_rx);
+ reset_control_deassert(lane->rst_tx);
+
+ err = tegra_mphy_rx_write_kick_go(lane, MPHY_RX_APB_VENDOR3B, MPHY_EQ_TIMEOUT);
+ if (err) {
+ dev_err(&phy->dev, "eq_timeout programming failed: %d\n", err);
+ goto err_reset;
+ }
+
+ err = tegra_mphy_rx_write_kick_go(lane, MPHY_RX_APB_VENDOR49, MPHY_PWR_CHANGE_CLK_BOOST);
+ if (err) {
+ dev_err(&phy->dev, "pwr_change_clk_boost programming failed: %d\n", err);
+ goto err_reset;
+ }
+
+ value = readl(lane->regs + MPHY_RX_APB_CAPABILITY_88_8B);
+ value &= ~RX_HS_G1_SYNC_LENGTH_CAPABILITY(~0);
+ value |= RX_HS_G1_SYNC_LENGTH_CAPABILITY(0xf);
+ writel(value, lane->regs + MPHY_RX_APB_CAPABILITY_88_8B);
+
+ value = readl(lane->regs + MPHY_RX_APB_CAPABILITY_94_97);
+ value &= ~RX_HS_G3_SYNC_LENGTH_CAPABILITY(~0);
+ value |= RX_HS_G3_SYNC_LENGTH_CAPABILITY(0xf);
+ value &= ~RX_HS_G2_SYNC_LENGTH_CAPABILITY(~0);
+ value |= RX_HS_G2_SYNC_LENGTH_CAPABILITY(0xf);
+ writel(value, lane->regs + MPHY_RX_APB_CAPABILITY_94_97);
+
+ value = readl(lane->rx_regs + MPHY_RX_APB_VENDOR3);
+ value |= RX_MPHY2UPHY_IF_OVR_CTRL;
+ writel(value, lane->rx_regs + MPHY_RX_APB_VENDOR3);
+
+ value = readl(lane->rx_regs + MPHY_RX_APB_VENDOR2);
+ value |= MPHY_GO_BIT;
+ writel(value, lane->rx_regs + MPHY_RX_APB_VENDOR2);
+
+ err = readl_poll_timeout(lane->rx_regs + MPHY_RX_APB_VENDOR2, value,
+ (value & MPHY_GO_BIT) == 0, 25, 5000);
+ if (err) {
+ dev_err(&phy->dev, "RX cap update failed: %d\n", err);
+ goto err_reset;
+ }
+
+ return 0;
+
+err_reset:
+ reset_control_assert(lane->rst_rx);
+ reset_control_assert(lane->rst_tx);
+
+ if (--mphy->power_count == 0) {
+ reset_control_assert(mphy->rst_clk_ctl);
+ clk_bulk_disable_unprepare(mphy->num_clks, mphy->clks);
+ }
+
+ return err;
+}
+
+static int tegra_mphy_rx_power_off(struct phy *phy)
+{
+ struct tegra_mphy *mphy = dev_get_drvdata(phy->dev.parent);
+ struct tegra_mphy_lane *lane = phy_get_drvdata(phy);
+
+ if (WARN_ON(mphy->power_count == 0))
+ return -EINVAL;
+
+ reset_control_assert(lane->rst_rx);
+ reset_control_assert(lane->rst_tx);
+
+ if (--mphy->power_count == 0)
+ clk_bulk_disable_unprepare(mphy->num_clks, mphy->clks);
+
+ return 0;
+}
+
+static int tegra_mphy_rx_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+ struct tegra_mphy_lane *lane = phy_get_drvdata(phy);
+ u32 value;
+ int err;
+
+ value = readl(lane->rx_regs + MPHY_RX_APB_VENDOR2);
+ value |= RX_CAL_EN;
+ writel(value, lane->rx_regs + MPHY_RX_APB_VENDOR2);
+
+ value = readl(lane->rx_regs + MPHY_RX_APB_VENDOR2);
+ value |= MPHY_GO_BIT;
+ writel(value, lane->rx_regs + MPHY_RX_APB_VENDOR2);
+
+ err = readl_poll_timeout(lane->rx_regs + MPHY_RX_APB_VENDOR2, value,
+ (value & MPHY_GO_BIT) == 0, 25, 5000);
+ if (err)
+ dev_err(&phy->dev, "failed to arm RX calibration: %d\n", err);
+
+ return err;
+}
+
+static int tegra_mphy_rx_calibrate(struct phy *phy)
+{
+ struct tegra_mphy_lane *lane = phy_get_drvdata(phy);
+ u32 value;
+ int err;
+
+ err = readl_poll_timeout(lane->rx_regs + MPHY_RX_APB_VENDOR2, value,
+ (value & RX_CAL_DONE) != 0, 25, 100000);
+ if (err) {
+ dev_err(&phy->dev, "RX calibration timed out: %d\n", err);
+ return err;
+ }
+
+ value = readl(lane->rx_regs + MPHY_RX_APB_VENDOR2);
+ value &= ~RX_CAL_EN;
+ writel(value, lane->rx_regs + MPHY_RX_APB_VENDOR2);
+
+ value = readl(lane->rx_regs + MPHY_RX_APB_VENDOR2);
+ value |= MPHY_GO_BIT;
+ writel(value, lane->rx_regs + MPHY_RX_APB_VENDOR2);
+
+ err = readl_poll_timeout(lane->rx_regs + MPHY_RX_APB_VENDOR2, value,
+ (value & MPHY_GO_BIT) == 0, 25, 5000);
+ if (err) {
+ dev_err(&phy->dev, "failed to clear RX calibration: %d\n", err);
+ return err;
+ }
+
+ err = readl_poll_timeout(lane->rx_regs + MPHY_RX_APB_VENDOR2, value,
+ (value & RX_CAL_DONE) == 0, 25, 100000);
+ if (err)
+ dev_err(&phy->dev, "RX calibration failed to clear: %d\n", err);
+
+ return err;
+}
+
+static const struct phy_ops tegra_mphy_rx_ops = {
+ .power_on = tegra_mphy_rx_power_on,
+ .power_off = tegra_mphy_rx_power_off,
+ .configure = tegra_mphy_rx_configure,
+ .calibrate = tegra_mphy_rx_calibrate,
+};
+
+static int tegra_mphy_tx_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+ struct tegra_mphy_lane *lane = phy_get_drvdata(phy);
+ u32 value;
+ int err;
+
+ value = readl(lane->tx_regs + MPHY_TX_APB_VENDOR2);
+ value |= TX_CAL_EN;
+ writel(value, lane->tx_regs + MPHY_TX_APB_VENDOR2);
+
+ value = readl(lane->tx_regs + MPHY_TX_APB_VENDOR0);
+ value |= MPHY_GO_BIT;
+ writel(value, lane->tx_regs + MPHY_TX_APB_VENDOR0);
+
+ err = readl_poll_timeout(lane->tx_regs + MPHY_TX_APB_VENDOR0, value,
+ (value & MPHY_GO_BIT) == 0, 25, 5000);
+ if (err)
+ dev_err(&phy->dev, "failed to arm TX calibration: %d\n", err);
+
+ return err;
+}
+
+static int tegra_mphy_tx_calibrate(struct phy *phy)
+{
+ struct tegra_mphy_lane *lane = phy_get_drvdata(phy);
+ u32 value;
+ int err;
+
+ err = readl_poll_timeout(lane->tx_regs + MPHY_TX_APB_VENDOR2, value,
+ (value & TX_CAL_DONE) != 0, 25, 50000);
+ if (err) {
+ dev_err(&phy->dev, "TX calibration timed out: %d\n", err);
+ return err;
+ }
+
+ value = readl(lane->tx_regs + MPHY_TX_APB_VENDOR2);
+ value &= ~TX_CAL_EN;
+ writel(value, lane->tx_regs + MPHY_TX_APB_VENDOR2);
+
+ value = readl(lane->tx_regs + MPHY_TX_APB_VENDOR0);
+ value |= MPHY_GO_BIT;
+ writel(value, lane->tx_regs + MPHY_TX_APB_VENDOR0);
+
+ err = readl_poll_timeout(lane->tx_regs + MPHY_TX_APB_VENDOR0, value,
+ (value & MPHY_GO_BIT) == 0, 25, 5000);
+ if (err) {
+ dev_err(&phy->dev, "GO bit failed to clear: %d\n", err);
+ return err;
+ }
+
+ err = readl_poll_timeout(lane->tx_regs + MPHY_TX_APB_VENDOR2, value,
+ (value & TX_CAL_DONE) == 0, 25, 50000);
+ if (err)
+ dev_err(&phy->dev, "TX calibration failed: %d\n", err);
+
+ return err;
+}
+
+static const struct phy_ops tegra_mphy_tx_ops = {
+ .configure = tegra_mphy_tx_configure,
+ .calibrate = tegra_mphy_tx_calibrate,
+};
+
+static struct phy *tegra_mphy_xlate(struct device *dev, const struct of_phandle_args *args)
+{
+ struct tegra_mphy *mphy = dev_get_drvdata(dev);
+
+ if (args->args_count != 1)
+ return ERR_PTR(-EINVAL);
+
+ switch (args->args[0]) {
+ case TEGRA_MPHY_L0_TX:
+ return mphy->l0.tx;
+ case TEGRA_MPHY_L0_RX:
+ return mphy->l0.rx;
+ case TEGRA_MPHY_L1_TX:
+ return mphy->l1.tx;
+ case TEGRA_MPHY_L1_RX:
+ return mphy->l1.rx;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+static int tegra_mphy_create_phys(struct tegra_mphy *mphy)
+{
+ struct device *dev = mphy->dev;
+
+ mphy->l0.tx = devm_phy_create(dev, dev->of_node, &tegra_mphy_tx_ops);
+ if (IS_ERR(mphy->l0.tx))
+ return dev_err_probe(dev, PTR_ERR(mphy->l0.tx), "failed to create l0-tx PHY\n");
+ phy_set_drvdata(mphy->l0.tx, &mphy->l0);
+
+ mphy->l0.rx = devm_phy_create(dev, dev->of_node, &tegra_mphy_rx_ops);
+ if (IS_ERR(mphy->l0.rx))
+ return dev_err_probe(dev, PTR_ERR(mphy->l0.rx), "failed to create l0-rx PHY\n");
+ phy_set_drvdata(mphy->l0.rx, &mphy->l0);
+
+ mphy->l1.tx = devm_phy_create(dev, dev->of_node, &tegra_mphy_tx_ops);
+ if (IS_ERR(mphy->l1.tx))
+ return dev_err_probe(dev, PTR_ERR(mphy->l1.tx), "failed to create l1-tx PHY\n");
+ phy_set_drvdata(mphy->l1.tx, &mphy->l1);
+
+ mphy->l1.rx = devm_phy_create(dev, dev->of_node, &tegra_mphy_rx_ops);
+ if (IS_ERR(mphy->l1.rx))
+ return dev_err_probe(dev, PTR_ERR(mphy->l1.rx), "failed to create l1-rx PHY\n");
+ phy_set_drvdata(mphy->l1.rx, &mphy->l1);
+
+ return 0;
+}
+
+static int tegra_mphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct phy_provider *provider;
+ struct tegra_mphy *mphy;
+ int err;
+
+ mphy = devm_kzalloc(dev, sizeof(*mphy), GFP_KERNEL);
+ if (!mphy)
+ return -ENOMEM;
+
+ mphy->dev = dev;
+
+ mphy->l0.regs = devm_platform_ioremap_resource_byname(pdev, "l0");
+ if (IS_ERR(mphy->l0.regs))
+ return PTR_ERR(mphy->l0.regs);
+
+ mphy->l1.regs = devm_platform_ioremap_resource_byname(pdev, "l1");
+ if (IS_ERR(mphy->l1.regs))
+ return PTR_ERR(mphy->l1.regs);
+
+ mphy->l0.tx_regs = mphy->l0.regs + MPHY_TX_OFFSET;
+ mphy->l0.rx_regs = mphy->l0.regs + MPHY_RX_OFFSET;
+
+ mphy->l1.tx_regs = mphy->l1.regs + MPHY_TX_OFFSET;
+ mphy->l1.rx_regs = mphy->l1.regs + MPHY_RX_OFFSET;
+
+ err = tegra_mphy_get_clocks(mphy);
+ if (err)
+ return err;
+
+ err = tegra_mphy_get_resets(mphy);
+ if (err)
+ return err;
+
+ err = tegra_mphy_create_phys(mphy);
+ if (err)
+ return err;
+
+ platform_set_drvdata(pdev, mphy);
+
+ provider = devm_of_phy_provider_register(dev, tegra_mphy_xlate);
+ if (IS_ERR(provider))
+ return dev_err_probe(dev, PTR_ERR(provider),
+ "failed to register PHY\n");
+
+ return 0;
+}
+
+static const struct of_device_id tegra_mphy_of_table[] = {
+ { .compatible = "nvidia,tegra264-mphy" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tegra_mphy_of_table);
+
+static struct platform_driver tegra_mphy_driver = {
+ .driver = {
+ .name = "tegra-mphy",
+ .of_match_table = tegra_mphy_of_table,
+ },
+ .probe = tegra_mphy_probe,
+};
+module_platform_driver(tegra_mphy_driver);
+
+MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
+MODULE_AUTHOR("Kartik Rajput <kkartik@nvidia.com>");
+MODULE_DESCRIPTION("NVIDIA Tegra MPHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c
index 60156aea2707..aa8ab94fad4c 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -636,7 +636,7 @@ static void tegra186_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl)
if (padctl->soc->poll_trk_completed) {
err = padctl_readl_poll(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1,
- USB2_TRK_COMPLETED, USB2_TRK_COMPLETED, 100);
+ USB2_TRK_COMPLETED, USB2_TRK_COMPLETED, 200);
if (err) {
/* The failure with polling on trk complete will not
* cause the failure of powering on the bias pad.
@@ -1045,7 +1045,7 @@ tegra186_usb2_pad_probe(struct tegra_xusb_padctl *padctl,
goto out;
}
- priv->usb2_trk_clk = devm_clk_get(&pad->dev, "trk");
+ priv->usb2_trk_clk = devm_clk_get_optional(&pad->dev, "trk");
if (IS_ERR(priv->usb2_trk_clk)) {
err = PTR_ERR(priv->usb2_trk_clk);
dev_dbg(&pad->dev, "failed to get usb2 trk clock: %d\n", err);
@@ -1599,7 +1599,6 @@ static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = {
.utmi_pad_power_down = tegra186_utmi_pad_power_down,
};
-#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
static const char * const tegra186_xusb_padctl_supply_names[] = {
"avdd-pll-erefeut",
"avdd-usb",
@@ -1665,10 +1664,7 @@ const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = {
.num_supplies = ARRAY_SIZE(tegra186_xusb_padctl_supply_names),
};
EXPORT_SYMBOL_GPL(tegra186_xusb_padctl_soc);
-#endif
-#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \
- IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
static const char * const tegra194_xusb_padctl_supply_names[] = {
"avdd-usb",
"vclamp-usb",
@@ -1753,7 +1749,53 @@ const struct tegra_xusb_padctl_soc tegra234_xusb_padctl_soc = {
.has_per_pad_term = true,
};
EXPORT_SYMBOL_GPL(tegra234_xusb_padctl_soc);
-#endif
+
+const struct tegra_xusb_padctl_soc tegra238_xusb_padctl_soc = {
+ .num_pads = ARRAY_SIZE(tegra186_pads),
+ .pads = tegra186_pads,
+ .ports = {
+ .usb2 = {
+ .ops = &tegra186_usb2_port_ops,
+ .count = 3,
+ },
+ .usb3 = {
+ .ops = &tegra186_usb3_port_ops,
+ .count = 3,
+ },
+ },
+ .ops = &tegra186_xusb_padctl_ops,
+ .supply_names = tegra194_xusb_padctl_supply_names,
+ .num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
+ .supports_gen2 = true,
+ .poll_trk_completed = true,
+ .trk_hw_mode = false,
+ .supports_lp_cfg_en = true,
+};
+EXPORT_SYMBOL_GPL(tegra238_xusb_padctl_soc);
+
+const struct tegra_xusb_padctl_soc tegra264_xusb_padctl_soc = {
+ .num_pads = ARRAY_SIZE(tegra194_pads),
+ .pads = tegra194_pads,
+ .ports = {
+ .usb2 = {
+ .ops = &tegra186_usb2_port_ops,
+ .count = 4,
+ },
+ .usb3 = {
+ .ops = &tegra186_usb3_port_ops,
+ .count = 4,
+ },
+ },
+ .ops = &tegra186_xusb_padctl_ops,
+ .supply_names = tegra194_xusb_padctl_supply_names,
+ .num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
+ .supports_gen2 = true,
+ .poll_trk_completed = true,
+ .trk_hw_mode = true,
+ .supports_lp_cfg_en = true,
+ .has_per_pad_term = true,
+};
+EXPORT_SYMBOL_GPL(tegra264_xusb_padctl_soc);
MODULE_AUTHOR("JC Kuo <jckuo@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra186 XUSB Pad Controller driver");
diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index 9d74c0ecc31b..89ce9ac87300 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -78,6 +78,18 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
.data = &tegra234_xusb_padctl_soc,
},
#endif
+#if defined(CONFIG_ARCH_TEGRA_238_SOC)
+ {
+ .compatible = "nvidia,tegra238-xusb-padctl",
+ .data = &tegra238_xusb_padctl_soc,
+ },
+#endif
+#if defined(CONFIG_ARCH_TEGRA_264_SOC)
+ {
+ .compatible = "nvidia,tegra264-xusb-padctl",
+ .data = &tegra264_xusb_padctl_soc,
+ },
+#endif
{ }
};
MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match);
@@ -1251,13 +1263,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
err = tegra_xusb_setup_ports(padctl);
if (err) {
- const char *level = KERN_ERR;
-
- if (err == -EPROBE_DEFER)
- level = KERN_DEBUG;
-
- dev_printk(level, &pdev->dev,
- dev_fmt("failed to setup XUSB ports: %d\n"), err);
+ dev_err_probe(&pdev->dev, err, "failed to setup XUSB ports\n");
goto remove_pads;
}
diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
index 77609e54de66..b9de4f3b5781 100644
--- a/drivers/phy/tegra/xusb.h
+++ b/drivers/phy/tegra/xusb.h
@@ -514,5 +514,11 @@ extern const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc;
#if defined(CONFIG_ARCH_TEGRA_234_SOC)
extern const struct tegra_xusb_padctl_soc tegra234_xusb_padctl_soc;
#endif
+#if defined(CONFIG_ARCH_TEGRA_238_SOC)
+extern const struct tegra_xusb_padctl_soc tegra238_xusb_padctl_soc;
+#endif
+#if defined(CONFIG_ARCH_TEGRA_264_SOC)
+extern const struct tegra_xusb_padctl_soc tegra264_xusb_padctl_soc;
+#endif
#endif /* __PHY_TEGRA_XUSB_H */