From c896e92eaad8e255c1cef65b9da2367742a61e5f Mon Sep 17 00:00:00 2001 From: Sumeet Pawnikar Date: Tue, 8 Jan 2019 19:52:54 +0530 Subject: soc/intel/cannonlake: Add processor power limits control support Add processor power limits control support to configure values. BRANCH=None BUG=b:122343940 TEST=Built and tested on Arcada system Change-Id: I5990dc05b51481a0074855914cef20cf07378cde Signed-off-by: Sumeet Pawnikar Reviewed-on: https://review.coreboot.org/c/30907 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi --- src/soc/intel/cannonlake/chip.h | 14 +++ src/soc/intel/cannonlake/cpu.c | 196 +++++++++++++++++++++++++++++ src/soc/intel/cannonlake/include/soc/msr.h | 3 + src/soc/intel/cannonlake/systemagent.c | 7 ++ 4 files changed, 220 insertions(+) diff --git a/src/soc/intel/cannonlake/chip.h b/src/soc/intel/cannonlake/chip.h index 6517b9e882..947cd65718 100644 --- a/src/soc/intel/cannonlake/chip.h +++ b/src/soc/intel/cannonlake/chip.h @@ -197,8 +197,22 @@ struct soc_intel_cannonlake_config { /* HeciEnabled decides the state of Heci1 at end of boot * Setting to 0 (default) disables Heci1 and hides the device from OS */ uint8_t HeciEnabled; + + /* PL1 Override value in Watts */ + uint32_t tdp_pl1_override; /* PL2 Override value in Watts */ uint32_t tdp_pl2_override; + /* SysPL2 Value in Watts */ + uint32_t tdp_psyspl2; + /* SysPL3 Value in Watts */ + uint32_t tdp_psyspl3; + /* SysPL3 window size */ + uint32_t tdp_psyspl3_time; + /* SysPL3 duty cycle */ + uint32_t tdp_psyspl3_dutycycle; + /* PL4 Value in Watts */ + uint32_t tdp_pl4; + /* Intel Speed Shift Technology */ uint8_t speed_shift_enable; /* Enable VR specific mailbox command diff --git a/src/soc/intel/cannonlake/cpu.c b/src/soc/intel/cannonlake/cpu.c index 534f87f676..d0ed3ba150 100644 --- a/src/soc/intel/cannonlake/cpu.c +++ b/src/soc/intel/cannonlake/cpu.c @@ -30,6 +30,198 @@ #include #include #include +#include + +/* Convert time in seconds to POWER_LIMIT_1_TIME MSR value */ +static const u8 power_limit_time_sec_to_msr[] = { + [0] = 0x00, + [1] = 0x0a, + [2] = 0x0b, + [3] = 0x4b, + [4] = 0x0c, + [5] = 0x2c, + [6] = 0x4c, + [7] = 0x6c, + [8] = 0x0d, + [10] = 0x2d, + [12] = 0x4d, + [14] = 0x6d, + [16] = 0x0e, + [20] = 0x2e, + [24] = 0x4e, + [28] = 0x6e, + [32] = 0x0f, + [40] = 0x2f, + [48] = 0x4f, + [56] = 0x6f, + [64] = 0x10, + [80] = 0x30, + [96] = 0x50, + [112] = 0x70, + [128] = 0x11, +}; + +/* Convert POWER_LIMIT_1_TIME MSR value to seconds */ +static const u8 power_limit_time_msr_to_sec[] = { + [0x00] = 0, + [0x0a] = 1, + [0x0b] = 2, + [0x4b] = 3, + [0x0c] = 4, + [0x2c] = 5, + [0x4c] = 6, + [0x6c] = 7, + [0x0d] = 8, + [0x2d] = 10, + [0x4d] = 12, + [0x6d] = 14, + [0x0e] = 16, + [0x2e] = 20, + [0x4e] = 24, + [0x6e] = 28, + [0x0f] = 32, + [0x2f] = 40, + [0x4f] = 48, + [0x6f] = 56, + [0x10] = 64, + [0x30] = 80, + [0x50] = 96, + [0x70] = 112, + [0x11] = 128, +}; + +/* + * Configure processor power limits if possible + * This must be done AFTER set of BIOS_RESET_CPL + */ +void set_power_limits(u8 power_limit_1_time) +{ + msr_t msr = rdmsr(MSR_PLATFORM_INFO); + msr_t limit; + unsigned int power_unit; + unsigned int tdp, min_power, max_power, max_time, tdp_pl2, tdp_pl1; + u8 power_limit_1_val; + struct device *dev = SA_DEV_ROOT; + config_t *conf = dev->chip_info; + + if (power_limit_1_time > ARRAY_SIZE(power_limit_time_sec_to_msr)) + power_limit_1_time = 28; + + if (!(msr.lo & PLATFORM_INFO_SET_TDP)) + return; + + /* Get units */ + msr = rdmsr(MSR_PKG_POWER_SKU_UNIT); + power_unit = 1 << (msr.lo & 0xf); + + /* Get power defaults for this SKU */ + msr = rdmsr(MSR_PKG_POWER_SKU); + tdp = msr.lo & 0x7fff; + min_power = (msr.lo >> 16) & 0x7fff; + max_power = msr.hi & 0x7fff; + max_time = (msr.hi >> 16) & 0x7f; + + printk(BIOS_DEBUG, "CPU TDP: %u Watts\n", tdp / power_unit); + + if (power_limit_time_msr_to_sec[max_time] > power_limit_1_time) + power_limit_1_time = power_limit_time_msr_to_sec[max_time]; + + if (min_power > 0 && tdp < min_power) + tdp = min_power; + + if (max_power > 0 && tdp > max_power) + tdp = max_power; + + power_limit_1_val = power_limit_time_sec_to_msr[power_limit_1_time]; + + /* Set long term power limit to TDP */ + limit.lo = 0; + tdp_pl1 = ((conf->tdp_pl1_override == 0) ? + tdp : (conf->tdp_pl1_override * power_unit)); + limit.lo |= (tdp_pl1 & PKG_POWER_LIMIT_MASK); + + /* Set PL1 Pkg Power clamp bit */ + limit.lo |= PKG_POWER_LIMIT_CLAMP; + + limit.lo |= PKG_POWER_LIMIT_EN; + limit.lo |= (power_limit_1_val & PKG_POWER_LIMIT_TIME_MASK) << + PKG_POWER_LIMIT_TIME_SHIFT; + + /* Set short term power limit to 1.25 * TDP if no config given */ + limit.hi = 0; + tdp_pl2 = (conf->tdp_pl2_override == 0) ? + (tdp * 125) / 100 : (conf->tdp_pl2_override * power_unit); + printk(BIOS_DEBUG, "CPU PL2 = %u Watts\n", tdp_pl2 / power_unit); + limit.hi |= (tdp_pl2) & PKG_POWER_LIMIT_MASK; + limit.hi |= PKG_POWER_LIMIT_CLAMP; + limit.hi |= PKG_POWER_LIMIT_EN; + + /* Power limit 2 time is only programmable on server SKU */ + wrmsr(MSR_PKG_POWER_LIMIT, limit); + + /* Set PL2 power limit values in MCHBAR and disable PL1 */ + MCHBAR32(MCH_PKG_POWER_LIMIT_LO) = limit.lo & (~(PKG_POWER_LIMIT_EN)); + MCHBAR32(MCH_PKG_POWER_LIMIT_HI) = limit.hi; + + /* Set PsysPl2 */ + if (conf->tdp_psyspl2) { + limit = rdmsr(MSR_PLATFORM_POWER_LIMIT); + limit.hi = 0; + printk(BIOS_DEBUG, "CPU PsysPL2 = %u Watts\n", + conf->tdp_psyspl2); + limit.hi |= (conf->tdp_psyspl2 * power_unit) & + PKG_POWER_LIMIT_MASK; + limit.hi |= PKG_POWER_LIMIT_CLAMP; + limit.hi |= PKG_POWER_LIMIT_EN; + + wrmsr(MSR_PLATFORM_POWER_LIMIT, limit); + } + + /* Set PsysPl3 */ + if (conf->tdp_psyspl3) { + limit = rdmsr(MSR_PL3_CONTROL); + limit.lo = 0; + printk(BIOS_DEBUG, "CPU PsysPL3 = %u Watts\n", + conf->tdp_psyspl3); + limit.lo |= (conf->tdp_psyspl3 * power_unit) & + PKG_POWER_LIMIT_MASK; + /* Enable PsysPl3 */ + limit.lo |= PKG_POWER_LIMIT_EN; + /* set PsysPl3 time window */ + limit.lo |= (conf->tdp_psyspl3_time & + PKG_POWER_LIMIT_TIME_MASK) << + PKG_POWER_LIMIT_TIME_SHIFT; + /* set PsysPl3 duty cycle */ + limit.lo |= (conf->tdp_psyspl3_dutycycle & + PKG_POWER_LIMIT_DUTYCYCLE_MASK) << + PKG_POWER_LIMIT_DUTYCYCLE_SHIFT; + wrmsr(MSR_PL3_CONTROL, limit); + } + + /* Set Pl4 */ + if (conf->tdp_pl4) { + limit = rdmsr(MSR_VR_CURRENT_CONFIG); + limit.lo = 0; + printk(BIOS_DEBUG, "CPU PL4 = %u Watts\n", + conf->tdp_pl4); + limit.lo |= (conf->tdp_pl4 * power_unit) & + PKG_POWER_LIMIT_MASK; + wrmsr(MSR_VR_CURRENT_CONFIG, limit); + } + + /* Set DDR RAPL power limit by copying from MMIO to MSR */ + msr.lo = MCHBAR32(MCH_DDR_POWER_LIMIT_LO); + msr.hi = MCHBAR32(MCH_DDR_POWER_LIMIT_HI); + wrmsr(MSR_DDR_RAPL_LIMIT, msr); + + /* Use nominal TDP values for CPUs with configurable TDP */ + if (cpu_config_tdp_levels()) { + msr = rdmsr(MSR_CONFIG_TDP_NOMINAL); + limit.hi = 0; + limit.lo = cpu_get_tdp_nominal_ratio(); + wrmsr(MSR_TURBO_ACTIVATION_RATIO, limit); + } +} static void soc_fsp_load(void) { @@ -65,6 +257,10 @@ static void configure_isst(void) static void configure_misc(void) { struct device *dev = SA_DEV_ROOT; + if (!dev) { + printk(BIOS_ERR, "SA_DEV_ROOT device not found!\n"); + return; + } config_t *conf = dev->chip_info; msr_t msr; diff --git a/src/soc/intel/cannonlake/include/soc/msr.h b/src/soc/intel/cannonlake/include/soc/msr.h index e3bd5f6ebc..63595f353f 100644 --- a/src/soc/intel/cannonlake/include/soc/msr.h +++ b/src/soc/intel/cannonlake/include/soc/msr.h @@ -20,6 +20,9 @@ #include #define MSR_PIC_MSG_CONTROL 0x2e +#define MSR_VR_CURRENT_CONFIG 0x601 +#define MSR_PL3_CONTROL 0x615 #define MSR_VR_MISC_CONFIG2 0x636 +#define MSR_PLATFORM_POWER_LIMIT 0x65c #endif diff --git a/src/soc/intel/cannonlake/systemagent.c b/src/soc/intel/cannonlake/systemagent.c index e6310c0c9a..26f8d9ed7b 100644 --- a/src/soc/intel/cannonlake/systemagent.c +++ b/src/soc/intel/cannonlake/systemagent.c @@ -16,8 +16,10 @@ */ #include +#include #include #include +#include #include #include @@ -52,6 +54,7 @@ void soc_add_fixed_mmio_resources(struct device *dev, int *index) sa_add_fixed_mmio_resources(dev, index, soc_fixed_resources, ARRAY_SIZE(soc_fixed_resources)); + } /* @@ -66,4 +69,8 @@ void soc_systemagent_init(struct device *dev) /* Enable BIOS Reset CPL */ enable_bios_reset_cpl(); + + /* Configure turbo power limits 1ms after reset complete bit */ + mdelay(1); + set_power_limits(28); } -- cgit v1.2.3