1 /* linux/drivers/usb/phy/phy-samsung-usb3.c
3 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
6 * Author: Vivek Gautam <gautam.vivek@samsung.com>
8 * Samsung USB 3.0 PHY transceiver; talks to DWC3 controller.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/clk.h>
23 #include <linux/delay.h>
24 #include <linux/err.h>
27 #include <linux/usb/samsung_usb_phy.h>
28 #include <linux/platform_data/samsung-usbphy.h>
30 #include "phy-samsung-usb.h"
33 * Sets the phy clk as EXTREFCLK (XXTI) which is internal clock from clock core.
35 static u32
samsung_usb3phy_set_refclk(struct samsung_usbphy
*sphy
)
40 refclk
= sphy
->ref_clk_freq
;
42 reg
= PHYCLKRST_REFCLKSEL_EXT_REFCLK
|
43 PHYCLKRST_FSEL(refclk
);
47 reg
|= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF
|
48 PHYCLKRST_SSC_REFCLKSEL(0x00));
51 reg
|= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF
|
52 PHYCLKRST_SSC_REFCLKSEL(0x00));
54 case FSEL_CLKSEL_19200K
:
55 reg
|= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF
|
56 PHYCLKRST_SSC_REFCLKSEL(0x88));
60 reg
|= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF
|
61 PHYCLKRST_SSC_REFCLKSEL(0x88));
68 static void samsung_exynos5_usb3phy_enable(struct samsung_usbphy
*sphy
)
70 void __iomem
*regs
= sphy
->regs
;
78 /* Reset USB 3.0 PHY */
79 writel(0x0, regs
+ EXYNOS5_DRD_PHYREG0
);
81 phyparam0
= readl(regs
+ EXYNOS5_DRD_PHYPARAM0
);
82 /* Select PHY CLK source */
83 phyparam0
&= ~PHYPARAM0_REF_USE_PAD
;
84 /* Set Loss-of-Signal Detector sensitivity */
85 phyparam0
&= ~PHYPARAM0_REF_LOSLEVEL_MASK
;
86 phyparam0
|= PHYPARAM0_REF_LOSLEVEL
;
87 writel(phyparam0
, regs
+ EXYNOS5_DRD_PHYPARAM0
);
89 writel(0x0, regs
+ EXYNOS5_DRD_PHYRESUME
);
92 * Setting the Frame length Adj value[6:1] to default 0x20
93 * See xHCI 1.0 spec, 5.2.4
95 linksystem
= LINKSYSTEM_XHCI_VERSION_CONTROL
|
96 LINKSYSTEM_FLADJ(0x20);
97 writel(linksystem
, regs
+ EXYNOS5_DRD_LINKSYSTEM
);
99 phyparam1
= readl(regs
+ EXYNOS5_DRD_PHYPARAM1
);
100 /* Set Tx De-Emphasis level */
101 phyparam1
&= ~PHYPARAM1_PCS_TXDEEMPH_MASK
;
102 phyparam1
|= PHYPARAM1_PCS_TXDEEMPH
;
103 writel(phyparam1
, regs
+ EXYNOS5_DRD_PHYPARAM1
);
105 phybatchg
= readl(regs
+ EXYNOS5_DRD_PHYBATCHG
);
106 phybatchg
|= PHYBATCHG_UTMI_CLKSEL
;
107 writel(phybatchg
, regs
+ EXYNOS5_DRD_PHYBATCHG
);
109 /* PHYTEST POWERDOWN Control */
110 phytest
= readl(regs
+ EXYNOS5_DRD_PHYTEST
);
111 phytest
&= ~(PHYTEST_POWERDOWN_SSP
|
112 PHYTEST_POWERDOWN_HSP
);
113 writel(phytest
, regs
+ EXYNOS5_DRD_PHYTEST
);
115 /* UTMI Power Control */
116 writel(PHYUTMI_OTGDISABLE
, regs
+ EXYNOS5_DRD_PHYUTMI
);
118 phyclkrst
= samsung_usb3phy_set_refclk(sphy
);
120 phyclkrst
|= PHYCLKRST_PORTRESET
|
121 /* Digital power supply in normal operating mode */
122 PHYCLKRST_RETENABLEN
|
123 /* Enable ref clock for SS function */
124 PHYCLKRST_REF_SSP_EN
|
125 /* Enable spread spectrum */
127 /* Power down HS Bias and PLL blocks in suspend mode */
130 writel(phyclkrst
, regs
+ EXYNOS5_DRD_PHYCLKRST
);
134 phyclkrst
&= ~(PHYCLKRST_PORTRESET
);
135 writel(phyclkrst
, regs
+ EXYNOS5_DRD_PHYCLKRST
);
138 static void samsung_exynos5_usb3phy_disable(struct samsung_usbphy
*sphy
)
143 void __iomem
*regs
= sphy
->regs
;
145 phyutmi
= PHYUTMI_OTGDISABLE
|
146 PHYUTMI_FORCESUSPEND
|
148 writel(phyutmi
, regs
+ EXYNOS5_DRD_PHYUTMI
);
150 /* Resetting the PHYCLKRST enable bits to reduce leakage current */
151 phyclkrst
= readl(regs
+ EXYNOS5_DRD_PHYCLKRST
);
152 phyclkrst
&= ~(PHYCLKRST_REF_SSP_EN
|
154 PHYCLKRST_COMMONONN
);
155 writel(phyclkrst
, regs
+ EXYNOS5_DRD_PHYCLKRST
);
157 /* Control PHYTEST to remove leakage current */
158 phytest
= readl(regs
+ EXYNOS5_DRD_PHYTEST
);
159 phytest
|= (PHYTEST_POWERDOWN_SSP
|
160 PHYTEST_POWERDOWN_HSP
);
161 writel(phytest
, regs
+ EXYNOS5_DRD_PHYTEST
);
164 static int samsung_usb3phy_init(struct usb_phy
*phy
)
166 struct samsung_usbphy
*sphy
;
170 sphy
= phy_to_sphy(phy
);
172 /* Enable the phy clock */
173 ret
= clk_prepare_enable(sphy
->clk
);
175 dev_err(sphy
->dev
, "%s: clk_prepare_enable failed\n", __func__
);
179 spin_lock_irqsave(&sphy
->lock
, flags
);
181 /* setting default phy-type for USB 3.0 */
182 samsung_usbphy_set_type(&sphy
->phy
, USB_PHY_TYPE_DEVICE
);
184 /* Disable phy isolation */
185 if (sphy
->drv_data
->set_isolation
)
186 sphy
->drv_data
->set_isolation(sphy
, false);
188 /* Initialize usb phy registers */
189 sphy
->drv_data
->phy_enable(sphy
);
191 spin_unlock_irqrestore(&sphy
->lock
, flags
);
193 /* Disable the phy clock */
194 clk_disable_unprepare(sphy
->clk
);
200 * The function passed to the usb driver for phy shutdown
202 static void samsung_usb3phy_shutdown(struct usb_phy
*phy
)
204 struct samsung_usbphy
*sphy
;
207 sphy
= phy_to_sphy(phy
);
209 if (clk_prepare_enable(sphy
->clk
)) {
210 dev_err(sphy
->dev
, "%s: clk_prepare_enable failed\n", __func__
);
214 spin_lock_irqsave(&sphy
->lock
, flags
);
216 /* setting default phy-type for USB 3.0 */
217 samsung_usbphy_set_type(&sphy
->phy
, USB_PHY_TYPE_DEVICE
);
219 /* De-initialize usb phy registers */
220 sphy
->drv_data
->phy_disable(sphy
);
222 /* Enable phy isolation */
223 if (sphy
->drv_data
->set_isolation
)
224 sphy
->drv_data
->set_isolation(sphy
, true);
226 spin_unlock_irqrestore(&sphy
->lock
, flags
);
228 clk_disable_unprepare(sphy
->clk
);
231 static int samsung_usb3phy_probe(struct platform_device
*pdev
)
233 struct samsung_usbphy
*sphy
;
234 struct samsung_usbphy_data
*pdata
= pdev
->dev
.platform_data
;
235 struct device
*dev
= &pdev
->dev
;
236 struct resource
*phy_mem
;
237 void __iomem
*phy_base
;
241 phy_mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
242 phy_base
= devm_ioremap_resource(dev
, phy_mem
);
243 if (IS_ERR(phy_base
))
244 return PTR_ERR(phy_base
);
246 sphy
= devm_kzalloc(dev
, sizeof(*sphy
), GFP_KERNEL
);
250 clk
= devm_clk_get(dev
, "usbdrd30");
252 dev_err(dev
, "Failed to get device clock\n");
259 ret
= samsung_usbphy_parse_dt(sphy
);
264 dev_err(dev
, "no platform data specified\n");
270 sphy
->regs
= phy_base
;
272 sphy
->phy
.dev
= sphy
->dev
;
273 sphy
->phy
.label
= "samsung-usb3phy";
274 sphy
->phy
.init
= samsung_usb3phy_init
;
275 sphy
->phy
.shutdown
= samsung_usb3phy_shutdown
;
276 sphy
->drv_data
= samsung_usbphy_get_driver_data(pdev
);
278 sphy
->ref_clk_freq
= samsung_usbphy_get_refclk_freq(sphy
);
279 if (sphy
->ref_clk_freq
< 0)
282 spin_lock_init(&sphy
->lock
);
284 platform_set_drvdata(pdev
, sphy
);
286 return usb_add_phy(&sphy
->phy
, USB_PHY_TYPE_USB3
);
289 static int samsung_usb3phy_remove(struct platform_device
*pdev
)
291 struct samsung_usbphy
*sphy
= platform_get_drvdata(pdev
);
293 usb_remove_phy(&sphy
->phy
);
296 iounmap(sphy
->pmuregs
);
298 iounmap(sphy
->sysreg
);
303 static struct samsung_usbphy_drvdata usb3phy_exynos5
= {
304 .cpu_type
= TYPE_EXYNOS5250
,
305 .devphy_en_mask
= EXYNOS_USBPHY_ENABLE
,
306 .rate_to_clksel
= samsung_usbphy_rate_to_clksel_4x12
,
307 .set_isolation
= samsung_usbphy_set_isolation_4210
,
308 .phy_enable
= samsung_exynos5_usb3phy_enable
,
309 .phy_disable
= samsung_exynos5_usb3phy_disable
,
313 static const struct of_device_id samsung_usbphy_dt_match
[] = {
315 .compatible
= "samsung,exynos5250-usb3phy",
316 .data
= &usb3phy_exynos5
320 MODULE_DEVICE_TABLE(of
, samsung_usbphy_dt_match
);
323 static struct platform_device_id samsung_usbphy_driver_ids
[] = {
325 .name
= "exynos5250-usb3phy",
326 .driver_data
= (unsigned long)&usb3phy_exynos5
,
331 MODULE_DEVICE_TABLE(platform
, samsung_usbphy_driver_ids
);
333 static struct platform_driver samsung_usb3phy_driver
= {
334 .probe
= samsung_usb3phy_probe
,
335 .remove
= samsung_usb3phy_remove
,
336 .id_table
= samsung_usbphy_driver_ids
,
338 .name
= "samsung-usb3phy",
339 .owner
= THIS_MODULE
,
340 .of_match_table
= of_match_ptr(samsung_usbphy_dt_match
),
344 module_platform_driver(samsung_usb3phy_driver
);
346 MODULE_DESCRIPTION("Samsung USB 3.0 phy controller");
347 MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>");
348 MODULE_LICENSE("GPL");
349 MODULE_ALIAS("platform:samsung-usb3phy");