david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[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>
This commit is contained in:
Michael Brown 2015-02-18 11:10:55 +00:00
parent 88448de720
commit 645458e5a0
2 changed files with 82 additions and 15 deletions

View 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;
}

View 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;