summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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