blob: c931290a419437f6155cc8d6ddc909cd5141dc88 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* drivers/usb/otg/msm_otg.c
2 *
3 * OTG Driver for HighSpeed USB
4 *
5 * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17#include <linux/module.h>
18#include <linux/device.h>
19#include <linux/platform_device.h>
20#include <linux/clk.h>
21#include <linux/interrupt.h>
22#include <linux/err.h>
23#include <linux/delay.h>
24#include <linux/io.h>
25
26#include <mach/msm_otg.h>
27#include <mach/msm_hsusb.h>
28#include <mach/msm_hsusb_hw.h>
29#include <mach/board.h>
30
31#define MSM_USB_BASE (xceiv->regs)
32
33#define A_HOST 0
34#define B_DEVICE 1
35#define A_TO_B 0
36#define B_TO_A 1
37
38static struct msm_otg_transceiver *xceiv;
39
40struct msm_otg_transceiver *msm_otg_get_transceiver(void)
41{
42 if (xceiv)
43 get_device(xceiv->dev);
44 return xceiv;
45}
46EXPORT_SYMBOL(msm_otg_get_transceiver);
47
48void msm_otg_put_transceiver(struct msm_otg_transceiver *xceiv)
49{
50 if (xceiv)
51 put_device(xceiv->dev);
52}
53EXPORT_SYMBOL(msm_otg_put_transceiver);
54
55static void msm_otg_set_clk(int on)
56{
57 if (on) {
58 clk_enable(xceiv->clk);
59 clk_enable(xceiv->pclk);
60 } else {
61 clk_disable(xceiv->clk);
62 clk_disable(xceiv->pclk);
63 }
64}
65
66static inline int is_host(void)
67{
68 int ret;
69
70 ret = (OTGSC_ID & readl(USB_OTGSC)) ? 0 : 1;
71 return ret;
72}
73
74static void msm_otg_enable(void)
75{
76 msm_otg_set_clk(1);
77 /* Enable ID interrupts */
78 writel(readl(USB_OTGSC) | OTGSC_IDIE, USB_OTGSC);
79
80 if (is_host()) {
81 pr_info("%s: configuring USB in host mode\n", __func__);
82 xceiv->hcd_ops->request(xceiv->hcd_ops->handle, REQUEST_START);
83 xceiv->state = A_HOST;
84 } else {
85 pr_info("%s: configuring USB in device mode\n", __func__);
86 xceiv->dcd_ops->request(xceiv->dcd_ops->handle, REQUEST_START);
87 xceiv->state = B_DEVICE;
88 }
89 msm_otg_set_clk(0);
90 xceiv->active = 1;
91 wake_lock_timeout(&xceiv->wlock, HZ/2);
92 enable_irq(xceiv->irq);
93}
94
95static void msm_otg_disable(int mode)
96{
97 unsigned long flags;
98
99 spin_lock_irqsave(&xceiv->lock, flags);
100 xceiv->active = 0;
101 spin_unlock_irqrestore(&xceiv->lock, flags);
102
103 pr_info("%s: OTG is disabled\n", __func__);
104
105 if (mode != xceiv->state)
106 return;
107 switch (mode) {
108 case A_HOST:
109 if (xceiv->state == A_HOST) {
110 pr_info("%s: configuring USB in device mode\n",
111 __func__);
112 xceiv->dcd_ops->request(xceiv->dcd_ops->handle,
113 REQUEST_START);
114 xceiv->state = B_DEVICE;
115 }
116 break;
117 case B_DEVICE:
118 if (xceiv->state == B_DEVICE) {
119 pr_info("%s: configuring USB in host mode\n",
120 __func__);
121 xceiv->hcd_ops->request(xceiv->hcd_ops->handle,
122 REQUEST_START);
123 xceiv->state = A_HOST;
124 }
125 break;
126 }
127
128}
129
130static void msm_otg_do_work(struct work_struct *w)
131{
132 switch (xceiv->state) {
133 case A_HOST:
134 if (xceiv->flags == A_TO_B) {
135 xceiv->hcd_ops->request(xceiv->hcd_ops->handle,
136 REQUEST_STOP);
137 pr_info("%s: configuring USB in device mode\n",
138 __func__);
139 xceiv->dcd_ops->request(xceiv->dcd_ops->handle,
140 REQUEST_START);
141 xceiv->state = B_DEVICE;
142 }
143 break;
144 case B_DEVICE:
145 if (xceiv->flags == B_TO_A) {
146 xceiv->dcd_ops->request(xceiv->dcd_ops->handle,
147 REQUEST_STOP);
148 pr_info("%s: configuring USB in host mode\n",
149 __func__);
150 xceiv->hcd_ops->request(xceiv->hcd_ops->handle,
151 REQUEST_START);
152 xceiv->state = A_HOST;
153 }
154 break;
155 }
156 wake_lock_timeout(&xceiv->wlock, HZ/2);
157 enable_irq(xceiv->irq);
158}
159
160static irqreturn_t msm_otg_irq(int irq, void *data)
161{
162 u32 otgsc;
163 u32 temp;
164
165 if (!xceiv->active)
166 return IRQ_HANDLED;
167
168 if (xceiv->in_lpm)
169 return IRQ_HANDLED;
170
171 otgsc = readl(USB_OTGSC);
172 temp = otgsc & ~OTGSC_INTR_STS_MASK;
173 if (otgsc & OTGSC_IDIS) {
174 wake_lock(&xceiv->wlock);
175 if (is_host()) {
176 xceiv->flags = B_TO_A;
177 schedule_work(&xceiv->work);
178 } else {
179 xceiv->flags = A_TO_B;
180 schedule_work(&xceiv->work);
181 }
182 disable_irq(xceiv->irq);
183 writel(temp | OTGSC_IDIS, USB_OTGSC);
184 }
185
186 return IRQ_HANDLED;
187
188}
189
190static DEFINE_MUTEX(otg_register_lock);
191
192static int msm_otg_set_peripheral(struct msm_otg_transceiver *xceiv,
193 struct msm_otg_ops *ops)
194{
195 int ret = 0;
196
197 mutex_lock(&otg_register_lock);
198 if (!xceiv) {
199 ret = -EINVAL;
200 goto unlock;
201 }
202 if (!ops) {
203 xceiv->dcd_ops = NULL;
204 pr_info("%s: Peripheral driver is deregistered with OTG\n",
205 __func__);
206 msm_otg_disable(B_DEVICE);
207 goto unlock;
208 }
209 if (xceiv->dcd_ops) {
210 ret = -EBUSY;
211 goto unlock;
212 }
213
214 xceiv->dcd_ops = ops;
215 xceiv->dcd_ops->request(xceiv->dcd_ops->handle, REQUEST_STOP);
216 if (xceiv->hcd_ops)
217 msm_otg_enable();
218unlock:
219 mutex_unlock(&otg_register_lock);
220 return ret;
221}
222
223static int msm_otg_set_host(struct msm_otg_transceiver *xceiv,
224 struct msm_otg_ops *hcd_ops)
225{
226 int ret = 0;
227
228 mutex_lock(&otg_register_lock);
229 if (!xceiv) {
230 ret = -EINVAL;
231 goto unlock;
232 }
233 if (!hcd_ops) {
234 xceiv->hcd_ops = NULL;
235 pr_info("%s: Host driver is deregistered with OTG\n",
236 __func__);
237 msm_otg_disable(A_HOST);
238 goto unlock;
239 }
240 if (xceiv->hcd_ops) {
241 ret = -EBUSY;
242 goto unlock;
243 }
244
245 xceiv->hcd_ops = hcd_ops;
246 xceiv->hcd_ops->request(xceiv->hcd_ops->handle, REQUEST_STOP);
247 if (xceiv->dcd_ops)
248 msm_otg_enable();
249
250unlock:
251 mutex_unlock(&otg_register_lock);
252 return ret;
253}
254
255static int msm_otg_set_suspend(struct msm_otg_transceiver *otg, int suspend)
256{
257 unsigned long flags;
258
259 spin_lock_irqsave(&xceiv->lock, flags);
260 xceiv->in_lpm = suspend;
261 spin_unlock_irqrestore(&xceiv->lock, flags);
262 return 0;
263}
264
265static int __init msm_otg_probe(struct platform_device *pdev)
266{
267 int ret;
268 struct resource *res;
269 xceiv = kzalloc(sizeof(struct msm_otg_transceiver), GFP_KERNEL);
270 if (!xceiv)
271 return -ENOMEM;
272
273 xceiv->clk = clk_get(NULL, "usb_hs_clk");
274 if (IS_ERR(xceiv->clk)) {
275 ret = PTR_ERR(xceiv->clk);
276 goto free_xceiv;
277 }
278 xceiv->pclk = clk_get(NULL, "usb_hs_pclk");
279 if (IS_ERR(xceiv->clk)) {
280 ret = PTR_ERR(xceiv->pclk);
281 goto put_clk;
282 }
283 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
284 if (!res) {
285 ret = -ENODEV;
286 goto put_pclk;
287 }
288
289 xceiv->regs = ioremap(res->start, resource_size(res));
290 if (!xceiv->regs) {
291 ret = -ENOMEM;
292 goto put_pclk;
293 }
294 xceiv->irq = platform_get_irq(pdev, 0);
295 if (!xceiv->irq) {
296 ret = -ENODEV;
297 goto free_regs;
298 }
299
300 /* disable interrupts before requesting irq */
301 msm_otg_set_clk(1);
302 writel(0, USB_USBINTR);
303 writel(readl(USB_OTGSC) & ~OTGSC_INTR_MASK, USB_OTGSC);
304 msm_otg_set_clk(0);
305
306 ret = request_irq(xceiv->irq, msm_otg_irq, IRQF_SHARED,
307 "msm_otg", pdev);
308 if (ret)
309 goto free_regs;
310 disable_irq(xceiv->irq);
311
312 INIT_WORK(&xceiv->work, msm_otg_do_work);
313 spin_lock_init(&xceiv->lock);
314 wake_lock_init(&xceiv->wlock, WAKE_LOCK_SUSPEND, "usb_otg");
315 wake_lock(&xceiv->wlock);
316
317 xceiv->set_host = msm_otg_set_host;
318 xceiv->set_peripheral = msm_otg_set_peripheral;
319 xceiv->set_suspend = msm_otg_set_suspend;
320
321 return 0;
322free_regs:
323 iounmap(xceiv->regs);
324put_pclk:
325 clk_put(xceiv->pclk);
326put_clk:
327 clk_put(xceiv->clk);
328free_xceiv:
329 kfree(xceiv);
330 return ret;
331
332}
333
334static int __exit msm_otg_remove(struct platform_device *pdev)
335{
336 cancel_work_sync(&xceiv->work);
337 free_irq(xceiv->irq, pdev);
338 iounmap(xceiv->regs);
339 clk_put(xceiv->pclk);
340 clk_put(xceiv->clk);
341 kfree(xceiv);
342 return 0;
343}
344
345static struct platform_driver msm_otg_driver = {
346 .remove = __exit_p(msm_otg_remove),
347 .driver = {
348 .name = "msm_hsusb_otg",
349 .owner = THIS_MODULE,
350 },
351};
352
353static int __init msm_otg_init(void)
354{
355 return platform_driver_probe(&msm_otg_driver, msm_otg_probe);
356}
357
358static void __exit msm_otg_exit(void)
359{
360 platform_driver_unregister(&msm_otg_driver);
361}
362
363subsys_initcall(msm_otg_init);
364module_exit(msm_otg_exit);
365
366MODULE_LICENSE("GPL v2");
367MODULE_DESCRIPTION("MSM USB OTG driver");
368MODULE_VERSION("1.00");