diff options
-rw-r--r-- | board/bolt/board.h | 1 | ||||
-rw-r--r-- | board/falco/board.h | 1 | ||||
-rw-r--r-- | board/host/chipset.c | 5 | ||||
-rw-r--r-- | board/link/board.h | 1 | ||||
-rw-r--r-- | board/peppy/board.h | 1 | ||||
-rw-r--r-- | board/slippy/board.h | 1 | ||||
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/chipset_haswell.c | 3 | ||||
-rw-r--r-- | common/extpower_falco.c | 36 | ||||
-rw-r--r-- | common/mock_charger.c | 3 | ||||
-rw-r--r-- | common/thermal.c | 9 | ||||
-rw-r--r-- | common/throttle_ap.c | 96 | ||||
-rw-r--r-- | include/chipset.h | 8 | ||||
-rw-r--r-- | include/config.h | 3 | ||||
-rw-r--r-- | include/throttle_ap.h | 51 | ||||
-rw-r--r-- | test/adapter.c | 205 | ||||
-rw-r--r-- | test/adapter.tasklist | 3 | ||||
-rw-r--r-- | test/build.mk | 3 | ||||
-rw-r--r-- | test/test_config.h | 15 | ||||
-rw-r--r-- | test/thermal.tasklist | 3 | ||||
-rw-r--r-- | test/thermal_falco.c | 874 | ||||
-rw-r--r-- | test/thermal_falco.tasklist | 19 | ||||
-rw-r--r-- | test/thermal_falco_externs.h | 15 |
23 files changed, 1322 insertions, 35 deletions
diff --git a/board/bolt/board.h b/board/bolt/board.h index a9c417f73b..98e8c3f42f 100644 --- a/board/bolt/board.h +++ b/board/bolt/board.h @@ -16,6 +16,7 @@ /* Optional features */ #define CONFIG_CHIPSET_X86 +#define CONFIG_CHIPSET_CAN_THROTTLE #define CONFIG_CUSTOM_KEYSCAN #define CONFIG_EXTPOWER_GPIO #define CONFIG_KEYBOARD_BOARD_CONFIG diff --git a/board/falco/board.h b/board/falco/board.h index 2c2e6a054a..4c9e2c0201 100644 --- a/board/falco/board.h +++ b/board/falco/board.h @@ -14,6 +14,7 @@ #define CONFIG_CHARGER #define CONFIG_CHARGER_BQ24738 #define CONFIG_CHARGER_DISCHARGE_ON_AC +#define CONFIG_CHIPSET_CAN_THROTTLE #define CONFIG_CHIPSET_HASWELL #define CONFIG_CHIPSET_X86 #define CONFIG_CMD_GSV diff --git a/board/host/chipset.c b/board/host/chipset.c index 4197c46326..678d5a97e0 100644 --- a/board/host/chipset.c +++ b/board/host/chipset.c @@ -21,6 +21,11 @@ test_mockable void chipset_reset(int cold_reset) fprintf(stderr, "Chipset reset!\n"); } +test_mockable void chipset_throttle_cpu(int throttle) +{ + /* Do nothing */ +} + test_mockable void chipset_force_shutdown(void) { /* Do nothing */ diff --git a/board/link/board.h b/board/link/board.h index 7d0a8ae268..668736b0b1 100644 --- a/board/link/board.h +++ b/board/link/board.h @@ -15,6 +15,7 @@ #define CONFIG_BOARD_VERSION #define CONFIG_CHARGER #define CONFIG_CHARGER_BQ24725 +#define CONFIG_CHIPSET_CAN_THROTTLE #define CONFIG_CHIPSET_IVYBRIDGE #define CONFIG_CHIPSET_X86 #define CONFIG_EXTPOWER_GPIO diff --git a/board/peppy/board.h b/board/peppy/board.h index 23f12fc3f0..ff51c73936 100644 --- a/board/peppy/board.h +++ b/board/peppy/board.h @@ -16,6 +16,7 @@ #define CONFIG_CHARGER #define CONFIG_CHARGER_BQ24707A #define CONFIG_CHARGER_DISCHARGE_ON_AC +#define CONFIG_CHIPSET_CAN_THROTTLE #define CONFIG_CHIPSET_HASWELL #define CONFIG_CHIPSET_X86 #define CONFIG_CMD_GSV diff --git a/board/slippy/board.h b/board/slippy/board.h index 118f346045..3ea81d97f4 100644 --- a/board/slippy/board.h +++ b/board/slippy/board.h @@ -16,6 +16,7 @@ #define CONFIG_CHARGER #define CONFIG_CHARGER_BQ24707A #define CONFIG_CHARGER_DISCHARGE_ON_AC +#define CONFIG_CHIPSET_CAN_THROTTLE #define CONFIG_CHIPSET_HASWELL #define CONFIG_CHIPSET_X86 #define CONFIG_CMD_GSV diff --git a/common/build.mk b/common/build.mk index a97d9fdf2e..3256982ef0 100644 --- a/common/build.mk +++ b/common/build.mk @@ -9,6 +9,7 @@ common-y=main.o util.o console_output.o uart_buffering.o common-y+=memory_commands.o shared_mem.o system_common.o hooks.o common-y+=gpio_common.o version.o printf.o queue.o getset.o +common-y+=throttle_ap.o common-$(BOARD_bolt)+=battery_link.o common-$(BOARD_daisy)+=extpower_snow.o diff --git a/common/chipset_haswell.c b/common/chipset_haswell.c index 1885f76d3c..fcdd00bdc4 100644 --- a/common/chipset_haswell.c +++ b/common/chipset_haswell.c @@ -106,7 +106,8 @@ void chipset_reset(int cold_reset) void chipset_throttle_cpu(int throttle) { - /* FIXME CPRINTF("[%T %s(%d)]\n", __func__, throttle);*/ + if (chipset_in_state(CHIPSET_STATE_ON)) + gpio_set_level(GPIO_CPU_PROCHOT, throttle); } enum x86_state x86_chipset_init(void) diff --git a/common/extpower_falco.c b/common/extpower_falco.c index 2730a06e3c..ff1166f778 100644 --- a/common/extpower_falco.c +++ b/common/extpower_falco.c @@ -26,6 +26,7 @@ #include "hooks.h" #include "host_command.h" #include "smart_battery.h" +#include "throttle_ap.h" #include "util.h" /* Console output macros */ @@ -194,21 +195,34 @@ 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) + +/* We need to OR all the possible reasons to throttle in order to decide + * whether it should happen or not. Use one bit per reason. + */ +#define BATT_REASON_OFFSET 0 +#define AC_REASON_OFFSET NUM_BATT_THRESHOLDS +BUILD_ASSERT(NUM_BATT_THRESHOLDS + NUM_AC_THRESHOLDS < 32); + +test_export_static uint32_t ap_is_throttled; +static void set_throttle(int on, int whosays) { - host_throttle_cpu(on); - ap_is_throttled = on; + if (on) + ap_is_throttled |= (1 << whosays); + else + ap_is_throttled &= ~(1 << whosays); + + throttle_ap(ap_is_throttled ? THROTTLE_ON : THROTTLE_OFF, + THROTTLE_SOFT, THROTTLE_SRC_POWER); } test_export_static -void check_threshold(int current, struct adapter_limits *lim) +void check_threshold(int current, struct adapter_limits *lim, int whoami) { if (lim->triggered) { /* watching for current to drop */ if (current < lim->lo_val) { if (++lim->count >= lim->lo_cnt) { - set_throttle(0); + set_throttle(0, whoami); lim->count = 0; lim->triggered = 0; } @@ -219,7 +233,7 @@ void check_threshold(int current, struct adapter_limits *lim) /* watching for current to rise */ if (current > lim->hi_val) { if (++lim->count >= lim->hi_cnt) { - set_throttle(1); + set_throttle(1, whoami); lim->count = 0; lim->triggered = 1; } @@ -251,7 +265,8 @@ void watch_battery_closely(struct power_state_context *ctx) /* Check limits against DISCHARGE current, not CHARGE current! */ for (i = 0; i < NUM_BATT_THRESHOLDS; i++) - check_threshold(-current, &batt_limits[i]); /* invert sign! */ + check_threshold(-current, &batt_limits[i], /* invert sign! */ + i + BATT_REASON_OFFSET); } void watch_adapter_closely(struct power_state_context *ctx) @@ -288,13 +303,14 @@ void watch_adapter_closely(struct power_state_context *ctx) /* 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]); + check_threshold(current, &ad_limits[ac_adapter][ac_turbo][i], + i + AC_REASON_OFFSET); } static int command_adapter(int argc, char **argv) { enum adapter_type v = identify_adapter(); - ccprintf("Adapter %s (%dmv), turbo %d, AP_throttled %d\n", + ccprintf("Adapter %s (%dmv), turbo %d, ap_is_throttled 0x%08x\n", ad_name[v], last_mv, ac_turbo, ap_is_throttled); return EC_SUCCESS; } diff --git a/common/mock_charger.c b/common/mock_charger.c index ebbf5e87bb..f63eebbea3 100644 --- a/common/mock_charger.c +++ b/common/mock_charger.c @@ -73,8 +73,9 @@ int charger_set_current(int current) if (current > info->current_max) current = info->current_max; + if (mock_current != current) + uart_printf("Charger set current: %d\n", current); mock_current = current; - uart_printf("Charger set current: %d\n", current); return EC_SUCCESS; } diff --git a/common/thermal.c b/common/thermal.c index 638bd0c0f7..c51b5dc01a 100644 --- a/common/thermal.c +++ b/common/thermal.c @@ -15,6 +15,7 @@ #include "host_command.h" #include "temp_sensor.h" #include "thermal.h" +#include "throttle_ap.h" #include "timer.h" #include "util.h" @@ -131,18 +132,18 @@ static void thermal_control(void) if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HIGH])) { CPRINTF("[%T thermal HIGH]\n"); - chipset_throttle_cpu(1); + throttle_ap(THROTTLE_ON, THROTTLE_HARD, THROTTLE_SRC_THERMAL); } else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_HIGH])) { CPRINTF("[%T thermal no longer high]\n"); - chipset_throttle_cpu(0); + throttle_ap(THROTTLE_OFF, THROTTLE_HARD, THROTTLE_SRC_THERMAL); } if (cond_went_true(&cond_hot[EC_TEMP_THRESH_WARN])) { CPRINTF("[%T thermal WARN]\n"); - host_throttle_cpu(1); + throttle_ap(THROTTLE_ON, THROTTLE_SOFT, THROTTLE_SRC_THERMAL); } else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_WARN])) { CPRINTF("[%T thermal no longer warn]\n"); - host_throttle_cpu(0); + throttle_ap(THROTTLE_OFF, THROTTLE_SOFT, THROTTLE_SRC_THERMAL); } /* Max fan needed is what's needed. */ diff --git a/common/throttle_ap.c b/common/throttle_ap.c new file mode 100644 index 0000000000..0c671842db --- /dev/null +++ b/common/throttle_ap.c @@ -0,0 +1,96 @@ +/* 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. + */ + +/* Common chipset throttling code for Chrome EC */ + +#include "chipset.h" +#include "common.h" +#include "console.h" +#include "host_command.h" +#include "task.h" +#include "throttle_ap.h" +#include "util.h" + +/* Console output macros */ +#define CPUTS(outstr) cputs(CC_CHIPSET, outstr) +#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args) + +/*****************************************************************************/ +/* This enforces the virtual OR of all throttling sources. */ +static struct mutex throttle_mutex; +static uint32_t throttle_request[NUM_THROTTLE_TYPES]; + +void throttle_ap(enum throttle_level level, + enum throttle_type type, + enum throttle_sources source) +{ + uint32_t tmpval, bitmask; + + mutex_lock(&throttle_mutex); + + bitmask = (1 << source); + + switch (level) { + case THROTTLE_ON: + throttle_request[type] |= bitmask; + break; + case THROTTLE_OFF: + throttle_request[type] &= ~bitmask; + break; + } + + tmpval = throttle_request[type]; /* save for printing */ + + switch (type) { + case THROTTLE_SOFT: +#ifdef HAS_TASK_HOSTCMD + host_throttle_cpu(tmpval); +#endif + break; + case THROTTLE_HARD: +#ifdef CONFIG_CHIPSET_CAN_THROTTLE + chipset_throttle_cpu(tmpval); +#endif + break; + + case NUM_THROTTLE_TYPES: + /* Make the compiler shut up. Don't use 'default', because + * we still want to catch any new types. + */ + break; + } + + mutex_unlock(&throttle_mutex); + + /* print outside the mutex */ + CPRINTF("[%T set AP throttling type %d to %s (0x%08x)]\n", + type, tmpval ? "on" : "off", tmpval); + +} + +/*****************************************************************************/ +/* Console commands */ + +static int command_apthrottle(int argc, char **argv) +{ + int i; + uint32_t tmpval; + + for (i = 0; i < NUM_THROTTLE_TYPES; i++) { + mutex_lock(&throttle_mutex); + tmpval = throttle_request[i]; + mutex_unlock(&throttle_mutex); + + ccprintf("AP throttling type %d is %s (0x%08x)\n", i, + tmpval ? "on" : "off", tmpval); + } + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(apthrottle, command_apthrottle, + NULL, + "Display the AP throttling state", + NULL); + diff --git a/include/chipset.h b/include/chipset.h index 03f5ef4318..932f1aec3e 100644 --- a/include/chipset.h +++ b/include/chipset.h @@ -63,15 +63,13 @@ void chipset_exit_hard_off(void); static inline void chipset_exit_hard_off(void) { } #endif -/** - * Enable/disable CPU throttling. - * - * @param throttle Enable (!=0) or disable(0) throttling +/* This is a private chipset-specific implementation for use only by + * throttle_ap() . Don't call this directly! */ void chipset_throttle_cpu(int throttle); /** - * Immedaitely shut off power to main processor and chipset. + * Immediately shut off power to main processor and chipset. * * This is intended for use when the system is too hot or battery power is * critical. diff --git a/include/config.h b/include/config.h index 8b5e2a9214..a5263c1985 100644 --- a/include/config.h +++ b/include/config.h @@ -181,6 +181,9 @@ #define CONFIG_CHIPSET_HAS_PP1350 #define CONFIG_CHIPSET_HAS_PP5000 +/* Compile support for chipset throttling */ +#undef CONFIG_CHIPSET_CAN_THROTTLE + /*****************************************************************************/ /* * Optional console commands diff --git a/include/throttle_ap.h b/include/throttle_ap.h new file mode 100644 index 0000000000..e66a5c6bec --- /dev/null +++ b/include/throttle_ap.h @@ -0,0 +1,51 @@ +/* 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 interface to throttle the AP */ + +#ifndef __CROS_EC_THROTTLE_AP_H +#define __CROS_EC_THROTTLE_AP_H + +/** + * Level of throttling desired. + */ +enum throttle_level { + THROTTLE_OFF = 0, + THROTTLE_ON, +}; + +/** + * Types of throttling desired. These are independent. + */ +enum throttle_type { + THROTTLE_SOFT = 0, /* for example, host events */ + THROTTLE_HARD, /* for example, PROCHOT */ + NUM_THROTTLE_TYPES +}; + +/** + * Possible sources for CPU throttling requests. + */ +enum throttle_sources { + THROTTLE_SRC_THERMAL = 0, + THROTTLE_SRC_POWER, +}; + + +/** + * Enable/disable CPU throttling. + * + * This is a virtual "OR" operation. Any caller can enable CPU throttling of + * any type, but all callers must agree in order to disable that type. + * + * @param level Level of throttling desired + * @param type Type of throttling desired + * @param source Which task is requesting throttling + */ +void throttle_ap(enum throttle_level level, + enum throttle_type type, + enum throttle_sources source); + +#endif /* __CROS_EC_THROTTLE_AP_H */ diff --git a/test/adapter.c b/test/adapter.c index de927343b6..4a93e6089a 100644 --- a/test/adapter.c +++ b/test/adapter.c @@ -240,34 +240,34 @@ static int test_thresholds_sequence(int entry) /* one more ought to do it */ check_threshold(mock_current, lim); TEST_ASSERT(lim->triggered == 1); - TEST_ASSERT(ap_is_throttled == 1); + TEST_ASSERT(ap_is_throttled); /* 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); + TEST_ASSERT(ap_is_throttled); /* 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); + TEST_ASSERT(ap_is_throttled); /* 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); + TEST_ASSERT(ap_is_throttled); /* 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); + TEST_ASSERT(ap_is_throttled); /* One more ought to do it */ check_threshold(mock_current, lim); @@ -374,34 +374,34 @@ static int test_batt(void) /* one more ought to do it */ watch_battery_closely(&ctx); TEST_ASSERT(l->triggered == 1); - TEST_ASSERT(ap_is_throttled == 1); + TEST_ASSERT(ap_is_throttled); /* 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); + TEST_ASSERT(ap_is_throttled); /* 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); + TEST_ASSERT(ap_is_throttled); /* 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); + TEST_ASSERT(ap_is_throttled); /* 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); + TEST_ASSERT(ap_is_throttled); /* One more ought to do it */ watch_battery_closely(&ctx); @@ -416,20 +416,203 @@ static int test_batt(void) /* one more */ watch_battery_closely(&ctx); TEST_ASSERT(h->triggered == 1); - TEST_ASSERT(ap_is_throttled == 1); + TEST_ASSERT(ap_is_throttled); return EC_SUCCESS; } +static int test_batt_vs_adapter(void) +{ + /* Only need one set of adapter thresholds for this test */ + struct adapter_limits *a_lim; + + /* Same structs are used for battery thresholds */ + struct adapter_limits *b_lim; + + int longtime; + int i; + + /* For adapter, we'll use ADAPTER_UNKNOWN, Turbo off, softer limits */ + a_lim = &ad_limits[0][0][0]; + + /* 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. We want the mild one. */ + if (batt_limits[0].hi_val > batt_limits[1].hi_val) + b_lim = &batt_limits[1]; + else + b_lim = &batt_limits[0]; + + + /* DANGER: we need these two to not be in sync. */ + TEST_ASSERT(a_lim->hi_cnt == 16); + TEST_ASSERT(b_lim->hi_cnt == 16); + /* Now that we know they're the same, let's change one */ + b_lim->hi_cnt = 12; + + /* DANGER: We also rely on these values */ + TEST_ASSERT(a_lim->lo_cnt == 80); + TEST_ASSERT(b_lim->lo_cnt == 50); + + + /* Find a time longer than all sample count limits */ + longtime = MAX(MAX(a_lim->lo_cnt, a_lim->hi_cnt), + MAX(b_lim->lo_cnt, b_lim->hi_cnt)) + 2; + + + test_reset_mocks(); /* everything == 0 */ + ctx.curr.batt.state_of_charge = 25; + change_ac(1); + + /* make sure no limits are triggered by staying low for a long time */ + mock_current = a_lim->lo_val - 1; /* charger current is safe */ + mock_batt(-1); /* battery is charging */ + + for (i = 1; i < longtime; i++) + watch_adapter_closely(&ctx); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(b_lim->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + + + /* battery discharge current is too high for almost long enough */ + mock_batt(b_lim->hi_val + 1); + for (i = 1; i < b_lim->hi_cnt; i++) + watch_adapter_closely(&ctx); + TEST_ASSERT(b_lim->count != 0); + TEST_ASSERT(b_lim->triggered == 0); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + + /* one more ought to do it */ + watch_adapter_closely(&ctx); + TEST_ASSERT(b_lim->triggered == 1); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(ap_is_throttled); + + + /* charge for not quite long enough */ + mock_batt(-1); + for (i = 1; i < b_lim->lo_cnt; i++) + watch_adapter_closely(&ctx); + TEST_ASSERT(b_lim->triggered == 1); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(ap_is_throttled); + + /* and one more */ + watch_adapter_closely(&ctx); + TEST_ASSERT(b_lim->triggered == 0); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + + + /* Now adapter current is too high for almost long enough */ + mock_current = a_lim->hi_val + 1; + for (i = 1; i < a_lim->hi_cnt; i++) + watch_adapter_closely(&ctx); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(b_lim->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + + /* one more ought to do it */ + watch_adapter_closely(&ctx); + TEST_ASSERT(a_lim->triggered == 1); + TEST_ASSERT(b_lim->triggered == 0); + TEST_ASSERT(ap_is_throttled); + + /* below low limit for not quite long enough */ + mock_current = a_lim->lo_val - 1; + for (i = 1; i < a_lim->lo_cnt; i++) + watch_adapter_closely(&ctx); + TEST_ASSERT(a_lim->triggered == 1); + TEST_ASSERT(b_lim->triggered == 0); + TEST_ASSERT(ap_is_throttled); + + /* One more ought to do it */ + watch_adapter_closely(&ctx); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(b_lim->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + + + /* Now both are high, but battery hi_cnt is shorter */ + mock_batt(b_lim->hi_val + 1); + mock_current = a_lim->hi_val + 1; + for (i = 1; i < b_lim->hi_cnt; i++) /* just under battery limit */ + watch_adapter_closely(&ctx); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(b_lim->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + + /* one more should kick the battery threshold */ + watch_adapter_closely(&ctx); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(b_lim->triggered == 1); + TEST_ASSERT(ap_is_throttled); + + /* don't quite reach the adapter threshold */ + for (i = 1; i < a_lim->hi_cnt - b_lim->hi_cnt; i++) + watch_adapter_closely(&ctx); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(b_lim->triggered == 1); + TEST_ASSERT(ap_is_throttled); + + /* okay, now */ + watch_adapter_closely(&ctx); + TEST_ASSERT(a_lim->triggered == 1); + TEST_ASSERT(b_lim->triggered == 1); + TEST_ASSERT(ap_is_throttled); + + /* Leave battery high, let the adapter come down */ + mock_current = a_lim->lo_val - 1; + for (i = 1; i < a_lim->lo_cnt; i++) + watch_adapter_closely(&ctx); + TEST_ASSERT(a_lim->triggered == 1); + TEST_ASSERT(b_lim->triggered == 1); + TEST_ASSERT(ap_is_throttled); + + /* One more ought to do it for the adapter */ + watch_adapter_closely(&ctx); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(b_lim->triggered == 1); + TEST_ASSERT(ap_is_throttled); + + /* now charge the battery again */ + mock_batt(-1); + for (i = 1; i < b_lim->lo_cnt; i++) + watch_adapter_closely(&ctx); + TEST_ASSERT(b_lim->triggered == 1); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(ap_is_throttled); + + /* and one more */ + watch_adapter_closely(&ctx); + TEST_ASSERT(b_lim->triggered == 0); + TEST_ASSERT(a_lim->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + + return EC_SUCCESS; +} + + void run_test(void) { test_reset(); + test_chipset_on(); RUN_TEST(test_identification); RUN_TEST(test_turbo); RUN_TEST(test_thresholds); RUN_TEST(test_batt); + RUN_TEST(test_batt_vs_adapter); + test_print_result(); } diff --git a/test/adapter.tasklist b/test/adapter.tasklist index c90588cbf5..8ef20bab32 100644 --- a/test/adapter.tasklist +++ b/test/adapter.tasklist @@ -14,4 +14,5 @@ * '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 */ +#define CONFIG_TEST_TASK_LIST \ + TASK_TEST(CHIPSET, chipset_task, NULL, TASK_STACK_SIZE) diff --git a/test/build.mk b/test/build.mk index 470e74dc75..e38e0ee11c 100644 --- a/test/build.mk +++ b/test/build.mk @@ -30,7 +30,7 @@ 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 adapter host_command +test-list-host+=sbs_charging adapter host_command thermal_falco adapter-y=adapter.o console_edit-y=console_edit.o @@ -51,6 +51,7 @@ sbs_charging-y=sbs_charging.o stress-y=stress.o system-y=system.o thermal-y=thermal.o +thermal_falco-y=thermal_falco.o timer_calib-y=timer_calib.o timer_dos-y=timer_dos.o utils-y=utils.o diff --git a/test/test_config.h b/test/test_config.h index 0dd63b9dd8..0310e80e40 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -26,11 +26,26 @@ int board_discharge_on_ac(int enabled); #endif #ifdef TEST_adapter +#define CONFIG_CHIPSET_CAN_THROTTLE #define CONFIG_EXTPOWER_FALCO #endif #ifdef TEST_thermal +#define CONFIG_CHIPSET_CAN_THROTTLE #define CONFIG_TEMP_SENSOR #endif +#ifdef TEST_thermal_falco +#define CONFIG_BATTERY_MOCK +#define CONFIG_BATTERY_SMART +#define CONFIG_CHARGER +#define CONFIG_CHARGER_INPUT_CURRENT 4032 +#define CONFIG_CHIPSET_CAN_THROTTLE +#define CONFIG_EXTPOWER_FALCO +#define CONFIG_TEMP_SENSOR +#define I2C_PORT_BATTERY 1 +#define I2C_PORT_CHARGER 1 +#define I2C_PORT_HOST 1 +#endif + #endif /* __CROS_EC_TEST_CONFIG_H */ diff --git a/test/thermal.tasklist b/test/thermal.tasklist index aaa89ce45c..8ef20bab32 100644 --- a/test/thermal.tasklist +++ b/test/thermal.tasklist @@ -14,4 +14,5 @@ * '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 */ +#define CONFIG_TEST_TASK_LIST \ + TASK_TEST(CHIPSET, chipset_task, NULL, TASK_STACK_SIZE) diff --git a/test/thermal_falco.c b/test/thermal_falco.c new file mode 100644 index 0000000000..0d56f4fe16 --- /dev/null +++ b/test/thermal_falco.c @@ -0,0 +1,874 @@ +/* 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 thermal engine. + */ + +#include "battery_pack.h" +#include "chipset.h" +#include "common.h" +#include "console.h" +#include "extpower.h" +#include "extpower_falco.h" +#include "fan.h" +#include "hooks.h" +#include "host_command.h" +#include "printf.h" +#include "smart_battery.h" +#include "temp_sensor.h" +#include "test_util.h" +#include "thermal.h" +#include "throttle_ap.h" +#include "timer.h" +#include "util.h" + +/* Normally private stuff from the modules we're going to test */ +#include "thermal_falco_externs.h" + + +/*****************************************************************************/ +/* Exported data */ + +struct ec_thermal_config thermal_params[TEMP_SENSOR_COUNT]; + +/* The tests below make some assumptions. */ +BUILD_ASSERT(TEMP_SENSOR_COUNT == 4); +BUILD_ASSERT(EC_TEMP_THRESH_COUNT == 3); + +/*****************************************************************************/ +/* Mock functions */ + +static int mock_temp[TEMP_SENSOR_COUNT]; +static int host_throttled; +static int cpu_throttled; +static int cpu_shutdown; +static int fan_pct; +static int no_temps_read; +static int mock_id; +static int mock_ac; +static int mock_charger_current; +static int mock_battery_discharge_current; + +/* constants to match against throttling sources */ +static const int t_s_therm = (1 << THROTTLE_SRC_THERMAL); +static const int t_s_power = (1 << THROTTLE_SRC_POWER); +static const int t_s_both = ((1 << THROTTLE_SRC_THERMAL) | + (1 << THROTTLE_SRC_POWER)); + +int dummy_temp_get_val(int idx, int *temp_ptr) +{ + if (mock_temp[idx] >= 0) { + *temp_ptr = mock_temp[idx]; + return EC_SUCCESS; + } + + return EC_ERROR_NOT_POWERED; +} + +void chipset_force_shutdown(void) +{ + cpu_shutdown = 1; +} + +void chipset_throttle_cpu(int throttled) +{ + cpu_throttled = throttled; +} + +void host_throttle_cpu(int throttled) +{ + host_throttled = throttled; +} + +void pwm_fan_set_percent_needed(int pct) +{ + fan_pct = pct; +} + +void smi_sensor_failure_warning(void) +{ + no_temps_read = 1; +} + +static void change_ac(int val) +{ + mock_ac = val; + extpower_interrupt(GPIO_AC_PRESENT); + sleep(1); +} + +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_charger_current; + default: + break; + } + + return 0; +} + +/*****************************************************************************/ +/* Test utilities */ + +static void set_temps(int t0, int t1, int t2, int t3) +{ + mock_temp[0] = t0; + mock_temp[1] = t1; + mock_temp[2] = t2; + mock_temp[3] = t3; +} + +static void all_temps(int t) +{ + set_temps(t, t, t, t); +} + +static void reset_mock_battery(void) +{ + const struct battery_info *bat_info = battery_get_info(); + + /* 50% of charge */ + sb_write(SB_RELATIVE_STATE_OF_CHARGE, 50); + sb_write(SB_ABSOLUTE_STATE_OF_CHARGE, 50); + /* 25 degree Celsius */ + sb_write(SB_TEMPERATURE, 250 + 2731); + /* Normal voltage */ + sb_write(SB_VOLTAGE, bat_info->voltage_normal); + sb_write(SB_CHARGING_VOLTAGE, bat_info->voltage_max); + sb_write(SB_CHARGING_CURRENT, 4000); + /* Discharging at 100mAh */ + sb_write(SB_CURRENT, -100); +} +DECLARE_HOOK(HOOK_INIT, reset_mock_battery, HOOK_PRIO_DEFAULT); + +static void mock_batt(int cur) +{ + sb_write(SB_CURRENT, -cur); /* discharge current is neg here */ +} + +static void reset_mocks(void) +{ + /* Ignore all sensors */ + memset(thermal_params, 0, sizeof(thermal_params)); + + /* All sensors report error anyway */ + set_temps(-1, -1 , -1, -1); + + /* Reset expectations */ + host_throttled = 0; + cpu_throttled = 0; + cpu_shutdown = 0; + fan_pct = 0; + no_temps_read = 0; + + /* other mocked inputs */ + mock_id = 0; + mock_ac = 0; + mock_charger_current = 0; + mock_battery_discharge_current = 0; +} + +/*****************************************************************************/ +/* Tests */ + +static int test_init_val(void) +{ + reset_mocks(); + sleep(2); + + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + TEST_ASSERT(fan_pct == 0); + TEST_ASSERT(no_temps_read); + + sleep(2); + + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + TEST_ASSERT(fan_pct == 0); + TEST_ASSERT(no_temps_read); + + return EC_SUCCESS; +} + +static int test_sensors_can_be_read(void) +{ + reset_mocks(); + mock_temp[2] = 100; + + sleep(2); + + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + TEST_ASSERT(fan_pct == 0); + TEST_ASSERT(no_temps_read == 0); + + return EC_SUCCESS; +} + + +static int test_one_fan(void) +{ + reset_mocks(); + thermal_params[2].temp_fan_off = 100; + thermal_params[2].temp_fan_max = 200; + + all_temps(50); + sleep(2); + TEST_ASSERT(fan_pct == 0); + + all_temps(100); + sleep(2); + TEST_ASSERT(fan_pct == 0); + + all_temps(101); + sleep(2); + TEST_ASSERT(fan_pct == 1); + + all_temps(130); + sleep(2); + TEST_ASSERT(fan_pct == 30); + + all_temps(150); + sleep(2); + TEST_ASSERT(fan_pct == 50); + + all_temps(170); + sleep(2); + TEST_ASSERT(fan_pct == 70); + + all_temps(200); + sleep(2); + TEST_ASSERT(fan_pct == 100); + + all_temps(300); + sleep(2); + TEST_ASSERT(fan_pct == 100); + + return EC_SUCCESS; +} + +static int test_two_fans(void) +{ + reset_mocks(); + + thermal_params[1].temp_fan_off = 120; + thermal_params[1].temp_fan_max = 160; + thermal_params[2].temp_fan_off = 100; + thermal_params[2].temp_fan_max = 200; + + all_temps(50); + sleep(2); + TEST_ASSERT(fan_pct == 0); + + all_temps(100); + sleep(2); + TEST_ASSERT(fan_pct == 0); + + all_temps(101); + sleep(2); + TEST_ASSERT(fan_pct == 1); + + all_temps(130); + sleep(2); + /* fan 2 is still higher */ + TEST_ASSERT(fan_pct == 30); + + all_temps(150); + sleep(2); + /* now fan 1 is higher: 150 = 75% of [120-160] */ + TEST_ASSERT(fan_pct == 75); + + all_temps(170); + sleep(2); + /* fan 1 is maxed now */ + TEST_ASSERT(fan_pct == 100); + + all_temps(200); + sleep(2); + TEST_ASSERT(fan_pct == 100); + + all_temps(300); + sleep(2); + TEST_ASSERT(fan_pct == 100); + + return EC_SUCCESS; +} + +static int test_all_fans(void) +{ + reset_mocks(); + + thermal_params[0].temp_fan_off = 20; + thermal_params[0].temp_fan_max = 60; + thermal_params[1].temp_fan_off = 120; + thermal_params[1].temp_fan_max = 160; + thermal_params[2].temp_fan_off = 100; + thermal_params[2].temp_fan_max = 200; + thermal_params[3].temp_fan_off = 300; + thermal_params[3].temp_fan_max = 500; + + set_temps(1, 1, 1, 1); + sleep(2); + TEST_ASSERT(fan_pct == 0); + + /* Each sensor has its own range */ + set_temps(40, 0, 0, 0); + sleep(2); + TEST_ASSERT(fan_pct == 50); + + set_temps(0, 140, 0, 0); + sleep(2); + TEST_ASSERT(fan_pct == 50); + + set_temps(0, 0, 150, 0); + sleep(2); + TEST_ASSERT(fan_pct == 50); + + set_temps(0, 0, 0, 400); + sleep(2); + TEST_ASSERT(fan_pct == 50); + + set_temps(60, 0, 0, 0); + sleep(2); + TEST_ASSERT(fan_pct == 100); + + set_temps(0, 160, 0, 0); + sleep(2); + TEST_ASSERT(fan_pct == 100); + + set_temps(0, 0, 200, 0); + sleep(2); + TEST_ASSERT(fan_pct == 100); + + set_temps(0, 0, 0, 500); + sleep(2); + TEST_ASSERT(fan_pct == 100); + + /* But sensor 0 needs the most cooling */ + all_temps(20); + sleep(2); + TEST_ASSERT(fan_pct == 0); + + all_temps(21); + sleep(2); + TEST_ASSERT(fan_pct == 2); + + all_temps(30); + sleep(2); + TEST_ASSERT(fan_pct == 25); + + all_temps(40); + sleep(2); + TEST_ASSERT(fan_pct == 50); + + all_temps(50); + sleep(2); + TEST_ASSERT(fan_pct == 75); + + all_temps(60); + sleep(2); + TEST_ASSERT(fan_pct == 100); + + all_temps(65); + sleep(2); + TEST_ASSERT(fan_pct == 100); + + return EC_SUCCESS; +} + +static int test_one_limit(void) +{ + reset_mocks(); + thermal_params[2].temp_host[EC_TEMP_THRESH_WARN] = 100; + thermal_params[2].temp_host[EC_TEMP_THRESH_HIGH] = 200; + thermal_params[2].temp_host[EC_TEMP_THRESH_HALT] = 300; + + all_temps(50); + sleep(2); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(100); + sleep(2); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(101); + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(100); + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(99); + sleep(2); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(199); + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(200); + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(201); + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(200); + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(199); + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(99); + sleep(2); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(201); + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(99); + sleep(2); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(301); + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 1); + + /* We probably won't be able to read the CPU temp while shutdown, + * so nothing will change. */ + all_temps(-1); + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + /* cpu_shutdown is only set for testing purposes. The thermal task + * doesn't do anything that could clear it. */ + + all_temps(50); + sleep(2); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + + return EC_SUCCESS; +} + +static int test_several_limits(void) +{ + reset_mocks(); + + thermal_params[1].temp_host[EC_TEMP_THRESH_WARN] = 150; + thermal_params[1].temp_host[EC_TEMP_THRESH_HIGH] = 200; + thermal_params[1].temp_host[EC_TEMP_THRESH_HALT] = 250; + + thermal_params[2].temp_host[EC_TEMP_THRESH_WARN] = 100; + thermal_params[2].temp_host[EC_TEMP_THRESH_HIGH] = 200; + thermal_params[2].temp_host[EC_TEMP_THRESH_HALT] = 300; + + thermal_params[3].temp_host[EC_TEMP_THRESH_WARN] = 20; + thermal_params[3].temp_host[EC_TEMP_THRESH_HIGH] = 30; + thermal_params[3].temp_host[EC_TEMP_THRESH_HALT] = 40; + + set_temps(500, 100, 150, 10); + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); /* 1=low, 2=warn, 3=low */ + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + set_temps(500, 50, -1, 10); /* 1=low, 2=X, 3=low */ + sleep(2); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + set_temps(500, 170, 210, 10); /* 1=warn, 2=high, 3=low */ + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 0); + + set_temps(500, 100, 50, 40); /* 1=low, 2=low, 3=high */ + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 0); + + set_temps(500, 100, 50, 41); /* 1=low, 2=low, 3=shutdown */ + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 1); + + all_temps(0); /* reset from shutdown */ + sleep(2); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + + + return EC_SUCCESS; +} + +static int test_batt(void) +{ + struct adapter_limits *l; + int longtime; + int i; + + reset_mocks(); + /* We're assuming two limits, mild and urgent. */ + TEST_ASSERT(NUM_BATT_THRESHOLDS == 2); + /* Find out which is which, only use the lower one */ + if (batt_limits[0].hi_val > batt_limits[1].hi_val) + l = &batt_limits[1]; + else + 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; + + /* On AC, but this doesn't actually matter for this test */ + mock_batt(0); + change_ac(1); + + TEST_ASSERT(ap_is_throttled == 0); + change_ac(0); + TEST_ASSERT(ap_is_throttled == 0); + + /* reset, by staying low for a long time */ + usleep(EXTPOWER_FALCO_POLL_PERIOD * longtime); + 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(-1); + usleep(EXTPOWER_FALCO_POLL_PERIOD * longtime); + 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); + usleep(EXTPOWER_FALCO_POLL_PERIOD * longtime); + 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); + usleep(EXTPOWER_FALCO_POLL_PERIOD * (l->hi_cnt - 1)); + 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); + usleep(EXTPOWER_FALCO_POLL_PERIOD * 1); + 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); + usleep(EXTPOWER_FALCO_POLL_PERIOD * (l->hi_cnt - 1)); + TEST_ASSERT(l->count != 0); + TEST_ASSERT(l->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + + /* one more ought to do it */ + usleep(EXTPOWER_FALCO_POLL_PERIOD * 1); + TEST_ASSERT(l->triggered == 1); + TEST_ASSERT(ap_is_throttled); + + /* going midrange for a long time shouldn't change anything */ + mock_batt((l->lo_val + l->hi_val) / 2); + usleep(EXTPOWER_FALCO_POLL_PERIOD * longtime); + TEST_ASSERT(l->triggered == 1); + TEST_ASSERT(ap_is_throttled); + + /* charge for not quite long enough */ + mock_batt(-1); + usleep(EXTPOWER_FALCO_POLL_PERIOD * (l->lo_cnt - 1)); + TEST_ASSERT(l->triggered == 1); + TEST_ASSERT(ap_is_throttled); + + /* back above the low limit once */ + mock_batt(l->lo_val + 1); + usleep(EXTPOWER_FALCO_POLL_PERIOD * 1); + TEST_ASSERT(l->triggered == 1); + TEST_ASSERT(ap_is_throttled); + + /* now charge again - that should have reset the count */ + mock_batt(-1); + usleep(EXTPOWER_FALCO_POLL_PERIOD * (l->lo_cnt - 1)); + TEST_ASSERT(l->triggered == 1); + TEST_ASSERT(ap_is_throttled); + + /* One more ought to do it */ + usleep(EXTPOWER_FALCO_POLL_PERIOD * 1); + TEST_ASSERT(l->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + + return EC_SUCCESS; +} + +static int test_several_limits_with_batt(void) +{ + struct adapter_limits *l; + int longtime; + int i; + + /* We're assuming two limits, mild and urgent. */ + TEST_ASSERT(NUM_BATT_THRESHOLDS == 2); + /* Find out which is which, only use the lower one */ + if (batt_limits[0].hi_val > batt_limits[1].hi_val) + l = &batt_limits[1]; + else + 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; + longtime *= EXTPOWER_FALCO_POLL_PERIOD; + + reset_mocks(); + + /* Set some thermal limits */ + thermal_params[1].temp_host[EC_TEMP_THRESH_WARN] = 150; + thermal_params[1].temp_host[EC_TEMP_THRESH_HIGH] = 200; + thermal_params[1].temp_host[EC_TEMP_THRESH_HALT] = 250; + + thermal_params[2].temp_host[EC_TEMP_THRESH_WARN] = 100; + thermal_params[2].temp_host[EC_TEMP_THRESH_HIGH] = 200; + thermal_params[2].temp_host[EC_TEMP_THRESH_HALT] = 300; + + thermal_params[3].temp_host[EC_TEMP_THRESH_WARN] = 20; + thermal_params[3].temp_host[EC_TEMP_THRESH_HIGH] = 30; + thermal_params[3].temp_host[EC_TEMP_THRESH_HALT] = 40; + + + /* On AC, charging */ + mock_batt(-1); + all_temps(0); + change_ac(1); + usleep(longtime); + /* Everything is ready */ + TEST_ASSERT(l->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + + set_temps(500, 100, 150, 10); + usleep(longtime); + TEST_ASSERT(host_throttled == t_s_therm); /* 1=low, 2=warn, 3=low */ + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + /* battery up and down */ + mock_batt(l->hi_val + 1); + usleep(longtime); + TEST_ASSERT(l->triggered == 1); + TEST_ASSERT(ap_is_throttled); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(host_throttled == t_s_both); /* 1=low, 2=warn, 3=low */ + TEST_ASSERT(cpu_shutdown == 0); + mock_batt(-1); + usleep(longtime); + TEST_ASSERT(l->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + TEST_ASSERT(host_throttled == t_s_therm); /* 1=low, 2=warn, 3=low */ + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + + set_temps(500, 50, -1, 10); /* 1=low, 2=X, 3=low */ + usleep(longtime); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + TEST_ASSERT(l->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + + /* battery up and down */ + mock_batt(l->hi_val + 1); + usleep(longtime); + TEST_ASSERT(l->triggered == 1); + TEST_ASSERT(ap_is_throttled); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(host_throttled == t_s_power); + TEST_ASSERT(cpu_shutdown == 0); + mock_batt(-1); + usleep(longtime); + TEST_ASSERT(l->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + + set_temps(500, 170, 210, 10); /* 1=warn, 2=high, 3=low */ + sleep(2); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 0); + + /* battery up and down */ + mock_batt(l->hi_val + 1); + usleep(longtime); + TEST_ASSERT(l->triggered == 1); + TEST_ASSERT(ap_is_throttled); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(host_throttled == t_s_both); + TEST_ASSERT(cpu_shutdown == 0); + mock_batt(-1); + usleep(longtime); + TEST_ASSERT(l->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 0); + + + set_temps(500, 100, 50, 40); /* 1=low, 2=low, 3=high */ + usleep(longtime); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 0); + + /* battery up and down */ + mock_batt(l->hi_val + 1); + usleep(longtime); + TEST_ASSERT(l->triggered == 1); + TEST_ASSERT(ap_is_throttled); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(host_throttled == t_s_both); + TEST_ASSERT(cpu_shutdown == 0); + mock_batt(-1); + usleep(longtime); + TEST_ASSERT(l->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 0); + + + set_temps(500, 100, 50, 41); /* 1=low, 2=low, 3=shutdown */ + usleep(longtime); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 1); + + /* battery up and down */ + mock_batt(l->hi_val + 1); + usleep(longtime); + TEST_ASSERT(l->triggered == 1); + TEST_ASSERT(ap_is_throttled); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(host_throttled == t_s_both); + TEST_ASSERT(cpu_shutdown == 1); + mock_batt(-1); + usleep(longtime); + TEST_ASSERT(l->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + TEST_ASSERT(host_throttled == t_s_therm); + TEST_ASSERT(cpu_throttled == t_s_therm); + TEST_ASSERT(cpu_shutdown == 1); + + + all_temps(0); + usleep(longtime); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(l->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + + /* battery up and down */ + mock_batt(l->hi_val + 1); + usleep(longtime); + TEST_ASSERT(l->triggered == 1); + TEST_ASSERT(ap_is_throttled); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(host_throttled == t_s_power); + mock_batt(-1); + usleep(longtime); + TEST_ASSERT(l->triggered == 0); + TEST_ASSERT(ap_is_throttled == 0); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + + return EC_SUCCESS; +} + + +void run_test(void) +{ + test_chipset_on(); + + RUN_TEST(test_init_val); + RUN_TEST(test_sensors_can_be_read); + RUN_TEST(test_one_fan); + RUN_TEST(test_two_fans); + RUN_TEST(test_all_fans); + + RUN_TEST(test_one_limit); + RUN_TEST(test_several_limits); + + RUN_TEST(test_batt); + RUN_TEST(test_several_limits_with_batt); + + test_print_result(); +} diff --git a/test/thermal_falco.tasklist b/test/thermal_falco.tasklist new file mode 100644 index 0000000000..6ca1e65553 --- /dev/null +++ b/test/thermal_falco.tasklist @@ -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. + */ + +/** + * 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 \ + TASK_TEST(CHARGER, charger_task, NULL, TASK_STACK_SIZE) \ + TASK_TEST(CHIPSET, chipset_task, NULL, TASK_STACK_SIZE) diff --git a/test/thermal_falco_externs.h b/test/thermal_falco_externs.h new file mode 100644 index 0000000000..6f202e0d5c --- /dev/null +++ b/test/thermal_falco_externs.h @@ -0,0 +1,15 @@ +/* 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 __THERMAL_FALCO_EXTERNS_H +#define __THERMAL_FALCO_EXTERNS_H + +/* Normally private symbols from the modules that we're testing. */ +extern struct adapter_limits + ad_limits[][NUM_AC_TURBO_STATES][NUM_AC_THRESHOLDS]; +extern int ap_is_throttled; +extern struct adapter_limits batt_limits[NUM_BATT_THRESHOLDS]; + +#endif /* __THERMAL_FALCO_EXTERNS_H */ |