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;
|
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
|
* 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,
|
static void xhci_complete ( struct xhci_device *xhci,
|
||||||
struct xhci_trb_complete *complete ) {
|
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 */
|
/* Dequeue command TRB */
|
||||||
xhci_dequeue ( &xhci->command );
|
xhci_dequeue ( &xhci->command );
|
||||||
|
@ -1582,15 +1614,9 @@ static void xhci_complete ( struct xhci_device *xhci,
|
||||||
assert ( xhci_ring_consumed ( &xhci->command ) ==
|
assert ( xhci_ring_consumed ( &xhci->command ) ==
|
||||||
le64_to_cpu ( complete->command ) );
|
le64_to_cpu ( complete->command ) );
|
||||||
|
|
||||||
/* Record completion if applicable */
|
/* Record completion */
|
||||||
if ( xhci->completion ) {
|
memcpy ( xhci->pending, complete, sizeof ( *xhci->pending ) );
|
||||||
memcpy ( xhci->completion, complete,
|
xhci->pending = NULL;
|
||||||
sizeof ( *xhci->completion ) );
|
|
||||||
xhci->completion = NULL;
|
|
||||||
} else {
|
|
||||||
DBGC ( xhci, "XHCI %p unexpected completion:\n", xhci );
|
|
||||||
DBGC_HDA ( xhci, 0, complete, sizeof ( *complete ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
* 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;
|
unsigned int i;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Record the completion buffer */
|
/* Record the pending command */
|
||||||
xhci->completion = trb;
|
xhci->pending = trb;
|
||||||
|
|
||||||
/* Enqueue the command */
|
/* Enqueue the command */
|
||||||
if ( ( rc = xhci_enqueue ( &xhci->command, NULL, trb ) ) != 0 )
|
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 );
|
xhci_event_poll ( xhci );
|
||||||
|
|
||||||
/* Check for completion */
|
/* Check for completion */
|
||||||
if ( ! xhci->completion ) {
|
if ( ! xhci->pending ) {
|
||||||
if ( complete->code != XHCI_CMPLT_SUCCESS ) {
|
if ( complete->code != XHCI_CMPLT_SUCCESS ) {
|
||||||
rc = -ECODE ( complete->code );
|
rc = -ECODE ( complete->code );
|
||||||
DBGC ( xhci, "XHCI %p command failed (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 );
|
DBGC ( xhci, "XHCI %p timed out waiting for completion\n", xhci );
|
||||||
rc = -ETIMEDOUT;
|
rc = -ETIMEDOUT;
|
||||||
|
|
||||||
|
/* Abort command */
|
||||||
|
xhci_abort ( xhci );
|
||||||
|
|
||||||
err_enqueue:
|
err_enqueue:
|
||||||
xhci->completion = NULL;
|
xhci->pending = NULL;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -178,6 +178,9 @@ enum xhci_default_psi_value {
|
||||||
/** Command ring cycle state */
|
/** Command ring cycle state */
|
||||||
#define XHCI_CRCR_RCS 0x00000001UL
|
#define XHCI_CRCR_RCS 0x00000001UL
|
||||||
|
|
||||||
|
/** Command abort */
|
||||||
|
#define XHCI_CRCR_CA 0x00000004UL
|
||||||
|
|
||||||
/** Command ring running */
|
/** Command ring running */
|
||||||
#define XHCI_CRCR_CRR 0x00000008UL
|
#define XHCI_CRCR_CRR 0x00000008UL
|
||||||
|
|
||||||
|
@ -629,6 +632,8 @@ enum xhci_completion_code {
|
||||||
XHCI_CMPLT_SUCCESS = 1,
|
XHCI_CMPLT_SUCCESS = 1,
|
||||||
/** Short packet */
|
/** Short packet */
|
||||||
XHCI_CMPLT_SHORT = 13,
|
XHCI_CMPLT_SHORT = 13,
|
||||||
|
/** Command ring stopped */
|
||||||
|
XHCI_CMPLT_CMD_STOPPED = 24,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A port status change transfer request block */
|
/** 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
|
#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
|
/** Maximum time to wait for a port reset to complete
|
||||||
*
|
*
|
||||||
* This is a policy decision.
|
* This is a policy decision.
|
||||||
|
@ -1042,8 +1053,8 @@ struct xhci_device {
|
||||||
struct xhci_trb_ring command;
|
struct xhci_trb_ring command;
|
||||||
/** Event ring */
|
/** Event ring */
|
||||||
struct xhci_event_ring event;
|
struct xhci_event_ring event;
|
||||||
/** Current command completion buffer (if any) */
|
/** Current command (if any) */
|
||||||
union xhci_trb *completion;
|
union xhci_trb *pending;
|
||||||
|
|
||||||
/** Device slots, indexed by slot ID */
|
/** Device slots, indexed by slot ID */
|
||||||
struct xhci_slot **slot;
|
struct xhci_slot **slot;
|
||||||
|
|
Loading…
Reference in New Issue