diff options
Diffstat (limited to 'hw/usb-ohci.c')
-rw-r--r-- | hw/usb-ohci.c | 362 |
1 files changed, 219 insertions, 143 deletions
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 55cb77b..c575480 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA * * TODO: * o Isochronous transfers @@ -32,6 +32,7 @@ #include "usb.h" #include "pci.h" #include "pxa.h" +#include "devices.h" //#define DEBUG_OHCI /* Dump packet contents. */ @@ -60,13 +61,13 @@ typedef struct OHCIPort { enum ohci_type { OHCI_TYPE_PCI, - OHCI_TYPE_PXA + OHCI_TYPE_PXA, + OHCI_TYPE_SM501, }; typedef struct { qemu_irq irq; enum ohci_type type; - target_phys_addr_t mem_base; int mem; int num_ports; const char *name; @@ -109,6 +110,9 @@ typedef struct { uint32_t hreset; uint32_t htest; + /* SM501 local memory offset */ + target_phys_addr_t localmem_base; + /* Active packets. */ uint32_t old_ctl; USBPacket usb_packet; @@ -426,10 +430,13 @@ static void ohci_reset(void *opaque) } /* Get an array of dwords from main memory */ -static inline int get_dwords(uint32_t addr, uint32_t *buf, int num) +static inline int get_dwords(OHCIState *ohci, + uint32_t addr, uint32_t *buf, int num) { int i; + addr += ohci->localmem_base; + for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0); *buf = le32_to_cpu(*buf); @@ -439,10 +446,13 @@ static inline int get_dwords(uint32_t addr, uint32_t *buf, int num) } /* Put an array of dwords in to main memory */ -static inline int put_dwords(uint32_t addr, uint32_t *buf, int num) +static inline int put_dwords(OHCIState *ohci, + uint32_t addr, uint32_t *buf, int num) { int i; + addr += ohci->localmem_base; + for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { uint32_t tmp = cpu_to_le32(*buf); cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1); @@ -452,10 +462,13 @@ static inline int put_dwords(uint32_t addr, uint32_t *buf, int num) } /* Get an array of words from main memory */ -static inline int get_words(uint32_t addr, uint16_t *buf, int num) +static inline int get_words(OHCIState *ohci, + uint32_t addr, uint16_t *buf, int num) { int i; + addr += ohci->localmem_base; + for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0); *buf = le16_to_cpu(*buf); @@ -465,10 +478,13 @@ static inline int get_words(uint32_t addr, uint16_t *buf, int num) } /* Put an array of words in to main memory */ -static inline int put_words(uint32_t addr, uint16_t *buf, int num) +static inline int put_words(OHCIState *ohci, + uint32_t addr, uint16_t *buf, int num) { int i; + addr += ohci->localmem_base; + for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { uint16_t tmp = cpu_to_le16(*buf); cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1); @@ -477,40 +493,63 @@ static inline int put_words(uint32_t addr, uint16_t *buf, int num) return 1; } -static inline int ohci_read_ed(uint32_t addr, struct ohci_ed *ed) +static inline int ohci_read_ed(OHCIState *ohci, + uint32_t addr, struct ohci_ed *ed) +{ + return get_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2); +} + +static inline int ohci_read_td(OHCIState *ohci, + uint32_t addr, struct ohci_td *td) +{ + return get_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2); +} + +static inline int ohci_read_iso_td(OHCIState *ohci, + uint32_t addr, struct ohci_iso_td *td) { - return get_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2); + return (get_dwords(ohci, addr, (uint32_t *)td, 4) && + get_words(ohci, addr + 16, td->offset, 8)); } -static inline int ohci_read_td(uint32_t addr, struct ohci_td *td) +static inline int ohci_read_hcca(OHCIState *ohci, + uint32_t addr, struct ohci_hcca *hcca) { - return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); + cpu_physical_memory_rw(addr + ohci->localmem_base, + (uint8_t *)hcca, sizeof(*hcca), 0); + return 1; } -static inline int ohci_read_iso_td(uint32_t addr, struct ohci_iso_td *td) +static inline int ohci_put_ed(OHCIState *ohci, + uint32_t addr, struct ohci_ed *ed) { - return (get_dwords(addr, (uint32_t *)td, 4) && - get_words(addr + 16, td->offset, 8)); + return put_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2); } -static inline int ohci_put_ed(uint32_t addr, struct ohci_ed *ed) +static inline int ohci_put_td(OHCIState *ohci, + uint32_t addr, struct ohci_td *td) { - return put_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2); + return put_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2); } -static inline int ohci_put_td(uint32_t addr, struct ohci_td *td) +static inline int ohci_put_iso_td(OHCIState *ohci, + uint32_t addr, struct ohci_iso_td *td) { - return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); + return (put_dwords(ohci, addr, (uint32_t *)td, 4) && + put_words(ohci, addr + 16, td->offset, 8)); } -static inline int ohci_put_iso_td(uint32_t addr, struct ohci_iso_td *td) +static inline int ohci_put_hcca(OHCIState *ohci, + uint32_t addr, struct ohci_hcca *hcca) { - return (put_dwords(addr, (uint32_t *)td, 4) && - put_words(addr + 16, td->offset, 8)); + cpu_physical_memory_rw(addr + ohci->localmem_base, + (uint8_t *)hcca, sizeof(*hcca), 1); + return 1; } /* Read/Write the contents of a TD from/to main memory. */ -static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write) +static void ohci_copy_td(OHCIState *ohci, struct ohci_td *td, + uint8_t *buf, int len, int write) { uint32_t ptr; uint32_t n; @@ -519,16 +558,17 @@ static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write) n = 0x1000 - (ptr & 0xfff); if (n > len) n = len; - cpu_physical_memory_rw(ptr, buf, n, write); + cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, n, write); if (n == len) return; ptr = td->be & ~0xfffu; buf += n; - cpu_physical_memory_rw(ptr, buf, len - n, write); + cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, len - n, write); } /* Read/Write the contents of an ISO TD from/to main memory. */ -static void ohci_copy_iso_td(uint32_t start_addr, uint32_t end_addr, +static void ohci_copy_iso_td(OHCIState *ohci, + uint32_t start_addr, uint32_t end_addr, uint8_t *buf, int len, int write) { uint32_t ptr; @@ -538,12 +578,12 @@ static void ohci_copy_iso_td(uint32_t start_addr, uint32_t end_addr, n = 0x1000 - (ptr & 0xfff); if (n > len) n = len; - cpu_physical_memory_rw(ptr, buf, n, write); + cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, n, write); if (n == len) return; ptr = end_addr & ~0xfffu; buf += n; - cpu_physical_memory_rw(ptr, buf, len - n, write); + cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, len - n, write); } static void ohci_process_lists(OHCIState *ohci, int completion); @@ -580,7 +620,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, addr = ed->head & OHCI_DPTR_MASK; - if (!ohci_read_iso_td(addr, &iso_td)) { + if (!ohci_read_iso_td(ohci, addr, &iso_td)) { printf("usb-ohci: ISO_TD read error at %x\n", addr); return 0; } @@ -622,7 +662,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, i = OHCI_BM(iso_td.flags, TD_DI); if (i < ohci->done_count) ohci->done_count = i; - ohci_put_iso_td(addr, &iso_td); + ohci_put_iso_td(ohci, addr, &iso_td); return 0; } @@ -697,7 +737,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, } if (len && dir != OHCI_TD_DIR_IN) { - ohci_copy_iso_td(start_addr, end_addr, ohci->usb_buf, len, 0); + ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, len, 0); } if (completion) { @@ -733,7 +773,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, /* Writeback */ if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) { /* IN transfer succeeded */ - ohci_copy_iso_td(start_addr, end_addr, ohci->usb_buf, ret, 1); + ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, ret, 1); OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, OHCI_CC_NOERROR); OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, ret); @@ -789,7 +829,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, if (i < ohci->done_count) ohci->done_count = i; } - ohci_put_iso_td(addr, &iso_td); + ohci_put_iso_td(ohci, addr, &iso_td); return 1; } @@ -819,7 +859,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) #endif return 1; } - if (!ohci_read_td(addr, &td)) { + if (!ohci_read_td(ohci, addr, &td)) { fprintf(stderr, "usb-ohci: TD read error at %x\n", addr); return 0; } @@ -860,7 +900,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) } if (len && dir != OHCI_TD_DIR_IN && !completion) { - ohci_copy_td(&td, ohci->usb_buf, len, 0); + ohci_copy_td(ohci, &td, ohci->usb_buf, len, 0); } } @@ -919,7 +959,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) } if (ret >= 0) { if (dir == OHCI_TD_DIR_IN) { - ohci_copy_td(&td, ohci->usb_buf, ret, 1); + ohci_copy_td(ohci, &td, ohci->usb_buf, ret, 1); #ifdef DEBUG_PACKET dprintf(" data:"); for (i = 0; i < ret; i++) @@ -988,7 +1028,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) i = OHCI_BM(td.flags, TD_DI); if (i < ohci->done_count) ohci->done_count = i; - ohci_put_td(addr, &td); + ohci_put_td(ohci, addr, &td); return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR; } @@ -1006,7 +1046,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) return 0; for (cur = head; cur; cur = next_ed) { - if (!ohci_read_ed(cur, &ed)) { + if (!ohci_read_ed(ohci, cur, &ed)) { fprintf(stderr, "usb-ohci: ED read error at %x\n", cur); return 0; } @@ -1047,7 +1087,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) } } - ohci_put_ed(cur, &ed); + ohci_put_ed(ohci, cur, &ed); } return active; @@ -1088,7 +1128,7 @@ static void ohci_frame_boundary(void *opaque) OHCIState *ohci = opaque; struct ohci_hcca hcca; - cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 0); + ohci_read_hcca(ohci, ohci->hcca, &hcca); /* Process all the lists at the end of the frame */ if (ohci->ctl & OHCI_CTL_PLE) { @@ -1132,7 +1172,7 @@ static void ohci_frame_boundary(void *opaque) ohci_sof(ohci); /* Writeback HCCA */ - cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 1); + ohci_put_hcca(ohci, ohci->hcca, &hcca); } /* Start sending SOF tokens across the USB bus, lists are processed in @@ -1361,106 +1401,134 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr) { OHCIState *ohci = ptr; - - addr -= ohci->mem_base; + uint32_t retval; /* Only aligned reads are allowed on OHCI */ if (addr & 3) { fprintf(stderr, "usb-ohci: Mis-aligned read\n"); return 0xffffffff; - } - - if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { + } else if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { /* HcRhPortStatus */ - return ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS; + retval = ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS; + } else { + switch (addr >> 2) { + case 0: /* HcRevision */ + retval = 0x10; + break; + + case 1: /* HcControl */ + retval = ohci->ctl; + break; + + case 2: /* HcCommandStatus */ + retval = ohci->status; + break; + + case 3: /* HcInterruptStatus */ + retval = ohci->intr_status; + break; + + case 4: /* HcInterruptEnable */ + case 5: /* HcInterruptDisable */ + retval = ohci->intr; + break; + + case 6: /* HcHCCA */ + retval = ohci->hcca; + break; + + case 7: /* HcPeriodCurrentED */ + retval = ohci->per_cur; + break; + + case 8: /* HcControlHeadED */ + retval = ohci->ctrl_head; + break; + + case 9: /* HcControlCurrentED */ + retval = ohci->ctrl_cur; + break; + + case 10: /* HcBulkHeadED */ + retval = ohci->bulk_head; + break; + + case 11: /* HcBulkCurrentED */ + retval = ohci->bulk_cur; + break; + + case 12: /* HcDoneHead */ + retval = ohci->done; + break; + + case 13: /* HcFmInterretval */ + retval = (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi); + break; + + case 14: /* HcFmRemaining */ + retval = ohci_get_frame_remaining(ohci); + break; + + case 15: /* HcFmNumber */ + retval = ohci->frame_number; + break; + + case 16: /* HcPeriodicStart */ + retval = ohci->pstart; + break; + + case 17: /* HcLSThreshold */ + retval = ohci->lst; + break; + + case 18: /* HcRhDescriptorA */ + retval = ohci->rhdesc_a; + break; + + case 19: /* HcRhDescriptorB */ + retval = ohci->rhdesc_b; + break; + + case 20: /* HcRhStatus */ + retval = ohci->rhstatus; + break; + + /* PXA27x specific registers */ + case 24: /* HcStatus */ + retval = ohci->hstatus & ohci->hmask; + break; + + case 25: /* HcHReset */ + retval = ohci->hreset; + break; + + case 26: /* HcHInterruptEnable */ + retval = ohci->hmask; + break; + + case 27: /* HcHInterruptTest */ + retval = ohci->htest; + break; + + default: + fprintf(stderr, "ohci_read: Bad offset %x\n", (int)addr); + retval = 0xffffffff; + } } - switch (addr >> 2) { - case 0: /* HcRevision */ - return 0x10; - - case 1: /* HcControl */ - return ohci->ctl; - - case 2: /* HcCommandStatus */ - return ohci->status; - - case 3: /* HcInterruptStatus */ - return ohci->intr_status; - - case 4: /* HcInterruptEnable */ - case 5: /* HcInterruptDisable */ - return ohci->intr; - - case 6: /* HcHCCA */ - return ohci->hcca; - - case 7: /* HcPeriodCurrentED */ - return ohci->per_cur; - - case 8: /* HcControlHeadED */ - return ohci->ctrl_head; - - case 9: /* HcControlCurrentED */ - return ohci->ctrl_cur; - - case 10: /* HcBulkHeadED */ - return ohci->bulk_head; - - case 11: /* HcBulkCurrentED */ - return ohci->bulk_cur; - - case 12: /* HcDoneHead */ - return ohci->done; - - case 13: /* HcFmInterval */ - return (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi); - - case 14: /* HcFmRemaining */ - return ohci_get_frame_remaining(ohci); - - case 15: /* HcFmNumber */ - return ohci->frame_number; - - case 16: /* HcPeriodicStart */ - return ohci->pstart; - - case 17: /* HcLSThreshold */ - return ohci->lst; - - case 18: /* HcRhDescriptorA */ - return ohci->rhdesc_a; - - case 19: /* HcRhDescriptorB */ - return ohci->rhdesc_b; - - case 20: /* HcRhStatus */ - return ohci->rhstatus; - - /* PXA27x specific registers */ - case 24: /* HcStatus */ - return ohci->hstatus & ohci->hmask; - - case 25: /* HcHReset */ - return ohci->hreset; - - case 26: /* HcHInterruptEnable */ - return ohci->hmask; - - case 27: /* HcHInterruptTest */ - return ohci->htest; - - default: - fprintf(stderr, "ohci_read: Bad offset %x\n", (int)addr); - return 0xffffffff; - } +#ifdef TARGET_WORDS_BIGENDIAN + retval = bswap32(retval); +#endif + return retval; } static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val) { OHCIState *ohci = ptr; - addr -= ohci->mem_base; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif /* Only aligned reads are allowed on OHCI */ if (addr & 3) { @@ -1593,7 +1661,8 @@ static CPUWriteMemoryFunc *ohci_writefn[3]={ }; static void usb_ohci_init(OHCIState *ohci, int num_ports, int devfn, - qemu_irq irq, enum ohci_type type, const char *name) + qemu_irq irq, enum ohci_type type, + const char *name, uint32_t localmem_base) { int i; @@ -1613,7 +1682,8 @@ static void usb_ohci_init(OHCIState *ohci, int num_ports, int devfn, usb_frame_time, usb_bit_time); } - ohci->mem = cpu_register_io_memory(0, ohci_readfn, ohci_writefn, ohci); + ohci->mem = cpu_register_io_memory(ohci_readfn, ohci_writefn, ohci); + ohci->localmem_base = localmem_base; ohci->name = name; ohci->irq = irq; @@ -1625,7 +1695,7 @@ static void usb_ohci_init(OHCIState *ohci, int num_ports, int devfn, } ohci->async_td = 0; - qemu_register_reset(ohci_reset, ohci); + qemu_register_reset(ohci_reset, 0, ohci); ohci_reset(ohci); } @@ -1638,15 +1708,12 @@ static void ohci_mapfunc(PCIDevice *pci_dev, int i, uint32_t addr, uint32_t size, int type) { OHCIPCIState *ohci = (OHCIPCIState *)pci_dev; - ohci->state.mem_base = addr; cpu_register_physical_memory(addr, size, ohci->state.mem); } void usb_ohci_init_pci(struct PCIBus *bus, int num_ports, int devfn) { OHCIPCIState *ohci; - int vid = 0x106b; - int did = 0x003f; ohci = (OHCIPCIState *)pci_register_device(bus, "OHCI USB", sizeof(*ohci), devfn, NULL, NULL); @@ -1655,19 +1722,17 @@ void usb_ohci_init_pci(struct PCIBus *bus, int num_ports, int devfn) return; } - ohci->pci_dev.config[0x00] = vid & 0xff; - ohci->pci_dev.config[0x01] = (vid >> 8) & 0xff; - ohci->pci_dev.config[0x02] = did & 0xff; - ohci->pci_dev.config[0x03] = (did >> 8) & 0xff; + pci_config_set_vendor_id(ohci->pci_dev.config, PCI_VENDOR_ID_APPLE); + pci_config_set_device_id(ohci->pci_dev.config, + PCI_DEVICE_ID_APPLE_IPID_USB); ohci->pci_dev.config[0x09] = 0x10; /* OHCI */ - ohci->pci_dev.config[0x0a] = 0x3; - ohci->pci_dev.config[0x0b] = 0xc; + pci_config_set_class(ohci->pci_dev.config, PCI_CLASS_SERIAL_USB); ohci->pci_dev.config[0x3d] = 0x01; /* interrupt pin 1 */ usb_ohci_init(&ohci->state, num_ports, devfn, ohci->pci_dev.irq[0], - OHCI_TYPE_PCI, ohci->pci_dev.name); + OHCI_TYPE_PCI, ohci->pci_dev.name, 0); - pci_register_io_region((struct PCIDevice *)ohci, 0, 256, + pci_register_bar((struct PCIDevice *)ohci, 0, 256, PCI_ADDRESS_SPACE_MEM, ohci_mapfunc); } @@ -1677,8 +1742,19 @@ void usb_ohci_init_pxa(target_phys_addr_t base, int num_ports, int devfn, OHCIState *ohci = (OHCIState *)qemu_mallocz(sizeof(OHCIState)); usb_ohci_init(ohci, num_ports, devfn, irq, - OHCI_TYPE_PXA, "OHCI USB"); - ohci->mem_base = base; + OHCI_TYPE_PXA, "OHCI USB", 0); - cpu_register_physical_memory(ohci->mem_base, 0x1000, ohci->mem); + cpu_register_physical_memory(base, 0x1000, ohci->mem); } + +void usb_ohci_init_sm501(uint32_t mmio_base, uint32_t localmem_base, + int num_ports, int devfn, qemu_irq irq) +{ + OHCIState *ohci = (OHCIState *)qemu_mallocz(sizeof(OHCIState)); + + usb_ohci_init(ohci, num_ports, devfn, irq, + OHCI_TYPE_SM501, "OHCI USB", localmem_base); + + cpu_register_physical_memory(mmio_base, 0x1000, ohci->mem); +} + |