diff options
Diffstat (limited to 'drivers/usb/musb/musb_gadget.c')
-rw-r--r-- | drivers/usb/musb/musb_gadget.c | 196 |
1 files changed, 102 insertions, 94 deletions
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 74073f9a43f0..c49b9ba025ab 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c | |||
@@ -429,112 +429,102 @@ void musb_g_tx(struct musb *musb, u8 epnum) | |||
429 | DBG(4, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr); | 429 | DBG(4, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr); |
430 | 430 | ||
431 | dma = is_dma_capable() ? musb_ep->dma : NULL; | 431 | dma = is_dma_capable() ? musb_ep->dma : NULL; |
432 | do { | 432 | |
433 | /* REVISIT for high bandwidth, MUSB_TXCSR_P_INCOMPTX | 433 | /* |
434 | * probably rates reporting as a host error | 434 | * REVISIT: for high bandwidth, MUSB_TXCSR_P_INCOMPTX |
435 | * probably rates reporting as a host error. | ||
436 | */ | ||
437 | if (csr & MUSB_TXCSR_P_SENTSTALL) { | ||
438 | csr |= MUSB_TXCSR_P_WZC_BITS; | ||
439 | csr &= ~MUSB_TXCSR_P_SENTSTALL; | ||
440 | musb_writew(epio, MUSB_TXCSR, csr); | ||
441 | return; | ||
442 | } | ||
443 | |||
444 | if (csr & MUSB_TXCSR_P_UNDERRUN) { | ||
445 | /* We NAKed, no big deal... little reason to care. */ | ||
446 | csr |= MUSB_TXCSR_P_WZC_BITS; | ||
447 | csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); | ||
448 | musb_writew(epio, MUSB_TXCSR, csr); | ||
449 | DBG(20, "underrun on ep%d, req %p\n", epnum, request); | ||
450 | } | ||
451 | |||
452 | if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { | ||
453 | /* | ||
454 | * SHOULD NOT HAPPEN... has with CPPI though, after | ||
455 | * changing SENDSTALL (and other cases); harmless? | ||
435 | */ | 456 | */ |
436 | if (csr & MUSB_TXCSR_P_SENTSTALL) { | 457 | DBG(5, "%s dma still busy?\n", musb_ep->end_point.name); |
437 | csr |= MUSB_TXCSR_P_WZC_BITS; | 458 | return; |
438 | csr &= ~MUSB_TXCSR_P_SENTSTALL; | 459 | } |
439 | musb_writew(epio, MUSB_TXCSR, csr); | 460 | |
440 | break; | 461 | if (request) { |
441 | } | 462 | u8 is_dma = 0; |
442 | 463 | ||
443 | if (csr & MUSB_TXCSR_P_UNDERRUN) { | 464 | if (dma && (csr & MUSB_TXCSR_DMAENAB)) { |
444 | /* we NAKed, no big deal ... little reason to care */ | 465 | is_dma = 1; |
445 | csr |= MUSB_TXCSR_P_WZC_BITS; | 466 | csr |= MUSB_TXCSR_P_WZC_BITS; |
446 | csr &= ~(MUSB_TXCSR_P_UNDERRUN | 467 | csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN | |
447 | | MUSB_TXCSR_TXPKTRDY); | 468 | MUSB_TXCSR_TXPKTRDY); |
448 | musb_writew(epio, MUSB_TXCSR, csr); | 469 | musb_writew(epio, MUSB_TXCSR, csr); |
449 | DBG(20, "underrun on ep%d, req %p\n", epnum, request); | 470 | /* Ensure writebuffer is empty. */ |
471 | csr = musb_readw(epio, MUSB_TXCSR); | ||
472 | request->actual += musb_ep->dma->actual_len; | ||
473 | DBG(4, "TXCSR%d %04x, DMA off, len %zu, req %p\n", | ||
474 | epnum, csr, musb_ep->dma->actual_len, request); | ||
450 | } | 475 | } |
451 | 476 | ||
452 | if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { | 477 | if (is_dma || request->actual == request->length) { |
453 | /* SHOULD NOT HAPPEN ... has with cppi though, after | 478 | /* |
454 | * changing SENDSTALL (and other cases); harmless? | 479 | * First, maybe a terminating short packet. Some DMA |
480 | * engines might handle this by themselves. | ||
455 | */ | 481 | */ |
456 | DBG(5, "%s dma still busy?\n", musb_ep->end_point.name); | 482 | if ((request->zero && request->length |
457 | break; | 483 | && request->length % musb_ep->packet_sz == 0) |
458 | } | ||
459 | |||
460 | if (request) { | ||
461 | u8 is_dma = 0; | ||
462 | |||
463 | if (dma && (csr & MUSB_TXCSR_DMAENAB)) { | ||
464 | is_dma = 1; | ||
465 | csr |= MUSB_TXCSR_P_WZC_BITS; | ||
466 | csr &= ~(MUSB_TXCSR_DMAENAB | ||
467 | | MUSB_TXCSR_P_UNDERRUN | ||
468 | | MUSB_TXCSR_TXPKTRDY); | ||
469 | musb_writew(epio, MUSB_TXCSR, csr); | ||
470 | /* ensure writebuffer is empty */ | ||
471 | csr = musb_readw(epio, MUSB_TXCSR); | ||
472 | request->actual += musb_ep->dma->actual_len; | ||
473 | DBG(4, "TXCSR%d %04x, dma off, " | ||
474 | "len %zu, req %p\n", | ||
475 | epnum, csr, | ||
476 | musb_ep->dma->actual_len, | ||
477 | request); | ||
478 | } | ||
479 | |||
480 | if (is_dma || request->actual == request->length) { | ||
481 | |||
482 | /* First, maybe a terminating short packet. | ||
483 | * Some DMA engines might handle this by | ||
484 | * themselves. | ||
485 | */ | ||
486 | if ((request->zero | ||
487 | && request->length | ||
488 | && (request->length | ||
489 | % musb_ep->packet_sz) | ||
490 | == 0) | ||
491 | #ifdef CONFIG_USB_INVENTRA_DMA | 484 | #ifdef CONFIG_USB_INVENTRA_DMA |
492 | || (is_dma && | 485 | || (is_dma && (!dma->desired_mode || |
493 | ((!dma->desired_mode) || | 486 | (request->actual & |
494 | (request->actual & | 487 | (musb_ep->packet_sz - 1)))) |
495 | (musb_ep->packet_sz - 1)))) | ||
496 | #endif | 488 | #endif |
497 | ) { | 489 | ) { |
498 | /* on dma completion, fifo may not | 490 | /* |
499 | * be available yet ... | 491 | * On DMA completion, FIFO may not be |
500 | */ | 492 | * available yet... |
501 | if (csr & MUSB_TXCSR_TXPKTRDY) | ||
502 | break; | ||
503 | |||
504 | DBG(4, "sending zero pkt\n"); | ||
505 | musb_writew(epio, MUSB_TXCSR, | ||
506 | MUSB_TXCSR_MODE | ||
507 | | MUSB_TXCSR_TXPKTRDY); | ||
508 | request->zero = 0; | ||
509 | } | ||
510 | |||
511 | /* ... or if not, then complete it */ | ||
512 | musb_g_giveback(musb_ep, request, 0); | ||
513 | |||
514 | /* kickstart next transfer if appropriate; | ||
515 | * the packet that just completed might not | ||
516 | * be transmitted for hours or days. | ||
517 | * REVISIT for double buffering... | ||
518 | * FIXME revisit for stalls too... | ||
519 | */ | 493 | */ |
520 | musb_ep_select(mbase, epnum); | 494 | if (csr & MUSB_TXCSR_TXPKTRDY) |
521 | csr = musb_readw(epio, MUSB_TXCSR); | 495 | return; |
522 | if (csr & MUSB_TXCSR_FIFONOTEMPTY) | 496 | |
523 | break; | 497 | DBG(4, "sending zero pkt\n"); |
524 | request = musb_ep->desc | 498 | musb_writew(epio, MUSB_TXCSR, MUSB_TXCSR_MODE |
525 | ? next_request(musb_ep) | 499 | | MUSB_TXCSR_TXPKTRDY); |
526 | : NULL; | 500 | request->zero = 0; |
527 | if (!request) { | ||
528 | DBG(4, "%s idle now\n", | ||
529 | musb_ep->end_point.name); | ||
530 | break; | ||
531 | } | ||
532 | } | 501 | } |
533 | 502 | ||
534 | txstate(musb, to_musb_request(request)); | 503 | /* ... or if not, then complete it. */ |
504 | musb_g_giveback(musb_ep, request, 0); | ||
505 | |||
506 | /* | ||
507 | * Kickstart next transfer if appropriate; | ||
508 | * the packet that just completed might not | ||
509 | * be transmitted for hours or days. | ||
510 | * REVISIT for double buffering... | ||
511 | * FIXME revisit for stalls too... | ||
512 | */ | ||
513 | musb_ep_select(mbase, epnum); | ||
514 | csr = musb_readw(epio, MUSB_TXCSR); | ||
515 | if (csr & MUSB_TXCSR_FIFONOTEMPTY) | ||
516 | return; | ||
517 | |||
518 | if (!musb_ep->desc) { | ||
519 | DBG(4, "%s idle now\n", | ||
520 | musb_ep->end_point.name); | ||
521 | return; | ||
522 | } else | ||
523 | request = next_request(musb_ep); | ||
535 | } | 524 | } |
536 | 525 | ||
537 | } while (0); | 526 | txstate(musb, to_musb_request(request)); |
527 | } | ||
538 | } | 528 | } |
539 | 529 | ||
540 | /* ------------------------------------------------------------ */ | 530 | /* ------------------------------------------------------------ */ |
@@ -966,6 +956,7 @@ static int musb_gadget_enable(struct usb_ep *ep, | |||
966 | 956 | ||
967 | musb_ep->desc = desc; | 957 | musb_ep->desc = desc; |
968 | musb_ep->busy = 0; | 958 | musb_ep->busy = 0; |
959 | musb_ep->wedged = 0; | ||
969 | status = 0; | 960 | status = 0; |
970 | 961 | ||
971 | pr_debug("%s periph: enabled %s for %s %s, %smaxpacket %d\n", | 962 | pr_debug("%s periph: enabled %s for %s %s, %smaxpacket %d\n", |
@@ -1220,7 +1211,7 @@ done: | |||
1220 | * | 1211 | * |
1221 | * exported to ep0 code | 1212 | * exported to ep0 code |
1222 | */ | 1213 | */ |
1223 | int musb_gadget_set_halt(struct usb_ep *ep, int value) | 1214 | static int musb_gadget_set_halt(struct usb_ep *ep, int value) |
1224 | { | 1215 | { |
1225 | struct musb_ep *musb_ep = to_musb_ep(ep); | 1216 | struct musb_ep *musb_ep = to_musb_ep(ep); |
1226 | u8 epnum = musb_ep->current_epnum; | 1217 | u8 epnum = musb_ep->current_epnum; |
@@ -1262,7 +1253,8 @@ int musb_gadget_set_halt(struct usb_ep *ep, int value) | |||
1262 | goto done; | 1253 | goto done; |
1263 | } | 1254 | } |
1264 | } | 1255 | } |
1265 | } | 1256 | } else |
1257 | musb_ep->wedged = 0; | ||
1266 | 1258 | ||
1267 | /* set/clear the stall and toggle bits */ | 1259 | /* set/clear the stall and toggle bits */ |
1268 | DBG(2, "%s: %s stall\n", ep->name, value ? "set" : "clear"); | 1260 | DBG(2, "%s: %s stall\n", ep->name, value ? "set" : "clear"); |
@@ -1301,6 +1293,21 @@ done: | |||
1301 | return status; | 1293 | return status; |
1302 | } | 1294 | } |
1303 | 1295 | ||
1296 | /* | ||
1297 | * Sets the halt feature with the clear requests ignored | ||
1298 | */ | ||
1299 | static int musb_gadget_set_wedge(struct usb_ep *ep) | ||
1300 | { | ||
1301 | struct musb_ep *musb_ep = to_musb_ep(ep); | ||
1302 | |||
1303 | if (!ep) | ||
1304 | return -EINVAL; | ||
1305 | |||
1306 | musb_ep->wedged = 1; | ||
1307 | |||
1308 | return usb_ep_set_halt(ep); | ||
1309 | } | ||
1310 | |||
1304 | static int musb_gadget_fifo_status(struct usb_ep *ep) | 1311 | static int musb_gadget_fifo_status(struct usb_ep *ep) |
1305 | { | 1312 | { |
1306 | struct musb_ep *musb_ep = to_musb_ep(ep); | 1313 | struct musb_ep *musb_ep = to_musb_ep(ep); |
@@ -1371,6 +1378,7 @@ static const struct usb_ep_ops musb_ep_ops = { | |||
1371 | .queue = musb_gadget_queue, | 1378 | .queue = musb_gadget_queue, |
1372 | .dequeue = musb_gadget_dequeue, | 1379 | .dequeue = musb_gadget_dequeue, |
1373 | .set_halt = musb_gadget_set_halt, | 1380 | .set_halt = musb_gadget_set_halt, |
1381 | .set_wedge = musb_gadget_set_wedge, | ||
1374 | .fifo_status = musb_gadget_fifo_status, | 1382 | .fifo_status = musb_gadget_fifo_status, |
1375 | .fifo_flush = musb_gadget_fifo_flush | 1383 | .fifo_flush = musb_gadget_fifo_flush |
1376 | }; | 1384 | }; |