Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d6e1957 authored by Chunfeng Yun's avatar Chunfeng Yun Committed by Kishon Vijay Abraham I
Browse files

phy: phy-mt65xx-usb3: add support for new version phy



There are some variations from mt2701 to mt2712:
1. banks shared by multiple ports are put back into each port,
    such as SPLLC and U2FREQ;
2. add a new bank MISC for u2port, and CHIP for u3port;
3. bank's offset in each port are also rearranged;

Signed-off-by: default avatarChunfeng Yun <chunfeng.yun@mediatek.com>
Signed-off-by: default avatarKishon Vijay Abraham I <kishon@ti.com>
parent 15de15c6
Loading
Loading
Loading
Loading
+218 −128
Original line number Diff line number Diff line
@@ -23,46 +23,54 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>

/*
 * for sifslv2 register, but exclude port's;
 * relative to USB3_SIF2_BASE base address
 */
#define SSUSB_SIFSLV_SPLLC		0x0000
#define SSUSB_SIFSLV_U2FREQ		0x0100

/* offsets of banks in each u2phy registers */
#define SSUSB_SIFSLV_U2PHY_COM_BASE	0x0000
/* offsets of banks in each u3phy registers */
#define SSUSB_SIFSLV_U3PHYD_BASE	0x0000
#define SSUSB_SIFSLV_U3PHYA_BASE	0x0200

#define U3P_USBPHYACR0		(SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0000)
/* version V1 sub-banks offset base address */
/* banks shared by multiple phys */
#define SSUSB_SIFSLV_V1_SPLLC		0x000	/* shared by u3 phys */
#define SSUSB_SIFSLV_V1_U2FREQ		0x100	/* shared by u2 phys */
/* u2 phy bank */
#define SSUSB_SIFSLV_V1_U2PHY_COM	0x000
/* u3 phy banks */
#define SSUSB_SIFSLV_V1_U3PHYD		0x000
#define SSUSB_SIFSLV_V1_U3PHYA		0x200

/* version V2 sub-banks offset base address */
/* u2 phy banks */
#define SSUSB_SIFSLV_V2_MISC		0x000
#define SSUSB_SIFSLV_V2_U2FREQ		0x100
#define SSUSB_SIFSLV_V2_U2PHY_COM	0x300
/* u3 phy banks */
#define SSUSB_SIFSLV_V2_SPLLC		0x000
#define SSUSB_SIFSLV_V2_CHIP		0x100
#define SSUSB_SIFSLV_V2_U3PHYD		0x200
#define SSUSB_SIFSLV_V2_U3PHYA		0x400

#define U3P_USBPHYACR0		0x000
#define PA0_RG_U2PLL_FORCE_ON		BIT(15)

#define U3P_USBPHYACR2		(SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0008)
#define U3P_USBPHYACR2		0x008
#define PA2_RG_SIF_U2PLL_FORCE_EN	BIT(18)

#define U3P_USBPHYACR5		(SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0014)
#define U3P_USBPHYACR5		0x014
#define PA5_RG_U2_HSTX_SRCAL_EN	BIT(15)
#define PA5_RG_U2_HSTX_SRCTRL		GENMASK(14, 12)
#define PA5_RG_U2_HSTX_SRCTRL_VAL(x)	((0x7 & (x)) << 12)
#define PA5_RG_U2_HS_100U_U3_EN	BIT(11)

#define U3P_USBPHYACR6		(SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0018)
#define U3P_USBPHYACR6		0x018
#define PA6_RG_U2_BC11_SW_EN		BIT(23)
#define PA6_RG_U2_OTG_VBUSCMP_EN	BIT(20)
#define PA6_RG_U2_SQTH		GENMASK(3, 0)
#define PA6_RG_U2_SQTH_VAL(x)	(0xf & (x))

#define U3P_U2PHYACR4		(SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0020)
#define U3P_U2PHYACR4		0x020
#define P2C_RG_USB20_GPIO_CTL		BIT(9)
#define P2C_USB20_GPIO_MODE		BIT(8)
#define P2C_U2_GPIO_CTR_MSK	(P2C_RG_USB20_GPIO_CTL | P2C_USB20_GPIO_MODE)

#define U3D_U2PHYDCR0		(SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0060)
#define U3D_U2PHYDCR0		0x060
#define P2C_RG_SIF_U2PLL_FORCE_ON	BIT(24)

#define U3P_U2PHYDTM0		(SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0068)
#define U3P_U2PHYDTM0		0x068
#define P2C_FORCE_UART_EN		BIT(26)
#define P2C_FORCE_DATAIN		BIT(23)
#define P2C_FORCE_DM_PULLDOWN		BIT(21)
@@ -84,59 +92,56 @@
		P2C_FORCE_TERMSEL | P2C_RG_DMPULLDOWN | \
		P2C_RG_DPPULLDOWN | P2C_RG_TERMSEL)

#define U3P_U2PHYDTM1		(SSUSB_SIFSLV_U2PHY_COM_BASE + 0x006C)
#define U3P_U2PHYDTM1		0x06C
#define P2C_RG_UART_EN			BIT(16)
#define P2C_RG_VBUSVALID		BIT(5)
#define P2C_RG_SESSEND			BIT(4)
#define P2C_RG_AVALID			BIT(2)

#define U3P_U3_PHYA_REG0	(SSUSB_SIFSLV_U3PHYA_BASE + 0x0000)
#define P3A_RG_U3_VUSB10_ON		BIT(5)

#define U3P_U3_PHYA_REG6	(SSUSB_SIFSLV_U3PHYA_BASE + 0x0018)
#define U3P_U3_PHYA_REG6	0x018
#define P3A_RG_TX_EIDLE_CM		GENMASK(31, 28)
#define P3A_RG_TX_EIDLE_CM_VAL(x)	((0xf & (x)) << 28)

#define U3P_U3_PHYA_REG9	(SSUSB_SIFSLV_U3PHYA_BASE + 0x0024)
#define U3P_U3_PHYA_REG9	0x024
#define P3A_RG_RX_DAC_MUX		GENMASK(5, 1)
#define P3A_RG_RX_DAC_MUX_VAL(x)	((0x1f & (x)) << 1)

#define U3P_U3PHYA_DA_REG0	(SSUSB_SIFSLV_U3PHYA_BASE + 0x0100)
#define U3P_U3_PHYA_DA_REG0	0x100
#define P3A_RG_XTAL_EXT_EN_U3		GENMASK(11, 10)
#define P3A_RG_XTAL_EXT_EN_U3_VAL(x)	((0x3 & (x)) << 10)

#define U3P_U3_PHYD_LFPS1		(SSUSB_SIFSLV_U3PHYD_BASE + 0x000c)
#define U3P_U3_PHYD_LFPS1		0x00c
#define P3D_RG_FWAKE_TH		GENMASK(21, 16)
#define P3D_RG_FWAKE_TH_VAL(x)	((0x3f & (x)) << 16)

#define U3P_PHYD_CDR1		(SSUSB_SIFSLV_U3PHYD_BASE + 0x005c)
#define U3P_U3_PHYD_CDR1		0x05c
#define P3D_RG_CDR_BIR_LTD1		GENMASK(28, 24)
#define P3D_RG_CDR_BIR_LTD1_VAL(x)	((0x1f & (x)) << 24)
#define P3D_RG_CDR_BIR_LTD0		GENMASK(12, 8)
#define P3D_RG_CDR_BIR_LTD0_VAL(x)	((0x1f & (x)) << 8)

#define U3P_U3_PHYD_RXDET1		(SSUSB_SIFSLV_U3PHYD_BASE + 0x128)
#define U3P_U3_PHYD_RXDET1		0x128
#define P3D_RG_RXDET_STB2_SET		GENMASK(17, 9)
#define P3D_RG_RXDET_STB2_SET_VAL(x)	((0x1ff & (x)) << 9)

#define U3P_U3_PHYD_RXDET2		(SSUSB_SIFSLV_U3PHYD_BASE + 0x12c)
#define U3P_U3_PHYD_RXDET2		0x12c
#define P3D_RG_RXDET_STB2_SET_P3	GENMASK(8, 0)
#define P3D_RG_RXDET_STB2_SET_P3_VAL(x)	(0x1ff & (x))

#define U3P_XTALCTL3		(SSUSB_SIFSLV_SPLLC + 0x0018)
#define U3P_SPLLC_XTALCTL3		0x018
#define XC3_RG_U3_XTAL_RX_PWD		BIT(9)
#define XC3_RG_U3_FRC_XTAL_RX_PWD	BIT(8)

#define U3P_U2FREQ_FMCR0	(SSUSB_SIFSLV_U2FREQ + 0x00)
#define U3P_U2FREQ_FMCR0	0x00
#define P2F_RG_MONCLK_SEL	GENMASK(27, 26)
#define P2F_RG_MONCLK_SEL_VAL(x)	((0x3 & (x)) << 26)
#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 U3P_U2FREQ_VALUE	(SSUSB_SIFSLV_U2FREQ + 0x0c)
#define U3P_U2FREQ_VALUE	0x0c

#define U3P_U2FREQ_FMMONR1	(SSUSB_SIFSLV_U2FREQ + 0x10)
#define U3P_U2FREQ_FMMONR1	0x10
#define P2F_USB_FM_VALID	BIT(0)
#define P2F_RG_FRCK_EN		BIT(8)

@@ -145,14 +150,37 @@
#define U3P_SR_COEF_DIVISOR	1000
#define U3P_FM_DET_CYCLE_CNT	1024

enum mt_phy_version {
	MT_PHY_V1 = 1,
	MT_PHY_V2,
};

struct mt65xx_phy_pdata {
	/* avoid RX sensitivity level degradation only for mt8173 */
	bool avoid_rx_sen_degradation;
	enum mt_phy_version version;
};

struct u2phy_banks {
	void __iomem *misc;
	void __iomem *fmreg;
	void __iomem *com;
};

struct u3phy_banks {
	void __iomem *spllc;
	void __iomem *chip;
	void __iomem *phyd; /* include u3phyd_bank2 */
	void __iomem *phya; /* include u3phya_da */
};

struct mt65xx_phy_instance {
	struct phy *phy;
	void __iomem *port_base;
	union {
		struct u2phy_banks u2_banks;
		struct u3phy_banks u3_banks;
	};
	struct clk *ref_clk;	/* reference clock of anolog phy */
	u32 index;
	u8 type;
@@ -171,49 +199,53 @@ struct mt65xx_u3phy {
static void hs_slew_rate_calibrate(struct mt65xx_u3phy *u3phy,
	struct mt65xx_phy_instance *instance)
{
	void __iomem *sif_base = u3phy->sif_base;
	struct u2phy_banks *u2_banks = &instance->u2_banks;
	void __iomem *fmreg = u2_banks->fmreg;
	void __iomem *com = u2_banks->com;
	int calibration_val;
	int fm_out;
	u32 tmp;

	/* enable USB ring oscillator */
	tmp = readl(instance->port_base + U3P_USBPHYACR5);
	tmp = readl(com + U3P_USBPHYACR5);
	tmp |= PA5_RG_U2_HSTX_SRCAL_EN;
	writel(tmp, instance->port_base + U3P_USBPHYACR5);
	writel(tmp, com + U3P_USBPHYACR5);
	udelay(1);

	/*enable free run clock */
	tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
	tmp = readl(fmreg + U3P_U2FREQ_FMMONR1);
	tmp |= P2F_RG_FRCK_EN;
	writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
	writel(tmp, fmreg + U3P_U2FREQ_FMMONR1);

	/* set cycle count as 1024, and select u2 channel */
	tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
	tmp = readl(fmreg + U3P_U2FREQ_FMCR0);
	tmp &= ~(P2F_RG_CYCLECNT | P2F_RG_MONCLK_SEL);
	tmp |= P2F_RG_CYCLECNT_VAL(U3P_FM_DET_CYCLE_CNT);
	if (u3phy->pdata->version == MT_PHY_V1)
		tmp |= P2F_RG_MONCLK_SEL_VAL(instance->index >> 1);
	writel(tmp, sif_base + U3P_U2FREQ_FMCR0);

	writel(tmp, fmreg + U3P_U2FREQ_FMCR0);

	/* enable frequency meter */
	tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
	tmp = readl(fmreg + U3P_U2FREQ_FMCR0);
	tmp |= P2F_RG_FREQDET_EN;
	writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
	writel(tmp, fmreg + U3P_U2FREQ_FMCR0);

	/* ignore return value */
	readl_poll_timeout(sif_base + U3P_U2FREQ_FMMONR1, tmp,
	readl_poll_timeout(fmreg + U3P_U2FREQ_FMMONR1, tmp,
			   (tmp & P2F_USB_FM_VALID), 10, 200);

	fm_out = readl(sif_base + U3P_U2FREQ_VALUE);
	fm_out = readl(fmreg + U3P_U2FREQ_VALUE);

	/* disable frequency meter */
	tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
	tmp = readl(fmreg + U3P_U2FREQ_FMCR0);
	tmp &= ~P2F_RG_FREQDET_EN;
	writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
	writel(tmp, fmreg + U3P_U2FREQ_FMCR0);

	/*disable free run clock */
	tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
	tmp = readl(fmreg + U3P_U2FREQ_FMMONR1);
	tmp &= ~P2F_RG_FRCK_EN;
	writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
	writel(tmp, fmreg + U3P_U2FREQ_FMMONR1);

	if (fm_out) {
		/* ( 1024 / FM_OUT ) x reference clock frequency x 0.028 */
@@ -228,63 +260,63 @@ static void hs_slew_rate_calibrate(struct mt65xx_u3phy *u3phy,
		instance->index, fm_out, calibration_val);

	/* set HS slew rate */
	tmp = readl(instance->port_base + U3P_USBPHYACR5);
	tmp = readl(com + U3P_USBPHYACR5);
	tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
	tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val);
	writel(tmp, instance->port_base + U3P_USBPHYACR5);
	writel(tmp, com + U3P_USBPHYACR5);

	/* disable USB ring oscillator */
	tmp = readl(instance->port_base + U3P_USBPHYACR5);
	tmp = readl(com + U3P_USBPHYACR5);
	tmp &= ~PA5_RG_U2_HSTX_SRCAL_EN;
	writel(tmp, instance->port_base + U3P_USBPHYACR5);
	writel(tmp, com + U3P_USBPHYACR5);
}

static void u3_phy_instance_init(struct mt65xx_u3phy *u3phy,
	struct mt65xx_phy_instance *instance)
{
	void __iomem *port_base = instance->port_base;
	struct u3phy_banks *u3_banks = &instance->u3_banks;
	u32 tmp;

	/* gating PCIe Analog XTAL clock */
	tmp = readl(u3phy->sif_base + U3P_XTALCTL3);
	tmp = readl(u3_banks->spllc + U3P_SPLLC_XTALCTL3);
	tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD;
	writel(tmp, u3phy->sif_base + U3P_XTALCTL3);
	writel(tmp, u3_banks->spllc + U3P_SPLLC_XTALCTL3);

	/* gating XSQ */
	tmp = readl(port_base + U3P_U3PHYA_DA_REG0);
	tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG0);
	tmp &= ~P3A_RG_XTAL_EXT_EN_U3;
	tmp |= P3A_RG_XTAL_EXT_EN_U3_VAL(2);
	writel(tmp, port_base + U3P_U3PHYA_DA_REG0);
	writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG0);

	tmp = readl(port_base + U3P_U3_PHYA_REG9);
	tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG9);
	tmp &= ~P3A_RG_RX_DAC_MUX;
	tmp |= P3A_RG_RX_DAC_MUX_VAL(4);
	writel(tmp, port_base + U3P_U3_PHYA_REG9);
	writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG9);

	tmp = readl(port_base + U3P_U3_PHYA_REG6);
	tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG6);
	tmp &= ~P3A_RG_TX_EIDLE_CM;
	tmp |= P3A_RG_TX_EIDLE_CM_VAL(0xe);
	writel(tmp, port_base + U3P_U3_PHYA_REG6);
	writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG6);

	tmp = readl(port_base + U3P_PHYD_CDR1);
	tmp = readl(u3_banks->phyd + U3P_U3_PHYD_CDR1);
	tmp &= ~(P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1);
	tmp |= P3D_RG_CDR_BIR_LTD0_VAL(0xc) | P3D_RG_CDR_BIR_LTD1_VAL(0x3);
	writel(tmp, port_base + U3P_PHYD_CDR1);
	writel(tmp, u3_banks->phyd + U3P_U3_PHYD_CDR1);

	tmp = readl(port_base + U3P_U3_PHYD_LFPS1);
	tmp = readl(u3_banks->phyd + U3P_U3_PHYD_LFPS1);
	tmp &= ~P3D_RG_FWAKE_TH;
	tmp |= P3D_RG_FWAKE_TH_VAL(0x34);
	writel(tmp, port_base + U3P_U3_PHYD_LFPS1);
	writel(tmp, u3_banks->phyd + U3P_U3_PHYD_LFPS1);

	tmp = readl(port_base + U3P_U3_PHYD_RXDET1);
	tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET1);
	tmp &= ~P3D_RG_RXDET_STB2_SET;
	tmp |= P3D_RG_RXDET_STB2_SET_VAL(0x10);
	writel(tmp, port_base + U3P_U3_PHYD_RXDET1);
	writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET1);

	tmp = readl(port_base + U3P_U3_PHYD_RXDET2);
	tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET2);
	tmp &= ~P3D_RG_RXDET_STB2_SET_P3;
	tmp |= P3D_RG_RXDET_STB2_SET_P3_VAL(0x10);
	writel(tmp, port_base + U3P_U3_PHYD_RXDET2);
	writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET2);

	dev_dbg(u3phy->dev, "%s(%d)\n", __func__, instance->index);
}
@@ -292,51 +324,52 @@ static void u3_phy_instance_init(struct mt65xx_u3phy *u3phy,
static void phy_instance_init(struct mt65xx_u3phy *u3phy,
	struct mt65xx_phy_instance *instance)
{
	void __iomem *port_base = instance->port_base;
	struct u2phy_banks *u2_banks = &instance->u2_banks;
	void __iomem *com = u2_banks->com;
	u32 index = instance->index;
	u32 tmp;

	/* switch to USB function. (system register, force ip into usb mode) */
	tmp = readl(port_base + U3P_U2PHYDTM0);
	tmp = readl(com + U3P_U2PHYDTM0);
	tmp &= ~P2C_FORCE_UART_EN;
	tmp |= P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0);
	writel(tmp, port_base + U3P_U2PHYDTM0);
	writel(tmp, com + U3P_U2PHYDTM0);

	tmp = readl(port_base + U3P_U2PHYDTM1);
	tmp = readl(com + U3P_U2PHYDTM1);
	tmp &= ~P2C_RG_UART_EN;
	writel(tmp, port_base + U3P_U2PHYDTM1);
	writel(tmp, com + U3P_U2PHYDTM1);

	if (!index) {
		tmp = readl(port_base + U3P_U2PHYACR4);
		tmp = readl(com + U3P_U2PHYACR4);
		tmp &= ~P2C_U2_GPIO_CTR_MSK;
		writel(tmp, port_base + U3P_U2PHYACR4);
		writel(tmp, com + U3P_U2PHYACR4);
	}

	if (u3phy->pdata->avoid_rx_sen_degradation) {
		if (!index) {
			tmp = readl(port_base + U3P_USBPHYACR2);
			tmp = readl(com + U3P_USBPHYACR2);
			tmp |= PA2_RG_SIF_U2PLL_FORCE_EN;
			writel(tmp, port_base + U3P_USBPHYACR2);
			writel(tmp, com + U3P_USBPHYACR2);

			tmp = readl(port_base + U3D_U2PHYDCR0);
			tmp = readl(com + U3D_U2PHYDCR0);
			tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
			writel(tmp, port_base + U3D_U2PHYDCR0);
			writel(tmp, com + U3D_U2PHYDCR0);
		} else {
			tmp = readl(port_base + U3D_U2PHYDCR0);
			tmp = readl(com + U3D_U2PHYDCR0);
			tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
			writel(tmp, port_base + U3D_U2PHYDCR0);
			writel(tmp, com + U3D_U2PHYDCR0);

			tmp = readl(port_base + U3P_U2PHYDTM0);
			tmp = readl(com + U3P_U2PHYDTM0);
			tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
			writel(tmp, port_base + U3P_U2PHYDTM0);
			writel(tmp, com + U3P_U2PHYDTM0);
		}
	}

	tmp = readl(port_base + U3P_USBPHYACR6);
	tmp = readl(com + U3P_USBPHYACR6);
	tmp &= ~PA6_RG_U2_BC11_SW_EN;	/* DP/DM BC1.1 path Disable */
	tmp &= ~PA6_RG_U2_SQTH;
	tmp |= PA6_RG_U2_SQTH_VAL(2);
	writel(tmp, port_base + U3P_USBPHYACR6);
	writel(tmp, com + U3P_USBPHYACR6);

	dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
}
@@ -344,41 +377,42 @@ static void phy_instance_init(struct mt65xx_u3phy *u3phy,
static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
	struct mt65xx_phy_instance *instance)
{
	void __iomem *port_base = instance->port_base;
	struct u2phy_banks *u2_banks = &instance->u2_banks;
	void __iomem *com = u2_banks->com;
	u32 index = instance->index;
	u32 tmp;

	/* (force_suspendm=0) (let suspendm=1, enable usb 480MHz pll) */
	tmp = readl(port_base + U3P_U2PHYDTM0);
	tmp = readl(com + U3P_U2PHYDTM0);
	tmp &= ~(P2C_FORCE_SUSPENDM | P2C_RG_XCVRSEL);
	tmp &= ~(P2C_RG_DATAIN | P2C_DTM0_PART_MASK);
	writel(tmp, port_base + U3P_U2PHYDTM0);
	writel(tmp, com + U3P_U2PHYDTM0);

	/* OTG Enable */
	tmp = readl(port_base + U3P_USBPHYACR6);
	tmp = readl(com + U3P_USBPHYACR6);
	tmp |= PA6_RG_U2_OTG_VBUSCMP_EN;
	writel(tmp, port_base + U3P_USBPHYACR6);
	writel(tmp, com + U3P_USBPHYACR6);

	if (!index) {
		/* switch 100uA current to SSUSB */
		tmp = readl(port_base + U3P_USBPHYACR5);
		tmp = readl(com + U3P_USBPHYACR5);
		tmp |= PA5_RG_U2_HS_100U_U3_EN;
		writel(tmp, port_base + U3P_USBPHYACR5);
		writel(tmp, com + U3P_USBPHYACR5);
	}

	tmp = readl(port_base + U3P_U2PHYDTM1);
	tmp = readl(com + U3P_U2PHYDTM1);
	tmp |= P2C_RG_VBUSVALID | P2C_RG_AVALID;
	tmp &= ~P2C_RG_SESSEND;
	writel(tmp, port_base + U3P_U2PHYDTM1);
	writel(tmp, com + U3P_U2PHYDTM1);

	if (u3phy->pdata->avoid_rx_sen_degradation && index) {
		tmp = readl(port_base + U3D_U2PHYDCR0);
		tmp = readl(com + U3D_U2PHYDCR0);
		tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
		writel(tmp, port_base + U3D_U2PHYDCR0);
		writel(tmp, com + U3D_U2PHYDCR0);

		tmp = readl(port_base + U3P_U2PHYDTM0);
		tmp = readl(com + U3P_U2PHYDTM0);
		tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
		writel(tmp, port_base + U3P_U2PHYDTM0);
		writel(tmp, com + U3P_U2PHYDTM0);
	}
	dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
}
@@ -386,42 +420,43 @@ static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
	struct mt65xx_phy_instance *instance)
{
	void __iomem *port_base = instance->port_base;
	struct u2phy_banks *u2_banks = &instance->u2_banks;
	void __iomem *com = u2_banks->com;
	u32 index = instance->index;
	u32 tmp;

	tmp = readl(port_base + U3P_U2PHYDTM0);
	tmp = readl(com + U3P_U2PHYDTM0);
	tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN);
	tmp |= P2C_FORCE_SUSPENDM;
	writel(tmp, port_base + U3P_U2PHYDTM0);
	writel(tmp, com + U3P_U2PHYDTM0);

	/* OTG Disable */
	tmp = readl(port_base + U3P_USBPHYACR6);
	tmp = readl(com + U3P_USBPHYACR6);
	tmp &= ~PA6_RG_U2_OTG_VBUSCMP_EN;
	writel(tmp, port_base + U3P_USBPHYACR6);
	writel(tmp, com + U3P_USBPHYACR6);

	if (!index) {
		/* switch 100uA current back to USB2.0 */
		tmp = readl(port_base + U3P_USBPHYACR5);
		tmp = readl(com + U3P_USBPHYACR5);
		tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
		writel(tmp, port_base + U3P_USBPHYACR5);
		writel(tmp, com + U3P_USBPHYACR5);
	}

	/* let suspendm=0, set utmi into analog power down */
	tmp = readl(port_base + U3P_U2PHYDTM0);
	tmp = readl(com + U3P_U2PHYDTM0);
	tmp &= ~P2C_RG_SUSPENDM;
	writel(tmp, port_base + U3P_U2PHYDTM0);
	writel(tmp, com + U3P_U2PHYDTM0);
	udelay(1);

	tmp = readl(port_base + U3P_U2PHYDTM1);
	tmp = readl(com + U3P_U2PHYDTM1);
	tmp &= ~(P2C_RG_VBUSVALID | P2C_RG_AVALID);
	tmp |= P2C_RG_SESSEND;
	writel(tmp, port_base + U3P_U2PHYDTM1);
	writel(tmp, com + U3P_U2PHYDTM1);

	if (u3phy->pdata->avoid_rx_sen_degradation && index) {
		tmp = readl(port_base + U3D_U2PHYDCR0);
		tmp = readl(com + U3D_U2PHYDCR0);
		tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
		writel(tmp, port_base + U3D_U2PHYDCR0);
		writel(tmp, com + U3D_U2PHYDCR0);
	}

	dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
@@ -430,18 +465,55 @@ static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
static void phy_instance_exit(struct mt65xx_u3phy *u3phy,
	struct mt65xx_phy_instance *instance)
{
	void __iomem *port_base = instance->port_base;
	struct u2phy_banks *u2_banks = &instance->u2_banks;
	void __iomem *com = u2_banks->com;
	u32 index = instance->index;
	u32 tmp;

	if (u3phy->pdata->avoid_rx_sen_degradation && index) {
		tmp = readl(port_base + U3D_U2PHYDCR0);
		tmp = readl(com + U3D_U2PHYDCR0);
		tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
		writel(tmp, port_base + U3D_U2PHYDCR0);
		writel(tmp, com + U3D_U2PHYDCR0);

		tmp = readl(port_base + U3P_U2PHYDTM0);
		tmp = readl(com + U3P_U2PHYDTM0);
		tmp &= ~P2C_FORCE_SUSPENDM;
		writel(tmp, port_base + U3P_U2PHYDTM0);
		writel(tmp, com + U3P_U2PHYDTM0);
	}
}

static void phy_v1_banks_init(struct mt65xx_u3phy *u3phy,
			      struct mt65xx_phy_instance *instance)
{
	struct u2phy_banks *u2_banks = &instance->u2_banks;
	struct u3phy_banks *u3_banks = &instance->u3_banks;

	if (instance->type == PHY_TYPE_USB2) {
		u2_banks->misc = NULL;
		u2_banks->fmreg = u3phy->sif_base + SSUSB_SIFSLV_V1_U2FREQ;
		u2_banks->com = instance->port_base + SSUSB_SIFSLV_V1_U2PHY_COM;
	} else if (instance->type == PHY_TYPE_USB3) {
		u3_banks->spllc = u3phy->sif_base + SSUSB_SIFSLV_V1_SPLLC;
		u3_banks->chip = NULL;
		u3_banks->phyd = instance->port_base + SSUSB_SIFSLV_V1_U3PHYD;
		u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V1_U3PHYA;
	}
}

static void phy_v2_banks_init(struct mt65xx_u3phy *u3phy,
			      struct mt65xx_phy_instance *instance)
{
	struct u2phy_banks *u2_banks = &instance->u2_banks;
	struct u3phy_banks *u3_banks = &instance->u3_banks;

	if (instance->type == PHY_TYPE_USB2) {
		u2_banks->misc = instance->port_base + SSUSB_SIFSLV_V2_MISC;
		u2_banks->fmreg = instance->port_base + SSUSB_SIFSLV_V2_U2FREQ;
		u2_banks->com = instance->port_base + SSUSB_SIFSLV_V2_U2PHY_COM;
	} else if (instance->type == PHY_TYPE_USB3) {
		u3_banks->spllc = instance->port_base + SSUSB_SIFSLV_V2_SPLLC;
		u3_banks->chip = instance->port_base + SSUSB_SIFSLV_V2_CHIP;
		u3_banks->phyd = instance->port_base + SSUSB_SIFSLV_V2_U3PHYD;
		u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V2_U3PHYA;
	}
}

@@ -515,7 +587,6 @@ static struct phy *mt65xx_phy_xlate(struct device *dev,
	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);
@@ -533,13 +604,21 @@ static struct phy *mt65xx_phy_xlate(struct device *dev,
	}

	instance->type = args->args[0];

	if (!(instance->type == PHY_TYPE_USB2 ||
	      instance->type == PHY_TYPE_USB3)) {
		dev_err(dev, "unsupported device type: %d\n", instance->type);
		return ERR_PTR(-EINVAL);
	}

	if (u3phy->pdata->version == MT_PHY_V1) {
		phy_v1_banks_init(u3phy, instance);
	} else if (u3phy->pdata->version == MT_PHY_V2) {
		phy_v2_banks_init(u3phy, instance);
	} else {
		dev_err(dev, "phy version is not supported\n");
		return ERR_PTR(-EINVAL);
	}

	return instance->phy;
}

@@ -553,14 +632,22 @@ static const struct phy_ops mt65xx_u3phy_ops = {

static const struct mt65xx_phy_pdata mt2701_pdata = {
	.avoid_rx_sen_degradation = false,
	.version = MT_PHY_V1,
};

static const struct mt65xx_phy_pdata mt2712_pdata = {
	.avoid_rx_sen_degradation = false,
	.version = MT_PHY_V2,
};

static const struct mt65xx_phy_pdata mt8173_pdata = {
	.avoid_rx_sen_degradation = true,
	.version = MT_PHY_V1,
};

static const struct of_device_id mt65xx_u3phy_id_table[] = {
	{ .compatible = "mediatek,mt2701-u3phy", .data = &mt2701_pdata },
	{ .compatible = "mediatek,mt2712-u3phy", .data = &mt2712_pdata },
	{ .compatible = "mediatek,mt8173-u3phy", .data = &mt8173_pdata },
	{ },
};
@@ -596,12 +683,15 @@ static int mt65xx_u3phy_probe(struct platform_device *pdev)
	u3phy->dev = dev;
	platform_set_drvdata(pdev, u3phy);

	if (u3phy->pdata->version == MT_PHY_V1) {
		/* get banks shared by multiple phys */
		sif_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
		u3phy->sif_base = devm_ioremap_resource(dev, sif_res);
		if (IS_ERR(u3phy->sif_base)) {
			dev_err(dev, "failed to remap sif regs\n");
			return PTR_ERR(u3phy->sif_base);
		}
	}

	/* it's deprecated, make it optional for backward compatibility */
	u3phy->u3phya_ref = devm_clk_get(dev, "u3phya_ref");