dcavalca / rpms / qemu

Forked from rpms/qemu 11 months ago
Clone

Blame 0012-usb-ehci-Add-support-for-registering-companion-contr.patch

Hans de Goede 3f1f29
From 692f238a2abea35607bc8c9e76d26ae5b15518da Mon Sep 17 00:00:00 2001
Hans de Goede 3f1f29
From: Hans de Goede <hdegoede@redhat.com>
Hans de Goede 3f1f29
Date: Fri, 24 Jun 2011 16:18:13 +0200
Hans de Goede 3f1f29
Subject: [PATCH 12/35] usb-ehci: Add support for registering companion
Hans de Goede 3f1f29
 controllers
Hans de Goede 3f1f29
Hans de Goede 3f1f29
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Hans de Goede 3f1f29
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Hans de Goede 3f1f29
---
Hans de Goede 3f1f29
 hw/usb-ehci.c |  174 +++++++++++++++++++++++++++++++++++++++++++++++----------
Hans de Goede 3f1f29
 1 files changed, 144 insertions(+), 30 deletions(-)
Hans de Goede 3f1f29
Hans de Goede 3f1f29
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
Hans de Goede 3f1f29
index 973c342..ec68c29 100644
Hans de Goede 3f1f29
--- a/hw/usb-ehci.c
Hans de Goede 3f1f29
+++ b/hw/usb-ehci.c
Hans de Goede 3f1f29
@@ -20,9 +20,6 @@
Hans de Goede 3f1f29
  *
Hans de Goede 3f1f29
  * You should have received a copy of the GNU General Public License
Hans de Goede 3f1f29
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
Hans de Goede 3f1f29
- *
Hans de Goede 3f1f29
- * TODO:
Hans de Goede 3f1f29
- *  o Downstream port handoff
Hans de Goede 3f1f29
  */
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
 #include "hw.h"
Hans de Goede 3f1f29
@@ -106,7 +103,7 @@
Hans de Goede 3f1f29
  * Bits that are reserved or are read-only are masked out of values
Hans de Goede 3f1f29
  * written to us by software
Hans de Goede 3f1f29
  */
Hans de Goede 3f1f29
-#define PORTSC_RO_MASK       0x007021c0
Hans de Goede 3f1f29
+#define PORTSC_RO_MASK       0x007001c0
Hans de Goede 3f1f29
 #define PORTSC_RWC_MASK      0x0000002a
Hans de Goede 3f1f29
 #define PORTSC_WKOC_E        (1 << 22)    // Wake on Over Current Enable
Hans de Goede 3f1f29
 #define PORTSC_WKDS_E        (1 << 21)    // Wake on Disconnect Enable
Hans de Goede 3f1f29
@@ -373,6 +370,7 @@ struct EHCIState {
Hans de Goede 3f1f29
     qemu_irq irq;
Hans de Goede 3f1f29
     target_phys_addr_t mem_base;
Hans de Goede 3f1f29
     int mem;
Hans de Goede 3f1f29
+    int companion_count;
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
     /* properties */
Hans de Goede 3f1f29
     uint32_t freq;
Hans de Goede 3f1f29
@@ -408,6 +406,7 @@ struct EHCIState {
Hans de Goede 3f1f29
     int astate;                        // Current state in asynchronous schedule
Hans de Goede 3f1f29
     int pstate;                        // Current state in periodic schedule
Hans de Goede 3f1f29
     USBPort ports[NB_PORTS];
Hans de Goede 3f1f29
+    USBPort *companion_ports[NB_PORTS];
Hans de Goede 3f1f29
     uint32_t usbsts_pending;
Hans de Goede 3f1f29
     QTAILQ_HEAD(, EHCIQueue) queues;
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
@@ -730,17 +729,17 @@ static void ehci_attach(USBPort *port)
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
     trace_usb_ehci_port_attach(port->index, port->dev->product_desc);
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
+    if (*portsc & PORTSC_POWNER) {
Hans de Goede 3f1f29
+        USBPort *companion = s->companion_ports[port->index];
Hans de Goede 3f1f29
+        companion->dev = port->dev;
Hans de Goede 3f1f29
+        companion->ops->attach(companion);
Hans de Goede 3f1f29
+        return;
Hans de Goede 3f1f29
+    }
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
     *portsc |= PORTSC_CONNECT;
Hans de Goede 3f1f29
     *portsc |= PORTSC_CSC;
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
-    /*
Hans de Goede 3f1f29
-     *  If a high speed device is attached then we own this port(indicated
Hans de Goede 3f1f29
-     *  by zero in the PORTSC_POWNER bit field) so set the status bit
Hans de Goede 3f1f29
-     *  and set an interrupt if enabled.
Hans de Goede 3f1f29
-     */
Hans de Goede 3f1f29
-    if ( !(*portsc & PORTSC_POWNER)) {
Hans de Goede 3f1f29
-        ehci_set_interrupt(s, USBSTS_PCD);
Hans de Goede 3f1f29
-    }
Hans de Goede 3f1f29
+    ehci_set_interrupt(s, USBSTS_PCD);
Hans de Goede 3f1f29
 }
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
 static void ehci_detach(USBPort *port)
Hans de Goede 3f1f29
@@ -750,36 +749,110 @@ static void ehci_detach(USBPort *port)
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
     trace_usb_ehci_port_detach(port->index);
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
+    if (*portsc & PORTSC_POWNER) {
Hans de Goede 3f1f29
+        USBPort *companion = s->companion_ports[port->index];
Hans de Goede 3f1f29
+        companion->ops->detach(companion);
Hans de Goede 3f1f29
+        companion->dev = NULL;
Hans de Goede 3f1f29
+        return;
Hans de Goede 3f1f29
+    }
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
     ehci_queues_rip_device(s, port->dev);
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
     *portsc &= ~(PORTSC_CONNECT|PORTSC_PED);
Hans de Goede 3f1f29
     *portsc |= PORTSC_CSC;
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
-    /*
Hans de Goede 3f1f29
-     *  If a high speed device is attached then we own this port(indicated
Hans de Goede 3f1f29
-     *  by zero in the PORTSC_POWNER bit field) so set the status bit
Hans de Goede 3f1f29
-     *  and set an interrupt if enabled.
Hans de Goede 3f1f29
-     */
Hans de Goede 3f1f29
-    if ( !(*portsc & PORTSC_POWNER)) {
Hans de Goede 3f1f29
-        ehci_set_interrupt(s, USBSTS_PCD);
Hans de Goede 3f1f29
-    }
Hans de Goede 3f1f29
+    ehci_set_interrupt(s, USBSTS_PCD);
Hans de Goede 3f1f29
 }
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
 static void ehci_child_detach(USBPort *port, USBDevice *child)
Hans de Goede 3f1f29
 {
Hans de Goede 3f1f29
     EHCIState *s = port->opaque;
Hans de Goede 3f1f29
+    uint32_t portsc = s->portsc[port->index];
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    if (portsc & PORTSC_POWNER) {
Hans de Goede 3f1f29
+        USBPort *companion = s->companion_ports[port->index];
Hans de Goede 3f1f29
+        companion->ops->child_detach(companion, child);
Hans de Goede 3f1f29
+        companion->dev = NULL;
Hans de Goede 3f1f29
+        return;
Hans de Goede 3f1f29
+    }
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
     ehci_queues_rip_device(s, child);
Hans de Goede 3f1f29
 }
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
+static void ehci_wakeup(USBPort *port)
Hans de Goede 3f1f29
+{
Hans de Goede 3f1f29
+    EHCIState *s = port->opaque;
Hans de Goede 3f1f29
+    uint32_t portsc = s->portsc[port->index];
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    if (portsc & PORTSC_POWNER) {
Hans de Goede 3f1f29
+        USBPort *companion = s->companion_ports[port->index];
Hans de Goede 3f1f29
+        if (companion->ops->wakeup) {
Hans de Goede 3f1f29
+            companion->ops->wakeup(companion);
Hans de Goede 3f1f29
+        }
Hans de Goede 3f1f29
+    }
Hans de Goede 3f1f29
+}
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+static int ehci_register_companion(USBBus *bus, USBPort *ports[],
Hans de Goede 3f1f29
+                                   uint32_t portcount, uint32_t firstport)
Hans de Goede 3f1f29
+{
Hans de Goede 3f1f29
+    EHCIState *s = container_of(bus, EHCIState, bus);
Hans de Goede 3f1f29
+    uint32_t i;
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    if (firstport + portcount > NB_PORTS) {
Hans de Goede 3f1f29
+        qerror_report(QERR_INVALID_PARAMETER_VALUE, "firstport",
Hans de Goede 3f1f29
+                      "firstport on masterbus");
Hans de Goede 3f1f29
+        error_printf_unless_qmp(
Hans de Goede 3f1f29
+            "firstport value of %u makes companion take ports %u - %u, which "
Hans de Goede 3f1f29
+            "is outside of the valid range of 0 - %u\n", firstport, firstport,
Hans de Goede 3f1f29
+            firstport + portcount - 1, NB_PORTS - 1);
Hans de Goede 3f1f29
+        return -1;
Hans de Goede 3f1f29
+    }
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    for (i = 0; i < portcount; i++) {
Hans de Goede 3f1f29
+        if (s->companion_ports[firstport + i]) {
Hans de Goede 3f1f29
+            qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
Hans de Goede 3f1f29
+                          "an USB masterbus");
Hans de Goede 3f1f29
+            error_printf_unless_qmp(
Hans de Goede 3f1f29
+                "port %u on masterbus %s already has a companion assigned\n",
Hans de Goede 3f1f29
+                firstport + i, bus->qbus.name);
Hans de Goede 3f1f29
+            return -1;
Hans de Goede 3f1f29
+        }
Hans de Goede 3f1f29
+    }
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    for (i = 0; i < portcount; i++) {
Hans de Goede 3f1f29
+        s->companion_ports[firstport + i] = ports[i];
Hans de Goede 3f1f29
+        s->ports[firstport + i].speedmask |=
Hans de Goede 3f1f29
+            USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL;
Hans de Goede 3f1f29
+        /* Ensure devs attached before the initial reset go to the companion */
Hans de Goede 3f1f29
+        s->portsc[firstport + i] = PORTSC_POWNER;
Hans de Goede 3f1f29
+    }
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    s->companion_count++;
Hans de Goede 3f1f29
+    s->mmio[0x05] = (s->companion_count << 4) | portcount;
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    return 0;
Hans de Goede 3f1f29
+}
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
 /* 4.1 host controller initialization */
Hans de Goede 3f1f29
 static void ehci_reset(void *opaque)
Hans de Goede 3f1f29
 {
Hans de Goede 3f1f29
     EHCIState *s = opaque;
Hans de Goede 3f1f29
     int i;
Hans de Goede 3f1f29
+    USBDevice *devs[NB_PORTS];
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
     trace_usb_ehci_reset();
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
+    /*
Hans de Goede 3f1f29
+     * Do the detach before touching portsc, so that it correctly gets send to
Hans de Goede 3f1f29
+     * us or to our companion based on PORTSC_POWNER before the reset.
Hans de Goede 3f1f29
+     */
Hans de Goede 3f1f29
+    for(i = 0; i < NB_PORTS; i++) {
Hans de Goede 3f1f29
+        devs[i] = s->ports[i].dev;
Hans de Goede 3f1f29
+        if (devs[i]) {
Hans de Goede 3f1f29
+            usb_attach(&s->ports[i], NULL);
Hans de Goede 3f1f29
+        }
Hans de Goede 3f1f29
+    }
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
     memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
     s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
Hans de Goede 3f1f29
@@ -791,10 +864,13 @@ static void ehci_reset(void *opaque)
Hans de Goede 3f1f29
     s->attach_poll_counter = 0;
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
     for(i = 0; i < NB_PORTS; i++) {
Hans de Goede 3f1f29
-        s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
Hans de Goede 3f1f29
-
Hans de Goede 3f1f29
-        if (s->ports[i].dev) {
Hans de Goede 3f1f29
-            usb_attach(&s->ports[i], s->ports[i].dev);
Hans de Goede 3f1f29
+        if (s->companion_ports[i]) {
Hans de Goede 3f1f29
+            s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
Hans de Goede 3f1f29
+        } else {
Hans de Goede 3f1f29
+            s->portsc[i] = PORTSC_PPOWER;
Hans de Goede 3f1f29
+        }
Hans de Goede 3f1f29
+        if (devs[i]) {
Hans de Goede 3f1f29
+            usb_attach(&s->ports[i], devs[i]);
Hans de Goede 3f1f29
         }
Hans de Goede 3f1f29
     }
Hans de Goede 3f1f29
     ehci_queues_rip_all(s);
Hans de Goede 3f1f29
@@ -844,6 +920,34 @@ static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
Hans de Goede 3f1f29
     exit(1);
Hans de Goede 3f1f29
 }
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
+static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
Hans de Goede 3f1f29
+{
Hans de Goede 3f1f29
+    USBDevice *dev = s->ports[port].dev;
Hans de Goede 3f1f29
+    uint32_t *portsc = &s->portsc[port];
Hans de Goede 3f1f29
+    uint32_t orig;
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    if (s->companion_ports[port] == NULL)
Hans de Goede 3f1f29
+        return;
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    owner = owner & PORTSC_POWNER;
Hans de Goede 3f1f29
+    orig  = *portsc & PORTSC_POWNER;
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    if (!(owner ^ orig)) {
Hans de Goede 3f1f29
+        return;
Hans de Goede 3f1f29
+    }
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    if (dev) {
Hans de Goede 3f1f29
+        usb_attach(&s->ports[port], NULL);
Hans de Goede 3f1f29
+    }
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    *portsc &= ~PORTSC_POWNER;
Hans de Goede 3f1f29
+    *portsc |= owner;
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    if (dev) {
Hans de Goede 3f1f29
+        usb_attach(&s->ports[port], dev);
Hans de Goede 3f1f29
+    }
Hans de Goede 3f1f29
+}
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
 static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
Hans de Goede 3f1f29
 {
Hans de Goede 3f1f29
     uint32_t *portsc = &s->portsc[port];
Hans de Goede 3f1f29
@@ -853,6 +957,9 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
Hans de Goede 3f1f29
     *portsc &= ~(val & PORTSC_RWC_MASK);
Hans de Goede 3f1f29
     /* The guest may clear, but not set the PED bit */
Hans de Goede 3f1f29
     *portsc &= val | ~PORTSC_PED;
Hans de Goede 3f1f29
+    /* POWNER is masked out by RO_MASK as it is RO when we've no companion */
Hans de Goede 3f1f29
+    handle_port_owner_write(s, port, val);
Hans de Goede 3f1f29
+    /* And finally apply RO_MASK */
Hans de Goede 3f1f29
     val &= PORTSC_RO_MASK;
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
     if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) {
Hans de Goede 3f1f29
@@ -956,7 +1063,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
Hans de Goede 3f1f29
         val &= 0x1;
Hans de Goede 3f1f29
         if (val) {
Hans de Goede 3f1f29
             for(i = 0; i < NB_PORTS; i++)
Hans de Goede 3f1f29
-                s->portsc[i] &= ~PORTSC_POWNER;
Hans de Goede 3f1f29
+                handle_port_owner_write(s, i, 0);
Hans de Goede 3f1f29
         }
Hans de Goede 3f1f29
         break;
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
@@ -1114,8 +1221,17 @@ static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw)
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
 static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
Hans de Goede 3f1f29
 {
Hans de Goede 3f1f29
-    EHCIQueue *q = container_of(packet, EHCIQueue, packet);
Hans de Goede 3f1f29
+    EHCIQueue *q;
Hans de Goede 3f1f29
+    EHCIState *s = port->opaque;
Hans de Goede 3f1f29
+    uint32_t portsc = s->portsc[port->index];
Hans de Goede 3f1f29
+
Hans de Goede 3f1f29
+    if (portsc & PORTSC_POWNER) {
Hans de Goede 3f1f29
+        USBPort *companion = s->companion_ports[port->index];
Hans de Goede 3f1f29
+        companion->ops->complete(companion, packet);
Hans de Goede 3f1f29
+        return;
Hans de Goede 3f1f29
+    }
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
+    q = container_of(packet, EHCIQueue, packet);
Hans de Goede 3f1f29
     trace_usb_ehci_queue_action(q, "wakeup");
Hans de Goede 3f1f29
     assert(q->async == EHCI_ASYNC_INFLIGHT);
Hans de Goede 3f1f29
     q->async = EHCI_ASYNC_FINISHED;
Hans de Goede 3f1f29
@@ -1245,8 +1361,6 @@ static int ehci_execute(EHCIQueue *q)
Hans de Goede 3f1f29
         port = &q->ehci->ports[i];
Hans de Goede 3f1f29
         dev = port->dev;
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
-        // TODO sometime we will also need to check if we are the port owner
Hans de Goede 3f1f29
-
Hans de Goede 3f1f29
         if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) {
Hans de Goede 3f1f29
             DPRINTF("Port %d, no exec, not connected(%08X)\n",
Hans de Goede 3f1f29
                     i, q->ehci->portsc[i]);
Hans de Goede 3f1f29
@@ -1339,8 +1453,6 @@ static int ehci_process_itd(EHCIState *ehci,
Hans de Goede 3f1f29
                 port = &ehci->ports[j];
Hans de Goede 3f1f29
                 dev = port->dev;
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
-                // TODO sometime we will also need to check if we are the port owner
Hans de Goede 3f1f29
-
Hans de Goede 3f1f29
                 if (!(ehci->portsc[j] &(PORTSC_CONNECT))) {
Hans de Goede 3f1f29
                     continue;
Hans de Goede 3f1f29
                 }
Hans de Goede 3f1f29
@@ -2124,10 +2236,12 @@ static USBPortOps ehci_port_ops = {
Hans de Goede 3f1f29
     .attach = ehci_attach,
Hans de Goede 3f1f29
     .detach = ehci_detach,
Hans de Goede 3f1f29
     .child_detach = ehci_child_detach,
Hans de Goede 3f1f29
+    .wakeup = ehci_wakeup,
Hans de Goede 3f1f29
     .complete = ehci_async_complete_packet,
Hans de Goede 3f1f29
 };
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
 static USBBusOps ehci_bus_ops = {
Hans de Goede 3f1f29
+    .register_companion = ehci_register_companion,
Hans de Goede 3f1f29
 };
Hans de Goede 3f1f29
 
Hans de Goede 3f1f29
 static PCIDeviceInfo ehci_info = {
Hans de Goede 3f1f29
-- 
Hans de Goede 3f1f29
1.7.5.1
Hans de Goede 3f1f29