diff --git a/src/soc/intel/cannonlake/chip.h b/src/soc/intel/cannonlake/chip.h index 3ebbc5e38d..57922e131c 100644 --- a/src/soc/intel/cannonlake/chip.h +++ b/src/soc/intel/cannonlake/chip.h @@ -314,6 +314,23 @@ struct soc_intel_cannonlake_config { */ uint8_t PchPmSlpAMinAssert; + /* + * PCH PM Reset Power Cycle Duration + * 0 = 4s + * 1 = 1s + * 2 = 2s + * 3 = 3s + * 4 = 4s (default) + * + * NOTE: Duration programmed in the PchPmPwrCycDur should never be smaller than the + * stretch duration programmed in the following registers - + * - GEN_PMCON_A.SLP_S3_MIN_ASST_WDTH (PchPmSlpS3MinAssert) + * - GEN_PMCON_A.S4MAW (PchPmSlpS4MinAssert) + * - PM_CFG.SLP_A_MIN_ASST_WDTH (PchPmSlpAMinAssert) + * - PM_CFG.SLP_LAN_MIN_ASST_WDTH + */ + uint8_t PchPmPwrCycDur; + /* * SerialIO device mode selection: * diff --git a/src/soc/intel/cannonlake/fsp_params.c b/src/soc/intel/cannonlake/fsp_params.c index a3b5588b77..bd2d7fc2d6 100644 --- a/src/soc/intel/cannonlake/fsp_params.c +++ b/src/soc/intel/cannonlake/fsp_params.c @@ -32,6 +32,39 @@ static const pci_devfn_t serial_io_dev[] = { PCH_DEVFN_UART2 }; +/* List of Minimum Assertion durations in microseconds */ +enum min_assrt_dur { + MinAssrtDur0s = 0, + MinAssrtDur60us = 60, + MinAssrtDur1ms = 1000, + MinAssrtDur50ms = 50000, + MinAssrtDur98ms = 98000, + MinAssrtDur500ms = 500000, + MinAssrtDur1s = 1000000, + MinAssrtDur2s = 2000000, + MinAssrtDur3s = 3000000, + MinAssrtDur4s = 4000000, +}; + + +/* Signal Assertion duration values */ +struct cfg_assrt_dur { + /* Minimum assertion duration of SLP_A signal */ + enum min_assrt_dur slp_a; + + /* Minimum assertion duration of SLP_4 signal */ + enum min_assrt_dur slp_s4; + + /* Minimum assertion duration of SLP_3 signal */ + enum min_assrt_dur slp_s3; + + /* PCH PM Power Cycle duration */ + enum min_assrt_dur pm_pwr_cyc_dur; +}; + +/* Default value of PchPmPwrCycDur */ +#define PCH_PM_PWR_CYC_DUR 4 + /* * Given an enum for PCH_SERIAL_IO_MODE, 1 needs to be subtracted to get the FSP * UPD expected value for Serial IO since valid enum index starts from 1. @@ -57,6 +90,93 @@ static uint8_t get_param_value(const config_t *config, uint32_t dev_offset) } #if CONFIG(SOC_INTEL_COMETLAKE) +static enum min_assrt_dur get_high_asst_width(const struct cfg_assrt_dur *cfg_assrt_dur) +{ + enum min_assrt_dur max_assert_dur = cfg_assrt_dur->slp_s4; + + if (max_assert_dur < cfg_assrt_dur->slp_s3) + max_assert_dur = cfg_assrt_dur->slp_s3; + + if (max_assert_dur < cfg_assrt_dur->slp_a) + max_assert_dur = cfg_assrt_dur->slp_a; + + return max_assert_dur; +} + +static void get_min_assrt_dur(uint8_t slp_s4_min_asst, uint8_t slp_s3_min_asst, + uint8_t slp_a_min_asst, uint8_t pm_pwr_cyc_dur, + struct cfg_assrt_dur *cfg_assrt_dur) +{ + /* + * Ensure slp_x_dur_list[] elements are in sync with devicetree config to FSP encoded + * values. + * slp_s4_asst_dur_list : 1s, 1s, 2s, 3s, 4s(Default) + */ + const enum min_assrt_dur slp_s4_asst_dur_list[] = { + MinAssrtDur1s, MinAssrtDur1s, MinAssrtDur2s, MinAssrtDur3s, MinAssrtDur4s + }; + + /* slp_s3_asst_dur_list: 50ms, 60us, 50ms (Default), 2s */ + const enum min_assrt_dur slp_s3_asst_dur_list[] = { + MinAssrtDur50ms, MinAssrtDur60us, MinAssrtDur50ms, MinAssrtDur2s + }; + + /* slp_a_asst_dur_list: 2s, 0s, 4s, 98ms, 2s(Default) */ + const enum min_assrt_dur slp_a_asst_dur_list[] = { + MinAssrtDur2s, MinAssrtDur0s, MinAssrtDur4s, MinAssrtDur98ms, MinAssrtDur2s + }; + + /* pm_pwr_cyc_dur_list: 4s(Default), 1s, 2s, 3s, 4s */ + const enum min_assrt_dur pm_pwr_cyc_dur_list[] = { + MinAssrtDur4s, MinAssrtDur1s, MinAssrtDur2s, MinAssrtDur3s, MinAssrtDur4s + }; + + /* Get signal assertion width */ + if (slp_s4_min_asst < ARRAY_SIZE(slp_s4_asst_dur_list)) + cfg_assrt_dur->slp_s4 = slp_s4_asst_dur_list[slp_s4_min_asst]; + + if (slp_s3_min_asst < ARRAY_SIZE(slp_s3_asst_dur_list)) + cfg_assrt_dur->slp_s3 = slp_s3_asst_dur_list[slp_s3_min_asst]; + + if (slp_a_min_asst < ARRAY_SIZE(slp_a_asst_dur_list)) + cfg_assrt_dur->slp_a = slp_a_asst_dur_list[slp_a_min_asst]; + + if (pm_pwr_cyc_dur < ARRAY_SIZE(pm_pwr_cyc_dur_list)) + cfg_assrt_dur->pm_pwr_cyc_dur = pm_pwr_cyc_dur_list[pm_pwr_cyc_dur]; +} + + +static uint8_t get_pm_pwr_cyc_dur(uint8_t slp_s4_min_asst, uint8_t slp_s3_min_asst, + uint8_t slp_a_min_asst, uint8_t pm_pwr_cyc_dur) +{ + /* Sets default minimum asserton duration values */ + struct cfg_assrt_dur cfg_assrt_dur = { + .slp_a = MinAssrtDur2s, + .slp_s4 = MinAssrtDur4s, + .slp_s3 = MinAssrtDur50ms, + .pm_pwr_cyc_dur = MinAssrtDur4s + }; + + enum min_assrt_dur high_asst_width; + + /* Convert assertion durations from register-encoded to microseconds */ + get_min_assrt_dur(slp_s4_min_asst, slp_s3_min_asst, slp_a_min_asst, pm_pwr_cyc_dur, + &cfg_assrt_dur); + + /* Get the higher assertion duration among PCH EDS specified signals for pwr_cyc_dur */ + high_asst_width = get_high_asst_width(&cfg_assrt_dur); + + if (cfg_assrt_dur.pm_pwr_cyc_dur >= high_asst_width) + return pm_pwr_cyc_dur; + + printk(BIOS_DEBUG, + "Set PmPwrCycDur to 4s as configured PmPwrCycDur(%d) violates PCH EDS " + "spec\n", pm_pwr_cyc_dur); + + return PCH_PM_PWR_CYC_DUR; +} + + static void parse_devicetree_param(const config_t *config, FSP_S_CONFIG *params) { uint32_t dev_offset = 0; @@ -412,6 +532,13 @@ void platform_fsp_silicon_init_params_cb(FSPS_UPD *supd) if (config->PchPmSlpAMinAssert) params->PchPmSlpAMinAssert = config->PchPmSlpAMinAssert; +#if CONFIG(SOC_INTEL_COMETLAKE) + if (config->PchPmPwrCycDur) + params->PchPmPwrCycDur = get_pm_pwr_cyc_dur(config->PchPmSlpS4MinAssert, + config->PchPmSlpS3MinAssert, config->PchPmSlpAMinAssert, + config->PchPmPwrCycDur); +#endif + /* Set TccActivationOffset */ tconfig->TccActivationOffset = config->tcc_offset;