diff options
-rw-r--r-- | board/snow/board.c | 9 | ||||
-rw-r--r-- | common/gaia_power.c | 3 | ||||
-rw-r--r-- | common/pmu_tps65090.c | 30 | ||||
-rw-r--r-- | common/pmu_tps65090_charger.c | 219 | ||||
-rw-r--r-- | include/clock.h | 1 |
5 files changed, 180 insertions, 82 deletions
diff --git a/board/snow/board.c b/board/snow/board.c index bf9aa71c98..50fcdfd04c 100644 --- a/board/snow/board.c +++ b/board/snow/board.c @@ -219,9 +219,12 @@ int board_i2c_claim(int port) void board_i2c_release(int port) { if (port == I2C_PORT_HOST) { - /* Release our claim */ - gpio_set_level(GPIO_EC_CLAIM, 1); - usleep(BUS_SLEW_DELAY_US); + /* Release our claim when AP is on */ + if (!chipset_in_state(CHIPSET_STATE_ANY_OFF | + CHIPSET_STATE_SUSPEND)) { + gpio_set_level(GPIO_EC_CLAIM, 1); + usleep(BUS_SLEW_DELAY_US); + } } } #endif /* CONFIG_ARBITRATE_I2C */ diff --git a/common/gaia_power.c b/common/gaia_power.c index ee266de2eb..d440ff5929 100644 --- a/common/gaia_power.c +++ b/common/gaia_power.c @@ -388,9 +388,6 @@ static int power_on(void) /* Call hooks now that AP is running */ hook_notify(HOOK_CHIPSET_STARTUP, 0); - /* Enable charger interrupt. */ - gpio_enable_interrupt(GPIO_CHARGER_INT); - CPRINTF("[%T AP running ...]\n"); return 0; } diff --git a/common/pmu_tps65090.c b/common/pmu_tps65090.c index 1d121b6a4e..b0391d42d5 100644 --- a/common/pmu_tps65090.c +++ b/common/pmu_tps65090.c @@ -6,6 +6,7 @@ */ #include "board.h" +#include "clock.h" #include "console.h" #include "common.h" #include "hooks.h" @@ -45,8 +46,11 @@ #define CG_NOITERM (1 << 5) /* IRQ events */ -#define EVENT_VACG (1 << 1) -#define EVENT_VBATG (1 << 3) +#define EVENT_VACG (1 << 1) /* AC voltage good */ +#define EVENT_VSYSG (1 << 2) /* System voltage good */ +#define EVENT_VBATG (1 << 3) /* Battery voltage good */ +#define EVENT_CGACT (1 << 4) /* Charging status */ +#define EVENT_CGCPL (1 << 5) /* Charging complete */ /* Charger alarm */ #define CHARGER_ALARM 3 @@ -219,9 +223,9 @@ int pmu_low_current_charging(int enable) void pmu_irq_handler(enum gpio_signal signal) { - CPRINTF("Charger IRQ received.\n"); gpio_set_level(GPIO_AC_STATUS, pmu_get_ac()); task_wake(TASK_ID_PMU_TPS65090_CHARGER); + CPRINTF("Charger IRQ received.\n"); } int pmu_get_ac(void) @@ -265,9 +269,18 @@ void pmu_init(void) 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(); - pmu_write(IRQ1MASK, 0xff); - pmu_write(IRQ2MASK, 0xff); + + /* Enable charger interrupt. */ + gpio_enable_interrupt(GPIO_CHARGER_INT); + } /* Initializes PMU when power is turned on. This is necessary because the TPS' @@ -304,6 +317,7 @@ static int command_pmu(int argc, char **argv) int repeat = 1; int rv = 0; int loop; + int value; char *e; if (argc > 1) { @@ -319,6 +333,12 @@ static int command_pmu(int argc, char **argv) usleep(1000); } + rv = pmu_read(IRQ1_REG, &value); + if (rv) + return rv; + CPRINTF("pmu events b%08b\n", value); + CPRINTF("ac gpio %d\n", pmu_get_ac()); + if (rv) ccprintf("Failed - error %d\n", rv); diff --git a/common/pmu_tps65090_charger.c b/common/pmu_tps65090_charger.c index 29f6dda19c..c717216b88 100644 --- a/common/pmu_tps65090_charger.c +++ b/common/pmu_tps65090_charger.c @@ -6,8 +6,10 @@ */ #include "board.h" +#include "clock.h" #include "chipset.h" #include "console.h" +#include "hooks.h" #include "gpio.h" #include "pmu_tpschrome.h" #include "smart_battery.h" @@ -25,13 +27,18 @@ ALARM_OVER_CHARGED | \ ALARM_OVER_TEMP) +/* Maximum retry count to revive a extremely low charge battery */ +#define PRE_CHARGING_RETRY 3 /* Time delay in usec for idle, charging and discharging. * Defined in battery charging flow. */ -#define T1_USEC 5000000 -#define T2_USEC 10000000 -#define T3_USEC 10000000 +#define SECOND (1000 * 1000) +#define T1_OFF_USEC (60 * SECOND) +#define T1_SUSPEND_USEC (60 * SECOND) +#define T1_USEC (5 * SECOND) +#define T2_USEC (10 * SECOND) +#define T3_USEC (10 * SECOND) /* Non-SBS charging states */ enum charging_state { @@ -84,24 +91,6 @@ static int battery_discharging_range(int t) return (t < 70); } -static int wait_t1_idle(void) -{ - usleep(T1_USEC); - return ST_IDLE; -} - -static int wait_t2_charging(void) -{ - usleep(T2_USEC); - return ST_CHARGING; -} - -static int wait_t3_discharging(void) -{ - usleep(T3_USEC); - return ST_DISCHARGING; -} - /* * Turn off the host application processor */ @@ -109,10 +98,8 @@ static int system_off(void) { if (chipset_in_state(CHIPSET_STATE_ON)) { CPUTS("[pmu] turn system off\n"); - chipset_exit_hard_off(); - - /* TODO(rongchang): After have impl in chipset_exit_hard_off(), - * remove these gpio hack + /* TODO(rongchang): need chipset_force_hard_off(), + * and remove these gpio hack */ gpio_set_level(GPIO_EN_PP3300, 0); gpio_set_level(GPIO_EN_PP1350, 0); @@ -120,7 +107,7 @@ static int system_off(void) gpio_set_level(GPIO_EN_PP5000, 0); } - return wait_t1_idle(); + return ST_IDLE; } /* @@ -142,72 +129,82 @@ static int notify_battery_low(void) return ST_DISCHARGING; } +static int config_low_current_charging(int charge) +{ + /* Disable low current termination */ + if (charge < 40) + return pmu_low_current_charging(1); + + /* Enable low current termination */ + if (charge > 60) + return pmu_low_current_charging(0); + + return EC_SUCCESS; +} + static int calc_next_state(int state) { int batt_temp, alarm, capacity, charge; switch (state) { case ST_IDLE: - /* Check AC and chiset state */ if (!pmu_get_ac()) { if (chipset_in_state(CHIPSET_STATE_ON)) return ST_DISCHARGING; - - /* Enable charging and wait ac on */ - enable_charging(1); - return wait_t1_idle(); + return ST_IDLE; } /* Enable charging when battery doesn't respond */ if (battery_temperature(&batt_temp)) { - enable_charging(1); - wait_t1_idle(); + if (config_low_current_charging(0)) + return ST_IDLE; return ST_PRE_CHARGING; } /* Turn off charger when battery temperature is out * of the start charging range. */ - if (!battery_start_charging_range(batt_temp)) { - enable_charging(0); - return wait_t1_idle(); - } + if (!battery_start_charging_range(batt_temp)) + return ST_IDLE; /* Turn off charger on battery charging alarm */ - if (battery_status(&alarm) || (alarm & ALARM_CHARGING)) { - if (!(alarm & ALARM_TERMINATE_CHARGE)) - CPRINTF("[pmu] idle %016b\n", alarm); - enable_charging(0); - return wait_t1_idle(); - } + if (battery_status(&alarm) || (alarm & ALARM_CHARGING)) + return ST_IDLE; /* Start charging only when battery charge lower than 100% */ - if (!battery_state_of_charge(&charge) && charge < 100) { - enable_charging(1); - wait_t1_idle(); - return ST_CHARGING; + if (!battery_state_of_charge(&charge)) { + config_low_current_charging(charge); + if (charge < 100) + return ST_CHARGING; } - return wait_t1_idle(); + return ST_IDLE; case ST_PRE_CHARGING: if (!pmu_get_ac()) - return wait_t1_idle(); + return ST_IDLE; /* If the battery goes online after enable the charger, * go into charging state. */ - if (battery_temperature(&batt_temp) == EC_SUCCESS) + if (battery_temperature(&batt_temp) == EC_SUCCESS) { + if (!battery_start_charging_range(batt_temp)) + return ST_IDLE; + if (!battery_state_of_charge(&charge)) { + config_low_current_charging(charge); + if (charge >= 100) + return ST_IDLE; + } return ST_CHARGING; + } - wait_t1_idle(); return ST_PRE_CHARGING; case ST_CHARGING: /* Go back to idle state when AC is unplugged */ if (!pmu_get_ac()) - break; + return ST_IDLE; /* * Disable charging on battery access error, or charging @@ -216,14 +213,12 @@ static int calc_next_state(int state) if (battery_temperature(&batt_temp)) { CPUTS("[pmu] charging: unable to get battery " "temperature\n"); - enable_charging(0); - break; + return ST_IDLE; } else if (!battery_charging_range(batt_temp)) { CPRINTF("[pmu] charging: temperature out of range " "%dC\n", battery_temperature_celsius(batt_temp)); - enable_charging(0); - break; + return ST_IDLE; } /* @@ -233,8 +228,7 @@ static int calc_next_state(int state) */ if (battery_status(&alarm) || (alarm & ALARM_CHARGING)) { CPUTS("[pmu] charging: battery alarm\n"); - enable_charging(0); - break; + return ST_IDLE; } /* @@ -244,16 +238,19 @@ static int calc_next_state(int state) */ if (pmu_is_charger_alarm()) { CPUTS("[pmu] charging: charger alarm\n"); - enable_charging(0); - break; + return ST_IDLE; } - return wait_t2_charging(); + return ST_CHARGING; case ST_DISCHARGING: /* Go back to idle state when AC is plugged */ if (pmu_get_ac()) - return wait_t1_idle(); + return ST_IDLE; + + /* Prepare EC sleep after system stopped discharging */ + if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) + return ST_IDLE; /* Check battery discharging temperature range */ if (battery_temperature(&batt_temp) == 0) { @@ -261,7 +258,6 @@ static int calc_next_state(int state) CPRINTF("[pmu] discharging: temperature out of" "range %dC\n", battery_temperature_celsius(batt_temp)); - enable_charging(0); return system_off(); } } @@ -269,36 +265,117 @@ static int calc_next_state(int state) if (!battery_status(&alarm) && (alarm & ALARM_DISCHARGING)) { CPRINTF("[pmu] discharging: battery alarm %016b\n", alarm); - enable_charging(0); return system_off(); } /* Check remaining charge % */ - if (battery_state_of_charge(&capacity) == 0 && capacity < 10) - return notify_battery_low(); + if (battery_state_of_charge(&capacity) == 0) { + if (capacity < 3) { + system_off(); + return ST_IDLE; + } else if (capacity < 10) { + notify_battery_low(); + } + } - return wait_t3_discharging(); + return ST_DISCHARGING; } - return wait_t1_idle(); + return ST_IDLE; } void pmu_charger_task(void) { int state = ST_IDLE; int next_state; + int event = 0; + int wait_time = T1_USEC; + unsigned int pre_charging_count = 0; + + pmu_init(); + /* + * EC STOP mode support + * The charging loop can be stopped in idle state with AC unplugged. + * Charging loop will be resumed by TPSCHROME interrupt. + */ + enable_charging(0); + disable_sleep(SLEEP_MASK_CHARGING); while (1) { pmu_clear_irq(); - next_state = calc_next_state(state); + /* + * When battery is extremely low, the internal voltage can not + * power on its gas guage IC. Charging loop will enable the + * charger and turn on trickle charging. For safty reason, + * charger should be disabled if the communication to battery + * failed. + */ + next_state = pre_charging_count > PRE_CHARGING_RETRY ? + calc_next_state(ST_IDLE) : + calc_next_state(state); + if (next_state != state) { + pre_charging_count = 0; CPRINTF("[batt] state %s -> %s\n", state_list[state], state_list[next_state]); state = next_state; + if (state == ST_PRE_CHARGING || state == ST_CHARGING) + enable_charging(1); + else + enable_charging(0); } - /* TODO(sjg@chromium.org): root cause crosbug.com/p/11285 */ - task_wait_event(5000 * 1000); + switch (state) { + case ST_CHARGING: + wait_time = T2_USEC; + break; + case ST_DISCHARGING: + wait_time = T3_USEC; + break; + case ST_PRE_CHARGING: + wait_time = T1_USEC; + if (pre_charging_count > PRE_CHARGING_RETRY) + enable_charging(0); + else + pre_charging_count++; + break; + default: + if (pmu_get_ac()) { + wait_time = T1_USEC; + break; + } else if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { + wait_time = T1_OFF_USEC; + enable_sleep(SLEEP_MASK_CHARGING); + } else if (chipset_in_state(CHIPSET_STATE_SUSPEND)) { + wait_time = T1_SUSPEND_USEC; + } else { + wait_time = T1_USEC; + } + } + + /* + * Throttle the charging loop. If previous loop was waked up + * by an event, sleep 0.5 seconds instead of wait for next + * event. + */ + if (event & TASK_EVENT_WAKE) { + usleep(0.5 * SECOND); + event = 0; + } else { + event = task_wait_event(wait_time); + disable_sleep(SLEEP_MASK_CHARGING); + } } } + +/* Wake charging task on chipset events */ +static int pmu_chipset_events(void) +{ + task_wake(TASK_ID_PMU_TPS65090_CHARGER); + return 0; +} +DECLARE_HOOK(HOOK_CHIPSET_STARTUP, pmu_chipset_events, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pmu_chipset_events, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pmu_chipset_events, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_CHIPSET_RESUME, pmu_chipset_events, HOOK_PRIO_DEFAULT); diff --git a/include/clock.h b/include/clock.h index 1d2506029d..4eb8a5b065 100644 --- a/include/clock.h +++ b/include/clock.h @@ -45,6 +45,7 @@ enum { SLEEP_MASK_AP_RUN = (1 << 0), /* the main CPU is running */ SLEEP_MASK_UART = (1 << 1), /* UART communication on-going */ SLEEP_MASK_I2C = (1 << 2), /* I2C master communication on-going */ + SLEEP_MASK_CHARGING = (1 << 3), /* Charging loop on-going */ SLEEP_MASK_FORCE = (1 << 31), /* Force disabling low power modes */ }; |