return -ETIMEDOUT;
}
+/**
+ * Mark xHCI device as permanently failed
+ *
+ * @v xhci xHCI device
+ * @ret rc Return status code
+ */
+static int xhci_fail ( struct xhci_device *xhci ) {
+ size_t len;
+ int rc;
+
+ /* Mark command mechanism as permanently failed */
+ xhci->failed = 1;
+
+ /* Reset device */
+ if ( ( rc = xhci_reset ( xhci ) ) != 0 )
+ return rc;
+
+ /* Discard DCBAA entries since DCBAAP has been cleared */
+ assert ( xhci->dcbaa.context != NULL );
+ len = ( ( xhci->slots + 1 ) * sizeof ( xhci->dcbaa.context[0] ) );
+ memset ( xhci->dcbaa.context, 0, len );
+
+ return 0;
+}
+
/******************************************************************************
*
* Transfer request blocks
unsigned int consumed;
unsigned int type;
+ /* Do nothing if device has permanently failed */
+ if ( xhci->failed )
+ return;
+
/* Poll for events */
profile_start ( &xhci_event_profiler );
for ( consumed = 0 ; ; consumed++ ) {
*/
static void xhci_abort ( struct xhci_device *xhci ) {
physaddr_t crp;
+ uint32_t crcr;
/* Abort the command */
DBGC2 ( xhci, "XHCI %s aborting command\n", xhci->name );
/* Allow time for command to abort */
mdelay ( XHCI_COMMAND_ABORT_DELAY_MS );
- /* Sanity check */
- assert ( ( readl ( xhci->op + XHCI_OP_CRCR ) & XHCI_CRCR_CRR ) == 0 );
+ /* Check for failure to abort */
+ crcr = readl ( xhci->op + XHCI_OP_CRCR );
+ if ( crcr & XHCI_CRCR_CRR ) {
+
+ /* Device has failed to abort a command and is almost
+ * certainly beyond repair. Reset device, abandoning
+ * all state, and mark device as failed to avoid
+ * delays on any future command attempts.
+ */
+ DBGC ( xhci, "XHCI %s failed to abort command\n", xhci->name );
+ xhci_fail ( xhci );
+ }
/* Consume (and ignore) any final command status */
xhci_event_poll ( xhci );
unsigned int i;
int rc;
+ /* Immediately fail all commands if command mechanism has failed */
+ if ( xhci->failed ) {
+ rc = -EPIPE;
+ goto err_failed;
+ }
+
/* Sanity check */
if ( xhci->pending ) {
DBGC ( xhci, "XHCI %s command ring busy\n", xhci->name );
err_enqueue:
xhci->pending = NULL;
err_pending:
+ err_failed:
return rc;
}