5544c1b
From 0b1ccd39faa8d1ea71f2d02dbab5dfd13f54ac98 Mon Sep 17 00:00:00 2001
c8dfc65
From: Gerd Hoffmann <kraxel@redhat.com>
c8dfc65
Date: Tue, 28 Aug 2012 13:38:01 +0200
5544c1b
Subject: [PATCH] xhci: update port handling
c8dfc65
c8dfc65
This patch changes the way xhci ports are linked to USBPorts.  The fixed
c8dfc65
1:1 relationship between xhci ports and USBPorts is gone.  Now each
c8dfc65
USBPort represents a physical plug which has usually two xhci ports
c8dfc65
assigned: one usb2 and ond usb3 port.  usb devices show up at one or the
c8dfc65
other, depending on whenever they support superspeed or not.
c8dfc65
c8dfc65
This patch also makes the number of usb2 and usb3 ports runtime
c8dfc65
configurable by adding 'p2' and 'p3' properties.  It is allowed to
c8dfc65
have different numbers of usb2 and usb3 ports.  Specifying p2=4,p3=2
c8dfc65
will give you an xhci adapter which supports all speeds on physical
c8dfc65
ports 1+2 and usb2 only on ports 3+4.
c8dfc65
---
c8dfc65
 hw/usb/hcd-xhci.c | 137 ++++++++++++++++++++++++++++++++++++++----------------
c8dfc65
 1 file changed, 97 insertions(+), 40 deletions(-)
c8dfc65
c8dfc65
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
5544c1b
index d47539d..5813b4a 100644
c8dfc65
--- a/hw/usb/hcd-xhci.c
c8dfc65
+++ b/hw/usb/hcd-xhci.c
c8dfc65
@@ -36,10 +36,10 @@
c8dfc65
 #define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \
c8dfc65
                              __func__, __LINE__); abort(); } while (0)
c8dfc65
 
c8dfc65
-#define USB2_PORTS 4
c8dfc65
-#define USB3_PORTS 4
c8dfc65
+#define MAXPORTS_2 8
c8dfc65
+#define MAXPORTS_3 8
c8dfc65
 
c8dfc65
-#define MAXPORTS (USB2_PORTS+USB3_PORTS)
c8dfc65
+#define MAXPORTS (MAXPORTS_2+MAXPORTS_3)
c8dfc65
 #define MAXSLOTS MAXPORTS
c8dfc65
 #define MAXINTRS 1 /* MAXPORTS */
c8dfc65
 
c8dfc65
@@ -300,8 +300,10 @@ typedef struct XHCIRing {
c8dfc65
 } XHCIRing;
c8dfc65
 
c8dfc65
 typedef struct XHCIPort {
c8dfc65
-    USBPort port;
c8dfc65
     uint32_t portsc;
c8dfc65
+    uint32_t portnr;
c8dfc65
+    USBPort  *uport;
c8dfc65
+    uint32_t speedmask;
c8dfc65
 } XHCIPort;
c8dfc65
 
c8dfc65
 struct XHCIState;
c8dfc65
@@ -379,9 +381,13 @@ struct XHCIState {
c8dfc65
     qemu_irq irq;
c8dfc65
     MemoryRegion mem;
c8dfc65
     const char *name;
c8dfc65
-    uint32_t msi;
c8dfc65
     unsigned int devaddr;
c8dfc65
 
c8dfc65
+    /* properties */
c8dfc65
+    uint32_t numports_2;
c8dfc65
+    uint32_t numports_3;
c8dfc65
+    uint32_t msi;
c8dfc65
+
c8dfc65
     /* Operational Registers */
c8dfc65
     uint32_t usbcmd;
c8dfc65
     uint32_t usbsts;
c8dfc65
@@ -392,8 +398,10 @@ struct XHCIState {
c8dfc65
     uint32_t dcbaap_high;
c8dfc65
     uint32_t config;
c8dfc65
 
c8dfc65
+    USBPort  uports[MAX(MAXPORTS_2, MAXPORTS_3)];
c8dfc65
     XHCIPort ports[MAXPORTS];
c8dfc65
     XHCISlot slots[MAXSLOTS];
c8dfc65
+    uint32_t numports;
c8dfc65
 
c8dfc65
     /* Runtime Registers */
c8dfc65
     uint32_t iman;
c8dfc65
@@ -578,6 +586,28 @@ static inline dma_addr_t xhci_mask64(uint64_t addr)
c8dfc65
     }
c8dfc65
 }
c8dfc65
 
c8dfc65
+static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
c8dfc65
+{
c8dfc65
+    int index;
c8dfc65
+
c8dfc65
+    if (!uport->dev) {
c8dfc65
+        return NULL;
c8dfc65
+    }
c8dfc65
+    switch (uport->dev->speed) {
c8dfc65
+    case USB_SPEED_LOW:
c8dfc65
+    case USB_SPEED_FULL:
c8dfc65
+    case USB_SPEED_HIGH:
c8dfc65
+        index = uport->index;
c8dfc65
+        break;
c8dfc65
+    case USB_SPEED_SUPER:
c8dfc65
+        index = uport->index + xhci->numports_2;
c8dfc65
+        break;
c8dfc65
+    default:
c8dfc65
+        return NULL;
c8dfc65
+    }
c8dfc65
+    return &xhci->ports[index];
c8dfc65
+}
c8dfc65
+
c8dfc65
 static void xhci_irq_update(XHCIState *xhci)
c8dfc65
 {
c8dfc65
     int level = 0;
c8dfc65
@@ -1126,7 +1156,7 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
c8dfc65
         ep |= 0x80;
c8dfc65
     }
c8dfc65
 
c8dfc65
-    dev = xhci->ports[xhci->slots[slotid-1].port-1].port.dev;
c8dfc65
+    dev = xhci->ports[xhci->slots[slotid-1].port-1].uport->dev;
c8dfc65
     if (!dev) {
c8dfc65
         return CC_USB_TRANSACTION_ERROR;
c8dfc65
     }
c8dfc65
@@ -1313,7 +1343,7 @@ static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr)
c8dfc65
     if (!(port->portsc & PORTSC_PED)) {
c8dfc65
         return NULL;
c8dfc65
     }
c8dfc65
-    return usb_find_device(&port->port, addr);
c8dfc65
+    return usb_find_device(port->uport, addr);
c8dfc65
 }
c8dfc65
 
c8dfc65
 static int xhci_setup_packet(XHCITransfer *xfer)
5544c1b
@@ -1734,9 +1764,9 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
c8dfc65
             ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
c8dfc65
 
c8dfc65
     port = (slot_ctx[1]>>16) & 0xFF;
c8dfc65
-    dev = xhci->ports[port-1].port.dev;
c8dfc65
+    dev = xhci->ports[port-1].uport->dev;
c8dfc65
 
c8dfc65
-    if (port < 1 || port > MAXPORTS) {
c8dfc65
+    if (port < 1 || port > xhci->numports) {
c8dfc65
         fprintf(stderr, "xhci: bad port %d\n", port);
c8dfc65
         return CC_TRB_ERROR;
c8dfc65
     } else if (!dev) {
5544c1b
@@ -1985,7 +2015,7 @@ static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *tr
c8dfc65
 static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
c8dfc65
 {
c8dfc65
     dma_addr_t ctx;
c8dfc65
-    uint8_t bw_ctx[MAXPORTS+1];
c8dfc65
+    uint8_t bw_ctx[xhci->numports+1];
c8dfc65
 
c8dfc65
     DPRINTF("xhci_get_port_bandwidth()\n");
c8dfc65
 
5544c1b
@@ -1995,7 +2025,7 @@ static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
c8dfc65
 
c8dfc65
     /* TODO: actually implement real values here */
c8dfc65
     bw_ctx[0] = 0;
c8dfc65
-    memset(&bw_ctx[1], 80, MAXPORTS); /* 80% */
c8dfc65
+    memset(&bw_ctx[1], 80, xhci->numports); /* 80% */
c8dfc65
     pci_dma_write(&xhci->pci_dev, ctx, bw_ctx, sizeof(bw_ctx));
c8dfc65
 
c8dfc65
     return CC_SUCCESS;
5544c1b
@@ -2165,12 +2195,11 @@ static void xhci_process_commands(XHCIState *xhci)
c8dfc65
 
c8dfc65
 static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
c8dfc65
 {
c8dfc65
-    int nr = port->port.index + 1;
c8dfc65
-
c8dfc65
     port->portsc = PORTSC_PP;
c8dfc65
-    if (port->port.dev && port->port.dev->attached && !is_detach) {
c8dfc65
+    if (port->uport->dev && port->uport->dev->attached && !is_detach &&
c8dfc65
+        (1 << port->uport->dev->speed) & port->speedmask) {
c8dfc65
         port->portsc |= PORTSC_CCS;
c8dfc65
-        switch (port->port.dev->speed) {
c8dfc65
+        switch (port->uport->dev->speed) {
c8dfc65
         case USB_SPEED_LOW:
c8dfc65
             port->portsc |= PORTSC_SPEED_LOW;
c8dfc65
             break;
5544c1b
@@ -2180,14 +2209,18 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
c8dfc65
         case USB_SPEED_HIGH:
c8dfc65
             port->portsc |= PORTSC_SPEED_HIGH;
c8dfc65
             break;
c8dfc65
+        case USB_SPEED_SUPER:
c8dfc65
+            port->portsc |= PORTSC_SPEED_SUPER;
c8dfc65
+            break;
c8dfc65
         }
c8dfc65
     }
c8dfc65
 
c8dfc65
     if (xhci_running(xhci)) {
c8dfc65
         port->portsc |= PORTSC_CSC;
c8dfc65
-        XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
c8dfc65
+        XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
c8dfc65
+                         port->portnr << 24};
c8dfc65
         xhci_event(xhci, &ev;;
c8dfc65
-        DPRINTF("xhci: port change event for port %d\n", nr);
c8dfc65
+        DPRINTF("xhci: port change event for port %d\n", port->portnr);
c8dfc65
     }
c8dfc65
 }
c8dfc65
 
5544c1b
@@ -2215,7 +2248,7 @@ static void xhci_reset(DeviceState *dev)
c8dfc65
         xhci_disable_slot(xhci, i+1);
c8dfc65
     }
c8dfc65
 
c8dfc65
-    for (i = 0; i < MAXPORTS; i++) {
c8dfc65
+    for (i = 0; i < xhci->numports; i++) {
c8dfc65
         xhci_update_port(xhci, xhci->ports + i, 0);
c8dfc65
     }
c8dfc65
 
5544c1b
@@ -2246,7 +2279,8 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
c8dfc65
         ret = 0x01000000 | LEN_CAP;
c8dfc65
         break;
c8dfc65
     case 0x04: /* HCSPARAMS 1 */
c8dfc65
-        ret = (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS;
c8dfc65
+        ret = ((xhci->numports_2+xhci->numports_3)<<24)
c8dfc65
+            | (MAXINTRS<<8) | MAXSLOTS;
c8dfc65
         break;
c8dfc65
     case 0x08: /* HCSPARAMS 2 */
c8dfc65
         ret = 0x0000000f;
5544c1b
@@ -2276,7 +2310,7 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
c8dfc65
         ret = 0x20425455; /* "USB " */
c8dfc65
         break;
c8dfc65
     case 0x28: /* Supported Protocol:08 */
c8dfc65
-        ret = 0x00000001 | (USB2_PORTS<<8);
c8dfc65
+        ret = 0x00000001 | (xhci->numports_2<<8);
c8dfc65
         break;
c8dfc65
     case 0x2c: /* Supported Protocol:0c */
c8dfc65
         ret = 0x00000000; /* reserved */
5544c1b
@@ -2288,7 +2322,7 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
c8dfc65
         ret = 0x20425455; /* "USB " */
c8dfc65
         break;
c8dfc65
     case 0x38: /* Supported Protocol:08 */
c8dfc65
-        ret = 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8);
c8dfc65
+        ret = 0x00000000 | (xhci->numports_2+1) | (xhci->numports_3<<8);
c8dfc65
         break;
c8dfc65
     case 0x3c: /* Supported Protocol:0c */
c8dfc65
         ret = 0x00000000; /* reserved */
5544c1b
@@ -2307,7 +2341,7 @@ static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg)
c8dfc65
     uint32_t port = reg >> 4;
c8dfc65
     uint32_t ret;
c8dfc65
 
c8dfc65
-    if (port >= MAXPORTS) {
c8dfc65
+    if (port >= xhci->numports) {
c8dfc65
         fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
c8dfc65
         ret = 0;
c8dfc65
         goto out;
5544c1b
@@ -2340,7 +2374,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
c8dfc65
 
c8dfc65
     trace_usb_xhci_port_write(port, reg & 0x0f, val);
c8dfc65
 
c8dfc65
-    if (port >= MAXPORTS) {
c8dfc65
+    if (port >= xhci->numports) {
c8dfc65
         fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
c8dfc65
         return;
c8dfc65
     }
5544c1b
@@ -2362,7 +2396,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
c8dfc65
         /* write-1-to-start bits */
c8dfc65
         if (val & PORTSC_PR) {
c8dfc65
             DPRINTF("xhci: port %d reset\n", port);
c8dfc65
-            usb_device_reset(xhci->ports[port].port.dev);
c8dfc65
+            usb_device_reset(xhci->ports[port].uport->dev);
c8dfc65
             portsc |= PORTSC_PRC | PORTSC_PED;
c8dfc65
         }
c8dfc65
         xhci->ports[port].portsc = portsc;
c8dfc65
@@ -2659,7 +2693,7 @@ static const MemoryRegionOps xhci_mem_ops = {
c8dfc65
 static void xhci_attach(USBPort *usbport)
c8dfc65
 {
c8dfc65
     XHCIState *xhci = usbport->opaque;
c8dfc65
-    XHCIPort *port = &xhci->ports[usbport->index];
c8dfc65
+    XHCIPort *port = xhci_lookup_port(xhci, usbport);
c8dfc65
 
c8dfc65
     xhci_update_port(xhci, port, 0);
c8dfc65
 }
c8dfc65
@@ -2667,7 +2701,7 @@ static void xhci_attach(USBPort *usbport)
c8dfc65
 static void xhci_detach(USBPort *usbport)
c8dfc65
 {
c8dfc65
     XHCIState *xhci = usbport->opaque;
c8dfc65
-    XHCIPort *port = &xhci->ports[usbport->index];
c8dfc65
+    XHCIPort *port = xhci_lookup_port(xhci, usbport);
c8dfc65
 
c8dfc65
     xhci_update_port(xhci, port, 1);
c8dfc65
 }
c8dfc65
@@ -2675,9 +2709,9 @@ static void xhci_detach(USBPort *usbport)
c8dfc65
 static void xhci_wakeup(USBPort *usbport)
c8dfc65
 {
c8dfc65
     XHCIState *xhci = usbport->opaque;
c8dfc65
-    XHCIPort *port = &xhci->ports[usbport->index];
c8dfc65
-    int nr = port->port.index + 1;
c8dfc65
-    XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
c8dfc65
+    XHCIPort *port = xhci_lookup_port(xhci, usbport);
c8dfc65
+    XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
c8dfc65
+                     port->portnr << 24};
c8dfc65
     uint32_t pls;
c8dfc65
 
c8dfc65
     pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK;
c8dfc65
@@ -2759,22 +2793,43 @@ static USBBusOps xhci_bus_ops = {
c8dfc65
 
c8dfc65
 static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)
c8dfc65
 {
c8dfc65
-    int i;
c8dfc65
+    XHCIPort *port;
c8dfc65
+    int i, usbports, speedmask;
c8dfc65
 
c8dfc65
     xhci->usbsts = USBSTS_HCH;
c8dfc65
 
c8dfc65
+    if (xhci->numports_2 > MAXPORTS_2) {
c8dfc65
+        xhci->numports_2 = MAXPORTS_2;
c8dfc65
+    }
c8dfc65
+    if (xhci->numports_3 > MAXPORTS_3) {
c8dfc65
+        xhci->numports_3 = MAXPORTS_3;
c8dfc65
+    }
c8dfc65
+    usbports = MAX(xhci->numports_2, xhci->numports_3);
c8dfc65
+    xhci->numports = xhci->numports_2 + xhci->numports_3;
c8dfc65
+
c8dfc65
     usb_bus_new(&xhci->bus, &xhci_bus_ops, &xhci->pci_dev.qdev);
c8dfc65
 
c8dfc65
-    for (i = 0; i < MAXPORTS; i++) {
c8dfc65
-        memset(&xhci->ports[i], 0, sizeof(xhci->ports[i]));
c8dfc65
-        usb_register_port(&xhci->bus, &xhci->ports[i].port, xhci, i,
c8dfc65
-                          &xhci_port_ops,
c8dfc65
-                          USB_SPEED_MASK_LOW  |
c8dfc65
-                          USB_SPEED_MASK_FULL |
c8dfc65
-                          USB_SPEED_MASK_HIGH);
c8dfc65
-    }
c8dfc65
-    for (i = 0; i < MAXSLOTS; i++) {
c8dfc65
-        xhci->slots[i].enabled = 0;
c8dfc65
+    for (i = 0; i < usbports; i++) {
c8dfc65
+        speedmask = 0;
c8dfc65
+        if (i < xhci->numports_2) {
c8dfc65
+            port = &xhci->ports[i];
c8dfc65
+            port->portnr = i + 1;
c8dfc65
+            port->uport = &xhci->uports[i];
c8dfc65
+            port->speedmask =
c8dfc65
+                USB_SPEED_MASK_LOW  |
c8dfc65
+                USB_SPEED_MASK_FULL |
c8dfc65
+                USB_SPEED_MASK_HIGH;
c8dfc65
+            speedmask |= port->speedmask;
c8dfc65
+        }
c8dfc65
+        if (i < xhci->numports_3) {
c8dfc65
+            port = &xhci->ports[i + xhci->numports_2];
c8dfc65
+            port->portnr = i + 1 + xhci->numports_2;
c8dfc65
+            port->uport = &xhci->uports[i];
c8dfc65
+            port->speedmask = USB_SPEED_MASK_SUPER;
c8dfc65
+            speedmask |= port->speedmask;
c8dfc65
+        }
c8dfc65
+        usb_register_port(&xhci->bus, &xhci->uports[i], xhci, i,
c8dfc65
+                          &xhci_port_ops, speedmask);
c8dfc65
     }
c8dfc65
 }
c8dfc65
 
c8dfc65
@@ -2830,6 +2885,8 @@ static const VMStateDescription vmstate_xhci = {
c8dfc65
 
c8dfc65
 static Property xhci_properties[] = {
c8dfc65
     DEFINE_PROP_UINT32("msi", XHCIState, msi, 0),
c8dfc65
+    DEFINE_PROP_UINT32("p2",  XHCIState, numports_2, 4),
c8dfc65
+    DEFINE_PROP_UINT32("p3",  XHCIState, numports_3, 4),
c8dfc65
     DEFINE_PROP_END_OF_LIST(),
c8dfc65
 };
c8dfc65
 
c8dfc65
-- 
5544c1b
1.7.12.1
c8dfc65