diff options
author | Bill Richardson <wfrichar@chromium.org> | 2013-09-06 13:01:48 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2013-09-11 01:49:48 +0000 |
commit | 793b52f327b20ee5897dc04093ac30a5d000d6e9 (patch) | |
tree | b85f745c8c3f0d3d877733d18caadb626e56927b /test | |
parent | e6401d2e83939a63cbd156fa193f9768063d9325 (diff) | |
download | chrome-ec-793b52f327b20ee5897dc04093ac30a5d000d6e9.tar.gz |
Handle multiple independent sources and types of CPU throttling
Depending on the system, the AP can be throttled in at least two different
ways - politely, where it's just asked to slow down a bit, and forcefully
using a hardware signal (like PROCHOT). In addition, the request for
throttling can come from multiple tasks.
This CL provides a single interface, specifying both the type of throttling
desired and the source of the throttling request.
For each type, any source can can start throttling, but all sources must
agree before it stops. The changes are protected by a mutex, so that
requests from multiple tasks don't interfere with each other.
BUG=chrome-os-partner:20739,chromium:287985,chromium:287983
BRANCH=ToT
TEST=manual
Build-time test:
cd src/platform/ec
make BOARD=falco runtests
Run-time test: Lower the temp thresholds, turn the fan off, and watch the
throttling turn off and on as things heat up. For example, on the EC
console:
> temps
PECI : 339 K = 66 C
ECInternal : 324 K = 51 C
G781Internal : 328 K = 55 C
G781External : 327 K = 54 C
> thermalset 0 341 343
sensor warn high halt fan_off fan_max name
0 341 343 383 333 363 PECI
1 0 0 0 0 0 ECInternal
2 0 0 0 0 0 G781Internal
3 0 0 0 0 0 G781External
>
> temps
PECI : 339 K = 66 C
ECInternal : 324 K = 51 C
G781Internal : 328 K = 55 C
G781External : 327 K = 54 C
>
> fanduty 0
Setting fan duty cycle to 0%
>
> apthrottle
AP throttling type 0 is off (0x00000000)
AP throttling type 1 is off (0x00000000)
>
[430.152000 thermal WARN]
[430.152233 event set 0x00020000]
[430.152497 event clear 0x00020000]
[430.152714 ACPI query = 18]
[430.152444 sci 0x00020000]
[430.153051 set AP throttling type 0 to on (0x00000001)]
> gpioget CPU_PROCHOT
0 CPU_PROCHOT
>
[436.153742 thermal HIGH]
[436.153979 set AP throttling type 1 to on (0x00000001)]
> gpioget CPU_PROCHOT
1* CPU_PROCHOT
> [441.155319 thermal no longer high]
[441.155587 set AP throttling type 1 to off (0x00000000)]
[442.155604 thermal HIGH]
[442.155841 set AP throttling type 1 to on (0x00000001)]
[446.156623 thermal no longer high]
[446.156890 set AP throttling type 1 to off (0x00000000)]
temps
PECI : 343 K = 70 C
ECInternal : 324 K = 51 C
G781Internal : 328 K = 55 C
G781External : 327 K = 54 C
>
[447.156827 thermal HIGH]
[447.157064 set AP throttling type 1 to on (0x00000001)]
apthrottle
AP throttling type 0 is on (0x00000001)
AP throttling type 1 is on (0x00000001)
> gpioget CPU_PROCHOT
1 CPU_PROCHOT
>
Now turn the fan back on:
> fanauto
>
[456.159306 thermal no longer high]
[456.159574 set AP throttling type 1 to off (0x00000000)]
> apthrottle
AP throttling type 0 is on (0x00000001)
AP throttling type 1 is off (0x00000000)
> temps
PECI : 341 K = 68 C
ECInternal : 324 K = 51 C
G781Internal : 328 K = 55 C
G781External : 327 K = 54 C
>
[473.163905 thermal no longer warn]
[473.164168 event set 0x00040000]
[473.164453 event clear 0x00040000]
[473.164670 ACPI query = 19]
[473.164379 sci 0x00040000]
[473.164987 set AP throttling type 0 to off (0x00000000)]
temps
PECI : 340 K = 67 C
ECInternal : 324 K = 51 C
G781Internal : 328 K = 55 C
G781External : 327 K = 54 C
>
> apthrottle
AP throttling type 0 is off (0x00000000)
AP throttling type 1 is off (0x00000000)
>
Change-Id: I9ee1491a637d7766395c71e57483fbd9177ea554
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/168802
Diffstat (limited to 'test')
-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 |
8 files changed, 1123 insertions, 14 deletions
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 */ |