diff options
Diffstat (limited to 'drivers/usb/musb/musb_gadget_ep0.c')
-rw-r--r-- | drivers/usb/musb/musb_gadget_ep0.c | 84 |
1 files changed, 53 insertions, 31 deletions
diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 522efb31b56b..53d06451f820 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c | |||
@@ -199,7 +199,6 @@ service_in_request(struct musb *musb, const struct usb_ctrlrequest *ctrlrequest) | |||
199 | static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req) | 199 | static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req) |
200 | { | 200 | { |
201 | musb_g_giveback(&musb->endpoints[0].ep_in, req, 0); | 201 | musb_g_giveback(&musb->endpoints[0].ep_in, req, 0); |
202 | musb->ep0_state = MUSB_EP0_STAGE_SETUP; | ||
203 | } | 202 | } |
204 | 203 | ||
205 | /* | 204 | /* |
@@ -258,30 +257,53 @@ __acquires(musb->lock) | |||
258 | case USB_RECIP_INTERFACE: | 257 | case USB_RECIP_INTERFACE: |
259 | break; | 258 | break; |
260 | case USB_RECIP_ENDPOINT:{ | 259 | case USB_RECIP_ENDPOINT:{ |
261 | const u8 num = ctrlrequest->wIndex & 0x0f; | 260 | const u8 epnum = |
262 | struct musb_ep *musb_ep; | 261 | ctrlrequest->wIndex & 0x0f; |
262 | struct musb_ep *musb_ep; | ||
263 | struct musb_hw_ep *ep; | ||
264 | void __iomem *regs; | ||
265 | int is_in; | ||
266 | u16 csr; | ||
263 | 267 | ||
264 | if (num == 0 | 268 | if (epnum == 0 || epnum >= MUSB_C_NUM_EPS || |
265 | || num >= MUSB_C_NUM_EPS | 269 | ctrlrequest->wValue != USB_ENDPOINT_HALT) |
266 | || ctrlrequest->wValue | ||
267 | != USB_ENDPOINT_HALT) | ||
268 | break; | 270 | break; |
269 | 271 | ||
270 | if (ctrlrequest->wIndex & USB_DIR_IN) | 272 | ep = musb->endpoints + epnum; |
271 | musb_ep = &musb->endpoints[num].ep_in; | 273 | regs = ep->regs; |
274 | is_in = ctrlrequest->wIndex & USB_DIR_IN; | ||
275 | if (is_in) | ||
276 | musb_ep = &ep->ep_in; | ||
272 | else | 277 | else |
273 | musb_ep = &musb->endpoints[num].ep_out; | 278 | musb_ep = &ep->ep_out; |
274 | if (!musb_ep->desc) | 279 | if (!musb_ep->desc) |
275 | break; | 280 | break; |
276 | 281 | ||
277 | /* REVISIT do it directly, no locking games */ | 282 | handled = 1; |
278 | spin_unlock(&musb->lock); | 283 | /* Ignore request if endpoint is wedged */ |
279 | musb_gadget_set_halt(&musb_ep->end_point, 0); | 284 | if (musb_ep->wedged) |
280 | spin_lock(&musb->lock); | 285 | break; |
286 | |||
287 | musb_ep_select(mbase, epnum); | ||
288 | if (is_in) { | ||
289 | csr = musb_readw(regs, MUSB_TXCSR); | ||
290 | csr |= MUSB_TXCSR_CLRDATATOG | | ||
291 | MUSB_TXCSR_P_WZC_BITS; | ||
292 | csr &= ~(MUSB_TXCSR_P_SENDSTALL | | ||
293 | MUSB_TXCSR_P_SENTSTALL | | ||
294 | MUSB_TXCSR_TXPKTRDY); | ||
295 | musb_writew(regs, MUSB_TXCSR, csr); | ||
296 | } else { | ||
297 | csr = musb_readw(regs, MUSB_RXCSR); | ||
298 | csr |= MUSB_RXCSR_CLRDATATOG | | ||
299 | MUSB_RXCSR_P_WZC_BITS; | ||
300 | csr &= ~(MUSB_RXCSR_P_SENDSTALL | | ||
301 | MUSB_RXCSR_P_SENTSTALL); | ||
302 | musb_writew(regs, MUSB_RXCSR, csr); | ||
303 | } | ||
281 | 304 | ||
282 | /* select ep0 again */ | 305 | /* select ep0 again */ |
283 | musb_ep_select(mbase, 0); | 306 | musb_ep_select(mbase, 0); |
284 | handled = 1; | ||
285 | } break; | 307 | } break; |
286 | default: | 308 | default: |
287 | /* class, vendor, etc ... delegate */ | 309 | /* class, vendor, etc ... delegate */ |
@@ -374,10 +396,8 @@ stall: | |||
374 | int is_in; | 396 | int is_in; |
375 | u16 csr; | 397 | u16 csr; |
376 | 398 | ||
377 | if (epnum == 0 | 399 | if (epnum == 0 || epnum >= MUSB_C_NUM_EPS || |
378 | || epnum >= MUSB_C_NUM_EPS | 400 | ctrlrequest->wValue != USB_ENDPOINT_HALT) |
379 | || ctrlrequest->wValue | ||
380 | != USB_ENDPOINT_HALT) | ||
381 | break; | 401 | break; |
382 | 402 | ||
383 | ep = musb->endpoints + epnum; | 403 | ep = musb->endpoints + epnum; |
@@ -392,24 +412,20 @@ stall: | |||
392 | 412 | ||
393 | musb_ep_select(mbase, epnum); | 413 | musb_ep_select(mbase, epnum); |
394 | if (is_in) { | 414 | if (is_in) { |
395 | csr = musb_readw(regs, | 415 | csr = musb_readw(regs, MUSB_TXCSR); |
396 | MUSB_TXCSR); | ||
397 | if (csr & MUSB_TXCSR_FIFONOTEMPTY) | 416 | if (csr & MUSB_TXCSR_FIFONOTEMPTY) |
398 | csr |= MUSB_TXCSR_FLUSHFIFO; | 417 | csr |= MUSB_TXCSR_FLUSHFIFO; |
399 | csr |= MUSB_TXCSR_P_SENDSTALL | 418 | csr |= MUSB_TXCSR_P_SENDSTALL |
400 | | MUSB_TXCSR_CLRDATATOG | 419 | | MUSB_TXCSR_CLRDATATOG |
401 | | MUSB_TXCSR_P_WZC_BITS; | 420 | | MUSB_TXCSR_P_WZC_BITS; |
402 | musb_writew(regs, MUSB_TXCSR, | 421 | musb_writew(regs, MUSB_TXCSR, csr); |
403 | csr); | ||
404 | } else { | 422 | } else { |
405 | csr = musb_readw(regs, | 423 | csr = musb_readw(regs, MUSB_RXCSR); |
406 | MUSB_RXCSR); | ||
407 | csr |= MUSB_RXCSR_P_SENDSTALL | 424 | csr |= MUSB_RXCSR_P_SENDSTALL |
408 | | MUSB_RXCSR_FLUSHFIFO | 425 | | MUSB_RXCSR_FLUSHFIFO |
409 | | MUSB_RXCSR_CLRDATATOG | 426 | | MUSB_RXCSR_CLRDATATOG |
410 | | MUSB_RXCSR_P_WZC_BITS; | 427 | | MUSB_RXCSR_P_WZC_BITS; |
411 | musb_writew(regs, MUSB_RXCSR, | 428 | musb_writew(regs, MUSB_RXCSR, csr); |
412 | csr); | ||
413 | } | 429 | } |
414 | 430 | ||
415 | /* select ep0 again */ | 431 | /* select ep0 again */ |
@@ -648,7 +664,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) | |||
648 | musb->ep0_state = MUSB_EP0_STAGE_STATUSIN; | 664 | musb->ep0_state = MUSB_EP0_STAGE_STATUSIN; |
649 | break; | 665 | break; |
650 | default: | 666 | default: |
651 | ERR("SetupEnd came in a wrong ep0stage %s", | 667 | ERR("SetupEnd came in a wrong ep0stage %s\n", |
652 | decode_ep0stage(musb->ep0_state)); | 668 | decode_ep0stage(musb->ep0_state)); |
653 | } | 669 | } |
654 | csr = musb_readw(regs, MUSB_CSR0); | 670 | csr = musb_readw(regs, MUSB_CSR0); |
@@ -771,12 +787,18 @@ setup: | |||
771 | handled = service_zero_data_request( | 787 | handled = service_zero_data_request( |
772 | musb, &setup); | 788 | musb, &setup); |
773 | 789 | ||
790 | /* | ||
791 | * We're expecting no data in any case, so | ||
792 | * always set the DATAEND bit -- doing this | ||
793 | * here helps avoid SetupEnd interrupt coming | ||
794 | * in the idle stage when we're stalling... | ||
795 | */ | ||
796 | musb->ackpend |= MUSB_CSR0_P_DATAEND; | ||
797 | |||
774 | /* status stage might be immediate */ | 798 | /* status stage might be immediate */ |
775 | if (handled > 0) { | 799 | if (handled > 0) |
776 | musb->ackpend |= MUSB_CSR0_P_DATAEND; | ||
777 | musb->ep0_state = | 800 | musb->ep0_state = |
778 | MUSB_EP0_STAGE_STATUSIN; | 801 | MUSB_EP0_STAGE_STATUSIN; |
779 | } | ||
780 | break; | 802 | break; |
781 | 803 | ||
782 | /* sequence #1 (IN to host), includes GET_STATUS | 804 | /* sequence #1 (IN to host), includes GET_STATUS |