summaryrefslogtreecommitdiff
path: root/drivers/usb/chipidea/udc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/chipidea/udc.c')
-rw-r--r--drivers/usb/chipidea/udc.c48
1 files changed, 38 insertions, 10 deletions
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 8223fe73ea85..3eafa2c9a2ba 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -26,7 +26,6 @@
#include "ci.h"
#include "udc.h"
#include "bits.h"
-#include "debug.h"
#include "otg.h"
#include "otg_fsm.h"
@@ -349,14 +348,13 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
if (node == NULL)
return -ENOMEM;
- node->ptr = dma_pool_alloc(hwep->td_pool, GFP_ATOMIC,
+ node->ptr = dma_pool_zalloc(hwep->td_pool, GFP_ATOMIC,
&node->dma);
if (node->ptr == NULL) {
kfree(node);
return -ENOMEM;
}
- memset(node->ptr, 0, sizeof(struct ci_hw_td));
node->ptr->token = cpu_to_le32(length << __ffs(TD_TOTAL_BYTES));
node->ptr->token &= cpu_to_le32(TD_TOTAL_BYTES);
node->ptr->token |= cpu_to_le32(TD_STATUS_ACTIVE);
@@ -404,9 +402,9 @@ static inline u8 _usb_addr(struct ci_hw_ep *ep)
}
/**
- * _hardware_queue: configures a request at hardware level
- * @gadget: gadget
+ * _hardware_enqueue: configures a request at hardware level
* @hwep: endpoint
+ * @hwreq: request
*
* This function returns an error code
*/
@@ -435,19 +433,28 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
if (hwreq->req.dma % PAGE_SIZE)
pages--;
- if (rest == 0)
- add_td_to_list(hwep, hwreq, 0);
+ if (rest == 0) {
+ ret = add_td_to_list(hwep, hwreq, 0);
+ if (ret < 0)
+ goto done;
+ }
while (rest > 0) {
unsigned count = min(hwreq->req.length - hwreq->req.actual,
(unsigned)(pages * CI_HDRC_PAGE_SIZE));
- add_td_to_list(hwep, hwreq, count);
+ ret = add_td_to_list(hwep, hwreq, count);
+ if (ret < 0)
+ goto done;
+
rest -= count;
}
if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX
- && (hwreq->req.length % hwep->ep.maxpacket == 0))
- add_td_to_list(hwep, hwreq, 0);
+ && (hwreq->req.length % hwep->ep.maxpacket == 0)) {
+ ret = add_td_to_list(hwep, hwreq, 0);
+ if (ret < 0)
+ goto done;
+ }
firstnode = list_first_entry(&hwreq->tds, struct td_node, td);
@@ -788,8 +795,12 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req)
/**
* _ep_queue: queues (submits) an I/O request to an endpoint
+ * @ep: endpoint
+ * @req: request
+ * @gfp_flags: GFP flags (not used)
*
* Caller must hold lock
+ * This function returns an error code
*/
static int _ep_queue(struct usb_ep *ep, struct usb_request *req,
gfp_t __maybe_unused gfp_flags)
@@ -1751,6 +1762,22 @@ static int ci_udc_start(struct usb_gadget *gadget,
return retval;
}
+static void ci_udc_stop_for_otg_fsm(struct ci_hdrc *ci)
+{
+ if (!ci_otg_is_fsm_mode(ci))
+ return;
+
+ mutex_lock(&ci->fsm.lock);
+ if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) {
+ ci->fsm.a_bidl_adis_tmout = 1;
+ ci_hdrc_otg_fsm_start(ci);
+ } else if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) {
+ ci->fsm.protocol = PROTO_UNDEF;
+ ci->fsm.otg->state = OTG_STATE_UNDEFINED;
+ }
+ mutex_unlock(&ci->fsm.lock);
+}
+
/**
* ci_udc_stop: unregister a gadget driver
*/
@@ -1775,6 +1802,7 @@ static int ci_udc_stop(struct usb_gadget *gadget)
ci->driver = NULL;
spin_unlock_irqrestore(&ci->lock, flags);
+ ci_udc_stop_for_otg_fsm(ci);
return 0;
}