Revision 439a97cc hw/usb-ehci.c
b/hw/usb-ehci.c | ||
---|---|---|
30 | 30 |
#include "usb.h" |
31 | 31 |
#include "pci.h" |
32 | 32 |
#include "monitor.h" |
33 |
#include "trace.h" |
|
33 | 34 |
|
34 | 35 |
#define EHCI_DEBUG 0 |
35 | 36 |
#define STATE_DEBUG 0 /* state transitions */ |
... | ... | |
417 | 418 |
} while(0) |
418 | 419 |
|
419 | 420 |
|
420 |
#if EHCI_DEBUG |
|
421 |
static const char *addr2str(unsigned addr) |
|
421 |
static const char *addr2str(target_phys_addr_t addr) |
|
422 | 422 |
{ |
423 |
const char *r = " unknown";
|
|
423 |
const char *r = "unknown"; |
|
424 | 424 |
const char *n[] = { |
425 |
[ CAPLENGTH ] = " CAPLENGTH",
|
|
425 |
[ CAPLENGTH ] = "CAPLENGTH", |
|
426 | 426 |
[ HCIVERSION ] = "HCIVERSION", |
427 |
[ HCSPARAMS ] = " HCSPARAMS",
|
|
428 |
[ HCCPARAMS ] = " HCCPARAMS",
|
|
429 |
[ USBCMD ] = " COMMAND",
|
|
430 |
[ USBSTS ] = " STATUS",
|
|
431 |
[ USBINTR ] = " INTERRUPT",
|
|
432 |
[ FRINDEX ] = " FRAME IDX",
|
|
427 |
[ HCSPARAMS ] = "HCSPARAMS", |
|
428 |
[ HCCPARAMS ] = "HCCPARAMS", |
|
429 |
[ USBCMD ] = "USBCMD",
|
|
430 |
[ USBSTS ] = "USBSTS",
|
|
431 |
[ USBINTR ] = "USBINTR",
|
|
432 |
[ FRINDEX ] = "FRINDEX",
|
|
433 | 433 |
[ PERIODICLISTBASE ] = "P-LIST BASE", |
434 | 434 |
[ ASYNCLISTADDR ] = "A-LIST ADDR", |
435 | 435 |
[ PORTSC_BEGIN ... |
436 |
PORTSC_END ] = "PORT STATUS",
|
|
437 |
[ CONFIGFLAG ] = "CONFIG FLAG",
|
|
436 |
PORTSC_END ] = "PORTSC",
|
|
437 |
[ CONFIGFLAG ] = "CONFIGFLAG", |
|
438 | 438 |
}; |
439 | 439 |
|
440 | 440 |
if (addr < ARRAY_SIZE(n) && n[addr] != NULL) { |
... | ... | |
443 | 443 |
return r; |
444 | 444 |
} |
445 | 445 |
} |
446 |
#endif |
|
447 | 446 |
|
447 |
static void ehci_trace_usbsts(uint32_t mask, int state) |
|
448 |
{ |
|
449 |
/* interrupts */ |
|
450 |
if (mask & USBSTS_INT) { |
|
451 |
trace_usb_ehci_usbsts("INT", state); |
|
452 |
} |
|
453 |
if (mask & USBSTS_ERRINT) { |
|
454 |
trace_usb_ehci_usbsts("ERRINT", state); |
|
455 |
} |
|
456 |
if (mask & USBSTS_PCD) { |
|
457 |
trace_usb_ehci_usbsts("PCD", state); |
|
458 |
} |
|
459 |
if (mask & USBSTS_FLR) { |
|
460 |
trace_usb_ehci_usbsts("FLR", state); |
|
461 |
} |
|
462 |
if (mask & USBSTS_HSE) { |
|
463 |
trace_usb_ehci_usbsts("HSE", state); |
|
464 |
} |
|
465 |
if (mask & USBSTS_IAA) { |
|
466 |
trace_usb_ehci_usbsts("IAA", state); |
|
467 |
} |
|
468 |
|
|
469 |
/* status */ |
|
470 |
if (mask & USBSTS_HALT) { |
|
471 |
trace_usb_ehci_usbsts("HALT", state); |
|
472 |
} |
|
473 |
if (mask & USBSTS_REC) { |
|
474 |
trace_usb_ehci_usbsts("REC", state); |
|
475 |
} |
|
476 |
if (mask & USBSTS_PSS) { |
|
477 |
trace_usb_ehci_usbsts("PSS", state); |
|
478 |
} |
|
479 |
if (mask & USBSTS_ASS) { |
|
480 |
trace_usb_ehci_usbsts("ASS", state); |
|
481 |
} |
|
482 |
} |
|
483 |
|
|
484 |
static inline void ehci_set_usbsts(EHCIState *s, int mask) |
|
485 |
{ |
|
486 |
if ((s->usbsts & mask) == mask) { |
|
487 |
return; |
|
488 |
} |
|
489 |
ehci_trace_usbsts(mask, 1); |
|
490 |
s->usbsts |= mask; |
|
491 |
} |
|
492 |
|
|
493 |
static inline void ehci_clear_usbsts(EHCIState *s, int mask) |
|
494 |
{ |
|
495 |
if ((s->usbsts & mask) == 0) { |
|
496 |
return; |
|
497 |
} |
|
498 |
ehci_trace_usbsts(mask, 0); |
|
499 |
s->usbsts &= ~mask; |
|
500 |
} |
|
448 | 501 |
|
449 | 502 |
static inline void ehci_set_interrupt(EHCIState *s, int intr) |
450 | 503 |
{ |
... | ... | |
452 | 505 |
|
453 | 506 |
// TODO honour interrupt threshold requests |
454 | 507 |
|
455 |
s->usbsts |= intr;
|
|
508 |
ehci_set_usbsts(s, intr);
|
|
456 | 509 |
|
457 | 510 |
if ((s->usbsts & USBINTR_MASK) & s->usbintr) { |
458 | 511 |
level = 1; |
... | ... | |
526 | 579 |
uint8_t *pci_conf; |
527 | 580 |
int i; |
528 | 581 |
|
582 |
trace_usb_ehci_reset(); |
|
529 | 583 |
pci_conf = s->dev.config; |
530 | 584 |
|
531 | 585 |
memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE); |
... | ... | |
576 | 630 |
val = s->mmio[addr] | (s->mmio[addr+1] << 8) | |
577 | 631 |
(s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24); |
578 | 632 |
|
633 |
trace_usb_ehci_mmio_readl(addr, addr2str(addr), val); |
|
579 | 634 |
return val; |
580 | 635 |
} |
581 | 636 |
|
... | ... | |
645 | 700 |
{ |
646 | 701 |
EHCIState *s = ptr; |
647 | 702 |
int i; |
648 |
#if EHCI_DEBUG |
|
649 |
const char *str;
|
|
650 |
#endif
|
|
703 |
|
|
704 |
trace_usb_ehci_mmio_writel(addr, addr2str(addr), val,
|
|
705 |
*(uint32_t *)(&s->mmio[addr]));
|
|
651 | 706 |
|
652 | 707 |
/* Only aligned reads are allowed on OHCI */ |
653 | 708 |
if (addr & 3) { |
... | ... | |
669 | 724 |
|
670 | 725 |
|
671 | 726 |
/* Do any register specific pre-write processing here. */ |
672 |
#if EHCI_DEBUG |
|
673 |
str = addr2str((unsigned) addr); |
|
674 |
#endif |
|
675 | 727 |
switch(addr) { |
676 | 728 |
case USBCMD: |
677 |
DPRINTF("ehci_mem_writel: USBCMD val=0x%08X, current cmd=0x%08X\n", |
|
678 |
val, s->usbcmd); |
|
679 |
|
|
680 | 729 |
if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) { |
681 |
DPRINTF("ehci_mem_writel: %s run, clear halt\n", str); |
|
682 | 730 |
qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); |
683 | 731 |
SET_LAST_RUN_CLOCK(s); |
684 |
s->usbsts &= ~USBSTS_HALT;
|
|
732 |
ehci_clear_usbsts(s, USBSTS_HALT);
|
|
685 | 733 |
} |
686 | 734 |
|
687 | 735 |
if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) { |
688 |
DPRINTF(" ** STOP **\n"); |
|
689 | 736 |
qemu_del_timer(s->frame_timer); |
690 | 737 |
// TODO - should finish out some stuff before setting halt |
691 |
s->usbsts |= USBSTS_HALT;
|
|
738 |
ehci_set_usbsts(s, USBSTS_HALT);
|
|
692 | 739 |
} |
693 | 740 |
|
694 | 741 |
if (val & USBCMD_HCRESET) { |
695 |
DPRINTF("ehci_mem_writel: %s run, resetting\n", str); |
|
696 | 742 |
ehci_reset(s); |
697 | 743 |
val &= ~USBCMD_HCRESET; |
698 | 744 |
} |
... | ... | |
703 | 749 |
val & USBCMD_FLS); |
704 | 750 |
val &= ~USBCMD_FLS; |
705 | 751 |
} |
706 |
#if EHCI_DEBUG |
|
707 |
if ((val & USBCMD_PSE) && !(s->usbcmd & USBCMD_PSE)) { |
|
708 |
DPRINTF("periodic scheduling enabled\n"); |
|
709 |
} |
|
710 |
if (!(val & USBCMD_PSE) && (s->usbcmd & USBCMD_PSE)) { |
|
711 |
DPRINTF("periodic scheduling disabled\n"); |
|
712 |
} |
|
713 |
if ((val & USBCMD_ASE) && !(s->usbcmd & USBCMD_ASE)) { |
|
714 |
DPRINTF("asynchronous scheduling enabled\n"); |
|
715 |
} |
|
716 |
if (!(val & USBCMD_ASE) && (s->usbcmd & USBCMD_ASE)) { |
|
717 |
DPRINTF("asynchronous scheduling disabled\n"); |
|
718 |
} |
|
719 |
if ((val & USBCMD_IAAD) && !(s->usbcmd & USBCMD_IAAD)) { |
|
720 |
DPRINTF("doorbell request received\n"); |
|
721 |
} |
|
722 |
if ((val & USBCMD_LHCR) && !(s->usbcmd & USBCMD_LHCR)) { |
|
723 |
DPRINTF("light host controller reset received\n"); |
|
724 |
} |
|
725 |
if ((val & USBCMD_ITC) != (s->usbcmd & USBCMD_ITC)) { |
|
726 |
DPRINTF("interrupt threshold control set to %x\n", |
|
727 |
(val & USBCMD_ITC)>>USBCMD_ITC_SH); |
|
728 |
} |
|
729 |
#endif |
|
730 | 752 |
break; |
731 | 753 |
|
732 |
|
|
733 | 754 |
case USBSTS: |
734 | 755 |
val &= USBSTS_RO_MASK; // bits 6 thru 31 are RO |
735 |
DPRINTF("ehci_mem_writel: %s RWC set to 0x%08X\n", str, val); |
|
736 |
|
|
737 |
val = (s->usbsts &= ~val); // bits 0 thru 5 are R/WC |
|
738 |
|
|
739 |
DPRINTF("ehci_mem_writel: %s updating interrupt condition\n", str); |
|
756 |
ehci_clear_usbsts(s, val); // bits 0 thru 5 are R/WC |
|
757 |
val = s->usbsts; |
|
740 | 758 |
ehci_set_interrupt(s, 0); |
741 | 759 |
break; |
742 | 760 |
|
743 |
|
|
744 | 761 |
case USBINTR: |
745 | 762 |
val &= USBINTR_MASK; |
746 |
DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val); |
|
747 | 763 |
break; |
748 | 764 |
|
749 | 765 |
case FRINDEX: |
750 | 766 |
s->sofv = val >> 3; |
751 |
DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val); |
|
752 | 767 |
break; |
753 | 768 |
|
754 | 769 |
case CONFIGFLAG: |
755 |
DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val); |
|
756 | 770 |
val &= 0x1; |
757 | 771 |
if (val) { |
758 | 772 |
for(i = 0; i < NB_PORTS; i++) |
... | ... | |
766 | 780 |
"ehci: PERIODIC list base register set while periodic schedule\n" |
767 | 781 |
" is enabled and HC is enabled\n"); |
768 | 782 |
} |
769 |
DPRINTF("ehci_mem_writel: P-LIST BASE set to 0x%08X\n", val); |
|
770 | 783 |
break; |
771 | 784 |
|
772 | 785 |
case ASYNCLISTADDR: |
... | ... | |
775 | 788 |
"ehci: ASYNC list address register set while async schedule\n" |
776 | 789 |
" is enabled and HC is enabled\n"); |
777 | 790 |
} |
778 |
DPRINTF("ehci_mem_writel: A-LIST ADDR set to 0x%08X\n", val); |
|
779 | 791 |
break; |
780 | 792 |
} |
781 | 793 |
|
... | ... | |
1227 | 1239 |
|
1228 | 1240 |
/* set reclamation flag at start event (4.8.6) */ |
1229 | 1241 |
if (async) { |
1230 |
ehci->usbsts |= USBSTS_REC;
|
|
1242 |
ehci_set_usbsts(ehci, USBSTS_REC);
|
|
1231 | 1243 |
} |
1232 | 1244 |
|
1233 | 1245 |
/* Find the head of the list (4.9.1.1) */ |
... | ... | |
1334 | 1346 |
|
1335 | 1347 |
/* EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */ |
1336 | 1348 |
if (ehci->usbsts & USBSTS_REC) { |
1337 |
ehci->usbsts &= ~USBSTS_REC;
|
|
1349 |
ehci_clear_usbsts(ehci, USBSTS_REC);
|
|
1338 | 1350 |
} else { |
1339 | 1351 |
DPRINTF("FETCHQH: QH 0x%08x. H-bit set, reclamation status reset" |
1340 | 1352 |
" - done processing\n", ehci->qhaddr); |
... | ... | |
1534 | 1546 |
} |
1535 | 1547 |
|
1536 | 1548 |
if (async) { |
1537 |
ehci->usbsts |= USBSTS_REC;
|
|
1549 |
ehci_set_usbsts(ehci, USBSTS_REC);
|
|
1538 | 1550 |
} |
1539 | 1551 |
|
1540 | 1552 |
ehci->exec_status = ehci_execute(ehci, qh); |
... | ... | |
1734 | 1746 |
if (!(ehci->usbcmd & USBCMD_ASE)) { |
1735 | 1747 |
break; |
1736 | 1748 |
} |
1737 |
ehci->usbsts |= USBSTS_ASS;
|
|
1749 |
ehci_set_usbsts(ehci, USBSTS_ASS);
|
|
1738 | 1750 |
ehci->astate = EST_ACTIVE; |
1739 | 1751 |
// No break, fall through to ACTIVE |
1740 | 1752 |
|
1741 | 1753 |
case EST_ACTIVE: |
1742 | 1754 |
if ( !(ehci->usbcmd & USBCMD_ASE)) { |
1743 |
ehci->usbsts &= ~USBSTS_ASS;
|
|
1755 |
ehci_clear_usbsts(ehci, USBSTS_ASS);
|
|
1744 | 1756 |
ehci->astate = EST_INACTIVE; |
1745 | 1757 |
break; |
1746 | 1758 |
} |
... | ... | |
1800 | 1812 |
switch(ehci->pstate) { |
1801 | 1813 |
case EST_INACTIVE: |
1802 | 1814 |
if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) { |
1803 |
DPRINTF("PERIODIC going active\n"); |
|
1804 |
ehci->usbsts |= USBSTS_PSS; |
|
1815 |
ehci_set_usbsts(ehci, USBSTS_PSS); |
|
1805 | 1816 |
ehci->pstate = EST_ACTIVE; |
1806 | 1817 |
// No break, fall through to ACTIVE |
1807 | 1818 |
} else |
... | ... | |
1809 | 1820 |
|
1810 | 1821 |
case EST_ACTIVE: |
1811 | 1822 |
if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) { |
1812 |
DPRINTF("PERIODIC going inactive\n"); |
|
1813 |
ehci->usbsts &= ~USBSTS_PSS; |
|
1823 |
ehci_clear_usbsts(ehci, USBSTS_PSS); |
|
1814 | 1824 |
ehci->pstate = EST_INACTIVE; |
1815 | 1825 |
break; |
1816 | 1826 |
} |
Also available in: Unified diff