blob: cb6f6ee8e6d777f8cc28cf806b47faaa2b5c847f [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <console/console.h>
#include <delay.h>
#include <device/mmio.h>
#include <device/pci_def.h>
#include <device/pci_ops.h>
#include <northbridge/intel/haswell/haswell.h>
#include <northbridge/intel/haswell/raminit.h>
#include <southbridge/intel/lynxpoint/iobp.h>
#include <southbridge/intel/lynxpoint/pch.h>
#include <timer.h>
#include <types.h>
static unsigned int is_usbr_enabled(void)
{
return !!(pci_read_config32(PCH_XHCI_DEV, XHCI_USB3FUS) & BIT(5));
}
static char *const xhci_bar = (char *)PCH_XHCI_TEMP_BAR0;
static void ehci_hcs_init(const pci_devfn_t dev, const uintptr_t ehci_bar)
{
pci_write_config32(dev, PCI_BASE_ADDRESS_0, ehci_bar);
/** FIXME: Determine whether Bus Master is required (or clean it up afterwards) **/
pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
char *const mem_bar = (char *)ehci_bar;
/**
* Shared EHCI/XHCI ports w/a.
* This step is required when some of the ports are routed to EHCI
* and other ports are routed XHCI at the same time.
*
* FIXME: Under which conditions should this be done?
*/
pci_and_config16(dev, 0x78, ~0x03);
/* Skip reset if usbdebug is enabled */
if (!CONFIG(USBDEBUG_IN_PRE_RAM))
setbits32(mem_bar + EHCI_USB_CMD, EHCI_USB_CMD_HCRESET);
/* 2: Configure number of controllers and ports */
pci_or_config16(dev, EHCI_ACCESS_CNTL, ACCESS_CNTL_ENABLE);
clrsetbits32(mem_bar + EHCI_HCS_PARAMS, 0xf << 12, 0);
clrsetbits32(mem_bar + EHCI_HCS_PARAMS, 0xf << 0, 2 + is_usbr_enabled());
pci_and_config16(dev, EHCI_ACCESS_CNTL, ~ACCESS_CNTL_ENABLE);
pci_or_config16(dev, 0x78, BIT(2));
pci_or_config16(dev, 0x7c, BIT(14) | BIT(7));
pci_update_config32(dev, 0x8c, ~(0xf << 8), (4 << 8));
pci_update_config32(dev, 0x8c, ~BIT(26), BIT(17));
}
static inline unsigned int physical_port_count(void)
{
return MAX_USB2_PORTS;
}
static unsigned int hs_port_count(void)
{
/** TODO: Apparently, WPT-LP has 10 USB2 ports **/
if (CONFIG(INTEL_LYNXPOINT_LP))
return 8;
switch ((pci_read_config32(PCH_XHCI_DEV, XHCI_USB3FUS) >> 1) & 3) {
case 3:
return 8;
case 2:
return 10;
case 1:
return 12;
case 0:
default:
return 14;
}
}
static unsigned int ss_port_count(void)
{
if (CONFIG(INTEL_LYNXPOINT_LP))
return 4;
switch ((pci_read_config32(PCH_XHCI_DEV, XHCI_USB3FUS) >> 3) & 3) {
case 3:
return 0;
case 2:
return 2;
case 1:
return 4;
case 0:
default:
return 6;
}
}
static void common_ehci_hcs_init(void)
{
const bool is_lp = CONFIG(INTEL_LYNXPOINT_LP);
ehci_hcs_init(PCH_EHCI1_DEV, PCH_EHCI1_TEMP_BAR0);
if (!is_lp)
ehci_hcs_init(PCH_EHCI2_DEV, PCH_EHCI2_TEMP_BAR0);
pch_iobp_update(0xe5007f04, 0, 0x00004481);
for (unsigned int port = 0; port < physical_port_count(); port++)
pch_iobp_update(0xe500400f + port * 0x100, ~(1 << 0), 0 << 0);
pch_iobp_update(0xe5007f14, ~(3 << 19), (3 << 19));
if (is_lp)
pch_iobp_update(0xe5007f02, ~(3 << 22), (0 << 22));
}
static void xhci_open_memory_space(void)
{
/** FIXME: Determine whether Bus Master is required (or clean it up afterwards) **/
pci_write_config32(PCH_XHCI_DEV, PCI_BASE_ADDRESS_0, (uintptr_t)xhci_bar);
pci_or_config16(PCH_XHCI_DEV, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
}
static void xhci_close_memory_space(void)
{
pci_and_config16(PCH_XHCI_DEV, PCI_COMMAND, ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY));
pci_write_config32(PCH_XHCI_DEV, PCI_BASE_ADDRESS_0, 0);
}
static void common_xhci_hc_init(void)
{
const bool is_lp = CONFIG(INTEL_LYNXPOINT_LP);
if (!is_lp) {
const unsigned int max_ports = 15 + ss_port_count();
clrsetbits32(xhci_bar + XHCI_HCS_PARAMS_1, 0xf << 28, max_ports << 28);
}
clrsetbits32(xhci_bar + XHCI_HCS_PARAMS_3, 0xffff << 16 | 0xff, 0x200 << 16 | 0x0a);
clrsetbits32(xhci_bar + XHCI_HCC_PARAMS, BIT(5), BIT(10) | BIT(9));
if (!is_lp)
clrsetbits32(xhci_bar + 0x8008, BIT(19), 0);
if (is_lp)
clrsetbits32(xhci_bar + 0x8058, BIT(8), BIT(16));
else
clrsetbits32(xhci_bar + 0x8058, BIT(8), BIT(16) | BIT(20));
clrsetbits32(xhci_bar + 0x8060, 0, BIT(25) | BIT(18));
clrsetbits32(xhci_bar + 0x8090, 0, BIT(14) | BIT(8));
clrsetbits32(xhci_bar + 0x8094, 0, BIT(23) | BIT(21) | BIT(14));
clrsetbits32(xhci_bar + 0x80e0, BIT(16), BIT(6));
clrsetbits32(xhci_bar + 0x80ec, (7 << 12) | (7 << 9), (0 << 12) | (6 << 9));
clrsetbits32(xhci_bar + 0x80f0, BIT(20), 0);
if (is_lp)
clrsetbits32(xhci_bar + 0x80fc, 0, BIT(25));
if (is_lp)
clrsetbits32(xhci_bar + 0x8110, BIT(8) | BIT(2), BIT(20) | BIT(11));
else
clrsetbits32(xhci_bar + 0x8110, BIT(2), BIT(20) | BIT(11));
if (is_lp)
write32(xhci_bar + 0x8140, 0xff00f03c);
else
write32(xhci_bar + 0x8140, 0xff03c132);
if (is_lp)
clrsetbits32(xhci_bar + 0x8154, BIT(21), BIT(13));
else
clrsetbits32(xhci_bar + 0x8154, BIT(21) | BIT(13), 0);
clrsetbits32(xhci_bar + 0x8154, BIT(3), 0);
if (is_lp) {
clrsetbits32(xhci_bar + 0x8164, 0, BIT(1) | BIT(0));
write32(xhci_bar + 0x8174, 0x01400c0a);
write32(xhci_bar + 0x817c, 0x033200a3);
write32(xhci_bar + 0x8180, 0x00cb0028);
write32(xhci_bar + 0x8184, 0x0064001e);
}
/*
* Note: Register at offset 0x44 is 32-bit, but bit 31 is write-once.
* We use these weird partial accesses here to avoid locking bit 31.
*/
pci_or_config16(PCH_XHCI_DEV, 0x44, BIT(15) | BIT(14) | BIT(10) | BIT(0));
pci_or_config8(PCH_XHCI_DEV, 0x44 + 2, 0x0f);
/* LPT-LP >= B0 */
if (is_lp)
clrsetbits32(xhci_bar + 0x8188, 0, BIT(26) | BIT(24));
/* LPT-H >= C0 */
if (!is_lp)
clrsetbits32(xhci_bar + 0x8188, 0, BIT(24));
}
static inline bool is_mem_sr(void)
{
return pci_read_config16(PCH_LPC_DEV, GEN_PMCON_2) & GEN_PMCON_2_MEM_SR;
}
static bool should_restore_xhci_smart_auto(void)
{
if (!is_mem_sr())
return false;
return pci_read_config32(PCH_LPC_DEV, PMIR) & PMIR_XHCI_SMART_AUTO;
}
enum usb_port_route {
ROUTE_TO_EHCI,
ROUTE_TO_XHCI,
};
/* Returns whether port reset was successful */
static bool reset_usb2_ports(const unsigned int ehci_ports)
{
for (unsigned int port = 0; port < ehci_ports; port++) {
/* Initiate port reset for all USB2 ports */
clrsetbits32(
xhci_bar + XHCI_USB2_PORTSC(port),
XHCI_USB2_PORTSC_PED,
XHCI_USB2_PORTSC_PR);
}
/* Poll for port reset bit to be cleared or time out at 100ms */
struct stopwatch timer;
stopwatch_init_msecs_expire(&timer, 100);
uint32_t reg32;
do {
reg32 = 0;
for (unsigned int port = 0; port < ehci_ports; port++)
reg32 |= read32(xhci_bar + XHCI_USB2_PORTSC(port));
reg32 &= XHCI_USB2_PORTSC_PR;
if (!reg32) {
const long elapsed_time = stopwatch_duration_usecs(&timer);
printk(BIOS_DEBUG, "%s: took %lu usecs\n", __func__, elapsed_time);
return true;
}
/* Reference code has a 10 ms delay here, but a smaller delay works too */
udelay(100);
} while (!stopwatch_expired(&timer));
printk(BIOS_ERR, "%s: timed out\n", __func__);
return !reg32;
}
/* Returns whether warm reset was successful */
static bool warm_reset_usb3_ports(const unsigned int xhci_ports)
{
for (unsigned int port = 0; port < xhci_ports; port++) {
/* Initiate warm reset for all USB3 ports */
clrsetbits32(
xhci_bar + XHCI_USB3_PORTSC(port),
XHCI_USB3_PORTSC_PED,
XHCI_USB3_PORTSC_WPR);
}
/* Poll for port reset bit to be cleared or time out at 100ms */
struct stopwatch timer;
stopwatch_init_msecs_expire(&timer, 100);
uint32_t reg32;
do {
reg32 = 0;
for (unsigned int port = 0; port < xhci_ports; port++)
reg32 |= read32(xhci_bar + XHCI_USB3_PORTSC(port));
reg32 &= XHCI_USB3_PORTSC_PR;
if (!reg32) {
const long elapsed_time = stopwatch_duration_usecs(&timer);
printk(BIOS_DEBUG, "%s: took %lu usecs\n", __func__, elapsed_time);
return true;
}
/* Reference code has a 10 ms delay here, but a smaller delay works too */
udelay(100);
} while (!stopwatch_expired(&timer));
printk(BIOS_ERR, "%s: timed out\n", __func__);
return !reg32;
}
static void perform_xhci_ehci_switching_flow(const enum usb_port_route usb_route)
{
const pci_devfn_t dev = PCH_XHCI_DEV;
const unsigned int ehci_ports = hs_port_count() + is_usbr_enabled();
const unsigned int xhci_ports = ss_port_count();
const uint32_t ehci_mask = BIT(ehci_ports) - 1;
const uint32_t xhci_mask = BIT(xhci_ports) - 1;
/** TODO: Handle USBr port? How, though? **/
pci_update_config32(dev, XHCI_USB2PRM, ~XHCI_USB2PR_HCSEL, ehci_mask);
pci_update_config32(dev, XHCI_USB3PRM, ~XHCI_USB3PR_SSEN, xhci_mask);
/*
* Workaround for USB2PR / USB3PR value not surviving warm reset.
* Restore USB Port Routing registers if OS HC Switch driver has been executed.
*/
if (should_restore_xhci_smart_auto()) {
/** FIXME: Derive values from mainboard code instead? **/
pci_update_config32(dev, XHCI_USB2PR, ~XHCI_USB2PR_HCSEL, ehci_mask);
pci_update_config32(dev, XHCI_USB3PR, ~XHCI_USB3PR_SSEN, xhci_mask);
}
/* Later stages shouldn't need the value of this bit */
pci_and_config32(PCH_LPC_DEV, PMIR, ~PMIR_XHCI_SMART_AUTO);
/**
* FIXME: Things here depend on the chosen routing mode.
* For now, implement both functions.
*/
/* Route to EHCI if xHCI disabled or auto mode */
if (usb_route == ROUTE_TO_EHCI) {
if (!reset_usb2_ports(ehci_ports))
printk(BIOS_ERR, "USB2 port reset timed out\n");
pci_and_config32(dev, XHCI_USB2PR, ~XHCI_USB2PR_HCSEL);
for (unsigned int port = 0; port < ehci_ports; port++) {
clrsetbits32(
xhci_bar + XHCI_USB2_PORTSC(port),
XHCI_USB2_PORTSC_PED,
XHCI_USB2_PORTSC_CHST);
}
if (!warm_reset_usb3_ports(xhci_ports))
printk(BIOS_ERR, "USB3 warm reset timed out\n");
/* FIXME: BWG says this should be inside the warm reset function */
pci_and_config32(dev, XHCI_USB3PR, ~XHCI_USB3PR_SSEN);
for (unsigned int port = 0; port < ehci_ports; port++) {
clrsetbits32(
xhci_bar + XHCI_USB3_PORTSC(port),
XHCI_USB3_PORTSC_PED,
XHCI_USB3_PORTSC_CHST);
}
setbits32(xhci_bar + XHCI_USBCMD, BIT(0));
clrbits32(xhci_bar + XHCI_USBCMD, BIT(0));
}
/* Route to xHCI if xHCI enabled */
if (usb_route == ROUTE_TO_XHCI) {
if (is_mem_sr()) {
if (!warm_reset_usb3_ports(xhci_ports))
printk(BIOS_ERR, "USB3 warm reset timed out\n");
}
const uint32_t xhci_port_mask = pci_read_config32(dev, XHCI_USB3PRM) & 0x3f;
pci_update_config32(dev, XHCI_USB3PR, ~XHCI_USB3PR_SSEN, xhci_port_mask);
const uint32_t ehci_port_mask = pci_read_config32(dev, XHCI_USB2PRM) & 0x7fff;
pci_update_config32(dev, XHCI_USB2PR, ~XHCI_USB2PR_HCSEL, ehci_port_mask);
}
}
/* Do not shift in this macro, as it can cause undefined behaviour for bad port/oc values */
#define PORT_TO_OC_SHIFT(port, oc) ((oc) * 8 + (port))
/* Avoid shifting into undefined behaviour */
static inline bool shift_ok(const int shift)
{
return shift >= 0 && shift < 32;
}
static void usb_overcurrent_mapping(void)
{
const bool is_lp = CONFIG(INTEL_LYNXPOINT_LP);
uint32_t ehci_1_ocmap = 0;
uint32_t ehci_2_ocmap = 0;
uint32_t xhci_1_ocmap = 0;
uint32_t xhci_2_ocmap = 0;
/*
* EHCI
*/
for (unsigned int idx = 0; idx < physical_port_count(); idx++) {
const struct usb2_port_config *const port = &mainboard_usb2_ports[idx];
printk(BIOS_DEBUG, "USB2 port %u => ", idx);
if (!port->enable) {
printk(BIOS_DEBUG, "disabled\n");
continue;
}
const unsigned short oc_pin = port->oc_pin;
if (oc_pin == USB_OC_PIN_SKIP) {
printk(BIOS_DEBUG, "not mapped to OC pin\n");
continue;
}
/* Ports 0 .. 7 => OC 0 .. 3 */
if (idx < 8 && oc_pin <= 3) {
const int shift = PORT_TO_OC_SHIFT(idx, oc_pin);
if (shift_ok(shift)) {
printk(BIOS_DEBUG, "mapped to OC pin %u\n", oc_pin);
ehci_1_ocmap |= 1 << shift;
continue;
}
}
/* Ports 8 .. 13 => OC 4 .. 7 (LPT-H only) */
if (!is_lp && idx >= 8 && oc_pin >= 4) {
const int shift = PORT_TO_OC_SHIFT(idx, oc_pin - 4);
if (shift_ok(shift)) {
printk(BIOS_DEBUG, "mapped to OC pin %u\n", oc_pin);
ehci_2_ocmap |= 1 << shift;
continue;
}
}
printk(BIOS_ERR, "Invalid OC pin %u for USB2 port %u\n", oc_pin, idx);
}
printk(BIOS_DEBUG, "\n");
pci_write_config32(PCH_EHCI1_DEV, EHCI_OCMAP, ehci_1_ocmap);
if (!is_lp)
pci_write_config32(PCH_EHCI2_DEV, EHCI_OCMAP, ehci_2_ocmap);
/*
* xHCI
*/
for (unsigned int idx = 0; idx < ss_port_count(); idx++) {
const struct usb3_port_config *const port = &mainboard_usb3_ports[idx];
printk(BIOS_DEBUG, "USB3 port %u => ", idx);
if (!port->enable) {
printk(BIOS_DEBUG, "disabled\n");
continue;
}
const unsigned short oc_pin = port->oc_pin;
if (oc_pin == USB_OC_PIN_SKIP) {
printk(BIOS_DEBUG, "not mapped to OC pin\n");
continue;
}
/* Ports 0 .. 5 => OC 0 .. 3 */
if (oc_pin <= 3) {
const int shift = PORT_TO_OC_SHIFT(idx, oc_pin);
if (shift_ok(shift)) {
printk(BIOS_DEBUG, "mapped to OC pin %u\n", oc_pin);
xhci_1_ocmap |= 1 << shift;
continue;
}
}
/* Ports 0 .. 5 => OC 4 .. 7 (LPT-H only) */
if (!is_lp && oc_pin >= 4) {
const int shift = PORT_TO_OC_SHIFT(idx, oc_pin - 4);
if (shift_ok(shift)) {
printk(BIOS_DEBUG, "mapped to OC pin %u\n", oc_pin);
xhci_2_ocmap |= 1 << shift;
continue;
}
}
printk(BIOS_ERR, "Invalid OC pin %u for USB3 port %u\n", oc_pin, idx);
}
printk(BIOS_DEBUG, "\n");
pci_write_config32(PCH_XHCI_DEV, XHCI_U2OCM1, ehci_1_ocmap);
pci_write_config32(PCH_XHCI_DEV, XHCI_U3OCM1, xhci_1_ocmap);
if (!is_lp) {
pci_write_config32(PCH_XHCI_DEV, XHCI_U2OCM2, ehci_2_ocmap);
pci_write_config32(PCH_XHCI_DEV, XHCI_U3OCM2, xhci_2_ocmap);
}
}
static uint8_t get_ehci_tune_param_1(const struct usb2_port_config *const port)
{
const bool is_lp = CONFIG(INTEL_LYNXPOINT_LP);
const enum pch_platform_type plat_type = get_pch_platform_type();
const enum usb2_port_location location = port->location;
const uint16_t length = port->length;
if (!is_lp) {
if (plat_type == PCH_TYPE_DESKTOP) {
if (location == USB_PORT_BACK_PANEL)
return 4; /* Back Panel */
else
return 3; /* Front Panel */
} else if (plat_type == PCH_TYPE_MOBILE) {
if (location == USB_PORT_INTERNAL)
return 5; /* Internal Topology */
else if (location == USB_PORT_DOCK)
return 4; /* Dock */
else if (length < 0x70)
return 5; /* Back Panel, less than 7" */
else
return 6; /* Back Panel, 7" or more */
}
} else {
if (location == USB_PORT_BACK_PANEL || location == USB_PORT_MINI_PCIE) {
if (length < 0x70)
return 5; /* Back Panel, less than 7" */
else
return 6; /* Back Panel, 7" or more */
} else if (location == USB_PORT_DOCK) {
return 4; /* Dock */
} else {
return 5; /* Internal Topology */
}
}
printk(BIOS_ERR, "%s: Unhandled case\n", __func__);
return 0;
}
static uint8_t get_ehci_tune_param_2(const struct usb2_port_config *const port)
{
const bool is_lp = CONFIG(INTEL_LYNXPOINT_LP);
const enum pch_platform_type plat_type = get_pch_platform_type();
const enum usb2_port_location location = port->location;
const uint16_t length = port->length;
if (!is_lp) {
if (plat_type == PCH_TYPE_DESKTOP) {
if (location == USB_PORT_BACK_PANEL) {
if (length < 0x80)
return 2; /* Back Panel, less than 8" */
else if (length < 0x130)
return 3; /* Back Panel, 8"-13" */
else
return 4; /* Back Panel, 13" or more */
} else {
return 2; /* Front Panel */
}
} else if (plat_type == PCH_TYPE_MOBILE) {
if (location == USB_PORT_INTERNAL) {
return 2; /* Internal Topology */
} else if (location == USB_PORT_DOCK) {
if (length < 0x50)
return 1; /* Dock, less than 5" */
else
return 2; /* Dock, 5" or more */
} else {
if (length < 0x100)
return 2; /* Back Panel, less than 10" */
else
return 3; /* Back Panel, 10" or more */
}
}
} else {
if (location == USB_PORT_BACK_PANEL || location == USB_PORT_MINI_PCIE) {
if (length < 0x100)
return 2; /* Back Panel, less than 10" */
else
return 3; /* Back Panel, 10" or more */
} else if (location == USB_PORT_DOCK) {
if (length < 0x50)
return 1; /* Dock, less than 5" */
else
return 2; /* Dock, 5" or more */
} else {
return 2; /* Internal Topology */
}
}
printk(BIOS_ERR, "%s: Unhandled case\n", __func__);
return 0;
}
static void program_ehci_port_length(void)
{
for (unsigned int port = 0; port < physical_port_count(); port++) {
if (!mainboard_usb2_ports[port].enable)
continue;
const uint32_t addr = 0xe5004000 + (port + 1) * 0x100;
const uint8_t param_1 = get_ehci_tune_param_1(&mainboard_usb2_ports[port]);
const uint8_t param_2 = get_ehci_tune_param_2(&mainboard_usb2_ports[port]);
pch_iobp_update(addr, ~0x7f00, param_2 << 11 | param_1 << 8);
}
}
void early_usb_init(void)
{
/** TODO: Make this configurable? How do the modes affect usbdebug? **/
const enum usb_port_route usb_route = ROUTE_TO_XHCI;
///(pd->boot_mode == 2 && pd->usb_xhci_on_resume) ? ROUTE_TO_XHCI : ROUTE_TO_EHCI;
common_ehci_hcs_init();
xhci_open_memory_space();
common_xhci_hc_init();
perform_xhci_ehci_switching_flow(usb_route);
usb_overcurrent_mapping();
program_ehci_port_length();
/** FIXME: USB per port control is missing, is it needed? **/
xhci_close_memory_space();
/** TODO: Close EHCI memory space? **/
}