Merge "Add battery charge state machine and task"

This commit is contained in:
Gerrit
2012-03-02 22:43:09 -08:00
committed by Gerrit Code Review
7 changed files with 497 additions and 2 deletions

View File

@@ -81,6 +81,7 @@ enum adc_channel
/* Battery module */
#define CONFIG_SMART_BATTERY
#define CONFIG_BATTERY_ATL706486
/* I2C ports */
#define I2C_PORT_BATTERY 0

View File

@@ -23,4 +23,5 @@
TASK(X86POWER, x86_power_task, NULL) \
TASK(CONSOLE, console_task, NULL) \
TASK(HOSTCMD, host_command_task, NULL) \
TASK(I8042CMD, i8042_command_task, NULL)
TASK(I8042CMD, i8042_command_task, NULL) \
TASK(POWERSTATE, charge_state_machine_task, NULL)

View File

@@ -0,0 +1,86 @@
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Battery pack vendor provided charging profile for ATL706486
*/
#include "battery_pack.h"
/* Convert Celsius degree to Deci Kelvin degree */
static inline int celsius_to_deci_kelvin(int degree_c)
{
return degree_c * 10 + 2731;
}
static inline void limit_value(int *val, int limit)
{
if (*val > limit)
*val = limit;
}
/* Vendor provided parameters for battery charging */
void battery_vendor_params(struct batt_params *batt)
{
/* Designed capacity
* Battery capacity = 8400 mAh
* 1C = 8400 mA
*/
const int C = 8400;
const int C_01 = C * 0.1;
const int C_02 = C * 0.2;
const int C_05 = C * 0.5;
const int C_07 = C * 0.7;
/* Designed voltage
* max = 8.4V
* normal = 7.4V
*/
const int V_max = 8400;
/* Operation temperation range
* 0 <= T_charge <= 45
* -20 <= T_discharge <= 60
*/
const int T_charge_min = 0;
const int T_charge_max = 45;
int *desired_current = &batt->desired_current;
/* Hard limits
* - charging voltage < 8.4V
* - charging temperature range 0 ~ 45 degree Celcius
* */
if (batt->desired_voltage > V_max)
batt->desired_voltage = V_max;
if (batt->temperature >= celsius_to_deci_kelvin(T_charge_max) ||
batt->temperature <= celsius_to_deci_kelvin(T_charge_min)) {
batt->desired_voltage = 0;
batt->desired_current = 0;
}
/* Vendor provided charging method
* temp : I - V , I - V
* - 0 ~ 10 : 0.2C - 8.0V, 0.1C to 8.4V
* - 10 ~ 23 : 0.5C - 8.0V, 0.2C to 8.4V
* - 23 ~ 45 : 0.7C - 8.0V, 0.2C to 8.4V
*/
if (batt->temperature <= celsius_to_deci_kelvin(10)) {
if (batt->voltage < 8000)
limit_value(desired_current, C_02);
else
limit_value(desired_current, C_01);
} else if (batt->temperature <= celsius_to_deci_kelvin(23)) {
if (batt->voltage < 8000)
limit_value(desired_current, C_05);
else
limit_value(desired_current, C_02);
} else {
if (batt->voltage < 8000)
limit_value(desired_current, C_07);
else
limit_value(desired_current, C_02);
}
}

24
common/battery_pack.h Normal file
View File

@@ -0,0 +1,24 @@
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Common API for battery pack vendor provided charging profile
*/
#ifndef __CROS_EC_BATTERY_PACK_H
#define __CROS_EC_BATTERY_PACK_H
/* Battery parameters */
struct batt_params {
int temperature;
int state_of_charge;
int voltage;
int current;
int desired_voltage;
int desired_current;
};
/* Vendor provided parameters for battery charging */
void battery_vendor_params(struct batt_params *batt);
#endif //__CROS_EC_BATTERY_PACK_H

View File

@@ -23,4 +23,5 @@ common-$(CONFIG_LIGHTBAR)+=leds.o
# Board driver modules
common-$(CONFIG_CHARGER_BQ24725)+=charger_bq24725.o
common-$(CONFIG_SMART_BATTERY)+=smart_battery.o
common-$(CONFIG_BATTERY_ATL706486)+=battery_atl706486.o
common-$(CONFIG_SMART_BATTERY)+=smart_battery.o charge_state.o

365
common/charge_state.c Normal file
View File

@@ -0,0 +1,365 @@
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Battery charging task and state machine.
*/
#include "battery_pack.h"
#include "board.h"
#include "console.h"
#include "charger.h"
#include "gpio.h"
#include "smart_battery.h"
#include "timer.h"
#include "uart.h"
#include "util.h"
/* Stop charge when state of charge reaches this percentage */
#define STOP_CHARGE_THRESHOLD 100
/* power state task polling period in usec */
#define POLL_PERIOD_LONG 500000
#define POLL_PERIOD_CHARGE 250000
#define POLL_PERIOD_SHORT 100000
#define MIN_SLEEP_USEC 50000
/* Power states */
enum power_state {
PWR_STATE_UNCHANGE = 0,
PWR_STATE_INIT,
PWR_STATE_IDLE,
PWR_STATE_DISCHARGE,
PWR_STATE_CHARGE,
PWR_STATE_ERROR
};
/* Debugging constants, in the same order as power_state.
* This state name table and debug print will be removed
* before production.
*/
const static char *_state_name[] = {
"null",
"init",
"idle",
"discharge",
"charge",
"error"
};
/* helper function(s) */
static inline int get_ac(void)
{
return gpio_get_level(GPIO_AC_PRESENT);
}
/* Get battery charging parameters from battery and
* battery pack vendor table
*/
static int battery_params(struct batt_params *batt)
{
int rv;
rv = battery_temperature(&batt->temperature);
if (rv)
return rv;
rv = battery_voltage(&batt->voltage);
if (rv)
return rv;
rv = battery_current(&batt->current);
if (rv)
return rv;
rv = battery_desired_voltage(&batt->desired_voltage);
if (rv)
return rv;
rv = battery_desired_current(&batt->desired_current);
if (rv)
return rv;
battery_vendor_params(batt);
return EC_SUCCESS;
}
/* Init state handler
* - check ac, charger, battery and temperature
* - initialize charger
* - new states: DISCHARGE, IDLE
*/
static enum power_state state_init(void)
{
int rv, val;
/* Stop charger, unconditionally */
charger_set_current(0);
charger_set_voltage(0);
/* Detect AC, init charger */
if (!get_ac())
return PWR_STATE_DISCHARGE;
/* Initialize charger to power on reset mode */
rv = charger_post_init();
if (rv)
return PWR_STATE_ERROR;
/* Check if charger is online */
rv = charger_get_status(&val);
if (rv)
return PWR_STATE_ERROR;
/* Detect battery */
if (battery_temperature(&val))
return PWR_STATE_ERROR;
return PWR_STATE_IDLE;
}
/* Idle state handler
* - both charger and battery are online
* - detect charger and battery status change
* - new states: CHARGE, INIT
*/
static enum power_state state_idle(void)
{
int voltage, current, state_of_charge;
struct batt_params batt;
if (!get_ac())
return PWR_STATE_INIT;
/* Prevent charging in idle mode */
if (charger_get_voltage(&voltage))
return PWR_STATE_ERROR;
if (charger_get_current(&current))
return PWR_STATE_ERROR;
if (voltage || current)
return PWR_STATE_INIT;
if (battery_state_of_charge(&state_of_charge))
return PWR_STATE_ERROR;
if (state_of_charge >= STOP_CHARGE_THRESHOLD)
return PWR_STATE_UNCHANGE;
/* Check if the batter is good to charge */
if (battery_params(&batt))
return PWR_STATE_ERROR;
/* Configure init charger state and switch to charge state */
if (batt.desired_voltage && batt.desired_current) {
if (charger_set_voltage(batt.desired_voltage))
return PWR_STATE_ERROR;
if (charger_set_current(batt.desired_current))
return PWR_STATE_ERROR;
return PWR_STATE_CHARGE;
}
return PWR_STATE_UNCHANGE;
}
/* Charge state handler
* - detect battery status change
* - new state: INIT
*/
static enum power_state state_charge(void)
{
int chg_voltage, chg_current;
struct batt_params batt;
if (!get_ac())
return PWR_STATE_INIT;
if (charger_get_voltage(&chg_voltage))
return PWR_STATE_ERROR;
if (charger_get_current(&chg_current))
return PWR_STATE_ERROR;
/* Check charger reset */
if (chg_voltage == 0 || chg_current == 0)
return PWR_STATE_INIT;
if (battery_params(&batt))
return PWR_STATE_ERROR;
if (batt.desired_voltage != chg_voltage)
if (charger_set_voltage(batt.desired_voltage))
return PWR_STATE_ERROR;
if (batt.desired_current != chg_current)
if (charger_set_current(batt.desired_current))
return PWR_STATE_ERROR;
if (battery_state_of_charge(&batt.state_of_charge))
return PWR_STATE_ERROR;
if (batt.state_of_charge >= STOP_CHARGE_THRESHOLD) {
if (charger_set_voltage(0) || charger_set_current(0))
return PWR_STATE_ERROR;
return PWR_STATE_IDLE;
}
return PWR_STATE_UNCHANGE;
}
/* Discharge state handler
* - detect ac status
* - new state: INIT
*/
static enum power_state state_discharge(void)
{
if (get_ac())
return PWR_STATE_INIT;
/* TODO: handle overtemp in discharge mode */
return PWR_STATE_UNCHANGE;
}
/* Error state handler
* - check charger and battery communication
* - log error
* - new state: INIT
*/
static enum power_state state_error(void)
{
enum { F_CHG_V, F_CHG_I, F_BAT_V, F_BAT_I,
F_DES_V, F_DES_I, F_BAT_T, F_LAST };
static int last_error_flags;
int error_flags = 0;
int ac = 0;
int bat_v = -1, bat_i = -1, bat_temp = -1;
int desired_v = -1, desired_i = -1;
ac = get_ac();
if (ac) {
if (charger_set_voltage(0))
error_flags |= (1 << F_CHG_V);
if (charger_set_current(0))
error_flags |= (1 << F_CHG_I);
}
if (battery_voltage(&bat_v))
error_flags |= (1 << F_BAT_V);
if (battery_current(&bat_i))
error_flags |= (1 << F_BAT_I);
if (battery_temperature(&bat_temp))
error_flags |= (1 << F_BAT_T);
if (battery_desired_voltage(&desired_v))
error_flags |= (1 << F_DES_V);
if (battery_desired_current(&desired_i))
error_flags |= (1 << F_DES_I);
if (error_flags == 0) {
last_error_flags = 0;
return PWR_STATE_INIT;
}
if (error_flags != last_error_flags) {
uart_printf("errors : %02x\n", error_flags);
uart_printf("previous : %02x\n", last_error_flags);
last_error_flags = error_flags;
uart_printf("ac : %d\n", ac);
uart_puts("charger\n");
if (ac)
if (error_flags & (F_CHG_V | F_CHG_I))
uart_puts(" error\n");
else
uart_puts(" ok\n");
else
uart_puts(" offline\n");
uart_puts("battery\n");
uart_printf(" voltage: %d\n", bat_v);
uart_printf(" current: %d\n", bat_i);
uart_printf(" temp : %d\n", bat_temp);
uart_printf(" des_vol: %d\n", desired_v);
uart_printf(" des_cur: %d\n", desired_i);
}
return PWR_STATE_UNCHANGE;
}
static void charging_progress(void)
{
static int state_of_charge;
int d;
if (battery_state_of_charge(&d))
return;
if (d != state_of_charge) {
state_of_charge = d;
if (get_ac())
battery_time_to_full(&d);
else
battery_time_to_empty(&d);
uart_printf("[Battery %3d%% / %dh:%d]\n", state_of_charge,
d / 60, d % 60);
}
}
/* Battery charging task */
void charge_state_machine_task(void)
{
timestamp_t prev_ts, ts;
int sleep_usec, diff_usec;
enum power_state current_state, new_state;
prev_ts.val = 0;
current_state = PWR_STATE_INIT;
while (1) {
ts = get_time();
switch (current_state) {
case PWR_STATE_INIT:
new_state = state_init();
break;
case PWR_STATE_IDLE:
new_state = state_idle();
break;
case PWR_STATE_DISCHARGE:
new_state = state_discharge();
break;
case PWR_STATE_CHARGE:
new_state = state_charge();
break;
case PWR_STATE_ERROR:
new_state = state_error();
break;
default:
new_state = PWR_STATE_ERROR;
}
if (new_state)
uart_printf("CHARGE: %s --> %s\n",
_state_name[current_state],
_state_name[new_state]);
switch (new_state) {
case PWR_STATE_IDLE:
case PWR_STATE_DISCHARGE:
sleep_usec = POLL_PERIOD_LONG;
break;
case PWR_STATE_CHARGE:
sleep_usec = POLL_PERIOD_CHARGE;
break;
default:
sleep_usec = POLL_PERIOD_SHORT;
}
diff_usec = (int)(ts.val - prev_ts.val);
sleep_usec -= diff_usec;
if (sleep_usec < MIN_SLEEP_USEC)
sleep_usec = MIN_SLEEP_USEC;
prev_ts = ts;
usleep(sleep_usec);
if (new_state)
current_state = new_state;
charging_progress();
}
}

View File

@@ -225,5 +225,22 @@ static inline int battery_design_voltage(int *voltage)
static inline int battery_serial_number(int *serial)
{ return sb_read(SB_SERIAL_NUMBER, serial); }
/* Read battery discharging current
* unit: mA
* negative value: charging
*/
int battery_current(int *current);
int battery_average_current(int *current);
/* Calculate battery time in minutes, under a charging rate
* rate > 0: charging, negative time to full
* rate < 0: discharging, positive time to empty
* rate == 0: invalid input, time = 0
*/
int battery_time_at_rate(int rate, int *minutes);
/* Read manufacturer date */
int battery_manufacturer_date(int *year, int *month, int *day);
#endif /* __CROS_EC_SMART_BATTERY_H */