OHCI drivers: Try disabling bulk list when cancelling bulk transfers

This commit is contained in:
Gregory Nutt 2015-05-15 07:31:13 -06:00
parent 4dc10dbe85
commit 9061809e2d
2 changed files with 159 additions and 58 deletions

View File

@ -192,7 +192,7 @@ struct lpc17_xfrinfo_s
uint8_t *buffer; /* Transfer buffer start */ uint8_t *buffer; /* Transfer buffer start */
uint16_t buflen; /* Buffer length */ uint16_t buflen; /* Buffer length */
uint16_t xfrd; /* Number of bytes transferred */ uint16_t xfrd; /* Number of bytes transferred */
#ifdef CONFIG_USBHOST_ASYNCH #ifdef CONFIG_USBHOST_ASYNCH
#if LPC17_IOBUFFERS > 0 #if LPC17_IOBUFFERS > 0
/* Remember the allocated DMA buffer address so that it can be freed when /* Remember the allocated DMA buffer address so that it can be freed when
@ -858,22 +858,30 @@ static void lpc17_free_xfrinfo(struct lpc17_xfrinfo_s *xfrinfo)
static inline int lpc17_addctrled(struct lpc17_usbhost_s *priv, static inline int lpc17_addctrled(struct lpc17_usbhost_s *priv,
struct lpc17_ed_s *ed) struct lpc17_ed_s *ed)
{ {
irqstate_t flags;
uint32_t regval; uint32_t regval;
/* Disable control list processing while we modify the list */
flags = irqsave();
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval &= ~OHCI_CTRL_CLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
/* Add the new bulk ED to the head of the bulk list */ /* Add the new bulk ED to the head of the bulk list */
ed->hw.nexted = lpc17_getreg(LPC17_USBHOST_CTRLHEADED); ed->hw.nexted = lpc17_getreg(LPC17_USBHOST_CTRLHEADED);
lpc17_putreg((uint32_t)ed, LPC17_USBHOST_CTRLHEADED); lpc17_putreg((uint32_t)ed, LPC17_USBHOST_CTRLHEADED);
/* ControlListEnable. This bit is set to enable the processing of the /* Re-enable control list processing. */
* Control list. Note: once enabled, it remains enabled and we may even
* complete list processing before we get the bit set. We really lpc17_putreg(0, LPC17_USBHOST_CTRLED);
* should never modify the control list while CLE is set.
*/
regval = lpc17_getreg(LPC17_USBHOST_CTRL); regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval |= OHCI_CTRL_CLE; regval |= OHCI_CTRL_CLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL); lpc17_putreg(regval, LPC17_USBHOST_CTRL);
irqrestore(flags);
return OK; return OK;
} }
@ -891,11 +899,17 @@ static inline int lpc17_remctrled(struct lpc17_usbhost_s *priv,
struct lpc17_ed_s *curr; struct lpc17_ed_s *curr;
struct lpc17_ed_s *prev; struct lpc17_ed_s *prev;
struct lpc17_ed_s *head; struct lpc17_ed_s *head;
irqstate_t flags;
uint32_t regval; uint32_t regval;
/* Find the ED in the control list. NOTE: We really should never be mucking /* Disable control list processing while we modify the list */
* with the control list while CLE is set.
*/ flags = irqsave();
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval &= ~OHCI_CTRL_CLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
/* Find the ED in the control list. */
head = (struct lpc17_ed_s *)lpc17_getreg(LPC17_USBHOST_CTRLHEADED); head = (struct lpc17_ed_s *)lpc17_getreg(LPC17_USBHOST_CTRLHEADED);
for (prev = NULL, curr = head; for (prev = NULL, curr = head;
@ -918,17 +932,6 @@ static inline int lpc17_remctrled(struct lpc17_usbhost_s *priv,
head = (struct lpc17_ed_s *)ed->hw.nexted; head = (struct lpc17_ed_s *)ed->hw.nexted;
lpc17_putreg((uint32_t)head, LPC17_USBHOST_CTRLHEADED); lpc17_putreg((uint32_t)head, LPC17_USBHOST_CTRLHEADED);
/* If the control list is now empty, then disable it.
* This should never happen!
*/
if (head == NULL)
{
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval &= ~OHCI_CTRL_CLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
}
} }
else else
{ {
@ -946,6 +949,21 @@ static inline int lpc17_remctrled(struct lpc17_usbhost_s *priv,
ed->hw.nexted = 0; ed->hw.nexted = 0;
} }
/* Re-enable control list processing if the control list is still non-empty
* after removing the ED node.
*/
lpc17_putreg(0, LPC17_USBHOST_CTRLED);
if (lpc17_getreg(LPC17_USBHOST_CTRLHEADED) != 0)
{
/* If the control list is now empty, then disable it */
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval &= ~OHCI_CTRL_CLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
}
irqrestore(flags);
return OK; return OK;
} }
@ -961,21 +979,30 @@ static inline int lpc17_addbulked(struct lpc17_usbhost_s *priv,
struct lpc17_ed_s *ed) struct lpc17_ed_s *ed)
{ {
#ifndef CONFIG_USBHOST_BULK_DISABLE #ifndef CONFIG_USBHOST_BULK_DISABLE
irqstate_t flags;
uint32_t regval; uint32_t regval;
/* Disable bulk list processing while we modify the list */
flags = irqsave();
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval &= ~OHCI_CTRL_BLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
/* Add the new bulk ED to the head of the bulk list */ /* Add the new bulk ED to the head of the bulk list */
ed->hw.nexted = lpc17_getreg(LPC17_USBHOST_BULKHEADED); ed->hw.nexted = lpc17_getreg(LPC17_USBHOST_BULKHEADED);
lpc17_putreg((uint32_t)ed, LPC17_USBHOST_BULKHEADED); lpc17_putreg((uint32_t)ed, LPC17_USBHOST_BULKHEADED);
/* BulkListEnable. This bit is set to enable the processing of the /* Re-enable bulk list processing. */
* Bulk list. Note: once enabled, it remains. We really should
* never modify the bulk list while BLE is set. lpc17_putreg(0, LPC17_USBHOST_BULKED);
*/
regval = lpc17_getreg(LPC17_USBHOST_CTRL); regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval |= OHCI_CTRL_BLE; regval |= OHCI_CTRL_BLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL); lpc17_putreg(regval, LPC17_USBHOST_CTRL);
irqrestore(flags);
return OK; return OK;
#else #else
return -ENOSYS; return -ENOSYS;
@ -997,18 +1024,24 @@ static inline int lpc17_rembulked(struct lpc17_usbhost_s *priv,
struct lpc17_ed_s *curr; struct lpc17_ed_s *curr;
struct lpc17_ed_s *prev; struct lpc17_ed_s *prev;
struct lpc17_ed_s *head; struct lpc17_ed_s *head;
irqstate_t flags;
uint32_t regval; uint32_t regval;
/* Find the ED in the bulk list. NOTE: We really should never be mucking /* Disable bulk list processing while we modify the list */
* with the bulk list while BLE is set.
*/ flags = irqsave();
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval &= ~OHCI_CTRL_BLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
/* Find the ED in the bulk list. */
head = (struct lpc17_ed_s *)lpc17_getreg(LPC17_USBHOST_BULKHEADED); head = (struct lpc17_ed_s *)lpc17_getreg(LPC17_USBHOST_BULKHEADED);
for (prev = NULL, curr = head; for (prev = NULL, curr = head;
curr && curr != ed; curr && curr != ed;
prev = curr, curr = (struct lpc17_ed_s *)curr->hw.nexted); prev = curr, curr = (struct lpc17_ed_s *)curr->hw.nexted);
/* Hmmm.. It would be a bug if we do not find the ED in the bulk list. */ /* It would be a bug if we do not find the ED in the bulk list. */
DEBUGASSERT(curr != NULL); DEBUGASSERT(curr != NULL);
@ -1024,15 +1057,6 @@ static inline int lpc17_rembulked(struct lpc17_usbhost_s *priv,
head = (struct lpc17_ed_s *)ed->hw.nexted; head = (struct lpc17_ed_s *)ed->hw.nexted;
lpc17_putreg((uint32_t)head, LPC17_USBHOST_BULKHEADED); lpc17_putreg((uint32_t)head, LPC17_USBHOST_BULKHEADED);
/* If the bulk list is now empty, then disable it */
if (head == NULL);
{
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval &= ~OHCI_CTRL_BLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
}
} }
else else
{ {
@ -1044,6 +1068,21 @@ static inline int lpc17_rembulked(struct lpc17_usbhost_s *priv,
} }
} }
/* Re-enable bulk list processing if the bulk list is still non-empty
* after removing the ED node.
*/
lpc17_putreg(0, LPC17_USBHOST_BULKED);
if (lpc17_getreg(LPC17_USBHOST_BULKHEADED) != 0)
{
/* If the bulk list is now empty, then disable it */
regval = lpc17_getreg(LPC17_USBHOST_CTRL);
regval |= OHCI_CTRL_BLE;
lpc17_putreg(regval, LPC17_USBHOST_CTRL);
}
irqrestore(flags);
return OK; return OK;
#else #else
return -ENOSYS; return -ENOSYS;
@ -3246,6 +3285,7 @@ static int lpc17_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
struct lpc17_gtd_s *td; struct lpc17_gtd_s *td;
struct lpc17_gtd_s *next; struct lpc17_gtd_s *next;
struct lpc17_xfrinfo_s *xfrinfo; struct lpc17_xfrinfo_s *xfrinfo;
uint32_t ctrl;
irqstate_t flags; irqstate_t flags;
DEBUGASSERT(priv != NULL && ed != NULL); DEBUGASSERT(priv != NULL && ed != NULL);
@ -3269,15 +3309,42 @@ static int lpc17_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
if (xfrinfo->wdhwait) if (xfrinfo->wdhwait)
#endif #endif
{ {
/* We really need some kind of atomic test and set to do this right */ /* Control endpoints should not come through this path and
* isochronous endpoints are not yet implemented. So we only have
* to distinguish bulk and interrupt endpoints.
*/
td = (struct lpc17_gtd_s *)(ed->hw.headp & ED_HEADP_ADDR_MASK); if (ed->xfrtype == USB_EP_ATTR_XFER_BULK)
ed->hw.headp = LPC17_TDTAIL_ADDR; {
ed->xfrinfo = NULL; /* Disable bulk list processing while we modify the list */
/* Free all transfer descriptors that were connected to the ED */ ctrl = lpc17_getreg(LPC17_USBHOST_CTRL);
lpc17_putreg(ctrl & ~OHCI_CTRL_BLE, LPC17_USBHOST_CTRL);
/* Remove the TDs attached to the ED, keeping the ED in the list */
td = (struct lpc17_gtd_s *)(ed->hw.headp & ED_HEADP_ADDR_MASK);
ed->hw.headp = LPC17_TDTAIL_ADDR;
ed->xfrinfo = NULL;
/* Re-enable bulk list processing, if it was enabled before */
lpc17_putreg(0, LPC17_USBHOST_BULKED);
lpc17_putreg(ctrl, LPC17_USBHOST_CTRL);
}
else
{
/* Remove the TDs attached to the ED, keeping the Ed in the list */
td = (struct lpc17_gtd_s *)(ed->hw.headp & ED_HEADP_ADDR_MASK);
ed->hw.headp = LPC17_TDTAIL_ADDR;
ed->xfrinfo = NULL;
}
/* Free all transfer descriptors that were connected to the ED. In
* some race conditions with the hardware, this might be none.
*/
DEBUGASSERT(td != (struct lpc17_gtd_s *)LPC17_TDTAIL_ADDR);
while (td != (struct lpc17_gtd_s *)LPC17_TDTAIL_ADDR) while (td != (struct lpc17_gtd_s *)LPC17_TDTAIL_ADDR)
{ {
next = (struct lpc17_gtd_s *)td->hw.nexttd; next = (struct lpc17_gtd_s *)td->hw.nexttd;

View File

@ -856,6 +856,8 @@ static int sam_addctrled(struct sam_ed_s *ed)
/* Re-enable control list processing. */ /* Re-enable control list processing. */
sam_putreg(0, SAM_USBHOST_CTRLED);
regval = sam_getreg(SAM_USBHOST_CTRL); regval = sam_getreg(SAM_USBHOST_CTRL);
regval |= OHCI_CTRL_CLE; regval |= OHCI_CTRL_CLE;
sam_putreg(regval, SAM_USBHOST_CTRL); sam_putreg(regval, SAM_USBHOST_CTRL);
@ -887,9 +889,7 @@ static inline int sam_remctrled(struct sam_ed_s *ed)
regval &= ~OHCI_CTRL_CLE; regval &= ~OHCI_CTRL_CLE;
sam_putreg(regval, SAM_USBHOST_CTRL); sam_putreg(regval, SAM_USBHOST_CTRL);
/* Find the ED in the control list. NOTE: We really should never be mucking /* Find the ED in the control list. */
* with the control list while BLE is set.
*/
physed = sam_getreg(SAM_USBHOST_CTRLHEADED); physed = sam_getreg(SAM_USBHOST_CTRLHEADED);
for (curr = (struct sam_ed_s *)sam_virtramaddr(physed), prev = NULL; for (curr = (struct sam_ed_s *)sam_virtramaddr(physed), prev = NULL;
@ -928,6 +928,7 @@ static inline int sam_remctrled(struct sam_ed_s *ed)
* after removing the ED node. * after removing the ED node.
*/ */
sam_putreg(0, SAM_USBHOST_CTRLED);
if (sam_getreg(SAM_USBHOST_CTRLHEADED) != 0) if (sam_getreg(SAM_USBHOST_CTRLHEADED) != 0)
{ {
/* If the control list is now empty, then disable it */ /* If the control list is now empty, then disable it */
@ -973,6 +974,8 @@ static inline int sam_addbulked(struct sam_ed_s *ed)
/* Re-enable bulk list processing. */ /* Re-enable bulk list processing. */
sam_putreg(0, SAM_USBHOST_BULKED);
regval = sam_getreg(SAM_USBHOST_CTRL); regval = sam_getreg(SAM_USBHOST_CTRL);
regval |= OHCI_CTRL_BLE; regval |= OHCI_CTRL_BLE;
sam_putreg(regval, SAM_USBHOST_CTRL); sam_putreg(regval, SAM_USBHOST_CTRL);
@ -1008,9 +1011,7 @@ static inline int sam_rembulked(struct sam_ed_s *ed)
regval &= ~OHCI_CTRL_BLE; regval &= ~OHCI_CTRL_BLE;
sam_putreg(regval, SAM_USBHOST_CTRL); sam_putreg(regval, SAM_USBHOST_CTRL);
/* Find the ED in the bulk list. NOTE: We really should never be mucking /* Find the ED in the bulk list. */
* with the bulk list while BLE is set.
*/
physed = sam_getreg(SAM_USBHOST_BULKHEADED); physed = sam_getreg(SAM_USBHOST_BULKHEADED);
for (curr = (struct sam_ed_s *)sam_virtramaddr(physed), prev = NULL; for (curr = (struct sam_ed_s *)sam_virtramaddr(physed), prev = NULL;
@ -1049,6 +1050,7 @@ static inline int sam_rembulked(struct sam_ed_s *ed)
* after removing the ED node. * after removing the ED node.
*/ */
sam_getreg(0, SAM_USBHOST_BULKED);
if (sam_getreg(SAM_USBHOST_BULKHEADED) != 0) if (sam_getreg(SAM_USBHOST_BULKHEADED) != 0)
{ {
/* If the bulk list is now empty, then disable it */ /* If the bulk list is now empty, then disable it */
@ -3646,20 +3648,52 @@ static int sam_cancel(struct usbhost_driver_s *drvr, usbhost_ep_t ep)
if (eplist->wdhwait) if (eplist->wdhwait)
#endif #endif
{ {
/* We really need some kind of atomic test and set to do this right */ /* Control endpoints should not come through this path and
* isochronous endpoints are not yet implemented. So we only have
* to distinguish bulk and interrupt endpoints.
*/
paddr = ed->hw.headp & ED_HEADP_ADDR_MASK; if (ed->xfrtype == USB_EP_ATTR_XFER_BULK)
td = (struct sam_gtd_s *)sam_virtramaddr(paddr); {
/* Disable bulk list processing while we modify the list */
paddr = sam_physramaddr((uintptr_t)eplist->tail); ctrl = sam_getreg(SAM_USBHOST_CTRL);
ed->hw.headp = paddr; sam_putreg(ctrl & ~OHCI_CTRL_BLE, SAM_USBHOST_CTRL);
arch_clean_dcache((uintptr_t)ed, /* Remove the TDs attached to the ED, keeping the ED in the list */
(uintptr_t)ed + sizeof(struct ohci_ed_s));
/* Free all transfer descriptors that were connected to the ED */ paddr = ed->hw.headp & ED_HEADP_ADDR_MASK;
td = (struct sam_gtd_s *)sam_virtramaddr(paddr);
paddr = sam_physramaddr((uintptr_t)eplist->tail);
ed->hw.headp = paddr;
arch_clean_dcache((uintptr_t)ed,
(uintptr_t)ed + sizeof(struct ohci_ed_s));
/* Re-enable bulk list processing, if it was enabled before */
sam_putreg(0, SAM_USBHOST_BULKED);
sam_putreg(ctrl, SAM_USBHOST_CTRL);
}
else
{
/* Remove the TDs attached to the ED, keeping the Ed in the list */
paddr = ed->hw.headp & ED_HEADP_ADDR_MASK;
td = (struct sam_gtd_s *)sam_virtramaddr(paddr);
paddr = sam_physramaddr((uintptr_t)eplist->tail);
ed->hw.headp = paddr;
arch_clean_dcache((uintptr_t)ed,
(uintptr_t)ed + sizeof(struct ohci_ed_s));
}
/* Free all transfer descriptors that were connected to the ED. In some
* race conditions with the hardware, this might be none.
*/
DEBUGASSERT(td != (struct sam_gtd_s *)eplist->tail);
while (td != (struct sam_gtd_s *)eplist->tail) while (td != (struct sam_gtd_s *)eplist->tail)
{ {
paddr = (uintptr_t)td->hw.nexttd; paddr = (uintptr_t)td->hw.nexttd;