diff options
-rw-r--r-- | src/include/cpu/x86/smm.h | 1 | ||||
-rw-r--r-- | src/southbridge/intel/lynxpoint/Kconfig | 7 | ||||
-rw-r--r-- | src/southbridge/intel/lynxpoint/pch.h | 1 | ||||
-rw-r--r-- | src/southbridge/intel/lynxpoint/smihandler.c | 7 | ||||
-rw-r--r-- | src/southbridge/intel/lynxpoint/usb_xhci.c | 102 |
5 files changed, 108 insertions, 10 deletions
diff --git a/src/include/cpu/x86/smm.h b/src/include/cpu/x86/smm.h index 607c0f0a0d..feb50ecb36 100644 --- a/src/include/cpu/x86/smm.h +++ b/src/include/cpu/x86/smm.h @@ -372,6 +372,7 @@ typedef struct { #define APM_CNT_ACPI_ENABLE 0xe1 #define APM_CNT_MBI_UPDATE 0xeb #define APM_CNT_GNVS_UPDATE 0xea +#define APM_CNT_FINALIZE 0xcb #define APM_STS 0xb3 /* SMI handler function prototypes */ diff --git a/src/southbridge/intel/lynxpoint/Kconfig b/src/southbridge/intel/lynxpoint/Kconfig index 28ebdb6c2f..5ff00db1e6 100644 --- a/src/southbridge/intel/lynxpoint/Kconfig +++ b/src/southbridge/intel/lynxpoint/Kconfig @@ -76,4 +76,11 @@ config ME_MBP_CLEAR_LATE finalize step. This can speed up boot time if the ME takes a long time to indicate this status. +config FINALIZE_USB_ROUTE_XHCI + bool "Route all ports to XHCI controller in finalize step" + default y + help + If you set this option to y, the USB ports will be routed + to the XHCI controller during the finalize SMM callback. + endif diff --git a/src/southbridge/intel/lynxpoint/pch.h b/src/southbridge/intel/lynxpoint/pch.h index c16c009038..6e1b10ca86 100644 --- a/src/southbridge/intel/lynxpoint/pch.h +++ b/src/southbridge/intel/lynxpoint/pch.h @@ -92,6 +92,7 @@ void intel_pch_finalize_smm(void); void usb_ehci_sleep_prepare(device_t dev, u8 slp_typ); void usb_ehci_disable(device_t dev); void usb_xhci_sleep_prepare(device_t dev, u8 slp_typ); +void usb_xhci_route_all(void); #endif diff --git a/src/southbridge/intel/lynxpoint/smihandler.c b/src/southbridge/intel/lynxpoint/smihandler.c index e920cfe3e2..d1e9bbc7cd 100644 --- a/src/southbridge/intel/lynxpoint/smihandler.c +++ b/src/southbridge/intel/lynxpoint/smihandler.c @@ -135,8 +135,10 @@ static void southbridge_smi_sleep(void) mainboard_smi_sleep(slp_typ-2); /* USB sleep preparations */ +#if !CONFIG_FINALIZE_USB_ROUTE_XHCI usb_ehci_sleep_prepare(PCH_EHCI1_DEV, slp_typ); usb_ehci_sleep_prepare(PCH_EHCI2_DEV, slp_typ); +#endif usb_xhci_sleep_prepare(PCH_XHCI_DEV, slp_typ); #if CONFIG_ELOG_GSMI @@ -314,6 +316,11 @@ static void southbridge_smi_apmc(void) printk(BIOS_DEBUG, "SMI#: Setting GNVS to %p\n", gnvs); } break; + case APM_CNT_FINALIZE: +#if CONFIG_FINALIZE_USB_ROUTE_XHCI + usb_xhci_route_all(); +#endif + break; #if CONFIG_ELOG_GSMI case ELOG_GSMI_APM_CNT: southbridge_smi_gsmi(); diff --git a/src/southbridge/intel/lynxpoint/usb_xhci.c b/src/southbridge/intel/lynxpoint/usb_xhci.c index df264e31f4..dbf0f4095d 100644 --- a/src/southbridge/intel/lynxpoint/usb_xhci.c +++ b/src/southbridge/intel/lynxpoint/usb_xhci.c @@ -26,8 +26,6 @@ #include <arch/io.h> #include "pch.h" -#ifdef __SMM__ - static u32 usb_xhci_mem_base(device_t dev) { u32 mem_base = pci_read_config32(dev, PCI_BASE_ADDRESS_0); @@ -39,10 +37,6 @@ static u32 usb_xhci_mem_base(device_t dev) return mem_base & ~0xf; } -#endif - -#if 0 - static int usb_xhci_port_count_usb3(device_t dev) { if (pch_is_lp()) { @@ -76,6 +70,8 @@ static void usb_xhci_reset_port_usb3(u32 mem_base, int port) write32(portsc, read32(portsc) | XHCI_USB3_PORTSC_WPR); } +#ifdef __SMM__ + #define XHCI_RESET_DELAY_US 1000 /* 1ms */ #define XHCI_RESET_TIMEOUT 100 /* 100ms */ @@ -155,10 +151,6 @@ static void usb_xhci_reset_usb3(device_t dev, int all) usb_xhci_reset_status_usb3(mem_base, port); } -#endif - -#ifdef __SMM__ - /* Handler for XHCI controller on entry to S3/S4/S5 */ void usb_xhci_sleep_prepare(device_t dev, u8 slp_typ) { @@ -197,6 +189,47 @@ void usb_xhci_sleep_prepare(device_t dev, u8 slp_typ) pci_or_config16(dev, XHCI_PWR_CTL_STS, PWR_CTL_ENABLE_PME); } +/* Route all ports to XHCI controller */ +void usb_xhci_route_all(void) +{ + u32 port_mask, route; + u16 reg16; + + /* Skip if EHCI is already disabled */ + if (RCBA32(FD) & PCH_DISABLE_EHCI1) + return; + + /* Set D0 state */ + reg16 = pci_read_config16(PCH_XHCI_DEV, XHCI_PWR_CTL_STS); + reg16 &= ~PWR_CTL_SET_MASK; + reg16 |= PWR_CTL_SET_D0; + pci_write_config16(PCH_XHCI_DEV, XHCI_PWR_CTL_STS, reg16); + + /* Set USB3 superspeed enable */ + port_mask = pci_read_config32(PCH_XHCI_DEV, XHCI_USB3PRM); + route = pci_read_config32(PCH_XHCI_DEV, XHCI_USB3PR); + route &= ~XHCI_USB3PR_SSEN; + route |= XHCI_USB3PR_SSEN & port_mask; + pci_write_config32(PCH_XHCI_DEV, XHCI_USB3PR, route); + + /* Route USB2 ports to XHCI controller */ + port_mask = pci_read_config32(PCH_XHCI_DEV, XHCI_USB2PRM); + route = pci_read_config32(PCH_XHCI_DEV, XHCI_USB2PR); + route &= ~XHCI_USB2PR_HCSEL; + route |= XHCI_USB2PR_HCSEL & port_mask; + pci_write_config32(PCH_XHCI_DEV, XHCI_USB2PR, route); + + /* Disable EHCI controller */ + usb_ehci_disable(PCH_EHCI1_DEV); + + /* LynxPoint-H has a second EHCI controller */ + if (!pch_is_lp()) + usb_ehci_disable(PCH_EHCI2_DEV); + + /* Reset and clear port change status */ + usb_xhci_reset_usb3(PCH_XHCI_DEV, 1); +} + #else /* !__SMM__ */ static void usb_xhci_clock_gating(device_t dev) @@ -245,6 +278,49 @@ static void usb_xhci_clock_gating(device_t dev) pci_write_config32(dev, 0xa4, reg32); } +/* Re-enable ports that are disabled */ +static void usb_xhci_enable_ports_usb3(device_t dev) +{ +#if CONFIG_FINALIZE_USB_ROUTE_XHCI + int port; + u32 portsc, status, disabled; + u32 mem_base = usb_xhci_mem_base(dev); + int port_count = usb_xhci_port_count_usb3(dev); + + if (!mem_base || !port_count) + return; + + /* Get port disable override map */ + disabled = pci_read_config32(dev, XHCI_USB3PDO); + + for (port = 0; port < port_count; port++) { + /* Skip overridden ports */ + if (disabled & (1 << port)) + continue; + portsc = mem_base + XHCI_USB3_PORTSC(port); + status = read32(portsc) & XHCI_USB3_PORTSC_PLS; + + switch (status) { + case XHCI_PLSR_RXDETECT: + /* Clear change status */ + printk(BIOS_DEBUG, "usb_xhci reset port %d\n", port); + usb_xhci_reset_status_usb3(mem_base, port); + break; + case XHCI_PLSR_DISABLED: + default: + /* Transition to enabled */ + printk(BIOS_DEBUG, "usb_xhci enable port %d\n", port); + usb_xhci_reset_port_usb3(mem_base, port); + status = read32(portsc); + status &= ~XHCI_USB3_PORTSC_PLS; + status |= XHCI_PLSW_ENABLE | XHCI_USB3_PORTSC_LWS; + write32(portsc, status); + break; + } + } +#endif +} + static void usb_xhci_init(device_t dev) { struct resource *bar0 = find_resource(dev, PCI_BASE_ADDRESS_0); @@ -313,6 +389,12 @@ static void usb_xhci_init(device_t dev) reg32 &= ~(1 << 23); /* unsupported request */ reg32 |= (1 << 31); pci_write_config32(dev, 0x40, reg32); + +#if CONFIG_HAVE_ACPI_RESUME + /* Enable ports that are disabled before returning to OS */ + if (acpi_slp_type == 3) + usb_xhci_enable_ports_usb3(dev); +#endif } static void usb_xhci_set_subsystem(device_t dev, unsigned vendor, |