2012-10-28 18:05:07 +00:00
|
|
|
From 093374b8c759db877691fde602912a7cafd72a2e Mon Sep 17 00:00:00 2001
|
2012-09-07 15:20:05 +00:00
|
|
|
From: Gerd Hoffmann <kraxel@redhat.com>
|
|
|
|
Date: Thu, 6 Sep 2012 11:24:51 +0200
|
2012-10-28 18:05:07 +00:00
|
|
|
Subject: [PATCH] ehci: switch to new-style memory ops
|
2012-09-07 15:20:05 +00:00
|
|
|
|
|
|
|
Also register different memory regions for capabilities,
|
|
|
|
operational registers and port status registers. Create
|
|
|
|
separate tracepoints for operational regs and port status
|
|
|
|
regs. Ditch a bunch of sanity checks because the memory
|
|
|
|
core will do this for us now.
|
|
|
|
|
|
|
|
Offloading the byte, word and dword access handling to the
|
|
|
|
memory core also has the side effect of fixing ehci register
|
|
|
|
access on bigendian hosts.
|
|
|
|
|
|
|
|
Cc: David Gibson <david@gibson.dropbear.id.au>
|
|
|
|
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
|
2012-10-28 18:05:07 +00:00
|
|
|
(cherry picked from commit 3e4f910c8d490a1490409a7e381dbbb229f9d272)
|
|
|
|
|
|
|
|
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
|
2012-09-07 15:20:05 +00:00
|
|
|
---
|
|
|
|
hw/usb/hcd-ehci.c | 173 ++++++++++++++++++++++++++----------------------------
|
|
|
|
trace-events | 9 ++-
|
|
|
|
2 files changed, 90 insertions(+), 92 deletions(-)
|
|
|
|
|
|
|
|
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
|
|
|
|
index 2f3e9c0..f5ba8e1 100644
|
|
|
|
--- a/hw/usb/hcd-ehci.c
|
|
|
|
+++ b/hw/usb/hcd-ehci.c
|
|
|
|
@@ -389,6 +389,9 @@ struct EHCIState {
|
|
|
|
USBBus bus;
|
|
|
|
qemu_irq irq;
|
|
|
|
MemoryRegion mem;
|
|
|
|
+ MemoryRegion mem_caps;
|
|
|
|
+ MemoryRegion mem_opreg;
|
|
|
|
+ MemoryRegion mem_ports;
|
|
|
|
int companion_count;
|
|
|
|
|
|
|
|
/* properties */
|
|
|
|
@@ -398,10 +401,10 @@ struct EHCIState {
|
|
|
|
* EHCI spec version 1.0 Section 2.3
|
|
|
|
* Host Controller Operational Registers
|
|
|
|
*/
|
|
|
|
+ uint8_t caps[OPREGBASE];
|
|
|
|
union {
|
|
|
|
- uint8_t mmio[MMIO_SIZE];
|
|
|
|
+ uint32_t opreg[(PORTSC_BEGIN-OPREGBASE)/sizeof(uint32_t)];
|
|
|
|
struct {
|
|
|
|
- uint8_t cap[OPREGBASE];
|
|
|
|
uint32_t usbcmd;
|
|
|
|
uint32_t usbsts;
|
|
|
|
uint32_t usbintr;
|
|
|
|
@@ -411,9 +414,9 @@ struct EHCIState {
|
|
|
|
uint32_t asynclistaddr;
|
|
|
|
uint32_t notused[9];
|
|
|
|
uint32_t configflag;
|
|
|
|
- uint32_t portsc[NB_PORTS];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
+ uint32_t portsc[NB_PORTS];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Internal states, shadow registers, etc
|
|
|
|
@@ -471,22 +474,12 @@ static const char *ehci_state_names[] = {
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *ehci_mmio_names[] = {
|
|
|
|
- [CAPLENGTH] = "CAPLENGTH",
|
|
|
|
- [HCIVERSION] = "HCIVERSION",
|
|
|
|
- [HCSPARAMS] = "HCSPARAMS",
|
|
|
|
- [HCCPARAMS] = "HCCPARAMS",
|
|
|
|
[USBCMD] = "USBCMD",
|
|
|
|
[USBSTS] = "USBSTS",
|
|
|
|
[USBINTR] = "USBINTR",
|
|
|
|
[FRINDEX] = "FRINDEX",
|
|
|
|
[PERIODICLISTBASE] = "P-LIST BASE",
|
|
|
|
[ASYNCLISTADDR] = "A-LIST ADDR",
|
|
|
|
- [PORTSC_BEGIN] = "PORTSC #0",
|
|
|
|
- [PORTSC_BEGIN + 4] = "PORTSC #1",
|
|
|
|
- [PORTSC_BEGIN + 8] = "PORTSC #2",
|
|
|
|
- [PORTSC_BEGIN + 12] = "PORTSC #3",
|
|
|
|
- [PORTSC_BEGIN + 16] = "PORTSC #4",
|
|
|
|
- [PORTSC_BEGIN + 20] = "PORTSC #5",
|
|
|
|
[CONFIGFLAG] = "CONFIGFLAG",
|
|
|
|
};
|
|
|
|
|
|
|
|
@@ -509,7 +502,8 @@ static const char *state2str(uint32_t state)
|
|
|
|
|
|
|
|
static const char *addr2str(target_phys_addr_t addr)
|
|
|
|
{
|
|
|
|
- return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr);
|
|
|
|
+ return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names),
|
|
|
|
+ addr + OPREGBASE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ehci_trace_usbsts(uint32_t mask, int state)
|
|
|
|
@@ -1018,7 +1012,7 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[],
|
|
|
|
}
|
|
|
|
|
|
|
|
s->companion_count++;
|
|
|
|
- s->mmio[0x05] = (s->companion_count << 4) | portcount;
|
|
|
|
+ s->caps[0x05] = (s->companion_count << 4) | portcount;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
@@ -1063,7 +1057,8 @@ static void ehci_reset(void *opaque)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
|
|
|
|
+ memset(&s->opreg, 0x00, sizeof(s->opreg));
|
|
|
|
+ memset(&s->portsc, 0x00, sizeof(s->portsc));
|
|
|
|
|
|
|
|
s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
|
|
|
|
s->usbsts = USBSTS_HALT;
|
|
|
|
@@ -1090,50 +1085,35 @@ static void ehci_reset(void *opaque)
|
|
|
|
qemu_bh_cancel(s->async_bh);
|
|
|
|
}
|
|
|
|
|
|
|
|
-static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
|
|
|
|
+static uint64_t ehci_caps_read(void *ptr, target_phys_addr_t addr,
|
|
|
|
+ unsigned size)
|
|
|
|
{
|
|
|
|
EHCIState *s = ptr;
|
|
|
|
- uint32_t val;
|
|
|
|
-
|
|
|
|
- val = s->mmio[addr];
|
|
|
|
-
|
|
|
|
- return val;
|
|
|
|
+ return s->caps[addr];
|
|
|
|
}
|
|
|
|
|
|
|
|
-static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr)
|
|
|
|
+static uint64_t ehci_opreg_read(void *ptr, target_phys_addr_t addr,
|
|
|
|
+ unsigned size)
|
|
|
|
{
|
|
|
|
EHCIState *s = ptr;
|
|
|
|
uint32_t val;
|
|
|
|
|
|
|
|
- val = s->mmio[addr] | (s->mmio[addr+1] << 8);
|
|
|
|
-
|
|
|
|
+ val = s->opreg[addr >> 2];
|
|
|
|
+ trace_usb_ehci_opreg_read(addr + OPREGBASE, addr2str(addr), val);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
-static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr)
|
|
|
|
+static uint64_t ehci_port_read(void *ptr, target_phys_addr_t addr,
|
|
|
|
+ unsigned size)
|
|
|
|
{
|
|
|
|
EHCIState *s = ptr;
|
|
|
|
uint32_t val;
|
|
|
|
|
|
|
|
- val = s->mmio[addr] | (s->mmio[addr+1] << 8) |
|
|
|
|
- (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24);
|
|
|
|
-
|
|
|
|
- trace_usb_ehci_mmio_readl(addr, addr2str(addr), val);
|
|
|
|
+ val = s->portsc[addr >> 2];
|
|
|
|
+ trace_usb_ehci_portsc_read(addr + PORTSC_BEGIN, addr >> 2, val);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
-static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val)
|
|
|
|
-{
|
|
|
|
- fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n");
|
|
|
|
- exit(1);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
|
|
|
|
-{
|
|
|
|
- fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n");
|
|
|
|
- exit(1);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
|
|
|
|
{
|
|
|
|
USBDevice *dev = s->ports[port].dev;
|
|
|
|
@@ -1162,11 +1142,17 @@ static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
-static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
|
|
|
|
+static void ehci_port_write(void *ptr, target_phys_addr_t addr,
|
|
|
|
+ uint64_t val, unsigned size)
|
|
|
|
{
|
|
|
|
+ EHCIState *s = ptr;
|
|
|
|
+ int port = addr >> 2;
|
|
|
|
uint32_t *portsc = &s->portsc[port];
|
|
|
|
+ uint32_t old = *portsc;
|
|
|
|
USBDevice *dev = s->ports[port].dev;
|
|
|
|
|
|
|
|
+ trace_usb_ehci_portsc_write(addr + PORTSC_BEGIN, addr >> 2, val);
|
|
|
|
+
|
|
|
|
/* Clear rwc bits */
|
|
|
|
*portsc &= ~(val & PORTSC_RWC_MASK);
|
|
|
|
/* The guest may clear, but not set the PED bit */
|
|
|
|
@@ -1198,39 +1184,20 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
|
|
|
|
|
|
|
|
*portsc &= ~PORTSC_RO_MASK;
|
|
|
|
*portsc |= val;
|
|
|
|
+ trace_usb_ehci_portsc_change(addr + PORTSC_BEGIN, addr >> 2, *portsc, old);
|
|
|
|
}
|
|
|
|
|
|
|
|
-static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
|
|
|
|
+static void ehci_opreg_write(void *ptr, target_phys_addr_t addr,
|
|
|
|
+ uint64_t val, unsigned size)
|
|
|
|
{
|
|
|
|
EHCIState *s = ptr;
|
|
|
|
- uint32_t *mmio = (uint32_t *)(&s->mmio[addr]);
|
|
|
|
+ uint32_t *mmio = s->opreg + (addr >> 2);
|
|
|
|
uint32_t old = *mmio;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
- trace_usb_ehci_mmio_writel(addr, addr2str(addr), val);
|
|
|
|
-
|
|
|
|
- /* Only aligned reads are allowed on OHCI */
|
|
|
|
- if (addr & 3) {
|
|
|
|
- fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x"
|
|
|
|
- TARGET_FMT_plx "\n", addr);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) {
|
|
|
|
- handle_port_status_write(s, (addr-PORTSC)/4, val);
|
|
|
|
- trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (addr < OPREGBASE) {
|
|
|
|
- fprintf(stderr, "usb-ehci: write attempt to read-only register"
|
|
|
|
- TARGET_FMT_plx "\n", addr);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
+ trace_usb_ehci_opreg_write(addr + OPREGBASE, addr2str(addr), val);
|
|
|
|
|
|
|
|
- /* Do any register specific pre-write processing here. */
|
|
|
|
- switch(addr) {
|
|
|
|
+ switch (addr + OPREGBASE) {
|
|
|
|
case USBCMD:
|
|
|
|
if (val & USBCMD_HCRESET) {
|
|
|
|
ehci_reset(s);
|
|
|
|
@@ -1241,7 +1208,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
|
|
|
|
/* not supporting dynamic frame list size at the moment */
|
|
|
|
if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) {
|
|
|
|
fprintf(stderr, "attempt to set frame list size -- value %d\n",
|
|
|
|
- val & USBCMD_FLS);
|
|
|
|
+ (int)val & USBCMD_FLS);
|
|
|
|
val &= ~USBCMD_FLS;
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -1308,7 +1275,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
|
|
|
|
}
|
|
|
|
|
|
|
|
*mmio = val;
|
|
|
|
- trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old);
|
|
|
|
+ trace_usb_ehci_opreg_change(addr + OPREGBASE, addr2str(addr), *mmio, old);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2520,11 +2487,28 @@ static void ehci_async_bh(void *opaque)
|
|
|
|
ehci_advance_async_state(ehci);
|
|
|
|
}
|
|
|
|
|
|
|
|
-static const MemoryRegionOps ehci_mem_ops = {
|
|
|
|
- .old_mmio = {
|
|
|
|
- .read = { ehci_mem_readb, ehci_mem_readw, ehci_mem_readl },
|
|
|
|
- .write = { ehci_mem_writeb, ehci_mem_writew, ehci_mem_writel },
|
|
|
|
- },
|
|
|
|
+static const MemoryRegionOps ehci_mmio_caps_ops = {
|
|
|
|
+ .read = ehci_caps_read,
|
|
|
|
+ .valid.min_access_size = 1,
|
|
|
|
+ .valid.max_access_size = 4,
|
|
|
|
+ .impl.min_access_size = 1,
|
|
|
|
+ .impl.max_access_size = 1,
|
|
|
|
+ .endianness = DEVICE_LITTLE_ENDIAN,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const MemoryRegionOps ehci_mmio_opreg_ops = {
|
|
|
|
+ .read = ehci_opreg_read,
|
|
|
|
+ .write = ehci_opreg_write,
|
|
|
|
+ .valid.min_access_size = 4,
|
|
|
|
+ .valid.max_access_size = 4,
|
|
|
|
+ .endianness = DEVICE_LITTLE_ENDIAN,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const MemoryRegionOps ehci_mmio_port_ops = {
|
|
|
|
+ .read = ehci_port_read,
|
|
|
|
+ .write = ehci_port_write,
|
|
|
|
+ .valid.min_access_size = 4,
|
|
|
|
+ .valid.max_access_size = 4,
|
|
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
|
|
};
|
|
|
|
|
|
|
|
@@ -2681,19 +2665,19 @@ static int usb_ehci_initfn(PCIDevice *dev)
|
|
|
|
pci_conf[0x6e] = 0x00;
|
|
|
|
pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS
|
|
|
|
|
|
|
|
- // 2.2 host controller interface version
|
|
|
|
- s->mmio[0x00] = (uint8_t) OPREGBASE;
|
|
|
|
- s->mmio[0x01] = 0x00;
|
|
|
|
- s->mmio[0x02] = 0x00;
|
|
|
|
- s->mmio[0x03] = 0x01; // HC version
|
|
|
|
- s->mmio[0x04] = NB_PORTS; // Number of downstream ports
|
|
|
|
- s->mmio[0x05] = 0x00; // No companion ports at present
|
|
|
|
- s->mmio[0x06] = 0x00;
|
|
|
|
- s->mmio[0x07] = 0x00;
|
|
|
|
- s->mmio[0x08] = 0x80; // We can cache whole frame, not 64-bit capable
|
|
|
|
- s->mmio[0x09] = 0x68; // EECP
|
|
|
|
- s->mmio[0x0a] = 0x00;
|
|
|
|
- s->mmio[0x0b] = 0x00;
|
|
|
|
+ /* 2.2 host controller interface version */
|
|
|
|
+ s->caps[0x00] = (uint8_t) OPREGBASE;
|
|
|
|
+ s->caps[0x01] = 0x00;
|
|
|
|
+ s->caps[0x02] = 0x00;
|
|
|
|
+ s->caps[0x03] = 0x01; /* HC version */
|
|
|
|
+ s->caps[0x04] = NB_PORTS; /* Number of downstream ports */
|
|
|
|
+ s->caps[0x05] = 0x00; /* No companion ports at present */
|
|
|
|
+ s->caps[0x06] = 0x00;
|
|
|
|
+ s->caps[0x07] = 0x00;
|
|
|
|
+ s->caps[0x08] = 0x80; /* We can cache whole frame, no 64-bit */
|
|
|
|
+ s->caps[0x09] = 0x68; /* EECP */
|
|
|
|
+ s->caps[0x0a] = 0x00;
|
|
|
|
+ s->caps[0x0b] = 0x00;
|
|
|
|
|
|
|
|
s->irq = s->dev.irq[3];
|
|
|
|
|
|
|
|
@@ -2712,7 +2696,18 @@ static int usb_ehci_initfn(PCIDevice *dev)
|
|
|
|
|
|
|
|
qemu_register_reset(ehci_reset, s);
|
|
|
|
|
|
|
|
- memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE);
|
|
|
|
+ memory_region_init(&s->mem, "ehci", MMIO_SIZE);
|
|
|
|
+ memory_region_init_io(&s->mem_caps, &ehci_mmio_caps_ops, s,
|
|
|
|
+ "capabilities", OPREGBASE);
|
|
|
|
+ memory_region_init_io(&s->mem_opreg, &ehci_mmio_opreg_ops, s,
|
|
|
|
+ "operational", PORTSC_BEGIN - OPREGBASE);
|
|
|
|
+ memory_region_init_io(&s->mem_ports, &ehci_mmio_port_ops, s,
|
|
|
|
+ "ports", PORTSC_END - PORTSC_BEGIN);
|
|
|
|
+
|
|
|
|
+ memory_region_add_subregion(&s->mem, 0, &s->mem_caps);
|
|
|
|
+ memory_region_add_subregion(&s->mem, OPREGBASE, &s->mem_opreg);
|
|
|
|
+ memory_region_add_subregion(&s->mem, PORTSC_BEGIN, &s->mem_ports);
|
|
|
|
+
|
|
|
|
pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
diff --git a/trace-events b/trace-events
|
2012-10-28 18:05:07 +00:00
|
|
|
index c83d65e..cf05414 100644
|
2012-09-07 15:20:05 +00:00
|
|
|
--- a/trace-events
|
|
|
|
+++ b/trace-events
|
|
|
|
@@ -243,9 +243,12 @@ usb_port_release(int bus, const char *port) "bus %d, port %s"
|
|
|
|
|
|
|
|
# hw/usb/hcd-ehci.c
|
|
|
|
usb_ehci_reset(void) "=== RESET ==="
|
|
|
|
-usb_ehci_mmio_readl(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x"
|
|
|
|
-usb_ehci_mmio_writel(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x"
|
|
|
|
-usb_ehci_mmio_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)"
|
|
|
|
+usb_ehci_opreg_read(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x"
|
|
|
|
+usb_ehci_opreg_write(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x"
|
|
|
|
+usb_ehci_opreg_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)"
|
|
|
|
+usb_ehci_portsc_read(uint32_t addr, uint32_t port, uint32_t val) "rd mmio %04x [port %d] = %x"
|
|
|
|
+usb_ehci_portsc_write(uint32_t addr, uint32_t port, uint32_t val) "wr mmio %04x [port %d] = %x"
|
|
|
|
+usb_ehci_portsc_change(uint32_t addr, uint32_t port, uint32_t new, uint32_t old) "ch mmio %04x [port %d] = %x (old: %x)"
|
|
|
|
usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d"
|
|
|
|
usb_ehci_state(const char *schedule, const char *state) "%s schedule %s"
|
|
|
|
usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x"
|
|
|
|
--
|
2012-10-28 18:05:07 +00:00
|
|
|
1.7.12.1
|
2012-09-07 15:20:05 +00:00
|
|
|
|