blob: 2494e7e9a379673dd14aaf4b33bbffa8ea74d922 [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 "xhci.h"
zx_status_t xhci_transfer_ring_init(xhci_transfer_ring_t* ring, int count) {
zx_status_t status = io_buffer_init(&ring->buffer, count * sizeof(xhci_trb_t), IO_BUFFER_RW);
if (status != ZX_OK) return status;
ring->start = io_buffer_virt(&ring->buffer);
ring->current = ring->start;
ring->dequeue_ptr = ring->start;
ring->size = count - 1; // subtract 1 for LINK TRB at the end
ring->pcs = TRB_C;
// set link TRB at end to point back to the beginning
trb_set_ptr(&ring->start[count - 1], (void *)io_buffer_phys(&ring->buffer));
trb_set_control(&ring->start[count - 1], TRB_LINK, TRB_TC);
return ZX_OK;
}
void xhci_transfer_ring_free(xhci_transfer_ring_t* ring) {
io_buffer_release(&ring->buffer);
}
// return the number of free TRBs in the ring
size_t xhci_transfer_ring_free_trbs(xhci_transfer_ring_t* ring) {
xhci_trb_t* current = ring->current;
xhci_trb_t* dequeue_ptr = ring->dequeue_ptr;
int size = ring->size;
if (current < dequeue_ptr) {
current += size;
}
int busy_count = current - dequeue_ptr;
return size - busy_count;
}
zx_status_t xhci_event_ring_init(xhci_t* xhci, int interrupter, int count) {
xhci_event_ring_t* ring = &xhci->event_rings[interrupter];
// allocate buffer for TRBs
zx_status_t status = io_buffer_init(&ring->buffer, count * sizeof(xhci_trb_t), IO_BUFFER_RW);
if (status != ZX_OK) return status;
ring->start = io_buffer_virt(&ring->buffer);
erst_entry_t* erst_array = xhci->erst_arrays[interrupter];
XHCI_WRITE64(&erst_array[0].ptr, io_buffer_phys(&ring->buffer));
XHCI_WRITE32(&erst_array[0].size, count);
ring->current = ring->start;
ring->end = ring->start + count;
ring->ccs = TRB_C;
return ZX_OK;
}
void xhci_event_ring_free(xhci_t* xhci, int interrupter) {
xhci_event_ring_t* ring = &xhci->event_rings[interrupter];
io_buffer_release(&ring->buffer);
}
void xhci_clear_trb(xhci_trb_t* trb) {
XHCI_WRITE64(&trb->ptr, 0);
XHCI_WRITE32(&trb->status, 0);
XHCI_WRITE32(&trb->control, 0);
}
void* xhci_read_trb_ptr(xhci_transfer_ring_t* ring, xhci_trb_t* trb) {
// convert physical address to virtual
uint8_t* ptr = trb_get_ptr(trb);
ptr += ((uint8_t *)io_buffer_virt(&ring->buffer) - (uint8_t *)io_buffer_phys(&ring->buffer));
return ptr;
}
xhci_trb_t* xhci_get_next_trb(xhci_transfer_ring_t* ring, xhci_trb_t* trb) {
trb++;
uint32_t control = XHCI_READ32(&trb->control);
if ((control & TRB_TYPE_MASK) == (TRB_LINK << TRB_TYPE_START)) {
trb = xhci_read_trb_ptr(ring, trb);
}
return trb;
}
void xhci_increment_ring(xhci_transfer_ring_t* ring) {
xhci_trb_t* trb = ring->current;
uint32_t control = XHCI_READ32(&trb->control);
uint32_t chain = control & TRB_CHAIN;
if (ring->pcs) {
XHCI_WRITE32(&trb->control, control | ring->pcs);
}
trb = ++ring->current;
// check for LINK TRB
control = XHCI_READ32(&trb->control);
if ((control & TRB_TYPE_MASK) == (TRB_LINK << TRB_TYPE_START)) {
control = (control & ~(TRB_CHAIN | TRB_C)) | chain | ring->pcs;
XHCI_WRITE32(&trb->control, control);
// toggle pcs if necessary
if (control & TRB_TC) {
ring->pcs ^= TRB_C;
}
ring->current = xhci_read_trb_ptr(ring, trb);
}
}