Falco: throttle if battery current drain is too high

I missed this requirement the first time. Now it's here. Also adding a test
for it as well.

BUG=chrome-os-partner:20739
BRANCH=falco
TEST=manual

make BOARD=falco runtests

Change-Id: I88aac8d12d09f7970b04c4aa02b6986b5ea16306
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/66684
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
This commit is contained in:
Bill Richardson
2013-08-22 10:18:58 -07:00
committed by ChromeBot
parent fcce7223a5
commit 8c7a18616f
4 changed files with 179 additions and 0 deletions

View File

@@ -120,6 +120,15 @@ struct adapter_limits ad_limits[][NUM_AC_TURBO_STATES][NUM_AC_THRESHOLDS] = {
};
BUILD_ASSERT(ARRAY_SIZE(ad_limits) == NUM_ADAPTER_TYPES);
/* The battery current limits are independent of Turbo or adapter rating.
* hi_val and lo_val are DISCHARGE current in mA.
*/
test_export_static
struct adapter_limits batt_limits[] = {
{ 7500, 7000, 16, 50, },
{ 8000, 7500, 1, 50, },
};
BUILD_ASSERT(ARRAY_SIZE(batt_limits) == NUM_BATT_THRESHOLDS);
static int last_mv;
static enum adapter_type identify_adapter(void)
@@ -220,10 +229,38 @@ void check_threshold(int current, struct adapter_limits *lim)
}
}
test_export_static
void watch_battery_closely(struct power_state_context *ctx)
{
int i;
int current = ctx->curr.batt.current;
/* NB: The values in batt_limits[] indicate DISCHARGE current (mA).
* However, the value returned from battery_current() is CHARGE
* current: postive for charging and negative for discharging.
*
* Turbo mode can discharge the battery even while connected to the
* charger. The spec says not to turn throttling off until the battery
* drain has been below the threshold for 5 seconds. That means we
* still need to check while on AC, or else just plugging the adapter
* in and out would mess up that 5-second timeout. Since the threshold
* logic uses signed numbers to compare the limits, everything Just
* Works.
*/
/* Check limits against DISCHARGE current, not CHARGE current! */
for (i = 0; i < NUM_BATT_THRESHOLDS; i++)
check_threshold(-current, &batt_limits[i]); /* invert sign! */
}
void watch_adapter_closely(struct power_state_context *ctx)
{
int current, i;
/* We always watch the battery current drain, even when on AC. */
watch_battery_closely(ctx);
/* We can only talk to the charger if we're on AC. If there are no
* errors and we recognize the adapter, enable Turbo at 15% charge,
* disable it at 10% to provide hysteresis. */

View File

@@ -38,6 +38,7 @@ struct adapter_limits {
/* Number of special states */
#define NUM_AC_TURBO_STATES 2
#define NUM_AC_THRESHOLDS 2
#define NUM_BATT_THRESHOLDS 2
/* Change turbo mode or throttle the AP depending on the adapter state. */
void watch_adapter_closely(struct power_state_context *ctx);

View File

@@ -74,6 +74,11 @@ int charger_set_option(int option)
return EC_SUCCESS;
}
void chipset_throttle_cpu(int throttle)
{
/* PROCHOT, ugh. */
}
/* Local functions to control the mocked functions. */
static void change_ac(int val)
@@ -88,6 +93,11 @@ static void set_id(int val)
mock_id = val;
}
/* Specify as discharge current */
static void mock_batt(int cur)
{
ctx.curr.batt.current = -cur;
}
/* And the tests themselves... */
@@ -284,6 +294,134 @@ static int test_thresholds(void)
return EC_SUCCESS;
}
static int test_batt(void)
{
struct adapter_limits *l, *h;
int longtime;
int i;
/* NB: struct adapter_limits assumes hi_val > lo_val, so the values in
* batt_limits[] indicate discharge current (mA). However, the value
* returned from battery_current() is postive for charging, and
* negative for discharging.
*/
/* We're assuming two limits, mild and urgent. */
TEST_ASSERT(NUM_BATT_THRESHOLDS == 2);
/* Find out which is which */
if (batt_limits[0].hi_val > batt_limits[1].hi_val) {
h = &batt_limits[0];
l = &batt_limits[1];
} else {
h = &batt_limits[1];
l = &batt_limits[0];
}
/* Find a time longer than all sample count limits */
for (i = longtime = 0; i < NUM_BATT_THRESHOLDS; i++)
longtime = MAX(longtime,
MAX(batt_limits[i].lo_cnt,
batt_limits[i].hi_cnt));
longtime += 2;
test_reset_mocks();
TEST_ASSERT(ap_is_throttled == 0);
/* reset, by staying low for a long time */
for (i = 1; i < longtime; i++)
watch_battery_closely(&ctx);
TEST_ASSERT(l->triggered == 0);
TEST_ASSERT(ap_is_throttled == 0);
/* mock_batt() specifies the DISCHARGE current. Charging
* should do nothing, no matter how high. */
mock_batt(-(h->hi_val + 2));
for (i = 1; i < longtime; i++)
watch_battery_closely(&ctx);
TEST_ASSERT(l->triggered == 0);
TEST_ASSERT(ap_is_throttled == 0);
/* midrange for a long time shouldn't do anything */
mock_batt((l->lo_val + l->hi_val) / 2);
for (i = 1; i < longtime; i++)
watch_battery_closely(&ctx);
TEST_ASSERT(l->triggered == 0);
TEST_ASSERT(ap_is_throttled == 0);
/* above high limit for not quite long enough */
mock_batt(l->hi_val + 1);
for (i = 1; i < l->hi_cnt; i++)
watch_battery_closely(&ctx);
TEST_ASSERT(l->count != 0);
TEST_ASSERT(l->triggered == 0);
TEST_ASSERT(ap_is_throttled == 0);
/* drop below the high limit once */
mock_batt(l->hi_val - 1);
watch_battery_closely(&ctx);
TEST_ASSERT(l->count == 0);
TEST_ASSERT(l->triggered == 0);
TEST_ASSERT(ap_is_throttled == 0);
/* now back up */
mock_batt(l->hi_val + 1);
for (i = 1; i < l->hi_cnt; i++)
watch_battery_closely(&ctx);
TEST_ASSERT(l->count != 0);
TEST_ASSERT(l->triggered == 0);
TEST_ASSERT(ap_is_throttled == 0);
/* one more ought to do it */
watch_battery_closely(&ctx);
TEST_ASSERT(l->triggered == 1);
TEST_ASSERT(ap_is_throttled == 1);
/* going midrange for a long time shouldn't change anything */
mock_batt((l->lo_val + l->hi_val) / 2);
for (i = 1; i < longtime; i++)
watch_battery_closely(&ctx);
TEST_ASSERT(l->triggered == 1);
TEST_ASSERT(ap_is_throttled == 1);
/* charge for not quite long enough */
mock_batt(-1);
for (i = 1; i < l->lo_cnt; i++)
watch_battery_closely(&ctx);
TEST_ASSERT(l->triggered == 1);
TEST_ASSERT(ap_is_throttled == 1);
/* back above the low limit once */
mock_batt(l->lo_val + 1);
watch_battery_closely(&ctx);
TEST_ASSERT(l->triggered == 1);
TEST_ASSERT(ap_is_throttled == 1);
/* now charge again - that should have reset the count */
mock_batt(-1);
for (i = 1; i < l->lo_cnt; i++)
watch_battery_closely(&ctx);
TEST_ASSERT(l->triggered == 1);
TEST_ASSERT(ap_is_throttled == 1);
/* One more ought to do it */
watch_battery_closely(&ctx);
TEST_ASSERT(l->triggered == 0);
TEST_ASSERT(ap_is_throttled == 0);
/* Check the high limits too, just for fun */
mock_batt(h->hi_val + 1);
for (i = 1; i < h->hi_cnt; i++)
watch_battery_closely(&ctx);
TEST_ASSERT(h->triggered == 0);
/* one more */
watch_battery_closely(&ctx);
TEST_ASSERT(h->triggered == 1);
TEST_ASSERT(ap_is_throttled == 1);
return EC_SUCCESS;
}
void run_test(void)
{
test_reset();
@@ -291,6 +429,7 @@ void run_test(void)
RUN_TEST(test_identification);
RUN_TEST(test_turbo);
RUN_TEST(test_thresholds);
RUN_TEST(test_batt);
test_print_result();
}

View File

@@ -15,5 +15,7 @@ extern struct adapter_limits
extern int ac_turbo;
extern int ap_is_throttled;
extern void check_threshold(int current, struct adapter_limits *lim);
extern struct adapter_limits batt_limits[NUM_BATT_THRESHOLDS];
extern void watch_battery_closely(struct power_state_context *ctx);
#endif /* __ADAPTER_EXTERNS_H */