[06/11] usb: dwc3: core: allocate scratch buffers

Message ID 1393357243-2958-7-git-send-email-balbi@ti.com
State Superseded
Headers show

Commit Message

Felipe Balbi Feb. 25, 2014, 7:40 p.m.
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 | 95 +++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc3/core.h |  6 ++++
 2 files changed, 101 insertions(+)

Comments

George Cherian March 5, 2014, 11:17 a.m. | #1
On 2/26/2014 1:10 AM, Felipe Balbi wrote:
> 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 | 95 +++++++++++++++++++++++++++++++++++++++++++++++++
>   drivers/usb/dwc3/core.h |  6 ++++
>   2 files changed, 101 insertions(+)
>
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> index a49217a..2864aad 100644
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
> @@ -242,6 +242,80 @@ 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;
> +

Shouldn't there be check for  dwc->scratchbuf and dwc->nr_scratch
before calling dma_map_single?

This fails in AM437x since both these are zero and leads to following crash

[   57.914762] Unable to handle kernel NULL pointer dereference at 
virtual address 00000000
[   57.926272] pgd = ed43c000
[   57.930222] [00000000] *pgd=ac05a831, *pte=00000000, *ppte=00000000
[   57.939425] Internal error: Oops: 817 [#1] SMP ARM
[   57.945964] Modules linked in: dwc3(+) snd_soc_omap snd_pcm_dmaengine 
snd_soc_core snd_compress regmap_spi snd_pcm snd_timer snd soundcore 
dwc3_omap matrix_keypad
[   57.965884] CPU: 0 PID: 1126 Comm: insmod Tainted: G        W 
3.14.0-rc5-00151-gc02b641-dirty #2
[   57.978198] task: ed1e2ac0 ti: ed456000 task.ti: ed456000
[   57.985570] PC is at v7_dma_clean_range+0x1c/0x34
[   57.992001] LR is at dma_cache_maint_page+0x90/0x120
[   57.998765] pc : [<c001d710>]    lr : [<c001963c>] psr: 000f0113
[   57.998765] sp : ed457d18  ip : edff2000  fp : c07f0924
[   58.014383] r10: c0888940  r9 : 00000000  r8 : c07f39c4
[   58.021495] r7 : 00080000  r6 : 00000000  r5 : 000c0000  r4 : c001d75c
[   58.030374] r3 : 0000001f  r2 : 00000020  r1 : 00000000  r0 : 00000000
[   58.039258] Flags: nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM 
Segment user
[   58.048971] Control: 10c53c7d  Table: ad43c059  DAC: 00000015
[   58.056793] Process insmod (pid: 1126, stack limit = 0xed456248)
[   58.064967] Stack: (0xed457d18 to 0xed458000)
[   58.070899] 
7d00:                                                       00000000 
ed456000
[   58.082031] 7d20: 00000000 00000000 ee7f2000 00000000 00000000 
48a22004 25803004 ed5ffc10
[   58.093164] 7d40: 000c0002 c00196f4 c001d75c c053205c bf0b1098 
00000000 ee7f2000 ed474810
[   58.104298] 7d60: c07f08c0 c0019810 00000000 00000000 c0000000 
bf0ab0e0 00000000 00000000
[   58.115425] 7d80: 00000000 ed5ffc10 bf0b2b70 00000000 00000000 
bf0b2b70 ed456000 00000001
[   58.126553] 7da0: 00000000 c0352de8 c0352dd0 c0de004c ed5ffc10 
c0351a30 ed5ffc10 bf0b2b70
[   58.137682] 7dc0: ed5ffc44 00000000 bf0b6000 c0351be0 00000000 
bf0b2b70 c0351b4c c03501c4
[   58.148810] 7de0: ed02f0a8 ed4fded0 bf0b2b70 ed2d7180 c0863090 
c03511c4 bf0b10b8 bf0b2b70
[   58.159937] 7e00: bf0b2bd8 bf0b2b70 bf0b2bd8 bf0b2bcc 00000001 
c0352210 00000000 ed457f58
[   58.171064] 7e20: bf0b2bd8 c0008908 ed1e2ac0 c0539790 00000001 
ed07de40 c0d9fe2c 00000001
[   58.182194] 7e40: ed456000 c008565c 60000013 c0846f60 bf0b2bcc 
00000001 ed07de40 c0539790
[   58.193325] 7e60: c0846f80 c0846f5c 00000000 c00627d4 00000000 
ed5f9f80 ed000080 ed457f58
[   58.204453] 7e80: bf0b2bd8 bf0b2bcc 00000001 ed07de40 c0d9fe2c 
00000001 ed456000 c00ac1a4
[   58.215582] 7ea0: bf0b2bd8 00007fff c00a9cd4 ed456000 c00a99a4 
c0d9fe24 ed456000 bf0b2c14
[   58.226713] 7ec0: bf0b2d44 c07f0940 ed457ef4 00000000 bf0b2bd8 
ed456000 ffffffff c0733ccc
[   58.237842] 7ee0: 73646e65 c0539cf0 f08c0000 b6f5a000 0000071e 
00000000 00000000 00000000
[   58.248971] 7f00: 00000000 00000000 00000000 00000000 00000000 
00000000 00000000 00000000
[   58.260099] 7f20: 00000000 00000000 00000000 00000000 000000d2 
000c479e b6e96000 b6f6d150
[   58.271227] 7f40: 00000080 c000e6e4 ed456000 00000000 00000000 
c00aca2c f07fc000 000c479e
[   58.282357] 7f60: f089166c f089147e f08bf834 00008d44 00009e54 
00000000 00000000 00000000
[   58.293484] 7f80: 00000030 00000031 00000017 0000001b 00000011 
00000000 4e9c8ef8 00000000
[   58.304612] 7fa0: 000210a0 c000e520 4e9c8ef8 00000000 b6e96000 
000c479e b6f6d150 00000002
[   58.315742] 7fc0: 4e9c8ef8 00000000 000210a0 00000080 00021088 
000c479e b6f6d150 00000000
[   58.326875] 7fe0: 4e978990 bec5bc08 b6f65aa8 4e9789a0 80000010 
b6e96000 c8c0c094 4ac84040
[   58.338044] [<c001d710>] (v7_dma_clean_range) from [<c001963c>] 
(dma_cache_maint_page+0x90/0x120)
[   58.350141] [<c001963c>] (dma_cache_maint_page) from [<c00196f4>] 
(__dma_page_cpu_to_dev+0x28/0xa0)
[   58.362471] [<c00196f4>] (__dma_page_cpu_to_dev) from [<c0019810>] 
(arm_dma_map_page+0x64/0x70)
[   58.374388] [<c0019810>] (arm_dma_map_page) from [<bf0ab0e0>] 
(dwc3_probe+0xa14/0xcc4 [dwc3])
[   58.386063] [<bf0ab0e0>] (dwc3_probe [dwc3]) from [<c0352de8>] 
(platform_drv_probe+0x18/0x48)
[   58.397690] [<c0352de8>] (platform_drv_probe) from [<c0351a30>] 
(driver_probe_device+0x110/0x22c)
[   58.409774] [<c0351a30>] (driver_probe_device) from [<c0351be0>] 
(__driver_attach+0x94/0x98)
[   58.421280] [<c0351be0>] (__driver_attach) from [<c03501c4>] 
(bus_for_each_dev+0x54/0x88)
[   58.432430] [<c03501c4>] (bus_for_each_dev) from [<c03511c4>] 
(bus_add_driver+0xd8/0x1d8)
[   58.443570] [<c03511c4>] (bus_add_driver) from [<c0352210>] 
(driver_register+0x78/0xf4)
[   58.454474] [<c0352210>] (driver_register) from [<c0008908>] 
(do_one_initcall+0xe4/0x144)
[   58.465625] [<c0008908>] (do_one_initcall) from [<c00ac1a4>] 
(load_module+0x1788/0x1f68)
[   58.476645] [<c00ac1a4>] (load_module) from [<c00aca2c>] 
(SyS_init_module+0xa8/0xec)
[   58.487201] [<c00aca2c>] (SyS_init_module) from [<c000e520>] 
(ret_fast_syscall+0x0/0x48)
[   58.498220] Code: e3a02004 e1a02312 e2423001 e1c00003 (ee070f3a)
[   58.508114] ---[ end trace d26e7c6505b06264 ]---


> +	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;
>

Patch

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index a49217a..2864aad 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -242,6 +242,80 @@  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;
+
+	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 +351,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;
 
@@ -316,6 +391,10 @@  static int dwc3_core_init(struct dwc3 *dwc)
 	case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
 		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");
 	}
@@ -333,14 +412,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);
 }
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 43fe290..deab3c5 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -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)
@@ -600,6 +601,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
@@ -608,6 +610,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)
@@ -650,10 +653,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 */
@@ -682,6 +687,7 @@  struct dwc3 {
 	u32			dcfg;
 	u32			gctl;
 
+	u32			nr_scratch;
 	u32			num_event_buffers;
 	u32			u1u2;
 	u32			maximum_speed;