diff options
-rw-r--r-- | board/puff/board.h | 11 | ||||
-rw-r--r-- | board/puff/gpio.inc | 4 | ||||
-rw-r--r-- | power/cometlake-discrete.c | 295 | ||||
-rw-r--r-- | power/cometlake-discrete.h | 56 |
4 files changed, 271 insertions, 95 deletions
diff --git a/board/puff/board.h b/board/puff/board.h index 85337b55af..0c4dcd2b2b 100644 --- a/board/puff/board.h +++ b/board/puff/board.h @@ -66,7 +66,6 @@ #define CONFIG_CHIPSET_COMETLAKE_DISCRETE /* check */ #define CONFIG_CHIPSET_CAN_THROTTLE -#define CONFIG_CHIPSET_HAS_PRE_INIT_CALLBACK #define CONFIG_CHIPSET_RESET_HOOK #define CONFIG_CPU_PROCHOT_ACTIVE_LOW @@ -231,10 +230,12 @@ void led_critical(void); #define GPIO_PCH_SLP_S0_L GPIO_SLP_S0_L #define GPIO_PCH_SLP_S3_L GPIO_SLP_S3_L #define GPIO_PCH_SLP_S4_L GPIO_SLP_S4_L -/* No equivalent signals for these pins, need to refactor the power handling */ -#define GPIO_RSMRST_L_PGOOD GPIO_PG_VPRIM_CORE_A_OD -#define GPIO_PG_EC_ALL_SYS_PWRGD GPIO_PG_VPRIM_CORE_A_OD -#define GPIO_EN_A_RAILS GPIO_EN_ROA_RAILS #define GPIO_AC_PRESENT GPIO_BJ_ADP_PRESENT_L +/* + * There is no RSMRST input, so alias it to the output. This short-circuits + * common_intel_x86_handle_rsmrst. + */ +#define GPIO_RSMRST_L_PGOOD GPIO_PCH_RSMRST_L + #endif /* __CROS_EC_BOARD_H */ diff --git a/board/puff/gpio.inc b/board/puff/gpio.inc index d66cf09c2f..ddfa7a4e6b 100644 --- a/board/puff/gpio.inc +++ b/board/puff/gpio.inc @@ -18,6 +18,8 @@ GPIO_INT(PG_PP5000_A_OD, PIN(D, 7), GPIO_INT_BOTH, power_signal_interrupt) GPIO_INT(PG_PP1800_A_OD, PIN(3, 1), GPIO_INT_BOTH, power_signal_interrupt) GPIO_INT(PG_VPRIM_CORE_A_OD, PIN(2, 3), GPIO_INT_BOTH, power_signal_interrupt) GPIO_INT(PG_PP1050_A_OD, PIN(2, 2), GPIO_INT_BOTH, power_signal_interrupt) +/* EC output, but also interrupt so this can be polled as a power signal */ +GPIO_INT(EC_PCH_RSMRST_L, PIN(A, 6), GPIO_OUTPUT | GPIO_INT_F_RISING | GPIO_INT_F_FALLING, power_signal_interrupt) #ifndef CONFIG_HOSTCMD_ESPI_VW_SLP_S4 GPIO_INT(SLP_S4_L, PIN(D, 4), GPIO_INT_BOTH, power_signal_interrupt) #endif @@ -46,8 +48,8 @@ GPIO_INT(USB_A3_OC_ODL, PIN(0, 3), GPIO_ODR_HIGH, port_ocp_interrupt) GPIO_INT(USB_A4_OC_ODL, PIN(B, 0), GPIO_ODR_HIGH, port_ocp_interrupt) /* PCH/CPU signals */ +GPIO(EC_PCH_PWROK, PIN(0, 5), GPIO_OUT_LOW) GPIO(EC_PCH_SYS_PWROK, PIN(3, 7), GPIO_OUT_LOW) -GPIO(EC_PCH_RSMRST_L, PIN(A, 6), GPIO_OUT_LOW) GPIO(EC_PCH_PWR_BTN_ODL, PIN(C, 1), GPIO_ODR_HIGH) GPIO(EC_PCH_RTCRST, PIN(7, 6), GPIO_ODR_HIGH) GPIO(EC_PCH_WAKE_ODL, PIN(7, 4), GPIO_ODR_HIGH) diff --git a/power/cometlake-discrete.c b/power/cometlake-discrete.c index 3b5d7575ac..5ab923499c 100644 --- a/power/cometlake-discrete.c +++ b/power/cometlake-discrete.c @@ -3,10 +3,12 @@ * found in the LICENSE file. */ -/* Chrome EC chipset power control for Cometlake with platform-controlled +/* + * Chrome EC chipset power control for Cometlake with platform-controlled * discrete sequencing. */ +#include "adc.h" #include "chipset.h" #include "console.h" #include "gpio.h" @@ -17,7 +19,7 @@ #include "timer.h" /* Console output macros */ -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) +#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ##args) /* Power signals list. Must match order of enum power_signal. */ const struct power_signal_info power_signal_list[] = { @@ -41,6 +43,11 @@ const struct power_signal_info power_signal_list[] = { POWER_SIGNAL_ACTIVE_HIGH, "PP1050_A_PGOOD", }, + [OUT_PCH_RSMRST_DEASSERTED] = { + GPIO_PCH_RSMRST_L, + POWER_SIGNAL_ACTIVE_HIGH, + "OUT_PCH_RSMRST_DEASSERTED", + }, [X86_SLP_S4_DEASSERTED] = { SLP_S4_SIGNAL_L, POWER_SIGNAL_ACTIVE_HIGH, @@ -84,40 +91,117 @@ const struct power_signal_info power_signal_list[] = { }; BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - /* TODO(b/143188569) update from base driver */ - int timeout_ms = 50; +/* + * The EC is responsible for most of the power-on sequence with this driver, + * enabling rails and waiting for power-good signals from regulators before + * continuing. The power sequencing works as follows. + * + * 1. From G3 (all-off), power is applied and EC power supplies come up. + * The power button task kicks off platform power-up as desired. + * 2. Power up the platform to reach S5 + * a. Enable PP5000_A and wait for PP5000_A_PGOOD. + * b. Enable PP3300_A (EN_ROA_RAILS). + * c. Wait for PP3300_A power good. This regulator doesn't provide a power + * good output, so the EC monitors ADC_SNS_PP3300. + * d. Enable PP1800_A and wait for PP1800_A_PGOOD. + * e. PP1800_A_PGOOD automatically enables PPVAR_VPRIM_CORE_A, which receives + * power from PP3300_A (hence PP3300_A must precede PP1800_A, even though + * PP1800_A draws power from PP3300_G which is guaranteed to already be on) + * f. PPVAR_VPRIM_CORE_A_PGOOD automatically enables PP1050_A + * g. Wait for PP1050_A_PGOOD, indicating that both PPVAR_VPRIM_CORE_A and + * PP1050_A are good. + * h. Wait 10ms to satisfy tPCH03, then bring the PCH out of reset by + * deasserting RSMRST. + * 3. The PCH controls transition from S5 up to S3 and higher-power states. + * a. PCH deasserts SLP_S4, automatically turning on PP2500_DRAM_U and + * PP1200_DRAM_U. + * b. Wait for PP2500_DRAM_PGOOD and PP1200_DRAM_PGOOD. + * 4. PCH deasserts SLP_S3 to switch to S0 + * a. SLP_S3 transition automatically enables PP1050_ST_S. + * b. Wait for PP1050_ST_S good. The power good output from this regulator is + * not connected, so the EC monitors ADC_SNS_PP1050_ST_S. + * c. Turn on EN_S0_RAILS (enabling PP1200_PLLOC and PP1050_STG). + * VCCIO must not ramp up before VCCST, VCCSTG and memory rails are good + * (PDG figure 424, note 14). + * d. Wait 2ms (for EN_S0_RAILS load switches to turn on). + * e. Enable PP950_VCCIO. + * f. Wait for PG_PP950_VCCIO. Although the PCH may be asserting CPU_C10_GATED + * which holds the VCCIO regulator in a low-power mode, the regulator will + * turn on normally and assert power good then drop into low power mode + * and continue asserting power good. + * 5. Transition fully to S0 following SLP_S0 + * a. Assert VCCST_PWRGD. This notionally tracks PP1050_ST_S but must be + * deasserted in S3 and lower. + * b. Enable IMVP8_VR. + * c. Wait 2ms. + * d. Assert SYS_PWROK. + * e. Wait for IMVP8_VRRDY. + * f. Wait 2ms. + * g. Assert PCH_PWROK. + * + * When CPU_C10_GATED is asserted, we are free to disable PP1200_PLLOC and + * PP1050_STG by deasserting EN_S0_RAILS to save some power. VCCIO is + * automatically placed in low-power mode by CPU_C10_GATED, and no further + * action is required- power-good signals will not change, just the relevant + * load switches (which are specified to meet the platform's minimum turn-on + * time when CPU_C10_GATED is deasserted again) are turned off. This gating is + * done asynchronously. + * + * For further reference, Figure 421 and Table 370 in the Comet Lake U PDG + * summarizes platform power rail requirements in a reasonably easy-to-digest + * manner, while section 12.11 (containing those diagrams) details the required + * operation. + */ - CPRINTS("%s(%d)", __func__, reason); - report_ap_reset(reason); +/* + * Reverse of S0->S3 transition. + * + * This is a separate function so it can be reused when forcing shutdown due to + * power failure or other reasons. + */ +static void shutdown_s0_rails(void) +{ + /* + * Deassert VCCST_PG as early as possible to satisfy tCPU22; VDDQ is + * derived directly from SLP_S3. + */ + gpio_set_level(GPIO_VCCST_PG_OD, 0); + gpio_set_level(GPIO_EC_PCH_PWROK, 0); + gpio_set_level(GPIO_EC_PCH_SYS_PWROK, 0); + gpio_set_level(GPIO_EN_IMVP8_VR, 0); + gpio_set_level(GPIO_EN_S0_RAILS, 0); + usleep(1); /* tPCH10: PCH_PWROK to VCCIO off >400 ns */ + gpio_set_level(GPIO_EN_PP950_VCCIO, 0); +} - /* Turn off RSMRST_L to meet tPCH12 */ +/* + * Reverse of G3->S5 transition. + * + * This is a separate function so it can be reused when forcing shutdown due to + * power failure or other reasons. + */ +static void shutdown_s5_rails(void) +{ gpio_set_level(GPIO_PCH_RSMRST_L, 0); - - /* Turn off A (except PP5000_A) rails*/ - gpio_set_level(GPIO_EN_A_RAILS, 0); - + /* tPCH12: RSMRST to VCCPRIM (PPVAR_VPRIM_CORE_A) off >400ns */ + usleep(1); + gpio_set_level(GPIO_EN_PP1800_A, 0); + gpio_set_level(GPIO_EN_ROA_RAILS, 0); #ifdef CONFIG_POWER_PP5000_CONTROL - /* Issue a request to turn off the rail. */ power_5v_enable(task_get_current(), 0); #else - /* Turn off PP5000_A rail */ gpio_set_level(GPIO_EN_PP5000_A, 0); #endif +} - /* Need to wait a min of 10 msec before check for power good */ - msleep(10); - - /* Now wait for PP5000_A and RSMRST_L to go low */ - while ((gpio_get_level(GPIO_PP5000_A_PG_OD) || - power_has_signals(IN_PGOOD_ALL_CORE)) && (timeout_ms > 0)) { - msleep(1); - timeout_ms--; - }; +void chipset_force_shutdown(enum chipset_shutdown_reason reason) +{ + CPRINTS("%s(%d)", __func__, reason); + report_ap_reset(reason); - if (!timeout_ms) - CPRINTS("PP5000_A rail still up! Assuming G3."); + shutdown_s0_rails(); + /* S3 is automatic based on SLP_S3 driving memory rails */ + shutdown_s5_rails(); } void chipset_handle_espi_reset_assert(void) @@ -139,76 +223,131 @@ enum power_state chipset_force_g3(void) return POWER_G3; } -/* Called by APL power state machine when transitioning from G3 to S5 */ -void chipset_pre_init_callback(void) +/* + * Wait for a power rail on an analog channel to become good. + * + * @param channel ADC channel to read + * @param min_voltage Minimum required voltage for rail (in mV) + * + * @return EC_SUCCESS, or non-zero if error. + */ +static int power_wait_analog(enum adc_channel channel, int min_voltage) { - /* TODO(b/143188569) update from base driver */ - /* Enable 5.0V and 3.3V rails, and wait for Power Good */ -#ifdef CONFIG_POWER_PP5000_CONTROL - power_5v_enable(task_get_current(), 1); -#else - /* Turn on PP5000_A rail */ - gpio_set_level(GPIO_EN_PP5000_A, 1); -#endif - /* Turn on A (except PP5000_A) rails*/ - gpio_set_level(GPIO_EN_A_RAILS, 1); + timestamp_t deadline; + int reading; - /* - * The status of the 5000_A rail is verified in the calling function via - * power_wait_signals() as PP5000_A_PGOOD is included in the - * CHIPSET_G3S5_POWERUP_SIGNAL macro. - */ + /* One second timeout */ + deadline = get_time(); + deadline.val += SECOND; + + do { + reading = adc_read_channel(channel); + if (reading == ADC_READ_ERROR) + return EC_ERROR_HW_INTERNAL; + if (timestamp_expired(deadline, NULL)) + return EC_ERROR_TIMEOUT; + } while (reading < min_voltage); + + return EC_SUCCESS; } -enum power_state power_handle_state(enum power_state state) +/* + * Force system power state if we time out waiting for a power rail to become + * good. + * + * In general the new state is to transition down to the next lower-power state, + * so if we time out in G3->S5 we return POWER_G3 to turn things off again and + * if S3->S0 times out we return POWER_S3S5 for the same reason. + * + * Correct sequencing of rails that might already be enabled is handled by + * chipset_force_shutdown(), so the caller of this function doesn't need to + * clean up after itself. + */ +static enum power_state pgood_timeout(enum power_state new_state) { - /* TODO(b/143188569) update from base driver */ - int all_sys_pwrgd_in; - int all_sys_pwrgd_out; + chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT); + return new_state; +} +/* + * Called in the chipset task when power signal inputs change state. + * If this doesn't request a different state, power_common_state handles it. + * + * @param state Current power state + * @return New power state + */ +enum power_state power_handle_state(enum power_state state) +{ /* - * Check if RSMRST_L signal state has changed and if so, pass the new - * value along to the PCH. However, if the new transition of RSMRST_L - * from the Sielgo is from low to high, then gate this transition to the - * AP by the PP5000_A rail. If the new transition is from high to low, - * then pass that through regardless of the PP5000_A value. - * - * The PP5000_A power good signal will float high if the - * regulator is not powered, so checking both that the EN and the PG - * signals are high. + * TODO(b/144719399) gate PP1050_STG and PP1200_PLLOC when C10 asserted + * Puff proto also gates HDMI power on EN_S0_RAILS so for that board + * we do not gate them since HDMI should remain powered. */ - if ((gpio_get_level(GPIO_PP5000_A_PG_OD) && - gpio_get_level(GPIO_EN_PP5000_A)) || - gpio_get_level(GPIO_PCH_RSMRST_L)) - common_intel_x86_handle_rsmrst(state); switch (state) { + case POWER_G3S5: + /* Power-up steps 2a-2h. */ +#ifdef CONFIG_POWER_PP5000_CONTROL + power_5v_enable(task_get_current(), 1); +#else + gpio_set_level(GPIO_EN_PP5000_A, 1); +#endif + if (power_wait_signals(POWER_SIGNAL_MASK(PP5000_A_PGOOD))) + return pgood_timeout(POWER_S5G3); + gpio_set_level(GPIO_EN_ROA_RAILS, 1); + if (power_wait_analog(ADC_SNS_PP3300, 3000) != EC_SUCCESS) + return pgood_timeout(POWER_S5G3); + gpio_set_level(GPIO_EN_PP1800_A, 1); + if (power_wait_signals(POWER_SIGNAL_MASK(PP1800_A_PGOOD) | + POWER_SIGNAL_MASK(PP1050_A_PGOOD))) + return pgood_timeout(POWER_S5G3); + msleep(10); /* tPCH03: VCCPRIM good -> RSMRST >10ms */ + gpio_set_level(GPIO_PCH_RSMRST_L, 1); + break; - case POWER_S5: - /* If RSMRST_L is asserted, we're no longer in S5. */ - if (!power_has_signals(IN_PGOOD_ALL_CORE)) - return POWER_S5G3; + case POWER_S5G3: + shutdown_s5_rails(); break; - case POWER_S0: - /* - * Check value of PG_EC_ALL_SYS_PWRGD to see if PCH_SYS_PWROK - * needs to be changed. If it's low->high transition, requires a - * 2msec delay. - */ - all_sys_pwrgd_in = gpio_get_level(GPIO_PG_EC_ALL_SYS_PWRGD); - all_sys_pwrgd_out = gpio_get_level(GPIO_PCH_SYS_PWROK); - - if (all_sys_pwrgd_in != all_sys_pwrgd_out) { - if (all_sys_pwrgd_in) - msleep(2); - gpio_set_level(GPIO_PCH_SYS_PWROK, all_sys_pwrgd_in); - } + case POWER_S5S3: + /* Power-up steps 3a-3b. */ + if (power_wait_signals(POWER_SIGNAL_MASK(PP2500_DRAM_PGOOD) | + POWER_SIGNAL_MASK(PP1200_DRAM_PGOOD))) + return pgood_timeout(POWER_S3S5); + break; + + case POWER_S3S0: + /* Power-up steps 4a-4f. */ + if (power_wait_analog(ADC_SNS_PP1050, 1000) != EC_SUCCESS) + return pgood_timeout(POWER_S3S5); + gpio_set_level(GPIO_EN_S0_RAILS, 1); + msleep(2); + gpio_set_level(GPIO_EN_PP950_VCCIO, 1); + if (power_wait_signals(POWER_SIGNAL_MASK(PP950_VCCIO_PGOOD))) + return pgood_timeout(POWER_S3S5); + + /* Power-up steps 5a-5h */ + gpio_set_level(GPIO_VCCST_PG_OD, 1); + gpio_set_level(GPIO_EN_IMVP8_VR, 1); + msleep(2); + gpio_set_level(GPIO_EC_PCH_SYS_PWROK, 1); + if (power_wait_signals(POWER_SIGNAL_MASK(IMVP8_READY))) + return pgood_timeout(POWER_S3S5); + msleep(2); + gpio_set_level(GPIO_EC_PCH_PWROK, 1); + break; + + case POWER_S0S3: + shutdown_s0_rails(); break; default: break; } + /* + * Power-up steps 3a-3b (S5->S3 via IN_PGOOD_ALL_CORE) plus general + * bookkeeping. + */ return common_intel_x86_power_handle_state(state); } diff --git a/power/cometlake-discrete.h b/power/cometlake-discrete.h index ad423126df..3e9a01baaa 100644 --- a/power/cometlake-discrete.h +++ b/power/cometlake-discrete.h @@ -3,7 +3,8 @@ * found in the LICENSE file. */ -/* Chrome EC chipset power control for Cometlake with platform-controlled +/* + * Chrome EC chipset power control for Cometlake with platform-controlled * discrete sequencing. */ @@ -17,18 +18,50 @@ #define IN_ALL_PM_SLP_DEASSERTED \ (IN_PCH_SLP_S3_DEASSERTED | IN_PCH_SLP_S4_DEASSERTED) -/* TODO(b/143188569) RSMRST_L is an EC output, can't use POWER_SIGNAL_MASK */ -#define IN_PGOOD_ALL_CORE \ - POWER_SIGNAL_MASK(/*X86_RSMRST_L_PGOOD*/ POWER_SIGNAL_COUNT) +/* + * Power mask used by intel_x86 to check that S5 is ready. + * + * This driver controls RSMRST in the G3->S5 transition so this check has nearly + * no use, but letting the common Intel code read RSMRST allows us to avoid + * duplicating the common code (introducing a little redundancy instead). + * + * PP3300 monitoring is analog-only: power_handle_state enforces that it's good + * before continuing to common_intel_x86_power_handle_state. This means we can't + * detect dropouts on that rail, however. + * + * Polling analog inputs as a signal for the common code would require + * modification to support non-power signals as inputs and incurs a minimum 12 + * microsecond time penalty on NPCX7 to do an ADC conversion. Running the ADC + * in repetitive scan mode and enabling threshold detection on the relevant + * channels would permit immediate readings (that might be up to 100 + * microseconds old) but is not currently supported by the ADC driver. + * TODO(b/143188569) try to implement analog watchdogs + */ +#define CHIPSET_G3S5_POWERUP_SIGNAL \ + (POWER_SIGNAL_MASK(PP5000_A_PGOOD) | \ + POWER_SIGNAL_MASK(PP1800_A_PGOOD) | \ + POWER_SIGNAL_MASK(PP1050_A_PGOOD) | \ + POWER_SIGNAL_MASK(OUT_PCH_RSMRST_DEASSERTED)) -#define IN_ALL_S0 \ - (IN_PGOOD_ALL_CORE | IN_ALL_PM_SLP_DEASSERTED | \ - PP5000_PGOOD_POWER_SIGNAL_MASK) +/* + * Power mask used by intel_x86 to check that S3 is ready. + * + * Transition S5->S3 only involves turning on the DRAM power rails which are + * controlled directly from the PCH, so this condition doesn't require any + * special code- just check that the DRAM rails are good. + */ +#define IN_PGOOD_ALL_CORE \ + (CHIPSET_G3S5_POWERUP_SIGNAL | POWER_SIGNAL_MASK(PP2500_DRAM_PGOOD) | \ + POWER_SIGNAL_MASK(PP1200_DRAM_PGOOD)) -/* TODO(b/143188569) RSMRST_L is an EC output, can't use POWER_SIGNAL_MASK */ -#define CHIPSET_G3S5_POWERUP_SIGNAL \ - (POWER_SIGNAL_MASK(/*X86_RSMRST_L_PGOOD*/ POWER_SIGNAL_COUNT) | \ - POWER_SIGNAL_MASK(PP5000_A_PGOOD)) +/* + * intel_x86 power mask for S0 all-OK. + * + * This is only used on power task init to check whether the system is powered + * up and already in S0, to correctly handle switching from RO to RW firmware. + */ +#define IN_ALL_S0 \ + (IN_PGOOD_ALL_CORE | IN_ALL_PM_SLP_DEASSERTED) #define CHARGER_INITIALIZED_DELAY_MS 100 #define CHARGER_INITIALIZED_TRIES 40 @@ -40,6 +73,7 @@ enum power_signal { PP1800_A_PGOOD, VPRIM_CORE_A_PGOOD, PP1050_A_PGOOD, + OUT_PCH_RSMRST_DEASSERTED, /* S5 ready */ X86_SLP_S4_DEASSERTED, PP2500_DRAM_PGOOD, |