broadwell: Clean up XHCI and EHCI ramstage drivers

The EHCI controller is disabled in refcode binary in the typical case,
it is only left enabled when using USBDEBUG.  So the code to disable
the controller can be removed.

The XHCI controller clock gating setup is done in the ramstage
refcode binary so that code can be removed now.  The SMM code for
preparing the controller prior to S3/S5 is reworked but still
made available to be called by the SMI handler.

BUG=chrome-os-partner:28234
TEST=None

Change-Id: I19d1df99d2ee5dbc9c93ca01b2d246432928d77f
Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/199181
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
This commit is contained in:
Duncan Laurie 2014-05-01 13:37:19 -07:00 committed by chrome-internal-fetch
commit d355247333
2 changed files with 26 additions and 370 deletions

View file

@ -23,150 +23,9 @@
#include <device/device.h>
#include <device/pci.h>
#include <device/pci_ids.h>
#include "pch.h"
#include <usbdebug.h>
#include <arch/io.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)
{
u32 reg32;
/* IOBP 0xE5004001[7:6] = 11b */
pch_iobp_update(0xe5004001, ~0, (1 << 7)|(1 << 6));
/* Dx:F0:DCh[5,2,1] = 111b
* Dx:F0:DCh[0] = 1b when EHCI controller is disabled */
reg32 = pci_read_config32(dev, 0xdc);
reg32 |= (1 << 5) | (1 << 2) | (1 << 1);
pci_write_config32(dev, 0xdc, reg32);
/* Dx:F0:78h[1:0] = 11b */
reg32 = pci_read_config32(dev, 0x78);
reg32 |= (1 << 1) | (1 << 0);
pci_write_config32(dev, 0x78, reg32);
}
static void usb_ehci_init(struct device *dev)
{
printk(BIOS_DEBUG, "EHCI: Setting up controller.. ");
usb_ehci_clock_gating(dev);
/* Disable Wake on Disconnect in RMH */
RCBA32_OR(0x35b0, 0x00000022);
printk(BIOS_DEBUG, "done.\n");
}
#include <broadwell/ehci.h>
static void usb_ehci_set_subsystem(device_t dev, unsigned vendor, unsigned device)
{
@ -233,5 +92,3 @@ static const struct pci_driver pch_usb_ehci __pci_driver = {
.vendor = PCI_VENDOR_ID_INTEL,
.devices = pci_device_ids,
};
#endif /* !__SMM__ */

View file

@ -23,11 +23,10 @@
#include <device/pci.h>
#include <device/pci_ids.h>
#include <arch/io.h>
#include "pch.h"
typedef struct southbridge_intel_lynxpoint_config config_t;
#include <broadwell/ramstage.h>
#include <broadwell/xhci.h>
#ifdef __SMM__
static u32 usb_xhci_mem_base(device_t dev)
{
u32 mem_base = pci_read_config32(dev, PCI_BASE_ADDRESS_0);
@ -41,23 +40,8 @@ static u32 usb_xhci_mem_base(device_t dev)
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;
/* PCH-LP has 4 SS ports */
return 4;
}
static void usb_xhci_reset_status_usb3(u32 mem_base, int port)
@ -157,8 +141,6 @@ static void usb_xhci_reset_usb3(device_t dev, int all)
usb_xhci_reset_status_usb3(mem_base, port);
}
#ifdef __SMM__
/* Handler for XHCI controller on entry to S3/S4/S5 */
void usb_xhci_sleep_prepare(device_t dev, u8 slp_typ)
{
@ -169,219 +151,36 @@ void usb_xhci_sleep_prepare(device_t dev, u8 slp_typ)
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);
/* Reset disconnected USB3 ports */
usb_xhci_reset_usb3(dev, 0);
/* 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_STATUS_PME);
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)
{
u32 reg32;
u16 reg16;
/* IOBP 0xE5004001[7:6] = 11b */
pch_iobp_update(0xe5004001, ~0, (1 << 7)|(1 << 6));
reg32 = pci_read_config32(dev, 0x40);
reg32 &= ~(1 << 23); /* unsupported request */
if (pch_is_lp()) {
/* D20:F0:40h[18,17,8] = 111b */
reg32 |= (1 << 18) | (1 << 17) | (1 << 8);
/* D20:F0:40h[21,20,19] = 110b to enable XHCI Idle L1 */
reg32 &= ~(1 << 19);
reg32 |= (1 << 21) | (1 << 20);
} else {
/* D20:F0:40h[21,20,18,17,8] = 11111b */
reg32 |= (1 << 21)|(1 << 20)|(1 << 18)|(1 << 17)|(1 << 8);
}
/* Avoid writing upper byte as it is write-once */
pci_write_config16(dev, 0x40, (u16)(reg32 & 0xffff));
pci_write_config8(dev, 0x40 + 2, (u8)((reg32 >> 16) & 0xff));
/* D20:F0:44h[9,7,3] = 111b */
reg16 = pci_read_config16(dev, 0x44);
reg16 |= (1 << 9) | (1 << 7) | (1 << 3);
pci_write_config16(dev, 0x44, reg16);
reg32 = pci_read_config32(dev, 0xa0);
if (pch_is_lp()) {
/* D20:F0:A0h[18] = 1 */
reg32 |= (1 << 18);
} else {
/* D20:F0:A0h[6] = 1 */
reg32 |= (1 << 6);
}
pci_write_config32(dev, 0xa0, reg32);
/* D20:F0:A4h[13] = 0 */
reg32 = pci_read_config32(dev, 0xa4);
reg32 &= ~(1 << 13);
pci_write_config32(dev, 0xa4, reg32);
}
static void usb_xhci_init(device_t dev)
{
u32 reg32;
u16 reg16;
u32 mem_base = usb_xhci_mem_base(dev);
config_t *config = dev->chip_info;
/* D20:F0:74h[1:0] = 00b (set D0 state) */
reg16 = pci_read_config16(dev, XHCI_PWR_CTL_STS);
reg16 &= ~PWR_CTL_SET_MASK;
reg16 |= PWR_CTL_SET_D0;
reg16 &= ~XHCI_PWR_CTL_SET_MASK;
reg16 |= XHCI_PWR_CTL_SET_D0;
pci_write_config16(dev, XHCI_PWR_CTL_STS, reg16);
/* Enable clock gating first */
usb_xhci_clock_gating(dev);
/* Clear PCI 0xB0[14:13] */
reg32 = pci_read_config32(dev, 0xb0);
reg32 &= ~((1 << 14) | (1 << 13));
pci_write_config32(dev, 0xb0, reg32);
reg32 = read32(mem_base + 0x8144);
if (pch_is_lp()) {
/* XHCIBAR + 8144h[8,7,6] = 111b */
reg32 |= (1 << 8) | (1 << 7) | (1 << 6);
} else {
/* XHCIBAR + 8144h[8,7,6] = 100b */
reg32 &= ~((1 << 7) | (1 << 6));
reg32 |= (1 << 8);
}
write32(mem_base + 0x8144, reg32);
/* Clear MMIO 0x816c[14,2] */
reg32 = read32(mem_base + 0x816c);
reg32 &= ~((1 << 14) | (1 << 2));
write32(mem_base + 0x816c, reg32);
if (pch_is_lp()) {
/* XHCIBAR + 816Ch[19:0] = 000e0038h */
reg32 = read32(mem_base + 0x816c);
reg32 &= ~0x000fffff;
reg32 |= 0x000e0038;
write32(mem_base + 0x816c, reg32);
/* Reset disconnected USB3 ports */
usb_xhci_reset_usb3(dev, 0);
/* D20:F0:B0h[17,14,13] = 100b */
reg32 = pci_read_config32(dev, 0xb0);
reg32 &= ~((1 << 14) | (1 << 13));
reg32 |= (1 << 17);
pci_write_config32(dev, 0xb0, reg32);
}
/* Set MMIO 0x80e0[15] */
reg32 = read32(mem_base + 0x80e0);
reg32 |= (1 << 15);
write32(mem_base + 0x80e0, reg32);
reg32 = pci_read_config32(dev, 0x50);
if (pch_is_lp()) {
/* D20:F0:50h[28:0] = 0FCE2E5Fh */
reg32 &= ~0x1fffffff;
reg32 |= 0x0fce2e5f;
} else {
/* D20:F0:50h[26:0] = 07886E9Fh */
reg32 &= ~0x07ffffff;
reg32 |= 0x07886e9f;
}
pci_write_config32(dev, 0x50, reg32);
/* D20:F0:44h[31] = 1 (Access Control Bit) */
reg32 = pci_read_config32(dev, 0x44);
reg32 |= (1 << 31);
pci_write_config32(dev, 0x44, reg32);
/* D20:F0:40h[31,23] = 10b (OC Configuration Done) */
reg32 = pci_read_config32(dev, 0x40);
reg32 &= ~(1 << 23); /* unsupported request */
reg32 |= (1 << 31);
pci_write_config32(dev, 0x40, reg32);
#if CONFIG_HAVE_ACPI_RESUME
if (acpi_slp_type == 3) {
/* Reset ports that are disabled or
* polling before returning to the OS. */
usb_xhci_reset_usb3(dev, 0);
} else
#endif
/* Route all ports to XHCI */
if (config->xhci_default)
outb(0xca, 0xb2);
/* Set D3Hot state and enable PME */
pci_or_config16(dev, XHCI_PWR_CTL_STS, XHCI_PWR_CTL_SET_D3);
pci_or_config16(dev, XHCI_PWR_CTL_STS, XHCI_PWR_CTL_STATUS_PME);
pci_or_config16(dev, XHCI_PWR_CTL_STS, XHCI_PWR_CTL_ENABLE_PME);
}
static void usb_xhci_set_subsystem(device_t dev, unsigned vendor,
unsigned device)
{
if (!vendor || !device) {
pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
pci_read_config32(dev, PCI_VENDOR_ID));
} else {
pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
((device & 0xffff) << 16) | (vendor & 0xffff));
}
}
static struct pci_operations lops_pci = {
.set_subsystem = &usb_xhci_set_subsystem,
};
#else /* !__SMM__ */
static struct device_operations usb_xhci_ops = {
.read_resources = &pci_dev_read_resources,