diff options
author | Li Yang <leoli@freescale.com> | 2011-11-23 07:12:56 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-11-29 09:19:04 -0500 |
commit | 6414e94c203d92b163ca61b5f51a25b80a621dbe (patch) | |
tree | 3fa3023953222644161601500170687399d75369 /drivers/usb | |
parent | 5ce1d0eb1cd1071347618d0a95515f300239d201 (diff) |
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: Li Yang <leoli@freescale.com>
Acked-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/gadget/fsl_udc_core.c | 68 | ||||
-rw-r--r-- | drivers/usb/gadget/fsl_usb2_udc.h | 10 |
2 files changed, 42 insertions, 36 deletions
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index bf1cd6e55ed9..dd28ef3def71 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c | |||
@@ -696,12 +696,31 @@ static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req) | |||
696 | kfree(req); | 696 | kfree(req); |
697 | } | 697 | } |
698 | 698 | ||
699 | /*-------------------------------------------------------------------------*/ | 699 | /* Actually add a dTD chain to an empty dQH and let go */ |
700 | static void fsl_prime_ep(struct fsl_ep *ep, struct ep_td_struct *td) | ||
701 | { | ||
702 | struct ep_queue_head *qh = get_qh_by_ep(ep); | ||
703 | |||
704 | /* Write dQH next pointer and terminate bit to 0 */ | ||
705 | qh->next_dtd_ptr = cpu_to_hc32(td->td_dma | ||
706 | & EP_QUEUE_HEAD_NEXT_POINTER_MASK); | ||
707 | |||
708 | /* Clear active and halt bit */ | ||
709 | qh->size_ioc_int_sts &= cpu_to_hc32(~(EP_QUEUE_HEAD_STATUS_ACTIVE | ||
710 | | EP_QUEUE_HEAD_STATUS_HALT)); | ||
711 | |||
712 | /* Ensure that updates to the QH will occur before priming. */ | ||
713 | wmb(); | ||
714 | |||
715 | /* Prime endpoint by writing correct bit to ENDPTPRIME */ | ||
716 | fsl_writel(ep_is_in(ep) ? (1 << (ep_index(ep) + 16)) | ||
717 | : (1 << (ep_index(ep))), &dr_regs->endpointprime); | ||
718 | } | ||
719 | |||
720 | /* Add dTD chain to the dQH of an EP */ | ||
700 | static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) | 721 | static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) |
701 | { | 722 | { |
702 | int i = ep_index(ep) * 2 + ep_is_in(ep); | ||
703 | u32 temp, bitmask, tmp_stat; | 723 | u32 temp, bitmask, tmp_stat; |
704 | struct ep_queue_head *dQH = &ep->udc->ep_qh[i]; | ||
705 | 724 | ||
706 | /* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr); | 725 | /* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr); |
707 | VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */ | 726 | 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 | cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK); | 738 | cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK); |
720 | /* Read prime bit, if 1 goto done */ | 739 | /* Read prime bit, if 1 goto done */ |
721 | if (fsl_readl(&dr_regs->endpointprime) & bitmask) | 740 | if (fsl_readl(&dr_regs->endpointprime) & bitmask) |
722 | goto out; | 741 | return; |
723 | 742 | ||
724 | do { | 743 | do { |
725 | /* Set ATDTW bit in USBCMD */ | 744 | /* Set ATDTW bit in USBCMD */ |
@@ -736,28 +755,10 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) | |||
736 | fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd); | 755 | fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd); |
737 | 756 | ||
738 | if (tmp_stat) | 757 | if (tmp_stat) |
739 | goto out; | 758 | return; |
740 | } | 759 | } |
741 | 760 | ||
742 | /* Write dQH next pointer and terminate bit to 0 */ | 761 | fsl_prime_ep(ep, req->head); |
743 | temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK; | ||
744 | dQH->next_dtd_ptr = cpu_to_hc32(temp); | ||
745 | |||
746 | /* Clear active and halt bit */ | ||
747 | temp = cpu_to_hc32(~(EP_QUEUE_HEAD_STATUS_ACTIVE | ||
748 | | EP_QUEUE_HEAD_STATUS_HALT)); | ||
749 | dQH->size_ioc_int_sts &= temp; | ||
750 | |||
751 | /* Ensure that updates to the QH will occur before priming. */ | ||
752 | wmb(); | ||
753 | |||
754 | /* Prime endpoint by writing 1 to ENDPTPRIME */ | ||
755 | temp = ep_is_in(ep) | ||
756 | ? (1 << (ep_index(ep) + 16)) | ||
757 | : (1 << (ep_index(ep))); | ||
758 | fsl_writel(temp, &dr_regs->endpointprime); | ||
759 | out: | ||
760 | return; | ||
761 | } | 762 | } |
762 | 763 | ||
763 | /* Fill in the dTD structure | 764 | /* Fill in the dTD structure |
@@ -973,25 +974,20 @@ static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) | |||
973 | 974 | ||
974 | /* The request isn't the last request in this ep queue */ | 975 | /* The request isn't the last request in this ep queue */ |
975 | if (req->queue.next != &ep->queue) { | 976 | if (req->queue.next != &ep->queue) { |
976 | struct ep_queue_head *qh; | ||
977 | struct fsl_req *next_req; | 977 | struct fsl_req *next_req; |
978 | 978 | ||
979 | qh = ep->qh; | ||
980 | next_req = list_entry(req->queue.next, struct fsl_req, | 979 | next_req = list_entry(req->queue.next, struct fsl_req, |
981 | queue); | 980 | queue); |
982 | 981 | ||
983 | /* Point the QH to the first TD of next request */ | 982 | /* prime with dTD of next request */ |
984 | fsl_writel((u32) next_req->head, &qh->curr_dtd_ptr); | 983 | fsl_prime_ep(ep, next_req->head); |
985 | } | 984 | } |
986 | 985 | /* The request hasn't been processed, patch up the TD chain */ | |
987 | /* The request hasn't been processed, patch up the TD chain */ | ||
988 | } else { | 986 | } else { |
989 | struct fsl_req *prev_req; | 987 | struct fsl_req *prev_req; |
990 | 988 | ||
991 | prev_req = list_entry(req->queue.prev, struct fsl_req, queue); | 989 | prev_req = list_entry(req->queue.prev, struct fsl_req, queue); |
992 | fsl_writel(fsl_readl(&req->tail->next_td_ptr), | 990 | prev_req->tail->next_td_ptr = req->tail->next_td_ptr; |
993 | &prev_req->tail->next_td_ptr); | ||
994 | |||
995 | } | 991 | } |
996 | 992 | ||
997 | done(ep, req, -ECONNRESET); | 993 | done(ep, req, -ECONNRESET); |
@@ -1068,7 +1064,7 @@ static int fsl_ep_fifo_status(struct usb_ep *_ep) | |||
1068 | struct fsl_udc *udc; | 1064 | struct fsl_udc *udc; |
1069 | int size = 0; | 1065 | int size = 0; |
1070 | u32 bitmask; | 1066 | u32 bitmask; |
1071 | struct ep_queue_head *d_qh; | 1067 | struct ep_queue_head *qh; |
1072 | 1068 | ||
1073 | ep = container_of(_ep, struct fsl_ep, ep); | 1069 | ep = container_of(_ep, struct fsl_ep, ep); |
1074 | if (!_ep || (!ep->desc && ep_index(ep) != 0)) | 1070 | if (!_ep || (!ep->desc && ep_index(ep) != 0)) |
@@ -1079,13 +1075,13 @@ static int fsl_ep_fifo_status(struct usb_ep *_ep) | |||
1079 | if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) | 1075 | if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) |
1080 | return -ESHUTDOWN; | 1076 | return -ESHUTDOWN; |
1081 | 1077 | ||
1082 | d_qh = &ep->udc->ep_qh[ep_index(ep) * 2 + ep_is_in(ep)]; | 1078 | qh = get_qh_by_ep(ep); |
1083 | 1079 | ||
1084 | bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) : | 1080 | bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) : |
1085 | (1 << (ep_index(ep))); | 1081 | (1 << (ep_index(ep))); |
1086 | 1082 | ||
1087 | if (fsl_readl(&dr_regs->endptstatus) & bitmask) | 1083 | if (fsl_readl(&dr_regs->endptstatus) & bitmask) |
1088 | size = (d_qh->size_ioc_int_sts & DTD_PACKET_SIZE) | 1084 | size = (qh->size_ioc_int_sts & DTD_PACKET_SIZE) |
1089 | >> DTD_LENGTH_BIT_POS; | 1085 | >> DTD_LENGTH_BIT_POS; |
1090 | 1086 | ||
1091 | pr_debug("%s %u\n", __func__, size); | 1087 | pr_debug("%s %u\n", __func__, size); |
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index 1d51be83fda8..f781f5dec417 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h | |||
@@ -569,6 +569,16 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length) | |||
569 | * 2 + ((windex & USB_DIR_IN) ? 1 : 0)) | 569 | * 2 + ((windex & USB_DIR_IN) ? 1 : 0)) |
570 | #define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP)) | 570 | #define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP)) |
571 | 571 | ||
572 | static inline struct ep_queue_head *get_qh_by_ep(struct fsl_ep *ep) | ||
573 | { | ||
574 | /* we only have one ep0 structure but two queue heads */ | ||
575 | if (ep_index(ep) != 0) | ||
576 | return ep->qh; | ||
577 | else | ||
578 | return &ep->udc->ep_qh[(ep->udc->ep0_dir == | ||
579 | USB_DIR_IN) ? 1 : 0]; | ||
580 | } | ||
581 | |||
572 | struct platform_device; | 582 | struct platform_device; |
573 | #ifdef CONFIG_ARCH_MXC | 583 | #ifdef CONFIG_ARCH_MXC |
574 | int fsl_udc_clk_init(struct platform_device *pdev); | 584 | int fsl_udc_clk_init(struct platform_device *pdev); |