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 |
}
|