blob: 96ba14b9f5e4b357b7d4f28c1453b34da2464171 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <ddk/debug.h>
#include <stdio.h>
#include "xhci-util.h"
static void xhci_sync_command_callback(void* data, uint32_t cc, xhci_trb_t* command_trb,
xhci_trb_t* event_trb) {
xhci_sync_command_t* command = (xhci_sync_command_t*)data;
command->status = XHCI_READ32(&event_trb->status);
command->control = XHCI_READ32(&event_trb->control);
completion_signal(&command->completion);
}
void xhci_sync_command_init(xhci_sync_command_t* command) {
completion_reset(&command->completion);
command->context.callback = xhci_sync_command_callback;
command->context.data = command;
}
// returns condition code
int xhci_sync_command_wait(xhci_sync_command_t* command) {
completion_wait(&command->completion, ZX_TIME_INFINITE);
return (command->status & XHCI_MASK(EVT_TRB_CC_START, EVT_TRB_CC_BITS)) >> EVT_TRB_CC_START;
}
zx_status_t xhci_send_command(xhci_t* xhci, uint32_t cmd, uint64_t ptr, uint32_t control_bits) {
xhci_sync_command_t command;
int cc;
xhci_sync_command_init(&command);
xhci_post_command(xhci, cmd, ptr, control_bits, &command.context);
// Wait for one second (arbitrarily chosen timeout)
// TODO(voydanoff) consider making the timeout a parameter to this function
zx_status_t status = completion_wait(&command.completion, ZX_SEC(1));
if (status == ZX_OK) {
cc = (command.status & XHCI_MASK(EVT_TRB_CC_START, EVT_TRB_CC_BITS)) >> EVT_TRB_CC_START;
if (cc == TRB_CC_SUCCESS) {
return ZX_OK;
}
dprintf(ERROR, "xhci_send_command %u failed, cc: %d\n", cmd, cc);
return ZX_ERR_INTERNAL;
} else if (status == ZX_ERR_TIMED_OUT) {
completion_reset(&command.completion);
// abort the command
volatile uint64_t* crcr_ptr = &xhci->op_regs->crcr;
XHCI_WRITE64(crcr_ptr, CRCR_CA);
// wait for TRB_CC_COMMAND_ABORTED
completion_wait(&command.completion, ZX_TIME_INFINITE);
cc = (command.status & XHCI_MASK(EVT_TRB_CC_START, EVT_TRB_CC_BITS)) >> EVT_TRB_CC_START;
if (cc == TRB_CC_SUCCESS) {
// command must have completed while we were trying to abort it
status = ZX_OK;
}
// ring doorbell to restart command ring
XHCI_WRITE32(&xhci->doorbells[0], 0);
xhci_wait_bits64(crcr_ptr, CRCR_CRR, CRCR_CRR);
}
return status;
}