From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Cyrus-Session-Id: sloti22d1t05-1521860-1525932685-2-1510267460018916331 X-Sieve: CMU Sieve 3.0 X-Spam-known-sender: no X-Spam-score: 0.0 X-Spam-hits: BAYES_00 -1.9, HEADER_FROM_DIFFERENT_DOMAINS 0.25, MAILING_LIST_MULTI -1, ME_NOAUTH 0.01, RCVD_IN_DNSWL_HI -5, UNPARSEABLE_RELAY 0.001, LANGUAGES unknown, BAYES_USED global, SA_VERSION 3.4.0 X-Spam-source: IP='209.132.180.67', Host='vger.kernel.org', Country='US', FromHeader='com', MailFrom='org' X-Spam-charsets: X-Resolved-to: greg@kroah.com X-Delivered-to: greg@kroah.com X-Mail-from: linux-usb-owner@vger.kernel.org ARC-Seal: i=1; a=rsa-sha256; cv=none; d=messagingengine.com; s=fm2; t= 1525932676; b=AB0SynWQHL5QX0/iRV2qzqm+WiwwXyNHhPRdoIOAsH0MJ8Cps2 S4H9BtvL+HXTMUBvEhjOluMgrFGWM2J/yNdqFfqQJ8uuC/sgGLsvef7oENajAuOh vAeNsTwiRdBmc7ojPRvusxDk3jI2dX55ith8hmE8BQJSHYjlaTBjpul51u4tvKhx RU1PPg2UkDiCGxUMaGcqcrSxLlwcC+m3qX9xEkzHDtd5hgCBlP2evZM4789XHMzl ZkUO8IhWsEPpwjo4rA0/r6WavFMXUEe7rU3FstuLWm0imOtkLSJiyBecBS0yiX0Q oeI2NEtnf/vjTmIFXhvJMPI/31/Fz2WNwS+g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:content-type:sender :list-id; s=fm2; t=1525932676; bh=2ZCKm78Gx4zSOlpRBs0uzgTBhdOF25 HnKFj2u/tXn6o=; b=Eid7RvPQLdeEB8hrJjehbd64Dp1xQp5zVOgXyeT8KV6jA7 1EyVsgKW2Wp0cW8BcfuQ0z0B1jxbyitAFdumvRhsIhoWElvbSH/Qg7S1N26F7DV3 u32q6INCiuNJa4rPdc4sQXHOqwj2tn6S27GqaNzHMWRfTc314duxKO/Ngv+qFie0 eiYIr66Wc4VsNgbsequZP3oOBVZpdEn7bng6dA7V51ePhThEMWFcPLS55XLmtpN5 cibokclmIo+JK6R7FggQYTPgy5rm4oBuyFWXUx0tXtwsKhUKHC1Kb3Vr+FpmXBPG UR9O2d5zfEKnX9EC5wAIaCK2/yK8+aljnJraoxNA== ARC-Authentication-Results: i=1; mx6.messagingengine.com; arc=none (no signatures found); dkim=none (no signatures found); dmarc=none (p=none,has-list-id=yes,d=none) header.from=mediatek.com; iprev=pass policy.iprev=209.132.180.67 (vger.kernel.org); spf=none smtp.mailfrom=linux-usb-owner@vger.kernel.org smtp.helo=vger.kernel.org; x-aligned-from=fail; x-cm=none score=0; x-ptr=pass x-ptr-helo=vger.kernel.org x-ptr-lookup=vger.kernel.org; x-return-mx=pass smtp.domain=vger.kernel.org smtp.result=pass smtp_org.domain=kernel.org smtp_org.result=pass smtp_is_org_domain=no header.domain=mediatek.com header.result=pass header_is_org_domain=yes; x-vs=clean score=-100 state=0 Authentication-Results: mx6.messagingengine.com; arc=none (no signatures found); dkim=none (no signatures found); dmarc=none (p=none,has-list-id=yes,d=none) header.from=mediatek.com; iprev=pass policy.iprev=209.132.180.67 (vger.kernel.org); spf=none smtp.mailfrom=linux-usb-owner@vger.kernel.org smtp.helo=vger.kernel.org; x-aligned-from=fail; x-cm=none score=0; x-ptr=pass x-ptr-helo=vger.kernel.org x-ptr-lookup=vger.kernel.org; x-return-mx=pass smtp.domain=vger.kernel.org smtp.result=pass smtp_org.domain=kernel.org smtp_org.result=pass smtp_is_org_domain=no header.domain=mediatek.com header.result=pass header_is_org_domain=yes; x-vs=clean score=-100 state=0 X-ME-VSCategory: clean X-CM-Envelope: MS4wfDsCATZkibFkawVlZQFC7jEvRK1UGZGsi4+8HAw2Szhv/51PPS2O88tOVsnT0l7ryAS84eUGvw/lzlxGklyUUngewgZx9Kj4Mh2td2zViv+oijajO5y/ KQ7dTBYoaRNiGErjPn5w9I5mRzZW8yhZE2gk47YaC/mVRDjjrnXSZievR4lshHFeSs1DDXkDfB7kxTfswhNqKDhn6ILsKP6DtL9RaLkInVX9a/2+ncd9vdDq X-CM-Analysis: v=2.3 cv=FKU1Odgs c=1 sm=1 tr=0 a=UK1r566ZdBxH71SXbqIOeA==:117 a=UK1r566ZdBxH71SXbqIOeA==:17 a=VUJBJC2UJ8kA:10 a=mpaa-ttXAAAA:8 a=VwQbUJbxAAAA:8 a=DNGGwUqUthaFdjVQzHAA:9 a=E4_R7kkSb2iiQBoF:21 a=ZN9WgvCKH2E5nXOX:21 a=x8gzFH9gYPwA:10 a=6heAxKwa5pAsJatQ0mat:22 a=AjGcO6oz07-iQ99wixmX:22 X-ME-CMScore: 0 X-ME-CMCategory: none Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756650AbeEJGLD (ORCPT ); Thu, 10 May 2018 02:11:03 -0400 Received: from mailgw01.mediatek.com ([210.61.82.183]:51015 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1756505AbeEJGKo (ORCPT ); Thu, 10 May 2018 02:10:44 -0400 X-UUID: 09d5ee98a6154e3b8cd4887928356c06-20180510 From: Chunfeng Yun To: Kishon Vijay Abraham I CC: Matthias Brugger , Rob Herring , Mark Rutland , Ian Campbell , Ryder Lee , "Chunfeng Yun" , , , , , Subject: [PATCH v3 2/2] phy: mediatek: add XS-PHY driver Date: Thu, 10 May 2018 14:10:29 +0800 Message-ID: <1525932629-10603-3-git-send-email-chunfeng.yun@mediatek.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1525932629-10603-1-git-send-email-chunfeng.yun@mediatek.com> References: <1525932629-10603-1-git-send-email-chunfeng.yun@mediatek.com> MIME-Version: 1.0 Content-Type: text/plain X-MTK: N Sender: linux-usb-owner@vger.kernel.org X-Mailing-List: linux-usb@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-Mailing-List: linux-kernel@vger.kernel.org List-ID: Support XS-PHY for MediaTek SoCs with USB3.1 GEN2 controller Signed-off-by: Chunfeng Yun --- drivers/phy/mediatek/Kconfig | 9 + drivers/phy/mediatek/Makefile | 1 + drivers/phy/mediatek/phy-mtk-xsphy.c | 600 ++++++++++++++++++++++++++++++++++ 3 files changed, 610 insertions(+) create mode 100644 drivers/phy/mediatek/phy-mtk-xsphy.c diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index 88ab4e2..8857d00 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -12,3 +12,12 @@ config PHY_MTK_TPHY different banks layout, the T-PHY with shared banks between multi-ports is first version, otherwise is second veriosn, so you can easily distinguish them by banks layout. + +config PHY_MTK_XSPHY + tristate "MediaTek XS-PHY Driver" + depends on ARCH_MEDIATEK && OF + select GENERIC_PHY + 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. diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile index 1bdab14..ee49edc 100644 --- a/drivers/phy/mediatek/Makefile +++ b/drivers/phy/mediatek/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o +obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o diff --git a/drivers/phy/mediatek/phy-mtk-xsphy.c b/drivers/phy/mediatek/phy-mtk-xsphy.c new file mode 100644 index 0000000..020cd02 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-xsphy.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek USB3.1 gen2 xsphy Driver + * + * Copyright (c) 2018 MediaTek Inc. + * Author: Chunfeng Yun + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x)) + +#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_INTR_CAL_VAL(x) ((0x1f & (x)) << 19) +#define P2A1_RG_VRT_SEL GENMASK(14, 12) +#define P2A1_RG_VRT_SEL_VAL(x) ((0x7 & (x)) << 12) +#define P2A1_RG_TERM_SEL GENMASK(10, 8) +#define P2A1_RG_TERM_SEL_VAL(x) ((0x7 & (x)) << 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 P2A5_RG_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 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 RG_XTP_GLB_BIAS_INTR_CTRL_VAL(x) ((0x3f & (x)) << 16) + +#define SSPXTP_PHYA_LN_04 ((SSPXTP_SIFSLV_PHYA_LN) + 0x04) +#define RG_XTP_LN0_TX_IMPSEL GENMASK(4, 0) +#define RG_XTP_LN0_TX_IMPSEL_VAL(x) (0x1f & (x)) + +#define SSPXTP_PHYA_LN_14 ((SSPXTP_SIFSLV_PHYA_LN) + 0x014) +#define RG_XTP_LN0_RX_IMPSEL GENMASK(4, 0) +#define RG_XTP_LN0_RX_IMPSEL_VAL(x) (0x1f & (x)) + +#define XSP_REF_CLK 26 /* MHZ */ +#define XSP_SLEW_RATE_COEF 17 +#define XSP_SR_COEF_DIVISOR 1000 +#define XSP_FM_DET_CYCLE_CNT 1024 + +struct xsphy_instance { + struct phy *phy; + void __iomem *port_base; + struct clk *ref_clk; /* reference clock of anolog phy */ + u32 index; + u32 type; + /* only for HQA test */ + int efuse_intr; + int efuse_tx_imp; + int efuse_rx_imp; + /* u2 eye diagram */ + int eye_src; + int eye_vrt; + int eye_term; +}; + +struct mtk_xsphy { + struct device *dev; + void __iomem *glb_base; /* only shared u3 sif */ + struct xsphy_instance **phys; + int nphys; + int src_ref_clk; /* MHZ, reference clock for slew rate calibrate */ + int src_coef; /* coefficient for slew rate calibrate */ +}; + +static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + int calib_val; + int fm_out; + u32 tmp; + + /* use force value */ + if (inst->eye_src) + return; + + /* enable USB ring oscillator */ + tmp = readl(pbase + XSP_USBPHYACR5); + tmp |= P2A5_RG_HSTX_SRCAL_EN; + writel(tmp, pbase + XSP_USBPHYACR5); + udelay(1); /* wait clock stable */ + + /* enable free run clock */ + tmp = readl(pbase + XSP_U2FREQ_FMMONR1); + tmp |= P2F_RG_FRCK_EN; + writel(tmp, pbase + XSP_U2FREQ_FMMONR1); + + /* set cycle count as 1024 */ + tmp = readl(pbase + XSP_U2FREQ_FMCR0); + tmp &= ~(P2F_RG_CYCLECNT); + tmp |= P2F_RG_CYCLECNT_VAL(XSP_FM_DET_CYCLE_CNT); + writel(tmp, pbase + XSP_U2FREQ_FMCR0); + + /* enable frequency meter */ + tmp = readl(pbase + XSP_U2FREQ_FMCR0); + tmp |= P2F_RG_FREQDET_EN; + writel(tmp, pbase + XSP_U2FREQ_FMCR0); + + /* ignore return value */ + readl_poll_timeout(pbase + XSP_U2FREQ_FMMONR1, tmp, + (tmp & P2F_USB_FM_VALID), 10, 200); + + fm_out = readl(pbase + XSP_U2FREQ_MMONR0); + + /* disable frequency meter */ + tmp = readl(pbase + XSP_U2FREQ_FMCR0); + tmp &= ~P2F_RG_FREQDET_EN; + writel(tmp, pbase + XSP_U2FREQ_FMCR0); + + /* disable free run clock */ + tmp = readl(pbase + XSP_U2FREQ_FMMONR1); + tmp &= ~P2F_RG_FRCK_EN; + writel(tmp, pbase + XSP_U2FREQ_FMMONR1); + + if (fm_out) { + /* (1024 / FM_OUT) x reference clock frequency x coefficient */ + tmp = xsphy->src_ref_clk * 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.%d, fm_out:%d, calib:%d (clk:%d, coef:%d)\n", + inst->index, fm_out, calib_val, + xsphy->src_ref_clk, xsphy->src_coef); + + /* set HS slew rate */ + tmp = readl(pbase + XSP_USBPHYACR5); + tmp &= ~P2A5_RG_HSTX_SRCTRL; + tmp |= P2A5_RG_HSTX_SRCTRL_VAL(calib_val); + writel(tmp, pbase + XSP_USBPHYACR5); + + /* disable USB ring oscillator */ + tmp = readl(pbase + XSP_USBPHYACR5); + tmp &= ~P2A5_RG_HSTX_SRCAL_EN; + writel(tmp, pbase + XSP_USBPHYACR5); +} + +static void u2_phy_instance_init(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 tmp; + + /* DP/DM BC1.1 path Disable */ + tmp = readl(pbase + XSP_USBPHYACR6); + tmp &= ~P2A6_RG_BC11_SW_EN; + writel(tmp, pbase + XSP_USBPHYACR6); + + tmp = readl(pbase + XSP_USBPHYACR0); + tmp |= P2A0_RG_INTR_EN; + writel(tmp, pbase + XSP_USBPHYACR0); +} + +static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 index = inst->index; + u32 tmp; + + tmp = readl(pbase + XSP_USBPHYACR6); + tmp |= P2A6_RG_OTG_VBUSCMP_EN; + writel(tmp, pbase + XSP_USBPHYACR6); + + tmp = readl(pbase + XSP_U2PHYDTM1); + tmp |= P2D_RG_VBUSVALID | P2D_RG_AVALID; + tmp &= ~P2D_RG_SESSEND; + writel(tmp, pbase + XSP_U2PHYDTM1); + + dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); +} + +static void u2_phy_instance_power_off(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 index = inst->index; + u32 tmp; + + tmp = readl(pbase + XSP_USBPHYACR6); + tmp &= ~P2A6_RG_OTG_VBUSCMP_EN; + writel(tmp, pbase + XSP_USBPHYACR6); + + tmp = readl(pbase + XSP_U2PHYDTM1); + tmp &= ~(P2D_RG_VBUSVALID | P2D_RG_AVALID); + tmp |= P2D_RG_SESSEND; + writel(tmp, pbase + XSP_U2PHYDTM1); + + dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); +} + +static void u2_phy_instance_set_mode(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst, + enum phy_mode mode) +{ + u32 tmp; + + tmp = readl(inst->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, inst->port_base + XSP_U2PHYDTM1); +} + +static void phy_parse_property(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + struct device *dev = &inst->phy->dev; + + switch (inst->type) { + case PHY_TYPE_USB2: + device_property_read_u32(dev, "mediatek,efuse-intr", + &inst->efuse_intr); + device_property_read_u32(dev, "mediatek,eye-src", + &inst->eye_src); + device_property_read_u32(dev, "mediatek,eye-vrt", + &inst->eye_vrt); + device_property_read_u32(dev, "mediatek,eye-term", + &inst->eye_term); + dev_dbg(dev, "intr:%d, src:%d, vrt:%d, term:%d\n", + inst->efuse_intr, inst->eye_src, + inst->eye_vrt, inst->eye_term); + break; + case PHY_TYPE_USB3: + device_property_read_u32(dev, "mediatek,efuse-intr", + &inst->efuse_intr); + device_property_read_u32(dev, "mediatek,efuse-tx-imp", + &inst->efuse_tx_imp); + device_property_read_u32(dev, "mediatek,efuse-rx-imp", + &inst->efuse_rx_imp); + dev_dbg(dev, "intr:%d, tx-imp:%d, rx-imp:%d\n", + inst->efuse_intr, inst->efuse_tx_imp, + inst->efuse_rx_imp); + break; + default: + dev_err(xsphy->dev, "incompatible phy type\n"); + return; + } +} + +static void u2_phy_props_set(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 tmp; + + if (inst->efuse_intr) { + tmp = readl(pbase + XSP_USBPHYACR1); + tmp &= ~P2A1_RG_INTR_CAL; + tmp |= P2A1_RG_INTR_CAL_VAL(inst->efuse_intr); + writel(tmp, pbase + XSP_USBPHYACR1); + } + + if (inst->eye_src) { + tmp = readl(pbase + XSP_USBPHYACR5); + tmp &= ~P2A5_RG_HSTX_SRCTRL; + tmp |= P2A5_RG_HSTX_SRCTRL_VAL(inst->eye_src); + writel(tmp, pbase + XSP_USBPHYACR5); + } + + if (inst->eye_vrt) { + tmp = readl(pbase + XSP_USBPHYACR1); + tmp &= ~P2A1_RG_VRT_SEL; + tmp |= P2A1_RG_VRT_SEL_VAL(inst->eye_vrt); + writel(tmp, pbase + XSP_USBPHYACR1); + } + + if (inst->eye_term) { + tmp = readl(pbase + XSP_USBPHYACR1); + tmp &= ~P2A1_RG_TERM_SEL; + tmp |= P2A1_RG_TERM_SEL_VAL(inst->eye_term); + writel(tmp, pbase + XSP_USBPHYACR1); + } +} + +static void u3_phy_props_set(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 tmp; + + if (inst->efuse_intr) { + tmp = readl(xsphy->glb_base + SSPXTP_PHYA_GLB_00); + tmp &= ~RG_XTP_GLB_BIAS_INTR_CTRL; + tmp |= RG_XTP_GLB_BIAS_INTR_CTRL_VAL(inst->efuse_intr); + writel(tmp, xsphy->glb_base + SSPXTP_PHYA_GLB_00); + } + + if (inst->efuse_tx_imp) { + tmp = readl(pbase + SSPXTP_PHYA_LN_04); + tmp &= ~RG_XTP_LN0_TX_IMPSEL; + tmp |= RG_XTP_LN0_TX_IMPSEL_VAL(inst->efuse_tx_imp); + writel(tmp, pbase + SSPXTP_PHYA_LN_04); + } + + if (inst->efuse_rx_imp) { + tmp = readl(pbase + SSPXTP_PHYA_LN_14); + tmp &= ~RG_XTP_LN0_RX_IMPSEL; + tmp |= RG_XTP_LN0_RX_IMPSEL_VAL(inst->efuse_rx_imp); + writel(tmp, pbase + SSPXTP_PHYA_LN_14); + } +} + +static int mtk_phy_init(struct phy *phy) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); + int ret; + + ret = clk_prepare_enable(inst->ref_clk); + if (ret) { + dev_err(xsphy->dev, "failed to enable ref_clk\n"); + return ret; + } + + switch (inst->type) { + case PHY_TYPE_USB2: + u2_phy_instance_init(xsphy, inst); + u2_phy_props_set(xsphy, inst); + break; + case PHY_TYPE_USB3: + u3_phy_props_set(xsphy, inst); + break; + default: + dev_err(xsphy->dev, "incompatible phy type\n"); + clk_disable_unprepare(inst->ref_clk); + return -EINVAL; + } + + return 0; +} + +static int mtk_phy_power_on(struct phy *phy) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); + + if (inst->type == PHY_TYPE_USB2) { + u2_phy_instance_power_on(xsphy, inst); + u2_phy_slew_rate_calibrate(xsphy, inst); + } + + return 0; +} + +static int mtk_phy_power_off(struct phy *phy) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); + + if (inst->type == PHY_TYPE_USB2) + u2_phy_instance_power_off(xsphy, inst); + + return 0; +} + +static int mtk_phy_exit(struct phy *phy) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + + clk_disable_unprepare(inst->ref_clk); + return 0; +} + +static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); + + if (inst->type == PHY_TYPE_USB2) + u2_phy_instance_set_mode(xsphy, inst, mode); + + return 0; +} + +static struct phy *mtk_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct mtk_xsphy *xsphy = dev_get_drvdata(dev); + struct xsphy_instance *inst = NULL; + struct device_node *phy_np = args->np; + int index; + + if (args->args_count != 1) { + dev_err(dev, "invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + for (index = 0; index < xsphy->nphys; index++) + if (phy_np == xsphy->phys[index]->phy->dev.of_node) { + inst = xsphy->phys[index]; + break; + } + + if (!inst) { + dev_err(dev, "failed to find appropriate phy\n"); + return ERR_PTR(-EINVAL); + } + + inst->type = args->args[0]; + if (!(inst->type == PHY_TYPE_USB2 || + inst->type == PHY_TYPE_USB3)) { + dev_err(dev, "unsupported phy type: %d\n", inst->type); + return ERR_PTR(-EINVAL); + } + + phy_parse_property(xsphy, inst); + + return inst->phy; +} + +static const struct phy_ops mtk_xsphy_ops = { + .init = mtk_phy_init, + .exit = mtk_phy_exit, + .power_on = mtk_phy_power_on, + .power_off = mtk_phy_power_off, + .set_mode = mtk_phy_set_mode, + .owner = THIS_MODULE, +}; + +static const struct of_device_id mtk_xsphy_id_table[] = { + { .compatible = "mediatek,xsphy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, mtk_xsphy_id_table); + +static int mtk_xsphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child_np; + struct phy_provider *provider; + struct resource *glb_res; + struct mtk_xsphy *xsphy; + struct resource res; + int port, retval; + + xsphy = devm_kzalloc(dev, sizeof(*xsphy), GFP_KERNEL); + if (!xsphy) + return -ENOMEM; + + xsphy->nphys = of_get_child_count(np); + xsphy->phys = devm_kcalloc(dev, xsphy->nphys, + sizeof(*xsphy->phys), GFP_KERNEL); + if (!xsphy->phys) + return -ENOMEM; + + xsphy->dev = dev; + platform_set_drvdata(pdev, xsphy); + + glb_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + /* optional, may not exist if no u3 phys */ + if (glb_res) { + /* get banks shared by multiple u3 phys */ + xsphy->glb_base = devm_ioremap_resource(dev, glb_res); + if (IS_ERR(xsphy->glb_base)) { + dev_err(dev, "failed to remap glb regs\n"); + return PTR_ERR(xsphy->glb_base); + } + } + + xsphy->src_ref_clk = XSP_REF_CLK; + xsphy->src_coef = XSP_SLEW_RATE_COEF; + /* update parameters of slew rate calibrate if exist */ + device_property_read_u32(dev, "mediatek,src-ref-clk-mhz", + &xsphy->src_ref_clk); + device_property_read_u32(dev, "mediatek,src-coef", &xsphy->src_coef); + + port = 0; + for_each_child_of_node(np, child_np) { + struct xsphy_instance *inst; + struct phy *phy; + + inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL); + if (!inst) { + retval = -ENOMEM; + goto put_child; + } + + xsphy->phys[port] = inst; + + phy = devm_phy_create(dev, child_np, &mtk_xsphy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create phy\n"); + retval = PTR_ERR(phy); + goto put_child; + } + + retval = of_address_to_resource(child_np, 0, &res); + if (retval) { + dev_err(dev, "failed to get address resource(id-%d)\n", + port); + goto put_child; + } + + inst->port_base = devm_ioremap_resource(&phy->dev, &res); + if (IS_ERR(inst->port_base)) { + dev_err(dev, "failed to remap phy regs\n"); + retval = PTR_ERR(inst->port_base); + goto put_child; + } + + inst->phy = phy; + inst->index = port; + phy_set_drvdata(phy, inst); + port++; + + inst->ref_clk = devm_clk_get(&phy->dev, "ref"); + if (IS_ERR(inst->ref_clk)) { + dev_err(dev, "failed to get ref_clk(id-%d)\n", port); + retval = PTR_ERR(inst->ref_clk); + goto put_child; + } + } + + provider = devm_of_phy_provider_register(dev, mtk_phy_xlate); + return PTR_ERR_OR_ZERO(provider); + +put_child: + of_node_put(child_np); + return retval; +} + +static struct platform_driver mtk_xsphy_driver = { + .probe = mtk_xsphy_probe, + .driver = { + .name = "mtk-xsphy", + .of_match_table = mtk_xsphy_id_table, + }, +}; + +module_platform_driver(mtk_xsphy_driver); + +MODULE_AUTHOR("Chunfeng Yun "); +MODULE_DESCRIPTION("MediaTek USB XS-PHY driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5 From mboxrd@z Thu Jan 1 00:00:00 1970 From: Chunfeng Yun Subject: [PATCH v3 2/2] phy: mediatek: add XS-PHY driver Date: Thu, 10 May 2018 14:10:29 +0800 Message-ID: <1525932629-10603-3-git-send-email-chunfeng.yun@mediatek.com> References: <1525932629-10603-1-git-send-email-chunfeng.yun@mediatek.com> Mime-Version: 1.0 Content-Type: text/plain Return-path: In-Reply-To: <1525932629-10603-1-git-send-email-chunfeng.yun@mediatek.com> Sender: linux-kernel-owner@vger.kernel.org To: Kishon Vijay Abraham I Cc: Matthias Brugger , Rob Herring , Mark Rutland , Ian Campbell , Ryder Lee , Chunfeng Yun , linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-usb@vger.kernel.org, linux-mediatek@lists.infradead.org, devicetree@vger.kernel.org List-Id: devicetree@vger.kernel.org Support XS-PHY for MediaTek SoCs with USB3.1 GEN2 controller Signed-off-by: Chunfeng Yun --- drivers/phy/mediatek/Kconfig | 9 + drivers/phy/mediatek/Makefile | 1 + drivers/phy/mediatek/phy-mtk-xsphy.c | 600 ++++++++++++++++++++++++++++++++++ 3 files changed, 610 insertions(+) create mode 100644 drivers/phy/mediatek/phy-mtk-xsphy.c diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index 88ab4e2..8857d00 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -12,3 +12,12 @@ config PHY_MTK_TPHY different banks layout, the T-PHY with shared banks between multi-ports is first version, otherwise is second veriosn, so you can easily distinguish them by banks layout. + +config PHY_MTK_XSPHY + tristate "MediaTek XS-PHY Driver" + depends on ARCH_MEDIATEK && OF + select GENERIC_PHY + 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. diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile index 1bdab14..ee49edc 100644 --- a/drivers/phy/mediatek/Makefile +++ b/drivers/phy/mediatek/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o +obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o diff --git a/drivers/phy/mediatek/phy-mtk-xsphy.c b/drivers/phy/mediatek/phy-mtk-xsphy.c new file mode 100644 index 0000000..020cd02 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-xsphy.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek USB3.1 gen2 xsphy Driver + * + * Copyright (c) 2018 MediaTek Inc. + * Author: Chunfeng Yun + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x)) + +#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_INTR_CAL_VAL(x) ((0x1f & (x)) << 19) +#define P2A1_RG_VRT_SEL GENMASK(14, 12) +#define P2A1_RG_VRT_SEL_VAL(x) ((0x7 & (x)) << 12) +#define P2A1_RG_TERM_SEL GENMASK(10, 8) +#define P2A1_RG_TERM_SEL_VAL(x) ((0x7 & (x)) << 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 P2A5_RG_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 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 RG_XTP_GLB_BIAS_INTR_CTRL_VAL(x) ((0x3f & (x)) << 16) + +#define SSPXTP_PHYA_LN_04 ((SSPXTP_SIFSLV_PHYA_LN) + 0x04) +#define RG_XTP_LN0_TX_IMPSEL GENMASK(4, 0) +#define RG_XTP_LN0_TX_IMPSEL_VAL(x) (0x1f & (x)) + +#define SSPXTP_PHYA_LN_14 ((SSPXTP_SIFSLV_PHYA_LN) + 0x014) +#define RG_XTP_LN0_RX_IMPSEL GENMASK(4, 0) +#define RG_XTP_LN0_RX_IMPSEL_VAL(x) (0x1f & (x)) + +#define XSP_REF_CLK 26 /* MHZ */ +#define XSP_SLEW_RATE_COEF 17 +#define XSP_SR_COEF_DIVISOR 1000 +#define XSP_FM_DET_CYCLE_CNT 1024 + +struct xsphy_instance { + struct phy *phy; + void __iomem *port_base; + struct clk *ref_clk; /* reference clock of anolog phy */ + u32 index; + u32 type; + /* only for HQA test */ + int efuse_intr; + int efuse_tx_imp; + int efuse_rx_imp; + /* u2 eye diagram */ + int eye_src; + int eye_vrt; + int eye_term; +}; + +struct mtk_xsphy { + struct device *dev; + void __iomem *glb_base; /* only shared u3 sif */ + struct xsphy_instance **phys; + int nphys; + int src_ref_clk; /* MHZ, reference clock for slew rate calibrate */ + int src_coef; /* coefficient for slew rate calibrate */ +}; + +static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + int calib_val; + int fm_out; + u32 tmp; + + /* use force value */ + if (inst->eye_src) + return; + + /* enable USB ring oscillator */ + tmp = readl(pbase + XSP_USBPHYACR5); + tmp |= P2A5_RG_HSTX_SRCAL_EN; + writel(tmp, pbase + XSP_USBPHYACR5); + udelay(1); /* wait clock stable */ + + /* enable free run clock */ + tmp = readl(pbase + XSP_U2FREQ_FMMONR1); + tmp |= P2F_RG_FRCK_EN; + writel(tmp, pbase + XSP_U2FREQ_FMMONR1); + + /* set cycle count as 1024 */ + tmp = readl(pbase + XSP_U2FREQ_FMCR0); + tmp &= ~(P2F_RG_CYCLECNT); + tmp |= P2F_RG_CYCLECNT_VAL(XSP_FM_DET_CYCLE_CNT); + writel(tmp, pbase + XSP_U2FREQ_FMCR0); + + /* enable frequency meter */ + tmp = readl(pbase + XSP_U2FREQ_FMCR0); + tmp |= P2F_RG_FREQDET_EN; + writel(tmp, pbase + XSP_U2FREQ_FMCR0); + + /* ignore return value */ + readl_poll_timeout(pbase + XSP_U2FREQ_FMMONR1, tmp, + (tmp & P2F_USB_FM_VALID), 10, 200); + + fm_out = readl(pbase + XSP_U2FREQ_MMONR0); + + /* disable frequency meter */ + tmp = readl(pbase + XSP_U2FREQ_FMCR0); + tmp &= ~P2F_RG_FREQDET_EN; + writel(tmp, pbase + XSP_U2FREQ_FMCR0); + + /* disable free run clock */ + tmp = readl(pbase + XSP_U2FREQ_FMMONR1); + tmp &= ~P2F_RG_FRCK_EN; + writel(tmp, pbase + XSP_U2FREQ_FMMONR1); + + if (fm_out) { + /* (1024 / FM_OUT) x reference clock frequency x coefficient */ + tmp = xsphy->src_ref_clk * 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.%d, fm_out:%d, calib:%d (clk:%d, coef:%d)\n", + inst->index, fm_out, calib_val, + xsphy->src_ref_clk, xsphy->src_coef); + + /* set HS slew rate */ + tmp = readl(pbase + XSP_USBPHYACR5); + tmp &= ~P2A5_RG_HSTX_SRCTRL; + tmp |= P2A5_RG_HSTX_SRCTRL_VAL(calib_val); + writel(tmp, pbase + XSP_USBPHYACR5); + + /* disable USB ring oscillator */ + tmp = readl(pbase + XSP_USBPHYACR5); + tmp &= ~P2A5_RG_HSTX_SRCAL_EN; + writel(tmp, pbase + XSP_USBPHYACR5); +} + +static void u2_phy_instance_init(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 tmp; + + /* DP/DM BC1.1 path Disable */ + tmp = readl(pbase + XSP_USBPHYACR6); + tmp &= ~P2A6_RG_BC11_SW_EN; + writel(tmp, pbase + XSP_USBPHYACR6); + + tmp = readl(pbase + XSP_USBPHYACR0); + tmp |= P2A0_RG_INTR_EN; + writel(tmp, pbase + XSP_USBPHYACR0); +} + +static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 index = inst->index; + u32 tmp; + + tmp = readl(pbase + XSP_USBPHYACR6); + tmp |= P2A6_RG_OTG_VBUSCMP_EN; + writel(tmp, pbase + XSP_USBPHYACR6); + + tmp = readl(pbase + XSP_U2PHYDTM1); + tmp |= P2D_RG_VBUSVALID | P2D_RG_AVALID; + tmp &= ~P2D_RG_SESSEND; + writel(tmp, pbase + XSP_U2PHYDTM1); + + dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); +} + +static void u2_phy_instance_power_off(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 index = inst->index; + u32 tmp; + + tmp = readl(pbase + XSP_USBPHYACR6); + tmp &= ~P2A6_RG_OTG_VBUSCMP_EN; + writel(tmp, pbase + XSP_USBPHYACR6); + + tmp = readl(pbase + XSP_U2PHYDTM1); + tmp &= ~(P2D_RG_VBUSVALID | P2D_RG_AVALID); + tmp |= P2D_RG_SESSEND; + writel(tmp, pbase + XSP_U2PHYDTM1); + + dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); +} + +static void u2_phy_instance_set_mode(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst, + enum phy_mode mode) +{ + u32 tmp; + + tmp = readl(inst->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, inst->port_base + XSP_U2PHYDTM1); +} + +static void phy_parse_property(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + struct device *dev = &inst->phy->dev; + + switch (inst->type) { + case PHY_TYPE_USB2: + device_property_read_u32(dev, "mediatek,efuse-intr", + &inst->efuse_intr); + device_property_read_u32(dev, "mediatek,eye-src", + &inst->eye_src); + device_property_read_u32(dev, "mediatek,eye-vrt", + &inst->eye_vrt); + device_property_read_u32(dev, "mediatek,eye-term", + &inst->eye_term); + dev_dbg(dev, "intr:%d, src:%d, vrt:%d, term:%d\n", + inst->efuse_intr, inst->eye_src, + inst->eye_vrt, inst->eye_term); + break; + case PHY_TYPE_USB3: + device_property_read_u32(dev, "mediatek,efuse-intr", + &inst->efuse_intr); + device_property_read_u32(dev, "mediatek,efuse-tx-imp", + &inst->efuse_tx_imp); + device_property_read_u32(dev, "mediatek,efuse-rx-imp", + &inst->efuse_rx_imp); + dev_dbg(dev, "intr:%d, tx-imp:%d, rx-imp:%d\n", + inst->efuse_intr, inst->efuse_tx_imp, + inst->efuse_rx_imp); + break; + default: + dev_err(xsphy->dev, "incompatible phy type\n"); + return; + } +} + +static void u2_phy_props_set(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 tmp; + + if (inst->efuse_intr) { + tmp = readl(pbase + XSP_USBPHYACR1); + tmp &= ~P2A1_RG_INTR_CAL; + tmp |= P2A1_RG_INTR_CAL_VAL(inst->efuse_intr); + writel(tmp, pbase + XSP_USBPHYACR1); + } + + if (inst->eye_src) { + tmp = readl(pbase + XSP_USBPHYACR5); + tmp &= ~P2A5_RG_HSTX_SRCTRL; + tmp |= P2A5_RG_HSTX_SRCTRL_VAL(inst->eye_src); + writel(tmp, pbase + XSP_USBPHYACR5); + } + + if (inst->eye_vrt) { + tmp = readl(pbase + XSP_USBPHYACR1); + tmp &= ~P2A1_RG_VRT_SEL; + tmp |= P2A1_RG_VRT_SEL_VAL(inst->eye_vrt); + writel(tmp, pbase + XSP_USBPHYACR1); + } + + if (inst->eye_term) { + tmp = readl(pbase + XSP_USBPHYACR1); + tmp &= ~P2A1_RG_TERM_SEL; + tmp |= P2A1_RG_TERM_SEL_VAL(inst->eye_term); + writel(tmp, pbase + XSP_USBPHYACR1); + } +} + +static void u3_phy_props_set(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 tmp; + + if (inst->efuse_intr) { + tmp = readl(xsphy->glb_base + SSPXTP_PHYA_GLB_00); + tmp &= ~RG_XTP_GLB_BIAS_INTR_CTRL; + tmp |= RG_XTP_GLB_BIAS_INTR_CTRL_VAL(inst->efuse_intr); + writel(tmp, xsphy->glb_base + SSPXTP_PHYA_GLB_00); + } + + if (inst->efuse_tx_imp) { + tmp = readl(pbase + SSPXTP_PHYA_LN_04); + tmp &= ~RG_XTP_LN0_TX_IMPSEL; + tmp |= RG_XTP_LN0_TX_IMPSEL_VAL(inst->efuse_tx_imp); + writel(tmp, pbase + SSPXTP_PHYA_LN_04); + } + + if (inst->efuse_rx_imp) { + tmp = readl(pbase + SSPXTP_PHYA_LN_14); + tmp &= ~RG_XTP_LN0_RX_IMPSEL; + tmp |= RG_XTP_LN0_RX_IMPSEL_VAL(inst->efuse_rx_imp); + writel(tmp, pbase + SSPXTP_PHYA_LN_14); + } +} + +static int mtk_phy_init(struct phy *phy) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); + int ret; + + ret = clk_prepare_enable(inst->ref_clk); + if (ret) { + dev_err(xsphy->dev, "failed to enable ref_clk\n"); + return ret; + } + + switch (inst->type) { + case PHY_TYPE_USB2: + u2_phy_instance_init(xsphy, inst); + u2_phy_props_set(xsphy, inst); + break; + case PHY_TYPE_USB3: + u3_phy_props_set(xsphy, inst); + break; + default: + dev_err(xsphy->dev, "incompatible phy type\n"); + clk_disable_unprepare(inst->ref_clk); + return -EINVAL; + } + + return 0; +} + +static int mtk_phy_power_on(struct phy *phy) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); + + if (inst->type == PHY_TYPE_USB2) { + u2_phy_instance_power_on(xsphy, inst); + u2_phy_slew_rate_calibrate(xsphy, inst); + } + + return 0; +} + +static int mtk_phy_power_off(struct phy *phy) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); + + if (inst->type == PHY_TYPE_USB2) + u2_phy_instance_power_off(xsphy, inst); + + return 0; +} + +static int mtk_phy_exit(struct phy *phy) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + + clk_disable_unprepare(inst->ref_clk); + return 0; +} + +static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); + + if (inst->type == PHY_TYPE_USB2) + u2_phy_instance_set_mode(xsphy, inst, mode); + + return 0; +} + +static struct phy *mtk_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct mtk_xsphy *xsphy = dev_get_drvdata(dev); + struct xsphy_instance *inst = NULL; + struct device_node *phy_np = args->np; + int index; + + if (args->args_count != 1) { + dev_err(dev, "invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + for (index = 0; index < xsphy->nphys; index++) + if (phy_np == xsphy->phys[index]->phy->dev.of_node) { + inst = xsphy->phys[index]; + break; + } + + if (!inst) { + dev_err(dev, "failed to find appropriate phy\n"); + return ERR_PTR(-EINVAL); + } + + inst->type = args->args[0]; + if (!(inst->type == PHY_TYPE_USB2 || + inst->type == PHY_TYPE_USB3)) { + dev_err(dev, "unsupported phy type: %d\n", inst->type); + return ERR_PTR(-EINVAL); + } + + phy_parse_property(xsphy, inst); + + return inst->phy; +} + +static const struct phy_ops mtk_xsphy_ops = { + .init = mtk_phy_init, + .exit = mtk_phy_exit, + .power_on = mtk_phy_power_on, + .power_off = mtk_phy_power_off, + .set_mode = mtk_phy_set_mode, + .owner = THIS_MODULE, +}; + +static const struct of_device_id mtk_xsphy_id_table[] = { + { .compatible = "mediatek,xsphy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, mtk_xsphy_id_table); + +static int mtk_xsphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child_np; + struct phy_provider *provider; + struct resource *glb_res; + struct mtk_xsphy *xsphy; + struct resource res; + int port, retval; + + xsphy = devm_kzalloc(dev, sizeof(*xsphy), GFP_KERNEL); + if (!xsphy) + return -ENOMEM; + + xsphy->nphys = of_get_child_count(np); + xsphy->phys = devm_kcalloc(dev, xsphy->nphys, + sizeof(*xsphy->phys), GFP_KERNEL); + if (!xsphy->phys) + return -ENOMEM; + + xsphy->dev = dev; + platform_set_drvdata(pdev, xsphy); + + glb_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + /* optional, may not exist if no u3 phys */ + if (glb_res) { + /* get banks shared by multiple u3 phys */ + xsphy->glb_base = devm_ioremap_resource(dev, glb_res); + if (IS_ERR(xsphy->glb_base)) { + dev_err(dev, "failed to remap glb regs\n"); + return PTR_ERR(xsphy->glb_base); + } + } + + xsphy->src_ref_clk = XSP_REF_CLK; + xsphy->src_coef = XSP_SLEW_RATE_COEF; + /* update parameters of slew rate calibrate if exist */ + device_property_read_u32(dev, "mediatek,src-ref-clk-mhz", + &xsphy->src_ref_clk); + device_property_read_u32(dev, "mediatek,src-coef", &xsphy->src_coef); + + port = 0; + for_each_child_of_node(np, child_np) { + struct xsphy_instance *inst; + struct phy *phy; + + inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL); + if (!inst) { + retval = -ENOMEM; + goto put_child; + } + + xsphy->phys[port] = inst; + + phy = devm_phy_create(dev, child_np, &mtk_xsphy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create phy\n"); + retval = PTR_ERR(phy); + goto put_child; + } + + retval = of_address_to_resource(child_np, 0, &res); + if (retval) { + dev_err(dev, "failed to get address resource(id-%d)\n", + port); + goto put_child; + } + + inst->port_base = devm_ioremap_resource(&phy->dev, &res); + if (IS_ERR(inst->port_base)) { + dev_err(dev, "failed to remap phy regs\n"); + retval = PTR_ERR(inst->port_base); + goto put_child; + } + + inst->phy = phy; + inst->index = port; + phy_set_drvdata(phy, inst); + port++; + + inst->ref_clk = devm_clk_get(&phy->dev, "ref"); + if (IS_ERR(inst->ref_clk)) { + dev_err(dev, "failed to get ref_clk(id-%d)\n", port); + retval = PTR_ERR(inst->ref_clk); + goto put_child; + } + } + + provider = devm_of_phy_provider_register(dev, mtk_phy_xlate); + return PTR_ERR_OR_ZERO(provider); + +put_child: + of_node_put(child_np); + return retval; +} + +static struct platform_driver mtk_xsphy_driver = { + .probe = mtk_xsphy_probe, + .driver = { + .name = "mtk-xsphy", + .of_match_table = mtk_xsphy_id_table, + }, +}; + +module_platform_driver(mtk_xsphy_driver); + +MODULE_AUTHOR("Chunfeng Yun "); +MODULE_DESCRIPTION("MediaTek USB XS-PHY driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5 From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Subject: [v3,2/2] phy: mediatek: add XS-PHY driver From: Chunfeng Yun Message-Id: <1525932629-10603-3-git-send-email-chunfeng.yun@mediatek.com> Date: Thu, 10 May 2018 14:10:29 +0800 To: Kishon Vijay Abraham I Cc: Matthias Brugger , Rob Herring , Mark Rutland , Ian Campbell , Ryder Lee , Chunfeng Yun , linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-usb@vger.kernel.org, linux-mediatek@lists.infradead.org, devicetree@vger.kernel.org List-ID: U3VwcG9ydCBYUy1QSFkgZm9yIE1lZGlhVGVrIFNvQ3Mgd2l0aCBVU0IzLjEgR0VOMiBjb250cm9s bGVyCgpTaWduZWQtb2ZmLWJ5OiBDaHVuZmVuZyBZdW4gPGNodW5mZW5nLnl1bkBtZWRpYXRlay5j b20+Ci0tLQogZHJpdmVycy9waHkvbWVkaWF0ZWsvS2NvbmZpZyAgICAgICAgIHwgICAgOSArCiBk cml2ZXJzL3BoeS9tZWRpYXRlay9NYWtlZmlsZSAgICAgICAgfCAgICAxICsKIGRyaXZlcnMvcGh5 L21lZGlhdGVrL3BoeS1tdGsteHNwaHkuYyB8ICA2MDAgKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKwogMyBmaWxlcyBjaGFuZ2VkLCA2MTAgaW5zZXJ0aW9ucygrKQogY3JlYXRlIG1v ZGUgMTAwNjQ0IGRyaXZlcnMvcGh5L21lZGlhdGVrL3BoeS1tdGsteHNwaHkuYwoKZGlmZiAtLWdp dCBhL2RyaXZlcnMvcGh5L21lZGlhdGVrL0tjb25maWcgYi9kcml2ZXJzL3BoeS9tZWRpYXRlay9L Y29uZmlnCmluZGV4IDg4YWI0ZTIuLjg4NTdkMDAgMTAwNjQ0Ci0tLSBhL2RyaXZlcnMvcGh5L21l ZGlhdGVrL0tjb25maWcKKysrIGIvZHJpdmVycy9waHkvbWVkaWF0ZWsvS2NvbmZpZwpAQCAtMTIs MyArMTIsMTIgQEAgY29uZmlnIFBIWV9NVEtfVFBIWQogCSAgZGlmZmVyZW50IGJhbmtzIGxheW91 dCwgdGhlIFQtUEhZIHdpdGggc2hhcmVkIGJhbmtzIGJldHdlZW4KIAkgIG11bHRpLXBvcnRzIGlz IGZpcnN0IHZlcnNpb24sIG90aGVyd2lzZSBpcyBzZWNvbmQgdmVyaW9zbiwKIAkgIHNvIHlvdSBj YW4gZWFzaWx5IGRpc3Rpbmd1aXNoIHRoZW0gYnkgYmFua3MgbGF5b3V0LgorCitjb25maWcgUEhZ X01US19YU1BIWQorICAgIHRyaXN0YXRlICJNZWRpYVRlayBYUy1QSFkgRHJpdmVyIgorICAgIGRl cGVuZHMgb24gQVJDSF9NRURJQVRFSyAmJiBPRgorICAgIHNlbGVjdCBHRU5FUklDX1BIWQorICAg IGhlbHAKKwkgIEVuYWJsZSB0aGlzIHRvIHN1cHBvcnQgdGhlIFN1cGVyU3BlZWRQbHVzIFhTLVBI WSB0cmFuc2NlaXZlciBmb3IKKwkgIFVTQjMuMSBHRU4yIGNvbnRyb2xsZXJzIG9uIE1lZGlhVGVr IGNoaXBzLiBUaGUgZHJpdmVyIHN1cHBvcnRzCisJICBtdWx0aXBsZSBVU0IyLjAsIFVTQjMuMSBH RU4yIHBvcnRzLgpkaWZmIC0tZ2l0IGEvZHJpdmVycy9waHkvbWVkaWF0ZWsvTWFrZWZpbGUgYi9k cml2ZXJzL3BoeS9tZWRpYXRlay9NYWtlZmlsZQppbmRleCAxYmRhYjE0Li5lZTQ5ZWRjIDEwMDY0 NAotLS0gYS9kcml2ZXJzL3BoeS9tZWRpYXRlay9NYWtlZmlsZQorKysgYi9kcml2ZXJzL3BoeS9t ZWRpYXRlay9NYWtlZmlsZQpAQCAtNCwzICs0LDQgQEAKICMKIAogb2JqLSQoQ09ORklHX1BIWV9N VEtfVFBIWSkJCSs9IHBoeS1tdGstdHBoeS5vCitvYmotJChDT05GSUdfUEhZX01US19YU1BIWSkJ CSs9IHBoeS1tdGsteHNwaHkubwpkaWZmIC0tZ2l0IGEvZHJpdmVycy9waHkvbWVkaWF0ZWsvcGh5 LW10ay14c3BoeS5jIGIvZHJpdmVycy9waHkvbWVkaWF0ZWsvcGh5LW10ay14c3BoeS5jCm5ldyBm aWxlIG1vZGUgMTAwNjQ0CmluZGV4IDAwMDAwMDAuLjAyMGNkMDIKLS0tIC9kZXYvbnVsbAorKysg Yi9kcml2ZXJzL3BoeS9tZWRpYXRlay9waHktbXRrLXhzcGh5LmMKQEAgLTAsMCArMSw2MDAgQEAK Ky8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBHUEwtMi4wCisvKgorICogTWVkaWFUZWsgVVNC My4xIGdlbjIgeHNwaHkgRHJpdmVyCisgKgorICogQ29weXJpZ2h0IChjKSAyMDE4IE1lZGlhVGVr IEluYy4KKyAqIEF1dGhvcjogQ2h1bmZlbmcgWXVuIDxjaHVuZmVuZy55dW5AbWVkaWF0ZWsuY29t PgorICoKKyAqLworCisjaW5jbHVkZSA8ZHQtYmluZGluZ3MvcGh5L3BoeS5oPgorI2luY2x1ZGUg PGxpbnV4L2Nsay5oPgorI2luY2x1ZGUgPGxpbnV4L2RlbGF5Lmg+CisjaW5jbHVkZSA8bGludXgv aW8uaD4KKyNpbmNsdWRlIDxsaW51eC9pb3BvbGwuaD4KKyNpbmNsdWRlIDxsaW51eC9tb2R1bGUu aD4KKyNpbmNsdWRlIDxsaW51eC9vZl9hZGRyZXNzLmg+CisjaW5jbHVkZSA8bGludXgvcGh5L3Bo eS5oPgorI2luY2x1ZGUgPGxpbnV4L3BsYXRmb3JtX2RldmljZS5oPgorCisvKiB1MiBwaHkgYmFu a3MgKi8KKyNkZWZpbmUgU1NVU0JfU0lGU0xWX01JU0MJCTB4MDAwCisjZGVmaW5lIFNTVVNCX1NJ RlNMVl9VMkZSRVEJCTB4MTAwCisjZGVmaW5lIFNTVVNCX1NJRlNMVl9VMlBIWV9DT00JMHgzMDAK KworLyogdTMgcGh5IHNoYXJlZCBiYW5rcyAqLworI2RlZmluZSBTU1BYVFBfU0lGU0xWX0RJR19H TEIJCTB4MDAwCisjZGVmaW5lIFNTUFhUUF9TSUZTTFZfUEhZQV9HTEIJCTB4MTAwCisKKy8qIHUz IHBoeSBiYW5rcyAqLworI2RlZmluZSBTU1BYVFBfU0lGU0xWX0RJR19MTl9UT1AJMHgwMDAKKyNk ZWZpbmUgU1NQWFRQX1NJRlNMVl9ESUdfTE5fVFgwCTB4MTAwCisjZGVmaW5lIFNTUFhUUF9TSUZT TFZfRElHX0xOX1JYMAkweDIwMAorI2RlZmluZSBTU1BYVFBfU0lGU0xWX0RJR19MTl9EQUlGCTB4 MzAwCisjZGVmaW5lIFNTUFhUUF9TSUZTTFZfUEhZQV9MTgkJMHg0MDAKKworI2RlZmluZSBYU1Bf VTJGUkVRX0ZNQ1IwCSgoU1NVU0JfU0lGU0xWX1UyRlJFUSkgKyAweDAwKQorI2RlZmluZSBQMkZf UkdfRlJFUURFVF9FTglCSVQoMjQpCisjZGVmaW5lIFAyRl9SR19DWUNMRUNOVAkJR0VOTUFTSygy MywgMCkKKyNkZWZpbmUgUDJGX1JHX0NZQ0xFQ05UX1ZBTCh4KQkoKFAyRl9SR19DWUNMRUNOVCkg JiAoeCkpCisKKyNkZWZpbmUgWFNQX1UyRlJFUV9NTU9OUjAgICgoU1NVU0JfU0lGU0xWX1UyRlJF USkgKyAweDBjKQorCisjZGVmaW5lIFhTUF9VMkZSRVFfRk1NT05SMQkoKFNTVVNCX1NJRlNMVl9V MkZSRVEpICsgMHgxMCkKKyNkZWZpbmUgUDJGX1JHX0ZSQ0tfRU4JCUJJVCg4KQorI2RlZmluZSBQ MkZfVVNCX0ZNX1ZBTElECUJJVCgwKQorCisjZGVmaW5lIFhTUF9VU0JQSFlBQ1IwCSgoU1NVU0Jf U0lGU0xWX1UyUEhZX0NPTSkgKyAweDAwKQorI2RlZmluZSBQMkEwX1JHX0lOVFJfRU4JQklUKDUp CisKKyNkZWZpbmUgWFNQX1VTQlBIWUFDUjEJCSgoU1NVU0JfU0lGU0xWX1UyUEhZX0NPTSkgKyAw eDA0KQorI2RlZmluZSBQMkExX1JHX0lOVFJfQ0FMCQlHRU5NQVNLKDIzLCAxOSkKKyNkZWZpbmUg UDJBMV9SR19JTlRSX0NBTF9WQUwoeCkJKCgweDFmICYgKHgpKSA8PCAxOSkKKyNkZWZpbmUgUDJB MV9SR19WUlRfU0VMCQkJR0VOTUFTSygxNCwgMTIpCisjZGVmaW5lIFAyQTFfUkdfVlJUX1NFTF9W QUwoeCkJKCgweDcgJiAoeCkpIDw8IDEyKQorI2RlZmluZSBQMkExX1JHX1RFUk1fU0VMCQlHRU5N QVNLKDEwLCA4KQorI2RlZmluZSBQMkExX1JHX1RFUk1fU0VMX1ZBTCh4KQkoKDB4NyAmICh4KSkg PDwgOCkKKworI2RlZmluZSBYU1BfVVNCUEhZQUNSNQkJKChTU1VTQl9TSUZTTFZfVTJQSFlfQ09N KSArIDB4MDE0KQorI2RlZmluZSBQMkE1X1JHX0hTVFhfU1JDQUxfRU4JQklUKDE1KQorI2RlZmlu ZSBQMkE1X1JHX0hTVFhfU1JDVFJMCQlHRU5NQVNLKDE0LCAxMikKKyNkZWZpbmUgUDJBNV9SR19I U1RYX1NSQ1RSTF9WQUwoeCkJKCgweDcgJiAoeCkpIDw8IDEyKQorCisjZGVmaW5lIFhTUF9VU0JQ SFlBQ1I2CQkoKFNTVVNCX1NJRlNMVl9VMlBIWV9DT00pICsgMHgwMTgpCisjZGVmaW5lIFAyQTZf UkdfQkMxMV9TV19FTglCSVQoMjMpCisjZGVmaW5lIFAyQTZfUkdfT1RHX1ZCVVNDTVBfRU4JQklU KDIwKQorCisjZGVmaW5lIFhTUF9VMlBIWURUTTEJCSgoU1NVU0JfU0lGU0xWX1UyUEhZX0NPTSkg KyAweDA2QykKKyNkZWZpbmUgUDJEX0ZPUkNFX0lERElHCQlCSVQoOSkKKyNkZWZpbmUgUDJEX1JH X1ZCVVNWQUxJRAlCSVQoNSkKKyNkZWZpbmUgUDJEX1JHX1NFU1NFTkQJCUJJVCg0KQorI2RlZmlu ZSBQMkRfUkdfQVZBTElECQlCSVQoMikKKyNkZWZpbmUgUDJEX1JHX0lERElHCQlCSVQoMSkKKwor I2RlZmluZSBTU1BYVFBfUEhZQV9HTEJfMDAJCSgoU1NQWFRQX1NJRlNMVl9QSFlBX0dMQikgKyAw eDAwKQorI2RlZmluZSBSR19YVFBfR0xCX0JJQVNfSU5UUl9DVFJMCQlHRU5NQVNLKDIxLCAxNikK KyNkZWZpbmUgUkdfWFRQX0dMQl9CSUFTX0lOVFJfQ1RSTF9WQUwoeCkJKCgweDNmICYgKHgpKSA8 PCAxNikKKworI2RlZmluZSBTU1BYVFBfUEhZQV9MTl8wNAkoKFNTUFhUUF9TSUZTTFZfUEhZQV9M TikgKyAweDA0KQorI2RlZmluZSBSR19YVFBfTE4wX1RYX0lNUFNFTAkJR0VOTUFTSyg0LCAwKQor I2RlZmluZSBSR19YVFBfTE4wX1RYX0lNUFNFTF9WQUwoeCkJKDB4MWYgJiAoeCkpCisKKyNkZWZp bmUgU1NQWFRQX1BIWUFfTE5fMTQJKChTU1BYVFBfU0lGU0xWX1BIWUFfTE4pICsgMHgwMTQpCisj ZGVmaW5lIFJHX1hUUF9MTjBfUlhfSU1QU0VMCQlHRU5NQVNLKDQsIDApCisjZGVmaW5lIFJHX1hU UF9MTjBfUlhfSU1QU0VMX1ZBTCh4KQkoMHgxZiAmICh4KSkKKworI2RlZmluZSBYU1BfUkVGX0NM SwkJMjYJLyogTUhaICovCisjZGVmaW5lIFhTUF9TTEVXX1JBVEVfQ09FRgkxNworI2RlZmluZSBY U1BfU1JfQ09FRl9ESVZJU09SCTEwMDAKKyNkZWZpbmUgWFNQX0ZNX0RFVF9DWUNMRV9DTlQJMTAy NAorCitzdHJ1Y3QgeHNwaHlfaW5zdGFuY2UgeworCXN0cnVjdCBwaHkgKnBoeTsKKwl2b2lkIF9f aW9tZW0gKnBvcnRfYmFzZTsKKwlzdHJ1Y3QgY2xrICpyZWZfY2xrOwkvKiByZWZlcmVuY2UgY2xv Y2sgb2YgYW5vbG9nIHBoeSAqLworCXUzMiBpbmRleDsKKwl1MzIgdHlwZTsKKwkvKiBvbmx5IGZv ciBIUUEgdGVzdCAqLworCWludCBlZnVzZV9pbnRyOworCWludCBlZnVzZV90eF9pbXA7CisJaW50 IGVmdXNlX3J4X2ltcDsKKwkvKiB1MiBleWUgZGlhZ3JhbSAqLworCWludCBleWVfc3JjOworCWlu dCBleWVfdnJ0OworCWludCBleWVfdGVybTsKK307CisKK3N0cnVjdCBtdGtfeHNwaHkgeworCXN0 cnVjdCBkZXZpY2UgKmRldjsKKwl2b2lkIF9faW9tZW0gKmdsYl9iYXNlOwkvKiBvbmx5IHNoYXJl ZCB1MyBzaWYgKi8KKwlzdHJ1Y3QgeHNwaHlfaW5zdGFuY2UgKipwaHlzOworCWludCBucGh5czsK KwlpbnQgc3JjX3JlZl9jbGs7IC8qIE1IWiwgcmVmZXJlbmNlIGNsb2NrIGZvciBzbGV3IHJhdGUg Y2FsaWJyYXRlICovCisJaW50IHNyY19jb2VmOyAgICAvKiBjb2VmZmljaWVudCBmb3Igc2xldyBy YXRlIGNhbGlicmF0ZSAqLworfTsKKworc3RhdGljIHZvaWQgdTJfcGh5X3NsZXdfcmF0ZV9jYWxp YnJhdGUoc3RydWN0IG10a194c3BoeSAqeHNwaHksCisJCQkJCXN0cnVjdCB4c3BoeV9pbnN0YW5j ZSAqaW5zdCkKK3sKKwl2b2lkIF9faW9tZW0gKnBiYXNlID0gaW5zdC0+cG9ydF9iYXNlOworCWlu dCBjYWxpYl92YWw7CisJaW50IGZtX291dDsKKwl1MzIgdG1wOworCisJLyogdXNlIGZvcmNlIHZh bHVlICovCisJaWYgKGluc3QtPmV5ZV9zcmMpCisJCXJldHVybjsKKworCS8qIGVuYWJsZSBVU0Ig cmluZyBvc2NpbGxhdG9yICovCisJdG1wID0gcmVhZGwocGJhc2UgKyBYU1BfVVNCUEhZQUNSNSk7 CisJdG1wIHw9IFAyQTVfUkdfSFNUWF9TUkNBTF9FTjsKKwl3cml0ZWwodG1wLCBwYmFzZSArIFhT UF9VU0JQSFlBQ1I1KTsKKwl1ZGVsYXkoMSk7CS8qIHdhaXQgY2xvY2sgc3RhYmxlICovCisKKwkv KiBlbmFibGUgZnJlZSBydW4gY2xvY2sgKi8KKwl0bXAgPSByZWFkbChwYmFzZSArIFhTUF9VMkZS RVFfRk1NT05SMSk7CisJdG1wIHw9IFAyRl9SR19GUkNLX0VOOworCXdyaXRlbCh0bXAsIHBiYXNl ICsgWFNQX1UyRlJFUV9GTU1PTlIxKTsKKworCS8qIHNldCBjeWNsZSBjb3VudCBhcyAxMDI0ICov CisJdG1wID0gcmVhZGwocGJhc2UgKyBYU1BfVTJGUkVRX0ZNQ1IwKTsKKwl0bXAgJj0gfihQMkZf UkdfQ1lDTEVDTlQpOworCXRtcCB8PSBQMkZfUkdfQ1lDTEVDTlRfVkFMKFhTUF9GTV9ERVRfQ1lD TEVfQ05UKTsKKwl3cml0ZWwodG1wLCBwYmFzZSArIFhTUF9VMkZSRVFfRk1DUjApOworCisJLyog ZW5hYmxlIGZyZXF1ZW5jeSBtZXRlciAqLworCXRtcCA9IHJlYWRsKHBiYXNlICsgWFNQX1UyRlJF UV9GTUNSMCk7CisJdG1wIHw9IFAyRl9SR19GUkVRREVUX0VOOworCXdyaXRlbCh0bXAsIHBiYXNl ICsgWFNQX1UyRlJFUV9GTUNSMCk7CisKKwkvKiBpZ25vcmUgcmV0dXJuIHZhbHVlICovCisJcmVh ZGxfcG9sbF90aW1lb3V0KHBiYXNlICsgWFNQX1UyRlJFUV9GTU1PTlIxLCB0bXAsCisJCQkgICAo dG1wICYgUDJGX1VTQl9GTV9WQUxJRCksIDEwLCAyMDApOworCisJZm1fb3V0ID0gcmVhZGwocGJh c2UgKyBYU1BfVTJGUkVRX01NT05SMCk7CisKKwkvKiBkaXNhYmxlIGZyZXF1ZW5jeSBtZXRlciAq LworCXRtcCA9IHJlYWRsKHBiYXNlICsgWFNQX1UyRlJFUV9GTUNSMCk7CisJdG1wICY9IH5QMkZf UkdfRlJFUURFVF9FTjsKKwl3cml0ZWwodG1wLCBwYmFzZSArIFhTUF9VMkZSRVFfRk1DUjApOwor CisJLyogZGlzYWJsZSBmcmVlIHJ1biBjbG9jayAqLworCXRtcCA9IHJlYWRsKHBiYXNlICsgWFNQ X1UyRlJFUV9GTU1PTlIxKTsKKwl0bXAgJj0gflAyRl9SR19GUkNLX0VOOworCXdyaXRlbCh0bXAs IHBiYXNlICsgWFNQX1UyRlJFUV9GTU1PTlIxKTsKKworCWlmIChmbV9vdXQpIHsKKwkJLyogKDEw MjQgLyBGTV9PVVQpIHggcmVmZXJlbmNlIGNsb2NrIGZyZXF1ZW5jeSB4IGNvZWZmaWNpZW50ICov CisJCXRtcCA9IHhzcGh5LT5zcmNfcmVmX2NsayAqIHhzcGh5LT5zcmNfY29lZjsKKwkJdG1wID0g KHRtcCAqIFhTUF9GTV9ERVRfQ1lDTEVfQ05UKSAvIGZtX291dDsKKwkJY2FsaWJfdmFsID0gRElW X1JPVU5EX0NMT1NFU1QodG1wLCBYU1BfU1JfQ09FRl9ESVZJU09SKTsKKwl9IGVsc2UgeworCQkv KiBpZiBGTSBkZXRlY3Rpb24gZmFpbCwgc2V0IGRlZmF1bHQgdmFsdWUgKi8KKwkJY2FsaWJfdmFs ID0gMzsKKwl9CisJZGV2X2RiZyh4c3BoeS0+ZGV2LCAicGh5LiVkLCBmbV9vdXQ6JWQsIGNhbGli OiVkIChjbGs6JWQsIGNvZWY6JWQpXG4iLAorCQlpbnN0LT5pbmRleCwgZm1fb3V0LCBjYWxpYl92 YWwsCisJCXhzcGh5LT5zcmNfcmVmX2NsaywgeHNwaHktPnNyY19jb2VmKTsKKworCS8qIHNldCBI UyBzbGV3IHJhdGUgKi8KKwl0bXAgPSByZWFkbChwYmFzZSArIFhTUF9VU0JQSFlBQ1I1KTsKKwl0 bXAgJj0gflAyQTVfUkdfSFNUWF9TUkNUUkw7CisJdG1wIHw9IFAyQTVfUkdfSFNUWF9TUkNUUkxf VkFMKGNhbGliX3ZhbCk7CisJd3JpdGVsKHRtcCwgcGJhc2UgKyBYU1BfVVNCUEhZQUNSNSk7CisK KwkvKiBkaXNhYmxlIFVTQiByaW5nIG9zY2lsbGF0b3IgKi8KKwl0bXAgPSByZWFkbChwYmFzZSAr IFhTUF9VU0JQSFlBQ1I1KTsKKwl0bXAgJj0gflAyQTVfUkdfSFNUWF9TUkNBTF9FTjsKKwl3cml0 ZWwodG1wLCBwYmFzZSArIFhTUF9VU0JQSFlBQ1I1KTsKK30KKworc3RhdGljIHZvaWQgdTJfcGh5 X2luc3RhbmNlX2luaXQoc3RydWN0IG10a194c3BoeSAqeHNwaHksCisJCQkJIHN0cnVjdCB4c3Bo eV9pbnN0YW5jZSAqaW5zdCkKK3sKKwl2b2lkIF9faW9tZW0gKnBiYXNlID0gaW5zdC0+cG9ydF9i YXNlOworCXUzMiB0bXA7CisKKwkvKiBEUC9ETSBCQzEuMSBwYXRoIERpc2FibGUgKi8KKwl0bXAg PSByZWFkbChwYmFzZSArIFhTUF9VU0JQSFlBQ1I2KTsKKwl0bXAgJj0gflAyQTZfUkdfQkMxMV9T V19FTjsKKwl3cml0ZWwodG1wLCBwYmFzZSArIFhTUF9VU0JQSFlBQ1I2KTsKKworCXRtcCA9IHJl YWRsKHBiYXNlICsgWFNQX1VTQlBIWUFDUjApOworCXRtcCB8PSBQMkEwX1JHX0lOVFJfRU47CisJ d3JpdGVsKHRtcCwgcGJhc2UgKyBYU1BfVVNCUEhZQUNSMCk7Cit9CisKK3N0YXRpYyB2b2lkIHUy X3BoeV9pbnN0YW5jZV9wb3dlcl9vbihzdHJ1Y3QgbXRrX3hzcGh5ICp4c3BoeSwKKwkJCQkgICAg IHN0cnVjdCB4c3BoeV9pbnN0YW5jZSAqaW5zdCkKK3sKKwl2b2lkIF9faW9tZW0gKnBiYXNlID0g aW5zdC0+cG9ydF9iYXNlOworCXUzMiBpbmRleCA9IGluc3QtPmluZGV4OworCXUzMiB0bXA7CisK Kwl0bXAgPSByZWFkbChwYmFzZSArIFhTUF9VU0JQSFlBQ1I2KTsKKwl0bXAgfD0gUDJBNl9SR19P VEdfVkJVU0NNUF9FTjsKKwl3cml0ZWwodG1wLCBwYmFzZSArIFhTUF9VU0JQSFlBQ1I2KTsKKwor CXRtcCA9IHJlYWRsKHBiYXNlICsgWFNQX1UyUEhZRFRNMSk7CisJdG1wIHw9IFAyRF9SR19WQlVT VkFMSUQgfCBQMkRfUkdfQVZBTElEOworCXRtcCAmPSB+UDJEX1JHX1NFU1NFTkQ7CisJd3JpdGVs KHRtcCwgcGJhc2UgKyBYU1BfVTJQSFlEVE0xKTsKKworCWRldl9kYmcoeHNwaHktPmRldiwgIiVz KCVkKVxuIiwgX19mdW5jX18sIGluZGV4KTsKK30KKworc3RhdGljIHZvaWQgdTJfcGh5X2luc3Rh bmNlX3Bvd2VyX29mZihzdHJ1Y3QgbXRrX3hzcGh5ICp4c3BoeSwKKwkJCQkgICAgICBzdHJ1Y3Qg eHNwaHlfaW5zdGFuY2UgKmluc3QpCit7CisJdm9pZCBfX2lvbWVtICpwYmFzZSA9IGluc3QtPnBv cnRfYmFzZTsKKwl1MzIgaW5kZXggPSBpbnN0LT5pbmRleDsKKwl1MzIgdG1wOworCisJdG1wID0g cmVhZGwocGJhc2UgKyBYU1BfVVNCUEhZQUNSNik7CisJdG1wICY9IH5QMkE2X1JHX09UR19WQlVT Q01QX0VOOworCXdyaXRlbCh0bXAsIHBiYXNlICsgWFNQX1VTQlBIWUFDUjYpOworCisJdG1wID0g cmVhZGwocGJhc2UgKyBYU1BfVTJQSFlEVE0xKTsKKwl0bXAgJj0gfihQMkRfUkdfVkJVU1ZBTElE IHwgUDJEX1JHX0FWQUxJRCk7CisJdG1wIHw9IFAyRF9SR19TRVNTRU5EOworCXdyaXRlbCh0bXAs IHBiYXNlICsgWFNQX1UyUEhZRFRNMSk7CisKKwlkZXZfZGJnKHhzcGh5LT5kZXYsICIlcyglZClc biIsIF9fZnVuY19fLCBpbmRleCk7Cit9CisKK3N0YXRpYyB2b2lkIHUyX3BoeV9pbnN0YW5jZV9z ZXRfbW9kZShzdHJ1Y3QgbXRrX3hzcGh5ICp4c3BoeSwKKwkJCQkgICAgIHN0cnVjdCB4c3BoeV9p bnN0YW5jZSAqaW5zdCwKKwkJCQkgICAgIGVudW0gcGh5X21vZGUgbW9kZSkKK3sKKwl1MzIgdG1w OworCisJdG1wID0gcmVhZGwoaW5zdC0+cG9ydF9iYXNlICsgWFNQX1UyUEhZRFRNMSk7CisJc3dp dGNoIChtb2RlKSB7CisJY2FzZSBQSFlfTU9ERV9VU0JfREVWSUNFOgorCQl0bXAgfD0gUDJEX0ZP UkNFX0lERElHIHwgUDJEX1JHX0lERElHOworCQlicmVhazsKKwljYXNlIFBIWV9NT0RFX1VTQl9I T1NUOgorCQl0bXAgfD0gUDJEX0ZPUkNFX0lERElHOworCQl0bXAgJj0gflAyRF9SR19JRERJRzsK KwkJYnJlYWs7CisJY2FzZSBQSFlfTU9ERV9VU0JfT1RHOgorCQl0bXAgJj0gfihQMkRfRk9SQ0Vf SURESUcgfCBQMkRfUkdfSURESUcpOworCQlicmVhazsKKwlkZWZhdWx0OgorCQlyZXR1cm47CisJ fQorCXdyaXRlbCh0bXAsIGluc3QtPnBvcnRfYmFzZSArIFhTUF9VMlBIWURUTTEpOworfQorCitz dGF0aWMgdm9pZCBwaHlfcGFyc2VfcHJvcGVydHkoc3RydWN0IG10a194c3BoeSAqeHNwaHksCisJ CQkJc3RydWN0IHhzcGh5X2luc3RhbmNlICppbnN0KQoreworCXN0cnVjdCBkZXZpY2UgKmRldiA9 ICZpbnN0LT5waHktPmRldjsKKworCXN3aXRjaCAoaW5zdC0+dHlwZSkgeworCWNhc2UgUEhZX1RZ UEVfVVNCMjoKKwkJZGV2aWNlX3Byb3BlcnR5X3JlYWRfdTMyKGRldiwgIm1lZGlhdGVrLGVmdXNl LWludHIiLAorCQkJCQkgJmluc3QtPmVmdXNlX2ludHIpOworCQlkZXZpY2VfcHJvcGVydHlfcmVh ZF91MzIoZGV2LCAibWVkaWF0ZWssZXllLXNyYyIsCisJCQkJCSAmaW5zdC0+ZXllX3NyYyk7CisJ CWRldmljZV9wcm9wZXJ0eV9yZWFkX3UzMihkZXYsICJtZWRpYXRlayxleWUtdnJ0IiwKKwkJCQkJ ICZpbnN0LT5leWVfdnJ0KTsKKwkJZGV2aWNlX3Byb3BlcnR5X3JlYWRfdTMyKGRldiwgIm1lZGlh dGVrLGV5ZS10ZXJtIiwKKwkJCQkJICZpbnN0LT5leWVfdGVybSk7CisJCWRldl9kYmcoZGV2LCAi aW50cjolZCwgc3JjOiVkLCB2cnQ6JWQsIHRlcm06JWRcbiIsCisJCQlpbnN0LT5lZnVzZV9pbnRy LCBpbnN0LT5leWVfc3JjLAorCQkJaW5zdC0+ZXllX3ZydCwgaW5zdC0+ZXllX3Rlcm0pOworCQli cmVhazsKKwljYXNlIFBIWV9UWVBFX1VTQjM6CisJCWRldmljZV9wcm9wZXJ0eV9yZWFkX3UzMihk ZXYsICJtZWRpYXRlayxlZnVzZS1pbnRyIiwKKwkJCQkJICZpbnN0LT5lZnVzZV9pbnRyKTsKKwkJ ZGV2aWNlX3Byb3BlcnR5X3JlYWRfdTMyKGRldiwgIm1lZGlhdGVrLGVmdXNlLXR4LWltcCIsCisJ CQkJCSAmaW5zdC0+ZWZ1c2VfdHhfaW1wKTsKKwkJZGV2aWNlX3Byb3BlcnR5X3JlYWRfdTMyKGRl diwgIm1lZGlhdGVrLGVmdXNlLXJ4LWltcCIsCisJCQkJCSAmaW5zdC0+ZWZ1c2VfcnhfaW1wKTsK KwkJZGV2X2RiZyhkZXYsICJpbnRyOiVkLCB0eC1pbXA6JWQsIHJ4LWltcDolZFxuIiwKKwkJCWlu c3QtPmVmdXNlX2ludHIsIGluc3QtPmVmdXNlX3R4X2ltcCwKKwkJCWluc3QtPmVmdXNlX3J4X2lt cCk7CisJCWJyZWFrOworCWRlZmF1bHQ6CisJCWRldl9lcnIoeHNwaHktPmRldiwgImluY29tcGF0 aWJsZSBwaHkgdHlwZVxuIik7CisJCXJldHVybjsKKwl9Cit9CisKK3N0YXRpYyB2b2lkIHUyX3Bo eV9wcm9wc19zZXQoc3RydWN0IG10a194c3BoeSAqeHNwaHksCisJCQkgICAgIHN0cnVjdCB4c3Bo eV9pbnN0YW5jZSAqaW5zdCkKK3sKKwl2b2lkIF9faW9tZW0gKnBiYXNlID0gaW5zdC0+cG9ydF9i YXNlOworCXUzMiB0bXA7CisKKwlpZiAoaW5zdC0+ZWZ1c2VfaW50cikgeworCQl0bXAgPSByZWFk bChwYmFzZSArIFhTUF9VU0JQSFlBQ1IxKTsKKwkJdG1wICY9IH5QMkExX1JHX0lOVFJfQ0FMOwor CQl0bXAgfD0gUDJBMV9SR19JTlRSX0NBTF9WQUwoaW5zdC0+ZWZ1c2VfaW50cik7CisJCXdyaXRl bCh0bXAsIHBiYXNlICsgWFNQX1VTQlBIWUFDUjEpOworCX0KKworCWlmIChpbnN0LT5leWVfc3Jj KSB7CisJCXRtcCA9IHJlYWRsKHBiYXNlICsgWFNQX1VTQlBIWUFDUjUpOworCQl0bXAgJj0gflAy QTVfUkdfSFNUWF9TUkNUUkw7CisJCXRtcCB8PSBQMkE1X1JHX0hTVFhfU1JDVFJMX1ZBTChpbnN0 LT5leWVfc3JjKTsKKwkJd3JpdGVsKHRtcCwgcGJhc2UgKyBYU1BfVVNCUEhZQUNSNSk7CisJfQor CisJaWYgKGluc3QtPmV5ZV92cnQpIHsKKwkJdG1wID0gcmVhZGwocGJhc2UgKyBYU1BfVVNCUEhZ QUNSMSk7CisJCXRtcCAmPSB+UDJBMV9SR19WUlRfU0VMOworCQl0bXAgfD0gUDJBMV9SR19WUlRf U0VMX1ZBTChpbnN0LT5leWVfdnJ0KTsKKwkJd3JpdGVsKHRtcCwgcGJhc2UgKyBYU1BfVVNCUEhZ QUNSMSk7CisJfQorCisJaWYgKGluc3QtPmV5ZV90ZXJtKSB7CisJCXRtcCA9IHJlYWRsKHBiYXNl ICsgWFNQX1VTQlBIWUFDUjEpOworCQl0bXAgJj0gflAyQTFfUkdfVEVSTV9TRUw7CisJCXRtcCB8 PSBQMkExX1JHX1RFUk1fU0VMX1ZBTChpbnN0LT5leWVfdGVybSk7CisJCXdyaXRlbCh0bXAsIHBi YXNlICsgWFNQX1VTQlBIWUFDUjEpOworCX0KK30KKworc3RhdGljIHZvaWQgdTNfcGh5X3Byb3Bz X3NldChzdHJ1Y3QgbXRrX3hzcGh5ICp4c3BoeSwKKwkJCSAgICAgc3RydWN0IHhzcGh5X2luc3Rh bmNlICppbnN0KQoreworCXZvaWQgX19pb21lbSAqcGJhc2UgPSBpbnN0LT5wb3J0X2Jhc2U7CisJ dTMyIHRtcDsKKworCWlmIChpbnN0LT5lZnVzZV9pbnRyKSB7CisJCXRtcCA9IHJlYWRsKHhzcGh5 LT5nbGJfYmFzZSArIFNTUFhUUF9QSFlBX0dMQl8wMCk7CisJCXRtcCAmPSB+UkdfWFRQX0dMQl9C SUFTX0lOVFJfQ1RSTDsKKwkJdG1wIHw9IFJHX1hUUF9HTEJfQklBU19JTlRSX0NUUkxfVkFMKGlu c3QtPmVmdXNlX2ludHIpOworCQl3cml0ZWwodG1wLCB4c3BoeS0+Z2xiX2Jhc2UgKyBTU1BYVFBf UEhZQV9HTEJfMDApOworCX0KKworCWlmIChpbnN0LT5lZnVzZV90eF9pbXApIHsKKwkJdG1wID0g cmVhZGwocGJhc2UgKyBTU1BYVFBfUEhZQV9MTl8wNCk7CisJCXRtcCAmPSB+UkdfWFRQX0xOMF9U WF9JTVBTRUw7CisJCXRtcCB8PSBSR19YVFBfTE4wX1RYX0lNUFNFTF9WQUwoaW5zdC0+ZWZ1c2Vf dHhfaW1wKTsKKwkJd3JpdGVsKHRtcCwgcGJhc2UgKyBTU1BYVFBfUEhZQV9MTl8wNCk7CisJfQor CisJaWYgKGluc3QtPmVmdXNlX3J4X2ltcCkgeworCQl0bXAgPSByZWFkbChwYmFzZSArIFNTUFhU UF9QSFlBX0xOXzE0KTsKKwkJdG1wICY9IH5SR19YVFBfTE4wX1JYX0lNUFNFTDsKKwkJdG1wIHw9 IFJHX1hUUF9MTjBfUlhfSU1QU0VMX1ZBTChpbnN0LT5lZnVzZV9yeF9pbXApOworCQl3cml0ZWwo dG1wLCBwYmFzZSArIFNTUFhUUF9QSFlBX0xOXzE0KTsKKwl9Cit9CisKK3N0YXRpYyBpbnQgbXRr X3BoeV9pbml0KHN0cnVjdCBwaHkgKnBoeSkKK3sKKwlzdHJ1Y3QgeHNwaHlfaW5zdGFuY2UgKmlu c3QgPSBwaHlfZ2V0X2RydmRhdGEocGh5KTsKKwlzdHJ1Y3QgbXRrX3hzcGh5ICp4c3BoeSA9IGRl dl9nZXRfZHJ2ZGF0YShwaHktPmRldi5wYXJlbnQpOworCWludCByZXQ7CisKKwlyZXQgPSBjbGtf cHJlcGFyZV9lbmFibGUoaW5zdC0+cmVmX2Nsayk7CisJaWYgKHJldCkgeworCQlkZXZfZXJyKHhz cGh5LT5kZXYsICJmYWlsZWQgdG8gZW5hYmxlIHJlZl9jbGtcbiIpOworCQlyZXR1cm4gcmV0Owor CX0KKworCXN3aXRjaCAoaW5zdC0+dHlwZSkgeworCWNhc2UgUEhZX1RZUEVfVVNCMjoKKwkJdTJf cGh5X2luc3RhbmNlX2luaXQoeHNwaHksIGluc3QpOworCQl1Ml9waHlfcHJvcHNfc2V0KHhzcGh5 LCBpbnN0KTsKKwkJYnJlYWs7CisJY2FzZSBQSFlfVFlQRV9VU0IzOgorCQl1M19waHlfcHJvcHNf c2V0KHhzcGh5LCBpbnN0KTsKKwkJYnJlYWs7CisJZGVmYXVsdDoKKwkJZGV2X2Vycih4c3BoeS0+ ZGV2LCAiaW5jb21wYXRpYmxlIHBoeSB0eXBlXG4iKTsKKwkJY2xrX2Rpc2FibGVfdW5wcmVwYXJl KGluc3QtPnJlZl9jbGspOworCQlyZXR1cm4gLUVJTlZBTDsKKwl9CisKKwlyZXR1cm4gMDsKK30K Kworc3RhdGljIGludCBtdGtfcGh5X3Bvd2VyX29uKHN0cnVjdCBwaHkgKnBoeSkKK3sKKwlzdHJ1 Y3QgeHNwaHlfaW5zdGFuY2UgKmluc3QgPSBwaHlfZ2V0X2RydmRhdGEocGh5KTsKKwlzdHJ1Y3Qg bXRrX3hzcGh5ICp4c3BoeSA9IGRldl9nZXRfZHJ2ZGF0YShwaHktPmRldi5wYXJlbnQpOworCisJ aWYgKGluc3QtPnR5cGUgPT0gUEhZX1RZUEVfVVNCMikgeworCQl1Ml9waHlfaW5zdGFuY2VfcG93 ZXJfb24oeHNwaHksIGluc3QpOworCQl1Ml9waHlfc2xld19yYXRlX2NhbGlicmF0ZSh4c3BoeSwg aW5zdCk7CisJfQorCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyBpbnQgbXRrX3BoeV9wb3dlcl9v ZmYoc3RydWN0IHBoeSAqcGh5KQoreworCXN0cnVjdCB4c3BoeV9pbnN0YW5jZSAqaW5zdCA9IHBo eV9nZXRfZHJ2ZGF0YShwaHkpOworCXN0cnVjdCBtdGtfeHNwaHkgKnhzcGh5ID0gZGV2X2dldF9k cnZkYXRhKHBoeS0+ZGV2LnBhcmVudCk7CisKKwlpZiAoaW5zdC0+dHlwZSA9PSBQSFlfVFlQRV9V U0IyKQorCQl1Ml9waHlfaW5zdGFuY2VfcG93ZXJfb2ZmKHhzcGh5LCBpbnN0KTsKKworCXJldHVy biAwOworfQorCitzdGF0aWMgaW50IG10a19waHlfZXhpdChzdHJ1Y3QgcGh5ICpwaHkpCit7CisJ c3RydWN0IHhzcGh5X2luc3RhbmNlICppbnN0ID0gcGh5X2dldF9kcnZkYXRhKHBoeSk7CisKKwlj bGtfZGlzYWJsZV91bnByZXBhcmUoaW5zdC0+cmVmX2Nsayk7CisJcmV0dXJuIDA7Cit9CisKK3N0 YXRpYyBpbnQgbXRrX3BoeV9zZXRfbW9kZShzdHJ1Y3QgcGh5ICpwaHksIGVudW0gcGh5X21vZGUg bW9kZSkKK3sKKwlzdHJ1Y3QgeHNwaHlfaW5zdGFuY2UgKmluc3QgPSBwaHlfZ2V0X2RydmRhdGEo cGh5KTsKKwlzdHJ1Y3QgbXRrX3hzcGh5ICp4c3BoeSA9IGRldl9nZXRfZHJ2ZGF0YShwaHktPmRl di5wYXJlbnQpOworCisJaWYgKGluc3QtPnR5cGUgPT0gUEhZX1RZUEVfVVNCMikKKwkJdTJfcGh5 X2luc3RhbmNlX3NldF9tb2RlKHhzcGh5LCBpbnN0LCBtb2RlKTsKKworCXJldHVybiAwOworfQor CitzdGF0aWMgc3RydWN0IHBoeSAqbXRrX3BoeV94bGF0ZShzdHJ1Y3QgZGV2aWNlICpkZXYsCisJ CQkJIHN0cnVjdCBvZl9waGFuZGxlX2FyZ3MgKmFyZ3MpCit7CisJc3RydWN0IG10a194c3BoeSAq eHNwaHkgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsKKwlzdHJ1Y3QgeHNwaHlfaW5zdGFuY2UgKmlu c3QgPSBOVUxMOworCXN0cnVjdCBkZXZpY2Vfbm9kZSAqcGh5X25wID0gYXJncy0+bnA7CisJaW50 IGluZGV4OworCisJaWYgKGFyZ3MtPmFyZ3NfY291bnQgIT0gMSkgeworCQlkZXZfZXJyKGRldiwg ImludmFsaWQgbnVtYmVyIG9mIGNlbGxzIGluICdwaHknIHByb3BlcnR5XG4iKTsKKwkJcmV0dXJu IEVSUl9QVFIoLUVJTlZBTCk7CisJfQorCisJZm9yIChpbmRleCA9IDA7IGluZGV4IDwgeHNwaHkt Pm5waHlzOyBpbmRleCsrKQorCQlpZiAocGh5X25wID09IHhzcGh5LT5waHlzW2luZGV4XS0+cGh5 LT5kZXYub2Zfbm9kZSkgeworCQkJaW5zdCA9IHhzcGh5LT5waHlzW2luZGV4XTsKKwkJCWJyZWFr OworCQl9CisKKwlpZiAoIWluc3QpIHsKKwkJZGV2X2VycihkZXYsICJmYWlsZWQgdG8gZmluZCBh cHByb3ByaWF0ZSBwaHlcbiIpOworCQlyZXR1cm4gRVJSX1BUUigtRUlOVkFMKTsKKwl9CisKKwlp bnN0LT50eXBlID0gYXJncy0+YXJnc1swXTsKKwlpZiAoIShpbnN0LT50eXBlID09IFBIWV9UWVBF X1VTQjIgfHwKKwkgICAgICBpbnN0LT50eXBlID09IFBIWV9UWVBFX1VTQjMpKSB7CisJCWRldl9l cnIoZGV2LCAidW5zdXBwb3J0ZWQgcGh5IHR5cGU6ICVkXG4iLCBpbnN0LT50eXBlKTsKKwkJcmV0 dXJuIEVSUl9QVFIoLUVJTlZBTCk7CisJfQorCisJcGh5X3BhcnNlX3Byb3BlcnR5KHhzcGh5LCBp bnN0KTsKKworCXJldHVybiBpbnN0LT5waHk7Cit9CisKK3N0YXRpYyBjb25zdCBzdHJ1Y3QgcGh5 X29wcyBtdGtfeHNwaHlfb3BzID0geworCS5pbml0CQk9IG10a19waHlfaW5pdCwKKwkuZXhpdAkJ PSBtdGtfcGh5X2V4aXQsCisJLnBvd2VyX29uCT0gbXRrX3BoeV9wb3dlcl9vbiwKKwkucG93ZXJf b2ZmCT0gbXRrX3BoeV9wb3dlcl9vZmYsCisJLnNldF9tb2RlCT0gbXRrX3BoeV9zZXRfbW9kZSwK Kwkub3duZXIJCT0gVEhJU19NT0RVTEUsCit9OworCitzdGF0aWMgY29uc3Qgc3RydWN0IG9mX2Rl dmljZV9pZCBtdGtfeHNwaHlfaWRfdGFibGVbXSA9IHsKKwl7IC5jb21wYXRpYmxlID0gIm1lZGlh dGVrLHhzcGh5IiwgfSwKKwl7IH0sCit9OworTU9EVUxFX0RFVklDRV9UQUJMRShvZiwgbXRrX3hz cGh5X2lkX3RhYmxlKTsKKworc3RhdGljIGludCBtdGtfeHNwaHlfcHJvYmUoc3RydWN0IHBsYXRm b3JtX2RldmljZSAqcGRldikKK3sKKwlzdHJ1Y3QgZGV2aWNlICpkZXYgPSAmcGRldi0+ZGV2Owor CXN0cnVjdCBkZXZpY2Vfbm9kZSAqbnAgPSBkZXYtPm9mX25vZGU7CisJc3RydWN0IGRldmljZV9u b2RlICpjaGlsZF9ucDsKKwlzdHJ1Y3QgcGh5X3Byb3ZpZGVyICpwcm92aWRlcjsKKwlzdHJ1Y3Qg cmVzb3VyY2UgKmdsYl9yZXM7CisJc3RydWN0IG10a194c3BoeSAqeHNwaHk7CisJc3RydWN0IHJl c291cmNlIHJlczsKKwlpbnQgcG9ydCwgcmV0dmFsOworCisJeHNwaHkgPSBkZXZtX2t6YWxsb2Mo ZGV2LCBzaXplb2YoKnhzcGh5KSwgR0ZQX0tFUk5FTCk7CisJaWYgKCF4c3BoeSkKKwkJcmV0dXJu IC1FTk9NRU07CisKKwl4c3BoeS0+bnBoeXMgPSBvZl9nZXRfY2hpbGRfY291bnQobnApOworCXhz cGh5LT5waHlzID0gZGV2bV9rY2FsbG9jKGRldiwgeHNwaHktPm5waHlzLAorCQkJCSAgICAgICBz aXplb2YoKnhzcGh5LT5waHlzKSwgR0ZQX0tFUk5FTCk7CisJaWYgKCF4c3BoeS0+cGh5cykKKwkJ cmV0dXJuIC1FTk9NRU07CisKKwl4c3BoeS0+ZGV2ID0gZGV2OworCXBsYXRmb3JtX3NldF9kcnZk YXRhKHBkZXYsIHhzcGh5KTsKKworCWdsYl9yZXMgPSBwbGF0Zm9ybV9nZXRfcmVzb3VyY2UocGRl diwgSU9SRVNPVVJDRV9NRU0sIDApOworCS8qIG9wdGlvbmFsLCBtYXkgbm90IGV4aXN0IGlmIG5v IHUzIHBoeXMgKi8KKwlpZiAoZ2xiX3JlcykgeworCQkvKiBnZXQgYmFua3Mgc2hhcmVkIGJ5IG11 bHRpcGxlIHUzIHBoeXMgKi8KKwkJeHNwaHktPmdsYl9iYXNlID0gZGV2bV9pb3JlbWFwX3Jlc291 cmNlKGRldiwgZ2xiX3Jlcyk7CisJCWlmIChJU19FUlIoeHNwaHktPmdsYl9iYXNlKSkgeworCQkJ ZGV2X2VycihkZXYsICJmYWlsZWQgdG8gcmVtYXAgZ2xiIHJlZ3NcbiIpOworCQkJcmV0dXJuIFBU Ul9FUlIoeHNwaHktPmdsYl9iYXNlKTsKKwkJfQorCX0KKworCXhzcGh5LT5zcmNfcmVmX2NsayA9 IFhTUF9SRUZfQ0xLOworCXhzcGh5LT5zcmNfY29lZiA9IFhTUF9TTEVXX1JBVEVfQ09FRjsKKwkv KiB1cGRhdGUgcGFyYW1ldGVycyBvZiBzbGV3IHJhdGUgY2FsaWJyYXRlIGlmIGV4aXN0ICovCisJ ZGV2aWNlX3Byb3BlcnR5X3JlYWRfdTMyKGRldiwgIm1lZGlhdGVrLHNyYy1yZWYtY2xrLW1oeiIs CisJCQkJICZ4c3BoeS0+c3JjX3JlZl9jbGspOworCWRldmljZV9wcm9wZXJ0eV9yZWFkX3UzMihk ZXYsICJtZWRpYXRlayxzcmMtY29lZiIsICZ4c3BoeS0+c3JjX2NvZWYpOworCisJcG9ydCA9IDA7 CisJZm9yX2VhY2hfY2hpbGRfb2Zfbm9kZShucCwgY2hpbGRfbnApIHsKKwkJc3RydWN0IHhzcGh5 X2luc3RhbmNlICppbnN0OworCQlzdHJ1Y3QgcGh5ICpwaHk7CisKKwkJaW5zdCA9IGRldm1fa3ph bGxvYyhkZXYsIHNpemVvZigqaW5zdCksIEdGUF9LRVJORUwpOworCQlpZiAoIWluc3QpIHsKKwkJ CXJldHZhbCA9IC1FTk9NRU07CisJCQlnb3RvIHB1dF9jaGlsZDsKKwkJfQorCisJCXhzcGh5LT5w aHlzW3BvcnRdID0gaW5zdDsKKworCQlwaHkgPSBkZXZtX3BoeV9jcmVhdGUoZGV2LCBjaGlsZF9u cCwgJm10a194c3BoeV9vcHMpOworCQlpZiAoSVNfRVJSKHBoeSkpIHsKKwkJCWRldl9lcnIoZGV2 LCAiZmFpbGVkIHRvIGNyZWF0ZSBwaHlcbiIpOworCQkJcmV0dmFsID0gUFRSX0VSUihwaHkpOwor CQkJZ290byBwdXRfY2hpbGQ7CisJCX0KKworCQlyZXR2YWwgPSBvZl9hZGRyZXNzX3RvX3Jlc291 cmNlKGNoaWxkX25wLCAwLCAmcmVzKTsKKwkJaWYgKHJldHZhbCkgeworCQkJZGV2X2VycihkZXYs ICJmYWlsZWQgdG8gZ2V0IGFkZHJlc3MgcmVzb3VyY2UoaWQtJWQpXG4iLAorCQkJCXBvcnQpOwor CQkJZ290byBwdXRfY2hpbGQ7CisJCX0KKworCQlpbnN0LT5wb3J0X2Jhc2UgPSBkZXZtX2lvcmVt YXBfcmVzb3VyY2UoJnBoeS0+ZGV2LCAmcmVzKTsKKwkJaWYgKElTX0VSUihpbnN0LT5wb3J0X2Jh c2UpKSB7CisJCQlkZXZfZXJyKGRldiwgImZhaWxlZCB0byByZW1hcCBwaHkgcmVnc1xuIik7CisJ CQlyZXR2YWwgPSBQVFJfRVJSKGluc3QtPnBvcnRfYmFzZSk7CisJCQlnb3RvIHB1dF9jaGlsZDsK KwkJfQorCisJCWluc3QtPnBoeSA9IHBoeTsKKwkJaW5zdC0+aW5kZXggPSBwb3J0OworCQlwaHlf c2V0X2RydmRhdGEocGh5LCBpbnN0KTsKKwkJcG9ydCsrOworCisJCWluc3QtPnJlZl9jbGsgPSBk ZXZtX2Nsa19nZXQoJnBoeS0+ZGV2LCAicmVmIik7CisJCWlmIChJU19FUlIoaW5zdC0+cmVmX2Ns aykpIHsKKwkJCWRldl9lcnIoZGV2LCAiZmFpbGVkIHRvIGdldCByZWZfY2xrKGlkLSVkKVxuIiwg cG9ydCk7CisJCQlyZXR2YWwgPSBQVFJfRVJSKGluc3QtPnJlZl9jbGspOworCQkJZ290byBwdXRf Y2hpbGQ7CisJCX0KKwl9CisKKwlwcm92aWRlciA9IGRldm1fb2ZfcGh5X3Byb3ZpZGVyX3JlZ2lz dGVyKGRldiwgbXRrX3BoeV94bGF0ZSk7CisJcmV0dXJuIFBUUl9FUlJfT1JfWkVSTyhwcm92aWRl cik7CisKK3B1dF9jaGlsZDoKKwlvZl9ub2RlX3B1dChjaGlsZF9ucCk7CisJcmV0dXJuIHJldHZh bDsKK30KKworc3RhdGljIHN0cnVjdCBwbGF0Zm9ybV9kcml2ZXIgbXRrX3hzcGh5X2RyaXZlciA9 IHsKKwkucHJvYmUJCT0gbXRrX3hzcGh5X3Byb2JlLAorCS5kcml2ZXIJCT0geworCQkubmFtZQk9 ICJtdGsteHNwaHkiLAorCQkub2ZfbWF0Y2hfdGFibGUgPSBtdGtfeHNwaHlfaWRfdGFibGUsCisJ fSwKK307CisKK21vZHVsZV9wbGF0Zm9ybV9kcml2ZXIobXRrX3hzcGh5X2RyaXZlcik7CisKK01P RFVMRV9BVVRIT1IoIkNodW5mZW5nIFl1biA8Y2h1bmZlbmcueXVuQG1lZGlhdGVrLmNvbT4iKTsK K01PRFVMRV9ERVNDUklQVElPTigiTWVkaWFUZWsgVVNCIFhTLVBIWSBkcml2ZXIiKTsKK01PRFVM RV9MSUNFTlNFKCJHUEwgdjIiKTsK From mboxrd@z Thu Jan 1 00:00:00 1970 From: chunfeng.yun@mediatek.com (Chunfeng Yun) Date: Thu, 10 May 2018 14:10:29 +0800 Subject: [PATCH v3 2/2] phy: mediatek: add XS-PHY driver In-Reply-To: <1525932629-10603-1-git-send-email-chunfeng.yun@mediatek.com> References: <1525932629-10603-1-git-send-email-chunfeng.yun@mediatek.com> Message-ID: <1525932629-10603-3-git-send-email-chunfeng.yun@mediatek.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org Support XS-PHY for MediaTek SoCs with USB3.1 GEN2 controller Signed-off-by: Chunfeng Yun --- drivers/phy/mediatek/Kconfig | 9 + drivers/phy/mediatek/Makefile | 1 + drivers/phy/mediatek/phy-mtk-xsphy.c | 600 ++++++++++++++++++++++++++++++++++ 3 files changed, 610 insertions(+) create mode 100644 drivers/phy/mediatek/phy-mtk-xsphy.c diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index 88ab4e2..8857d00 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -12,3 +12,12 @@ config PHY_MTK_TPHY different banks layout, the T-PHY with shared banks between multi-ports is first version, otherwise is second veriosn, so you can easily distinguish them by banks layout. + +config PHY_MTK_XSPHY + tristate "MediaTek XS-PHY Driver" + depends on ARCH_MEDIATEK && OF + select GENERIC_PHY + 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. diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile index 1bdab14..ee49edc 100644 --- a/drivers/phy/mediatek/Makefile +++ b/drivers/phy/mediatek/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o +obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o diff --git a/drivers/phy/mediatek/phy-mtk-xsphy.c b/drivers/phy/mediatek/phy-mtk-xsphy.c new file mode 100644 index 0000000..020cd02 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-xsphy.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek USB3.1 gen2 xsphy Driver + * + * Copyright (c) 2018 MediaTek Inc. + * Author: Chunfeng Yun + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x)) + +#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_INTR_CAL_VAL(x) ((0x1f & (x)) << 19) +#define P2A1_RG_VRT_SEL GENMASK(14, 12) +#define P2A1_RG_VRT_SEL_VAL(x) ((0x7 & (x)) << 12) +#define P2A1_RG_TERM_SEL GENMASK(10, 8) +#define P2A1_RG_TERM_SEL_VAL(x) ((0x7 & (x)) << 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 P2A5_RG_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 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 RG_XTP_GLB_BIAS_INTR_CTRL_VAL(x) ((0x3f & (x)) << 16) + +#define SSPXTP_PHYA_LN_04 ((SSPXTP_SIFSLV_PHYA_LN) + 0x04) +#define RG_XTP_LN0_TX_IMPSEL GENMASK(4, 0) +#define RG_XTP_LN0_TX_IMPSEL_VAL(x) (0x1f & (x)) + +#define SSPXTP_PHYA_LN_14 ((SSPXTP_SIFSLV_PHYA_LN) + 0x014) +#define RG_XTP_LN0_RX_IMPSEL GENMASK(4, 0) +#define RG_XTP_LN0_RX_IMPSEL_VAL(x) (0x1f & (x)) + +#define XSP_REF_CLK 26 /* MHZ */ +#define XSP_SLEW_RATE_COEF 17 +#define XSP_SR_COEF_DIVISOR 1000 +#define XSP_FM_DET_CYCLE_CNT 1024 + +struct xsphy_instance { + struct phy *phy; + void __iomem *port_base; + struct clk *ref_clk; /* reference clock of anolog phy */ + u32 index; + u32 type; + /* only for HQA test */ + int efuse_intr; + int efuse_tx_imp; + int efuse_rx_imp; + /* u2 eye diagram */ + int eye_src; + int eye_vrt; + int eye_term; +}; + +struct mtk_xsphy { + struct device *dev; + void __iomem *glb_base; /* only shared u3 sif */ + struct xsphy_instance **phys; + int nphys; + int src_ref_clk; /* MHZ, reference clock for slew rate calibrate */ + int src_coef; /* coefficient for slew rate calibrate */ +}; + +static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + int calib_val; + int fm_out; + u32 tmp; + + /* use force value */ + if (inst->eye_src) + return; + + /* enable USB ring oscillator */ + tmp = readl(pbase + XSP_USBPHYACR5); + tmp |= P2A5_RG_HSTX_SRCAL_EN; + writel(tmp, pbase + XSP_USBPHYACR5); + udelay(1); /* wait clock stable */ + + /* enable free run clock */ + tmp = readl(pbase + XSP_U2FREQ_FMMONR1); + tmp |= P2F_RG_FRCK_EN; + writel(tmp, pbase + XSP_U2FREQ_FMMONR1); + + /* set cycle count as 1024 */ + tmp = readl(pbase + XSP_U2FREQ_FMCR0); + tmp &= ~(P2F_RG_CYCLECNT); + tmp |= P2F_RG_CYCLECNT_VAL(XSP_FM_DET_CYCLE_CNT); + writel(tmp, pbase + XSP_U2FREQ_FMCR0); + + /* enable frequency meter */ + tmp = readl(pbase + XSP_U2FREQ_FMCR0); + tmp |= P2F_RG_FREQDET_EN; + writel(tmp, pbase + XSP_U2FREQ_FMCR0); + + /* ignore return value */ + readl_poll_timeout(pbase + XSP_U2FREQ_FMMONR1, tmp, + (tmp & P2F_USB_FM_VALID), 10, 200); + + fm_out = readl(pbase + XSP_U2FREQ_MMONR0); + + /* disable frequency meter */ + tmp = readl(pbase + XSP_U2FREQ_FMCR0); + tmp &= ~P2F_RG_FREQDET_EN; + writel(tmp, pbase + XSP_U2FREQ_FMCR0); + + /* disable free run clock */ + tmp = readl(pbase + XSP_U2FREQ_FMMONR1); + tmp &= ~P2F_RG_FRCK_EN; + writel(tmp, pbase + XSP_U2FREQ_FMMONR1); + + if (fm_out) { + /* (1024 / FM_OUT) x reference clock frequency x coefficient */ + tmp = xsphy->src_ref_clk * 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.%d, fm_out:%d, calib:%d (clk:%d, coef:%d)\n", + inst->index, fm_out, calib_val, + xsphy->src_ref_clk, xsphy->src_coef); + + /* set HS slew rate */ + tmp = readl(pbase + XSP_USBPHYACR5); + tmp &= ~P2A5_RG_HSTX_SRCTRL; + tmp |= P2A5_RG_HSTX_SRCTRL_VAL(calib_val); + writel(tmp, pbase + XSP_USBPHYACR5); + + /* disable USB ring oscillator */ + tmp = readl(pbase + XSP_USBPHYACR5); + tmp &= ~P2A5_RG_HSTX_SRCAL_EN; + writel(tmp, pbase + XSP_USBPHYACR5); +} + +static void u2_phy_instance_init(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 tmp; + + /* DP/DM BC1.1 path Disable */ + tmp = readl(pbase + XSP_USBPHYACR6); + tmp &= ~P2A6_RG_BC11_SW_EN; + writel(tmp, pbase + XSP_USBPHYACR6); + + tmp = readl(pbase + XSP_USBPHYACR0); + tmp |= P2A0_RG_INTR_EN; + writel(tmp, pbase + XSP_USBPHYACR0); +} + +static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 index = inst->index; + u32 tmp; + + tmp = readl(pbase + XSP_USBPHYACR6); + tmp |= P2A6_RG_OTG_VBUSCMP_EN; + writel(tmp, pbase + XSP_USBPHYACR6); + + tmp = readl(pbase + XSP_U2PHYDTM1); + tmp |= P2D_RG_VBUSVALID | P2D_RG_AVALID; + tmp &= ~P2D_RG_SESSEND; + writel(tmp, pbase + XSP_U2PHYDTM1); + + dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); +} + +static void u2_phy_instance_power_off(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 index = inst->index; + u32 tmp; + + tmp = readl(pbase + XSP_USBPHYACR6); + tmp &= ~P2A6_RG_OTG_VBUSCMP_EN; + writel(tmp, pbase + XSP_USBPHYACR6); + + tmp = readl(pbase + XSP_U2PHYDTM1); + tmp &= ~(P2D_RG_VBUSVALID | P2D_RG_AVALID); + tmp |= P2D_RG_SESSEND; + writel(tmp, pbase + XSP_U2PHYDTM1); + + dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); +} + +static void u2_phy_instance_set_mode(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst, + enum phy_mode mode) +{ + u32 tmp; + + tmp = readl(inst->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, inst->port_base + XSP_U2PHYDTM1); +} + +static void phy_parse_property(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + struct device *dev = &inst->phy->dev; + + switch (inst->type) { + case PHY_TYPE_USB2: + device_property_read_u32(dev, "mediatek,efuse-intr", + &inst->efuse_intr); + device_property_read_u32(dev, "mediatek,eye-src", + &inst->eye_src); + device_property_read_u32(dev, "mediatek,eye-vrt", + &inst->eye_vrt); + device_property_read_u32(dev, "mediatek,eye-term", + &inst->eye_term); + dev_dbg(dev, "intr:%d, src:%d, vrt:%d, term:%d\n", + inst->efuse_intr, inst->eye_src, + inst->eye_vrt, inst->eye_term); + break; + case PHY_TYPE_USB3: + device_property_read_u32(dev, "mediatek,efuse-intr", + &inst->efuse_intr); + device_property_read_u32(dev, "mediatek,efuse-tx-imp", + &inst->efuse_tx_imp); + device_property_read_u32(dev, "mediatek,efuse-rx-imp", + &inst->efuse_rx_imp); + dev_dbg(dev, "intr:%d, tx-imp:%d, rx-imp:%d\n", + inst->efuse_intr, inst->efuse_tx_imp, + inst->efuse_rx_imp); + break; + default: + dev_err(xsphy->dev, "incompatible phy type\n"); + return; + } +} + +static void u2_phy_props_set(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 tmp; + + if (inst->efuse_intr) { + tmp = readl(pbase + XSP_USBPHYACR1); + tmp &= ~P2A1_RG_INTR_CAL; + tmp |= P2A1_RG_INTR_CAL_VAL(inst->efuse_intr); + writel(tmp, pbase + XSP_USBPHYACR1); + } + + if (inst->eye_src) { + tmp = readl(pbase + XSP_USBPHYACR5); + tmp &= ~P2A5_RG_HSTX_SRCTRL; + tmp |= P2A5_RG_HSTX_SRCTRL_VAL(inst->eye_src); + writel(tmp, pbase + XSP_USBPHYACR5); + } + + if (inst->eye_vrt) { + tmp = readl(pbase + XSP_USBPHYACR1); + tmp &= ~P2A1_RG_VRT_SEL; + tmp |= P2A1_RG_VRT_SEL_VAL(inst->eye_vrt); + writel(tmp, pbase + XSP_USBPHYACR1); + } + + if (inst->eye_term) { + tmp = readl(pbase + XSP_USBPHYACR1); + tmp &= ~P2A1_RG_TERM_SEL; + tmp |= P2A1_RG_TERM_SEL_VAL(inst->eye_term); + writel(tmp, pbase + XSP_USBPHYACR1); + } +} + +static void u3_phy_props_set(struct mtk_xsphy *xsphy, + struct xsphy_instance *inst) +{ + void __iomem *pbase = inst->port_base; + u32 tmp; + + if (inst->efuse_intr) { + tmp = readl(xsphy->glb_base + SSPXTP_PHYA_GLB_00); + tmp &= ~RG_XTP_GLB_BIAS_INTR_CTRL; + tmp |= RG_XTP_GLB_BIAS_INTR_CTRL_VAL(inst->efuse_intr); + writel(tmp, xsphy->glb_base + SSPXTP_PHYA_GLB_00); + } + + if (inst->efuse_tx_imp) { + tmp = readl(pbase + SSPXTP_PHYA_LN_04); + tmp &= ~RG_XTP_LN0_TX_IMPSEL; + tmp |= RG_XTP_LN0_TX_IMPSEL_VAL(inst->efuse_tx_imp); + writel(tmp, pbase + SSPXTP_PHYA_LN_04); + } + + if (inst->efuse_rx_imp) { + tmp = readl(pbase + SSPXTP_PHYA_LN_14); + tmp &= ~RG_XTP_LN0_RX_IMPSEL; + tmp |= RG_XTP_LN0_RX_IMPSEL_VAL(inst->efuse_rx_imp); + writel(tmp, pbase + SSPXTP_PHYA_LN_14); + } +} + +static int mtk_phy_init(struct phy *phy) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); + int ret; + + ret = clk_prepare_enable(inst->ref_clk); + if (ret) { + dev_err(xsphy->dev, "failed to enable ref_clk\n"); + return ret; + } + + switch (inst->type) { + case PHY_TYPE_USB2: + u2_phy_instance_init(xsphy, inst); + u2_phy_props_set(xsphy, inst); + break; + case PHY_TYPE_USB3: + u3_phy_props_set(xsphy, inst); + break; + default: + dev_err(xsphy->dev, "incompatible phy type\n"); + clk_disable_unprepare(inst->ref_clk); + return -EINVAL; + } + + return 0; +} + +static int mtk_phy_power_on(struct phy *phy) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); + + if (inst->type == PHY_TYPE_USB2) { + u2_phy_instance_power_on(xsphy, inst); + u2_phy_slew_rate_calibrate(xsphy, inst); + } + + return 0; +} + +static int mtk_phy_power_off(struct phy *phy) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); + + if (inst->type == PHY_TYPE_USB2) + u2_phy_instance_power_off(xsphy, inst); + + return 0; +} + +static int mtk_phy_exit(struct phy *phy) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + + clk_disable_unprepare(inst->ref_clk); + return 0; +} + +static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode) +{ + struct xsphy_instance *inst = phy_get_drvdata(phy); + struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent); + + if (inst->type == PHY_TYPE_USB2) + u2_phy_instance_set_mode(xsphy, inst, mode); + + return 0; +} + +static struct phy *mtk_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct mtk_xsphy *xsphy = dev_get_drvdata(dev); + struct xsphy_instance *inst = NULL; + struct device_node *phy_np = args->np; + int index; + + if (args->args_count != 1) { + dev_err(dev, "invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + for (index = 0; index < xsphy->nphys; index++) + if (phy_np == xsphy->phys[index]->phy->dev.of_node) { + inst = xsphy->phys[index]; + break; + } + + if (!inst) { + dev_err(dev, "failed to find appropriate phy\n"); + return ERR_PTR(-EINVAL); + } + + inst->type = args->args[0]; + if (!(inst->type == PHY_TYPE_USB2 || + inst->type == PHY_TYPE_USB3)) { + dev_err(dev, "unsupported phy type: %d\n", inst->type); + return ERR_PTR(-EINVAL); + } + + phy_parse_property(xsphy, inst); + + return inst->phy; +} + +static const struct phy_ops mtk_xsphy_ops = { + .init = mtk_phy_init, + .exit = mtk_phy_exit, + .power_on = mtk_phy_power_on, + .power_off = mtk_phy_power_off, + .set_mode = mtk_phy_set_mode, + .owner = THIS_MODULE, +}; + +static const struct of_device_id mtk_xsphy_id_table[] = { + { .compatible = "mediatek,xsphy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, mtk_xsphy_id_table); + +static int mtk_xsphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child_np; + struct phy_provider *provider; + struct resource *glb_res; + struct mtk_xsphy *xsphy; + struct resource res; + int port, retval; + + xsphy = devm_kzalloc(dev, sizeof(*xsphy), GFP_KERNEL); + if (!xsphy) + return -ENOMEM; + + xsphy->nphys = of_get_child_count(np); + xsphy->phys = devm_kcalloc(dev, xsphy->nphys, + sizeof(*xsphy->phys), GFP_KERNEL); + if (!xsphy->phys) + return -ENOMEM; + + xsphy->dev = dev; + platform_set_drvdata(pdev, xsphy); + + glb_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + /* optional, may not exist if no u3 phys */ + if (glb_res) { + /* get banks shared by multiple u3 phys */ + xsphy->glb_base = devm_ioremap_resource(dev, glb_res); + if (IS_ERR(xsphy->glb_base)) { + dev_err(dev, "failed to remap glb regs\n"); + return PTR_ERR(xsphy->glb_base); + } + } + + xsphy->src_ref_clk = XSP_REF_CLK; + xsphy->src_coef = XSP_SLEW_RATE_COEF; + /* update parameters of slew rate calibrate if exist */ + device_property_read_u32(dev, "mediatek,src-ref-clk-mhz", + &xsphy->src_ref_clk); + device_property_read_u32(dev, "mediatek,src-coef", &xsphy->src_coef); + + port = 0; + for_each_child_of_node(np, child_np) { + struct xsphy_instance *inst; + struct phy *phy; + + inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL); + if (!inst) { + retval = -ENOMEM; + goto put_child; + } + + xsphy->phys[port] = inst; + + phy = devm_phy_create(dev, child_np, &mtk_xsphy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create phy\n"); + retval = PTR_ERR(phy); + goto put_child; + } + + retval = of_address_to_resource(child_np, 0, &res); + if (retval) { + dev_err(dev, "failed to get address resource(id-%d)\n", + port); + goto put_child; + } + + inst->port_base = devm_ioremap_resource(&phy->dev, &res); + if (IS_ERR(inst->port_base)) { + dev_err(dev, "failed to remap phy regs\n"); + retval = PTR_ERR(inst->port_base); + goto put_child; + } + + inst->phy = phy; + inst->index = port; + phy_set_drvdata(phy, inst); + port++; + + inst->ref_clk = devm_clk_get(&phy->dev, "ref"); + if (IS_ERR(inst->ref_clk)) { + dev_err(dev, "failed to get ref_clk(id-%d)\n", port); + retval = PTR_ERR(inst->ref_clk); + goto put_child; + } + } + + provider = devm_of_phy_provider_register(dev, mtk_phy_xlate); + return PTR_ERR_OR_ZERO(provider); + +put_child: + of_node_put(child_np); + return retval; +} + +static struct platform_driver mtk_xsphy_driver = { + .probe = mtk_xsphy_probe, + .driver = { + .name = "mtk-xsphy", + .of_match_table = mtk_xsphy_id_table, + }, +}; + +module_platform_driver(mtk_xsphy_driver); + +MODULE_AUTHOR("Chunfeng Yun "); +MODULE_DESCRIPTION("MediaTek USB XS-PHY driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5