From 288180cba4c2a5d7bdd54093a5f7b00bfe73187b Mon Sep 17 00:00:00 2001 From: Wu Liang feng Date: Wed, 2 Sep 2015 10:07:56 +0800 Subject: [PATCH] usb: dwc_otg_310: gadget: stop current transfer on dequeue If the request being dequeued is already started, disable endpoint to stop the transfer and then call dwc_otg_request_done(). Endpoint will be re-enabled on next call to dwc_otg_ep_start_transfer(). TEST=use RK3128/RK3288/RK3368 board, adb root and then adb reboot, check if usb_req->buf is used after free. Change-Id: I8c9304f29c578f679ad0841350920f1350bd32df Signed-off-by: Wu Liang feng --- drivers/usb/dwc_otg_310/dwc_otg_pcd.c | 133 ++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/drivers/usb/dwc_otg_310/dwc_otg_pcd.c b/drivers/usb/dwc_otg_310/dwc_otg_pcd.c index a82bd0f89e34..00f1ea90c289 100755 --- a/drivers/usb/dwc_otg_310/dwc_otg_pcd.c +++ b/drivers/usb/dwc_otg_310/dwc_otg_pcd.c @@ -1712,6 +1712,127 @@ out_unlocked: } +static int dwc_otg_wait_bit_set(volatile uint32_t *reg, + uint32_t bit, uint32_t timeout) +{ + int i; + + for (i = 0; i < timeout; i++) { + if (DWC_READ_REG32(reg) & bit) + return 0; + + dwc_udelay(1); + } + + return -ETIMEDOUT; +} + +static void dwc_otg_pcd_ep_stop_transfer(dwc_otg_core_if_t + *core_if, dwc_ep_t *ep) +{ + depctl_data_t depctl = {.d32 = 0 }; + + /* Read DEPCTLn register */ + if (ep->is_in == 1) + depctl.d32 = DWC_READ_REG32(&core_if->dev_if-> + in_ep_regs[ep->num]-> + diepctl); + else + depctl.d32 = DWC_READ_REG32(&core_if->dev_if-> + out_ep_regs[ep->num]-> + doepctl); + + if (ep->is_in == 1) { + diepint_data_t diepint = {.d32 = 0 }; + + depctl.b.snak = 1; + DWC_WRITE_REG32(&core_if->dev_if-> + in_ep_regs[ep->num]->diepctl, + depctl.d32); + + diepint.b.inepnakeff = 1; + /* Wait for Nak effect */ + if (dwc_otg_wait_bit_set(&core_if->dev_if-> + in_ep_regs[ep->num] + ->diepint, + diepint.d32, + 100)) { + DWC_WARN("%s: timeout diepctl.snak\n", + __func__); + } else { + DWC_WRITE_REG32(&core_if->dev_if-> + in_ep_regs[ep->num]-> + diepint, diepint.d32); + } + + depctl.d32 = 0; + depctl.b.epdis = 1; + DWC_WRITE_REG32(&core_if->dev_if-> + in_ep_regs[ep->num]->diepctl, + depctl.d32); + + diepint.d32 = 0; + diepint.b.epdisabled = 1; + if (dwc_otg_wait_bit_set(&core_if->dev_if-> + in_ep_regs[ep->num] + ->diepint, + diepint.d32, + 100)) { + DWC_WARN("%s: timeout diepctl.epdis\n", + __func__); + } else { + DWC_WRITE_REG32(&core_if->dev_if-> + in_ep_regs[ep->num]-> + diepint, diepint.d32); + } + } else { + dctl_data_t dctl = {.d32 = 0 }; + gintmsk_data_t gintsts = {.d32 = 0 }; + doepint_data_t doepint = {.d32 = 0 }; + + dctl.b.sgoutnak = 1; + DWC_MODIFY_REG32(&core_if->dev_if-> + dev_global_regs->dctl, 0, dctl.d32); + + /* Wait for global nak to take effect */ + gintsts.d32 = 0; + gintsts.b.goutnakeff = 1; + if (dwc_otg_wait_bit_set(&core_if->core_global_regs-> + gintsts, gintsts.d32, + 100)) { + DWC_WARN("%s: timeout dctl.sgoutnak\n", + __func__); + } else { + DWC_WRITE_REG32(&core_if->core_global_regs + ->gintsts, gintsts.d32); + } + + depctl.d32 = 0; + depctl.b.epdis = 1; + depctl.b.snak = 1; + DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]-> + doepctl, depctl.d32); + + doepint.b.epdisabled = 1; + if (dwc_otg_wait_bit_set(&core_if->dev_if + ->out_ep_regs[ep->num] + ->doepint, doepint.d32, + 100)) { + DWC_WARN("%s: timeout doepctl.epdis\n", + __func__); + } else { + DWC_WRITE_REG32(&core_if->dev_if-> + out_ep_regs[ep->num]-> + doepint, doepint.d32); + } + + dctl.d32 = 0; + dctl.b.cgoutnak = 1; + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs-> + dctl, 0, dctl.d32); + } +} + /******************************************************************************/ #ifdef DWC_UTE_PER_IO @@ -2369,6 +2490,18 @@ int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t *pcd, void *ep_handle, } if (!DWC_CIRCLEQ_EMPTY_ENTRY(req, queue_entry)) { + dwc_otg_pcd_ep_stop_transfer(GET_CORE_IF(pcd), + &ep->dwc_ep); + /* Flush the Tx FIFO */ + if (ep->dwc_ep.is_in) { + dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), + ep->dwc_ep.tx_fifo_num); + release_perio_tx_fifo(GET_CORE_IF(pcd), + ep->dwc_ep.tx_fifo_num); + release_tx_fifo(GET_CORE_IF(pcd), + ep->dwc_ep.tx_fifo_num); + } + dwc_otg_request_done(ep, req, -DWC_E_RESTART); } else { req = NULL; -- 2.34.1