diff options
Diffstat (limited to 'drivers/usb/musb/omap2430.c')
-rw-r--r-- | drivers/usb/musb/omap2430.c | 378 |
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 | ||
40 | struct 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 | ||
39 | static struct timer_list musb_idle_timer; | 47 | static 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 | ||
101 | void musb_platform_try_idle(struct musb *musb, unsigned long timeout) | 105 | static 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 | ||
134 | void musb_platform_enable(struct musb *musb) | 138 | static void omap2430_musb_set_vbus(struct musb *musb, int is_on) |
135 | { | ||
136 | } | ||
137 | void musb_platform_disable(struct musb *musb) | ||
138 | { | ||
139 | } | ||
140 | static 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 | ||
178 | static int musb_platform_resume(struct musb *musb); | 201 | static int omap2430_musb_set_mode(struct musb *musb, u8 musb_mode) |
179 | |||
180 | int 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 | ||
190 | int __init musb_platform_init(struct musb *musb, void *board_data) | 211 | static 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 | |||
225 | static 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 */ | ||
239 | static 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 | |||
293 | static 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 | 363 | static int omap2430_musb_exit(struct musb *musb) |
251 | void 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 | ||
258 | void 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 | ||
266 | static int musb_platform_suspend(struct musb *musb) | 372 | static 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 | |||
382 | static u64 omap2430_dmamask = DMA_BIT_MASK(32); | ||
383 | |||
384 | static 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 | |||
451 | err4: | ||
452 | clk_disable(clk); | ||
453 | |||
454 | err3: | ||
455 | clk_put(clk); | ||
456 | |||
457 | err2: | ||
458 | platform_device_put(musb); | ||
459 | |||
460 | err1: | ||
461 | kfree(glue); | ||
462 | |||
463 | err0: | ||
464 | return ret; | ||
290 | } | 465 | } |
291 | 466 | ||
292 | static int musb_platform_resume(struct musb *musb) | 467 | static 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); | 481 | static 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); | 487 | static 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); | 493 | static 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 | 506 | static int omap2430_resume(struct device *dev) | |
318 | int 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 | |||
525 | static 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 | |||
535 | static 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 | |||
543 | MODULE_DESCRIPTION("OMAP2PLUS MUSB Glue Layer"); | ||
544 | MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); | ||
545 | MODULE_LICENSE("GPL v2"); | ||
546 | |||
547 | static int __init omap2430_init(void) | ||
548 | { | ||
549 | return platform_driver_probe(&omap2430_driver, omap2430_probe); | ||
550 | } | ||
551 | subsys_initcall(omap2430_init); | ||
552 | |||
553 | static void __exit omap2430_exit(void) | ||
554 | { | ||
555 | platform_driver_unregister(&omap2430_driver); | ||
556 | } | ||
557 | module_exit(omap2430_exit); | ||