mirror of https://github.com/ipxe/ipxe.git
[xhci] Abort commands on timeout
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>pull/34/head
parent
88448de720
commit
645458e5a0
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue