aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKishon Vijay Abraham I <kishon@ti.com>2013-01-24 21:51:48 -0500
committerFelipe Balbi <balbi@ti.com>2013-01-25 06:02:50 -0500
commit57f6ce072e35770a63be0c5d5e82f90d8da7d665 (patch)
tree4d839c576e033640468fef2fd702acd35fe30ede
parentca784be36cc725bca9b526eba342de7550329731 (diff)
usb: phy: add a new driver for usb3 phy
Added a driver for usb3 phy that handles the interaction between usb phy device and dwc3 controller. This also includes device tree support for usb3 phy driver and the documentation with device tree binding information is updated. Currently writing to control module register is taken care in this driver which will be removed once the control module driver is in place. Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> Signed-off-by: Felipe Balbi <balbi@ti.com> Signed-off-by: Moiz Sonasath <m-sonasath@ti.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r--Documentation/devicetree/bindings/usb/usb-phy.txt23
-rw-r--r--drivers/usb/phy/Kconfig10
-rw-r--r--drivers/usb/phy/Makefile1
-rw-r--r--drivers/usb/phy/omap-usb3.c355
-rw-r--r--include/linux/usb/omap_usb.h23
5 files changed, 412 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt
index b4b86bb831b2..61496f5cb095 100644
--- a/Documentation/devicetree/bindings/usb/usb-phy.txt
+++ b/Documentation/devicetree/bindings/usb/usb-phy.txt
@@ -17,3 +17,26 @@ usb2phy@4a0ad080 {
17 reg = <0x4a0ad080 0x58>; 17 reg = <0x4a0ad080 0x58>;
18 ctrl-module = <&omap_control_usb>; 18 ctrl-module = <&omap_control_usb>;
19}; 19};
20
21OMAP USB3 PHY
22
23Required properties:
24 - compatible: Should be "ti,omap-usb3"
25 - reg : Address and length of the register set for the device.
26 - reg-names: The names of the register addresses corresponding to the registers
27 filled in "reg".
28
29Optional properties:
30 - ctrl-module : phandle of the control module used by PHY driver to power on
31 the PHY.
32
33This is usually a subnode of ocp2scp to which it is connected.
34
35usb3phy@4a084400 {
36 compatible = "ti,omap-usb3";
37 reg = <0x4a084400 0x80>,
38 <0x4a084800 0x64>,
39 <0x4a084c00 0x40>;
40 reg-names = "phy_rx", "phy_tx", "pll_ctrl";
41 ctrl-module = <&omap_control_usb>;
42};
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index f989511f13da..9bbedecb3371 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -15,6 +15,16 @@ config OMAP_USB2
15 The USB OTG controller communicates with the comparator using this 15 The USB OTG controller communicates with the comparator using this
16 driver. 16 driver.
17 17
18config OMAP_USB3
19 tristate "OMAP USB3 PHY Driver"
20 select USB_OTG_UTILS
21 select OMAP_CONTROL_USB
22 help
23 Enable this to support the USB3 PHY that is part of SOC. This
24 driver takes care of all the PHY functionality apart from comparator.
25 This driver interacts with the "OMAP Control USB Driver" to power
26 on/off the PHY.
27
18config OMAP_CONTROL_USB 28config OMAP_CONTROL_USB
19 tristate "OMAP CONTROL USB Driver" 29 tristate "OMAP CONTROL USB Driver"
20 depends on ARCH_OMAP2PLUS 30 depends on ARCH_OMAP2PLUS
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index ee977671f530..b13faa193e0c 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -5,6 +5,7 @@
5ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG 5ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG
6 6
7obj-$(CONFIG_OMAP_USB2) += omap-usb2.o 7obj-$(CONFIG_OMAP_USB2) += omap-usb2.o
8obj-$(CONFIG_OMAP_USB3) += omap-usb3.o
8obj-$(CONFIG_OMAP_CONTROL_USB) += omap-control-usb.o 9obj-$(CONFIG_OMAP_CONTROL_USB) += omap-control-usb.o
9obj-$(CONFIG_USB_ISP1301) += isp1301.o 10obj-$(CONFIG_USB_ISP1301) += isp1301.o
10obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o 11obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o
diff --git a/drivers/usb/phy/omap-usb3.c b/drivers/usb/phy/omap-usb3.c
new file mode 100644
index 000000000000..fadc0c2b65bb
--- /dev/null
+++ b/drivers/usb/phy/omap-usb3.c
@@ -0,0 +1,355 @@
1/*
2 * omap-usb3 - USB PHY, talking to dwc3 controller in OMAP.
3 *
4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * Author: Kishon Vijay Abraham I <kishon@ti.com>
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/platform_device.h>
21#include <linux/slab.h>
22#include <linux/usb/omap_usb.h>
23#include <linux/of.h>
24#include <linux/clk.h>
25#include <linux/err.h>
26#include <linux/pm_runtime.h>
27#include <linux/delay.h>
28#include <linux/usb/omap_control_usb.h>
29
30#define NUM_SYS_CLKS 5
31#define PLL_STATUS 0x00000004
32#define PLL_GO 0x00000008
33#define PLL_CONFIGURATION1 0x0000000C
34#define PLL_CONFIGURATION2 0x00000010
35#define PLL_CONFIGURATION3 0x00000014
36#define PLL_CONFIGURATION4 0x00000020
37
38#define PLL_REGM_MASK 0x001FFE00
39#define PLL_REGM_SHIFT 0x9
40#define PLL_REGM_F_MASK 0x0003FFFF
41#define PLL_REGM_F_SHIFT 0x0
42#define PLL_REGN_MASK 0x000001FE
43#define PLL_REGN_SHIFT 0x1
44#define PLL_SELFREQDCO_MASK 0x0000000E
45#define PLL_SELFREQDCO_SHIFT 0x1
46#define PLL_SD_MASK 0x0003FC00
47#define PLL_SD_SHIFT 0x9
48#define SET_PLL_GO 0x1
49#define PLL_TICOPWDN 0x10000
50#define PLL_LOCK 0x2
51#define PLL_IDLE 0x1
52
53/*
54 * This is an Empirical value that works, need to confirm the actual
55 * value required for the USB3PHY_PLL_CONFIGURATION2.PLL_IDLE status
56 * to be correctly reflected in the USB3PHY_PLL_STATUS register.
57 */
58# define PLL_IDLE_TIME 100;
59
60enum sys_clk_rate {
61 CLK_RATE_UNDEFINED = -1,
62 CLK_RATE_12MHZ,
63 CLK_RATE_16MHZ,
64 CLK_RATE_19MHZ,
65 CLK_RATE_26MHZ,
66 CLK_RATE_38MHZ
67};
68
69static struct usb_dpll_params omap_usb3_dpll_params[NUM_SYS_CLKS] = {
70 {1250, 5, 4, 20, 0}, /* 12 MHz */
71 {3125, 20, 4, 20, 0}, /* 16.8 MHz */
72 {1172, 8, 4, 20, 65537}, /* 19.2 MHz */
73 {1250, 12, 4, 20, 0}, /* 26 MHz */
74 {3125, 47, 4, 20, 92843}, /* 38.4 MHz */
75};
76
77static int omap_usb3_suspend(struct usb_phy *x, int suspend)
78{
79 struct omap_usb *phy = phy_to_omapusb(x);
80 int val;
81 int timeout = PLL_IDLE_TIME;
82
83 if (suspend && !phy->is_suspended) {
84 val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
85 val |= PLL_IDLE;
86 omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
87
88 do {
89 val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
90 if (val & PLL_TICOPWDN)
91 break;
92 udelay(1);
93 } while (--timeout);
94
95 omap_control_usb3_phy_power(phy->control_dev, 0);
96
97 phy->is_suspended = 1;
98 } else if (!suspend && phy->is_suspended) {
99 phy->is_suspended = 0;
100
101 val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
102 val &= ~PLL_IDLE;
103 omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
104
105 do {
106 val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
107 if (!(val & PLL_TICOPWDN))
108 break;
109 udelay(1);
110 } while (--timeout);
111 }
112
113 return 0;
114}
115
116static inline enum sys_clk_rate __get_sys_clk_index(unsigned long rate)
117{
118 switch (rate) {
119 case 12000000:
120 return CLK_RATE_12MHZ;
121 case 16800000:
122 return CLK_RATE_16MHZ;
123 case 19200000:
124 return CLK_RATE_19MHZ;
125 case 26000000:
126 return CLK_RATE_26MHZ;
127 case 38400000:
128 return CLK_RATE_38MHZ;
129 default:
130 return CLK_RATE_UNDEFINED;
131 }
132}
133
134static void omap_usb_dpll_relock(struct omap_usb *phy)
135{
136 u32 val;
137 unsigned long timeout;
138
139 omap_usb_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO);
140
141 timeout = jiffies + msecs_to_jiffies(20);
142 do {
143 val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
144 if (val & PLL_LOCK)
145 break;
146 } while (!WARN_ON(time_after(jiffies, timeout)));
147}
148
149static int omap_usb_dpll_lock(struct omap_usb *phy)
150{
151 u32 val;
152 unsigned long rate;
153 enum sys_clk_rate clk_index;
154
155 rate = clk_get_rate(phy->sys_clk);
156 clk_index = __get_sys_clk_index(rate);
157
158 if (clk_index == CLK_RATE_UNDEFINED) {
159 pr_err("dpll cannot be locked for sys clk freq:%luHz\n", rate);
160 return -EINVAL;
161 }
162
163 val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
164 val &= ~PLL_REGN_MASK;
165 val |= omap_usb3_dpll_params[clk_index].n << PLL_REGN_SHIFT;
166 omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
167
168 val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
169 val &= ~PLL_SELFREQDCO_MASK;
170 val |= omap_usb3_dpll_params[clk_index].freq << PLL_SELFREQDCO_SHIFT;
171 omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
172
173 val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
174 val &= ~PLL_REGM_MASK;
175 val |= omap_usb3_dpll_params[clk_index].m << PLL_REGM_SHIFT;
176 omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
177
178 val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4);
179 val &= ~PLL_REGM_F_MASK;
180 val |= omap_usb3_dpll_params[clk_index].mf << PLL_REGM_F_SHIFT;
181 omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val);
182
183 val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3);
184 val &= ~PLL_SD_MASK;
185 val |= omap_usb3_dpll_params[clk_index].sd << PLL_SD_SHIFT;
186 omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val);
187
188 omap_usb_dpll_relock(phy);
189
190 return 0;
191}
192
193static int omap_usb3_init(struct usb_phy *x)
194{
195 struct omap_usb *phy = phy_to_omapusb(x);
196
197 omap_usb_dpll_lock(phy);
198 omap_control_usb3_phy_power(phy->control_dev, 1);
199
200 return 0;
201}
202
203static int omap_usb3_probe(struct platform_device *pdev)
204{
205 struct omap_usb *phy;
206 struct resource *res;
207
208 phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
209 if (!phy) {
210 dev_err(&pdev->dev, "unable to alloc mem for OMAP USB3 PHY\n");
211 return -ENOMEM;
212 }
213
214 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl");
215 phy->pll_ctrl_base = devm_request_and_ioremap(&pdev->dev, res);
216 if (!phy->pll_ctrl_base) {
217 dev_err(&pdev->dev, "ioremap of pll_ctrl failed\n");
218 return -ENOMEM;
219 }
220
221 phy->dev = &pdev->dev;
222
223 phy->phy.dev = phy->dev;
224 phy->phy.label = "omap-usb3";
225 phy->phy.init = omap_usb3_init;
226 phy->phy.set_suspend = omap_usb3_suspend;
227 phy->phy.type = USB_PHY_TYPE_USB3;
228
229 phy->is_suspended = 1;
230 phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
231 if (IS_ERR(phy->wkupclk)) {
232 dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
233 return PTR_ERR(phy->wkupclk);
234 }
235 clk_prepare(phy->wkupclk);
236
237 phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
238 if (IS_ERR(phy->optclk)) {
239 dev_err(&pdev->dev, "unable to get usb_otg_ss_refclk960m\n");
240 return PTR_ERR(phy->optclk);
241 }
242 clk_prepare(phy->optclk);
243
244 phy->sys_clk = devm_clk_get(phy->dev, "sys_clkin");
245 if (IS_ERR(phy->sys_clk)) {
246 pr_err("%s: unable to get sys_clkin\n", __func__);
247 return -EINVAL;
248 }
249
250 phy->control_dev = omap_get_control_dev();
251 if (IS_ERR(phy->control_dev)) {
252 dev_dbg(&pdev->dev, "Failed to get control device\n");
253 return -ENODEV;
254 }
255
256 omap_control_usb3_phy_power(phy->control_dev, 0);
257 usb_add_phy_dev(&phy->phy);
258
259 platform_set_drvdata(pdev, phy);
260
261 pm_runtime_enable(phy->dev);
262 pm_runtime_get(&pdev->dev);
263
264 return 0;
265}
266
267static int omap_usb3_remove(struct platform_device *pdev)
268{
269 struct omap_usb *phy = platform_get_drvdata(pdev);
270
271 clk_unprepare(phy->wkupclk);
272 clk_unprepare(phy->optclk);
273 usb_remove_phy(&phy->phy);
274 if (!pm_runtime_suspended(&pdev->dev))
275 pm_runtime_put(&pdev->dev);
276 pm_runtime_disable(&pdev->dev);
277
278 return 0;
279}
280
281#ifdef CONFIG_PM_RUNTIME
282
283static int omap_usb3_runtime_suspend(struct device *dev)
284{
285 struct platform_device *pdev = to_platform_device(dev);
286 struct omap_usb *phy = platform_get_drvdata(pdev);
287
288 clk_disable(phy->wkupclk);
289 clk_disable(phy->optclk);
290
291 return 0;
292}
293
294static int omap_usb3_runtime_resume(struct device *dev)
295{
296 u32 ret = 0;
297 struct platform_device *pdev = to_platform_device(dev);
298 struct omap_usb *phy = platform_get_drvdata(pdev);
299
300 ret = clk_enable(phy->optclk);
301 if (ret) {
302 dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
303 goto err1;
304 }
305
306 ret = clk_enable(phy->wkupclk);
307 if (ret) {
308 dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
309 goto err2;
310 }
311
312 return 0;
313
314err2:
315 clk_disable(phy->optclk);
316
317err1:
318 return ret;
319}
320
321static const struct dev_pm_ops omap_usb3_pm_ops = {
322 SET_RUNTIME_PM_OPS(omap_usb3_runtime_suspend, omap_usb3_runtime_resume,
323 NULL)
324};
325
326#define DEV_PM_OPS (&omap_usb3_pm_ops)
327#else
328#define DEV_PM_OPS NULL
329#endif
330
331#ifdef CONFIG_OF
332static const struct of_device_id omap_usb3_id_table[] = {
333 { .compatible = "ti,omap-usb3" },
334 {}
335};
336MODULE_DEVICE_TABLE(of, omap_usb3_id_table);
337#endif
338
339static struct platform_driver omap_usb3_driver = {
340 .probe = omap_usb3_probe,
341 .remove = omap_usb3_remove,
342 .driver = {
343 .name = "omap-usb3",
344 .owner = THIS_MODULE,
345 .pm = DEV_PM_OPS,
346 .of_match_table = of_match_ptr(omap_usb3_id_table),
347 },
348};
349
350module_platform_driver(omap_usb3_driver);
351
352MODULE_ALIAS("platform: omap_usb3");
353MODULE_AUTHOR("Texas Instruments Inc.");
354MODULE_DESCRIPTION("OMAP USB3 phy driver");
355MODULE_LICENSE("GPL v2");
diff --git a/include/linux/usb/omap_usb.h b/include/linux/usb/omap_usb.h
index 3db9b5316b10..6ae29360e1d2 100644
--- a/include/linux/usb/omap_usb.h
+++ b/include/linux/usb/omap_usb.h
@@ -19,14 +19,26 @@
19#ifndef __DRIVERS_OMAP_USB2_H 19#ifndef __DRIVERS_OMAP_USB2_H
20#define __DRIVERS_OMAP_USB2_H 20#define __DRIVERS_OMAP_USB2_H
21 21
22#include <linux/io.h>
22#include <linux/usb/otg.h> 23#include <linux/usb/otg.h>
23 24
25struct usb_dpll_params {
26 u16 m;
27 u8 n;
28 u8 freq:3;
29 u8 sd;
30 u32 mf;
31};
32
24struct omap_usb { 33struct omap_usb {
25 struct usb_phy phy; 34 struct usb_phy phy;
26 struct phy_companion *comparator; 35 struct phy_companion *comparator;
36 void __iomem *pll_ctrl_base;
27 struct device *dev; 37 struct device *dev;
28 struct device *control_dev; 38 struct device *control_dev;
29 struct clk *wkupclk; 39 struct clk *wkupclk;
40 struct clk *sys_clk;
41 struct clk *optclk;
30 u8 is_suspended:1; 42 u8 is_suspended:1;
31}; 43};
32 44
@@ -41,4 +53,15 @@ static inline int omap_usb2_set_comparator(struct phy_companion *comparator)
41} 53}
42#endif 54#endif
43 55
56static inline u32 omap_usb_readl(void __iomem *addr, unsigned offset)
57{
58 return __raw_readl(addr + offset);
59}
60
61static inline void omap_usb_writel(void __iomem *addr, unsigned offset,
62 u32 data)
63{
64 __raw_writel(data, addr + offset);
65}
66
44#endif /* __DRIVERS_OMAP_USB_H */ 67#endif /* __DRIVERS_OMAP_USB_H */