summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/puff/board.h11
-rw-r--r--board/puff/gpio.inc4
-rw-r--r--power/cometlake-discrete.c295
-rw-r--r--power/cometlake-discrete.h56
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,