summaryrefslogtreecommitdiff
path: root/src/southbridge/intel/lynxpoint/usb_ehci.c
diff options
context:
space:
mode:
authorDuncan Laurie <dlaurie@chromium.org>2013-07-30 15:53:45 -0700
committerPatrick Georgi <patrick@georgi-clan.de>2013-12-21 23:54:42 +0100
commit1f529083fca3e7a39d3cfacc0f6bb02c2d232362 (patch)
tree97e2cc9ed1ce851ac989387ced63f2fa17d43a62 /src/southbridge/intel/lynxpoint/usb_ehci.c
parentc81187f2312ef3487fa6bd534dcc689317f9423e (diff)
downloadcoreboot-1f529083fca3e7a39d3cfacc0f6bb02c2d232362.tar.xz
lynxpoint: Move USB SMI sleep code to separate USB files
Move this to the existing USB source files so they can share some helper functions and keep the main smihandler code cleaner. The XHCI sleep prepare code now implements the actual sleep preparation steps from the ref code instead of the docs. Change-Id: Ic90adbdaba947a6b53824e548c785b4fb3990ab5 Signed-off-by: Duncan Laurie <dlaurie@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/63800 Reviewed-by: Aaron Durbin <adurbin@chromium.org> Reviewed-on: http://review.coreboot.org/4406 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
Diffstat (limited to 'src/southbridge/intel/lynxpoint/usb_ehci.c')
-rw-r--r--src/southbridge/intel/lynxpoint/usb_ehci.c115
1 files changed, 114 insertions, 1 deletions
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__ */