diff options
40 files changed, 1573 insertions, 819 deletions
diff --git a/board/bolt/board.c b/board/bolt/board.c index de4a3d18e9..b3e622af20 100644 --- a/board/bolt/board.c +++ b/board/bolt/board.c @@ -26,6 +26,7 @@ #include "switch.h" #include "temp_sensor.h" #include "timer.h" +#include "thermal.h" #include "tmp006.h" #include "util.h" @@ -198,6 +199,16 @@ const struct temp_sensor_t temp_sensors[] = { }; BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT); +/* Thermal limits for each temp sensor. All temps are in degrees K. Must be in + * same order as enum temp_sensor_id. To always ignore any temp, use 0. + */ +struct ec_thermal_config thermal_params[] = { + { {0, 0, 0}, 0, 0}, + /* Only the AP affects the thermal limits and fan speed. */ + {{C_TO_K(100), C_TO_K(114), C_TO_K(110)}, C_TO_K(60), C_TO_K(90)}, +}; +BUILD_ASSERT(ARRAY_SIZE(thermal_params) == TEMP_SENSOR_COUNT); + struct keyboard_scan_config keyscan_config = { .output_settle_us = 40, .debounce_down_us = 6 * MSEC, diff --git a/board/bolt/board.h b/board/bolt/board.h index 8cf1993b56..af60583e44 100644 --- a/board/bolt/board.h +++ b/board/bolt/board.h @@ -36,6 +36,9 @@ /* External Charger maximum current. */ #define CONFIG_CHARGER_INPUT_CURRENT 5000 #define CONFIG_PWM_FAN +#define CONFIG_PWM_FAN_RPM_MIN 1000 +#define CONFIG_PWM_FAN_RPM_MAX 5050 +#define CONFIG_PWM_FAN_POWER_GOOD GPIO_PP5000_PGOOD #define CONFIG_PWM_KBLIGHT #define CONFIG_TEMP_SENSOR #define CONFIG_UART_HOST 2 diff --git a/board/bolt/ec.tasklist b/board/bolt/ec.tasklist index 35eefdfb77..395d068bbb 100644 --- a/board/bolt/ec.tasklist +++ b/board/bolt/ec.tasklist @@ -20,7 +20,6 @@ TASK_ALWAYS(HOOKS, hook_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(VBOOTHASH, vboot_hash_task, NULL, LARGER_TASK_STACK_SIZE) \ TASK_NOTEST(LIGHTBAR, lightbar_task, NULL, TASK_STACK_SIZE) \ - TASK_NOTEST(THERMAL, thermal_task, NULL, TASK_STACK_SIZE) \ TASK_ALWAYS(CHARGER, charger_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(CHIPSET, chipset_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \ diff --git a/board/falco/board.c b/board/falco/board.c index b2c53791af..e65e68dae0 100644 --- a/board/falco/board.c +++ b/board/falco/board.c @@ -14,6 +14,7 @@ #include "common.h" #include "ec_commands.h" #include "extpower.h" +#include "fan.h" #include "gpio.h" #include "host_command.h" #include "i2c.h" @@ -26,6 +27,7 @@ #include "switch.h" #include "temp_sensor.h" #include "temp_sensor_g781.h" +#include "thermal.h" #include "timer.h" #include "util.h" @@ -201,6 +203,18 @@ const struct temp_sensor_t temp_sensors[] = { }; BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT); +/* Thermal limits for each temp sensor. All temps are in degrees K. Must be in + * same order as enum temp_sensor_id. To always ignore any temp, use 0. + */ +struct ec_thermal_config thermal_params[] = { + /* Only the AP affects the thermal limits and fan speed. */ + { {C_TO_K(100), C_TO_K(114), C_TO_K(110)}, C_TO_K(60), C_TO_K(90)}, + { {0, 0, 0}, 0, 0}, + { {0, 0, 0}, 0, 0}, + { {0, 0, 0}, 0, 0}, +}; +BUILD_ASSERT(ARRAY_SIZE(thermal_params) == TEMP_SENSOR_COUNT); + struct keyboard_scan_config keyscan_config = { .output_settle_us = 40, .debounce_down_us = 6 * MSEC, @@ -246,3 +260,22 @@ int board_discharge_on_ac(int enable) { return charger_discharge_on_ac(enable); } + + +/* + * Take a nice smooth ramp and make it all chunky. + * And never turn it off. Bah. That'll do wonders for battery life. + */ +#ifdef CONFIG_PWM_FAN_RPM_CUSTOM +int pwm_fan_percent_to_rpm(int pct) +{ + const int FAN_MAX = 5050; + const int FAN_MIN = 2700; + const int NUM_STEPS = 7; + const int m = 100 * 100 / NUM_STEPS; + const int m0 = m / 200; + + int chunky = 100 * (pct + m0) / m; + return FAN_MIN + (FAN_MAX - FAN_MIN) * m * chunky / 10000; +} +#endif /* CONFIG_PWM_FAN_RPM_CUSTOM */ diff --git a/board/falco/board.h b/board/falco/board.h index d36245c56b..1a75037284 100644 --- a/board/falco/board.h +++ b/board/falco/board.h @@ -23,6 +23,9 @@ #define CONFIG_POWER_BUTTON #define CONFIG_POWER_BUTTON_X86 #define CONFIG_PWM_FAN +#define CONFIG_PWM_FAN_RPM_MIN 1000 +#define CONFIG_PWM_FAN_RPM_MAX 5050 +#define CONFIG_PWM_FAN_POWER_GOOD GPIO_PP5000_PGOOD #define CONFIG_TEMP_SENSOR #define CONFIG_TEMP_SENSOR_G781 #define CONFIG_UART_HOST 2 diff --git a/board/falco/ec.tasklist b/board/falco/ec.tasklist index 40d5ec4f60..3bde650552 100644 --- a/board/falco/ec.tasklist +++ b/board/falco/ec.tasklist @@ -20,7 +20,6 @@ TASK_ALWAYS(HOOKS, hook_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(VBOOTHASH, vboot_hash_task, NULL, LARGER_TASK_STACK_SIZE) \ TASK_ALWAYS(CHARGER, charger_task, NULL, TASK_STACK_SIZE) \ - TASK_NOTEST(THERMAL, thermal_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(CHIPSET, chipset_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \ TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \ diff --git a/board/host/board.c b/board/host/board.c index 0490d2287e..4e246fa7a3 100644 --- a/board/host/board.c +++ b/board/host/board.c @@ -35,5 +35,6 @@ const struct temp_sensor_t temp_sensors[] = { {"CPU", TEMP_SENSOR_TYPE_CPU, dummy_temp_get_val, 0, 3}, {"Board", TEMP_SENSOR_TYPE_BOARD, dummy_temp_get_val, 0, 3}, {"Case", TEMP_SENSOR_TYPE_CASE, dummy_temp_get_val, 0, 0}, + {"Battery", TEMP_SENSOR_TYPE_BOARD, dummy_temp_get_val, 0, 0}, }; BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT); diff --git a/board/host/board.h b/board/host/board.h index b80ac27fdb..a9528cf055 100644 --- a/board/host/board.h +++ b/board/host/board.h @@ -12,7 +12,6 @@ #define CONFIG_EXTPOWER_GPIO #undef CONFIG_FMAP #define CONFIG_POWER_BUTTON -#define CONFIG_TEMP_SENSOR #undef CONFIG_WATCHDOG #undef CONFIG_CONSOLE_HISTORY @@ -60,6 +59,7 @@ enum temp_sensor_id { TEMP_SENSOR_CPU = 0, TEMP_SENSOR_BOARD, TEMP_SENSOR_CASE, + TEMP_SENSOR_BATTERY, TEMP_SENSOR_COUNT }; diff --git a/board/link/board.c b/board/link/board.c index 1eac758978..3a00d16a03 100644 --- a/board/link/board.c +++ b/board/link/board.c @@ -24,6 +24,7 @@ #include "switch.h" #include "temp_sensor.h" #include "timer.h" +#include "thermal.h" #include "tmp006.h" #include "util.h" @@ -215,6 +216,24 @@ const struct temp_sensor_t temp_sensors[] = { }; BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT); +/* Thermal limits for each temp sensor. All temps are in degrees K. Must be in + * same order as enum temp_sensor_id. To always ignore any temp, use 0. + */ +struct ec_thermal_config thermal_params[] = { + {{0, 0, 0}, 0, 0}, + {{0, 0, 0}, 0, 0}, + {{0, 0, 0}, 0, 0}, + {{0, 0, 0}, 0, 0}, + {{0, 0, 0}, 0, 0}, + {{0, 0, 0}, 0, 0}, + {{0, 0, 0}, 0, 0}, + {{0, 0, 0}, 0, 0}, + {{0, 0, 0}, 0, 0}, + /* Only the AP affects the thermal limits and fan speed. */ + {{C_TO_K(100), C_TO_K(114), C_TO_K(110)}, C_TO_K(60), C_TO_K(90)}, +}; +BUILD_ASSERT(ARRAY_SIZE(thermal_params) == TEMP_SENSOR_COUNT); + const struct tmp006_t tmp006_sensors[TMP006_COUNT] = { {"USB C", TEMP_USB_ADDR}, {"PCH D", TEMP_PCH_ADDR}, diff --git a/board/link/board.h b/board/link/board.h index e5a6c58691..b45de1981a 100644 --- a/board/link/board.h +++ b/board/link/board.h @@ -26,6 +26,9 @@ #define CONFIG_POWER_BUTTON #define CONFIG_POWER_BUTTON_X86 #define CONFIG_PWM_FAN +#define CONFIG_PWM_FAN_RPM_MIN 1500 +#define CONFIG_PWM_FAN_RPM_MAX 9300 +#define CONFIG_PWM_FAN_POWER_GOOD GPIO_PGOOD_5VALW #define CONFIG_PWM_KBLIGHT #define CONFIG_TEMP_SENSOR #define CONFIG_TEMP_SENSOR_TMP006 diff --git a/board/link/ec.tasklist b/board/link/ec.tasklist index 2b3233e8e0..395d068bbb 100644 --- a/board/link/ec.tasklist +++ b/board/link/ec.tasklist @@ -21,7 +21,6 @@ TASK_NOTEST(VBOOTHASH, vboot_hash_task, NULL, LARGER_TASK_STACK_SIZE) \ TASK_NOTEST(LIGHTBAR, lightbar_task, NULL, TASK_STACK_SIZE) \ TASK_ALWAYS(CHARGER, charger_task, NULL, TASK_STACK_SIZE) \ - TASK_NOTEST(THERMAL, thermal_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(CHIPSET, chipset_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \ TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \ diff --git a/board/peppy/board.c b/board/peppy/board.c index 540247e191..9b30ac8865 100644 --- a/board/peppy/board.c +++ b/board/peppy/board.c @@ -24,6 +24,7 @@ #include "switch.h" #include "temp_sensor.h" #include "temp_sensor_g781.h" +#include "thermal.h" #include "timer.h" #include "util.h" @@ -180,7 +181,6 @@ const struct i2c_port_t i2c_ports[] = { }; BUILD_ASSERT(ARRAY_SIZE(i2c_ports) == I2C_PORTS_USED); - /* Temperature sensors data; must be in same order as enum temp_sensor_id. */ const struct temp_sensor_t temp_sensors[] = { {"PECI", TEMP_SENSOR_TYPE_CPU, peci_temp_sensor_get_val, 0, 2}, @@ -192,6 +192,18 @@ const struct temp_sensor_t temp_sensors[] = { }; BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT); +/* Thermal limits for each temp sensor. All temps are in degrees K. Must be in + * same order as enum temp_sensor_id. To always ignore any temp, use 0. + */ +struct ec_thermal_config thermal_params[] = { + /* Only the AP affects the thermal limits and fan speed. */ + {{C_TO_K(100), C_TO_K(114), C_TO_K(110)}, C_TO_K(60), C_TO_K(90)}, + {{0, 0, 0}, 0, 0}, + {{0, 0, 0}, 0, 0}, + {{0, 0, 0}, 0, 0}, +}; +BUILD_ASSERT(ARRAY_SIZE(thermal_params) == TEMP_SENSOR_COUNT); + struct keyboard_scan_config keyscan_config = { .output_settle_us = 40, .debounce_down_us = 6 * MSEC, diff --git a/board/peppy/board.h b/board/peppy/board.h index 9f313ea9a6..7c3a385071 100644 --- a/board/peppy/board.h +++ b/board/peppy/board.h @@ -25,6 +25,9 @@ #define CONFIG_POWER_BUTTON_X86 #define CONFIG_PWM_FAN #define CONFIG_PWM_FAN_EN_GPIO GPIO_PP5000_FAN_EN +#define CONFIG_PWM_FAN_RPM_MIN 1000 +#define CONFIG_PWM_FAN_RPM_MAX 5050 +#define CONFIG_PWM_FAN_POWER_GOOD GPIO_PP5000_PGOOD #define CONFIG_TEMP_SENSOR #define CONFIG_TEMP_SENSOR_G781 #define CONFIG_UART_HOST 2 diff --git a/board/peppy/ec.tasklist b/board/peppy/ec.tasklist index 40d5ec4f60..3bde650552 100644 --- a/board/peppy/ec.tasklist +++ b/board/peppy/ec.tasklist @@ -20,7 +20,6 @@ TASK_ALWAYS(HOOKS, hook_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(VBOOTHASH, vboot_hash_task, NULL, LARGER_TASK_STACK_SIZE) \ TASK_ALWAYS(CHARGER, charger_task, NULL, TASK_STACK_SIZE) \ - TASK_NOTEST(THERMAL, thermal_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(CHIPSET, chipset_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \ TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \ diff --git a/board/slippy/board.c b/board/slippy/board.c index c05e5dc5be..12de82d8a6 100644 --- a/board/slippy/board.c +++ b/board/slippy/board.c @@ -24,6 +24,7 @@ #include "switch.h" #include "temp_sensor.h" #include "temp_sensor_g781.h" +#include "thermal.h" #include "timer.h" #include "util.h" @@ -180,7 +181,6 @@ const struct i2c_port_t i2c_ports[] = { }; BUILD_ASSERT(ARRAY_SIZE(i2c_ports) == I2C_PORTS_USED); - /* Temperature sensors data; must be in same order as enum temp_sensor_id. */ const struct temp_sensor_t temp_sensors[] = { {"PECI", TEMP_SENSOR_TYPE_CPU, peci_temp_sensor_get_val, 0, 2}, @@ -192,6 +192,18 @@ const struct temp_sensor_t temp_sensors[] = { }; BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT); +/* Thermal limits for each temp sensor. All temps are in degrees K. Must be in + * same order as enum temp_sensor_id. To always ignore any temp, use 0. + */ +struct ec_thermal_config thermal_params[] = { + /* Only the AP affects the thermal limits and fan speed. */ + {{C_TO_K(100), C_TO_K(114), C_TO_K(110)}, C_TO_K(60), C_TO_K(90)}, + {{0, 0, 0}, 0, 0}, + {{0, 0, 0}, 0, 0}, + {{0, 0, 0}, 0, 0}, +}; +BUILD_ASSERT(ARRAY_SIZE(thermal_params) == TEMP_SENSOR_COUNT); + struct keyboard_scan_config keyscan_config = { .output_settle_us = 40, .debounce_down_us = 6 * MSEC, diff --git a/board/slippy/board.h b/board/slippy/board.h index 19c741a0cc..0047aa58d0 100644 --- a/board/slippy/board.h +++ b/board/slippy/board.h @@ -25,6 +25,9 @@ #define CONFIG_POWER_BUTTON #define CONFIG_POWER_BUTTON_X86 #define CONFIG_PWM_FAN +#define CONFIG_PWM_FAN_RPM_MIN 1000 +#define CONFIG_PWM_FAN_RPM_MAX 5050 +#define CONFIG_PWM_FAN_POWER_GOOD GPIO_PP5000_PGOOD #define CONFIG_TEMP_SENSOR #define CONFIG_TEMP_SENSOR_G781 #define CONFIG_UART_HOST 2 diff --git a/board/slippy/ec.tasklist b/board/slippy/ec.tasklist index 40d5ec4f60..3bde650552 100644 --- a/board/slippy/ec.tasklist +++ b/board/slippy/ec.tasklist @@ -20,7 +20,6 @@ TASK_ALWAYS(HOOKS, hook_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(VBOOTHASH, vboot_hash_task, NULL, LARGER_TASK_STACK_SIZE) \ TASK_ALWAYS(CHARGER, charger_task, NULL, TASK_STACK_SIZE) \ - TASK_NOTEST(THERMAL, thermal_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(CHIPSET, chipset_task, NULL, TASK_STACK_SIZE) \ TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \ TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \ diff --git a/chip/lm4/chip_temp_sensor.c b/chip/lm4/chip_temp_sensor.c index d470da407f..e908391f7a 100644 --- a/chip/lm4/chip_temp_sensor.c +++ b/chip/lm4/chip_temp_sensor.c @@ -17,7 +17,7 @@ static void chip_temp_sensor_poll(void) { last_val = adc_read_channel(ADC_CH_EC_TEMP); } -DECLARE_HOOK(HOOK_SECOND, chip_temp_sensor_poll, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_SECOND, chip_temp_sensor_poll, HOOK_PRIO_TEMP_SENSOR); int chip_temp_sensor_get_val(int idx, int *temp_ptr) { diff --git a/chip/lm4/peci.c b/chip/lm4/peci.c index 88e13d18c1..d753c52730 100644 --- a/chip/lm4/peci.c +++ b/chip/lm4/peci.c @@ -78,7 +78,7 @@ static void peci_temp_sensor_poll(void) temp_vals[temp_idx] = peci_get_cpu_temp(); temp_idx = (temp_idx + 1) & (TEMP_AVG_LENGTH - 1); } -DECLARE_HOOK(HOOK_TICK, peci_temp_sensor_poll, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_TICK, peci_temp_sensor_poll, HOOK_PRIO_TEMP_SENSOR); static void peci_freq_changed(void) { diff --git a/chip/lm4/pwm_fan.c b/chip/lm4/pwm_fan.c index ffe75b68fa..ac5b7579c7 100644 --- a/chip/lm4/pwm_fan.c +++ b/chip/lm4/pwm_fan.c @@ -8,17 +8,19 @@ #include "clock.h" #include "common.h" #include "console.h" +#include "fan.h" #include "gpio.h" #include "hooks.h" #include "host_command.h" -#include "pwm.h" #include "registers.h" #include "system.h" #include "task.h" -#include "thermal.h" #include "timer.h" #include "util.h" +/*****************************************************************************/ +/* Chip-specific stuff */ + /* Maximum RPM for fan controller */ #define MAX_RPM 0x1fff /* Max PWM for fan controller */ @@ -33,16 +35,12 @@ */ #define CPU_FAN_SCALE 2 -#define PWMFAN_SYSJUMP_TAG 0x5046 /* "PF" */ -#define PWM_HOOK_VERSION 1 -/* Saved PWM state across sysjumps */ -struct pwm_fan_state { - uint16_t fan_rpm; - uint8_t fan_en; - char pad; /* Pad to multiple of 4 bytes. */ -}; +static int fan_get_enabled(void) +{ + return (LM4_FAN_FANCTL & (1 << FAN_CH_CPU)) ? 1 : 0; +} -void pwm_enable_fan(int enable) +static void fan_set_enabled(int enable) { if (enable) LM4_FAN_FANCTL |= (1 << FAN_CH_CPU); @@ -54,46 +52,40 @@ void pwm_enable_fan(int enable) #endif /* CONFIG_PWM_FAN_EN_GPIO */ } -int pwm_get_fan_enabled(void) -{ - return (LM4_FAN_FANCTL & (1 << FAN_CH_CPU)) ? 1 : 0; -} - -static int pwm_get_rpm_mode(void) +static int fan_get_rpm_mode(void) { return (LM4_FAN_FANCH(FAN_CH_CPU) & 0x0001) ? 0 : 1; } -void pwm_set_fan_rpm_mode(int rpm_mode) +static void fan_set_rpm_mode(int rpm_mode) { - int was_enabled = pwm_get_fan_enabled(); - int was_rpm = pwm_get_rpm_mode(); + int was_enabled = fan_get_enabled(); + int was_rpm = fan_get_rpm_mode(); if (!was_rpm && rpm_mode) { /* Enable RPM control */ - pwm_enable_fan(0); + fan_set_enabled(0); LM4_FAN_FANCH(FAN_CH_CPU) &= ~0x0001; - - pwm_enable_fan(was_enabled); + fan_set_enabled(was_enabled); } else if (was_rpm && !rpm_mode) { /* Disable RPM mode */ - pwm_enable_fan(0); + fan_set_enabled(0); LM4_FAN_FANCH(FAN_CH_CPU) |= 0x0001; - pwm_enable_fan(was_enabled); + fan_set_enabled(was_enabled); } } -int pwm_get_fan_rpm(void) +static int fan_get_rpm_actual(void) { return (LM4_FAN_FANCST(FAN_CH_CPU) & MAX_RPM) * CPU_FAN_SCALE; } -int pwm_get_fan_target_rpm(void) +static int fan_get_rpm_target(void) { return (LM4_FAN_FANCMD(FAN_CH_CPU) & MAX_RPM) * CPU_FAN_SCALE; } -void pwm_set_fan_target_rpm(int rpm) +static void fan_set_rpm_target(int rpm) { /* Apply fan scaling */ if (rpm > 0) @@ -106,7 +98,72 @@ void pwm_set_fan_target_rpm(int rpm) LM4_FAN_FANCMD(FAN_CH_CPU) = rpm; } -void pwm_set_fan_duty(int percent) +static int fan_get_duty_raw(void) +{ + return (LM4_FAN_FANCMD(FAN_CH_CPU) >> 16) & MAX_PWM; +} + +static void fan_set_duty_raw(int pwm) +{ + LM4_FAN_FANCMD(FAN_CH_CPU) = pwm << 16; +} + +static int fan_get_status(void) +{ + return (LM4_FAN_FANSTS >> (2 * FAN_CH_CPU)) & 0x03; +} +static const char * const human_status[] = { + "not spinning", "changing", "locked", "frustrated" +}; + +/** + * Return non-zero if fan is enabled but stalled. + */ +static int fan_is_stalled(void) +{ + /* Must be enabled with non-zero target to stall */ + if (!fan_get_enabled() || fan_get_rpm_target() == 0) + return 0; + + /* Check for stall condition */ + return (((LM4_FAN_FANSTS >> (2 * FAN_CH_CPU)) & 0x03) == 0) ? 1 : 0; +} + +/*****************************************************************************/ +/* Control functions */ + +/* True if we're listening to the thermal control task. False if we're setting + * things manually. */ +static int thermal_control_enabled; + +static void fan_set_thermal_control_enabled(int enable) +{ + thermal_control_enabled = enable; + + /* If controlling the fan, need it in RPM-control mode */ + if (enable) + fan_set_rpm_mode(1); +} + +/* The thermal task will only call this function with pct in [0,100]. */ +void pwm_fan_set_percent_needed(int pct) +{ + int rpm; + + if (!thermal_control_enabled) + return; + + rpm = pwm_fan_percent_to_rpm(pct); + + fan_set_rpm_target(rpm); +} + +static int fan_get_duty_cycle(void) +{ + return fan_get_duty_raw() * 100 / MAX_PWM; +} + +static void fan_set_duty_cycle(int percent) { int pwm; @@ -118,91 +175,100 @@ void pwm_set_fan_duty(int percent) pwm = (MAX_PWM * percent) / 100; /* Move the fan to manual control */ - pwm_set_fan_rpm_mode(0); + fan_set_rpm_mode(0); /* Always enable the fan */ - pwm_enable_fan(1); + fan_set_enabled(1); -#ifdef HAS_TASK_THERMAL /* Disable thermal engine automatic fan control. */ - thermal_control_fan(0); -#endif + fan_set_thermal_control_enabled(0); /* Set the duty cycle */ - LM4_FAN_FANCMD(FAN_CH_CPU) = pwm << 16; + fan_set_duty_raw(pwm); } -/** - * Return non-zero if fan is enabled but stalled. - */ -static int fan_is_stalled(void) -{ - /* Must be enabled with non-zero target to stall */ - if (!pwm_get_fan_enabled() || pwm_get_fan_target_rpm() == 0) - return 0; +/*****************************************************************************/ +/* Console commands */ - /* Check for stall condition */ - return (((LM4_FAN_FANSTS >> (2 * FAN_CH_CPU)) & 0x03) == 0) ? 1 : 0; +static int cc_fanauto(int argc, char **argv) +{ + fan_set_thermal_control_enabled(1); + return EC_SUCCESS; } +DECLARE_CONSOLE_COMMAND(fanauto, cc_fanauto, + NULL, + "Enable thermal fan control", + NULL); -/*****************************************************************************/ -/* Console commands */ -static int command_fan_info(int argc, char **argv) +static int cc_faninfo(int argc, char **argv) { - ccprintf("Actual: %4d rpm\n", pwm_get_fan_rpm()); - ccprintf("Target: %4d rpm\n", pwm_get_fan_target_rpm()); - ccprintf("Duty: %d%%\n", - ((LM4_FAN_FANCMD(FAN_CH_CPU) >> 16)) * 100 / MAX_PWM); - ccprintf("Status: %d\n", - (LM4_FAN_FANSTS >> (2 * FAN_CH_CPU)) & 0x03); - ccprintf("Mode: %s\n", pwm_get_rpm_mode() ? "rpm" : "duty"); - ccprintf("Enable: %s\n", pwm_get_fan_enabled() ? "yes" : "no"); -#ifdef BOARD_link /* HEY: Slippy? */ + int tmp; + ccprintf("Actual: %4d rpm\n", fan_get_rpm_actual()); + ccprintf("Target: %4d rpm\n", fan_get_rpm_target()); + ccprintf("Duty: %d%%\n", fan_get_duty_cycle()); + tmp = fan_get_status(); + ccprintf("Status: %d (%s)\n", tmp, human_status[tmp]); + ccprintf("Mode: %s\n", fan_get_rpm_mode() ? "rpm" : "duty"); + ccprintf("Auto: %s\n", thermal_control_enabled ? "yes" : "no"); + ccprintf("Enable: %s\n", fan_get_enabled() ? "yes" : "no"); +#ifdef CONFIG_PWM_FAN_POWER_GOOD ccprintf("Power: %s\n", - gpio_get_level(GPIO_PGOOD_5VALW) ? "yes" : "no"); +#ifdef CONFIG_PWM_FAN_EN_GPIO + gpio_get_level(CONFIG_PWM_FAN_EN_GPIO) && #endif + gpio_get_level(CONFIG_PWM_FAN_POWER_GOOD) ? "yes" : "no"); +#endif + return EC_SUCCESS; } -DECLARE_CONSOLE_COMMAND(faninfo, command_fan_info, +DECLARE_CONSOLE_COMMAND(faninfo, cc_faninfo, NULL, "Print fan info", NULL); -static int command_fan_set(int argc, char **argv) +static int cc_fanset(int argc, char **argv) { - int rpm = 0; + int rpm; char *e; if (argc < 2) return EC_ERROR_PARAM_COUNT; rpm = strtoi(argv[1], &e, 0); - if (*e) + if (*e == '%') { /* Wait, that's a percentage */ + ccprintf("Fan rpm given as %d%%\n", rpm); + if (rpm < 0) + rpm = 0; + else if (rpm > 100) + rpm = 100; + rpm = pwm_fan_percent_to_rpm(rpm); + } else if (*e) { return EC_ERROR_PARAM1; + } /* Move the fan to automatic control */ - pwm_set_fan_rpm_mode(1); + fan_set_rpm_mode(1); /* Always enable the fan */ - pwm_enable_fan(1); + fan_set_enabled(1); -#ifdef HAS_TASK_THERMAL /* Disable thermal engine automatic fan control. */ - thermal_control_fan(0); -#endif + fan_set_thermal_control_enabled(0); + + fan_set_rpm_target(rpm); - pwm_set_fan_target_rpm(rpm); + ccprintf("Setting fan rpm target to %d\n", rpm); return EC_SUCCESS; } -DECLARE_CONSOLE_COMMAND(fanset, command_fan_set, - "rpm", +DECLARE_CONSOLE_COMMAND(fanset, cc_fanset, + "rpm | pct%", "Set fan speed", NULL); -static int ec_command_fan_duty(int argc, char **argv) +static int cc_fanduty(int argc, char **argv) { int percent = 0; char *e; @@ -215,11 +281,11 @@ static int ec_command_fan_duty(int argc, char **argv) return EC_ERROR_PARAM1; ccprintf("Setting fan duty cycle to %d%%\n", percent); - pwm_set_fan_duty(percent); + fan_set_duty_cycle(percent); return EC_SUCCESS; } -DECLARE_CONSOLE_COMMAND(fanduty, ec_command_fan_duty, +DECLARE_CONSOLE_COMMAND(fanduty, cc_fanduty, "percent", "Set fan duty cycle", NULL); @@ -227,49 +293,66 @@ DECLARE_CONSOLE_COMMAND(fanduty, ec_command_fan_duty, /*****************************************************************************/ /* Host commands */ -int pwm_command_get_fan_target_rpm(struct host_cmd_handler_args *args) +static int hc_pwm_get_fan_target_rpm(struct host_cmd_handler_args *args) { struct ec_response_pwm_get_fan_rpm *r = args->response; - r->rpm = pwm_get_fan_target_rpm(); + r->rpm = fan_get_rpm_target(); args->response_size = sizeof(*r); return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_PWM_GET_FAN_TARGET_RPM, - pwm_command_get_fan_target_rpm, + hc_pwm_get_fan_target_rpm, EC_VER_MASK(0)); -int pwm_command_set_fan_target_rpm(struct host_cmd_handler_args *args) +static int hc_pwm_set_fan_target_rpm(struct host_cmd_handler_args *args) { const struct ec_params_pwm_set_fan_target_rpm *p = args->params; -#ifdef HAS_TASK_THERMAL - thermal_control_fan(0); -#endif - pwm_set_fan_rpm_mode(1); - pwm_set_fan_target_rpm(p->rpm); + fan_set_thermal_control_enabled(0); + fan_set_rpm_mode(1); + fan_set_rpm_target(p->rpm); return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_FAN_TARGET_RPM, - pwm_command_set_fan_target_rpm, + hc_pwm_set_fan_target_rpm, EC_VER_MASK(0)); -int pwm_command_fan_duty(struct host_cmd_handler_args *args) +static int hc_pwm_set_fan_duty(struct host_cmd_handler_args *args) { const struct ec_params_pwm_set_fan_duty *p = args->params; - pwm_set_fan_duty(p->percent); + fan_set_duty_cycle(p->percent); return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_FAN_DUTY, - pwm_command_fan_duty, + hc_pwm_set_fan_duty, EC_VER_MASK(0)); +static int hc_thermal_auto_fan_ctrl(struct host_cmd_handler_args *args) +{ + fan_set_thermal_control_enabled(1); + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_THERMAL_AUTO_FAN_CTRL, + hc_thermal_auto_fan_ctrl, + EC_VER_MASK(0)); + + /*****************************************************************************/ /* Hooks */ +#define PWMFAN_SYSJUMP_TAG 0x5046 /* "PF" */ +#define PWM_HOOK_VERSION 1 +/* Saved PWM state across sysjumps */ +struct pwm_fan_state { + uint16_t fan_rpm; + uint8_t fan_en; + char pad; /* Pad to multiple of 4 bytes. */ +}; + static void pwm_fan_init(void) { const struct pwm_fan_state *prev; @@ -305,13 +388,15 @@ static void pwm_fan_init(void) system_get_jump_tag(PWMFAN_SYSJUMP_TAG, &version, &size); if (prev && version == PWM_HOOK_VERSION && size == sizeof(*prev)) { /* Restore previous state. */ - pwm_enable_fan(prev->fan_en); - pwm_set_fan_target_rpm(prev->fan_rpm); + fan_set_enabled(prev->fan_en); + fan_set_rpm_target(prev->fan_rpm); } else { /* Set initial fan speed to maximum */ - pwm_set_fan_target_rpm(-1); + pwm_fan_set_percent_needed(100); } + fan_set_thermal_control_enabled(1); + /* Initialize memory-mapped data */ mapped = (uint16_t *)host_get_memmap(EC_MEMMAP_FAN); for (i = 0; i < EC_FAN_SPEED_ENTRIES; i++) @@ -332,7 +417,7 @@ static void pwm_fan_second(void) host_set_single_event(EC_HOST_EVENT_THERMAL); cprintf(CC_PWM, "[%T Fan stalled!]\n"); } else { - mapped[0] = pwm_get_fan_rpm(); + mapped[0] = fan_get_rpm_actual(); } } DECLARE_HOOK(HOOK_SECOND, pwm_fan_second, HOOK_PRIO_DEFAULT); @@ -341,8 +426,8 @@ static void pwm_fan_preserve_state(void) { struct pwm_fan_state state; - state.fan_en = pwm_get_fan_enabled(); - state.fan_rpm = pwm_get_fan_target_rpm(); + state.fan_en = fan_get_enabled(); + state.fan_rpm = fan_get_rpm_target(); system_add_jump_tag(PWMFAN_SYSJUMP_TAG, PWM_HOOK_VERSION, sizeof(state), &state); @@ -351,13 +436,20 @@ DECLARE_HOOK(HOOK_SYSJUMP, pwm_fan_preserve_state, HOOK_PRIO_DEFAULT); static void pwm_fan_resume(void) { - pwm_enable_fan(1); + fan_set_enabled(1); } DECLARE_HOOK(HOOK_CHIPSET_RESUME, pwm_fan_resume, HOOK_PRIO_DEFAULT); -static void pwm_fan_suspend(void) +static void pwm_fan_S3_S5(void) { - pwm_enable_fan(0); - pwm_set_fan_target_rpm(0); + /* Take back fan control when the processor shuts down */ + fan_set_thermal_control_enabled(1); + /* For now don't do anything with it. We'll have to turn it on again if + * we need active cooling during heavy battery charging or something. + */ + fan_set_rpm_target(0); + fan_set_enabled(0); /* crosbug.com/p/8097 */ + } -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pwm_fan_suspend, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pwm_fan_S3_S5, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pwm_fan_S3_S5, HOOK_PRIO_DEFAULT); diff --git a/chip/lm4/pwm_kblight.c b/chip/lm4/pwm_kblight.c index f4628303c9..25ad3dc45b 100644 --- a/chip/lm4/pwm_kblight.c +++ b/chip/lm4/pwm_kblight.c @@ -16,7 +16,6 @@ #include "registers.h" #include "system.h" #include "task.h" -#include "thermal.h" #include "timer.h" #include "util.h" diff --git a/common/build.mk b/common/build.mk index c617611a43..3594cbf780 100644 --- a/common/build.mk +++ b/common/build.mk @@ -54,10 +54,11 @@ common-$(CONFIG_ONEWIRE)+=onewire.o common-$(CONFIG_POWER_BUTTON)+=power_button.o common-$(CONFIG_POWER_BUTTON_X86)+=power_button_x86.o common-$(CONFIG_PSTORE)+=pstore_commands.o +common-$(CONFIG_PWM_FAN)+=pwm_fan.o common-$(CONFIG_REGULATOR_IR357X)+=regulator_ir357x.o common-$(CONFIG_SWITCH)+=switch.o common-$(CONFIG_WIRELESS)+=wireless.o -common-$(CONFIG_TEMP_SENSOR)+=temp_sensor.o +common-$(CONFIG_TEMP_SENSOR)+=temp_sensor.o thermal.o common-$(CONFIG_TEMP_SENSOR_G781)+=temp_sensor_g781.o common-$(CONFIG_TEMP_SENSOR_TMP006)+=temp_sensor_tmp006.o common-$(CONFIG_USB_PORT_POWER_SMART)+=usb_port_power_smart.o @@ -68,6 +69,5 @@ common-$(HAS_TASK_CONSOLE)+=console.o common-$(HAS_TASK_HOSTCMD)+=host_command.o host_event_commands.o common-$(HAS_TASK_KEYSCAN)+=keyboard_scan.o common-$(HAS_TASK_LIGHTBAR)+=lightbar.o -common-$(HAS_TASK_THERMAL)+=thermal.o common-$(HAS_TASK_VBOOTHASH)+=sha256.o vboot_hash.o common-$(TEST_BUILD)+=test_util.o diff --git a/common/host_event_commands.c b/common/host_event_commands.c index 4ee11a9de2..1d085b71cc 100644 --- a/common/host_event_commands.c +++ b/common/host_event_commands.c @@ -88,14 +88,12 @@ static void host_clear_events_b(uint32_t mask) * * @param throttle Enable (!=0) or disable(0) throttling */ -void host_throttle_cpu(int throttle) +test_mockable void host_throttle_cpu(int throttle) { if (throttle) - host_set_events(EC_HOST_EVENT_MASK( - EC_HOST_EVENT_THROTTLE_START)); + host_set_single_event(EC_HOST_EVENT_THROTTLE_START); else - host_set_events(EC_HOST_EVENT_MASK( - EC_HOST_EVENT_THROTTLE_STOP)); + host_set_single_event(EC_HOST_EVENT_THROTTLE_STOP); } /*****************************************************************************/ diff --git a/common/pwm_fan.c b/common/pwm_fan.c new file mode 100644 index 0000000000..758ee94ea7 --- /dev/null +++ b/common/pwm_fan.c @@ -0,0 +1,27 @@ +/* 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. + */ + +#include "common.h" +#include "fan.h" + +#ifndef CONFIG_PWM_FAN_RPM_CUSTOM +/* This is the default implementation. It's only called over [0,100]. + * Convert the percentage to a target RPM. We can't simply scale all + * the way down to zero because most fans won't turn that slowly, so + * we'll map [1,100] => [FAN_MIN,FAN_MAX], and [0] => "off". +*/ +int pwm_fan_percent_to_rpm(int pct) +{ + int rpm; + + if (!pct) + rpm = 0; + else + rpm = ((pct - 1) * CONFIG_PWM_FAN_RPM_MAX + + (100 - pct) * CONFIG_PWM_FAN_RPM_MIN) / 99; + + return rpm; +} +#endif /* CONFIG_PWM_FAN_RPM_CUSTOM */ diff --git a/common/temp_sensor.c b/common/temp_sensor.c index 9b8fbe32fb..3b5a59c587 100644 --- a/common/temp_sensor.c +++ b/common/temp_sensor.c @@ -16,7 +16,6 @@ #include "peci.h" #include "task.h" #include "temp_sensor.h" -#include "thermal.h" #include "timer.h" #include "tmp006.h" #include "util.h" @@ -66,8 +65,8 @@ static void update_mapped_memory(void) } } } -/* Run after other tick tasks, so sensors will have updated first. */ -DECLARE_HOOK(HOOK_SECOND, update_mapped_memory, HOOK_PRIO_DEFAULT + 1); +/* Run after other TEMP tasks, so sensors will have updated first. */ +DECLARE_HOOK(HOOK_SECOND, update_mapped_memory, HOOK_PRIO_TEMP_SENSOR_DONE); static void temp_sensor_init(void) { diff --git a/common/temp_sensor_tmp006.c b/common/temp_sensor_tmp006.c index 3cda55ac1a..45e81814f4 100644 --- a/common/temp_sensor_tmp006.c +++ b/common/temp_sensor_tmp006.c @@ -253,7 +253,7 @@ static void tmp006_poll(void) for (i = 0; i < TMP006_COUNT; ++i) tmp006_poll_sensor(i); } -DECLARE_HOOK(HOOK_SECOND, tmp006_poll, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_SECOND, tmp006_poll, HOOK_PRIO_TEMP_SENSOR); static void tmp006_init(void) { diff --git a/common/thermal.c b/common/thermal.c index 0015f5cca6..638bd0c0f7 100644 --- a/common/thermal.c +++ b/common/thermal.c @@ -3,16 +3,16 @@ * found in the LICENSE file. */ -/* Thermal engine module for Chrome EC */ +/* NEW thermal engine module for Chrome EC. This is a completely different + * implementation from the original version that shipped on Link. + */ #include "chipset.h" #include "common.h" #include "console.h" -#include "gpio.h" +#include "fan.h" #include "hooks.h" #include "host_command.h" -#include "pwm.h" -#include "task.h" #include "temp_sensor.h" #include "thermal.h" #include "timer.h" @@ -22,366 +22,246 @@ #define CPUTS(outstr) cputs(CC_THERMAL, outstr) #define CPRINTF(format, args...) cprintf(CC_THERMAL, format, ## args) -/* - * Temperature threshold configuration. Must be in the same order as in enum - * temp_sensor_type. Threshold values for overheated action first (warning, - * prochot, power-down), followed by fan speed stepping thresholds. - */ -test_export_static struct thermal_config_t - thermal_config[TEMP_SENSOR_TYPE_COUNT] = { - /* TEMP_SENSOR_TYPE_CPU */ - {THERMAL_CONFIG_WARNING_ON_FAIL, - {373, 378, 383, 327, 335, 343, 351, 359} } , - /* TEMP_SENSOR_TYPE_BOARD */ - {THERMAL_CONFIG_NO_FLAG, {THERMAL_THRESHOLD_DISABLE_ALL} }, - /* TEMP_SENSOR_TYPE_CASE */ - {THERMAL_CONFIG_NO_FLAG, {THERMAL_THRESHOLD_DISABLE_ALL} }, -}; - -/* Fan speed settings. Real max RPM is about 9300. */ -test_export_static const int fan_speed[THERMAL_FAN_STEPS + 1] = - {0, 3000, 4575, 6150, 7725, -1}; - -/* Number of consecutive overheated events for each temperature sensor. */ -static int8_t ot_count[TEMP_SENSOR_COUNT][THRESHOLD_COUNT + THERMAL_FAN_STEPS]; - -/* - * Flag that indicate if each threshold is reached. Note that higher threshold - * reached does not necessarily mean lower thresholds are reached (since we can - * disable any threshold.) - */ -static int8_t overheated[THRESHOLD_COUNT + THERMAL_FAN_STEPS]; -static int8_t *fan_threshold_reached = overheated + THRESHOLD_COUNT; - -static int fan_ctrl_on = 1; - -int thermal_set_threshold(enum temp_sensor_type type, int threshold_id, - int value) +test_mockable_static void smi_sensor_failure_warning(void) { - if (type < 0 || type >= TEMP_SENSOR_TYPE_COUNT) - return EC_ERROR_INVAL; - if (threshold_id < 0 || - threshold_id >= THRESHOLD_COUNT + THERMAL_FAN_STEPS) - return EC_ERROR_INVAL; - if (value < 0) - return EC_ERROR_INVAL; - - thermal_config[type].thresholds[threshold_id] = value; - - return EC_SUCCESS; + CPRINTF("[%T can't read any temp sensors!]\n"); + host_set_single_event(EC_HOST_EVENT_THERMAL); } -int thermal_get_threshold(enum temp_sensor_type type, int threshold_id) +static int fan_percent(int low, int high, int cur) { - if (type < 0 || type >= TEMP_SENSOR_TYPE_COUNT) - return -1; - if (threshold_id < 0 || - threshold_id >= THRESHOLD_COUNT + THERMAL_FAN_STEPS) - return -1; - - return thermal_config[type].thresholds[threshold_id]; + if (cur < low) + return 0; + if (cur > high) + return 100; + return 100 * (cur - low) / (high - low); } -void thermal_control_fan(int enable) -{ - fan_ctrl_on = enable; - - /* If controlling the fan, need it in RPM-control mode */ - if (enable) - pwm_set_fan_rpm_mode(1); -} +/* The logic below is hard-coded for only three thresholds: WARN, HIGH, HALT. + * This is just a sanity check to be sure we catch any changes in thermal.h + */ +BUILD_ASSERT(EC_TEMP_THRESH_COUNT == 3); -static void smi_overheated_warning(void) -{ - host_set_single_event(EC_HOST_EVENT_THERMAL_OVERLOAD); -} +/* Keep track of which thresholds have triggered */ +static cond_t cond_hot[EC_TEMP_THRESH_COUNT]; -static void smi_sensor_failure_warning(void) +static void thermal_control(void) { - host_set_single_event(EC_HOST_EVENT_THERMAL); -} + int i, j, t, rv, f; + int count_over[EC_TEMP_THRESH_COUNT]; + int count_under[EC_TEMP_THRESH_COUNT]; + int num_valid_limits[EC_TEMP_THRESH_COUNT]; + int num_sensors_read; + int fmax; + + /* Get ready to count things */ + memset(count_over, 0, sizeof(count_over)); + memset(count_under, 0, sizeof(count_under)); + memset(num_valid_limits, 0, sizeof(num_valid_limits)); + num_sensors_read = 0; + fmax = 0; + + /* go through all the sensors */ + for (i = 0; i < TEMP_SENSOR_COUNT; ++i) { -/* - * TODO: When we need different overheated action for different boards, move - * these actiona to a board-specific file. (e.g. board_thermal.c) - */ -static void overheated_action(void) -{ - static int cpu_down_count; + /* read one */ + rv = temp_sensor_read(i, &t); + if (rv != EC_SUCCESS) + continue; + else + num_sensors_read++; + + /* check all the limits */ + for (j = 0; j < EC_TEMP_THRESH_COUNT; j++) { + int limit = thermal_params[i].temp_host[j]; + if (limit) { + num_valid_limits[j]++; + if (t > limit) + count_over[j]++; + else if (t < limit) + count_under[j]++; + } + } - if (overheated[THRESHOLD_POWER_DOWN]) { - cprintf(CC_CHIPSET, - "[%T critical temperature; shutting down]\n"); - chipset_force_shutdown(); - host_set_single_event(EC_HOST_EVENT_THERMAL_SHUTDOWN); - return; + /* figure out the max fan needed, too */ + if (thermal_params[i].temp_fan_off && + thermal_params[i].temp_fan_max) { + f = fan_percent(thermal_params[i].temp_fan_off, + thermal_params[i].temp_fan_max, + t); + if (f > fmax) + fmax = f; + } } - if (overheated[THRESHOLD_CPU_DOWN]) { - cpu_down_count++; - if (cpu_down_count > 3) { - CPRINTF("[%T overheated; shutting down]\n"); - chipset_force_shutdown(); - host_set_single_event(EC_HOST_EVENT_THERMAL_SHUTDOWN); - } - } else { - cpu_down_count = 0; + if (!num_sensors_read) { + /* If we can't read any sensors, do nothing and hope + * it gets better. + * FIXME: What *should* we do? + */ + smi_sensor_failure_warning(); + return; } - if (overheated[THRESHOLD_WARNING]) { - smi_overheated_warning(); - chipset_throttle_cpu(1); - } else { - chipset_throttle_cpu(0); + /* See what the aggregated limits are. Any temp over the limit + * means it's hot, but all temps have to be under the limit to + * be cool again. + */ + for (j = 0; j < EC_TEMP_THRESH_COUNT; j++) { + if (count_over[j]) + cond_set_true(&cond_hot[j]); + else if (count_under[j] == num_valid_limits[j]) + cond_set_false(&cond_hot[j]); } - if (fan_ctrl_on) { - int i; - for (i = THERMAL_FAN_STEPS - 1; i >= 0; --i) - if (fan_threshold_reached[i]) - break; - pwm_set_fan_target_rpm(fan_speed[i + 1]); - } -} + /* What do we do about it? (note hard-coded logic). */ -/** - * Update counter and check if the counter has reached delay limit. - * - * Note that we have various delay periods to prevent one error value - * triggering an overheated action. - */ -static inline void update_and_check_stat(int temp, - int sensor_id, - int threshold_id) -{ - enum temp_sensor_type type = temp_sensors[sensor_id].type; - const struct thermal_config_t *config = thermal_config + type; - const int16_t threshold = config->thresholds[threshold_id]; - const int delay = temp_sensors[sensor_id].action_delay_sec; - - if (threshold <= 0) { - ot_count[sensor_id][threshold_id] = 0; - } else if (temp >= threshold) { - ++ot_count[sensor_id][threshold_id]; - if (ot_count[sensor_id][threshold_id] >= delay) { - ot_count[sensor_id][threshold_id] = delay; - overheated[threshold_id] = 1; - } - } else if (ot_count[sensor_id][threshold_id] >= delay && - temp >= threshold - 3) { - /* - * Once the threshold is reached, only deassert overheated if - * the temperature drops to 3 degrees below threshold. This - * hysteresis prevents a temperature oscillating around the - * threshold causing overheated actions to trigger repeatedly. + if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HALT])) { + CPRINTF("[%T thermal SHUTDOWN]\n"); + chipset_force_shutdown(); + } else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_HALT])) { + /* We don't reboot automatically - the user has to push + * the power button. It's likely that we can't even + * detect this sensor transition until then, but we + * do have to check in order to clear the cond_t. */ - overheated[threshold_id] = 1; + CPRINTF("[%T thermal no longer shutdown]\n"); } -} - -static void thermal_process(void) -{ - int i, j; - int cur_temp; - int flag; - int rv; - - for (i = 0; i < THRESHOLD_COUNT + THERMAL_FAN_STEPS; ++i) - overheated[i] = 0; - for (i = 0; i < TEMP_SENSOR_COUNT; ++i) { - enum temp_sensor_type type = temp_sensors[i].type; - - if (type == TEMP_SENSOR_TYPE_IGNORED) - continue; - - flag = thermal_config[type].config_flags; - - rv = temp_sensor_read(i, &cur_temp); - if (rv == EC_ERROR_NOT_POWERED) { - /* Sensor not powered; ignore it */ - continue; - } else if (rv) { - /* Other sensor failure */ - if (flag & THERMAL_CONFIG_WARNING_ON_FAIL) - smi_sensor_failure_warning(); - continue; - } - - for (j = 0; j < THRESHOLD_COUNT + THERMAL_FAN_STEPS; ++j) - update_and_check_stat(cur_temp, i, j); + if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HIGH])) { + CPRINTF("[%T thermal HIGH]\n"); + chipset_throttle_cpu(1); + } else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_HIGH])) { + CPRINTF("[%T thermal no longer high]\n"); + chipset_throttle_cpu(0); } - overheated_action(); -} - -void thermal_task(void) -{ - while (1) { - thermal_process(); - usleep(SECOND); + if (cond_went_true(&cond_hot[EC_TEMP_THRESH_WARN])) { + CPRINTF("[%T thermal WARN]\n"); + host_throttle_cpu(1); + } else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_WARN])) { + CPRINTF("[%T thermal no longer warn]\n"); + host_throttle_cpu(0); } -} -static void thermal_shutdown(void) -{ - /* Take back fan control when the processor shuts down */ - thermal_control_fan(1); + /* Max fan needed is what's needed. */ + pwm_fan_set_percent_needed(fmax); } -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, thermal_shutdown, HOOK_PRIO_DEFAULT); + +/* Wait until after the sensors have been read */ +DECLARE_HOOK(HOOK_SECOND, thermal_control, HOOK_PRIO_TEMP_SENSOR + 1); /*****************************************************************************/ /* Console commands */ -static void print_thermal_config(enum temp_sensor_type type) +static int command_thermalget(int argc, char **argv) { - const struct thermal_config_t *config = thermal_config + type; - ccprintf("Sensor Type %d:\n", type); - ccprintf("\tWarning: %d K\n", - config->thresholds[THRESHOLD_WARNING]); - ccprintf("\tCPU Down: %d K\n", - config->thresholds[THRESHOLD_CPU_DOWN]); - ccprintf("\tPower Down: %d K\n", - config->thresholds[THRESHOLD_POWER_DOWN]); -} - -static void print_fan_stepping(enum temp_sensor_type type) -{ - const struct thermal_config_t *config = thermal_config + type; int i; - ccprintf("Sensor Type %d:\n", type); - ccprintf("\tLowest speed: %d RPM\n", fan_speed[0]); - for (i = 0; i < THERMAL_FAN_STEPS; ++i) - ccprintf("\t%3d K: %d RPM\n", - config->thresholds[THRESHOLD_COUNT + i], - fan_speed[i+1]); -} - -static int command_thermal_config(int argc, char **argv) -{ - char *e; - int sensor_type, threshold_id, value; - - if (argc != 2 && argc != 4) - return EC_ERROR_PARAM_COUNT; - - sensor_type = strtoi(argv[1], &e, 0); - if (*e || sensor_type < 0 || sensor_type >= TEMP_SENSOR_TYPE_COUNT) - return EC_ERROR_PARAM1; - - if (argc == 2) { - print_thermal_config(sensor_type); - return EC_SUCCESS; + ccprintf("sensor warn high halt fan_off fan_max name\n"); + for (i = 0; i < TEMP_SENSOR_COUNT; i++) { + ccprintf(" %2d %3d %3d %3d %3d %3d %s\n", + i, + thermal_params[i].temp_host[EC_TEMP_THRESH_WARN], + thermal_params[i].temp_host[EC_TEMP_THRESH_HIGH], + thermal_params[i].temp_host[EC_TEMP_THRESH_HALT], + thermal_params[i].temp_fan_off, + thermal_params[i].temp_fan_max, + temp_sensors[i].name); } - threshold_id = strtoi(argv[2], &e, 0); - if (*e || threshold_id < 0 || threshold_id >= THRESHOLD_COUNT) - return EC_ERROR_PARAM2; - - value = strtoi(argv[3], &e, 0); - if (*e || value < 0) - return EC_ERROR_PARAM3; - - thermal_config[sensor_type].thresholds[threshold_id] = value; - ccprintf("Setting threshold %d of sensor type %d to %d\n", - threshold_id, sensor_type, value); - return EC_SUCCESS; } -DECLARE_CONSOLE_COMMAND(thermalconf, command_thermal_config, - "sensortype [threshold_id temp]", - "Get/set thermal threshold temp", +DECLARE_CONSOLE_COMMAND(thermalget, command_thermalget, + NULL, + "Print thermal parameters (degrees Kelvin)", NULL); -static int command_fan_config(int argc, char **argv) + +static int command_thermalset(int argc, char **argv) { + unsigned int n; + int i, val; char *e; - int sensor_type, stepping_id, value; - if (argc != 2 && argc != 4) + if (argc < 3 || argc > 7) return EC_ERROR_PARAM_COUNT; - sensor_type = strtoi(argv[1], &e, 0); - if ((e && *e) || sensor_type < 0 || - sensor_type >= TEMP_SENSOR_TYPE_COUNT) + n = (unsigned int)strtoi(argv[1], &e, 0); + if (*e) return EC_ERROR_PARAM1; - if (argc == 2) { - print_fan_stepping(sensor_type); - return EC_SUCCESS; + for (i = 2; i < argc; i++) { + val = strtoi(argv[i], &e, 0); + if (*e) + return EC_ERROR_PARAM1 + i - 1; + if (val < 0) + continue; + switch (i) { + case 2: + thermal_params[n].temp_host[EC_TEMP_THRESH_WARN] = val; + break; + case 3: + thermal_params[n].temp_host[EC_TEMP_THRESH_HIGH] = val; + break; + case 4: + thermal_params[n].temp_host[EC_TEMP_THRESH_HALT] = val; + break; + case 5: + thermal_params[n].temp_fan_off = val; + break; + case 6: + thermal_params[n].temp_fan_max = val; + break; + } } - stepping_id = strtoi(argv[2], &e, 0); - if ((e && *e) || stepping_id < 0 || stepping_id >= THERMAL_FAN_STEPS) - return EC_ERROR_PARAM2; - - value = strtoi(argv[3], &e, 0); - if (*e || value < 0) - return EC_ERROR_PARAM3; - - thermal_config[sensor_type].thresholds[THRESHOLD_COUNT + stepping_id] = - value; - ccprintf("Setting fan step %d of sensor type %d to %d K\n", - stepping_id, sensor_type, value); - + command_thermalget(0, 0); return EC_SUCCESS; } -DECLARE_CONSOLE_COMMAND(thermalfan, command_fan_config, - "sensortype [threshold_id rpm]", - "Get/set thermal threshold fan rpm", +DECLARE_CONSOLE_COMMAND(thermalset, command_thermalset, + "sensor warn [high [shutdown [fan_off [fan_max]]]]", + "Set thermal parameters (degrees Kelvin)." + " Use -1 to skip.", NULL); -static int command_thermal_auto_fan_ctrl(int argc, char **argv) -{ - thermal_control_fan(1); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(autofan, command_thermal_auto_fan_ctrl, - NULL, - "Enable thermal fan control", - NULL); + + /*****************************************************************************/ -/* Host commands */ +/* Host commands. We'll reuse the host command number, but this is version 1, + * not version 0. Different structs, different meanings. + */ static int thermal_command_set_threshold(struct host_cmd_handler_args *args) { - const struct ec_params_thermal_set_threshold *p = args->params; + const struct ec_params_thermal_set_threshold_v1 *p = args->params; - if (thermal_set_threshold(p->sensor_type, p->threshold_id, p->value)) - return EC_RES_ERROR; + if (p->sensor_num >= TEMP_SENSOR_COUNT) + return EC_RES_INVALID_PARAM; + + thermal_params[p->sensor_num] = p->cfg; return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_THERMAL_SET_THRESHOLD, thermal_command_set_threshold, - EC_VER_MASK(0)); + EC_VER_MASK(1)); static int thermal_command_get_threshold(struct host_cmd_handler_args *args) { - const struct ec_params_thermal_get_threshold *p = args->params; - struct ec_response_thermal_get_threshold *r = args->response; - int value = thermal_get_threshold(p->sensor_type, p->threshold_id); + const struct ec_params_thermal_get_threshold_v1 *p = args->params; + struct ec_thermal_config *r = args->response; - if (value == -1) - return EC_RES_ERROR; - r->value = value; + if (p->sensor_num >= TEMP_SENSOR_COUNT) + return EC_RES_INVALID_PARAM; + *r = thermal_params[p->sensor_num]; args->response_size = sizeof(*r); - return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_THERMAL_GET_THRESHOLD, thermal_command_get_threshold, - EC_VER_MASK(0)); + EC_VER_MASK(1)); -static int thermal_command_auto_fan_ctrl(struct host_cmd_handler_args *args) -{ - thermal_control_fan(1); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_THERMAL_AUTO_FAN_CTRL, - thermal_command_auto_fan_ctrl, - EC_VER_MASK(0)); diff --git a/include/config.h b/include/config.h index dd1c327f9b..9bdbc0b57d 100644 --- a/include/config.h +++ b/include/config.h @@ -465,12 +465,31 @@ */ #undef CONFIG_PSTORE +/*****************************************************************************/ /* Compile support for PWM control of cooling fans */ #undef CONFIG_PWM_FAN /* Name of active high GPIO to control power to the cooling fan */ #undef CONFIG_PWM_FAN_EN_GPIO +/* Fan speeds corresponding to 1% and 100% cooling (0% == off). */ +#undef CONFIG_PWM_FAN_RPM_MIN +#undef CONFIG_PWM_FAN_RPM_MAX + +/* Alternately, define this to replace the default mapping with your own + * board-specific function in board.c: + * + * int pwm_fan_percent_to_rpm(int pct); + * + */ +#undef CONFIG_PWM_FAN_RPM_CUSTOM + +/* If you define this, the "faninfo" console command will read the GPIO to + * display the state of the fan's power rail. + */ +#undef CONFIG_PWM_FAN_POWER_GOOD + +/*****************************************************************************/ /* Compile support for PWM output to keyboard backlight */ #undef CONFIG_PWM_KBLIGHT diff --git a/include/ec_commands.h b/include/ec_commands.h index 019c1ab88f..08fb20a996 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -1169,20 +1169,27 @@ struct ec_response_port80_last_boot { } __packed; /*****************************************************************************/ -/* Thermal engine commands */ +/* Thermal engine commands. Note that there are two implementations. We'll + * reuse the command number, but the data and behavior is incompatible. + * Version 0 is what originally shipped on Link. + * Version 1 separates the CPU thermal limits from the fan control. + */ -/* Set thershold value */ #define EC_CMD_THERMAL_SET_THRESHOLD 0x50 +#define EC_CMD_THERMAL_GET_THRESHOLD 0x51 +/* The version 0 structs are opaque. You have to know what they are for + * the get/set commands to make any sense. + */ + +/* Version 0 - set */ struct ec_params_thermal_set_threshold { uint8_t sensor_type; uint8_t threshold_id; uint16_t value; } __packed; -/* Get threshold value */ -#define EC_CMD_THERMAL_GET_THRESHOLD 0x51 - +/* Version 0 - get */ struct ec_params_thermal_get_threshold { uint8_t sensor_type; uint8_t threshold_id; @@ -1192,6 +1199,41 @@ struct ec_response_thermal_get_threshold { uint16_t value; } __packed; + +/* The version 1 structs are visible. */ +enum ec_temp_thresholds { + EC_TEMP_THRESH_WARN = 0, + EC_TEMP_THRESH_HIGH, + EC_TEMP_THRESH_HALT, + + EC_TEMP_THRESH_COUNT +}; + +/* Thermal configuration for one temperature sensor. Temps are in degrees K. + * Zero values will be silently ignored by the thermal task. + */ +struct ec_thermal_config { + uint32_t temp_host[EC_TEMP_THRESH_COUNT]; /* levels of hotness */ + uint32_t temp_fan_off; /* no active cooling needed */ + uint32_t temp_fan_max; /* max active cooling needed */ +} __packed; + +/* Version 1 - get config for one sensor. */ +struct ec_params_thermal_get_threshold_v1 { + uint32_t sensor_num; +} __packed; +/* This returns a struct ec_thermal_config */ + +/* Version 1 - set config for one sensor. + * Use read-modify-write for best results! */ +struct ec_params_thermal_set_threshold_v1 { + uint32_t sensor_num; + struct ec_thermal_config cfg; +} __packed; +/* This returns no data */ + +/****************************************************************************/ + /* Toggle automatic fan control */ #define EC_CMD_THERMAL_AUTO_FAN_CTRL 0x52 diff --git a/include/fan.h b/include/fan.h new file mode 100644 index 0000000000..ed91a8d3b4 --- /dev/null +++ b/include/fan.h @@ -0,0 +1,34 @@ +/* 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. + */ + +/* Fan control module for Chrome EC */ + +#ifndef __CROS_EC_FAN_H +#define __CROS_EC_FAN_H + +/** + * Set the amount of active cooling needed. The thermal control task will call + * this frequently, and the fan control logic will attempt to provide it. + * + * @param pct Percentage of cooling effort needed (0 - 100) + */ +void pwm_fan_set_percent_needed(int pct); + +/** + * This function translates the percentage of cooling needed into a target RPM. + * The default implementation should be sufficient for most needs, but + * individual boards may provide a custom version if needed (see config.h). + * + * @param pct Percentage of cooling effort needed (always in [0,100]) + * Return Target RPM for fan + */ +int pwm_fan_percent_to_rpm(int pct); + +/** + * Configure the fan GPIOs for the pwm module -- board-specific. + */ +void configure_fan_gpios(void); + +#endif /* __CROS_EC_FAN_H */ diff --git a/include/hooks.h b/include/hooks.h index d4bcea5ba8..2ef5523d1d 100644 --- a/include/hooks.h +++ b/include/hooks.h @@ -16,8 +16,6 @@ enum hook_priority { HOOK_PRIO_DEFAULT = 5000, /* Default priority */ HOOK_PRIO_LAST = 9999, /* Lowest priority */ - /* Specific values to lump related hooks together */ - HOOK_PRIO_TEMP_SENSOR = 6000, /* Specific hook vales for HOOK_INIT */ /* DMA inits before ADC, I2C, SPI */ HOOK_PRIO_INIT_DMA = HOOK_PRIO_FIRST + 1, @@ -29,6 +27,11 @@ enum hook_priority { HOOK_PRIO_INIT_LID = HOOK_PRIO_FIRST + 3, /* Power button inits before chipset and switch */ HOOK_PRIO_INIT_POWER_BUTTON = HOOK_PRIO_FIRST + 4, + + /* Specific values to lump temperature-related hooks together */ + HOOK_PRIO_TEMP_SENSOR = 6000, + /* After all sensors have been polled */ + HOOK_PRIO_TEMP_SENSOR_DONE = HOOK_PRIO_TEMP_SENSOR + 1, }; enum hook_type { diff --git a/include/pwm.h b/include/pwm.h index bb6b5a8549..4f399832c2 100644 --- a/include/pwm.h +++ b/include/pwm.h @@ -3,45 +3,9 @@ * found in the LICENSE file. */ -/* PWM module for Chrome EC */ - #ifndef __CROS_EC_PWM_H #define __CROS_EC_PWM_H -#include "common.h" - -/** - * Enable/disable the fan. - * - * Should be called by whatever function enables the power supply to the fan. - */ -void pwm_enable_fan(int enable); - -/** - * Enable/disable fan RPM control logic. - * - * @param rpm_mode Enable (1) or disable (0) RPM control loop; when - * disabled, fan duty cycle will be used. - */ -void pwm_set_fan_rpm_mode(int enable); - -/** - * Get the current fan RPM. - */ -int pwm_get_fan_rpm(void); - -/** - * Get the target fan RPM. - */ -int pwm_get_fan_target_rpm(void); - -/** - * Set the target fan RPM. - * - * @param rpm Target RPM; pass -1 to set fan to maximum. - */ -void pwm_set_fan_target_rpm(int rpm); - /** * Set the fan PWM duty cycle (0-100), disabling the automatic control. */ diff --git a/include/temp_sensor.h b/include/temp_sensor.h index c4943dda38..a63beb218b 100644 --- a/include/temp_sensor.h +++ b/include/temp_sensor.h @@ -49,7 +49,7 @@ extern const struct temp_sensor_t temp_sensors[]; #endif /** - * Get the most recently measured temperature for the sensor. + * Get the most recently measured temperature (in degrees K) for the sensor. * * @param id Sensor ID * @param temp_ptr Destination for temperature diff --git a/include/thermal.h b/include/thermal.h index 3e43dfcd1a..d3729591a0 100644 --- a/include/thermal.h +++ b/include/thermal.h @@ -8,70 +8,12 @@ #ifndef __CROS_EC_THERMAL_H #define __CROS_EC_THERMAL_H -#include "temp_sensor.h" +/* The thermal configuration for a single temp sensor is defined here. */ +#include "ec_commands.h" -#define THERMAL_CONFIG_NO_FLAG 0x0 -#define THERMAL_CONFIG_WARNING_ON_FAIL 0x1 - -/* - * Number of steps for fan speed control. Speed of each step is defined - * in thermal.c. - */ -#define THERMAL_FAN_STEPS 5 - -/* Set a threshold temperature to this value to disable the threshold limit. */ -#define THERMAL_THRESHOLD_DISABLE 0 - -/* This macro is used to disable all threshold for a sensor. The value 0 - * expands to all field in the array 'thresholds'. Change this if - * THERMAL_THRESHOLD_DISABLE is no longer 0. - */ -#define THERMAL_THRESHOLD_DISABLE_ALL 0 - -enum thermal_threshold { - THRESHOLD_WARNING = 0, /* Issue overheating warning */ - THRESHOLD_CPU_DOWN, /* Shut down CPU */ - THRESHOLD_POWER_DOWN, /* Shut down everything we can */ - THRESHOLD_COUNT -}; - -/* Configuration for temperature sensor */ -struct thermal_config_t { - /* Configuration flags */ - int8_t config_flags; - /* Threshold temperatures in K */ - int16_t thresholds[THRESHOLD_COUNT + THERMAL_FAN_STEPS]; -}; - -/** - * Set a threshold temperature. - * - * @param type Sensor type to set threshold for - * @param threshold_id Threshold ID to set - * @param value New threshold temperature in K, or - * THERMAL_THRESHOLD_DISABLE to disable this threshold. - * - * @return EC_SUCCESS if success, non-zero if error. - */ -int thermal_set_threshold(enum temp_sensor_type type, int threshold_id, - int value); - -/** - * Read a threshold temperature. - * - * @param type Sensor type to get threshold for - * @param threshold_id Threshold ID - * - * @return The threshold temperature in K, THERMAL_THRESHOLD_DISABLE if - * disabled, -1 if error. - */ -int thermal_get_threshold(enum temp_sensor_type type, int threshold_id); - -/** - * Enable/disable automatic fan speed control - * - * @param enable Enable (!=0) or disable (0) auto fan control +/* We need to to hold a config for each board's sensors. Not const, so we can + * tweak it at run-time if we have to. */ -void thermal_control_fan(int enable); +extern struct ec_thermal_config thermal_params[]; #endif /* __CROS_EC_THERMAL_H */ diff --git a/test/test_config.h b/test/test_config.h index 827a82642f..620a9dc096 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -23,4 +23,8 @@ #define CONFIG_EXTPOWER_FALCO #endif +#ifdef TEST_thermal +#define CONFIG_TEMP_SENSOR +#endif + #endif /* __CROS_EC_TEST_CONFIG_H */ diff --git a/test/thermal.c b/test/thermal.c index 485e19bfb5..7b2e3f9976 100644 --- a/test/thermal.c +++ b/test/thermal.c @@ -7,6 +7,7 @@ #include "common.h" #include "console.h" +#include "fan.h" #include "hooks.h" #include "host_command.h" #include "printf.h" @@ -16,41 +17,39 @@ #include "timer.h" #include "util.h" -static int mock_temp[TEMP_SENSOR_COUNT]; -static int fan_rpm; -static int fan_rpm_mode = 1; -static int cpu_throttled; -static int cpu_down; -extern struct thermal_config_t thermal_config[TEMP_SENSOR_TYPE_COUNT]; -extern const int fan_speed[THERMAL_FAN_STEPS + 1]; +/*****************************************************************************/ +/* 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; + int temp_sensor_read(enum temp_sensor_id id, int *temp_ptr) { if (mock_temp[id] >= 0) { *temp_ptr = mock_temp[id]; return EC_SUCCESS; - } else { - return -mock_temp[id]; } -} -void pwm_set_fan_rpm_mode(int rpm_mode) -{ - fan_rpm_mode = rpm_mode; -} - -void pwm_set_fan_target_rpm(int rpm) -{ - fan_rpm = rpm; + return EC_ERROR_NOT_POWERED; } void chipset_force_shutdown(void) { - cpu_down = 1; + cpu_shutdown = 1; } void chipset_throttle_cpu(int throttled) @@ -58,369 +57,440 @@ void chipset_throttle_cpu(int throttled) cpu_throttled = throttled; } -/*****************************************************************************/ -/* Test utilities */ - -/* Test shorthands */ -#define T_CPU TEMP_SENSOR_CPU -#define T_BOARD TEMP_SENSOR_BOARD -#define T_CASE TEMP_SENSOR_CASE -#define THRESHOLD(x, y) (thermal_config[x].thresholds[y]) -#define FAN_THRESHOLD(x, y) THRESHOLD(x, THRESHOLD_COUNT + (y)) - -static void reset_mock_temp(void) +void host_throttle_cpu(int throttled) { - int i; - enum temp_sensor_type type; - for (i = 0; i < TEMP_SENSOR_COUNT; ++i) { - type = temp_sensors[i].type; - mock_temp[i] = FAN_THRESHOLD(type, 0) - 1; - } + host_throttled = throttled; } -static int wait_fan_rpm(int rpm, int timeout_secs) +void pwm_fan_set_percent_needed(int pct) { - do { - if (fan_rpm == rpm) - return 1; - usleep(SECOND); - } while (timeout_secs--); - - return 0; + fan_pct = pct; } -static int wait_value(int *v, int target, int timeout_secs) +void smi_sensor_failure_warning(void) { - do { - if (*v == target) - return 1; - usleep(SECOND); - } while (timeout_secs--); + no_temps_read = 1; +} - 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 int wait_set(int *v, int timeout_secs) +static void all_temps(int t) { - return wait_value(v, 1, timeout_secs); + set_temps(t, t, t, t); } -static int wait_clear(int *v, int timeout_secs) +static void reset_mocks(void) { - return wait_value(v, 0, timeout_secs); + /* 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; } + /*****************************************************************************/ /* Tests */ static int test_init_val(void) { - /* Initial mock temperature values are all zero. */ + reset_mocks(); + sleep(2); + + TEST_ASSERT(host_throttled == 0); TEST_ASSERT(cpu_throttled == 0); - TEST_ASSERT(cpu_down == 0); - TEST_ASSERT(!(host_get_events() & - EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_OVERLOAD))); - TEST_ASSERT(!(host_get_events() & - EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN))); + TEST_ASSERT(cpu_shutdown == 0); + TEST_ASSERT(fan_pct == 0); + TEST_ASSERT(no_temps_read); - return EC_SUCCESS; -} + sleep(2); -static int test_cpu_fan(void) -{ - reset_mock_temp(); - - /* - * Increase CPU temperature to first fan step and check if - * the fan comes up. - */ - mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 0); - TEST_ASSERT(wait_fan_rpm(fan_speed[1], 11)); - - /* Increase CPU temperature to second fan step */ - mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 1); - TEST_ASSERT(wait_fan_rpm(fan_speed[2], 11)); - - /* Test threshold hysteresis */ - mock_temp[T_CPU]--; - usleep(15 * SECOND); - TEST_ASSERT(fan_rpm == fan_speed[2]); - - /* Test action delay */ - mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 4); - usleep((temp_sensors[T_CPU].action_delay_sec - 1) * SECOND); - TEST_ASSERT(fan_rpm == fan_speed[2]); - mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 0); + 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_safety(void) +static int test_sensors_can_be_read(void) { - reset_mock_temp(); + reset_mocks(); + mock_temp[2] = 100; - /* Trigger CPU throttling */ - mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_WARNING); - TEST_ASSERT(wait_set(&cpu_throttled, 11)); - TEST_ASSERT(host_get_events() & - EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_OVERLOAD)); + sleep(2); - /* Lower temperature. CPU not throttled anymore. */ - mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_WARNING) - 5; - TEST_ASSERT(wait_clear(&cpu_throttled, 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); - /* Thermal shutdown */ - mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_CPU_DOWN); - TEST_ASSERT(wait_set(&cpu_down, 11)); - TEST_ASSERT(host_get_events() & - EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN)); + return EC_SUCCESS; +} - mock_temp[T_CPU] = 0; - usleep(SECOND); - cpu_down = 0; - mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_POWER_DOWN); - TEST_ASSERT(wait_set(&cpu_down, 11)); - TEST_ASSERT(host_get_events() & - EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN)); +static int test_one_fan(void) +{ + reset_mocks(); + thermal_params[2].temp_fan_off = 100; + thermal_params[2].temp_fan_max = 200; - mock_temp[T_CPU] = 0; - cpu_down = 0; + all_temps(50); + sleep(2); + TEST_ASSERT(fan_pct == 0); - return EC_SUCCESS; -} + all_temps(100); + sleep(2); + TEST_ASSERT(fan_pct == 0); -static int test_sensor_failure(void) -{ - reset_mock_temp(); + all_temps(101); + sleep(2); + TEST_ASSERT(fan_pct == 1); - /* Failure due to sensor not powered should be ignored */ - mock_temp[T_CPU] = -EC_ERROR_NOT_POWERED; - usleep(5 * SECOND); - TEST_ASSERT(!(host_get_events() & - EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL))); + all_temps(130); + sleep(2); + TEST_ASSERT(fan_pct == 30); - /* Other failure should be pumped up to host */ - mock_temp[T_CPU] = -EC_ERROR_UNKNOWN; - usleep(5 * SECOND); - TEST_ASSERT(host_get_events() & - EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL)); + all_temps(150); + sleep(2); + TEST_ASSERT(fan_pct == 50); - return EC_SUCCESS; -} + all_temps(170); + sleep(2); + TEST_ASSERT(fan_pct == 70); -static int test_sensor_info(void) -{ - struct ec_params_temp_sensor_get_info params; - struct ec_response_temp_sensor_get_info resp; - int i; - - for (i = 0; i < TEMP_SENSOR_COUNT; ++i) { - params.id = i; - TEST_ASSERT(test_send_host_command( - EC_CMD_TEMP_SENSOR_GET_INFO, - 0, ¶ms, sizeof(params), - &resp, sizeof(resp)) == EC_RES_SUCCESS); - TEST_ASSERT_ARRAY_EQ(resp.sensor_name, - temp_sensors[i].name, - strlen(resp.sensor_name)); - TEST_ASSERT(resp.sensor_type == temp_sensors[i].type); - } + all_temps(200); + sleep(2); + TEST_ASSERT(fan_pct == 100); - params.id = TEMP_SENSOR_COUNT; - TEST_ASSERT(test_send_host_command( - EC_CMD_TEMP_SENSOR_GET_INFO, - 0, ¶ms, sizeof(params), - &resp, sizeof(resp)) != EC_RES_SUCCESS); + all_temps(300); + sleep(2); + TEST_ASSERT(fan_pct == 100); return EC_SUCCESS; } -static int set_threshold(int type, int threshold_id, int val) +static int test_two_fans(void) { - struct ec_params_thermal_set_threshold params; + reset_mocks(); - params.sensor_type = type; - params.threshold_id = threshold_id; - params.value = val; + 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; - return test_send_host_command(EC_CMD_THERMAL_SET_THRESHOLD, 0, ¶ms, - sizeof(params), NULL, 0); -} + all_temps(50); + sleep(2); + TEST_ASSERT(fan_pct == 0); -static int get_threshold(int type, int threshold_id, int *val) -{ - struct ec_params_thermal_get_threshold params; - struct ec_response_thermal_get_threshold resp; - int rv; + all_temps(100); + sleep(2); + TEST_ASSERT(fan_pct == 0); - params.sensor_type = type; - params.threshold_id = threshold_id; + all_temps(101); + sleep(2); + TEST_ASSERT(fan_pct == 1); - rv = test_send_host_command(EC_CMD_THERMAL_GET_THRESHOLD, 0, ¶ms, - sizeof(params), &resp, sizeof(resp)); - if (rv != EC_RES_SUCCESS) - return rv; + all_temps(130); + sleep(2); + /* fan 2 is still higher */ + TEST_ASSERT(fan_pct == 30); - *val = resp.value; - return EC_RES_SUCCESS; -} + all_temps(150); + sleep(2); + /* now fan 1 is higher: 150 = 75% of [120-160] */ + TEST_ASSERT(fan_pct == 75); -static int verify_threshold(int type, int threshold_id, int val) -{ - int actual_val; + all_temps(170); + sleep(2); + /* fan 1 is maxed now */ + TEST_ASSERT(fan_pct == 100); - if (get_threshold(type, threshold_id, &actual_val) != EC_RES_SUCCESS) - return 0; - return val == actual_val; -} + all_temps(200); + sleep(2); + TEST_ASSERT(fan_pct == 100); -static int test_threshold_hostcmd(void) -{ - reset_mock_temp(); - - /* Verify thresholds */ - TEST_ASSERT(verify_threshold(T_CPU, THRESHOLD_WARNING, - THRESHOLD(T_CPU, THRESHOLD_WARNING))); - TEST_ASSERT(verify_threshold(T_BOARD, THRESHOLD_WARNING, - THRESHOLD(T_BOARD, THRESHOLD_WARNING))); - TEST_ASSERT(verify_threshold(T_CPU, THRESHOLD_CPU_DOWN, - THRESHOLD(T_CPU, THRESHOLD_CPU_DOWN))); - - /* Lower CPU throttling threshold and trigger */ - TEST_ASSERT(set_threshold(T_CPU, THRESHOLD_WARNING, 350) == - EC_RES_SUCCESS); - mock_temp[T_CPU] = 355; - TEST_ASSERT(wait_set(&cpu_throttled, 11)); - TEST_ASSERT(host_get_events() & - EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_OVERLOAD)); - - /* Lower thermal shutdown threshold */ - TEST_ASSERT(set_threshold(T_CPU, THRESHOLD_CPU_DOWN, 353) == - EC_RES_SUCCESS); - TEST_ASSERT(wait_set(&cpu_down, 11)); - TEST_ASSERT(host_get_events() & - EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN)); - - /* Clear */ - mock_temp[T_CPU] = 0; - TEST_ASSERT(wait_clear(&cpu_throttled, 2)); - cpu_down = 0; + all_temps(300); + sleep(2); + TEST_ASSERT(fan_pct == 100); return EC_SUCCESS; } -static int test_threshold_console_cmd(void) +static int test_all_fans(void) { - char buf[100]; - - reset_mock_temp(); - - /* Lower CPU threshold and trigger */ - snprintf(buf, 100, "thermalconf %d %d 330\n", T_CPU, THRESHOLD_WARNING); - UART_INJECT(buf); - msleep(100); - mock_temp[T_CPU] = 335; - TEST_ASSERT(wait_set(&cpu_throttled, 11)); - TEST_ASSERT(host_get_events() & - EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_OVERLOAD)); - - /* Set first fan step to 280 K */ - snprintf(buf, 100, "thermalfan %d 0 280\n", T_CPU); - UART_INJECT(buf); - msleep(100); - mock_temp[T_CPU] = 280; - TEST_ASSERT(wait_fan_rpm(fan_speed[1], 11)); + 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_invalid_hostcmd(void) +static int test_one_limit(void) { - int dummy; - - TEST_ASSERT(set_threshold(TEMP_SENSOR_TYPE_COUNT, THRESHOLD_WARNING, - 100) != EC_RES_SUCCESS); - TEST_ASSERT(set_threshold(T_CPU, THRESHOLD_COUNT + THERMAL_FAN_STEPS, - 100) != EC_RES_SUCCESS); - TEST_ASSERT(get_threshold(TEMP_SENSOR_TYPE_COUNT, THRESHOLD_WARNING, - &dummy) != EC_RES_SUCCESS); - TEST_ASSERT(get_threshold(T_CPU, THRESHOLD_COUNT + THERMAL_FAN_STEPS, - &dummy) != EC_RES_SUCCESS); + 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); - return EC_SUCCESS; -} + all_temps(100); + sleep(2); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); -static int test_auto_fan_ctrl(void) -{ - reset_mock_temp(); + all_temps(101); + sleep(2); + TEST_ASSERT(host_throttled == 1); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); - /* Disable fan control */ - pwm_set_fan_rpm_mode(0); - thermal_control_fan(0); + all_temps(100); + sleep(2); + TEST_ASSERT(host_throttled == 1); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); - /* - * Increase CPU temperature to first fan step and check the fan - * doesn't come up. - */ - mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 0); - TEST_ASSERT(!wait_fan_rpm(fan_speed[1], 11)); + all_temps(99); + sleep(2); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); - /* Enable fan control */ - TEST_ASSERT(test_send_host_command(EC_CMD_THERMAL_AUTO_FAN_CTRL, 0, - NULL, 0, NULL, 0) == EC_RES_SUCCESS); - TEST_ASSERT(fan_rpm_mode == 1); - TEST_ASSERT(wait_fan_rpm(fan_speed[1], 11)); + all_temps(199); + sleep(2); + TEST_ASSERT(host_throttled == 1); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); - /* Disable fan control */ - pwm_set_fan_rpm_mode(0); - thermal_control_fan(0); + all_temps(200); + sleep(2); + TEST_ASSERT(host_throttled == 1); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(201); + sleep(2); + TEST_ASSERT(host_throttled == 1); + TEST_ASSERT(cpu_throttled == 1); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(200); + sleep(2); + TEST_ASSERT(host_throttled == 1); + TEST_ASSERT(cpu_throttled == 1); + TEST_ASSERT(cpu_shutdown == 0); + + all_temps(199); + sleep(2); + TEST_ASSERT(host_throttled == 1); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); - /* Increase CPU temperature to second fan step */ - mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 1); - TEST_ASSERT(!wait_fan_rpm(fan_speed[2], 11)); + all_temps(99); + sleep(2); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_shutdown == 0); - /* Enable fan control by console command */ - UART_INJECT("autofan\n"); - msleep(100); - TEST_ASSERT(fan_rpm_mode == 1); - TEST_ASSERT(wait_fan_rpm(fan_speed[2], 11)); + all_temps(201); + sleep(2); + TEST_ASSERT(host_throttled == 1); + TEST_ASSERT(cpu_throttled == 1); + 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 == 1); + TEST_ASSERT(cpu_throttled == 1); + 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 == 1); + TEST_ASSERT(cpu_throttled == 1); + /* 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 check_assumption(void) +static int test_several_limits(void) { - TEST_ASSERT((int)TEMP_SENSOR_CPU == (int)TEMP_SENSOR_TYPE_CPU); - TEST_ASSERT((int)TEMP_SENSOR_BOARD == (int)TEMP_SENSOR_TYPE_BOARD); - TEST_ASSERT((int)TEMP_SENSOR_CASE == (int)TEMP_SENSOR_TYPE_CASE); + 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; - TEST_ASSERT(temp_sensors[T_CPU].action_delay_sec != 0); + 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 == 1); /* 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 == 1); + TEST_ASSERT(cpu_throttled == 1); + TEST_ASSERT(cpu_shutdown == 0); + + set_temps(500, 100, 50, 40); /* 1=low, 2=low, 3=high */ + sleep(2); + TEST_ASSERT(host_throttled == 1); + TEST_ASSERT(cpu_throttled == 1); + TEST_ASSERT(cpu_shutdown == 0); + + set_temps(500, 100, 50, 41); /* 1=low, 2=low, 3=shutdown */ + sleep(2); + TEST_ASSERT(host_throttled == 1); + TEST_ASSERT(cpu_throttled == 1); + TEST_ASSERT(cpu_shutdown == 1); + + all_temps(0); /* reset from shutdown */ + sleep(2); + TEST_ASSERT(host_throttled == 0); + TEST_ASSERT(cpu_throttled == 0); - TEST_ASSERT(thermal_config[T_CPU].config_flags & - THERMAL_CONFIG_WARNING_ON_FAIL); return EC_SUCCESS; } + void run_test(void) { - test_reset(); - - /* Test assumptions */ - RUN_TEST(check_assumption); - RUN_TEST(test_init_val); - RUN_TEST(test_cpu_fan); - /* No tests for board and case temp sensors as they are ignored. */ - RUN_TEST(test_safety); - RUN_TEST(test_sensor_failure); - RUN_TEST(test_auto_fan_ctrl); - RUN_TEST(test_sensor_info); - RUN_TEST(test_threshold_hostcmd); - RUN_TEST(test_invalid_hostcmd); - RUN_TEST(test_threshold_console_cmd); + 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); test_print_result(); } diff --git a/test/thermal.tasklist b/test/thermal.tasklist index ab0b5484f5..aaa89ce45c 100644 --- a/test/thermal.tasklist +++ b/test/thermal.tasklist @@ -14,5 +14,4 @@ * '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(THERMAL, thermal_task, NULL, TASK_STACK_SIZE) + #define CONFIG_TEST_TASK_LIST /* No test tasks */ diff --git a/test/thermal_old.c b/test/thermal_old.c new file mode 100644 index 0000000000..485e19bfb5 --- /dev/null +++ b/test/thermal_old.c @@ -0,0 +1,426 @@ +/* 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 "common.h" +#include "console.h" +#include "hooks.h" +#include "host_command.h" +#include "printf.h" +#include "temp_sensor.h" +#include "test_util.h" +#include "thermal.h" +#include "timer.h" +#include "util.h" + +static int mock_temp[TEMP_SENSOR_COUNT]; +static int fan_rpm; +static int fan_rpm_mode = 1; +static int cpu_throttled; +static int cpu_down; + +extern struct thermal_config_t thermal_config[TEMP_SENSOR_TYPE_COUNT]; +extern const int fan_speed[THERMAL_FAN_STEPS + 1]; + +/*****************************************************************************/ +/* Mock functions */ + +int temp_sensor_read(enum temp_sensor_id id, int *temp_ptr) +{ + if (mock_temp[id] >= 0) { + *temp_ptr = mock_temp[id]; + return EC_SUCCESS; + } else { + return -mock_temp[id]; + } +} + +void pwm_set_fan_rpm_mode(int rpm_mode) +{ + fan_rpm_mode = rpm_mode; +} + +void pwm_set_fan_target_rpm(int rpm) +{ + fan_rpm = rpm; +} + +void chipset_force_shutdown(void) +{ + cpu_down = 1; +} + +void chipset_throttle_cpu(int throttled) +{ + cpu_throttled = throttled; +} + +/*****************************************************************************/ +/* Test utilities */ + +/* Test shorthands */ +#define T_CPU TEMP_SENSOR_CPU +#define T_BOARD TEMP_SENSOR_BOARD +#define T_CASE TEMP_SENSOR_CASE +#define THRESHOLD(x, y) (thermal_config[x].thresholds[y]) +#define FAN_THRESHOLD(x, y) THRESHOLD(x, THRESHOLD_COUNT + (y)) + +static void reset_mock_temp(void) +{ + int i; + enum temp_sensor_type type; + for (i = 0; i < TEMP_SENSOR_COUNT; ++i) { + type = temp_sensors[i].type; + mock_temp[i] = FAN_THRESHOLD(type, 0) - 1; + } +} + +static int wait_fan_rpm(int rpm, int timeout_secs) +{ + do { + if (fan_rpm == rpm) + return 1; + usleep(SECOND); + } while (timeout_secs--); + + return 0; +} + +static int wait_value(int *v, int target, int timeout_secs) +{ + do { + if (*v == target) + return 1; + usleep(SECOND); + } while (timeout_secs--); + + return 0; +} + +static int wait_set(int *v, int timeout_secs) +{ + return wait_value(v, 1, timeout_secs); +} + +static int wait_clear(int *v, int timeout_secs) +{ + return wait_value(v, 0, timeout_secs); +} + +/*****************************************************************************/ +/* Tests */ + +static int test_init_val(void) +{ + /* Initial mock temperature values are all zero. */ + TEST_ASSERT(cpu_throttled == 0); + TEST_ASSERT(cpu_down == 0); + TEST_ASSERT(!(host_get_events() & + EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_OVERLOAD))); + TEST_ASSERT(!(host_get_events() & + EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN))); + + return EC_SUCCESS; +} + +static int test_cpu_fan(void) +{ + reset_mock_temp(); + + /* + * Increase CPU temperature to first fan step and check if + * the fan comes up. + */ + mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 0); + TEST_ASSERT(wait_fan_rpm(fan_speed[1], 11)); + + /* Increase CPU temperature to second fan step */ + mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 1); + TEST_ASSERT(wait_fan_rpm(fan_speed[2], 11)); + + /* Test threshold hysteresis */ + mock_temp[T_CPU]--; + usleep(15 * SECOND); + TEST_ASSERT(fan_rpm == fan_speed[2]); + + /* Test action delay */ + mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 4); + usleep((temp_sensors[T_CPU].action_delay_sec - 1) * SECOND); + TEST_ASSERT(fan_rpm == fan_speed[2]); + mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 0); + + return EC_SUCCESS; +} + +static int test_safety(void) +{ + reset_mock_temp(); + + /* Trigger CPU throttling */ + mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_WARNING); + TEST_ASSERT(wait_set(&cpu_throttled, 11)); + TEST_ASSERT(host_get_events() & + EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_OVERLOAD)); + + /* Lower temperature. CPU not throttled anymore. */ + mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_WARNING) - 5; + TEST_ASSERT(wait_clear(&cpu_throttled, 2)); + + /* Thermal shutdown */ + mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_CPU_DOWN); + TEST_ASSERT(wait_set(&cpu_down, 11)); + TEST_ASSERT(host_get_events() & + EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN)); + + mock_temp[T_CPU] = 0; + usleep(SECOND); + cpu_down = 0; + + mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_POWER_DOWN); + TEST_ASSERT(wait_set(&cpu_down, 11)); + TEST_ASSERT(host_get_events() & + EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN)); + + mock_temp[T_CPU] = 0; + cpu_down = 0; + + return EC_SUCCESS; +} + +static int test_sensor_failure(void) +{ + reset_mock_temp(); + + /* Failure due to sensor not powered should be ignored */ + mock_temp[T_CPU] = -EC_ERROR_NOT_POWERED; + usleep(5 * SECOND); + TEST_ASSERT(!(host_get_events() & + EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL))); + + /* Other failure should be pumped up to host */ + mock_temp[T_CPU] = -EC_ERROR_UNKNOWN; + usleep(5 * SECOND); + TEST_ASSERT(host_get_events() & + EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL)); + + return EC_SUCCESS; +} + +static int test_sensor_info(void) +{ + struct ec_params_temp_sensor_get_info params; + struct ec_response_temp_sensor_get_info resp; + int i; + + for (i = 0; i < TEMP_SENSOR_COUNT; ++i) { + params.id = i; + TEST_ASSERT(test_send_host_command( + EC_CMD_TEMP_SENSOR_GET_INFO, + 0, ¶ms, sizeof(params), + &resp, sizeof(resp)) == EC_RES_SUCCESS); + TEST_ASSERT_ARRAY_EQ(resp.sensor_name, + temp_sensors[i].name, + strlen(resp.sensor_name)); + TEST_ASSERT(resp.sensor_type == temp_sensors[i].type); + } + + params.id = TEMP_SENSOR_COUNT; + TEST_ASSERT(test_send_host_command( + EC_CMD_TEMP_SENSOR_GET_INFO, + 0, ¶ms, sizeof(params), + &resp, sizeof(resp)) != EC_RES_SUCCESS); + + return EC_SUCCESS; +} + +static int set_threshold(int type, int threshold_id, int val) +{ + struct ec_params_thermal_set_threshold params; + + params.sensor_type = type; + params.threshold_id = threshold_id; + params.value = val; + + return test_send_host_command(EC_CMD_THERMAL_SET_THRESHOLD, 0, ¶ms, + sizeof(params), NULL, 0); +} + +static int get_threshold(int type, int threshold_id, int *val) +{ + struct ec_params_thermal_get_threshold params; + struct ec_response_thermal_get_threshold resp; + int rv; + + params.sensor_type = type; + params.threshold_id = threshold_id; + + rv = test_send_host_command(EC_CMD_THERMAL_GET_THRESHOLD, 0, ¶ms, + sizeof(params), &resp, sizeof(resp)); + if (rv != EC_RES_SUCCESS) + return rv; + + *val = resp.value; + return EC_RES_SUCCESS; +} + +static int verify_threshold(int type, int threshold_id, int val) +{ + int actual_val; + + if (get_threshold(type, threshold_id, &actual_val) != EC_RES_SUCCESS) + return 0; + return val == actual_val; +} + +static int test_threshold_hostcmd(void) +{ + reset_mock_temp(); + + /* Verify thresholds */ + TEST_ASSERT(verify_threshold(T_CPU, THRESHOLD_WARNING, + THRESHOLD(T_CPU, THRESHOLD_WARNING))); + TEST_ASSERT(verify_threshold(T_BOARD, THRESHOLD_WARNING, + THRESHOLD(T_BOARD, THRESHOLD_WARNING))); + TEST_ASSERT(verify_threshold(T_CPU, THRESHOLD_CPU_DOWN, + THRESHOLD(T_CPU, THRESHOLD_CPU_DOWN))); + + /* Lower CPU throttling threshold and trigger */ + TEST_ASSERT(set_threshold(T_CPU, THRESHOLD_WARNING, 350) == + EC_RES_SUCCESS); + mock_temp[T_CPU] = 355; + TEST_ASSERT(wait_set(&cpu_throttled, 11)); + TEST_ASSERT(host_get_events() & + EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_OVERLOAD)); + + /* Lower thermal shutdown threshold */ + TEST_ASSERT(set_threshold(T_CPU, THRESHOLD_CPU_DOWN, 353) == + EC_RES_SUCCESS); + TEST_ASSERT(wait_set(&cpu_down, 11)); + TEST_ASSERT(host_get_events() & + EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN)); + + /* Clear */ + mock_temp[T_CPU] = 0; + TEST_ASSERT(wait_clear(&cpu_throttled, 2)); + cpu_down = 0; + + return EC_SUCCESS; +} + +static int test_threshold_console_cmd(void) +{ + char buf[100]; + + reset_mock_temp(); + + /* Lower CPU threshold and trigger */ + snprintf(buf, 100, "thermalconf %d %d 330\n", T_CPU, THRESHOLD_WARNING); + UART_INJECT(buf); + msleep(100); + mock_temp[T_CPU] = 335; + TEST_ASSERT(wait_set(&cpu_throttled, 11)); + TEST_ASSERT(host_get_events() & + EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_OVERLOAD)); + + /* Set first fan step to 280 K */ + snprintf(buf, 100, "thermalfan %d 0 280\n", T_CPU); + UART_INJECT(buf); + msleep(100); + mock_temp[T_CPU] = 280; + TEST_ASSERT(wait_fan_rpm(fan_speed[1], 11)); + + return EC_SUCCESS; +} + +static int test_invalid_hostcmd(void) +{ + int dummy; + + TEST_ASSERT(set_threshold(TEMP_SENSOR_TYPE_COUNT, THRESHOLD_WARNING, + 100) != EC_RES_SUCCESS); + TEST_ASSERT(set_threshold(T_CPU, THRESHOLD_COUNT + THERMAL_FAN_STEPS, + 100) != EC_RES_SUCCESS); + TEST_ASSERT(get_threshold(TEMP_SENSOR_TYPE_COUNT, THRESHOLD_WARNING, + &dummy) != EC_RES_SUCCESS); + TEST_ASSERT(get_threshold(T_CPU, THRESHOLD_COUNT + THERMAL_FAN_STEPS, + &dummy) != EC_RES_SUCCESS); + + return EC_SUCCESS; +} + +static int test_auto_fan_ctrl(void) +{ + reset_mock_temp(); + + /* Disable fan control */ + pwm_set_fan_rpm_mode(0); + thermal_control_fan(0); + + /* + * Increase CPU temperature to first fan step and check the fan + * doesn't come up. + */ + mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 0); + TEST_ASSERT(!wait_fan_rpm(fan_speed[1], 11)); + + /* Enable fan control */ + TEST_ASSERT(test_send_host_command(EC_CMD_THERMAL_AUTO_FAN_CTRL, 0, + NULL, 0, NULL, 0) == EC_RES_SUCCESS); + TEST_ASSERT(fan_rpm_mode == 1); + TEST_ASSERT(wait_fan_rpm(fan_speed[1], 11)); + + /* Disable fan control */ + pwm_set_fan_rpm_mode(0); + thermal_control_fan(0); + + /* Increase CPU temperature to second fan step */ + mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 1); + TEST_ASSERT(!wait_fan_rpm(fan_speed[2], 11)); + + /* Enable fan control by console command */ + UART_INJECT("autofan\n"); + msleep(100); + TEST_ASSERT(fan_rpm_mode == 1); + TEST_ASSERT(wait_fan_rpm(fan_speed[2], 11)); + + + return EC_SUCCESS; +} + +static int check_assumption(void) +{ + TEST_ASSERT((int)TEMP_SENSOR_CPU == (int)TEMP_SENSOR_TYPE_CPU); + TEST_ASSERT((int)TEMP_SENSOR_BOARD == (int)TEMP_SENSOR_TYPE_BOARD); + TEST_ASSERT((int)TEMP_SENSOR_CASE == (int)TEMP_SENSOR_TYPE_CASE); + + TEST_ASSERT(temp_sensors[T_CPU].action_delay_sec != 0); + + TEST_ASSERT(thermal_config[T_CPU].config_flags & + THERMAL_CONFIG_WARNING_ON_FAIL); + + return EC_SUCCESS; +} + +void run_test(void) +{ + test_reset(); + + /* Test assumptions */ + RUN_TEST(check_assumption); + + RUN_TEST(test_init_val); + RUN_TEST(test_cpu_fan); + /* No tests for board and case temp sensors as they are ignored. */ + RUN_TEST(test_safety); + RUN_TEST(test_sensor_failure); + RUN_TEST(test_auto_fan_ctrl); + RUN_TEST(test_sensor_info); + RUN_TEST(test_threshold_hostcmd); + RUN_TEST(test_invalid_hostcmd); + RUN_TEST(test_threshold_console_cmd); + + test_print_result(); +} diff --git a/test/thermal.py b/test/thermal_old.py index 3a0453c3bb..3a0453c3bb 100644 --- a/test/thermal.py +++ b/test/thermal_old.py diff --git a/util/ectool.c b/util/ectool.c index 0ee39dcb9e..b25edb88b8 100644 --- a/util/ectool.c +++ b/util/ectool.c @@ -140,10 +140,10 @@ const char help_str[] = " Print temperature.\n" " tempsinfo <sensorid>\n" " Print temperature sensor info.\n" - " thermalget <sensor_id> <threshold_id>\n" - " Get the threshold temperature value from thermal engine.\n" - " thermalset <sensor_id> <threshold_id> <value>\n" - " Set the threshold temperature value for thermal engine.\n" + " thermalget <platform-specific args>\n" + " Get the threshold temperature values from the thermal engine.\n" + " thermalset <platform-specific args>\n" + " Set the threshold temperature values for the thermal engine.\n" " tmp006cal <tmp006_index> [<S0> <b0> <b1> <b2>]\n" " Get/set TMP006 calibration\n" " usbchargemode <port> <mode>\n" @@ -859,7 +859,7 @@ int cmd_temp_sensor_info(int argc, char *argv[]) } -int cmd_thermal_get_threshold(int argc, char *argv[]) +int cmd_thermal_get_threshold_v0(int argc, char *argv[]) { struct ec_params_thermal_get_threshold p; struct ec_response_thermal_get_threshold r; @@ -899,7 +899,7 @@ int cmd_thermal_get_threshold(int argc, char *argv[]) } -int cmd_thermal_set_threshold(int argc, char *argv[]) +int cmd_thermal_set_threshold_v0(int argc, char *argv[]) { struct ec_params_thermal_set_threshold p; char *e; @@ -942,6 +942,164 @@ int cmd_thermal_set_threshold(int argc, char *argv[]) } +int cmd_thermal_get_threshold_v1(int argc, char *argv[]) +{ + struct ec_params_thermal_get_threshold_v1 p; + struct ec_thermal_config r; + struct ec_params_temp_sensor_get_info pi; + struct ec_response_temp_sensor_get_info ri; + int rv; + int i; + + printf("sensor warn high halt fan_off fan_max name\n"); + for (i = 0; i < 99; i++) { /* number of sensors is unknown */ + + /* ask for one */ + p.sensor_num = i; + rv = ec_command(EC_CMD_THERMAL_GET_THRESHOLD, 1, + &p, sizeof(p), &r, sizeof(r)); + if (rv <= 0) /* stop on first failure */ + break; + + /* ask for its name, too */ + pi.id = i; + rv = ec_command(EC_CMD_TEMP_SENSOR_GET_INFO, 0, + &pi, sizeof(pi), &ri, sizeof(ri)); + + /* print what we know */ + printf(" %2d %3d %3d %3d %3d %3d %s\n", + i, + r.temp_host[EC_TEMP_THRESH_WARN], + r.temp_host[EC_TEMP_THRESH_HIGH], + r.temp_host[EC_TEMP_THRESH_HALT], + r.temp_fan_off, r.temp_fan_max, + rv > 0 ? ri.sensor_name : "?"); + } + if (i) + printf("(all temps in degrees Kelvin)\n"); + + return 0; +} + +int cmd_thermal_set_threshold_v1(int argc, char *argv[]) +{ + struct ec_params_thermal_get_threshold_v1 p; + struct ec_thermal_config r; + struct ec_params_thermal_set_threshold_v1 s; + int i, n, val, rv; + char *e; + + if (argc < 3 || argc > 7) { + printf("Usage: %s" + " sensor warn [high [shutdown [fan_off [fan_max]]]]\n", + argv[0]); + return 1; + } + + n = strtod(argv[1], &e); + if (e && *e) { + printf("arg %d is invalid\n", 1); + return 1; + } + + p.sensor_num = n; + rv = ec_command(EC_CMD_THERMAL_GET_THRESHOLD, 1, + &p, sizeof(p), &r, sizeof(r)); + if (rv <= 0) + return rv; + + s.sensor_num = n; + s.cfg = r; + + for (i = 2; i < argc; i++) { + val = strtod(argv[i], &e); + if (e && *e) { + printf("arg %d is invalid\n", i); + return 1; + } + + if (val < 0) + continue; + switch (i) { + case 2: + case 3: + case 4: + s.cfg.temp_host[i-2] = val; + break; + case 5: + s.cfg.temp_fan_off = val; + break; + case 6: + s.cfg.temp_fan_max = val; + break; + } + } + + rv = ec_command(EC_CMD_THERMAL_SET_THRESHOLD, 1, + &s, sizeof(s), NULL, 0); + + return rv; +} + + +static int thermal_threshold_version(void) +{ + struct ec_params_thermal_get_threshold v0_p; + struct ec_response_thermal_get_threshold v0_r; + struct ec_params_thermal_get_threshold_v1 v1_p; + struct ec_thermal_config v1_r; + int rv; + + v1_p.sensor_num = 0; + rv = ec_command(EC_CMD_THERMAL_GET_THRESHOLD, 1, + &v1_p, sizeof(v1_p), &v1_r, sizeof(v1_r)); + /* FIXME: Verson 1 will only return these responses */ + /* FIXME: if (??? == EC_RES_SUCCESS || ??? == EC_RES_INVALID_PARAM) */ + if (rv > 0) + return 1; + + v0_p.sensor_type = 0; + v0_p.threshold_id = 0; + rv = ec_command(EC_CMD_THERMAL_GET_THRESHOLD, 0, + &v0_p, sizeof(v0_p), &v0_r, sizeof(v0_r)); + /* FIXME: Verson 0 will only return these responses */ + /* FIXME: if (??? == EC_RES_SUCCESS || ??? == EC_RES_ERROR) */ + if (rv > 0) + return 0; + + /* Anything else is most likely EC_RES_INVALID_COMMAND, + * but we don't care because it's nothing we can use. + */ + return -1; +} + +int cmd_thermal_get_threshold(int argc, char *argv[]) +{ + switch (thermal_threshold_version()) { + case 0: + return cmd_thermal_get_threshold_v0(argc, argv); + case 1: + return cmd_thermal_get_threshold_v1(argc, argv); + default: + printf("I got nuthin.\n"); + return -1; + } +} + +int cmd_thermal_set_threshold(int argc, char *argv[]) +{ + switch (thermal_threshold_version()) { + case 0: + return cmd_thermal_set_threshold_v0(argc, argv); + case 1: + return cmd_thermal_set_threshold_v1(argc, argv); + default: + printf("I got nuthin.\n"); + return -1; + } +} + + int cmd_thermal_auto_fan_ctrl(int argc, char *argv[]) { int rv; |