summaryrefslogtreecommitdiff
path: root/src/southbridge/intel/lynxpoint
diff options
context:
space:
mode:
Diffstat (limited to 'src/southbridge/intel/lynxpoint')
-rw-r--r--src/southbridge/intel/lynxpoint/Makefile.inc2
-rw-r--r--src/southbridge/intel/lynxpoint/pch.h51
-rw-r--r--src/southbridge/intel/lynxpoint/smihandler.c97
-rw-r--r--src/southbridge/intel/lynxpoint/usb_ehci.c115
-rw-r--r--src/southbridge/intel/lynxpoint/usb_xhci.c176
5 files changed, 340 insertions, 101 deletions
diff --git a/src/southbridge/intel/lynxpoint/Makefile.inc b/src/southbridge/intel/lynxpoint/Makefile.inc
index 1689132510..7be2d03bb4 100644
--- a/src/southbridge/intel/lynxpoint/Makefile.inc
+++ b/src/southbridge/intel/lynxpoint/Makefile.inc
@@ -49,7 +49,7 @@ smm-$(CONFIG_SPI_FLASH_SMM) += spi.c
ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smi.c pmutil.c
smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c me_9.x.c finalize.c pch.c
-smm-$(CONFIG_HAVE_SMI_HANDLER) += pmutil.c
+smm-$(CONFIG_HAVE_SMI_HANDLER) += pmutil.c usb_ehci.c usb_xhci.c
romstage-y += early_usb.c early_smbus.c early_me.c me_status.c early_pch.c
romstage-y += reset.c early_spi.c rcba.c
diff --git a/src/southbridge/intel/lynxpoint/pch.h b/src/southbridge/intel/lynxpoint/pch.h
index 32c7d9bd01..c16c009038 100644
--- a/src/southbridge/intel/lynxpoint/pch.h
+++ b/src/southbridge/intel/lynxpoint/pch.h
@@ -89,6 +89,9 @@
#if defined (__SMM__) && !defined(__ASSEMBLER__)
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);
#endif
@@ -356,11 +359,49 @@ int early_pch_init(const void *gpio_map,
#define SATA_DTLE_DATA_SHIFT 24
#define SATA_DTLE_EDGE_SHIFT 16
-/* USB Registers */
-#define EHCI_PWR_CNTL_STS 0x54
-#define EHCI_PWR_STS_MASK 0x3
-#define EHCI_PWR_STS_SET_D0 0x0
-#define EHCI_PWR_STS_SET_D3 0x3
+/* EHCI PCI Registers */
+#define EHCI_PWR_CTL_STS 0x54
+#define PWR_CTL_SET_MASK 0x3
+#define PWR_CTL_SET_D0 0x0
+#define PWR_CTL_SET_D3 0x3
+#define PWR_CTL_ENABLE_PME (1 << 8)
+
+/* EHCI Memory Registers */
+#define EHCI_USB_CMD 0x20
+#define EHCI_USB_CMD_RUN (1 << 0)
+#define EHCI_USB_CMD_PSE (1 << 4)
+#define EHCI_USB_CMD_ASE (1 << 5)
+#define EHCI_PORTSC(port) (0x64 + (port * 4))
+#define EHCI_PORTSC_ENABLED (1 << 2)
+#define EHCI_PORTSC_SUSPEND (1 << 7)
+
+/* XHCI PCI Registers */
+#define XHCI_PWR_CTL_STS 0x74
+#define XHCI_USB2PR 0xd0
+#define XHCI_USB2PRM 0xd4
+#define XHCI_USB2PR_HCSEL 0x7fff
+#define XHCI_USB3PR 0xd8
+#define XHCI_USB3PR_SSEN 0x3f
+#define XHCI_USB3PRM 0xdc
+#define XHCI_USB3FUS 0xe0
+#define XHCI_USB3FUS_SS_MASK 3
+#define XHCI_USB3FUS_SS_SHIFT 3
+#define XHCI_USB3PDO 0xe8
+
+/* XHCI Memory Registers */
+#define XHCI_USB3_PORTSC(port) ((pch_is_lp() ? 0x510 : 0x570) + (port * 0x10))
+#define XHCI_USB3_PORTSC_CHST (0x7f << 17)
+#define XHCI_USB3_PORTSC_WCE (1 << 25) /* Wake on Connect */
+#define XHCI_USB3_PORTSC_WDE (1 << 26) /* Wake on Disconnect */
+#define XHCI_USB3_PORTSC_WOE (1 << 27) /* Wake on Overcurrent */
+#define XHCI_USB3_PORTSC_WRC (1 << 19) /* Warm Reset Complete */
+#define XHCI_USB3_PORTSC_LWS (1 << 16) /* Link Write Strobe */
+#define XHCI_USB3_PORTSC_WPR (1 << 31) /* Warm Port Reset */
+#define XHCI_USB3_PORTSC_PLS (0xf << 5) /* Port Link State */
+#define XHCI_PLSR_DISABLED (4 << 5) /* Port is disabled */
+#define XHCI_PLSR_RXDETECT (5 << 5) /* Port is disconnected */
+#define XHCI_PLSR_POLLING (7 << 5) /* Port is polling */
+#define XHCI_PLSW_ENABLE (5 << 5) /* Transition from disabled */
/* Serial IO IOBP Registers */
#define SIO_IOBP_PORTCTRL0 0xcb000000 /* SDIO D23:F0 */
diff --git a/src/southbridge/intel/lynxpoint/smihandler.c b/src/southbridge/intel/lynxpoint/smihandler.c
index add53ed5a9..e920cfe3e2 100644
--- a/src/southbridge/intel/lynxpoint/smihandler.c
+++ b/src/southbridge/intel/lynxpoint/smihandler.c
@@ -106,97 +106,6 @@ static void busmaster_disable_on_bus(int bus)
}
}
-/* Handler for EHCI controller on entry to S3/S4/S5 */
-static void ehci_sleep_prepare(device_t dev, u8 slp_typ)
-{
- u32 reg32;
- u32 bar0_base;
- u16 pwr_state;
- u16 pci_cmd;
-
- /* Check if the controller is disabled or not present */
- bar0_base = pci_read_config32(dev, PCI_BASE_ADDRESS_0);
- if (bar0_base == 0 || bar0_base == 0xffffffff)
- return;
- pci_cmd = pci_read_config32(dev, PCI_COMMAND);
-
- switch (slp_typ) {
- case SLP_TYP_S4:
- case SLP_TYP_S5:
- /* Check if controller is in D3 power state */
- pwr_state = pci_read_config16(dev, EHCI_PWR_CNTL_STS);
- if ((pwr_state & EHCI_PWR_STS_MASK) == EHCI_PWR_STS_SET_D3) {
- /* Put in D0 */
- pwr_state &= ~EHCI_PWR_STS_MASK;
- pwr_state |= EHCI_PWR_STS_SET_D0;
- pci_write_config16(dev, EHCI_PWR_CNTL_STS, pwr_state);
-
- /* Make sure memory bar is set */
- pci_write_config32(dev, PCI_BASE_ADDRESS_0, bar0_base);
-
- /* Make sure memory space is enabled */
- pci_write_config16(dev, PCI_COMMAND, pci_cmd |
- PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
- }
-
- /*
- * If Run/Stop (bit0) is clear in USB2.0_CMD:
- * - Clear Async Schedule Enable (bit5) and
- * - Clear Periodic Schedule Enable (bit4) and
- * - Set Run/Stop (bit0)
- */
- reg32 = read32(bar0_base + 0x20);
- if (reg32 & (1 << 0)) {
- reg32 &= ~((1 << 5) | (1 << 4));
- reg32 |= (1 << 0);
- write32(bar0_base + 0x20, reg32);
- }
-
- /* Check for Port Enabled in PORTSC */
- reg32 = read32(bar0_base + 0x64);
- if (reg32 & (1 << 2)) {
- /* Set suspend bit in PORTSC if not already set */
- if (!(reg32 & (1 << 7))) {
- reg32 |= (1 << 7);
- write32(bar0_base + 0x64, reg32);
- }
-
- /* Delay 25ms !! */
- udelay(25 * 1000);
-
- /* Clear Run/Stop bit */
- reg32 = read32(bar0_base + 0x20);
- reg32 &= (1 << 0);
- write32(bar0_base + 0x20, reg32);
- }
-
- pwr_state = pci_read_config16(dev, EHCI_PWR_CNTL_STS);
- if ((pwr_state & EHCI_PWR_STS_MASK) == EHCI_PWR_STS_SET_D3) {
- /* Restore pci command reg */
- pci_write_config16(dev, PCI_COMMAND, pci_cmd);
-
- /* Enable D3 */
- pwr_state |= EHCI_PWR_STS_SET_D3;
- pci_write_config16(dev, EHCI_PWR_CNTL_STS, pwr_state);
- }
- }
-}
-
-/* Handler for XHCI controller on entry to S3/S4/S5 */
-static void xhci_sleep_prepare(device_t dev, u8 slp_typ)
-{
- u16 reg16;
-
- switch (slp_typ) {
- case SLP_TYP_S3:
- case SLP_TYP_S4:
- case SLP_TYP_S5:
- /* Set D3Hot state and PME enable bit */
- reg16 = pci_read_config16(dev, 0x74);
- reg16 |= (1 << 8) | (1 << 1) | (1 << 0);
- pci_write_config16(dev, 0x74, reg16);
- }
-}
static void southbridge_smi_sleep(void)
{
@@ -226,9 +135,9 @@ static void southbridge_smi_sleep(void)
mainboard_smi_sleep(slp_typ-2);
/* USB sleep preparations */
- ehci_sleep_prepare(PCH_EHCI1_DEV, slp_typ);
- ehci_sleep_prepare(PCH_EHCI2_DEV, slp_typ);
- xhci_sleep_prepare(PCH_XHCI_DEV, slp_typ);
+ usb_ehci_sleep_prepare(PCH_EHCI1_DEV, slp_typ);
+ usb_ehci_sleep_prepare(PCH_EHCI2_DEV, slp_typ);
+ usb_xhci_sleep_prepare(PCH_XHCI_DEV, slp_typ);
#if CONFIG_ELOG_GSMI
/* Log S3, S4, and S5 entry */
diff --git a/src/southbridge/intel/lynxpoint/usb_ehci.c b/src/southbridge/intel/lynxpoint/usb_ehci.c
index f06151b138..4382611cf7 100644
--- a/src/southbridge/intel/lynxpoint/usb_ehci.c
+++ b/src/southbridge/intel/lynxpoint/usb_ehci.c
@@ -19,12 +19,123 @@
*/
#include <console/console.h>
+#include <delay.h>
#include <device/device.h>
#include <device/pci.h>
#include <device/pci_ids.h>
-#include "pch.h"
#include <usbdebug.h>
#include <arch/io.h>
+#include "pch.h"
+
+#ifdef __SMM__
+
+void usb_ehci_disable(device_t dev)
+{
+ u16 reg16;
+ u32 reg32;
+
+ /* Set 0xDC[0]=1 */
+ pci_or_config32(dev, 0xdc, (1 << 0));
+
+ /* Set D3Hot state and disable PME */
+ reg16 = pci_read_config16(dev, EHCI_PWR_CTL_STS);
+ reg16 &= ~(PWR_CTL_ENABLE_PME | PWR_CTL_SET_MASK);
+ reg16 |= PWR_CTL_SET_D3;
+ pci_write_config16(dev, EHCI_PWR_CTL_STS, reg16);
+
+ /* Clear memory and bus master */
+ pci_write_config32(dev, PCI_BASE_ADDRESS_0, 0);
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+
+ /* Disable device */
+ switch (dev) {
+ case PCH_EHCI1_DEV:
+ RCBA32_OR(FD, PCH_DISABLE_EHCI1);
+ break;
+ case PCH_EHCI2_DEV:
+ RCBA32_OR(FD, PCH_DISABLE_EHCI2);
+ break;
+ }
+}
+
+/* Handler for EHCI controller on entry to S3/S4/S5 */
+void usb_ehci_sleep_prepare(device_t dev, u8 slp_typ)
+{
+ u32 reg32;
+ u32 bar0_base;
+ u16 pwr_state;
+ u16 pci_cmd;
+
+ /* Check if the controller is disabled or not present */
+ bar0_base = pci_read_config32(dev, PCI_BASE_ADDRESS_0);
+ if (bar0_base == 0 || bar0_base == 0xffffffff)
+ return;
+ pci_cmd = pci_read_config32(dev, PCI_COMMAND);
+
+ switch (slp_typ) {
+ case SLP_TYP_S4:
+ case SLP_TYP_S5:
+ /* Check if controller is in D3 power state */
+ pwr_state = pci_read_config16(dev, EHCI_PWR_CTL_STS);
+ if ((pwr_state & PWR_CTL_SET_MASK) == PWR_CTL_SET_D3) {
+ /* Put in D0 */
+ u32 new_state = pwr_state & ~PWR_CTL_SET_MASK;
+ new_state |= PWR_CTL_SET_D0;
+ pci_write_config16(dev, EHCI_PWR_CTL_STS, new_state);
+
+ /* Make sure memory bar is set */
+ pci_write_config32(dev, PCI_BASE_ADDRESS_0, bar0_base);
+
+ /* Make sure memory space is enabled */
+ pci_write_config16(dev, PCI_COMMAND, pci_cmd |
+ PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
+ }
+
+ /*
+ * If Run/Stop (bit0) is clear in USB2.0_CMD:
+ * - Clear Async Schedule Enable (bit5) and
+ * - Clear Periodic Schedule Enable (bit4) and
+ * - Set Run/Stop (bit0)
+ */
+ reg32 = read32(bar0_base + EHCI_USB_CMD);
+ if (reg32 & EHCI_USB_CMD_RUN) {
+ reg32 &= ~(EHCI_USB_CMD_PSE | EHCI_USB_CMD_ASE);
+ reg32 |= EHCI_USB_CMD_RUN;
+ write32(bar0_base + EHCI_USB_CMD, reg32);
+ }
+
+ /* Check for Port Enabled in PORTSC(0) (RMH) */
+ reg32 = read32(bar0_base + EHCI_PORTSC(0));
+ if (reg32 & EHCI_PORTSC_ENABLED) {
+ /* Set suspend bit in PORTSC if not already set */
+ if (!(reg32 & EHCI_PORTSC_SUSPEND)) {
+ reg32 |= EHCI_PORTSC_SUSPEND;
+ write32(bar0_base + EHCI_PORTSC(0), reg32);
+ }
+
+ /* Delay 25ms !! */
+ udelay(25 * 1000);
+
+ /* Clear Run/Stop bit */
+ reg32 = read32(bar0_base + EHCI_USB_CMD);
+ reg32 &= EHCI_USB_CMD_RUN;
+ write32(bar0_base + EHCI_USB_CMD, reg32);
+ }
+
+ /* Restore state to D3 if that is what it was at the start */
+ if ((pwr_state & PWR_CTL_SET_MASK) == PWR_CTL_SET_D3) {
+ /* Restore pci command reg */
+ pci_write_config16(dev, PCI_COMMAND, pci_cmd);
+
+ /* Enable D3 */
+ pci_write_config16(dev, EHCI_PWR_CTL_STS, pwr_state);
+ }
+ }
+}
+
+#else /* !__SMM__ */
static void usb_ehci_clock_gating(struct device *dev)
{
@@ -98,3 +209,5 @@ static const struct pci_driver pch_usb_ehci __pci_driver = {
.vendor = PCI_VENDOR_ID_INTEL,
.devices = pci_device_ids,
};
+
+#endif /* !__SMM__ */
diff --git a/src/southbridge/intel/lynxpoint/usb_xhci.c b/src/southbridge/intel/lynxpoint/usb_xhci.c
index 33f33f4d70..df264e31f4 100644
--- a/src/southbridge/intel/lynxpoint/usb_xhci.c
+++ b/src/southbridge/intel/lynxpoint/usb_xhci.c
@@ -18,12 +18,187 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <console/console.h>
+#include <delay.h>
#include <device/device.h>
#include <device/pci.h>
#include <device/pci_ids.h>
#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);
+
+ /* Check if the controller is disabled or not present */
+ if (mem_base == 0 || mem_base == 0xffffffff)
+ return 0;
+
+ return mem_base & ~0xf;
+}
+
+#endif
+
+#if 0
+
+static int usb_xhci_port_count_usb3(device_t dev)
+{
+ if (pch_is_lp()) {
+ /* LynxPoint-LP has 4 SS ports */
+ return 4;
+ } else {
+ /* LynxPoint-H can have 0, 2, 4, or 6 SS ports */
+ u32 mem_base = usb_xhci_mem_base(dev);
+ u32 fus = read32(mem_base + XHCI_USB3FUS);
+ fus >>= XHCI_USB3FUS_SS_SHIFT;
+ fus &= XHCI_USB3FUS_SS_MASK;
+ switch (fus) {
+ case 3: return 0;
+ case 2: return 2;
+ case 1: return 4;
+ case 0: default: return 6;
+ }
+ }
+ return 0;
+}
+
+static void usb_xhci_reset_status_usb3(u32 mem_base, int port)
+{
+ u32 portsc = mem_base + XHCI_USB3_PORTSC(port);
+ write32(portsc, read32(portsc) | XHCI_USB3_PORTSC_CHST);
+}
+
+static void usb_xhci_reset_port_usb3(u32 mem_base, int port)
+{
+ u32 portsc = mem_base + XHCI_USB3_PORTSC(port);
+ write32(portsc, read32(portsc) | XHCI_USB3_PORTSC_WPR);
+}
+
+#define XHCI_RESET_DELAY_US 1000 /* 1ms */
+#define XHCI_RESET_TIMEOUT 100 /* 100ms */
+
+/*
+ * 1) Wait until port is done polling
+ * 2) If port is disconnected
+ * a) Issue warm port reset
+ * b) Poll for warm reset complete
+ * c) Write 1 to port change status bits
+ */
+static void usb_xhci_reset_usb3(device_t dev, int all)
+{
+ u32 status, port_disabled;
+ int timeout, port;
+ int port_count = usb_xhci_port_count_usb3(dev);
+ u32 mem_base = usb_xhci_mem_base(dev);
+
+ if (!mem_base || !port_count)
+ return;
+
+ /* Get mask of disabled ports */
+ port_disabled = pci_read_config32(dev, XHCI_USB3PDO);
+
+ /* Wait until all enabled ports are done polling */
+ for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) {
+ int complete = 1;
+ for (port = 0; port < port_count; port++) {
+ /* Skip disabled ports */
+ if (port_disabled & (1 << port))
+ continue;
+ /* Read port link status field */
+ status = read32(mem_base + XHCI_USB3_PORTSC(port));
+ status &= XHCI_USB3_PORTSC_PLS;
+ if (status == XHCI_PLSR_POLLING)
+ complete = 0;
+ }
+ /* Exit if all ports not polling */
+ if (complete)
+ break;
+ udelay(XHCI_RESET_DELAY_US);
+ }
+
+ /* Reset all requested ports */
+ for (port = 0; port < port_count; port++) {
+ u32 portsc = mem_base + XHCI_USB3_PORTSC(port);
+ /* Skip disabled ports */
+ if (port_disabled & (1 << port))
+ continue;
+ status = read32(portsc) & XHCI_USB3_PORTSC_PLS;
+ /* Reset all or only disconnected ports */
+ if (all || status == XHCI_PLSR_RXDETECT)
+ usb_xhci_reset_port_usb3(mem_base, port);
+ else
+ port_disabled |= 1 << port; /* No reset */
+ }
+
+ /* Wait for warm reset complete on all reset ports */
+ for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) {
+ int complete = 1;
+ for (port = 0; port < port_count; port++) {
+ /* Only check ports that were reset */
+ if (port_disabled & (1 << port))
+ continue;
+ /* Check if warm reset is complete */
+ status = read32(mem_base + XHCI_USB3_PORTSC(port));
+ if (!(status & XHCI_USB3_PORTSC_WRC))
+ complete = 0;
+ }
+ /* Check for warm reset complete in any port */
+ if (complete)
+ break;
+ udelay(XHCI_RESET_DELAY_US);
+ }
+
+ /* Clear port change status bits */
+ for (port = 0; port < port_count; port++)
+ 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)
+{
+ u16 reg16;
+ u32 reg32;
+ u32 mem_base = usb_xhci_mem_base(dev);
+
+ if (!mem_base || slp_typ < 3)
+ return;
+
+ if (pch_is_lp()) {
+ /* Set D0 state */
+ reg16 = pci_read_config16(dev, XHCI_PWR_CTL_STS);
+ reg16 &= ~PWR_CTL_SET_MASK;
+ reg16 |= PWR_CTL_SET_D0;
+ pci_write_config16(dev, XHCI_PWR_CTL_STS, reg16);
+
+ /* Clear PCI 0xB0[14:13] */
+ reg32 = pci_read_config32(dev, 0xb0);
+ reg32 &= ~((1 << 14) | (1 << 13));
+ pci_write_config32(dev, 0xb0, reg32);
+
+ /* Clear MMIO 0x816c[14,2] */
+ reg32 = read32(mem_base + 0x816c);
+ reg32 &= ~((1 << 14) | (1 << 2));
+ write32(mem_base + 0x816c, reg32);
+
+ /* Set MMIO 0x80e0[15] */
+ reg32 = read32(mem_base + 0x80e0);
+ reg32 |= (1 << 15);
+ write32(mem_base + 0x80e0, reg32);
+ }
+
+ /* Set D3Hot state and enable PME */
+ pci_or_config16(dev, XHCI_PWR_CTL_STS, PWR_CTL_SET_D3);
+ pci_or_config16(dev, XHCI_PWR_CTL_STS, PWR_CTL_ENABLE_PME);
+}
+
+#else /* !__SMM__ */
+
static void usb_xhci_clock_gating(device_t dev)
{
u32 reg32;
@@ -173,3 +348,4 @@ static const struct pci_driver pch_usb_xhci __pci_driver = {
.vendor = PCI_VENDOR_ID_INTEL,
.devices = pci_device_ids,
};
+#endif /* !__SMM__ */