blob: 4d2558057e1e4427e22ebe5d51e8bdd878264054 [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.
#pragma once
#include <ddk/device.h>
#include <ddk/io-buffer.h>
#include <ddk/protocol/pci.h>
#include <ddk/protocol/usb/bus.h>
#include <ddk/protocol/usb/request.h>
#include <fbl/array.h>
#include <fbl/mutex.h>
#include <lib/mmio/mmio.h>
#include <lib/sync/completion.h>
#include <lib/zx/bti.h>
#include <lib/zx/interrupt.h>
#include <lib/zx/profile.h>
#include <usb/usb-request.h>
#include <zircon/hw/usb.h>
#include <zircon/listnode.h>
#include <zircon/types.h>
#include <atomic>
#include <limits.h>
#include <threads.h>
#include "trb-sizes.h"
#include "xhci-hw.h"
#include "xhci-root-hub.h"
#include "xhci-transfer-common.h"
#include "xhci-trb.h"
namespace usb_xhci {
#define XHCI_RH_USB_2 0 // index of USB 2.0 virtual root hub device
#define XHCI_RH_USB_3 1 // index of USB 2.0 virtual root hub device
#define XHCI_RH_COUNT 2 // number of virtual root hub devices
#define ISOCH_INTERRUPTER 1
#if __x86_64__
// cache is coherent on x86, so we always use cached buffers
#define XHCI_IO_BUFFER_UNCACHED 0
#else
#define XHCI_IO_BUFFER_UNCACHED IO_BUFFER_UNCACHED
#endif
enum xhci_ep_state_t {
EP_STATE_DEAD = 0, // device does not exist or has been removed
EP_STATE_RUNNING,
EP_STATE_HALTED, // halted due to stall
EP_STATE_PAUSED, // temporarily stopped for canceling a transfer
EP_STATE_DISABLED, // endpoint is not enabled
EP_STATE_ERROR, // endpoint has error condition
};
struct xhci_endpoint_t {
const xhci_endpoint_context_t* epc = nullptr;
xhci_transfer_ring_t transfer_ring = {};
// requests waiting to be processed
list_node_t queued_reqs;
// request currently being processed
usb_request_t* current_req = nullptr;
// processed requests waiting for completion, including current_req
list_node_t pending_reqs;
// transfer state for current_req
xhci_transfer_state_t* transfer_state = nullptr;
fbl::Mutex lock;
xhci_ep_state_t state = EP_STATE_DEAD;
uint16_t max_packet_size;
uint8_t ep_type;
};
struct xhci_slot_t {
// buffer for our device context
io_buffer_t buffer = {};
const xhci_slot_context_t* sc = nullptr;
// epcs point into DMA memory past sc
xhci_endpoint_t eps[XHCI_NUM_EPS];
usb_request_t* current_ctrl_req = nullptr;
uint32_t hub_address;
uint32_t port;
uint32_t rh_port;
usb_speed_t speed;
};
struct xhci_usb_request_internal_t {
// callback to the upper layer
usb_request_complete_t complete_cb;
// for queueing request at xhci level
list_node_t node;
xhci_trb_t* context;
};
#define USB_REQ_TO_XHCI_INTERNAL(req) \
((xhci_usb_request_internal_t *)((uintptr_t)(req) + sizeof(usb_request_t)))
#define XHCI_INTERNAL_TO_USB_REQ(ctx) ((usb_request_t *)((uintptr_t)(ctx) - sizeof(usb_request_t)))
typedef void (*xhci_command_complete_cb)(void* data, uint32_t cc, xhci_trb_t* command_trb,
xhci_trb_t* event_trb);
struct xhci_command_context_t {
xhci_command_complete_cb callback;
xhci_trb_t* next_trb;
void* data;
};
enum xhci_mode_t {
XHCI_PCI_LEGACY,
XHCI_PCI_MSI,
XHCI_PDEV,
};
static constexpr uint32_t INTERRUPTER_COUNT = 2;
struct xhci_t {
// interface for calling back to usb bus driver
usb_bus_interface_protocol_t bus = {};
xhci_mode_t mode;
std::atomic<bool> suspended;
// Desired number of interrupters. This may be greater than what is
// supported by hardware. The actual number of interrupts configured
// will not exceed this, and is stored in num_interrupts.
thrd_t completer_threads[INTERRUPTER_COUNT];
zx::interrupt irq_handles[INTERRUPTER_COUNT];
// actual number of interrupts we are using
uint32_t num_interrupts;
std::optional<ddk::MmioBuffer> mmio;
// PCI support
pci_protocol_t pci = {};
// MMIO data structures
xhci_cap_regs_t* cap_regs;
xhci_op_regs_t* op_regs;
volatile uint32_t* doorbells;
xhci_runtime_regs_t* runtime_regs;
// DMA data structures
uint64_t* dcbaa;
zx_paddr_t dcbaa_phys;
xhci_transfer_ring_t command_ring = {};
fbl::Mutex command_ring_lock;
xhci_command_context_t* command_contexts[COMMAND_RING_SIZE] = {};
// Each interrupter has an event ring.
// Only indices up to num_interrupts will be populated.
xhci_event_ring_t event_rings[INTERRUPTER_COUNT];
erst_entry_t* erst_arrays[INTERRUPTER_COUNT] = {};
zx_paddr_t erst_arrays_phys[INTERRUPTER_COUNT] = {};
size_t page_size;
uint32_t max_slots;
size_t context_size;
// true if controller supports large ESIT payloads
bool large_esit = false;
// total number of ports for the root hub
uint8_t rh_num_ports;
// state for virtual root hub devices
// one for USB 2.0 and the other for USB 3.0
xhci_root_hub_t root_hubs[XHCI_RH_COUNT];
// Maps root hub port index to the index of their virtual root hub
fbl::Array<uint8_t> rh_map;
// Maps root hub port index to index relative to their virtual root hub
fbl::Array<uint8_t> rh_port_map;
// Pointer to the USB Legacy Support Capability, if present.
xhci_usb_legacy_support_cap_t* usb_legacy_support_cap = nullptr;
// device thread stuff
thrd_t device_thread;
fbl::Array<xhci_slot_t> slots;
// for command processing in xhci-device-manager.c
list_node_t command_queue;
fbl::Mutex command_queue_mutex;
sync_completion_t command_queue_completion;
// DMA buffers used by xhci_device_thread in xhci-device-manager.c
uint8_t* input_context;
zx_paddr_t input_context_phys;
fbl::Mutex input_context_lock;
// for xhci_get_current_frame()
fbl::Mutex mfindex_mutex;
// number of times mfindex has wrapped
uint64_t mfindex_wrap_count;
// time of last mfindex wrap
zx_time_t last_mfindex_wrap;
// VMO buffer for DCBAA and ERST array
ddk::IoBuffer dcbaa_erst_buffer;
ddk::IoBuffer erst_buffers[INTERRUPTER_COUNT];
size_t erst_sizes[INTERRUPTER_COUNT];
// VMO buffer for input context
ddk::IoBuffer input_context_buffer;
// VMO buffer for scratch pad pages
ddk::IoBuffer scratch_pad_pages_buffer;
// VMO buffer for scratch pad index
ddk::IoBuffer scratch_pad_index_buffer;
zx::bti bti_handle;
zx::profile profile_handle;
// pool of control requests that can be reused
usb_request_pool_t free_reqs = {};
};
zx_status_t xhci_init(xhci_t* xhci, xhci_mode_t mode, uint32_t num_interrupts);
// Returns the max number of interrupters supported by the xhci.
// This is different to xhci->num_interrupts.
uint32_t xhci_get_max_interrupters(xhci_t* xhci);
int xhci_get_slot_ctx_state(xhci_slot_t* slot);
int xhci_get_ep_ctx_state(xhci_slot_t* slot, xhci_endpoint_t* ep);
void xhci_set_dbcaa(xhci_t* xhci, uint32_t slot_id, zx_paddr_t paddr);
zx_status_t xhci_start(xhci_t* xhci);
void xhci_handle_interrupt(xhci_t* xhci, uint32_t interrupter);
zx_status_t xhci_post_command(xhci_t* xhci, uint32_t command, uint64_t ptr, uint32_t control_bits,
xhci_command_context_t* context);
void xhci_wait_bits(volatile uint32_t* ptr, uint32_t bits, uint32_t expected);
void xhci_wait_bits64(volatile uint64_t* ptr, uint64_t bits, uint64_t expected);
void xhci_stop(xhci_t* xhci);
void xhci_free(xhci_t* xhci);
bool xhci_add_to_list_tail(xhci_t* xhci, list_node_t* list, usb_request_t* req);
bool xhci_add_to_list_head(xhci_t* xhci, list_node_t* list, usb_request_t* req);
bool xhci_remove_from_list_head(xhci_t* xhci, list_node_t* list, usb_request_t** req);
bool xhci_remove_from_list_tail(xhci_t* xhci, list_node_t* list, usb_request_t** req);
void xhci_delete_req_node(xhci_t* xhci, usb_request_t* req);
// returns monotonically increasing frame count
uint64_t xhci_get_current_frame(xhci_t* xhci);
uint8_t xhci_endpoint_index(uint8_t ep_address);
// returns index into xhci->root_hubs[], or -1 if not a root hub
int xhci_get_root_hub_index(xhci_t* xhci, uint32_t device_id);
static inline bool xhci_is_root_hub(xhci_t* xhci, uint32_t device_id) {
return xhci_get_root_hub_index(xhci, device_id) >= 0;
}
// upper layer routines in usb-xhci.c
zx_status_t xhci_add_device(xhci_t* xhci, int slot_id, int hub_address, int speed);
void xhci_remove_device(xhci_t* xhci, int slot_id);
void xhci_request_queue(xhci_t* xhci, usb_request_t* req,
const usb_request_complete_t* complete_cb);
} // namespace usb_xhci