blob: fe2392ac0eb44ab9ec65f6a4da110cf11f6530b5 [file] [log] [blame]
Chunfeng Yundc7f1902015-09-29 11:01:36 +08001/*
2 * Copyright (c) 2015 MediaTek Inc.
3 * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <dt-bindings/phy/phy.h>
17#include <linux/clk.h>
18#include <linux/delay.h>
19#include <linux/io.h>
Chunfeng Yun75f072f2015-12-04 10:11:05 +080020#include <linux/iopoll.h>
Chunfeng Yundc7f1902015-09-29 11:01:36 +080021#include <linux/module.h>
22#include <linux/of_address.h>
23#include <linux/phy/phy.h>
24#include <linux/platform_device.h>
25
26/*
27 * for sifslv2 register, but exclude port's;
28 * relative to USB3_SIF2_BASE base address
29 */
30#define SSUSB_SIFSLV_SPLLC 0x0000
Chunfeng Yun75f072f2015-12-04 10:11:05 +080031#define SSUSB_SIFSLV_U2FREQ 0x0100
Chunfeng Yundc7f1902015-09-29 11:01:36 +080032
33/* offsets of sub-segment in each port registers */
34#define SSUSB_SIFSLV_U2PHY_COM_BASE 0x0000
35#define SSUSB_SIFSLV_U3PHYD_BASE 0x0100
36#define SSUSB_USB30_PHYA_SIV_B_BASE 0x0300
37#define SSUSB_SIFSLV_U3PHYA_DA_BASE 0x0400
38
39#define U3P_USBPHYACR0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0000)
40#define PA0_RG_U2PLL_FORCE_ON BIT(15)
41
42#define U3P_USBPHYACR2 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0008)
43#define PA2_RG_SIF_U2PLL_FORCE_EN BIT(18)
44
45#define U3P_USBPHYACR5 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0014)
Chunfeng Yun75f072f2015-12-04 10:11:05 +080046#define PA5_RG_U2_HSTX_SRCAL_EN BIT(15)
Chunfeng Yundc7f1902015-09-29 11:01:36 +080047#define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12)
48#define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12)
49#define PA5_RG_U2_HS_100U_U3_EN BIT(11)
50
51#define U3P_USBPHYACR6 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0018)
52#define PA6_RG_U2_ISO_EN BIT(31)
53#define PA6_RG_U2_BC11_SW_EN BIT(23)
54#define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20)
Chunfeng Yun43f53b12015-12-04 10:08:56 +080055#define PA6_RG_U2_SQTH GENMASK(3, 0)
56#define PA6_RG_U2_SQTH_VAL(x) (0xf & (x))
Chunfeng Yundc7f1902015-09-29 11:01:36 +080057
58#define U3P_U2PHYACR4 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0020)
59#define P2C_RG_USB20_GPIO_CTL BIT(9)
60#define P2C_USB20_GPIO_MODE BIT(8)
61#define P2C_U2_GPIO_CTR_MSK (P2C_RG_USB20_GPIO_CTL | P2C_USB20_GPIO_MODE)
62
63#define U3D_U2PHYDCR0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0060)
64#define P2C_RG_SIF_U2PLL_FORCE_ON BIT(24)
65
66#define U3P_U2PHYDTM0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0068)
67#define P2C_FORCE_UART_EN BIT(26)
68#define P2C_FORCE_DATAIN BIT(23)
69#define P2C_FORCE_DM_PULLDOWN BIT(21)
70#define P2C_FORCE_DP_PULLDOWN BIT(20)
71#define P2C_FORCE_XCVRSEL BIT(19)
72#define P2C_FORCE_SUSPENDM BIT(18)
73#define P2C_FORCE_TERMSEL BIT(17)
74#define P2C_RG_DATAIN GENMASK(13, 10)
75#define P2C_RG_DATAIN_VAL(x) ((0xf & (x)) << 10)
76#define P2C_RG_DMPULLDOWN BIT(7)
77#define P2C_RG_DPPULLDOWN BIT(6)
78#define P2C_RG_XCVRSEL GENMASK(5, 4)
79#define P2C_RG_XCVRSEL_VAL(x) ((0x3 & (x)) << 4)
80#define P2C_RG_SUSPENDM BIT(3)
81#define P2C_RG_TERMSEL BIT(2)
82#define P2C_DTM0_PART_MASK \
83 (P2C_FORCE_DATAIN | P2C_FORCE_DM_PULLDOWN | \
84 P2C_FORCE_DP_PULLDOWN | P2C_FORCE_XCVRSEL | \
85 P2C_FORCE_TERMSEL | P2C_RG_DMPULLDOWN | \
86 P2C_RG_DPPULLDOWN | P2C_RG_TERMSEL)
87
88#define U3P_U2PHYDTM1 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x006C)
89#define P2C_RG_UART_EN BIT(16)
90#define P2C_RG_VBUSVALID BIT(5)
91#define P2C_RG_SESSEND BIT(4)
92#define P2C_RG_AVALID BIT(2)
93
94#define U3P_U3_PHYA_REG0 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0000)
95#define P3A_RG_U3_VUSB10_ON BIT(5)
96
97#define U3P_U3_PHYA_REG6 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0018)
98#define P3A_RG_TX_EIDLE_CM GENMASK(31, 28)
99#define P3A_RG_TX_EIDLE_CM_VAL(x) ((0xf & (x)) << 28)
100
101#define U3P_U3_PHYA_REG9 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0024)
102#define P3A_RG_RX_DAC_MUX GENMASK(5, 1)
103#define P3A_RG_RX_DAC_MUX_VAL(x) ((0x1f & (x)) << 1)
104
105#define U3P_U3PHYA_DA_REG0 (SSUSB_SIFSLV_U3PHYA_DA_BASE + 0x0000)
106#define P3A_RG_XTAL_EXT_EN_U3 GENMASK(11, 10)
107#define P3A_RG_XTAL_EXT_EN_U3_VAL(x) ((0x3 & (x)) << 10)
108
109#define U3P_PHYD_CDR1 (SSUSB_SIFSLV_U3PHYD_BASE + 0x005c)
110#define P3D_RG_CDR_BIR_LTD1 GENMASK(28, 24)
111#define P3D_RG_CDR_BIR_LTD1_VAL(x) ((0x1f & (x)) << 24)
112#define P3D_RG_CDR_BIR_LTD0 GENMASK(12, 8)
113#define P3D_RG_CDR_BIR_LTD0_VAL(x) ((0x1f & (x)) << 8)
114
Chunfeng Yun1969f692017-03-31 15:35:27 +0800115#define U3P_U3_PHYD_RXDET1 (SSUSB_SIFSLV_U3PHYD_BASE + 0x128)
116#define P3D_RG_RXDET_STB2_SET GENMASK(17, 9)
117#define P3D_RG_RXDET_STB2_SET_VAL(x) ((0x1ff & (x)) << 9)
118
119#define U3P_U3_PHYD_RXDET2 (SSUSB_SIFSLV_U3PHYD_BASE + 0x12c)
120#define P3D_RG_RXDET_STB2_SET_P3 GENMASK(8, 0)
121#define P3D_RG_RXDET_STB2_SET_P3_VAL(x) (0x1ff & (x))
122
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800123#define U3P_XTALCTL3 (SSUSB_SIFSLV_SPLLC + 0x0018)
124#define XC3_RG_U3_XTAL_RX_PWD BIT(9)
125#define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8)
126
Chunfeng Yun75f072f2015-12-04 10:11:05 +0800127#define U3P_U2FREQ_FMCR0 (SSUSB_SIFSLV_U2FREQ + 0x00)
128#define P2F_RG_MONCLK_SEL GENMASK(27, 26)
129#define P2F_RG_MONCLK_SEL_VAL(x) ((0x3 & (x)) << 26)
130#define P2F_RG_FREQDET_EN BIT(24)
131#define P2F_RG_CYCLECNT GENMASK(23, 0)
132#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x))
133
134#define U3P_U2FREQ_VALUE (SSUSB_SIFSLV_U2FREQ + 0x0c)
135
136#define U3P_U2FREQ_FMMONR1 (SSUSB_SIFSLV_U2FREQ + 0x10)
137#define P2F_USB_FM_VALID BIT(0)
138#define P2F_RG_FRCK_EN BIT(8)
139
140#define U3P_REF_CLK 26 /* MHZ */
141#define U3P_SLEW_RATE_COEF 28
142#define U3P_SR_COEF_DIVISOR 1000
143#define U3P_FM_DET_CYCLE_CNT 1024
144
Chunfeng Yune1d76532016-04-20 08:14:02 +0800145struct mt65xx_phy_pdata {
146 /* avoid RX sensitivity level degradation only for mt8173 */
147 bool avoid_rx_sen_degradation;
148};
149
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800150struct mt65xx_phy_instance {
151 struct phy *phy;
152 void __iomem *port_base;
153 u32 index;
154 u8 type;
155};
156
157struct mt65xx_u3phy {
158 struct device *dev;
159 void __iomem *sif_base; /* include sif2, but exclude port's */
160 struct clk *u3phya_ref; /* reference clock of usb3 anolog phy */
Chunfeng Yune1d76532016-04-20 08:14:02 +0800161 const struct mt65xx_phy_pdata *pdata;
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800162 struct mt65xx_phy_instance **phys;
163 int nphys;
164};
165
Chunfeng Yun75f072f2015-12-04 10:11:05 +0800166static void hs_slew_rate_calibrate(struct mt65xx_u3phy *u3phy,
167 struct mt65xx_phy_instance *instance)
168{
169 void __iomem *sif_base = u3phy->sif_base;
170 int calibration_val;
171 int fm_out;
172 u32 tmp;
173
174 /* enable USB ring oscillator */
175 tmp = readl(instance->port_base + U3P_USBPHYACR5);
176 tmp |= PA5_RG_U2_HSTX_SRCAL_EN;
177 writel(tmp, instance->port_base + U3P_USBPHYACR5);
178 udelay(1);
179
180 /*enable free run clock */
181 tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
182 tmp |= P2F_RG_FRCK_EN;
183 writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
184
185 /* set cycle count as 1024, and select u2 channel */
186 tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
187 tmp &= ~(P2F_RG_CYCLECNT | P2F_RG_MONCLK_SEL);
188 tmp |= P2F_RG_CYCLECNT_VAL(U3P_FM_DET_CYCLE_CNT);
189 tmp |= P2F_RG_MONCLK_SEL_VAL(instance->index);
190 writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
191
192 /* enable frequency meter */
193 tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
194 tmp |= P2F_RG_FREQDET_EN;
195 writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
196
197 /* ignore return value */
198 readl_poll_timeout(sif_base + U3P_U2FREQ_FMMONR1, tmp,
199 (tmp & P2F_USB_FM_VALID), 10, 200);
200
201 fm_out = readl(sif_base + U3P_U2FREQ_VALUE);
202
203 /* disable frequency meter */
204 tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
205 tmp &= ~P2F_RG_FREQDET_EN;
206 writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
207
208 /*disable free run clock */
209 tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
210 tmp &= ~P2F_RG_FRCK_EN;
211 writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
212
213 if (fm_out) {
214 /* ( 1024 / FM_OUT ) x reference clock frequency x 0.028 */
215 tmp = U3P_FM_DET_CYCLE_CNT * U3P_REF_CLK * U3P_SLEW_RATE_COEF;
216 tmp /= fm_out;
217 calibration_val = DIV_ROUND_CLOSEST(tmp, U3P_SR_COEF_DIVISOR);
218 } else {
219 /* if FM detection fail, set default value */
220 calibration_val = 4;
221 }
222 dev_dbg(u3phy->dev, "phy:%d, fm_out:%d, calib:%d\n",
223 instance->index, fm_out, calibration_val);
224
225 /* set HS slew rate */
226 tmp = readl(instance->port_base + U3P_USBPHYACR5);
227 tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
228 tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val);
229 writel(tmp, instance->port_base + U3P_USBPHYACR5);
230
231 /* disable USB ring oscillator */
232 tmp = readl(instance->port_base + U3P_USBPHYACR5);
233 tmp &= ~PA5_RG_U2_HSTX_SRCAL_EN;
234 writel(tmp, instance->port_base + U3P_USBPHYACR5);
235}
236
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800237static void phy_instance_init(struct mt65xx_u3phy *u3phy,
238 struct mt65xx_phy_instance *instance)
239{
240 void __iomem *port_base = instance->port_base;
241 u32 index = instance->index;
242 u32 tmp;
243
244 /* switch to USB function. (system register, force ip into usb mode) */
245 tmp = readl(port_base + U3P_U2PHYDTM0);
246 tmp &= ~P2C_FORCE_UART_EN;
247 tmp |= P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0);
248 writel(tmp, port_base + U3P_U2PHYDTM0);
249
250 tmp = readl(port_base + U3P_U2PHYDTM1);
251 tmp &= ~P2C_RG_UART_EN;
252 writel(tmp, port_base + U3P_U2PHYDTM1);
253
254 if (!index) {
255 tmp = readl(port_base + U3P_U2PHYACR4);
256 tmp &= ~P2C_U2_GPIO_CTR_MSK;
257 writel(tmp, port_base + U3P_U2PHYACR4);
Chunfeng Yune1d76532016-04-20 08:14:02 +0800258 }
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800259
Chunfeng Yune1d76532016-04-20 08:14:02 +0800260 if (u3phy->pdata->avoid_rx_sen_degradation) {
261 if (!index) {
262 tmp = readl(port_base + U3P_USBPHYACR2);
263 tmp |= PA2_RG_SIF_U2PLL_FORCE_EN;
264 writel(tmp, port_base + U3P_USBPHYACR2);
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800265
Chunfeng Yune1d76532016-04-20 08:14:02 +0800266 tmp = readl(port_base + U3D_U2PHYDCR0);
267 tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
268 writel(tmp, port_base + U3D_U2PHYDCR0);
269 } else {
270 tmp = readl(port_base + U3D_U2PHYDCR0);
271 tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
272 writel(tmp, port_base + U3D_U2PHYDCR0);
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800273
Chunfeng Yune1d76532016-04-20 08:14:02 +0800274 tmp = readl(port_base + U3P_U2PHYDTM0);
275 tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
276 writel(tmp, port_base + U3P_U2PHYDTM0);
277 }
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800278 }
279
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800280 tmp = readl(port_base + U3P_USBPHYACR6);
Chunfeng Yun43f53b12015-12-04 10:08:56 +0800281 tmp &= ~PA6_RG_U2_BC11_SW_EN; /* DP/DM BC1.1 path Disable */
282 tmp &= ~PA6_RG_U2_SQTH;
283 tmp |= PA6_RG_U2_SQTH_VAL(2);
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800284 writel(tmp, port_base + U3P_USBPHYACR6);
285
286 tmp = readl(port_base + U3P_U3PHYA_DA_REG0);
287 tmp &= ~P3A_RG_XTAL_EXT_EN_U3;
288 tmp |= P3A_RG_XTAL_EXT_EN_U3_VAL(2);
289 writel(tmp, port_base + U3P_U3PHYA_DA_REG0);
290
291 tmp = readl(port_base + U3P_U3_PHYA_REG9);
292 tmp &= ~P3A_RG_RX_DAC_MUX;
293 tmp |= P3A_RG_RX_DAC_MUX_VAL(4);
294 writel(tmp, port_base + U3P_U3_PHYA_REG9);
295
296 tmp = readl(port_base + U3P_U3_PHYA_REG6);
297 tmp &= ~P3A_RG_TX_EIDLE_CM;
298 tmp |= P3A_RG_TX_EIDLE_CM_VAL(0xe);
299 writel(tmp, port_base + U3P_U3_PHYA_REG6);
300
301 tmp = readl(port_base + U3P_PHYD_CDR1);
302 tmp &= ~(P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1);
303 tmp |= P3D_RG_CDR_BIR_LTD0_VAL(0xc) | P3D_RG_CDR_BIR_LTD1_VAL(0x3);
304 writel(tmp, port_base + U3P_PHYD_CDR1);
305
Chunfeng Yun1969f692017-03-31 15:35:27 +0800306 tmp = readl(port_base + U3P_U3_PHYD_RXDET1);
307 tmp &= ~P3D_RG_RXDET_STB2_SET;
308 tmp |= P3D_RG_RXDET_STB2_SET_VAL(0x10);
309 writel(tmp, port_base + U3P_U3_PHYD_RXDET1);
310
311 tmp = readl(port_base + U3P_U3_PHYD_RXDET2);
312 tmp &= ~P3D_RG_RXDET_STB2_SET_P3;
313 tmp |= P3D_RG_RXDET_STB2_SET_P3_VAL(0x10);
314 writel(tmp, port_base + U3P_U3_PHYD_RXDET2);
315
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800316 dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
317}
318
319static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
320 struct mt65xx_phy_instance *instance)
321{
322 void __iomem *port_base = instance->port_base;
323 u32 index = instance->index;
324 u32 tmp;
325
326 if (!index) {
327 /* Set RG_SSUSB_VUSB10_ON as 1 after VUSB10 ready */
328 tmp = readl(port_base + U3P_U3_PHYA_REG0);
329 tmp |= P3A_RG_U3_VUSB10_ON;
330 writel(tmp, port_base + U3P_U3_PHYA_REG0);
331 }
332
333 /* (force_suspendm=0) (let suspendm=1, enable usb 480MHz pll) */
334 tmp = readl(port_base + U3P_U2PHYDTM0);
335 tmp &= ~(P2C_FORCE_SUSPENDM | P2C_RG_XCVRSEL);
336 tmp &= ~(P2C_RG_DATAIN | P2C_DTM0_PART_MASK);
337 writel(tmp, port_base + U3P_U2PHYDTM0);
338
339 /* OTG Enable */
340 tmp = readl(port_base + U3P_USBPHYACR6);
341 tmp |= PA6_RG_U2_OTG_VBUSCMP_EN;
342 writel(tmp, port_base + U3P_USBPHYACR6);
343
344 if (!index) {
345 tmp = readl(u3phy->sif_base + U3P_XTALCTL3);
346 tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD;
347 writel(tmp, u3phy->sif_base + U3P_XTALCTL3);
348
Chunfeng Yune1d76532016-04-20 08:14:02 +0800349 /* switch 100uA current to SSUSB */
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800350 tmp = readl(port_base + U3P_USBPHYACR5);
Chunfeng Yun75f072f2015-12-04 10:11:05 +0800351 tmp |= PA5_RG_U2_HS_100U_U3_EN;
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800352 writel(tmp, port_base + U3P_USBPHYACR5);
353 }
354
355 tmp = readl(port_base + U3P_U2PHYDTM1);
356 tmp |= P2C_RG_VBUSVALID | P2C_RG_AVALID;
357 tmp &= ~P2C_RG_SESSEND;
358 writel(tmp, port_base + U3P_U2PHYDTM1);
359
360 /* USB 2.0 slew rate calibration */
361 tmp = readl(port_base + U3P_USBPHYACR5);
362 tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
363 tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(4);
364 writel(tmp, port_base + U3P_USBPHYACR5);
365
Chunfeng Yune1d76532016-04-20 08:14:02 +0800366 if (u3phy->pdata->avoid_rx_sen_degradation && index) {
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800367 tmp = readl(port_base + U3D_U2PHYDCR0);
368 tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
369 writel(tmp, port_base + U3D_U2PHYDCR0);
370
371 tmp = readl(port_base + U3P_U2PHYDTM0);
372 tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
373 writel(tmp, port_base + U3P_U2PHYDTM0);
374 }
375 dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
376}
377
378static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
379 struct mt65xx_phy_instance *instance)
380{
381 void __iomem *port_base = instance->port_base;
382 u32 index = instance->index;
383 u32 tmp;
384
385 tmp = readl(port_base + U3P_U2PHYDTM0);
386 tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN);
387 tmp |= P2C_FORCE_SUSPENDM;
388 writel(tmp, port_base + U3P_U2PHYDTM0);
389
390 /* OTG Disable */
391 tmp = readl(port_base + U3P_USBPHYACR6);
392 tmp &= ~PA6_RG_U2_OTG_VBUSCMP_EN;
393 writel(tmp, port_base + U3P_USBPHYACR6);
394
395 if (!index) {
Chunfeng Yun75f072f2015-12-04 10:11:05 +0800396 /* switch 100uA current back to USB2.0 */
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800397 tmp = readl(port_base + U3P_USBPHYACR5);
398 tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
399 writel(tmp, port_base + U3P_USBPHYACR5);
400 }
401
402 /* let suspendm=0, set utmi into analog power down */
403 tmp = readl(port_base + U3P_U2PHYDTM0);
404 tmp &= ~P2C_RG_SUSPENDM;
405 writel(tmp, port_base + U3P_U2PHYDTM0);
406 udelay(1);
407
408 tmp = readl(port_base + U3P_U2PHYDTM1);
409 tmp &= ~(P2C_RG_VBUSVALID | P2C_RG_AVALID);
410 tmp |= P2C_RG_SESSEND;
411 writel(tmp, port_base + U3P_U2PHYDTM1);
412
413 if (!index) {
414 tmp = readl(port_base + U3P_U3_PHYA_REG0);
415 tmp &= ~P3A_RG_U3_VUSB10_ON;
416 writel(tmp, port_base + U3P_U3_PHYA_REG0);
Chunfeng Yune1d76532016-04-20 08:14:02 +0800417 }
418
419 if (u3phy->pdata->avoid_rx_sen_degradation && index) {
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800420 tmp = readl(port_base + U3D_U2PHYDCR0);
421 tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
422 writel(tmp, port_base + U3D_U2PHYDCR0);
423 }
424
425 dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
426}
427
428static void phy_instance_exit(struct mt65xx_u3phy *u3phy,
429 struct mt65xx_phy_instance *instance)
430{
431 void __iomem *port_base = instance->port_base;
432 u32 index = instance->index;
433 u32 tmp;
434
Chunfeng Yune1d76532016-04-20 08:14:02 +0800435 if (u3phy->pdata->avoid_rx_sen_degradation && index) {
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800436 tmp = readl(port_base + U3D_U2PHYDCR0);
437 tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
438 writel(tmp, port_base + U3D_U2PHYDCR0);
439
440 tmp = readl(port_base + U3P_U2PHYDTM0);
441 tmp &= ~P2C_FORCE_SUSPENDM;
442 writel(tmp, port_base + U3P_U2PHYDTM0);
443 }
444}
445
446static int mt65xx_phy_init(struct phy *phy)
447{
448 struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
449 struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
450 int ret;
451
452 ret = clk_prepare_enable(u3phy->u3phya_ref);
453 if (ret) {
454 dev_err(u3phy->dev, "failed to enable u3phya_ref\n");
455 return ret;
456 }
457
458 phy_instance_init(u3phy, instance);
459 return 0;
460}
461
462static int mt65xx_phy_power_on(struct phy *phy)
463{
464 struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
465 struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
466
467 phy_instance_power_on(u3phy, instance);
Chunfeng Yun75f072f2015-12-04 10:11:05 +0800468 hs_slew_rate_calibrate(u3phy, instance);
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800469 return 0;
470}
471
472static int mt65xx_phy_power_off(struct phy *phy)
473{
474 struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
475 struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
476
477 phy_instance_power_off(u3phy, instance);
478 return 0;
479}
480
481static int mt65xx_phy_exit(struct phy *phy)
482{
483 struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
484 struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
485
486 phy_instance_exit(u3phy, instance);
487 clk_disable_unprepare(u3phy->u3phya_ref);
488 return 0;
489}
490
491static struct phy *mt65xx_phy_xlate(struct device *dev,
492 struct of_phandle_args *args)
493{
494 struct mt65xx_u3phy *u3phy = dev_get_drvdata(dev);
495 struct mt65xx_phy_instance *instance = NULL;
496 struct device_node *phy_np = args->np;
497 int index;
498
499
500 if (args->args_count != 1) {
501 dev_err(dev, "invalid number of cells in 'phy' property\n");
502 return ERR_PTR(-EINVAL);
503 }
504
505 for (index = 0; index < u3phy->nphys; index++)
506 if (phy_np == u3phy->phys[index]->phy->dev.of_node) {
507 instance = u3phy->phys[index];
508 break;
509 }
510
511 if (!instance) {
512 dev_err(dev, "failed to find appropriate phy\n");
513 return ERR_PTR(-EINVAL);
514 }
515
516 instance->type = args->args[0];
517
518 if (!(instance->type == PHY_TYPE_USB2 ||
519 instance->type == PHY_TYPE_USB3)) {
520 dev_err(dev, "unsupported device type: %d\n", instance->type);
521 return ERR_PTR(-EINVAL);
522 }
523
524 return instance->phy;
525}
526
Bhumika Goyala8df2762017-01-08 16:05:56 +0530527static const struct phy_ops mt65xx_u3phy_ops = {
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800528 .init = mt65xx_phy_init,
529 .exit = mt65xx_phy_exit,
530 .power_on = mt65xx_phy_power_on,
531 .power_off = mt65xx_phy_power_off,
532 .owner = THIS_MODULE,
533};
534
Chunfeng Yune1d76532016-04-20 08:14:02 +0800535static const struct mt65xx_phy_pdata mt2701_pdata = {
536 .avoid_rx_sen_degradation = false,
537};
538
539static const struct mt65xx_phy_pdata mt8173_pdata = {
540 .avoid_rx_sen_degradation = true,
541};
542
543static const struct of_device_id mt65xx_u3phy_id_table[] = {
544 { .compatible = "mediatek,mt2701-u3phy", .data = &mt2701_pdata },
545 { .compatible = "mediatek,mt8173-u3phy", .data = &mt8173_pdata },
546 { },
547};
548MODULE_DEVICE_TABLE(of, mt65xx_u3phy_id_table);
549
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800550static int mt65xx_u3phy_probe(struct platform_device *pdev)
551{
Chunfeng Yune1d76532016-04-20 08:14:02 +0800552 const struct of_device_id *match;
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800553 struct device *dev = &pdev->dev;
554 struct device_node *np = dev->of_node;
555 struct device_node *child_np;
556 struct phy_provider *provider;
557 struct resource *sif_res;
558 struct mt65xx_u3phy *u3phy;
559 struct resource res;
Julia Lawall2bb80cc2015-11-16 12:33:15 +0100560 int port, retval;
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800561
Chunfeng Yune1d76532016-04-20 08:14:02 +0800562 match = of_match_node(mt65xx_u3phy_id_table, pdev->dev.of_node);
563 if (!match)
564 return -EINVAL;
565
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800566 u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL);
567 if (!u3phy)
568 return -ENOMEM;
569
Chunfeng Yune1d76532016-04-20 08:14:02 +0800570 u3phy->pdata = match->data;
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800571 u3phy->nphys = of_get_child_count(np);
572 u3phy->phys = devm_kcalloc(dev, u3phy->nphys,
573 sizeof(*u3phy->phys), GFP_KERNEL);
574 if (!u3phy->phys)
575 return -ENOMEM;
576
577 u3phy->dev = dev;
578 platform_set_drvdata(pdev, u3phy);
579
580 sif_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
581 u3phy->sif_base = devm_ioremap_resource(dev, sif_res);
582 if (IS_ERR(u3phy->sif_base)) {
583 dev_err(dev, "failed to remap sif regs\n");
584 return PTR_ERR(u3phy->sif_base);
585 }
586
587 u3phy->u3phya_ref = devm_clk_get(dev, "u3phya_ref");
588 if (IS_ERR(u3phy->u3phya_ref)) {
589 dev_err(dev, "error to get u3phya_ref\n");
590 return PTR_ERR(u3phy->u3phya_ref);
591 }
592
593 port = 0;
594 for_each_child_of_node(np, child_np) {
595 struct mt65xx_phy_instance *instance;
596 struct phy *phy;
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800597
598 instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL);
Julia Lawall2bb80cc2015-11-16 12:33:15 +0100599 if (!instance) {
600 retval = -ENOMEM;
601 goto put_child;
602 }
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800603
604 u3phy->phys[port] = instance;
605
606 phy = devm_phy_create(dev, child_np, &mt65xx_u3phy_ops);
607 if (IS_ERR(phy)) {
608 dev_err(dev, "failed to create phy\n");
Julia Lawall2bb80cc2015-11-16 12:33:15 +0100609 retval = PTR_ERR(phy);
610 goto put_child;
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800611 }
612
613 retval = of_address_to_resource(child_np, 0, &res);
614 if (retval) {
615 dev_err(dev, "failed to get address resource(id-%d)\n",
616 port);
Julia Lawall2bb80cc2015-11-16 12:33:15 +0100617 goto put_child;
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800618 }
619
620 instance->port_base = devm_ioremap_resource(&phy->dev, &res);
621 if (IS_ERR(instance->port_base)) {
622 dev_err(dev, "failed to remap phy regs\n");
Julia Lawall2bb80cc2015-11-16 12:33:15 +0100623 retval = PTR_ERR(instance->port_base);
624 goto put_child;
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800625 }
626
627 instance->phy = phy;
628 instance->index = port;
629 phy_set_drvdata(phy, instance);
630 port++;
631 }
632
633 provider = devm_of_phy_provider_register(dev, mt65xx_phy_xlate);
634
635 return PTR_ERR_OR_ZERO(provider);
Julia Lawall2bb80cc2015-11-16 12:33:15 +0100636put_child:
637 of_node_put(child_np);
638 return retval;
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800639}
640
Chunfeng Yundc7f1902015-09-29 11:01:36 +0800641static struct platform_driver mt65xx_u3phy_driver = {
642 .probe = mt65xx_u3phy_probe,
643 .driver = {
644 .name = "mt65xx-u3phy",
645 .of_match_table = mt65xx_u3phy_id_table,
646 },
647};
648
649module_platform_driver(mt65xx_u3phy_driver);
650
651MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
652MODULE_DESCRIPTION("mt65xx USB PHY driver");
653MODULE_LICENSE("GPL v2");