summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/snow/board.c9
-rw-r--r--common/gaia_power.c3
-rw-r--r--common/pmu_tps65090.c30
-rw-r--r--common/pmu_tps65090_charger.c219
-rw-r--r--include/clock.h1
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 */
};