summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRong Chang <rongchang@chromium.org>2012-08-15 04:48:34 +0800
committerGerrit <chrome-bot@google.com>2012-08-16 03:36:19 -0700
commitcebbe4c0e708be36140c8c0fdce45a106f824650 (patch)
tree365ddbf198f5b87046241d7607e0af928e755602
parent9c45a309b9d462358dfecd4713340ea23d9f12f2 (diff)
downloadchrome-ec-cebbe4c0e708be36140c8c0fdce45a106f824650.tar.gz
daisy: Modify charging flow to comply charging specification
This change corrects charger interrupt event handling, charger enable gpio, battery full condition, EC deep sleep mode support when AC unplugged, and lid controlled power off. Signed-off-by: Rong Chang <rongchang@chromium.org> BRANCH=snow BUG=chrome-os-partner:12573,12574,12575 TEST=manual - ec console command 'gpioget': - SPI1_MISO should be 0 when AP off - CHARGER_EN should be 0 after AC unplugged - charging led should be off after AC unplugged - when battery remaining charge < 3%, system should be powered off without AC. - ec console command 'sleepmask 0', turn off AP: - deep sleep only when AC unplugged Change-Id: I0f63835dae67d90de7a8c8c6c3537ca9a16faed4 Reviewed-on: https://gerrit.chromium.org/gerrit/30316 Commit-Ready: Rong Chang <rongchang@chromium.org> Reviewed-by: Rong Chang <rongchang@chromium.org> Tested-by: Rong Chang <rongchang@chromium.org>
-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 */
};