PCIe: Add L1 Sub-State support.
Enable L1 Sub-State when both root port and endpoint support it. [pg: keyed the feature to MMCONF_SUPPORT, otherwise boards without that capability fail to build.] Change-Id: Id11fc7c73eb865411747eef63f5f901e00a17f84 Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: 6ac04ad7e2261846e40da297f7fa317ccebda092 Original-BUG=chrome-os-partner:31424 Original-TEST=Build a image and run on Samus proto boards to check if the settings are applied correctly. I just only have proto boards and need someone having EVT boards to confirm the settings. Original-Signed-off-by: Kenji Chen <kenji.chen@intel.com> Original-Change-Id: Id1b5a52ff0b896f4531c4a6e68e70a2cea8c736a Original-Reviewed-on: https://chromium-review.googlesource.com/221436 Original-Reviewed-by: Duncan Laurie <dlaurie@chromium.org> Reviewed-on: http://review.coreboot.org/8832 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
parent
431e51ec2a
commit
31c6e632cf
|
@ -271,6 +271,14 @@ config EARLY_PCI_BRIDGE
|
|||
This option enables static configuration for a single pre-defined
|
||||
PCI bridge function on bus 0.
|
||||
|
||||
config PCIEXP_L1_SUB_STATE
|
||||
prompt "Enable PCIe ASPM L1 SubState"
|
||||
bool
|
||||
depends on PCIEXP_PLUGIN_SUPPORT && MMCONF_SUPPORT
|
||||
default n
|
||||
help
|
||||
Detect and enable ASPM on PCIe links.
|
||||
|
||||
if EARLY_PCI_BRIDGE
|
||||
|
||||
config EARLY_PCI_BRIDGE_DEVICE
|
||||
|
|
|
@ -25,6 +25,31 @@
|
|||
#include <device/pci_ids.h>
|
||||
#include <device/pciexp.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_MMCONF_SUPPORT)
|
||||
unsigned int pciexp_find_extended_cap(device_t dev, unsigned int cap)
|
||||
{
|
||||
unsigned int this_cap_offset, next_cap_offset;
|
||||
unsigned int this_cap, cafe;
|
||||
|
||||
this_cap_offset = PCIE_EXT_CAP_OFFSET;
|
||||
do {
|
||||
this_cap = pci_mmio_read_config32(dev, this_cap_offset);
|
||||
next_cap_offset = this_cap >> 20;
|
||||
this_cap &= 0xffff;
|
||||
cafe = pci_mmio_read_config32(dev, this_cap_offset + 4);
|
||||
cafe &= 0xffff;
|
||||
if (this_cap == cap)
|
||||
return this_cap_offset;
|
||||
else if (cafe == cap)
|
||||
return this_cap_offset + 4;
|
||||
else
|
||||
this_cap_offset = next_cap_offset;
|
||||
} while (next_cap_offset != 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_PCIEXP_COMMON_CLOCK
|
||||
/*
|
||||
* Re-train a PCIe link
|
||||
|
@ -107,6 +132,160 @@ static void pciexp_enable_clock_power_pm(device_t endp, unsigned endp_cap)
|
|||
}
|
||||
#endif /* CONFIG_PCIEXP_CLK_PM */
|
||||
|
||||
#if IS_ENABLED(CONFIG_PCIEXP_L1_SUB_STATE) && IS_ENABLED(CONFIG_MMCONF_SUPPORT)
|
||||
static void pcie_update_cfg(device_t dev, int reg, u32 mask, u32 or)
|
||||
{
|
||||
u32 reg32;
|
||||
|
||||
reg32 = pci_mmio_read_config32(dev, reg);
|
||||
reg32 &= mask;
|
||||
reg32 |= or;
|
||||
pci_mmio_write_config32(dev, reg, reg32);
|
||||
}
|
||||
|
||||
static void pciexp_config_max_latency(device_t root, device_t dev)
|
||||
{
|
||||
unsigned int cap;
|
||||
cap = pciexp_find_extended_cap(dev, PCIE_EXT_CAP_LTR_ID);
|
||||
if (root->ops->ops_pci->set_L1_ss_latency != NULL)
|
||||
root->ops->ops_pci->set_L1_ss_latency(dev, cap + 4);
|
||||
}
|
||||
|
||||
static void pciexp_enable_ltr(device_t dev)
|
||||
{
|
||||
unsigned int cap;
|
||||
cap = pci_find_capability(dev, PCI_CAP_ID_PCIE);
|
||||
|
||||
pcie_update_cfg(dev, cap + 0x28, ~(1 << 10), 1 << 10);
|
||||
}
|
||||
|
||||
static unsigned char pciexp_L1_substate_cal(device_t dev, unsigned int endp_cap,
|
||||
unsigned int *data)
|
||||
{
|
||||
unsigned char mult[4] = {2, 10, 100, 0};
|
||||
|
||||
unsigned int L1SubStateSupport = *data & 0xf;
|
||||
unsigned int comm_mode_rst_time = (*data >> 8) & 0xff;
|
||||
unsigned int power_on_scale = (*data >> 16) & 0x3;
|
||||
unsigned int power_on_value = (*data >> 19) & 0x1f;
|
||||
|
||||
unsigned int endp_data = pci_mmio_read_config32(dev, endp_cap + 4);
|
||||
unsigned int endp_L1SubStateSupport = endp_data & 0xf;
|
||||
unsigned int endp_comm_mode_restore_time = (endp_data >> 8) & 0xff;
|
||||
unsigned int endp_power_on_scale = (endp_data >> 16) & 0x3;
|
||||
unsigned int endp_power_on_value = (endp_data >> 19) & 0x1f;
|
||||
|
||||
L1SubStateSupport &= endp_L1SubStateSupport;
|
||||
|
||||
if (L1SubStateSupport == 0)
|
||||
return 0;
|
||||
|
||||
if (power_on_value * mult[power_on_scale] <
|
||||
endp_power_on_value * mult[endp_power_on_scale]) {
|
||||
power_on_value = endp_power_on_value;
|
||||
power_on_scale = endp_power_on_scale;
|
||||
}
|
||||
if (comm_mode_rst_time < endp_comm_mode_restore_time)
|
||||
comm_mode_rst_time = endp_comm_mode_restore_time;
|
||||
|
||||
*data = (comm_mode_rst_time << 8) | (power_on_scale << 16)
|
||||
| (power_on_value << 19) | L1SubStateSupport;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void pciexp_L1_substate_commit(device_t root, device_t dev,
|
||||
unsigned int root_cap, unsigned int end_cap)
|
||||
{
|
||||
device_t dev_t;
|
||||
unsigned char L1_ss_ok;
|
||||
unsigned int rp_L1_support = pci_mmio_read_config32(root, root_cap + 4);
|
||||
unsigned int L1SubStateSupport;
|
||||
unsigned int comm_mode_rst_time;
|
||||
unsigned int power_on_scale;
|
||||
unsigned int endp_power_on_value;
|
||||
|
||||
for (dev_t = dev; dev_t; dev_t = dev_t->sibling) {
|
||||
/*
|
||||
* rp_L1_support is init'd above from root port.
|
||||
* it needs coordination with endpoints to reach in common.
|
||||
* if certain endpoint doesn't support L1 Sub-State, abort
|
||||
* this feature enabling.
|
||||
*/
|
||||
L1_ss_ok = pciexp_L1_substate_cal(dev_t, end_cap,
|
||||
&rp_L1_support);
|
||||
if (!L1_ss_ok)
|
||||
return;
|
||||
}
|
||||
|
||||
L1SubStateSupport = rp_L1_support & 0xf;
|
||||
comm_mode_rst_time = (rp_L1_support >> 8) & 0xff;
|
||||
power_on_scale = (rp_L1_support >> 16) & 0x3;
|
||||
endp_power_on_value = (rp_L1_support >> 19) & 0x1f;
|
||||
|
||||
printk(BIOS_INFO, "L1 Sub-State supported from root port %d\n",
|
||||
root->path.pci.devfn >> 3);
|
||||
printk(BIOS_INFO, "L1 Sub-State Support = 0x%x\n", L1SubStateSupport);
|
||||
printk(BIOS_INFO, "CommonModeRestoreTime = 0x%x\n", comm_mode_rst_time);
|
||||
printk(BIOS_INFO, "Power On Value = 0x%x, Power On Scale = 0x%x\n",
|
||||
endp_power_on_value, power_on_scale);
|
||||
|
||||
pciexp_enable_ltr(root);
|
||||
|
||||
pcie_update_cfg(root, root_cap + 0x08, ~0xff00,
|
||||
(comm_mode_rst_time << 8));
|
||||
|
||||
pcie_update_cfg(root, root_cap + 0x0c , 0xffffff04,
|
||||
(endp_power_on_value << 3) | (power_on_scale));
|
||||
|
||||
pcie_update_cfg(root, root_cap + 0x08, ~0xe3ff0000,
|
||||
(1 << 21) | (1 << 23) | (1 << 30));
|
||||
|
||||
pcie_update_cfg(root, root_cap + 0x08, ~0xf,
|
||||
L1SubStateSupport);
|
||||
|
||||
for (dev_t = dev; dev_t; dev_t = dev_t->sibling) {
|
||||
pcie_update_cfg(dev_t, end_cap + 0x08, ~0xff00,
|
||||
(comm_mode_rst_time << 8));
|
||||
|
||||
pcie_update_cfg(dev_t, end_cap + 0x0c , 0xffffff04,
|
||||
(endp_power_on_value << 3) | (power_on_scale));
|
||||
|
||||
pcie_update_cfg(dev_t, end_cap + 0x08, ~0xe3ff0000,
|
||||
(1 << 21) | (1 << 23) | (1 << 30));
|
||||
|
||||
pcie_update_cfg(dev_t, end_cap + 0x08, ~0xf,
|
||||
L1SubStateSupport);
|
||||
|
||||
pciexp_enable_ltr(dev_t);
|
||||
|
||||
pciexp_config_max_latency(root, dev_t);
|
||||
}
|
||||
}
|
||||
|
||||
static void pciexp_config_L1_sub_state(device_t root, device_t dev)
|
||||
{
|
||||
unsigned int root_cap, end_cap;
|
||||
|
||||
/* Do it for function 0 only */
|
||||
if (dev->path.pci.devfn & 0x7)
|
||||
return;
|
||||
|
||||
root_cap = pciexp_find_extended_cap(root, PCIE_EXT_CAP_L1SS_ID);
|
||||
if (!root_cap)
|
||||
return;
|
||||
|
||||
end_cap = pciexp_find_extended_cap(dev, PCIE_EXT_CAP_L1SS_ID);
|
||||
if (!end_cap) {
|
||||
end_cap = pciexp_find_extended_cap(dev, 0xcafe);
|
||||
if (!end_cap)
|
||||
return;
|
||||
}
|
||||
|
||||
pciexp_L1_substate_commit(root, dev, root_cap, end_cap);
|
||||
}
|
||||
#endif /* CONFIG_PCIEXP_L1_SUB_STATE */
|
||||
|
||||
#if CONFIG_PCIEXP_ASPM
|
||||
/*
|
||||
* Determine the ASPM L0s or L1 exit latency for a link
|
||||
|
@ -222,6 +401,11 @@ static void pciexp_tune_dev(device_t dev)
|
|||
pciexp_enable_clock_power_pm(dev, cap);
|
||||
#endif
|
||||
|
||||
#if CONFIG_PCIEXP_L1_SUB_STATE
|
||||
/* Enable L1 Sub-State when both root port and endpoint support */
|
||||
pciexp_config_L1_sub_state(root, dev);
|
||||
#endif /* CONFIG_PCIEXP_L1_SUB_STATE */
|
||||
|
||||
#if CONFIG_PCIEXP_ASPM
|
||||
/* Check for and enable ASPM */
|
||||
enum aspm_type apmc = pciexp_enable_aspm(root, root_cap, dev, cap);
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
struct pci_operations {
|
||||
/* set the Subsystem IDs for the PCI device */
|
||||
void (*set_subsystem)(device_t dev, unsigned vendor, unsigned device);
|
||||
void (*set_L1_ss_latency)(device_t dev, unsigned int off);
|
||||
};
|
||||
|
||||
/* Common pci bus operations */
|
||||
|
|
|
@ -405,6 +405,12 @@
|
|||
#define PCI_EXT_CAP_ID_DSN 3
|
||||
#define PCI_EXT_CAP_ID_PWR 4
|
||||
|
||||
/* Extended Capability lists*/
|
||||
#define PCIE_EXT_CAP_OFFSET 0x100
|
||||
#define PCIE_EXT_CAP_AER_ID 0x0001
|
||||
#define PCIE_EXT_CAP_L1SS_ID 0x001E
|
||||
#define PCIE_EXT_CAP_LTR_ID 0x0018
|
||||
|
||||
/* Advanced Error Reporting */
|
||||
#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */
|
||||
#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */
|
||||
|
|
|
@ -15,4 +15,5 @@ unsigned int pciexp_scan_bridge(device_t dev, unsigned int max);
|
|||
|
||||
extern struct device_operations default_pciexp_ops_bus;
|
||||
|
||||
unsigned int pciexp_find_extended_cap(device_t dev, unsigned int cap);
|
||||
#endif /* DEVICE_PCIEXP_H */
|
||||
|
|
Loading…
Reference in New Issue