diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-28 07:21:13 -1000 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-28 07:21:13 -1000 |
| commit | e191b05f321b064a973399b7cdc003c0de89e5b9 (patch) | |
| tree | 3d39ae34f3c0662113ee33edb6f4f539499ddb78 /drivers | |
| parent | 1cb6cfab0916d18f88d44e4a23806b6c4cb355f8 (diff) | |
| parent | 8f56fdf3d8cb9f1dbdc93bf9ac816698aa6f70b7 (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')
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 |= 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 */ |
