usb: dwc3: core: allocate scratch buffers
authorFelipe Balbi <balbi@ti.com>
Thu, 19 Dec 2013 19:04:28 +0000 (13:04 -0600)
committerFelipe Balbi <balbi@ti.com>
Wed, 5 Mar 2014 20:39:55 +0000 (14:39 -0600)
We must read HWPARAMS4 register to figure out
how many scratch buffers we should allocate.

Later patch will use "Set Scratchpad Buffer
Array" command to pass the pointer to the
IP so it can be used during hibernation.

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

index 785feeeac3c1888f4223631e5b6225cebaa18ff9..8c627c9a51305c7ec80ccb315483449cb1e52360 100644 (file)
@@ -242,6 +242,90 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
        }
 }
 
+static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
+{
+       if (!dwc->has_hibernation)
+               return 0;
+
+       if (!dwc->nr_scratch)
+               return 0;
+
+       dwc->scratchbuf = kmalloc_array(dwc->nr_scratch,
+                       DWC3_SCRATCHBUF_SIZE, GFP_KERNEL);
+       if (!dwc->scratchbuf)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
+{
+       dma_addr_t scratch_addr;
+       u32 param;
+       int ret;
+
+       if (!dwc->has_hibernation)
+               return 0;
+
+       if (!dwc->nr_scratch)
+               return 0;
+
+        /* should never fall here */
+       if (!WARN_ON(dwc->scratchbuf))
+               return 0;
+
+       scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf,
+                       dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
+                       DMA_BIDIRECTIONAL);
+       if (dma_mapping_error(dwc->dev, scratch_addr)) {
+               dev_err(dwc->dev, "failed to map scratch buffer\n");
+               ret = -EFAULT;
+               goto err0;
+       }
+
+       dwc->scratch_addr = scratch_addr;
+
+       param = lower_32_bits(scratch_addr);
+
+       ret = dwc3_send_gadget_generic_command(dwc,
+                       DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param);
+       if (ret < 0)
+               goto err1;
+
+       param = upper_32_bits(scratch_addr);
+
+       ret = dwc3_send_gadget_generic_command(dwc,
+                       DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param);
+       if (ret < 0)
+               goto err1;
+
+       return 0;
+
+err1:
+       dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+                       DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
+
+err0:
+       return ret;
+}
+
+static void dwc3_free_scratch_buffers(struct dwc3 *dwc)
+{
+       if (!dwc->has_hibernation)
+               return;
+
+       if (!dwc->nr_scratch)
+               return;
+
+        /* should never fall here */
+       if (!WARN_ON(dwc->scratchbuf))
+               return;
+
+       dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+                       DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
+       kfree(dwc->scratchbuf);
+}
+
 static void dwc3_core_num_eps(struct dwc3 *dwc)
 {
        struct dwc3_hwparams    *parms = &dwc->hwparams;
@@ -277,6 +361,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
 static int dwc3_core_init(struct dwc3 *dwc)
 {
        unsigned long           timeout;
+       u32                     hwparams4 = dwc->hwparams.hwparams4;
        u32                     reg;
        int                     ret;
 
@@ -334,6 +419,10 @@ static int dwc3_core_init(struct dwc3 *dwc)
                else
                        reg &= ~DWC3_GCTL_DSBLCLKGTNG;
                break;
+       case DWC3_GHWPARAMS1_EN_PWROPT_HIB:
+               /* enable hibernation here */
+               dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);
+               break;
        default:
                dev_dbg(dwc->dev, "No power optimization available\n");
        }
@@ -351,14 +440,30 @@ static int dwc3_core_init(struct dwc3 *dwc)
 
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
 
+       ret = dwc3_alloc_scratch_buffers(dwc);
+       if (ret)
+               goto err1;
+
+       ret = dwc3_setup_scratch_buffers(dwc);
+       if (ret)
+               goto err2;
+
        return 0;
 
+err2:
+       dwc3_free_scratch_buffers(dwc);
+
+err1:
+       usb_phy_shutdown(dwc->usb2_phy);
+       usb_phy_shutdown(dwc->usb3_phy);
+
 err0:
        return ret;
 }
 
 static void dwc3_core_exit(struct dwc3 *dwc)
 {
+       dwc3_free_scratch_buffers(dwc);
        usb_phy_shutdown(dwc->usb2_phy);
        usb_phy_shutdown(dwc->usb3_phy);
 }
index 4c14796a5ffcad6639bc2b59e3b7d5e21add2369..bbb5b78eaf01cd54459fa5c9dd5ea84127a50049 100644 (file)
@@ -36,6 +36,7 @@
 #define DWC3_ENDPOINTS_NUM     32
 #define DWC3_XHCI_RESOURCES_NUM        2
 
+#define DWC3_SCRATCHBUF_SIZE   4096    /* each buffer is assumed to be 4KiB */
 #define DWC3_EVENT_SIZE                4       /* bytes */
 #define DWC3_EVENT_MAX_NUM     64      /* 2 events/endpoint */
 #define DWC3_EVENT_BUFFERS_SIZE        (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM)
@@ -601,6 +602,7 @@ struct dwc3_scratchpad_array {
  * @ep0_trb: dma address of ep0_trb
  * @ep0_usb_req: dummy req used while handling STD USB requests
  * @ep0_bounce_addr: dma address of ep0_bounce
+ * @scratch_addr: dma address of scratchbuf
  * @lock: for synchronizing
  * @dev: pointer to our struct device
  * @xhci: pointer to our xHCI child
@@ -609,6 +611,7 @@ struct dwc3_scratchpad_array {
  * @gadget_driver: pointer to the gadget driver
  * @regs: base address for our registers
  * @regs_size: address space size
+ * @nr_scratch: number of scratch buffers
  * @num_event_buffers: calculated number of event buffers
  * @u1u2: only used on revisions <1.83a for workaround
  * @maximum_speed: maximum speed requested (mainly for testing purposes)
@@ -651,10 +654,12 @@ struct dwc3 {
        struct usb_ctrlrequest  *ctrl_req;
        struct dwc3_trb         *ep0_trb;
        void                    *ep0_bounce;
+       void                    *scratchbuf;
        u8                      *setup_buf;
        dma_addr_t              ctrl_req_addr;
        dma_addr_t              ep0_trb_addr;
        dma_addr_t              ep0_bounce_addr;
+       dma_addr_t              scratch_addr;
        struct dwc3_request     ep0_usb_req;
 
        /* device lock */
@@ -683,6 +688,7 @@ struct dwc3 {
        u32                     dcfg;
        u32                     gctl;
 
+       u32                     nr_scratch;
        u32                     num_event_buffers;
        u32                     u1u2;
        u32                     maximum_speed;