aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/musb/omap2430.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/musb/omap2430.c')
-rw-r--r--drivers/usb/musb/omap2430.c378
1 files changed, 305 insertions, 73 deletions
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index ed618bde1eec..a3f12333fc41 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -31,10 +31,18 @@
31#include <linux/list.h> 31#include <linux/list.h>
32#include <linux/clk.h> 32#include <linux/clk.h>
33#include <linux/io.h> 33#include <linux/io.h>
34#include <linux/platform_device.h>
35#include <linux/dma-mapping.h>
34 36
35#include "musb_core.h" 37#include "musb_core.h"
36#include "omap2430.h" 38#include "omap2430.h"
37 39
40struct omap2430_glue {
41 struct device *dev;
42 struct platform_device *musb;
43 struct clk *clk;
44};
45#define glue_to_musb(g) platform_get_drvdata(g->musb)
38 46
39static struct timer_list musb_idle_timer; 47static struct timer_list musb_idle_timer;
40 48
@@ -49,12 +57,8 @@ static void musb_do_idle(unsigned long _musb)
49 57
50 spin_lock_irqsave(&musb->lock, flags); 58 spin_lock_irqsave(&musb->lock, flags);
51 59
52 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
53
54 switch (musb->xceiv->state) { 60 switch (musb->xceiv->state) {
55 case OTG_STATE_A_WAIT_BCON: 61 case OTG_STATE_A_WAIT_BCON:
56 devctl &= ~MUSB_DEVCTL_SESSION;
57 musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
58 62
59 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); 63 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
60 if (devctl & MUSB_DEVCTL_BDEVICE) { 64 if (devctl & MUSB_DEVCTL_BDEVICE) {
@@ -98,7 +102,7 @@ static void musb_do_idle(unsigned long _musb)
98} 102}
99 103
100 104
101void musb_platform_try_idle(struct musb *musb, unsigned long timeout) 105static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout)
102{ 106{
103 unsigned long default_timeout = jiffies + msecs_to_jiffies(3); 107 unsigned long default_timeout = jiffies + msecs_to_jiffies(3);
104 static unsigned long last_timer; 108 static unsigned long last_timer;
@@ -131,15 +135,11 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
131 mod_timer(&musb_idle_timer, timeout); 135 mod_timer(&musb_idle_timer, timeout);
132} 136}
133 137
134void musb_platform_enable(struct musb *musb) 138static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
135{
136}
137void musb_platform_disable(struct musb *musb)
138{
139}
140static void omap_set_vbus(struct musb *musb, int is_on)
141{ 139{
142 u8 devctl; 140 u8 devctl;
141 unsigned long timeout = jiffies + msecs_to_jiffies(1000);
142 int ret = 1;
143 /* HDRC controls CPEN, but beware current surges during device 143 /* HDRC controls CPEN, but beware current surges during device
144 * connect. They can trigger transient overcurrent conditions 144 * connect. They can trigger transient overcurrent conditions
145 * that must be ignored. 145 * that must be ignored.
@@ -148,12 +148,35 @@ static void omap_set_vbus(struct musb *musb, int is_on)
148 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); 148 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
149 149
150 if (is_on) { 150 if (is_on) {
151 musb->is_active = 1; 151 if (musb->xceiv->state == OTG_STATE_A_IDLE) {
152 musb->xceiv->default_a = 1; 152 /* start the session */
153 musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; 153 devctl |= MUSB_DEVCTL_SESSION;
154 devctl |= MUSB_DEVCTL_SESSION; 154 musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
155 155 /*
156 MUSB_HST_MODE(musb); 156 * Wait for the musb to set as A device to enable the
157 * VBUS
158 */
159 while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) {
160
161 cpu_relax();
162
163 if (time_after(jiffies, timeout)) {
164 dev_err(musb->controller,
165 "configured as A device timeout");
166 ret = -EINVAL;
167 break;
168 }
169 }
170
171 if (ret && musb->xceiv->set_vbus)
172 otg_set_vbus(musb->xceiv, 1);
173 } else {
174 musb->is_active = 1;
175 musb->xceiv->default_a = 1;
176 musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
177 devctl |= MUSB_DEVCTL_SESSION;
178 MUSB_HST_MODE(musb);
179 }
157 } else { 180 } else {
158 musb->is_active = 0; 181 musb->is_active = 0;
159 182
@@ -175,9 +198,7 @@ static void omap_set_vbus(struct musb *musb, int is_on)
175 musb_readb(musb->mregs, MUSB_DEVCTL)); 198 musb_readb(musb->mregs, MUSB_DEVCTL));
176} 199}
177 200
178static int musb_platform_resume(struct musb *musb); 201static int omap2430_musb_set_mode(struct musb *musb, u8 musb_mode)
179
180int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
181{ 202{
182 u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); 203 u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
183 204
@@ -187,10 +208,94 @@ int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
187 return 0; 208 return 0;
188} 209}
189 210
190int __init musb_platform_init(struct musb *musb, void *board_data) 211static inline void omap2430_low_level_exit(struct musb *musb)
191{ 212{
192 u32 l; 213 u32 l;
193 struct omap_musb_board_data *data = board_data; 214
215 /* in any role */
216 l = musb_readl(musb->mregs, OTG_FORCESTDBY);
217 l |= ENABLEFORCE; /* enable MSTANDBY */
218 musb_writel(musb->mregs, OTG_FORCESTDBY, l);
219
220 l = musb_readl(musb->mregs, OTG_SYSCONFIG);
221 l |= ENABLEWAKEUP; /* enable wakeup */
222 musb_writel(musb->mregs, OTG_SYSCONFIG, l);
223}
224
225static inline void omap2430_low_level_init(struct musb *musb)
226{
227 u32 l;
228
229 l = musb_readl(musb->mregs, OTG_SYSCONFIG);
230 l &= ~ENABLEWAKEUP; /* disable wakeup */
231 musb_writel(musb->mregs, OTG_SYSCONFIG, l);
232
233 l = musb_readl(musb->mregs, OTG_FORCESTDBY);
234 l &= ~ENABLEFORCE; /* disable MSTANDBY */
235 musb_writel(musb->mregs, OTG_FORCESTDBY, l);
236}
237
238/* blocking notifier support */
239static int musb_otg_notifications(struct notifier_block *nb,
240 unsigned long event, void *unused)
241{
242 struct musb *musb = container_of(nb, struct musb, nb);
243 struct device *dev = musb->controller;
244 struct musb_hdrc_platform_data *pdata = dev->platform_data;
245 struct omap_musb_board_data *data = pdata->board_data;
246
247 switch (event) {
248 case USB_EVENT_ID:
249 DBG(4, "ID GND\n");
250
251 if (is_otg_enabled(musb)) {
252#ifdef CONFIG_USB_GADGET_MUSB_HDRC
253 if (musb->gadget_driver) {
254 otg_init(musb->xceiv);
255
256 if (data->interface_type ==
257 MUSB_INTERFACE_UTMI)
258 omap2430_musb_set_vbus(musb, 1);
259
260 }
261#endif
262 } else {
263 otg_init(musb->xceiv);
264 if (data->interface_type ==
265 MUSB_INTERFACE_UTMI)
266 omap2430_musb_set_vbus(musb, 1);
267 }
268 break;
269
270 case USB_EVENT_VBUS:
271 DBG(4, "VBUS Connect\n");
272
273 otg_init(musb->xceiv);
274 break;
275
276 case USB_EVENT_NONE:
277 DBG(4, "VBUS Disconnect\n");
278
279 if (data->interface_type == MUSB_INTERFACE_UTMI) {
280 if (musb->xceiv->set_vbus)
281 otg_set_vbus(musb->xceiv, 0);
282 }
283 otg_shutdown(musb->xceiv);
284 break;
285 default:
286 DBG(4, "ID float\n");
287 return NOTIFY_DONE;
288 }
289
290 return NOTIFY_OK;
291}
292
293static int omap2430_musb_init(struct musb *musb)
294{
295 u32 l, status = 0;
296 struct device *dev = musb->controller;
297 struct musb_hdrc_platform_data *plat = dev->platform_data;
298 struct omap_musb_board_data *data = plat->board_data;
194 299
195 /* We require some kind of external transceiver, hooked 300 /* We require some kind of external transceiver, hooked
196 * up through ULPI. TWL4030-family PMICs include one, 301 * up through ULPI. TWL4030-family PMICs include one,
@@ -202,7 +307,7 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
202 return -ENODEV; 307 return -ENODEV;
203 } 308 }
204 309
205 musb_platform_resume(musb); 310 omap2430_low_level_init(musb);
206 311
207 l = musb_readl(musb->mregs, OTG_SYSCONFIG); 312 l = musb_readl(musb->mregs, OTG_SYSCONFIG);
208 l &= ~ENABLEWAKEUP; /* disable wakeup */ 313 l &= ~ENABLEWAKEUP; /* disable wakeup */
@@ -239,87 +344,214 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
239 musb_readl(musb->mregs, OTG_INTERFSEL), 344 musb_readl(musb->mregs, OTG_INTERFSEL),
240 musb_readl(musb->mregs, OTG_SIMENABLE)); 345 musb_readl(musb->mregs, OTG_SIMENABLE));
241 346
242 if (is_host_enabled(musb)) 347 musb->nb.notifier_call = musb_otg_notifications;
243 musb->board_set_vbus = omap_set_vbus; 348 status = otg_register_notifier(musb->xceiv, &musb->nb);
349
350 if (status)
351 DBG(1, "notification register failed\n");
352
353 /* check whether cable is already connected */
354 if (musb->xceiv->state ==OTG_STATE_B_IDLE)
355 musb_otg_notifications(&musb->nb, 1,
356 musb->xceiv->gadget);
244 357
245 setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); 358 setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
246 359
247 return 0; 360 return 0;
248} 361}
249 362
250#ifdef CONFIG_PM 363static int omap2430_musb_exit(struct musb *musb)
251void musb_platform_save_context(struct musb *musb,
252 struct musb_context_registers *musb_context)
253{ 364{
254 musb_context->otg_sysconfig = musb_readl(musb->mregs, OTG_SYSCONFIG);
255 musb_context->otg_forcestandby = musb_readl(musb->mregs, OTG_FORCESTDBY);
256}
257 365
258void musb_platform_restore_context(struct musb *musb, 366 omap2430_low_level_exit(musb);
259 struct musb_context_registers *musb_context) 367 otg_put_transceiver(musb->xceiv);
260{ 368
261 musb_writel(musb->mregs, OTG_SYSCONFIG, musb_context->otg_sysconfig); 369 return 0;
262 musb_writel(musb->mregs, OTG_FORCESTDBY, musb_context->otg_forcestandby);
263} 370}
264#endif
265 371
266static int musb_platform_suspend(struct musb *musb) 372static const struct musb_platform_ops omap2430_ops = {
373 .init = omap2430_musb_init,
374 .exit = omap2430_musb_exit,
375
376 .set_mode = omap2430_musb_set_mode,
377 .try_idle = omap2430_musb_try_idle,
378
379 .set_vbus = omap2430_musb_set_vbus,
380};
381
382static u64 omap2430_dmamask = DMA_BIT_MASK(32);
383
384static int __init omap2430_probe(struct platform_device *pdev)
267{ 385{
268 u32 l; 386 struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
387 struct platform_device *musb;
388 struct omap2430_glue *glue;
389 struct clk *clk;
269 390
270 if (!musb->clock) 391 int ret = -ENOMEM;
271 return 0;
272 392
273 /* in any role */ 393 glue = kzalloc(sizeof(*glue), GFP_KERNEL);
274 l = musb_readl(musb->mregs, OTG_FORCESTDBY); 394 if (!glue) {
275 l |= ENABLEFORCE; /* enable MSTANDBY */ 395 dev_err(&pdev->dev, "failed to allocate glue context\n");
276 musb_writel(musb->mregs, OTG_FORCESTDBY, l); 396 goto err0;
397 }
277 398
278 l = musb_readl(musb->mregs, OTG_SYSCONFIG); 399 musb = platform_device_alloc("musb-hdrc", -1);
279 l |= ENABLEWAKEUP; /* enable wakeup */ 400 if (!musb) {
280 musb_writel(musb->mregs, OTG_SYSCONFIG, l); 401 dev_err(&pdev->dev, "failed to allocate musb device\n");
402 goto err1;
403 }
281 404
282 otg_set_suspend(musb->xceiv, 1); 405 clk = clk_get(&pdev->dev, "ick");
406 if (IS_ERR(clk)) {
407 dev_err(&pdev->dev, "failed to get clock\n");
408 ret = PTR_ERR(clk);
409 goto err2;
410 }
283 411
284 if (musb->set_clock) 412 ret = clk_enable(clk);
285 musb->set_clock(musb->clock, 0); 413 if (ret) {
286 else 414 dev_err(&pdev->dev, "failed to enable clock\n");
287 clk_disable(musb->clock); 415 goto err3;
416 }
417
418 musb->dev.parent = &pdev->dev;
419 musb->dev.dma_mask = &omap2430_dmamask;
420 musb->dev.coherent_dma_mask = omap2430_dmamask;
421
422 glue->dev = &pdev->dev;
423 glue->musb = musb;
424 glue->clk = clk;
425
426 pdata->platform_ops = &omap2430_ops;
427
428 platform_set_drvdata(pdev, glue);
429
430 ret = platform_device_add_resources(musb, pdev->resource,
431 pdev->num_resources);
432 if (ret) {
433 dev_err(&pdev->dev, "failed to add resources\n");
434 goto err4;
435 }
436
437 ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
438 if (ret) {
439 dev_err(&pdev->dev, "failed to add platform_data\n");
440 goto err4;
441 }
442
443 ret = platform_device_add(musb);
444 if (ret) {
445 dev_err(&pdev->dev, "failed to register musb device\n");
446 goto err4;
447 }
288 448
289 return 0; 449 return 0;
450
451err4:
452 clk_disable(clk);
453
454err3:
455 clk_put(clk);
456
457err2:
458 platform_device_put(musb);
459
460err1:
461 kfree(glue);
462
463err0:
464 return ret;
290} 465}
291 466
292static int musb_platform_resume(struct musb *musb) 467static int __exit omap2430_remove(struct platform_device *pdev)
293{ 468{
294 u32 l; 469 struct omap2430_glue *glue = platform_get_drvdata(pdev);
295 470
296 if (!musb->clock) 471 platform_device_del(glue->musb);
297 return 0; 472 platform_device_put(glue->musb);
473 clk_disable(glue->clk);
474 clk_put(glue->clk);
475 kfree(glue);
298 476
299 otg_set_suspend(musb->xceiv, 0); 477 return 0;
478}
300 479
301 if (musb->set_clock) 480#ifdef CONFIG_PM
302 musb->set_clock(musb->clock, 1); 481static void omap2430_save_context(struct musb *musb)
303 else 482{
304 clk_enable(musb->clock); 483 musb->context.otg_sysconfig = musb_readl(musb->mregs, OTG_SYSCONFIG);
484 musb->context.otg_forcestandby = musb_readl(musb->mregs, OTG_FORCESTDBY);
485}
305 486
306 l = musb_readl(musb->mregs, OTG_SYSCONFIG); 487static void omap2430_restore_context(struct musb *musb)
307 l &= ~ENABLEWAKEUP; /* disable wakeup */ 488{
308 musb_writel(musb->mregs, OTG_SYSCONFIG, l); 489 musb_writel(musb->mregs, OTG_SYSCONFIG, musb->context.otg_sysconfig);
490 musb_writel(musb->mregs, OTG_FORCESTDBY, musb->context.otg_forcestandby);
491}
309 492
310 l = musb_readl(musb->mregs, OTG_FORCESTDBY); 493static int omap2430_suspend(struct device *dev)
311 l &= ~ENABLEFORCE; /* disable MSTANDBY */ 494{
312 musb_writel(musb->mregs, OTG_FORCESTDBY, l); 495 struct omap2430_glue *glue = dev_get_drvdata(dev);
496 struct musb *musb = glue_to_musb(glue);
497
498 omap2430_low_level_exit(musb);
499 otg_set_suspend(musb->xceiv, 1);
500 omap2430_save_context(musb);
501 clk_disable(glue->clk);
313 502
314 return 0; 503 return 0;
315} 504}
316 505
317 506static int omap2430_resume(struct device *dev)
318int musb_platform_exit(struct musb *musb)
319{ 507{
508 struct omap2430_glue *glue = dev_get_drvdata(dev);
509 struct musb *musb = glue_to_musb(glue);
510 int ret;
511
512 ret = clk_enable(glue->clk);
513 if (ret) {
514 dev_err(dev, "faled to enable clock\n");
515 return ret;
516 }
320 517
321 musb_platform_suspend(musb); 518 omap2430_low_level_init(musb);
519 omap2430_restore_context(musb);
520 otg_set_suspend(musb->xceiv, 0);
322 521
323 otg_put_transceiver(musb->xceiv);
324 return 0; 522 return 0;
325} 523}
524
525static struct dev_pm_ops omap2430_pm_ops = {
526 .suspend = omap2430_suspend,
527 .resume = omap2430_resume,
528};
529
530#define DEV_PM_OPS (&omap2430_pm_ops)
531#else
532#define DEV_PM_OPS NULL
533#endif
534
535static struct platform_driver omap2430_driver = {
536 .remove = __exit_p(omap2430_remove),
537 .driver = {
538 .name = "musb-omap2430",
539 .pm = DEV_PM_OPS,
540 },
541};
542
543MODULE_DESCRIPTION("OMAP2PLUS MUSB Glue Layer");
544MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
545MODULE_LICENSE("GPL v2");
546
547static int __init omap2430_init(void)
548{
549 return platform_driver_probe(&omap2430_driver, omap2430_probe);
550}
551subsys_initcall(omap2430_init);
552
553static void __exit omap2430_exit(void)
554{
555 platform_driver_unregister(&omap2430_driver);
556}
557module_exit(omap2430_exit);