usb: dwc3: add a bounce buffer for control endpoints
authorFelipe Balbi <balbi@ti.com>
Sat, 27 Aug 2011 19:07:53 +0000 (22:07 +0300)
committerFelipe Balbi <balbi@ti.com>
Fri, 9 Sep 2011 10:02:04 +0000 (13:02 +0300)
This core cannot handle OUT transfers which aren't
aligned to wMaxPacketSize, but that can happen at
least on control endpoint with the USB Audio Class.

This patch adds a bounce buffer to be used on the
case of a non-aligned ep0out request is queued.

Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/dwc3/core.h
drivers/usb/dwc3/gadget.c

index 08d8ff6b943a9408d7587a02bebac87cee24c6ce..8688b5a809d2433e0593bd08de0c012886ec3d18 100644 (file)
@@ -505,11 +505,13 @@ static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat)
  * struct dwc3 - representation of our controller
  * @ctrl_req: usb control request which is used for ep0
  * @ep0_trb: trb which is used for the ctrl_req
+ * @ep0_bounce: bounce buffer for ep0
  * @setup_buf: used while precessing STD USB requests
  * @ctrl_req_addr: dma address of ctrl_req
  * @ep0_trb: dma address of ep0_trb
  * @ep0_usb_req: dummy req used while handling STD USB requests
  * @setup_buf_addr: dma address of setup_buf
+ * @ep0_bounce_addr: dma address of ep0_bounce
  * @lock: for synchronizing
  * @dev: pointer to our struct device
  * @event_buffer_list: a list of event buffers
@@ -522,6 +524,7 @@ static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat)
  * @is_selfpowered: true when we are selfpowered
  * @three_stage_setup: set if we perform a three phase setup
  * @ep0_status_pending: ep0 status response without a req is pending
+ * @ep0_bounced: true when we used bounce buffer
  * @ep0state: state of endpoint zero
  * @link_state: link state
  * @speed: device speed (super, high, full, low)
@@ -531,10 +534,12 @@ static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat)
 struct dwc3 {
        struct usb_ctrlrequest  *ctrl_req;
        struct dwc3_trb_hw      *ep0_trb;
+       void                    *ep0_bounce;
        u8                      *setup_buf;
        dma_addr_t              ctrl_req_addr;
        dma_addr_t              ep0_trb_addr;
        dma_addr_t              setup_buf_addr;
+       dma_addr_t              ep0_bounce_addr;
        struct usb_request      ep0_usb_req;
        /* device lock */
        spinlock_t              lock;
@@ -564,6 +569,7 @@ struct dwc3 {
        unsigned                is_selfpowered:1;
        unsigned                three_stage_setup:1;
        unsigned                ep0_status_pending:1;
+       unsigned                ep0_bounced:1;
 
        enum dwc3_ep0_state     ep0state;
        enum dwc3_link_state    link_state;
index 56ccd97000a6a092749f1b1814f82a6604c443b5..569473bcfb508ccd294e3c5fc3f6265146ae489a 100644 (file)
@@ -1957,6 +1957,14 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
                goto err2;
        }
 
+       dwc->ep0_bounce = dma_alloc_coherent(dwc->dev,
+                       512, &dwc->ep0_bounce_addr, GFP_KERNEL);
+       if (!dwc->ep0_bounce) {
+               dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
+               ret = -ENOMEM;
+               goto err3;
+       }
+
        dev_set_name(&dwc->gadget.dev, "gadget");
 
        dwc->gadget.ops                 = &dwc3_gadget_ops;
@@ -1978,7 +1986,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
 
        ret = dwc3_gadget_init_endpoints(dwc);
        if (ret)
-               goto err3;
+               goto err4;
 
        irq = platform_get_irq(to_platform_device(dwc->dev), 0);
 
@@ -1987,7 +1995,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
        if (ret) {
                dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
                                irq, ret);
-               goto err4;
+               goto err5;
        }
 
        /* Enable all but Start and End of Frame IRQs */
@@ -2006,27 +2014,31 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
        if (ret) {
                dev_err(dwc->dev, "failed to register gadget device\n");
                put_device(&dwc->gadget.dev);
-               goto err5;
+               goto err6;
        }
 
        ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
        if (ret) {
                dev_err(dwc->dev, "failed to register udc\n");
-               goto err6;
+               goto err7;
        }
 
        return 0;
 
-err6:
+err7:
        device_unregister(&dwc->gadget.dev);
 
-err5:
+err6:
        dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
        free_irq(irq, dwc);
 
-err4:
+err5:
        dwc3_gadget_free_endpoints(dwc);
 
+err4:
+       dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
+                       dwc->ep0_bounce_addr);
+
 err3:
        dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2,
                        dwc->setup_buf, dwc->setup_buf_addr);
@@ -2059,6 +2071,9 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
 
        dwc3_gadget_free_endpoints(dwc);
 
+       dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
+                       dwc->ep0_bounce_addr);
+
        dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2,
                        dwc->setup_buf, dwc->setup_buf_addr);