]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[xhci] Abort commands on timeout
authorMichael Brown <mcb30@ipxe.org>
Wed, 18 Feb 2015 11:10:55 +0000 (11:10 +0000)
committerMichael Brown <mcb30@ipxe.org>
Wed, 18 Feb 2015 11:10:55 +0000 (11:10 +0000)
When a command times out, abort it (via the Command Abort bit in the
Command Ring Control Register) so that subsequent commands may execute
as expected.

This improves robustness when a device fails to respond to the Set
Address command, since the subsequent Disable Slot command will now
succeed.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/usb/xhci.c
src/drivers/usb/xhci.h

index 3574192df8c7ee596aa212ffe4e360baafac783e..a5bcb62bdbe60705897b2ff3eb407cfdac5c7ebe 100644 (file)
@@ -1198,6 +1198,22 @@ static int xhci_ring_alloc ( struct xhci_device *xhci,
        return rc;
 }
 
+/**
+ * Reset transfer request block ring
+ *
+ * @v ring             TRB ring
+ */
+static void xhci_ring_reset ( struct xhci_trb_ring *ring ) {
+       unsigned int count = ( 1U << ring->shift );
+
+       /* Reset producer and consumer counters */
+       ring->prod = 0;
+       ring->cons = 0;
+
+       /* Reset TRBs (except Link TRB) */
+       memset ( ring->trb, 0, ( count * sizeof ( ring->trb[0] ) ) );
+}
+
 /**
  * Free transfer request block ring
  *
@@ -1574,6 +1590,22 @@ static void xhci_transfer ( struct xhci_device *xhci,
  */
 static void xhci_complete ( struct xhci_device *xhci,
                            struct xhci_trb_complete *complete ) {
+       int rc;
+
+       /* Ignore "command ring stopped" notifications */
+       if ( complete->code == XHCI_CMPLT_CMD_STOPPED ) {
+               DBGC2 ( xhci, "XHCI %p command ring stopped\n", xhci );
+               return;
+       }
+
+       /* Ignore unexpected completions */
+       if ( ! xhci->pending ) {
+               rc = -ECODE ( complete->code );
+               DBGC ( xhci, "XHCI %p unexpected completion (code %d): %s\n",
+                      xhci, complete->code, strerror ( rc ) );
+               DBGC_HDA ( xhci, 0, complete, sizeof ( *complete ) );
+               return;
+       }
 
        /* Dequeue command TRB */
        xhci_dequeue ( &xhci->command );
@@ -1582,15 +1614,9 @@ static void xhci_complete ( struct xhci_device *xhci,
        assert ( xhci_ring_consumed ( &xhci->command ) ==
                 le64_to_cpu ( complete->command ) );
 
-       /* Record completion if applicable */
-       if ( xhci->completion ) {
-               memcpy ( xhci->completion, complete,
-                        sizeof ( *xhci->completion ) );
-               xhci->completion = NULL;
-       } else {
-               DBGC ( xhci, "XHCI %p unexpected completion:\n", xhci );
-               DBGC_HDA ( xhci, 0, complete, sizeof ( *complete ) );
-       }
+       /* Record completion */
+       memcpy ( xhci->pending, complete, sizeof ( *xhci->pending ) );
+       xhci->pending = NULL;
 }
 
 /**
@@ -1696,6 +1722,33 @@ static void xhci_event_poll ( struct xhci_device *xhci ) {
        }
 }
 
+/**
+ * Abort command
+ *
+ * @v xhci             xHCI device
+ */
+static void xhci_abort ( struct xhci_device *xhci ) {
+       physaddr_t crp;
+
+       /* Abort the command */
+       DBGC2 ( xhci, "XHCI %p aborting command\n", xhci );
+       xhci_writeq ( xhci, XHCI_CRCR_CA, xhci->op + XHCI_OP_CRCR );
+
+       /* 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 );
+
+       /* Consume (and ignore) any final command status */
+       xhci_event_poll ( xhci );
+
+       /* Reset the command ring control register */
+       xhci_ring_reset ( &xhci->command );
+       crp = virt_to_phys ( xhci->command.trb );
+       xhci_writeq ( xhci, ( crp | XHCI_CRCR_RCS ), xhci->op + XHCI_OP_CRCR );
+}
+
 /**
  * Issue command and wait for completion
  *
@@ -1711,8 +1764,8 @@ static int xhci_command ( struct xhci_device *xhci, union xhci_trb *trb ) {
        unsigned int i;
        int rc;
 
-       /* Record the completion buffer */
-       xhci->completion = trb;
+       /* Record the pending command */
+       xhci->pending = trb;
 
        /* Enqueue the command */
        if ( ( rc = xhci_enqueue ( &xhci->command, NULL, trb ) ) != 0 )
@@ -1728,7 +1781,7 @@ static int xhci_command ( struct xhci_device *xhci, union xhci_trb *trb ) {
                xhci_event_poll ( xhci );
 
                /* Check for completion */
-               if ( ! xhci->completion ) {
+               if ( ! xhci->pending ) {
                        if ( complete->code != XHCI_CMPLT_SUCCESS ) {
                                rc = -ECODE ( complete->code );
                                DBGC ( xhci, "XHCI %p command failed (code "
@@ -1748,8 +1801,11 @@ static int xhci_command ( struct xhci_device *xhci, union xhci_trb *trb ) {
        DBGC ( xhci, "XHCI %p timed out waiting for completion\n", xhci );
        rc = -ETIMEDOUT;
 
+       /* Abort command */
+       xhci_abort ( xhci );
+
  err_enqueue:
-       xhci->completion = NULL;
+       xhci->pending = NULL;
        return rc;
 }
 
index ec951bd61f35ec4680f9d35743186506a629731e..30c6d1f4442c90dc3f71257876bb3a2532ea6f92 100644 (file)
@@ -178,6 +178,9 @@ enum xhci_default_psi_value {
 /** Command ring cycle state */
 #define XHCI_CRCR_RCS 0x00000001UL
 
+/** Command abort */
+#define XHCI_CRCR_CA 0x00000004UL
+
 /** Command ring running */
 #define XHCI_CRCR_CRR 0x00000008UL
 
@@ -629,6 +632,8 @@ enum xhci_completion_code {
        XHCI_CMPLT_SUCCESS = 1,
        /** Short packet */
        XHCI_CMPLT_SHORT = 13,
+       /** Command ring stopped */
+       XHCI_CMPLT_CMD_STOPPED = 24,
 };
 
 /** A port status change transfer request block */
@@ -987,6 +992,12 @@ xhci_ring_consumed ( struct xhci_trb_ring *ring ) {
  */
 #define XHCI_COMMAND_MAX_WAIT_MS 500
 
+/** Time to delay after aborting a command
+ *
+ * This is a policy decision
+ */
+#define XHCI_COMMAND_ABORT_DELAY_MS 500
+
 /** Maximum time to wait for a port reset to complete
  *
  * This is a policy decision.
@@ -1042,8 +1053,8 @@ struct xhci_device {
        struct xhci_trb_ring command;
        /** Event ring */
        struct xhci_event_ring event;
-       /** Current command completion buffer (if any) */
-       union xhci_trb *completion;
+       /** Current command (if any) */
+       union xhci_trb *pending;
 
        /** Device slots, indexed by slot ID */
        struct xhci_slot **slot;