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