Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / lib / libusb / usb-xhci.c
diff --git a/qemu/roms/SLOF/lib/libusb/usb-xhci.c b/qemu/roms/SLOF/lib/libusb/usb-xhci.c
new file mode 100644 (file)
index 0000000..0c3d6e4
--- /dev/null
@@ -0,0 +1,1316 @@
+/*****************************************************************************
+ * 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 <string.h>
+#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 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 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 (setup_new_device(dev, newport))
+               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->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_hub_check_ports(struct xhci_hcd *xhcd)
+{
+       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 = 1, 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(stderr, "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)) == 3 &&
+                   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(stderr, "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 ((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);
+                       if (!usb3_dev_init(xhcd, (i - (port_off - 1)))) {
+                               dprintf("USB device initialization failed\n");
+                       }
+               }
+       }
+       dprintf("exit\n");
+       return true;
+}
+
+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);
+       }
+       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;
+       uint32_t size;
+       struct xhci_link_trb *link;
+
+       enq = val = seg->enq;
+       val = val + XHCI_TRB_SIZE;
+       size = seg->size * XHCI_TRB_SIZE;
+        /* TRBs being a cyclic buffer, here we cycle back to beginning. */
+       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,
+};
+
+void usb_xhci_register(void)
+{
+       usb_hcd_register(&xhci_ops);
+}