diff options
Diffstat (limited to 'src/southbridge/intel/lynxpoint/usb_ehci.c')
-rw-r--r-- | src/southbridge/intel/lynxpoint/usb_ehci.c | 115 |
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__ */ |