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 <assert.h>
6#include <hw/arch_ops.h>
7
8#include "xhci.h"
9
10zx_status_t xhci_transfer_ring_init(xhci_transfer_ring_t* ring, zx_handle_t bti_handle, int count) {
11    zx_status_t status = io_buffer_init(&ring->buffer, bti_handle,
12                                        count * sizeof(xhci_trb_t),
13                                        IO_BUFFER_RW | IO_BUFFER_CONTIG | XHCI_IO_BUFFER_UNCACHED);
14    if (status != ZX_OK) return status;
15
16    ring->start = io_buffer_virt(&ring->buffer);
17    ring->current = ring->start;
18    ring->dequeue_ptr = ring->start;
19    ring->full = false;
20    ring->size = count - 1;    // subtract 1 for LINK TRB at the end
21    ring->pcs = TRB_C;
22
23    // set link TRB at end to point back to the beginning
24    trb_set_ptr(&ring->start[count - 1], (void *)io_buffer_phys(&ring->buffer));
25    trb_set_control(&ring->start[count - 1], TRB_LINK, TRB_TC);
26    return ZX_OK;
27}
28
29void xhci_transfer_ring_free(xhci_transfer_ring_t* ring) {
30    io_buffer_release(&ring->buffer);
31}
32
33// return the number of free TRBs in the ring
34size_t xhci_transfer_ring_free_trbs(xhci_transfer_ring_t* ring) {
35    xhci_trb_t* current = ring->current;
36    xhci_trb_t* dequeue_ptr = ring->dequeue_ptr;
37
38    if (ring->full) {
39        assert(current == dequeue_ptr);
40        return 0;
41    }
42
43    int size = ring->size;
44
45    if (current < dequeue_ptr) {
46        current += size;
47    }
48
49    int busy_count = current - dequeue_ptr;
50    return size - busy_count;
51}
52
53zx_status_t xhci_event_ring_init(xhci_event_ring_t* ring, zx_handle_t bti_handle,
54                                 erst_entry_t* erst_array, int count) {
55    // allocate a read-only buffer for TRBs
56    zx_status_t status = io_buffer_init(&ring->buffer, bti_handle,
57                                        count * sizeof(xhci_trb_t),
58                                        IO_BUFFER_RO | IO_BUFFER_CONTIG | XHCI_IO_BUFFER_UNCACHED);
59    if (status != ZX_OK) return status;
60
61    ring->start = io_buffer_virt(&ring->buffer);
62    XHCI_WRITE64(&erst_array[0].ptr, io_buffer_phys(&ring->buffer));
63    XHCI_WRITE32(&erst_array[0].size, count);
64
65    ring->current = ring->start;
66    ring->end = ring->start + count;
67    ring->ccs = TRB_C;
68    return ZX_OK;
69}
70
71void xhci_event_ring_free(xhci_event_ring_t* ring) {
72    io_buffer_release(&ring->buffer);
73}
74
75void xhci_clear_trb(xhci_trb_t* trb) {
76    XHCI_WRITE64(&trb->ptr, 0);
77    XHCI_WRITE32(&trb->status, 0);
78    XHCI_WRITE32(&trb->control, 0);
79}
80
81void xhci_set_transfer_noop_trb(xhci_trb_t* trb) {
82    uint32_t control = XHCI_READ32(&trb->control);
83    if ((control & TRB_TYPE_MASK) == (TRB_LINK << TRB_TYPE_START)) {
84        // Don't do anything if it's the LINK TRB.
85        return;
86    }
87    XHCI_WRITE64(&trb->ptr, 0);
88    XHCI_WRITE32(&trb->status, 0);
89    // Preserve the cycle bit of the TRB.
90    trb_set_control(trb, TRB_TRANSFER_NOOP, control & TRB_C);
91}
92
93void* xhci_read_trb_ptr(xhci_transfer_ring_t* ring, xhci_trb_t* trb) {
94    // convert physical address to virtual
95    uint8_t* ptr = trb_get_ptr(trb);
96    ptr += ((uint8_t *)io_buffer_virt(&ring->buffer) - (uint8_t *)io_buffer_phys(&ring->buffer));
97    return ptr;
98}
99
100xhci_trb_t* xhci_get_next_trb(xhci_transfer_ring_t* ring, xhci_trb_t* trb) {
101    trb++;
102    uint32_t control = XHCI_READ32(&trb->control);
103    if ((control & TRB_TYPE_MASK) == (TRB_LINK << TRB_TYPE_START)) {
104        trb = xhci_read_trb_ptr(ring, trb);
105    }
106    return trb;
107}
108
109void xhci_increment_ring(xhci_transfer_ring_t* ring) {
110    xhci_trb_t* trb = ring->current;
111    uint32_t control = XHCI_READ32(&trb->control);
112    uint32_t chain = control & TRB_CHAIN;
113    if (ring->pcs) {
114        XHCI_WRITE32(&trb->control, control | ring->pcs);
115    }
116    trb = ++ring->current;
117
118    // check for LINK TRB
119    control = XHCI_READ32(&trb->control);
120    if ((control & TRB_TYPE_MASK) == (TRB_LINK << TRB_TYPE_START)) {
121        control = (control & ~(TRB_CHAIN | TRB_C)) | chain | ring->pcs;
122        XHCI_WRITE32(&trb->control, control);
123
124        // toggle pcs if necessary
125        if (control & TRB_TC) {
126            ring->pcs ^= TRB_C;
127        }
128        ring->current = xhci_read_trb_ptr(ring, trb);
129    }
130
131    if (ring->current == ring->dequeue_ptr) {
132        // We've just enqueued something, so if the pointers are equal,
133        // the ring must be full.
134        ring->full = true;
135    }
136}
137
138void xhci_set_dequeue_ptr(xhci_transfer_ring_t* ring, xhci_trb_t* new_ptr) {
139    ring->dequeue_ptr = new_ptr;
140    ring->full = false;
141}
142
143xhci_trb_t* xhci_transfer_ring_phys_to_trb(xhci_transfer_ring_t* ring, zx_paddr_t phys) {
144    zx_paddr_t first_trb_phys = xhci_transfer_ring_start_phys(ring);
145    // Get the physical address of the start of the last trb,
146    // ring->size does not include the LINK TRB at the end of the ring.
147    zx_paddr_t last_trb_phys = first_trb_phys + (ring->size * sizeof(xhci_trb_t));
148
149    if (phys < first_trb_phys || phys > last_trb_phys) {
150        return NULL;
151    }
152   return ring->start + ((phys - first_trb_phys) / sizeof(xhci_trb_t));
153}
154