diff options
author | Charlie Mooney <charliemooney@chromium.org> | 2012-08-27 14:12:39 -0700 |
---|---|---|
committer | Gerrit <chrome-bot@google.com> | 2012-08-28 12:09:19 -0700 |
commit | bb3bb9262893453ac6a57d5ee02abd73540cf705 (patch) | |
tree | b4c7fa1c0d78d0a5e887b83a99d02987aac8d305 | |
parent | 619032a77c171ff11251648a4af61fb0578dd097 (diff) | |
download | chrome-ec-bb3bb9262893453ac6a57d5ee02abd73540cf705.tar.gz |
Snow: Adding in EC ability to hard reset pmic
By pulling line gio_A15 high, you can for a hard reset of the pmic after
the stuff resistor is changed. This change adds a function that you can
call from the EC and trigger this event (board_hard_reset). The user has
access to this command over the EC console by running "pmu reset" and
it will force the emergency reset.
The board_hard_reset function is used in the pmu's reset code. Whenever
it is trying to initialize or shut down the pmu, it resets many or all
of its registers over i2c. If the i2c commands fail to get a response
from the pmu, the EC will now force a hard reset of the system, which
restores everything, allowing for a restart to fix any situation where
the pmu has gotten its configuration trashed.
BUG=chrome-os-partner:12913
TEST=boot the machine. From EC console check the pmic's register
values, then alter them. Run "pmu reset" to force a reset, and check
the values again. They should be safe values, which you can confirm
by powering on the AP. Repeat this from various starting states: only
the EC on, AP on as well, and setting various registers to 0x00's and
0xff's. To stress test the hard-reset ability from the EC's POV, run
while true; do echo "pmu reset"; sleep 5; done | cu -l DEVICE -s 15200
BRANCH=snow
Change-Id: I911fb9623a7c106d1f993ee4681258c05d4dedae
Signed-off-by: Charlie Mooney <charliemooney@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/31524
Reviewed-by: Simon Glass <sjg@chromium.org>
-rw-r--r-- | board/snow/board.c | 60 | ||||
-rw-r--r-- | board/snow/board.h | 6 | ||||
-rw-r--r-- | common/gaia_power.c | 3 | ||||
-rw-r--r-- | common/pmu_tps65090.c | 89 | ||||
-rw-r--r-- | include/pmu_tpschrome.h | 4 |
5 files changed, 118 insertions, 44 deletions
diff --git a/board/snow/board.c b/board/snow/board.c index 55edbedd2d..672df1002b 100644 --- a/board/snow/board.c +++ b/board/snow/board.c @@ -68,6 +68,7 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"EN_PP5000", GPIO_A, (1<<11), GPIO_OUT_LOW, NULL}, {"EN_PP3300", GPIO_A, (1<<8), GPIO_OUT_LOW, NULL}, {"PMIC_PWRON_L",GPIO_A, (1<<12), GPIO_OUT_HIGH, NULL}, + {"PMIC_RESET", GPIO_A, (1<<15), GPIO_OUT_LOW, NULL}, {"ENTERING_RW", GPIO_D, (1<<0), GPIO_OUT_LOW, NULL}, {"CHARGER_EN", GPIO_B, (1<<2), GPIO_OUT_LOW, NULL}, {"EC_INT", GPIO_B, (1<<9), GPIO_HI_Z, NULL}, @@ -287,41 +288,63 @@ void board_i2c_release(int port) } #endif /* CONFIG_ARBITRATE_I2C */ +/* + * Force the pmic to reset completely. This forces an entire system reset, + * and therefore should never return + */ +void board_hard_reset(void) +{ + /* Force a hard reset of tps Chrome */ + gpio_set_level(GPIO_PMIC_RESET, 1); + /* Hang until the power is cut */ + while (1) + ; +} + #ifdef CONFIG_PMU_BOARD_INIT + /** * Initialize PMU register settings * * PMU init settings depend on board configuration. This function should be * called inside PMU init function. */ -void board_pmu_init(void) +int board_pmu_init(void) { - int ver; + int ver, failure = 0; /* Set fast charging timeout to 6 hours*/ - pmu_set_fastcharge(TIMEOUT_6HRS); + if (!failure) + failure = pmu_set_fastcharge(TIMEOUT_6HRS); /* Enable external gpio CHARGER_EN control */ - pmu_enable_ext_control(1); + if (!failure) + failure = pmu_enable_ext_control(1); /* Disable force charging */ - pmu_enable_charger(0); + if (!failure) + failure = pmu_enable_charger(0); /* Set NOITERM bit */ - pmu_low_current_charging(1); + if (!failure) + failure = pmu_low_current_charging(1); /* * High temperature charging * termination voltage: 2.1V * termination current: 100% */ - pmu_set_term_voltage(RANGE_T34, TERM_V2100); - pmu_set_term_current(RANGE_T34, TERM_I1000); + if (!failure) + failure = pmu_set_term_voltage(RANGE_T34, TERM_V2100); + if (!failure) + failure = pmu_set_term_current(RANGE_T34, TERM_I1000); /* * Standard temperature charging * termination voltage: 2.1V * termination current: 100% */ - pmu_set_term_voltage(RANGE_T23, TERM_V2100); - pmu_set_term_current(RANGE_T23, TERM_I1000); + if (!failure) + failure = pmu_set_term_voltage(RANGE_T23, TERM_V2100); + if (!failure) + failure = pmu_set_term_current(RANGE_T23, TERM_I1000); /* * Ignore TPSCHROME NTC reading in T40. This is snow board specific @@ -329,15 +352,22 @@ void board_pmu_init(void) * http://crosbug.com/p/12221 * http://crosbug.com/p/13171 */ - pmu_set_term_voltage(RANGE_T40, TERM_V2100); - pmu_set_term_current(RANGE_T40, TERM_I1000); + if (!failure) + failure = pmu_set_term_voltage(RANGE_T40, TERM_V2100); + if (!failure) + failure = pmu_set_term_current(RANGE_T40, TERM_I1000); /* Workaround init values before ES3 */ if (pmu_version(&ver) || ver < 3) { /* Termination current: 75% */ - pmu_set_term_current(RANGE_T34, TERM_I0750); - pmu_set_term_current(RANGE_T23, TERM_I0750); - pmu_set_term_current(RANGE_T40, TERM_I0750); + if (!failure) + failure = pmu_set_term_current(RANGE_T34, TERM_I0750); + if (!failure) + failure = pmu_set_term_current(RANGE_T23, TERM_I0750); + if (!failure) + failure = pmu_set_term_current(RANGE_T40, TERM_I0750); } + + return failure ? EC_ERROR_UNKNOWN : EC_SUCCESS; } #endif /* CONFIG_BOARD_PMU_INIT */ diff --git a/board/snow/board.h b/board/snow/board.h index 27eb5d7c6d..79d56ecb5b 100644 --- a/board/snow/board.h +++ b/board/snow/board.h @@ -91,6 +91,7 @@ enum gpio_signal { GPIO_EN_PP5000, /* 5.0v rail enable */ GPIO_EN_PP3300, /* 3.3v rail enable */ GPIO_PMIC_PWRON_L, /* 5v rail ready */ + GPIO_PMIC_RESET, /* Force hard reset of the pmic */ GPIO_EC_ENTERING_RW, /* EC is R/W mode for the kbc mux */ GPIO_CHARGER_EN, GPIO_EC_INT, @@ -123,7 +124,10 @@ void matrix_interrupt(enum gpio_signal signal); void board_interrupt_host(int active); /* Initialize PMU registers using board settings */ -void board_pmu_init(void); +int board_pmu_init(void); + +/* Force the pmu to reset everything on the board */ +void board_hard_reset(void); #endif /* !__ASSEMBLER__ */ diff --git a/common/gaia_power.c b/common/gaia_power.c index 88936d18fb..2ef6f3ddf5 100644 --- a/common/gaia_power.c +++ b/common/gaia_power.c @@ -447,7 +447,8 @@ static void power_off(void) lid_changed = 0; enable_sleep(SLEEP_MASK_AP_RUN); powerled_set_state(POWERLED_STATE_OFF); - pmu_shutdown(); + if (pmu_shutdown()) + board_hard_reset(); CPUTS("Shutdown complete.\n"); } diff --git a/common/pmu_tps65090.c b/common/pmu_tps65090.c index 0fdbef1d89..4ed7c5a5c6 100644 --- a/common/pmu_tps65090.c +++ b/common/pmu_tps65090.c @@ -77,6 +77,13 @@ /* Charger alarm */ #define CHARGER_ALARM 3 +void __board_hard_reset(void) +{ + CPRINTF("This board is not capable of a hard reset.\n"); +} +void board_hard_reset(void) + __attribute__((weak, alias("__board_hard_reset"))); + /* Charger temperature threshold table */ static const uint8_t const pmu_temp_threshold[] = { 1, /* 0b001, 0 degree C */ @@ -402,25 +409,30 @@ int pmu_get_ac(void) int pmu_shutdown(void) { - int offset, rv = 0; + int offset, failure = 0; /* Disable each of the DCDCs */ - for (offset = DCDC1_CTRL; offset <= DCDC3_CTRL; offset++) - rv |= pmu_write(offset, 0x0e); + for (offset = DCDC1_CTRL; offset <= DCDC3_CTRL; offset++) { + if (!failure) + failure = pmu_write(offset, 0x0e); + } /* Disable each of the FETs */ - for (offset = FET1_CTRL; offset <= FET7_CTRL; offset++) - rv |= pmu_write(offset, 0x02); + for (offset = FET1_CTRL; offset <= FET7_CTRL; offset++) { + if (!failure) + failure = pmu_write(offset, 0x02); + } /* Clearing AD controls/status */ - rv |= pmu_write(AD_CTRL, 0x00); + if (!failure) + failure = pmu_write(AD_CTRL, 0x00); - return rv ? EC_ERROR_UNKNOWN : EC_SUCCESS; + return failure ? EC_ERROR_UNKNOWN : EC_SUCCESS; } /* * Fill all of the pmu registers with known good values, this allows the * pmu to recover by rebooting the system if its registers were trashed. */ -static void pmu_init_registers(void) +static int pmu_init_registers(void) { const struct { uint8_t index; @@ -447,19 +459,28 @@ static void pmu_init_registers(void) {AD_CTRL, 0x00}, {IRQ1_REG, 0x00} }; + uint8_t i, rv; + + for (i = 0; i < ARRAY_SIZE(reg); i++) { + rv = pmu_write(reg[i].index, reg[i].value); + if (rv) + return rv; + } - uint8_t i; - for (i = 0; i < ARRAY_SIZE(reg); i++) - pmu_write(reg[i].index, reg[i].value); + return EC_SUCCESS; } void pmu_init(void) { + int failure = 0; + /* Reset everything to default, safe values */ - pmu_init_registers(); + if (!failure) + failure = pmu_init_registers(); #ifdef CONFIG_PMU_BOARD_INIT - board_pmu_init(); + if (!failure) + failure = board_pmu_init(); #else /* Init configuration * Fast charge timer : 2 hours @@ -468,28 +489,39 @@ void pmu_init(void) * * TODO: move settings to battery pack specific init */ - pmu_write(CG_CTRL0, 2); + if (!failure) + failure = pmu_write(CG_CTRL0, 2); /* Limit full charge current to 50% * TODO: remove this temporary hack. */ - pmu_write(CG_CTRL3, 0xbb); + if (!failure) + failure = pmu_write(CG_CTRL3, 0xbb); #endif /* Enable interrupts */ - pmu_write(IRQ1MASK, - EVENT_VACG | /* AC voltage good */ - EVENT_VSYSG | /* System voltage good */ - EVENT_VBATG | /* Battery voltage good */ - EVENT_CGACT | /* Charging status */ - EVENT_CGCPL); /* Charging complete */ - pmu_write(IRQ2MASK, 0); - pmu_clear_irq(); + if (!failure) { + failure = pmu_write(IRQ1MASK, + EVENT_VACG | /* AC voltage good */ + EVENT_VSYSG | /* System voltage good */ + EVENT_VBATG | /* Battery voltage good */ + EVENT_CGACT | /* Charging status */ + EVENT_CGCPL); /* Charging complete */ + } + if (!failure) + failure = pmu_write(IRQ2MASK, 0); + if (!failure) + failure = pmu_clear_irq(); /* Enable charger interrupt. */ - gpio_enable_interrupt(GPIO_CHARGER_INT); + if (!failure) + failure = gpio_enable_interrupt(GPIO_CHARGER_INT); #ifdef CONFIG_AC_POWER_STATUS - gpio_set_flags(GPIO_AC_STATUS, GPIO_OUT_HIGH); + if (!failure) + failure = gpio_set_flags(GPIO_AC_STATUS, GPIO_OUT_HIGH); #endif + + if (failure) + board_hard_reset(); } /* Initializes PMU when power is turned on. This is necessary because the TPS' @@ -532,6 +564,9 @@ static int command_pmu(int argc, char **argv) if (argc > 1) { repeat = strtoi(argv[1], &e, 0); if (*e) { + if (strlen(argv[1]) >= 1 && argv[1][0] == 'r') + board_hard_reset(); + ccputs("Invalid repeat count\n"); return EC_ERROR_INVAL; } @@ -554,7 +589,7 @@ static int command_pmu(int argc, char **argv) return rv ? EC_ERROR_UNKNOWN : EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(pmu, command_pmu, - "<repeat_count>", - "Print PMU info", + "<repeat_count|reset>", + "Print PMU info or force a hard reset", NULL); #endif diff --git a/include/pmu_tpschrome.h b/include/pmu_tpschrome.h index 2f7c213d9d..1d60c6b1cd 100644 --- a/include/pmu_tpschrome.h +++ b/include/pmu_tpschrome.h @@ -216,6 +216,10 @@ int pmu_enable_ext_control(int enable); */ int pmu_set_fastcharge(enum FASTCHARGE_TIMEOUT timeout); +/** + * Reset the entire board if it is capable + */ +void board_hard_reset(void); #endif /* __CROS_EC_TPSCHROME_H */ |