The following is my test series for XHCI. I've gotten it to the point that I can boot from a "super speed" USB flash drive. However, high speed flash drives and low speed keyboard/mouse still don't work for me. Hopefully, when Gerd returns he'll have some insight.
The most notable part of this series is the work to make the code auto-detect and handle 64 byte contexts (which the xhci controller on my e350m1 requires).
The series is also available at: https://github.com/KevinOConnor/seabios/tree/xhci-testing
-Kevin
Gerd Hoffmann (1): xhci: allocate scratch pad buffers
Kevin O'Connor (9): xhci: Use 64bit writes to ERDP register. xhci: Fix incorrect direction setting on status transmissions. xhci: Set the interval parameter on interrupt pipes. xhci: Verify PAGESIZE register before initializing driver. xhci: Allocate and free the xhci inctx structure on each use. xhci: Move set_address code from xhci_control to xhci_alloc_pipe. xhci: Eliminate 'struct xhci_device'. xhci: Support xhci controllers with 64 byte contexts. xhci: Allow the XHCI USB controller to be enabled for coreboot.
src/Kconfig | 2 +- src/hw/usb-xhci.c | 342 +++++++++++++++++++++++++++++------------------------- src/hw/usb-xhci.h | 8 -- src/hw/usb.c | 4 +- src/hw/usb.h | 1 + 5 files changed, 186 insertions(+), 171 deletions(-)
Untested. Anyone who tried to get xhci run on real hardware is welcome to test whenever this patch improves things.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/hw/usb-xhci.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/src/hw/usb-xhci.c b/src/hw/usb-xhci.c index 14f243c..dc98e5d 100644 --- a/src/hw/usb-xhci.c +++ b/src/hw/usb-xhci.c @@ -711,6 +711,19 @@ configure_xhci(void *data) writel(&xhci->ir->erstba_high, 0); xhci->evts->cs = 1;
+ reg = readl(&xhci->caps->hcsparams2); + u32 spb = reg >> 27; + if (spb) { + dprintf(3, "%s: setup %d scratch pad buffers\n", __func__, spb); + u64 *spba = memalign_high(64, sizeof(*spba) * spb); + void *pad = memalign_high(4096, 4096 * spb); + int i; + for (i = 0; i < spb; i++) + spba[i] = (u32)pad + (i * 4096); + xhci->devs[0].ptr_low = (u32)spba; + xhci->devs[0].ptr_high = 0; + } + reg = readl(&xhci->op->usbcmd); reg |= XHCI_CMD_RS; writel(&xhci->op->usbcmd, reg);
At least some real-world XHCI controllers expect a 64bit write to the ERDP register.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/usb-xhci.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/src/hw/usb-xhci.c b/src/hw/usb-xhci.c index dc98e5d..d0df6ee 100644 --- a/src/hw/usb-xhci.c +++ b/src/hw/usb-xhci.c @@ -379,6 +379,7 @@ static void xhci_process_events(struct usb_xhci_s *xhci) u32 addr = (u32)(&ir->erdp_low); u32 erdp = (u32)(evts->ring + nidx); pci_writel(addr, erdp); + pci_writel((u32)(&ir->erdp_high), 0); } }
The status part of a control message was likely to have an incorrect direction set.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/usb-xhci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/hw/usb-xhci.c b/src/hw/usb-xhci.c index d0df6ee..0833cb9 100644 --- a/src/hw/usb-xhci.c +++ b/src/hw/usb-xhci.c @@ -621,7 +621,7 @@ static void xhci_xfer_data(struct xhci_pipe *pipe, xhci_xfer_queue(pipe, &trb); }
-static void xhci_xfer_status(struct xhci_pipe *pipe, int dir) +static void xhci_xfer_status(struct xhci_pipe *pipe, int dir, int datalen) { ASSERT32FLAT(); struct xhci_trb trb; @@ -629,7 +629,7 @@ static void xhci_xfer_status(struct xhci_pipe *pipe, int dir) memset(&trb, 0, sizeof(trb)); trb.control |= (TR_STATUS << 10); // trb type trb.control |= TRB_TR_IOC; - if (dir) + if (!datalen || !dir) trb.control |= (1 << 16);
xhci_xfer_queue(pipe, &trb); @@ -1007,7 +1007,7 @@ xhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize xhci_xfer_setup(pipe, req, dir, datalen); if (datalen) xhci_xfer_data(pipe, dir, data, datalen); - xhci_xfer_status(pipe, dir); + xhci_xfer_status(pipe, dir, datalen);
cc = xhci_event_wait(xhci, &pipe->reqs, 1000); if (cc != CC_SUCCESS) {
Be sure to set the interval parameter when creating an interrupt based pipe.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/usb-xhci.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/src/hw/usb-xhci.c b/src/hw/usb-xhci.c index 0833cb9..b10fceb 100644 --- a/src/hw/usb-xhci.c +++ b/src/hw/usb-xhci.c @@ -895,6 +895,8 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev in->slot.ctx[0] |= (31 << 27); // context entries
int e = pipe->epid-1; + if (eptype == USB_ENDPOINT_XFER_INT) + in->ep[e].ctx[0] = (usb_getFrameExp(usbdev, epdesc) + 3) << 16; in->ep[e].ctx[1] |= (eptype << 3); if (epdesc->bEndpointAddress & USB_DIR_IN) in->ep[e].ctx[1] |= (1 << 5);
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/usb-xhci.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/src/hw/usb-xhci.c b/src/hw/usb-xhci.c index b10fceb..ac13c4c 100644 --- a/src/hw/usb-xhci.c +++ b/src/hw/usb-xhci.c @@ -1,15 +1,16 @@ +#include "biosvar.h" // GET_LOWFLAT #include "config.h" // CONFIG_* -#include "output.h" // dprintf -#include "string.h" // memcpy_fl -#include "util.h" // timer_calc -#include "x86.h" // readl #include "malloc.h" // memalign_low +#include "memmap.h" // PAGE_SIZE +#include "output.h" // dprintf #include "pci.h" // pci_bdf_to_bus #include "pci_ids.h" // PCI_CLASS_SERIAL_USB_XHCI #include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "string.h" // memcpy_fl #include "usb.h" // struct usb_s #include "usb-xhci.h" // struct ehci_qh -#include "biosvar.h" // GET_LOWFLAT +#include "util.h" // timer_calc +#include "x86.h" // readl
// -------------------------------------------------------------- // configuration @@ -717,10 +718,10 @@ configure_xhci(void *data) if (spb) { dprintf(3, "%s: setup %d scratch pad buffers\n", __func__, spb); u64 *spba = memalign_high(64, sizeof(*spba) * spb); - void *pad = memalign_high(4096, 4096 * spb); + void *pad = memalign_high(PAGE_SIZE, PAGE_SIZE * spb); int i; for (i = 0; i < spb; i++) - spba[i] = (u32)pad + (i * 4096); + spba[i] = (u32)pad + (i * PAGE_SIZE); xhci->devs[0].ptr_low = (u32)spba; xhci->devs[0].ptr_high = 0; } @@ -1135,6 +1136,14 @@ xhci_controller_setup(struct pci_device *pci) } while (off > 0); }
+ u32 pagesize = readl(&xhci->op->pagesize); + if (PAGE_SIZE != (pagesize<<12)) { + dprintf(1, "XHCI driver does not support page size code %d\n" + , pagesize<<12); + free(xhci); + return; + } + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
run_thread(configure_xhci, xhci);
The inctx struct isn't long lived, so it doesn't need to be allocated in permanent memory.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/usb-xhci.c | 105 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 44 deletions(-)
diff --git a/src/hw/usb-xhci.c b/src/hw/usb-xhci.c index ac13c4c..53b179b 100644 --- a/src/hw/usb-xhci.c +++ b/src/hw/usb-xhci.c @@ -249,7 +249,6 @@ struct usb_xhci_s {
struct xhci_device { struct xhci_devctx devctx; - struct xhci_inctx inctx;
struct usbdevice_s *usbdev; struct usb_xhci_s *xhci; @@ -545,11 +544,12 @@ static int xhci_cmd_disable_slot(struct xhci_device *dev) return xhci_cmd_submit(dev->xhci, &cmd); }
-static int xhci_cmd_address_device(struct xhci_device *dev) +static int xhci_cmd_address_device(struct xhci_device *dev + , struct xhci_inctx *inctx) { ASSERT32FLAT(); struct xhci_trb cmd = { - .ptr_low = (u32)&dev->inctx, + .ptr_low = (u32)inctx, .ptr_high = 0, .status = 0, .control = (dev->slotid << 24) | (CR_ADDRESS_DEVICE << 10) @@ -558,31 +558,33 @@ static int xhci_cmd_address_device(struct xhci_device *dev) return xhci_cmd_submit(dev->xhci, &cmd); }
-static int xhci_cmd_configure_endpoint(struct xhci_device *dev) +static int xhci_cmd_configure_endpoint(struct xhci_device *dev + , struct xhci_inctx *inctx) { ASSERT32FLAT(); struct xhci_trb cmd = { - .ptr_low = (u32)&dev->inctx, + .ptr_low = (u32)inctx, .ptr_high = 0, .status = 0, .control = (dev->slotid << 24) | (CR_CONFIGURE_ENDPOINT << 10) }; dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, - dev->slotid, dev->inctx.add, dev->inctx.del); + dev->slotid, inctx->add, inctx->del); return xhci_cmd_submit(dev->xhci, &cmd); }
-static int xhci_cmd_evaluate_context(struct xhci_device *dev) +static int xhci_cmd_evaluate_context(struct xhci_device *dev + , struct xhci_inctx *inctx) { ASSERT32FLAT(); struct xhci_trb cmd = { - .ptr_low = (u32)&dev->inctx, + .ptr_low = (u32)inctx, .ptr_high = 0, .status = 0, .control = (dev->slotid << 24) | (CR_EVALUATE_CONTEXT << 10) }; dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, - dev->slotid, dev->inctx.add, dev->inctx.del); + dev->slotid, inctx->add, inctx->del); return xhci_cmd_submit(dev->xhci, &cmd); }
@@ -845,6 +847,35 @@ static struct usbhub_op_s xhci_hub_ops = { // -------------------------------------------------------------- // external interface
+ +static struct xhci_inctx * +xhci_alloc_inctx(struct xhci_pipe *pipe) +{ + struct xhci_inctx *in = memalign_tmphigh(4096, sizeof(*in)); + if (!in) { + warn_noalloc(); + return NULL; + } + memset(in, 0, sizeof(*in)); + + struct usbdevice_s *usbdev = pipe->dev->usbdev; + u32 route = 0; + while (usbdev->hub->usbdev) { + route <<= 4; + route |= (usbdev->port+1) & 0xf; + usbdev = usbdev->hub->usbdev; + } + + in->add = 0x01; + in->slot.ctx[0] |= (1 << 27); // context entries + in->slot.ctx[0] |= speed_to_xhci[pipe->dev->usbdev->speed] << 20; + in->slot.ctx[0] |= route; + in->slot.ctx[1] |= (usbdev->port+1) << 16; + /* TODO ctx0: hub bit */ + /* TODO ctx1: hub ports */ + return in; +} + struct usb_pipe * xhci_alloc_pipe(struct usbdevice_s *usbdev , struct usb_endpoint_descriptor *epdesc) @@ -877,10 +908,8 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev
usb_desc2pipe(&pipe->pipe, usbdev, epdesc); pipe->dev = xhci_find_alloc_device(xhci, usbdev); - if (!pipe->dev) { - free(pipe); - return NULL; - } + if (!pipe->dev) + goto fail; pipe->epid = epid; pipe->reqs.cs = 1; if (eptype == USB_ENDPOINT_XFER_INT) @@ -889,11 +918,10 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__, usbdev, &pipe->reqs, pipe->dev->slotid, pipe->epid); if (pipe->epid > 1 && pipe->dev->slotid) { - struct xhci_inctx *in = &pipe->dev->inctx; - in->add = (1 << pipe->epid) | 1; - in->del = 0; - - in->slot.ctx[0] |= (31 << 27); // context entries + struct xhci_inctx *in = xhci_alloc_inctx(pipe); + if (!in) + goto fail; + in->add |= (1 << pipe->epid);
int e = pipe->epid-1; if (eptype == USB_ENDPOINT_XFER_INT) @@ -907,15 +935,18 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev in->ep[e].deq_high = 0; in->ep[e].length = pipe->pipe.maxpacket;
- int cc = xhci_cmd_configure_endpoint(pipe->dev); + int cc = xhci_cmd_configure_endpoint(pipe->dev, in); + free(in); if (cc != CC_SUCCESS) { dprintf(1, "%s: configure endpoint: failed (cc %d)\n", __func__, cc); - free(pipe); - return NULL; + goto fail; } }
return &pipe->pipe; +fail: + free(pipe); + return NULL; }
struct usb_pipe * @@ -934,16 +965,17 @@ xhci_update_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe dprintf(1, "%s: reconf ctl endpoint pkt size: %d -> %d\n", __func__, pipe->pipe.maxpacket, epdesc->wMaxPacketSize); pipe->pipe.maxpacket = epdesc->wMaxPacketSize; - struct xhci_inctx *in = &pipe->dev->inctx; + struct xhci_inctx *in = xhci_alloc_inctx(pipe); + if (!in) + return upipe; in->add = (1 << 1); - in->del = 0; - in->ep[0].ctx[1] &= 0xffff; in->ep[0].ctx[1] |= (pipe->pipe.maxpacket << 16); - int cc = xhci_cmd_evaluate_context(pipe->dev); + int cc = xhci_cmd_evaluate_context(pipe->dev, in); if (cc != CC_SUCCESS) { dprintf(1, "%s: reconf ctl endpoint: failed (cc %d)\n", __func__, cc); } + free(in); } return upipe; } @@ -971,24 +1003,8 @@ xhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize xhci->devs[slotid].ptr_low = (u32)&pipe->dev->devctx; xhci->devs[slotid].ptr_high = 0;
- struct usbdevice_s *usbdev = pipe->dev->usbdev; - u32 route = 0; - while (usbdev->hub->usbdev) { - route <<= 4; - route |= (usbdev->port+1) & 0xf; - usbdev = usbdev->hub->usbdev; - } - dprintf(3, "%s: root port %d, route 0x%x\n", - __func__, usbdev->port+1, route); - - struct xhci_inctx *in = &pipe->dev->inctx; - in->add = 0x03; - in->slot.ctx[0] |= (1 << 27); // context entries - in->slot.ctx[0] |= speed_to_xhci[pipe->dev->usbdev->speed] << 20; - in->slot.ctx[0] |= route; - in->slot.ctx[1] |= (usbdev->port+1) << 16; - /* TODO ctx0: hub bit */ - /* TODO ctx1: hub ports */ + struct xhci_inctx *in = xhci_alloc_inctx(pipe); + in->add |= (1 << 1);
in->ep[0].ctx[0] |= (3 << 16); // interval: 1ms in->ep[0].ctx[1] |= (4 << 3); // control pipe @@ -999,7 +1015,8 @@ xhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize in->ep[0].deq_high = 0; in->ep[0].length = 8;
- cc = xhci_cmd_address_device(pipe->dev); + cc = xhci_cmd_address_device(pipe->dev, in); + free(in); if (cc != CC_SUCCESS) { dprintf(1, "%s: address device: failed (cc %d)\n", __func__, cc); return -1;
It's easier to run the set_address from xhci_alloc_pipe as there is more information available there.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/usb-xhci.c | 67 ++++++++++++++++++++++++++++--------------------------- src/hw/usb.c | 4 ++-- 2 files changed, 36 insertions(+), 35 deletions(-)
diff --git a/src/hw/usb-xhci.c b/src/hw/usb-xhci.c index 53b179b..69727ae 100644 --- a/src/hw/usb-xhci.c +++ b/src/hw/usb-xhci.c @@ -917,7 +917,37 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev
dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__, usbdev, &pipe->reqs, pipe->dev->slotid, pipe->epid); - if (pipe->epid > 1 && pipe->dev->slotid) { + if (pipe->epid == 1) { + // Enable slot and send set_address command. + int slotid = xhci_cmd_enable_slot(xhci); + if (slotid < 0) { + dprintf(1, "%s: enable slot: failed\n", __func__); + goto fail; + } + dprintf(3, "%s: enable slot: got slotid %d\n", __func__, slotid); + pipe->dev->slotid = slotid; + xhci->devs[slotid].ptr_low = (u32)&pipe->dev->devctx; + xhci->devs[slotid].ptr_high = 0; + + struct xhci_inctx *in = xhci_alloc_inctx(pipe); + in->add |= (1 << 1); + + in->ep[0].ctx[0] |= (3 << 16); // interval: 1ms + in->ep[0].ctx[1] |= (4 << 3); // control pipe + in->ep[0].ctx[1] |= (speed_to_ctlsize[pipe->dev->usbdev->speed] << 16); + + in->ep[0].deq_low = (u32)&pipe->reqs.ring[0]; + in->ep[0].deq_low |= 1; // dcs + in->ep[0].deq_high = 0; + in->ep[0].length = 8; + + int cc = xhci_cmd_address_device(pipe->dev, in); + free(in); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: address device: failed (cc %d)\n", __func__, cc); + goto fail; + } + } else { struct xhci_inctx *in = xhci_alloc_inctx(pipe); if (!in) goto fail; @@ -990,46 +1020,17 @@ xhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize const struct usb_ctrlrequest *req = cmd; struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); struct usb_xhci_s *xhci = pipe->dev->xhci; - int cc;
- if (req->bRequest == USB_REQ_SET_ADDRESS) { - int slotid = xhci_cmd_enable_slot(xhci); - if (slotid < 0) { - dprintf(1, "%s: enable slot: failed\n", __func__); - return -1; - } - dprintf(3, "%s: enable slot: got slotid %d\n", __func__, slotid); - pipe->dev->slotid = slotid; - xhci->devs[slotid].ptr_low = (u32)&pipe->dev->devctx; - xhci->devs[slotid].ptr_high = 0; - - struct xhci_inctx *in = xhci_alloc_inctx(pipe); - in->add |= (1 << 1); - - in->ep[0].ctx[0] |= (3 << 16); // interval: 1ms - in->ep[0].ctx[1] |= (4 << 3); // control pipe - in->ep[0].ctx[1] |= (speed_to_ctlsize[pipe->dev->usbdev->speed] << 16); - - in->ep[0].deq_low = (u32)&pipe->reqs.ring[0]; - in->ep[0].deq_low |= 1; // dcs - in->ep[0].deq_high = 0; - in->ep[0].length = 8; - - cc = xhci_cmd_address_device(pipe->dev, in); - free(in); - if (cc != CC_SUCCESS) { - dprintf(1, "%s: address device: failed (cc %d)\n", __func__, cc); - return -1; - } + if (req->bRequest == USB_REQ_SET_ADDRESS) + // Set address command sent during xhci_alloc_pipe. return 0; - }
xhci_xfer_setup(pipe, req, dir, datalen); if (datalen) xhci_xfer_data(pipe, dir, data, datalen); xhci_xfer_status(pipe, dir, datalen);
- cc = xhci_event_wait(xhci, &pipe->reqs, 1000); + int cc = xhci_event_wait(xhci, &pipe->reqs, 1000); if (cc != CC_SUCCESS) { dprintf(1, "%s: control xfer failed (cc %d)\n", __func__, cc); return -1; diff --git a/src/hw/usb.c b/src/hw/usb.c index 8430e50..e1356f9 100644 --- a/src/hw/usb.c +++ b/src/hw/usb.c @@ -263,6 +263,8 @@ usb_set_address(struct usbdevice_s *usbdev) if (cntl->maxaddr >= USB_MAXADDR) return -1;
+ msleep(USB_TIME_RSTRCY); + // Create a pipe for the default address. struct usb_endpoint_descriptor epdesc = { .wMaxPacketSize = 8, @@ -272,8 +274,6 @@ usb_set_address(struct usbdevice_s *usbdev) if (!usbdev->defpipe) return -1;
- msleep(USB_TIME_RSTRCY); - // Send set_address command. struct usb_ctrlrequest req; req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
Eliminate the xhci_device struct by storing the slotid in usbdevice_s and in xhci_pipe. The remaining storage in that struct (xhci_devctx) is available from xhci->devs.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/usb-xhci.c | 143 +++++++++++++++++++++--------------------------------- src/hw/usb.h | 1 + 2 files changed, 57 insertions(+), 87 deletions(-)
diff --git a/src/hw/usb-xhci.c b/src/hw/usb-xhci.c index 69727ae..15fd6a6 100644 --- a/src/hw/usb-xhci.c +++ b/src/hw/usb-xhci.c @@ -247,20 +247,11 @@ struct usb_xhci_s { struct hlist_head list; };
-struct xhci_device { - struct xhci_devctx devctx; - - struct usbdevice_s *usbdev; - struct usb_xhci_s *xhci; - u32 slotid; - struct hlist_node next; -}; - struct xhci_pipe { struct xhci_ring reqs;
struct usb_pipe pipe; - struct xhci_device *dev; + u32 slotid; u32 epid; void *buf; int bufused; @@ -458,9 +449,9 @@ static void xhci_xfer_queue(struct xhci_pipe *pipe,
static void xhci_xfer_kick(struct xhci_pipe *pipe) { - struct xhci_device *dev = GET_LOWFLAT(pipe->dev); - struct usb_xhci_s *xhci = GET_LOWFLAT(dev->xhci); - u32 slotid = GET_LOWFLAT(dev->slotid); + struct usb_xhci_s *xhci = container_of( + GET_LOWFLAT(pipe->pipe.cntl), struct usb_xhci_s, usb); + u32 slotid = GET_LOWFLAT(pipe->slotid); u32 epid = GET_LOWFLAT(pipe->epid);
dprintf(5, "%s: ring %p, slotid %d, epid %d\n", @@ -531,20 +522,22 @@ static int xhci_cmd_enable_slot(struct usb_xhci_s *xhci) return (xhci->cmds->evt.control >> 24) & 0xff; }
-static int xhci_cmd_disable_slot(struct xhci_device *dev) +#if 0 +static int xhci_cmd_disable_slot(struct usb_xhci_s *xhci, u32 slotid) { ASSERT32FLAT(); struct xhci_trb cmd = { .ptr_low = 0, .ptr_high = 0, .status = 0, - .control = (dev->slotid << 24) | (CR_DISABLE_SLOT << 10) + .control = (slotid << 24) | (CR_DISABLE_SLOT << 10) }; - dprintf(3, "%s: slotid %d\n", __func__, dev->slotid); - return xhci_cmd_submit(dev->xhci, &cmd); + dprintf(3, "%s: slotid %d\n", __func__, slotid); + return xhci_cmd_submit(xhci, &cmd); } +#endif
-static int xhci_cmd_address_device(struct xhci_device *dev +static int xhci_cmd_address_device(struct usb_xhci_s *xhci, u32 slotid , struct xhci_inctx *inctx) { ASSERT32FLAT(); @@ -552,13 +545,13 @@ static int xhci_cmd_address_device(struct xhci_device *dev .ptr_low = (u32)inctx, .ptr_high = 0, .status = 0, - .control = (dev->slotid << 24) | (CR_ADDRESS_DEVICE << 10) + .control = (slotid << 24) | (CR_ADDRESS_DEVICE << 10) }; - dprintf(3, "%s: slotid %d\n", __func__, dev->slotid); - return xhci_cmd_submit(dev->xhci, &cmd); + dprintf(3, "%s: slotid %d\n", __func__, slotid); + return xhci_cmd_submit(xhci, &cmd); }
-static int xhci_cmd_configure_endpoint(struct xhci_device *dev +static int xhci_cmd_configure_endpoint(struct usb_xhci_s *xhci, u32 slotid , struct xhci_inctx *inctx) { ASSERT32FLAT(); @@ -566,14 +559,14 @@ static int xhci_cmd_configure_endpoint(struct xhci_device *dev .ptr_low = (u32)inctx, .ptr_high = 0, .status = 0, - .control = (dev->slotid << 24) | (CR_CONFIGURE_ENDPOINT << 10) + .control = (slotid << 24) | (CR_CONFIGURE_ENDPOINT << 10) }; dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, - dev->slotid, inctx->add, inctx->del); - return xhci_cmd_submit(dev->xhci, &cmd); + slotid, inctx->add, inctx->del); + return xhci_cmd_submit(xhci, &cmd); }
-static int xhci_cmd_evaluate_context(struct xhci_device *dev +static int xhci_cmd_evaluate_context(struct usb_xhci_s *xhci, u32 slotid , struct xhci_inctx *inctx) { ASSERT32FLAT(); @@ -581,11 +574,11 @@ static int xhci_cmd_evaluate_context(struct xhci_device *dev .ptr_low = (u32)inctx, .ptr_high = 0, .status = 0, - .control = (dev->slotid << 24) | (CR_EVALUATE_CONTEXT << 10) + .control = (slotid << 24) | (CR_EVALUATE_CONTEXT << 10) }; dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, - dev->slotid, inctx->add, inctx->del); - return xhci_cmd_submit(dev->xhci, &cmd); + slotid, inctx->add, inctx->del); + return xhci_cmd_submit(xhci, &cmd); }
static void xhci_xfer_setup(struct xhci_pipe *pipe, @@ -639,30 +632,6 @@ static void xhci_xfer_status(struct xhci_pipe *pipe, int dir, int datalen) xhci_xfer_kick(pipe); }
-static struct xhci_device *xhci_find_alloc_device(struct usb_xhci_s *xhci, - struct usbdevice_s *usbdev) -{ - ASSERT32FLAT(); - struct xhci_device *dev; - - hlist_for_each_entry(dev, &xhci->list, next) { - if (dev->usbdev == usbdev) { - return dev; - } - } - - dev = memalign_low(64, sizeof(*dev)); - if (!dev) { - warn_noalloc(); - return NULL; - } - memset(dev, 0, sizeof(*dev)); - dev->usbdev = usbdev; - dev->xhci = xhci; - hlist_add_head(&dev->next, &xhci->list); - return dev; -} - static void configure_xhci(void *data) { @@ -736,6 +705,7 @@ configure_xhci(void *data) mdelay(100);
usb_enumerate(&xhci->hub); + // XXX - should walk list of pipes and free unused pipes. if (xhci->hub.devcount) return;
@@ -824,18 +794,7 @@ static void xhci_hub_disconnect(struct usbhub_s *hub, u32 port) { ASSERT32FLAT(); - struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); - struct xhci_device *dev; - - hlist_for_each_entry(dev, &xhci->list, next) { - if (dev->usbdev->hub == hub && - dev->usbdev->port == port && - dev->slotid != 0) { - xhci_cmd_disable_slot(dev); - hlist_del(&dev->next); - return; - } - } + // XXX - should turn the port power off. }
static struct usbhub_op_s xhci_hub_ops = { @@ -849,7 +808,7 @@ static struct usbhub_op_s xhci_hub_ops = {
static struct xhci_inctx * -xhci_alloc_inctx(struct xhci_pipe *pipe) +xhci_alloc_inctx(struct usbdevice_s *usbdev) { struct xhci_inctx *in = memalign_tmphigh(4096, sizeof(*in)); if (!in) { @@ -858,7 +817,6 @@ xhci_alloc_inctx(struct xhci_pipe *pipe) } memset(in, 0, sizeof(*in));
- struct usbdevice_s *usbdev = pipe->dev->usbdev; u32 route = 0; while (usbdev->hub->usbdev) { route <<= 4; @@ -868,7 +826,7 @@ xhci_alloc_inctx(struct xhci_pipe *pipe)
in->add = 0x01; in->slot.ctx[0] |= (1 << 27); // context entries - in->slot.ctx[0] |= speed_to_xhci[pipe->dev->usbdev->speed] << 20; + in->slot.ctx[0] |= speed_to_xhci[usbdev->speed] << 20; in->slot.ctx[0] |= route; in->slot.ctx[1] |= (usbdev->port+1) << 16; /* TODO ctx0: hub bit */ @@ -907,48 +865,55 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev memset(pipe, 0, sizeof(*pipe));
usb_desc2pipe(&pipe->pipe, usbdev, epdesc); - pipe->dev = xhci_find_alloc_device(xhci, usbdev); - if (!pipe->dev) - goto fail; pipe->epid = epid; pipe->reqs.cs = 1; if (eptype == USB_ENDPOINT_XFER_INT) pipe->buf = malloc_low(pipe->pipe.maxpacket);
dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__, - usbdev, &pipe->reqs, pipe->dev->slotid, pipe->epid); + usbdev, &pipe->reqs, pipe->slotid, pipe->epid); if (pipe->epid == 1) { // Enable slot and send set_address command. + struct xhci_devctx *dev = memalign_high(2048, sizeof(*dev)); + if (!dev) { + warn_noalloc(); + goto fail; + } int slotid = xhci_cmd_enable_slot(xhci); if (slotid < 0) { dprintf(1, "%s: enable slot: failed\n", __func__); + free(dev); goto fail; } dprintf(3, "%s: enable slot: got slotid %d\n", __func__, slotid); - pipe->dev->slotid = slotid; - xhci->devs[slotid].ptr_low = (u32)&pipe->dev->devctx; + + memset(dev, 0, sizeof(*dev)); + pipe->slotid = usbdev->slotid = slotid; + xhci->devs[slotid].ptr_low = (u32)dev; xhci->devs[slotid].ptr_high = 0;
- struct xhci_inctx *in = xhci_alloc_inctx(pipe); + struct xhci_inctx *in = xhci_alloc_inctx(usbdev); + if (!in) + goto fail; in->add |= (1 << 1);
in->ep[0].ctx[0] |= (3 << 16); // interval: 1ms in->ep[0].ctx[1] |= (4 << 3); // control pipe - in->ep[0].ctx[1] |= (speed_to_ctlsize[pipe->dev->usbdev->speed] << 16); + in->ep[0].ctx[1] |= (speed_to_ctlsize[usbdev->speed] << 16);
in->ep[0].deq_low = (u32)&pipe->reqs.ring[0]; in->ep[0].deq_low |= 1; // dcs in->ep[0].deq_high = 0; in->ep[0].length = 8;
- int cc = xhci_cmd_address_device(pipe->dev, in); + int cc = xhci_cmd_address_device(xhci, slotid, in); free(in); if (cc != CC_SUCCESS) { dprintf(1, "%s: address device: failed (cc %d)\n", __func__, cc); goto fail; } } else { - struct xhci_inctx *in = xhci_alloc_inctx(pipe); + struct xhci_inctx *in = xhci_alloc_inctx(usbdev); if (!in) goto fail; in->add |= (1 << pipe->epid); @@ -965,7 +930,8 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev in->ep[e].deq_high = 0; in->ep[e].length = pipe->pipe.maxpacket;
- int cc = xhci_cmd_configure_endpoint(pipe->dev, in); + pipe->slotid = usbdev->slotid; + int cc = xhci_cmd_configure_endpoint(xhci, pipe->slotid, in); free(in); if (cc != CC_SUCCESS) { dprintf(1, "%s: configure endpoint: failed (cc %d)\n", __func__, cc); @@ -988,19 +954,21 @@ xhci_update_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe return NULL; u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; struct xhci_pipe *pipe = container_of(upipe, struct xhci_pipe, pipe); + struct usb_xhci_s *xhci = container_of( + pipe->pipe.cntl, struct usb_xhci_s, usb); dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__, - usbdev, &pipe->reqs, pipe->dev->slotid, pipe->epid); + usbdev, &pipe->reqs, pipe->slotid, pipe->epid); if (eptype == USB_ENDPOINT_XFER_CONTROL && pipe->pipe.maxpacket != epdesc->wMaxPacketSize) { dprintf(1, "%s: reconf ctl endpoint pkt size: %d -> %d\n", __func__, pipe->pipe.maxpacket, epdesc->wMaxPacketSize); pipe->pipe.maxpacket = epdesc->wMaxPacketSize; - struct xhci_inctx *in = xhci_alloc_inctx(pipe); + struct xhci_inctx *in = xhci_alloc_inctx(usbdev); if (!in) return upipe; in->add = (1 << 1); in->ep[0].ctx[1] |= (pipe->pipe.maxpacket << 16); - int cc = xhci_cmd_evaluate_context(pipe->dev, in); + int cc = xhci_cmd_evaluate_context(xhci, pipe->slotid, in); if (cc != CC_SUCCESS) { dprintf(1, "%s: reconf ctl endpoint: failed (cc %d)\n", __func__, cc); @@ -1019,7 +987,8 @@ xhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize return -1; const struct usb_ctrlrequest *req = cmd; struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); - struct usb_xhci_s *xhci = pipe->dev->xhci; + struct usb_xhci_s *xhci = container_of( + pipe->pipe.cntl, struct usb_xhci_s, usb);
if (req->bRequest == USB_REQ_SET_ADDRESS) // Set address command sent during xhci_alloc_pipe. @@ -1046,8 +1015,8 @@ xhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datalen) return -1;
struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); - struct xhci_device *dev = GET_LOWFLAT(pipe->dev); - struct usb_xhci_s *xhci = GET_LOWFLAT(dev->xhci); + struct usb_xhci_s *xhci = container_of( + GET_LOWFLAT(pipe->pipe.cntl), struct usb_xhci_s, usb);
xhci_xfer_normal(pipe, data, datalen); int cc = xhci_event_wait(xhci, &pipe->reqs, 1000); @@ -1065,8 +1034,8 @@ xhci_poll_intr(struct usb_pipe *p, void *data) return -1;
struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); - struct xhci_device *dev = GET_LOWFLAT(pipe->dev); - struct usb_xhci_s *xhci = GET_LOWFLAT(dev->xhci); + struct usb_xhci_s *xhci = container_of( + GET_LOWFLAT(pipe->pipe.cntl), struct usb_xhci_s, usb); u32 len = GET_LOWFLAT(pipe->pipe.maxpacket); void *buf = GET_LOWFLAT(pipe->buf); int bufused = GET_LOWFLAT(pipe->bufused); diff --git a/src/hw/usb.h b/src/hw/usb.h index 883c608..cb5a05c 100644 --- a/src/hw/usb.h +++ b/src/hw/usb.h @@ -22,6 +22,7 @@ struct usb_pipe { struct usbdevice_s { struct usbhub_s *hub; struct usb_pipe *defpipe; + u32 slotid; u32 port; struct usb_config_descriptor *config; struct usb_interface_descriptor *iface;
At least some real-world controllers require 64 byte contexts. Detect this case and handle it appropriately.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/usb-xhci.c | 64 ++++++++++++++++++++++++++++++++----------------------- src/hw/usb-xhci.h | 8 ------- 2 files changed, 37 insertions(+), 35 deletions(-)
diff --git a/src/hw/usb-xhci.c b/src/hw/usb-xhci.c index 15fd6a6..21c5ccf 100644 --- a/src/hw/usb-xhci.c +++ b/src/hw/usb-xhci.c @@ -229,6 +229,7 @@ struct usb_xhci_s { u32 xcap; u32 ports; u32 slots; + u8 context64;
/* xhci registers */ struct xhci_caps *caps; @@ -810,12 +811,15 @@ static struct usbhub_op_s xhci_hub_ops = { static struct xhci_inctx * xhci_alloc_inctx(struct usbdevice_s *usbdev) { - struct xhci_inctx *in = memalign_tmphigh(4096, sizeof(*in)); + struct usb_xhci_s *xhci = container_of( + usbdev->hub->cntl, struct usb_xhci_s, usb); + int size = (sizeof(struct xhci_inctx) * 33) << xhci->context64; + struct xhci_inctx *in = memalign_tmphigh(2048 << xhci->context64, size); if (!in) { warn_noalloc(); return NULL; } - memset(in, 0, sizeof(*in)); + memset(in, 0, size);
u32 route = 0; while (usbdev->hub->usbdev) { @@ -825,10 +829,11 @@ xhci_alloc_inctx(struct usbdevice_s *usbdev) }
in->add = 0x01; - in->slot.ctx[0] |= (1 << 27); // context entries - in->slot.ctx[0] |= speed_to_xhci[usbdev->speed] << 20; - in->slot.ctx[0] |= route; - in->slot.ctx[1] |= (usbdev->port+1) << 16; + struct xhci_slotctx *slot = (void*)&in[1 << xhci->context64]; + slot->ctx[0] |= (1 << 27); // context entries + slot->ctx[0] |= speed_to_xhci[usbdev->speed] << 20; + slot->ctx[0] |= route; + slot->ctx[1] |= (usbdev->port+1) << 16; /* TODO ctx0: hub bit */ /* TODO ctx1: hub ports */ return in; @@ -874,7 +879,8 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev usbdev, &pipe->reqs, pipe->slotid, pipe->epid); if (pipe->epid == 1) { // Enable slot and send set_address command. - struct xhci_devctx *dev = memalign_high(2048, sizeof(*dev)); + u32 size = (sizeof(struct xhci_slotctx) * 32) << xhci->context64; + struct xhci_devctx *dev = memalign_high(1024 << xhci->context64, size); if (!dev) { warn_noalloc(); goto fail; @@ -887,7 +893,7 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev } dprintf(3, "%s: enable slot: got slotid %d\n", __func__, slotid);
- memset(dev, 0, sizeof(*dev)); + memset(dev, 0, size); pipe->slotid = usbdev->slotid = slotid; xhci->devs[slotid].ptr_low = (u32)dev; xhci->devs[slotid].ptr_high = 0; @@ -897,14 +903,15 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev goto fail; in->add |= (1 << 1);
- in->ep[0].ctx[0] |= (3 << 16); // interval: 1ms - in->ep[0].ctx[1] |= (4 << 3); // control pipe - in->ep[0].ctx[1] |= (speed_to_ctlsize[usbdev->speed] << 16); + struct xhci_epctx *ep = (void*)&in[2 << xhci->context64]; + ep->ctx[0] |= (3 << 16); // interval: 1ms + ep->ctx[1] |= (4 << 3); // control pipe + ep->ctx[1] |= (speed_to_ctlsize[usbdev->speed] << 16);
- in->ep[0].deq_low = (u32)&pipe->reqs.ring[0]; - in->ep[0].deq_low |= 1; // dcs - in->ep[0].deq_high = 0; - in->ep[0].length = 8; + ep->deq_low = (u32)&pipe->reqs.ring[0]; + ep->deq_low |= 1; // dcs + ep->deq_high = 0; + ep->length = 8;
int cc = xhci_cmd_address_device(xhci, slotid, in); free(in); @@ -918,17 +925,17 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev goto fail; in->add |= (1 << pipe->epid);
- int e = pipe->epid-1; + struct xhci_epctx *ep = (void*)&in[(pipe->epid+1) << xhci->context64]; if (eptype == USB_ENDPOINT_XFER_INT) - in->ep[e].ctx[0] = (usb_getFrameExp(usbdev, epdesc) + 3) << 16; - in->ep[e].ctx[1] |= (eptype << 3); + ep->ctx[0] = (usb_getFrameExp(usbdev, epdesc) + 3) << 16; + ep->ctx[1] |= (eptype << 3); if (epdesc->bEndpointAddress & USB_DIR_IN) - in->ep[e].ctx[1] |= (1 << 5); - in->ep[e].ctx[1] |= (pipe->pipe.maxpacket << 16); - in->ep[e].deq_low = (u32)&pipe->reqs.ring[0]; - in->ep[e].deq_low |= 1; // dcs - in->ep[e].deq_high = 0; - in->ep[e].length = pipe->pipe.maxpacket; + ep->ctx[1] |= (1 << 5); + ep->ctx[1] |= (pipe->pipe.maxpacket << 16); + ep->deq_low = (u32)&pipe->reqs.ring[0]; + ep->deq_low |= 1; // dcs + ep->deq_high = 0; + ep->length = pipe->pipe.maxpacket;
pipe->slotid = usbdev->slotid; int cc = xhci_cmd_configure_endpoint(xhci, pipe->slotid, in); @@ -967,7 +974,8 @@ xhci_update_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe if (!in) return upipe; in->add = (1 << 1); - in->ep[0].ctx[1] |= (pipe->pipe.maxpacket << 16); + struct xhci_epctx *ep = (void*)&in[2 << xhci->context64]; + ep->ctx[1] |= (pipe->pipe.maxpacket << 16); int cc = xhci_cmd_evaluate_context(xhci, pipe->slotid, in); if (cc != CC_SUCCESS) { dprintf(1, "%s: reconf ctl endpoint: failed (cc %d)\n", @@ -1083,6 +1091,7 @@ xhci_controller_setup(struct pci_device *pci) xhci->ports = (hcs1 >> 24) & 0xff; xhci->slots = hcs1 & 0xff; xhci->xcap = ((hcc >> 16) & 0xffff) << 2; + xhci->context64 = (hcc & 0x04) ? 1 : 0;
xhci->usb.pci = pci; xhci->usb.type = USB_TYPE_XHCI; @@ -1090,10 +1099,11 @@ xhci_controller_setup(struct pci_device *pci) xhci->hub.portcount = xhci->ports; xhci->hub.op = &xhci_hub_ops;
- dprintf(1, "XHCI init on dev %02x:%02x.%x: regs @ %p, %d ports, %d slots\n" + dprintf(1, "XHCI init on dev %02x:%02x.%x: regs @ %p, %d ports, %d slots" + ", %d byte contexts\n" , pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf) , pci_bdf_to_fn(pci->bdf), xhci->caps - , xhci->ports, xhci->slots); + , xhci->ports, xhci->slots, xhci->context64 ? 64 : 32);
if (xhci->xcap) { u32 off, addr = xhci->baseaddr + xhci->xcap; diff --git a/src/hw/usb-xhci.h b/src/hw/usb-xhci.h index be6094c..bd98d17 100644 --- a/src/hw/usb-xhci.h +++ b/src/hw/usb-xhci.h @@ -104,12 +104,6 @@ struct xhci_epctx { u32 reserved_01[3]; } PACKED;
-// device context -struct xhci_devctx { - struct xhci_slotctx slot; - struct xhci_epctx ep[31]; -} PACKED; - // device context array element struct xhci_devlist { u32 ptr_low; @@ -121,8 +115,6 @@ struct xhci_inctx { u32 del; u32 add; u32 reserved_01[6]; - struct xhci_slotctx slot; - struct xhci_epctx ep[31]; } PACKED;
// transfer block (ring element)
Initial tests look promising and at least two different real-world chipsets show limited functionality with the support enabled.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Kconfig b/src/Kconfig index a42ab2d..e68c585 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -240,7 +240,7 @@ menu "Hardware support" help Support USB EHCI controllers. config USB_XHCI - depends on USB && QEMU_HARDWARE + depends on USB bool "USB XHCI controllers" default y help
On Fr, 2013-12-27 at 22:14 -0500, Kevin O'Connor wrote:
The following is my test series for XHCI. I've gotten it to the point that I can boot from a "super speed" USB flash drive. However, high speed flash drives and low speed keyboard/mouse still don't work for me. Hopefully, when Gerd returns he'll have some insight.
The most notable part of this series is the work to make the code auto-detect and handle 64 byte contexts (which the xhci controller on my e350m1 requires).
Back online now, will have a closer look at the bits after processing the email backlog ...
cheers, Gerd