diff options
Diffstat (limited to 'qemu/roms/SLOF/lib/libusb/usb-ehci.c')
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-ehci.c | 611 |
1 files changed, 0 insertions, 611 deletions
diff --git a/qemu/roms/SLOF/lib/libusb/usb-ehci.c b/qemu/roms/SLOF/lib/libusb/usb-ehci.c deleted file mode 100644 index 60af9e101..000000000 --- a/qemu/roms/SLOF/lib/libusb/usb-ehci.c +++ /dev/null @@ -1,611 +0,0 @@ -/***************************************************************************** - * 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-ehci.h" -#include "tools.h" -#include "paflof.h" - -#undef EHCI_DEBUG -//#define EHCI_DEBUG -#ifdef EHCI_DEBUG -#define dprintf(_x ...) do { printf(_x); } while(0) -#else -#define dprintf(_x ...) -#endif - -#ifdef EHCI_DEBUG -static void dump_ehci_regs(struct ehci_hcd *ehcd) -{ - struct ehci_cap_regs *cap_regs; - struct ehci_op_regs *op_regs; - - cap_regs = ehcd->cap_regs; - op_regs = ehcd->op_regs; - - dprintf("\n - CAPLENGTH %02X", read_reg8(&cap_regs->caplength)); - dprintf("\n - HCIVERSION %04X", read_reg16(&cap_regs->hciversion)); - dprintf("\n - HCSPARAMS %08X", read_reg32(&cap_regs->hcsparams)); - dprintf("\n - HCCPARAMS %08X", read_reg32(&cap_regs->hccparams)); - dprintf("\n - HCSP_PORTROUTE %016llX", read_reg64(&cap_regs->portroute)); - dprintf("\n"); - - dprintf("\n - USBCMD %08X", read_reg32(&op_regs->usbcmd)); - dprintf("\n - USBSTS %08X", read_reg32(&op_regs->usbsts)); - dprintf("\n - USBINTR %08X", read_reg32(&op_regs->usbintr)); - dprintf("\n - FRINDEX %08X", read_reg32(&op_regs->frindex)); - dprintf("\n - CTRLDSSEGMENT %08X", read_reg32(&op_regs->ctrldssegment)); - dprintf("\n - PERIODICLISTBASE %08X", read_reg32(&op_regs->periodiclistbase)); - dprintf("\n - ASYNCLISTADDR %08X", read_reg32(&op_regs->asynclistaddr)); - dprintf("\n - CONFIGFLAG %08X", read_reg32(&op_regs->configflag)); - dprintf("\n - PORTSC %08X", read_reg32(&op_regs->portsc[0])); - dprintf("\n"); -} -#endif - -static int ehci_hub_check_ports(struct ehci_hcd *ehcd) -{ - uint32_t num_ports, portsc, i; - struct usb_dev *dev; - - dprintf("%s: enter\n", __func__); - num_ports = read_reg32(&ehcd->cap_regs->hcsparams) & HCS_NPORTS_MASK; - for (i = 0; i < num_ports; i++) { - dprintf("%s: device %d\n", __func__, i); - portsc = read_reg32(&ehcd->op_regs->portsc[i]); - if (portsc & PORT_CONNECT) { /* Device present */ - dprintf("usb-ehci: Device present on port %d\n", i); - /* Reset the port */ - portsc = read_reg32(&ehcd->op_regs->portsc[i]); - portsc = (portsc & ~PORT_PE) | PORT_RESET; - write_reg32(&ehcd->op_regs->portsc[i], portsc); - SLOF_msleep(20); - portsc = read_reg32(&ehcd->op_regs->portsc[i]); - portsc &= ~PORT_RESET; - write_reg32(&ehcd->op_regs->portsc[i], portsc); - SLOF_msleep(20); - dev = usb_devpool_get(); - dprintf("usb-ehci: allocated device %p\n", dev); - dev->hcidev = ehcd->hcidev; - dev->speed = USB_HIGH_SPEED; /* TODO: Check for Low/Full speed device */ - if (usb_setup_new_device(dev, i)) - usb_slof_populate_new_device(dev); - else - printf("usb-ehci: unable to setup device on port %d\n", i); - } - } - dprintf("%s: exit\n", __func__); - return 0; -} - -static int ehci_hcd_init(struct ehci_hcd *ehcd) -{ - uint32_t usbcmd; - uint32_t time; - struct ehci_framelist *fl; - struct ehci_qh *qh_intr, *qh_async; - int i; - long fl_phys = 0, qh_intr_phys = 0, qh_async_phys; - - /* Reset the host controller */ - time = SLOF_GetTimer() + 250; - usbcmd = read_reg32(&ehcd->op_regs->usbcmd); - write_reg32(&ehcd->op_regs->usbcmd, (usbcmd & ~(CMD_PSE | CMD_ASE)) | CMD_HCRESET); - while (time > SLOF_GetTimer()) - cpu_relax(); - usbcmd = read_reg32(&ehcd->op_regs->usbcmd); - if (usbcmd & CMD_HCRESET) { - printf("usb-ehci: reset failed\n"); - return -1; - } - - /* Initialize periodic list */ - fl = SLOF_dma_alloc(sizeof(*fl)); - if (!fl) { - printf("usb-ehci: Unable to allocate frame list\n"); - goto fail; - } - fl_phys = SLOF_dma_map_in(fl, sizeof(*fl), true); - dprintf("fl %p, fl_phys %lx\n", fl, fl_phys); - - /* TODO: allocate qh pool */ - qh_intr = SLOF_dma_alloc(sizeof(*qh_intr)); - if (!qh_intr) { - printf("usb-ehci: Unable to allocate interrupt queue head\n"); - goto fail_qh_intr; - } - qh_intr_phys = SLOF_dma_map_in(qh_intr, sizeof(*qh_intr), true); - dprintf("qh_intr %p, qh_intr_phys %lx\n", qh_intr, qh_intr_phys); - - memset(qh_intr, 0, sizeof(*qh_intr)); - qh_intr->qh_ptr = QH_PTR_TERM; - qh_intr->ep_cap2 = cpu_to_le32(0x01 << QH_SMASK_SHIFT); - qh_intr->next_qtd = qh_intr->alt_next_qtd = QH_PTR_TERM; - qh_intr->token = cpu_to_le32(QH_STS_HALTED); - for (i = 0; i < FL_SIZE; i++) - fl->fl_ptr[i] = cpu_to_le32(qh_intr_phys | EHCI_TYP_QH); - write_reg32(&ehcd->op_regs->periodiclistbase, fl_phys); - - /* Initialize async list */ - qh_async = SLOF_dma_alloc(sizeof(*qh_async)); - if (!qh_async) { - printf("usb-ehci: Unable to allocate async queue head\n"); - goto fail_qh_async; - } - qh_async_phys = SLOF_dma_map_in(qh_async, sizeof(*qh_async), true); - dprintf("qh_async %p, qh_async_phys %lx\n", qh_async, qh_async_phys); - - memset(qh_async, 0, sizeof(*qh_async)); - qh_async->qh_ptr = cpu_to_le32(qh_async_phys | EHCI_TYP_QH); - qh_async->ep_cap1 = cpu_to_le32(QH_CAP_H); - qh_async->next_qtd = qh_async->alt_next_qtd = QH_PTR_TERM; - qh_async->token = cpu_to_le32(QH_STS_HALTED); - write_reg32(&ehcd->op_regs->asynclistaddr, qh_async_phys); - ehcd->qh_async = qh_async; - ehcd->qh_async_phys = qh_async_phys; - ehcd->qh_intr = qh_intr; - ehcd->qh_intr_phys = qh_intr_phys; - ehcd->fl = fl; - ehcd->fl_phys = fl_phys; - - write_reg32(&ehcd->op_regs->usbcmd, usbcmd | CMD_ASE | CMD_RUN); - write_reg32(&ehcd->op_regs->configflag, 1); - - return 0; - -fail_qh_async: - SLOF_dma_map_out(qh_intr_phys, qh_intr, sizeof(*qh_intr)); - SLOF_dma_free(qh_intr, sizeof(*qh_intr)); -fail_qh_intr: - SLOF_dma_map_out(fl_phys, fl, sizeof(*fl)); - SLOF_dma_free(fl, sizeof(*fl)); -fail: - return -1; -} - -static int ehci_hcd_exit(struct ehci_hcd *ehcd) -{ - uint32_t usbcmd; - - if (!ehcd) { - dprintf("NULL pointer\n"); - return false; - } - - usbcmd = read_reg32(&ehcd->op_regs->usbcmd); - write_reg32(&ehcd->op_regs->usbcmd, usbcmd | ~CMD_RUN); - write_reg32(&ehcd->op_regs->periodiclistbase, 0); - - if (ehcd->pool) { - SLOF_dma_map_out(ehcd->pool_phys, ehcd->pool, EHCI_PIPE_POOL_SIZE); - SLOF_dma_free(ehcd->pool, EHCI_PIPE_POOL_SIZE); - } - if (ehcd->qh_intr) { - SLOF_dma_map_out(ehcd->qh_intr_phys, ehcd->qh_intr, sizeof(struct ehci_qh)); - SLOF_dma_free(ehcd->qh_intr, sizeof(struct ehci_qh)); - } - if (ehcd->qh_async) { - SLOF_dma_map_out(ehcd->qh_async_phys, ehcd->qh_async, sizeof(struct ehci_qh)); - SLOF_dma_free(ehcd->qh_async, sizeof(struct ehci_qh)); - } - if (ehcd->fl) { - SLOF_dma_map_out(ehcd->fl_phys, ehcd->fl, sizeof(struct ehci_framelist)); - SLOF_dma_free(ehcd->fl, sizeof(struct ehci_framelist)); - } - return true; -} - -static int ehci_alloc_pipe_pool(struct ehci_hcd *ehcd) -{ - struct ehci_pipe *epipe, *curr, *prev; - unsigned int i, count; - long epipe_phys = 0; - - count = EHCI_PIPE_POOL_SIZE/sizeof(*epipe); - ehcd->pool = epipe = SLOF_dma_alloc(EHCI_PIPE_POOL_SIZE); - if (!epipe) - return -1; - ehcd->pool_phys = epipe_phys = SLOF_dma_map_in(epipe, EHCI_PIPE_POOL_SIZE, true); - dprintf("%s: epipe %p, epipe_phys %lx\n", __func__, epipe, epipe_phys); - - /* Although an array, link them */ - for (i = 0, curr = epipe, prev = NULL; i < count; i++, curr++) { - if (prev) - prev->pipe.next = &curr->pipe; - curr->pipe.next = NULL; - prev = curr; - curr->qh_phys = epipe_phys + (curr - epipe) * sizeof(*curr) + - offset_of(struct ehci_pipe, qh); - dprintf("%s - %d: qh %p, qh_phys %lx\n", __func__, - i, &curr->qh, curr->qh_phys); - } - - if (!ehcd->freelist) - ehcd->freelist = &epipe->pipe; - else - ehcd->end->next = &epipe->pipe; - ehcd->end = &prev->pipe; - - return 0; -} - -static void ehci_init(struct usb_hcd_dev *hcidev) -{ - struct ehci_hcd *ehcd; - - printf(" EHCI: Initializing\n"); - dprintf("%s: device base address %p\n", __func__, hcidev->base); - - ehcd = SLOF_alloc_mem(sizeof(*ehcd)); - if (!ehcd) { - printf("usb-ehci: Unable to allocate memory\n"); - return; - } - memset(ehcd, 0, sizeof(*ehcd)); - - hcidev->nextaddr = 1; - hcidev->priv = ehcd; - ehcd->hcidev = hcidev; - ehcd->cap_regs = (struct ehci_cap_regs *)(hcidev->base); - ehcd->op_regs = (struct ehci_op_regs *)(hcidev->base + - read_reg8(&ehcd->cap_regs->caplength)); -#ifdef EHCI_DEBUG - dump_ehci_regs(ehcd); -#endif - ehci_hcd_init(ehcd); - ehci_hub_check_ports(ehcd); -} - -static void ehci_exit(struct usb_hcd_dev *hcidev) -{ - struct ehci_hcd *ehcd; - static int count = 0; - - dprintf("%s: enter \n", __func__); - - if (!hcidev && !hcidev->priv) { - return; - } - count++; - if (count > 1) { - printf("%s: already called once \n", __func__); - return; - } - ehcd = hcidev->priv; - ehci_hcd_exit(ehcd); - SLOF_free_mem(ehcd, sizeof(*ehcd)); - hcidev->priv = NULL; -} - -static void ehci_detect(void) -{ - -} - -static void ehci_disconnect(void) -{ - -} - -static int ehci_handshake(struct ehci_hcd *ehcd, uint32_t timeout) -{ - uint32_t usbsts = 0, time; - uint32_t usbcmd; - mb(); - usbcmd = read_reg32(&ehcd->op_regs->usbcmd); - /* Ring a doorbell */ - write_reg32(&ehcd->op_regs->usbcmd, usbcmd | CMD_IAAD); - mb(); - time = SLOF_GetTimer() + timeout; - while ((time > SLOF_GetTimer())) { - /* Wait for controller to confirm */ - usbsts = read_reg32(&ehcd->op_regs->usbsts); - if (usbsts & STS_IAA) { - /* Acknowledge it, for next doorbell to work */ - write_reg32(&ehcd->op_regs->usbsts, STS_IAA); - return true; - } - cpu_relax(); - } - return false; -} - -static int fill_qtd_buff(struct ehci_qtd *qtd, long data, uint32_t size) -{ - long i, rem; - long pos = (data + 0x1000) & ~0xfff; - - qtd->buffer[0] = cpu_to_le32(PTR_U32(data)); - for (i = 1; i < 5; i++) { - if ((data + size - 1) >= pos) { - //dprintf("data spans page boundary: %d, %p\n", i, pos); - qtd->buffer[i] = cpu_to_le32(pos); - pos += 0x1000; - } else - break; - } - if ((data + size) > pos) - rem = data + size - pos; - else - rem = 0; - return rem; -} - -static int ehci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data) -{ - struct ehci_hcd *ehcd; - struct ehci_qtd *qtd, *qtds, *qtds_phys; - struct ehci_pipe *epipe; - uint32_t transfer_size = sizeof(*req); - uint32_t datalen, pid; - uint32_t time; - long req_phys = 0, data_phys = 0; - int ret = true; - - if (pipe->type != USB_EP_TYPE_CONTROL) { - printf("usb-ehci: Not a control pipe.\n"); - return false; - } - - ehcd = pipe->dev->hcidev->priv; - qtds = qtd = SLOF_dma_alloc(sizeof(*qtds) * 3); - if (!qtds) { - printf("Error allocating qTDs.\n"); - return false; - } - qtds_phys = (struct ehci_qtd *)SLOF_dma_map_in(qtds, sizeof(*qtds) * 3, true); - memset(qtds, 0, sizeof(*qtds) * 3); - req_phys = SLOF_dma_map_in(req, sizeof(struct usb_dev_req), true); - qtd->next_qtd = cpu_to_le32(PTR_U32(&qtds_phys[1])); - qtd->alt_next_qtd = QH_PTR_TERM; - qtd->token = cpu_to_le32((transfer_size << TOKEN_TBTT_SHIFT) | - (3 << TOKEN_CERR_SHIFT) | - (PID_SETUP << TOKEN_PID_SHIFT) | - (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)); - fill_qtd_buff(qtd, req_phys, sizeof(*req)); - - qtd++; - datalen = cpu_to_le16(req->wLength); - pid = (req->bmRequestType & REQT_DIR_IN) ? PID_IN : PID_OUT; - if (datalen) { - data_phys = SLOF_dma_map_in(data, datalen, true); - qtd->next_qtd = cpu_to_le32(PTR_U32(&qtds_phys[2])); - qtd->alt_next_qtd = QH_PTR_TERM; - qtd->token = cpu_to_le32((1 << TOKEN_DT_SHIFT) | - (datalen << TOKEN_TBTT_SHIFT) | - (3 << TOKEN_CERR_SHIFT) | - (pid << TOKEN_PID_SHIFT) | - (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)); - fill_qtd_buff(qtd, data_phys, datalen); - qtd++; - } - - if (pid == PID_IN) - pid = PID_OUT; - else - pid = PID_IN; - qtd->next_qtd = QH_PTR_TERM; - qtd->alt_next_qtd = QH_PTR_TERM; - qtd->token = cpu_to_le32((1 << TOKEN_DT_SHIFT) | - (3 << TOKEN_CERR_SHIFT) | - (pid << TOKEN_PID_SHIFT) | - (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)); - - /* link qtd to qh and attach to ehcd */ - mb(); - epipe = container_of(pipe, struct ehci_pipe, pipe); - epipe->qh.next_qtd = cpu_to_le32(PTR_U32(qtds_phys)); - epipe->qh.qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH); - epipe->qh.ep_cap1 = cpu_to_le32((pipe->mps << QH_MPS_SHIFT) | - (pipe->speed << QH_EPS_SHIFT) | - (pipe->epno << QH_EP_SHIFT) | - (pipe->dev->addr << QH_DEV_ADDR_SHIFT)); - mb(); - - ehcd->qh_async->qh_ptr = cpu_to_le32(epipe->qh_phys | EHCI_TYP_QH); - - /* transfer data */ - mb(); - qtd = &qtds[0]; - time = SLOF_GetTimer() + USB_TIMEOUT; - do { - if (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)) - mb(); - else - qtd++; - - if (time < SLOF_GetTimer()) { /* timed out */ - printf("usb-ehci: control transfer timed out_\n"); - ret = false; - break; - } - } while (qtd->next_qtd != QH_PTR_TERM); - - ehcd->qh_async->qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH); - mb(); - if (!ehci_handshake(ehcd, USB_TIMEOUT)) { - printf("%s: handshake failed\n", __func__); - ret = false; - } - - SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req)); - SLOF_dma_map_out(data_phys, data, datalen); - SLOF_dma_map_out(PTR_U32(qtds_phys), qtds, sizeof(*qtds) * 3); - SLOF_dma_free(qtds, sizeof(*qtds) * 3); - - return ret; -} - -static int ehci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys, - void *data_phys, int size) -{ - struct ehci_hcd *ehcd; - struct ehci_qtd *qtd, *qtd_phys; - struct ehci_pipe *epipe; - uint32_t pid; - int i, rem, ret = true; - uint32_t time; - long ptr; - - dprintf("usb-ehci: bulk transfer: data %p, size %d, td %p, td_phys %p\n", - data_phys, size, td, td_phys); - - if (pipe->type != USB_EP_TYPE_BULK) { - printf("usb-ehci: Not a bulk pipe.\n"); - return false; - } - - if (size > QTD_MAX_TRANSFER_LEN) { - printf("usb-ehci: bulk transfer size too big\n"); - return false; - } - - ehcd = pipe->dev->hcidev->priv; - pid = (pipe->dir == USB_PIPE_OUT) ? PID_OUT : PID_IN; - qtd = (struct ehci_qtd *)td; - qtd_phys = (struct ehci_qtd *)td_phys; - ptr = (long)data_phys; - for (i = 0; i < NUM_BULK_QTDS; i++) { - memset(qtd, 0, sizeof(*qtd)); - rem = fill_qtd_buff(qtd, ptr, size); - qtd->token = cpu_to_le32((1 << TOKEN_DT_SHIFT) | - ((size - rem) << TOKEN_TBTT_SHIFT) | - (3 << TOKEN_CERR_SHIFT) | - (pid << TOKEN_PID_SHIFT) | - (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)); - if (rem) { - qtd->next_qtd = cpu_to_le32(PTR_U32(&qtd_phys[i+1])); - qtd->alt_next_qtd = QH_PTR_TERM; - ptr += size - rem; - size = rem; - qtd++; - } else { - qtd->next_qtd = qtd->alt_next_qtd = QH_PTR_TERM; - break; /* no more data */ - } - } - - /* link qtd to qh and attach to ehcd */ - mb(); - epipe = container_of(pipe, struct ehci_pipe, pipe); - epipe->qh.next_qtd = cpu_to_le32(PTR_U32(qtd_phys)); - epipe->qh.qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH); - epipe->qh.ep_cap1 = cpu_to_le32((pipe->mps << QH_MPS_SHIFT) | - (pipe->speed << QH_EPS_SHIFT) | - (pipe->epno << QH_EP_SHIFT) | - (pipe->dev->addr << QH_DEV_ADDR_SHIFT)); - mb(); - - ehcd->qh_async->qh_ptr = cpu_to_le32(epipe->qh_phys | EHCI_TYP_QH); - - /* transfer data */ - mb(); - qtd = (struct ehci_qtd *)td; - for (i = 0; i < NUM_BULK_QTDS; i++) { - time = SLOF_GetTimer() + USB_TIMEOUT; - while ((time > SLOF_GetTimer()) && - (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT))) - cpu_relax(); - mb(); - if (qtd->next_qtd == QH_PTR_TERM) - break; - - if (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)) { - printf("usb-ehci: bulk transfer timed out_\n"); - ret = false; - break; - } - qtd++; - } - - ehcd->qh_async->qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH); - mb(); - if (!ehci_handshake(ehcd, USB_TIMEOUT)) { - printf("%s: handshake failed\n", __func__); - ret = false; - } - return ret; -} - -static struct usb_pipe *ehci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, - char *buf, size_t len) -{ - struct ehci_hcd *ehcd; - struct usb_pipe *new = NULL; - - if (!dev) - return NULL; - - ehcd = (struct ehci_hcd *)dev->hcidev->priv; - if (!ehcd->freelist) { - dprintf("usb-ehci: %s allocating pool\n", __func__); - if (ehci_alloc_pipe_pool(ehcd)) - return NULL; - } - - new = ehcd->freelist; - ehcd->freelist = ehcd->freelist->next; - if (!ehcd->freelist) - ehcd->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; - - return new; -} - -static void ehci_put_pipe(struct usb_pipe *pipe) -{ - struct ehci_hcd *ehcd; - - dprintf("usb-ehci: %s enter - %p\n", __func__, pipe); - if (!pipe || !pipe->dev) - return; - ehcd = pipe->dev->hcidev->priv; - if (ehcd->end) - ehcd->end->next = pipe; - else - ehcd->freelist = pipe; - - ehcd->end = pipe; - pipe->next = NULL; - pipe->dev = NULL; - memset(pipe, 0, sizeof(*pipe)); - dprintf("usb-ehci: %s exit\n", __func__); -} - -struct usb_hcd_ops ehci_ops = { - .name = "ehci-hcd", - .init = ehci_init, - .exit = ehci_exit, - .detect = ehci_detect, - .disconnect = ehci_disconnect, - .get_pipe = ehci_get_pipe, - .put_pipe = ehci_put_pipe, - .send_ctrl = ehci_send_ctrl, - .transfer_bulk = ehci_transfer_bulk, - .usb_type = USB_EHCI, - .next = NULL, -}; - -void usb_ehci_register(void) -{ - usb_hcd_register(&ehci_ops); -} |