1c1eb8f83SChunfeng Yun // SPDX-License-Identifier: GPL-2.0 2c1eb8f83SChunfeng Yun /* 3c1eb8f83SChunfeng Yun * MediaTek USB3.1 gen2 xsphy Driver 4c1eb8f83SChunfeng Yun * 5c1eb8f83SChunfeng Yun * Copyright (c) 2018 MediaTek Inc. 6c1eb8f83SChunfeng Yun * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> 7c1eb8f83SChunfeng Yun * 8c1eb8f83SChunfeng Yun */ 9c1eb8f83SChunfeng Yun 10c1eb8f83SChunfeng Yun #include <dt-bindings/phy/phy.h> 11c1eb8f83SChunfeng Yun #include <linux/clk.h> 12c1eb8f83SChunfeng Yun #include <linux/delay.h> 13c1eb8f83SChunfeng Yun #include <linux/io.h> 14c1eb8f83SChunfeng Yun #include <linux/iopoll.h> 15c1eb8f83SChunfeng Yun #include <linux/module.h> 16c1eb8f83SChunfeng Yun #include <linux/of_address.h> 17c1eb8f83SChunfeng Yun #include <linux/phy/phy.h> 18c1eb8f83SChunfeng Yun #include <linux/platform_device.h> 19c1eb8f83SChunfeng Yun 20c1eb8f83SChunfeng Yun /* u2 phy banks */ 21c1eb8f83SChunfeng Yun #define SSUSB_SIFSLV_MISC 0x000 22c1eb8f83SChunfeng Yun #define SSUSB_SIFSLV_U2FREQ 0x100 23c1eb8f83SChunfeng Yun #define SSUSB_SIFSLV_U2PHY_COM 0x300 24c1eb8f83SChunfeng Yun 25c1eb8f83SChunfeng Yun /* u3 phy shared banks */ 26c1eb8f83SChunfeng Yun #define SSPXTP_SIFSLV_DIG_GLB 0x000 27c1eb8f83SChunfeng Yun #define SSPXTP_SIFSLV_PHYA_GLB 0x100 28c1eb8f83SChunfeng Yun 29c1eb8f83SChunfeng Yun /* u3 phy banks */ 30c1eb8f83SChunfeng Yun #define SSPXTP_SIFSLV_DIG_LN_TOP 0x000 31c1eb8f83SChunfeng Yun #define SSPXTP_SIFSLV_DIG_LN_TX0 0x100 32c1eb8f83SChunfeng Yun #define SSPXTP_SIFSLV_DIG_LN_RX0 0x200 33c1eb8f83SChunfeng Yun #define SSPXTP_SIFSLV_DIG_LN_DAIF 0x300 34c1eb8f83SChunfeng Yun #define SSPXTP_SIFSLV_PHYA_LN 0x400 35c1eb8f83SChunfeng Yun 36c1eb8f83SChunfeng Yun #define XSP_U2FREQ_FMCR0 ((SSUSB_SIFSLV_U2FREQ) + 0x00) 37c1eb8f83SChunfeng Yun #define P2F_RG_FREQDET_EN BIT(24) 38c1eb8f83SChunfeng Yun #define P2F_RG_CYCLECNT GENMASK(23, 0) 39c1eb8f83SChunfeng Yun #define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x)) 40c1eb8f83SChunfeng Yun 41c1eb8f83SChunfeng Yun #define XSP_U2FREQ_MMONR0 ((SSUSB_SIFSLV_U2FREQ) + 0x0c) 42c1eb8f83SChunfeng Yun 43c1eb8f83SChunfeng Yun #define XSP_U2FREQ_FMMONR1 ((SSUSB_SIFSLV_U2FREQ) + 0x10) 44c1eb8f83SChunfeng Yun #define P2F_RG_FRCK_EN BIT(8) 45c1eb8f83SChunfeng Yun #define P2F_USB_FM_VALID BIT(0) 46c1eb8f83SChunfeng Yun 47c1eb8f83SChunfeng Yun #define XSP_USBPHYACR0 ((SSUSB_SIFSLV_U2PHY_COM) + 0x00) 48c1eb8f83SChunfeng Yun #define P2A0_RG_INTR_EN BIT(5) 49c1eb8f83SChunfeng Yun 50c1eb8f83SChunfeng Yun #define XSP_USBPHYACR1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x04) 51c1eb8f83SChunfeng Yun #define P2A1_RG_INTR_CAL GENMASK(23, 19) 52c1eb8f83SChunfeng Yun #define P2A1_RG_INTR_CAL_VAL(x) ((0x1f & (x)) << 19) 53c1eb8f83SChunfeng Yun #define P2A1_RG_VRT_SEL GENMASK(14, 12) 54c1eb8f83SChunfeng Yun #define P2A1_RG_VRT_SEL_VAL(x) ((0x7 & (x)) << 12) 55c1eb8f83SChunfeng Yun #define P2A1_RG_TERM_SEL GENMASK(10, 8) 56c1eb8f83SChunfeng Yun #define P2A1_RG_TERM_SEL_VAL(x) ((0x7 & (x)) << 8) 57c1eb8f83SChunfeng Yun 58c1eb8f83SChunfeng Yun #define XSP_USBPHYACR5 ((SSUSB_SIFSLV_U2PHY_COM) + 0x014) 59c1eb8f83SChunfeng Yun #define P2A5_RG_HSTX_SRCAL_EN BIT(15) 60c1eb8f83SChunfeng Yun #define P2A5_RG_HSTX_SRCTRL GENMASK(14, 12) 61c1eb8f83SChunfeng Yun #define P2A5_RG_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12) 62c1eb8f83SChunfeng Yun 63c1eb8f83SChunfeng Yun #define XSP_USBPHYACR6 ((SSUSB_SIFSLV_U2PHY_COM) + 0x018) 64c1eb8f83SChunfeng Yun #define P2A6_RG_BC11_SW_EN BIT(23) 65c1eb8f83SChunfeng Yun #define P2A6_RG_OTG_VBUSCMP_EN BIT(20) 66c1eb8f83SChunfeng Yun 67c1eb8f83SChunfeng Yun #define XSP_U2PHYDTM1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x06C) 68c1eb8f83SChunfeng Yun #define P2D_FORCE_IDDIG BIT(9) 69c1eb8f83SChunfeng Yun #define P2D_RG_VBUSVALID BIT(5) 70c1eb8f83SChunfeng Yun #define P2D_RG_SESSEND BIT(4) 71c1eb8f83SChunfeng Yun #define P2D_RG_AVALID BIT(2) 72c1eb8f83SChunfeng Yun #define P2D_RG_IDDIG BIT(1) 73c1eb8f83SChunfeng Yun 74c1eb8f83SChunfeng Yun #define SSPXTP_PHYA_GLB_00 ((SSPXTP_SIFSLV_PHYA_GLB) + 0x00) 75c1eb8f83SChunfeng Yun #define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(21, 16) 76c1eb8f83SChunfeng Yun #define RG_XTP_GLB_BIAS_INTR_CTRL_VAL(x) ((0x3f & (x)) << 16) 77c1eb8f83SChunfeng Yun 78c1eb8f83SChunfeng Yun #define SSPXTP_PHYA_LN_04 ((SSPXTP_SIFSLV_PHYA_LN) + 0x04) 79c1eb8f83SChunfeng Yun #define RG_XTP_LN0_TX_IMPSEL GENMASK(4, 0) 80c1eb8f83SChunfeng Yun #define RG_XTP_LN0_TX_IMPSEL_VAL(x) (0x1f & (x)) 81c1eb8f83SChunfeng Yun 82c1eb8f83SChunfeng Yun #define SSPXTP_PHYA_LN_14 ((SSPXTP_SIFSLV_PHYA_LN) + 0x014) 83c1eb8f83SChunfeng Yun #define RG_XTP_LN0_RX_IMPSEL GENMASK(4, 0) 84c1eb8f83SChunfeng Yun #define RG_XTP_LN0_RX_IMPSEL_VAL(x) (0x1f & (x)) 85c1eb8f83SChunfeng Yun 86c1eb8f83SChunfeng Yun #define XSP_REF_CLK 26 /* MHZ */ 87c1eb8f83SChunfeng Yun #define XSP_SLEW_RATE_COEF 17 88c1eb8f83SChunfeng Yun #define XSP_SR_COEF_DIVISOR 1000 89c1eb8f83SChunfeng Yun #define XSP_FM_DET_CYCLE_CNT 1024 90c1eb8f83SChunfeng Yun 91c1eb8f83SChunfeng Yun struct xsphy_instance { 92c1eb8f83SChunfeng Yun struct phy *phy; 93c1eb8f83SChunfeng Yun void __iomem *port_base; 94c1eb8f83SChunfeng Yun struct clk *ref_clk; /* reference clock of anolog phy */ 95c1eb8f83SChunfeng Yun u32 index; 96c1eb8f83SChunfeng Yun u32 type; 97c1eb8f83SChunfeng Yun /* only for HQA test */ 98c1eb8f83SChunfeng Yun int efuse_intr; 99c1eb8f83SChunfeng Yun int efuse_tx_imp; 100c1eb8f83SChunfeng Yun int efuse_rx_imp; 101c1eb8f83SChunfeng Yun /* u2 eye diagram */ 102c1eb8f83SChunfeng Yun int eye_src; 103c1eb8f83SChunfeng Yun int eye_vrt; 104c1eb8f83SChunfeng Yun int eye_term; 105c1eb8f83SChunfeng Yun }; 106c1eb8f83SChunfeng Yun 107c1eb8f83SChunfeng Yun struct mtk_xsphy { 108c1eb8f83SChunfeng Yun struct device *dev; 109c1eb8f83SChunfeng Yun void __iomem *glb_base; /* only shared u3 sif */ 110c1eb8f83SChunfeng Yun struct xsphy_instance **phys; 111c1eb8f83SChunfeng Yun int nphys; 112c1eb8f83SChunfeng Yun int src_ref_clk; /* MHZ, reference clock for slew rate calibrate */ 113c1eb8f83SChunfeng Yun int src_coef; /* coefficient for slew rate calibrate */ 114c1eb8f83SChunfeng Yun }; 115c1eb8f83SChunfeng Yun 116c1eb8f83SChunfeng Yun static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, 117c1eb8f83SChunfeng Yun struct xsphy_instance *inst) 118c1eb8f83SChunfeng Yun { 119c1eb8f83SChunfeng Yun void __iomem *pbase = inst->port_base; 120c1eb8f83SChunfeng Yun int calib_val; 121c1eb8f83SChunfeng Yun int fm_out; 122c1eb8f83SChunfeng Yun u32 tmp; 123c1eb8f83SChunfeng Yun 124c1eb8f83SChunfeng Yun /* use force value */ 125c1eb8f83SChunfeng Yun if (inst->eye_src) 126c1eb8f83SChunfeng Yun return; 127c1eb8f83SChunfeng Yun 128c1eb8f83SChunfeng Yun /* enable USB ring oscillator */ 129c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_USBPHYACR5); 130c1eb8f83SChunfeng Yun tmp |= P2A5_RG_HSTX_SRCAL_EN; 131c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_USBPHYACR5); 132c1eb8f83SChunfeng Yun udelay(1); /* wait clock stable */ 133c1eb8f83SChunfeng Yun 134c1eb8f83SChunfeng Yun /* enable free run clock */ 135c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_U2FREQ_FMMONR1); 136c1eb8f83SChunfeng Yun tmp |= P2F_RG_FRCK_EN; 137c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_U2FREQ_FMMONR1); 138c1eb8f83SChunfeng Yun 139c1eb8f83SChunfeng Yun /* set cycle count as 1024 */ 140c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_U2FREQ_FMCR0); 141c1eb8f83SChunfeng Yun tmp &= ~(P2F_RG_CYCLECNT); 142c1eb8f83SChunfeng Yun tmp |= P2F_RG_CYCLECNT_VAL(XSP_FM_DET_CYCLE_CNT); 143c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_U2FREQ_FMCR0); 144c1eb8f83SChunfeng Yun 145c1eb8f83SChunfeng Yun /* enable frequency meter */ 146c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_U2FREQ_FMCR0); 147c1eb8f83SChunfeng Yun tmp |= P2F_RG_FREQDET_EN; 148c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_U2FREQ_FMCR0); 149c1eb8f83SChunfeng Yun 150c1eb8f83SChunfeng Yun /* ignore return value */ 151c1eb8f83SChunfeng Yun readl_poll_timeout(pbase + XSP_U2FREQ_FMMONR1, tmp, 152c1eb8f83SChunfeng Yun (tmp & P2F_USB_FM_VALID), 10, 200); 153c1eb8f83SChunfeng Yun 154c1eb8f83SChunfeng Yun fm_out = readl(pbase + XSP_U2FREQ_MMONR0); 155c1eb8f83SChunfeng Yun 156c1eb8f83SChunfeng Yun /* disable frequency meter */ 157c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_U2FREQ_FMCR0); 158c1eb8f83SChunfeng Yun tmp &= ~P2F_RG_FREQDET_EN; 159c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_U2FREQ_FMCR0); 160c1eb8f83SChunfeng Yun 161c1eb8f83SChunfeng Yun /* disable free run clock */ 162c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_U2FREQ_FMMONR1); 163c1eb8f83SChunfeng Yun tmp &= ~P2F_RG_FRCK_EN; 164c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_U2FREQ_FMMONR1); 165c1eb8f83SChunfeng Yun 166c1eb8f83SChunfeng Yun if (fm_out) { 167c1eb8f83SChunfeng Yun /* (1024 / FM_OUT) x reference clock frequency x coefficient */ 168c1eb8f83SChunfeng Yun tmp = xsphy->src_ref_clk * xsphy->src_coef; 169c1eb8f83SChunfeng Yun tmp = (tmp * XSP_FM_DET_CYCLE_CNT) / fm_out; 170c1eb8f83SChunfeng Yun calib_val = DIV_ROUND_CLOSEST(tmp, XSP_SR_COEF_DIVISOR); 171c1eb8f83SChunfeng Yun } else { 172c1eb8f83SChunfeng Yun /* if FM detection fail, set default value */ 173c1eb8f83SChunfeng Yun calib_val = 3; 174c1eb8f83SChunfeng Yun } 175c1eb8f83SChunfeng Yun dev_dbg(xsphy->dev, "phy.%d, fm_out:%d, calib:%d (clk:%d, coef:%d)\n", 176c1eb8f83SChunfeng Yun inst->index, fm_out, calib_val, 177c1eb8f83SChunfeng Yun xsphy->src_ref_clk, xsphy->src_coef); 178c1eb8f83SChunfeng Yun 179c1eb8f83SChunfeng Yun /* set HS slew rate */ 180c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_USBPHYACR5); 181c1eb8f83SChunfeng Yun tmp &= ~P2A5_RG_HSTX_SRCTRL; 182c1eb8f83SChunfeng Yun tmp |= P2A5_RG_HSTX_SRCTRL_VAL(calib_val); 183c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_USBPHYACR5); 184c1eb8f83SChunfeng Yun 185c1eb8f83SChunfeng Yun /* disable USB ring oscillator */ 186c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_USBPHYACR5); 187c1eb8f83SChunfeng Yun tmp &= ~P2A5_RG_HSTX_SRCAL_EN; 188c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_USBPHYACR5); 189c1eb8f83SChunfeng Yun } 190c1eb8f83SChunfeng Yun 191c1eb8f83SChunfeng Yun static void u2_phy_instance_init(struct mtk_xsphy *xsphy, 192c1eb8f83SChunfeng Yun struct xsphy_instance *inst) 193c1eb8f83SChunfeng Yun { 194c1eb8f83SChunfeng Yun void __iomem *pbase = inst->port_base; 195c1eb8f83SChunfeng Yun u32 tmp; 196c1eb8f83SChunfeng Yun 197c1eb8f83SChunfeng Yun /* DP/DM BC1.1 path Disable */ 198c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_USBPHYACR6); 199c1eb8f83SChunfeng Yun tmp &= ~P2A6_RG_BC11_SW_EN; 200c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_USBPHYACR6); 201c1eb8f83SChunfeng Yun 202c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_USBPHYACR0); 203c1eb8f83SChunfeng Yun tmp |= P2A0_RG_INTR_EN; 204c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_USBPHYACR0); 205c1eb8f83SChunfeng Yun } 206c1eb8f83SChunfeng Yun 207c1eb8f83SChunfeng Yun static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy, 208c1eb8f83SChunfeng Yun struct xsphy_instance *inst) 209c1eb8f83SChunfeng Yun { 210c1eb8f83SChunfeng Yun void __iomem *pbase = inst->port_base; 211c1eb8f83SChunfeng Yun u32 index = inst->index; 212c1eb8f83SChunfeng Yun u32 tmp; 213c1eb8f83SChunfeng Yun 214c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_USBPHYACR6); 215c1eb8f83SChunfeng Yun tmp |= P2A6_RG_OTG_VBUSCMP_EN; 216c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_USBPHYACR6); 217c1eb8f83SChunfeng Yun 218c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_U2PHYDTM1); 219c1eb8f83SChunfeng Yun tmp |= P2D_RG_VBUSVALID | P2D_RG_AVALID; 220c1eb8f83SChunfeng Yun tmp &= ~P2D_RG_SESSEND; 221c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_U2PHYDTM1); 222c1eb8f83SChunfeng Yun 223c1eb8f83SChunfeng Yun dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); 224c1eb8f83SChunfeng Yun } 225c1eb8f83SChunfeng Yun 226c1eb8f83SChunfeng Yun static void u2_phy_instance_power_off(struct mtk_xsphy *xsphy, 227c1eb8f83SChunfeng Yun struct xsphy_instance *inst) 228c1eb8f83SChunfeng Yun { 229c1eb8f83SChunfeng Yun void __iomem *pbase = inst->port_base; 230c1eb8f83SChunfeng Yun u32 index = inst->index; 231c1eb8f83SChunfeng Yun u32 tmp; 232c1eb8f83SChunfeng Yun 233c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_USBPHYACR6); 234c1eb8f83SChunfeng Yun tmp &= ~P2A6_RG_OTG_VBUSCMP_EN; 235c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_USBPHYACR6); 236c1eb8f83SChunfeng Yun 237c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_U2PHYDTM1); 238c1eb8f83SChunfeng Yun tmp &= ~(P2D_RG_VBUSVALID | P2D_RG_AVALID); 239c1eb8f83SChunfeng Yun tmp |= P2D_RG_SESSEND; 240c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_U2PHYDTM1); 241c1eb8f83SChunfeng Yun 242c1eb8f83SChunfeng Yun dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); 243c1eb8f83SChunfeng Yun } 244c1eb8f83SChunfeng Yun 245c1eb8f83SChunfeng Yun static void u2_phy_instance_set_mode(struct mtk_xsphy *xsphy, 246c1eb8f83SChunfeng Yun struct xsphy_instance *inst, 247c1eb8f83SChunfeng Yun enum phy_mode mode) 248c1eb8f83SChunfeng Yun { 249c1eb8f83SChunfeng Yun u32 tmp; 250c1eb8f83SChunfeng Yun 251c1eb8f83SChunfeng Yun tmp = readl(inst->port_base + XSP_U2PHYDTM1); 252c1eb8f83SChunfeng Yun switch (mode) { 253c1eb8f83SChunfeng Yun case PHY_MODE_USB_DEVICE: 254c1eb8f83SChunfeng Yun tmp |= P2D_FORCE_IDDIG | P2D_RG_IDDIG; 255c1eb8f83SChunfeng Yun break; 256c1eb8f83SChunfeng Yun case PHY_MODE_USB_HOST: 257c1eb8f83SChunfeng Yun tmp |= P2D_FORCE_IDDIG; 258c1eb8f83SChunfeng Yun tmp &= ~P2D_RG_IDDIG; 259c1eb8f83SChunfeng Yun break; 260c1eb8f83SChunfeng Yun case PHY_MODE_USB_OTG: 261c1eb8f83SChunfeng Yun tmp &= ~(P2D_FORCE_IDDIG | P2D_RG_IDDIG); 262c1eb8f83SChunfeng Yun break; 263c1eb8f83SChunfeng Yun default: 264c1eb8f83SChunfeng Yun return; 265c1eb8f83SChunfeng Yun } 266c1eb8f83SChunfeng Yun writel(tmp, inst->port_base + XSP_U2PHYDTM1); 267c1eb8f83SChunfeng Yun } 268c1eb8f83SChunfeng Yun 269c1eb8f83SChunfeng Yun static void phy_parse_property(struct mtk_xsphy *xsphy, 270c1eb8f83SChunfeng Yun struct xsphy_instance *inst) 271c1eb8f83SChunfeng Yun { 272c1eb8f83SChunfeng Yun struct device *dev = &inst->phy->dev; 273c1eb8f83SChunfeng Yun 274c1eb8f83SChunfeng Yun switch (inst->type) { 275c1eb8f83SChunfeng Yun case PHY_TYPE_USB2: 276c1eb8f83SChunfeng Yun device_property_read_u32(dev, "mediatek,efuse-intr", 277c1eb8f83SChunfeng Yun &inst->efuse_intr); 278c1eb8f83SChunfeng Yun device_property_read_u32(dev, "mediatek,eye-src", 279c1eb8f83SChunfeng Yun &inst->eye_src); 280c1eb8f83SChunfeng Yun device_property_read_u32(dev, "mediatek,eye-vrt", 281c1eb8f83SChunfeng Yun &inst->eye_vrt); 282c1eb8f83SChunfeng Yun device_property_read_u32(dev, "mediatek,eye-term", 283c1eb8f83SChunfeng Yun &inst->eye_term); 284c1eb8f83SChunfeng Yun dev_dbg(dev, "intr:%d, src:%d, vrt:%d, term:%d\n", 285c1eb8f83SChunfeng Yun inst->efuse_intr, inst->eye_src, 286c1eb8f83SChunfeng Yun inst->eye_vrt, inst->eye_term); 287c1eb8f83SChunfeng Yun break; 288c1eb8f83SChunfeng Yun case PHY_TYPE_USB3: 289c1eb8f83SChunfeng Yun device_property_read_u32(dev, "mediatek,efuse-intr", 290c1eb8f83SChunfeng Yun &inst->efuse_intr); 291c1eb8f83SChunfeng Yun device_property_read_u32(dev, "mediatek,efuse-tx-imp", 292c1eb8f83SChunfeng Yun &inst->efuse_tx_imp); 293c1eb8f83SChunfeng Yun device_property_read_u32(dev, "mediatek,efuse-rx-imp", 294c1eb8f83SChunfeng Yun &inst->efuse_rx_imp); 295c1eb8f83SChunfeng Yun dev_dbg(dev, "intr:%d, tx-imp:%d, rx-imp:%d\n", 296c1eb8f83SChunfeng Yun inst->efuse_intr, inst->efuse_tx_imp, 297c1eb8f83SChunfeng Yun inst->efuse_rx_imp); 298c1eb8f83SChunfeng Yun break; 299c1eb8f83SChunfeng Yun default: 300c1eb8f83SChunfeng Yun dev_err(xsphy->dev, "incompatible phy type\n"); 301c1eb8f83SChunfeng Yun return; 302c1eb8f83SChunfeng Yun } 303c1eb8f83SChunfeng Yun } 304c1eb8f83SChunfeng Yun 305c1eb8f83SChunfeng Yun static void u2_phy_props_set(struct mtk_xsphy *xsphy, 306c1eb8f83SChunfeng Yun struct xsphy_instance *inst) 307c1eb8f83SChunfeng Yun { 308c1eb8f83SChunfeng Yun void __iomem *pbase = inst->port_base; 309c1eb8f83SChunfeng Yun u32 tmp; 310c1eb8f83SChunfeng Yun 311c1eb8f83SChunfeng Yun if (inst->efuse_intr) { 312c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_USBPHYACR1); 313c1eb8f83SChunfeng Yun tmp &= ~P2A1_RG_INTR_CAL; 314c1eb8f83SChunfeng Yun tmp |= P2A1_RG_INTR_CAL_VAL(inst->efuse_intr); 315c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_USBPHYACR1); 316c1eb8f83SChunfeng Yun } 317c1eb8f83SChunfeng Yun 318c1eb8f83SChunfeng Yun if (inst->eye_src) { 319c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_USBPHYACR5); 320c1eb8f83SChunfeng Yun tmp &= ~P2A5_RG_HSTX_SRCTRL; 321c1eb8f83SChunfeng Yun tmp |= P2A5_RG_HSTX_SRCTRL_VAL(inst->eye_src); 322c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_USBPHYACR5); 323c1eb8f83SChunfeng Yun } 324c1eb8f83SChunfeng Yun 325c1eb8f83SChunfeng Yun if (inst->eye_vrt) { 326c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_USBPHYACR1); 327c1eb8f83SChunfeng Yun tmp &= ~P2A1_RG_VRT_SEL; 328c1eb8f83SChunfeng Yun tmp |= P2A1_RG_VRT_SEL_VAL(inst->eye_vrt); 329c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_USBPHYACR1); 330c1eb8f83SChunfeng Yun } 331c1eb8f83SChunfeng Yun 332c1eb8f83SChunfeng Yun if (inst->eye_term) { 333c1eb8f83SChunfeng Yun tmp = readl(pbase + XSP_USBPHYACR1); 334c1eb8f83SChunfeng Yun tmp &= ~P2A1_RG_TERM_SEL; 335c1eb8f83SChunfeng Yun tmp |= P2A1_RG_TERM_SEL_VAL(inst->eye_term); 336c1eb8f83SChunfeng Yun writel(tmp, pbase + XSP_USBPHYACR1); 337c1eb8f83SChunfeng Yun } 338c1eb8f83SChunfeng Yun } 339c1eb8f83SChunfeng Yun 340c1eb8f83SChunfeng Yun static void u3_phy_props_set(struct mtk_xsphy *xsphy, 341c1eb8f83SChunfeng Yun struct xsphy_instance *inst) 342c1eb8f83SChunfeng Yun { 343c1eb8f83SChunfeng Yun void __iomem *pbase = inst->port_base; 344c1eb8f83SChunfeng Yun u32 tmp; 345c1eb8f83SChunfeng Yun 346c1eb8f83SChunfeng Yun if (inst->efuse_intr) { 347c1eb8f83SChunfeng Yun tmp = readl(xsphy->glb_base + SSPXTP_PHYA_GLB_00); 348c1eb8f83SChunfeng Yun tmp &= ~RG_XTP_GLB_BIAS_INTR_CTRL; 349c1eb8f83SChunfeng Yun tmp |= RG_XTP_GLB_BIAS_INTR_CTRL_VAL(inst->efuse_intr); 350c1eb8f83SChunfeng Yun writel(tmp, xsphy->glb_base + SSPXTP_PHYA_GLB_00); 351c1eb8f83SChunfeng Yun } 352c1eb8f83SChunfeng Yun 353c1eb8f83SChunfeng Yun if (inst->efuse_tx_imp) { 354c1eb8f83SChunfeng Yun tmp = readl(pbase + SSPXTP_PHYA_LN_04); 355c1eb8f83SChunfeng Yun tmp &= ~RG_XTP_LN0_TX_IMPSEL; 356c1eb8f83SChunfeng Yun tmp |= RG_XTP_LN0_TX_IMPSEL_VAL(inst->efuse_tx_imp); 357c1eb8f83SChunfeng Yun writel(tmp, pbase + SSPXTP_PHYA_LN_04); 358c1eb8f83SChunfeng Yun } 359c1eb8f83SChunfeng Yun 360c1eb8f83SChunfeng Yun if (inst->efuse_rx_imp) { 361c1eb8f83SChunfeng Yun tmp = readl(pbase + SSPXTP_PHYA_LN_14); 362c1eb8f83SChunfeng Yun tmp &= ~RG_XTP_LN0_RX_IMPSEL; 363c1eb8f83SChunfeng Yun tmp |= RG_XTP_LN0_RX_IMPSEL_VAL(inst->efuse_rx_imp); 364c1eb8f83SChunfeng Yun writel(tmp, pbase + SSPXTP_PHYA_LN_14); 365c1eb8f83SChunfeng Yun } 366c1eb8f83SChunfeng Yun } 367c1eb8f83SChunfeng Yun 368c1eb8f83SChunfeng Yun static int mtk_phy_init(struct phy *phy) 369c1eb8f83SChunfeng Yun { 370c1eb8f83SChunfeng Yun struct xsphy_instance *inst = phy_get_drvdata(phy); 371c1eb8f83SChunfeng Yun struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); 372c1eb8f83SChunfeng Yun int ret; 373c1eb8f83SChunfeng Yun 374c1eb8f83SChunfeng Yun ret = clk_prepare_enable(inst->ref_clk); 375c1eb8f83SChunfeng Yun if (ret) { 376c1eb8f83SChunfeng Yun dev_err(xsphy->dev, "failed to enable ref_clk\n"); 377c1eb8f83SChunfeng Yun return ret; 378c1eb8f83SChunfeng Yun } 379c1eb8f83SChunfeng Yun 380c1eb8f83SChunfeng Yun switch (inst->type) { 381c1eb8f83SChunfeng Yun case PHY_TYPE_USB2: 382c1eb8f83SChunfeng Yun u2_phy_instance_init(xsphy, inst); 383c1eb8f83SChunfeng Yun u2_phy_props_set(xsphy, inst); 384c1eb8f83SChunfeng Yun break; 385c1eb8f83SChunfeng Yun case PHY_TYPE_USB3: 386c1eb8f83SChunfeng Yun u3_phy_props_set(xsphy, inst); 387c1eb8f83SChunfeng Yun break; 388c1eb8f83SChunfeng Yun default: 389c1eb8f83SChunfeng Yun dev_err(xsphy->dev, "incompatible phy type\n"); 390c1eb8f83SChunfeng Yun clk_disable_unprepare(inst->ref_clk); 391c1eb8f83SChunfeng Yun return -EINVAL; 392c1eb8f83SChunfeng Yun } 393c1eb8f83SChunfeng Yun 394c1eb8f83SChunfeng Yun return 0; 395c1eb8f83SChunfeng Yun } 396c1eb8f83SChunfeng Yun 397c1eb8f83SChunfeng Yun static int mtk_phy_power_on(struct phy *phy) 398c1eb8f83SChunfeng Yun { 399c1eb8f83SChunfeng Yun struct xsphy_instance *inst = phy_get_drvdata(phy); 400c1eb8f83SChunfeng Yun struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); 401c1eb8f83SChunfeng Yun 402c1eb8f83SChunfeng Yun if (inst->type == PHY_TYPE_USB2) { 403c1eb8f83SChunfeng Yun u2_phy_instance_power_on(xsphy, inst); 404c1eb8f83SChunfeng Yun u2_phy_slew_rate_calibrate(xsphy, inst); 405c1eb8f83SChunfeng Yun } 406c1eb8f83SChunfeng Yun 407c1eb8f83SChunfeng Yun return 0; 408c1eb8f83SChunfeng Yun } 409c1eb8f83SChunfeng Yun 410c1eb8f83SChunfeng Yun static int mtk_phy_power_off(struct phy *phy) 411c1eb8f83SChunfeng Yun { 412c1eb8f83SChunfeng Yun struct xsphy_instance *inst = phy_get_drvdata(phy); 413c1eb8f83SChunfeng Yun struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); 414c1eb8f83SChunfeng Yun 415c1eb8f83SChunfeng Yun if (inst->type == PHY_TYPE_USB2) 416c1eb8f83SChunfeng Yun u2_phy_instance_power_off(xsphy, inst); 417c1eb8f83SChunfeng Yun 418c1eb8f83SChunfeng Yun return 0; 419c1eb8f83SChunfeng Yun } 420c1eb8f83SChunfeng Yun 421c1eb8f83SChunfeng Yun static int mtk_phy_exit(struct phy *phy) 422c1eb8f83SChunfeng Yun { 423c1eb8f83SChunfeng Yun struct xsphy_instance *inst = phy_get_drvdata(phy); 424c1eb8f83SChunfeng Yun 425c1eb8f83SChunfeng Yun clk_disable_unprepare(inst->ref_clk); 426c1eb8f83SChunfeng Yun return 0; 427c1eb8f83SChunfeng Yun } 428c1eb8f83SChunfeng Yun 42979a5a18aSGrygorii Strashko static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) 430c1eb8f83SChunfeng Yun { 431c1eb8f83SChunfeng Yun struct xsphy_instance *inst = phy_get_drvdata(phy); 432c1eb8f83SChunfeng Yun struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); 433c1eb8f83SChunfeng Yun 434c1eb8f83SChunfeng Yun if (inst->type == PHY_TYPE_USB2) 435c1eb8f83SChunfeng Yun u2_phy_instance_set_mode(xsphy, inst, mode); 436c1eb8f83SChunfeng Yun 437c1eb8f83SChunfeng Yun return 0; 438c1eb8f83SChunfeng Yun } 439c1eb8f83SChunfeng Yun 440c1eb8f83SChunfeng Yun static struct phy *mtk_phy_xlate(struct device *dev, 441c1eb8f83SChunfeng Yun struct of_phandle_args *args) 442c1eb8f83SChunfeng Yun { 443c1eb8f83SChunfeng Yun struct mtk_xsphy *xsphy = dev_get_drvdata(dev); 444c1eb8f83SChunfeng Yun struct xsphy_instance *inst = NULL; 445c1eb8f83SChunfeng Yun struct device_node *phy_np = args->np; 446c1eb8f83SChunfeng Yun int index; 447c1eb8f83SChunfeng Yun 448c1eb8f83SChunfeng Yun if (args->args_count != 1) { 449c1eb8f83SChunfeng Yun dev_err(dev, "invalid number of cells in 'phy' property\n"); 450c1eb8f83SChunfeng Yun return ERR_PTR(-EINVAL); 451c1eb8f83SChunfeng Yun } 452c1eb8f83SChunfeng Yun 453c1eb8f83SChunfeng Yun for (index = 0; index < xsphy->nphys; index++) 454c1eb8f83SChunfeng Yun if (phy_np == xsphy->phys[index]->phy->dev.of_node) { 455c1eb8f83SChunfeng Yun inst = xsphy->phys[index]; 456c1eb8f83SChunfeng Yun break; 457c1eb8f83SChunfeng Yun } 458c1eb8f83SChunfeng Yun 459c1eb8f83SChunfeng Yun if (!inst) { 460c1eb8f83SChunfeng Yun dev_err(dev, "failed to find appropriate phy\n"); 461c1eb8f83SChunfeng Yun return ERR_PTR(-EINVAL); 462c1eb8f83SChunfeng Yun } 463c1eb8f83SChunfeng Yun 464c1eb8f83SChunfeng Yun inst->type = args->args[0]; 465c1eb8f83SChunfeng Yun if (!(inst->type == PHY_TYPE_USB2 || 466c1eb8f83SChunfeng Yun inst->type == PHY_TYPE_USB3)) { 467c1eb8f83SChunfeng Yun dev_err(dev, "unsupported phy type: %d\n", inst->type); 468c1eb8f83SChunfeng Yun return ERR_PTR(-EINVAL); 469c1eb8f83SChunfeng Yun } 470c1eb8f83SChunfeng Yun 471c1eb8f83SChunfeng Yun phy_parse_property(xsphy, inst); 472c1eb8f83SChunfeng Yun 473c1eb8f83SChunfeng Yun return inst->phy; 474c1eb8f83SChunfeng Yun } 475c1eb8f83SChunfeng Yun 476c1eb8f83SChunfeng Yun static const struct phy_ops mtk_xsphy_ops = { 477c1eb8f83SChunfeng Yun .init = mtk_phy_init, 478c1eb8f83SChunfeng Yun .exit = mtk_phy_exit, 479c1eb8f83SChunfeng Yun .power_on = mtk_phy_power_on, 480c1eb8f83SChunfeng Yun .power_off = mtk_phy_power_off, 481c1eb8f83SChunfeng Yun .set_mode = mtk_phy_set_mode, 482c1eb8f83SChunfeng Yun .owner = THIS_MODULE, 483c1eb8f83SChunfeng Yun }; 484c1eb8f83SChunfeng Yun 485c1eb8f83SChunfeng Yun static const struct of_device_id mtk_xsphy_id_table[] = { 486c1eb8f83SChunfeng Yun { .compatible = "mediatek,xsphy", }, 487c1eb8f83SChunfeng Yun { }, 488c1eb8f83SChunfeng Yun }; 489c1eb8f83SChunfeng Yun MODULE_DEVICE_TABLE(of, mtk_xsphy_id_table); 490c1eb8f83SChunfeng Yun 491c1eb8f83SChunfeng Yun static int mtk_xsphy_probe(struct platform_device *pdev) 492c1eb8f83SChunfeng Yun { 493c1eb8f83SChunfeng Yun struct device *dev = &pdev->dev; 494c1eb8f83SChunfeng Yun struct device_node *np = dev->of_node; 495c1eb8f83SChunfeng Yun struct device_node *child_np; 496c1eb8f83SChunfeng Yun struct phy_provider *provider; 497c1eb8f83SChunfeng Yun struct resource *glb_res; 498c1eb8f83SChunfeng Yun struct mtk_xsphy *xsphy; 499c1eb8f83SChunfeng Yun struct resource res; 500c1eb8f83SChunfeng Yun int port, retval; 501c1eb8f83SChunfeng Yun 502c1eb8f83SChunfeng Yun xsphy = devm_kzalloc(dev, sizeof(*xsphy), GFP_KERNEL); 503c1eb8f83SChunfeng Yun if (!xsphy) 504c1eb8f83SChunfeng Yun return -ENOMEM; 505c1eb8f83SChunfeng Yun 506c1eb8f83SChunfeng Yun xsphy->nphys = of_get_child_count(np); 507c1eb8f83SChunfeng Yun xsphy->phys = devm_kcalloc(dev, xsphy->nphys, 508c1eb8f83SChunfeng Yun sizeof(*xsphy->phys), GFP_KERNEL); 509c1eb8f83SChunfeng Yun if (!xsphy->phys) 510c1eb8f83SChunfeng Yun return -ENOMEM; 511c1eb8f83SChunfeng Yun 512c1eb8f83SChunfeng Yun xsphy->dev = dev; 513c1eb8f83SChunfeng Yun platform_set_drvdata(pdev, xsphy); 514c1eb8f83SChunfeng Yun 515c1eb8f83SChunfeng Yun glb_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 516c1eb8f83SChunfeng Yun /* optional, may not exist if no u3 phys */ 517c1eb8f83SChunfeng Yun if (glb_res) { 518c1eb8f83SChunfeng Yun /* get banks shared by multiple u3 phys */ 519c1eb8f83SChunfeng Yun xsphy->glb_base = devm_ioremap_resource(dev, glb_res); 520c1eb8f83SChunfeng Yun if (IS_ERR(xsphy->glb_base)) { 521c1eb8f83SChunfeng Yun dev_err(dev, "failed to remap glb regs\n"); 522c1eb8f83SChunfeng Yun return PTR_ERR(xsphy->glb_base); 523c1eb8f83SChunfeng Yun } 524c1eb8f83SChunfeng Yun } 525c1eb8f83SChunfeng Yun 526c1eb8f83SChunfeng Yun xsphy->src_ref_clk = XSP_REF_CLK; 527c1eb8f83SChunfeng Yun xsphy->src_coef = XSP_SLEW_RATE_COEF; 528c1eb8f83SChunfeng Yun /* update parameters of slew rate calibrate if exist */ 529c1eb8f83SChunfeng Yun device_property_read_u32(dev, "mediatek,src-ref-clk-mhz", 530c1eb8f83SChunfeng Yun &xsphy->src_ref_clk); 531c1eb8f83SChunfeng Yun device_property_read_u32(dev, "mediatek,src-coef", &xsphy->src_coef); 532c1eb8f83SChunfeng Yun 533c1eb8f83SChunfeng Yun port = 0; 534c1eb8f83SChunfeng Yun for_each_child_of_node(np, child_np) { 535c1eb8f83SChunfeng Yun struct xsphy_instance *inst; 536c1eb8f83SChunfeng Yun struct phy *phy; 537c1eb8f83SChunfeng Yun 538c1eb8f83SChunfeng Yun inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL); 539c1eb8f83SChunfeng Yun if (!inst) { 540c1eb8f83SChunfeng Yun retval = -ENOMEM; 541c1eb8f83SChunfeng Yun goto put_child; 542c1eb8f83SChunfeng Yun } 543c1eb8f83SChunfeng Yun 544c1eb8f83SChunfeng Yun xsphy->phys[port] = inst; 545c1eb8f83SChunfeng Yun 546c1eb8f83SChunfeng Yun phy = devm_phy_create(dev, child_np, &mtk_xsphy_ops); 547c1eb8f83SChunfeng Yun if (IS_ERR(phy)) { 548c1eb8f83SChunfeng Yun dev_err(dev, "failed to create phy\n"); 549c1eb8f83SChunfeng Yun retval = PTR_ERR(phy); 550c1eb8f83SChunfeng Yun goto put_child; 551c1eb8f83SChunfeng Yun } 552c1eb8f83SChunfeng Yun 553c1eb8f83SChunfeng Yun retval = of_address_to_resource(child_np, 0, &res); 554c1eb8f83SChunfeng Yun if (retval) { 555c1eb8f83SChunfeng Yun dev_err(dev, "failed to get address resource(id-%d)\n", 556c1eb8f83SChunfeng Yun port); 557c1eb8f83SChunfeng Yun goto put_child; 558c1eb8f83SChunfeng Yun } 559c1eb8f83SChunfeng Yun 560c1eb8f83SChunfeng Yun inst->port_base = devm_ioremap_resource(&phy->dev, &res); 561c1eb8f83SChunfeng Yun if (IS_ERR(inst->port_base)) { 562c1eb8f83SChunfeng Yun dev_err(dev, "failed to remap phy regs\n"); 563c1eb8f83SChunfeng Yun retval = PTR_ERR(inst->port_base); 564c1eb8f83SChunfeng Yun goto put_child; 565c1eb8f83SChunfeng Yun } 566c1eb8f83SChunfeng Yun 567c1eb8f83SChunfeng Yun inst->phy = phy; 568c1eb8f83SChunfeng Yun inst->index = port; 569c1eb8f83SChunfeng Yun phy_set_drvdata(phy, inst); 570c1eb8f83SChunfeng Yun port++; 571c1eb8f83SChunfeng Yun 572c1eb8f83SChunfeng Yun inst->ref_clk = devm_clk_get(&phy->dev, "ref"); 573c1eb8f83SChunfeng Yun if (IS_ERR(inst->ref_clk)) { 574c1eb8f83SChunfeng Yun dev_err(dev, "failed to get ref_clk(id-%d)\n", port); 575c1eb8f83SChunfeng Yun retval = PTR_ERR(inst->ref_clk); 576c1eb8f83SChunfeng Yun goto put_child; 577c1eb8f83SChunfeng Yun } 578c1eb8f83SChunfeng Yun } 579c1eb8f83SChunfeng Yun 580c1eb8f83SChunfeng Yun provider = devm_of_phy_provider_register(dev, mtk_phy_xlate); 581c1eb8f83SChunfeng Yun return PTR_ERR_OR_ZERO(provider); 582c1eb8f83SChunfeng Yun 583c1eb8f83SChunfeng Yun put_child: 584c1eb8f83SChunfeng Yun of_node_put(child_np); 585c1eb8f83SChunfeng Yun return retval; 586c1eb8f83SChunfeng Yun } 587c1eb8f83SChunfeng Yun 588c1eb8f83SChunfeng Yun static struct platform_driver mtk_xsphy_driver = { 589c1eb8f83SChunfeng Yun .probe = mtk_xsphy_probe, 590c1eb8f83SChunfeng Yun .driver = { 591c1eb8f83SChunfeng Yun .name = "mtk-xsphy", 592c1eb8f83SChunfeng Yun .of_match_table = mtk_xsphy_id_table, 593c1eb8f83SChunfeng Yun }, 594c1eb8f83SChunfeng Yun }; 595c1eb8f83SChunfeng Yun 596c1eb8f83SChunfeng Yun module_platform_driver(mtk_xsphy_driver); 597c1eb8f83SChunfeng Yun 598c1eb8f83SChunfeng Yun MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>"); 599c1eb8f83SChunfeng Yun MODULE_DESCRIPTION("MediaTek USB XS-PHY driver"); 600c1eb8f83SChunfeng Yun MODULE_LICENSE("GPL v2"); 601