Commit initial version of new XHCI driver which was written from

scratch. This driver adds support for USB3.0 devices. The XHCI
interface is also backwards compatible to USB2.0 and USB1.0 and will
evntually replace the OHCI/UHCI and EHCI drivers.

There will be follow-up commits during the coming week to link the
driver into the default kernel build and add missing USB3.0
functionality in the USB core. Currently only the driver files are
committed.

Approved by:	thompsa (mentor)
This commit is contained in:
hselasky 2010-10-03 08:12:17 +00:00
parent a5c3ee2add
commit b571d139e1
4 changed files with 4898 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,499 @@
/* $FreeBSD$ */
/*-
* Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _XHCI_H_
#define _XHCI_H_
#define XHCI_MAX_DEVICES MIN(USB_MAX_DEVICES, 128)
#define XHCI_MAX_ENDPOINTS 32 /* hardcoded - do not change */
#define XHCI_MAX_SCRATCHPADS 32
#define XHCI_MAX_EVENTS (16 * 13)
#define XHCI_MAX_COMMANDS (16 * 1)
#define XHCI_MAX_RSEG 1
#define XHCI_MAX_TRANSFERS 4
#define XHCI_DEV_CTX_ADDR_ALIGN 64 /* bytes */
#define XHCI_DEV_CTX_ALIGN 64 /* bytes */
#define XHCI_INPUT_CTX_ALIGN 64 /* bytes */
#define XHCI_SLOT_CTX_ALIGN 32 /* bytes */
#define XHCI_ENDP_CTX_ALIGN 32 /* bytes */
#define XHCI_STREAM_CTX_ALIGN 16 /* bytes */
#define XHCI_TRANS_RING_SEG_ALIGN 16 /* bytes */
#define XHCI_CMD_RING_SEG_ALIGN 64 /* bytes */
#define XHCI_EVENT_RING_SEG_ALIGN 64 /* bytes */
#define XHCI_SCRATCH_BUF_ARRAY_ALIGN 64 /* bytes */
#define XHCI_SCRATCH_BUFFER_ALIGN USB_PAGE_SIZE
#define XHCI_TRB_ALIGN 16 /* bytes */
#define XHCI_TD_ALIGN 64 /* bytes */
#define XHCI_PAGE_SIZE 4096 /* bytes */
struct xhci_dev_ctx_addr {
volatile uint64_t qwBaaDevCtxAddr[USB_MAX_DEVICES + 1];
struct {
volatile uint64_t dummy;
} __aligned(64) padding;
volatile uint64_t qwSpBufPtr[XHCI_MAX_SCRATCHPADS];
};
#define XHCI_EPNO2EPID(x) \
((((x) & UE_DIR_IN) ? 1 : 0) | (2 * ((x) & UE_ADDR)))
struct xhci_slot_ctx {
volatile uint32_t dwSctx0;
#define XHCI_SCTX_0_ROUTE_SET(x) ((x) & 0xFFFFF)
#define XHCI_SCTX_0_ROUTE_GET(x) ((x) & 0xFFFFF)
#define XHCI_SCTX_0_SPEED_SET(x) (((x) & 0xF) << 20)
#define XHCI_SCTX_0_SPEED_GET(x) (((x) >> 20) & 0xF)
#define XHCI_SCTX_0_MTT_SET(x) (((x) & 0x1) << 25)
#define XHCI_SCTX_0_MTT_GET(x) (((x) >> 25) & 0x1)
#define XHCI_SCTX_0_HUB_SET(x) (((x) & 0x1) << 26)
#define XHCI_SCTX_0_HUB_GET(x) (((x) >> 26) & 0x1)
#define XHCI_SCTX_0_CTX_NUM_SET(x) (((x) & 0x1F) << 27)
#define XHCI_SCTX_0_CTX_NUM_GET(x) (((x) >> 27) & 0x1F)
volatile uint32_t dwSctx1;
#define XHCI_SCTX_1_MAX_EL_SET(x) ((x) & 0xFFFF)
#define XHCI_SCTX_1_MAX_EL_GET(x) ((x) & 0xFFFF)
#define XHCI_SCTX_1_RH_PORT_SET(x) (((x) & 0xFF) << 16)
#define XHCI_SCTX_1_RH_PORT_GET(x) (((x) >> 16) & 0xFF)
#define XHCI_SCTX_1_NUM_PORTS_SET(x) (((x) & 0xFF) << 24)
#define XHCI_SCTX_1_NUM_PORTS_GET(x) (((x) >> 24) & 0xFF)
volatile uint32_t dwSctx2;
#define XHCI_SCTX_2_TT_HUB_SID_SET(x) ((x) & 0xFF)
#define XHCI_SCTX_2_TT_HUB_SID_GET(x) ((x) & 0xFF)
#define XHCI_SCTX_2_TT_PORT_NUM_SET(x) (((x) & 0xFF) << 8)
#define XHCI_SCTX_2_TT_PORT_NUM_GET(x) (((x) >> 8) & 0xFF)
#define XHCI_SCTX_2_TT_THINK_TIME_SET(x) (((x) & 0x3) << 16)
#define XHCI_SCTX_2_TT_THINK_TIME_GET(x) (((x) >> 16) & 0x3)
#define XHCI_SCTX_2_IRQ_TARGET_SET(x) (((x) & 0x3FF) << 22)
#define XHCI_SCTX_2_IRQ_TARGET_GET(x) (((x) >> 22) & 0x3FF)
volatile uint32_t dwSctx3;
#define XHCI_SCTX_3_DEV_ADDR_SET(x) ((x) & 0xFF)
#define XHCI_SCTX_3_DEV_ADDR_GET(x) ((x) & 0xFF)
#define XHCI_SCTX_3_SLOT_STATE_SET(x) (((x) & 0x1F) << 27)
#define XHCI_SCTX_3_SLOT_STATE_GET(x) (((x) >> 27) & 0x1F)
volatile uint32_t dwSctx4;
volatile uint32_t dwSctx5;
volatile uint32_t dwSctx6;
volatile uint32_t dwSctx7;
};
struct xhci_endp_ctx {
volatile uint32_t dwEpCtx0;
#define XHCI_EPCTX_0_EPSTATE_SET(x) ((x) & 0x7)
#define XHCI_EPCTX_0_EPSTATE_GET(x) ((x) & 0x7)
#define XHCI_EPCTX_0_MULT_SET(x) (((x) & 0x3) << 8)
#define XHCI_EPCTX_0_MULT_GET(x) (((x) >> 8) & 0x3)
#define XHCI_EPCTX_0_MAXP_STREAMS_SET(x) (((x) & 0x1F) << 10)
#define XHCI_EPCTX_0_MAXP_STREAMS_GET(x) (((x) >> 10) & 0x1F)
#define XHCI_EPCTX_0_LSA_SET(x) (((x) & 0x1) << 15)
#define XHCI_EPCTX_0_LSA_GET(x) (((x) >> 15) & 0x1)
#define XHCI_EPCTX_0_IVAL_SET(x) (((x) & 0xFF) << 16)
#define XHCI_EPCTX_0_IVAL_GET(x) (((x) >> 16) & 0xFF)
volatile uint32_t dwEpCtx1;
#define XHCI_EPCTX_1_CERR_SET(x) (((x) & 0x3) << 1)
#define XHCI_EPCTX_1_CERR_GET(x) (((x) >> 1) & 0x3)
#define XHCI_EPCTX_1_EPTYPE_SET(x) (((x) & 0x7) << 3)
#define XHCI_EPCTX_1_EPTYPE_GET(x) (((x) >> 3) & 0x7)
#define XHCI_EPCTX_1_HID_SET(x) (((x) & 0x1) << 7)
#define XHCI_EPCTX_1_HID_GET(x) (((x) >> 7) & 0x1)
#define XHCI_EPCTX_1_MAXB_SET(x) (((x) & 0xFF) << 8)
#define XHCI_EPCTX_1_MAXB_GET(x) (((x) >> 8) & 0xFF)
#define XHCI_EPCTX_1_MAXP_SIZE_SET(x) (((x) & 0xFFFF) << 16)
#define XHCI_EPCTX_1_MAXP_SIZE_GET(x) (((x) >> 16) & 0xFFFF)
volatile uint64_t qwEpCtx2;
#define XHCI_EPCTX_2_DCS_SET(x) ((x) & 0x1)
#define XHCI_EPCTX_2_DCS_GET(x) ((x) & 0x1)
#define XHCI_EPCTX_2_TR_DQ_PTR_MASK 0xFFFFFFFFFFFFFFF0U
volatile uint32_t dwEpCtx4;
#define XHCI_EPCTX_4_AVG_TRB_LEN_SET(x) ((x) & 0xFFFF)
#define XHCI_EPCTX_4_AVG_TRB_LEN_GET(x) ((x) & 0xFFFF)
#define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(x) (((x) & 0xFFFF) << 16)
#define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_GET(x) (((x) >> 16) & 0xFFFF)
volatile uint32_t dwEpCtx5;
volatile uint32_t dwEpCtx6;
volatile uint32_t dwEpCtx7;
};
struct xhci_input_ctx {
#define XHCI_INCTX_NON_CTRL_MASK 0xFFFFFFFCU
volatile uint32_t dwInCtx0;
#define XHCI_INCTX_0_DROP_MASK(n) (1U << (n))
volatile uint32_t dwInCtx1;
#define XHCI_INCTX_1_ADD_MASK(n) (1U << (n))
volatile uint32_t dwInCtx2;
volatile uint32_t dwInCtx3;
volatile uint32_t dwInCtx4;
volatile uint32_t dwInCtx5;
volatile uint32_t dwInCtx6;
volatile uint32_t dwInCtx7;
};
struct xhci_input_dev_ctx {
struct xhci_input_ctx ctx_input;
struct xhci_slot_ctx ctx_slot;
struct xhci_endp_ctx ctx_ep[XHCI_MAX_ENDPOINTS - 1];
};
struct xhci_dev_ctx {
struct xhci_slot_ctx ctx_slot;
struct xhci_endp_ctx ctx_ep[XHCI_MAX_ENDPOINTS - 1];
} __aligned(XHCI_DEV_CTX_ALIGN);
struct xhci_stream_ctx {
volatile uint64_t qwSctx0;
#define XHCI_SCTX_0_DCS_GET(x) ((x) & 0x1)
#define XHCI_SCTX_0_DCS_SET(x) ((x) & 0x1)
#define XHCI_SCTX_0_SCT_SET(x) (((x) & 0x7) << 1)
#define XHCI_SCTX_0_SCT_GET(x) (((x) >> 1) & 0x7)
#define XHCI_SCTX_0_SCT_SEC_TR_RING 0x0
#define XHCI_SCTX_0_SCT_PRIM_TR_RING 0x1
#define XHCI_SCTX_0_SCT_PRIM_SSA_8 0x2
#define XHCI_SCTX_0_SCT_PRIM_SSA_16 0x3
#define XHCI_SCTX_0_SCT_PRIM_SSA_32 0x4
#define XHCI_SCTX_0_SCT_PRIM_SSA_64 0x5
#define XHCI_SCTX_0_SCT_PRIM_SSA_128 0x6
#define XHCI_SCTX_0_SCT_PRIM_SSA_256 0x7
#define XHCI_SCTX_0_TR_DQ_PTR_MASK 0xFFFFFFFFFFFFFFF0U
volatile uint32_t dwSctx2;
volatile uint32_t dwSctx3;
};
struct xhci_trb {
volatile uint64_t qwTrb0;
#define XHCI_TRB_0_WLENGTH_MASK (0xFFFFULL << 48)
volatile uint32_t dwTrb2;
#define XHCI_TRB_2_ERROR_GET(x) (((x) >> 24) & 0xFF)
#define XHCI_TRB_2_ERROR_SET(x) (((x) & 0xFF) << 24)
#define XHCI_TRB_2_TDSZ_GET(x) (((x) >> 17) & 0x1F)
#define XHCI_TRB_2_TDSZ_SET(x) (((x) & 0x1F) << 17)
#define XHCI_TRB_2_REM_GET(x) ((x) & 0xFFFFFF)
#define XHCI_TRB_2_REM_SET(x) ((x) & 0xFFFFFF)
#define XHCI_TRB_2_BYTES_GET(x) ((x) & 0x1FFFF)
#define XHCI_TRB_2_BYTES_SET(x) ((x) & 0x1FFFF)
#define XHCI_TRB_2_IRQ_GET(x) (((x) >> 22) & 0x3FF)
#define XHCI_TRB_2_IRQ_SET(x) (((x) & 0x3FF) << 22)
#define XHCI_TRB_2_STREAM_GET(x) (((x) >> 16) & 0xFFFF)
#define XHCI_TRB_2_STREAM_SET(x) (((x) & 0xFFFF) << 16)
volatile uint32_t dwTrb3;
#define XHCI_TRB_3_TYPE_GET(x) (((x) >> 10) & 0x3F)
#define XHCI_TRB_3_TYPE_SET(x) (((x) & 0x3F) << 10)
#define XHCI_TRB_3_CYCLE_BIT (1U << 0)
#define XHCI_TRB_3_TC_BIT (1U << 1) /* command ring only */
#define XHCI_TRB_3_ENT_BIT (1U << 1) /* transfer ring only */
#define XHCI_TRB_3_ISP_BIT (1U << 2)
#define XHCI_TRB_3_NSNOOP_BIT (1U << 3)
#define XHCI_TRB_3_CHAIN_BIT (1U << 4)
#define XHCI_TRB_3_IOC_BIT (1U << 5)
#define XHCI_TRB_3_IDT_BIT (1U << 6)
#define XHCI_TRB_3_TBC_GET(x) (((x) >> 7) & 3)
#define XHCI_TRB_3_TBC_SET(x) (((x) & 3) << 7)
#define XHCI_TRB_3_BEI_BIT (1U << 9)
#define XHCI_TRB_3_DCEP_BIT (1U << 9)
#define XHCI_TRB_3_PRSV_BIT (1U << 9)
#define XHCI_TRB_3_BSR_BIT (1U << 9)
#define XHCI_TRB_3_TRT_MASK (3U << 16)
#define XHCI_TRB_3_TRT_NONE (0U << 16)
#define XHCI_TRB_3_TRT_OUT (2U << 16)
#define XHCI_TRB_3_TRT_IN (3U << 16)
#define XHCI_TRB_3_DIR_IN (1U << 16)
#define XHCI_TRB_3_TLBPC_GET(x) (((x) >> 16) & 0xF)
#define XHCI_TRB_3_TLBPC_SET(x) (((x) & 0xF) << 16)
#define XHCI_TRB_3_EP_GET(x) (((x) >> 16) & 0x1F)
#define XHCI_TRB_3_EP_SET(x) (((x) & 0x1F) << 16)
#define XHCI_TRB_3_FRID_GET(x) (((x) >> 20) & 0x7FF)
#define XHCI_TRB_3_FRID_SET(x) (((x) & 0x7FF) << 20)
#define XHCI_TRB_3_ISO_SIA_BIT (1U << 31)
#define XHCI_TRB_3_SUSP_EP_BIT (1U << 23)
#define XHCI_TRB_3_SLOT_GET(x) (((x) >> 24) & 0xFF)
#define XHCI_TRB_3_SLOT_SET(x) (((x) & 0xFF) << 24)
/* Commands */
#define XHCI_TRB_TYPE_RESERVED 0x00
#define XHCI_TRB_TYPE_NORMAL 0x01
#define XHCI_TRB_TYPE_SETUP_STAGE 0x02
#define XHCI_TRB_TYPE_DATA_STAGE 0x03
#define XHCI_TRB_TYPE_STATUS_STAGE 0x04
#define XHCI_TRB_TYPE_ISOCH 0x05
#define XHCI_TRB_TYPE_LINK 0x06
#define XHCI_TRB_TYPE_EVENT_DATA 0x07
#define XHCI_TRB_TYPE_NOOP 0x08
#define XHCI_TRB_TYPE_ENABLE_SLOT 0x09
#define XHCI_TRB_TYPE_DISABLE_SLOT 0x0A
#define XHCI_TRB_TYPE_ADDRESS_DEVICE 0x0B
#define XHCI_TRB_TYPE_CONFIGURE_EP 0x0C
#define XHCI_TRB_TYPE_EVALUATE_CTX 0x0D
#define XHCI_TRB_TYPE_RESET_EP 0x0E
#define XHCI_TRB_TYPE_STOP_EP 0x0F
#define XHCI_TRB_TYPE_SET_TR_DEQUEUE 0x10
#define XHCI_TRB_TYPE_RESET_DEVICE 0x11
#define XHCI_TRB_TYPE_FORCE_EVENT 0x12
#define XHCI_TRB_TYPE_NEGOTIATE_BW 0x13
#define XHCI_TRB_TYPE_SET_LATENCY_TOL 0x14
#define XHCI_TRB_TYPE_GET_PORT_BW 0x15
#define XHCI_TRB_TYPE_FORCE_HEADER 0x16
#define XHCI_TRB_TYPE_NOOP_CMD 0x17
/* Events */
#define XHCI_TRB_EVENT_TRANSFER 0x20
#define XHCI_TRB_EVENT_CMD_COMPLETE 0x21
#define XHCI_TRB_EVENT_PORT_STS_CHANGE 0x22
#define XHCI_TRB_EVENT_BW_REQUEST 0x23
#define XHCI_TRB_EVENT_DOORBELL 0x24
#define XHCI_TRB_EVENT_HOST_CTRL 0x25
#define XHCI_TRB_EVENT_DEVICE_NOTIFY 0x26
#define XHCI_TRB_EVENT_MFINDEX_WRAP 0x27
/* Error codes */
#define XHCI_TRB_ERROR_INVALID 0x00
#define XHCI_TRB_ERROR_SUCCESS 0x01
#define XHCI_TRB_ERROR_DATA_BUF 0x02
#define XHCI_TRB_ERROR_BABBLE 0x03
#define XHCI_TRB_ERROR_XACT 0x04
#define XHCI_TRB_ERROR_TRB 0x05
#define XHCI_TRB_ERROR_STALL 0x06
#define XHCI_TRB_ERROR_RESOURCE 0x07
#define XHCI_TRB_ERROR_BANDWIDTH 0x08
#define XHCI_TRB_ERROR_NO_SLOTS 0x09
#define XHCI_TRB_ERROR_STREAM_TYPE 0x0A
#define XHCI_TRB_ERROR_SLOT_NOT_ON 0x0B
#define XHCI_TRB_ERROR_ENDP_NOT_ON 0x0C
#define XHCI_TRB_ERROR_SHORT_PKT 0x0D
#define XHCI_TRB_ERROR_RING_UNDERRUN 0x0E
#define XHCI_TRB_ERROR_RING_OVERRUN 0x0F
#define XHCI_TRB_ERROR_VF_RING_FULL 0x10
#define XHCI_TRB_ERROR_PARAMETER 0x11
#define XHCI_TRB_ERROR_BW_OVERRUN 0x12
#define XHCI_TRB_ERROR_CONTEXT_STATE 0x13
#define XHCI_TRB_ERROR_NO_PING_RESP 0x14
#define XHCI_TRB_ERROR_EV_RING_FULL 0x15
#define XHCI_TRB_ERROR_INCOMPAT_DEV 0x16
#define XHCI_TRB_ERROR_MISSED_SERVICE 0x17
#define XHCI_TRB_ERROR_CMD_RING_STOP 0x18
#define XHCI_TRB_ERROR_CMD_ABORTED 0x19
#define XHCI_TRB_ERROR_STOPPED 0x1A
#define XHCI_TRB_ERROR_LENGTH 0x1B
#define XHCI_TRB_ERROR_BAD_MELAT 0x1D
#define XHCI_TRB_ERROR_ISOC_OVERRUN 0x1F
#define XHCI_TRB_ERROR_EVENT_LOST 0x20
#define XHCI_TRB_ERROR_UNDEFINED 0x21
#define XHCI_TRB_ERROR_INVALID_SID 0x22
#define XHCI_TRB_ERROR_SEC_BW 0x23
#define XHCI_TRB_ERROR_SPLIT_XACT 0x24
} __aligned(4);
struct xhci_dev_endpoint_trbs {
struct xhci_trb trb[XHCI_MAX_ENDPOINTS][XHCI_MAX_TRANSFERS];
};
#define XHCI_TD_PAGE_NBUF 17 /* units, room enough for 64Kbytes */
#define XHCI_TD_PAGE_SIZE 4096 /* bytes */
#define XHCI_TD_PAYLOAD_MAX (XHCI_TD_PAGE_SIZE * (XHCI_TD_PAGE_NBUF - 1))
struct xhci_td {
struct xhci_trb td_trb[XHCI_TD_PAGE_NBUF + 1];
/*
* Extra information needed:
*/
uint64_t td_self;
struct xhci_td *next;
struct xhci_td *alt_next;
struct xhci_td *obj_next;
struct usb_page_cache *page_cache;
uint32_t len;
uint32_t remainder;
uint8_t ntrb;
uint8_t status;
} __aligned(XHCI_TRB_ALIGN);
struct xhci_command {
struct xhci_trb trb;
TAILQ_ENTRY(xhci_command) entry;
};
struct xhci_event_ring_seg {
volatile uint64_t qwEvrsTablePtr;
volatile uint32_t dwEvrsTableSize;
volatile uint32_t dwEvrsReserved;
};
struct xhci_hw_root {
struct xhci_event_ring_seg hwr_ring_seg[XHCI_MAX_RSEG];
struct {
volatile uint64_t dummy;
} __aligned(64) padding;
struct xhci_trb hwr_events[XHCI_MAX_EVENTS];
struct xhci_trb hwr_commands[XHCI_MAX_COMMANDS];
};
struct xhci_endpoint_ext {
struct xhci_trb *trb;
struct usb_xfer *xfer[XHCI_MAX_TRANSFERS - 1];
struct usb_page_cache *page_cache;
uint64_t physaddr;
uint8_t trb_used;
uint8_t trb_index;
uint8_t trb_halted;
uint8_t trb_running;
};
enum {
XHCI_ST_DISABLED,
XHCI_ST_ENABLED,
XHCI_ST_DEFAULT,
XHCI_ST_ADDRESSED,
XHCI_ST_CONFIGURED,
XHCI_ST_MAX
};
struct xhci_hw_dev {
struct usb_page_cache device_pc;
struct usb_page_cache input_pc;
struct usb_page_cache endpoint_pc;
struct usb_page device_pg;
struct usb_page input_pg;
struct usb_page endpoint_pg;
struct xhci_endpoint_ext endp[XHCI_MAX_ENDPOINTS];
uint8_t state;
uint8_t nports;
uint8_t tt;
uint8_t reserved;
};
struct xhci_hw_softc {
struct usb_page_cache root_pc;
struct usb_page_cache ctx_pc;
struct usb_page_cache scratch_pc[XHCI_MAX_SCRATCHPADS];
struct usb_page root_pg;
struct usb_page ctx_pg;
struct usb_page scratch_pg[XHCI_MAX_SCRATCHPADS];
struct xhci_hw_dev devs[XHCI_MAX_DEVICES + 1];
};
struct xhci_config_desc {
struct usb_config_descriptor confd;
struct usb_interface_descriptor ifcd;
struct usb_endpoint_descriptor endpd;
struct usb_endpoint_ss_comp_descriptor endpcd;
} __packed;
struct xhci_bos_desc {
struct usb_bos_descriptor bosd;
struct usb_devcap_usb2ext_descriptor usb2extd;
struct usb_devcap_ss_descriptor usbdcd;
struct usb_devcap_container_id_descriptor cidd;
} __packed;
union xhci_hub_desc {
struct usb_status stat;
struct usb_port_status ps;
struct usb_hub_ss_descriptor hubd;
uint8_t temp[128];
};
struct xhci_softc {
struct xhci_hw_softc sc_hw;
/* base device */
struct usb_bus sc_bus;
/* configure process */
struct usb_process sc_config_proc;
struct usb_bus_msg sc_config_msg[2];
union xhci_hub_desc sc_hub_desc;
struct cv sc_cmd_cv;
struct sx sc_cmd_sx;
struct usb_device *sc_devices[XHCI_MAX_DEVICES];
struct resource *sc_io_res;
struct resource *sc_irq_res;
void *sc_intr_hdl;
bus_size_t sc_io_size;
bus_space_tag_t sc_io_tag;
bus_space_handle_t sc_io_hdl;
/* last pending command address */
uint64_t sc_cmd_addr;
/* result of command */
uint32_t sc_cmd_result[2];
/* copy of cmd register */
uint32_t sc_cmd;
/* worst case exit latency */
uint32_t sc_exit_lat_max;
/* offset to operational registers */
uint32_t sc_oper_off;
/* offset to capability registers */
uint32_t sc_capa_off;
/* offset to runtime registers */
uint32_t sc_runt_off;
/* offset to doorbell registers */
uint32_t sc_door_off;
/* chip specific */
uint16_t sc_erst_max;
uint16_t sc_event_idx;
uint16_t sc_command_idx;
uint8_t sc_event_ccs;
uint8_t sc_command_ccs;
/* number of XHCI device slots */
uint8_t sc_noslot;
/* number of ports on root HUB */
uint8_t sc_noport;
/* number of scratch pages */
uint8_t sc_noscratch;
/* root HUB device configuration */
uint8_t sc_conf;
uint8_t sc_hub_idata[2];
/* vendor string for root HUB */
char sc_vendor[16];
};
#define XHCI_CMD_LOCK(sc) sx_xlock(&(sc)->sc_cmd_sx)
#define XHCI_CMD_UNLOCK(sc) sx_xunlock(&(sc)->sc_cmd_sx)
#define XHCI_CMD_ASSERT_LOCKED(sc) sx_assert(&(sc)->sc_cmd_sx, SA_LOCKED)
/* prototypes */
usb_error_t xhci_halt_controller(struct xhci_softc *);
usb_error_t xhci_init(struct xhci_softc *, device_t);
usb_error_t xhci_start_controller(struct xhci_softc *);
void xhci_interrupt(struct xhci_softc *);
void xhci_resume(struct xhci_softc *);
void xhci_shutdown(struct xhci_softc *);
void xhci_suspend(struct xhci_softc *);
void xhci_uninit(struct xhci_softc *);
#endif /* _XHCI_H_ */

View File

@ -0,0 +1,318 @@
/*-
* Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/stdint.h>
#include <sys/stddef.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/linker_set.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <sys/unistd.h>
#include <sys/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_busdma.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_util.h>
#include <dev/usb/usb_controller.h>
#include <dev/usb/usb_bus.h>
#include <dev/usb/usb_pci.h>
#include <dev/usb/controller/xhci.h>
#include <dev/usb/controller/xhcireg.h>
static device_probe_t xhci_pci_probe;
static device_attach_t xhci_pci_attach;
static device_detach_t xhci_pci_detach;
static device_suspend_t xhci_pci_suspend;
static device_resume_t xhci_pci_resume;
static device_shutdown_t xhci_pci_shutdown;
static void xhci_pci_takecontroller(device_t);
static device_method_t xhci_device_methods[] = {
/* device interface */
DEVMETHOD(device_probe, xhci_pci_probe),
DEVMETHOD(device_attach, xhci_pci_attach),
DEVMETHOD(device_detach, xhci_pci_detach),
DEVMETHOD(device_suspend, xhci_pci_suspend),
DEVMETHOD(device_resume, xhci_pci_resume),
DEVMETHOD(device_shutdown, xhci_pci_shutdown),
/* bus interface */
DEVMETHOD(bus_print_child, bus_generic_print_child),
{0, 0}
};
static driver_t xhci_driver = {
.name = "xhci",
.methods = xhci_device_methods,
.size = sizeof(struct xhci_softc),
};
static devclass_t xhci_devclass;
DRIVER_MODULE(xhci, pci, xhci_driver, xhci_devclass, 0, 0);
MODULE_DEPEND(xhci, usb, 1, 1, 1);
static int
xhci_pci_suspend(device_t self)
{
struct xhci_softc *sc = device_get_softc(self);
int err;
err = bus_generic_suspend(self);
if (err)
return (err);
xhci_suspend(sc);
return (0);
}
static int
xhci_pci_resume(device_t self)
{
struct xhci_softc *sc = device_get_softc(self);
xhci_pci_takecontroller(self);
xhci_resume(sc);
bus_generic_resume(self);
return (0);
}
static int
xhci_pci_shutdown(device_t self)
{
struct xhci_softc *sc = device_get_softc(self);
int err;
err = bus_generic_shutdown(self);
if (err)
return (err);
xhci_shutdown(sc);
return (0);
}
static const char *
xhci_pci_match(device_t self)
{
if ((pci_get_class(self) == PCIC_SERIALBUS)
&& (pci_get_subclass(self) == PCIS_SERIALBUS_USB)
&& (pci_get_progif(self) == PCI_INTERFACE_XHCI)) {
return ("XHCI (generic) USB 3.0 controller");
}
return (NULL); /* dunno */
}
static int
xhci_pci_probe(device_t self)
{
const char *desc = xhci_pci_match(self);
if (desc) {
device_set_desc(self, desc);
return (0);
} else {
return (ENXIO);
}
}
static int
xhci_pci_attach(device_t self)
{
struct xhci_softc *sc = device_get_softc(self);
int err;
int rid;
/* XXX check for 64-bit capability */
if (xhci_init(sc, self)) {
device_printf(self, "Could not initialize softc\n");
goto error;
}
pci_enable_busmaster(self);
rid = PCI_XHCI_CBMEM;
sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
if (!sc->sc_io_res) {
device_printf(self, "Could not map memory\n");
goto error;
}
sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
sc->sc_io_size = rman_get_size(sc->sc_io_res);
rid = 0;
sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
RF_SHAREABLE | RF_ACTIVE);
if (sc->sc_irq_res == NULL) {
device_printf(self, "Could not allocate IRQ\n");
goto error;
}
sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
if (sc->sc_bus.bdev == NULL) {
device_printf(self, "Could not add USB device\n");
goto error;
}
device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
sprintf(sc->sc_vendor, "0x%04x", pci_get_vendor(self));
#if (__FreeBSD_version >= 700031)
err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
NULL, (driver_intr_t *)xhci_interrupt, sc, &sc->sc_intr_hdl);
#else
err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
(driver_intr_t *)xhci_interrupt, sc, &sc->sc_intr_hdl);
#endif
if (err) {
device_printf(self, "Could not setup IRQ, err=%d\n", err);
sc->sc_intr_hdl = NULL;
goto error;
}
xhci_pci_takecontroller(self);
err = xhci_halt_controller(sc);
if (err == 0)
err = xhci_start_controller(sc);
if (err == 0)
err = device_probe_and_attach(sc->sc_bus.bdev);
if (err) {
device_printf(self, "XHCI halt/start/probe failed err=%d\n", err);
goto error;
}
return (0);
error:
xhci_pci_detach(self);
return (ENXIO);
}
static int
xhci_pci_detach(device_t self)
{
struct xhci_softc *sc = device_get_softc(self);
device_t bdev;
if (sc->sc_bus.bdev != NULL) {
bdev = sc->sc_bus.bdev;
device_detach(bdev);
device_delete_child(self, bdev);
}
/* during module unload there are lots of children leftover */
device_delete_all_children(self);
pci_disable_busmaster(self);
if (sc->sc_irq_res && sc->sc_intr_hdl) {
xhci_halt_controller(sc);
bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
sc->sc_intr_hdl = NULL;
}
if (sc->sc_irq_res) {
bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res);
sc->sc_irq_res = NULL;
}
if (sc->sc_io_res) {
bus_release_resource(self, SYS_RES_MEMORY, PCI_XHCI_CBMEM,
sc->sc_io_res);
sc->sc_io_res = NULL;
}
xhci_uninit(sc);
return (0);
}
static void
xhci_pci_takecontroller(device_t self)
{
struct xhci_softc *sc = device_get_softc(self);
uint32_t cparams;
uint32_t eecp;
uint32_t eec;
uint16_t to;
uint8_t bios_sem;
cparams = XREAD4(sc, capa, XHCI_HCSPARAMS0);
eec = -1;
/* Synchronise with the BIOS if it owns the controller. */
for (eecp = XHCI_HCS0_XECP(cparams) << 2; eecp != 0 && XHCI_XECP_NEXT(eec);
eecp += XHCI_XECP_NEXT(eec) << 2) {
eec = XREAD4(sc, capa, eecp);
if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY)
continue;
bios_sem = XREAD1(sc, capa, eecp +
XHCI_XECP_BIOS_SEM);
if (bios_sem == 0)
continue;
device_printf(sc->sc_bus.bdev, "waiting for BIOS "
"to give up control\n");
XWRITE1(sc, capa, eecp +
XHCI_XECP_OS_SEM, 1);
to = 500;
while (1) {
bios_sem = XREAD1(sc, capa, eecp +
XHCI_XECP_BIOS_SEM);
if (bios_sem == 0)
break;
if (--to == 0) {
device_printf(sc->sc_bus.bdev,
"timed out waiting for BIOS\n");
break;
}
usb_pause_mtx(NULL, hz / 100); /* wait 10ms */
}
}
}

View File

@ -0,0 +1,219 @@
/* $FreeBSD$ */
/*-
* Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _XHCIREG_H_
#define _XHCIREG_H_
/* XHCI PCI config registers */
#define PCI_XHCI_CBMEM 0x10 /* configuration base MEM */
#define PCI_XHCI_USBREV 0x60 /* RO USB protocol revision */
#define PCI_USB_REV_3_0 0x30 /* USB 3.0 */
#define PCI_XHCI_FLADJ 0x61 /* RW frame length adjust */
#define PCI_INTERFACE_XHCI 0x30 /* USB 3.0 - XHCI */
/* XHCI capability registers */
#define XHCI_CAPLENGTH 0x00 /* RO capability */
#define XHCI_RESERVED 0x01 /* Reserved */
#define XHCI_HCIVERSION 0x02 /* RO Interface version number */
#define XHCI_HCIVERSION_0_9 0x0090 /* xHCI version 0.9 */
#define XHCI_HCIVERSION_1_0 0x0100 /* xHCI version 1.0 */
#define XHCI_HCSPARAMS1 0x04 /* RO structual parameters 1 */
#define XHCI_HCS1_DEVSLOT_MAX(x)((x) & 0xFF)
#define XHCI_HCS1_IRQ_MAX(x) (((x) >> 8) & 0x3FF)
#define XHCI_HCS1_N_PORTS(x) (((x) >> 24) & 0xFF)
#define XHCI_HCSPARAMS2 0x08 /* RO structual parameters 2 */
#define XHCI_HCS2_IST(x) ((x) & 0xF)
#define XHCI_HCS2_ERST_MAX(x) (((x) >> 4) & 0xF)
#define XHCI_HCS2_SPR(x) (((x) >> 24) & 0x1)
#define XHCI_HCS2_SPB_MAX(x) (((x) >> 27) & 0x7F)
#define XHCI_HCSPARAMS3 0x0C /* RO structual parameters 3 */
#define XHCI_HCS3_U1_DEL(x) ((x) & 0xFF)
#define XHCI_HCS3_U2_DEL(x) (((x) >> 16) & 0xFFFF)
#define XHCI_HCSPARAMS0 0x10 /* RO capability parameters */
#define XHCI_HCS0_AC64(x) ((x) & 0x1) /* 64-bit capable */
#define XHCI_HCS0_BNC(x) (((x) >> 1) & 0x1) /* BW negotiation */
#define XHCI_HCS0_CSZ(x) (((x) >> 2) & 0x1) /* context size */
#define XHCI_HCS0_PPC(x) (((x) >> 3) & 0x1) /* port power control */
#define XHCI_HCS0_PIND(x) (((x) >> 4) & 0x1) /* port indicators */
#define XHCI_HCS0_LHRC(x) (((x) >> 5) & 0x1) /* light HC reset */
#define XHCI_HCS0_LTC(x) (((x) >> 6) & 0x1) /* latency tolerance msg */
#define XHCI_HCS0_NSS(x) (((x) >> 7) & 0x1) /* no secondary sid */
#define XHCI_HCS0_PSA_SZ_MAX(x) (((x) >> 12) & 0xF) /* max pri. stream array size */
#define XHCI_HCS0_XECP(x) (((x) >> 16) & 0xFFFF) /* extended capabilities pointer */
#define XHCI_DBOFF 0x14 /* RO doorbell offset */
#define XHCI_RTSOFF 0x18 /* RO runtime register space offset */
/* XHCI operational registers. Offset given by XHCI_CAPLENGTH register */
#define XHCI_USBCMD 0x00 /* XHCI command */
#define XHCI_CMD_RS 0x00000001 /* RW Run/Stop */
#define XHCI_CMD_HCRST 0x00000002 /* RW Host Controller Reset */
#define XHCI_CMD_INTE 0x00000004 /* RW Interrupter Enable */
#define XHCI_CMD_HSEE 0x00000008 /* RW Host System Error Enable */
#define XHCI_CMD_LHCRST 0x00000080 /* RO/RW Light Host Controller Reset */
#define XHCI_CMD_CSS 0x00000100 /* RW Controller Save State */
#define XHCI_CMD_CRS 0x00000200 /* RW Controller Restore State */
#define XHCI_CMD_EWE 0x00000400 /* RW Enable Wrap Event */
#define XHCI_CMD_EU3S 0x00000800 /* RW Enable U3 MFINDEX Stop */
#define XHCI_USBSTS 0x04 /* XHCI status */
#define XHCI_STS_HCH 0x00000001 /* RO - Host Controller Halted */
#define XHCI_STS_HSE 0x00000004 /* RW - Host System Error */
#define XHCI_STS_EINT 0x00000008 /* RW - Event Interrupt */
#define XHCI_STS_PCD 0x00000010 /* RW - Port Change Detect */
#define XHCI_STS_SSS 0x00000100 /* RO - Save State Status */
#define XHCI_STS_RSS 0x00000200 /* RO - Restore State Status */
#define XHCI_STS_SRE 0x00000400 /* RW - Save/Restore Error */
#define XHCI_STS_CNR 0x00000800 /* RO - Controller Not Ready */
#define XHCI_STS_HCE 0x00001000 /* RO - Host Controller Error */
#define XHCI_PAGESIZE 0x08 /* XHCI page size mask */
#define XHCI_PAGESIZE_4K 0x00000001 /* 4K Page Size */
#define XHCI_PAGESIZE_8K 0x00000002 /* 8K Page Size */
#define XHCI_PAGESIZE_16K 0x00000004 /* 16K Page Size */
#define XHCI_PAGESIZE_32K 0x00000008 /* 32K Page Size */
#define XHCI_PAGESIZE_64K 0x00000010 /* 64K Page Size */
#define XHCI_DNCTRL 0x14 /* XHCI device notification control */
#define XHCI_DNCTRL_MASK(n) (1U << (n))
#define XHCI_CRCR_LO 0x18 /* XHCI command ring control */
#define XHCI_CRCR_LO_RCS 0x00000001 /* RW - consumer cycle state */
#define XHCI_CRCR_LO_CS 0x00000002 /* RW - command stop */
#define XHCI_CRCR_LO_CA 0x00000004 /* RW - command abort */
#define XHCI_CRCR_LO_CRR 0x00000008 /* RW - command ring running */
#define XHCI_CRCR_LO_MASK 0x0000000F
#define XHCI_CRCR_HI 0x1C /* XHCI command ring control */
#define XHCI_DCBAAP_LO 0x30 /* XHCI dev context BA pointer */
#define XHCI_DCBAAP_HI 0x34 /* XHCI dev context BA pointer */
#define XHCI_CONFIG 0x38
#define XHCI_CONFIG_SLOTS_MASK 0x000000FF /* RW - number of device slots enabled */
/* XHCI port status registers */
#define XHCI_PORTSC(n) (0x3F0 + (0x10 * (n))) /* XHCI port status */
#define XHCI_PS_CCS 0x00000001 /* RO - current connect status */
#define XHCI_PS_PED 0x00000002 /* RW - port enabled / disabled */
#define XHCI_PS_OCA 0x00000008 /* RO - over current active */
#define XHCI_PS_PR 0x00000010 /* RW - port reset */
#define XHCI_PS_PLS_GET(x) (((x) >> 5) & 0xF) /* RW - port link state */
#define XHCI_PS_PLS_SET(x) (((x) & 0xF) << 5) /* RW - port link state */
#define XHCI_PS_PP 0x00000100 /* RW - port power */
#define XHCI_PS_SPEED_GET(x) (((x) >> 10) & 0xF) /* RO - port speed */
#define XHCI_PS_PIC_GET(x) (((x) >> 14) & 0x3) /* RW - port indicator */
#define XHCI_PS_PIC_SET(x) (((x) & 0x3) << 14) /* RW - port indicator */
#define XHCI_PS_LWS 0x00010000 /* RW - port link state write strobe */
#define XHCI_PS_CSC 0x00020000 /* RW - connect status change */
#define XHCI_PS_PEC 0x00040000 /* RW - port enable/disable change */
#define XHCI_PS_WRC 0x00080000 /* RW - warm port reset change */
#define XHCI_PS_OCC 0x00100000 /* RW - over-current change */
#define XHCI_PS_PRC 0x00200000 /* RW - port reset change */
#define XHCI_PS_PLC 0x00400000 /* RW - port link state change */
#define XHCI_PS_CEC 0x00800000 /* RW - config error change */
#define XHCI_PS_CAS 0x01000000 /* RO - cold attach status */
#define XHCI_PS_WCE 0x02000000 /* RW - wake on connect enable */
#define XHCI_PS_WDE 0x04000000 /* RW - wake on disconnect enable */
#define XHCI_PS_WOE 0x08000000 /* RW - wake on over-current enable */
#define XHCI_PS_DR 0x40000000 /* RO - device removable */
#define XHCI_PS_WPR 0x80000000U /* RW - warm port reset */
#define XHCI_PS_CLEAR 0x80FF00F7U /* command bits */
#define XHCI_PORTPMSC(n) (0x3F4 + (0x10 * (n))) /* XHCI status and control */
#define XHCI_PM3_U1TO_GET(x) (((x) >> 0) & 0xFF) /* RW - U1 timeout */
#define XHCI_PM3_U1TO_SET(x) (((x) & 0xFF) << 0) /* RW - U1 timeout */
#define XHCI_PM3_U2TO_GET(x) (((x) >> 8) & 0xFF) /* RW - U2 timeout */
#define XHCI_PM3_U2TO_SET(x) (((x) & 0xFF) << 8) /* RW - U2 timeout */
#define XHCI_PM3_FLA 0x00010000 /* RW - Force Link PM Accept */
#define XHCI_PM2_L1S_GET(x) (((x) >> 0) & 0x7) /* RO - L1 status */
#define XHCI_PM2_RWE 0x00000008 /* RW - remote wakup enable */
#define XHCI_PM2_HIRD_GET(x) (((x) >> 4) & 0xF) /* RW - host initiated resume duration */
#define XHCI_PM2_HIRD_SET(x) (((x) & 0xF) << 4) /* RW - host initiated resume duration */
#define XHCI_PM2_L1SLOT_GET(x) (((x) >> 8) & 0xFF) /* RW - L1 device slot */
#define XHCI_PM2_L1SLOT_SET(x) (((x) & 0xFF) << 8) /* RW - L1 device slot */
#define XHCI_PM2_HLE 0x00010000 /* RW - hardware LPM enable */
#define XHCI_PORTLI(n) (0x3F8 + (0x10 * (n))) /* XHCI port link info */
#define XHCI_PLI3_ERR_GET(x) (((x) >> 0) & 0xFFFF) /* RO - port link errors */
#define XHCI_PORTRSV(n) (0x3FC + (0x10 * (n))) /* XHCI port reserved */
/* XHCI runtime registers. Offset given by XHCI_CAPLENGTH + XHCI_RTSOFF registers */
#define XHCI_MFINDEX 0x0000 /* RO - microframe index */
#define XHCI_MFINDEX_GET(x) ((x) & 0x3FFF)
#define XHCI_IMAN(n) (0x0020 + (0x20 * (n))) /* XHCI interrupt management */
#define XHCI_IMAN_INTR_PEND 0x00000001 /* RW - interrupt pending */
#define XHCI_IMAN_INTR_ENA 0x00000002 /* RW - interrupt enable */
#define XHCI_IMOD(n) (0x0024 + (0x20 * (n))) /* XHCI interrupt moderation */
#define XHCI_IMOD_IVAL_GET(x) (((x) >> 0) & 0xFFFF) /* 250ns unit */
#define XHCI_IMOD_IVAL_SET(x) (((x) & 0xFFFF) << 0) /* 250ns unit */
#define XHCI_IMOD_ICNT_GET(x) (((x) >> 16) & 0xFFFF) /* 250ns unit */
#define XHCI_IMOD_ICNT_SET(x) (((x) & 0xFFFF) << 16) /* 250ns unit */
#define XHCI_IMOD_DEFAULT 0x000001F4U /* 8000 IRQ/second */
#define XHCI_ERSTSZ(n) (0x0028 + (0x20 * (n))) /* XHCI event ring segment table size */
#define XHCI_ERSTS_GET(x) ((x) & 0xFFFF)
#define XHCI_ERSTS_SET(x) ((x) & 0xFFFF)
#define XHCI_ERSTBA_LO(n) (0x0030 + (0x20 * (n))) /* XHCI event ring segment table BA */
#define XHCI_ERSTBA_HI(n) (0x0034 + (0x20 * (n))) /* XHCI event ring segment table BA */
#define XHCI_ERDP_LO(n) (0x0038 + (0x20 * (n))) /* XHCI event ring dequeue pointer */
#define XHCI_ERDP_LO_SINDEX(x) ((x) & 0x7) /* RO - dequeue segment index */
#define XHCI_ERDP_LO_BUSY 0x00000008 /* RW - event handler busy */
#define XHCI_ERDP_HI(n) (0x003C + (0x20 * (n))) /* XHCI event ring dequeue pointer */
/* XHCI doorbell registers. Offset given by XHCI_CAPLENGTH + XHCI_DBOFF registers */
#define XHCI_DOORBELL(n) (0x0000 + (4 * (n)))
#define XHCI_DB_TARGET_GET(x) ((x) & 0xFF) /* RW - doorbell target */
#define XHCI_DB_TARGET_SET(x) ((x) & 0xFF) /* RW - doorbell target */
#define XHCI_DB_SID_GET(x) (((x) >> 16) & 0xFFFF) /* RW - doorbell stream ID */
#define XHCI_DB_SID_SET(x) (((x) & 0xFFFF) << 16) /* RW - doorbell stream ID */
/* XHCI legacy support */
#define XHCI_XECP_ID(x) ((x) & 0xFF)
#define XHCI_XECP_NEXT(x) (((x) >> 8) & 0xFF)
#define XHCI_XECP_BIOS_SEM 0x0002
#define XHCI_XECP_OS_SEM 0x0003
/* XHCI capability ID's */
#define XHCI_ID_USB_LEGACY 0x0001
#define XHCI_ID_PROTOCOLS 0x0002
#define XHCI_ID_POWER_MGMT 0x0003
#define XHCI_ID_VIRTUALIZATION 0x0004
#define XHCI_ID_MSG_IRQ 0x0005
#define XHCI_ID_USB_LOCAL_MEM 0x0006
/* XHCI register R/W wrappers */
#define XREAD1(sc, what, a) \
bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \
(a) + (sc)->sc_##what##_off)
#define XREAD2(sc, what, a) \
bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, \
(a) + (sc)->sc_##what##_off)
#define XREAD4(sc, what, a) \
bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, \
(a) + (sc)->sc_##what##_off)
#define XWRITE1(sc, what, a, x) \
bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \
(a) + (sc)->sc_##what##_off, (x))
#define XWRITE2(sc, what, a, x) \
bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, \
(a) + (sc)->sc_##what##_off, (x))
#define XWRITE4(sc, what, a, x) \
bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, \
(a) + (sc)->sc_##what##_off, (x))
#endif /* _XHCIREG_H_ */