summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2013-07-25 11:00:03 -0700
committerBill Richardson <wfrichar@chromium.org>2013-07-30 10:37:08 -0700
commit6a4c0ce1fffe582b612b06531145b0dede3b14cd (patch)
tree00e088f1c72de6d8aff87a023729b9f31792eeb6
parent9d2a38fcabf59273edfd5dbb17cddeb467b2a5a0 (diff)
downloadchrome-ec-6a4c0ce1fffe582b612b06531145b0dede3b14cd.tar.gz
Add turbo mode charger support, and tests for it.
Some chargers can run in a "turbo" mode, which lets it draw from the battery to provide extra power to the AP in short bursts. In order for this to work properly, the EC has to watch the current closely to make sure specific limits are observed. It also has to recognize specific adapters, since those limits vary depending on the rated power that the adapter can provide. This adds the basic functionality, plus a test for it. BUG=chrome-os-partner:20739 BRANCH=falco,peppy TEST=manual make BOARD=${BOARD} runtests On Falco, you can also use the "adapter" EC command to see what's going on. Try replacing the adapters and running that command to be sure they're correctly identified, too: > adapter Adapter 65W (590mv), turbo 1, AP_throttled 0 > We currently support 45W, 65W, and 90W adapters. Unknown adapters are treated as 65W, but don't enable turbo mode. Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/63372 Reviewed-by: Aaron Durbin <adurbin@chromium.org> Reviewed-by: Randall Spangler <rspangler@chromium.org> (cherry picked from commit 298a7276c27d1a7b6402dadfc1b0f2f45247ae2d) Change-Id: I73dc8a03734f67713b0b89af6f382af4b5d28118 Reviewed-on: https://gerrit.chromium.org/gerrit/63735 Commit-Queue: Bill Richardson <wfrichar@chromium.org> Reviewed-by: Bill Richardson <wfrichar@chromium.org> Tested-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r--board/falco/board.h1
-rw-r--r--board/host/board.h12
-rw-r--r--common/build.mk3
-rw-r--r--common/charge_state.c5
-rw-r--r--common/extpower_falco.c257
-rw-r--r--include/config.h3
-rw-r--r--include/extpower.h5
-rw-r--r--include/extpower_falco.h45
-rw-r--r--test/adapter.c296
-rw-r--r--test/adapter.tasklist17
-rw-r--r--test/adapter_externs.h19
-rw-r--r--test/build.mk3
-rw-r--r--test/test_config.mk1
13 files changed, 645 insertions, 22 deletions
diff --git a/board/falco/board.h b/board/falco/board.h
index 04bdc86d8c..cb5190f8b3 100644
--- a/board/falco/board.h
+++ b/board/falco/board.h
@@ -19,6 +19,7 @@
#define CONFIG_CHIPSET_HASWELL
#define CONFIG_CHIPSET_X86
#endif
+#define CONFIG_EXTPOWER_FALCO
#define CONFIG_EXTPOWER_GPIO
#define CONFIG_KEYBOARD_BOARD_CONFIG
#ifdef HAS_TASK_KEYPROTO
diff --git a/board/host/board.h b/board/host/board.h
index fe0d29e069..f84696d58b 100644
--- a/board/host/board.h
+++ b/board/host/board.h
@@ -29,6 +29,11 @@
#define CONFIG_KEYBOARD_PROTOCOL_MKBP
#endif
+/* Turbo-mode charger tests */
+#ifdef TEST_EXTPOWER_FALCO
+#define CONFIG_EXTPOWER_FALCO
+#endif
+
#define CONFIG_WP_ACTIVE_HIGH
enum gpio_signal {
@@ -50,4 +55,11 @@ enum temp_sensor_id {
TEMP_SENSOR_COUNT
};
+enum adc_channel {
+ ADC_CH_CHARGER_CURRENT,
+ ADC_AC_ADAPTER_ID_VOLTAGE,
+
+ ADC_CH_COUNT
+};
+
#endif /* __BOARD_H */
diff --git a/common/build.mk b/common/build.mk
index 6596216232..73912d0afe 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -12,7 +12,7 @@ common-y+=gpio_common.o version.o printf.o queue.o
common-$(BOARD_bolt)+=battery_link.o
common-$(BOARD_daisy)+=extpower_snow.o
-common-$(BOARD_falco)+=battery_falco.o extpower_falco.o led_falco.o
+common-$(BOARD_falco)+=battery_falco.o led_falco.o
common-$(BOARD_link)+=battery_link.o
common-$(BOARD_peppy)+=battery_peppy.o led_peppy.o
common-$(BOARD_slippy)+=battery_slippy.o
@@ -35,6 +35,7 @@ common-$(CONFIG_CHIPSET_IVYBRIDGE)+=chipset_ivybridge.o
common-$(CONFIG_CHIPSET_X86)+=chipset_x86_common.o
common-$(CONFIG_PMU_TPS65090)+=pmu_tps65090.o
common-$(CONFIG_EOPTION)+=eoption.o
+common-$(CONFIG_EXTPOWER_FALCO)+=extpower_falco.o
common-$(CONFIG_EXTPOWER_GPIO)+=extpower_gpio.o
common-$(CONFIG_EXTPOWER_USB)+=extpower_usb.o
common-$(CONFIG_FLASH)+=flash_common.o
diff --git a/common/charge_state.c b/common/charge_state.c
index 49630ff7be..c234ced434 100644
--- a/common/charge_state.c
+++ b/common/charge_state.c
@@ -759,6 +759,11 @@ void charger_task(void)
sleep_usec = POLL_PERIOD_SHORT;
}
+#ifdef CONFIG_EXTPOWER_FALCO
+ watch_adapter_closely(ctx);
+ sleep_usec = EXTPOWER_FALCO_POLL_PERIOD;
+#endif
+
/* Show charging progress in console */
charging_progress(ctx);
diff --git a/common/extpower_falco.c b/common/extpower_falco.c
index 04e86abadf..dec406703b 100644
--- a/common/extpower_falco.c
+++ b/common/extpower_falco.c
@@ -3,47 +3,264 @@
* found in the LICENSE file.
*/
-/* Special AC Adapter logic for Falco */
+/*
+ * Falco adapters can support "charger hybrid turbo boost" mode and other
+ * buzzwords. The limits vary depending on each adapter's power rating, so we
+ * need to watch for changes and adjust the limits and high-current thresholds
+ * accordingly. If we go over, the AP needs to throttle itself. The EC's
+ * charging state logic isn't affected, just the AP's P-State. We try to save
+ * PROCHOT as a last resort.
+ */
+
+#include <limits.h> /* part of the compiler */
#include "adc.h"
+#include "charge_state.h"
+#include "charger.h"
+#include "charger_bq24738.h"
+#include "chipset.h"
#include "common.h"
#include "console.h"
+#include "extpower.h"
+#include "extpower_falco.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "smart_battery.h"
#include "util.h"
-enum adapter_type {
- ADAPTER_UNKNOWN,
- ADAPTER_45W,
- ADAPTER_65W,
- ADAPTER_90W,
-};
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_CHARGER, outstr)
+#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args)
-static const char * const adapter_str[] = {
+/* Values for our supported adapters */
+static const char * const ad_name[] = {
"unknown",
"45W",
"65W",
"90W"
};
+BUILD_ASSERT(ARRAY_SIZE(ad_name) == NUM_ADAPTER_TYPES);
+
+test_export_static
+struct adapter_id_vals ad_id_vals[] = {
+ /* mV low, mV high */
+ {INT_MIN, INT_MAX}, /* anything = ADAPTER_UNKNOWN */
+ {434, 554}, /* ADAPTER_45W */
+ {561, 717}, /* ADAPTER_65W */
+ {725, 925} /* ADAPTER_90W */
+};
+BUILD_ASSERT(ARRAY_SIZE(ad_id_vals) == NUM_ADAPTER_TYPES);
+
+test_export_static
+int ad_input_current[][NUM_AC_TURBO_STATES] = {
+ /* Current limits in mA, for turbo off and turbo on. In hex,
+ * because the BQ24738 Input Current Register masks off bits 6-0.
+ * FIXME: That constraint may vary with other chargers. */
+ {0x0a00, 0x0a00}, /* ADAPTER_UNKNOWN */
+ {0x0a00, 0x0800}, /* ADAPTER_45W */
+ {0x0a00, 0x0c00}, /* ADAPTER_65W */
+ {0x0f00, 0x1100} /* ADAPTER_90W */
+};
+BUILD_ASSERT(ARRAY_SIZE(ad_input_current) == NUM_ADAPTER_TYPES);
+
+test_export_static
+struct adapter_limits ad_limits[][NUM_AC_TURBO_STATES][NUM_AC_THRESHOLDS] = {
+ /* ADAPTER_UNKNOWN - treat as 65W, no turbo */
+ {
+ /* Turbo off */
+ {
+ { 3080, 2730, 16, 80, },
+ { 3280, 2930, 1, 80, },
+ },
+ /* Turbo on - unused, except for testing */
+ {
+ { 3080, 2730, 16, 80, },
+ { 3280, 2930, 1, 80, },
+ }
+ },
+ /* ADAPTER_45W */
+ {
+ /* Turbo off */
+ {
+ { 2050, 1700, 16, 80, },
+ { 2260, 1910, 1, 80, },
+ },
+ /* Turbo on */
+ {
+ { 2310, 1960, 16, 80, },
+ { 2560, 2210, 1, 80, },
+ }
+ },
+ /* ADAPTER_65W */
+ {
+ /* Turbo off */
+ {
+ { 3080, 2730, 16, 80, },
+ { 3280, 2930, 1, 80, },
+ },
+ /* Turbo on */
+ {
+ { 3330, 2980, 16, 80, },
+ { 3590, 3240, 1, 80, },
+ }
+ },
+ /* ADAPTER_90W */
+ {
+ /* Turbo off */
+ {
+ { 4360, 4010, 16, 80, },
+ { 4560, 4210, 1, 80, },
+ },
+ /* Turbo on */
+ {
+ { 4620, 4270, 16, 80, },
+ { 4870, 4520, 1, 80, },
+ }
+ }
+};
+BUILD_ASSERT(ARRAY_SIZE(ad_limits) == NUM_ADAPTER_TYPES);
+
+static int last_mv;
static enum adapter_type identify_adapter(void)
{
- int mv;
- mv = adc_read_channel(ADC_AC_ADAPTER_ID_VOLTAGE);
- if (mv >= 434 && mv <= 554)
- return ADAPTER_45W;
- if (mv >= 561 && mv <= 717)
- return ADAPTER_65W;
- if (mv >= 725 && mv <= 925)
- return ADAPTER_90W;
-
- return ADAPTER_UNKNOWN;
+ int i;
+ last_mv = adc_read_channel(ADC_AC_ADAPTER_ID_VOLTAGE);
+
+ /* ADAPTER_UNKNOWN matches everything, so search backwards */
+ for (i = NUM_ADAPTER_TYPES - 1; i >= 0; i--)
+ if (last_mv >= ad_id_vals[i].lo && last_mv <= ad_id_vals[i].hi)
+ return i;
+
+ return ADAPTER_UNKNOWN; /* should never get here */
+}
+
+test_export_static enum adapter_type ac_adapter;
+static void ac_change_callback(void)
+{
+ if (extpower_is_present()) {
+ ac_adapter = identify_adapter();
+ CPRINTF("[%T AC Adapter is %s (%dmv)]\n",
+ ad_name[ac_adapter], last_mv);
+ } else {
+ ac_adapter = ADAPTER_UNKNOWN;
+ CPRINTF("[%T AC Adapter is not present]\n");
+ /* Charger unavailable. Clear local flags */
+ }
+}
+DECLARE_HOOK(HOOK_AC_CHANGE, ac_change_callback, HOOK_PRIO_DEFAULT);
+
+test_export_static int ac_turbo = -1;
+static void set_turbo(int on)
+{
+ int tmp, r;
+
+ if (ac_turbo == on)
+ return;
+
+ CPRINTF("[%T turbo mode => %d]\n", on);
+
+ /* Set/clear turbo mode in charger */
+ r = charger_get_option(&tmp);
+ if (r != EC_SUCCESS)
+ goto bad;
+
+ if (on)
+ tmp |= OPTION_BOOST_MODE_ENABLE;
+ else
+ tmp &= ~OPTION_BOOST_MODE_ENABLE;
+
+ r = charger_set_option(tmp);
+ if (r != EC_SUCCESS)
+ goto bad;
+
+ /* Set allowed Io based on adapter */
+ r = charger_set_input_current(ad_input_current[ac_adapter][on]);
+ if (r != EC_SUCCESS)
+ goto bad;
+
+ ac_turbo = on;
+ return;
+bad:
+ CPRINTF("[%T ERROR: can't talk to charger: %d]\n", r);
+}
+
+test_export_static int ap_is_throttled;
+static void set_throttle(int on)
+{
+ ap_is_throttled = on;
+}
+
+test_export_static
+void check_threshold(int current, struct adapter_limits *lim)
+{
+ if (lim->triggered) {
+ /* watching for current to drop */
+ if (current < lim->lo_val) {
+ if (++lim->count >= lim->lo_cnt) {
+ set_throttle(0);
+ lim->count = 0;
+ lim->triggered = 0;
+ }
+ } else {
+ lim->count = 0;
+ }
+ } else {
+ /* watching for current to rise */
+ if (current > lim->hi_val) {
+ if (++lim->count >= lim->hi_cnt) {
+ set_throttle(1);
+ lim->count = 0;
+ lim->triggered = 1;
+ }
+ } else {
+ lim->count = 0;
+ }
+ }
+}
+
+void watch_adapter_closely(struct power_state_context *ctx)
+{
+ int current, i;
+
+ /* 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. */
+ if (extpower_is_present()) {
+ if (ctx->curr.error ||
+ ctx->curr.batt.state_of_charge < 10 ||
+ ac_adapter == ADAPTER_UNKNOWN) {
+ set_turbo(0);
+ } else if (ctx->curr.batt.state_of_charge > 15) {
+ set_turbo(1);
+ }
+ }
+
+ /* If the AP is off, we won't need to throttle it. */
+ if (chipset_in_state(CHIPSET_STATE_ANY_OFF |
+ CHIPSET_STATE_SUSPEND))
+ return;
+
+ /* And if we're not on AC, we can't monitor the current. */
+ if (!extpower_is_present()) {
+ ac_turbo = -1; /* watch for its return */
+ return;
+ }
+
+ /* Check all the thresholds. */
+ current = adc_read_channel(ADC_CH_CHARGER_CURRENT);
+ for (i = 0; i < NUM_AC_THRESHOLDS; i++)
+ check_threshold(current, &ad_limits[ac_adapter][ac_turbo][i]);
}
static int command_adapter(int argc, char **argv)
{
- ccprintf("%s\n", adapter_str[identify_adapter()]);
+ enum adapter_type v = identify_adapter();
+ ccprintf("Adapter %s (%dmv), turbo %d, AP_throttled %d\n",
+ ad_name[v], last_mv, ac_turbo, ap_is_throttled);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(adapter, command_adapter,
NULL,
- "Identify AC adapter type",
+ "Display AC adapter information",
NULL);
diff --git a/include/config.h b/include/config.h
index 8cd5ffcea4..f0a0f08614 100644
--- a/include/config.h
+++ b/include/config.h
@@ -242,6 +242,9 @@
*/
#undef CONFIG_EOPTION
+/* Compile support for handling turbo-mode chargers */
+#undef CONFIG_EXTPOWER_FALCO
+
/* Compile support for detecting external power presence via a GPIO */
#undef CONFIG_EXTPOWER_GPIO
diff --git a/include/extpower.h b/include/extpower.h
index 36c5535633..1b51f3fa83 100644
--- a/include/extpower.h
+++ b/include/extpower.h
@@ -27,4 +27,9 @@ int extpower_is_present(void);
*/
void extpower_interrupt(enum gpio_signal signal);
+#ifdef CONFIG_EXTPOWER_FALCO
+/* Adapter-specific logic */
+#include "extpower_falco.h"
+#endif
+
#endif /* __CROS_EC_EXTPOWER_H */
diff --git a/include/extpower_falco.h b/include/extpower_falco.h
new file mode 100644
index 0000000000..6731b9f0c6
--- /dev/null
+++ b/include/extpower_falco.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2013 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.
+ */
+
+/* We can do even smarter charging if we can identify the AC adapter */
+
+#ifndef __CROS_EC_EXTPOWER_FALCO_H
+#define __CROS_EC_EXTPOWER_FALCO_H
+
+#include "charge_state.h"
+
+/* Supported adapters */
+enum adapter_type {
+ ADAPTER_UNKNOWN = 0,
+ ADAPTER_45W,
+ ADAPTER_65W,
+ ADAPTER_90W,
+ NUM_ADAPTER_TYPES
+};
+
+/* Adapter identification values */
+struct adapter_id_vals {
+ int lo, hi;
+};
+
+/* Adapter-specific parameters. */
+struct adapter_limits {
+ int hi_val, lo_val; /* current thresholds (mA) */
+ int hi_cnt, lo_cnt; /* count needed to trigger */
+ int count; /* samples past the limit */
+ int triggered; /* threshold reached */
+};
+
+/* Rate at which adapter samples are collected. */
+#define EXTPOWER_FALCO_POLL_PERIOD (MSEC * 100)
+
+/* Number of special states */
+#define NUM_AC_TURBO_STATES 2
+#define NUM_AC_THRESHOLDS 2
+
+/* Change turbo mode or throttle the AP depending on the adapter state. */
+void watch_adapter_closely(struct power_state_context *ctx);
+
+#endif /* __CROS_EC_EXTPOWER_FALCO_H */
diff --git a/test/adapter.c b/test/adapter.c
new file mode 100644
index 0000000000..4a2af66404
--- /dev/null
+++ b/test/adapter.c
@@ -0,0 +1,296 @@
+/* Copyright (c) 2013 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.
+ *
+ * Test GPIO extpower module.
+ */
+
+#include "adc.h"
+#include "common.h"
+#include "console.h"
+#include "extpower.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "test_util.h"
+#include "timer.h"
+#include "util.h"
+#include "chipset.h"
+#include "chipset_x86_common.h"
+
+/* Normally private stuff from the modules we're going to test */
+#include "adapter_externs.h"
+
+/* Local state */
+static int mock_ac;
+static int mock_id;
+static int mock_current;
+static struct power_state_context ctx;
+
+static void test_reset_mocks(void)
+{
+ mock_ac = 0;
+ mock_id = 0;
+ mock_current = 0;
+ memset(&ctx, 0, sizeof(ctx));
+}
+
+/* Mocked functions from the rest of the EC */
+
+int gpio_get_level(enum gpio_signal signal)
+{
+ if (signal == GPIO_AC_PRESENT)
+ return mock_ac;
+ return 0;
+}
+
+int adc_read_channel(enum adc_channel ch)
+{
+ switch (ch) {
+ case ADC_AC_ADAPTER_ID_VOLTAGE:
+ return mock_id;
+ case ADC_CH_CHARGER_CURRENT:
+ return mock_current;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int charger_set_input_current(int input_current)
+{
+ return EC_SUCCESS;
+}
+
+int charger_get_option(int *option)
+{
+ return EC_SUCCESS;
+}
+
+
+int charger_set_option(int option)
+{
+ return EC_SUCCESS;
+}
+
+/* Local functions to control the mocked functions. */
+
+static void change_ac(int val)
+{
+ mock_ac = val;
+ extpower_interrupt(GPIO_AC_PRESENT);
+ msleep(50);
+}
+
+static void set_id(int val)
+{
+ mock_id = val;
+}
+
+
+/* And the tests themselves... */
+
+/*
+ * Run through the known ID ranges, making sure that values inside are
+ * correctly identified, and values outside are not. We'll skip the default
+ * ADAPTER_UNKNOWN range, of course.
+ *
+ * NOTE: This assumes that the ranges have a gap between them.
+ */
+static int test_identification(void)
+{
+ int i;
+
+ test_reset_mocks();
+
+ for (i = 1; i < NUM_ADAPTER_TYPES; i++) {
+
+ change_ac(0);
+ TEST_ASSERT(ac_adapter == ADAPTER_UNKNOWN);
+
+ set_id(ad_id_vals[i].lo - 1);
+ change_ac(1);
+ TEST_ASSERT(ac_adapter == ADAPTER_UNKNOWN);
+
+ change_ac(0);
+ TEST_ASSERT(ac_adapter == ADAPTER_UNKNOWN);
+
+ set_id(ad_id_vals[i].lo);
+ change_ac(1);
+ TEST_ASSERT(ac_adapter == i);
+
+ change_ac(0);
+ TEST_ASSERT(ac_adapter == ADAPTER_UNKNOWN);
+
+ set_id(ad_id_vals[i].hi);
+ change_ac(1);
+ TEST_ASSERT(ac_adapter == i);
+
+ change_ac(0);
+ TEST_ASSERT(ac_adapter == ADAPTER_UNKNOWN);
+
+ set_id(ad_id_vals[i].hi + 1);
+ change_ac(1);
+ TEST_ASSERT(ac_adapter == ADAPTER_UNKNOWN);
+ }
+
+ return EC_SUCCESS;
+}
+
+/* Helper function */
+static void test_turbo_init(void)
+{
+ /* Battery is awake and in good shape */
+ ctx.curr.error = 0;
+ ctx.curr.batt.state_of_charge = 25;
+
+ /* Adapter is present and known */
+ set_id(ad_id_vals[1].lo + 1);
+ change_ac(1);
+}
+
+/* Test all the things that can turn turbo mode on and off */
+static int test_turbo(void)
+{
+ test_reset_mocks();
+
+ /* There's only one path that can enable turbo. Check it first. */
+ test_turbo_init();
+ watch_adapter_closely(&ctx);
+ TEST_ASSERT(ac_turbo == 1);
+
+ /* Now test things that turn turbo off. */
+
+ test_turbo_init();
+ ctx.curr.error = 1;
+ watch_adapter_closely(&ctx);
+ TEST_ASSERT(ac_turbo == 0);
+
+ test_turbo_init();
+ ctx.curr.batt.state_of_charge = 5;
+ watch_adapter_closely(&ctx);
+ TEST_ASSERT(ac_turbo == 0);
+
+ test_turbo_init();
+ set_id(ad_id_vals[1].lo - 1);
+ change_ac(1);
+ watch_adapter_closely(&ctx);
+ TEST_ASSERT(ac_turbo == 0);
+
+ test_turbo_init();
+ change_ac(0);
+ watch_adapter_closely(&ctx);
+ TEST_ASSERT(ac_turbo == -1);
+
+ return EC_SUCCESS;
+}
+
+/* Check the detection logic on one set of struct adapter_limits */
+static int test_thresholds_sequence(int entry)
+{
+ struct adapter_limits *lim = &ad_limits[ac_adapter][ac_turbo][entry];
+ int longtime = MAX(lim->lo_cnt, lim->hi_cnt) + 2;
+ int i;
+
+ /* reset, by staying low for a long time */
+ mock_current = lim->lo_val - 1;
+ for (i = 1; i < longtime; i++)
+ check_threshold(mock_current, lim);
+ TEST_ASSERT(lim->triggered == 0);
+ TEST_ASSERT(ap_is_throttled == 0);
+
+ /* midrange for a long time shouldn't do anything */
+ mock_current = (lim->lo_val + lim->hi_val) / 2;
+ for (i = 1; i < longtime; i++)
+ check_threshold(mock_current, lim);
+ TEST_ASSERT(lim->triggered == 0);
+ TEST_ASSERT(ap_is_throttled == 0);
+
+ /* above high limit for not quite long enough */
+ mock_current = lim->hi_val + 1;
+ for (i = 1; i < lim->hi_cnt; i++)
+ check_threshold(mock_current, lim);
+ TEST_ASSERT(lim->triggered == 0);
+ TEST_ASSERT(ap_is_throttled == 0);
+
+ /* drop below the high limit once */
+ mock_current = lim->hi_val - 1;
+ check_threshold(mock_current, lim);
+ TEST_ASSERT(lim->triggered == 0);
+ TEST_ASSERT(ap_is_throttled == 0);
+
+ /* now back up - that should have reset the count */
+ mock_current = lim->hi_val + 1;
+ for (i = 1; i < lim->hi_cnt; i++)
+ check_threshold(mock_current, lim);
+ TEST_ASSERT(lim->triggered == 0);
+ TEST_ASSERT(ap_is_throttled == 0);
+
+ /* one more ought to do it */
+ check_threshold(mock_current, lim);
+ TEST_ASSERT(lim->triggered == 1);
+ TEST_ASSERT(ap_is_throttled == 1);
+
+ /* going midrange for a long time shouldn't change anything */
+ mock_current = (lim->lo_val + lim->hi_val) / 2;
+ for (i = 1; i < longtime; i++)
+ check_threshold(mock_current, lim);
+ TEST_ASSERT(lim->triggered == 1);
+ TEST_ASSERT(ap_is_throttled == 1);
+
+ /* below low limit for not quite long enough */
+ mock_current = lim->lo_val - 1;
+ for (i = 1; i < lim->lo_cnt; i++)
+ check_threshold(mock_current, lim);
+ TEST_ASSERT(lim->triggered == 1);
+ TEST_ASSERT(ap_is_throttled == 1);
+
+ /* back above the low limit once */
+ mock_current = lim->lo_val + 1;
+ check_threshold(mock_current, lim);
+ TEST_ASSERT(lim->triggered == 1);
+ TEST_ASSERT(ap_is_throttled == 1);
+
+ /* now back down - that should have reset the count */
+ mock_current = lim->lo_val - 1;
+ for (i = 1; i < lim->lo_cnt; i++)
+ check_threshold(mock_current, lim);
+ TEST_ASSERT(lim->triggered == 1);
+ TEST_ASSERT(ap_is_throttled == 1);
+
+ /* One more ought to do it */
+ check_threshold(mock_current, lim);
+ TEST_ASSERT(lim->triggered == 0);
+ TEST_ASSERT(ap_is_throttled == 0);
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Check all sets of thresholds. This probably doesn't add much value, but at
+ * least it ensures that they're somewhat sane.
+ */
+static int test_thresholds(void)
+{
+ int e;
+
+ for (ac_adapter = 0; ac_adapter < NUM_ADAPTER_TYPES; ac_adapter++)
+ for (ac_turbo = 0; ac_turbo < NUM_AC_TURBO_STATES; ac_turbo++)
+ for (e = 0; e < NUM_AC_THRESHOLDS; e++)
+ TEST_ASSERT(EC_SUCCESS ==
+ test_thresholds_sequence(e));
+
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ test_reset();
+
+ RUN_TEST(test_identification);
+ RUN_TEST(test_turbo);
+ RUN_TEST(test_thresholds);
+
+ test_print_result();
+}
diff --git a/test/adapter.tasklist b/test/adapter.tasklist
new file mode 100644
index 0000000000..c90588cbf5
--- /dev/null
+++ b/test/adapter.tasklist
@@ -0,0 +1,17 @@
+/* Copyright (c) 2013 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.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST /* No test tasks */
diff --git a/test/adapter_externs.h b/test/adapter_externs.h
new file mode 100644
index 0000000000..6e9d5fd1d3
--- /dev/null
+++ b/test/adapter_externs.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2013 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.
+ */
+
+#ifndef __ADAPTER_EXTERNS_H
+#define __ADAPTER_EXTERNS_H
+
+/* Normally private symbols from the modules that we're testing. */
+
+extern enum adapter_type ac_adapter;
+extern struct adapter_id_vals ad_id_vals[];
+extern struct adapter_limits
+ ad_limits[][NUM_AC_TURBO_STATES][NUM_AC_THRESHOLDS];
+extern int ac_turbo;
+extern int ap_is_throttled;
+extern void check_threshold(int current, struct adapter_limits *lim);
+
+#endif /* __ADAPTER_EXTERNS_H */
diff --git a/test/build.mk b/test/build.mk
index e2212fa71a..d3deafc668 100644
--- a/test/build.mk
+++ b/test/build.mk
@@ -31,8 +31,9 @@ test-list-$(BOARD_bolt)=
# Emulator tests
test-list-host=mutex pingpong utils kb_scan kb_mkbp lid_sw power_button hooks
test-list-host+=thermal flash queue kb_8042 extpwr_gpio console_edit system
-test-list-host+=sbs_charging
+test-list-host+=sbs_charging adapter
+adapter-y=adapter.o
console_edit-y=console_edit.o
extpwr_gpio-y=extpwr_gpio.o
flash-y=flash.o
diff --git a/test/test_config.mk b/test/test_config.mk
index 47cf63c64a..50214feed7 100644
--- a/test/test_config.mk
+++ b/test/test_config.mk
@@ -8,3 +8,4 @@
CFLAGS-kb_8042=-DTEST_KB_8042
CFLAGS-sbs_charging=-DTEST_SMART_BATTERY_CHARGER
+CFLAGS-adapter=-DTEST_EXTPOWER_FALCO