Commit 6414e94c authored by Li Yang's avatar Li Yang Committed by Greg Kroah-Hartman

usb: gadget: fsl_udc: fix dequeuing a request in progress

The original implementation of dequeuing a request in progress
is not correct.  Change to use a correct process and also clean
up the related functions a little bit.
Signed-off-by: default avatarLi Yang <leoli@freescale.com>
Acked-by: default avatarPeter Chen <peter.chen@freescale.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 5ce1d0eb
...@@ -696,12 +696,31 @@ static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req) ...@@ -696,12 +696,31 @@ static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req)
kfree(req); kfree(req);
} }
/*-------------------------------------------------------------------------*/ /* Actually add a dTD chain to an empty dQH and let go */
static void fsl_prime_ep(struct fsl_ep *ep, struct ep_td_struct *td)
{
struct ep_queue_head *qh = get_qh_by_ep(ep);
/* Write dQH next pointer and terminate bit to 0 */
qh->next_dtd_ptr = cpu_to_hc32(td->td_dma
& EP_QUEUE_HEAD_NEXT_POINTER_MASK);
/* Clear active and halt bit */
qh->size_ioc_int_sts &= cpu_to_hc32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
| EP_QUEUE_HEAD_STATUS_HALT));
/* Ensure that updates to the QH will occur before priming. */
wmb();
/* Prime endpoint by writing correct bit to ENDPTPRIME */
fsl_writel(ep_is_in(ep) ? (1 << (ep_index(ep) + 16))
: (1 << (ep_index(ep))), &dr_regs->endpointprime);
}
/* Add dTD chain to the dQH of an EP */
static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
{ {
int i = ep_index(ep) * 2 + ep_is_in(ep);
u32 temp, bitmask, tmp_stat; u32 temp, bitmask, tmp_stat;
struct ep_queue_head *dQH = &ep->udc->ep_qh[i];
/* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr); /* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr);
VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */ VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */
...@@ -719,7 +738,7 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) ...@@ -719,7 +738,7 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK); cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK);
/* Read prime bit, if 1 goto done */ /* Read prime bit, if 1 goto done */
if (fsl_readl(&dr_regs->endpointprime) & bitmask) if (fsl_readl(&dr_regs->endpointprime) & bitmask)
goto out; return;
do { do {
/* Set ATDTW bit in USBCMD */ /* Set ATDTW bit in USBCMD */
...@@ -736,28 +755,10 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) ...@@ -736,28 +755,10 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd); fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd);
if (tmp_stat) if (tmp_stat)
goto out; return;
} }
/* Write dQH next pointer and terminate bit to 0 */ fsl_prime_ep(ep, req->head);
temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
dQH->next_dtd_ptr = cpu_to_hc32(temp);
/* Clear active and halt bit */
temp = cpu_to_hc32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
| EP_QUEUE_HEAD_STATUS_HALT));
dQH->size_ioc_int_sts &= temp;
/* Ensure that updates to the QH will occur before priming. */
wmb();
/* Prime endpoint by writing 1 to ENDPTPRIME */
temp = ep_is_in(ep)
? (1 << (ep_index(ep) + 16))
: (1 << (ep_index(ep)));
fsl_writel(temp, &dr_regs->endpointprime);
out:
return;
} }
/* Fill in the dTD structure /* Fill in the dTD structure
...@@ -973,25 +974,20 @@ static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) ...@@ -973,25 +974,20 @@ static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
/* The request isn't the last request in this ep queue */ /* The request isn't the last request in this ep queue */
if (req->queue.next != &ep->queue) { if (req->queue.next != &ep->queue) {
struct ep_queue_head *qh;
struct fsl_req *next_req; struct fsl_req *next_req;
qh = ep->qh;
next_req = list_entry(req->queue.next, struct fsl_req, next_req = list_entry(req->queue.next, struct fsl_req,
queue); queue);
/* Point the QH to the first TD of next request */ /* prime with dTD of next request */
fsl_writel((u32) next_req->head, &qh->curr_dtd_ptr); fsl_prime_ep(ep, next_req->head);
} }
/* The request hasn't been processed, patch up the TD chain */
/* The request hasn't been processed, patch up the TD chain */
} else { } else {
struct fsl_req *prev_req; struct fsl_req *prev_req;
prev_req = list_entry(req->queue.prev, struct fsl_req, queue); prev_req = list_entry(req->queue.prev, struct fsl_req, queue);
fsl_writel(fsl_readl(&req->tail->next_td_ptr), prev_req->tail->next_td_ptr = req->tail->next_td_ptr;
&prev_req->tail->next_td_ptr);
} }
done(ep, req, -ECONNRESET); done(ep, req, -ECONNRESET);
...@@ -1068,7 +1064,7 @@ static int fsl_ep_fifo_status(struct usb_ep *_ep) ...@@ -1068,7 +1064,7 @@ static int fsl_ep_fifo_status(struct usb_ep *_ep)
struct fsl_udc *udc; struct fsl_udc *udc;
int size = 0; int size = 0;
u32 bitmask; u32 bitmask;
struct ep_queue_head *d_qh; struct ep_queue_head *qh;
ep = container_of(_ep, struct fsl_ep, ep); ep = container_of(_ep, struct fsl_ep, ep);
if (!_ep || (!ep->desc && ep_index(ep) != 0)) if (!_ep || (!ep->desc && ep_index(ep) != 0))
...@@ -1079,13 +1075,13 @@ static int fsl_ep_fifo_status(struct usb_ep *_ep) ...@@ -1079,13 +1075,13 @@ static int fsl_ep_fifo_status(struct usb_ep *_ep)
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN; return -ESHUTDOWN;
d_qh = &ep->udc->ep_qh[ep_index(ep) * 2 + ep_is_in(ep)]; qh = get_qh_by_ep(ep);
bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) : bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) :
(1 << (ep_index(ep))); (1 << (ep_index(ep)));
if (fsl_readl(&dr_regs->endptstatus) & bitmask) if (fsl_readl(&dr_regs->endptstatus) & bitmask)
size = (d_qh->size_ioc_int_sts & DTD_PACKET_SIZE) size = (qh->size_ioc_int_sts & DTD_PACKET_SIZE)
>> DTD_LENGTH_BIT_POS; >> DTD_LENGTH_BIT_POS;
pr_debug("%s %u\n", __func__, size); pr_debug("%s %u\n", __func__, size);
......
...@@ -569,6 +569,16 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length) ...@@ -569,6 +569,16 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length)
* 2 + ((windex & USB_DIR_IN) ? 1 : 0)) * 2 + ((windex & USB_DIR_IN) ? 1 : 0))
#define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP)) #define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP))
static inline struct ep_queue_head *get_qh_by_ep(struct fsl_ep *ep)
{
/* we only have one ep0 structure but two queue heads */
if (ep_index(ep) != 0)
return ep->qh;
else
return &ep->udc->ep_qh[(ep->udc->ep0_dir ==
USB_DIR_IN) ? 1 : 0];
}
struct platform_device; struct platform_device;
#ifdef CONFIG_ARCH_MXC #ifdef CONFIG_ARCH_MXC
int fsl_udc_clk_init(struct platform_device *pdev); int fsl_udc_clk_init(struct platform_device *pdev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment