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