SAMA5D OHCI: Add some missing logic required for USB hub support

This commit is contained in:
Gregory Nutt 2015-05-01 10:13:23 -06:00
parent 530c45d946
commit d506f9d0aa

View File

@ -355,7 +355,7 @@ static void sam_tbfree(uint8_t *buffer);
/* ED list helper functions ****************************************************/ /* ED list helper functions ****************************************************/
static inline int sam_addctrled(struct sam_ed_s *ed); static int sam_addctrled(struct sam_ed_s *ed);
static inline int sam_remctrled(struct sam_ed_s *ed); static inline int sam_remctrled(struct sam_ed_s *ed);
static inline int sam_addbulked(struct sam_ed_s *ed); static inline int sam_addbulked(struct sam_ed_s *ed);
@ -795,6 +795,122 @@ static void sam_tbfree(uint8_t *buffer)
} }
} }
/*******************************************************************************
* Name: sam_addctrled
*
* Description:
* Helper function to add an ED to the control list.
*
*******************************************************************************/
static int sam_addctrled(struct sam_ed_s *ed)
{
irqstate_t flags;
uint32_t regval;
uintptr_t physed;
/* Disable control list processing while we modify the list */
flags = irqsave();
regval = sam_getreg(SAM_USBHOST_CTRL);
regval &= ~OHCI_CTRL_CLE;
sam_putreg(regval, SAM_USBHOST_CTRL);
/* Add the new control ED to the head of the control list */
ed->hw.nexted = sam_getreg(SAM_USBHOST_CTRLHEADED);
arch_clean_dcache((uintptr_t)ed, (uintptr_t)ed + sizeof(struct ohci_ed_s));
physed = sam_physramaddr((uintptr_t)ed);
sam_putreg((uint32_t)physed, SAM_USBHOST_CTRLHEADED);
/* Re-enable control list processing. */
regval = sam_getreg(SAM_USBHOST_CTRL);
regval |= OHCI_CTRL_CLE;
sam_putreg(regval, SAM_USBHOST_CTRL);
irqrestore(flags);
return OK;
}
/*******************************************************************************
* Name: sam_remctrled
*
* Description:
* Helper function remove an ED from the control list.
*
*******************************************************************************/
static inline int sam_remctrled(struct sam_ed_s *ed)
{
struct sam_ed_s *curr;
struct sam_ed_s *prev;
irqstate_t flags;
uintptr_t physed;
uint32_t regval;
/* Disable control list processing while we modify the list */
flags = irqsave();
regval = sam_getreg(SAM_USBHOST_CTRL);
regval &= ~OHCI_CTRL_CLE;
sam_putreg(regval, SAM_USBHOST_CTRL);
/* Find the ED in the control list. NOTE: We really should never be mucking
* with the control list while BLE is set.
*/
physed = sam_getreg(SAM_USBHOST_CTRLHEADED);
for (curr = (struct sam_ed_s *)sam_virtramaddr(physed), prev = NULL;
curr && curr != ed;
prev = curr, curr = (struct sam_ed_s *)curr->hw.nexted);
/* Hmmm.. It would be a bug if we do not find the ED in the control list. */
DEBUGASSERT(curr != NULL);
/* Remove the ED from the control list */
if (curr != NULL)
{
/* Is this ED the first on in the control list? */
if (prev == NULL)
{
/* Yes... set the head of the control list to skip over this ED */
sam_putreg(ed->hw.nexted, SAM_USBHOST_CTRLHEADED);
}
else
{
/* No.. set the forward link of the previous ED in the list
* skip over this ED.
*/
prev->hw.nexted = ed->hw.nexted;
arch_clean_dcache((uintptr_t)prev,
(uintptr_t)prev + sizeof(struct sam_ed_s));
}
}
/* Re-enable control list processing if the control list is still non-empty
* after removing the ED node.
*/
if (sam_getreg(SAM_USBHOST_CTRLHEADED) != 0)
{
/* If the control list is now empty, then disable it */
regval = sam_getreg(SAM_USBHOST_CTRL);
regval |= OHCI_CTRL_CLE;
sam_putreg(regval, SAM_USBHOST_CTRL);
}
irqrestore(flags);
return OK;
}
/******************************************************************************* /*******************************************************************************
* Name: sam_addbulked * Name: sam_addbulked
* *
@ -838,34 +954,6 @@ static inline int sam_addbulked(struct sam_ed_s *ed)
#endif #endif
} }
/*******************************************************************************
* Name: sam_addctrled
*
* Description:
* Helper function to add an ED to the control list.
*
*******************************************************************************/
static inline int sam_addctrled(struct sam_ed_s *ed)
{
#error Not Implemented
return -ENOSYS;
}
/*******************************************************************************
* Name: sam_remctrled
*
* Description:
* Helper function remove an ED from the control list.
*
*******************************************************************************/
static inline int sam_remctrled(struct sam_ed_s *ed)
{
#error Not Implemented
return -ENOSYS;
}
/******************************************************************************* /*******************************************************************************
* Name: sam_rembulked * Name: sam_rembulked
* *
@ -1432,7 +1520,7 @@ static int sam_ep0enqueue(struct sam_rhport_s *rhport)
struct sam_gtd_s *tdtail; struct sam_gtd_s *tdtail;
irqstate_t flags; irqstate_t flags;
uintptr_t physaddr; uintptr_t physaddr;
uint32_t regval; int ret;
DEBUGASSERT(rhport && !rhport->ep0init && rhport->ep0.ed == NULL && DEBUGASSERT(rhport && !rhport->ep0init && rhport->ep0.ed == NULL &&
rhport->ep0.tail == NULL); rhport->ep0.tail == NULL);
@ -1458,14 +1546,6 @@ static int sam_ep0enqueue(struct sam_rhport_s *rhport)
rhport->ep0.ed = edctrl; rhport->ep0.ed = edctrl;
rhport->ep0.tail = tdtail; rhport->ep0.tail = tdtail;
/* ControlListEnable. This bit is cleared to disable the processing of the
* Control list. We should never modify the control list while CLE is set.
*/
regval = sam_getreg(SAM_USBHOST_CTRL);
regval &= ~OHCI_CTRL_CLE;
sam_putreg(regval, SAM_USBHOST_CTRL);
/* Initialize the common tail TD for this port */ /* Initialize the common tail TD for this port */
memset(tdtail, 0, sizeof(struct sam_gtd_s)); memset(tdtail, 0, sizeof(struct sam_gtd_s));
@ -1478,7 +1558,7 @@ static int sam_ep0enqueue(struct sam_rhport_s *rhport)
memset(edctrl, 0, sizeof(struct sam_ed_s)); memset(edctrl, 0, sizeof(struct sam_ed_s));
(void)sam_ep0configure(&rhport->drvr, &rhport->ep0, 0, (void)sam_ep0configure(&rhport->drvr, &rhport->ep0, 0,
rhport->hport.speed, 8); rhport->hport.hport.speed, 8);
edctrl->hw.ctrl |= ED_CONTROL_K; edctrl->hw.ctrl |= ED_CONTROL_K;
edctrl->eplist = &rhport->ep0; edctrl->eplist = &rhport->ep0;
@ -1490,18 +1570,6 @@ static int sam_ep0enqueue(struct sam_rhport_s *rhport)
edctrl->hw.headp = (uint32_t)physaddr; edctrl->hw.headp = (uint32_t)physaddr;
edctrl->hw.tailp = (uint32_t)physaddr; edctrl->hw.tailp = (uint32_t)physaddr;
/* The new ED will be the first ED in the list. Set the nexted
* pointer of the ED old head of the list
*/
physaddr = sam_getreg(SAM_USBHOST_CTRLHEADED);
edctrl->hw.nexted = physaddr;
/* Set the control list head to the new ED */
physaddr = (uintptr_t)sam_physramaddr((uintptr_t)edctrl);
sam_putreg(physaddr, SAM_USBHOST_CTRLHEADED);
/* Flush the affected control ED and tail TD to RAM */ /* Flush the affected control ED and tail TD to RAM */
arch_clean_dcache((uintptr_t)edctrl, arch_clean_dcache((uintptr_t)edctrl,
@ -1509,16 +1577,11 @@ static int sam_ep0enqueue(struct sam_rhport_s *rhport)
arch_clean_dcache((uintptr_t)tdtail, arch_clean_dcache((uintptr_t)tdtail,
(uintptr_t)tdtail + sizeof(struct ohci_gtd_s)); (uintptr_t)tdtail + sizeof(struct ohci_gtd_s));
/* ControlListEnable. This bit is set to (re-)enable the processing of the /* Add the ED to the control list */
* Control list. Note: once enabled, it remains enabled and we may even
* complete list processing before we get the bit set.
*/
regval = sam_getreg(SAM_USBHOST_CTRL); ret = sam_addctrled(edctrl);
regval |= OHCI_CTRL_CLE;
sam_putreg(regval, SAM_USBHOST_CTRL);
irqrestore(flags); irqrestore(flags);
return OK; return ret;
} }
/******************************************************************************* /*******************************************************************************
@ -1879,12 +1942,12 @@ static void sam_rhsc_bottomhalf(void)
/* Are we bound to a class instance? */ /* Are we bound to a class instance? */
if (rhport->hport.devclass) if (rhport->hport.hport.devclass)
{ {
/* Yes.. Disconnect the class */ /* Yes.. Disconnect the class */
CLASS_DISCONNECTED(rhport->hport.devclass); CLASS_DISCONNECTED(rhport->hport.hport.devclass);
rhport->hport.devclass = NULL; rhport->hport.hport.devclass = NULL;
} }
/* Notify any waiters for the Root Hub Status change /* Notify any waiters for the Root Hub Status change
@ -2141,13 +2204,16 @@ static int sam_wait(FAR struct usbhost_connection_s *conn,
for (rhpndx = 0; rhpndx < SAM_OHCI_NRHPORT; rhpndx++) for (rhpndx = 0; rhpndx < SAM_OHCI_NRHPORT; rhpndx++)
{ {
struct sam_rhport_s *rhport = &g_ohci.rhport[rhpndx];
struct usbhost_hubport_s *connport;
#if 0 /* #ifdef CONFIG_SAMA5_EHCI */ #if 0 /* #ifdef CONFIG_SAMA5_EHCI */
/* If a device is no longer connected, return the port to the EHCI /* If a device is no longer connected, return the port to the EHCI
* controller. Zero is the reset value for all ports; one makes * controller. Zero is the reset value for all ports; one makes
* the corresponding port available to OHCI. * the corresponding port available to OHCI.
*/ */
if (!g_ohci.rhport[rhpndx].connected) if (!rhport->connected)
{ {
uint32_t regval = getreg32(SAM_SFR_OHCIICR); uint32_t regval = getreg32(SAM_SFR_OHCIICR);
regval &= ~SFR_OHCIICR_RES(rhpndx); regval &= ~SFR_OHCIICR_RES(rhpndx);
@ -2157,7 +2223,8 @@ static int sam_wait(FAR struct usbhost_connection_s *conn,
/* Has the connection state changed on the RH port? */ /* Has the connection state changed on the RH port? */
if (g_ohci.rhport[rhpndx].connected != connected[rhpndx]) connport = &rhport->hport.hport;
if (rhport->connected != connport->connected)
{ {
/* Yes.. Return the RH port number to inform the caller which /* Yes.. Return the RH port number to inform the caller which
* port has the connection change. * port has the connection change.
@ -2167,7 +2234,8 @@ static int sam_wait(FAR struct usbhost_connection_s *conn,
usbhost_vtrace2(OHCI_VTRACE2_WAKEUP, usbhost_vtrace2(OHCI_VTRACE2_WAKEUP,
rhpndx + 1, g_ohci.rhport[rhpndx].connected); rhpndx + 1, g_ohci.rhport[rhpndx].connected);
*hport = &g_ohci.rphort[rhpndx].hport; connport->connected = rhport->connected;
*hport = connport;
return OK; return OK;
} }
} }
@ -2387,7 +2455,7 @@ static int sam_ep0configure(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
hwctrl |= ED_CONTROL_S; hwctrl |= ED_CONTROL_S;
} }
ed->hw.ctrl = hwctrl; edctrl->hw.ctrl = hwctrl;
/* Flush the modified control ED to RAM */ /* Flush the modified control ED to RAM */
@ -2396,7 +2464,8 @@ static int sam_ep0configure(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
sam_givesem(&g_ohci.exclsem); sam_givesem(&g_ohci.exclsem);
usbhost_vtrace2(OHCI_VTRACE2_EP0CONFIGURE, usbhost_vtrace2(OHCI_VTRACE2_EP0CONFIGURE,
RHPORT(rhport), (uint16_t)edctrl->hw.ctrl); RHPORT(rhport->hport.port), (uint16_t)edctrl->hw.ctrl);
UNUSED(rhport);
return OK; return OK;
} }
@ -2439,6 +2508,7 @@ static int sam_epalloc(FAR struct usbhost_driver_s *drvr,
DEBUGASSERT(rhport != NULL && epdesc != NULL && epdesc->hport != NULL); DEBUGASSERT(rhport != NULL && epdesc != NULL && epdesc->hport != NULL);
DEBUGASSERT(ep != NULL && rhport->connected); DEBUGASSERT(ep != NULL && rhport->connected);
UNUSED(rhport);
hport = epdesc->hport; hport = epdesc->hport;
@ -3302,7 +3372,7 @@ static void sam_disconnect(FAR struct usbhost_driver_s *drvr)
/* Unbind the class */ /* Unbind the class */
rhport->hport.devclass = NULL; rhport->hport.hport.devclass = NULL;
} }
/******************************************************************************* /*******************************************************************************