/* Copyright 2014 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Test thermal engine. */ #include "common.h" #include "console.h" #include "fan.h" #include "hooks.h" #include "host_command.h" #include "temp_sensor.h" #include "test_util.h" #include "thermal.h" #include "timer.h" #include "util.h" #define FAN_RPM(fan) fans[fan].rpm /*****************************************************************************/ /* Tests */ void set_thermal_control_enabled(int fan, int enable); static int test_fan(void) { /* "actual" fan speed from board/host/fan.c */ extern int mock_rpm; sleep(2); /* Fans initialize disabled. */ TEST_ASSERT(fan_get_rpm_actual(0) == 0); set_thermal_control_enabled(0, 1); /* * fan_set_percent_needed() is normally called once a second by the * thermal task, but we're not using a thermal test in this test so * we can dink around with the fans without having to wait. The host * implementation just sets mock_rpm to whatever it's asked for. */ /* Off */ fan_set_percent_needed(0, 0); TEST_ASSERT(fan_get_rpm_actual(0) == 0); fan_set_percent_needed(0, 0); TEST_ASSERT(fan_get_rpm_actual(0) == 0); /* On, but just barely */ fan_set_percent_needed(0, 1); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_start); /* fan is above min speed now, so should be set to min */ fan_set_percent_needed(0, 1); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_min); /* Full speed */ fan_set_percent_needed(0, 100); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_max); fan_set_percent_needed(0, 100); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_max); /* Slow again */ fan_set_percent_needed(0, 1); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_min); fan_set_percent_needed(0, 1); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_min); /* Off */ fan_set_percent_needed(0, 0); TEST_ASSERT(fan_get_rpm_actual(0) == 0); fan_set_percent_needed(0, 0); TEST_ASSERT(fan_get_rpm_actual(0) == 0); /* On, but just barely */ fan_set_percent_needed(0, 1); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_start); /* Force the mock_rpm to be slow, to simulate dragging */ mock_rpm = FAN_RPM(0)->rpm_min - 105; /* It should keep trying for the start speed */ fan_set_percent_needed(0, 1); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_start); /* But we have to keep forcing the mock_rpm back down */ mock_rpm = FAN_RPM(0)->rpm_min - 105; fan_set_percent_needed(0, 1); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_start); /* Now let it turn just under rpm_min. Should be okay there. */ mock_rpm = FAN_RPM(0)->rpm_min - 10; fan_set_percent_needed(0, 1); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_min); /* Let it go a little faster, still okay */ mock_rpm = FAN_RPM(0)->rpm_min + 10; fan_set_percent_needed(0, 1); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_min); /* But if it drops too low, it should go back to the start speed */ mock_rpm = FAN_RPM(0)->rpm_min - 105; fan_set_percent_needed(0, 1); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_start); /* And then relax */ fan_set_percent_needed(0, 1); TEST_ASSERT(fan_get_rpm_actual(0) == FAN_RPM(0)->rpm_min); return EC_SUCCESS; } /* Provide a test driver to make test easier to read. */ int temp_to_rpm(int temperature_c) { const int temp_fan_off = C_TO_K(35); const int temp_fan_max = C_TO_K(55); const struct fan_step_1_1 fan_table[] = { { .decreasing_temp_ratio_threshold = TEMP_TO_RATIO(35), .increasing_temp_ratio_threshold = TEMP_TO_RATIO(41), .rpm = 2500 }, { .decreasing_temp_ratio_threshold = TEMP_TO_RATIO(37), .increasing_temp_ratio_threshold = TEMP_TO_RATIO(43), .rpm = 3200 }, { .decreasing_temp_ratio_threshold = TEMP_TO_RATIO(42), .increasing_temp_ratio_threshold = TEMP_TO_RATIO(45), .rpm = 3500 }, { .decreasing_temp_ratio_threshold = TEMP_TO_RATIO(44), .increasing_temp_ratio_threshold = TEMP_TO_RATIO(47), .rpm = 3900 }, { .decreasing_temp_ratio_threshold = TEMP_TO_RATIO(46), .increasing_temp_ratio_threshold = TEMP_TO_RATIO(49), .rpm = 4500 }, { .decreasing_temp_ratio_threshold = TEMP_TO_RATIO(48), .increasing_temp_ratio_threshold = TEMP_TO_RATIO(52), .rpm = 5100 }, { .decreasing_temp_ratio_threshold = TEMP_TO_RATIO(51), .increasing_temp_ratio_threshold = TEMP_TO_RATIO(55), .rpm = 5400 }, }; const int num_fan_levels = ARRAY_SIZE(fan_table); int temp_ratio = TEMP_TO_RATIO(temperature_c); int rpm = temp_ratio_to_rpm_hysteresis(fan_table, num_fan_levels, 0, temp_ratio, NULL); fan_set_rpm_target(FAN_CH(0), rpm); return rpm; } static int test_temp_ratio_to_rpm_hysteresis(void) { const int ZERO = 0; /* set initial value to be different so that a log message appears */ fan_set_rpm_target(FAN_CH(0), 5400); /* initial turn-on behavior, ramp up. @ represents fan speed; + temp */ TEST_ASSERT(temp_to_rpm(30) == ZERO); /* @+. . 40 . 50 .60 */ TEST_ASSERT(temp_to_rpm(30) == ZERO); /* @+. . . . . . */ TEST_ASSERT(temp_to_rpm(35) == ZERO); /* @ + . . . . */ TEST_ASSERT(temp_to_rpm(37) == ZERO); /* @ . + . . . . */ TEST_ASSERT(temp_to_rpm(39) == ZERO); /* @ . +. . . . */ TEST_ASSERT(temp_to_rpm(40) == ZERO); /* @ . + . . . */ TEST_ASSERT(temp_to_rpm(41) == 2500); /* @. .+ . . . */ TEST_ASSERT(temp_to_rpm(36) == 2500); /* @.+ . . . . */ TEST_ASSERT(temp_to_rpm(42) == 2500); /* @. . + . . . */ TEST_ASSERT(temp_to_rpm(43) == 3200); /* @ . + . . . */ TEST_ASSERT(temp_to_rpm(38) == 3200); /* @ + . . . . */ TEST_ASSERT(temp_to_rpm(44) == 3200); /* @ . +. . . */ TEST_ASSERT(temp_to_rpm(45) == 3500); /* .@ . + . . */ TEST_ASSERT(temp_to_rpm(43) == 3500); /* .@ . + . . . */ TEST_ASSERT(temp_to_rpm(46) == 3500); /* .@ . .+ . . */ TEST_ASSERT(temp_to_rpm(47) == 3900); /* . @ . . + . . */ TEST_ASSERT(temp_to_rpm(45) == 3900); /* . @ . + . . */ TEST_ASSERT(temp_to_rpm(48) == 3900); /* . @ . . + . . */ TEST_ASSERT(temp_to_rpm(49) == 4500); /* . @ . . +. . */ TEST_ASSERT(temp_to_rpm(47) == 4500); /* . @ . . + . . */ TEST_ASSERT(temp_to_rpm(51) == 4500); /* . @ . . .+ . */ TEST_ASSERT(temp_to_rpm(52) == 5100); /* . @. . . + . */ TEST_ASSERT(temp_to_rpm(49) == 5100); /* . @. . +. . */ TEST_ASSERT(temp_to_rpm(54) == 5100); /* . @. . . +. */ TEST_ASSERT(temp_to_rpm(55) == 5400); /* . @ . . + */ TEST_ASSERT(temp_to_rpm(52) == 5400); /* . @ . . + . */ TEST_ASSERT(temp_to_rpm(60) == 5400); /* . @ . 50 ..+ */ /* cool-down */ TEST_ASSERT(temp_to_rpm(55) == 5400); /* . @ . . + */ TEST_ASSERT(temp_to_rpm(52) == 5400); /* . @ . . + . */ TEST_ASSERT(temp_to_rpm(51) == 5100); /* . @. . .+ . */ TEST_ASSERT(temp_to_rpm(54) == 5100); /* . @. . . +. */ TEST_ASSERT(temp_to_rpm(49) == 5100); /* . @. . +. . */ TEST_ASSERT(temp_to_rpm(48) == 4500); /* . @ . . + . . */ TEST_ASSERT(temp_to_rpm(51) == 4500); /* . @ . . .+ . */ TEST_ASSERT(temp_to_rpm(47) == 4500); /* . @ . . + . . */ TEST_ASSERT(temp_to_rpm(46) == 3900); /* . @ . .+ . . */ TEST_ASSERT(temp_to_rpm(48) == 3900); /* . @ . . + . . */ TEST_ASSERT(temp_to_rpm(45) == 3900); /* . @ . + . . */ TEST_ASSERT(temp_to_rpm(44) == 3500); /* .@ . +. . . */ TEST_ASSERT(temp_to_rpm(46) == 3500); /* .@ . .+ . . */ TEST_ASSERT(temp_to_rpm(43) == 3500); /* .@ . + . . . */ TEST_ASSERT(temp_to_rpm(42) == 3200); /* @ . + . . . */ TEST_ASSERT(temp_to_rpm(44) == 3200); /* @ . +. . . */ TEST_ASSERT(temp_to_rpm(38) == 3200); /* @ + . . . . */ TEST_ASSERT(temp_to_rpm(37) == 2500); /* @. + . . . . */ TEST_ASSERT(temp_to_rpm(42) == 2500); /* @. . + . . . */ TEST_ASSERT(temp_to_rpm(36) == 2500); /* @.+ . . . . */ TEST_ASSERT(temp_to_rpm(35) == ZERO); /* @ + 40 . 50 . */ /* warm up again */ TEST_ASSERT(temp_to_rpm(38) == ZERO); /* @ . + . . . . */ /* jumping */ TEST_ASSERT(temp_to_rpm(46) == 3500); /* .@ . .+ . . */ TEST_ASSERT(temp_to_rpm(36) == 2500); /* @.+ . . . . */ TEST_ASSERT(temp_to_rpm(35) == ZERO); /* @ + . . . . */ TEST_ASSERT(temp_to_rpm(37) == ZERO); /* @ . + . . . . */ TEST_ASSERT(temp_to_rpm(46) == 3500); /* .@ . .+ . . */ TEST_ASSERT(temp_to_rpm(54) == 5100); /* . @. . . +. */ TEST_ASSERT(temp_to_rpm(55) == 5400); /* . @ . . + */ TEST_ASSERT(temp_to_rpm(60) == 5400); /* . @ . . ..+ */ TEST_ASSERT(temp_to_rpm(53) == 5400); /* . @ . . + . */ TEST_ASSERT(temp_to_rpm(46) == 3900); /* . @ . .+ . . */ TEST_ASSERT(temp_to_rpm(30) == ZERO); /* @+. . 40 . 50 . */ return EC_SUCCESS; } void run_test(int argc, const char **argv) { RUN_TEST(test_fan); RUN_TEST(test_temp_ratio_to_rpm_hysteresis); test_print_result(); } /* Doesn't do anything, but it makes this test stop intermittently covering * some code in core/host/task.c:fast_forward(). */ void interrupt_generator(void) { }