aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChunfeng Yun <chunfeng.yun@mediatek.com>2015-09-28 23:01:36 -0400
committerKishon Vijay Abraham I <kishon@ti.com>2015-10-06 10:51:47 -0400
commitdc7f190fd51f5c64d7d9280e5f052b46ce8cee24 (patch)
tree933142ebfa1bee2e433dae86eca27e6b4425e579
parent538b2a4e96990a4839086cc47b657a12b1364d3c (diff)
phy: add usb3.0 phy driver for mt65xx SoCs
support usb3.0 phy of mt65xx SoCs Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
-rw-r--r--drivers/phy/Kconfig9
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-mt65xx-usb3.c506
3 files changed, 516 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 47da573d0bab..ec436c1e3d7e 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -206,6 +206,15 @@ config PHY_HIX5HD2_SATA
206 help 206 help
207 Support for SATA PHY on Hisilicon hix5hd2 Soc. 207 Support for SATA PHY on Hisilicon hix5hd2 Soc.
208 208
209config PHY_MT65XX_USB3
210 tristate "Mediatek USB3.0 PHY Driver"
211 depends on ARCH_MEDIATEK && OF
212 select GENERIC_PHY
213 help
214 Say 'Y' here to add support for Mediatek USB3.0 PHY driver
215 for mt65xx SoCs. it supports two usb2.0 ports and
216 one usb3.0 port.
217
209config PHY_SUN4I_USB 218config PHY_SUN4I_USB
210 tristate "Allwinner sunxi SoC USB PHY driver" 219 tristate "Allwinner sunxi SoC USB PHY driver"
211 depends on ARCH_SUNXI && HAS_IOMEM && OF 220 depends on ARCH_SUNXI && HAS_IOMEM && OF
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index a5b18c18fc12..a7cc629451ea 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
23obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o 23obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
24obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o 24obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
25obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o 25obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
26obj-$(CONFIG_PHY_MT65XX_USB3) += phy-mt65xx-usb3.o
26obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o 27obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
27obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o 28obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o
28obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o 29obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o
diff --git a/drivers/phy/phy-mt65xx-usb3.c b/drivers/phy/phy-mt65xx-usb3.c
new file mode 100644
index 000000000000..f30b28bd41fe
--- /dev/null
+++ b/drivers/phy/phy-mt65xx-usb3.c
@@ -0,0 +1,506 @@
1/*
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>
20#include <linux/module.h>
21#include <linux/of_address.h>
22#include <linux/phy/phy.h>
23#include <linux/platform_device.h>
24
25/*
26 * for sifslv2 register, but exclude port's;
27 * relative to USB3_SIF2_BASE base address
28 */
29#define SSUSB_SIFSLV_SPLLC 0x0000
30
31/* offsets of sub-segment in each port registers */
32#define SSUSB_SIFSLV_U2PHY_COM_BASE 0x0000
33#define SSUSB_SIFSLV_U3PHYD_BASE 0x0100
34#define SSUSB_USB30_PHYA_SIV_B_BASE 0x0300
35#define SSUSB_SIFSLV_U3PHYA_DA_BASE 0x0400
36
37#define U3P_USBPHYACR0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0000)
38#define PA0_RG_U2PLL_FORCE_ON BIT(15)
39
40#define U3P_USBPHYACR2 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0008)
41#define PA2_RG_SIF_U2PLL_FORCE_EN BIT(18)
42
43#define U3P_USBPHYACR5 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0014)
44#define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12)
45#define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12)
46#define PA5_RG_U2_HS_100U_U3_EN BIT(11)
47
48#define U3P_USBPHYACR6 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0018)
49#define PA6_RG_U2_ISO_EN BIT(31)
50#define PA6_RG_U2_BC11_SW_EN BIT(23)
51#define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20)
52
53#define U3P_U2PHYACR4 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0020)
54#define P2C_RG_USB20_GPIO_CTL BIT(9)
55#define P2C_USB20_GPIO_MODE BIT(8)
56#define P2C_U2_GPIO_CTR_MSK (P2C_RG_USB20_GPIO_CTL | P2C_USB20_GPIO_MODE)
57
58#define U3D_U2PHYDCR0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0060)
59#define P2C_RG_SIF_U2PLL_FORCE_ON BIT(24)
60
61#define U3P_U2PHYDTM0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0068)
62#define P2C_FORCE_UART_EN BIT(26)
63#define P2C_FORCE_DATAIN BIT(23)
64#define P2C_FORCE_DM_PULLDOWN BIT(21)
65#define P2C_FORCE_DP_PULLDOWN BIT(20)
66#define P2C_FORCE_XCVRSEL BIT(19)
67#define P2C_FORCE_SUSPENDM BIT(18)
68#define P2C_FORCE_TERMSEL BIT(17)
69#define P2C_RG_DATAIN GENMASK(13, 10)
70#define P2C_RG_DATAIN_VAL(x) ((0xf & (x)) << 10)
71#define P2C_RG_DMPULLDOWN BIT(7)
72#define P2C_RG_DPPULLDOWN BIT(6)
73#define P2C_RG_XCVRSEL GENMASK(5, 4)
74#define P2C_RG_XCVRSEL_VAL(x) ((0x3 & (x)) << 4)
75#define P2C_RG_SUSPENDM BIT(3)
76#define P2C_RG_TERMSEL BIT(2)
77#define P2C_DTM0_PART_MASK \
78 (P2C_FORCE_DATAIN | P2C_FORCE_DM_PULLDOWN | \
79 P2C_FORCE_DP_PULLDOWN | P2C_FORCE_XCVRSEL | \
80 P2C_FORCE_TERMSEL | P2C_RG_DMPULLDOWN | \
81 P2C_RG_DPPULLDOWN | P2C_RG_TERMSEL)
82
83#define U3P_U2PHYDTM1 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x006C)
84#define P2C_RG_UART_EN BIT(16)
85#define P2C_RG_VBUSVALID BIT(5)
86#define P2C_RG_SESSEND BIT(4)
87#define P2C_RG_AVALID BIT(2)
88
89#define U3P_U3_PHYA_REG0 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0000)
90#define P3A_RG_U3_VUSB10_ON BIT(5)
91
92#define U3P_U3_PHYA_REG6 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0018)
93#define P3A_RG_TX_EIDLE_CM GENMASK(31, 28)
94#define P3A_RG_TX_EIDLE_CM_VAL(x) ((0xf & (x)) << 28)
95
96#define U3P_U3_PHYA_REG9 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0024)
97#define P3A_RG_RX_DAC_MUX GENMASK(5, 1)
98#define P3A_RG_RX_DAC_MUX_VAL(x) ((0x1f & (x)) << 1)
99
100#define U3P_U3PHYA_DA_REG0 (SSUSB_SIFSLV_U3PHYA_DA_BASE + 0x0000)
101#define P3A_RG_XTAL_EXT_EN_U3 GENMASK(11, 10)
102#define P3A_RG_XTAL_EXT_EN_U3_VAL(x) ((0x3 & (x)) << 10)
103
104#define U3P_PHYD_CDR1 (SSUSB_SIFSLV_U3PHYD_BASE + 0x005c)
105#define P3D_RG_CDR_BIR_LTD1 GENMASK(28, 24)
106#define P3D_RG_CDR_BIR_LTD1_VAL(x) ((0x1f & (x)) << 24)
107#define P3D_RG_CDR_BIR_LTD0 GENMASK(12, 8)
108#define P3D_RG_CDR_BIR_LTD0_VAL(x) ((0x1f & (x)) << 8)
109
110#define U3P_XTALCTL3 (SSUSB_SIFSLV_SPLLC + 0x0018)
111#define XC3_RG_U3_XTAL_RX_PWD BIT(9)
112#define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8)
113
114struct mt65xx_phy_instance {
115 struct phy *phy;
116 void __iomem *port_base;
117 u32 index;
118 u8 type;
119};
120
121struct mt65xx_u3phy {
122 struct device *dev;
123 void __iomem *sif_base; /* include sif2, but exclude port's */
124 struct clk *u3phya_ref; /* reference clock of usb3 anolog phy */
125 struct mt65xx_phy_instance **phys;
126 int nphys;
127};
128
129static void phy_instance_init(struct mt65xx_u3phy *u3phy,
130 struct mt65xx_phy_instance *instance)
131{
132 void __iomem *port_base = instance->port_base;
133 u32 index = instance->index;
134 u32 tmp;
135
136 /* switch to USB function. (system register, force ip into usb mode) */
137 tmp = readl(port_base + U3P_U2PHYDTM0);
138 tmp &= ~P2C_FORCE_UART_EN;
139 tmp |= P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0);
140 writel(tmp, port_base + U3P_U2PHYDTM0);
141
142 tmp = readl(port_base + U3P_U2PHYDTM1);
143 tmp &= ~P2C_RG_UART_EN;
144 writel(tmp, port_base + U3P_U2PHYDTM1);
145
146 if (!index) {
147 tmp = readl(port_base + U3P_U2PHYACR4);
148 tmp &= ~P2C_U2_GPIO_CTR_MSK;
149 writel(tmp, port_base + U3P_U2PHYACR4);
150
151 tmp = readl(port_base + U3P_USBPHYACR2);
152 tmp |= PA2_RG_SIF_U2PLL_FORCE_EN;
153 writel(tmp, port_base + U3P_USBPHYACR2);
154
155 tmp = readl(port_base + U3D_U2PHYDCR0);
156 tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
157 writel(tmp, port_base + U3D_U2PHYDCR0);
158 } else {
159 tmp = readl(port_base + U3D_U2PHYDCR0);
160 tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
161 writel(tmp, port_base + U3D_U2PHYDCR0);
162
163 tmp = readl(port_base + U3P_U2PHYDTM0);
164 tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
165 writel(tmp, port_base + U3P_U2PHYDTM0);
166 }
167
168 /* DP/DM BC1.1 path Disable */
169 tmp = readl(port_base + U3P_USBPHYACR6);
170 tmp &= ~PA6_RG_U2_BC11_SW_EN;
171 writel(tmp, port_base + U3P_USBPHYACR6);
172
173 tmp = readl(port_base + U3P_U3PHYA_DA_REG0);
174 tmp &= ~P3A_RG_XTAL_EXT_EN_U3;
175 tmp |= P3A_RG_XTAL_EXT_EN_U3_VAL(2);
176 writel(tmp, port_base + U3P_U3PHYA_DA_REG0);
177
178 tmp = readl(port_base + U3P_U3_PHYA_REG9);
179 tmp &= ~P3A_RG_RX_DAC_MUX;
180 tmp |= P3A_RG_RX_DAC_MUX_VAL(4);
181 writel(tmp, port_base + U3P_U3_PHYA_REG9);
182
183 tmp = readl(port_base + U3P_U3_PHYA_REG6);
184 tmp &= ~P3A_RG_TX_EIDLE_CM;
185 tmp |= P3A_RG_TX_EIDLE_CM_VAL(0xe);
186 writel(tmp, port_base + U3P_U3_PHYA_REG6);
187
188 tmp = readl(port_base + U3P_PHYD_CDR1);
189 tmp &= ~(P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1);
190 tmp |= P3D_RG_CDR_BIR_LTD0_VAL(0xc) | P3D_RG_CDR_BIR_LTD1_VAL(0x3);
191 writel(tmp, port_base + U3P_PHYD_CDR1);
192
193 dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
194}
195
196static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
197 struct mt65xx_phy_instance *instance)
198{
199 void __iomem *port_base = instance->port_base;
200 u32 index = instance->index;
201 u32 tmp;
202
203 if (!index) {
204 /* Set RG_SSUSB_VUSB10_ON as 1 after VUSB10 ready */
205 tmp = readl(port_base + U3P_U3_PHYA_REG0);
206 tmp |= P3A_RG_U3_VUSB10_ON;
207 writel(tmp, port_base + U3P_U3_PHYA_REG0);
208 }
209
210 /* (force_suspendm=0) (let suspendm=1, enable usb 480MHz pll) */
211 tmp = readl(port_base + U3P_U2PHYDTM0);
212 tmp &= ~(P2C_FORCE_SUSPENDM | P2C_RG_XCVRSEL);
213 tmp &= ~(P2C_RG_DATAIN | P2C_DTM0_PART_MASK);
214 writel(tmp, port_base + U3P_U2PHYDTM0);
215
216 /* OTG Enable */
217 tmp = readl(port_base + U3P_USBPHYACR6);
218 tmp |= PA6_RG_U2_OTG_VBUSCMP_EN;
219 writel(tmp, port_base + U3P_USBPHYACR6);
220
221 if (!index) {
222 tmp = readl(u3phy->sif_base + U3P_XTALCTL3);
223 tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD;
224 writel(tmp, u3phy->sif_base + U3P_XTALCTL3);
225
226 /* [mt8173]disable Change 100uA current from SSUSB */
227 tmp = readl(port_base + U3P_USBPHYACR5);
228 tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
229 writel(tmp, port_base + U3P_USBPHYACR5);
230 }
231
232 tmp = readl(port_base + U3P_U2PHYDTM1);
233 tmp |= P2C_RG_VBUSVALID | P2C_RG_AVALID;
234 tmp &= ~P2C_RG_SESSEND;
235 writel(tmp, port_base + U3P_U2PHYDTM1);
236
237 /* USB 2.0 slew rate calibration */
238 tmp = readl(port_base + U3P_USBPHYACR5);
239 tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
240 tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(4);
241 writel(tmp, port_base + U3P_USBPHYACR5);
242
243 if (index) {
244 tmp = readl(port_base + U3D_U2PHYDCR0);
245 tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
246 writel(tmp, port_base + U3D_U2PHYDCR0);
247
248 tmp = readl(port_base + U3P_U2PHYDTM0);
249 tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
250 writel(tmp, port_base + U3P_U2PHYDTM0);
251 }
252 dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
253}
254
255static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
256 struct mt65xx_phy_instance *instance)
257{
258 void __iomem *port_base = instance->port_base;
259 u32 index = instance->index;
260 u32 tmp;
261
262 tmp = readl(port_base + U3P_U2PHYDTM0);
263 tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN);
264 tmp |= P2C_FORCE_SUSPENDM;
265 writel(tmp, port_base + U3P_U2PHYDTM0);
266
267 /* OTG Disable */
268 tmp = readl(port_base + U3P_USBPHYACR6);
269 tmp &= ~PA6_RG_U2_OTG_VBUSCMP_EN;
270 writel(tmp, port_base + U3P_USBPHYACR6);
271
272 if (!index) {
273 /* (also disable)Change 100uA current switch to USB2.0 */
274 tmp = readl(port_base + U3P_USBPHYACR5);
275 tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
276 writel(tmp, port_base + U3P_USBPHYACR5);
277 }
278
279 /* let suspendm=0, set utmi into analog power down */
280 tmp = readl(port_base + U3P_U2PHYDTM0);
281 tmp &= ~P2C_RG_SUSPENDM;
282 writel(tmp, port_base + U3P_U2PHYDTM0);
283 udelay(1);
284
285 tmp = readl(port_base + U3P_U2PHYDTM1);
286 tmp &= ~(P2C_RG_VBUSVALID | P2C_RG_AVALID);
287 tmp |= P2C_RG_SESSEND;
288 writel(tmp, port_base + U3P_U2PHYDTM1);
289
290 if (!index) {
291 tmp = readl(port_base + U3P_U3_PHYA_REG0);
292 tmp &= ~P3A_RG_U3_VUSB10_ON;
293 writel(tmp, port_base + U3P_U3_PHYA_REG0);
294 } else {
295 tmp = readl(port_base + U3D_U2PHYDCR0);
296 tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
297 writel(tmp, port_base + U3D_U2PHYDCR0);
298 }
299
300 dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
301}
302
303static void phy_instance_exit(struct mt65xx_u3phy *u3phy,
304 struct mt65xx_phy_instance *instance)
305{
306 void __iomem *port_base = instance->port_base;
307 u32 index = instance->index;
308 u32 tmp;
309
310 if (index) {
311 tmp = readl(port_base + U3D_U2PHYDCR0);
312 tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
313 writel(tmp, port_base + U3D_U2PHYDCR0);
314
315 tmp = readl(port_base + U3P_U2PHYDTM0);
316 tmp &= ~P2C_FORCE_SUSPENDM;
317 writel(tmp, port_base + U3P_U2PHYDTM0);
318 }
319}
320
321static int mt65xx_phy_init(struct phy *phy)
322{
323 struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
324 struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
325 int ret;
326
327 ret = clk_prepare_enable(u3phy->u3phya_ref);
328 if (ret) {
329 dev_err(u3phy->dev, "failed to enable u3phya_ref\n");
330 return ret;
331 }
332
333 phy_instance_init(u3phy, instance);
334 return 0;
335}
336
337static int mt65xx_phy_power_on(struct phy *phy)
338{
339 struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
340 struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
341
342 phy_instance_power_on(u3phy, instance);
343 return 0;
344}
345
346static int mt65xx_phy_power_off(struct phy *phy)
347{
348 struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
349 struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
350
351 phy_instance_power_off(u3phy, instance);
352 return 0;
353}
354
355static int mt65xx_phy_exit(struct phy *phy)
356{
357 struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
358 struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
359
360 phy_instance_exit(u3phy, instance);
361 clk_disable_unprepare(u3phy->u3phya_ref);
362 return 0;
363}
364
365static struct phy *mt65xx_phy_xlate(struct device *dev,
366 struct of_phandle_args *args)
367{
368 struct mt65xx_u3phy *u3phy = dev_get_drvdata(dev);
369 struct mt65xx_phy_instance *instance = NULL;
370 struct device_node *phy_np = args->np;
371 int index;
372
373
374 if (args->args_count != 1) {
375 dev_err(dev, "invalid number of cells in 'phy' property\n");
376 return ERR_PTR(-EINVAL);
377 }
378
379 for (index = 0; index < u3phy->nphys; index++)
380 if (phy_np == u3phy->phys[index]->phy->dev.of_node) {
381 instance = u3phy->phys[index];
382 break;
383 }
384
385 if (!instance) {
386 dev_err(dev, "failed to find appropriate phy\n");
387 return ERR_PTR(-EINVAL);
388 }
389
390 instance->type = args->args[0];
391
392 if (!(instance->type == PHY_TYPE_USB2 ||
393 instance->type == PHY_TYPE_USB3)) {
394 dev_err(dev, "unsupported device type: %d\n", instance->type);
395 return ERR_PTR(-EINVAL);
396 }
397
398 return instance->phy;
399}
400
401static struct phy_ops mt65xx_u3phy_ops = {
402 .init = mt65xx_phy_init,
403 .exit = mt65xx_phy_exit,
404 .power_on = mt65xx_phy_power_on,
405 .power_off = mt65xx_phy_power_off,
406 .owner = THIS_MODULE,
407};
408
409static int mt65xx_u3phy_probe(struct platform_device *pdev)
410{
411 struct device *dev = &pdev->dev;
412 struct device_node *np = dev->of_node;
413 struct device_node *child_np;
414 struct phy_provider *provider;
415 struct resource *sif_res;
416 struct mt65xx_u3phy *u3phy;
417 struct resource res;
418 int port;
419
420 u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL);
421 if (!u3phy)
422 return -ENOMEM;
423
424 u3phy->nphys = of_get_child_count(np);
425 u3phy->phys = devm_kcalloc(dev, u3phy->nphys,
426 sizeof(*u3phy->phys), GFP_KERNEL);
427 if (!u3phy->phys)
428 return -ENOMEM;
429
430 u3phy->dev = dev;
431 platform_set_drvdata(pdev, u3phy);
432
433 sif_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
434 u3phy->sif_base = devm_ioremap_resource(dev, sif_res);
435 if (IS_ERR(u3phy->sif_base)) {
436 dev_err(dev, "failed to remap sif regs\n");
437 return PTR_ERR(u3phy->sif_base);
438 }
439
440 u3phy->u3phya_ref = devm_clk_get(dev, "u3phya_ref");
441 if (IS_ERR(u3phy->u3phya_ref)) {
442 dev_err(dev, "error to get u3phya_ref\n");
443 return PTR_ERR(u3phy->u3phya_ref);
444 }
445
446 port = 0;
447 for_each_child_of_node(np, child_np) {
448 struct mt65xx_phy_instance *instance;
449 struct phy *phy;
450 int retval;
451
452 instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL);
453 if (!instance)
454 return -ENOMEM;
455
456 u3phy->phys[port] = instance;
457
458 phy = devm_phy_create(dev, child_np, &mt65xx_u3phy_ops);
459 if (IS_ERR(phy)) {
460 dev_err(dev, "failed to create phy\n");
461 return PTR_ERR(phy);
462 }
463
464 retval = of_address_to_resource(child_np, 0, &res);
465 if (retval) {
466 dev_err(dev, "failed to get address resource(id-%d)\n",
467 port);
468 return retval;
469 }
470
471 instance->port_base = devm_ioremap_resource(&phy->dev, &res);
472 if (IS_ERR(instance->port_base)) {
473 dev_err(dev, "failed to remap phy regs\n");
474 return PTR_ERR(instance->port_base);
475 }
476
477 instance->phy = phy;
478 instance->index = port;
479 phy_set_drvdata(phy, instance);
480 port++;
481 }
482
483 provider = devm_of_phy_provider_register(dev, mt65xx_phy_xlate);
484
485 return PTR_ERR_OR_ZERO(provider);
486}
487
488static const struct of_device_id mt65xx_u3phy_id_table[] = {
489 { .compatible = "mediatek,mt8173-u3phy", },
490 { },
491};
492MODULE_DEVICE_TABLE(of, mt65xx_u3phy_id_table);
493
494static struct platform_driver mt65xx_u3phy_driver = {
495 .probe = mt65xx_u3phy_probe,
496 .driver = {
497 .name = "mt65xx-u3phy",
498 .of_match_table = mt65xx_u3phy_id_table,
499 },
500};
501
502module_platform_driver(mt65xx_u3phy_driver);
503
504MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
505MODULE_DESCRIPTION("mt65xx USB PHY driver");
506MODULE_LICENSE("GPL v2");