diff options
| author | zhugengyu <[email protected]> | 2023-04-21 22:56:38 +0800 |
|---|---|---|
| committer | zhugengyu <[email protected]> | 2023-04-21 22:56:46 +0800 |
| commit | ef126eafa66dcb0a0ebb7908be68fdebcc2b651a (patch) | |
| tree | c1664c0e24ccea37750286f17ec060a53fa0c753 | |
| parent | 53fe0f2cd510a4f1a21da5a1a241cedce89fa4c5 (diff) | |
update xhci driver
| -rw-r--r-- | port/xhci/usb_hc_xhci.c | 1292 | ||||
| -rw-r--r-- | port/xhci/usb_hc_xhci.h | 177 | ||||
| -rw-r--r-- | port/xhci/xhci.c | 2563 | ||||
| -rw-r--r-- | port/xhci/xhci.h | 940 | ||||
| -rw-r--r-- | port/xhci/xhci_dbg.c | 353 | ||||
| -rw-r--r-- | port/xhci/xhci_reg.h | 784 |
6 files changed, 4457 insertions, 1652 deletions
diff --git a/port/xhci/usb_hc_xhci.c b/port/xhci/usb_hc_xhci.c index 58e5177d..43c62f54 100644 --- a/port/xhci/usb_hc_xhci.c +++ b/port/xhci/usb_hc_xhci.c @@ -14,749 +14,79 @@ * FilePath: usb_hc_xhci.c * Date: 2022-09-19 17:24:36 * LastEditTime: 2022-09-19 17:24:36 - * Description: This files is for xhci function implementation + * Description: This file is for xhci function implementation. * * Modify History: * Ver Who Date Changes * ----- ------ -------- -------------------------------------- * 1.0 zhugengyu 2022/9/19 init commit + * 1.1 zhugengyu 2022/10/9 add dumpers and fix command abort + * 2.0 zhugengyu 2023/3/29 support usb3.0 device attached at roothub */ /***************************** Include Files *********************************/ -#include "usbh_core.h" #include "usbh_hub.h" -#include "xhci_reg.h" -#include "usb_hc_xhci.h" + +#include "xhci.h" /************************** Constant Definitions *****************************/ /************************** Variable Definitions *****************************/ -/* input is xhci speed */ -static const char *speed_name[16] = { - [ 0 ] = " - ", - [ 1 ] = "Full", - [ 2 ] = "Low", - [ 3 ] = "High", - [ 4 ] = "Super", -}; - -static const int speed_from_xhci[16] = { - [ 0 ] = -1, - [ 1 ] = USB_SPEED_FULL, - [ 2 ] = USB_SPEED_LOW, - [ 3 ] = USB_SPEED_HIGH, - [ 4 ] = USB_SPEED_SUPER, - [ 5 ... 15 ] = -1, -}; - -static const int speed_to_xhci[] = { - [ USB_SPEED_FULL ] = 1, - [ USB_SPEED_LOW ] = 2, - [ USB_SPEED_HIGH ] = 3, - [ USB_SPEED_SUPER ] = 4, -}; -static volatile struct xhci_pipe *cur_cmd_pipe = NULL; /* pass current command pipe to interrupt */ /***************** Macros (Inline Functions) Definitions *********************/ /************************** Function Prototypes ******************************/ -void usb_hc_dcache_invalidate(void *addr, unsigned long len); /*****************************************************************************/ -static void xhci_dump_slot_ctx(const struct xhci_slotctx *const sc) -{ - USB_LOG_DBG("ctx[0]=0x%x\n", sc->ctx[0]); - USB_LOG_DBG(" route=0x%x\n", - XHCI_SLOTCTX_0_ROUTE_GET(sc->ctx[0])); - USB_LOG_DBG("ctx[1]=0x%x\n", sc->ctx[1]); - USB_LOG_DBG("ctx[2]=0x%x\n", sc->ctx[2]); - USB_LOG_DBG(" tt-port=0x%x, tt-hub-slot=0x%x\n", - XHCI_SLOTCTX_2_HUB_PORT_GET(sc->ctx[2]), - XHCI_SLOTCTX_2_HUB_SLOT_GET(sc->ctx[2])); - USB_LOG_DBG("ctx[3]=0x%x\n", sc->ctx[3]); -} - -static void xhci_dump_ep_ctx(const struct xhci_epctx *const ec) -{ - USB_LOG_DBG("ctx[0]=0x%x\n", ec->ctx[0]); - USB_LOG_DBG("ctx[1]=0x%x\n", ec->ctx[1]); - USB_LOG_DBG(" ep_type=%d, mps=%d\n", - XHCI_EPCTX_1_EPTYPE_GET(ec->ctx[1]), - XHCI_EPCTX_1_MPS_GET(ec->ctx[1])); - USB_LOG_DBG("deq_low=0x%x\n", ec->deq_low); - USB_LOG_DBG("deq_high=0x%x\n", ec->deq_high); - USB_LOG_DBG("length=0x%x\n", ec->length); -} - -static void xhci_dump_input_ctx(struct xhci_s *xhci, const struct xhci_inctx *const inctx) -{ - USB_LOG_DBG("===input ctx====\n"); - USB_LOG_DBG("add=0x%x\n", inctx->add); - USB_LOG_DBG("del=0x%x\n", inctx->del); - if (inctx->add & (0x1)) { - USB_LOG_DBG("===slot ctx====\n"); - const struct xhci_slotctx *const sc = (void*)&inctx[1 << xhci->context64]; - xhci_dump_slot_ctx(sc); - } - - for (int epid = 1; epid <= 31; epid++){ - if (inctx->add & (0x1 << (epid))) - { - USB_LOG_DBG("===ep-%d ctx====\n", epid); - const struct xhci_epctx *const ec = (void*)&inctx[(epid+1) << xhci->context64]; - xhci_dump_ep_ctx(ec); - } - } - USB_LOG_DBG("\n"); -} - -static void xhci_dump_pipe(const struct xhci_pipe *const ppipe) -{ - USB_LOG_DBG("pipe@%p\n", ppipe); - if (NULL == ppipe) - return; - - USB_LOG_DBG("epaddr=%d\n", ppipe->epaddr); - USB_LOG_DBG("speed=%d\n", ppipe->speed); - USB_LOG_DBG("interval=%d\n", ppipe->interval); - USB_LOG_DBG("maxpacket=%d\n", ppipe->maxpacket); - USB_LOG_DBG("eptype=%d\n", ppipe->eptype); - USB_LOG_DBG("slotid=%d\n", ppipe->slotid); - USB_LOG_DBG("epid=%d\n", ppipe->epid); - USB_LOG_DBG("timeout=%d\n", ppipe->timeout); - USB_LOG_DBG("waiter=%d\n", ppipe->waiter); - USB_LOG_DBG("waitsem=%p\n", ppipe->waitsem); - USB_LOG_DBG("hport=%p\n", ppipe->hport); - USB_LOG_DBG("urb=%p\n", ppipe->urb); -} - -/* Wait until bit = value or timeout */ -static int wait_bit(unsigned long reg_off, uint32_t mask, uint32_t value, uint32_t timeout) -{ - uint32_t delay = 0U; /* calc wait end time */ - - while ((readl(reg_off) & mask) != value) { - if (++delay > timeout) { /* check if timeout */ - USB_LOG_ERR("wait timeout !!!\n"); - return -1; - } - usb_osal_msleep(1U); - } - return 0; -} - -/* Check if device attached to port */ -static void xhci_print_port_state(const char *prefix, uint32_t port, uint32_t portsc) -{ - uint32_t pls = XHCI_REG_OP_PORTS_PORTSC_PLS_GET(portsc); - uint32_t speed = XHCI_REG_OP_PORTS_PORTSC_PORT_SPEED_GET(portsc); - - USB_LOG_DBG("%s port #%d: 0x%08x,%s%s pls %d, speed %d [%s]\n", - prefix, port + 1, portsc, - (portsc & XHCI_REG_OP_PORTS_PORTSC_PP) ? " powered," : "", - (portsc & XHCI_REG_OP_PORTS_PORTSC_PED) ? " enabled," : "", - pls, speed, speed_name[speed]); -} - -static inline uint32_t xhci_readl_port(struct xhci_s *xhci, uint32_t port, uint32_t offset) -{ - /* Operational Base + (400h + (10h * (n–1))) */ - return readl(xhci->pr + port * XHCI_REG_OP_PORTS_SIZE + offset); -} - -static inline void xhci_writel_port(struct xhci_s *xhci, uint32_t port, uint32_t offset, uint32_t val) -{ - /* Operational Base + (400h + (10h * (n–1))) */ - return writel(xhci->pr + port * XHCI_REG_OP_PORTS_SIZE + offset, val); -} - -static void xhci_setup_mmio(struct xhci_s *xhci, unsigned long base_addr) -{ - xhci->base = base_addr; - xhci->caps = xhci->base; - - /* add to register base to find the beginning of the Operational Register Space */ - xhci->op = xhci->base + readb(xhci->caps + XHCI_REG_CAP_CAPLENGTH); - xhci->db = xhci->base + XHCI_REG_CAP_DBOFF_GET(readl(xhci->caps + XHCI_REG_CAP_DBOFF)); - xhci->ir = xhci->base + XHCI_REG_CAP_RTSOFF_GET(readl(xhci->caps + XHCI_REG_CAP_RTSOFF)) + - XHCI_REG_RT_IR0; - xhci->pr = xhci->op + XHCI_REG_OP_PORTS_BASE; - - /* cache static information of CAP_HCSPARAMS */ - xhci->hcs[0] = readl(xhci->caps + XHCI_REG_CAP_HCS1); - xhci->hcs[1] = readl(xhci->caps + XHCI_REG_CAP_HCS2); - xhci->hcs[2] = readl(xhci->caps + XHCI_REG_CAP_HCS3); - xhci->hcc = readl(xhci->caps + XHCI_REG_CAP_HCC); - - /* avoid access XHCI_REG_CAP_HCIVERSION = 0x2, unaligned memory */ - xhci->version = ((readl(xhci->caps + XHCI_REG_CAP_CAPLENGTH) >> 16) & 0xffff); - - xhci->ports = XHCI_REG_CAP_HCS1_MAX_PORTS_GET(xhci->hcs[0]); /* bit[31:24] max ports */ - xhci->slots = XHCI_REG_CAP_HCS1_MAX_SLOTS_GET(xhci->hcs[0]); /* bit[7:0] max device slots */ - - /* For example, using the offset of Base is 1000h and the xECP value of 0068h, we can calculated - the following effective address of the first extended capability: - 1000h + (0068h << 2) -> 1000h + 01A0h -> 11A0h */ - xhci->xcap = XHCI_REG_CAP_HCC_XECP_GET(xhci->hcc) << 2; /* bit[31:16] xHCI extended cap pointer */ - xhci->context64 = (XHCI_REG_CAP_HCC_CSZ & xhci->hcc) ? true : false; - - USB_LOG_INFO(" hc version: 0x%x\n", xhci->version); - USB_LOG_INFO(" mmio base: 0x%x\n", xhci->base); - USB_LOG_INFO(" oper base: 0x%x\n", xhci->op); - USB_LOG_INFO(" doorbell base: 0x%x\n", xhci->db); - USB_LOG_INFO(" runtime base: 0x%x\n", xhci->ir); - USB_LOG_INFO(" port base: 0x%x\n", xhci->pr); - USB_LOG_INFO(" xcap base: 0x%x\n", xhci->xcap); - - USB_LOG_INFO(" slot num: 0x%x\n", xhci->slots); - USB_LOG_INFO(" port num: 0x%x\n", xhci->ports); - USB_LOG_INFO(" context: %d bit\n", xhci->context64 ? 64 : 32); -} - - -/* Reset device on port */ -static int xhci_hub_reset(struct xhci_s *xhci, uint32_t port) -{ - uint32_t portsc = xhci_readl_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC); - if (!(portsc & XHCI_REG_OP_PORTS_PORTSC_CCS)) /* double check if connected */ - /* Device no longer connected?! */ - return -1; - - switch (XHCI_REG_OP_PORTS_PORTSC_PLS_GET(portsc)) { - case PLS_U0: - /* A USB3 port - controller automatically performs reset */ - break; - case PLS_POLLING: - /* A USB2 port - perform device reset */ - xhci_print_port_state(__func__, port, portsc); - xhci_writel_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC, - portsc | XHCI_REG_OP_PORTS_PORTSC_PR); /* reset port */ - break; - default: - return -1; - } - - // Wait for device to complete reset and be enabled - uint32_t end = 1000U, start = 0U; - for (;;) { - portsc = xhci_readl_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC); - if (!(portsc & XHCI_REG_OP_PORTS_PORTSC_CCS)) - /* Device disconnected during reset */ - return -1; - - if (portsc & XHCI_REG_OP_PORTS_PORTSC_PED) /* check if port enabled */ - /* Reset complete */ - break; - - if (++start > end) { - USB_LOG_ERR("wait timeout, portsc=0x%x!!!\n", portsc); - return -1; - } - - usb_osal_msleep(1); - } - - int rc = speed_from_xhci[XHCI_REG_OP_PORTS_PORTSC_PORT_SPEED_GET(portsc)]; - xhci_print_port_state("XHCI", port, portsc); - return rc; -} - -/* Add a TRB to the given ring */ -static void xhci_trb_fill(struct xhci_ring *ring, void *data, uint32_t xferlen, uint32_t flags) -{ - struct xhci_trb *dst = &ring->ring[ring->nidx]; - if (flags & TRB_TR_IDT) { - memcpy(&dst->ptr_low, data, xferlen); - } else { - dst->ptr_low = (uint32_t)(unsigned long)data; -#ifdef XHCI_AARCH64 - dst->ptr_high = (uint32_t)((uint64_t)data >> 32U); -#else - dst->ptr_high = 0U; -#endif - } - dst->status = xferlen; - /* cycle bit */ - dst->control = flags | (ring->cs ? TRB_C : 0); -} - -/* Queue a TRB onto a ring, wrapping ring as needed */ -static void xhci_trb_queue(struct xhci_ring *ring, void *data, uint32_t xferlen, uint32_t flags) -{ - if (ring->nidx >= ARRAY_SIZE(ring->ring) - 1) { /* if it is the last trb in the list */ - /* indicate the end of ring, bit[1], toggle cycle = 1, xHc shall toggle cycle bit interpretation */ - xhci_trb_fill(ring, ring->ring, 0, (TR_LINK << 10) | TRB_LK_TC); - ring->nidx = 0; /* adjust dequeue index to 0 */ - ring->cs ^= 1; /* toggle cycle interpretation of sw */ - } - - xhci_trb_fill(ring, data, xferlen, flags); - ring->nidx++; /* ahead dequeue index */ -} - -/* Signal the hardware to process events on a TRB ring */ -static void xhci_doorbell(struct xhci_s *xhci, uint32_t slotid, uint32_t value) -{ - writel(xhci->db + slotid * XHCI_REG_DB_SIZE, value); /* bit[7:0] db target, is ep_id */ -} - -/* Wait for a ring to empty (all TRBs processed by hardware) */ -static int xhci_event_wait(struct xhci_s *xhci, - struct xhci_pipe *pipe, - struct xhci_ring *ring) -{ - int ret = CC_SUCCESS; - - if (pipe->timeout > 0){ - ret = usb_osal_sem_take(pipe->waitsem, pipe->timeout); - if (ret < 0) { - ret = CC_TIMEOUT; - } else { - ret = TRB_CC_GET(ring->evt.status); /* bit[31:24] completion code */ - } - } - - return ret; -} - -/* Submit a command to the xhci controller ring */ -static int xhci_cmd_submit(struct xhci_s *xhci, struct xhci_inctx *inctx, - struct xhci_pipe *pipe, uint32_t flags) -{ - if (inctx) { - struct xhci_slotctx *slot = (void*)&inctx[1 << xhci->context64]; - uint32_t port = XHCI_SLOTCTX_1_ROOT_PORT_GET(slot->ctx[1]) - 1; - uint32_t portsc = xhci_readl_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC); - if (!(portsc & XHCI_REG_OP_PORTS_PORTSC_CCS)) { - /* Device no longer connected?! */ - xhci_print_port_state(__func__, port, portsc); - return CC_DISCONNECTED; - } - } - - usb_osal_mutex_take(xhci->cmds->lock); /* handle command one by one */ - - pipe->timeout = 5000; - pipe->waiter = true; - cur_cmd_pipe = pipe; - - xhci_trb_queue(xhci->cmds, inctx, 0, flags); - - /* pass command trb to hardware */ - DSB(); - - xhci_doorbell(xhci, 0, 0); /* 0 = db host controller, 0 = db targe hc command */ - int rc = xhci_event_wait(xhci, pipe, xhci->cmds); - - usb_osal_mutex_give(xhci->cmds->lock); - - return rc; -} - -static int xhci_cmd_nop(struct xhci_s *xhci, struct xhci_pipe *pipe) -{ - USB_LOG_DBG("%s\n", __func__); - return xhci_cmd_submit(xhci, NULL, pipe, TRB_TYPE_SET(CR_NOOP)); -} - -static int xhci_cmd_enable_slot(struct xhci_s *xhci, struct xhci_pipe *pipe) -{ - int cc = xhci_cmd_submit(xhci, NULL, pipe, TRB_TYPE_SET(CR_ENABLE_SLOT)); - if (cc != CC_SUCCESS) - return cc; - - struct xhci_trb *evt = &(xhci->cmds->evt); - return TRB_CR_SLOTID_GET(evt->control); /* bit [31:24] slot id */ -} - -static int xhci_cmd_disable_slot(struct xhci_s *xhci, struct xhci_pipe *pipe) -{ - USB_LOG_DBG("%s: slotid %d\n", __func__, pipe->slotid); - return xhci_cmd_submit(xhci, NULL, pipe, TRB_TYPE_SET(CR_DISABLE_SLOT) | TRB_CR_SLOTID_SET(pipe->slotid)); -} - -static int xhci_cmd_address_device(struct xhci_s *xhci, struct xhci_pipe *pipe, struct xhci_inctx *inctx) +__WEAK void usb_hc_low_level_init(void) { - USB_LOG_DBG("%s: slotid %d\n", __func__, pipe->slotid); - return xhci_cmd_submit(xhci, inctx, pipe, TRB_TYPE_SET(CR_ADDRESS_DEVICE) | TRB_CR_SLOTID_SET(pipe->slotid)); } -static int xhci_cmd_configure_endpoint(struct xhci_s *xhci, struct xhci_pipe *pipe, - struct xhci_inctx *inctx, bool defconfig) +__WEAK void *usb_hc_malloc(size_t size) { - USB_LOG_DBG("%s: slotid %d\n", __func__, pipe->slotid); - return xhci_cmd_submit(xhci, inctx, pipe, TRB_TYPE_SET(CR_CONFIGURE_ENDPOINT) | - TRB_CR_SLOTID_SET(pipe->slotid) | - (defconfig ? TRB_CR_DC : 0)); + return NULL; } -static int xhci_cmd_evaluate_context(struct xhci_s *xhci, struct xhci_pipe *pipe, struct xhci_inctx *inctx) -{ - USB_LOG_DBG("%s: slotid %d\n", __func__, pipe->slotid); - /* bit[15:10] TRB type, bit[31:24] Slot id */ - return xhci_cmd_submit(xhci, inctx, pipe, TRB_TYPE_SET(CR_EVALUATE_CONTEXT) | TRB_CR_SLOTID_SET(pipe->slotid)); -} -static int xhci_cmd_reset_endpoint(struct xhci_s *xhci, struct xhci_pipe *pipe) +__WEAK void *usb_hc_malloc_align(size_t align, size_t size) { - USB_LOG_DBG("%s: slotid %d, epid %d\n", __func__, pipe->slotid, pipe->epid); - /* bit[15:10] TRB type, bit[31:24] Slot id */ - return xhci_cmd_submit(xhci, NULL, pipe, TRB_TYPE_SET(CR_RESET_ENDPOINT) | TRB_CR_SLOTID_SET(pipe->slotid) | - TRB_CR_EPID_SET(pipe->epid)); + return NULL; } -static int xhci_controller_configure(struct xhci_s *xhci) +__WEAK void usb_hc_free() { - uint32_t reg; - - /* trbs */ - xhci->devs = usb_hc_malloc_align(XHCI_ALIGMENT, sizeof(*xhci->devs) * (xhci->slots + 1)); /* device slot */ - xhci->eseg = usb_hc_malloc_align(XHCI_ALIGMENT, sizeof(*xhci->eseg)); /* event segment */ - xhci->cmds = usb_hc_malloc_align(XHCI_RING_SIZE, sizeof(*xhci->cmds)); /* command ring */ - xhci->evts = usb_hc_malloc_align(XHCI_RING_SIZE, sizeof(*xhci->evts)); /* event ring */ - - if (!xhci->devs || !xhci->cmds || !xhci->evts || !xhci->eseg) { - USB_LOG_ERR("allocate memory failed !!!\n"); - goto fail; - } - - memset(xhci->devs, 0U, sizeof(*xhci->devs) * (xhci->slots + 1)); - memset(xhci->cmds, 0U, sizeof(*xhci->cmds)); - memset(xhci->evts, 0U, sizeof(*xhci->evts)); - memset(xhci->eseg, 0U, sizeof(*xhci->eseg)); - - xhci->cmds->lock = usb_osal_mutex_create(); - USB_ASSERT(xhci->cmds->lock); - - reg = readl(xhci->op + XHCI_REG_OP_USBCMD); - if (reg & XHCI_REG_OP_USBCMD_RUN_STOP) { /* if xHc running, stop it first */ - reg &= ~XHCI_REG_OP_USBCMD_RUN_STOP; - writel(xhci->op + XHCI_REG_OP_USBCMD, reg); /* stop xHc */ - - if (wait_bit(xhci->op + XHCI_REG_OP_USBSTS, - XHCI_REG_OP_USBSTS_HCH, XHCI_REG_OP_USBSTS_HCH, 32) != 0) /* wait xHc halt */ - goto fail; - } - - USB_LOG_DBG("%s: resetting\n", __func__); - - writel(xhci->op + XHCI_REG_OP_USBCMD, XHCI_REG_OP_USBCMD_HCRST); /* reset xHc */ - if (wait_bit(xhci->op + XHCI_REG_OP_USBCMD, XHCI_REG_OP_USBCMD_HCRST, 0, 1000) != 0) /* wait reset process done */ - goto fail; - - if (wait_bit(xhci->op + XHCI_REG_OP_USBSTS, XHCI_REG_OP_USBSTS_CNR, 0, 1000) != 0) /* wait until xHc ready */ - goto fail; - - writel(xhci->op + XHCI_REG_OP_CONFIG, xhci->slots); /* bit[7:0], set max num of device slot enabled */ - writeq(xhci->op + XHCI_REG_OP_DCBAAP, (uint64_t)xhci->devs); /* bit[63:6] device context base address array pointer */ - writeq(xhci->op + XHCI_REG_OP_CRCR, (uint64_t)xhci->cmds | 1); /* command ring pointer, cycle state = 1 */ - xhci->cmds->cs = 1; /* cycle state = 1 */ - - xhci->eseg->ptr_low = (uint32_t)((unsigned long)xhci->evts); /* event ring pointer */ -#ifdef XHCI_AARCH64 - xhci->eseg->ptr_high = (uint32_t)((unsigned long)xhci->evts >> 32U); -#else - xhci->eseg->ptr_high = 0U; -#endif - - xhci->eseg->size = XHCI_RING_ITEMS; /* items of event ring TRB */ - writel(xhci->ir + XHCI_REG_RT_IR_ERSTSZ, 1); /* bit[15:0] event ring segment table size */ - writeq(xhci->ir + XHCI_REG_RT_IR_ERDP, (uint64_t)xhci->evts); /* bit[63:4] event ring dequeue pointer */ - writeq(xhci->ir + XHCI_REG_RT_IR_ERSTBA, (uint64_t)xhci->eseg); /* bit[63:6] event ring segment table base addr */ - xhci->evts->cs = 1; /* cycle state = 1 */ - - reg = xhci->hcs[1]; - uint32_t spb = XHCI_REG_CAP_HCS2_MAX_SCRATCHPAD_BUFS_GET(reg); /* bit [25:21] | bit [31:27] max scratchpad buffers */ - if (spb) { - /* reserve scratchpad buffers for xHc */ - USB_LOG_DBG("%s: setup %d scratch pad buffers\n", __func__, spb); - uint64_t *spba = usb_hc_malloc_align(XHCI_ALIGMENT, sizeof(*spba) * spb); /* base addr of scratchpad buffers */ - void *pad = usb_hc_malloc_align(CONFIG_XHCI_PAGE_SIZE, CONFIG_XHCI_PAGE_SIZE * spb); /* the whole scratchpad buffers */ - if (!spba || !pad) { - USB_LOG_ERR("allocate memory failed !!!\n"); - usb_free(spba); - usb_free(pad); - goto fail; - } - for (uint32_t i = 0; i < spb; i++) - spba[i] = (uint64_t)(pad + (i * CONFIG_XHCI_PAGE_SIZE)); /* assign base addr for each pad */ - xhci->devs[0].ptr_low = (uint32_t)((unsigned long)spba); /* bit[63:6] scratchpad buffers base addr */ -#ifdef XHCI_AARCH64 - xhci->devs[0].ptr_high = (uint32_t)((uint64_t)spba >> 32U); -#else - xhci->devs[0].ptr_high = 0U; -#endif - } - - /* enable port interrupt */ - writel(xhci->ir + XHCI_REG_RT_IR_IMOD, 500U); - reg = readl(xhci->ir + XHCI_REG_RT_IR_IMAN); - reg |= XHCI_REG_RT_IR_IMAN_IE; - writel(xhci->ir + + XHCI_REG_RT_IR_IMAN, reg); - - reg = readl(xhci->op + XHCI_REG_OP_USBCMD); - reg |= XHCI_REG_OP_USBCMD_INTE; /* enable interrupt */ - writel(xhci->op + XHCI_REG_OP_USBCMD, reg); - - USB_LOG_DBG("Start xHc ....\n"); - - reg = readl(xhci->op + XHCI_REG_OP_USBCMD); - reg |= XHCI_REG_OP_USBCMD_RUN_STOP; /* start xHc */ - writel(xhci->op + XHCI_REG_OP_USBCMD, reg); - - return 0U; /* Success */ -fail: - USB_LOG_ERR("Configure Roothub failed !!!\n"); - usb_free(xhci->eseg); - usb_free(xhci->evts); - usb_free(xhci->cmds); - usb_free(xhci->devs); - return -1; /* Failed */ } -static void xhci_check_xcap(struct xhci_s *xhci) +/* one may get xhci register base address by PCIe bus emuration */ +__WEAK unsigned long usb_hc_get_register_base(void) { - if (xhci->xcap) { /* read Extended Capabilities */ - uint32_t off; - unsigned long addr = xhci->base + xhci->xcap; /* start read ext-cap */ - - do { - unsigned long xcap = addr; - uint32_t ports, name, cap = readl(xcap + XHCI_REG_EXT_CAP_USBSPCF_OFFSET); - switch (XHCI_REG_EXT_CAP_CAP_ID_GET(cap)) { - case XHCI_EXT_CAP_ID_SUPPORT_PROTOCOL: /* 0x2, supported protocol */ - name = readl(xcap + XHCI_REG_EXT_CAP_USBSPCFDEF_OFFSET); - ports = readl(xcap + XHCI_REG_EXT_CAP_USBSPCFDEF2_OFFSET); - uint8_t major = XHCI_USBSPCF_MAJOR_REVERSION_GET(cap); - uint8_t minor = XHCI_USBSPCF_MINOR_REVERSION_GET(cap); - uint8_t count = XHCI_USBSPCFDEF2_COMPATIBLE_PORT_CNT_GET(ports); - uint8_t start = XHCI_USBSPCFDEF2_COMPATIBLE_PORT_OFF_GET(ports); - USB_LOG_INFO("XHCI protocol %c%c%c%c %x.%02x" - ", %d ports (offset %d), def %x\n" - , (name >> 0) & 0xff - , (name >> 8) & 0xff - , (name >> 16) & 0xff - , (name >> 24) & 0xff /* Print string 'USB' */ - , major, minor - , count, start - , ports >> 16); - if (name == XHCI_USBSPCFDEF_NAME_STRING_USB /* ASCII "USB " */) { - if (major == 2) { - xhci->usb2.start = start; /* USB 2.0 port range */ - xhci->usb2.count = count; - } - if (major == 3) { - xhci->usb3.start = start; /* USB 3.0 port range */ - xhci->usb3.count = count; - } - } - break; - default: - USB_LOG_INFO("XHCI extcap 0x%x @ %p\n", - XHCI_REG_EXT_CAP_CAP_ID_GET(cap), addr); - break; - } - off = XHCI_REG_EXT_NEXT_CAP_PTR_GET(cap); - addr += off << 2; /* addr of next ext-cap */ - } while (off > 0); - } - + return 0U; } -static int xhci_controller_setup(struct xhci_s *xhci, unsigned long baseaddr) -{ - USB_LOG_INFO("%s@0x%x\n", __func__, baseaddr); - - /* get register offset */ - xhci_setup_mmio(xhci, baseaddr); - if (xhci->version < 0x96 || xhci->version > 0x120) { - USB_LOG_ERR("xHCI-0x%x not support\n", xhci->version); - return -1; - } +static struct xhci_host xhci_host; - xhci_check_xcap(xhci); - - uint32_t pagesize = readl(xhci->op + XHCI_REG_OP_PAGESIZE); /* get page-size */ - USB_LOG_INFO("XHCI page size %d \n", (pagesize << CONFIG_XHCI_PAGE_SHIFT)); - if (CONFIG_XHCI_PAGE_SIZE != (pagesize << CONFIG_XHCI_PAGE_SHIFT)) { - USB_LOG_ERR("XHCI driver does not support page size code %d\n" - , pagesize << CONFIG_XHCI_PAGE_SHIFT); - return -1; - } - - USB_LOG_INFO("config XHCI ....\n"); - if (xhci_controller_configure(xhci)) { - USB_LOG_ERR("init XHCI failed !!!\n"); - return -1; - } else { - USB_LOG_INFO("init XHCI success !!!\n"); - } - - return 0; -} - -static struct xhci_inctx *xhci_alloc_inctx_config_ep(struct xhci_s *xhci, struct usbh_hubport *hport, int maxepid) +/* xhci hardware init */ +int usb_hc_init() { - USB_ASSERT(xhci && hport); - - int size = (sizeof(struct xhci_inctx) * XHCI_INCTX_ENTRY_NUM) << xhci->context64; - struct xhci_inctx *in = usb_hc_malloc_align(XHCI_INCTX_ALIGMENT << xhci->context64, size); - int devport = hport->port; - - if (!in) { - USB_LOG_ERR("allocate memory failed !!!\n"); - return NULL; - } - memset(in, 0, size); - - struct xhci_slotctx *slot = (void*)&in[1 << xhci->context64]; /* slot context */ - slot->ctx[0] |= XHCI_SLOTCTX_0_MAX_EPID_SET(maxepid); /* bit[31:27] index of the last valid ep context */ - slot->ctx[0] |= XHCI_SLOTCTX_0_SPEED_SET(speed_to_xhci[hport->speed]); /* bit[23:20] speed of this device */ - - /* Set high-speed hub flags. */ - struct usbh_hub *hubdev = hport->parent; - USB_ASSERT(hubdev); - if (!hubdev->is_roothub) { /* if device is not under roothub */ - struct usbh_hubport *hub_port = hubdev->parent; - if (hport->speed == USB_SPEED_LOW || hport->speed == USB_SPEED_FULL) { - if (hub_port->speed == USB_SPEED_HIGH) { - slot->ctx[2] |= XHCI_SLOTCTX_2_HUB_SLOT_SET(hub_port->dev_addr); /* bit[7:0] parent hub slot id */ - slot->ctx[2] |= XHCI_SLOTCTX_2_HUB_PORT_SET(hport->port); /* bit[15:8] parent port num */ - } else { - struct xhci_slotctx *hslot = (void*)(unsigned long)xhci->devs[hub_port->dev_addr].ptr_low; - slot->ctx[2] = hslot->ctx[2]; /* 08h, copy hub slot context */ - } - } + int rc = 0; + struct xhci_host *xhci = &(xhci_host); - uint32_t route = 0U; - do { - route <<= 4; - route |= (hport->port) & 0xf; /* record port for each tire */ + size_t flag = usb_osal_enter_critical_section(); /* no interrupt when init hc */ - hport = hubdev->parent; - hubdev = hport->parent; - } while ((!hubdev) || (!hubdev->is_roothub)); + usb_hc_low_level_init(); /* set gic and memp */ - slot->ctx[0] |= XHCI_SLOTCTX_0_ROUTE_SET(route); /* bit[19:0] route string, max 5 tires */ + memset(xhci, 0, sizeof(*xhci)); + xhci->id = CONFIG_USBHOST_XHCI_ID; + if (rc = xhci_probe(xhci, usb_hc_get_register_base()) != 0) { + goto err_open; } - /* refer to spec. Ports are numbered from 1 to MaxPorts. */ - slot->ctx[1] |= XHCI_SLOTCTX_1_ROOT_PORT_SET(hport->port); /* bit[23:16] root hub port number */ - - return in; -} - -/* allocate input context to update max packet size of endpoint-0 */ -static struct xhci_inctx *xhci_alloc_inctx_set_ep_mps(struct xhci_s *xhci, uint32_t slotid, uint16_t ep_mps) -{ - USB_ASSERT(xhci); - - int size = (sizeof(struct xhci_inctx) * XHCI_INCTX_ENTRY_NUM) << xhci->context64; - struct xhci_inctx *in = usb_hc_malloc_align(XHCI_INCTX_ALIGMENT << xhci->context64, size); - if (!in) { - USB_LOG_ERR("allocate memory failed !!!\n"); - return NULL; + if (rc = xhci_open(xhci) != 0 ) { + goto err_open; } - memset(in, 0, size); - - /* copy 32 entries after inctx controller field from devctx to inctx */ -#ifdef XHCI_AARCH64 - struct xhci_slotctx *devctx = (struct xhci_slotctx *)(((uint64_t)xhci->devs[slotid].ptr_high << 32U) | - ((uint64_t)xhci->devs[slotid].ptr_low)); -#else - struct xhci_slotctx *devctx = (struct xhci_slotctx *)(unsigned long)xhci->devs[slotid].ptr_low; -#endif - struct xhci_slotctx *input_devctx = (void*)&in[1 << xhci->context64]; - - memcpy(input_devctx, devctx, XHCI_SLOTCTX_ENTRY_NUM * sizeof(struct xhci_slotctx)); - - in->add = (1 << 1); /* update ep0 context */ - in->del = (1 << 1); - - /* - input ctrl context - slot - ep-0 context, offset = 2 * xhci->context64, e.g, for 64 bit (0x40), offset = 0x80 - */ - struct xhci_epctx *ep = (void*)&in[2 << xhci->context64]; /* ep context */ - ep->ctx[1] |= XHCI_EPCTX_1_MPS_SET(ep_mps); /* bit[31:16] update maxpacket size */ - - return in; -} - -// Submit a USB "setup" message request to the pipe's ring -static void xhci_xfer_setup(struct xhci_s *xhci, struct xhci_pipe *pipe, - bool dir_in, void *cmd, void *data, int datalen) -{ - /* SETUP TRB ctrl, bit[15:10] trb type - bit[6] Immediate Data (IDT), parameters take effect - bit[17:16] Transfer type, 2 = OUT Data, 3 = IN Data */ - uint32_t trans_type = (datalen > 0) ? (dir_in ? TRB_TR_IN_DATA : TRB_TR_OUT_DATA): TRB_TR_NO_DATA; - xhci_trb_queue(&pipe->reqs, cmd, USB_SIZEOF_SETUP_PACKET, - TRB_TYPE_SET(TR_SETUP) | TRB_TR_IDT | TRB_TR_TYPE_SET(trans_type)); - - /* DATA TRB ctrl, bit[15:10] trb type - bit[16] Direction, 0 = OUT, 1 = IN */ - if (datalen) { - xhci_trb_queue(&pipe->reqs, data, datalen, - TRB_TYPE_SET(TR_DATA) | (dir_in ? TRB_TR_DIR : 0)); - } - - /* STATUS TRB ctrl, bit[5] Interrupt On Completion (IOC). - bit[16] Direction, 0 = OUT, 1 = IN */ - xhci_trb_queue(&pipe->reqs, NULL, 0, - TRB_TYPE_SET(TR_STATUS) | TRB_TR_IOC | ((dir_in ? 0 : TRB_TR_DIR))); - - /* pass command trb to hardware */ - DSB(); - - /* notfiy xHc that device slot - epid */ - xhci_doorbell(xhci, pipe->slotid, pipe->epid); -} - -/* Submit a USB transfer request to the pipe's ring */ -static void xhci_xfer_normal(struct xhci_s *xhci, struct xhci_pipe *pipe, - void *data, int datalen) -{ - /* Normal trb, used in bulk and interrupt transfer */ - xhci_trb_queue(&pipe->reqs, data, datalen, TRB_TYPE_SET(TR_NORMAL) | TRB_TR_IOC); - - /* pass command trb to hardware */ - DSB(); - - xhci_doorbell(xhci, pipe->slotid, pipe->epid); -} - -/* -------------------------------------------------------------- */ -/* port functions */ -static struct xhci_s xhci_host; - -__WEAK void usb_hc_low_level_init(void) -{ -} - -__WEAK void *usb_hc_malloc(size_t size) -{ - return NULL; -} - -__WEAK void *usb_hc_malloc_align(size_t align, size_t size) -{ - return NULL; -} - -__WEAK void usb_hc_free() -{ -} -int usb_hc_init(void) -{ - usb_hc_low_level_init(); - - memset(&xhci_host, 0, sizeof(xhci_host)); - if (xhci_controller_setup(&xhci_host, CONFIG_XHCI_BASE_ADDR)) - return -1; - - return 0; + err_open: + usb_osal_leave_critical_section(flag); + return rc; } int usbh_roothub_control(struct usb_setup_packet *setup, uint8_t *buf) @@ -766,52 +96,71 @@ int usbh_roothub_control(struct usb_setup_packet *setup, uint8_t *buf) uint32_t portsc; uint32_t status; int ret = 0; - struct xhci_s *xhci = &xhci_host; - + struct xhci_host *xhci = &xhci_host; nports = CONFIG_USBHOST_MAX_RHPORTS; port = setup->wIndex; - if (setup->bmRequestType & USB_REQUEST_RECIPIENT_DEVICE) { - switch (setup->bRequest) { - case HUB_REQUEST_CLEAR_FEATURE: - switch (setup->wValue) { + + /* + bmRequestType bit[4:0], define whether the request is directed to the device (0000b), + specific interface (00001b), endpoint (00010b), or other element (00011b) + bRequest, identifies the request + wValue, pass the request-specific info to the device + */ + if (setup->bmRequestType & USB_REQUEST_RECIPIENT_DEVICE) /* request is directed to device */ + { + switch (setup->bRequest) + { + case HUB_REQUEST_CLEAR_FEATURE: /* disable the feature */ + switch (setup->wValue) + { case HUB_FEATURE_HUB_C_LOCALPOWER: + USB_LOG_ERR("HUB_FEATURE_HUB_C_LOCALPOWER not implmented.\n"); break; case HUB_FEATURE_HUB_C_OVERCURRENT: + USB_LOG_ERR("HUB_FEATURE_HUB_C_OVERCURRENT not implmented.\n"); break; default: return -EPIPE; } break; - case HUB_REQUEST_SET_FEATURE: - switch (setup->wValue) { + case HUB_REQUEST_SET_FEATURE: /* set a value reported in the hub status */ + switch (setup->wValue) + { case HUB_FEATURE_HUB_C_LOCALPOWER: + USB_LOG_ERR("HUB_FEATURE_HUB_C_LOCALPOWER not implmented.\n"); break; case HUB_FEATURE_HUB_C_OVERCURRENT: + USB_LOG_ERR("HUB_FEATURE_HUB_C_OVERCURRENT not implmented.\n"); break; default: return -EPIPE; } break; case HUB_REQUEST_GET_DESCRIPTOR: - USB_LOG_ERR("HUB_REQUEST_GET_DESCRIPTOR not implmented \n"); + USB_LOG_ERR("HUB_REQUEST_GET_DESCRIPTOR not implmented.\n"); break; case HUB_REQUEST_GET_STATUS: + USB_ASSERT(buf); memset(buf, 0, 4); break; default: break; } - } else if (setup->bmRequestType & USB_REQUEST_RECIPIENT_OTHER) { - switch (setup->bRequest) { + } + else if (setup->bmRequestType & USB_REQUEST_RECIPIENT_OTHER) + { + switch (setup->bRequest) + { case HUB_REQUEST_CLEAR_FEATURE: - if (!port || port > nports) { + if (!port || port > nports) + { return -EPIPE; } - portsc = xhci_readl_port(xhci, port - 1, XHCI_REG_OP_PORTS_PORTSC); - - switch (setup->wValue) { + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) ); + switch (setup->wValue) + { case HUB_PORT_FEATURE_ENABLE: break; case HUB_PORT_FEATURE_SUSPEND: @@ -820,10 +169,10 @@ int usbh_roothub_control(struct usb_setup_packet *setup, uint8_t *buf) case HUB_PORT_FEATURE_POWER: break; case HUB_PORT_FEATURE_C_CONNECTION: - portsc |= XHCI_REG_OP_PORTS_PORTSC_CSC; + portsc |= XHCI_PORTSC_CSC; break; case HUB_PORT_FEATURE_C_ENABLE: - portsc |= XHCI_REG_OP_PORTS_PORTSC_PEC; + portsc |= XHCI_PORTSC_PEC; break; case HUB_PORT_FEATURE_C_OVER_CURREN: break; @@ -833,367 +182,231 @@ int usbh_roothub_control(struct usb_setup_packet *setup, uint8_t *buf) return -EPIPE; } - uint32_t pclear = portsc & XHCI_REG_OP_PORTS_PORTSC_RW_MASK; - xhci_writel_port(xhci, port - 1, XHCI_REG_OP_PORTS_PORTSC, pclear); /* clear port status */ + uint32_t pclear = portsc & XHCI_PORTSC_RW_MASK; + /* clear port status */ + writel(pclear, xhci->op + XHCI_OP_PORTSC ( port )); break; case HUB_REQUEST_SET_FEATURE: - if (!port || port > nports) { + if (!port || port > nports) + { return -EPIPE; } - switch (setup->wValue) { + switch (setup->wValue) + { case HUB_PORT_FEATURE_SUSPEND: break; case HUB_PORT_FEATURE_POWER: break; case HUB_PORT_FEATURE_RESET: - ret = xhci_hub_reset(xhci, port - 1); + ret = xhci_port_enable(xhci, port); break; - default: return -EPIPE; } break; case HUB_REQUEST_GET_STATUS: - if (!port || port > nports) { + if (!port || port > nports) + { return -EPIPE; } - portsc = xhci_readl_port(xhci, port - 1, XHCI_REG_OP_PORTS_PORTSC); - + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) ); status = 0; - if (portsc & XHCI_REG_OP_PORTS_PORTSC_CSC) { + + if (portsc & XHCI_PORTSC_CSC) + { /* Port connection status changed */ status |= (1 << HUB_PORT_FEATURE_C_CONNECTION); + + /* always clear all the status change bits */ + uint32_t temp = portsc & ( XHCI_PORTSC_PRESERVE | XHCI_PORTSC_CHANGE ); + writel ( temp, xhci->op + XHCI_OP_PORTSC ( port ) ); } - if (portsc & XHCI_REG_OP_PORTS_PORTSC_PEC) { + if (portsc & XHCI_PORTSC_PEC) + { /* Port enabled status changed */ status |= (1 << HUB_PORT_FEATURE_C_ENABLE); } - if (portsc & XHCI_REG_OP_PORTS_PORTSC_OCC) { + if (portsc & XHCI_PORTSC_OCC) + { /* Port status changed due to over-current */ status |= (1 << HUB_PORT_FEATURE_C_OVER_CURREN); } - if (portsc & XHCI_REG_OP_PORTS_PORTSC_CCS) { + if (portsc & XHCI_PORTSC_CCS) + { /* Port connected */ status |= (1 << HUB_PORT_FEATURE_CONNECTION); } - if (portsc & XHCI_REG_OP_PORTS_PORTSC_PED) { + if (portsc & XHCI_PORTSC_PED) + { /* Port enabled */ status |= (1 << HUB_PORT_FEATURE_ENABLE); - const int speed = speed_from_xhci[XHCI_REG_OP_PORTS_PORTSC_PORT_SPEED_GET(portsc)]; - if (speed == USB_SPEED_LOW) { + const unsigned int speed = xhci_root_speed(xhci, port); + USB_LOG_DBG("Port-%d speed = %d \r\n", port, speed); + if (speed == USB_SPEED_LOW) + { status |= (1 << HUB_PORT_FEATURE_LOWSPEED); - } else if ((speed == USB_SPEED_HIGH) || (speed == USB_SPEED_SUPER) || - (speed == USB_SPEED_FULL)) { + } + else if (speed == USB_SPEED_HIGH) + { status |= (1 << HUB_PORT_FEATURE_HIGHSPEED); } + /* else is full-speed */ } - if (portsc & XHCI_REG_OP_PORTS_PORTSC_OCA) { + if (portsc & XHCI_PORTSC_OCA) + { /* Over-current condition */ status |= (1 << HUB_PORT_FEATURE_OVERCURRENT); } - if (portsc & XHCI_REG_OP_PORTS_PORTSC_PR) { + if (portsc & XHCI_PORTSC_PR) + { /* Reset is in progress */ status |= (1 << HUB_PORT_FEATURE_RESET); } - if (portsc & XHCI_REG_OP_PORTS_PORTSC_PP) { + if (portsc & XHCI_PORTSC_PP) + { /* Port is not power off */ status |= (1 << HUB_PORT_FEATURE_POWER); } - memcpy(buf, &status, 4); + memcpy(buf, &status, 4); break; default: break; } - } + } - return ret; -} - -int usbh_get_xhci_devaddr(usbh_pipe_t *pipe) -{ - struct xhci_pipe *ppipe = (struct xhci_pipe *)pipe; - return ppipe->slotid; + return 0; } -int usbh_ep0_pipe_reconfigure(usbh_pipe_t pipe, uint8_t dev_addr, uint8_t ep_mps, uint8_t speed) +uint8_t usbh_get_port_speed(struct usbh_hub *hub, const uint8_t port) { - int ret = 0; - struct xhci_s *xhci = &xhci_host; - struct xhci_pipe *ppipe = (struct xhci_pipe *)pipe; - int oldmaxpacket = ppipe->maxpacket; /* original max packetsz */ - - if (ep_mps != oldmaxpacket) { - /* maxpacket has changed on control endpoint - update controller. */ - USB_LOG_DBG("update ep0 mps from %d to %d\r\n", oldmaxpacket, ep_mps); - struct xhci_inctx *in = xhci_alloc_inctx_set_ep_mps(xhci, ppipe->slotid, ep_mps); /* allocate input context */ - if (!in) - return -1; - - usb_hc_dcache_invalidate(in, sizeof(struct xhci_inctx) * XHCI_INCTX_ENTRY_NUM); - xhci_dump_input_ctx(xhci, in); - - int cc = xhci_cmd_evaluate_context(xhci, ppipe, in); - if (cc != CC_SUCCESS) { - USB_LOG_ERR("%s: reconf ctl endpoint: failed (cc %d) (mps %d => %d)\n", - __func__, cc, oldmaxpacket, ep_mps); - ret = -1; - } else { - ppipe->maxpacket = ep_mps; /* mps update success */ - } + USB_ASSERT(hub); + struct usbh_bus *usb = hub->usb; + USB_ASSERT(usb && usb->priv); + struct xhci_host *xhci = usb->priv; - usb_free(in); + if (hub->is_roothub) { + return xhci_root_speed(xhci, port); + } else { + struct usbh_hubport *roothub_port = usbh_root_hub_port(&(hub->child[port])); + /* TODO, hanlde TT for low-speed device on high-speed hub, but it doesn't matter, since + we haven't well handle hub yet */ + USB_ASSERT(roothub_port); + return xhci_root_speed(xhci, roothub_port->port); } - - return ret; } -static int usbh_get_period(int speed, int interval) +int usbh_ep_pipe_reconfigure(usbh_pipe_t pipe, uint8_t dev_addr, uint8_t mtu, uint8_t speed) { - if (speed != USB_SPEED_HIGH){ - /* fls - find last (most-significant) bit set */ - return (interval <= 4) ? 0 : fls(interval); + struct xhci_endpoint *ppipe = (struct xhci_endpoint *)pipe; + size_t old_mtu = ppipe->mtu; + int rc = 0; + + if (mtu != old_mtu) { + ppipe->mtu = mtu; + rc = xhci_endpoint_mtu(ppipe); } - return (interval <= 4) ? 0 : interval - 4; + return rc; } int usbh_pipe_alloc(usbh_pipe_t *pipe, const struct usbh_endpoint_cfg *ep_cfg) { - int ret = 0; - struct xhci_s *xhci = &xhci_host; - struct xhci_pipe *ppipe = usb_hc_malloc_align(XHCI_RING_SIZE, sizeof(struct xhci_pipe)); + int rc = 0; + int slot_id = 0; + struct xhci_host *xhci = &xhci_host; struct usbh_hubport *hport = ep_cfg->hport; + struct xhci_endpoint *ppipe = usb_align(XHCI_RING_SIZE, sizeof(struct xhci_endpoint)); + struct xhci_slot *slot; - uint32_t epid = 0U; - uint8_t eptype = 0U; - - if (ppipe == NULL) { + if (NULL == ppipe) { return -ENOMEM; } - memset(ppipe, 0, sizeof(struct xhci_pipe)); + memset(ppipe, 0, sizeof(struct xhci_endpoint)); - ppipe->epaddr = ep_cfg->ep_addr; - if (ep_cfg->ep_addr == 0) { - epid = 1; /* ep0, ep_id = 1 */ - } else { - /* refer to spec. Figure 4-4: Endpoint Context Addressing - ep0 = 1, ep1-out = 2, ep1-in = 3, ... ep15-out = 30, ep15-in = 31 */ - epid = (ep_cfg->ep_addr & 0x0f) * 2; - epid += (ep_cfg->ep_addr & USB_EP_DIR_IN) ? 1 : 0; - } - ppipe->epid = epid; - eptype = ep_cfg->ep_type; - ppipe->eptype = eptype; - ppipe->maxpacket = ep_cfg->ep_mps; - ppipe->interval = ep_cfg->ep_interval; - ppipe->speed = ep_cfg->hport->speed; - ppipe->hport = ep_cfg->hport; ppipe->waitsem = usb_osal_sem_create(0); ppipe->waiter = false; ppipe->urb = NULL; + ppipe->hport = hport; - USB_LOG_DBG("%s epid = %d, epaddr = 0x%x eptype = %d, mps = %d, speed = %d, urb = %p\n", - __func__, ppipe->epid, ppipe->epaddr, ppipe->eptype, ppipe->maxpacket, - ppipe->speed, ppipe->urb); - - ppipe->reqs.cs = 1; /* cycle state = 1 */ - - /* Allocate input context and initialize endpoint info. */ - struct xhci_inctx *in = xhci_alloc_inctx_config_ep(xhci, hport, epid); - if (!in){ - ret = -1; - goto fail; - } - - /* add slot context and ep context */ - in->add = 0x01 /* Slot Context */ | (1 << epid); /* EP Context */ - struct xhci_epctx *ep = (void*)&in[(epid + 1) << xhci->context64]; - if (eptype == USB_ENDPOINT_TYPE_INTERRUPT) - ep->ctx[0] = XHCI_EPCTX_0_INTERVAL_SET(usbh_get_period(ppipe->speed, ppipe->interval) + 3); /* bit[23:16] for interrupt ep, set interval to control interrupt period */ - - /* - Value Endpoint Type Direction - 0 Not Valid N/A - 1 Isoch Out - 2 Bulk Out - 3 Interrupt Out - 4 Control Bidirectional - 5 Isoch In - 6 Bulk In - 7 Interrupt In - */ - ep->ctx[1] |= eptype << 3; /* bit[5:3] endpoint type */ - if (ppipe->epaddr & USB_EP_DIR_IN || eptype == USB_ENDPOINT_TYPE_CONTROL) - ep->ctx[1] |= 1 << 5; /* ep_type 4 ~ 7 */ - - ep->ctx[1] |= XHCI_EPCTX_1_MPS_SET(ppipe->maxpacket); /* bit[31:16] max packet size */ - if (eptype == USB_ENDPOINT_TYPE_INTERRUPT) - ep->ctx[1] |= XHCI_EPCTX_1_CERR_SET(3); - - ep->deq_low = (uint32_t)((unsigned long)&ppipe->reqs.ring[0]); /* bit[63:4] tr dequeue pointer */ - ep->deq_low |= 1; /* bit[0] dequeue cycle state */ -#ifdef XHCI_AARCH64 - ep->deq_high = (uint32_t)((uint64_t)&ppipe->reqs.ring[0] >> 32U); -#else - ep->deq_high = 0U; -#endif - - if (eptype == USB_ENDPOINT_TYPE_BULK){ - ep->length = XHCI_EPCTX_AVE_TRB_LEN_SET(256U); /* bit[15:0] average trb length */ - - } else if (eptype == USB_ENDPOINT_TYPE_INTERRUPT){ - ep->length = XHCI_EPCTX_AVE_TRB_LEN_SET(16U) | /* bit[15:0] average trb length */ - XHCI_EPCTX_MAX_ESIT_SET(ppipe->maxpacket); /* bit[31:16] max ESIT payload */ - } - - if (ppipe->epid == 1) { /* when allocate ep-0, allocate device first */ - struct usbh_hub *hubdev = hport->parent; - if (!hubdev->is_roothub){ - /* make sure parent hub has been configured */ - struct usbh_hubport *hubport = hubdev->parent; - struct xhci_pipe *hubep0 = (struct xhci_pipe *)hubport->ep0; - struct xhci_slotctx *hdslot = (void*)(unsigned long)xhci->devs[hubep0->slotid].ptr_low; - if (XHCI_SLOTCTX_3_SLOT_STATE_GET(hdslot->ctx[3]) != XHCI_SLOT_CONFIG) { /* bit [31:27] slot state, 3 means configured */ - USB_LOG_ERR("%s parent hub-%d not yet configured\n", __func__, hubep0->slotid); - ret = -1; - goto fail; - } - } + ppipe->address = ep_cfg->ep_addr; + ppipe->mtu = ep_cfg->ep_mps; + ppipe->interval = ep_cfg->ep_interval; + ppipe->ep_type = ep_cfg->ep_type; + ppipe->burst = 0U; - /* enable slot. */ - size_t size = (sizeof(struct xhci_slotctx) * XHCI_SLOTCTX_ENTRY_NUM) << xhci->context64; - struct xhci_slotctx *dev = usb_hc_malloc_align(XHCI_SLOTCTX_ALIGMENT << xhci->context64, size); - if (!dev) { - USB_LOG_ERR("allocate memory failed !!!\n"); - ret = -1; - goto fail; + if (ppipe->address == 0) { /* if try to allocate ctrl ep, open device first */ + USB_LOG_DBG("allocate device for port-%d \r\n", hport->port); + rc = xhci_device_open(xhci, ppipe, &slot_id); + if (rc) { + goto failed; } - /* send nop command to test if command ring ok */ - for (int i = 0 ; i < 3; ++i) { - int cc = xhci_cmd_nop(xhci, ppipe); - if (cc != CC_SUCCESS) { - USB_LOG_ERR("%s: nop: failed\n", __func__); - usb_free(dev); - ret = -1; - goto fail; - } + slot = xhci->slot[slot_id]; + USB_ASSERT(slot); + rc = xhci_ctrl_endpoint_open(xhci, slot, ppipe); + if (rc) { + goto failed; } - int slotid = xhci_cmd_enable_slot(xhci, ppipe); /* get slot-id */ - if (slotid < 0) { - USB_LOG_ERR("%s: enable slot: failed\n", __func__); - usb_free(dev); - ret = -1; - goto fail; + rc = xhci_device_address(xhci, slot, ppipe); + if (rc) { + goto failed; } + } else { + slot_id = hport->dev_addr; + slot = xhci->slot[slot_id]; - ppipe->slotid = slotid; - USB_LOG_DBG("%s: enable slot: got slotid %d\n", __func__, ppipe->slotid); - memset(dev, 0, size); - xhci->devs[slotid].ptr_low = (uint32_t)(unsigned long)dev; /* DCBAA */ -#ifdef XHCI_AARCH64 - xhci->devs[slotid].ptr_high = (uint32_t)((uint64_t)dev >> 32U); -#else - xhci->devs[slotid].ptr_high = 0; -#endif - - usb_hc_dcache_invalidate(in, sizeof(struct xhci_inctx) * XHCI_INCTX_ENTRY_NUM); - - /* Send set_address command. */ - int cc = xhci_cmd_address_device(xhci, ppipe, in); - if (cc != CC_SUCCESS) { - USB_LOG_ERR("%s: address device: failed (cc %d)\n", __func__, cc); - cc = xhci_cmd_disable_slot(xhci, ppipe); - if (cc != CC_SUCCESS) { - USB_LOG_ERR("%s: disable failed (cc %d)\n", __func__, cc); - ret = -1; - goto fail; - } - - xhci->devs[slotid].ptr_low = 0; /* free DCBAA */ - xhci->devs[slotid].ptr_high = 0; - usb_free(dev); - - ret = -1; - goto fail; - } - } else { /* when allocate other ep, config ep */ - struct xhci_pipe *defpipe = (struct xhci_pipe *)hport->ep0; - ppipe->slotid = defpipe->slotid; - - /* reset if endpoint is not running */ -#ifdef XHCI_AARCH64 - struct xhci_slotctx *devctx = (struct xhci_slotctx *)(((uint64_t)xhci->devs[ppipe->slotid].ptr_high << 32U) | - ((uint64_t)xhci->devs[ppipe->slotid].ptr_low)); -#else - struct xhci_slotctx *devctx = (struct xhci_slotctx *)(unsigned long)xhci->devs[ppipe->slotid].ptr_low; -#endif - struct xhci_epctx *epctx = (void*)&devctx[epid << xhci->context64]; /* ep0 context */ - uint32_t epstate = XHCI_EPCTX_0_EP_STATE_GET(epctx->ctx[0]); - int cc; - - - /* Reset endpoint in case it is not running */ - if (epstate > 1){ - cc = xhci_cmd_reset_endpoint(xhci, ppipe); - if (cc != CC_SUCCESS) { - USB_LOG_ERR("%s: reset endpoint: failed (cc %d)\n", __func__, cc); - ret = -1; - goto fail; - } - } - - usb_hc_dcache_invalidate(in, sizeof(struct xhci_inctx) * XHCI_INCTX_ENTRY_NUM); - xhci_dump_input_ctx(xhci, in); - - /* Send configure command. */ - cc = xhci_cmd_configure_endpoint(xhci, ppipe, in, false); - if (cc != CC_SUCCESS) { - epctx = (void*)&devctx[epid << xhci->context64]; /* ep0 context */ - epstate = XHCI_EPCTX_0_EP_STATE_GET(epctx->ctx[0]); - USB_LOG_ERR("%s: configure endpoint: failed (cc %d, epstate %d)\n", __func__, cc, epstate); - ret = -1; - goto fail; + rc = xhci_work_endpoint_open(xhci, slot, ppipe); + if (rc) { + goto failed; } } *pipe = (usbh_pipe_t)ppipe; + return rc; +failed: + usb_free(ppipe); + *pipe = NULL; + return rc; +} -fail: - usb_free(in); - return ret; +int usbh_get_xhci_devaddr(usbh_pipe_t *pipe) +{ + struct xhci_endpoint *ppipe = (struct xhci_endpoint *)pipe; + USB_ASSERT(ppipe && (ppipe->slot)); + return ppipe->slot->id; } int usbh_pipe_free(usbh_pipe_t pipe) { int ret = 0; - struct xhci_s *xhci = &xhci_host; - struct xhci_pipe *ppipe = (struct xhci_pipe *)pipe; + struct xhci_endpoint *ppipe = (struct xhci_endpoint *)pipe; if (!ppipe) { return -EINVAL; } struct usbh_urb *urb = ppipe->urb; - if (urb) { usbh_kill_urb(urb); } - return -1; + size_t flags = usb_osal_enter_critical_section(); + xhci_endpoint_close(ppipe); + usb_osal_leave_critical_section(flags); + return 0; } int usbh_submit_urb(struct usbh_urb *urb) @@ -1203,41 +416,37 @@ int usbh_submit_urb(struct usbh_urb *urb) return -EINVAL; } - struct xhci_s *xhci = &xhci_host; - struct xhci_pipe *ppipe = urb->pipe; + struct xhci_endpoint *ppipe = (struct xhci_endpoint *)urb->pipe; + struct xhci_host *xhci = ppipe->xhci; struct usb_setup_packet *setup = urb->setup; - size_t flags; - - flags = usb_osal_enter_critical_section(); + size_t flags = usb_osal_enter_critical_section(); urb->errorcode = -EBUSY; urb->actual_length = 0U; - ppipe->waiter = false; ppipe->urb = urb; ppipe->timeout = urb->timeout; - if (ppipe->timeout > 0) { ppipe->waiter = true; + } else { + ppipe->waiter = false; } usb_osal_leave_critical_section(flags); - switch (ppipe->eptype){ + switch (ppipe->ep_type) { case USB_ENDPOINT_TYPE_CONTROL: USB_ASSERT(setup); - if (setup->bRequest == USB_REQUEST_SET_ADDRESS){ + if (setup->bRequest == USB_REQUEST_SET_ADDRESS) { /* Set address command sent during xhci_alloc_pipe. */ goto skip_req; } - USB_LOG_DBG("%s request-%d\n", __func__, setup->bRequest); - /* send setup in/out for command */ - xhci_xfer_setup(xhci, ppipe, setup->bmRequestType & USB_EP_DIR_IN, (void*)setup, - urb->transfer_buffer, urb->transfer_buffer_length); + USB_LOG_DBG("%s request-%d.\n", __func__, setup->bRequest); + xhci_endpoint_message(ppipe, setup, urb->transfer_buffer, urb->transfer_buffer_length); break; case USB_ENDPOINT_TYPE_INTERRUPT: case USB_ENDPOINT_TYPE_BULK: - xhci_xfer_normal(xhci, ppipe, urb->transfer_buffer, urb->transfer_buffer_length); + xhci_endpoint_stream(ppipe, urb->transfer_buffer, urb->transfer_buffer_length); break; default: USB_ASSERT(0U); @@ -1246,10 +455,11 @@ int usbh_submit_urb(struct usbh_urb *urb) /* wait all ring handled by xHc */ int cc = xhci_event_wait(xhci, ppipe, &ppipe->reqs); - if ((cc != CC_SUCCESS) && - !((cc == CC_TIMEOUT) && (ppipe->eptype == USB_ENDPOINT_TYPE_INTERRUPT))) { + if ((cc != XHCI_CMPLT_SUCCESS) && + !((cc == XHCI_CMPLT_TIMEOUT) && (ppipe->ep_type == USB_ENDPOINT_TYPE_INTERRUPT))) + { /* ignore transfer timeout for interrupt type */ - USB_LOG_ERR("%s: xfer failed (cc %d)\n", __func__, cc); + USB_LOG_ERR("%s: xfer failed (cc %d).\n", __func__, cc); ret = -1; urb->errorcode = cc; goto errout_timeout; @@ -1267,109 +477,23 @@ int usbh_kill_urb(struct usbh_urb *urb) return 0; } -void USBH_IRQHandler(void) +void USBH_IRQHandler(void *param) { - uint32_t reg, status; - struct xhci_s *xhci = &xhci_host; - struct xhci_ring *evts = xhci->evts; - struct xhci_pipe *work_pipe = NULL; - - USB_LOG_DBG("%s\n", __func__); - - status = readl(xhci->op + XHCI_REG_OP_USBSTS); - status |= XHCI_REG_OP_USBSTS_EINT; /* clear interrupt status */ - writel(xhci->op + XHCI_REG_OP_USBSTS, status); - - reg = readl(xhci->ir + XHCI_REG_RT_IR_IMAN); - reg |= XHCI_REG_RT_IR_IMAN_IP; /* ack interrupt */ - writel(xhci->ir + XHCI_REG_RT_IR_IMAN, reg); - - /* check and ack event */ - for (;;) { - uint32_t nidx = evts->nidx; /* index of dequeue trb */ - uint32_t cs = evts->cs; /* cycle state toggle by xHc */ - struct xhci_trb *etrb = evts->ring + nidx; /* current trb */ - uint32_t control = etrb->control; /* trb control field */ - - if ((control & TRB_C) != (cs ? 1 : 0)) /* if cycle state not toggle, no events need to handle */ - break; - - /* process event on etrb */ - uint32_t evt_type = TRB_TYPE_GET(control); - uint32_t evt_cc = TRB_CC_GET(etrb->status); /* bit[31:24] completion code */ - - if (ER_PORT_STATUS_CHANGE == evt_type) { - /* bit[31:24] port id, the port num of root hub port that generated this event */ - uint32_t port = TRB_PORT_ID_GET(etrb->ptr_low) - 1; - uint32_t portsc = xhci_readl_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC); /* Read status */ - - xhci_print_port_state(__func__, port, portsc); - if (portsc & XHCI_REG_OP_PORTS_PORTSC_CSC) { - usbh_roothub_thread_wakeup(port + 1); /* wakeup when connection status changed */ - } - } else if ((ER_COMMAND_COMPLETE == evt_type) || (ER_TRANSFER_COMPLETE == evt_type)) { - struct xhci_trb *rtrb = (void*)(unsigned long)etrb->ptr_low; - struct xhci_ring *ring = XHCI_RING(rtrb); /* to align addr is ring base */ - struct xhci_trb *evt = &ring->evt; /* first event trb */ - uint32_t eidx = rtrb - ring->ring + 1; /* calculate current evt trb index */ - - memcpy(evt, etrb, sizeof(*etrb)); /* copy current trb to cmd/transfer ring */ - ring->eidx = eidx; - - if (ER_COMMAND_COMPLETE == evt_type) { - work_pipe = (struct xhci_pipe *)cur_cmd_pipe; - } else if (ER_TRANSFER_COMPLETE == evt_type) { - /* xhci_pipe begin with reqs ring, therefore we get pipe instance from reqs ring */ - work_pipe = (struct xhci_pipe *)(void *)ring; - } - - if (work_pipe) { - if (work_pipe->waiter) { - work_pipe->waiter = false; - usb_osal_sem_give(work_pipe->waitsem); - } - - if (work_pipe->urb) { - struct usbh_urb *cur_urb = work_pipe->urb; - cur_urb->errorcode = evt_cc; - /* bit [23:0] TRB Transfer length, residual number of bytes not transferred - for OUT, is the value of (len of trb) - (data bytes transmitted), '0' means successful - for IN, is the value of (len of trb) - (data bytes received), - if cc is Short Packet, value is the diff between expected trans size and actual recv bytes - if cc is other error, value is the diff between expected trans size and actual recv bytes */ - cur_urb->actual_length += cur_urb->transfer_buffer_length - - TRB_TR_TRANS_LEN_SET(evt->status); /* bit [23:0] */ - } - } - } else { - USB_LOG_ERR("%s: unknown event, type %d, cc %d\n", - __func__, evt_type, evt_cc); - } + struct xhci_host *xhci = &xhci_host; + struct xhci_endpoint *work_pipe = NULL; + USB_ASSERT(xhci); + uint32_t usbsts; + uint32_t runtime; - /* move ring index, notify xhci */ - nidx++; /* head to next trb */ - if (nidx == XHCI_RING_ITEMS) { - nidx = 0; /* roll-back if reach end of list */ - cs = cs ? 0 : 1; - evts->cs = cs; /* sw toggle cycle state */ - } - evts->nidx = nidx; - uint64_t erdp = (uint64_t)(evts->ring + nidx); - writeq(xhci->ir + XHCI_REG_RT_IR_ERDP, erdp | XHCI_REG_RT_IR_ERDP_EHB); /* bit[63:4] update current event ring dequeue pointer */ - } + /* clear interrupt status */ + usbsts = readl ( xhci->op + XHCI_OP_USBSTS ); + usbsts |= XHCI_USBSTS_EINT; + writel(usbsts, xhci->op + XHCI_OP_USBSTS); - /* handle callbacks in interrupt */ - if ((work_pipe) && (work_pipe->urb)) { - struct usbh_urb *cur_urb = work_pipe->urb; - if (cur_urb->complete) { - if (cur_urb->errorcode < 0) { - cur_urb->complete(cur_urb->arg, cur_urb->errorcode); - } else { - cur_urb->complete(cur_urb->arg, cur_urb->actual_length); - } - } - } + /* ack interrupt */ + runtime = readl(xhci->run + XHCI_RUN_IR_IMAN ( 0 )); + runtime |= XHCI_RUN_IR_IMAN_IP; + writel (runtime, xhci->run + XHCI_RUN_IR_IMAN ( 0 )); - USB_LOG_DBG("%s exit\n", __func__); - return; + work_pipe = xhci_event_process(xhci); }
\ No newline at end of file diff --git a/port/xhci/usb_hc_xhci.h b/port/xhci/usb_hc_xhci.h index f4a20761..151b5bdc 100644 --- a/port/xhci/usb_hc_xhci.h +++ b/port/xhci/usb_hc_xhci.h @@ -1,183 +1,38 @@ /* - * Copyright : (C) 2022 Phytium Information Technology, Inc. + * Copyright : (C) 2022 Phytium Information Technology, Inc. * All Rights Reserved. - * - * This program is OPEN SOURCE software: you can redistribute it and/or modify it - * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, - * either version 1.0 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the Phytium Public License for more details. - * - * + * See the Phytium Public License for more details. + * + * * FilePath: usb_hc_xhci.h * Date: 2022-07-19 09:26:25 * LastEditTime: 2022-07-19 09:26:25 - * Description: This files is for xhci data structure definition - * - * Modify History: + * Description: This file is for xhci data structure definition. + * + * Modify History: * Ver Who Date Changes * ----- ------ -------- -------------------------------------- * 1.0 zhugengyu 2022/9/19 init commit + * 2.0 zhugengyu 2023/3/29 support usb3.0 device attached at roothub */ + #ifndef USB_HC_XHCI_H #define USB_HC_XHCI_H /***************************** Include Files *********************************/ #include "usbh_core.h" -#ifdef __cplusplus -extern "C" -{ -#endif - /************************** Constant Definitions *****************************/ -/************************** Type Definitions *****************************/ -/* slot context */ -struct xhci_slotctx { - uint32_t ctx[4]; -#define XHCI_SLOTCTX_0_MAX_EPID_SET(maxid) XHCI32_SET_BITS(maxid, 31, 27) -#define XHCI_SLOTCTX_0_SPEED_SET(speed) XHCI32_SET_BITS(speed, 23, 20) -#define XHCI_SLOTCTX_1_ROOT_PORT_GET(val) XHCI32_GET_BITS(val, 23, 16) -#define XHCI_SLOTCTX_1_ROOT_PORT_SET(port) XHCI32_SET_BITS(port, 23, 16) -#define XHCI_SLOTCTX_0_ROUTE_SET(route) XHCI32_SET_BITS(route, 19, 0) -#define XHCI_SLOTCTX_0_ROUTE_GET(route) XHCI32_GET_BITS(route, 19, 0) -#define XHCI_SLOTCTX_2_HUB_SLOT_SET(slot) XHCI32_SET_BITS(slot, 7, 0) -#define XHCI_SLOTCTX_2_HUB_SLOT_GET(slot) XHCI32_GET_BITS(slot, 7, 0) -#define XHCI_SLOTCTX_2_HUB_PORT_SET(port) XHCI32_SET_BITS(port, 15, 8) -#define XHCI_SLOTCTX_2_HUB_PORT_GET(port) XHCI32_GET_BITS(port, 15, 8) -#define XHCI_SLOTCTX_3_SLOT_STATE_GET(ctx) XHCI32_GET_BITS(ctx, 31, 27) - uint32_t reserved_01[4]; -#define XHCI_SLOTCTX_ENTRY_NUM 32U -#define XHCI_SLOTCTX_ALIGMENT 1024U -} __PACKED; - -enum xhci_slot_state { - XHCI_SLOT_DEFAULT = 1, - XHCI_SLOT_ADDRESS = 2, - XHCI_SLOT_CONFIG = 3 -}; - -/* endpoint context */ -struct xhci_epctx { - uint32_t ctx[2]; -#define XHCI_EPCTX_0_EP_STATE_GET(ctx) XHCI32_GET_BITS(ctx, 2, 0) -#define XHCI_EPCTX_0_INTERVAL_SET(interval) XHCI32_SET_BITS(interval, 23, 16) -#define XHCI_EPCTX_1_MPS_SET(mps) XHCI32_SET_BITS(mps, 31, 16) -#define XHCI_EPCTX_1_MPS_GET(ctx) XHCI32_GET_BITS(ctx, 31, 16) -#define XHCI_EPCTX_1_EPTYPE_GET(ctx) XHCI32_GET_BITS(ctx, 5, 3) -#define XHCI_EPCTX_1_CERR_SET(cerr) XHCI32_SET_BITS(cerr, 2, 1) - uint32_t deq_low; - uint32_t deq_high; - uint32_t length; -#define XHCI_EPCTX_AVE_TRB_LEN_SET(len) XHCI32_SET_BITS(len, 15, 0) -#define XHCI_EPCTX_MAX_ESIT_SET(esit) XHCI32_SET_BITS(esit, 31, 16) - uint32_t reserved_01[3]; -} __PACKED; - -/* device context array element */ -struct xhci_devlist { - uint32_t ptr_low; - uint32_t ptr_high; -} __PACKED; - -/* input context */ -struct xhci_inctx { - uint32_t del; - uint32_t add; - uint32_t reserved_01[6]; -/* refer to spec. The Input Context is an array of up to 33 context data structure entries */ -#define XHCI_INCTX_ENTRY_NUM 33U -#define XHCI_INCTX_ALIGMENT 2048 -} __PACKED; - -/* transfer block (ring element) */ -struct xhci_trb { - uint32_t ptr_low; - uint32_t ptr_high; - uint32_t status; - uint32_t control; -} __PACKED; - -/* event ring segment */ -struct xhci_er_seg { - uint32_t ptr_low; - uint32_t ptr_high; - uint32_t size; - uint32_t reserved_01; -} __PACKED; - -struct xhci_portmap { - uint8_t start; - uint8_t count; -}; - -struct xhci_ring { - struct xhci_trb ring[XHCI_RING_ITEMS]; - struct xhci_trb evt; - uint32_t eidx; - uint32_t nidx; - uint32_t cs; - usb_osal_mutex_t lock; -}; - -struct xhci_pipe { - struct xhci_ring reqs; /* DO NOT MOVE reqs from structure beg */ - uint8_t epaddr; - uint8_t speed; - uint8_t interval; - uint8_t eptype; - uint16_t maxpacket; - uint32_t slotid; - uint32_t epid; - - /* command or transfer waiter */ - int timeout; /* = 0 no need to wait */ - bool waiter; - usb_osal_sem_t waitsem; - - /* handle urb */ - struct usbh_hubport *hport; - struct usbh_urb *urb; /* NULL if no active URB */ -}; - -struct xhci_s { - /* devinfo */ - uint32_t ports; - uint32_t slots; - bool context64; - struct xhci_portmap usb2; - struct xhci_portmap usb3; - - /* xhci registers base addr */ - unsigned long base; /* register base */ - unsigned long caps; /* capability register base */ - unsigned long op; /* operational register base */ - unsigned long pr; /* port register base */ - unsigned long ir; /* interrupt runtime register base */ - unsigned long db; /* doorbell register base */ - unsigned long xcap; /* extended capability */ - uint32_t hcs[3]; /* capability cache */ - uint32_t hcc; - uint16_t version; /* xhci version */ - - /* xhci data structures */ - struct xhci_devlist *devs; - struct xhci_ring *cmds; - struct xhci_ring *evts; - struct xhci_er_seg *eseg; -}; +/************************** Variable Definitions *****************************/ /***************** Macros (Inline Functions) Definitions *********************/ /************************** Function Prototypes ******************************/ - -/*****************************************************************************/ - -#ifdef __cplusplus -} -#endif - -#endif
\ No newline at end of file diff --git a/port/xhci/xhci.c b/port/xhci/xhci.c new file mode 100644 index 00000000..21c62f01 --- /dev/null +++ b/port/xhci/xhci.c @@ -0,0 +1,2563 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: xhci.c + * Date: 2022-07-19 09:26:25 + * LastEditTime: 2022-07-19 09:26:25 + * Description: This file is for xhci functions implmentation. + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + * 1.0 zhugengyu 2022/9/19 init commit + * 2.0 zhugengyu 2023/3/29 support usb3.0 device attached at roothub + */ + +#include "usbh_core.h" +#include "usbh_hub.h" + +#include "xhci_reg.h" +#include "xhci.h" + + +/** @file + * + * USB eXtensible Host Controller Interface (xHCI) driver + * + */ + +#define XHCI_DUMP 1 + +/** Define a XHCI speed in PSI + * + * @v mantissa Mantissa + * @v exponent Exponent (in engineering terms: 1=k, 2=M, 3=G) + * @ret speed USB speed + */ +#define XCHI_PSI( mantissa, exponent ) ( (exponent << 16) | (mantissa) ) + +/** USB device speeds */ +enum { + /** Not connected */ + XCHI_PSI_NONE = 0, + /** Low speed (1.5Mbps) */ + XCHI_PSI_LOW = XCHI_PSI ( 1500, 1 ), + /** Full speed (12Mbps) */ + XCHI_PSI_FULL = XCHI_PSI ( 12, 2 ), + /** High speed (480Mbps) */ + XCHI_PSI_HIGH = XCHI_PSI ( 480, 2 ), + /** Super speed (5Gbps) */ + XCHI_PSI_SUPER = XCHI_PSI ( 5, 3 ), +}; + + +/** + * Calculate buffer alignment + * + * @v len Length + * @ret align Buffer alignment + * + * Determine alignment required for a buffer which must be aligned to + * at least XHCI_MIN_ALIGN and which must not cross a page boundary. + */ +static inline size_t xhci_align ( size_t len ) { + size_t align; + + /* Align to own length (rounded up to a power of two) */ + align = ( 1 << fls ( len - 1 ) ); + + /* Round up to XHCI_MIN_ALIGN if needed */ + if ( align < XHCI_MIN_ALIGN ) + align = XHCI_MIN_ALIGN; + + return align; +} + +/** + * Write potentially 64-bit register + * + * @v xhci xHCI device + * @v value Value + * @v reg Register address + * @ret rc Return status code + */ +static inline int xhci_writeq ( struct xhci_host *xhci, uintptr_t value, void *reg ) { + + /* If this is a 32-bit build, then this can never fail + * (allowing the compiler to optimise out the error path). + */ + if ( sizeof ( value ) <= sizeof ( uint32_t ) ) { + writel ( value, reg ); + writel ( 0, ( reg + sizeof ( uint32_t ) ) ); + return 0; + } + + /* If the device does not support 64-bit addresses and this + * address is outside the 32-bit address space, then fail. + */ + if ( ( value & ~0xffffffffULL ) && ! xhci->addr64 ) { + USB_LOG_DBG("XHCI %s cannot access address %lx\n", + xhci->name, value ); + return -ENOTSUP; + } + + /* If this is a 64-bit build, then writeq() is available */ + writeq ( value, reg ); + return 0; +} + +/** + * Initialise device + * + * @v xhci xHCI device + * @v regs MMIO registers + */ +static void xhci_init ( struct xhci_host *xhci, void *regs ) { + uint32_t hcsparams1; + uint32_t hcsparams2; + uint32_t hccparams1; + uint32_t pagesize; + size_t caplength; + size_t rtsoff; + size_t dboff; + + /* Locate capability, operational, runtime, and doorbell registers */ + xhci->cap = regs; + caplength = readb ( xhci->cap + XHCI_CAP_CAPLENGTH ); + rtsoff = readl ( xhci->cap + XHCI_CAP_RTSOFF ); + dboff = readl ( xhci->cap + XHCI_CAP_DBOFF ); + xhci->op = ( xhci->cap + caplength ); + xhci->run = ( xhci->cap + rtsoff ); + xhci->db = ( xhci->cap + dboff ); + + /* avoid access XHCI_REG_CAP_HCIVERSION = 0x2, unaligned memory */ + xhci->version = ((readl ( xhci->cap + XHCI_CAP_CAPLENGTH ) >> 16) & 0xffff); + + USB_LOG_DBG("XHCI %s version %x cap %08lx op %08lx run %08lx db %08lx\n", + xhci->name, xhci->version, ( xhci->cap ), + ( xhci->op ), ( xhci->run ), + ( xhci->db ) ); + + if (xhci->version < 0x96 || xhci->version > 0x120) { + USB_LOG_WRN("XHCI %s not support.\n", xhci->name); + } + + /* Read structural parameters 1 */ + hcsparams1 = readl ( xhci->cap + XHCI_CAP_HCSPARAMS1 ); + xhci->slots = XHCI_HCSPARAMS1_SLOTS ( hcsparams1 ); + xhci->intrs = XHCI_HCSPARAMS1_INTRS ( hcsparams1 ); + xhci->ports = XHCI_HCSPARAMS1_PORTS ( hcsparams1 ); + USB_LOG_DBG("XHCI %s has %d slots %d intrs %d ports\n", + xhci->name, xhci->slots, xhci->intrs, xhci->ports ); + + /* Read structural parameters 2 */ + hcsparams2 = readl ( xhci->cap + XHCI_CAP_HCSPARAMS2 ); + xhci->scratch.count = XHCI_HCSPARAMS2_SCRATCHPADS ( hcsparams2 ); + USB_LOG_DBG("XHCI %s needs %d scratchpads\n", + xhci->name, xhci->scratch.count ); + + /* Read capability parameters 1 */ + hccparams1 = readl ( xhci->cap + XHCI_CAP_HCCPARAMS1 ); + xhci->addr64 = XHCI_HCCPARAMS1_ADDR64 ( hccparams1 ); + xhci->csz_shift = XHCI_HCCPARAMS1_CSZ_SHIFT ( hccparams1 ); + xhci->xecp = (XHCI_HCCPARAMS1_XECP ( hccparams1 )); + USB_LOG_DBG("XHCI %s context %d bit\n", + xhci->name, (xhci->addr64 ? 64 : 32) ); + + /* Read page size */ + pagesize = readl ( xhci->op + XHCI_OP_PAGESIZE ); + xhci->pagesize = XHCI_PAGESIZE ( pagesize ); + USB_ASSERT ( xhci->pagesize != 0 ); + USB_ASSERT ( ( ( xhci->pagesize ) & ( xhci->pagesize - 1 ) ) == 0 ); + USB_LOG_DBG("XHCI %s page size %zd bytes\n", + xhci->name, xhci->pagesize ); +} + +/** + * Find extended capability + * + * @v xhci xHCI device + * @v id Capability ID + * @v offset Offset to previous extended capability instance, or zero + * @ret offset Offset to extended capability, or zero if not found + */ +static unsigned int xhci_extended_capability ( struct xhci_host *xhci, + unsigned int id, + unsigned int offset ) { + uint32_t xecp; + unsigned int next; + + /* Locate the extended capability */ + while ( 1 ) { + + /* Locate first or next capability as applicable */ + if ( offset ) { + xecp = readl ( xhci->cap + offset ); + next = XHCI_XECP_NEXT ( xecp ); + } else { + next = xhci->xecp; + } + if ( ! next ) + return 0; + offset += next; + + /* Check if this is the requested capability */ + xecp = readl ( xhci->cap + offset ); + if ( XHCI_XECP_ID ( xecp ) == id ) + return offset; + } +} + +/** + * Initialise USB legacy support + * + * @v xhci xHCI device + */ +static void xhci_legacy_init ( struct xhci_host *xhci ) { + unsigned int legacy; + uint8_t bios; + + /* Locate USB legacy support capability (if present) */ + legacy = xhci_extended_capability ( xhci, XHCI_XECP_ID_LEGACY, 0 ); + if ( ! legacy ) { + /* Not an error; capability may not be present */ + USB_LOG_DBG("XHCI %s has no USB legacy support capability\n", + xhci->name ); + return; + } + + /* Check if legacy USB support is enabled */ + USB_LOG_DBG("XHCI %s bios offset 0x%x\n", xhci->name, (xhci->cap + legacy + XHCI_USBLEGSUP_BIOS)); + /* bios = readb ( xhci->cap + legacy + XHCI_USBLEGSUP_BIOS ); cannot access offset 0x2, work around */ + bios = readl ( xhci->cap + legacy ); + bios = (bios >> 16) & 0xffff; + if ( ! ( bios & XHCI_USBLEGSUP_BIOS_OWNED ) ) { + /* Not an error; already owned by OS */ + USB_LOG_DBG("XHCI %s USB legacy support already disabled\n", + xhci->name ); + return; + } + + /* Record presence of USB legacy support capability */ + xhci->legacy = legacy; +} + +/** + * Claim ownership from BIOS + * + * @v xhci xHCI device + */ +static void xhci_legacy_claim ( struct xhci_host *xhci ) { + uint32_t ctlsts; + uint8_t bios; + unsigned int i; + + /* Do nothing unless legacy support capability is present */ + if ( ! xhci->legacy ) + return; + + /* Claim ownership */ + writeb ( XHCI_USBLEGSUP_OS_OWNED, + xhci->cap + xhci->legacy + XHCI_USBLEGSUP_OS ); + + /* Wait for BIOS to release ownership */ + for ( i = 0 ; i < XHCI_USBLEGSUP_MAX_WAIT_MS ; i++ ) { + + /* Check if BIOS has released ownership */ + bios = readb ( xhci->cap + xhci->legacy + XHCI_USBLEGSUP_BIOS ); + if ( ! ( bios & XHCI_USBLEGSUP_BIOS_OWNED ) ) { + USB_LOG_DBG("XHCI %s claimed ownership from BIOS\n", + xhci->name ); + ctlsts = readl ( xhci->cap + xhci->legacy + + XHCI_USBLEGSUP_CTLSTS ); + if ( ctlsts ) { + USB_LOG_DBG("XHCI %s warning: BIOS retained " + "SMIs: %08x\n", xhci->name, ctlsts ); + } + return; + } + + /* Delay */ + usb_osal_msleep ( 1 ); + } + + /* BIOS did not release ownership. Claim it forcibly by + * disabling all SMIs. + */ + USB_LOG_DBG("XHCI %s could not claim ownership from BIOS: forcibly " + "disabling SMIs\n", xhci->name ); + writel ( 0, xhci->cap + xhci->legacy + XHCI_USBLEGSUP_CTLSTS ); +} + +/** Prevent the release of ownership back to BIOS */ +static int xhci_legacy_prevent_release; + +/** + * Release ownership back to BIOS + * + * @v xhci xHCI device + */ +static void xhci_legacy_release ( struct xhci_host *xhci ) { + + /* Do nothing unless legacy support capability is present */ + if ( ! xhci->legacy ) + return; + + /* Do nothing if releasing ownership is prevented */ + if ( xhci_legacy_prevent_release ) { + USB_LOG_DBG("XHCI %s not releasing ownership to BIOS\n", + xhci->name ); + return; + } + + /* Release ownership */ + writeb ( 0, xhci->cap + xhci->legacy + XHCI_USBLEGSUP_OS ); + USB_LOG_DBG("XHCI %s released ownership to BIOS\n", xhci->name ); +} + +/** + * Stop xHCI device + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_stop ( struct xhci_host *xhci ) { + uint32_t usbcmd; + uint32_t usbsts; + unsigned int i; + + /* Clear run/stop bit */ + usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); + usbcmd &= ~XHCI_USBCMD_RUN; + writel ( usbcmd, xhci->op + XHCI_OP_USBCMD ); + + /* Wait for device to stop */ + for ( i = 0 ; i < XHCI_STOP_MAX_WAIT_MS ; i++ ) { + + /* Check if device is stopped */ + usbsts = readl ( xhci->op + XHCI_OP_USBSTS ); + if ( usbsts & XHCI_USBSTS_HCH ) + return 0; + + /* Delay */ + usb_osal_msleep ( 1 ); + } + + USB_LOG_DBG("XHCI %s timed out waiting for stop\n", xhci->name ); + return -ETIMEDOUT; +} + +/** + * Reset xHCI device + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_reset ( struct xhci_host *xhci ) { + uint32_t usbcmd; + unsigned int i; + int rc; + + /* The xHCI specification states that resetting a running + * device may result in undefined behaviour, so try stopping + * it first. + */ + if ( ( rc = xhci_stop ( xhci ) ) != 0 ) { + /* Ignore errors and attempt to reset the device anyway */ + } + + /* Reset device */ + writel ( XHCI_USBCMD_HCRST, xhci->op + XHCI_OP_USBCMD ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < XHCI_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if reset is complete */ + usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); + if ( ! ( usbcmd & XHCI_USBCMD_HCRST ) ) + return 0; + + /* Delay */ + usb_osal_msleep ( 1 ); + } + + USB_LOG_DBG("XHCI %s timed out waiting for reset\n", xhci->name ); + return -ETIMEDOUT; +} + + +/** + * Find supported protocol extended capability for a port + * + * @v xhci xHCI device + * @v port Port number + * @ret supported Offset to extended capability, or zero if not found + */ +static unsigned int xhci_supported_protocol ( struct xhci_host *xhci, + unsigned int port ) { + unsigned int supported = 0; + unsigned int offset; + unsigned int count; + uint32_t ports; + + /* Iterate over all supported protocol structures */ + while ( ( supported = xhci_extended_capability ( xhci, + XHCI_XECP_ID_SUPPORTED, + supported ) ) ) { + + /* Determine port range */ + ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); + offset = XHCI_SUPPORTED_PORTS_OFFSET ( ports ); + count = XHCI_SUPPORTED_PORTS_COUNT ( ports ); + + /* Check if port lies within this range */ + if ( ( port - offset ) < count ) + return supported; + } + + USB_LOG_DBG("XHCI %s Port-%d has no supported protocol\n", + xhci->name, port ); + return 0; +} + +/** + * Transcribe port speed (for debugging) + * + * @v psi Protocol speed ID + * @ret speed Transcribed speed + */ +static inline const char * xhci_speed_name ( uint32_t psi ) { + static const char *exponents[4] = { "", "k", "M", "G" }; + static char buf[ 10 /* "xxxxxXbps" + NUL */ ]; + unsigned int mantissa; + unsigned int exponent; + + /* Extract mantissa and exponent */ + mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); + exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); + + /* Transcribe speed */ + snprintf ( buf, sizeof ( buf ), "%d%sbps", + mantissa, exponents[exponent] ); + return buf; +} + +/** + * Find port protocol + * + * @v xhci xHCI device + * @v port Port number + * @ret protocol USB protocol, or zero if not found + */ +static unsigned int xhci_port_protocol ( struct xhci_host *xhci, + unsigned int port ) { + unsigned int supported = xhci_supported_protocol ( xhci, port ); + union { + uint32_t raw; + char text[5]; + } name; + unsigned int protocol; + unsigned int type; + unsigned int psic; + unsigned int psiv; + unsigned int i; + uint32_t revision; + uint32_t ports; + uint32_t slot; + uint32_t psi; + + /* Fail if there is no supported protocol */ + if ( ! supported ) + return 0; + + /* Determine protocol version */ + revision = readl ( xhci->cap + supported + XHCI_SUPPORTED_REVISION ); + protocol = XHCI_SUPPORTED_REVISION_VER ( revision ); + + /* Describe port protocol */ +#if XHCI_DUMP + { + name.raw = CPU_TO_LE32 ( readl ( xhci->cap + supported + + XHCI_SUPPORTED_NAME ) ); + name.text[4] = '\0'; + slot = readl ( xhci->cap + supported + XHCI_SUPPORTED_SLOT ); + type = XHCI_SUPPORTED_SLOT_TYPE ( slot ); + USB_LOG_DBG("XHCI %s-%d %sv%04x type %d \r\n", + xhci->name, port, name.text, protocol, type ); + ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); + psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); + if ( psic ) { + USB_LOG_DBG(" speeds \r\n" ); + for ( i = 0 ; i < psic ; i++ ) { + psi = readl ( xhci->cap + supported + + XHCI_SUPPORTED_PSI ( i ) ); + psiv = XHCI_SUPPORTED_PSI_VALUE ( psi ); + USB_LOG_DBG(" %d:%s \r\n", psiv, xhci_speed_name ( psi ) ); + } + } + } +#endif + + return protocol; +} + +/** + * Probe XCHI device + * + * @v xhci XCHI device + * @v baseaddr XHCI device register base address + * @ret rc Return status code + */ +int xhci_probe ( struct xhci_host *xhci, unsigned long baseaddr ) { + USB_ASSERT(xhci); + int error = 0; + struct usbh_hubport *port; + unsigned int i; + + xhci->base = (void *)baseaddr; + xhci->name[0] = '0' + xhci->id; /* Assert there are less than 10 xhci host */ + xhci->name[1] = '\0'; + + /* Initialise xHCI device */ + xhci_init ( xhci, xhci->base ); + + /* Initialise USB legacy support and claim ownership */ + xhci_legacy_init ( xhci ); + xhci_legacy_claim ( xhci ); + + /* Reset device */ + if ( ( error = xhci_reset ( xhci ) ) != 0 ) + goto err_reset; + + /* Set port protocols */ + for ( i = 1 ; i <= xhci->ports ; i++ ) { + port = usbh_get_roothub_port ( i ); + port->protocol = xhci_port_protocol ( xhci, i ); + } + + return error; + +err_reset: + xhci_legacy_release ( xhci ); + return -1; +} + +/*********************************************************************/ + +/** + * Allocate device context base address array + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_dcbaa_alloc ( struct xhci_host *xhci ) { + size_t len; + uintptr_t dcbaap; + int rc; + + /* Allocate and initialise structure. Must be at least + * 64-byte aligned and must not cross a page boundary, so + * align on its own size (rounded up to a power of two and + * with a minimum of 64 bytes). + */ + len = ( ( xhci->slots + 1 ) * sizeof ( xhci->dcbaa.context[0] ) ); + xhci->dcbaa.context = usb_align(xhci_align ( len ), len); + if ( ! xhci->dcbaa.context ) { + USB_LOG_ERR("XHCI %s could not allocate DCBAA\n", xhci->name ); + rc = -ENOMEM; + goto err_alloc; + } + memset ( xhci->dcbaa.context, 0, len ); + + /* Program DCBAA pointer */ + dcbaap = (uintptr_t)( xhci->dcbaa.context ); + if ( ( rc = xhci_writeq ( xhci, dcbaap, + xhci->op + XHCI_OP_DCBAAP ) ) != 0 ) + goto err_writeq; + + USB_LOG_DBG("XHCI %s DCBAA at [%08lx,%08lx)\n", xhci->name, + ( xhci->dcbaa.context ), + ( ( xhci->dcbaa.context ) + len ) ); + return 0; + + err_writeq: + usb_free ( xhci->dcbaa.context ); + err_alloc: + return rc; +} + + +/** + * Allocate scratchpad buffers + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_scratchpad_alloc ( struct xhci_host *xhci ) { + struct xhci_scratchpad *scratch = &xhci->scratch; + size_t buffer_len; + size_t array_len; + uintptr_t addr; + unsigned int i; + int rc; + + /* Do nothing if no scratchpad buffers are used */ + if ( ! scratch->count ) { + USB_LOG_INFO("XHCI %s no need to allocate scratchpad buffers\n", + xhci->name ); + return 0; + } + + /* Allocate scratchpad buffers */ + buffer_len = ( scratch->count * xhci->pagesize ); + scratch->buffer = (uintptr_t)usb_align ( xhci->pagesize, buffer_len ); + if ( ! scratch->buffer ) { + USB_LOG_ERR("XHCI %s could not allocate scratchpad buffers\n", + xhci->name ); + rc = -ENOMEM; + goto err_alloc; + } + memset ( (void *)scratch->buffer, 0, buffer_len ); + + /* Allocate scratchpad array */ + array_len = ( scratch->count * sizeof ( scratch->array[0] ) ); + scratch->array = usb_align(xhci_align ( array_len ), array_len); + if ( ! scratch->array ) { + USB_LOG_ERR("XHCI %s could not allocate scratchpad buffer " + "array\n", xhci->name ); + rc = -ENOMEM; + goto err_alloc_array; + } + + /* Populate scratchpad array */ + addr = ( scratch->buffer + 0 ); + for ( i = 0 ; i < scratch->count ; i++ ) { + scratch->array[i] = CPU_TO_LE64 ( addr ); + addr += xhci->pagesize; + } + + /* Set scratchpad array pointer */ + USB_ASSERT ( xhci->dcbaa.context != NULL ); + xhci->dcbaa.context[0] = CPU_TO_LE64 ( ( scratch->array ) ); + + USB_LOG_DBG("XHCI %s scratchpad [%08lx,%08lx) array [%08lx,%08lx)\n", + xhci->name, ( scratch->buffer, 0 ), + ( scratch->buffer, buffer_len ), + ( scratch->array ), + ( ( scratch->array ) + array_len ) ); + return 0; + + usb_free ( scratch->array ); + err_alloc_array: + usb_free ( scratch->buffer ); + err_alloc: + return rc; +} + +/** + * Allocate command ring + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_command_alloc ( struct xhci_host *xhci ) { + uintptr_t crp; + int rc; + + /* Allocate TRB ring */ + xhci->cmds = usb_align(XHCI_RING_SIZE, sizeof(*xhci->cmds)); /* command ring */ + if (! xhci->cmds) + goto err_ring_alloc; + + memset(xhci->cmds, 0U, sizeof(*xhci->cmds)); + + xhci->cmds->lock = usb_osal_mutex_create(); + USB_ASSERT(xhci->cmds->lock); + + xhci->cmds->cs = 1; /* cycle state = 1 */ + + /* Program command ring control register */ + crp = (uintptr_t)( xhci->cmds ); + if ( ( rc = xhci_writeq ( xhci, ( crp | XHCI_CRCR_RCS ), + xhci->op + XHCI_OP_CRCR ) ) != 0 ) + goto err_writeq; + + USB_LOG_DBG("XHCI %s CRCR at [%08lx,%08lx)\n", xhci->name, + ( xhci->cmds->ring ), + ( ( xhci->cmds->ring ) + sizeof(xhci->cmds->ring) ) ); + return 0; + + err_writeq: + usb_free(xhci->cmds);; + err_ring_alloc: + return rc; +} + +/** + * Allocate event ring + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_event_alloc ( struct xhci_host *xhci ) { + unsigned int count; + size_t len; + int rc; + + /* Allocate event ring */ + xhci->evts = usb_align(XHCI_RING_SIZE, sizeof(*xhci->evts)); /* event ring */ + if ( ! xhci->evts ) { + rc = -ENOMEM; + goto err_alloc_trb; + } + + memset(xhci->evts, 0U, sizeof(*xhci->evts)); + + /* Allocate event ring segment table */ + xhci->eseg = usb_align(XHCI_ALIGMENT, sizeof(*xhci->eseg)); /* event segment */ + if ( ! xhci->eseg ) { + rc = -ENOMEM; + goto err_alloc_segment; + } + memset(xhci->eseg, 0U, sizeof(*xhci->eseg)); + xhci->eseg->base = CPU_TO_LE64 ( ( xhci->evts ) ); + xhci->eseg->count = XHCI_RING_ITEMS; /* items of event ring TRB */ + + /* Program event ring registers */ + writel ( 1, xhci->run + XHCI_RUN_ERSTSZ ( 0 ) ); /* bit[15:0] event ring segment table size */ + if ( ( rc = xhci_writeq ( xhci, (uintptr_t)( xhci->evts ), + xhci->run + XHCI_RUN_ERDP ( 0 ) ) ) != 0 ) /* bit[63:4] event ring dequeue pointer */ + goto err_writeq_erdp; + if ( ( rc = xhci_writeq ( xhci, (uintptr_t)( xhci->eseg ), + xhci->run + XHCI_RUN_ERSTBA ( 0 ) ) ) != 0 ) /* bit[63:6] event ring segment table base addr */ + goto err_writeq_erstba; + + xhci->evts->cs = 1; /* cycle state = 1 */ + USB_LOG_DBG("XHCI %s event ring [%08lx,%08lx) table [%08lx,%08lx)\n", + xhci->name, ( xhci->evts->ring ), + ( ( xhci->evts->ring ) + sizeof(xhci->evts->ring) ), + ( xhci->eseg ), + ( ( xhci->eseg ) + + sizeof ( xhci->eseg[0] ) ) ); + return 0; + + xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERSTBA ( 0 ) ); + err_writeq_erstba: + xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERDP ( 0 ) ); + err_writeq_erdp: + usb_free ( xhci->eseg ); + err_alloc_segment: + usb_free ( xhci->evts ); + err_alloc_trb: + return rc; +} + +/** + * Start xHCI device + * + * @v xhci xHCI device + */ +static void xhci_run ( struct xhci_host *xhci ) { + uint32_t config; + uint32_t usbcmd; + uint32_t runtime; + + /* Configure number of device slots */ + config = readl ( xhci->op + XHCI_OP_CONFIG ); + config &= ~XHCI_CONFIG_MAX_SLOTS_EN_MASK; + config |= XHCI_CONFIG_MAX_SLOTS_EN ( xhci->slots ); + writel ( config, xhci->op + XHCI_OP_CONFIG ); + + /* Enable port interrupt */ + writel ( 500U, xhci->run + XHCI_RUN_IR_IMOD ( 0 ) ); + runtime = readl(xhci->run + XHCI_RUN_IR_IMAN ( 0 )); + runtime |= XHCI_RUN_IR_IMAN_IE; + writel (runtime, xhci->run + XHCI_RUN_IR_IMAN ( 0 )); + + /* Set run/stop bit and enable interrupt */ + usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); + usbcmd |= XHCI_USBCMD_RUN | XHCI_USBCMD_INTE; + writel ( usbcmd, xhci->op + XHCI_OP_USBCMD ); + + USB_LOG_DBG("XHCI %s start running\n", xhci->name ); +} + +/** + * Free event ring + * + * @v xhci xHCI device + */ +static void xhci_event_free ( struct xhci_host *xhci ) { + + /* Clear event ring registers */ + writel ( 0, xhci->run + XHCI_RUN_ERSTSZ ( 0 ) ); + xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERSTBA ( 0 ) ); + xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERDP ( 0 ) ); + + /* Free event ring segment table */ + usb_free ( xhci->eseg ); + + /* Free event ring */ + usb_free ( xhci->evts ); +} + +/** + * Free command ring + * + * @v xhci xHCI device + */ +static void xhci_command_free ( struct xhci_host *xhci ) { + + /* Sanity check */ + USB_ASSERT ( ( readl ( xhci->op + XHCI_OP_CRCR ) & XHCI_CRCR_CRR ) == 0 ); + + /* Clear command ring control register */ + xhci_writeq ( xhci, 0, xhci->op + XHCI_OP_CRCR ); + + /* Free TRB ring */ + usb_free ( xhci->cmds ); +} + +/** + * Free scratchpad buffers + * + * @v xhci xHCI device + */ +static void xhci_scratchpad_free ( struct xhci_host *xhci ) { + struct xhci_scratchpad *scratch = &xhci->scratch; + size_t array_len; + size_t buffer_len; + + /* Do nothing if no scratchpad buffers are used */ + if ( ! scratch->count ) + return; + + /* Clear scratchpad array pointer */ + USB_ASSERT ( xhci->dcbaa.context != NULL ); + xhci->dcbaa.context[0] = 0; + + /* Free scratchpad array */ + array_len = ( scratch->count * sizeof ( scratch->array[0] ) ); + usb_free ( scratch->array ); + + /* Free scratchpad buffers */ + buffer_len = ( scratch->count * xhci->pagesize ); + usb_free ( scratch->buffer ); +} + +/** + * Free device context base address array + * + * @v xhci xHCI device + */ +static void xhci_dcbaa_free ( struct xhci_host *xhci ) { + size_t len; + unsigned int i; + + /* Sanity check */ + for ( i = 0 ; i <= xhci->slots ; i++ ) + USB_ASSERT ( xhci->dcbaa.context[i] == 0 ); + + /* Clear DCBAA pointer */ + xhci_writeq ( xhci, 0, xhci->op + XHCI_OP_DCBAAP ); + + /* Free DCBAA */ + len = ( ( xhci->slots + 1 ) * sizeof ( xhci->dcbaa.context[0] ) ); + usb_free ( xhci->dcbaa.context ); +} + +/** + * Open XHCI device + * + * @v xhci XHCI device + * @ret rc Return status code + */ +int xhci_open ( struct xhci_host *xhci ) { + int rc; + + /* Allocate device slot array */ + xhci->slot = usb_malloc ( ( xhci->slots + 1 ) * sizeof ( xhci->slot[0] ) ); + if ( ! xhci->slot ) { + rc = -ENOMEM; + goto err_slot_alloc; + } + + /* Allocate device context base address array */ + if ( ( rc = xhci_dcbaa_alloc ( xhci ) ) != 0 ) + goto err_dcbaa_alloc; + + /* Allocate scratchpad buffers */ + if ( ( rc = xhci_scratchpad_alloc ( xhci ) ) != 0 ) + goto err_scratchpad_alloc; + + /* Allocate command ring */ + if ( ( rc = xhci_command_alloc ( xhci ) ) != 0 ) + goto err_command_alloc; + + /* Allocate event ring */ + if ( ( rc = xhci_event_alloc ( xhci ) ) != 0 ) + goto err_event_alloc; + + /* Start controller */ + xhci_run ( xhci ); + + return 0; + + xhci_stop ( xhci ); + xhci_event_free ( xhci ); + err_event_alloc: + xhci_command_free ( xhci ); + err_command_alloc: + xhci_scratchpad_free ( xhci ); + err_scratchpad_alloc: + xhci_dcbaa_free ( xhci ); + err_dcbaa_alloc: + usb_free ( xhci->slot ); + err_slot_alloc: + return rc; + +} + +/** + * Close XHCI device + * + * @v xhci XHCI Device + */ +void xhci_close ( struct xhci_host *xhci ) { + unsigned int i; + + /* Sanity checks */ + USB_ASSERT ( xhci->slot != NULL ); + for ( i = 0 ; i <= xhci->slots ; i++ ) + USB_ASSERT ( xhci->slot[i] == NULL ); + + xhci_stop ( xhci ); + usb_free (xhci->evts); + usb_free (xhci->eseg); + usb_free (xhci->cmds); + xhci_scratchpad_free ( xhci ); + xhci_dcbaa_free ( xhci ); + usb_free ( xhci->slot ); +} + +/** + * Remove XHCI device + * + * @v xhci XHCI device + */ +void xhci_remove ( struct xhci_host *xhci ) { + xhci_reset ( xhci ); + xhci_legacy_release ( xhci ); + usb_free ( xhci ); + + /* If we are shutting down to boot an OS, then prevent the + * release of ownership back to BIOS. + */ + xhci_legacy_prevent_release = 0; +} + +/*********************************************************************/ + +/** + * Enable port + * + * @v xhci XHCI device + * @v port Port number + * @ret rc Return status code + */ +int xhci_port_enable(struct xhci_host *xhci, uint32_t port) { + uint32_t portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) ); + + /* double check if connected */ + if (!(portsc & XHCI_PORTSC_CCS)) { + USB_LOG_ERR("device connectiong lost !!! \r\n"); + return -ENOENT; + } + + switch ( portsc & XHCI_PORTSC_PLS_MASK ) + { + case XHCI_PORTSC_PLS_U0: + /* A USB3 port - controller automatically performs reset */ + break; + case XHCI_PORTSC_PLS_POLLING: + /* A USB2 port - perform device reset */ + xhci_dump_port_status(port, portsc); + writel ((portsc | XHCI_PORTSC_PR), (xhci->op + XHCI_OP_PORTSC ( port ))); /* reset port */ + break; + default: + USB_LOG_ERR("PLS: %d \r\n", (portsc & XHCI_PORTSC_PLS_MASK)); + return -ENOENT; + } + + /* Wait for device to complete reset and be enabled */ + uint32_t end = 1000U, start = 0U; + for (;;) { + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) ); + if (!(portsc & XHCI_PORTSC_CCS)) { + /* USB 2.0 port would be disconnected after reset */ + USB_LOG_INFO("USB 2.0 port disconnected during reset, need rescan \r\n"); + return 0; + } + + if (portsc & XHCI_PORTSC_PED) { /* check if port enabled */ + /* Reset complete */ + break; + } + + if (++start > end) { + USB_LOG_ERR("Wait timeout, portsc=0x%x !!!\n", portsc); + return -ENXIO; + } + + usb_osal_msleep(1); + } + + xhci_dump_port_status(port, portsc); + return 0; +} + +/** + * Convert USB Speed to PSI + * + * @v speed USB speed + * @ret psi USB speed in PSI + */ +static unsigned int xhci_speed_to_psi(int speed) { + unsigned int psi = USB_SPEED_UNKNOWN; + + switch (speed) { + case USB_SPEED_LOW: + psi = XCHI_PSI_LOW; + break; + case USB_SPEED_FULL: + psi = XCHI_PSI_FULL; + break; + case USB_SPEED_HIGH: + psi = XCHI_PSI_HIGH; + break; + case USB_SPEED_SUPER: + psi = XCHI_PSI_SUPER; + break; + } + + return psi; +} + +/** + * Convert USB PSI to Speed + * + * @v psi USB speed in PSI + * @ret speed USB speed + */ +static int xhci_psi_to_speed(int psi) { + int speed = USB_SPEED_UNKNOWN; + + switch (psi) { + case XCHI_PSI_LOW: + speed = USB_SPEED_LOW; + break; + case XCHI_PSI_FULL: + speed = USB_SPEED_FULL; + break; + case XCHI_PSI_HIGH: + speed = USB_SPEED_HIGH; + break; + case XCHI_PSI_SUPER: + speed = USB_SPEED_SUPER; + break; + } + + return speed; +} + +/** + * Find port speed + * + * @v xhci xHCI device + * @v port Port number + * @v psiv Protocol speed ID value + * @ret speed Port speed, or negative error + */ +static int xhci_port_speed ( struct xhci_host *xhci, unsigned int port, + unsigned int psiv ) { + unsigned int supported = xhci_supported_protocol ( xhci, port ); + unsigned int psic; + unsigned int mantissa; + unsigned int exponent; + unsigned int speed; + unsigned int i; + uint32_t ports; + uint32_t psi; + + /* Fail if there is no supported protocol */ + if ( ! supported ) + return -ENOTSUP; + + /* Get protocol speed ID count */ + ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); + psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); + + /* Use protocol speed ID table unless device is known to be faulty */ + /* Iterate over PSI dwords looking for a match */ + for ( i = 0 ; i < psic ; i++ ) { + psi = readl ( xhci->cap + supported + + XHCI_SUPPORTED_PSI ( i ) ); + if ( psiv == XHCI_SUPPORTED_PSI_VALUE ( psi ) ) { + mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); + exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); + return xhci_psi_to_speed(XCHI_PSI ( mantissa, exponent )); + } + } + + /* Record device as faulty if no match is found */ + if ( psic != 0 ) { + USB_LOG_ERR("XHCI %s-%d spurious PSI value %d: " + "assuming PSI table is invalid\n", + xhci->name, port, psiv ); + } + + /* Use the default mappings */ + switch ( psiv ) { + case XHCI_SPEED_LOW : return USB_SPEED_LOW; + case XHCI_SPEED_FULL : return USB_SPEED_FULL; + case XHCI_SPEED_HIGH : return USB_SPEED_HIGH; + case XHCI_SPEED_SUPER : return USB_SPEED_SUPER; + default: + USB_LOG_ERR("XHCI %s-%d unrecognised PSI value %d\n", + xhci->name, port, psiv ); + return -ENOTSUP; + } +} + +/** + * Find protocol speed ID value + * + * @v xhci xHCI device + * @v port Port number + * @v speed USB speed + * @ret psiv Protocol speed ID value, or negative error + */ +static int xhci_port_psiv ( struct xhci_host *xhci, unsigned int port, + unsigned int speed ) { + unsigned int supported = xhci_supported_protocol ( xhci, port ); + unsigned int psic; + unsigned int mantissa; + unsigned int exponent; + unsigned int psiv; + unsigned int i; + uint32_t ports; + uint32_t psi; + + /* Fail if there is no supported protocol */ + if ( ! supported ) + return -ENOTSUP; + + /* Get protocol speed ID count */ + ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); + psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); + + /* Use the default mappings if applicable */ + if ( psic == 0 ) { + switch ( speed ) { + case USB_SPEED_LOW : return XHCI_SPEED_LOW; + case USB_SPEED_FULL : return XHCI_SPEED_FULL; + case USB_SPEED_HIGH : return XHCI_SPEED_HIGH; + case USB_SPEED_SUPER : return XHCI_SPEED_SUPER; + default: + USB_LOG_DBG("XHCI %s-%d non-standard speed %d\n", + xhci->name, port, speed ); + return -ENOTSUP; + } + } + + /* Iterate over PSI dwords looking for a match */ + for ( i = 0 ; i < psic ; i++ ) { + psi = readl ( xhci->cap + supported + XHCI_SUPPORTED_PSI ( i )); + mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); + exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); + if ( xhci_speed_to_psi(speed) == XCHI_PSI ( mantissa, exponent ) ) { + psiv = XHCI_SUPPORTED_PSI_VALUE ( psi ); + return psiv; + } + } + + USB_LOG_DBG("XHCI %s-%d unrepresentable speed %#x\n", + xhci->name, port, speed ); + return -ENOENT; +} + +/** + * Update root hub port speed + * + * @v xhci XHCI device + * @v port Port number + * @ret rc Return status code (speed) + */ +uint32_t xhci_root_speed ( struct xhci_host *xhci, uint8_t port ) { + uint32_t portsc; + unsigned int psiv; + int ccs; + int ped; + int csc; + int speed; + unsigned int protocol = xhci_port_protocol(xhci, port); + + /* Read port status */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) ); + USB_LOG_DBG("XHCI %s-%d status is 0x%08x, protocol 0x%x\n", + xhci->name, port, portsc, protocol ); + ccs = ( portsc & XHCI_PORTSC_CCS ); + ped = ( portsc & XHCI_PORTSC_PED ); + csc = ( portsc & XHCI_PORTSC_CSC ); + psiv = XHCI_PORTSC_PSIV ( portsc ); + + USB_LOG_DBG("XHCI %s port-%d ccs: %d, ped: %d, csc: %d, psiv: 0x%x\n", + xhci->name, port, !!ccs, !!ped, !!csc, psiv); + + /* Port speed is not valid unless port is connected */ + if ( ! ccs ) { + speed = USB_SPEED_UNKNOWN; + USB_LOG_ERR("XHCI %s port-%d speed unkown\n", xhci->name, port); + return speed; + } + + /* For USB2 ports, the PSIV field is not valid until the port + * completes reset and becomes enabled. + */ + if ( ( protocol < USB_3_0 ) && ! ped ) { + speed = USB_SPEED_FULL; + return speed; + } + + /* Get port speed and map to generic USB speed */ + speed = xhci_port_speed ( xhci, port, psiv ); + if ( speed < 0 ) { + return speed; + } + + return speed; +} + +/*********************************************************************/ +/** + * Add a TRB to the given ring + * + * @v ring XHCI TRB ring + * @v trb TRB content to be added + */ +static inline void xhci_trb_fill(struct xhci_ring *ring, union xhci_trb *trb) { + union xhci_trb *dst = &ring->ring[ring->nidx]; + memcpy((void *)dst, (void *)trb, sizeof(*trb)); + dst->template.control |= (ring->cs ? XHCI_TRB_C : 0); + xhci_dump_trbs(dst, 1); +} + +/** + * Queue a TRB onto a ring, wrapping ring as needed + * + * @v ring XHCI TRB ring + * @v trb TRB content to be added + */ +static void xhci_trb_queue(struct xhci_ring *ring, union xhci_trb *trb) { + + if (ring->nidx >= XHCI_RING_ITEMS - 1) { + /* if it is the last trb in the list, put a link trb in the end */ + union xhci_trb link_trb; + link_trb.link.type = XHCI_TRB_LINK; + link_trb.link.flags = XHCI_TRB_TC; + link_trb.link.next = CPU_TO_LE64((ring->ring)); + + xhci_trb_fill(ring, &link_trb); + + ring->nidx = 0; /* adjust dequeue index to 0 */ + ring->cs ^= 1; /* toggle cycle interpretation of sw */ + } + + xhci_trb_fill(ring, trb); + ring->nidx++; /* ahead dequeue index */ +} + +/** + * Wait for a ring to empty (all TRBs processed by hardware) + * + * @v xhci XHCI Device + * @v ep Owner Endpoint of current TRB ring + * @ ring XHCI TRB ring + */ +int xhci_event_wait(struct xhci_host *xhci, + struct xhci_endpoint *ep, + struct xhci_ring *ring) { + int cc = XHCI_CMPLT_SUCCESS; + + if (ep->timeout > 0) + { + if (usb_osal_sem_take(ep->waitsem, ep->timeout) < 0) + { + cc = XHCI_CMPLT_TIMEOUT; + } + else + { + cc = ring->evt.complete.code; /* bit[31:24] completion code */ + } + } + + return cc; +} + +/** + * Ring doorbell register + * + * @v xhci XHCI device + * @v slotid Slot id to ring + * @v value Value send to doorbell + */ +static inline void xhci_doorbell ( struct xhci_host *xhci, uint32_t slotid, uint32_t value ) { + + DSB(); + writel ( value, xhci->db + slotid * XHCI_REG_DB_SIZE ); /* bit[7:0] db target, is ep_id */ +} + +/** + * Abort command + * + * @v xhci xHCI device + */ +static void xhci_abort ( struct xhci_host *xhci ) { + uintptr_t crp; + + /* Abort the command */ + USB_LOG_WRN("XHCI %s aborting command\n", xhci->name ); + xhci_writeq ( xhci, XHCI_CRCR_CA, xhci->op + XHCI_OP_CRCR ); + + /* Allow time for command to abort */ + usb_osal_msleep ( XHCI_COMMAND_ABORT_DELAY_MS ); + + /* Sanity check */ + USB_ASSERT ( ( readl ( xhci->op + XHCI_OP_CRCR ) & XHCI_CRCR_CRR ) == 0 ); + + /* Consume (and ignore) any final command status */ + int cc = xhci_event_wait(xhci, xhci->cur_cmd_pipe, xhci->cmds); + if (XHCI_CMPLT_SUCCESS != cc) { + USB_LOG_ERR("XHCI %s abort command failed, cc %d\n", xhci->name, cc); + } + + /* Reset the command ring control register */ + memset(xhci->cmds->ring, 0U, XHCI_RING_ITEMS * sizeof(union xhci_trb)); + xhci_writeq ( xhci, ( (uint64_t)(uintptr_t)xhci->cmds | xhci->cmds->cs ), xhci->op + XHCI_OP_CRCR ); +} + +/** + * Submit a command to the xhci controller ring + * + * @v xhci XHCI Device + * @v ep Owner Endpoint of current TRB ring + * @ trb Command TRB to be sent + */ +static int xhci_cmd_submit(struct xhci_host *xhci, struct xhci_endpoint *ep, union xhci_trb *trb) { + + int rc = 0; + usb_osal_mutex_take(xhci->cmds->lock); /* handle command one by one */ + + ep->timeout = 5000; + ep->waiter = true; + xhci->cur_cmd_pipe = ep; + + xhci_trb_queue(xhci->cmds, trb); + + /* pass command trb to hardware */ + DSB(); + + xhci_doorbell(xhci, 0, 0); /* 0 = db host controller, 0 = db targe hc command */ + int cc = xhci_event_wait(xhci, ep, xhci->cmds); + if (XHCI_CMPLT_SUCCESS != cc) { + USB_LOG_ERR("XHCI %s cmd failed, cc %d\n", xhci->name, cc); + xhci_abort(xhci); /* Abort command */ + rc = -ENOTSUP; + } + + usb_osal_mutex_give(xhci->cmds->lock); + xhci->cur_cmd_pipe = NULL; + return rc; +} + +/** + * Issue NOP and wait for completion + * + * @v xhci xHCI device + * @v ep Command Endpoint + * @ret rc Return status code + */ +static int xhci_nop ( struct xhci_host *xhci, struct xhci_endpoint *ep ) { + union xhci_trb trb; + struct xhci_trb_common *nop = &trb.common; + int rc; + + /* Construct command */ + memset ( nop, 0, sizeof ( *nop ) ); + nop->flags = XHCI_TRB_IOC; + nop->type = XHCI_TRB_NOP_CMD; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_cmd_submit(xhci, ep, &trb ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Issue Enable slot and wait for completion + * + * @v xhci xHCI device + * @v ep Command Endpoint + * @v type Type of Slot to be enabled + * @ret rc Return status code + */ +static int xhci_enable_slot(struct xhci_host *xhci, struct xhci_endpoint *ep, unsigned int type) { + union xhci_trb trb; + struct xhci_trb_enable_slot *enable = &trb.enable; + struct xhci_trb_complete *enabled; + unsigned int slot; + int rc; + + /* Construct command */ + memset ( enable, 0, sizeof ( *enable ) ); + enable->slot = type; + enable->type = XHCI_TRB_ENABLE_SLOT; + + /* Issue command and Wait for completion */ + if ( ( rc = xhci_cmd_submit(xhci, ep, &trb) ) != 0 ) { + USB_LOG_ERR("XHCI %s could not enable new slot, type %d\n", + xhci->name, type ); + return rc; + } + + /* Extract slot number */ + enabled = &(xhci->cmds->evt.complete); + slot = enabled->slot; + + USB_LOG_DBG("XHCI %s slot %d enabled\n", xhci->name, slot ); + return slot; +} + +/** + * Disable slot + * + * @v xhci xHCI device + * @v ep Command Endpoint + * @v slot Device slot + * @ret rc Return status code + */ +static int xhci_disable_slot ( struct xhci_host *xhci, struct xhci_endpoint *ep, + unsigned int slot ) { + union xhci_trb trb; + struct xhci_trb_disable_slot *disable = &trb.disable; + int rc; + + /* Construct command */ + memset ( disable, 0, sizeof ( *disable ) ); + disable->type = XHCI_TRB_DISABLE_SLOT; + disable->slot = slot; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_cmd_submit ( xhci, ep, &trb ) ) != 0 ) { + USB_LOG_DBG("XHCI %s could not disable slot %d: %s\n", + xhci->name, slot, strerror ( rc ) ); + return rc; + } + + USB_LOG_DBG("XHCI %s slot %d disabled\n", xhci->name, slot ); + return 0; +} + +/** + * Issue context-based command and wait for completion + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v type TRB type + * @v populate Input context populater + * @ret rc Return status code + */ +static int xhci_context ( struct xhci_host *xhci, struct xhci_slot *slot, + struct xhci_endpoint *ep, unsigned int type, + void ( * populate ) ( struct xhci_host *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *ep, + void *input ) ) { + union xhci_trb trb; + struct xhci_trb_context *context = &trb.context; + size_t len; + void *input; + int rc; + + /* Allocate an input context */ + len = xhci_input_context_offset ( xhci, XHCI_CTX_END ); + input = usb_align(xhci_align ( len ), len); + if ( ! input ) { + rc = -ENOMEM; + goto err_alloc; + } + memset ( input, 0, len ); + + /* Populate input context */ + populate ( xhci, slot, ep, input ); + + /* Construct command */ + memset ( context, 0, sizeof ( *context ) ); + context->type = type; + context->input = CPU_TO_LE64 ( ( input ) ); + context->slot = slot->id; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_cmd_submit ( xhci, ep, &trb ) ) != 0 ) { + xhci_dump_input_ctx(xhci, ep, input); + goto err_command; + } + + err_command: + usb_free ( input ); + err_alloc: + return rc; +} + +/** + * Populate address device input context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v input Input context + */ +static void xhci_address_device_input ( struct xhci_host *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint, + void *input ) { + struct xhci_control_context *control_ctx; + struct xhci_slot_context *slot_ctx; + struct xhci_endpoint_context *ep_ctx; + + /* Sanity checks */ + USB_ASSERT ( endpoint->ctx == XHCI_CTX_EP0 ); + + /* Populate control context, add slot context and ep context */ + control_ctx = input; + control_ctx->add = CPU_TO_LE32 ( ( 1 << XHCI_CTX_SLOT ) | + ( 1 << XHCI_CTX_EP0 ) ); + + /* Populate slot context */ + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + slot_ctx->info = CPU_TO_LE32 ( XHCI_SLOT_INFO ( 1, 0, slot->psiv, + slot->route ) ); + slot_ctx->port = slot->port; + slot_ctx->tt_id = slot->tt_id; + slot_ctx->tt_port = slot->tt_port; + + /* Populate control endpoint context */ + ep_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_EP0 ) ); + ep_ctx->type = XHCI_EP_TYPE_CONTROL; /* bit[5:3] endpoint type */ + ep_ctx->burst = endpoint->burst; /* bit[16:8] max burst size */ + ep_ctx->mtu = CPU_TO_LE16 ( endpoint->mtu ); /* bit[31:16] max packet size */ + + /* + bit[63:4] tr dequeue pointer + bit[0] dequeue cycle state + */ + ep_ctx->dequeue = CPU_TO_LE64 ( (uint64_t)( &endpoint->reqs.ring[0] ) | XHCI_EP_DCS ); + ep_ctx->trb_len = CPU_TO_LE16 ( XHCI_EP0_TRB_LEN ); /* bit[15:0] average trb length */ +} + +/** + * Address device + * + * @v xhci xHCI device + * @v endpoint Endpoint + * @v slot Device slot + * @ret rc Return status code + */ +static inline int xhci_address_device ( struct xhci_host *xhci, + struct xhci_endpoint *ep, + struct xhci_slot *slot ) { + struct xhci_slot_context *slot_ctx; + int rc; + + /* Assign device address */ + if ( ( rc = xhci_context ( xhci, slot, slot->endpoint[XHCI_CTX_EP0], + XHCI_TRB_ADDRESS_DEVICE, + xhci_address_device_input ) ) != 0 ) + + USB_LOG_DBG("XHCI %s slot ctx 0x%x\n", xhci->name, slot->context); + + /* Get assigned address for check */ + slot_ctx = ( slot->context + + xhci_device_context_offset ( xhci, XHCI_CTX_SLOT ) ); + USB_LOG_DBG("XHCI %s slot ctx 0x%x assigned address 0x%x\n", + xhci->name, slot_ctx, slot_ctx->address ); + + return rc; +} + +/** + * Reset endpoint + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +int xhci_reset_endpoint ( struct xhci_host *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + union xhci_trb trb; + struct xhci_trb_reset_endpoint *reset = &trb.reset; + int rc; + + /* Construct command */ + memset ( reset, 0, sizeof ( *reset ) ); + reset->slot = slot->id; + reset->endpoint = endpoint->ctx; + reset->type = XHCI_TRB_RESET_ENDPOINT; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_cmd_submit ( xhci, endpoint, &trb ) ) != 0 ) { + USB_LOG_DBG("XHCI %s slot %d ctx %d could not reset endpoint " + "in state %d: %s\n", xhci->name, slot->id, endpoint->ctx, + endpoint->context->state, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Stop endpoint + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_stop_endpoint ( struct xhci_host *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + union xhci_trb trb; + struct xhci_trb_stop_endpoint *stop = &trb.stop; + int rc; + + /* Construct command */ + memset ( stop, 0, sizeof ( *stop ) ); + stop->slot = slot->id; + stop->endpoint = endpoint->ctx; + stop->type = XHCI_TRB_STOP_ENDPOINT; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_cmd_submit ( xhci, endpoint, &trb ) ) != 0 ) { + USB_LOG_DBG("XHCI %s slot %d ctx %d could not stop endpoint " + "in state %d: %s\n", xhci->name, slot->id, endpoint->ctx, + endpoint->context->state, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/*********************************************************************/ + +/** + * Find port slot type + * + * @v xhci xHCI device + * @v port Port number + * @ret type Slot type, or negative error + */ +static int xhci_port_slot_type ( struct xhci_host *xhci, unsigned int port ) { + unsigned int supported = xhci_supported_protocol ( xhci, port ); + unsigned int type; + uint32_t slot; + + /* Fail if there is no supported protocol */ + if ( ! supported ) + return -ENOTSUP; + + /* Get slot type */ + slot = readl ( xhci->cap + supported + XHCI_SUPPORTED_SLOT ); + type = XHCI_SUPPORTED_SLOT_TYPE ( slot ); + + return type; +} + +/** + * Open device + * + * @v xhci XHCI device + * @v ep Endpoint + * @ret slot_id Return device slot id + * @ret rc Return status code + */ +int xhci_device_open ( struct xhci_host *xhci, struct xhci_endpoint *ep, int *slot_id ) { + struct usbh_hubport *hport = ep->hport; + struct usbh_hubport *tt = usbh_transaction_translator(hport); + struct xhci_slot *slot; + struct xhci_slot *tt_slot; + int type; + int rc; + int id; + size_t len; + + /* Determine applicable slot type */ + type = xhci_port_slot_type ( xhci, hport->port ); + if ( type < 0 ) { + rc = type; + USB_LOG_ERR("XHCI %s-%d has no slot type\n", + xhci->name, hport->port ); + goto err_type; + } + + /* Allocate a device slot number */ + id = xhci_enable_slot ( xhci, ep, type ); + if ( id < 0 ) { + rc = id; + goto err_enable_slot; + } + + USB_ASSERT ( ( id > 0 ) && ( ( unsigned int ) id <= xhci->slots ) ); + USB_ASSERT ( xhci->slot[id] == NULL ); + + /* Allocate and initialise structure */ + slot = usb_malloc ( sizeof ( *slot ) ); + if ( ! slot ) { + rc = -ENOMEM; + goto err_alloc; + } + slot->id = id; + xhci->slot[id] = slot; + slot->xhci = xhci; + if ( tt ) { + tt_slot = xhci->slot[tt->dev_addr]; + slot->tt_id = tt_slot->id; + slot->tt_port = tt->port; + } + + /* Allocate a device context */ + len = xhci_device_context_offset ( xhci, XHCI_CTX_END ); + slot->context = usb_align(xhci_align ( len ), len); + if ( ! slot->context ) { + rc = -ENOMEM; + goto err_alloc_context; + } + memset ( slot->context, 0, len ); + + /* Set device context base address */ + USB_ASSERT ( xhci->dcbaa.context[id] == 0 ); + xhci->dcbaa.context[id] = CPU_TO_LE64 ( ( slot->context ) ); + + USB_LOG_DBG("XHCI %s slot %d device context [%08lx,%08lx)\n", + xhci->name, slot->id, ( slot->context ), + ( ( slot->context ) + len ) ); + *slot_id = id; + return 0; + + xhci->dcbaa.context[id] = 0; + usb_free ( slot->context ); + +err_alloc_context: + xhci->slot[id] = NULL; + usb_free ( slot ); +err_alloc: + xhci_disable_slot ( xhci, ep, id ); +err_enable_slot: +err_type: + return rc; +} + +/*********************************************************************/ + +/** + * Assign device address + * + * @v xhci XHCI device + * @v slot Slot + * @v ep Endpoint + * @ret rc Return status code + */ +int xhci_device_address ( struct xhci_host *xhci, struct xhci_slot *slot, struct xhci_endpoint *ep ) { + USB_ASSERT((slot->xhci) && (ep->hport)); + struct usbh_hubport *hport = ep->hport; + int psiv; + int rc; + + /* Calculate route string */ + slot->route = usbh_route_string (hport); + + /* Calculate root hub port number */ + struct usbh_hubport *root_port = usbh_root_hub_port (hport); + slot->port = root_port->port; + + /* Calculate protocol speed ID */ + psiv = xhci_port_psiv ( xhci, slot->port, hport->speed ); + if ( psiv < 0 ) { + rc = psiv; + return rc; + } + slot->psiv = psiv; + + /* Address device */ + if ( ( rc = xhci_address_device ( xhci, ep, slot ) ) != 0 ) + return rc; + + return 0; +} + + +/** + * Close device + * + * @v slot Slot + */ +void xhci_device_close ( struct xhci_slot *slot ) { + struct xhci_host *xhci = slot->xhci; + size_t len = xhci_device_context_offset ( xhci, XHCI_CTX_END ); + unsigned int id = slot->id; + int rc; + + /* Disable slot */ + if ( ( rc = xhci_disable_slot ( xhci, slot->endpoint[0], id ) ) != 0 ) { + /* Slot is still enabled. Leak the slot context, + * since the controller may still write to this + * memory, and leave the DCBAA entry intact. + * + * If the controller later reports that this same slot + * has been re-enabled, then some assertions will be + * triggered. + */ + USB_LOG_DBG("XHCI %s slot %d leaking context memory\n", + xhci->name, slot->id ); + slot->context = NULL; + } + + /* Free slot */ + if ( slot->context ) { + usb_free ( slot->context ); + xhci->dcbaa.context[id] = 0; + } + xhci->slot[id] = NULL; + usb_free ( slot ); +} + + +/** + * Populate configure endpoint input context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v input Input context + */ +static void xhci_configure_endpoint_input ( struct xhci_host *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint, + void *input ) { + struct xhci_control_context *control_ctx; + struct xhci_slot_context *slot_ctx; + struct xhci_endpoint_context *ep_ctx; + + xhci_dump_endpoint(endpoint); + + /* Populate control context */ + control_ctx = input; + control_ctx->add = CPU_TO_LE32 (( 1 << XHCI_CTX_SLOT ) | ( 1 << endpoint->ctx ) ); + control_ctx->drop = CPU_TO_LE32 (( 1 << XHCI_CTX_SLOT ) | ( 1 << endpoint->ctx ) ); + + /* Populate slot context */ + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + slot_ctx->info = CPU_TO_LE32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ), + ( slot->ports ? 1 : 0 ), + slot->psiv, 0 ) ); + slot_ctx->ports = slot->ports; + + /* Populate endpoint context */ + ep_ctx = ( input + xhci_input_context_offset ( xhci, endpoint->ctx ) ); + ep_ctx->interval = endpoint->interval; /* bit[23:16] for interrupt ep, set interval to control interrupt period */ + /* + Value Endpoint Type Direction + 0 Not Valid N/A + 1 Isoch Out + 2 Bulk Out + 3 Interrupt Out + 4 Control Bidirectional + 5 Isoch In + 6 Bulk In + 7 Interrupt In + */ + ep_ctx->type = endpoint->ctx_type; + ep_ctx->burst = endpoint->burst; + ep_ctx->mtu = CPU_TO_LE16 ( endpoint->mtu ); /* bit[31:16] max packet size */ + ep_ctx->dequeue = CPU_TO_LE64 ( (uint64_t)( &(endpoint->reqs.ring[0]) ) | XHCI_EP_DCS ); + + /* TODO: endpoint attached on hub may need different setting here */ + if (endpoint->ep_type == USB_ENDPOINT_TYPE_BULK) { + ep_ctx->trb_len = CPU_TO_LE16 ( 256U ); /* bit[15:0] average trb length */ + } else if (endpoint->ep_type == USB_ENDPOINT_TYPE_INTERRUPT) { + ep_ctx->trb_len = CPU_TO_LE16 (16U); + ep_ctx->esit_low = CPU_TO_LE16 ( endpoint->mtu ); /* bit[31:16] max ESIT payload */ + } + + xhci_dump_input_ctx(xhci, endpoint, input); +} + +/** + * Configure endpoint + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_configure_endpoint ( struct xhci_host *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ){ + int rc; + + /* Configure endpoint */ + if ( ( rc = xhci_context ( xhci, slot, endpoint, + XHCI_TRB_CONFIGURE_ENDPOINT, + xhci_configure_endpoint_input ) ) != 0 ){ + USB_LOG_ERR("XHCI %s slot %d ctx %d configure failed, error %d !!!\n", + xhci->name, slot->id, endpoint->ctx, rc ); + return rc; + } + + /* Sanity check */ + if ( ( endpoint->context->state & XHCI_ENDPOINT_STATE_MASK ) + != XHCI_ENDPOINT_RUNNING ){ + xhci_dump_slot_ctx(endpoint->slot->context); + xhci_dump_ep_ctx(endpoint->context); + USB_LOG_ERR("XHCI %s slot %d ctx %d configure failed !!!\n", + xhci->name, slot->id, endpoint->ctx ); + return -1; + } + + USB_LOG_DBG("XHCI %s slot %d ctx %d configured\n", + xhci->name, slot->id, endpoint->ctx ); + return 0; +} + +/** + * Populate deconfigure endpoint input context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v input Input context + */ +static void +xhci_deconfigure_endpoint_input ( struct xhci_host *xhci __unused, + struct xhci_slot *slot __unused, + struct xhci_endpoint *endpoint, + void *input ) { + struct xhci_control_context *control_ctx; + struct xhci_slot_context *slot_ctx; + + /* Populate control context */ + control_ctx = input; + control_ctx->add = CPU_TO_LE32 ( 1 << XHCI_CTX_SLOT ); + control_ctx->drop = CPU_TO_LE32 ( 1 << endpoint->ctx ); + + /* Populate slot context */ + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + slot_ctx->info = CPU_TO_LE32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ), + 0, 0, 0 ) ); +} + +/** + * Deconfigure endpoint + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_deconfigure_endpoint ( struct xhci_host *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + int rc; + + /* Deconfigure endpoint */ + if ( ( rc = xhci_context ( xhci, slot, endpoint, + XHCI_TRB_CONFIGURE_ENDPOINT, + xhci_deconfigure_endpoint_input ) ) != 0 ) + return rc; + + USB_LOG_DBG("XHCI %s slot %d ctx %d deconfigured\n", + xhci->name, slot->id, endpoint->ctx ); + return 0; +} + +/*********************************************************************/ + +/** + * Open control endpoint + * + * @v xhci XHCI device + * @v slot Slot + * @v ep Endpoint + * @ret rc Return status code + */ +int xhci_ctrl_endpoint_open ( struct xhci_host *xhci, struct xhci_slot *slot, struct xhci_endpoint *ep ) { + unsigned int ctx; + + /* Calculate context index */ + ctx = XHCI_CTX ( ep->address ); + USB_ASSERT ( slot->endpoint[ctx] == NULL ); + + if (USB_ENDPOINT_TYPE_CONTROL != ep->ep_type) { + return -EINVAL; + } + + /* initialise structure */ + slot->endpoint[ctx] = ep; + ep->xhci = xhci; + ep->slot = slot; + ep->ctx = ctx; + ep->ctx_type = XHCI_EP_TYPE_CONTROL; + ep->context = ( ( ( void * ) slot->context ) + + xhci_device_context_offset ( xhci, ctx ) ); + ep->reqs.cs = 1; /* cycle state = 1 */ + + USB_LOG_DBG("XHCI %s slot %d endpoint 0x%x ep type %d xhci ep type 0x%x\n", + xhci->name, slot->id, ep->address, ep->ep_type, + (ep->ctx_type >> 3) ); + + USB_LOG_DBG("XHCI %s slot %d ctx %d ring [%08lx,%08lx)\n", + xhci->name, slot->id, ctx, ( ep->reqs.ring ), + ( ( ep->reqs.ring ) + sizeof(ep->reqs.ring) ) ); + + return 0; +} + +/*********************************************************************/ + +/** + * Open work endpoint + * + * @v xhci XHCI device + * @v slot Slot + * @v ep USB endpoint + * @ret rc Return status code + */ +int xhci_work_endpoint_open ( struct xhci_host *xhci, struct xhci_slot *slot, struct xhci_endpoint *ep ) { + unsigned int ctx; + unsigned int interval; + unsigned int ctx_type; + int rc; + + /* Calculate context index */ + ctx = XHCI_CTX ( ep->address ); + USB_ASSERT ( slot->endpoint[ctx] == NULL ); + + if (USB_ENDPOINT_TYPE_CONTROL == ep->ep_type) { + return -EINVAL; + } + + /* Calculate endpoint type */ + /* + Value Endpoint Type Direction, bit[5:3] + 0 Not Valid N/A + 1 Isoch Out + 2 Bulk Out + 3 Interrupt Out + 4 Control Bidirectional + 5 Isoch In + 6 Bulk In + 7 Interrupt In + */ + ctx_type = XHCI_EP_TYPE ( ep->ep_type ); + if ( ep->address & USB_EP_DIR_IN ) + ctx_type |= XHCI_EP_TYPE_IN; + + /* initialise structure */ + slot->endpoint[ctx] = ep; + ep->xhci = xhci; + ep->slot = slot; + ep->ctx = ctx; + + /* Calculate interval */ + if ( ctx_type & XHCI_EP_TYPE_PERIODIC ) { + ep->interval = ( fls ( ep->interval ) - 1 ); + } + + ep->ctx_type = ctx_type; + ep->context = ( ( ( void * ) slot->context ) + + xhci_device_context_offset ( xhci, ctx ) ); + ep->reqs.cs = 1; /* cycle state = 1 */ + + USB_LOG_DBG("XHCI %s slot %d endpoint 0x%x ep type %d xhci ep type 0x%x\n", + xhci->name, slot->id, ep->address, ep->ep_type, + (ep->ctx_type >> 3) ); + + /* Configure endpoint */ + if (( rc = xhci_configure_endpoint ( xhci, slot, ep ) ) != 0) { + goto err_configure_endpoint; + } + + USB_LOG_DBG("XHCI %s slot %d ctx %d ring [%08lx,%08lx)\n", + xhci->name, slot->id, ctx, ( ep->reqs.ring ), + ( ( ep->reqs.ring ) + sizeof(ep->reqs.ring) ) ); + + return 0; + err_configure_endpoint: + (void)xhci_deconfigure_endpoint ( xhci, slot, ep ); + slot->endpoint[ctx] = NULL; + return rc; +} + +/** + * Close endpoint + * + * @v ep USB endpoint + */ +void xhci_endpoint_close ( struct xhci_endpoint *ep ) { + struct xhci_slot *slot = ep->slot; + struct xhci_host *xhci = slot->xhci; + unsigned int ctx = ep->ctx; + + /* Deconfigure endpoint, if applicable */ + if ( ctx != XHCI_CTX_EP0 ) + (void)xhci_deconfigure_endpoint ( xhci, slot, ep ); + + slot->endpoint[ctx] = NULL; + usb_free(ep); +} + +/*********************************************************************/ + +/** + * Enqueue message transfer + * + * @v ep USB endpoint + * @v packet Setup packet buffer + * @v data_buff Data buffer + * @v datalen Data length + * @ret rc Return status code + */ +void xhci_endpoint_message ( struct xhci_endpoint *ep, + struct usb_setup_packet *packet, + void *data_buff, + int datalen ) { + struct xhci_host *xhci = ep->xhci; + struct xhci_slot *slot = ep->slot; + union xhci_trb trb; + struct xhci_trb_setup *setup; + struct xhci_trb_data *data; + struct xhci_trb_status *status; + unsigned int input; + + /* Construct setup stage TRB */ + setup = &(trb.setup); + memset ( setup, 0, sizeof ( *setup ) ); + + memcpy ( &setup->packet, packet, sizeof ( setup->packet ) ); + setup->len = CPU_TO_LE32 ( sizeof ( *packet ) ); /* bit[16:0] trb transfer length, always 8 */ + setup->flags = XHCI_TRB_IDT; /* bit[6] Immediate Data (IDT), parameters take effect */ + setup->type = XHCI_TRB_SETUP; /* bit[15:10] trb type */ + input = ( packet->bmRequestType & CPU_TO_LE16 ( USB_REQUEST_DIR_IN ) ); + if (datalen > 0) { + /* bit[17:16] Transfer type, 2 = OUT Data, 3 = IN Data */ + setup->direction = ( input ? XHCI_SETUP_IN : XHCI_SETUP_OUT ); + } + + xhci_trb_queue(&(ep->reqs), &trb); + + /* Construct data stage TRB, if applicable */ + if (datalen > 0) { + data = &(trb.data); + memset ( data, 0, sizeof ( *data ) ); + + data->data = CPU_TO_LE64 ( data_buff ); + data->len = CPU_TO_LE32 ( datalen ); + data->type = XHCI_TRB_DATA; /* bit[15:10] trb type */ + data->direction = ( input ? XHCI_DATA_IN : XHCI_DATA_OUT ); /* bit[16] Direction, 0 = OUT, 1 = IN */ + + xhci_trb_queue(&(ep->reqs), &trb); + } + + status = &(trb.status); + memset ( status, 0, sizeof ( *status ) ); + status->flags = XHCI_TRB_IOC; + status->type = XHCI_TRB_STATUS; + status->direction = + ( ( datalen && input ) ? XHCI_STATUS_OUT : XHCI_STATUS_IN ); + + xhci_trb_queue(&(ep->reqs), &trb); + + /* pass command trb to hardware */ + DSB(); + + USB_LOG_DBG("ring doorbell slot-%d ep-%d \r\n", slot->id, ep->ctx); + xhci_doorbell(xhci, slot->id, ep->ctx); /* 0 = db host controller, 0 = db targe hc command */ + + return; +} + +/*********************************************************************/ + +/** + * Enqueue stream transfer + * + * @v ep USB endpoint + * @v data_buff Data buffer + * @v datalen Data length + * @ret rc Return status code + */ +void xhci_endpoint_stream ( struct xhci_endpoint *ep, + void *data_buff, + int datalen ) { + struct xhci_host *xhci = ep->xhci; + struct xhci_slot *slot = ep->slot; + union xhci_trb trbs; + union xhci_trb *trb = &trbs; + struct xhci_trb_normal *normal; + int trb_len; + + /* Calculate TRB length */ + trb_len = XHCI_MTU; + if ( trb_len > datalen ) { + trb_len = datalen; + } else { + USB_LOG_ERR("transfer length %d exceed MTU %d \r\n", datalen, trb_len); + goto err_enqueue; + } + + /* Construct normal TRBs */ + normal = &trb->normal; + memset ( normal, 0, sizeof ( *normal ) ); + normal->data = CPU_TO_LE64 ( (uintptr_t)data_buff ); + normal->len = CPU_TO_LE32 ( trb_len ); + normal->type = XHCI_TRB_NORMAL; + normal->flags = XHCI_TRB_IOC; + + xhci_trb_queue(&(ep->reqs), trb); + + /* pass command trb to hardware */ + DSB(); + + xhci_doorbell(xhci, slot->id, ep->ctx); + +err_enqueue: + return; +} + +/*********************************************************************/ + +/** + * Populate evaluate context input context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v input Input context + */ +static void xhci_evaluate_context_input ( struct xhci_host *xhci, + struct xhci_slot *slot __unused, + struct xhci_endpoint *endpoint, + void *input ) { + struct xhci_control_context *control_ctx; + struct xhci_slot_context *slot_ctx; + struct xhci_endpoint_context *ep_ctx; + + /* Populate control context */ + control_ctx = input; + control_ctx->add = CPU_TO_LE32 ( ( 1 << XHCI_CTX_SLOT ) /*| + ( 1 << endpoint->ctx )*/ ); + control_ctx->drop = CPU_TO_LE32 ( ( 1 << XHCI_CTX_SLOT ) /* | + ( 1 << endpoint->ctx )*/ ); + + /* Populate slot context */ + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + slot_ctx->info = CPU_TO_LE32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ), + 0, 0, 0 ) ); + + /* Populate endpoint context */ + ep_ctx = ( input + xhci_input_context_offset ( xhci, endpoint->ctx ) ); + ep_ctx->mtu = CPU_TO_LE16 ( endpoint->mtu ); +} + +/** + * Evaluate context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_evaluate_context ( struct xhci_host *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + int rc; + + /* Configure endpoint */ + if ( ( rc = xhci_context ( xhci, slot, endpoint, + XHCI_TRB_EVALUATE_CONTEXT, + xhci_evaluate_context_input ) ) != 0 ) + return rc; + + USB_LOG_DBG("XHCI %s slot %d ctx %d (re-)evaluated\n", + xhci->name, slot->id, endpoint->ctx ); + return 0; +} + +/** + * Update MTU + * + * @v ep USB endpoint + * @ret rc Return status code + */ +int xhci_endpoint_mtu ( struct xhci_endpoint *ep ) { + struct xhci_endpoint *endpoint = ( ep ); + struct xhci_slot *slot = endpoint->slot; + struct xhci_host *xhci = slot->xhci; + int rc; + + /* Evalulate context */ + if ( ( rc = xhci_evaluate_context ( xhci, slot, endpoint ) ) != 0 ) + return rc; + + return 0; +} + +/*********************************************************************/ + +/** + * Handle port status event + * + * @v xhci xHCI device + * @v trb Port status event + */ +static void xhci_port_status ( struct xhci_host *xhci, + struct xhci_trb_port_status *trb ) { + uint32_t portsc; + + /* Sanity check */ + USB_ASSERT ( ( trb->port > 0 ) && ( trb->port <= xhci->ports ) ); + + /* Record disconnections, changes flag will be cleared later */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( trb->port ) ); + xhci_dump_port_status(trb->port, portsc); + + if (portsc & XHCI_PORTSC_CSC) { + /* Report port status change */ + usbh_roothub_thread_wakeup ( trb->port ); + } +} + +/** + * Handle transfer event + * + * @v xhci xHCI device + * @v trb Transfer event TRB + */ +static void xhci_transfer ( struct xhci_host *xhci, + struct xhci_trb_transfer *trb ) { + struct xhci_slot *slot; + struct xhci_endpoint *endpoint; + union xhci_trb *trans_trb = (void *)(uintptr_t)(trb->transfer); + struct xhci_ring *trans_ring = XHCI_RING(trans_trb); /* to align addr is ring base */ + union xhci_trb *pending = &trans_ring->evt; /* preserve event trb pending to handle */ + uint32_t eidx = trans_trb - trans_ring->ring + 1; /* calculate current evt trb index */ + int rc; + + /* Identify slot */ + if ( ( trb->slot > xhci->slots ) || + ( ( slot = xhci->slot[trb->slot] ) == NULL ) ) { + USB_LOG_DBG("XHCI %s transfer event invalid slot %d:\n", + xhci->name, trb->slot ); + return; + } + + /* Identify endpoint */ + if ( ( trb->endpoint >= XHCI_CTX_END ) || + ( ( endpoint = slot->endpoint[trb->endpoint] ) == NULL ) ) { + USB_LOG_DBG("XHCI %s slot %d transfer event invalid epid " + "%d:\n", xhci->name, slot->id, trb->endpoint ); + return; + } + + /* Record completion */ + memcpy(pending, trb, sizeof(*trb)); /* copy current trb to cmd/transfer ring */ + trans_ring->eidx = eidx; + + /* Check for errors */ + if ( ! ( ( trb->code == XHCI_CMPLT_SUCCESS ) || + ( trb->code == XHCI_CMPLT_SHORT ) ) ) { + USB_LOG_ERR("XHCI %s slot %d ctx %d failed (code %d)\n", + xhci->name, slot->id, endpoint->ctx, trb->code); + + /* Sanity check */ + USB_ASSERT ( ( endpoint->context->state & XHCI_ENDPOINT_STATE_MASK ) + != XHCI_ENDPOINT_RUNNING ); + + xhci_dump_ep_ctx(endpoint->context); + return; + } + + if (endpoint->waiter) { + endpoint->waiter = false; + usb_osal_sem_give(endpoint->waitsem); + } + + if (endpoint->urb) { + struct usbh_urb *cur_urb = endpoint->urb; + cur_urb->errorcode = trb->code; + /* bit [23:0] TRB Transfer length, residual number of bytes not transferred + for OUT, is the value of (len of trb) - (data bytes transmitted), '0' means successful + for IN, is the value of (len of trb) - (data bytes received), + if cc is Short Packet, value is the diff between expected trans size and actual recv bytes + if cc is other error, value is the diff between expected trans size and actual recv bytes */ + cur_urb->actual_length += cur_urb->transfer_buffer_length - trb->residual; /* bit [23:0] */ + + if (cur_urb->complete) { + if (cur_urb->errorcode < 0) { + cur_urb->complete(cur_urb->arg, cur_urb->errorcode); + } else { + cur_urb->complete(cur_urb->arg, cur_urb->actual_length); + } + } + } + + return; +} + +/** + * Handle command completion event + * + * @v xhci xHCI device + * @v trb Command completion event + */ +static void xhci_complete ( struct xhci_host *xhci, + struct xhci_trb_complete *trb ) { + int rc; + union xhci_trb *cmd_trb = (void *)(uintptr_t)(trb->command); + struct xhci_ring *cmd_ring = XHCI_RING(cmd_trb); /* to align addr is ring base */ + union xhci_trb *pending = &cmd_ring->evt; /* preserve event trb pending to handle */ + uint32_t eidx = cmd_trb - cmd_ring->ring + 1; /* calculate current evt trb index */ + struct xhci_endpoint *work_pipe = xhci->cur_cmd_pipe; + + /* Ignore "command ring stopped" notifications */ + if ( trb->code == XHCI_CMPLT_CMD_STOPPED ) { + USB_LOG_DBG("XHCI %s command ring stopped\n", xhci->name ); + return; + } + + /* Record completion */ + USB_LOG_DBG("command-0x%x completed !!! \r\n", pending); + memcpy(pending, trb, sizeof(*trb)); /* copy current trb to cmd/transfer ring */ + cmd_ring->eidx = eidx; + + USB_ASSERT(work_pipe); + if (work_pipe->waiter) + { + work_pipe->waiter = false; + usb_osal_sem_give(work_pipe->waitsem); + } +} + +/** + * Handle host controller event + * + * @v xhci xHCI device + * @v trb Host controller event + */ +static void xhci_host_controller ( struct xhci_host *xhci, + struct xhci_trb_host_controller *trb ) { + int rc; + + /* Construct error */ + rc = -( trb->code ); + USB_LOG_ERR("XHCI %s host controller event (code %d)\n", + xhci->name, trb->code ); +} + +/** + * Process event ring in interrupt + * + * @v xhci xHCI device + * @r workpip current work endpoint + */ +struct xhci_endpoint *xhci_event_process(struct xhci_host *xhci) { + struct xhci_endpoint *work_pipe = NULL; + struct xhci_ring *evts = xhci->evts; + unsigned int evt_type; + unsigned int evt_cc; + + /* check and ack event */ + for (;;) { + /* Stop if we reach an empty TRB */ + DSB(); + + uint32_t nidx = evts->nidx; /* index of dequeue trb */ + uint32_t cs = evts->cs; /* cycle state toggle by xHc */ + union xhci_trb *trb = evts->ring + nidx; /* current trb */ + uint32_t control = trb->common.flags; /* trb control field */ + + if ((control & XHCI_TRB_C) != (cs ? 1 : 0)) { /* if cycle state not toggle, no events need to handle */ + break; + } + + /* Handle TRB */ + evt_type = ( trb->common.type & XHCI_TRB_TYPE_MASK ); + switch ( evt_type ) { + + case XHCI_TRB_TRANSFER : + evt_cc = trb->transfer.code; + xhci_transfer ( xhci, &trb->transfer ); + break; + + case XHCI_TRB_COMPLETE : + evt_cc = trb->complete.code; + xhci_complete ( xhci, &trb->complete ); + break; + + case XHCI_TRB_PORT_STATUS: + evt_cc = trb->port.code; + xhci_port_status ( xhci, &trb->port ); + break; + + case XHCI_TRB_HOST_CONTROLLER: + evt_cc = trb->host.code; + xhci_host_controller ( xhci, &trb->host ); + break; + + default: + USB_LOG_DBG("XHCI %s unrecognised event type %d, cc %d\n:", + xhci->name, ( evt_type ) ); + break; + } + + /* move ring index, notify xhci */ + nidx++; /* head to next trb */ + if (nidx == XHCI_RING_ITEMS) + { + nidx = 0; /* roll-back if reach end of list */ + cs = cs ? 0 : 1; + evts->cs = cs; /* sw toggle cycle state */ + } + evts->nidx = nidx; + + /* Update dequeue pointer if applicable */ + uint64_t erdp = (uint64_t)(unsigned long)(evts->ring + nidx); + xhci_writeq ( xhci, (uintptr_t)( erdp ) | XHCI_RUN_ERDP_EHB, + xhci->run + XHCI_RUN_ERDP ( 0 ) ); + } + + return work_pipe; +}
\ No newline at end of file diff --git a/port/xhci/xhci.h b/port/xhci/xhci.h new file mode 100644 index 00000000..eafd5843 --- /dev/null +++ b/port/xhci/xhci.h @@ -0,0 +1,940 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: xhci.h + * Date: 2022-07-19 09:26:25 + * LastEditTime: 2022-07-19 09:26:25 + * Description: This file is for xhci register definition. + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + * 1.0 zhugengyu 2022/9/19 init commit + * 2.0 zhugengyu 2023/3/29 support usb3.0 device attached at roothub + */ +#ifndef XHCI_H +#define XHCI_H + +#include "xhci_reg.h" + +#include "usbh_core.h" + +/** @file + * + * USB eXtensible Host Controller Interface (xHCI) driver + * + */ + +#define XHCI_RING_ITEMS 16U +#define XHCI_ALIGMENT 64U +#define XHCI_RING_SIZE (XHCI_RING_ITEMS * sizeof(union xhci_trb)) + +/* + * xhci_ring structs are allocated with XHCI_RING_SIZE alignment, + * then we can get it from a trb pointer (provided by evt ring). + */ +#define XHCI_RING(_trb) \ + ((struct xhci_ring*)((unsigned long)(_trb) & ~(XHCI_RING_SIZE-1))) + +/** Device context base address array */ +struct xhci_dcbaa { + /** Context base addresses */ + uint64_t *context; +}; + +/** Scratchpad buffer */ +struct xhci_scratchpad { + /** Number of page-sized scratchpad buffers */ + unsigned int count; + /** Scratchpad buffer area */ + uintptr_t buffer; + /** Scratchpad array */ + uint64_t *array; +}; + +/** An input control context */ +struct xhci_control_context { + /** Drop context flags */ + uint32_t drop; + /** Add context flags */ + uint32_t add; + /** Reserved */ + uint32_t reserved_a[5]; + /** Configuration value */ + uint8_t config; + /** Interface number */ + uint8_t intf; + /** Alternate setting */ + uint8_t alt; + /** Reserved */ + uint8_t reserved_b; +} __attribute__ (( packed )); + +/** A slot context */ +struct xhci_slot_context { + /** Device info 03-00h */ + uint32_t info; + /** Maximum exit latency */ + uint16_t latency; + /** Root hub port number */ + uint8_t port; + /** Number of downstream ports 07-04h */ + uint8_t ports; + /** TT hub slot ID */ + uint8_t tt_id; + /** TT port number */ + uint8_t tt_port; + /** Interrupter target 0b-08h */ + uint16_t intr; + /** USB address */ + uint8_t address; + /** Reserved */ + uint16_t reserved_a; + /** Slot state 0f-0ch */ + uint8_t state; + /** Reserved */ + uint32_t reserved_b[4]; +} __attribute__ (( packed )); + +/** Construct slot context device info */ +#define XHCI_SLOT_INFO( entries, hub, speed, route ) \ + ( ( (entries) << 27 ) | ( (hub) << 26 ) | ( (speed) << 20 ) | (route) ) + +/** An endpoint context */ +struct xhci_endpoint_context { + /** Endpoint state 03-00h*/ + uint8_t state; + /** Stream configuration */ + uint8_t stream; +#define XHCI_EPCTX_MULT_GET(stream) XHCI32_GET_BITS(stream, 1, 0) +#define XHCI_EPCTX_STREAM_GET(stream) XHCI32_GET_BITS(stream, 6, 2) +#define XHCI_EPCTX_LSA BIT(7) + /** Polling interval */ + uint8_t interval; + /** Max ESIT payload high */ + uint8_t esit_high; + /** Endpoint type 04-04h */ + uint8_t type; +#define XHCI_EPCTX_CERR_GET(type) XHCI32_GET_BITS(type, 2, 1) +#define XHCI_EPCTX_TYPE_GET(type) XHCI32_GET_BITS(type, 5, 3) +#define XHCI_EPCTX_HID BIT(7) + /** Maximum burst size */ + uint8_t burst; + /** Maximum packet size */ + uint16_t mtu; + /** Transfer ring dequeue pointer 0f-08h */ + uint64_t dequeue; +#define XHCI_EPCTX_DCS BIT(0) + /** Average TRB length 13-10h */ + uint16_t trb_len; + /** Max ESIT payload low */ + uint16_t esit_low; + /** Reserved */ + uint32_t reserved[3]; +} __attribute__ (( packed )); + +/** Endpoint states */ +enum { + /** Endpoint is disabled */ + XHCI_ENDPOINT_DISABLED = 0, + /** Endpoint is running */ + XHCI_ENDPOINT_RUNNING = 1, + /** Endpoint is halted due to a USB Halt condition */ + XHCI_ENDPOINT_HALTED = 2, + /** Endpoint is stopped */ + XHCI_ENDPOINT_STOPPED = 3, + /** Endpoint is halted due to a TRB error */ + XHCI_ENDPOINT_ERROR = 4, +}; + +/** Endpoint state mask */ +#define XHCI_ENDPOINT_STATE_MASK 0x07 + +/** Endpoint type */ +#define XHCI_EP_TYPE(type) ( (type) << 3 ) + +/** Control endpoint type */ +#define XHCI_EP_TYPE_CONTROL XHCI_EP_TYPE ( 4 ) + +/** Input endpoint type */ +#define XHCI_EP_TYPE_IN XHCI_EP_TYPE ( 4 ) + +/** Periodic endpoint type */ +#define XHCI_EP_TYPE_PERIODIC XHCI_EP_TYPE ( 1 ) + +/** Endpoint dequeue cycle state */ +#define XHCI_EP_DCS 0x00000001UL + +/** Control endpoint average TRB length */ +#define XHCI_EP0_TRB_LEN 8 + +/** + * Calculate doorbell register value + * + * @v target Doorbell target + * @v stream Doorbell stream ID + * @ret dbval Doorbell register value + */ +#define XHCI_DBVAL( target, stream ) ( (target) | ( (stream) << 16 ) ) + + +/** Slot context index */ +#define XHCI_CTX_SLOT 0 + +/** Calculate context index from USB endpoint address */ +/* refer to spec. Figure 4-4: Endpoint Context Addressing + ep0 = 1, ep1-out = 2, ep1-in = 3, ... ep15-out = 30, ep15-in = 31 */ +#define XHCI_CTX(address) \ + ( (address) ? ( ( ( (address) & 0x0f ) << 1 ) | \ + ( ( (address) & 0x80 ) >> 7 ) ) : 1 ) + +/** Endpoint zero context index */ +#define XHCI_CTX_EP0 XHCI_CTX ( 0x00 ) + +/** End of contexts */ +#define XHCI_CTX_END 32 + +/** Device context index */ +#define XHCI_DCI(ctx) ( (ctx) + 0 ) + +/** Input context index */ +#define XHCI_ICI(ctx) ( (ctx) + 1 ) + +/** Number of TRBs (excluding Link TRB) in the command ring + * + * This is a policy decision. + */ +#define XHCI_CMD_TRBS_LOG2 2 + +/** Number of TRBs in the event ring + * + * This is a policy decision. + */ +#define XHCI_EVENT_TRBS_LOG2 6 + +/** Number of TRBs in a transfer ring + * + * This is a policy decision. + */ +#define XHCI_TRANSFER_TRBS_LOG2 6 + +/** Maximum time to wait for BIOS to release ownership + * + * This is a policy decision. + */ +#define XHCI_USBLEGSUP_MAX_WAIT_MS 100 + +/** Maximum time to wait for host controller to stop + * + * This is a policy decision. + */ +#define XHCI_STOP_MAX_WAIT_MS 100 + +/** Maximum time to wait for reset to complete + * + * This is a policy decision. + */ +#define XHCI_RESET_MAX_WAIT_MS 500 + +/** Maximum time to wait for a command to complete + * + * The "address device" command involves waiting for a response to a + * USB control transaction, and so we must wait for up to the 5000ms + * that USB allows for devices to respond to control transactions. + */ +#define XHCI_COMMAND_MAX_WAIT_MS 5000 + +/** Time to delay after aborting a command + * + * This is a policy decision + */ +#define XHCI_COMMAND_ABORT_DELAY_MS 500 + +/** Maximum time to wait for a port reset to complete + * + * This is a policy decision. + */ +#define XHCI_PORT_RESET_MAX_WAIT_MS 500 + +/** A transfer request block template */ +struct xhci_trb_template { + /** Parameter */ + uint64_t parameter; + /** Status */ + uint32_t status; + /** Control */ + uint32_t control; +}; + +/** A transfer request block */ +struct xhci_trb_common { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved_c; +} __attribute__ (( packed )); + +/** Transfer request block cycle bit flag */ +#define XHCI_TRB_C 0x01 + +/** Transfer request block toggle cycle bit flag */ +#define XHCI_TRB_TC 0x02 + +/** Transfer request block chain flag */ +#define XHCI_TRB_CH 0x10 + +/** Transfer request block interrupt on completion flag */ +#define XHCI_TRB_IOC 0x20 + +/** Transfer request block immediate data flag */ +#define XHCI_TRB_IDT 0x40 + +/** Transfer request block type */ +#define XHCI_TRB_TYPE(type) ( (type) << 2 ) + +/** Transfer request block type mask */ +#define XHCI_TRB_TYPE_MASK XHCI_TRB_TYPE ( 0x3f ) + +/** A normal transfer request block */ +struct xhci_trb_normal { + /** Data buffer */ + uint64_t data; + /** Length */ + uint32_t len; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved; +} __attribute__ (( packed )); + +/** A normal transfer request block */ +#define XHCI_TRB_NORMAL XHCI_TRB_TYPE ( 1 ) + +/** Construct TD size field */ +#define XHCI_TD_SIZE(remaining) \ + ( ( ( (remaining) <= 0xf ) ? remaining : 0xf ) << 17 ) + +/** A setup stage transfer request block */ +struct xhci_trb_setup { + /** Setup packet, 04-00h sw will copy request content to this field */ + struct usb_setup_packet packet; + /** Length 08h */ + uint32_t len; + /** Flags 0ch */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Transfer direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved; +} __attribute__ (( packed )); + +/** A setup stage transfer request block */ +#define XHCI_TRB_SETUP XHCI_TRB_TYPE ( 2 ) + +/** Setup stage input data direction */ +#define XHCI_SETUP_IN 3 + +/** Setup stage output data direction */ +#define XHCI_SETUP_OUT 2 + +/** A data stage transfer request block */ +struct xhci_trb_data { + /** Data buffer */ + uint64_t data; + /** Length */ + uint32_t len; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Transfer direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved; +} __attribute__ (( packed )); + +/** A data stage transfer request block */ +#define XHCI_TRB_DATA XHCI_TRB_TYPE ( 3 ) + +/** Input data direction */ +#define XHCI_DATA_IN 0x01 + +/** Output data direction */ +#define XHCI_DATA_OUT 0x00 + +/** A status stage transfer request block */ +struct xhci_trb_status { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved_c; +} __attribute__ (( packed )); + +/** A status stage transfer request block */ +#define XHCI_TRB_STATUS XHCI_TRB_TYPE ( 4 ) + +/** Input status direction */ +#define XHCI_STATUS_IN 0x01 + +/** Output status direction */ +#define XHCI_STATUS_OUT 0x00 + +/** A link transfer request block */ +struct xhci_trb_link { + /** Next ring segment */ + uint64_t next; + /** Reserved */ + uint32_t reserved_a; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved_c; +} __attribute__ (( packed )); + +/** A link transfer request block */ +#define XHCI_TRB_LINK XHCI_TRB_TYPE ( 6 ) + +/** A no-op transfer request block */ +#define XHCI_TRB_NOP XHCI_TRB_TYPE ( 8 ) + +/** An enable slot transfer request block */ +struct xhci_trb_enable_slot { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Slot type */ + uint8_t slot; + /** Reserved */ + uint8_t reserved_c; +} __attribute__ (( packed )); + +/** An enable slot transfer request block */ +#define XHCI_TRB_ENABLE_SLOT XHCI_TRB_TYPE ( 9 ) + +/** A disable slot transfer request block */ +struct xhci_trb_disable_slot { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint8_t reserved_c; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A disable slot transfer request block */ +#define XHCI_TRB_DISABLE_SLOT XHCI_TRB_TYPE ( 10 ) + +/** A context transfer request block */ +struct xhci_trb_context { + /** Input context */ + uint64_t input; + /** Reserved */ + uint32_t reserved_a; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint8_t reserved_b; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** An address device transfer request block */ +#define XHCI_TRB_ADDRESS_DEVICE XHCI_TRB_TYPE ( 11 ) + +/** A configure endpoint transfer request block */ +#define XHCI_TRB_CONFIGURE_ENDPOINT XHCI_TRB_TYPE ( 12 ) + +/** An evaluate context transfer request block */ +#define XHCI_TRB_EVALUATE_CONTEXT XHCI_TRB_TYPE ( 13 ) + +/** A reset endpoint transfer request block */ +struct xhci_trb_reset_endpoint { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Endpoint ID */ + uint8_t endpoint; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A reset endpoint transfer request block */ +#define XHCI_TRB_RESET_ENDPOINT XHCI_TRB_TYPE ( 14 ) + +/** A stop endpoint transfer request block */ +struct xhci_trb_stop_endpoint { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Endpoint ID */ + uint8_t endpoint; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A stop endpoint transfer request block */ +#define XHCI_TRB_STOP_ENDPOINT XHCI_TRB_TYPE ( 15 ) + +/** A set transfer ring dequeue pointer transfer request block */ +struct xhci_trb_set_tr_dequeue_pointer { + /** Dequeue pointer */ + uint64_t dequeue; + /** Reserved */ + uint32_t reserved; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Endpoint ID */ + uint8_t endpoint; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A set transfer ring dequeue pointer transfer request block */ +#define XHCI_TRB_SET_TR_DEQUEUE_POINTER XHCI_TRB_TYPE ( 16 ) + +/** A no-op command transfer request block */ +#define XHCI_TRB_NOP_CMD XHCI_TRB_TYPE ( 23 ) + +/** A transfer event transfer request block */ +struct xhci_trb_transfer { + /** Transfer TRB pointer */ + uint64_t transfer; + /** Residual transfer length */ + uint16_t residual; + /** Reserved */ + uint8_t reserved; + /** Completion code */ + uint8_t code; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Endpoint ID */ + uint8_t endpoint; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A transfer event transfer request block */ +#define XHCI_TRB_TRANSFER XHCI_TRB_TYPE ( 32 ) + +/** A command completion event transfer request block */ +struct xhci_trb_complete { + /** Command TRB pointer */ + uint64_t command; + /** Parameter */ + uint8_t parameter[3]; + /** Completion code */ + uint8_t code; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Virtual function ID */ + uint8_t vf; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A command completion event transfer request block */ +#define XHCI_TRB_COMPLETE XHCI_TRB_TYPE ( 33 ) + +/** xHCI completion codes */ +enum xhci_completion_code { + /** Timeout */ + XHCI_CMPLT_TIMEOUT = -1, + /** Success */ + XHCI_CMPLT_SUCCESS = 1, + /** Stall Error */ + XHCI_CMPLT_STALL = 6, + /** Bandwidth Error */ + XHCI_CMPLT_BANDWIDTH = 8, + /** Endpoint Not Enabled Error */ + XHCI_CMPLT_ENDPOINT_NOT_ENABLED = 12, + /** Short packet */ + XHCI_CMPLT_SHORT = 13, + /** Parameter Error */ + XHCI_CMPLT_PARAMETER = 17, + /** Command ring stopped */ + XHCI_CMPLT_CMD_STOPPED = 24, +}; + +/** A port status change transfer request block */ +struct xhci_trb_port_status { + /** Reserved */ + uint8_t reserved_a[3]; + /** Port ID */ + uint8_t port; + /** Reserved */ + uint8_t reserved_b[7]; + /** Completion code */ + uint8_t code; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved_c; +} __attribute__ (( packed )); + +/** A port status change transfer request block */ +#define XHCI_TRB_PORT_STATUS XHCI_TRB_TYPE ( 34 ) + +/** A port status change transfer request block */ +struct xhci_trb_host_controller { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint8_t reserved_b[3]; + /** Completion code */ + uint8_t code; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved_c; +} __attribute__ (( packed )); + +/** A port status change transfer request block */ +#define XHCI_TRB_HOST_CONTROLLER XHCI_TRB_TYPE ( 37 ) + +/** A transfer request block */ +union xhci_trb { + /** Template */ + struct xhci_trb_template template; + /** Common fields */ + struct xhci_trb_common common; + /** Normal TRB */ + struct xhci_trb_normal normal; + /** Setup stage TRB */ + struct xhci_trb_setup setup; + /** Data stage TRB */ + struct xhci_trb_data data; + /** Status stage TRB */ + struct xhci_trb_status status; + /** Link TRB */ + struct xhci_trb_link link; + /** Enable slot TRB */ + struct xhci_trb_enable_slot enable; + /** Disable slot TRB */ + struct xhci_trb_disable_slot disable; + /** Input context TRB */ + struct xhci_trb_context context; + /** Reset endpoint TRB */ + struct xhci_trb_reset_endpoint reset; + /** Stop endpoint TRB */ + struct xhci_trb_stop_endpoint stop; + /** Set transfer ring dequeue pointer TRB */ + struct xhci_trb_set_tr_dequeue_pointer dequeue; + /** Transfer event */ + struct xhci_trb_transfer transfer; + /** Command completion event */ + struct xhci_trb_complete complete; + /** Port status changed event */ + struct xhci_trb_port_status port; + /** Host controller event */ + struct xhci_trb_host_controller host; +} __attribute__ (( packed )); + +struct xhci_ring { + union xhci_trb ring[XHCI_RING_ITEMS]; + union xhci_trb evt; + uint32_t eidx; + uint32_t nidx; + uint32_t cs; + usb_osal_mutex_t lock; +}; + +/** An event ring segment */ +struct xhci_er_seg { + /** Base address */ + uint64_t base; + /** Number of TRBs */ + uint32_t count; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** An xHCI endpoint */ +struct xhci_endpoint { + struct xhci_ring reqs; /* DO NOT MOVE reqs from structure beg */ + /** xHCI device */ + struct xhci_host *xhci; + /** xHCI slot */ + struct xhci_slot *slot; + /** Endpoint address */ + unsigned int address; + /** Context index */ + unsigned int ctx; + /** Endpoint type in USB definition */ + unsigned int ep_type; + /** Endpoint type in XHCI Endpoint context definition */ + unsigned int ctx_type; + + /** Maximum transfer size (Maximum packet size) */ + unsigned int mtu; + /** Maximum burst size */ + unsigned int burst; + /** Endpoint interval */ + unsigned int interval; + + /** Endpoint context */ + struct xhci_endpoint_context *context; + + /* command or transfer waiter */ + int timeout; /* = 0 no need to wait */ + bool waiter; + usb_osal_sem_t waitsem; + + /* handle urb */ + struct usbh_hubport *hport; + struct usbh_urb *urb; /* NULL if no active URB */ +}; + +/** An xHCI device slot */ +struct xhci_slot { + /** xHCI device */ + struct xhci_host *xhci; + /** Slot ID */ + unsigned int id; + /** Slot context */ + struct xhci_slot_context *context; + + /** Route string */ + unsigned int route; + /** Root hub port number */ + unsigned int port; + /** Protocol speed ID */ + unsigned int psiv; + /** Number of ports (if this device is a hub) */ + unsigned int ports; + /** Transaction translator slot ID */ + unsigned int tt_id; + /** Transaction translator port */ + unsigned int tt_port; + + /** Endpoints, indexed by context ID */ + struct xhci_endpoint *endpoint[XHCI_CTX_END]; +}; + +/** An xHCI device */ +struct xhci_host { + /** ID */ + uint32_t id; + /** Name */ + char name[4]; + + /* xhci registers base addr */ + /** Registers base */ + void *base; + /** Capability registers */ + void *cap; + /** Operational registers */ + void *op; + /** Runtime registers */ + void *run; + /** Doorbell registers */ + void *db; + /** extended capability */ + unsigned int xecp; + /** capability cache */ + uint32_t hcs[3]; + uint32_t hcc; + /** xhci version */ + uint16_t version; + + /** Number of device slots */ + unsigned int slots; + /** Number of interrupters */ + unsigned int intrs; + /** Number of ports */ + unsigned int ports; + + /** 64-bit addressing capability */ + int addr64; + /** Context size shift */ + unsigned int csz_shift; + /** Page size */ + size_t pagesize; + + /** USB legacy support capability (if present and enabled) */ + unsigned int legacy; + + /** Device context base address array */ + struct xhci_dcbaa dcbaa; + + /** Scratchpad buffer */ + struct xhci_scratchpad scratch; + + /** Device slots, indexed by slot ID */ + struct xhci_slot **slot; + + /** Command ring */ + struct xhci_ring *cmds; + /** Event ring */ + struct xhci_ring *evts; + struct xhci_er_seg *eseg; + + struct xhci_endpoint *cur_cmd_pipe; +}; + +/** + * Calculate input context offset + * + * @v xhci xHCI device + * @v ctx Context index + */ +static inline size_t xhci_input_context_offset ( struct xhci_host *xhci, + unsigned int ctx ) { + + return ( XHCI_ICI ( ctx ) << xhci->csz_shift ); +} + +/** + * Calculate device context offset + * + * @v xhci xHCI device + * @v ctx Context index + */ +static inline size_t xhci_device_context_offset ( struct xhci_host *xhci, + unsigned int ctx ) { + + return ( XHCI_DCI ( ctx ) << xhci->csz_shift ); +} + +/* Probe XCHI device */ +int xhci_probe ( struct xhci_host *xhci, unsigned long baseaddr ); + +/* Open XHCI device and start running */ +int xhci_open ( struct xhci_host *xhci ); + +/* Close XHCI device and stop running */ +void xhci_close ( struct xhci_host *xhci ); + +/* Remove XHCI device and free allocated memory */ +void xhci_remove ( struct xhci_host *xhci ); + +/* Enable port */ +int xhci_port_enable (struct xhci_host *xhci, uint32_t port); + +/* Get port speed */ +uint32_t xhci_root_speed ( struct xhci_host *xhci, uint8_t port ); + +/* Open and enable device */ +int xhci_device_open ( struct xhci_host *xhci, struct xhci_endpoint *pipe, int *slot_id ); + +/* Assign device address */ +int xhci_device_address ( struct xhci_host *xhci, struct xhci_slot *slot, struct xhci_endpoint *pipe ); + +/* Close device and free allocated memory */ +void xhci_device_close ( struct xhci_slot *slot ); + +/* Open control endpoint for slot */ +int xhci_ctrl_endpoint_open ( struct xhci_host *xhci, struct xhci_slot *slot, struct xhci_endpoint *pipe ); + +/* Open work endpoint for slot */ +int xhci_work_endpoint_open ( struct xhci_host *xhci, struct xhci_slot *slot, struct xhci_endpoint *pipe ); + +/* Close endpoint and free allocated memory */ +void xhci_endpoint_close ( struct xhci_endpoint *ep ); + +/* Update MTU (Max packet size) of endpoint */ +int xhci_endpoint_mtu ( struct xhci_endpoint *ep ); + +/* Enqueue message transfer, usually for control transfer */ +void xhci_endpoint_message ( struct xhci_endpoint *ep, struct usb_setup_packet *packet, + void *data_buff, int datalen ); + +/* Enqueue stream transfer, usually for bulk/interrupt transfer */ +void xhci_endpoint_stream ( struct xhci_endpoint *ep, void *data_buff, int datalen ); + +/* Process event ring in interrupt */ +struct xhci_endpoint *xhci_event_process(struct xhci_host *xhci); + +/* Wait for a ring to empty (all TRBs processed by hardware) */ +int xhci_event_wait(struct xhci_host *xhci, + struct xhci_endpoint *pipe, + struct xhci_ring *ring); + +/* Dump host controller registers */ +void xhci_dump(struct xhci_host *xhci); + +/* Dump port registers */ +void xhci_dump_port ( struct xhci_host *xhci, + unsigned int port ); + +/* Dump Port status */ +void xhci_dump_port_status(uint32_t port, uint32_t portsc); + +/* Dump input context */ +void xhci_dump_input_ctx( struct xhci_host *xhci, const struct xhci_endpoint *endpoint, const void *input); + +/* Dump Endpoint */ +void xhci_dump_endpoint(const struct xhci_endpoint *ep); + +/* Dump endpoint context */ +void xhci_dump_ep_ctx(const struct xhci_endpoint_context *ep); + +/* Dump TRB */ +void xhci_dump_trbs(const union xhci_trb *trbs, unsigned int count); + +/* Dump slot context */ +void xhci_dump_slot_ctx(const struct xhci_slot_context *const sc); + +#endif /* XHCI_H */
\ No newline at end of file diff --git a/port/xhci/xhci_dbg.c b/port/xhci/xhci_dbg.c new file mode 100644 index 00000000..61ca296f --- /dev/null +++ b/port/xhci/xhci_dbg.c @@ -0,0 +1,353 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: xhci_dbg.c + * Date: 2022-07-19 09:26:25 + * LastEditTime: 2022-07-19 09:26:25 + * Description: This file is for implment xhci debug functions + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + * 1.0 zhugengyu 2022/9/19 init commit + * 2.0 zhugengyu 2023/3/29 support usb3.0 device attached at roothub + */ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <strings.h> + +#include "usbh_core.h" + +#include "xhci.h" + +/* macro to enable dump */ +#define XHCI_DUMP 1 +#define XHCI_DUMP_PORT 1 +#define XHCI_DUMP_TRB 0 +#define XHCI_DUMP_SLOT 0 +#define XHCI_DUMP_EP_CTX 0 +#define XHCI_DUMP_INPUT_CTX 0 +#define XHCI_DUMP_ENDPOINT 0 +#define XHCI_DUMP_PORT_STATUS 0 + +/** + * Dump host controller registers + * + * @v xhci xHCI device + */ +void xhci_dump(struct xhci_host *xhci) { +#if XHCI_DUMP + uint32_t usbcmd; + uint32_t usbsts; + uint32_t pagesize; + uint32_t dnctrl; + uint32_t config; + + /* Dump USBCMD */ + usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); + USB_LOG_DBG ( "XHCI %s USBCMD %08x%s%s\n", xhci->name, usbcmd, + ( ( usbcmd & XHCI_USBCMD_RUN ) ? " run" : "" ), + ( ( usbcmd & XHCI_USBCMD_HCRST ) ? " hcrst" : "" ) ); + + /* Dump USBSTS */ + usbsts = readl ( xhci->op + XHCI_OP_USBSTS ); + USB_LOG_DBG ( "XHCI %s USBSTS %08x%s\n", xhci->name, usbsts, + ( ( usbsts & XHCI_USBSTS_HCH ) ? " hch" : "" ) ); + + /* Dump PAGESIZE */ + pagesize = readl ( xhci->op + XHCI_OP_PAGESIZE ); + USB_LOG_DBG ( "XHCI %s PAGESIZE %08x\n", xhci->name, pagesize ); + + /* Dump DNCTRL */ + dnctrl = readl ( xhci->op + XHCI_OP_DNCTRL ); + USB_LOG_DBG ( "XHCI %s DNCTRL %08x\n", xhci->name, dnctrl ); + + /* Dump CONFIG */ + config = readl ( xhci->op + XHCI_OP_CONFIG ); + USB_LOG_DBG ( "XHCI %s CONFIG %08x\n", xhci->name, config ); +#endif +} + +/** + * Dump port registers + * + * @v xhci xHCI device + * @v port Port number + */ +void xhci_dump_port ( struct xhci_host *xhci, + unsigned int port ) { +#if XHCI_DUMP_PORT + uint32_t portsc; + uint32_t portpmsc; + uint32_t portli; + uint32_t porthlpmc; + + /* Dump PORTSC */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) ); + USB_LOG_DBG ( "XHCI %s-%d PORTSC %08x%s%s%s%s psiv=%d\n", + xhci->name, port, portsc, + ( ( portsc & XHCI_PORTSC_CCS ) ? " ccs" : "" ), + ( ( portsc & XHCI_PORTSC_PED ) ? " ped" : "" ), + ( ( portsc & XHCI_PORTSC_PR ) ? " pr" : "" ), + ( ( portsc & XHCI_PORTSC_PP ) ? " pp" : "" ), + XHCI_PORTSC_PSIV ( portsc ) ); + + /* Dump PORTPMSC */ + portpmsc = readl ( xhci->op + XHCI_OP_PORTPMSC ( port ) ); + USB_LOG_DBG ( "XHCI %s-%d PORTPMSC %08x\n", xhci->name, port, portpmsc ); + + /* Dump PORTLI */ + portli = readl ( xhci->op + XHCI_OP_PORTLI ( port ) ); + USB_LOG_DBG ( "XHCI %s-%d PORTLI %08x\n", xhci->name, port, portli ); + + /* Dump PORTHLPMC */ + porthlpmc = readl ( xhci->op + XHCI_OP_PORTHLPMC ( port ) ); + USB_LOG_DBG ( "XHCI %s-%d PORTHLPMC %08x\n", + xhci->name, port, porthlpmc ); +#endif +} + +/** + * Dump slot context + * + * @v sc Slot context + */ +void xhci_dump_slot_ctx(const struct xhci_slot_context *const sc) { +#if XHCI_DUMP_SLOT + const uint8_t *ctx = (uint8_t *)sc; + + USB_LOG_DBG("===== slot ctx ===== \r\n"); + USB_LOG_DBG("ctx[0]=0x%x\n", *((uint32_t*)ctx)); + USB_LOG_DBG(" info: 0x%x \r\n", sc->info); + USB_LOG_DBG("ctx[1]=0x%x\n", *((uint32_t*)ctx + 1)); + USB_LOG_DBG(" latency: 0x%x \r\n", sc->latency); + USB_LOG_DBG(" port: 0x%x \r\n", sc->port); + USB_LOG_DBG(" ports: 0x%x \r\n", sc->ports); + USB_LOG_DBG("ctx[2]=0x%x\n", *((uint32_t*)ctx + 2)); + USB_LOG_DBG(" tt_id: 0x%x \r\n", sc->tt_id); + USB_LOG_DBG(" tt_port: 0x%x \r\n", sc->tt_port); + USB_LOG_DBG("ctx[3]=0x%x\n", *((uint32_t*)ctx + 3)); + USB_LOG_DBG(" intr: 0x%x \r\n", sc->intr); + USB_LOG_DBG(" address: 0x%x \r\n", sc->address); + USB_LOG_DBG(" state: 0x%x \r\n", sc->state); + USB_LOG_DBG("=====+++++++++===== \r\n"); +#endif +} + +/** + * Dump endpoint context + * + * @v ep Endpoint context + */ +void xhci_dump_ep_ctx(const struct xhci_endpoint_context *ep) { +#if XHCI_DUMP_EP_CTX + const uint8_t *ctx = (uint8_t *)ep; + + USB_LOG_DBG("===== ep ctx ===== \r\n"); + USB_LOG_DBG("ctx[0]=0x%x\n", *((uint32_t*)ctx)); + USB_LOG_DBG(" ep_state: 0x%x \r\n", ep->state); + USB_LOG_DBG(" mult: 0x%x \r\n", XHCI_EPCTX_MULT_GET(ep->stream)); + USB_LOG_DBG(" stream: 0x%x \r\n", XHCI_EPCTX_STREAM_GET(ep->stream)); + USB_LOG_DBG(" lsa: 0x%x \r\n", !!(XHCI_EPCTX_LSA & (ep->stream))); + USB_LOG_DBG(" interval: 0x%x \r\n", ep->interval); + USB_LOG_DBG(" esit_high: 0x%x \r\n", ep->esit_high); + USB_LOG_DBG("ctx[1]=0x%x\n", *((uint32_t*)ctx + 1)); + USB_LOG_DBG(" cerr: 0x%x \r\n", XHCI_EPCTX_CERR_GET(ep->type)); + USB_LOG_DBG(" type: 0x%x \r\n", XHCI_EPCTX_TYPE_GET(ep->type)); + USB_LOG_DBG(" hid: 0x%x \r\n", !!(XHCI_EPCTX_HID & (ep->type))); + USB_LOG_DBG(" burst: 0x%x \r\n", ep->burst); + USB_LOG_DBG(" mtu: 0x%x \r\n", ep->mtu); + USB_LOG_DBG("ctx[2]=0x%x\n", *((uint32_t*)ctx + 2)); + USB_LOG_DBG("ctx[3]=0x%x\n", *((uint32_t*)ctx + 3)); + USB_LOG_DBG(" dequeue: 0x%lx \r\n", ep->dequeue); + USB_LOG_DBG(" dcs: 0x%lx \r\n", !!(XHCI_EPCTX_DCS & ep->dequeue)); + USB_LOG_DBG("ctx[4]=0x%x\n", *((uint32_t*)ctx + 4)); + USB_LOG_DBG(" trb_len: 0x%x \r\n", ep->trb_len); + USB_LOG_DBG(" esit_low: 0x%x \r\n", ep->esit_low); + USB_LOG_DBG("=====+++++++++===== \r\n"); +#endif +} + +/** + * Dump input context + * + * @v xhci XHCI device + * @v endpoint Endpoint + * @v input Input context + */ +void xhci_dump_input_ctx( struct xhci_host *xhci, const struct xhci_endpoint *endpoint, const void *input) { +#if XHCI_DUMP_INPUT_CTX + const struct xhci_control_context *control_ctx; + const struct xhci_slot_context *slot_ctx; + const struct xhci_endpoint_context *ep_ctx; + + control_ctx = input; + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + ep_ctx = ( input + xhci_input_context_offset ( xhci, endpoint->ctx ) ); + + USB_LOG_DBG("===input ctx====\n"); + USB_LOG_DBG("ctrl@%p=0x%x\n", control_ctx, *control_ctx); + USB_LOG_DBG("add=0x%x\n", control_ctx->add); + USB_LOG_DBG("del=0x%x\n", control_ctx->drop); + + USB_LOG_DBG("slot@%p\n", slot_ctx); + xhci_dump_slot_ctx(slot_ctx); + + USB_LOG_DBG("ep-%d@%p\n", endpoint->ctx, ep_ctx); + xhci_dump_ep_ctx(ep_ctx); + + USB_LOG_DBG("=====+++++++++===== \r\n"); +#endif +} + +/** + * Get next TRB in ring + * + * @v trbs TRB in ring + * @v trb Next TRB + */ +static const union xhci_trb * xhci_get_next_trb(const union xhci_trb *trbs) { + const struct xhci_trb_link *link = &(trbs->link); + if (link->type == XHCI_TRB_LINK) { + return (link->next) ? (const union xhci_trb *)(link->next) : NULL; + } else { + return trbs + 1; + } +} + +/** + * Dump TRB + * + * @v trbs TRB header + * @v count Number of TRB to dump + */ +void xhci_dump_trbs(const union xhci_trb *trbs, unsigned int count) { +#if XHCI_DUMP_TRB + const union xhci_trb *cur; + const struct xhci_trb_common *comm; + const uint8_t *dword; + + USB_LOG_DBG("===trbs ====\n"); + for (unsigned int i = 0; i < count; i++) { + cur = &(trbs[i]); + dword = (const uint8_t *)cur; + + comm = &(cur->common); + USB_LOG_DBG("[0x%x-0x%x-0x%x-0x%x]\n", + *((uint32_t *)dword), *((uint32_t *)dword + 1), + *((uint32_t *)dword + 2), *((uint32_t *)dword + 3)); + if (XHCI_TRB_SETUP == comm->type) { + const struct xhci_trb_setup *setup = (const struct xhci_trb_setup *)comm; + + USB_LOG_DBG("setup trb\n"); + USB_LOG_DBG(" packet=0x%x\n", setup->packet); + USB_LOG_DBG(" bmRequestType type=0x%x\n", setup->packet.bmRequestType); + USB_LOG_DBG(" bRequest=0x%x\n", setup->packet.bRequest); + USB_LOG_DBG(" wValue=0x%x\n", setup->packet.wValue); + USB_LOG_DBG(" wIndex=0x%x\n", setup->packet.wIndex); + USB_LOG_DBG(" wLength=0x%x\n", setup->packet.wLength); + USB_LOG_DBG(" trb_trans_len=%d\n", setup->len); + USB_LOG_DBG(" flags=0x%x\n", setup->flags); + USB_LOG_DBG(" direction=%d\n", setup->direction); + } else if (XHCI_TRB_DATA == comm->type) { + const struct xhci_trb_data *data = (const struct xhci_trb_data *)comm; + + USB_LOG_DBG("data trb......\n"); + USB_LOG_DBG(" data=0x%x\n", data->data); + USB_LOG_DBG(" len=%d\n", data->len); + USB_LOG_DBG(" direction=%d\n", data->direction); + } else if (XHCI_TRB_STATUS == comm->type) { + const struct xhci_trb_status *status = (const struct xhci_trb_status *)comm; + + USB_LOG_DBG("status trb......\n"); + USB_LOG_DBG(" direction=%d\n", status->direction); + } + } + USB_LOG_DBG("=====+++++++++===== \r\n"); +#endif +} + +static const char *xhci_endpoint_xhci_type (unsigned int ep_xhci_type) { + static const char *ep_names[] = {"not_valid", "isoc_out", "bulk_out", + "intr_out", "ctrl", "isoc_in", + "bulk_in", "intr_in"}; + unsigned int index = ep_xhci_type >> 3; + if (index < sizeof(ep_names)/sizeof(ep_names[0])) { + return ep_names[index]; + } + + return "unkown"; +} + +static const char *xhci_endpoint_type (unsigned int ep_type) { + const char *ep_name = "unkown"; + + switch (ep_type) + { + case USB_ENDPOINT_TYPE_CONTROL: + ep_name = "ctrl-ep"; + break; + case USB_ENDPOINT_TYPE_ISOCHRONOUS: + ep_name = "isoc-ep"; + break; + case USB_ENDPOINT_TYPE_BULK: + ep_name = "bulk-ep"; + break; + case USB_ENDPOINT_TYPE_INTERRUPT: + ep_name = "intr-ep"; + break; + default: + break; + } + + return ep_name; +} + +/** + * Dump Endpoint + * + * @v ep Endpoint + */ +void xhci_dump_endpoint(const struct xhci_endpoint *ep) { +#if XHCI_DUMP_ENDPOINT + USB_LOG_DBG("===endpoint====\n"); + USB_LOG_DBG("xhci=0x%x\n", ep->xhci); + USB_LOG_DBG("slot=0x%x\n", ep->slot); + USB_LOG_DBG("address=0x%x\n", ep->address); + USB_LOG_DBG("mtu=0x%x\n", ep->mtu); + USB_LOG_DBG("burst=0x%x\n", ep->burst); + USB_LOG_DBG("ctx=0x%x\n", ep->ctx); + USB_LOG_DBG("ep_type=%s\n", xhci_endpoint_type(ep->ep_type)); + USB_LOG_DBG("xhci_type=%s\n", xhci_endpoint_xhci_type(ep->ctx_type)); + USB_LOG_DBG("interval=0x%x\n", ep->interval); + USB_LOG_DBG("=====+++++++++===== \r\n"); +#endif +} + +/** + * Dump Port status + * + * @v port Port number + * @v portsc Port status + */ +void xhci_dump_port_status(uint32_t port, uint32_t portsc) { +#if XHCI_DUMP_PORT_STATUS + USB_LOG_DBG("====port-%d====\n"); + USB_LOG_DBG("connect=%d \n", !!(XHCI_PORTSC_CCS & port)); + USB_LOG_DBG("enabled=%d \n", !!(XHCI_PORTSC_PED & port)); + USB_LOG_DBG("powered=%d \n", !!(XHCI_PORTSC_PP & port)); + USB_LOG_DBG("=====+++++++++===== \r\n"); +#endif +}
\ No newline at end of file diff --git a/port/xhci/xhci_reg.h b/port/xhci/xhci_reg.h index 613dcb33..a981daa6 100644 --- a/port/xhci/xhci_reg.h +++ b/port/xhci/xhci_reg.h @@ -1,38 +1,31 @@ /* - * Copyright : (C) 2022 Phytium Information Technology, Inc. + * Copyright : (C) 2022 Phytium Information Technology, Inc. * All Rights Reserved. - * - * This program is OPEN SOURCE software: you can redistribute it and/or modify it - * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, - * either version 1.0 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the Phytium Public License for more details. - * - * + * See the Phytium Public License for more details. + * + * * FilePath: xhci_reg.h * Date: 2022-07-19 09:26:25 * LastEditTime: 2022-07-19 09:26:25 - * Description: This files is for xhci register definition - * - * Modify History: + * Description: This file is for xhci register definition. + * + * Modify History: * Ver Who Date Changes * ----- ------ -------- -------------------------------------- * 1.0 zhugengyu 2022/9/19 init commit + * 2.0 zhugengyu 2023/3/29 support usb3.0 device attached at roothub */ -#ifndef XHCI_REG_H -#define XHCI_REG_H - -/***************************** Include Files *********************************/ -#include "usbh_core.h" -#ifdef __cplusplus -extern "C" -{ -#endif +#ifndef XHCI_REG_H +#define XHCI_REG_H -/************************** Constant Definitions *****************************/ #if defined(__aarch64__) #define BITS_PER_LONG 64U #define XHCI_AARCH64 @@ -42,431 +35,356 @@ extern "C" #endif #define XHCI_GENMASK(h, l) \ - (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) + (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) #define XHCI_GENMASK_ULL(h, l) \ - (((~0ULL) - (1ULL << (l)) + 1) & \ - (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) + (((~0ULL) - (1ULL << (l)) + 1) & \ + (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) #define XHCI32_GET_BITS(x, a, b) (uint32_t)((((uint32_t)(x)) & XHCI_GENMASK(a, b)) >> b) #define XHCI32_SET_BITS(x, a, b) (uint32_t)((((uint32_t)(x)) << b) & XHCI_GENMASK(a, b)) #define XHCI64_GET_BITS(x, a, b) (uint64_t)((((uint64_t)(x)) & XHCI_GENMASK_ULL(a, b)) >> b) #define XHCI64_SET_BITS(x, a, b) (uint64_t)((((uint64_t)(x)) << b) & XHCI_GENMASK_ULL(a, b)) -/** @name Register Map - * - * Register offsets from the base address of an XHCI device. - * @{ - */ -#define XHCI_REG_CAP_CAPLENGTH 0x00 /* specify the limits, restrictions and capabilities */ -#define XHCI_REG_CAP_HCIVERSION 0x02 /* Interface Version Number */ -#define XHCI_REG_CAP_HCS1 0x04 /* Host Controller Structural Parameters 1 */ -#define XHCI_REG_CAP_HCS2 0x08 /* Host Controller Structural Parameters 2 */ -#define XHCI_REG_CAP_HCS3 0x0C /* Host Controller Structural Parameters 3 */ -#define XHCI_REG_CAP_HCC 0x10 /* Capability Parameters 1 */ -#define XHCI_REG_CAP_DBOFF 0x14 /* Doorbell Offset Register */ -#define XHCI_REG_CAP_RTSOFF 0x18 /* Runtime Register Space Offset Register */ +/** Capability register length */ +#define XHCI_CAP_CAPLENGTH 0x00 -/***************** Host Controller Operational Registers ***********************/ -#define XHCI_REG_OP_USBCMD 0x00 /* USB Command Register */ -#define XHCI_REG_OP_USBSTS 0x04 /* USB Status Register */ -#define XHCI_REG_OP_PAGESIZE 0x08 /* Page Size Register */ -#define XHCI_REG_OP_DNCTRL 0x14 /* Device Notification Control Register */ -#define XHCI_REG_OP_CRCR 0x18 /* Command Ring Control Register */ -#define XHCI_REG_OP_DCBAAP 0x30 /* Device Context Base Address Array Pointer Register */ -#define XHCI_REG_OP_CONFIG 0x38 /* Configure Register */ +/** Host controller interface version number */ +#define XHCI_CAP_HCIVERSION 0x02 -/* Port Status and Ctrl Register : OP Base + (400h + (10h * (n–1))) 'n' is port num */ -#define XHCI_REG_OP_PORTS_BASE 0x400 /* Port Status and Control Register Base */ -#define XHCI_REG_OP_PORTS_SIZE 0x10 /* Size of one Port SC Register */ -#define XHCI_REG_OP_PORTS_OFF(port, off) ((port) * XHCI_REG_OP_PORTS_SIZE + offset) +/** Structural parameters 1 */ +#define XHCI_CAP_HCSPARAMS1 0x04 -#define XHCI_REG_OP_PORTS_PORTSC 0x00 /* Port Status and Control Register */ -#define XHCI_REG_OP_PORTS_PORTPMSC 0x04 /* USB3 Port Power Management Status and Control Register */ -#define XHCI_REG_OP_PORTS_PORTLI 0x08 /* Port Link Info Register */ +/** Number of device slots */ +#define XHCI_HCSPARAMS1_SLOTS(params) ( ( (params) >> 0 ) & 0xff ) -/***************** Host Controller Runtime Registers ***********************/ -#define XHCI_REG_RT_MFINDEX 0x00 /* Microframe Index */ -#define XHCI_REG_RT_IR0 0x20 /* Interrupter Register Set 0 */ -#define XHCI_REG_RT_IR1023 0x8000 /* Interrupter Register Set 1023 */ +/** Number of interrupters */ +#define XHCI_HCSPARAMS1_INTRS(params) ( ( (params) >> 8 ) & 0x3ff ) -/* Interrupter Register Set : RT Base + 020h + (32 * Interrupter) */ -#define XHCI_REG_RT_IR_IMAN 0x00 /* Interrupter Management Register */ -#define XHCI_REG_RT_IR_IMOD 0x04 /* Interrupter Moderation Register */ -#define XHCI_REG_RT_IR_ERSTSZ 0x08 /* Event Ring Segment Table Size Register */ -#define XHCI_REG_RT_IR_ERSTBA 0x10 /* Event Ring Segment Table Base Address Register */ -#define XHCI_REG_RT_IR_ERDP 0x18 /* Event Ring Dequeue Pointer Register */ -#define XHCI_REG_RT_IR_SIZE 0x20 /* Size of one IR Register */ +/** Number of ports */ +#define XHCI_HCSPARAMS1_PORTS(params) ( ( (params) >> 24 ) & 0xff ) -/***************** Doorbell Register ***********************/ -#define XHCI_REG_DB_SIZE 4 /* Doorbell registers are 32 bits in length */ +/** Structural parameters 2 */ +#define XHCI_CAP_HCSPARAMS2 0x08 -/***************** eXtensible Host Controller Capability Registers ***********************/ +/** Number of page-sized scratchpad buffers */ +#define XHCI_HCSPARAMS2_SCRATCHPADS(params) \ + ( ( ( (params) >> 16 ) & 0x3e0 ) | ( ( (params) >> 27 ) & 0x1f ) ) -/** @name XHCI_REG_CAP_HCS1 Register - */ -#define XHCI_REG_CAP_HCS1_MAX_SLOTS_GET(x) XHCI32_GET_BITS(x, 7, 0) /* Number of Device Slots (MaxSlots) */ -#define XHCI_REG_CAP_HCS1_MAX_INTRS_GET(x) XHCI32_GET_BITS(x, 18, 8) /* Number of Interrupters (MaxIntrs) */ -#define XHCI_REG_CAP_HCS1_MAX_PORTS_GET(x) XHCI32_GET_BITS(x, 31, 24) /* Number of Ports (MaxPorts) */ +/** Capability parameters */ +#define XHCI_CAP_HCCPARAMS1 0x10 -/** @name XHCI_REG_CAP_HCS2 Register - */ -#define XHCI_REG_CAP_HCS2_IST_GET(x) XHCI32_GET_BITS(x, 3, 0) /* Isochronous Scheduling Threshold (IST) */ -#define XHCI_REG_CAP_HCS2_ERST_MAX_GET(x) XHCI32_GET_BITS(x, 7, 4) /* Event Ring Segment Table Max (ERST Max) */ -#define XHCI_REG_CAP_HCS2_SPR (1 << 26) /* Scratchpad Restore (SPR) */ -#define XHCI_REG_CAP_HCS2_MAX_SCRATCHPAD_BUFS_GET(x) XHCI32_GET_BITS(x, 25, 21) | XHCI32_GET_BITS(x, 31, 27) /* Max Scratchpad Buffers (Max Scratchpad Bufs) */ +/** 64-bit addressing capability */ +#define XHCI_HCCPARAMS1_ADDR64(params) ( ( (params) >> 0 ) & 0x1 ) -/** @name XHCI_REG_CAP_HCS3 Register - */ -#define XHCI_REG_CAP_HCS3_U1_DEV_EXIT_LATENCY_GET(x) XHCI32_GET_BITS(x, 7, 0) /* U1 Device Exit Latency */ -#define XHCI_REG_CAP_HCS3_U2_DEV_EXIT_LATENCY_GET(x) XHCI32_GET_BITS(x, 31, 16) /* U2 Device Exit Latency */ +/** Context size shift */ +#define XHCI_HCCPARAMS1_CSZ_SHIFT(params) ( 5 + ( ( (params) >> 2 ) & 0x1 ) ) -/** @name XHCI_REG_CAP_HCC Register - */ -#define XHCI_REG_CAP_HCC_AC64 (1 << 0) /* 64-bit Addressing Capabilitya 1: 64-bit */ -#define XHCI_REG_CAP_HCC_BNC (1 << 1) /* BW Negotiation Capability (BNC) 1: support */ -#define XHCI_REG_CAP_HCC_CSZ (1 << 2) /* Context Size (CSZ) 1: 64 byte context data */ -#define XHCI_REG_CAP_HCC_PPC (1 << 3) /* Port Power Control (PPC) 1: support */ -#define XHCI_REG_CAP_HCC_PIND (1 << 4) /* Port Indicators (PIND) 1: support */ -#define XHCI_REG_CAP_HCC_LHRC (1 << 5) /* Light HC Reset Capability (LHRC) 1: support */ -#define XHCI_REG_CAP_HCC_LTC (1 << 6) /* Latency Tolerance Messaging Capability (LTC) */ -#define XHCI_REG_CAP_HCC_NSS (1 << 7) /* No Secondary SID Support (NSS) */ -#define XHCI_REG_CAP_HCC_MAX_PSA_SIZE_GET(x) XHCI32_GET_BITS(x, 15, 12) /* Maximum Primary Stream Array Size (MaxPSASize) */ -#define XHCI_REG_CAP_HCC_XECP_GET(x) XHCI32_GET_BITS(x, 31, 16) /* xHCI Extended Capabilities Pointer (xECP) */ +/** xHCI extended capabilities pointer */ +#define XHCI_HCCPARAMS1_XECP(params) ( ( ( (params) >> 16 ) & 0xffff ) << 2 ) -/** @name XHCI_REG_CAP_DBOFF Register - */ -#define XHCI_REG_CAP_DBOFF_GET(x) ((x) & XHCI_GENMASK(31, 2)) /* 32-byte offset of the Doorbell Array base address from the Base */ +/** Doorbell offset */ +#define XHCI_CAP_DBOFF 0x14 -/** @name XHCI_REG_CAP_RTSOFF Register - */ -#define XHCI_REG_CAP_RTSOFF_GET(x) ((x) & XHCI_GENMASK(31, 5)) /* 32-byte offset of the xHCI Runtime Registers */ +/** Runtime register space offset */ +#define XHCI_CAP_RTSOFF 0x18 +/** xHCI extended capability ID */ +#define XHCI_XECP_ID(xecp) ( ( (xecp) >> 0 ) & 0xff ) -/***************** Host Controller Operational Registers ***********************/ +/** Next xHCI extended capability pointer */ +#define XHCI_XECP_NEXT(xecp) ( ( ( (xecp) >> 8 ) & 0xff ) << 2 ) -/** @name XHCI_REG_OP_USBCMD Register - */ -#define XHCI_REG_OP_USBCMD_RUN_STOP (1 << 0) /* Run/Stop (R/S) 1: RUN, 0: STOP - RW */ -#define XHCI_REG_OP_USBCMD_HCRST (1 << 1) /* Host Controller Reset (HCRST) 1: RESET - RW */ -#define XHCI_REG_OP_USBCMD_INTE (1 << 2) /* Interrupter Enable (INTE) 1: enabled - RW */ -#define XHCI_REG_OP_USBCMD_HSEE (1 << 3) /* Host System Error Enable (HSEE) - RW */ -#define XHCI_REG_OP_USBCMD_LHCRST (1 << 7) /* Light Host Controller Reset (LHCRST) - RW */ -#define XHCI_REG_OP_USBCMD_CSS (1 << 8) /* Controller Save State (CSS) - RW */ -#define XHCI_REG_OP_USBCMD_CRS (1 << 9) /* Controller Restore State (CRS) - RW */ -#define XHCI_REG_OP_USBCMD_EWE (1 << 10) /* Enable Wrap Event (EWE) - RW */ -#define XHCI_REG_OP_USBCMD_EU3S (1 << 11) /* Enable U3 MFINDEX Stop (EU3S) - RW */ +/** USB legacy support extended capability */ +#define XHCI_XECP_ID_LEGACY 1 -/** @name XHCI_REG_OP_USBSTS Register - */ -#define XHCI_REG_OP_USBSTS_HCH (1 << 0) /* 1: Stopped executing */ -#define XHCI_REG_OP_USBSTS_HSE (1 << 2) /* 1: Serious error detected */ -#define XHCI_REG_OP_USBSTS_EINT (1 << 3) /* 1: Interrupt Pending (IP) */ -#define XHCI_REG_OP_USBSTS_PCD (1 << 4) /* 1: Port Change Detect */ -#define XHCI_REG_OP_USBSTS_SSS (1 << 8) /* remain 1 while the xHC saves its internal state */ -#define XHCI_REG_OP_USBSTS_RSS (1 << 9) /* remain 1 while the xHC restores its internal state */ -#define XHCI_REG_OP_USBSTS_SRE (1 << 10) /* if error occurs during a Save or Restore operation this bit shall be set to ‘1’. */ -#define XHCI_REG_OP_USBSTS_CNR (1 << 11) /* 1: Controller Not Ready */ -#define XHCI_REG_OP_USBSTS_HCE (1 << 12) /* 1: Internal xHC error condition */ -#define XHCI_REG_OP_USBSTS_PRSRV_MASK ((1 << 1) | 0xffffe000) /* Rsvd bits */ +/** USB legacy support BIOS owned semaphore */ +#define XHCI_USBLEGSUP_BIOS 0x02 +/** USB legacy support BIOS ownership flag */ +#define XHCI_USBLEGSUP_BIOS_OWNED 0x01 -/** @name XHCI_REG_OP_PAGESIZE Register - */ -/* This xHC supports a page size of 2^(n+12) if bit n is Set */ -#define XHCI_REG_OP_PAGESIZE_4K (1 << 0) /* if bit 0 is Set, the xHC supports 4k byte page sizes */ +/** USB legacy support OS owned semaphore */ +#define XHCI_USBLEGSUP_OS 0x03 -/** @name XHCI_REG_OP_CRCR Register - */ -#define XHCI_REG_OP_CRCR_RCS (1 << 0) /* Ring Cycle State, value of the xHC Consumer Cycle State (CCS) flag */ -#define XHCI_REG_OP_CRCR_CS (1 << 1) /* Command Stop, 1 */ -#define XHCI_REG_OP_CRCR_CA (1 << 2) /* Command Abort, 1 */ -#define XHCI_REG_OP_CRCR_CRR (1 << 3) /* Command Ring Running */ -#define XHCI_REG_OP_CRCR_CR_PTR_MASK XHCI_GENMASK_ULL(63, 6) /* Command Ring Pointer, Dequeue Ptr of Command Ring */ +/** USB legacy support OS ownership flag */ +#define XHCI_USBLEGSUP_OS_OWNED 0x01 -/** @name XHCI_REG_OP_DCBAAP Register - */ -#define XHCI_REG_OP_DCBAAP_MASK XHCI_GENMASK_ULL(63, 6) /* bit[31:6] Ptr of DCBAA */ +/** USB legacy support control/status */ +#define XHCI_USBLEGSUP_CTLSTS 0x04 -/** @name XHCI_REG_OP_CONFIG Register - */ -#define XHCI_REG_OP_CONFIG_MAX_SLOTS_EN_MASK XHCI_GENMASK(7, 0) /* Max Device Slots Enabled (MaxSlotsEn) – RW */ -#define XHCI_REG_OP_CONFIG_MAX_SLOTS_EN_SET(x) XHCI32_SET_BITS(x, 7, 0) /* bit[7:0] Max Device Slots Enabled */ -#define XHCI_REG_OP_CONFIG_MAX_SLOTS_EN_GET(x) XHCI32_GET_BITS(x, 7, 0) +/** Supported protocol extended capability */ +#define XHCI_XECP_ID_SUPPORTED 2 -/** @name XHCI_REG_OP_PORTS_PORTSC Register - */ -#define XHCI_REG_OP_PORTS_PORTSC_CCS (1 << 0) /* Current Connect Status (CCS) – ROS */ -#define XHCI_REG_OP_PORTS_PORTSC_PED (1 << 1) /* Port Enabled/Disabled (PED) – RW1CS */ -#define XHCI_REG_OP_PORTS_PORTSC_OCA (1 << 3) /* Over-current Active (OCA) – RO */ -#define XHCI_REG_OP_PORTS_PORTSC_PR (1 << 4) /* Port Reset (PR) – RW1S */ -#define XHCI_REG_OP_PORTS_PORTSC_PLS_GET(x) XHCI32_GET_BITS(x, 8, 5) /* Port Link State (PLS) – RWS */ -#define XHCI_REG_OP_PORTS_PORTSC_PLS_SET(x) XHCI32_SET_BITS(x, 8, 5) -#define XHCI_REG_OP_PORTS_PORTSC_PLS_MASK XHCI_GENMASK(8, 5) -#define XHCI_REG_OP_PORTS_PORTSC_PLS(x) (x << 5) -#define XHCI_REG_OP_PORTS_PORTSC_PLS_SET(x) XHCI32_SET_BITS(x, 8, 5) +/** Supported protocol revision */ +#define XHCI_SUPPORTED_REVISION 0x00 -enum PLSStatus{ - PLS_U0 = 0, - PLS_U1 = 1, - PLS_U2 = 2, - PLS_U3 = 3, - PLS_DISABLED = 4, - PLS_RX_DETECT = 5, - PLS_INACTIVE = 6, - PLS_POLLING = 7, - PLS_RECOVERY = 8, - PLS_HOT_RESET = 9, - PLS_COMPILANCE_MODE = 10, - PLS_TEST_MODE = 11, - PLS_RESUME = 15, -}; /* Port status type */ +/** Supported protocol minor revision */ +#define XHCI_SUPPORTED_REVISION_VER(revision) ( ( (revision) >> 16 ) & 0xffff ) -#define XHCI_REG_OP_PORTS_PORTSC_PP (1 << 9) /* Port Power (PP) – RWS */ -#define XHCI_REG_OP_PORTS_PORTSC_PORT_SPEED_GET(x) XHCI32_GET_BITS(x, 13, 10) /* Port Speed (Port Speed) – ROS */ +/** Supported protocol name */ +#define XHCI_SUPPORTED_NAME 0x04 -/* Protocol Speed ID (PSI) */ -#define XHCI_PORT_SPEED_UNKOWN 0U -#define XHCI_PORT_SPEED_FULL 1U -#define XHCI_PORT_SPEED_LOW 2U -#define XHCI_PORT_SPEED_HIGH 3U -#define XHCI_PORT_SPEED_SUPER 4U +/** Supported protocol ports */ +#define XHCI_SUPPORTED_PORTS 0x08 -#define XHCI_REG_OP_PORTS_PORTSC_PIC_SET(x) XHCI32_SET_BITS(x, 15, 14) -#define XHCI_REG_OP_PORTS_PORTSC_PIC_MASK XHCI_GENMASK(15, 14) +/** Supported protocol port offset */ +#define XHCI_SUPPORTED_PORTS_OFFSET(ports) ( ( (ports) >> 0 ) & 0xff ) -#define XHCI_REG_OP_PORTS_PORTSC_LWS (1 << 16) /* Port Link State Write Strobe (LWS) */ -#define XHCI_REG_OP_PORTS_PORTSC_CSC (1 << 17) /* Connect Status Change (CSC) */ -#define XHCI_REG_OP_PORTS_PORTSC_PEC (1 << 18) /* Port Enabled/Disabled Change (PEC) 1: clear PED */ -#define XHCI_REG_OP_PORTS_PORTSC_WRC (1 << 19) /* Warm Port Reset Change 1: Warm Reset complete */ -#define XHCI_REG_OP_PORTS_PORTSC_OCC (1 << 20) /* Over-current Change 1: Over-current Active */ -#define XHCI_REG_OP_PORTS_PORTSC_PRC (1 << 21) /* Port Reset Change 1: Transition of Port Reset */ -#define XHCI_REG_OP_PORTS_PORTSC_PLC (1 << 22) /* Port Link State Change 1: PLS transition */ -#define XHCI_REG_OP_PORTS_PORTSC_CEC (1 << 23) /* Port Config Error Change 1: Port Config Error detected */ -#define XHCI_REG_OP_PORTS_PORTSC_CAS (1 << 24) /* Cold Attach Status 1: Far-end Receiver Terminations were detected */ -#define XHCI_REG_OP_PORTS_PORTSC_WCE (1 << 25) /* Wake on Connect Enable 1: enable port to be sensitive to device connects */ -#define XHCI_REG_OP_PORTS_PORTSC_WDE (1 << 26) /* Wake on Disconnect Enable 1: enable port to be sensitive to device disconnects */ -#define XHCI_REG_OP_PORTS_PORTSC_WOE (1 << 27) /* Wake on Over-current Enable 1: enable port to be sensitive to over-current conditions */ -#define XHCI_REG_OP_PORTS_PORTSC_DR (1 << 30) /* Device Removable, 0: Device is removable. 1: Device is non-removable */ -#define XHCI_REG_OP_PORTS_PORTSC_WPR (1 << 31) /* Warm Port Reset 1: follow Warm Reset sequence */ -#define XHCI_REG_OP_PORTS_PORTSC_RW_MASK (XHCI_REG_OP_PORTS_PORTSC_PR | XHCI_REG_OP_PORTS_PORTSC_PLS_MASK | XHCI_REG_OP_PORTS_PORTSC_PP \ - | XHCI_REG_OP_PORTS_PORTSC_PIC_MASK | XHCI_REG_OP_PORTS_PORTSC_LWS | XHCI_REG_OP_PORTS_PORTSC_WCE \ - | XHCI_REG_OP_PORTS_PORTSC_WDE | XHCI_REG_OP_PORTS_PORTSC_WOE) +/** Supported protocol port count */ +#define XHCI_SUPPORTED_PORTS_COUNT(ports) ( ( (ports) >> 8 ) & 0xff ) +/** Supported protocol PSI count */ +#define XHCI_SUPPORTED_PORTS_PSIC(ports) ( ( (ports) >> 28 ) & 0x0f ) -/***************** Host Controller Runtime Registers ***********************/ +/** Supported protocol slot */ +#define XHCI_SUPPORTED_SLOT 0x0c -/** @name XHCI_REG_RT_IR_IMAN Register - */ -#define XHCI_REG_RT_IR_IMAN_IP (1 << 0) /* Interrupt Pending, 1: an interrupt is pending for this Interrupter */ -#define XHCI_REG_RT_IR_IMAN_IE (1 << 1) /* Interrupt Enable, 1: capable of generating an interrupt. */ +/** Supported protocol slot type */ +#define XHCI_SUPPORTED_SLOT_TYPE(slot) ( ( (slot) >> 0 ) & 0x1f ) -/** @name XHCI_REG_RT_IR_IMOD Register - */ -#define XHCI_REG_RT_IR_IMOD_IMODI_MASK XHCI_GENMASK(15, 0) /* bit[15:0] Interrupt Moderation Interval default 4000 ==> 1ms */ -#define XHCI_REG_RT_IR_IMOD_IMODC_MASK XHCI_GENMASK(31, 16) /* bit[31:16] Interrupt Moderation Counter(Down counter) */ +/** Supported protocol PSI */ +#define XHCI_SUPPORTED_PSI(index) ( 0x10 + ( (index) * 4 ) ) -/** @name XHCI_REG_RT_IR_ERSTSZ Register - */ -#define XHCI_REG_RT_IR_ERSTSZ_MASK XHCI_GENMASK(15, 0) /* bit[15:0] the number of valid Event Ring Segment Table entries */ +/** Supported protocol PSI value */ +#define XHCI_SUPPORTED_PSI_VALUE(psi) ( ( (psi) >> 0 ) & 0x0f ) -/** @name XHCI_REG_RT_IR_ERSTBA Register - */ -#define XHCI_REG_RT_IR_ERSTBA_MASK XHCI_GENMASK_ULL(63, 6) /* Event Ring Segment Table Base Address */ +/** Supported protocol PSI mantissa */ +#define XHCI_SUPPORTED_PSI_MANTISSA(psi) ( ( (psi) >> 16 ) & 0xffff ) -/** @name XHCI_REG_RT_IR_ERDP Register - */ -#define XHCI_REG_RT_IR_ERDP_DESI_MASK XHCI_GENMASK_ULL(2, 0) /* bit[2:0] Dequeue ERST Segment Index */ -#define XHCI_REG_RT_IR_ERDP_EHB (1 << 3) /* Event Handler Busy */ -#define XHCI_REG_RT_IR_ERDP_MASK XHCI_GENMASK_ULL(63, 4) /* Event Ring Dequeue Pointer */ +/** Supported protocol PSI exponent */ +#define XHCI_SUPPORTED_PSI_EXPONENT(psi) ( ( (psi) >> 4 ) & 0x03 ) -/***************** Doorbell Register ***********************/ -#define XHCI_REG_DB_TARGET_HC_COMMAND 0 /* Host Controller Doorbell (0) Command Doorbell */ -#define XHCI_REG_DB_TARGET_EP0 1 /* Device Context Doorbells Control EP 0 Enqueue Pointer Update */ -#define XHCI_REG_DB_TARGET_EP1_OUT 2 /* EP 1 OUT Enqueue Pointer Update */ -#define XHCI_REG_DB_TARGET_EP1_IN 3 /* EP 1 IN Enqueue Pointer Update */ -#define XHCI_REG_DB_TARGET_EP15_OUT 30 /* EP 15 OUT Enqueue Pointer Update */ -#define XHCI_REG_DB_TARGET_EP15_IN 31 /* EP 15 IN Enqueue Pointer Update */ +/** Default PSI values */ +enum { + /** Full speed (12Mbps) */ + XHCI_SPEED_FULL = 1, + /** Low speed (1.5Mbps) */ + XHCI_SPEED_LOW = 2, + /** High speed (480Mbps) */ + XHCI_SPEED_HIGH = 3, + /** Super speed */ + XHCI_SPEED_SUPER = 4, +}; -/***************** xHCI Extended Capabilities Registers ***********************/ -#define XHCI_REG_EXT_CAP_USBSPCF_OFFSET 0x0 -#define XHCI_REG_EXT_CAP_CAP_ID_GET(x) XHCI32_GET_BITS(x, 7, 0) -#define XHCI_REG_EXT_NEXT_CAP_PTR_GET(x) XHCI32_GET_BITS(x, 15, 8) -/* refer to 'Table 138: xHCI Extended Capability Codes' for more details */ -enum -{ - XHCI_EXT_CAP_ID_USB_LEGACY_SUPPORT = 1, - XHCI_EXT_CAP_ID_SUPPORT_PROTOCOL = 2, - XHCI_EXT_CAP_ID_EXTEND_POWER_MANAGEMENT = 3, - XHCI_EXT_CAP_ID_IO_VIRTUALIZATION = 4, - XHCI_EXT_CAP_ID_MESSAGE_INTERRUPT = 5, - XHCI_EXT_CAP_ID_LOCAL_MEMORY = 6, - XHCI_EXT_CAP_ID_USB_DEBUG_CAPABILITY = 10, - XHCI_EXT_CAP_ID_EXT_MESSAGE_INTERRUPT = 17, +/** USB command register */ +#define XHCI_OP_USBCMD 0x00 - XHCI_EXT_CAP_ID_VENDOR_DEFINED_MIN = 192, - XHCI_EXT_CAP_ID_VENDOR_DEFINED_MAX = 255 -}; +/** Run/stop */ +#define XHCI_USBCMD_RUN 0x00000001UL -/* xHCI Supported Protocol Capability */ -#define XHCI_REG_EXT_CAP_USBSPCFDEF_OFFSET 0x4 +/* Interrupter Enable (INTE) 1: enabled - RW */ +#define XHCI_USBCMD_INTE (1UL << 2) -#define XHCI_USBSPCF_MINOR_REVERSION_GET(x) XHCI32_GET_BITS(x, 23, 16) -#define XHCI_USBSPCF_MAJOR_REVERSION_GET(x) XHCI32_GET_BITS(x, 31, 24) +/** Host controller reset */ +#define XHCI_USBCMD_HCRST 0x00000002UL -#define XHCI_USBSPCFDEF_NAME_STRING_GET(x) XHCI32_GET_BITS(x, 31, 0) /* four ASCII characters may be defined */ -#define XHCI_USBSPCFDEF_NAME_STRING_USB 0x20425355 /* ASCII = "USB" */ +/** USB status register */ +#define XHCI_OP_USBSTS 0x04 -#define XHCI_REG_EXT_CAP_USBSPCFDEF2_OFFSET 0x8 -#define XHCI_USBSPCFDEF2_COMPATIBLE_PORT_OFF_GET(x) XHCI32_GET_BITS(x, 7, 0) -#define XHCI_USBSPCFDEF2_COMPATIBLE_PORT_CNT_GET(x) XHCI32_GET_BITS(x, 15, 8) -#define XHCI_USBSPCFDEF2_PROTOCOL_DEFINED_GET(x) XHCI32_GET_BITS(x, 27, 16) +/** Host controller halted */ +#define XHCI_USBSTS_HCH 0x00000001UL -/* trb bit definitions */ +/** Interrupt Pending (IP) */ +#define XHCI_USBSTS_EINT (1UL << 3) -/* configuration */ -#define XHCI_RING_ITEMS 16U -#define XHCI_ALIGMENT 64U -#define XHCI_RING_SIZE (XHCI_RING_ITEMS*sizeof(struct xhci_trb)) +/** Page size register */ +#define XHCI_OP_PAGESIZE 0x08 -#define TRB_C (1<<0) -#define TRB_TYPE_SHIFT 10 -#define TRB_TYPE_MASK 0x3f -#define TRB_TYPE_GET(val) XHCI32_GET_BITS(val, 15, 10) -#define TRB_TYPE_SET(t) XHCI32_SET_BITS(t, 15, 10) +/** Page size */ +#define XHCI_PAGESIZE(pagesize) ( (pagesize) << 12 ) -#define TRB_EV_ED (1<<2) +/** Device notifcation control register */ +#define XHCI_OP_DNCTRL 0x14 -#define TRB_TR_ENT (1<<1) -#define TRB_TR_ISP (1<<2) -#define TRB_TR_NS (1<<3) -#define TRB_TR_CH (1<<4) -#define TRB_TR_IOC (1<<5) -#define TRB_TR_IDT (1<<6) -#define TRB_TR_TBC_SHIFT 7 -#define TRB_TR_TBC_MASK 0x3 -#define TRB_TR_BEI (1<<9) -#define TRB_TR_TLBPC_SHIFT 16 -#define TRB_TR_TLBPC_MASK 0xf -#define TRB_TR_FRAMEID_SHIFT 20 -#define TRB_TR_FRAMEID_MASK 0x7ff -#define TRB_TR_SIA (1<<31) -#define TRB_TR_TRANS_LEN_SET(len) XHCI32_SET_BITS(len, 23, 0) -#define TRB_TR_TRANS_LEN_MASK XHCI_GENMASK(23, 0) +/** Command ring control register */ +#define XHCI_OP_CRCR 0x18 -#define TRB_TR_DIR (1<<16) -#define TRB_TR_TYPE_SET(t) XHCI32_SET_BITS(t, 17, 16) -#define TRB_TR_NO_DATA 0U -#define TRB_TR_OUT_DATA 2U -#define TRB_TR_IN_DATA 3U +/** Command ring cycle state */ +#define XHCI_CRCR_RCS 0x00000001UL -#define TRB_CC_GET(val) XHCI32_GET_BITS(val, 31, 24) -#define TRB_PORT_ID_GET(val) XHCI32_GET_BITS(val, 31, 24) +/** Command abort */ +#define XHCI_CRCR_CA 0x00000004UL +/** Command ring running */ +#define XHCI_CRCR_CRR 0x00000008UL -#define TRB_CR_SLOTID_SHIFT 24 -#define TRB_CR_SLOTID_MASK 0xff -#define TRB_CR_SLOTID_SET(id) XHCI32_SET_BITS(id, 31, 24) -#define TRB_CR_SLOTID_GET(val) XHCI32_GET_BITS(val, 31, 24) +/** Device context base address array pointer */ +#define XHCI_OP_DCBAAP 0x30 -#define TRB_CR_EPID_SHIFT 16 -#define TRB_CR_EPID_MASK 0x1f -#define TRB_CR_EPID_SET(id) XHCI32_SET_BITS(id, 20, 16) -#define TRB_CR_EPID_GET(field) XHCI32_GET_BITS(field, 20, 16) +/** Configure register */ +#define XHCI_OP_CONFIG 0x38 -#define TRB_CR_BSR (1<<9) -#define TRB_CR_DC (1<<9) +/** Maximum device slots enabled */ +#define XHCI_CONFIG_MAX_SLOTS_EN(slots) ( (slots) << 0 ) -#define TRB_LK_TC (1<<1) +/** Maximum device slots enabled mask */ +#define XHCI_CONFIG_MAX_SLOTS_EN_MASK \ + XHCI_CONFIG_MAX_SLOTS_EN ( 0xff ) -#define TRB_INTR_SHIFT 22 -#define TRB_INTR_MASK 0x3ff -#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK) +/** Port status and control register */ +#define XHCI_OP_PORTSC(port) ( 0x400 - 0x10 + ( (port) << 4 ) ) -/************************** Type Definitions *********************************/ -enum TRBType { - TRB_RESERVED = 0, - TR_NORMAL, - TR_SETUP, - TR_DATA, - TR_STATUS, - TR_ISOCH, - TR_LINK, - TR_EVDATA, - TR_NOOP, - CR_ENABLE_SLOT, - CR_DISABLE_SLOT, - CR_ADDRESS_DEVICE, - CR_CONFIGURE_ENDPOINT, - CR_EVALUATE_CONTEXT, - CR_RESET_ENDPOINT, - CR_STOP_ENDPOINT, - CR_SET_TR_DEQUEUE, - CR_RESET_DEVICE, - CR_FORCE_EVENT, - CR_NEGOTIATE_BW, - CR_SET_LATENCY_TOLERANCE, - CR_GET_PORT_BANDWIDTH, - CR_FORCE_HEADER, - CR_NOOP, - ER_TRANSFER_COMPLETE = 32, - ER_COMMAND_COMPLETE, - ER_PORT_STATUS_CHANGE, - ER_BANDWIDTH_REQUEST, - ER_DOORBELL, - ER_HOST_CONTROLLER, - ER_DEVICE_NOTIFICATION, - ER_MFINDEX_WRAP, -}; +/** Current connect status */ +#define XHCI_PORTSC_CCS 0x00000001UL -enum TRBCCode { - CC_DISCONNECTED = -2, - CC_TIMEOUT = -1, - CC_INVALID = 0, - CC_SUCCESS, - CC_DATA_BUFFER_ERROR, - CC_BABBLE_DETECTED, - CC_USB_TRANSACTION_ERROR, - CC_TRB_ERROR, - CC_STALL_ERROR, - CC_RESOURCE_ERROR, - CC_BANDWIDTH_ERROR, - CC_NO_SLOTS_ERROR, - CC_INVALID_STREAM_TYPE_ERROR, - CC_SLOT_NOT_ENABLED_ERROR, - CC_EP_NOT_ENABLED_ERROR, - CC_SHORT_PACKET, - CC_RING_UNDERRUN, - CC_RING_OVERRUN, - CC_VF_ER_FULL, - CC_PARAMETER_ERROR, - CC_BANDWIDTH_OVERRUN, - CC_CONTEXT_STATE_ERROR, - CC_NO_PING_RESPONSE_ERROR, - CC_EVENT_RING_FULL_ERROR, - CC_INCOMPATIBLE_DEVICE_ERROR, - CC_MISSED_SERVICE_ERROR, - CC_COMMAND_RING_STOPPED, - CC_COMMAND_ABORTED, - CC_STOPPED, - CC_STOPPED_LENGTH_INVALID, - CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29, - CC_ISOCH_BUFFER_OVERRUN = 31, - CC_EVENT_LOST_ERROR, - CC_UNDEFINED_ERROR, - CC_INVALID_STREAM_ID_ERROR, - CC_SECONDARY_BANDWIDTH_ERROR, - CC_SPLIT_TRANSACTION_ERROR -}; +/** Port enabled */ +#define XHCI_PORTSC_PED 0x00000002UL -/***************** Macros (Inline Functions) Definitions *********************/ -/* - * xhci_ring structs are allocated with XHCI_RING_SIZE alignment, - * then we can get it from a trb pointer (provided by evt ring). +#define XHCI_PORTSC_OCA (1 << 3) + +/** Port reset */ +#define XHCI_PORTSC_PR 0x00000010UL + +/** Port link state */ +#define XHCI_PORTSC_PLS(pls) ( (pls) << 5 ) + +/** U0 state */ +#define XHCI_PORTSC_PLS_U0 XHCI_PORTSC_PLS ( 0 ) + +/** Disabled port link state */ +#define XHCI_PORTSC_PLS_DISABLED XHCI_PORTSC_PLS ( 4 ) + +/** RxDetect port link state */ +#define XHCI_PORTSC_PLS_RXDETECT XHCI_PORTSC_PLS ( 5 ) + +/** Polling state */ +#define XHCI_PORTSC_PLS_POLLING XHCI_PORTSC_PLS ( 7 ) + +/** Port link state mask */ +#define XHCI_PORTSC_PLS_MASK XHCI_PORTSC_PLS ( 0xf ) + +/** Port power */ +#define XHCI_PORTSC_PP 0x00000200UL + +/** Time to delay after enabling power to a port */ +#define XHCI_PORT_POWER_DELAY_MS 20 + +/** Port speed ID value */ +#define XHCI_PORTSC_PSIV(portsc) ( ( (portsc) >> 10 ) & 0xf ) + +/** Port indicator control */ +#define XHCI_PORTSC_PIC(indicators) ( (indicators) << 14 ) + +/** Port indicator control mask */ +#define XHCI_PORTSC_PIC_MASK XHCI_PORTSC_PIC ( 3 ) + +/** Port link state write strobe */ +#define XHCI_PORTSC_LWS 0x00010000UL + +/** Time to delay after writing the port link state */ +#define XHCI_LINK_STATE_DELAY_MS 100 + +/** Connect status change */ +#define XHCI_PORTSC_CSC (1 << 17) + +/** Port enabled/disabled change */ +#define XHCI_PORTSC_PEC (1 << 18) + +/** Warm port reset change */ +#define XHCI_PORTSC_WRC (1 << 19) + +/** Over-current change */ +#define XHCI_PORTSC_OCC (1 << 20) + +/** Port reset change */ +#define XHCI_PORTSC_PRC (1 << 21) + +/** Port link state change */ +#define XHCI_PORTSC_PLC (1 << 22) + +/** Port config error change */ +#define XHCI_PORTSC_CEC (1 << 23) + +/* Cold Attach Status 1: Far-end Receiver Terminations were detected */ +#define XHCI_PORTSC_CAS (1 << 24) + +/* Wake on Connect Enable 1: enable port to be sensitive to device connects */ +#define XHCI_PORTSC_WCE (1 << 25) + +/* Wake on Disconnect Enable 1: enable port to be sensitive to device disconnects */ +#define XHCI_PORTSC_WDE (1 << 26) + +/* Wake on Over-current Enable 1: enable port to be sensitive to over-current conditions */ +#define XHCI_PORTSC_WOE (1 << 27) + +/* Device Removable, 0: Device is removable. 1: Device is non-removable */ +#define XHCI_PORTSC_DR (1 << 30) + +/* Warm Port Reset 1: follow Warm Reset sequence */ +#define XHCI_PORTSC_WPR (1 << 31) + +/** Port status change mask */ +#define XHCI_PORTSC_CHANGE \ + ( XHCI_PORTSC_CSC | XHCI_PORTSC_PEC | XHCI_PORTSC_WRC | \ + XHCI_PORTSC_OCC | XHCI_PORTSC_PRC | XHCI_PORTSC_PLC | \ + XHCI_PORTSC_CEC ) + +#define XHCI_PORTSC_RW_MASK (XHCI_PORTSC_PR | XHCI_PORTSC_PLS_MASK | XHCI_PORTSC_PP \ + | XHCI_PORTSC_PIC_MASK | XHCI_PORTSC_LWS | XHCI_PORTSC_WCE \ + | XHCI_PORTSC_WDE | XHCI_PORTSC_WOE) + +/** Port status and control bits which should be preserved + * + * The port status and control register is a horrendous mix of + * differing semantics. Some bits are written to only when a separate + * write strobe bit is set. Some bits should be preserved when + * modifying other bits. Some bits will be cleared if written back as + * a one. Most excitingly, the "port enabled" bit has the semantics + * that 1=enabled, 0=disabled, yet writing a 1 will disable the port. */ -#define XHCI_RING(_trb) \ - ((struct xhci_ring*)((unsigned long)(_trb) & ~(XHCI_RING_SIZE-1))) +#define XHCI_PORTSC_PRESERVE ( XHCI_PORTSC_PP | XHCI_PORTSC_PIC_MASK ) + +/** Port power management status and control register */ +#define XHCI_OP_PORTPMSC(port) ( 0x404 - 0x10 + ( (port) << 4 ) ) + +/** Port link info register */ +#define XHCI_OP_PORTLI(port) ( 0x408 - 0x10 + ( (port) << 4 ) ) + +/** Port hardware link power management control register */ +#define XHCI_OP_PORTHLPMC(port) ( 0x40c - 0x10 + ( (port) << 4 ) ) + +/* Doorbell registers are 32 bits in length */ +#define XHCI_REG_DB_SIZE 4 + +/** Interrupter Management Register */ +#define XHCI_RUN_IR_IMAN(intr) ( 0x20 + ( (intr) << 5 ) ) +/* Interrupt Pending, 1: an interrupt is pending for this Interrupter */ +#define XHCI_RUN_IR_IMAN_IP (1 << 0) + +/* Interrupt Enable, 1: capable of generating an interrupt. */ +#define XHCI_RUN_IR_IMAN_IE (1 << 1) + +/** Interrupter Moderation Register */ +#define XHCI_RUN_IR_IMOD(intr) ( 0x24 + ( (intr) << 5 ) ) + +/** Event ring segment table size register */ +#define XHCI_RUN_ERSTSZ(intr) ( 0x28 + ( (intr) << 5 ) ) + +/** Event ring segment table base address register */ +#define XHCI_RUN_ERSTBA(intr) ( 0x30 + ( (intr) << 5 ) ) + +/** Event ring dequeue pointer register */ +#define XHCI_RUN_ERDP(intr) ( 0x38 + ( (intr) << 5 ) ) + +/** Event Handler Busy */ +#define XHCI_RUN_ERDP_EHB (1 << 3) + +/** Minimum alignment required for data structures + * + * With the exception of the scratchpad buffer pages (which are + * page-aligned), data structures used by xHCI generally require from + * 16 to 64 byte alignment and must not cross an (xHCI) page boundary. + * We simplify this requirement by aligning each structure on its own + * size, with a minimum of a 64 byte alignment. + */ +#define XHCI_MIN_ALIGN 64 + +/** Maximum transfer size */ +#define XHCI_MTU 65536 + +/** Read/Write Data Barrier for ARM */ #define BARRIER() __asm__ __volatile__("": : :"memory") #ifdef XHCI_AARCH64 @@ -475,55 +393,107 @@ enum TRBCCode { #define DSB() __asm__ __volatile__("dsb": : : "memory") #endif -/************************** Function Prototypes ******************************/ - -/*****************************************************************************/ -static inline void writeq(unsigned long addr, uint64_t val) { +/** + * Read byte from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +static inline uint8_t readb(void *io_addr ) { + uint8_t val = *(volatile const uint8_t *)io_addr; BARRIER(); - *(volatile uint64_t *)addr = val; + return val; } -static inline void writel(unsigned long addr, uint32_t val) { +/** + * Read 16-bit word from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +static inline uint16_t readw(void * io_addr ) { + uint16_t val = *(volatile const uint16_t *)io_addr; BARRIER(); - *(volatile uint32_t *)addr = val; + return val; } -static inline void writew(unsigned long addr, uint16_t val) { +/** + * Read 32-bit dword from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +static inline uint32_t readl(void * io_addr ) { + uint32_t val = *(volatile const uint32_t *)io_addr; BARRIER(); - *(volatile uint16_t *)addr = val; + return val; } -static inline void writeb(unsigned long addr, uint8_t val) { +/** + * Read 64-bit qword from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +static inline uint64_t readq(void * io_addr ) { + uint64_t val = *(volatile const uint64_t *)io_addr; BARRIER(); - *(volatile uint8_t *)addr = val; + return val; } -static inline uint64_t readq(unsigned long addr) { - uint64_t val = *(volatile const uint64_t *)addr; +/** + * Write byte to memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +static inline void writeb(uint8_t data, void * io_addr ) { BARRIER(); - return val; + *(volatile uint8_t *)io_addr = data; } -static inline uint32_t readl(unsigned long addr) { - uint32_t val = *(volatile const uint32_t *)addr; +/** + * Write 16-bit word to memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +static inline void writew(uint16_t data, void * io_addr ) { BARRIER(); - return val; + *(volatile uint16_t *)io_addr = data; } -static inline uint16_t readw(unsigned long addr) { - uint16_t val = *(volatile const uint16_t *)addr; +/** + * Write 32-bit dword to memory-mapped device + * + * @v data Value to writed + * @v io_addr I/O address + */ +static inline void writel(uint32_t data, void * io_addr ) { BARRIER(); - return val; + *(volatile uint32_t *)io_addr = data; } -static inline uint8_t readb(unsigned long addr) { - uint8_t val = *(volatile const uint8_t *)addr; +/** + * Write 64-bit qword to memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +static inline void writeq(uint64_t data, void * io_addr ) { BARRIER(); - return val; + *(volatile uint64_t *)io_addr = data; } -#ifdef __cplusplus -} -#endif +/** + * Byte-order converter for ARM-Little-End + */ +#define CPU_TO_LE64(x) ((uint64_t)(x)) +#define LE64_to_CPU(x) ((uint64_t)(x)) +#define CPU_TO_LE32(x) ((uint32_t)(x)) +#define LE32_TO_CPU(x) ((uint32_t)(x)) +#define CPU_TO_LE16(x) ((uint16_t)(x)) +#define LE16_TO_CPU(x) ((uint16_t)(x)) + -#endif
\ No newline at end of file +#endif /* XHCI_REG_H */
\ No newline at end of file |
