mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2025-02-25 18:55:23 -06:00
Implement EHCI handover to companion controllers.
Low/full speed USB devices attached directly to the root hub must be rerouted to a companion controller. We can't rely on the BIOS to do this for us. This requires us to initialise the EHCI device before initialising any of its companions. This also allows us to support keyboards attached via a high speed hub on a system with EHCI plus companions.
This commit is contained in:
@@ -72,6 +72,12 @@
|
||||
#define EHCI_PORT_SC_PP 0x00001000 // Port Power
|
||||
#define EHCI_PORT_SC_PO 0x00002000 // Port Owner
|
||||
|
||||
#define EHCI_PORT_SC_LS_MASK 0x00000c00 // Line Status
|
||||
#define EHCI_PORT_SC_LS_SE0 0x00000000 // Line Status is SE0
|
||||
#define EHCI_PORT_SC_LS_K 0x00000400 // Line Status is K-state
|
||||
#define EHCI_PORT_SC_LS_J 0x00000800 // Line Status is J-state
|
||||
#define EHCI_PORT_SC_LS_U 0x00000c00 // Line Status is undefined
|
||||
|
||||
// Link Pointer
|
||||
|
||||
#define EHCI_LP_TERMINATE 0x00000001 // Terminate (T) bit
|
||||
@@ -245,6 +251,11 @@ int num_ehci_ports(uint32_t hcs_params)
|
||||
return (hcs_params >> 0) & 0xf;
|
||||
}
|
||||
|
||||
int num_ehci_companions(uint32_t hcs_params)
|
||||
{
|
||||
return (hcs_params >> 12) & 0xf;
|
||||
}
|
||||
|
||||
int ehci_ext_cap_ptr(uint32_t hcc_params)
|
||||
{
|
||||
return (hcc_params >> 8) & 0xff;
|
||||
@@ -305,6 +316,12 @@ static void disable_ehci_port(ehci_op_regs_t *op_regs, int port_idx)
|
||||
(void)wait_until_clr(&op_regs->port_sc[port_idx], EHCI_PORT_SC_PED, 1000*MILLISEC);
|
||||
}
|
||||
|
||||
static void release_ehci_port(ehci_op_regs_t *op_regs, int port_idx)
|
||||
{
|
||||
uint32_t port_status = read32(&op_regs->port_sc[port_idx]);
|
||||
write32(&op_regs->port_sc[port_idx], port_status | EHCI_PORT_SC_PO);
|
||||
}
|
||||
|
||||
static void build_ehci_qtd(ehci_qtd_t *this_qtd, const ehci_qtd_t *final_qtd, uint8_t control, uint16_t dt,
|
||||
const void *buffer, size_t length)
|
||||
{
|
||||
@@ -589,10 +606,13 @@ bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
|
||||
usleep(100*MILLISEC); // USB maximum device attach time
|
||||
|
||||
bool i_have_companions = (num_ehci_companions(hcs_params) > 0);
|
||||
|
||||
// Scan the ports, looking for hubs and keyboards.
|
||||
usb_ep_t keyboards[MAX_KEYBOARDS];
|
||||
int num_keyboards = 0;
|
||||
int num_devices = 0;
|
||||
int num_ls_devices = 0;
|
||||
int num_hs_devices = 0;
|
||||
for (int port_idx = 0; port_idx < root_hub.num_ports; port_idx++) {
|
||||
// If we've filled the keyboard info table, abort now.
|
||||
if (num_keyboards >= MAX_KEYBOARDS) break;
|
||||
@@ -605,6 +625,15 @@ bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
// Check if anything is connected to this port.
|
||||
if (~port_status & EHCI_PORT_SC_CCS) continue;
|
||||
|
||||
// Check for low speed device.
|
||||
if ((port_status & EHCI_PORT_SC_LS_MASK) == EHCI_PORT_SC_LS_K) {
|
||||
if (i_have_companions) {
|
||||
release_ehci_port(op_regs, port_idx);
|
||||
}
|
||||
num_ls_devices++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reset the port.
|
||||
if (!reset_ehci_port(op_regs, port_idx)) continue;
|
||||
|
||||
@@ -612,14 +641,20 @@ bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
|
||||
port_status = read32(&op_regs->port_sc[port_idx]);
|
||||
|
||||
// Check the port is active.
|
||||
if (~port_status & EHCI_PORT_SC_PED) continue;
|
||||
// Check for full speed device.
|
||||
if (~port_status & EHCI_PORT_SC_PED) {
|
||||
if (i_have_companions) {
|
||||
release_ehci_port(op_regs, port_idx);
|
||||
}
|
||||
num_ls_devices++;
|
||||
continue;
|
||||
}
|
||||
|
||||
num_devices++;
|
||||
num_hs_devices++;
|
||||
|
||||
// Look for keyboards attached directly or indirectly to this port.
|
||||
if (find_attached_usb_keyboards(hcd, &root_hub, 1 + port_idx, USB_SPEED_HIGH, num_devices,
|
||||
&num_devices, keyboards, MAX_KEYBOARDS, &num_keyboards)) {
|
||||
if (find_attached_usb_keyboards(hcd, &root_hub, 1 + port_idx, USB_SPEED_HIGH, num_hs_devices,
|
||||
&num_hs_devices, keyboards, MAX_KEYBOARDS, &num_keyboards)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -627,9 +662,13 @@ bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
disable_ehci_port(op_regs, port_idx);
|
||||
}
|
||||
|
||||
print_usb_info(" Found %i device%s, %i keyboard%s",
|
||||
num_devices, num_devices != 1 ? "s" : "",
|
||||
num_keyboards, num_keyboards != 1 ? "s" : "");
|
||||
print_usb_info(" Found %i low/full speed device%s, %i high speed device%s, %i keyboard%s",
|
||||
num_ls_devices, num_ls_devices != 1 ? "s" : "",
|
||||
num_hs_devices, num_hs_devices != 1 ? "s" : "",
|
||||
num_keyboards, num_keyboards != 1 ? "s" : "");
|
||||
if (num_ls_devices > 0 && i_have_companions) {
|
||||
print_usb_info(" Handed over low/full speed devices to companion controllers");
|
||||
}
|
||||
|
||||
if (num_keyboards == 0) {
|
||||
// Halt the host controller.
|
||||
|
||||
188
system/usbhcd.c
188
system/usbhcd.c
@@ -30,6 +30,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef enum {
|
||||
NOT_HCI = -1,
|
||||
UHCI = 0,
|
||||
OHCI = 1,
|
||||
EHCI = 2,
|
||||
@@ -583,6 +584,86 @@ bool find_attached_usb_keyboards(const usb_hcd_t *hcd, const usb_hub_t *hub, int
|
||||
return keyboard_found;
|
||||
}
|
||||
|
||||
static void probe_usb_controller(int bus, int dev, int func, hci_type_t controller_type)
|
||||
{
|
||||
uint16_t vendor_id = pci_config_read16(bus, dev, func, 0x00);
|
||||
uint16_t device_id = pci_config_read16(bus, dev, func, 0x02);
|
||||
uint16_t pci_status = pci_config_read16(bus, dev, func, 0x06);
|
||||
|
||||
// Disable the device while we probe it.
|
||||
uint16_t control = pci_config_read16(bus, dev, func, 0x04);
|
||||
pci_config_write16(bus, dev, func, 0x04, control & ~0x0007);
|
||||
|
||||
int bar = (controller_type == UHCI) ? 0x20 : 0x10;
|
||||
uintptr_t base_addr = pci_config_read32(bus, dev, func, bar);
|
||||
pci_config_write32(bus, dev, func, bar, 0xffffffff);
|
||||
uintptr_t mmio_size = pci_config_read32(bus, dev, func, bar);
|
||||
pci_config_write32(bus, dev, func, bar, base_addr);
|
||||
#ifdef __x86_64__
|
||||
if (base_addr & 0x4) {
|
||||
base_addr += (uintptr_t)pci_config_read32(bus, dev, func, bar + 4) << 32;
|
||||
pci_config_write32(bus, dev, func, bar + 4, 0xffffffff);
|
||||
mmio_size += (uintptr_t)pci_config_read32(bus, dev, func, bar + 4) << 32;
|
||||
pci_config_write32(bus, dev, func, bar + 4, base_addr >> 32);
|
||||
} else {
|
||||
mmio_size += (uintptr_t)0xffffffff << 32;
|
||||
}
|
||||
#endif
|
||||
base_addr &= ~(uintptr_t)0xf;
|
||||
mmio_size &= ~(uintptr_t)0xf;
|
||||
mmio_size = ~mmio_size + 1;
|
||||
|
||||
print_usb_info("Found %s controller %04x:%04x at %08x size %08x", hci_name[controller_type],
|
||||
(uintptr_t)vendor_id, (uintptr_t)device_id, base_addr, mmio_size);
|
||||
|
||||
base_addr = map_device(base_addr, mmio_size);
|
||||
if (base_addr == 0) {
|
||||
print_usb_info(" Failed to map device into virtual memory");
|
||||
return;
|
||||
}
|
||||
|
||||
// Search for power management capability.
|
||||
//uint8_t pm_cap_ptr;
|
||||
if (pci_status & 0x10) {
|
||||
uint8_t cap_ptr = pci_config_read8(bus, dev, func, 0x34) & 0xfe;
|
||||
while (cap_ptr != 0) {
|
||||
uint8_t cap_id = pci_config_read8(bus, dev, func, cap_ptr);
|
||||
if (cap_id == 1) {
|
||||
uint16_t pm_status = pci_config_read16(bus, dev, func, cap_ptr+2);
|
||||
// Power on if necessary.
|
||||
if ((pm_status & 0x3) != 0) {
|
||||
pci_config_write16(bus, dev, func, cap_ptr+2, 0x8000);
|
||||
usleep(10000);
|
||||
}
|
||||
//pm_cap_ptr = cap_ptr;
|
||||
break;
|
||||
}
|
||||
cap_ptr = pci_config_read8(bus, dev, func, cap_ptr+1) & 0xfe;
|
||||
}
|
||||
}
|
||||
|
||||
// Enable the device.
|
||||
pci_config_write16(bus, dev, func, 0x04, control | 0x0007);
|
||||
|
||||
// Initialise the device according to its type.
|
||||
bool keyboards_found = false;
|
||||
if (controller_type == UHCI) {
|
||||
print_usb_info(" This controller type is not supported yet");
|
||||
}
|
||||
if (controller_type == OHCI) {
|
||||
keyboards_found = ohci_init(base_addr, &usb_controllers[num_usb_controllers]);
|
||||
}
|
||||
if (controller_type == EHCI) {
|
||||
keyboards_found = ehci_init(bus, dev, func, base_addr, &usb_controllers[num_usb_controllers]);
|
||||
}
|
||||
if (controller_type == XHCI) {
|
||||
keyboards_found = xhci_init(base_addr, &usb_controllers[num_usb_controllers]);
|
||||
}
|
||||
if (keyboards_found) {
|
||||
num_usb_controllers++;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -595,100 +676,28 @@ void find_usb_keyboards(bool pause_at_end)
|
||||
num_usb_controllers = 0;
|
||||
for (int bus = 0; bus < PCI_MAX_BUS; bus++) {
|
||||
for (int dev = 0; dev < PCI_MAX_DEV; dev++) {
|
||||
hci_type_t controller_type[PCI_MAX_FUNC];
|
||||
for (int func = 0; func < PCI_MAX_FUNC; func++) {
|
||||
controller_type[func] = NOT_HCI;
|
||||
}
|
||||
for (int func = 0; func < PCI_MAX_FUNC; func++) {
|
||||
uint16_t vendor_id = pci_config_read16(bus, dev, func, 0x00);
|
||||
|
||||
// Test for device/function present.
|
||||
uint16_t vendor_id = pci_config_read16(bus, dev, func, 0x00);
|
||||
uint8_t hdr_type = pci_config_read8 (bus, dev, func, 0x0e);
|
||||
if (vendor_id != 0xffff) {
|
||||
uint16_t device_id = pci_config_read16(bus, dev, func, 0x02);
|
||||
uint16_t pci_status = pci_config_read16(bus, dev, func, 0x06);
|
||||
uint16_t class_code = pci_config_read16(bus, dev, func, 0x0a);
|
||||
uint8_t hdr_type = pci_config_read8 (bus, dev, func, 0x0e);
|
||||
|
||||
// Test for a USB controller.
|
||||
uint16_t class_code = pci_config_read16(bus, dev, func, 0x0a);
|
||||
if (class_code == 0x0c03) {
|
||||
// Disable the device while we probe it.
|
||||
uint16_t control = pci_config_read16(bus, dev, func, 0x04);
|
||||
pci_config_write16(bus, dev, func, 0x04, control & ~0x0007);
|
||||
|
||||
hci_type_t controller_type = pci_config_read8(bus, dev, func, 0x09) >> 4;
|
||||
if (controller_type >= MAX_HCI_TYPE) break;
|
||||
|
||||
int bar = (controller_type == UHCI) ? 0x20 : 0x10;
|
||||
uintptr_t base_addr = pci_config_read32(bus, dev, func, bar);
|
||||
pci_config_write32(bus, dev, func, bar, 0xffffffff);
|
||||
uintptr_t mmio_size = pci_config_read32(bus, dev, func, bar);
|
||||
pci_config_write32(bus, dev, func, bar, base_addr);
|
||||
#ifdef __x86_64__
|
||||
if (base_addr & 0x4) {
|
||||
base_addr += (uintptr_t)pci_config_read32(bus, dev, func, bar + 4) << 32;
|
||||
pci_config_write32(bus, dev, func, bar + 4, 0xffffffff);
|
||||
mmio_size += (uintptr_t)pci_config_read32(bus, dev, func, bar + 4) << 32;
|
||||
pci_config_write32(bus, dev, func, bar + 4, base_addr >> 32);
|
||||
} else {
|
||||
mmio_size += (uintptr_t)0xffffffff << 32;
|
||||
}
|
||||
#endif
|
||||
base_addr &= ~(uintptr_t)0xf;
|
||||
mmio_size &= ~(uintptr_t)0xf;
|
||||
mmio_size = ~mmio_size + 1;
|
||||
|
||||
print_usb_info("Found %s controller %04x:%04x at %08x size %08x", hci_name[controller_type],
|
||||
(uintptr_t)vendor_id, (uintptr_t)device_id, base_addr, mmio_size);
|
||||
|
||||
base_addr = map_device(base_addr, mmio_size);
|
||||
if (base_addr == 0) {
|
||||
print_usb_info(" Failed to map device into virtual memory");
|
||||
break;
|
||||
}
|
||||
|
||||
// Search for power management capability.
|
||||
//uint8_t pm_cap_ptr;
|
||||
if (pci_status & 0x10) {
|
||||
uint8_t cap_ptr = pci_config_read8(bus, dev, func, 0x34) & 0xfe;
|
||||
while (cap_ptr != 0) {
|
||||
uint8_t cap_id = pci_config_read8(bus, dev, func, cap_ptr);
|
||||
if (cap_id == 1) {
|
||||
uint16_t pm_status = pci_config_read16(bus, dev, func, cap_ptr+2);
|
||||
// Power on if necessary.
|
||||
if ((pm_status & 0x3) != 0) {
|
||||
pci_config_write16(bus, dev, func, cap_ptr+2, 0x8000);
|
||||
usleep(10000);
|
||||
}
|
||||
//pm_cap_ptr = cap_ptr;
|
||||
break;
|
||||
}
|
||||
cap_ptr = pci_config_read8(bus, dev, func, cap_ptr+1) & 0xfe;
|
||||
}
|
||||
}
|
||||
|
||||
// Enable the device.
|
||||
pci_config_write16(bus, dev, func, 0x04, control | 0x0007);
|
||||
|
||||
// Initialise the device according to its type.
|
||||
bool keyboards_found = false;
|
||||
if (controller_type == UHCI) {
|
||||
print_usb_info(" This controller type is not supported yet");
|
||||
}
|
||||
if (controller_type == OHCI) {
|
||||
keyboards_found = ohci_init(base_addr, &usb_controllers[num_usb_controllers]);
|
||||
}
|
||||
if (controller_type == EHCI) {
|
||||
if (func == 0) {
|
||||
keyboards_found = ehci_init(bus, dev, func, base_addr, &usb_controllers[num_usb_controllers]);
|
||||
} else {
|
||||
print_usb_info(" Skipping");
|
||||
}
|
||||
}
|
||||
if (controller_type == XHCI) {
|
||||
keyboards_found = xhci_init(base_addr, &usb_controllers[num_usb_controllers]);
|
||||
}
|
||||
if (keyboards_found) {
|
||||
num_usb_controllers++;
|
||||
controller_type[func] = pci_config_read8(bus, dev, func, 0x09) >> 4;
|
||||
// We need to initialise EHCI controllers before initialising any of their companion
|
||||
// controllers, so do it now.
|
||||
if (controller_type[func] == EHCI) {
|
||||
probe_usb_controller(bus, dev, func, controller_type[func]);
|
||||
// If we've filled the controller table, abort now.
|
||||
if (num_usb_controllers == MAX_USB_CONTROLLERS) {
|
||||
return;
|
||||
}
|
||||
controller_type[func] = NOT_HCI; // prevent reprobing
|
||||
}
|
||||
}
|
||||
// Break out if this is a single function device.
|
||||
@@ -702,6 +711,15 @@ void find_usb_keyboards(bool pause_at_end)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int func = 0; func < PCI_MAX_FUNC; func++) {
|
||||
if (controller_type[func] != NOT_HCI) {
|
||||
probe_usb_controller(bus, dev, func, controller_type[func]);
|
||||
// If we've filled the controller table, abort now.
|
||||
if (num_usb_controllers == MAX_USB_CONTROLLERS) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user