1 /* linux/drivers/usb/phy/phy-samsung-usb2.c
3 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
6 * Author: Praveen Paneri <p.paneri@samsung.com>
8 * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and
9 * OHCI-EXYNOS controllers.
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include <linux/module.h>
22 #include <linux/platform_device.h>
23 #include <linux/clk.h>
24 #include <linux/delay.h>
25 #include <linux/device.h>
26 #include <linux/err.h>
29 #include <linux/usb/otg.h>
30 #include <linux/usb/samsung_usb_phy.h>
31 #include <linux/platform_data/samsung-usbphy.h>
33 #include "phy-samsung-usb.h"
35 static int samsung_usbphy_set_host(struct usb_otg
*otg
, struct usb_bus
*host
)
46 static bool exynos5_phyhost_is_on(void __iomem
*regs
)
50 reg
= readl(regs
+ EXYNOS5_PHY_HOST_CTRL0
);
52 return !(reg
& HOST_CTRL0_SIDDQ
);
55 static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy
*sphy
)
57 void __iomem
*regs
= sphy
->regs
;
58 u32 phyclk
= sphy
->ref_clk_freq
;
66 * phy_usage helps in keeping usage count for phy
67 * so that the first consumer enabling the phy is also
68 * the last consumer to disable it.
71 atomic_inc(&sphy
->phy_usage
);
73 if (exynos5_phyhost_is_on(regs
)) {
74 dev_info(sphy
->dev
, "Already power on PHY\n");
78 /* Host configuration */
79 phyhost
= readl(regs
+ EXYNOS5_PHY_HOST_CTRL0
);
81 /* phy reference clock configuration */
82 phyhost
&= ~HOST_CTRL0_FSEL_MASK
;
83 phyhost
|= HOST_CTRL0_FSEL(phyclk
);
86 phyhost
&= ~(HOST_CTRL0_PHYSWRST
|
87 HOST_CTRL0_PHYSWRSTALL
|
89 /* Enable normal mode of operation */
90 HOST_CTRL0_FORCESUSPEND
|
91 HOST_CTRL0_FORCESLEEP
);
94 phyhost
|= (HOST_CTRL0_LINKSWRST
|
95 HOST_CTRL0_UTMISWRST
|
96 /* COMMON Block configuration during suspend */
97 HOST_CTRL0_COMMONON_N
);
98 writel(phyhost
, regs
+ EXYNOS5_PHY_HOST_CTRL0
);
100 phyhost
&= ~(HOST_CTRL0_LINKSWRST
|
101 HOST_CTRL0_UTMISWRST
);
102 writel(phyhost
, regs
+ EXYNOS5_PHY_HOST_CTRL0
);
104 /* OTG configuration */
105 phyotg
= readl(regs
+ EXYNOS5_PHY_OTG_SYS
);
107 /* phy reference clock configuration */
108 phyotg
&= ~OTG_SYS_FSEL_MASK
;
109 phyotg
|= OTG_SYS_FSEL(phyclk
);
111 /* Enable normal mode of operation */
112 phyotg
&= ~(OTG_SYS_FORCESUSPEND
|
115 OTG_SYS_REFCLKSEL_MASK
|
116 /* COMMON Block configuration during suspend */
119 /* OTG phy & link reset */
120 phyotg
|= (OTG_SYS_PHY0_SWRST
|
121 OTG_SYS_LINKSWRST_UOTG
|
122 OTG_SYS_PHYLINK_SWRESET
|
125 OTG_SYS_REFCLKSEL_CLKCORE
);
127 writel(phyotg
, regs
+ EXYNOS5_PHY_OTG_SYS
);
129 phyotg
&= ~(OTG_SYS_PHY0_SWRST
|
130 OTG_SYS_LINKSWRST_UOTG
|
131 OTG_SYS_PHYLINK_SWRESET
);
132 writel(phyotg
, regs
+ EXYNOS5_PHY_OTG_SYS
);
134 /* HSIC phy configuration */
135 phyhsic
= (HSIC_CTRL_REFCLKDIV_12
|
136 HSIC_CTRL_REFCLKSEL
|
138 writel(phyhsic
, regs
+ EXYNOS5_PHY_HSIC_CTRL1
);
139 writel(phyhsic
, regs
+ EXYNOS5_PHY_HSIC_CTRL2
);
141 phyhsic
&= ~HSIC_CTRL_PHYSWRST
;
142 writel(phyhsic
, regs
+ EXYNOS5_PHY_HSIC_CTRL1
);
143 writel(phyhsic
, regs
+ EXYNOS5_PHY_HSIC_CTRL2
);
147 /* enable EHCI DMA burst */
148 ehcictrl
= readl(regs
+ EXYNOS5_PHY_HOST_EHCICTRL
);
149 ehcictrl
|= (HOST_EHCICTRL_ENAINCRXALIGN
|
150 HOST_EHCICTRL_ENAINCR4
|
151 HOST_EHCICTRL_ENAINCR8
|
152 HOST_EHCICTRL_ENAINCR16
);
153 writel(ehcictrl
, regs
+ EXYNOS5_PHY_HOST_EHCICTRL
);
155 /* set ohci_suspend_on_n */
156 ohcictrl
= readl(regs
+ EXYNOS5_PHY_HOST_OHCICTRL
);
157 ohcictrl
|= HOST_OHCICTRL_SUSPLGCY
;
158 writel(ohcictrl
, regs
+ EXYNOS5_PHY_HOST_OHCICTRL
);
161 static void samsung_usb2phy_enable(struct samsung_usbphy
*sphy
)
163 void __iomem
*regs
= sphy
->regs
;
168 /* set clock frequency for PLL */
169 phyclk
= sphy
->ref_clk_freq
;
170 phypwr
= readl(regs
+ SAMSUNG_PHYPWR
);
171 rstcon
= readl(regs
+ SAMSUNG_RSTCON
);
173 switch (sphy
->drv_data
->cpu_type
) {
175 phyclk
&= ~PHYCLK_COMMON_ON_N
;
176 phypwr
&= ~PHYPWR_NORMAL_MASK
;
177 rstcon
|= RSTCON_SWRST
;
179 case TYPE_EXYNOS4210
:
180 phypwr
&= ~PHYPWR_NORMAL_MASK_PHY0
;
181 rstcon
|= RSTCON_SWRST
;
186 writel(phyclk
, regs
+ SAMSUNG_PHYCLK
);
187 /* Configure PHY0 for normal operation*/
188 writel(phypwr
, regs
+ SAMSUNG_PHYPWR
);
189 /* reset all ports of PHY and Link */
190 writel(rstcon
, regs
+ SAMSUNG_RSTCON
);
192 rstcon
&= ~RSTCON_SWRST
;
193 writel(rstcon
, regs
+ SAMSUNG_RSTCON
);
196 static void samsung_exynos5_usb2phy_disable(struct samsung_usbphy
*sphy
)
198 void __iomem
*regs
= sphy
->regs
;
203 if (atomic_dec_return(&sphy
->phy_usage
) > 0) {
204 dev_info(sphy
->dev
, "still being used\n");
208 phyhsic
= (HSIC_CTRL_REFCLKDIV_12
|
209 HSIC_CTRL_REFCLKSEL
|
211 HSIC_CTRL_FORCESLEEP
|
212 HSIC_CTRL_FORCESUSPEND
);
213 writel(phyhsic
, regs
+ EXYNOS5_PHY_HSIC_CTRL1
);
214 writel(phyhsic
, regs
+ EXYNOS5_PHY_HSIC_CTRL2
);
216 phyhost
= readl(regs
+ EXYNOS5_PHY_HOST_CTRL0
);
217 phyhost
|= (HOST_CTRL0_SIDDQ
|
218 HOST_CTRL0_FORCESUSPEND
|
219 HOST_CTRL0_FORCESLEEP
|
220 HOST_CTRL0_PHYSWRST
|
221 HOST_CTRL0_PHYSWRSTALL
);
222 writel(phyhost
, regs
+ EXYNOS5_PHY_HOST_CTRL0
);
224 phyotg
= readl(regs
+ EXYNOS5_PHY_OTG_SYS
);
225 phyotg
|= (OTG_SYS_FORCESUSPEND
|
228 writel(phyotg
, regs
+ EXYNOS5_PHY_OTG_SYS
);
231 static void samsung_usb2phy_disable(struct samsung_usbphy
*sphy
)
233 void __iomem
*regs
= sphy
->regs
;
236 phypwr
= readl(regs
+ SAMSUNG_PHYPWR
);
238 switch (sphy
->drv_data
->cpu_type
) {
240 phypwr
|= PHYPWR_NORMAL_MASK
;
242 case TYPE_EXYNOS4210
:
243 phypwr
|= PHYPWR_NORMAL_MASK_PHY0
;
248 /* Disable analog and otg block power */
249 writel(phypwr
, regs
+ SAMSUNG_PHYPWR
);
253 * The function passed to the usb driver for phy initialization
255 static int samsung_usb2phy_init(struct usb_phy
*phy
)
257 struct samsung_usbphy
*sphy
;
258 struct usb_bus
*host
= NULL
;
262 sphy
= phy_to_sphy(phy
);
264 host
= phy
->otg
->host
;
266 /* Enable the phy clock */
267 ret
= clk_prepare_enable(sphy
->clk
);
269 dev_err(sphy
->dev
, "%s: clk_prepare_enable failed\n", __func__
);
273 spin_lock_irqsave(&sphy
->lock
, flags
);
276 /* setting default phy-type for USB 2.0 */
277 if (!strstr(dev_name(host
->controller
), "ehci") ||
278 !strstr(dev_name(host
->controller
), "ohci"))
279 samsung_usbphy_set_type(&sphy
->phy
, USB_PHY_TYPE_HOST
);
281 samsung_usbphy_set_type(&sphy
->phy
, USB_PHY_TYPE_DEVICE
);
284 /* Disable phy isolation */
285 if (sphy
->plat
&& sphy
->plat
->pmu_isolation
)
286 sphy
->plat
->pmu_isolation(false);
288 samsung_usbphy_set_isolation(sphy
, false);
290 /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */
291 samsung_usbphy_cfg_sel(sphy
);
293 /* Initialize usb phy registers */
294 if (sphy
->drv_data
->cpu_type
== TYPE_EXYNOS5250
)
295 samsung_exynos5_usb2phy_enable(sphy
);
297 samsung_usb2phy_enable(sphy
);
299 spin_unlock_irqrestore(&sphy
->lock
, flags
);
301 /* Disable the phy clock */
302 clk_disable_unprepare(sphy
->clk
);
308 * The function passed to the usb driver for phy shutdown
310 static void samsung_usb2phy_shutdown(struct usb_phy
*phy
)
312 struct samsung_usbphy
*sphy
;
313 struct usb_bus
*host
= NULL
;
316 sphy
= phy_to_sphy(phy
);
318 host
= phy
->otg
->host
;
320 if (clk_prepare_enable(sphy
->clk
)) {
321 dev_err(sphy
->dev
, "%s: clk_prepare_enable failed\n", __func__
);
325 spin_lock_irqsave(&sphy
->lock
, flags
);
328 /* setting default phy-type for USB 2.0 */
329 if (!strstr(dev_name(host
->controller
), "ehci") ||
330 !strstr(dev_name(host
->controller
), "ohci"))
331 samsung_usbphy_set_type(&sphy
->phy
, USB_PHY_TYPE_HOST
);
333 samsung_usbphy_set_type(&sphy
->phy
, USB_PHY_TYPE_DEVICE
);
336 /* De-initialize usb phy registers */
337 if (sphy
->drv_data
->cpu_type
== TYPE_EXYNOS5250
)
338 samsung_exynos5_usb2phy_disable(sphy
);
340 samsung_usb2phy_disable(sphy
);
342 /* Enable phy isolation */
343 if (sphy
->plat
&& sphy
->plat
->pmu_isolation
)
344 sphy
->plat
->pmu_isolation(true);
346 samsung_usbphy_set_isolation(sphy
, true);
348 spin_unlock_irqrestore(&sphy
->lock
, flags
);
350 clk_disable_unprepare(sphy
->clk
);
353 static int samsung_usb2phy_probe(struct platform_device
*pdev
)
355 struct samsung_usbphy
*sphy
;
357 struct samsung_usbphy_data
*pdata
= pdev
->dev
.platform_data
;
358 const struct samsung_usbphy_drvdata
*drv_data
;
359 struct device
*dev
= &pdev
->dev
;
360 struct resource
*phy_mem
;
361 void __iomem
*phy_base
;
365 phy_mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
366 phy_base
= devm_ioremap_resource(dev
, phy_mem
);
367 if (IS_ERR(phy_base
))
368 return PTR_ERR(phy_base
);
370 sphy
= devm_kzalloc(dev
, sizeof(*sphy
), GFP_KERNEL
);
374 otg
= devm_kzalloc(dev
, sizeof(*otg
), GFP_KERNEL
);
378 drv_data
= samsung_usbphy_get_driver_data(pdev
);
380 if (drv_data
->cpu_type
== TYPE_EXYNOS5250
)
381 clk
= devm_clk_get(dev
, "usbhost");
383 clk
= devm_clk_get(dev
, "otg");
386 dev_err(dev
, "Failed to get otg clock\n");
393 ret
= samsung_usbphy_parse_dt(sphy
);
398 dev_err(dev
, "no platform data specified\n");
404 sphy
->regs
= phy_base
;
406 sphy
->drv_data
= drv_data
;
407 sphy
->phy
.dev
= sphy
->dev
;
408 sphy
->phy
.label
= "samsung-usb2phy";
409 sphy
->phy
.init
= samsung_usb2phy_init
;
410 sphy
->phy
.shutdown
= samsung_usb2phy_shutdown
;
412 sphy
->ref_clk_freq
= samsung_usbphy_get_refclk_freq(sphy
);
413 if (sphy
->ref_clk_freq
< 0)
417 sphy
->phy
.otg
->phy
= &sphy
->phy
;
418 sphy
->phy
.otg
->set_host
= samsung_usbphy_set_host
;
420 spin_lock_init(&sphy
->lock
);
422 platform_set_drvdata(pdev
, sphy
);
424 return usb_add_phy(&sphy
->phy
, USB_PHY_TYPE_USB2
);
427 static int samsung_usb2phy_remove(struct platform_device
*pdev
)
429 struct samsung_usbphy
*sphy
= platform_get_drvdata(pdev
);
431 usb_remove_phy(&sphy
->phy
);
434 iounmap(sphy
->pmuregs
);
436 iounmap(sphy
->sysreg
);
441 static const struct samsung_usbphy_drvdata usb2phy_s3c64xx
= {
442 .cpu_type
= TYPE_S3C64XX
,
443 .devphy_en_mask
= S3C64XX_USBPHY_ENABLE
,
444 .rate_to_clksel
= samsung_usbphy_rate_to_clksel_64xx
,
447 static const struct samsung_usbphy_drvdata usb2phy_exynos4
= {
448 .cpu_type
= TYPE_EXYNOS4210
,
449 .devphy_en_mask
= EXYNOS_USBPHY_ENABLE
,
450 .hostphy_en_mask
= EXYNOS_USBPHY_ENABLE
,
451 .rate_to_clksel
= samsung_usbphy_rate_to_clksel_64xx
,
454 static struct samsung_usbphy_drvdata usb2phy_exynos5
= {
455 .cpu_type
= TYPE_EXYNOS5250
,
456 .hostphy_en_mask
= EXYNOS_USBPHY_ENABLE
,
457 .hostphy_reg_offset
= EXYNOS_USBHOST_PHY_CTRL_OFFSET
,
458 .rate_to_clksel
= samsung_usbphy_rate_to_clksel_4x12
,
462 static const struct of_device_id samsung_usbphy_dt_match
[] = {
464 .compatible
= "samsung,s3c64xx-usb2phy",
465 .data
= &usb2phy_s3c64xx
,
467 .compatible
= "samsung,exynos4210-usb2phy",
468 .data
= &usb2phy_exynos4
,
470 .compatible
= "samsung,exynos5250-usb2phy",
471 .data
= &usb2phy_exynos5
475 MODULE_DEVICE_TABLE(of
, samsung_usbphy_dt_match
);
478 static struct platform_device_id samsung_usbphy_driver_ids
[] = {
480 .name
= "s3c64xx-usb2phy",
481 .driver_data
= (unsigned long)&usb2phy_s3c64xx
,
483 .name
= "exynos4210-usb2phy",
484 .driver_data
= (unsigned long)&usb2phy_exynos4
,
486 .name
= "exynos5250-usb2phy",
487 .driver_data
= (unsigned long)&usb2phy_exynos5
,
492 MODULE_DEVICE_TABLE(platform
, samsung_usbphy_driver_ids
);
494 static struct platform_driver samsung_usb2phy_driver
= {
495 .probe
= samsung_usb2phy_probe
,
496 .remove
= samsung_usb2phy_remove
,
497 .id_table
= samsung_usbphy_driver_ids
,
499 .name
= "samsung-usb2phy",
500 .owner
= THIS_MODULE
,
501 .of_match_table
= of_match_ptr(samsung_usbphy_dt_match
),
505 module_platform_driver(samsung_usb2phy_driver
);
507 MODULE_DESCRIPTION("Samsung USB 2.0 phy controller");
508 MODULE_AUTHOR("Praveen Paneri <p.paneri@samsung.com>");
509 MODULE_LICENSE("GPL");
510 MODULE_ALIAS("platform:samsung-usb2phy");