1// Copyright 2016 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <ddk/debug.h> 6#include <hw/arch_ops.h> 7#include <stdio.h> 8 9#include "xhci-util.h" 10 11static void xhci_sync_command_callback(void* data, uint32_t cc, xhci_trb_t* command_trb, 12 xhci_trb_t* event_trb) { 13 xhci_sync_command_t* command = (xhci_sync_command_t*)data; 14 command->status = XHCI_READ32(&event_trb->status); 15 command->control = XHCI_READ32(&event_trb->control); 16 sync_completion_signal(&command->completion); 17} 18 19void xhci_sync_command_init(xhci_sync_command_t* command) { 20 sync_completion_reset(&command->completion); 21 command->context.callback = xhci_sync_command_callback; 22 command->context.data = command; 23} 24 25// returns condition code 26int xhci_sync_command_wait(xhci_sync_command_t* command) { 27 sync_completion_wait(&command->completion, ZX_TIME_INFINITE); 28 29 return (command->status & XHCI_MASK(EVT_TRB_CC_START, EVT_TRB_CC_BITS)) >> EVT_TRB_CC_START; 30} 31 32zx_status_t xhci_send_command(xhci_t* xhci, uint32_t cmd, uint64_t ptr, uint32_t control_bits) { 33 xhci_sync_command_t command; 34 int cc; 35 36 xhci_sync_command_init(&command); 37 xhci_post_command(xhci, cmd, ptr, control_bits, &command.context); 38 39 // Wait for one second (arbitrarily chosen timeout) 40 // TODO(voydanoff) consider making the timeout a parameter to this function 41 zx_status_t status = sync_completion_wait(&command.completion, ZX_SEC(1)); 42 if (status == ZX_OK) { 43 cc = (command.status & XHCI_MASK(EVT_TRB_CC_START, EVT_TRB_CC_BITS)) >> EVT_TRB_CC_START; 44 if (cc == TRB_CC_SUCCESS) { 45 return ZX_OK; 46 } 47 zxlogf(ERROR, "xhci_send_command %u failed, cc: %d\n", cmd, cc); 48 return ZX_ERR_INTERNAL; 49 } else if (status == ZX_ERR_TIMED_OUT) { 50 sync_completion_reset(&command.completion); 51 52 // abort the command 53 volatile uint64_t* crcr_ptr = &xhci->op_regs->crcr; 54 XHCI_WRITE64(crcr_ptr, CRCR_CA); 55 56 // wait for TRB_CC_COMMAND_ABORTED 57 sync_completion_wait(&command.completion, ZX_TIME_INFINITE); 58 cc = (command.status & XHCI_MASK(EVT_TRB_CC_START, EVT_TRB_CC_BITS)) >> EVT_TRB_CC_START; 59 if (cc == TRB_CC_SUCCESS) { 60 // command must have completed while we were trying to abort it 61 status = ZX_OK; 62 } 63 64 // ring doorbell to restart command ring 65 hw_mb(); 66 XHCI_WRITE32(&xhci->doorbells[0], 0); 67 xhci_wait_bits64(crcr_ptr, CRCR_CRR, CRCR_CRR); 68 } 69 70 return status; 71} 72 73uint32_t* xhci_get_next_ext_cap(void* mmio, uint32_t* prev_cap, uint32_t* match_cap_id) { 74 uint32_t* cap_ptr = prev_cap; 75 if (!cap_ptr) { 76 // Find the first cap. 77 xhci_cap_regs_t* xhci_cap_regs = (xhci_cap_regs_t*)mmio; 78 volatile uint32_t* hccparams1 = &xhci_cap_regs->hccparams1; 79 80 uint32_t offset = XHCI_GET_BITS32(hccparams1, HCCPARAMS1_EXT_CAP_PTR_START, 81 HCCPARAMS1_EXT_CAP_PTR_BITS); 82 if (!offset) { 83 return NULL; 84 } 85 // offset is 32-bit words from MMIO base 86 cap_ptr = (uint32_t *)(mmio + (offset << 2)); 87 } 88 89 while (cap_ptr) { 90 // We only want to check the current cap for a match if it's not the 91 // one the user gave us. 92 if (cap_ptr != prev_cap) { 93 uint32_t cap_id = XHCI_GET_BITS32(cap_ptr, EXT_CAP_CAPABILITY_ID_START, 94 EXT_CAP_CAPABILITY_ID_BITS); 95 96 // The cap only matches if the user didn't specify an id to match, 97 // or the ids are equal. 98 if (!match_cap_id || (*match_cap_id == cap_id)) { 99 return cap_ptr; 100 } 101 } 102 // Get the next cap ptr, offset is 32-bit words from cap_ptr 103 uint32_t offset = XHCI_GET_BITS32(cap_ptr, EXT_CAP_NEXT_PTR_START, 104 EXT_CAP_NEXT_PTR_BITS); 105 cap_ptr = (offset ? cap_ptr + offset : NULL); 106 } 107 return NULL; 108} 109