diff options
Diffstat (limited to 'drivers/phy')
| -rw-r--r-- | drivers/phy/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/phy/Makefile | 1 | ||||
| -rw-r--r-- | drivers/phy/phy-mtk-ufs.c | 128 | ||||
| -rw-r--r-- | drivers/phy/phy-mtk-xsphy.c | 600 | ||||
| -rw-r--r-- | drivers/phy/qcom/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/phy/qcom/Makefile | 1 | ||||
| -rw-r--r-- | drivers/phy/qcom/phy-qcom-qmp-combo.c | 643 | ||||
| -rw-r--r-- | drivers/phy/qcom/phy-qcom-qmp-common.h | 62 | ||||
| -rw-r--r-- | drivers/phy/qcom/phy-qcom-qmp-dp-com-v3.h | 18 | ||||
| -rw-r--r-- | drivers/phy/qcom/phy-qcom-qmp-pcs-usb-v4.h | 34 | ||||
| -rw-r--r-- | drivers/phy/qcom/phy-qcom-qmp-ufs.c | 88 | ||||
| -rw-r--r-- | drivers/phy/qcom/phy-qcom-qmp.h | 17 | ||||
| -rw-r--r-- | drivers/phy/qcom/phy-qcom-qusb2.c | 38 |
13 files changed, 1574 insertions, 75 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 09810b62b51..fc4daa00661 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -292,6 +292,17 @@ config PHY_MTK_UFS initialization, power on and power off flow of specified M-PHYs. +config PHY_MTK_XSPHY + bool "MediaTek XS-PHY Driver" + depends on PHY + depends on ARCH_MEDIATEK + select REGMAP + select SYSCON + help + Enable this to support the SuperSpeedPlus XS-PHY transceiver for + USB3.1 GEN2 controllers on MediaTek chips. The driver supports + multiple USB2.0, USB3.1 GEN2 ports. + config PHY_NPCM_USB bool "Nuvoton NPCM USB PHY support" depends on PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 83102349669..684e9a99af8 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o obj-$(CONFIG_PHY_EXYNOS_USBDRD) += phy-exynos-usbdrd.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 obj-$(CONFIG_PHY_NPCM_USB) += phy-npcm-usb.o obj-$(CONFIG_$(PHASE_)PHY_IMX8MQ_USB) += phy-imx8mq-usb.o obj-$(CONFIG_PHY_IMX8M_PCIE) += phy-imx8m-pcie.o diff --git a/drivers/phy/phy-mtk-ufs.c b/drivers/phy/phy-mtk-ufs.c index 1eda3df858d..c4c214dcde0 100644 --- a/drivers/phy/phy-mtk-ufs.c +++ b/drivers/phy/phy-mtk-ufs.c @@ -6,52 +6,41 @@ * Copyright (c) 2025, Igor Belwon <[email protected]> */ -#include "dm/ofnode.h" -#include "dm/read.h" -#include <clk.h> -#include <dm.h> -#include <generic-phy.h> -#include <malloc.h> -#include <mapmem.h> -#include <regmap.h> -#include <syscon.h> #include <asm/io.h> +#include <clk.h> +#include <dm/device.h> #include <dm/device_compat.h> -#include <dm/devres.h> -#include <linux/bitfield.h> +#include <dm/read.h> +#include <generic-phy.h> #include <linux/bitops.h> #include <linux/delay.h> - -#include <dt-bindings/phy/phy.h> +#include <mapmem.h> /* mphy register and offsets */ -#define MP_GLB_DIG_8C 0x008C -#define FRC_PLL_ISO_EN BIT(8) -#define PLL_ISO_EN BIT(9) -#define FRC_FRC_PWR_ON BIT(10) -#define PLL_PWR_ON BIT(11) - -#define MP_LN_DIG_RX_9C 0xA09C -#define FSM_DIFZ_FRC BIT(18) +#define MP_GLB_DIG_8C 0x008C +#define FRC_PLL_ISO_EN BIT(8) +#define PLL_ISO_EN BIT(9) +#define FRC_FRC_PWR_ON BIT(10) +#define PLL_PWR_ON BIT(11) -#define MP_LN_DIG_RX_AC 0xA0AC -#define FRC_RX_SQ_EN BIT(0) -#define RX_SQ_EN BIT(1) +#define MP_LN_DIG_RX_9C 0xA09C +#define FSM_DIFZ_FRC BIT(18) -#define MP_LN_RX_44 0xB044 -#define FRC_CDR_PWR_ON BIT(17) -#define CDR_PWR_ON BIT(18) -#define FRC_CDR_ISO_EN BIT(19) -#define CDR_ISO_EN BIT(20) +#define MP_LN_DIG_RX_AC 0xA0AC +#define FRC_RX_SQ_EN BIT(0) +#define RX_SQ_EN BIT(1) -#define UFSPHY_CLKS_CNT 2 +#define MP_LN_RX_44 0xB044 +#define FRC_CDR_PWR_ON BIT(17) +#define CDR_PWR_ON BIT(18) +#define FRC_CDR_ISO_EN BIT(19) +#define CDR_ISO_EN BIT(20) struct mtk_ufs_phy { struct udevice *dev; void __iomem *mmio; - struct clk *unipro_clk; - struct clk *mp_clk; + struct clk_bulk clk_bulk; }; static void ufs_mtk_phy_set_active(struct mtk_ufs_phy *phy) @@ -88,16 +77,9 @@ static int mtk_phy_power_on(struct phy *phy) struct mtk_ufs_phy *ufs_phy = dev_get_priv(phy->dev); int ret; - ret = clk_enable(ufs_phy->mp_clk); - if (ret < 0) { - dev_err(phy->dev, "failed to enable mp_clk\n"); - return ret; - } - - ret = clk_enable(ufs_phy->unipro_clk); - if (ret < 0) { - dev_err(phy->dev, "failed to enable unipro_clk %d\n", ret); - clk_disable(ufs_phy->unipro_clk); + ret = clk_enable_bulk(&ufs_phy->clk_bulk); + if (ret) { + dev_err(phy->dev, "failed to enable clocks (ret=%d)\n", ret); return ret; } @@ -106,34 +88,44 @@ static int mtk_phy_power_on(struct phy *phy) return 0; } -static int mtk_phy_power_off(struct phy *phy) +static void ufs_mtk_phy_set_inactive(struct mtk_ufs_phy *phy) { - struct mtk_ufs_phy *ufs_phy = dev_get_priv(phy->dev); - /* Set PHY to Deep Hibernate mode */ - setbits_le32(ufs_phy->mmio + MP_LN_DIG_RX_9C, FSM_DIFZ_FRC); + setbits_le32(phy->mmio + MP_LN_DIG_RX_9C, FSM_DIFZ_FRC); /* force DA_MP_RX0_SQ_EN */ - setbits_le32(ufs_phy->mmio + MP_LN_DIG_RX_AC, FRC_RX_SQ_EN); - clrbits_le32(ufs_phy->mmio + MP_LN_DIG_RX_AC, RX_SQ_EN); + setbits_le32(phy->mmio + MP_LN_DIG_RX_AC, FRC_RX_SQ_EN); + clrbits_le32(phy->mmio + MP_LN_DIG_RX_AC, RX_SQ_EN); /* force DA_MP_CDR_ISO_EN */ - setbits_le32(ufs_phy->mmio + MP_LN_RX_44, FRC_CDR_ISO_EN); - setbits_le32(ufs_phy->mmio + MP_LN_RX_44, CDR_ISO_EN); + setbits_le32(phy->mmio + MP_LN_RX_44, FRC_CDR_ISO_EN); + setbits_le32(phy->mmio + MP_LN_RX_44, CDR_ISO_EN); /* force DA_MP_CDR_PWR_ON */ - setbits_le32(ufs_phy->mmio + MP_LN_RX_44, FRC_CDR_PWR_ON); - clrbits_le32(ufs_phy->mmio + MP_LN_RX_44, CDR_PWR_ON); + setbits_le32(phy->mmio + MP_LN_RX_44, FRC_CDR_PWR_ON); + clrbits_le32(phy->mmio + MP_LN_RX_44, CDR_PWR_ON); /* force DA_MP_PLL_ISO_EN */ - setbits_le32(ufs_phy->mmio + MP_GLB_DIG_8C, FRC_PLL_ISO_EN); - setbits_le32(ufs_phy->mmio + MP_GLB_DIG_8C, PLL_ISO_EN); + setbits_le32(phy->mmio + MP_GLB_DIG_8C, FRC_PLL_ISO_EN); + setbits_le32(phy->mmio + MP_GLB_DIG_8C, PLL_ISO_EN); /* force DA_MP_PLL_PWR_ON */ - setbits_le32(ufs_phy->mmio + MP_GLB_DIG_8C, FRC_FRC_PWR_ON); - clrbits_le32(ufs_phy->mmio + MP_GLB_DIG_8C, PLL_PWR_ON); + setbits_le32(phy->mmio + MP_GLB_DIG_8C, FRC_FRC_PWR_ON); + clrbits_le32(phy->mmio + MP_GLB_DIG_8C, PLL_PWR_ON); +} - return 0; +static int mtk_phy_power_off(struct phy *phy) +{ + struct mtk_ufs_phy *ufs_phy = dev_get_priv(phy->dev); + int ret; + + ufs_mtk_phy_set_inactive(ufs_phy); + + ret = clk_disable_bulk(&ufs_phy->clk_bulk); + if (ret) + dev_err(phy->dev, "failed to disable clocks (ret=%d)\n", ret); + + return ret; } static const struct phy_ops mtk_ufs_phy_ops = { @@ -147,10 +139,6 @@ static int mtk_ufs_phy_probe(struct udevice *dev) fdt_addr_t addr; int ret; - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return -ENOMEM; - addr = dev_read_addr(dev); if (addr == FDT_ADDR_T_NONE) return -ENOMEM; @@ -158,21 +146,11 @@ static int mtk_ufs_phy_probe(struct udevice *dev) phy->dev = dev; phy->mmio = map_sysmem(addr, 0); - phy->mp_clk = devm_clk_get(dev, "mp"); - if (IS_ERR(phy->mp_clk)) { - ret = PTR_ERR(phy->mp_clk); - dev_err(dev, "Failed to get mp clock (ret=%d)\n", ret); - return ret; - } + ret = clk_get_bulk(dev, &phy->clk_bulk); + if (ret) + dev_err(dev, "Failed to get clocks (ret=%d)\n", ret); - phy->unipro_clk = devm_clk_get(dev, "unipro"); - if (IS_ERR(phy->unipro_clk)) { - ret = PTR_ERR(phy->unipro_clk); - dev_err(dev, "Failed to get unipro clock (ret=%d)\n", ret); - return ret; - } - - return 0; + return ret; } static const struct udevice_id mtk_ufs_phy_id_table[] = { diff --git a/drivers/phy/phy-mtk-xsphy.c b/drivers/phy/phy-mtk-xsphy.c new file mode 100644 index 00000000000..d3418ffb101 --- /dev/null +++ b/drivers/phy/phy-mtk-xsphy.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MediaTek USB3.1 gen2 xsphy Driver + * + * Copyright (c) 2026 MediaTek Inc. + * Copyright (c) 2026 BayLibre, SAS + * + * Based on Linux mtk-xsphy driver: + * Copyright (c) 2018 MediaTek Inc. + * Author: Chunfeng Yun <[email protected]> + * + * And U-Boot mtk-tphy driver: + * Copyright (c) 2015 - 2019 MediaTek Inc. + * Author: Chunfeng Yun <[email protected]> + * Ryder Lee <[email protected]> + */ + +#include <clk.h> +#include <dm.h> +#include <generic-phy.h> +#include <malloc.h> +#include <mapmem.h> +#include <regmap.h> +#include <syscon.h> + +#include <asm/io.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/iopoll.h> + +#include <dt-bindings/phy/phy.h> + +/* u2 phy banks */ +#define SSUSB_SIFSLV_MISC 0x000 +#define SSUSB_SIFSLV_U2FREQ 0x100 +#define SSUSB_SIFSLV_U2PHY_COM 0x300 + +/* u3 phy shared banks */ +#define SSPXTP_SIFSLV_DIG_GLB 0x000 +#define SSPXTP_SIFSLV_PHYA_GLB 0x100 + +/* u3 phy banks */ +#define SSPXTP_SIFSLV_DIG_LN_TOP 0x000 +#define SSPXTP_SIFSLV_DIG_LN_TX0 0x100 +#define SSPXTP_SIFSLV_DIG_LN_RX0 0x200 +#define SSPXTP_SIFSLV_DIG_LN_DAIF 0x300 +#define SSPXTP_SIFSLV_PHYA_LN 0x400 + +#define XSP_U2FREQ_FMCR0 ((SSUSB_SIFSLV_U2FREQ) + 0x00) +#define P2F_RG_FREQDET_EN BIT(24) +#define P2F_RG_CYCLECNT GENMASK(23, 0) + +#define XSP_U2FREQ_MMONR0 ((SSUSB_SIFSLV_U2FREQ) + 0x0c) + +#define XSP_U2FREQ_FMMONR1 ((SSUSB_SIFSLV_U2FREQ) + 0x10) +#define P2F_RG_FRCK_EN BIT(8) +#define P2F_USB_FM_VALID BIT(0) + +#define XSP_USBPHYACR0 ((SSUSB_SIFSLV_U2PHY_COM) + 0x00) +#define P2A0_RG_INTR_EN BIT(5) + +#define XSP_USBPHYACR1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x04) +#define P2A1_RG_INTR_CAL GENMASK(23, 19) +#define P2A1_RG_VRT_SEL GENMASK(14, 12) +#define P2A1_RG_TERM_SEL GENMASK(10, 8) + +#define XSP_USBPHYACR5 ((SSUSB_SIFSLV_U2PHY_COM) + 0x014) +#define P2A5_RG_HSTX_SRCAL_EN BIT(15) +#define P2A5_RG_HSTX_SRCTRL GENMASK(14, 12) + +#define XSP_USBPHYACR6 ((SSUSB_SIFSLV_U2PHY_COM) + 0x018) +#define P2A6_RG_BC11_SW_EN BIT(23) +#define P2A6_RG_OTG_VBUSCMP_EN BIT(20) + +#define XSP_U2PHYDTM1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x06C) +#define P2D_FORCE_IDDIG BIT(9) +#define P2D_RG_VBUSVALID BIT(5) +#define P2D_RG_SESSEND BIT(4) +#define P2D_RG_AVALID BIT(2) +#define P2D_RG_IDDIG BIT(1) + +#define SSPXTP_PHYA_GLB_00 ((SSPXTP_SIFSLV_PHYA_GLB) + 0x00) +#define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(21, 16) + +#define SSPXTP_PHYA_LN_04 ((SSPXTP_SIFSLV_PHYA_LN) + 0x04) +#define RG_XTP_LN0_TX_IMPSEL GENMASK(4, 0) + +#define SSPXTP_PHYA_LN_14 ((SSPXTP_SIFSLV_PHYA_LN) + 0x014) +#define RG_XTP_LN0_RX_IMPSEL GENMASK(4, 0) + +#define XSP_REF_CLK_MHZ 26 +#define XSP_SLEW_RATE_COEF 17 +#define XSP_SR_COEF_DIVISOR 1000 +#define XSP_FM_DET_CYCLE_CNT 1024 + +/* PHY switch between pcie/usb3/sgmii */ +#define USB_PHY_SWITCH_CTRL 0x0 +#define RG_PHY_SW_TYPE GENMASK(3, 0) +#define RG_PHY_SW_PCIE 0x0 +#define RG_PHY_SW_USB3 0x1 +#define RG_PHY_SW_SGMII 0x2 + +struct mtk_xsphy_instance { + void __iomem *port_base; + struct device_node *np; + struct clk ref_clk; /* reference clock of analog phy */ + u32 index; + u32 type; + struct regmap *type_sw; + u32 type_sw_reg; + u32 type_sw_index; + /* only for HQA test */ + u32 efuse_intr; + u32 efuse_tx_imp; + u32 efuse_rx_imp; + /* u2 eye diagram */ + u32 eye_src; + u32 eye_vrt; + u32 eye_term; +}; + +struct mtk_xsphy { + struct udevice *dev; + void __iomem *sif_base; + struct mtk_xsphy_instance **phys; + u32 nphys; + u32 src_ref_clk_mhz; /* reference clock for slew rate calibrate */ + u32 src_coef; /* coefficient for slew rate calibrate */ +}; + +static void mtk_xsphy_u2_slew_rate_calibrate(struct mtk_xsphy *xsphy, + struct mtk_xsphy_instance *instance) +{ + void __iomem *pbase = instance->port_base; + u32 calib_val; + u32 fm_out; + u32 tmp; + + /* use force value */ + if (instance->eye_src) + return; + + /* enable USB ring oscillator */ + setbits_le32(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN); + /* wait for clock to become stable */ + udelay(1); + + /* enable free run clock */ + setbits_le32(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN); + + /* set cycle count as 1024 */ + clrsetbits_le32(pbase + XSP_U2FREQ_FMCR0, P2F_RG_CYCLECNT, + FIELD_PREP(P2F_RG_CYCLECNT, XSP_FM_DET_CYCLE_CNT)); + + /* enable frequency meter */ + setbits_le32(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN); + + /* ignore return value */ + readl_poll_sleep_timeout(pbase + XSP_U2FREQ_FMMONR1, tmp, + (tmp & P2F_USB_FM_VALID), 10, 200); + + fm_out = readl(pbase + XSP_U2FREQ_MMONR0); + + /* disable frequency meter */ + clrbits_le32(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN); + + /* disable free run clock */ + clrbits_le32(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN); + + if (fm_out) { + /* (1024 / FM_OUT) x reference clock frequency x coefficient */ + tmp = xsphy->src_ref_clk_mhz * xsphy->src_coef; + tmp = (tmp * XSP_FM_DET_CYCLE_CNT) / fm_out; + calib_val = DIV_ROUND_CLOSEST(tmp, XSP_SR_COEF_DIVISOR); + } else { + /* if FM detection fail, set default value */ + calib_val = 3; + } + dev_dbg(xsphy->dev, "phy.%u, fm_out:%u, calib:%u (clk:%u, coef:%u)\n", + instance->index, fm_out, calib_val, xsphy->src_ref_clk_mhz, + xsphy->src_coef); + + /* set HS slew rate */ + clrsetbits_le32(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL, + FIELD_PREP(P2A5_RG_HSTX_SRCTRL, calib_val)); + + /* disable USB ring oscillator */ + clrbits_le32(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN); +} + +static void mtk_xsphy_u2_instance_init(struct mtk_xsphy *xsphy, + struct mtk_xsphy_instance *instance) +{ + void __iomem *pbase = instance->port_base; + + /* DP/DM BC1.1 path Disable */ + clrbits_le32(pbase + XSP_USBPHYACR6, P2A6_RG_BC11_SW_EN); + + setbits_le32(pbase + XSP_USBPHYACR0, P2A0_RG_INTR_EN); +} + +static void mtk_xsphy_u2_instance_power_on(struct mtk_xsphy *xsphy, + struct mtk_xsphy_instance *instance) +{ + void __iomem *pbase = instance->port_base; + + setbits_le32(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN); + + clrsetbits_le32(pbase + XSP_U2PHYDTM1, + P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND, + P2D_RG_VBUSVALID | P2D_RG_AVALID); + + dev_dbg(xsphy->dev, "%s(%u)\n", __func__, instance->index); +} + +static void mtk_xsphy_u2_instance_power_off(struct mtk_xsphy *xsphy, + struct mtk_xsphy_instance *instance) +{ + void __iomem *pbase = instance->port_base; + + clrbits_le32(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN); + + clrsetbits_le32(pbase + XSP_U2PHYDTM1, + P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND, + P2D_RG_SESSEND); + + dev_dbg(xsphy->dev, "%s(%u)\n", __func__, instance->index); +} + +static void mtk_xsphy_u2_instance_set_mode(struct mtk_xsphy *xsphy, + struct mtk_xsphy_instance *instance, + enum phy_mode mode) +{ + u32 tmp; + + tmp = readl(instance->port_base + XSP_U2PHYDTM1); + + switch (mode) { + case PHY_MODE_USB_DEVICE: + tmp |= P2D_FORCE_IDDIG | P2D_RG_IDDIG; + break; + case PHY_MODE_USB_HOST: + tmp |= P2D_FORCE_IDDIG; + tmp &= ~P2D_RG_IDDIG; + break; + case PHY_MODE_USB_OTG: + tmp &= ~(P2D_FORCE_IDDIG | P2D_RG_IDDIG); + break; + default: + return; + } + + writel(tmp, instance->port_base + XSP_U2PHYDTM1); +} + +static void mtk_xsphy_parse_property(struct mtk_xsphy *xsphy, + struct mtk_xsphy_instance *instance) +{ + ofnode node = np_to_ofnode(instance->np); + + switch (instance->type) { + case PHY_TYPE_USB2: + ofnode_read_u32(node, "mediatek,efuse-intr", &instance->efuse_intr); + ofnode_read_u32(node, "mediatek,eye-src", &instance->eye_src); + ofnode_read_u32(node, "mediatek,eye-vrt", &instance->eye_vrt); + ofnode_read_u32(node, "mediatek,eye-term", &instance->eye_term); + + dev_dbg(xsphy->dev, "intr:%u, src:%u, vrt:%u, term:%u\n", + instance->efuse_intr, instance->eye_src, + instance->eye_vrt, instance->eye_term); + return; + case PHY_TYPE_USB3: + ofnode_read_u32(node, "mediatek,efuse-intr", &instance->efuse_intr); + ofnode_read_u32(node, "mediatek,efuse-tx-imp", &instance->efuse_tx_imp); + ofnode_read_u32(node, "mediatek,efuse-rx-imp", &instance->efuse_rx_imp); + + dev_dbg(xsphy->dev, "intr:%u, tx-imp:%u, rx-imp:%u\n", + instance->efuse_intr, instance->efuse_tx_imp, + instance->efuse_rx_imp); + return; + case PHY_TYPE_PCIE: + case PHY_TYPE_SGMII: + /* nothing to do */ + return; + default: + dev_err(xsphy->dev, "incompatible PHY type\n"); + return; + } +} + +static void mtk_xsphy_u2_props_set(struct mtk_xsphy *xsphy, + struct mtk_xsphy_instance *instance) +{ + void __iomem *pbase = instance->port_base; + + if (instance->efuse_intr) + clrsetbits_le32(pbase + XSP_USBPHYACR1, P2A1_RG_INTR_CAL, + FIELD_PREP(P2A1_RG_INTR_CAL, instance->efuse_intr)); + + if (instance->eye_src) + clrsetbits_le32(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL, + FIELD_PREP(P2A5_RG_HSTX_SRCTRL, instance->eye_src)); + + if (instance->eye_vrt) + clrsetbits_le32(pbase + XSP_USBPHYACR1, P2A1_RG_VRT_SEL, + FIELD_PREP(P2A1_RG_VRT_SEL, instance->eye_vrt)); + + if (instance->eye_term) + clrsetbits_le32(pbase + XSP_USBPHYACR1, P2A1_RG_TERM_SEL, + FIELD_PREP(P2A1_RG_TERM_SEL, instance->eye_term)); +} + +static void mtk_xsphy_u3_props_set(struct mtk_xsphy *xsphy, + struct mtk_xsphy_instance *instance) +{ + void __iomem *pbase = instance->port_base; + + if (instance->efuse_intr) + clrsetbits_le32(xsphy->sif_base + SSPXTP_PHYA_GLB_00, + RG_XTP_GLB_BIAS_INTR_CTRL, + FIELD_PREP(RG_XTP_GLB_BIAS_INTR_CTRL, instance->efuse_intr)); + + if (instance->efuse_tx_imp) + clrsetbits_le32(pbase + SSPXTP_PHYA_LN_04, RG_XTP_LN0_TX_IMPSEL, + FIELD_PREP(RG_XTP_LN0_TX_IMPSEL, instance->efuse_tx_imp)); + + if (instance->efuse_rx_imp) + clrsetbits_le32(pbase + SSPXTP_PHYA_LN_14, RG_XTP_LN0_RX_IMPSEL, + FIELD_PREP(RG_XTP_LN0_RX_IMPSEL, instance->efuse_rx_imp)); +} + +/* type switch for usb3/pcie/sgmii */ +static int mtk_xsphy_type_syscon_get(struct udevice *dev, + struct mtk_xsphy_instance *instance, + ofnode dn) +{ + struct ofnode_phandle_args args; + int ret; + + if (!ofnode_read_bool(dn, "mediatek,syscon-type")) + return 0; + + ret = ofnode_parse_phandle_with_args(dn, "mediatek,syscon-type", + NULL, 2, 0, &args); + if (ret) + return ret; + + instance->type_sw_reg = args.args[0]; + instance->type_sw_index = args.args[1] & 0x3; /* <=3 */ + instance->type_sw = syscon_node_to_regmap(args.node); + if (IS_ERR(instance->type_sw)) + return PTR_ERR(instance->type_sw); + + dev_dbg(dev, "phy-%s.%d: type_sw - reg %#x, index %d\n", + dev->name, instance->index, instance->type_sw_reg, + instance->type_sw_index); + + return 0; +} + +static int mtk_xsphy_type_set(struct mtk_xsphy_instance *instance) +{ + int type; + u32 offset; + + if (!instance->type_sw) + return 0; + + switch (instance->type) { + case PHY_TYPE_USB3: + type = RG_PHY_SW_USB3; + break; + case PHY_TYPE_PCIE: + type = RG_PHY_SW_PCIE; + break; + case PHY_TYPE_SGMII: + type = RG_PHY_SW_SGMII; + break; + case PHY_TYPE_USB2: + default: + return 0; + } + + offset = instance->type_sw_index * BITS_PER_BYTE; + regmap_update_bits(instance->type_sw, instance->type_sw_reg, + RG_PHY_SW_TYPE << offset, type << offset); + + return 0; +} + +static int mtk_xsphy_init(struct phy *phy) +{ + struct mtk_xsphy *xsphy = dev_get_priv(phy->dev); + struct mtk_xsphy_instance *instance = xsphy->phys[phy->id]; + int ret; + + ret = clk_enable(&instance->ref_clk); + if (ret) { + dev_err(xsphy->dev, "failed to enable ref_clk\n"); + return ret; + } + + switch (instance->type) { + case PHY_TYPE_USB2: + mtk_xsphy_u2_instance_init(xsphy, instance); + mtk_xsphy_u2_props_set(xsphy, instance); + break; + case PHY_TYPE_USB3: + mtk_xsphy_u3_props_set(xsphy, instance); + break; + case PHY_TYPE_PCIE: + case PHY_TYPE_SGMII: + /* nothing to do, only used to set type */ + break; + default: + dev_err(xsphy->dev, "incompatible PHY type\n"); + clk_disable(&instance->ref_clk); + return -EINVAL; + } + + return 0; +} + +static int mtk_xsphy_power_on(struct phy *phy) +{ + struct mtk_xsphy *xsphy = dev_get_priv(phy->dev); + struct mtk_xsphy_instance *instance = xsphy->phys[phy->id]; + + if (instance->type == PHY_TYPE_USB2) { + mtk_xsphy_u2_instance_power_on(xsphy, instance); + mtk_xsphy_u2_slew_rate_calibrate(xsphy, instance); + } + + return 0; +} + +static int mtk_xsphy_power_off(struct phy *phy) +{ + struct mtk_xsphy *xsphy = dev_get_priv(phy->dev); + struct mtk_xsphy_instance *instance = xsphy->phys[phy->id]; + + if (instance->type == PHY_TYPE_USB2) + mtk_xsphy_u2_instance_power_off(xsphy, instance); + + return 0; +} + +static int mtk_xsphy_exit(struct phy *phy) +{ + struct mtk_xsphy *xsphy = dev_get_priv(phy->dev); + struct mtk_xsphy_instance *instance = xsphy->phys[phy->id]; + + clk_disable(&instance->ref_clk); + + return 0; +} + +static int mtk_xsphy_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct mtk_xsphy *xsphy = dev_get_priv(phy->dev); + struct mtk_xsphy_instance *instance = xsphy->phys[phy->id]; + + if (instance->type == PHY_TYPE_USB2) + mtk_xsphy_u2_instance_set_mode(xsphy, instance, mode); + + return 0; +} + +static int mtk_xsphy_xlate(struct phy *phy, struct ofnode_phandle_args *args) +{ + struct mtk_xsphy *xsphy = dev_get_priv(phy->dev); + struct mtk_xsphy_instance *instance = NULL; + const struct device_node *phy_np = ofnode_to_np(args->node); + u32 index; + + if (!phy_np) { + dev_err(phy->dev, "null pointer phy node\n"); + return -EINVAL; + } + + if (args->args_count != 2) { + dev_err(phy->dev, "invalid number of cells in 'phy' property\n"); + return -EINVAL; + } + + for (index = 0; index < xsphy->nphys; index++) + if (phy_np == xsphy->phys[index]->np) { + instance = xsphy->phys[index]; + break; + } + + if (!instance) { + dev_err(phy->dev, "failed to find appropriate phy\n"); + return -EINVAL; + } + + phy->id = index; + instance->type = args->args[1]; + if (!(instance->type == PHY_TYPE_USB2 || + instance->type == PHY_TYPE_USB3 || + instance->type == PHY_TYPE_PCIE || + instance->type == PHY_TYPE_SGMII)) { + dev_err(phy->dev, "unsupported PHY type\n"); + return -EINVAL; + } + + mtk_xsphy_parse_property(xsphy, instance); + mtk_xsphy_type_set(instance); + + return 0; +} + +static const struct phy_ops mtk_xsphy_ops = { + .init = mtk_xsphy_init, + .exit = mtk_xsphy_exit, + .power_on = mtk_xsphy_power_on, + .power_off = mtk_xsphy_power_off, + .set_mode = mtk_xsphy_set_mode, + .of_xlate = mtk_xsphy_xlate, +}; + +static int mtk_xsphy_probe(struct udevice *dev) +{ + struct mtk_xsphy *xsphy = dev_get_priv(dev); + fdt_addr_t sif_addr; + ofnode subnode; + int index = 0; + + xsphy->nphys = dev_get_child_count(dev); + + xsphy->phys = devm_kcalloc(dev, xsphy->nphys, sizeof(*xsphy->phys), + GFP_KERNEL); + if (!xsphy->phys) + return -ENOMEM; + + xsphy->dev = dev; + + sif_addr = ofnode_get_addr(dev_ofnode(dev)); + /* optional, may not exist if no u3 phys */ + if (sif_addr != FDT_ADDR_T_NONE) + xsphy->sif_base = map_sysmem(sif_addr, 0); + + xsphy->src_ref_clk_mhz = XSP_REF_CLK_MHZ; + xsphy->src_coef = XSP_SLEW_RATE_COEF; + /* update parameters of slew rate calibrate if exist */ + ofnode_read_u32(dev_ofnode(dev), "mediatek,src-ref-clk-mhz", + &xsphy->src_ref_clk_mhz); + ofnode_read_u32(dev_ofnode(dev), "mediatek,src-coef", &xsphy->src_coef); + + dev_for_each_subnode(subnode, dev) { + struct mtk_xsphy_instance *inst; + fdt_addr_t addr; + int ret; + + inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + xsphy->phys[index] = inst; + + addr = ofnode_get_addr(subnode); + if (addr == FDT_ADDR_T_NONE) + return -EADDRNOTAVAIL; + + inst->port_base = map_sysmem(addr, 0); + inst->index = index; + inst->np = ofnode_to_np(subnode); + + ret = clk_get_by_name_nodev(subnode, "ref", &inst->ref_clk); + if (ret) { + dev_err(dev, "failed to get ref_clk(id-%d)\n", index); + return ret; + } + + ret = mtk_xsphy_type_syscon_get(dev, inst, subnode); + if (ret) + return ret; + + index++; + } + + return 0; +} + +static const struct udevice_id mtk_xsphy_id_table[] = { + { .compatible = "mediatek,xsphy" }, + { } +}; + +U_BOOT_DRIVER(mtk_xsphy) = { + .name = "mtk-xsphy", + .id = UCLASS_PHY, + .of_match = mtk_xsphy_id_table, + .ops = &mtk_xsphy_ops, + .probe = mtk_xsphy_probe, + .priv_auto = sizeof(struct mtk_xsphy), +}; diff --git a/drivers/phy/qcom/Kconfig b/drivers/phy/qcom/Kconfig index 0dd69f7ffd0..49f830abf01 100644 --- a/drivers/phy/qcom/Kconfig +++ b/drivers/phy/qcom/Kconfig @@ -12,6 +12,14 @@ config PHY_QCOM_IPQ4019_USB help Support for the USB PHY-s on Qualcomm IPQ40xx SoC-s. +config PHY_QCOM_QMP_COMBO + bool "Qualcomm QMP USB3-DP Combo PHY driver" + depends on PHY && ARCH_SNAPDRAGON + help + Enable this to support the USB3-DP Combo QMP PHY on various Qualcomm + chipsets. This driver supports the USB3 PHY functionality of the combo + PHY (USB3 + DisplayPort). Currently only USB3 mode is supported. + config PHY_QCOM_QMP_PCIE tristate "Qualcomm QMP PCIe PHY driver" depends on PHY && ARCH_SNAPDRAGON diff --git a/drivers/phy/qcom/Makefile b/drivers/phy/qcom/Makefile index 1c4e7d8d391..714013dc572 100644 --- a/drivers/phy/qcom/Makefile +++ b/drivers/phy/qcom/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o +obj-$(CONFIG_PHY_QCOM_QMP_COMBO) += phy-qcom-qmp-combo.o obj-$(CONFIG_PHY_QCOM_QMP_PCIE) += phy-qcom-qmp-pcie.o obj-$(CONFIG_PHY_QCOM_QMP_UFS) += phy-qcom-qmp-ufs.o obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o diff --git a/drivers/phy/qcom/phy-qcom-qmp-combo.c b/drivers/phy/qcom/phy-qcom-qmp-combo.c new file mode 100644 index 00000000000..0d63e482ee4 --- /dev/null +++ b/drivers/phy/qcom/phy-qcom-qmp-combo.c @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <generic-phy.h> +#include <reset.h> +#include <power/regulator.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include <linux/compat.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/err.h> + +#include "phy-qcom-qmp-common.h" + +#include "phy-qcom-qmp.h" +#include "phy-qcom-qmp-pcs-misc-v3.h" +#include "phy-qcom-qmp-pcs-usb-v4.h" +#include "phy-qcom-qmp-dp-com-v3.h" + +/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */ +/* DP PHY soft reset */ +#define SW_DPPHY_RESET BIT(0) +/* mux to select DP PHY reset control, 0:HW control, 1: software reset */ +#define SW_DPPHY_RESET_MUX BIT(1) +/* USB3 PHY soft reset */ +#define SW_USB3PHY_RESET BIT(2) +/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */ +#define SW_USB3PHY_RESET_MUX BIT(3) + +/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */ +#define USB3_MODE BIT(0) /* enables USB3 mode */ +#define DP_MODE BIT(1) /* enables DP mode */ + +/* QPHY_V3_DP_COM_TYPEC_CTRL register bits */ +#define SW_PORTSELECT_MUX BIT(1) + +/* PHY slot identifiers for device tree phandle arguments */ +#define QMP_USB43DP_USB3_PHY 0 +#define QMP_USB43DP_DP_PHY 1 + +#define PHY_INIT_COMPLETE_TIMEOUT 10000 + +struct qmp_combo_offsets { + u16 com; + u16 txa; + u16 rxa; + u16 txb; + u16 rxb; + u16 usb3_serdes; + u16 usb3_pcs_misc; + u16 usb3_pcs; + u16 usb3_pcs_usb; +}; + +/* + * Initialisation tables + */ + +static const struct qmp_combo_offsets qmp_combo_offsets_v3 = { + .com = 0x0000, + .txa = 0x1200, + .rxa = 0x1400, + .txb = 0x1600, + .rxb = 0x1800, + .usb3_serdes = 0x1000, + .usb3_pcs_misc = 0x1a00, + .usb3_pcs = 0x1c00, + .usb3_pcs_usb = 0x1f00, +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x40, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x54, 2), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f, 2), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff, 2), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x97), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +struct qmp_phy_cfg { + const struct qmp_combo_offsets *offsets; + const struct qmp_phy_init_tbl *serdes_tbl; + int serdes_tbl_num; + const struct qmp_phy_init_tbl *tx_tbl; + int tx_tbl_num; + const struct qmp_phy_init_tbl *rx_tbl; + int rx_tbl_num; + const struct qmp_phy_init_tbl *pcs_tbl; + int pcs_tbl_num; + const struct qmp_phy_init_tbl *pcs_usb_tbl; + int pcs_usb_tbl_num; + const char * const *vreg_list; + int num_vregs; + /* true, if PHY needs delay after POWER_DOWN */ + bool has_pwrdn_delay; +}; + +/* list of clocks required by phy */ +static const char * const qmp_combo_phy_clk_l[] = { + "aux", "com_aux", +}; + +/* list of regulators */ +static const char * const qmp_phy_vreg_l[] = { + "vdda-phy-supply", + "vdda-pll-supply", +}; + +struct qmp_combo { + struct udevice *dev; + void __iomem *com; + void __iomem *serdes; + void __iomem *tx; + void __iomem *rx; + void __iomem *tx2; + void __iomem *rx2; + void __iomem *pcs; + void __iomem *pcs_usb; + void __iomem *pcs_misc; + struct clk *clks; + struct clk *pipe_clk; + int num_clks; + struct reset_ctl_bulk resets; + int num_resets; + struct udevice **vregs; + int num_vregs; + const struct qmp_phy_cfg *cfg; +}; + +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 int qmp_combo_com_exit(struct qmp_combo *qmp) +{ + int i, ret; + + for (i = 0; i < qmp->num_clks; i++) + clk_disable(&qmp->clks[i]); + + reset_assert_bulk(&qmp->resets); + + for (i = qmp->num_vregs - 1; i >= 0; i--) { + ret = regulator_set_enable(qmp->vregs[i], false); + if (ret) + dev_warn(qmp->dev, "failed to disable %s: %d\n", + qmp->cfg->vreg_list[i], ret); + } + + return 0; +} + +static int qmp_combo_com_init(struct qmp_combo *qmp) +{ + void __iomem *com = qmp->com; + void __iomem *pcs = qmp->pcs; + u32 val; + int ret, i; + + ret = reset_assert_bulk(&qmp->resets); + if (ret) { + printf("Failed to assert resets: %d\n", ret); + return ret; + } + + ret = reset_deassert_bulk(&qmp->resets); + if (ret) { + printf("Failed to deassert resets: %d\n", ret); + return ret; + } + + for (i = 0; i < qmp->num_vregs; i++) { + ret = regulator_set_enable(qmp->vregs[i], true); + if (ret) { + dev_err(qmp->dev, "Failed to enable regulator %d: %d\n", i, ret); + while (--i >= 0) + regulator_set_enable(qmp->vregs[i], false); + reset_assert_bulk(&qmp->resets); + return ret; + } + } + + for (i = 0; i < qmp->num_clks; i++) { + ret = clk_enable(&qmp->clks[i]); + if (ret) { + printf("Failed to enable clock %d: %d\n", i, ret); + while (--i >= 0) + clk_disable(&qmp->clks[i]); + for (i = qmp->num_vregs - 1; i >= 0; i--) + regulator_set_enable(qmp->vregs[i], false); + reset_assert_bulk(&qmp->resets); + return ret; + } + } + + /* Common block register writes */ + qphy_setbits(com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, SW_PWRDN); + qphy_setbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + + val = SW_PORTSELECT_MUX; + writel(val, com + QPHY_V3_DP_COM_TYPEC_CTRL); + + writel(USB3_MODE | DP_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL); + + qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + + qphy_clrbits(com, QPHY_V3_DP_COM_SWI_CTRL, 0x03); + + qphy_clrbits(com, QPHY_V3_DP_COM_SW_RESET, SW_RESET); + + qphy_setbits(pcs, QPHY_V4_PCS_POWER_DOWN_CONTROL, SW_PWRDN); + + return 0; +} + +static int qmp_combo_usb_power_on(struct qmp_combo *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *serdes = qmp->serdes; + void __iomem *tx = qmp->tx; + void __iomem *rx = qmp->rx; + void __iomem *tx2 = qmp->tx2; + void __iomem *rx2 = qmp->rx2; + void __iomem *pcs = qmp->pcs; + void __iomem *pcs_usb = qmp->pcs_usb; + u32 val; + int ret; + + /* Serdes configuration */ + qmp_configure(qmp->dev, serdes, cfg->serdes_tbl, cfg->serdes_tbl_num); + + ret = clk_prepare_enable(qmp->pipe_clk); + if (ret) { + dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret); + return ret; + } + + /* Tx, Rx 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, rx, cfg->rx_tbl, cfg->rx_tbl_num, 1); + qmp_configure_lane(qmp->dev, rx2, cfg->rx_tbl, cfg->rx_tbl_num, 2); + + /* PCS configuration */ + qmp_configure(qmp->dev, pcs, cfg->pcs_tbl, cfg->pcs_tbl_num); + + if (pcs_usb) { + qmp_configure(qmp->dev, pcs_usb, + cfg->pcs_usb_tbl, + cfg->pcs_usb_tbl_num); + } + + if (cfg->has_pwrdn_delay) + udelay(20); + + /* Pull PHY out of reset */ + qphy_clrbits(pcs, QPHY_V4_PCS_SW_RESET, SW_RESET); + + /* Start SerDes and Phy-Coding-Sublayer */ + qphy_setbits(pcs, QPHY_V4_PCS_START_CONTROL, + SERDES_START | PCS_START); + + /* Wait for PHY initialization */ + ret = readl_poll_timeout(pcs + QPHY_V4_PCS_PCS_STATUS1, val, + !(val & PHYSTATUS), PHY_INIT_COMPLETE_TIMEOUT); + + if (ret) { + printf("QMP USB3 PHY initialization timeout\n"); + clk_disable(qmp->pipe_clk); + return ret; + } + + return 0; +} + +static int qmp_combo_power_on(struct phy *phy) +{ + struct qmp_combo *qmp = dev_get_priv(phy->dev); + int ret; + + /* Initialize common block */ + ret = qmp_combo_com_init(qmp); + if (ret) + return ret; + + /* Initialize USB3-specific configuration */ + ret = qmp_combo_usb_power_on(qmp); + if (ret) { + qmp_combo_com_exit(qmp); + return ret; + } + + return 0; +} + +static int qmp_combo_power_off(struct phy *phy) +{ + struct qmp_combo *qmp = dev_get_priv(phy->dev); + void __iomem *com = qmp->com; + + clk_disable(qmp->pipe_clk); + + /* PHY reset */ + qphy_setbits(qmp->pcs, QPHY_V4_PCS_SW_RESET, SW_RESET); + + /* Stop SerDes and Phy-Coding-Sublayer */ + qphy_clrbits(qmp->pcs, QPHY_V4_PCS_START_CONTROL, + SERDES_START | PCS_START); + + /* Put PHY into POWER DOWN state: active low */ + qphy_clrbits(qmp->pcs, QPHY_V4_PCS_POWER_DOWN_CONTROL, SW_PWRDN); + + /* Power down common block */ + qphy_clrbits(com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, SW_PWRDN); + + return qmp_combo_com_exit(qmp); +} + +static int qmp_combo_reset_init(struct qmp_combo *qmp) +{ + struct udevice *dev = qmp->dev; + int ret; + + ret = reset_get_bulk(dev, &qmp->resets); + if (ret) { + printf("Failed to get resets: %d\n", ret); + return ret; + } + + qmp->num_resets = qmp->resets.count; + + return 0; +} + +static int qmp_combo_clk_init(struct qmp_combo *qmp) +{ + struct udevice *dev = qmp->dev; + int num = ARRAY_SIZE(qmp_combo_phy_clk_l); + int i, ret; + + qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL); + if (!qmp->clks) + return -ENOMEM; + + for (i = 0; i < num; i++) { + ret = clk_get_by_name(dev, qmp_combo_phy_clk_l[i], &qmp->clks[i]); + if (ret) { + dev_err(dev, "failed to get %s clock: %d\n", + qmp_combo_phy_clk_l[i], ret); + return ret; + } + } + + qmp->num_clks = num; + return 0; +} + +static int qmp_combo_vreg_init(struct qmp_combo *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + struct udevice *dev = qmp->dev; + int num = cfg->num_vregs; + int i, ret; + + if (!num) + return 0; + + qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); + if (!qmp->vregs) + return -ENOMEM; + + for (i = 0; i < num; i++) { + ret = device_get_supply_regulator(dev, cfg->vreg_list[i], + &qmp->vregs[i]); + if (ret) { + dev_err(dev, "failed to get regulator %s: %d\n", + cfg->vreg_list[i], ret); + return ret; + } + } + + qmp->num_vregs = num; + return 0; +} + +static int qmp_combo_parse_dt(struct qmp_combo *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + const struct qmp_combo_offsets *offs = cfg->offsets; + struct udevice *dev = qmp->dev; + void __iomem *base; + int ret; + + if (!offs) + return -EINVAL; + + base = (void __iomem *)dev_read_addr(dev); + if (IS_ERR(base)) + return PTR_ERR(base); + + qmp->com = base + offs->com; + qmp->serdes = base + offs->usb3_serdes; + qmp->tx = base + offs->txa; + qmp->rx = base + offs->rxa; + qmp->tx2 = base + offs->txb; + qmp->rx2 = base + offs->rxb; + qmp->pcs = base + offs->usb3_pcs; + qmp->pcs_usb = base + offs->usb3_pcs_usb; + qmp->pcs_misc = base + offs->usb3_pcs_misc; + + ret = qmp_combo_clk_init(qmp); + if (ret) + return ret; + + qmp->pipe_clk = devm_clk_get(dev, "usb3_pipe"); + if (IS_ERR(qmp->pipe_clk)) { + dev_err(dev, "failed to get pipe clock (%ld)\n", + PTR_ERR(qmp->pipe_clk)); + return ret; + } + + ret = qmp_combo_reset_init(qmp); + if (ret) + return ret; + + ret = qmp_combo_vreg_init(qmp); + if (ret) + return ret; + + return 0; +} + +static int qmp_combo_probe(struct udevice *dev) +{ + struct qmp_combo *qmp = dev_get_priv(dev); + int ret; + + qmp->dev = dev; + qmp->cfg = (const struct qmp_phy_cfg *)dev_get_driver_data(dev); + if (!qmp->cfg) { + printf("Failed to get PHY configuration\n"); + return -EINVAL; + } + + ret = qmp_combo_parse_dt(qmp); + + return ret; +} + +static const struct qmp_phy_cfg sc7280_usb3dpphy_cfg = { + .offsets = &qmp_combo_offsets_v3, + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8250_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_tx_tbl), + .rx_tbl = sm8250_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_rx_tbl), + .pcs_tbl = sm8250_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_tbl), + .pcs_usb_tbl = sm8250_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_usb_tbl), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + + .has_pwrdn_delay = true, +}; + +static int qmp_combo_xlate(struct phy *phy, struct ofnode_phandle_args *args) +{ + if (args->args_count != 1) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + /* We only support the USB3 phy at slot 0 */ + if (args->args[0] == QMP_USB43DP_DP_PHY) + return -EINVAL; + + phy->id = QMP_USB43DP_USB3_PHY; + + return 0; +} + +static struct phy_ops qmp_combo_ops = { + .init = qmp_combo_power_on, + .exit = qmp_combo_power_off, + .of_xlate = qmp_combo_xlate, +}; + +static const struct udevice_id qmp_combo_ids[] = { + { + .compatible = "qcom,sc7280-qmp-usb3-dp-phy", + .data = (ulong)&sc7280_usb3dpphy_cfg, + }, + { } +}; + +U_BOOT_DRIVER(qmp_combo) = { + .name = "qcom-qmp-usb3-dp-phy", + .id = UCLASS_PHY, + .of_match = qmp_combo_ids, + .ops = &qmp_combo_ops, + .probe = qmp_combo_probe, + .priv_auto = sizeof(struct qmp_combo), +}; diff --git a/drivers/phy/qcom/phy-qcom-qmp-common.h b/drivers/phy/qcom/phy-qcom-qmp-common.h new file mode 100644 index 00000000000..71356fb7dd0 --- /dev/null +++ b/drivers/phy/qcom/phy-qcom-qmp-common.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_COMMON_H_ +#define QCOM_PHY_QMP_COMMON_H_ + +struct qmp_phy_init_tbl { + unsigned int offset; + unsigned int val; + char *name; + /* + * mask of lanes for which this register is written + * for cases when second lane needs different values + */ + u8 lane_mask; +}; + +#define QMP_PHY_INIT_CFG(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .name = #o, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_LANE(o, v, l) \ + { \ + .offset = o, \ + .val = v, \ + .name = #o, \ + .lane_mask = l, \ + } + +static inline void qmp_configure_lane(struct udevice *dev, void __iomem *base, + const struct qmp_phy_init_tbl tbl[], + int num, u8 lane_mask) +{ + int i; + const struct qmp_phy_init_tbl *t = tbl; + + if (!t) + return; + + for (i = 0; i < num; i++, t++) { + if (!(t->lane_mask & lane_mask)) + continue; + + dev_dbg(dev, "Writing Reg: %s Offset: 0x%04x Val: 0x%02x\n", + t->name, t->offset, t->val); + writel(t->val, base + t->offset); + } +} + +static inline void qmp_configure(struct udevice *dev, void __iomem *base, + const struct qmp_phy_init_tbl tbl[], int num) +{ + qmp_configure_lane(dev, base, tbl, num, 0xff); +} + +#endif diff --git a/drivers/phy/qcom/phy-qcom-qmp-dp-com-v3.h b/drivers/phy/qcom/phy-qcom-qmp-dp-com-v3.h new file mode 100644 index 00000000000..396179ef38b --- /dev/null +++ b/drivers/phy/qcom/phy-qcom-qmp-dp-com-v3.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_DP_COM_V3_H_ +#define QCOM_PHY_QMP_DP_COM_V3_H_ + +/* Only for QMP V3 & V4 PHY - DP COM registers */ +#define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00 +#define QPHY_V3_DP_COM_SW_RESET 0x04 +#define QPHY_V3_DP_COM_POWER_DOWN_CTRL 0x08 +#define QPHY_V3_DP_COM_SWI_CTRL 0x0c +#define QPHY_V3_DP_COM_TYPEC_CTRL 0x10 +#define QPHY_V3_DP_COM_TYPEC_PWRDN_CTRL 0x14 +#define QPHY_V3_DP_COM_RESET_OVRD_CTRL 0x1c + +#endif diff --git a/drivers/phy/qcom/phy-qcom-qmp-pcs-usb-v4.h b/drivers/phy/qcom/phy-qcom-qmp-pcs-usb-v4.h new file mode 100644 index 00000000000..d7fd4ac0fc5 --- /dev/null +++ b/drivers/phy/qcom/phy-qcom-qmp-pcs-usb-v4.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_USB_V4_H_ +#define QCOM_PHY_QMP_PCS_USB_V4_H_ + +/* Only for QMP V4 PHY - USB3 PCS registers */ +#define QPHY_V4_PCS_USB3_POWER_STATE_CONFIG1 0x000 +#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_STATUS 0x004 +#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL 0x008 +#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL2 0x00c +#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x010 +#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR 0x014 +#define QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL 0x018 +#define QPHY_V4_PCS_USB3_LFPS_TX_ECSTART 0x01c +#define QPHY_V4_PCS_USB3_LFPS_PER_TIMER_VAL 0x020 +#define QPHY_V4_PCS_USB3_LFPS_TX_END_CNT_U3_START 0x024 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_LOCK_TIME 0x028 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME 0x02c +#define QPHY_V4_PCS_USB3_RXEQTRAINING_CTLE_TIME 0x030 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME_S2 0x034 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2 0x038 +#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_L 0x03c +#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_H 0x040 +#define QPHY_V4_PCS_USB3_ARCVR_DTCT_EN_PERIOD 0x044 +#define QPHY_V4_PCS_USB3_ARCVR_DTCT_CM_DLY 0x048 +#define QPHY_V4_PCS_USB3_TXONESZEROS_RUN_LENGTH 0x04c +#define QPHY_V4_PCS_USB3_ALFPS_DEGLITCH_VAL 0x050 +#define QPHY_V4_PCS_USB3_SIGDET_STARTUP_TIMER_VAL 0x054 +#define QPHY_V4_PCS_USB3_TEST_CONTROL 0x058 + +#endif diff --git a/drivers/phy/qcom/phy-qcom-qmp-ufs.c b/drivers/phy/qcom/phy-qcom-qmp-ufs.c index 907f34744eb..80eba734a63 100644 --- a/drivers/phy/qcom/phy-qcom-qmp-ufs.c +++ b/drivers/phy/qcom/phy-qcom-qmp-ufs.c @@ -119,6 +119,68 @@ static const unsigned int ufsphy_v6_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_PCS_UFS_POWER_DOWN_CONTROL, }; +static const struct qmp_ufs_init_tbl milos_ufsphy_serdes[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IETRIM, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IPTRIM, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BG_TIMER, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_INITVAL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x98), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x32), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x0f), +}; + +static const struct qmp_ufs_init_tbl milos_ufsphy_tx[] = { + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_RES_CODE_LANE_OFFSET_TX, 0x07), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_RES_CODE_LANE_OFFSET_RX, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_FR_DCC_CTRL, 0xcc), +}; + +static const struct qmp_ufs_init_tbl milos_ufsphy_rx[] = { + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_VGA_CAL_MAN_VAL, 0x3e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B0, 0xce), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B1, 0xce), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B2, 0x18), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B3, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B4, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B6, 0x60), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE2_B3, 0x9e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE2_B6, 0x60), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B3, 0x9e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B4, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B5, 0x36), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B8, 0x02), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_PI_CTRL1, 0x94), +}; + +static const struct qmp_ufs_init_tbl milos_ufsphy_pcs[] = { + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_MID_TERM_CTRL1, 0x43), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x0b), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_SIGDET_CTRL2, 0x68), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x04), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x04), +}; + static const struct qmp_ufs_init_tbl sdm845_ufsphy_serdes[] = { QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04), @@ -982,6 +1044,31 @@ static const struct qmp_ufs_offsets qmp_ufs_offsets_v6 = { .rx2 = 0x1a00, }; +static const struct qmp_ufs_cfg milos_ufsphy_cfg = { + .lanes = 2, + + .offsets = &qmp_ufs_offsets_v6, + + .tbls = { + .serdes = milos_ufsphy_serdes, + .serdes_num = ARRAY_SIZE(milos_ufsphy_serdes), + .tx = milos_ufsphy_tx, + .tx_num = ARRAY_SIZE(milos_ufsphy_tx), + .rx = milos_ufsphy_rx, + .rx_num = ARRAY_SIZE(milos_ufsphy_rx), + .pcs = milos_ufsphy_pcs, + .pcs_num = ARRAY_SIZE(milos_ufsphy_pcs), + }, + .tbls_hs_b = { + .serdes = sm8550_ufsphy_hs_b_serdes, + .serdes_num = ARRAY_SIZE(sm8550_ufsphy_hs_b_serdes), + }, + + .vreg_list = qmp_ufs_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_ufs_vreg_l), + .regs = ufsphy_v6_regs_layout, +}; + static const struct qmp_ufs_cfg sdm845_ufsphy_cfg = { .lanes = 2, @@ -1651,6 +1738,7 @@ static struct phy_ops qmp_ufs_ops = { }; static const struct udevice_id qmp_ufs_ids[] = { + { .compatible = "qcom,milos-qmp-ufs-phy", .data = (ulong)&milos_ufsphy_cfg, }, { .compatible = "qcom,sa8775p-qmp-ufs-phy", .data = (ulong)&sa8775p_ufsphy_cfg, }, { .compatible = "qcom,sdm845-qmp-ufs-phy", .data = (ulong)&sdm845_ufsphy_cfg }, { .compatible = "qcom,sm6350-qmp-ufs-phy", .data = (ulong)&sdm845_ufsphy_cfg }, diff --git a/drivers/phy/qcom/phy-qcom-qmp.h b/drivers/phy/qcom/phy-qcom-qmp.h index 99f4d447caf..06dac21ddc4 100644 --- a/drivers/phy/qcom/phy-qcom-qmp.h +++ b/drivers/phy/qcom/phy-qcom-qmp.h @@ -12,12 +12,17 @@ #include "phy-qcom-qmp-qserdes-com-v3.h" #include "phy-qcom-qmp-qserdes-txrx-v3.h" +#include "phy-qcom-qmp-qserdes-com-v4.h" +#include "phy-qcom-qmp-qserdes-txrx-v4.h" + #include "phy-qcom-qmp-qserdes-pll.h" #include "phy-qcom-qmp-pcs-v2.h" #include "phy-qcom-qmp-pcs-v3.h" +#include "phy-qcom-qmp-pcs-v4.h" + /* Only for QMP V3 & V4 PHY - DP COM registers */ #define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00 #define QPHY_V3_DP_COM_SW_RESET 0x04 @@ -112,4 +117,16 @@ #define QSERDES_V6_DP_PHY_AUX_INTERRUPT_STATUS 0x0e0 #define QSERDES_V6_DP_PHY_STATUS 0x0e4 +/* QPHY_SW_RESET bit */ +#define SW_RESET BIT(0) +/* QPHY_POWER_DOWN_CONTROL */ +#define SW_PWRDN BIT(0) + +/* QPHY_START_CONTROL bits */ +#define SERDES_START BIT(0) +#define PCS_START BIT(1) + +/* QPHY_PCS_STATUS bit */ +#define PHYSTATUS BIT(6) + #endif diff --git a/drivers/phy/qcom/phy-qcom-qusb2.c b/drivers/phy/qcom/phy-qcom-qusb2.c index 9e821365c15..6278171b100 100644 --- a/drivers/phy/qcom/phy-qcom-qusb2.c +++ b/drivers/phy/qcom/phy-qcom-qusb2.c @@ -176,6 +176,19 @@ static const unsigned int sm6115_regs_layout[] = { [QUSB2PHY_PORT_POWERDOWN] = 0xb4, [QUSB2PHY_INTR_CTRL] = 0xbc, }; +static const unsigned int ipq6018_regs_layout[] = { + [QUSB2PHY_PLL_STATUS] = 0x38, + [QUSB2PHY_PORT_TUNE1] = 0x80, + [QUSB2PHY_PORT_TUNE2] = 0x84, + [QUSB2PHY_PORT_TUNE3] = 0x88, + [QUSB2PHY_PORT_TUNE4] = 0x8C, + [QUSB2PHY_PORT_TUNE5] = 0x90, + [QUSB2PHY_PORT_TEST1] = 0x98, + [QUSB2PHY_PORT_TEST2] = 0x9C, + [QUSB2PHY_PORT_POWERDOWN] = 0xB4, + [QUSB2PHY_INTR_CTRL] = 0xBC, +}; + static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = { QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8), QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0xb3), @@ -189,6 +202,19 @@ static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = { QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), }; +static const struct qusb2_phy_init_tbl qcs615_init_tbl[] = { + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xc8), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0xb3), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x83), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0xc0), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), +}; + static const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = { QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x03), QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c), @@ -260,6 +286,16 @@ static const struct qusb2_phy_cfg sdm660_phy_cfg = { .autoresume_en = BIT(3), }; +static const struct qusb2_phy_cfg qcs615_phy_cfg = { + .tbl = qcs615_init_tbl, + .tbl_num = ARRAY_SIZE(qcs615_init_tbl), + .regs = ipq6018_regs_layout, + + .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN), + .mask_core_ready = PLL_LOCKED, + .autoresume_en = BIT(0), +}; + static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = { .tbl = qusb2_v2_init_tbl, .tbl_num = ARRAY_SIZE(qusb2_v2_init_tbl), @@ -467,6 +503,8 @@ static const struct udevice_id qusb2phy_ids[] = { { .compatible = "qcom,qusb2-phy" }, { .compatible = "qcom,qcm2290-qusb2-phy", .data = (ulong)&sm6115_phy_cfg }, + { .compatible = "qcom,qcs615-qusb2-phy", + .data = (ulong)&qcs615_phy_cfg }, { .compatible = "qcom,sdm660-qusb2-phy", .data = (ulong)&sdm660_phy_cfg }, { .compatible = "qcom,sm6115-qusb2-phy", |
