summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/bolt/board.c11
-rw-r--r--board/bolt/board.h3
-rw-r--r--board/bolt/ec.tasklist1
-rw-r--r--board/falco/board.c33
-rw-r--r--board/falco/board.h3
-rw-r--r--board/falco/ec.tasklist1
-rw-r--r--board/host/board.c1
-rw-r--r--board/host/board.h2
-rw-r--r--board/link/board.c19
-rw-r--r--board/link/board.h3
-rw-r--r--board/link/ec.tasklist1
-rw-r--r--board/peppy/board.c14
-rw-r--r--board/peppy/board.h3
-rw-r--r--board/peppy/ec.tasklist1
-rw-r--r--board/slippy/board.c14
-rw-r--r--board/slippy/board.h3
-rw-r--r--board/slippy/ec.tasklist1
-rw-r--r--chip/lm4/chip_temp_sensor.c2
-rw-r--r--chip/lm4/peci.c2
-rw-r--r--chip/lm4/pwm_fan.c286
-rw-r--r--chip/lm4/pwm_kblight.c1
-rw-r--r--common/build.mk4
-rw-r--r--common/host_event_commands.c8
-rw-r--r--common/pwm_fan.c27
-rw-r--r--common/temp_sensor.c5
-rw-r--r--common/temp_sensor_tmp006.c2
-rw-r--r--common/thermal.c464
-rw-r--r--include/config.h19
-rw-r--r--include/ec_commands.h52
-rw-r--r--include/fan.h34
-rw-r--r--include/hooks.h7
-rw-r--r--include/pwm.h36
-rw-r--r--include/temp_sensor.h2
-rw-r--r--include/thermal.h68
-rw-r--r--test/test_config.h4
-rw-r--r--test/thermal.c656
-rw-r--r--test/thermal.tasklist3
-rw-r--r--test/thermal_old.c426
-rw-r--r--test/thermal_old.py (renamed from test/thermal.py)0
-rw-r--r--util/ectool.c170
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, &params, 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, &params, 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, &params,
- 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, &params,
- 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, &params, 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, &params, 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, &params,
+ 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, &params,
+ 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;