aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelipe Balbi <balbi@ti.com>2013-12-19 13:04:28 -0600
committerMark Brown <broonie@kernel.org>2014-12-10 12:49:42 +0000
commit7282b34d58a2a67b2d272d2e7ef805cdfcf50f7f (patch)
tree8da4b17d434159302bf51088bdba23ac5788f1a7
parentb58f6024f9b807a4f7464b021101be6c4dc16ba3 (diff)
downloadlinux-linaro-stable-7282b34d58a2a67b2d272d2e7ef805cdfcf50f7f.tar.gz
usb: dwc3: core: allocate scratch buffers
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> (cherry picked from commit adac070445873c119bb40fdf8bdb7fef701a953e) Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--drivers/usb/dwc3/core.c105
-rw-r--r--drivers/usb/dwc3/core.h6
2 files changed, 111 insertions, 0 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 74e705e3d5c3..f3d2c5167416 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -254,6 +254,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;
@@ -289,6 +373,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;
@@ -348,6 +433,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");
}
@@ -365,14 +454,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);
phy_exit(dwc->usb2_generic_phy);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index b287e69c1417..fc0c3e8a2355 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -38,6 +38,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)
@@ -653,10 +656,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 */
@@ -688,6 +693,7 @@ struct dwc3 {
u32 dcfg;
u32 gctl;
+ u32 nr_scratch;
u32 num_event_buffers;
u32 u1u2;
u32 maximum_speed;