mirror of
https://github.com/apache/nuttx.git
synced 2026-10-01 11:26:29 +00:00
drivers/usbhost: Flush the xHCI rings and structures by address.
xhci_ctrl_start() published the event ring segment table, the device context base address array and the scratchpad pointers with up_flush_dcache_all(), which an architecture whose cache can only be maintained by address implements as a barrier and nothing more, so none of them reached memory. The controller then reads whatever those addresses held before, which presents as every command timing out with no events arriving. Flush each structure by address. xhci_ring_init() has the same fault from the other direction: it clears a whole ring and flushes only the link entry it writes afterwards, leaving the rest of the clearing in the cache. The controller writes into that memory itself, so a line written back later lands on top of an event somebody is waiting for. Flush the whole ring. Assisted-by: Claude:claude-opus-5 Signed-off-by: Justin Hammond <justin@dynam.ac>
This commit is contained in:
parent
8f88d73275
commit
cd30b682a4
1 changed files with 28 additions and 5 deletions
|
|
@ -808,9 +808,16 @@ static int xhci_ring_init(FAR struct xhci_ring_s *ring, size_t len)
|
|||
ring->len = len;
|
||||
}
|
||||
|
||||
/* Reset data in ring */
|
||||
/* Reset data in ring.
|
||||
*
|
||||
* Clearing dirties every line, and the controller writes into this
|
||||
* memory itself. Flush now, or a later writeback lands on top of an
|
||||
* event somebody is waiting for.
|
||||
*/
|
||||
|
||||
memset(ring->ring, 0, ring->len * sizeof(struct xhci_trb_s));
|
||||
up_flush_dcache((uintptr_t)ring->ring,
|
||||
(uintptr_t)(ring->ring + ring->len));
|
||||
|
||||
/* Fill Link TRB */
|
||||
|
||||
|
|
@ -1112,9 +1119,22 @@ static int xhci_ctrl_start(FAR struct usbhost_xhci_s *priv)
|
|||
evnt->size = XHCI_EVENT_MAX;
|
||||
evnt->res = 0;
|
||||
|
||||
/* Flush all memory before write to ERDP so xhci sees correct data */
|
||||
/* Push the structures the controller is about to be pointed at.
|
||||
*
|
||||
* Flush by address: up_flush_dcache_all() is a no-op on architectures
|
||||
* whose cache can only be maintained by address.
|
||||
*/
|
||||
|
||||
up_flush_dcache_all();
|
||||
up_flush_dcache((uintptr_t)priv->pg_erst,
|
||||
(uintptr_t)priv->pg_erst +
|
||||
sizeof(struct xhci_event_ring_s) * priv->no_erst);
|
||||
up_flush_dcache((uintptr_t)priv->pg_ctx,
|
||||
(uintptr_t)(priv->pg_ctx + priv->no_slots + 1));
|
||||
if (priv->pg_sb != NULL)
|
||||
{
|
||||
up_flush_dcache((uintptr_t)priv->pg_sb,
|
||||
(uintptr_t)(priv->pg_sb + priv->no_scratch));
|
||||
}
|
||||
|
||||
xhci_runt_putreg_8b(priv, XHCI_ERDP(0),
|
||||
up_addrenv_va_to_pa(priv->evnt.ring));
|
||||
|
|
@ -1151,9 +1171,12 @@ static int xhci_ctrl_start(FAR struct usbhost_xhci_s *priv)
|
|||
regval |= XHCI_IMAN_IE;
|
||||
xhci_runt_putreg(priv, XHCI_IMAN(0), regval);
|
||||
|
||||
/* Flush all memory once again */
|
||||
/* And the command ring, whose last entry was just made to point back at
|
||||
* its own beginning.
|
||||
*/
|
||||
|
||||
up_flush_dcache_all();
|
||||
up_flush_dcache((uintptr_t)priv->cmd.ring,
|
||||
(uintptr_t)(priv->cmd.ring + XHCI_CMD_MAX));
|
||||
|
||||
/* Turn the host controller ON, enable interrupts and system errors */
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue