aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/phy
diff options
context:
space:
mode:
authorVivek Gautam <gautam.vivek@samsung.com>2013-03-14 06:29:11 -0400
committerFelipe Balbi <balbi@ti.com>2013-03-18 05:18:15 -0400
commitb52767581765d3d1a1ba7106674791e540574704 (patch)
tree95823207cf8cf597ffa444b7efc9a953e76a7eeb /drivers/usb/phy
parentdc2377d0b0a298ec9d7d232c0d757f462dedcca2 (diff)
usb: phy: samsung: Add PHY support for USB 3.0 controller
Adding PHY driver support for USB 3.0 controller for Samsung's SoCs. Signed-off-by: Vivek Gautam <gautam.vivek@samsung.com> Acked-by: Kukjin Kim <kgene.kim@samsung.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/phy')
-rw-r--r--drivers/usb/phy/Kconfig7
-rw-r--r--drivers/usb/phy/Makefile1
-rw-r--r--drivers/usb/phy/phy-samsung-usb.h80
-rw-r--r--drivers/usb/phy/phy-samsung-usb3.c349
4 files changed, 437 insertions, 0 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index e8cd52ac5c05..7e8fe0f0b8c6 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -99,6 +99,13 @@ config SAMSUNG_USB2PHY
99 Enable this to support Samsung USB 2.0 (High Speed) PHY controller 99 Enable this to support Samsung USB 2.0 (High Speed) PHY controller
100 driver for Samsung SoCs. 100 driver for Samsung SoCs.
101 101
102config SAMSUNG_USB3PHY
103 tristate "Samsung USB 3.0 PHY controller Driver"
104 select SAMSUNG_USBPHY
105 help
106 Enable this to support Samsung USB 3.0 (Super Speed) phy controller
107 for samsung SoCs.
108
102config TWL4030_USB 109config TWL4030_USB
103 tristate "TWL4030 USB Transceiver Driver" 110 tristate "TWL4030 USB Transceiver Driver"
104 depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS 111 depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 8cd355f051f6..33863c09f3dc 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
19obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o 19obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o
20obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o 20obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o
21obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o 21obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o
22obj-$(CONFIG_SAMSUNG_USB3PHY) += phy-samsung-usb3.o
22obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o 23obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
23obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o 24obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o
24obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o 25obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o
diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h
index 481737d743d5..70a9cae5e37f 100644
--- a/drivers/usb/phy/phy-samsung-usb.h
+++ b/drivers/usb/phy/phy-samsung-usb.h
@@ -145,6 +145,86 @@
145 145
146#define EXYNOS5_PHY_OTG_TUNE (0x40) 146#define EXYNOS5_PHY_OTG_TUNE (0x40)
147 147
148/* EXYNOS5: USB 3.0 DRD */
149#define EXYNOS5_DRD_LINKSYSTEM (0x04)
150
151#define LINKSYSTEM_FLADJ_MASK (0x3f << 1)
152#define LINKSYSTEM_FLADJ(_x) ((_x) << 1)
153#define LINKSYSTEM_XHCI_VERSION_CONTROL (0x1 << 27)
154
155#define EXYNOS5_DRD_PHYUTMI (0x08)
156
157#define PHYUTMI_OTGDISABLE (0x1 << 6)
158#define PHYUTMI_FORCESUSPEND (0x1 << 1)
159#define PHYUTMI_FORCESLEEP (0x1 << 0)
160
161#define EXYNOS5_DRD_PHYPIPE (0x0c)
162
163#define EXYNOS5_DRD_PHYCLKRST (0x10)
164
165#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23)
166#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23)
167
168#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21)
169#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21)
170
171#define PHYCLKRST_SSC_EN (0x1 << 20)
172#define PHYCLKRST_REF_SSP_EN (0x1 << 19)
173#define PHYCLKRST_REF_CLKDIV2 (0x1 << 18)
174
175#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11)
176#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11)
177#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x02 << 11)
178#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11)
179#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11)
180#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11)
181
182#define PHYCLKRST_FSEL_MASK (0x3f << 5)
183#define PHYCLKRST_FSEL(_x) ((_x) << 5)
184#define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5)
185#define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5)
186#define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5)
187#define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5)
188
189#define PHYCLKRST_RETENABLEN (0x1 << 4)
190
191#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2)
192#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2)
193#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2)
194
195#define PHYCLKRST_PORTRESET (0x1 << 1)
196#define PHYCLKRST_COMMONONN (0x1 << 0)
197
198#define EXYNOS5_DRD_PHYREG0 (0x14)
199#define EXYNOS5_DRD_PHYREG1 (0x18)
200
201#define EXYNOS5_DRD_PHYPARAM0 (0x1c)
202
203#define PHYPARAM0_REF_USE_PAD (0x1 << 31)
204#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26)
205#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26)
206
207#define EXYNOS5_DRD_PHYPARAM1 (0x20)
208
209#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0)
210#define PHYPARAM1_PCS_TXDEEMPH (0x1c)
211
212#define EXYNOS5_DRD_PHYTERM (0x24)
213
214#define EXYNOS5_DRD_PHYTEST (0x28)
215
216#define PHYTEST_POWERDOWN_SSP (0x1 << 3)
217#define PHYTEST_POWERDOWN_HSP (0x1 << 2)
218
219#define EXYNOS5_DRD_PHYADP (0x2c)
220
221#define EXYNOS5_DRD_PHYBATCHG (0x30)
222
223#define PHYBATCHG_UTMI_CLKSEL (0x1 << 2)
224
225#define EXYNOS5_DRD_PHYRESUME (0x34)
226#define EXYNOS5_DRD_LINKPORT (0x44)
227
148#ifndef MHZ 228#ifndef MHZ
149#define MHZ (1000*1000) 229#define MHZ (1000*1000)
150#endif 230#endif
diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c
new file mode 100644
index 000000000000..54f641860f9e
--- /dev/null
+++ b/drivers/usb/phy/phy-samsung-usb3.c
@@ -0,0 +1,349 @@
1/* linux/drivers/usb/phy/phy-samsung-usb3.c
2 *
3 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
5 *
6 * Author: Vivek Gautam <gautam.vivek@samsung.com>
7 *
8 * Samsung USB 3.0 PHY transceiver; talks to DWC3 controller.
9 *
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.
13 *
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.
18 */
19
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>
25#include <linux/io.h>
26#include <linux/of.h>
27#include <linux/usb/samsung_usb_phy.h>
28#include <linux/platform_data/samsung-usbphy.h>
29
30#include "phy-samsung-usb.h"
31
32/*
33 * Sets the phy clk as EXTREFCLK (XXTI) which is internal clock from clock core.
34 */
35static u32 samsung_usb3phy_set_refclk(struct samsung_usbphy *sphy)
36{
37 u32 reg;
38 u32 refclk;
39
40 refclk = sphy->ref_clk_freq;
41
42 reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK |
43 PHYCLKRST_FSEL(refclk);
44
45 switch (refclk) {
46 case FSEL_CLKSEL_50M:
47 reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF |
48 PHYCLKRST_SSC_REFCLKSEL(0x00));
49 break;
50 case FSEL_CLKSEL_20M:
51 reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF |
52 PHYCLKRST_SSC_REFCLKSEL(0x00));
53 break;
54 case FSEL_CLKSEL_19200K:
55 reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF |
56 PHYCLKRST_SSC_REFCLKSEL(0x88));
57 break;
58 case FSEL_CLKSEL_24M:
59 default:
60 reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
61 PHYCLKRST_SSC_REFCLKSEL(0x88));
62 break;
63 }
64
65 return reg;
66}
67
68static int samsung_exynos5_usb3phy_enable(struct samsung_usbphy *sphy)
69{
70 void __iomem *regs = sphy->regs;
71 u32 phyparam0;
72 u32 phyparam1;
73 u32 linksystem;
74 u32 phybatchg;
75 u32 phytest;
76 u32 phyclkrst;
77
78 /* Reset USB 3.0 PHY */
79 writel(0x0, regs + EXYNOS5_DRD_PHYREG0);
80
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);
88
89 writel(0x0, regs + EXYNOS5_DRD_PHYRESUME);
90
91 /*
92 * Setting the Frame length Adj value[6:1] to default 0x20
93 * See xHCI 1.0 spec, 5.2.4
94 */
95 linksystem = LINKSYSTEM_XHCI_VERSION_CONTROL |
96 LINKSYSTEM_FLADJ(0x20);
97 writel(linksystem, regs + EXYNOS5_DRD_LINKSYSTEM);
98
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);
104
105 phybatchg = readl(regs + EXYNOS5_DRD_PHYBATCHG);
106 phybatchg |= PHYBATCHG_UTMI_CLKSEL;
107 writel(phybatchg, regs + EXYNOS5_DRD_PHYBATCHG);
108
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);
114
115 /* UTMI Power Control */
116 writel(PHYUTMI_OTGDISABLE, regs + EXYNOS5_DRD_PHYUTMI);
117
118 phyclkrst = samsung_usb3phy_set_refclk(sphy);
119
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 */
126 PHYCLKRST_SSC_EN |
127 /* Power down HS Bias and PLL blocks in suspend mode */
128 PHYCLKRST_COMMONONN;
129
130 writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST);
131
132 udelay(10);
133
134 phyclkrst &= ~(PHYCLKRST_PORTRESET);
135 writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST);
136
137 return 0;
138}
139
140static void samsung_exynos5_usb3phy_disable(struct samsung_usbphy *sphy)
141{
142 u32 phyutmi;
143 u32 phyclkrst;
144 u32 phytest;
145 void __iomem *regs = sphy->regs;
146
147 phyutmi = PHYUTMI_OTGDISABLE |
148 PHYUTMI_FORCESUSPEND |
149 PHYUTMI_FORCESLEEP;
150 writel(phyutmi, regs + EXYNOS5_DRD_PHYUTMI);
151
152 /* Resetting the PHYCLKRST enable bits to reduce leakage current */
153 phyclkrst = readl(regs + EXYNOS5_DRD_PHYCLKRST);
154 phyclkrst &= ~(PHYCLKRST_REF_SSP_EN |
155 PHYCLKRST_SSC_EN |
156 PHYCLKRST_COMMONONN);
157 writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST);
158
159 /* Control PHYTEST to remove leakage current */
160 phytest = readl(regs + EXYNOS5_DRD_PHYTEST);
161 phytest |= (PHYTEST_POWERDOWN_SSP |
162 PHYTEST_POWERDOWN_HSP);
163 writel(phytest, regs + EXYNOS5_DRD_PHYTEST);
164}
165
166static int samsung_usb3phy_init(struct usb_phy *phy)
167{
168 struct samsung_usbphy *sphy;
169 unsigned long flags;
170 int ret = 0;
171
172 sphy = phy_to_sphy(phy);
173
174 /* Enable the phy clock */
175 ret = clk_prepare_enable(sphy->clk);
176 if (ret) {
177 dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__);
178 return ret;
179 }
180
181 spin_lock_irqsave(&sphy->lock, flags);
182
183 /* setting default phy-type for USB 3.0 */
184 samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE);
185
186 /* Disable phy isolation */
187 samsung_usbphy_set_isolation(sphy, false);
188
189 /* Initialize usb phy registers */
190 samsung_exynos5_usb3phy_enable(sphy);
191
192 spin_unlock_irqrestore(&sphy->lock, flags);
193
194 /* Disable the phy clock */
195 clk_disable_unprepare(sphy->clk);
196
197 return ret;
198}
199
200/*
201 * The function passed to the usb driver for phy shutdown
202 */
203static void samsung_usb3phy_shutdown(struct usb_phy *phy)
204{
205 struct samsung_usbphy *sphy;
206 unsigned long flags;
207
208 sphy = phy_to_sphy(phy);
209
210 if (clk_prepare_enable(sphy->clk)) {
211 dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__);
212 return;
213 }
214
215 spin_lock_irqsave(&sphy->lock, flags);
216
217 /* setting default phy-type for USB 3.0 */
218 samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE);
219
220 /* De-initialize usb phy registers */
221 samsung_exynos5_usb3phy_disable(sphy);
222
223 /* Enable phy isolation */
224 samsung_usbphy_set_isolation(sphy, true);
225
226 spin_unlock_irqrestore(&sphy->lock, flags);
227
228 clk_disable_unprepare(sphy->clk);
229}
230
231static int samsung_usb3phy_probe(struct platform_device *pdev)
232{
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;
238 struct clk *clk;
239 int ret;
240
241 phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
242 if (!phy_mem) {
243 dev_err(dev, "%s: missing mem resource\n", __func__);
244 return -ENODEV;
245 }
246
247 phy_base = devm_request_and_ioremap(dev, phy_mem);
248 if (!phy_base) {
249 dev_err(dev, "%s: register mapping failed\n", __func__);
250 return -ENXIO;
251 }
252
253 sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL);
254 if (!sphy)
255 return -ENOMEM;
256
257 clk = devm_clk_get(dev, "usbdrd30");
258 if (IS_ERR(clk)) {
259 dev_err(dev, "Failed to get device clock\n");
260 return PTR_ERR(clk);
261 }
262
263 sphy->dev = dev;
264
265 if (dev->of_node) {
266 ret = samsung_usbphy_parse_dt(sphy);
267 if (ret < 0)
268 return ret;
269 } else {
270 if (!pdata) {
271 dev_err(dev, "no platform data specified\n");
272 return -EINVAL;
273 }
274 }
275
276 sphy->plat = pdata;
277 sphy->regs = phy_base;
278 sphy->clk = clk;
279 sphy->phy.dev = sphy->dev;
280 sphy->phy.label = "samsung-usb3phy";
281 sphy->phy.init = samsung_usb3phy_init;
282 sphy->phy.shutdown = samsung_usb3phy_shutdown;
283 sphy->drv_data = samsung_usbphy_get_driver_data(pdev);
284 sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy);
285
286 spin_lock_init(&sphy->lock);
287
288 platform_set_drvdata(pdev, sphy);
289
290 return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB3);
291}
292
293static int samsung_usb3phy_remove(struct platform_device *pdev)
294{
295 struct samsung_usbphy *sphy = platform_get_drvdata(pdev);
296
297 usb_remove_phy(&sphy->phy);
298
299 if (sphy->pmuregs)
300 iounmap(sphy->pmuregs);
301 if (sphy->sysreg)
302 iounmap(sphy->sysreg);
303
304 return 0;
305}
306
307static struct samsung_usbphy_drvdata usb3phy_exynos5 = {
308 .cpu_type = TYPE_EXYNOS5250,
309 .devphy_en_mask = EXYNOS_USBPHY_ENABLE,
310};
311
312#ifdef CONFIG_OF
313static const struct of_device_id samsung_usbphy_dt_match[] = {
314 {
315 .compatible = "samsung,exynos5250-usb3phy",
316 .data = &usb3phy_exynos5
317 },
318 {},
319};
320MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match);
321#endif
322
323static struct platform_device_id samsung_usbphy_driver_ids[] = {
324 {
325 .name = "exynos5250-usb3phy",
326 .driver_data = (unsigned long)&usb3phy_exynos5,
327 },
328 {},
329};
330
331MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids);
332
333static struct platform_driver samsung_usb3phy_driver = {
334 .probe = samsung_usb3phy_probe,
335 .remove = samsung_usb3phy_remove,
336 .id_table = samsung_usbphy_driver_ids,
337 .driver = {
338 .name = "samsung-usb3phy",
339 .owner = THIS_MODULE,
340 .of_match_table = of_match_ptr(samsung_usbphy_dt_match),
341 },
342};
343
344module_platform_driver(samsung_usb3phy_driver);
345
346MODULE_DESCRIPTION("Samsung USB 3.0 phy controller");
347MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>");
348MODULE_LICENSE("GPL");
349MODULE_ALIAS("platform:samsung-usb3phy");