diff options
author | Nikunj A Dadhania <nikunj@linux.vnet.ibm.com> | 2013-11-14 15:07:21 +0530 |
---|---|---|
committer | Nikunj A Dadhania <nikunj@linux.vnet.ibm.com> | 2013-11-15 10:36:51 +0530 |
commit | 32dcbfadcddb33ea7a74581a7935accea1aee4c6 (patch) | |
tree | 612cd46a83bea5f2ead2d4ebe8e86ec25f01059a /lib | |
parent | ce91e6a2827c0921be774c7ba9bfd3056778b57d (diff) | |
download | SLOF-32dcbfadcddb33ea7a74581a7935accea1aee4c6.zip SLOF-32dcbfadcddb33ea7a74581a7935accea1aee4c6.tar.gz SLOF-32dcbfadcddb33ea7a74581a7935accea1aee4c6.tar.bz2 |
usb-xhci: add xhci host controller support
Signed-off-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libusb/usb-core.c | 6 | ||||
-rw-r--r-- | lib/libusb/usb-core.h | 2 | ||||
-rw-r--r-- | lib/libusb/usb-xhci.c | 1180 | ||||
-rw-r--r-- | lib/libusb/usb-xhci.h | 274 |
4 files changed, 1453 insertions, 9 deletions
diff --git a/lib/libusb/usb-core.c b/lib/libusb/usb-core.c index 6c40913..3f94341 100644 --- a/lib/libusb/usb-core.c +++ b/lib/libusb/usb-core.c @@ -506,8 +506,10 @@ int setup_new_device(struct usb_dev *dev, unsigned int port) goto fail; dev->control->mps = descr.bMaxPacketSize0; - if (!usb_set_address(dev, port)) - goto fail; + if (dev->speed != USB_SUPER_SPEED) { + if(!usb_set_address(dev, port)) + goto fail; + } mb(); SLOF_msleep(100); diff --git a/lib/libusb/usb-core.h b/lib/libusb/usb-core.h index fc9a0be..7441979 100644 --- a/lib/libusb/usb-core.h +++ b/lib/libusb/usb-core.h @@ -70,6 +70,7 @@ enum USB_SPEED_TYPE { USB_LOW_SPEED = 0, USB_FULL_SPEED = 1, USB_HIGH_SPEED = 2, + USB_SUPER_SPEED = 3, }; /* Max number of endpoints supported in a device */ @@ -84,6 +85,7 @@ struct usb_dev { struct usb_pipe *bulk_in; struct usb_pipe *bulk_out; struct usb_ep_descr ep[USB_DEV_EP_MAX]; + void *priv; uint32_t ep_cnt; uint32_t class; uint32_t speed; diff --git a/lib/libusb/usb-xhci.c b/lib/libusb/usb-xhci.c index c6022f9..5c8cd3a 100644 --- a/lib/libusb/usb-xhci.c +++ b/lib/libusb/usb-xhci.c @@ -61,12 +61,785 @@ static void dump_xhci_regs(struct xhci_hcd *xhcd) #endif } +static void print_port_status(struct xhci_port_regs *prs) +{ +#ifdef XHCI_DEBUG + uint32_t portsc; + uint32_t CCS, PED, PP, PLS, i, PR = 0; + + portsc = read_reg32(&prs->portsc); + dprintf("portsc %08x portpmsc %08x portli %08x\n", + portsc, + read_reg32(&prs->portpmsc), + read_reg32(&prs->portli)); + + if (portsc & PORTSC_CCS) { + printf("CCS "); + CCS = 1; + } + if (portsc & PORTSC_PED) { + printf("PED "); + PED = 1; + } + if (portsc & PORTSC_OCA) + printf("OCA "); + if (portsc & PORTSC_PR) + printf("OCA "); + PLS = (portsc & PORTSC_PLS_MASK) >> 5; + printf("PLS:%d ", PLS); + if (portsc & PORTSC_PP) { + printf("PP "); + PP = 1; + } + printf("PS:%d ", (portsc & PORTSC_PS_MASK) >> 10); + printf("PIC:%d ", (portsc & PORTSC_PIC_MASK) >> 14); + if (portsc & PORTSC_LWS) + printf("LWS "); + if (portsc & PORTSC_CSC) + printf("CSC "); + if (portsc & PORTSC_PEC) + printf("PEC "); + if (portsc & PORTSC_WRC) + printf("WRC "); + if (portsc & PORTSC_OCC) + printf("OCC "); + if (portsc & PORTSC_PRC) + printf("PRC "); + if (portsc & PORTSC_PLC) + printf("PLC "); + if (portsc & PORTSC_CEC) + printf("CEC "); + if (portsc & PORTSC_CAS) + printf("CAS "); + if (portsc & PORTSC_WCE) + printf("WCE "); + if (portsc & PORTSC_WDE) + printf("WDE "); + if (portsc & PORTSC_WOE) + printf("WOE "); + if (portsc & PORTSC_DR) + printf("DR "); + if (portsc & PORTSC_WPR) + printf("WPR "); + printf("\n"); + + for (i = 0 ; i < (sizeof(ps_array_usb3)/sizeof(struct port_state)); i++) { + if (PP == ps_array_usb3[i].PP) { + if (CCS == ps_array_usb3[i].CCS) { + if (PED == ps_array_usb3[i].PED) { + if (PR == ps_array_usb3[i].PR) { + dprintf("%s - PLS %d\n", ps_array_usb3[i].state, PLS); + break; + } + } + } + } + } +#endif + +} + +static inline bool xhci_is_hc_ready(uint32_t *usbsts) +{ + return (read_reg32(usbsts) & XHCI_USBSTS_CNR) ? false : true; +} + +static inline bool xhci_wait_for_cnr(uint32_t *usbsts) +{ + /* Standard: + * Note: The xHC should halt within 16 ms. of software clearing the + * R/S bit to ‘0’. + * Give some more time... 32ms + */ + int count = 320; + dprintf("Waiting for Controller ready .."); + while (!xhci_is_hc_ready(usbsts)) { + dprintf("."); + count--; + if (!count) { + dprintf(" failed %08X\n", read_reg32(usbsts)); + return false; + } + SLOF_usleep(100); + } + dprintf(" done\n"); + return true; +} + +/* value: + true = start controller + false = stop controller +*/ +static int xhci_hcd_set_runstop(struct xhci_op_regs *op, bool value) +{ + uint32_t reg; + + dprintf("Request %s\n", value ? "RUN" : "STOP"); + if (!xhci_is_hc_ready(&op->usbsts)) { + dprintf("Controller not ready\n"); + return false; + } + + reg = read_reg32(&op->usbcmd); + if (value) + reg |= value; + else + reg &= ~value; + dprintf("writing %08X\n", reg); + write_reg32(&op->usbcmd, reg); + mb(); + xhci_wait_for_cnr(&op->usbsts); + return true; +} + +static int xhci_hcd_reset(struct xhci_op_regs *op) +{ + uint32_t reg; + + /* Check if the controller is halted, else halt it */ + if (!(read_reg32(&op->usbsts) & XHCI_USBSTS_HCH)) { + dprintf("HCHalted not set\n"); + if (!xhci_hcd_set_runstop(op, false)) + return false; + } + + if (read_reg32(&op->usbsts) & XHCI_USBSTS_CNR) { + dprintf("Controller not ready\n"); + return false; + } + + reg = read_reg32(&op->usbcmd) | XHCI_USBCMD_HCRST; + /* Ready to Reset the controller now */ + write_reg32(&op->usbcmd, reg); + xhci_wait_for_cnr(&op->usbsts); + return true; +} + +static void xhci_handle_cmd_completion(struct xhci_hcd *xhcd, + struct xhci_event_trb *event) +{ + uint32_t flags, slot_id, status; + + status = le32_to_cpu(event->status); + flags = le32_to_cpu(event->flags); + slot_id = TRB_SLOT_ID(flags); + if (TRB_STATUS(status) == COMP_SUCCESS) + xhcd->slot_id = slot_id; + else + xhcd->slot_id = 0; +} + +static struct xhci_event_trb *xhci_poll_event(struct xhci_hcd *xhcd, + uint32_t event_type) +{ + struct xhci_event_trb *event; + uint64_t val; + uint32_t flags, time; + int index; + + mb(); + event = (struct xhci_event_trb *)xhcd->ering.deq; + flags = le32_to_cpu(event->flags); + + dprintf("Reading from event ptr %p %08x\n", event, flags); + time = SLOF_GetTimer() + USB_TIMEOUT; + + while ((flags & TRB_CYCLE_STATE) != xhcd->ering.cycle_state) { + mb(); + flags = le32_to_cpu(event->flags); + if (time < SLOF_GetTimer()) + return NULL; + } + + mb(); + flags = le32_to_cpu(event->flags); + switch(TRB_TYPE(flags)) + { + case TRB_CMD_COMPLETION: + dprintf("CMD Completion\n"); + xhci_handle_cmd_completion(xhcd, event); + break; + case TRB_PORT_STATUS: + dprintf("Port status event\n"); + break; + case TRB_TRANSFER_EVENT: + dprintf("XFER event addr %16lx, status %08x, flags %08x\n", + le64_to_cpu(event->addr), + le32_to_cpu(event->status), + le32_to_cpu(event->flags)); + break; + default: + printf("TRB_TYPE %d\n", TRB_TYPE(flags)); + dprintf("Event addr %16lx, status %08x, flags %08x state %d\n", + le64_to_cpu(event->addr), + le32_to_cpu(event->status), + flags, xhcd->ering.cycle_state); + break; + } + xhcd->ering.deq = (uint64_t) (event + 1); + + event->addr = 0; + event->status = 0; + event->flags = cpu_to_le32(xhcd->ering.cycle_state); + + index = xhcd->ering.deq - (uint64_t)xhcd->ering.trbs; + val = xhcd->ering.trbs_dma; + val += (index % XHCI_EVENT_TRBS_SIZE); + if (!(index % XHCI_EVENT_TRBS_SIZE)) { + xhcd->ering.deq = (uint64_t)xhcd->ering.trbs; + xhcd->ering.cycle_state = xhcd->ering.cycle_state ? 0 : 1; + dprintf("Rounding %d\n", xhcd->ering.cycle_state); + } + dprintf("Update start %x deq %x index %d\n", + xhcd->ering.trbs_dma, val, index/sizeof(*event)); + write_reg64(&xhcd->run_regs->irs[0].erdp, val); + return event; +} + +static void xhci_send_cmd(struct xhci_hcd *xhcd, uint32_t field1, + uint32_t field2, uint32_t field3, uint32_t field4) +{ + struct xhci_db_regs *dbr; + struct xhci_command_trb *cmd; + uint32_t val, cycle_state; + + dbr = xhcd->db_regs; + cmd = (struct xhci_command_trb *)xhcd->crseg.enq; + + cmd->field[0] = cpu_to_le32(field1); + cmd->field[1] = cpu_to_le32(field2); + cmd->field[2] = cpu_to_le32(field3); + + val = le32_to_cpu(cmd->field[3]); + cycle_state = (val & 0x1) ? 0 : 1; + val = field4 | cycle_state; + cmd->field[3] = cpu_to_le32(val); + + dprintf("CMD %016lx val %08x cycle_state %d field1 %08x, field2 %08x, field3 %08x field4 %08x\n", + cmd, val, cycle_state, + le32_to_cpu(cmd->field[0]), + le32_to_cpu(cmd->field[1]), + le32_to_cpu(cmd->field[2]), + le32_to_cpu(cmd->field[3]) + ); + + /* Ring the doorbell */ + write_reg32(&dbr->db[0], 0); + xhci_poll_event(xhcd, 0); + cmd++; + xhcd->crseg.enq = (uint64_t)cmd; + return; +} + +static void xhci_send_enable_slot(struct xhci_hcd *xhcd, uint32_t port) +{ + uint32_t field1, field2, field3, field4; + + field1 = 0; + field2 = 0; + field3 = 0; + field4 = TRB_CMD_TYPE(TRB_ENABLE_SLOT); + xhci_send_cmd(xhcd, field1, field2, field3, field4); +} + +static void xhci_send_addr_device(struct xhci_hcd *xhcd, uint32_t slot_id, + uint64_t dma_in_ctx) +{ + uint32_t field1, field2, field3, field4; + + dprintf("Address device %lx, low %x, high %x\n", dma_in_ctx, + TRB_ADDR_LOW(dma_in_ctx), + TRB_ADDR_HIGH(dma_in_ctx)); + field1 = TRB_ADDR_LOW(dma_in_ctx) & ~0xF; + field2 = TRB_ADDR_HIGH(dma_in_ctx); + field3 = 0; + field4 = TRB_CMD_TYPE(TRB_ADDRESS_DEV) | TRB_CMD_SLOT_ID(slot_id); + xhci_send_cmd(xhcd, field1, field2, field3, field4); +} + +static uint32_t xhci_get_epno(struct usb_pipe *pipe) +{ + uint32_t x_epno; + x_epno = pipe->dir | 2 * pipe->epno; + dprintf("EPno %d:%d DIR %d\n", pipe->epno, x_epno, pipe->dir); + return x_epno; +} + +static void xhci_configure_ep(struct xhci_hcd *xhcd, uint32_t slot_id, + uint64_t dma_in_ctx) +{ + uint32_t field1, field2, field3, field4; + + dprintf("Configure EP %lx, low %x, high %x\n", dma_in_ctx, + TRB_ADDR_LOW(dma_in_ctx), + TRB_ADDR_HIGH(dma_in_ctx)); + field1 = TRB_ADDR_LOW(dma_in_ctx) & ~0xF; + field2 = TRB_ADDR_HIGH(dma_in_ctx); + field3 = 0; + field4 = TRB_CMD_TYPE(TRB_CONFIG_EP) | TRB_CMD_SLOT_ID(slot_id); + xhci_send_cmd(xhcd, field1, field2, field3, field4); +} + +static void xhci_init_seg(struct xhci_seg *seg, uint32_t size, uint32_t type) +{ + struct xhci_link_trb *link; + + seg->size = size / XHCI_TRB_SIZE; + seg->next = NULL; + seg->type = type; + seg->cycle_state = 1; + seg->enq = (uint64_t)seg->trbs; + seg->deq = (uint64_t)seg->trbs; + memset((void *)seg->trbs, 0, size); + + link =(struct xhci_link_trb *) (seg->trbs + seg->size - 1); + link->addr = cpu_to_le64(seg->trbs_dma); + link->field2 = 0; + link->field3 = cpu_to_le32(0x1 | TRB_CMD_TYPE(TRB_LINK)); + return; +} + +static int xhci_alloc_seg(struct xhci_seg *seg, uint32_t size, uint32_t type) +{ + seg->trbs = (union xhci_trb *)SLOF_dma_alloc(size); + if (!seg->trbs) { + dprintf("Alloc failed\n"); + return false; + } + xhci_init_seg(seg, size, type); + seg->trbs_dma = SLOF_dma_map_in((void *)seg->trbs, size, false); + + dprintf(" TRBs %016lX TRBS-DMA %016lX\n", seg->trbs, seg->trbs_dma); + return true; +} + +static void xhci_free_seg(struct xhci_seg *seg, uint32_t size) +{ + if (seg->trbs) { + dprintf(" TRBs %016lX TRBS-DMA %016lX size %x\n", seg->trbs, seg->trbs_dma, size); + SLOF_dma_map_out(seg->trbs_dma, (void *)seg->trbs, size); + SLOF_dma_free((void *)seg->trbs, size); + } + memset(seg, 0, sizeof(*seg)); +} + +#define CTX_SIZE(x) ( (x) ? 64 : 32 ) + +static int xhci_alloc_ctx(struct xhci_ctx *ctx, uint32_t size, uint32_t type) +{ + ctx->addr = (uint8_t *)SLOF_dma_alloc(size); + if (!ctx->addr) { + dprintf("Alloc failed\n"); + return false; + } + ctx->size = size; + ctx->type = type; + memset((void *)ctx->addr, 0, size); + ctx->dma_addr = SLOF_dma_map_in((void *)ctx->addr, size, false); + dprintf("ctx %llx, ctx_dma %llx\n", ctx->addr, ctx->dma_addr); + return true; +} + +static struct xhci_control_ctx *xhci_get_control_ctx(struct xhci_ctx *ctx) +{ + if (ctx->type == XHCI_CTX_TYPE_INPUT) + return (struct xhci_control_ctx *) ctx->addr; + return NULL; +} + +static struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctx *ctx, uint32_t ctx_size) +{ + uint32_t offset = 0; + + if (ctx->type == XHCI_CTX_TYPE_INPUT) + offset += ctx_size; + return (struct xhci_slot_ctx *)(ctx->addr + offset); +} + +static struct xhci_ep_ctx *xhci_get_ep0_ctx(struct xhci_ctx *ctx, uint32_t ctx_size) +{ + uint32_t offset = 0; + + offset = ctx_size; + if (ctx->type == XHCI_CTX_TYPE_INPUT) + offset += ctx_size; + return (struct xhci_ep_ctx *)(ctx->addr + offset); +} + +static struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctx *ctx, uint32_t ctx_size, + uint32_t epno) +{ + uint32_t offset = 0; + + offset = ctx_size * epno; + if (ctx->type == XHCI_CTX_TYPE_INPUT) + offset += ctx_size; + return (struct xhci_ep_ctx *)(ctx->addr + offset); +} + +static void xhci_free_ctx(struct xhci_ctx *ctx, uint32_t size) +{ + SLOF_dma_map_out(ctx->dma_addr, (void *)ctx->addr, size); + SLOF_dma_free((void *)ctx->addr, size); +} + +static uint32_t usb_control_max_packet(uint32_t speed) +{ + uint32_t max_packet = 0; + + switch(speed) + { + case USB_LOW_SPEED: + max_packet = 8; + break; + case USB_FULL_SPEED: + max_packet = 8; + break; + case USB_HIGH_SPEED: + max_packet = 64; + break; + case USB_SUPER_SPEED: + max_packet = 512; + break; + default: + /* should not reach here */ + dprintf("Unknown speed\n"); + } + return max_packet; +} + +static uint32_t xhci_alloc_dev(struct xhci_hcd *xhcd, uint32_t slot_id, uint32_t port) +{ + struct usb_dev *dev; + struct xhci_dev *xdev; + struct xhci_slot_ctx *slot; + struct xhci_control_ctx *ctrl; + struct xhci_ep_ctx *ep0; + uint32_t ctx_size, val; + uint16_t max_packet; + uint32_t newport; + + ctx_size = CTX_SIZE(xhcd->hcc_csz_64); + xdev = &xhcd->xdevs[slot_id]; + xdev->slot_id = slot_id; + xdev->ctx_size = ctx_size; + + /* 4.3.3 Device Slot initialization */ + /* Step 1 */ + if (!xhci_alloc_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE, XHCI_CTX_TYPE_INPUT)) { + dprintf("Failed allocating in_ctx\n"); + goto fail; + } + + /* Step 2 */ + ctrl = xhci_get_control_ctx(&xdev->in_ctx); + ctrl->a_flags = cpu_to_le32(0x3); /* A0, A1 */ + ctrl->d_flags = 0; + + /* Step 3 */ + slot = xhci_get_slot_ctx(&xdev->in_ctx, ctx_size); + newport = port - XHCI_CONFIG_MAX_SLOT + 1; + val = LAST_CONTEXT(1) | SLOT_SPEED_SS | (newport << 16); /* FIXME speed, read from PS */ + slot->field1 = cpu_to_le32(val); + slot->field2 = cpu_to_le32(ROOT_HUB_PORT(newport)); /* FIXME how to get port no */ + + /* Step 4 */ + if (!xhci_alloc_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE, TYPE_CTRL)) { + dprintf("Failed allocating control\n"); + goto fail_out_ctx; + } + + /* Step 5 */ + ep0 = xhci_get_ep0_ctx(&xdev->in_ctx, ctx_size); + val = 0; + max_packet = usb_control_max_packet(USB_SUPER_SPEED); + max_packet = 64; + val = EP_TYPE(EP_CTRL) | MAX_BURST(0) | ERROR_COUNT(3) | + MAX_PACKET_SIZE(max_packet); + ep0->field2 = cpu_to_le32(val);; + ep0->deq_addr = cpu_to_le64(xdev->control.trbs_dma | xdev->control.cycle_state); + ep0->field4 = cpu_to_le32(8); + + /* Step 6 */ + if (!xhci_alloc_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE, XHCI_CTX_TYPE_DEVICE)) { + dprintf("Failed allocating out_ctx\n"); + goto fail_in_ctx; + } + + /* Step 7 */ + xhcd->dcbaap[slot_id] = cpu_to_le64(xdev->out_ctx.dma_addr); + + /* Step 8 */ + slot = xhci_get_slot_ctx(&xdev->out_ctx, ctx_size); + ep0 = xhci_get_ep0_ctx(&xdev->out_ctx, ctx_size); + + dprintf("Slot State %x \n", SLOT_STATE(le32_to_cpu(slot->field4))); + xhci_send_addr_device(xhcd, slot_id, xdev->in_ctx.dma_addr); + mb(); + dprintf("Slot State %x \n", SLOT_STATE(le32_to_cpu(slot->field4))); + + dprintf("EP0 f0 %08X f1 %08X %016lX %08X\n", + le32_to_cpu(ep0->field1), + le32_to_cpu(ep0->field2), + le64_to_cpu(ep0->deq_addr), + le32_to_cpu(ep0->field4)); + + /* Step 9 - configure ep */ + ctrl->a_flags = cpu_to_le32(0x1); /* A0 */ + ctrl->d_flags = 0; + xhci_configure_ep(xhcd, slot_id, xdev->in_ctx.dma_addr); + mb(); + dprintf("Slot State %x \n", SLOT_STATE(le32_to_cpu(slot->field4))); + dprintf("USB Device address %d \n", USB_DEV_ADDRESS(le32_to_cpu(slot->field4))); + dprintf("EP0 f0 %08X f1 %08X %016lX %08X\n", + le32_to_cpu(ep0->field1), + le32_to_cpu(ep0->field2), + le64_to_cpu(ep0->deq_addr), + le32_to_cpu(ep0->field4)); + + dev = usb_devpool_get(); + dprintf("allocated device %p\n", dev); + dev->hcidev = xhcd->hcidev; + dev->speed = USB_SUPER_SPEED; + dev->addr = USB_DEV_ADDRESS(slot->field4); + dev->port = newport; + dev->priv = xdev; + xdev->dev = dev; + if (setup_new_device(dev, newport)) + return true; +fail_out_ctx: + xhci_free_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE); +fail_in_ctx: + xhci_free_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE); +fail: + return false; +} + +static void xhci_free_dev(struct xhci_dev *xdev) +{ + xhci_free_seg(&xdev->bulk_in, XHCI_DATA_TRBS_SIZE); + xhci_free_seg(&xdev->bulk_out, XHCI_DATA_TRBS_SIZE); + xhci_free_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE); + xhci_free_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE); + xhci_free_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE); +} + +static uint32_t usb3_dev_init(struct xhci_hcd *xhcd, uint32_t port) +{ + /* Device enable slot */ + xhci_send_enable_slot(xhcd, port); + if (!xhcd->slot_id) { + dprintf("Unable to get slot id\n"); + return false; + } + dprintf("SLOT ID: %d\n", xhcd->slot_id); + if (!xhci_alloc_dev(xhcd, xhcd->slot_id, port)) { + dprintf("Unable to allocate device\n"); + return false; + } + return true; +} + +static int xhci_hub_check_ports(struct xhci_hcd *xhcd) +{ + uint32_t num_ports, portsc, i; + struct xhci_op_regs *op; + struct xhci_port_regs *prs; + + dprintf("enter\n"); + + op = xhcd->op_regs; + num_ports = read_reg32(&op->config); + for (i = 0; i < num_ports * 2; i++) { + prs = &op->prs[i]; + portsc = read_reg32(&prs->portsc); + if ((portsc & PORTSC_CCS) && + (portsc & PORTSC_PP) && + (portsc & PORTSC_PED)) { + /* Device present and enabled */ + dprintf("Device present on port %d\n", i); + /* Reset the port */ + portsc = read_reg32(&prs->portsc); + portsc = portsc | PORTSC_PR; + write_reg32(&prs->portsc, portsc); + /* FIXME poll for port event */ + SLOF_msleep(20); + xhci_poll_event(xhcd, 0); + portsc = read_reg32(&prs->portsc); + if (portsc & ~PORTSC_PRC) { + dprintf("Port reset complete %d\n", i); + } + print_port_status(prs); + /* FIXME need to check if this is usb3 device */ + if (!usb3_dev_init(xhcd, i)) { + dprintf("USB device initialization failed\n"); + } + } + } + dprintf("exit\n"); + return 0; +} + +static int xhci_hcd_init(struct xhci_hcd *xhcd) +{ + struct xhci_op_regs *op; + struct xhci_int_regs *irs; + uint64_t val; + uint32_t reg; + + if (!xhcd) { + dprintf("NULL pointer\n"); + goto fail; + } + + op = xhcd->op_regs; + irs = &xhcd->run_regs->irs[0]; + if (!xhci_hcd_reset(op)) { + dprintf("Reset failed\n"); + goto fail; + } + + write_reg32(&op->config, XHCI_CONFIG_MAX_SLOT); + reg = read_reg32(&xhcd->cap_regs->hccparams); + /* 64byte context !! */ + xhcd->hcc_csz_64 = (reg & XHCI_HCCPARAMS_CSZ) ? 1 : 0; + + if (xhcd->hcc_csz_64) { + printf("usb-xhci: 64 Byte context not supported\n"); + goto fail; + } + /* + * 6.1 Device Context Base Address Array + * + * Allocate memory and initialize + */ + xhcd->dcbaap = (uint64_t *)SLOF_dma_alloc(XHCI_DCBAAP_MAX_SIZE); + if (!xhcd->dcbaap) { + dprintf("Alloc failed\n"); + goto fail; + } + memset((void *)xhcd->dcbaap, 0, XHCI_DCBAAP_MAX_SIZE); + xhcd->dcbaap_dma = SLOF_dma_map_in((void *)xhcd->dcbaap, + XHCI_DCBAAP_MAX_SIZE, false); + dprintf("dcbaap %llx, dcbaap_phys %llx\n", xhcd->dcbaap, xhcd->dcbaap_dma); + write_reg64(&op->dcbaap, xhcd->dcbaap_dma); + + /* + * Command Ring Control - TRB + * FIXME - better way to allocate it... + */ + if (!xhci_alloc_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE, TYPE_COMMAND)) + goto fail_dcbaap; + + val = read_reg64(&op->crcr) & ~XHCI_CRCR_CRP_MASK; + val = val | (xhcd->crseg.trbs_dma & XHCI_CRCR_CRP_MASK); + write_reg64(&op->crcr, val); + + /* + * Event Ring Control - TRB + * Allocate event TRBS + */ + if (!xhci_alloc_seg(&xhcd->ering, XHCI_EVENT_TRBS_SIZE, TYPE_EVENT)) + goto fail_crseg; + + /* + * Populate event ring segment table. + * Note: only using one segment. + */ + xhcd->erst.entries = SLOF_dma_alloc(XHCI_EVENT_TRBS_SIZE); + if (!xhcd->erst.entries) + goto fail_ering; + xhcd->erst.dma = SLOF_dma_map_in((void *)xhcd->erst.entries, + XHCI_EVENT_TRBS_SIZE, false); + xhcd->erst.num_segs = XHCI_ERST_NUM_SEGS; + + /* populate entries[0] */ + write_reg64(&xhcd->erst.entries->addr, xhcd->ering.trbs_dma); + write_reg32(&xhcd->erst.entries->size, xhcd->ering.size); + write_reg32(&xhcd->erst.entries->reserved, 0); + + /* populate erdp */ + val = read_reg64(&irs->erdp) & ~XHCI_ERDP_MASK; + val = val | (xhcd->ering.trbs_dma & XHCI_ERDP_MASK); + write_reg64(&irs->erdp, val); + + /* populate erstsz */ + val = read_reg32(&irs->erstsz) & ~XHCI_ERST_SIZE_MASK; + val = val | xhcd->erst.num_segs; + write_reg32(&irs->erstsz, val); + + /* Now write the erstba */ + val = read_reg64(&irs->erstba) & ~XHCI_ERST_ADDR_MASK; + val = val | (xhcd->erst.dma & XHCI_ERST_ADDR_MASK); + write_reg64(&irs->erstba, val); + + dprintf("ERDP %llx TRB-DMA %llx\n", read_reg64(&irs->erdp), + xhcd->ering.trbs_dma); + dprintf("ERST %llx, ERST DMA %llx, size %d\n", + (uint64_t)xhcd->erst.entries, xhcd->erst.dma, + xhcd->erst.num_segs); + + mb(); + if (!xhci_hcd_set_runstop(op, true)) + goto fail_ering; + + xhci_hub_check_ports(xhcd); + + return true; +fail_ering: + xhci_free_seg(&xhcd->ering, XHCI_EVENT_TRBS_SIZE); +fail_crseg: + val = read_reg64(&op->crcr) & ~XHCI_CRCR_CRP_MASK; + write_reg64(&op->crcr, val); + xhci_free_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE); +fail_dcbaap: + write_reg64(&op->dcbaap, 0); + SLOF_dma_map_out(xhcd->dcbaap_dma, (void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); + SLOF_dma_free((void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); +fail: + return false; +} + +static int xhci_hcd_exit(struct xhci_hcd *xhcd) +{ + struct xhci_op_regs *op; + uint64_t val; + int i; + + if (!xhcd) { + dprintf("NULL pointer\n"); + return false; + } + op = xhcd->op_regs; + + if (!xhci_hcd_set_runstop(op, false)) { + dprintf("NULL pointer\n"); + } + + for (i = 1; i < XHCI_CONFIG_MAX_SLOT; i++) { + if (xhcd->xdevs[i].dev) + xhci_free_dev(&xhcd->xdevs[i]); + } + + SLOF_dma_map_out(xhcd->erst.dma, xhcd->erst.entries,XHCI_EVENT_TRBS_SIZE); + SLOF_dma_free(xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE); + xhci_free_seg(&xhcd->ering, XHCI_EVENT_TRBS_SIZE); + + val = read_reg64(&op->crcr) & ~XHCI_CRCR_CRP_MASK; + write_reg64(&op->crcr, val); + xhci_free_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE); + write_reg64(&op->dcbaap, 0); + SLOF_dma_map_out(xhcd->dcbaap_dma, (void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); + SLOF_dma_free((void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); + return true; +} + static void xhci_init(struct usb_hcd_dev *hcidev) { struct xhci_hcd *xhcd; printf(" XHCI: Initializing\n"); - dprintf("%s: device base address %p\n", __func__, hcidev->base); + dprintf("device base address %p\n", hcidev->base); hcidev->base = (void *)((uint64_t)hcidev->base & ~7); xhcd = SLOF_alloc_mem(sizeof(*xhcd)); @@ -86,16 +859,415 @@ static void xhci_init(struct usb_hcd_dev *hcidev) read_reg32(&xhcd->cap_regs->rtsoff)); xhcd->db_regs = (struct xhci_db_regs *)(hcidev->base + read_reg32(&xhcd->cap_regs->dboff)); - -#ifdef XHCI_DEBUG dump_xhci_regs(xhcd); -#endif + xhci_hcd_init(xhcd); + dump_xhci_regs(xhcd); +} + +static void xhci_exit(struct usb_hcd_dev *hcidev) +{ + struct xhci_hcd *xhcd; + + dprintf("%s: enter \n", __func__); + if (!hcidev && !hcidev->priv) { + return; + } + + xhcd = hcidev->priv; + xhci_hcd_exit(xhcd); + SLOF_free_mem(xhcd, sizeof(*xhcd)); + hcidev->priv = NULL; +} + +static void fill_trb_buff(struct xhci_command_trb *cmd, uint32_t field1, + uint32_t field2, uint32_t field3, uint32_t field4) +{ + uint32_t val, cycle_state; + + cmd->field[0] = cpu_to_le32(field1); + cmd->field[1] = cpu_to_le32(field2); + cmd->field[2] = cpu_to_le32(field3); + + val = le32_to_cpu(cmd->field[3]); + cycle_state = (val & 0x1) ? 0 : 1; + val = cycle_state | (field4 & ~0x1); + cmd->field[3] = cpu_to_le32(val); + + dprintf("CMD %016lx val %08x cycle_state %d field1 %08x, field2 %08x, field3 %08x field4 %08x\n", + cmd, val, cycle_state, + le32_to_cpu(cmd->field[0]), + le32_to_cpu(cmd->field[1]), + le32_to_cpu(cmd->field[2]), + le32_to_cpu(cmd->field[3]) + ); + + return; +} + +static void fill_setup_trb(struct xhci_command_trb *cmd, struct usb_dev_req *req, + uint32_t size) +{ + uint32_t field1, field2, field3, field4 = 0; + uint64_t req_raw; + uint32_t datalen = 0, pid = 0; + + req_raw = *((uint64_t *)req); + dprintf("%lx %lx \n", *((uint64_t *)req), req_raw); + /* req_raw is already in right byte order... */ + field1 = cpu_to_le32(TRB_ADDR_HIGH(req_raw)); + field2 = cpu_to_le32(TRB_ADDR_LOW(req_raw)); + field3 = 8; /* ALWAYS 8 */ + + datalen = cpu_to_le16(req->wLength); + if (datalen) { + pid = (req->bmRequestType & REQT_DIR_IN) ? 3 : 2; + field4 = TRB_TRT(pid); + } + field4 |= TRB_CMD_TYPE(TRB_SETUP_STAGE) | TRB_IDT; + fill_trb_buff(cmd, field1, field2, field3, field4); +} + +static void fill_setup_data(struct xhci_command_trb *cmd, void *data, + uint32_t size, uint32_t dir) +{ + uint32_t field1, field2, field3, field4; + + field1 = TRB_ADDR_LOW(data); + field2 = TRB_ADDR_HIGH(data); + field3 = size; + if (dir) + field4 = TRB_DIR_IN; + field4 |= TRB_CMD_TYPE(TRB_DATA_STAGE); + fill_trb_buff(cmd, field1, field2, field3, field4); +} + +static void fill_status_trb(struct xhci_command_trb *cmd, uint32_t dir) +{ + uint32_t field1, field2, field3, field4; + + field1 = 0; + field2 = 0; + field3 = 0; + if (dir) + field4 = TRB_DIR_IN; + + field4 |= TRB_CMD_TYPE(TRB_STATUS_STAGE) | TRB_IOC; + fill_trb_buff(cmd, field1, field2, field3, field4); +} + +static void fill_normal_trb(struct xhci_transfer_trb *trb, void *data, + uint32_t size) +{ + uint32_t field1, field2, field3, field4; + + field1 = TRB_ADDR_LOW(data); + field2 = TRB_ADDR_HIGH(data); + field3 = size; + field4 = TRB_CMD_TYPE(TRB_NORMAL) | TRB_IOC; + fill_trb_buff((struct xhci_command_trb *)trb, field1, field2, field3, field4); +} + +static int xhci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data) +{ + struct xhci_dev *xdev; + struct xhci_seg *ctrl; + struct xhci_hcd *xhcd; + struct xhci_command_trb *cmd; + struct xhci_db_regs *dbr; + long req_phys = 0, data_phys = 0; + int ret = true; + uint32_t slot_id, pid = 0, datalen = 0; + + if (!pipe->dev || !pipe->dev->hcidev) { + dprintf(" NULL pointer\n"); + return false; + } + + xdev = pipe->dev->priv; + slot_id = xdev->slot_id; + ctrl = &xdev->control; + xhcd = (struct xhci_hcd *)pipe->dev->hcidev->priv; + dbr = xhcd->db_regs; + if (!ctrl || !xdev || !xhcd) { + dprintf(" NULL pointer\n"); + return false; + } + + cmd = (struct xhci_command_trb *)ctrl->enq; + req_phys = SLOF_dma_map_in(req, sizeof(struct usb_dev_req), true); + fill_setup_trb(cmd, req, sizeof(*req)); + + cmd++; + datalen = cpu_to_le16(req->wLength); + if (datalen) + pid = 1; + if (datalen) { + data_phys = SLOF_dma_map_in(data, datalen, true); + fill_setup_data(cmd, (void *) data_phys, datalen, pid); + cmd++; + } + + fill_status_trb(cmd, pid); + cmd++; + + /* Ring the doorbell - ep0 */ + write_reg32(&dbr->db[slot_id], 1); + if (!xhci_poll_event(xhcd, 0)) { + dprintf("Command failed\n"); + ret = false; + } + ctrl->enq = (uint64_t) cmd; + SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req)); + if (datalen) + SLOF_dma_map_out(data_phys, data, datalen); + return ret; +} + +static inline struct xhci_pipe *xhci_pipe_get_xpipe(struct usb_pipe *pipe) +{ + struct xhci_pipe *xpipe; + xpipe = container_of(pipe, struct xhci_pipe, pipe); + dprintf("%s: xpipe is %p\n", __func__, xpipe); + return xpipe; +} + +static inline struct xhci_seg *xhci_pipe_get_seg(struct usb_pipe *pipe) +{ + struct xhci_pipe *xpipe; + xpipe = xhci_pipe_get_xpipe(pipe); + return xpipe->seg; +} + +static inline void *xhci_get_trb(struct xhci_seg *seg) +{ + uint64_t val, enq; + uint32_t size; + struct xhci_link_trb *link; + + enq = val = seg->enq; + val = val + XHCI_TRB_SIZE; + size = seg->size * XHCI_TRB_SIZE; + if ((val % size) == 0) { + seg->enq = (uint64_t)seg->trbs; + enq = seg->enq; + seg->enq = seg->enq + XHCI_TRB_SIZE; + val = 0; + seg->cycle_state ^= seg->cycle_state; + link = (struct xhci_link_trb *) (seg->trbs + seg->size - 1); + link->addr = cpu_to_le64(seg->trbs_dma); + link->field2 = 0; + link->field3 = cpu_to_le32(0x1 | TRB_CMD_TYPE(TRB_LINK)); + mb(); + } + else { + seg->enq = seg->enq + XHCI_TRB_SIZE; + } + + return (void *)enq; +} + +static int xhci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys, + void *data, int datalen) +{ + struct xhci_dev *xdev; + struct xhci_seg *seg; + struct xhci_hcd *xhcd; + struct xhci_transfer_trb *trb; + struct xhci_db_regs *dbr; + int ret = true; + uint32_t slot_id, epno; + + if (!pipe->dev || !pipe->dev->hcidev) { + dprintf(" NULL pointer\n"); + dprintf(" pipe dev %p hcidev %p\n", pipe->dev, pipe->dev->hcidev); + return false; + } + + xdev = pipe->dev->priv; + slot_id = xdev->slot_id; + seg = xhci_pipe_get_seg(pipe); + xhcd = (struct xhci_hcd *)pipe->dev->hcidev->priv; + dbr = xhcd->db_regs; + if (!seg || !xdev || !xhcd) { + dprintf(" NULL pointer\n"); + dprintf(" seg %p xdev %p xhcd %p\n", seg, xdev, xhcd); + return false; + } + + if (datalen > XHCI_MAX_BULK_SIZE) { + printf("usb-xhci: bulk transfer size too big\n"); + return false; + } + + trb = xhci_get_trb(seg); + fill_normal_trb(trb, (void *)data, datalen); + + epno = xhci_get_epno(pipe); + write_reg32(&dbr->db[slot_id], epno); + if (!xhci_poll_event(xhcd, 0)) { + dprintf("Bulk failed\n"); + ret = false; + } + trb->addr = 0; + trb->len = 0; + trb->flags = 0; + mb(); + + return ret; +} + +static int xhci_alloc_pipe_pool(struct xhci_hcd *xhcd) +{ + struct xhci_pipe *xpipe, *curr, *prev; + unsigned int i, count; + long xpipe_phys = 0; + + count = XHCI_PIPE_POOL_SIZE/sizeof(*xpipe); + xhcd->pool = xpipe = SLOF_dma_alloc(XHCI_PIPE_POOL_SIZE); + if (!xpipe) + return -1; + xhcd->pool_phys = xpipe_phys = SLOF_dma_map_in(xpipe, XHCI_PIPE_POOL_SIZE, true); + dprintf("%s: xpipe %p, xpipe_phys %lx\n", __func__, xpipe, xpipe_phys); + + /* Although an array, link them */ + for (i = 0, curr = xpipe, prev = NULL; i < count; i++, curr++) { + if (prev) + prev->pipe.next = &curr->pipe; + curr->pipe.next = NULL; + prev = curr; + } + + if (!xhcd->freelist) + xhcd->freelist = &xpipe->pipe; + else + xhcd->end->next = &xpipe->pipe; + xhcd->end = &prev->pipe; + + return 0; +} + +static void xhci_init_bulk_ep(struct usb_dev *dev, struct usb_pipe *pipe) +{ + struct xhci_hcd *xhcd; + struct xhci_dev *xdev; + struct xhci_seg *seg; + struct xhci_pipe *xpipe; + struct xhci_control_ctx *ctrl; + struct xhci_ep_ctx *ep; + uint32_t x_epno, val, type; + + if (!pipe || !dev || !dev->priv) + return; + + xdev = dev->priv; + xhcd = dev->hcidev->priv; + dprintf("dir %d\n", pipe->dir); + seg = xhci_pipe_get_seg(pipe); + xpipe = xhci_pipe_get_xpipe(pipe); + if (pipe->dir) { + type = EP_BULK_IN; + seg = &xdev->bulk_in; + } + else { + type = EP_BULK_OUT; + seg = &xdev->bulk_out; + } + + if (!seg->trbs) { + if (!xhci_alloc_seg(seg, XHCI_DATA_TRBS_SIZE, TYPE_BULK)) { + dprintf("Failed allocating seg\n"); + } + } else { + xhci_init_seg(seg, XHCI_DATA_TRBS_SIZE, TYPE_BULK); + } + + pipe->mps = XHCI_MAX_BULK_SIZE; + ctrl = xhci_get_control_ctx(&xdev->in_ctx); + x_epno = xhci_get_epno(pipe); + ep = xhci_get_ep_ctx(&xdev->in_ctx, xdev->ctx_size, x_epno); + val = EP_TYPE(type) | MAX_BURST(0) | ERROR_COUNT(3) | + MAX_PACKET_SIZE(pipe->mps); + ep->field2 = cpu_to_le32(val);; + ep->deq_addr = cpu_to_le64(seg->trbs_dma | seg->cycle_state); + ep->field4 = cpu_to_le32(8); + ctrl->a_flags = cpu_to_le32(BIT(x_epno) | 0x1); + ctrl->d_flags = 0; + xhci_configure_ep(xhcd, xdev->slot_id, xdev->in_ctx.dma_addr); + xpipe->seg = seg; +} + +static struct usb_pipe* xhci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, char *buf, size_t len) +{ + struct xhci_hcd *xhcd; + struct usb_pipe *new = NULL; + + if (!dev) + return NULL; + + xhcd = (struct xhci_hcd *)dev->hcidev->priv; + if (!xhcd->freelist) { + dprintf("usb-xhci: %s allocating pool\n", __func__); + if (xhci_alloc_pipe_pool(xhcd)) + return NULL; + } + + new = xhcd->freelist; + xhcd->freelist = xhcd->freelist->next; + if (!xhcd->freelist) + xhcd->end = NULL; + + memset(new, 0, sizeof(*new)); + new->dev = dev; + new->next = NULL; + new->type = ep->bmAttributes & USB_EP_TYPE_MASK; + new->speed = dev->speed; + new->mps = ep->wMaxPacketSize; + new->dir = (ep->bEndpointAddress & 0x80) >> 7; + new->epno = ep->bEndpointAddress & 0x0f; + + if (new->type == USB_EP_TYPE_BULK) + xhci_init_bulk_ep(dev, new); + + return new; +} + +static void xhci_put_pipe(struct usb_pipe *pipe) +{ + struct xhci_hcd *xhcd; + struct xhci_pipe *xpipe; + + dprintf("usb-xhci: %s enter - %p\n", __func__, pipe); + if (!pipe || !pipe->dev) + return; + xhcd = pipe->dev->hcidev->priv; + + dprintf("dir %d\n", pipe->dir); + if (pipe->type == USB_EP_TYPE_BULK) { + xpipe = xhci_pipe_get_xpipe(pipe); + xpipe->seg = NULL; + } + if (xhcd->end) + xhcd->end->next = pipe; + else + xhcd->freelist = pipe; + + xhcd->end = pipe; + pipe->next = NULL; + pipe->dev = NULL; + memset(pipe, 0, sizeof(*pipe)); + + dprintf("usb-xhci: %s exit\n", __func__); } struct usb_hcd_ops xhci_ops = { .name = "xhci-hcd", .init = xhci_init, + .exit = xhci_exit, .usb_type = USB_XHCI, + .get_pipe = xhci_get_pipe, + .put_pipe = xhci_put_pipe, + .send_ctrl = xhci_send_ctrl, + .transfer_bulk = xhci_transfer_bulk, .next = NULL, }; diff --git a/lib/libusb/usb-xhci.h b/lib/libusb/usb-xhci.h index 02e83f7..9ee2b80 100644 --- a/lib/libusb/usb-xhci.h +++ b/lib/libusb/usb-xhci.h @@ -20,6 +20,8 @@ #include <stdint.h> #include "usb-core.h" +#define BIT(x) (1 << x) + /* 5.3 Host Controller Capability Registers * Table 19 */ @@ -31,6 +33,7 @@ struct xhci_cap_regs { uint32_t hcsparams2; uint32_t hcsparams3; uint32_t hccparams; +#define XHCI_HCCPARAMS_CSZ BIT(2) uint32_t dboff; uint32_t rtsoff; } __attribute__ ((packed)); @@ -38,24 +41,101 @@ struct xhci_cap_regs { /* Table 27: Host Controller USB Port Register Set */ struct xhci_port_regs { uint32_t portsc; +#define PORTSC_CCS BIT(0) +#define PORTSC_PED BIT(1) +#define PORTSC_OCA BIT(3) +#define PORTSC_PR BIT(4) +#define PORTSC_PLS_MASK (0xF << 5) +#define PORTSC_PLS_U0 0 +#define PORTSC_PLS_U1 1 +#define PORTSC_PLS_U2 2 +#define PORTSC_PLS_U3 3 +#define PORTSC_PLS_DISABLED 4 +#define PORTSC_PLS_RXDETECT 5 +#define PORTSC_PLS_INACTIVE 6 +#define PORTSC_PLS_POLLING 7 +#define PORTSC_PLS_RECOVERY 8 +#define PORTSC_PLS_HOTRESET 9 +#define PORTSC_PLS_COMP_MODE 10 +#define PORTSC_PLS_TEST_MODE 11 +#define PORTSC_PLS_RESUME 15 +#define PORTSC_PP BIT(9) +#define PORTSC_PS_MASK (0xF << 10) +#define PORTSC_PIC_MASK (0x3 << 14) +#define PORTSC_LWS BIT(16) +#define PORTSC_CSC BIT(17) +#define PORTSC_PEC BIT(18) +#define PORTSC_WRC BIT(19) +#define PORTSC_OCC BIT(20) +#define PORTSC_PRC BIT(21) +#define PORTSC_PLC BIT(22) +#define PORTSC_CEC BIT(23) +#define PORTSC_CAS BIT(24) +#define PORTSC_WCE BIT(25) +#define PORTSC_WDE BIT(26) +#define PORTSC_WOE BIT(27) +#define PORTSC_DR BIT(30) +#define PORTSC_WPR BIT(31) + uint32_t portpmsc; uint32_t portli; uint32_t reserved; } __attribute__ ((packed)); +struct port_state { + bool PP; + bool CCS; + bool PED; + bool PR; + uint8_t PLS; + char *state; +}; + + +struct port_state ps_array_usb2[] = { + {1, 0, 0, 0, PORTSC_PLS_U0, "ERROR"} +}; + +struct port_state ps_array_usb3[] = { + {0, 0, 0, 0, PORTSC_PLS_DISABLED, "Powered-OFF"}, + {1, 0, 0, 0, PORTSC_PLS_POLLING, "Polling"}, + {1, 0, 0, 0, PORTSC_PLS_U0, "Polling"}, + {1, 0, 0, 0, PORTSC_PLS_RXDETECT, "*** Disconnected ***"}, + {1, 0, 0, 0, PORTSC_PLS_DISABLED, "Disabled"}, + {1, 0, 0, 0, PORTSC_PLS_INACTIVE, "Error"}, + {1, 0, 0, 0, PORTSC_PLS_TEST_MODE,"Loopback"}, + {1, 0, 0, 0, PORTSC_PLS_COMP_MODE,"Compliancek"}, + {1, 1, 0, 1, PORTSC_PLS_U0, "****** Reset ******"}, + {1, 1, 1, 0, PORTSC_PLS_U0, "****** Enabled ******"}, +}; + /* 5.4 Host Controller Operational Registers * Table 26 */ struct xhci_op_regs { uint32_t usbcmd; +#define XHCI_USBCMD_RS BIT(0) +#define XHCI_USBCMD_HCRST BIT(1) + uint32_t usbsts; +#define XHCI_USBSTS_HCH BIT(0) +#define XHCI_USBSTS_CNR BIT(11) + uint32_t pagesize; uint8_t reserved[8]; /* 0C - 13 */ uint32_t dnctrl; /* Device notification control */ uint64_t crcr; /* Command ring control */ +#define XHCI_CRCR_CRP_MASK 0xFFFFFFFFFFFFFFC0 +#define XHCI_CRCR_CRR BIT(3) +#define XHCI_CRCR_CRP_SIZE 4096 + uint8_t reserved1[16]; /* 20 - 2F */ uint64_t dcbaap; /* Device Context Base Address Array Pointer */ - uint8_t config; /* Configure */ +#define XHCI_DCBAAP_MAX_SIZE 2048 + + uint32_t config; /* Configure */ +#define XHCI_CONFIG_MAX_SLOT 4 + uint8_t reserved2[964]; /* 3C - 3FF */ /* USB Port register set */ #define XHCI_PORT_MAX 256 @@ -70,16 +150,19 @@ struct xhci_int_regs { uint32_t iman; uint32_t imod; uint32_t erstsz; +#define XHCI_ERST_SIZE_MASK 0xFFFF uint32_t reserved; uint64_t erstba; +#define XHCI_ERST_ADDR_MASK (~(0x3FUL)) uint64_t erdp; +#define XHCI_ERDP_MASK (~(0xFUL)) } __attribute__ ((packed)); /* 5.5 Host Controller Runtime Registers */ struct xhci_run_regs { uint32_t mfindex; /* microframe index */ uint8_t reserved[28]; -#define XHCI_IRS_MAX +#define XHCI_IRS_MAX 1024 struct xhci_int_regs irs[XHCI_IRS_MAX]; } __attribute__ ((packed)); @@ -88,21 +171,206 @@ struct xhci_db_regs { uint32_t db[256]; } __attribute__ ((packed)); +#define COMP_SUCCESS 1 + +#define TRB_SLOT_ID(x) (((x) & (0xFF << 24)) >> 24) +#define TRB_CMD_SLOT_ID(x) ((x & 0xFF) << 24) +#define TRB_TYPE(x) (((x) & (0x3F << 10)) >> 10) +#define TRB_CMD_TYPE(x) ((x & 0x3F) << 10) +#define TRB_STATUS(x) (((x) & (0xFF << 24)) >> 24) +#define TRB_ADDR_LOW(x) ((uint32_t)((uint64_t)(x))) +#define TRB_ADDR_HIGH(x) ((uint32_t)((uint64_t)(x) >> 32)) +#define TRB_TRT(x) (((x) & 0x3) << 16 ) +#define TRB_DIR_IN BIT(16) +#define TRB_IOC BIT(5) +#define TRB_IDT BIT(6) + +#define TRB_CYCLE_STATE BIT(0) + +struct xhci_transfer_trb { + uint64_t addr; + uint32_t len; + uint32_t flags; +} __attribute__ ((packed)); + +struct xhci_link_trb { + uint64_t addr; + uint32_t field2; + uint32_t field3; +} __attribute__ ((packed)); + +/* Event TRB */ +struct xhci_event_trb { + uint64_t addr; + uint32_t status; + uint32_t flags; +} __attribute__ ((packed)); + +#define TRB_NORMAL 1 +#define TRB_SETUP_STAGE 2 +#define TRB_DATA_STAGE 3 +#define TRB_STATUS_STAGE 4 +#define TRB_ISOCH 5 +#define TRB_LINK 6 +#define TRB_EVENT_DATA 7 +#define TRB_NOOP 8 +#define TRB_ENABLE_SLOT 9 +#define TRB_DISABLE_SLOT 10 +#define TRB_ADDRESS_DEV 11 +#define TRB_CONFIG_EP 12 +#define TRB_EVAL_CNTX 13 +#define TRB_TRANSFER_EVENT 32 +#define TRB_CMD_COMPLETION 33 +#define TRB_PORT_STATUS 34 + +struct xhci_command_trb { + uint32_t field[4]; +}__attribute__ ((packed)); + +union xhci_trb { + struct xhci_event_trb event; + struct xhci_transfer_trb xfer; + struct xhci_command_trb cmd; + struct xhci_link_trb link; +}; + +enum xhci_seg_type { + TYPE_CTRL = 0, + TYPE_BULK, + TYPE_COMMAND, + TYPE_EVENT, +}; + +struct xhci_seg { + union xhci_trb *trbs; + struct xhci_seg *next; + uint64_t enq; + uint64_t deq; + uint64_t trbs_dma; + uint32_t size; + uint32_t cycle_state; + enum xhci_seg_type type; +}; + +#define XHCI_TRB_SIZE 16 +#define XHCI_EVENT_TRBS_SIZE 4096 +#define XHCI_CONTROL_TRBS_SIZE 4096 +#define XHCI_DATA_TRBS_SIZE 4096 +#define XHCI_ERST_NUM_SEGS 1 + +#define XHCI_MAX_BULK_SIZE 0xF000 + +struct xhci_erst_entry { + uint64_t addr; + uint32_t size; + uint32_t reserved; +} __attribute__ ((packed)); + +struct xhci_erst { + struct xhci_erst_entry *entries; + uint64_t dma; + uint32_t num_segs; /* number of segments */ +}; + +struct xhci_control_ctx { + uint32_t d_flags; + uint32_t a_flags; + uint32_t reserved[6]; +} __attribute__ ((packed)); + +struct xhci_slot_ctx { + uint32_t field1; +#define SLOT_SPEED_FS BIT(20) +#define SLOT_SPEED_LS BIT(21) +#define SLOT_SPEED_HS BIT(22) +#define SLOT_SPEED_SS BIT(23) +#define LAST_CONTEXT(x) (x << 27) + + uint32_t field2; +#define ROOT_HUB_PORT(x) ((x & 0xff) << 16) + + uint32_t field3; + uint32_t field4; +#define USB_DEV_ADDRESS(x) (x & 0xFFU) +#define SLOT_STATE(x) ((x >> 27) & 0x1FU) +#define SLOT_STATE_DIS_ENA 0 +#define SLOT_STATE_DEFAULT 1 +#define SLOT_STATE_ADDRESSED 2 +#define SLOT_STATE_CONFIGURED 3 + + + uint32_t reserved[4]; +} __attribute__ ((packed)); + +struct xhci_ep_ctx { + uint32_t field1; + uint32_t field2; +#define MAX_PACKET_SIZE(x) (((x) & 0xFFFF) << 16) +#define MAX_BURST(x) (((x) & 0xFF) << 8) +#define EP_TYPE(x) (((x) & 0x07) << 3) +#define EP_ISOC_OUT 1 +#define EP_BULK_OUT 2 +#define EP_INT_OUT 3 +#define EP_CTRL 4 +#define EP_ISOC_IN 5 +#define EP_BULK_IN 6 +#define EP_INT_IN 7 + +#define ERROR_COUNT(x) (((x) & 0x03) << 1) + + uint64_t deq_addr; + uint32_t field4; + uint32_t reserved[3]; +} __attribute__ ((packed)); + +struct xhci_ctx { + uint8_t type; +#define XHCI_CTX_TYPE_DEVICE 0x1 +#define XHCI_CTX_TYPE_INPUT 0x2 + uint32_t size; + uint8_t *addr; +#define XHCI_CTX_BUF_SIZE 4096 + uint64_t dma_addr; +}; + +struct xhci_dev { + struct usb_dev *dev; + uint32_t slot_id; + struct xhci_ctx in_ctx; + struct xhci_ctx out_ctx; + struct xhci_seg control; + struct xhci_seg bulk_in; + struct xhci_seg bulk_out; + uint32_t ctx_size; +}; + struct xhci_hcd { struct xhci_cap_regs *cap_regs; struct xhci_op_regs *op_regs; struct xhci_run_regs *run_regs; struct xhci_db_regs *db_regs; struct usb_hcd_dev *hcidev; + struct xhci_dev xdevs[XHCI_CONFIG_MAX_SLOT + 1]; struct usb_pipe *freelist; struct usb_pipe *end; + uint64_t *dcbaap; + uint64_t dcbaap_dma; + struct xhci_seg ering; + struct xhci_seg crseg; + struct xhci_erst erst; + uint64_t erds_dma; + uint32_t erds_size; + uint32_t slot_id; + uint32_t hcc_csz_64; void *pool; +#define XHCI_PIPE_POOL_SIZE 4096 + long pool_phys; }; struct xhci_pipe { struct usb_pipe pipe; - long qh_phys; + struct xhci_seg *seg; }; #endif /* USB_XHCI_H */ |