18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MediaTek USB3.1 gen2 xsphy Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2018 MediaTek Inc. 68c2ecf20Sopenharmony_ci * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <dt-bindings/phy/phy.h> 118c2ecf20Sopenharmony_ci#include <linux/clk.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of_address.h> 178c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* u2 phy banks */ 218c2ecf20Sopenharmony_ci#define SSUSB_SIFSLV_MISC 0x000 228c2ecf20Sopenharmony_ci#define SSUSB_SIFSLV_U2FREQ 0x100 238c2ecf20Sopenharmony_ci#define SSUSB_SIFSLV_U2PHY_COM 0x300 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* u3 phy shared banks */ 268c2ecf20Sopenharmony_ci#define SSPXTP_SIFSLV_DIG_GLB 0x000 278c2ecf20Sopenharmony_ci#define SSPXTP_SIFSLV_PHYA_GLB 0x100 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* u3 phy banks */ 308c2ecf20Sopenharmony_ci#define SSPXTP_SIFSLV_DIG_LN_TOP 0x000 318c2ecf20Sopenharmony_ci#define SSPXTP_SIFSLV_DIG_LN_TX0 0x100 328c2ecf20Sopenharmony_ci#define SSPXTP_SIFSLV_DIG_LN_RX0 0x200 338c2ecf20Sopenharmony_ci#define SSPXTP_SIFSLV_DIG_LN_DAIF 0x300 348c2ecf20Sopenharmony_ci#define SSPXTP_SIFSLV_PHYA_LN 0x400 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define XSP_U2FREQ_FMCR0 ((SSUSB_SIFSLV_U2FREQ) + 0x00) 378c2ecf20Sopenharmony_ci#define P2F_RG_FREQDET_EN BIT(24) 388c2ecf20Sopenharmony_ci#define P2F_RG_CYCLECNT GENMASK(23, 0) 398c2ecf20Sopenharmony_ci#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x)) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define XSP_U2FREQ_MMONR0 ((SSUSB_SIFSLV_U2FREQ) + 0x0c) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define XSP_U2FREQ_FMMONR1 ((SSUSB_SIFSLV_U2FREQ) + 0x10) 448c2ecf20Sopenharmony_ci#define P2F_RG_FRCK_EN BIT(8) 458c2ecf20Sopenharmony_ci#define P2F_USB_FM_VALID BIT(0) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define XSP_USBPHYACR0 ((SSUSB_SIFSLV_U2PHY_COM) + 0x00) 488c2ecf20Sopenharmony_ci#define P2A0_RG_INTR_EN BIT(5) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define XSP_USBPHYACR1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x04) 518c2ecf20Sopenharmony_ci#define P2A1_RG_INTR_CAL GENMASK(23, 19) 528c2ecf20Sopenharmony_ci#define P2A1_RG_INTR_CAL_VAL(x) ((0x1f & (x)) << 19) 538c2ecf20Sopenharmony_ci#define P2A1_RG_VRT_SEL GENMASK(14, 12) 548c2ecf20Sopenharmony_ci#define P2A1_RG_VRT_SEL_VAL(x) ((0x7 & (x)) << 12) 558c2ecf20Sopenharmony_ci#define P2A1_RG_TERM_SEL GENMASK(10, 8) 568c2ecf20Sopenharmony_ci#define P2A1_RG_TERM_SEL_VAL(x) ((0x7 & (x)) << 8) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define XSP_USBPHYACR5 ((SSUSB_SIFSLV_U2PHY_COM) + 0x014) 598c2ecf20Sopenharmony_ci#define P2A5_RG_HSTX_SRCAL_EN BIT(15) 608c2ecf20Sopenharmony_ci#define P2A5_RG_HSTX_SRCTRL GENMASK(14, 12) 618c2ecf20Sopenharmony_ci#define P2A5_RG_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define XSP_USBPHYACR6 ((SSUSB_SIFSLV_U2PHY_COM) + 0x018) 648c2ecf20Sopenharmony_ci#define P2A6_RG_BC11_SW_EN BIT(23) 658c2ecf20Sopenharmony_ci#define P2A6_RG_OTG_VBUSCMP_EN BIT(20) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define XSP_U2PHYDTM1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x06C) 688c2ecf20Sopenharmony_ci#define P2D_FORCE_IDDIG BIT(9) 698c2ecf20Sopenharmony_ci#define P2D_RG_VBUSVALID BIT(5) 708c2ecf20Sopenharmony_ci#define P2D_RG_SESSEND BIT(4) 718c2ecf20Sopenharmony_ci#define P2D_RG_AVALID BIT(2) 728c2ecf20Sopenharmony_ci#define P2D_RG_IDDIG BIT(1) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define SSPXTP_PHYA_GLB_00 ((SSPXTP_SIFSLV_PHYA_GLB) + 0x00) 758c2ecf20Sopenharmony_ci#define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(21, 16) 768c2ecf20Sopenharmony_ci#define RG_XTP_GLB_BIAS_INTR_CTRL_VAL(x) ((0x3f & (x)) << 16) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define SSPXTP_PHYA_LN_04 ((SSPXTP_SIFSLV_PHYA_LN) + 0x04) 798c2ecf20Sopenharmony_ci#define RG_XTP_LN0_TX_IMPSEL GENMASK(4, 0) 808c2ecf20Sopenharmony_ci#define RG_XTP_LN0_TX_IMPSEL_VAL(x) (0x1f & (x)) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define SSPXTP_PHYA_LN_14 ((SSPXTP_SIFSLV_PHYA_LN) + 0x014) 838c2ecf20Sopenharmony_ci#define RG_XTP_LN0_RX_IMPSEL GENMASK(4, 0) 848c2ecf20Sopenharmony_ci#define RG_XTP_LN0_RX_IMPSEL_VAL(x) (0x1f & (x)) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define XSP_REF_CLK 26 /* MHZ */ 878c2ecf20Sopenharmony_ci#define XSP_SLEW_RATE_COEF 17 888c2ecf20Sopenharmony_ci#define XSP_SR_COEF_DIVISOR 1000 898c2ecf20Sopenharmony_ci#define XSP_FM_DET_CYCLE_CNT 1024 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistruct xsphy_instance { 928c2ecf20Sopenharmony_ci struct phy *phy; 938c2ecf20Sopenharmony_ci void __iomem *port_base; 948c2ecf20Sopenharmony_ci struct clk *ref_clk; /* reference clock of anolog phy */ 958c2ecf20Sopenharmony_ci u32 index; 968c2ecf20Sopenharmony_ci u32 type; 978c2ecf20Sopenharmony_ci /* only for HQA test */ 988c2ecf20Sopenharmony_ci int efuse_intr; 998c2ecf20Sopenharmony_ci int efuse_tx_imp; 1008c2ecf20Sopenharmony_ci int efuse_rx_imp; 1018c2ecf20Sopenharmony_ci /* u2 eye diagram */ 1028c2ecf20Sopenharmony_ci int eye_src; 1038c2ecf20Sopenharmony_ci int eye_vrt; 1048c2ecf20Sopenharmony_ci int eye_term; 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistruct mtk_xsphy { 1088c2ecf20Sopenharmony_ci struct device *dev; 1098c2ecf20Sopenharmony_ci void __iomem *glb_base; /* only shared u3 sif */ 1108c2ecf20Sopenharmony_ci struct xsphy_instance **phys; 1118c2ecf20Sopenharmony_ci int nphys; 1128c2ecf20Sopenharmony_ci int src_ref_clk; /* MHZ, reference clock for slew rate calibrate */ 1138c2ecf20Sopenharmony_ci int src_coef; /* coefficient for slew rate calibrate */ 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, 1178c2ecf20Sopenharmony_ci struct xsphy_instance *inst) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci void __iomem *pbase = inst->port_base; 1208c2ecf20Sopenharmony_ci int calib_val; 1218c2ecf20Sopenharmony_ci int fm_out; 1228c2ecf20Sopenharmony_ci u32 tmp; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* use force value */ 1258c2ecf20Sopenharmony_ci if (inst->eye_src) 1268c2ecf20Sopenharmony_ci return; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* enable USB ring oscillator */ 1298c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_USBPHYACR5); 1308c2ecf20Sopenharmony_ci tmp |= P2A5_RG_HSTX_SRCAL_EN; 1318c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_USBPHYACR5); 1328c2ecf20Sopenharmony_ci udelay(1); /* wait clock stable */ 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* enable free run clock */ 1358c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_U2FREQ_FMMONR1); 1368c2ecf20Sopenharmony_ci tmp |= P2F_RG_FRCK_EN; 1378c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_U2FREQ_FMMONR1); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* set cycle count as 1024 */ 1408c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_U2FREQ_FMCR0); 1418c2ecf20Sopenharmony_ci tmp &= ~(P2F_RG_CYCLECNT); 1428c2ecf20Sopenharmony_ci tmp |= P2F_RG_CYCLECNT_VAL(XSP_FM_DET_CYCLE_CNT); 1438c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_U2FREQ_FMCR0); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* enable frequency meter */ 1468c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_U2FREQ_FMCR0); 1478c2ecf20Sopenharmony_ci tmp |= P2F_RG_FREQDET_EN; 1488c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_U2FREQ_FMCR0); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* ignore return value */ 1518c2ecf20Sopenharmony_ci readl_poll_timeout(pbase + XSP_U2FREQ_FMMONR1, tmp, 1528c2ecf20Sopenharmony_ci (tmp & P2F_USB_FM_VALID), 10, 200); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci fm_out = readl(pbase + XSP_U2FREQ_MMONR0); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* disable frequency meter */ 1578c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_U2FREQ_FMCR0); 1588c2ecf20Sopenharmony_ci tmp &= ~P2F_RG_FREQDET_EN; 1598c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_U2FREQ_FMCR0); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* disable free run clock */ 1628c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_U2FREQ_FMMONR1); 1638c2ecf20Sopenharmony_ci tmp &= ~P2F_RG_FRCK_EN; 1648c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_U2FREQ_FMMONR1); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (fm_out) { 1678c2ecf20Sopenharmony_ci /* (1024 / FM_OUT) x reference clock frequency x coefficient */ 1688c2ecf20Sopenharmony_ci tmp = xsphy->src_ref_clk * xsphy->src_coef; 1698c2ecf20Sopenharmony_ci tmp = (tmp * XSP_FM_DET_CYCLE_CNT) / fm_out; 1708c2ecf20Sopenharmony_ci calib_val = DIV_ROUND_CLOSEST(tmp, XSP_SR_COEF_DIVISOR); 1718c2ecf20Sopenharmony_ci } else { 1728c2ecf20Sopenharmony_ci /* if FM detection fail, set default value */ 1738c2ecf20Sopenharmony_ci calib_val = 3; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci dev_dbg(xsphy->dev, "phy.%d, fm_out:%d, calib:%d (clk:%d, coef:%d)\n", 1768c2ecf20Sopenharmony_ci inst->index, fm_out, calib_val, 1778c2ecf20Sopenharmony_ci xsphy->src_ref_clk, xsphy->src_coef); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* set HS slew rate */ 1808c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_USBPHYACR5); 1818c2ecf20Sopenharmony_ci tmp &= ~P2A5_RG_HSTX_SRCTRL; 1828c2ecf20Sopenharmony_ci tmp |= P2A5_RG_HSTX_SRCTRL_VAL(calib_val); 1838c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_USBPHYACR5); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* disable USB ring oscillator */ 1868c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_USBPHYACR5); 1878c2ecf20Sopenharmony_ci tmp &= ~P2A5_RG_HSTX_SRCAL_EN; 1888c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_USBPHYACR5); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void u2_phy_instance_init(struct mtk_xsphy *xsphy, 1928c2ecf20Sopenharmony_ci struct xsphy_instance *inst) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci void __iomem *pbase = inst->port_base; 1958c2ecf20Sopenharmony_ci u32 tmp; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* DP/DM BC1.1 path Disable */ 1988c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_USBPHYACR6); 1998c2ecf20Sopenharmony_ci tmp &= ~P2A6_RG_BC11_SW_EN; 2008c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_USBPHYACR6); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_USBPHYACR0); 2038c2ecf20Sopenharmony_ci tmp |= P2A0_RG_INTR_EN; 2048c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_USBPHYACR0); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void u2_phy_instance_power_on(struct mtk_xsphy *xsphy, 2088c2ecf20Sopenharmony_ci struct xsphy_instance *inst) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci void __iomem *pbase = inst->port_base; 2118c2ecf20Sopenharmony_ci u32 index = inst->index; 2128c2ecf20Sopenharmony_ci u32 tmp; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_USBPHYACR6); 2158c2ecf20Sopenharmony_ci tmp |= P2A6_RG_OTG_VBUSCMP_EN; 2168c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_USBPHYACR6); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_U2PHYDTM1); 2198c2ecf20Sopenharmony_ci tmp |= P2D_RG_VBUSVALID | P2D_RG_AVALID; 2208c2ecf20Sopenharmony_ci tmp &= ~P2D_RG_SESSEND; 2218c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_U2PHYDTM1); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void u2_phy_instance_power_off(struct mtk_xsphy *xsphy, 2278c2ecf20Sopenharmony_ci struct xsphy_instance *inst) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci void __iomem *pbase = inst->port_base; 2308c2ecf20Sopenharmony_ci u32 index = inst->index; 2318c2ecf20Sopenharmony_ci u32 tmp; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_USBPHYACR6); 2348c2ecf20Sopenharmony_ci tmp &= ~P2A6_RG_OTG_VBUSCMP_EN; 2358c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_USBPHYACR6); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_U2PHYDTM1); 2388c2ecf20Sopenharmony_ci tmp &= ~(P2D_RG_VBUSVALID | P2D_RG_AVALID); 2398c2ecf20Sopenharmony_ci tmp |= P2D_RG_SESSEND; 2408c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_U2PHYDTM1); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic void u2_phy_instance_set_mode(struct mtk_xsphy *xsphy, 2468c2ecf20Sopenharmony_ci struct xsphy_instance *inst, 2478c2ecf20Sopenharmony_ci enum phy_mode mode) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci u32 tmp; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci tmp = readl(inst->port_base + XSP_U2PHYDTM1); 2528c2ecf20Sopenharmony_ci switch (mode) { 2538c2ecf20Sopenharmony_ci case PHY_MODE_USB_DEVICE: 2548c2ecf20Sopenharmony_ci tmp |= P2D_FORCE_IDDIG | P2D_RG_IDDIG; 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci case PHY_MODE_USB_HOST: 2578c2ecf20Sopenharmony_ci tmp |= P2D_FORCE_IDDIG; 2588c2ecf20Sopenharmony_ci tmp &= ~P2D_RG_IDDIG; 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci case PHY_MODE_USB_OTG: 2618c2ecf20Sopenharmony_ci tmp &= ~(P2D_FORCE_IDDIG | P2D_RG_IDDIG); 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci default: 2648c2ecf20Sopenharmony_ci return; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci writel(tmp, inst->port_base + XSP_U2PHYDTM1); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic void phy_parse_property(struct mtk_xsphy *xsphy, 2708c2ecf20Sopenharmony_ci struct xsphy_instance *inst) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct device *dev = &inst->phy->dev; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci switch (inst->type) { 2758c2ecf20Sopenharmony_ci case PHY_TYPE_USB2: 2768c2ecf20Sopenharmony_ci device_property_read_u32(dev, "mediatek,efuse-intr", 2778c2ecf20Sopenharmony_ci &inst->efuse_intr); 2788c2ecf20Sopenharmony_ci device_property_read_u32(dev, "mediatek,eye-src", 2798c2ecf20Sopenharmony_ci &inst->eye_src); 2808c2ecf20Sopenharmony_ci device_property_read_u32(dev, "mediatek,eye-vrt", 2818c2ecf20Sopenharmony_ci &inst->eye_vrt); 2828c2ecf20Sopenharmony_ci device_property_read_u32(dev, "mediatek,eye-term", 2838c2ecf20Sopenharmony_ci &inst->eye_term); 2848c2ecf20Sopenharmony_ci dev_dbg(dev, "intr:%d, src:%d, vrt:%d, term:%d\n", 2858c2ecf20Sopenharmony_ci inst->efuse_intr, inst->eye_src, 2868c2ecf20Sopenharmony_ci inst->eye_vrt, inst->eye_term); 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci case PHY_TYPE_USB3: 2898c2ecf20Sopenharmony_ci device_property_read_u32(dev, "mediatek,efuse-intr", 2908c2ecf20Sopenharmony_ci &inst->efuse_intr); 2918c2ecf20Sopenharmony_ci device_property_read_u32(dev, "mediatek,efuse-tx-imp", 2928c2ecf20Sopenharmony_ci &inst->efuse_tx_imp); 2938c2ecf20Sopenharmony_ci device_property_read_u32(dev, "mediatek,efuse-rx-imp", 2948c2ecf20Sopenharmony_ci &inst->efuse_rx_imp); 2958c2ecf20Sopenharmony_ci dev_dbg(dev, "intr:%d, tx-imp:%d, rx-imp:%d\n", 2968c2ecf20Sopenharmony_ci inst->efuse_intr, inst->efuse_tx_imp, 2978c2ecf20Sopenharmony_ci inst->efuse_rx_imp); 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci default: 3008c2ecf20Sopenharmony_ci dev_err(xsphy->dev, "incompatible phy type\n"); 3018c2ecf20Sopenharmony_ci return; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic void u2_phy_props_set(struct mtk_xsphy *xsphy, 3068c2ecf20Sopenharmony_ci struct xsphy_instance *inst) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci void __iomem *pbase = inst->port_base; 3098c2ecf20Sopenharmony_ci u32 tmp; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (inst->efuse_intr) { 3128c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_USBPHYACR1); 3138c2ecf20Sopenharmony_ci tmp &= ~P2A1_RG_INTR_CAL; 3148c2ecf20Sopenharmony_ci tmp |= P2A1_RG_INTR_CAL_VAL(inst->efuse_intr); 3158c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_USBPHYACR1); 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (inst->eye_src) { 3198c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_USBPHYACR5); 3208c2ecf20Sopenharmony_ci tmp &= ~P2A5_RG_HSTX_SRCTRL; 3218c2ecf20Sopenharmony_ci tmp |= P2A5_RG_HSTX_SRCTRL_VAL(inst->eye_src); 3228c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_USBPHYACR5); 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (inst->eye_vrt) { 3268c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_USBPHYACR1); 3278c2ecf20Sopenharmony_ci tmp &= ~P2A1_RG_VRT_SEL; 3288c2ecf20Sopenharmony_ci tmp |= P2A1_RG_VRT_SEL_VAL(inst->eye_vrt); 3298c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_USBPHYACR1); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (inst->eye_term) { 3338c2ecf20Sopenharmony_ci tmp = readl(pbase + XSP_USBPHYACR1); 3348c2ecf20Sopenharmony_ci tmp &= ~P2A1_RG_TERM_SEL; 3358c2ecf20Sopenharmony_ci tmp |= P2A1_RG_TERM_SEL_VAL(inst->eye_term); 3368c2ecf20Sopenharmony_ci writel(tmp, pbase + XSP_USBPHYACR1); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic void u3_phy_props_set(struct mtk_xsphy *xsphy, 3418c2ecf20Sopenharmony_ci struct xsphy_instance *inst) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci void __iomem *pbase = inst->port_base; 3448c2ecf20Sopenharmony_ci u32 tmp; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (inst->efuse_intr) { 3478c2ecf20Sopenharmony_ci tmp = readl(xsphy->glb_base + SSPXTP_PHYA_GLB_00); 3488c2ecf20Sopenharmony_ci tmp &= ~RG_XTP_GLB_BIAS_INTR_CTRL; 3498c2ecf20Sopenharmony_ci tmp |= RG_XTP_GLB_BIAS_INTR_CTRL_VAL(inst->efuse_intr); 3508c2ecf20Sopenharmony_ci writel(tmp, xsphy->glb_base + SSPXTP_PHYA_GLB_00); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (inst->efuse_tx_imp) { 3548c2ecf20Sopenharmony_ci tmp = readl(pbase + SSPXTP_PHYA_LN_04); 3558c2ecf20Sopenharmony_ci tmp &= ~RG_XTP_LN0_TX_IMPSEL; 3568c2ecf20Sopenharmony_ci tmp |= RG_XTP_LN0_TX_IMPSEL_VAL(inst->efuse_tx_imp); 3578c2ecf20Sopenharmony_ci writel(tmp, pbase + SSPXTP_PHYA_LN_04); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (inst->efuse_rx_imp) { 3618c2ecf20Sopenharmony_ci tmp = readl(pbase + SSPXTP_PHYA_LN_14); 3628c2ecf20Sopenharmony_ci tmp &= ~RG_XTP_LN0_RX_IMPSEL; 3638c2ecf20Sopenharmony_ci tmp |= RG_XTP_LN0_RX_IMPSEL_VAL(inst->efuse_rx_imp); 3648c2ecf20Sopenharmony_ci writel(tmp, pbase + SSPXTP_PHYA_LN_14); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int mtk_phy_init(struct phy *phy) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct xsphy_instance *inst = phy_get_drvdata(phy); 3718c2ecf20Sopenharmony_ci struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); 3728c2ecf20Sopenharmony_ci int ret; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci ret = clk_prepare_enable(inst->ref_clk); 3758c2ecf20Sopenharmony_ci if (ret) { 3768c2ecf20Sopenharmony_ci dev_err(xsphy->dev, "failed to enable ref_clk\n"); 3778c2ecf20Sopenharmony_ci return ret; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci switch (inst->type) { 3818c2ecf20Sopenharmony_ci case PHY_TYPE_USB2: 3828c2ecf20Sopenharmony_ci u2_phy_instance_init(xsphy, inst); 3838c2ecf20Sopenharmony_ci u2_phy_props_set(xsphy, inst); 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci case PHY_TYPE_USB3: 3868c2ecf20Sopenharmony_ci u3_phy_props_set(xsphy, inst); 3878c2ecf20Sopenharmony_ci break; 3888c2ecf20Sopenharmony_ci default: 3898c2ecf20Sopenharmony_ci dev_err(xsphy->dev, "incompatible phy type\n"); 3908c2ecf20Sopenharmony_ci clk_disable_unprepare(inst->ref_clk); 3918c2ecf20Sopenharmony_ci return -EINVAL; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int mtk_phy_power_on(struct phy *phy) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct xsphy_instance *inst = phy_get_drvdata(phy); 4008c2ecf20Sopenharmony_ci struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (inst->type == PHY_TYPE_USB2) { 4038c2ecf20Sopenharmony_ci u2_phy_instance_power_on(xsphy, inst); 4048c2ecf20Sopenharmony_ci u2_phy_slew_rate_calibrate(xsphy, inst); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int mtk_phy_power_off(struct phy *phy) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct xsphy_instance *inst = phy_get_drvdata(phy); 4138c2ecf20Sopenharmony_ci struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (inst->type == PHY_TYPE_USB2) 4168c2ecf20Sopenharmony_ci u2_phy_instance_power_off(xsphy, inst); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic int mtk_phy_exit(struct phy *phy) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct xsphy_instance *inst = phy_get_drvdata(phy); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci clk_disable_unprepare(inst->ref_clk); 4268c2ecf20Sopenharmony_ci return 0; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci struct xsphy_instance *inst = phy_get_drvdata(phy); 4328c2ecf20Sopenharmony_ci struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (inst->type == PHY_TYPE_USB2) 4358c2ecf20Sopenharmony_ci u2_phy_instance_set_mode(xsphy, inst, mode); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic struct phy *mtk_phy_xlate(struct device *dev, 4418c2ecf20Sopenharmony_ci struct of_phandle_args *args) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct mtk_xsphy *xsphy = dev_get_drvdata(dev); 4448c2ecf20Sopenharmony_ci struct xsphy_instance *inst = NULL; 4458c2ecf20Sopenharmony_ci struct device_node *phy_np = args->np; 4468c2ecf20Sopenharmony_ci int index; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (args->args_count != 1) { 4498c2ecf20Sopenharmony_ci dev_err(dev, "invalid number of cells in 'phy' property\n"); 4508c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci for (index = 0; index < xsphy->nphys; index++) 4548c2ecf20Sopenharmony_ci if (phy_np == xsphy->phys[index]->phy->dev.of_node) { 4558c2ecf20Sopenharmony_ci inst = xsphy->phys[index]; 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (!inst) { 4608c2ecf20Sopenharmony_ci dev_err(dev, "failed to find appropriate phy\n"); 4618c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci inst->type = args->args[0]; 4658c2ecf20Sopenharmony_ci if (!(inst->type == PHY_TYPE_USB2 || 4668c2ecf20Sopenharmony_ci inst->type == PHY_TYPE_USB3)) { 4678c2ecf20Sopenharmony_ci dev_err(dev, "unsupported phy type: %d\n", inst->type); 4688c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci phy_parse_property(xsphy, inst); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci return inst->phy; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic const struct phy_ops mtk_xsphy_ops = { 4778c2ecf20Sopenharmony_ci .init = mtk_phy_init, 4788c2ecf20Sopenharmony_ci .exit = mtk_phy_exit, 4798c2ecf20Sopenharmony_ci .power_on = mtk_phy_power_on, 4808c2ecf20Sopenharmony_ci .power_off = mtk_phy_power_off, 4818c2ecf20Sopenharmony_ci .set_mode = mtk_phy_set_mode, 4828c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4838c2ecf20Sopenharmony_ci}; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic const struct of_device_id mtk_xsphy_id_table[] = { 4868c2ecf20Sopenharmony_ci { .compatible = "mediatek,xsphy", }, 4878c2ecf20Sopenharmony_ci { }, 4888c2ecf20Sopenharmony_ci}; 4898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtk_xsphy_id_table); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int mtk_xsphy_probe(struct platform_device *pdev) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4948c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 4958c2ecf20Sopenharmony_ci struct device_node *child_np; 4968c2ecf20Sopenharmony_ci struct phy_provider *provider; 4978c2ecf20Sopenharmony_ci struct resource *glb_res; 4988c2ecf20Sopenharmony_ci struct mtk_xsphy *xsphy; 4998c2ecf20Sopenharmony_ci struct resource res; 5008c2ecf20Sopenharmony_ci int port, retval; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci xsphy = devm_kzalloc(dev, sizeof(*xsphy), GFP_KERNEL); 5038c2ecf20Sopenharmony_ci if (!xsphy) 5048c2ecf20Sopenharmony_ci return -ENOMEM; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci xsphy->nphys = of_get_child_count(np); 5078c2ecf20Sopenharmony_ci xsphy->phys = devm_kcalloc(dev, xsphy->nphys, 5088c2ecf20Sopenharmony_ci sizeof(*xsphy->phys), GFP_KERNEL); 5098c2ecf20Sopenharmony_ci if (!xsphy->phys) 5108c2ecf20Sopenharmony_ci return -ENOMEM; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci xsphy->dev = dev; 5138c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, xsphy); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci glb_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5168c2ecf20Sopenharmony_ci /* optional, may not exist if no u3 phys */ 5178c2ecf20Sopenharmony_ci if (glb_res) { 5188c2ecf20Sopenharmony_ci /* get banks shared by multiple u3 phys */ 5198c2ecf20Sopenharmony_ci xsphy->glb_base = devm_ioremap_resource(dev, glb_res); 5208c2ecf20Sopenharmony_ci if (IS_ERR(xsphy->glb_base)) { 5218c2ecf20Sopenharmony_ci dev_err(dev, "failed to remap glb regs\n"); 5228c2ecf20Sopenharmony_ci return PTR_ERR(xsphy->glb_base); 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci xsphy->src_ref_clk = XSP_REF_CLK; 5278c2ecf20Sopenharmony_ci xsphy->src_coef = XSP_SLEW_RATE_COEF; 5288c2ecf20Sopenharmony_ci /* update parameters of slew rate calibrate if exist */ 5298c2ecf20Sopenharmony_ci device_property_read_u32(dev, "mediatek,src-ref-clk-mhz", 5308c2ecf20Sopenharmony_ci &xsphy->src_ref_clk); 5318c2ecf20Sopenharmony_ci device_property_read_u32(dev, "mediatek,src-coef", &xsphy->src_coef); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci port = 0; 5348c2ecf20Sopenharmony_ci for_each_child_of_node(np, child_np) { 5358c2ecf20Sopenharmony_ci struct xsphy_instance *inst; 5368c2ecf20Sopenharmony_ci struct phy *phy; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL); 5398c2ecf20Sopenharmony_ci if (!inst) { 5408c2ecf20Sopenharmony_ci retval = -ENOMEM; 5418c2ecf20Sopenharmony_ci goto put_child; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci xsphy->phys[port] = inst; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci phy = devm_phy_create(dev, child_np, &mtk_xsphy_ops); 5478c2ecf20Sopenharmony_ci if (IS_ERR(phy)) { 5488c2ecf20Sopenharmony_ci dev_err(dev, "failed to create phy\n"); 5498c2ecf20Sopenharmony_ci retval = PTR_ERR(phy); 5508c2ecf20Sopenharmony_ci goto put_child; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci retval = of_address_to_resource(child_np, 0, &res); 5548c2ecf20Sopenharmony_ci if (retval) { 5558c2ecf20Sopenharmony_ci dev_err(dev, "failed to get address resource(id-%d)\n", 5568c2ecf20Sopenharmony_ci port); 5578c2ecf20Sopenharmony_ci goto put_child; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci inst->port_base = devm_ioremap_resource(&phy->dev, &res); 5618c2ecf20Sopenharmony_ci if (IS_ERR(inst->port_base)) { 5628c2ecf20Sopenharmony_ci dev_err(dev, "failed to remap phy regs\n"); 5638c2ecf20Sopenharmony_ci retval = PTR_ERR(inst->port_base); 5648c2ecf20Sopenharmony_ci goto put_child; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci inst->phy = phy; 5688c2ecf20Sopenharmony_ci inst->index = port; 5698c2ecf20Sopenharmony_ci phy_set_drvdata(phy, inst); 5708c2ecf20Sopenharmony_ci port++; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci inst->ref_clk = devm_clk_get(&phy->dev, "ref"); 5738c2ecf20Sopenharmony_ci if (IS_ERR(inst->ref_clk)) { 5748c2ecf20Sopenharmony_ci dev_err(dev, "failed to get ref_clk(id-%d)\n", port); 5758c2ecf20Sopenharmony_ci retval = PTR_ERR(inst->ref_clk); 5768c2ecf20Sopenharmony_ci goto put_child; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci provider = devm_of_phy_provider_register(dev, mtk_phy_xlate); 5818c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(provider); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ciput_child: 5848c2ecf20Sopenharmony_ci of_node_put(child_np); 5858c2ecf20Sopenharmony_ci return retval; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic struct platform_driver mtk_xsphy_driver = { 5898c2ecf20Sopenharmony_ci .probe = mtk_xsphy_probe, 5908c2ecf20Sopenharmony_ci .driver = { 5918c2ecf20Sopenharmony_ci .name = "mtk-xsphy", 5928c2ecf20Sopenharmony_ci .of_match_table = mtk_xsphy_id_table, 5938c2ecf20Sopenharmony_ci }, 5948c2ecf20Sopenharmony_ci}; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cimodule_platform_driver(mtk_xsphy_driver); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>"); 5998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MediaTek USB XS-PHY driver"); 6008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 601