summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzhugengyu <[email protected]>2023-04-21 22:56:38 +0800
committerzhugengyu <[email protected]>2023-04-21 22:56:46 +0800
commitef126eafa66dcb0a0ebb7908be68fdebcc2b651a (patch)
treec1664c0e24ccea37750286f17ec060a53fa0c753
parent53fe0f2cd510a4f1a21da5a1a241cedce89fa4c5 (diff)
update xhci driver
-rw-r--r--port/xhci/usb_hc_xhci.c1292
-rw-r--r--port/xhci/usb_hc_xhci.h177
-rw-r--r--port/xhci/xhci.c2563
-rw-r--r--port/xhci/xhci.h940
-rw-r--r--port/xhci/xhci_dbg.c353
-rw-r--r--port/xhci/xhci_reg.h784
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