/***************************************************************************** * Copyright (c) 2013 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include "usb.h" #include "usb-core.h" #include "usb-xhci.h" #include "tools.h" #include "paflof.h" #undef XHCI_DEBUG //#define XHCI_DEBUG #ifdef XHCI_DEBUG #define dprintf(_x ...) do { printf("%s: ", __func__); printf(_x); } while (0) #else #define dprintf(_x ...) #endif static void dump_xhci_regs(struct xhci_hcd *xhcd) { #ifdef XHCI_DEBUG struct xhci_cap_regs *cap; struct xhci_op_regs *op; struct xhci_run_regs *run; cap = xhcd->cap_regs; op = xhcd->op_regs; run = xhcd->run_regs; dprintf("\n"); dprintf(" - CAPLENGTH %02X\n", read_reg8 (&cap->caplength)); dprintf(" - HCIVERSION %04X\n", read_reg16(&cap->hciversion)); dprintf(" - HCSPARAMS1 %08X\n", read_reg32(&cap->hcsparams1)); dprintf(" - HCSPARAMS2 %08X\n", read_reg32(&cap->hcsparams2)); dprintf(" - HCSPARAMS3 %08X\n", read_reg32(&cap->hcsparams3)); dprintf(" - HCCPARAMS %08X\n", read_reg32(&cap->hccparams)); dprintf(" - DBOFF %08X\n", read_reg32(&cap->dboff)); dprintf(" - RTSOFF %08X\n", read_reg32(&cap->rtsoff)); dprintf("\n"); dprintf(" - USBCMD %08X\n", read_reg32(&op->usbcmd)); dprintf(" - USBSTS %08X\n", read_reg32(&op->usbsts)); dprintf(" - PAGESIZE %08X\n", read_reg32(&op->pagesize)); dprintf(" - DNCTRL %08X\n", read_reg32(&op->dnctrl)); dprintf(" - CRCR %016llX\n", read_reg64(&op->crcr)); dprintf(" - DCBAAP %016llX\n", read_reg64(&op->dcbaap)); dprintf(" - CONFIG %08X\n", read_reg32(&op->config)); dprintf("\n"); dprintf(" - MFINDEX %08X\n", read_reg32(&run->mfindex)); dprintf("\n"); #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); } 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; } static bool xhci_hcd_set_runstop(struct xhci_op_regs *op, bool run_req) { uint32_t reg; dprintf("Request %s\n", run_req ? "RUN" : "STOP"); if (!xhci_is_hc_ready(&op->usbsts)) { dprintf("Controller not ready\n"); return false; } reg = read_reg32(&op->usbcmd); if (run_req) reg |= run_req; else reg &= (uint32_t)~1; dprintf("writing %08X\n", reg); write_reg32(&op->usbcmd, reg); mb(); xhci_wait_for_cnr(&op->usbsts); return true; } static bool 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 uint64_t xhci_poll_event(struct xhci_hcd *xhcd, uint32_t event_type) { struct xhci_event_trb *event; uint64_t val, retval = 0; 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 0; } 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); retval = le64_to_cpu(event->addr); 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); if (retval == 0) return (uint64_t)event; else return retval; } 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); if (type != TYPE_EVENT) { 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 bool 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 bool 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 bool 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"); return false; } /* 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 + 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_in_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_control_seg; } /* 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 (usb_setup_new_device(dev, newport)) { usb_slof_populate_new_device(dev); return true; } xhci_free_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE); fail_control_seg: xhci_free_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE); fail_in_ctx: xhci_free_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE); 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->intr, XHCI_INTR_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 bool 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_device_present(uint32_t portsc, uint32_t usb_ver) { if (usb_ver == USB_XHCI) { /* Device present and enabled state */ if ((portsc & PORTSC_CCS) && (portsc & PORTSC_PP) && (portsc & PORTSC_PED)) { return true; } } else if (usb_ver == USB_EHCI) { /* Device present and in disabled state */ if ((portsc & PORTSC_CCS) && (portsc & PORTSC_CSC)) return true; } return false; } static int xhci_port_scan(struct xhci_hcd *xhcd, uint32_t usb_ver) { uint32_t num_ports, portsc, i; struct xhci_op_regs *op; struct xhci_port_regs *prs; struct xhci_cap_regs *cap; uint32_t xecp_off; uint32_t *xecp_addr, *base; uint32_t port_off = 0, port_cnt; dprintf("enter\n"); op = xhcd->op_regs; cap = xhcd->cap_regs; port_cnt = num_ports = read_reg32(&cap->hcsparams1) >> 24; /* Read the xHCI extented capability to find usb3 ports and offset*/ xecp_off = XHCI_HCCPARAMS_XECP(read_reg32(&cap->hccparams)); base = (uint32_t *)cap; while (xecp_off > 0) { xecp_addr = base + xecp_off; dprintf("xecp_off %d %p %p \n", xecp_off, base, xecp_addr); if (XHCI_XECP_CAP_ID(read_reg32(xecp_addr)) == XHCI_XECP_CAP_SP && XHCI_XECP_CAP_SP_MJ(read_reg32(xecp_addr)) == usb_ver && XHCI_XECP_CAP_SP_MN(read_reg32(xecp_addr)) == 0) { port_cnt = XHCI_XECP_CAP_SP_PC(read_reg32(xecp_addr + 2)); port_off = XHCI_XECP_CAP_SP_PO(read_reg32(xecp_addr + 2)); dprintf("PortCount %d Portoffset %d\n", port_cnt, port_off); } base = xecp_addr; xecp_off = XHCI_XECP_NEXT_PTR(read_reg32(xecp_addr)); } if (port_off == 0) /* port_off should always start from 1 */ return false; for (i = (port_off - 1); i < (port_off + port_cnt - 1); i++) { prs = &op->prs[i]; portsc = read_reg32(&prs->portsc); if (xhci_device_present(portsc, usb_ver)) { /* Device present */ 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); if (!usb3_dev_init(xhcd, (i - (port_off - 1)))) { dprintf("USB device initialization failed\n"); } } } dprintf("exit\n"); return true; } static int xhci_hub_check_ports(struct xhci_hcd *xhcd) { return xhci_port_scan(xhcd, USB_XHCI) | xhci_port_scan(xhcd, USB_EHCI); } static bool 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_erst_entries; if (!xhci_hub_check_ports(xhcd)) goto fail_erst_entries; return true; fail_erst_entries: write_reg64(&irs->erstba, 0); mb(); SLOF_dma_map_out(xhcd->erst.dma, (void *)xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE); SLOF_dma_free((void *)xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE); 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); mb(); xhci_free_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE); fail_dcbaap: write_reg64(&op->dcbaap, 0); mb(); 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 bool xhci_hcd_exit(struct xhci_hcd *xhcd) { struct xhci_op_regs *op; struct xhci_int_regs *irs; 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]); } irs = &xhcd->run_regs->irs[0]; write_reg64(&irs->erstba, 0); mb(); if (xhcd->erst.entries) { 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); if (xhcd->dcbaap) { SLOF_dma_map_out(xhcd->dcbaap_dma, (void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); SLOF_dma_free((void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); } /* * QEMU implementation of XHCI doesn't implement halt * properly. It basically says that it's halted immediately * but doesn't actually terminate ongoing activities and * DMAs. This needs to be fixed in QEMU. * * For now, wait for 50ms grace time till qemu stops using * this device. */ SLOF_msleep(50); return true; } static void xhci_init(struct usb_hcd_dev *hcidev) { struct xhci_hcd *xhcd; printf(" XHCI: Initializing\n"); dprintf("device base address %p\n", hcidev->base); hcidev->base = (void *)((uint64_t)hcidev->base & ~7); xhcd = SLOF_alloc_mem(sizeof(*xhcd)); if (!xhcd) { printf("usb-xhci: Unable to allocate memory\n"); return; } memset(xhcd, 0, sizeof(*xhcd)); hcidev->nextaddr = 1; hcidev->priv = xhcd; xhcd->hcidev = hcidev; xhcd->cap_regs = (struct xhci_cap_regs *)(hcidev->base); xhcd->op_regs = (struct xhci_op_regs *)(hcidev->base + read_reg8(&xhcd->cap_regs->caplength)); xhcd->run_regs = (struct xhci_run_regs *)(hcidev->base + read_reg32(&xhcd->cap_regs->rtsoff)); xhcd->db_regs = (struct xhci_db_regs *)(hcidev->base + read_reg32(&xhcd->cap_regs->dboff)); dump_xhci_regs(xhcd); if (!xhci_hcd_init(xhcd)) printf("usb-xhci: failed to initialize XHCI controller.\n"); 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; int index; struct xhci_link_trb *link; enq = val = seg->enq; val = val + XHCI_TRB_SIZE; index = (enq - (uint64_t)seg->trbs) / XHCI_TRB_SIZE + 1; dprintf("%s: enq %llx, val %llx %x\n", __func__, enq, val, index); /* TRBs being a cyclic buffer, here we cycle back to beginning. */ if (index == (seg->size - 1)) { dprintf("%s: rounding \n", __func__); seg->enq = (uint64_t)seg->trbs; 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 uint64_t xhci_get_trb_phys(struct xhci_seg *seg, uint64_t trb) { return seg->trbs_dma + (trb - (uint64_t)seg->trbs); } static int usb_kb = false; 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, time; uint64_t trb_phys, event_phys; 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); trb_phys = xhci_get_trb_phys(seg, (uint64_t)trb); fill_normal_trb(trb, (void *)data, datalen); epno = xhci_get_epno(pipe); write_reg32(&dbr->db[slot_id], epno); time = SLOF_GetTimer() + USB_TIMEOUT; while (1) { event_phys = xhci_poll_event(xhcd, 0); if (event_phys == trb_phys) { break; } else if (event_phys == 0) { /* polling timed out */ ret = false; break; } else usb_kb = true; /* transfer timed out */ if (time < SLOF_GetTimer()) return 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)) { printf("usb-xhci: allocation failed for bulk endpoint\n"); return; } } 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 int xhci_get_pipe_intr(struct usb_pipe *pipe, struct xhci_hcd *xhcd, char *buf, size_t len) { 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; struct usb_dev *dev; struct xhci_transfer_trb *trb; dev = pipe->dev; if (dev->class != DEV_HID_KEYB) return false; xdev = dev->priv; pipe->mps = 8; seg = xhci_pipe_get_seg(pipe); xpipe = xhci_pipe_get_xpipe(pipe); type = EP_INT_IN; seg = &xdev->intr; if (!seg->trbs) { if (!xhci_alloc_seg(seg, XHCI_INTR_TRBS_SIZE, TYPE_BULK)) { printf("usb-xhci: allocation failed for interrupt endpoint\n"); return false; } } else { xhci_init_seg(seg, XHCI_EVENT_TRBS_SIZE, TYPE_BULK); } xpipe->buf = buf; xpipe->buf_phys = SLOF_dma_map_in(buf, len, false); xpipe->buflen = len; 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; trb = xhci_get_trb(seg); fill_normal_trb(trb, (void *)xpipe->buf_phys, pipe->mps); return true; } 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_INTR) { if (!xhci_get_pipe_intr(new, xhcd, buf, len)) { printf("usb-xhci: %s alloc_intr failed %p\n", __func__, new); } } 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; } else if (pipe->type == USB_EP_TYPE_INTR) { xpipe = xhci_pipe_get_xpipe(pipe); SLOF_dma_map_out(xpipe->buf_phys, xpipe->buf, xpipe->buflen); 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__); } static int xhci_poll_intr(struct usb_pipe *pipe, uint8_t *data) { struct xhci_transfer_trb *trb; struct xhci_seg *seg; struct xhci_pipe *xpipe; struct xhci_dev *xdev; struct xhci_hcd *xhcd; struct xhci_db_regs *dbr; uint32_t x_epno; uint8_t *buf, ret = 1; if (!pipe || !pipe->dev || !pipe->dev->hcidev) return 0; xdev = pipe->dev->priv; xhcd = (struct xhci_hcd *)pipe->dev->hcidev->priv; x_epno = xhci_get_epno(pipe); seg = xhci_pipe_get_seg(pipe); xpipe = xhci_pipe_get_xpipe(pipe); if (usb_kb == true) { /* This event was consumed by bulk transfer */ usb_kb = false; goto skip_poll; } buf = xpipe->buf; memset(buf, 0, 8); mb(); /* Ring the doorbell - x_epno */ dbr = xhcd->db_regs; write_reg32(&dbr->db[xdev->slot_id], x_epno); if (!xhci_poll_event(xhcd, 0)) { printf("poll intr failed\n"); return 0; } mb(); memcpy(data, buf, 8); skip_poll: trb = xhci_get_trb(seg); fill_normal_trb(trb, (void *)xpipe->buf_phys, pipe->mps); mb(); return ret; } 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, .poll_intr = xhci_poll_intr, .send_ctrl = xhci_send_ctrl, .transfer_bulk = xhci_transfer_bulk, .next = NULL, }; void usb_xhci_register(void) { usb_hcd_register(&xhci_ops); }