summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/include/cpu/x86/smm.h1
-rw-r--r--src/southbridge/intel/lynxpoint/Kconfig7
-rw-r--r--src/southbridge/intel/lynxpoint/pch.h1
-rw-r--r--src/southbridge/intel/lynxpoint/smihandler.c7
-rw-r--r--src/southbridge/intel/lynxpoint/usb_xhci.c102
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,