Revision a0a3167a hw/usb-ehci.c
b/hw/usb-ehci.c | ||
---|---|---|
20 | 20 |
* |
21 | 21 |
* You should have received a copy of the GNU General Public License |
22 | 22 |
* along with this program; if not, see <http://www.gnu.org/licenses/>. |
23 |
* |
|
24 |
* TODO: |
|
25 |
* o Downstream port handoff |
|
26 | 23 |
*/ |
27 | 24 |
|
28 | 25 |
#include "hw.h" |
... | ... | |
106 | 103 |
* Bits that are reserved or are read-only are masked out of values |
107 | 104 |
* written to us by software |
108 | 105 |
*/ |
109 |
#define PORTSC_RO_MASK 0x007021c0
|
|
106 |
#define PORTSC_RO_MASK 0x007001c0
|
|
110 | 107 |
#define PORTSC_RWC_MASK 0x0000002a |
111 | 108 |
#define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable |
112 | 109 |
#define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable |
... | ... | |
373 | 370 |
qemu_irq irq; |
374 | 371 |
target_phys_addr_t mem_base; |
375 | 372 |
int mem; |
373 |
int companion_count; |
|
376 | 374 |
|
377 | 375 |
/* properties */ |
378 | 376 |
uint32_t freq; |
... | ... | |
408 | 406 |
int astate; // Current state in asynchronous schedule |
409 | 407 |
int pstate; // Current state in periodic schedule |
410 | 408 |
USBPort ports[NB_PORTS]; |
409 |
USBPort *companion_ports[NB_PORTS]; |
|
411 | 410 |
uint32_t usbsts_pending; |
412 | 411 |
QTAILQ_HEAD(, EHCIQueue) queues; |
413 | 412 |
|
... | ... | |
730 | 729 |
|
731 | 730 |
trace_usb_ehci_port_attach(port->index, port->dev->product_desc); |
732 | 731 |
|
732 |
if (*portsc & PORTSC_POWNER) { |
|
733 |
USBPort *companion = s->companion_ports[port->index]; |
|
734 |
companion->dev = port->dev; |
|
735 |
companion->ops->attach(companion); |
|
736 |
return; |
|
737 |
} |
|
738 |
|
|
733 | 739 |
*portsc |= PORTSC_CONNECT; |
734 | 740 |
*portsc |= PORTSC_CSC; |
735 | 741 |
|
736 |
/* |
|
737 |
* If a high speed device is attached then we own this port(indicated |
|
738 |
* by zero in the PORTSC_POWNER bit field) so set the status bit |
|
739 |
* and set an interrupt if enabled. |
|
740 |
*/ |
|
741 |
if ( !(*portsc & PORTSC_POWNER)) { |
|
742 |
ehci_set_interrupt(s, USBSTS_PCD); |
|
743 |
} |
|
742 |
ehci_set_interrupt(s, USBSTS_PCD); |
|
744 | 743 |
} |
745 | 744 |
|
746 | 745 |
static void ehci_detach(USBPort *port) |
... | ... | |
750 | 749 |
|
751 | 750 |
trace_usb_ehci_port_detach(port->index); |
752 | 751 |
|
752 |
if (*portsc & PORTSC_POWNER) { |
|
753 |
USBPort *companion = s->companion_ports[port->index]; |
|
754 |
companion->ops->detach(companion); |
|
755 |
companion->dev = NULL; |
|
756 |
return; |
|
757 |
} |
|
758 |
|
|
753 | 759 |
ehci_queues_rip_device(s, port->dev); |
754 | 760 |
|
755 | 761 |
*portsc &= ~(PORTSC_CONNECT|PORTSC_PED); |
756 | 762 |
*portsc |= PORTSC_CSC; |
757 | 763 |
|
758 |
/* |
|
759 |
* If a high speed device is attached then we own this port(indicated |
|
760 |
* by zero in the PORTSC_POWNER bit field) so set the status bit |
|
761 |
* and set an interrupt if enabled. |
|
762 |
*/ |
|
763 |
if ( !(*portsc & PORTSC_POWNER)) { |
|
764 |
ehci_set_interrupt(s, USBSTS_PCD); |
|
765 |
} |
|
764 |
ehci_set_interrupt(s, USBSTS_PCD); |
|
766 | 765 |
} |
767 | 766 |
|
768 | 767 |
static void ehci_child_detach(USBPort *port, USBDevice *child) |
769 | 768 |
{ |
770 | 769 |
EHCIState *s = port->opaque; |
770 |
uint32_t portsc = s->portsc[port->index]; |
|
771 |
|
|
772 |
if (portsc & PORTSC_POWNER) { |
|
773 |
USBPort *companion = s->companion_ports[port->index]; |
|
774 |
companion->ops->child_detach(companion, child); |
|
775 |
companion->dev = NULL; |
|
776 |
return; |
|
777 |
} |
|
771 | 778 |
|
772 | 779 |
ehci_queues_rip_device(s, child); |
773 | 780 |
} |
774 | 781 |
|
782 |
static void ehci_wakeup(USBPort *port) |
|
783 |
{ |
|
784 |
EHCIState *s = port->opaque; |
|
785 |
uint32_t portsc = s->portsc[port->index]; |
|
786 |
|
|
787 |
if (portsc & PORTSC_POWNER) { |
|
788 |
USBPort *companion = s->companion_ports[port->index]; |
|
789 |
if (companion->ops->wakeup) { |
|
790 |
companion->ops->wakeup(companion); |
|
791 |
} |
|
792 |
} |
|
793 |
} |
|
794 |
|
|
795 |
static int ehci_register_companion(USBBus *bus, USBPort *ports[], |
|
796 |
uint32_t portcount, uint32_t firstport) |
|
797 |
{ |
|
798 |
EHCIState *s = container_of(bus, EHCIState, bus); |
|
799 |
uint32_t i; |
|
800 |
|
|
801 |
if (firstport + portcount > NB_PORTS) { |
|
802 |
qerror_report(QERR_INVALID_PARAMETER_VALUE, "firstport", |
|
803 |
"firstport on masterbus"); |
|
804 |
error_printf_unless_qmp( |
|
805 |
"firstport value of %u makes companion take ports %u - %u, which " |
|
806 |
"is outside of the valid range of 0 - %u\n", firstport, firstport, |
|
807 |
firstport + portcount - 1, NB_PORTS - 1); |
|
808 |
return -1; |
|
809 |
} |
|
810 |
|
|
811 |
for (i = 0; i < portcount; i++) { |
|
812 |
if (s->companion_ports[firstport + i]) { |
|
813 |
qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus", |
|
814 |
"an USB masterbus"); |
|
815 |
error_printf_unless_qmp( |
|
816 |
"port %u on masterbus %s already has a companion assigned\n", |
|
817 |
firstport + i, bus->qbus.name); |
|
818 |
return -1; |
|
819 |
} |
|
820 |
} |
|
821 |
|
|
822 |
for (i = 0; i < portcount; i++) { |
|
823 |
s->companion_ports[firstport + i] = ports[i]; |
|
824 |
s->ports[firstport + i].speedmask |= |
|
825 |
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL; |
|
826 |
/* Ensure devs attached before the initial reset go to the companion */ |
|
827 |
s->portsc[firstport + i] = PORTSC_POWNER; |
|
828 |
} |
|
829 |
|
|
830 |
s->companion_count++; |
|
831 |
s->mmio[0x05] = (s->companion_count << 4) | portcount; |
|
832 |
|
|
833 |
return 0; |
|
834 |
} |
|
835 |
|
|
775 | 836 |
/* 4.1 host controller initialization */ |
776 | 837 |
static void ehci_reset(void *opaque) |
777 | 838 |
{ |
778 | 839 |
EHCIState *s = opaque; |
779 | 840 |
int i; |
841 |
USBDevice *devs[NB_PORTS]; |
|
780 | 842 |
|
781 | 843 |
trace_usb_ehci_reset(); |
782 | 844 |
|
845 |
/* |
|
846 |
* Do the detach before touching portsc, so that it correctly gets send to |
|
847 |
* us or to our companion based on PORTSC_POWNER before the reset. |
|
848 |
*/ |
|
849 |
for(i = 0; i < NB_PORTS; i++) { |
|
850 |
devs[i] = s->ports[i].dev; |
|
851 |
if (devs[i]) { |
|
852 |
usb_attach(&s->ports[i], NULL); |
|
853 |
} |
|
854 |
} |
|
855 |
|
|
783 | 856 |
memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE); |
784 | 857 |
|
785 | 858 |
s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH; |
... | ... | |
791 | 864 |
s->attach_poll_counter = 0; |
792 | 865 |
|
793 | 866 |
for(i = 0; i < NB_PORTS; i++) { |
794 |
s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; |
|
795 |
|
|
796 |
if (s->ports[i].dev) { |
|
797 |
usb_attach(&s->ports[i], s->ports[i].dev); |
|
867 |
if (s->companion_ports[i]) { |
|
868 |
s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; |
|
869 |
} else { |
|
870 |
s->portsc[i] = PORTSC_PPOWER; |
|
871 |
} |
|
872 |
if (devs[i]) { |
|
873 |
usb_attach(&s->ports[i], devs[i]); |
|
798 | 874 |
} |
799 | 875 |
} |
800 | 876 |
ehci_queues_rip_all(s); |
... | ... | |
844 | 920 |
exit(1); |
845 | 921 |
} |
846 | 922 |
|
923 |
static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner) |
|
924 |
{ |
|
925 |
USBDevice *dev = s->ports[port].dev; |
|
926 |
uint32_t *portsc = &s->portsc[port]; |
|
927 |
uint32_t orig; |
|
928 |
|
|
929 |
if (s->companion_ports[port] == NULL) |
|
930 |
return; |
|
931 |
|
|
932 |
owner = owner & PORTSC_POWNER; |
|
933 |
orig = *portsc & PORTSC_POWNER; |
|
934 |
|
|
935 |
if (!(owner ^ orig)) { |
|
936 |
return; |
|
937 |
} |
|
938 |
|
|
939 |
if (dev) { |
|
940 |
usb_attach(&s->ports[port], NULL); |
|
941 |
} |
|
942 |
|
|
943 |
*portsc &= ~PORTSC_POWNER; |
|
944 |
*portsc |= owner; |
|
945 |
|
|
946 |
if (dev) { |
|
947 |
usb_attach(&s->ports[port], dev); |
|
948 |
} |
|
949 |
} |
|
950 |
|
|
847 | 951 |
static void handle_port_status_write(EHCIState *s, int port, uint32_t val) |
848 | 952 |
{ |
849 | 953 |
uint32_t *portsc = &s->portsc[port]; |
... | ... | |
853 | 957 |
*portsc &= ~(val & PORTSC_RWC_MASK); |
854 | 958 |
/* The guest may clear, but not set the PED bit */ |
855 | 959 |
*portsc &= val | ~PORTSC_PED; |
960 |
/* POWNER is masked out by RO_MASK as it is RO when we've no companion */ |
|
961 |
handle_port_owner_write(s, port, val); |
|
962 |
/* And finally apply RO_MASK */ |
|
856 | 963 |
val &= PORTSC_RO_MASK; |
857 | 964 |
|
858 | 965 |
if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) { |
... | ... | |
956 | 1063 |
val &= 0x1; |
957 | 1064 |
if (val) { |
958 | 1065 |
for(i = 0; i < NB_PORTS; i++) |
959 |
s->portsc[i] &= ~PORTSC_POWNER;
|
|
1066 |
handle_port_owner_write(s, i, 0);
|
|
960 | 1067 |
} |
961 | 1068 |
break; |
962 | 1069 |
|
... | ... | |
1114 | 1221 |
|
1115 | 1222 |
static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) |
1116 | 1223 |
{ |
1117 |
EHCIQueue *q = container_of(packet, EHCIQueue, packet); |
|
1224 |
EHCIQueue *q; |
|
1225 |
EHCIState *s = port->opaque; |
|
1226 |
uint32_t portsc = s->portsc[port->index]; |
|
1227 |
|
|
1228 |
if (portsc & PORTSC_POWNER) { |
|
1229 |
USBPort *companion = s->companion_ports[port->index]; |
|
1230 |
companion->ops->complete(companion, packet); |
|
1231 |
return; |
|
1232 |
} |
|
1118 | 1233 |
|
1234 |
q = container_of(packet, EHCIQueue, packet); |
|
1119 | 1235 |
trace_usb_ehci_queue_action(q, "wakeup"); |
1120 | 1236 |
assert(q->async == EHCI_ASYNC_INFLIGHT); |
1121 | 1237 |
q->async = EHCI_ASYNC_FINISHED; |
... | ... | |
1245 | 1361 |
port = &q->ehci->ports[i]; |
1246 | 1362 |
dev = port->dev; |
1247 | 1363 |
|
1248 |
// TODO sometime we will also need to check if we are the port owner |
|
1249 |
|
|
1250 | 1364 |
if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) { |
1251 | 1365 |
DPRINTF("Port %d, no exec, not connected(%08X)\n", |
1252 | 1366 |
i, q->ehci->portsc[i]); |
... | ... | |
1339 | 1453 |
port = &ehci->ports[j]; |
1340 | 1454 |
dev = port->dev; |
1341 | 1455 |
|
1342 |
// TODO sometime we will also need to check if we are the port owner |
|
1343 |
|
|
1344 | 1456 |
if (!(ehci->portsc[j] &(PORTSC_CONNECT))) { |
1345 | 1457 |
continue; |
1346 | 1458 |
} |
... | ... | |
2124 | 2236 |
.attach = ehci_attach, |
2125 | 2237 |
.detach = ehci_detach, |
2126 | 2238 |
.child_detach = ehci_child_detach, |
2239 |
.wakeup = ehci_wakeup, |
|
2127 | 2240 |
.complete = ehci_async_complete_packet, |
2128 | 2241 |
}; |
2129 | 2242 |
|
2130 | 2243 |
static USBBusOps ehci_bus_ops = { |
2244 |
.register_companion = ehci_register_companion, |
|
2131 | 2245 |
}; |
2132 | 2246 |
|
2133 | 2247 |
static PCIDeviceInfo ehci_info = { |
Also available in: Unified diff