summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2013-08-07 15:34:04 -0700
committerChromeBot <chrome-bot@google.com>2013-08-23 10:38:36 -0700
commitfcce7223a5493e512e77d9f7b758d874ac502df5 (patch)
tree3cfc90b9f4d16b1937f9927b48a80abc2035c48c
parent0e024b2bae085f12ec4df33000799339aef38809 (diff)
downloadchrome-ec-fcce7223a5493e512e77d9f7b758d874ac502df5.tar.gz
Completely new thermal/fan implementation
Problems with existing thermal control loop: * Not multi-board friendly. thermal.c only supports Link and needs refactoring. Temp thresholds and fan speeds are hard-coded. * Only the PECI temp is used to determine the fan speed. Other temp sensors are ignored. * Has confusing data structures. Values in the CPU temp thresholds array mix ACPI thresholds with fan step values. With this change, the thermal task monitors all temp sensors in order to perform two completely independent functions: Function one: Determine if the host needs to be throttled by or informed of any thermal events. For thermal events, each temp sensor will have three threshold levels. TEMP_HOST_WARN * When any sensor goes above this level, host_throttle_cpu(1) will be called to ask the CPU to slow itself down. * When all sensors drop below this level, host_throttle_cpu(0) will be called. * Exactly AT this level, nothing happens (this provides hysteresis). TEMP_HOST_HIGH * When any sensor goes above this level, chipset_throttle_cpu(1) will be called to slow the CPU down whether it wants to or not. * When all sensors drop below this level, chipset_throttle_cpu(0) will be called. * Exactly AT this level, nothing happens (this provides hysteresis). TEMP_HOST_SHUTDOWN * When any sensor is above this level, chipset_force_shutdown() will be called to halt the CPU. * Nothing turns the CPU back on again - the user just has to wait for things to cool off. Pressing the power button too soon will just trigger shutdown again as soon as the EC can read the host temp. Function two: Determine the amount of fan cooling needed For fan cooling, each temp sensor will have two levels. TEMP_FAN_OFF * At or below this temperature, no active cooling is needed. TEMP_FAN_MAX * At or above this temperature, active cooling should be running at maximum. The highest level of all temp sensors will be used to request the amount of active cooling needed. The function pwm_fan_percent_to_rpm() is invoked to convert the amount of cooling to the target fan RPM. The default pwm_fan_percent_to_rpm() function converts smoothly between the configured CONFIG_PWM_FAN_RPM_MIN and CONFIG_PWM_FAN_RPM_MAX for percentages between 1 and 100. 0% means "off". The default function probably provide the smoothest and quietest behavior, but individual boards can provide their own pwm_fan_percent_to_rpm() to implement whatever curves, hysteresis, feedback, or other hackery they wish. BUG=chrome-os-partner:20805 BRANCH=none TEST=manual Compile-time test with make BOARD=falco runtests On the EC console, the existing fan commands should work correctly: faninfo - display the fan state fanduty NUM - force the fan PWM to the specified percentage (0-100) fanset RPM - force the fan to the specified RPM fanset NUM% - force the fan to the specified percentage (0-100) between its configured minimum and maximum speeds from board.h (CONFIG_PWM_FAN_RPM_MIN and CONFIG_PWM_FAN_RPM_MAX) fanauto - let the EC control the fan automatically You can test the default pwm_fan_percent_to_rpm() with fanset 1% faninfo The fan should be turning at CONFIG_PWM_FAN_RPM_MIN. Let the EC control it automatically again with fanauto Also on the EC console, the thermal settings can be examined or changed: > temps PECI : 327 K = 54 C ECInternal : 320 K = 47 C G781Internal : 319 K = 46 C G781External : 318 K = 45 C > > thermalget sensor warn high shutdown fan_off fan_max name 0 373 387 383 333 363 PECI 1 0 0 0 0 0 ECInternal 2 0 0 0 0 0 G781Internal 3 0 0 0 0 0 G781External > > help thermalset Usage: thermalset sensor warn [high [shutdown [fan_off [fan_max]]]] set thermal parameters (-1 to skip) > > thermalset 2 -1 -1 999 sensor warn high shutdown fan_off fan_max name 0 373 387 383 333 363 PECI 1 0 0 0 0 0 ECInternal 2 0 0 999 0 0 G781Internal 3 0 0 0 0 0 G781External > From the host, ectool can be used to get and set these parameters with nearly identical commands: ectool thermalget ectool thermalset 2 -1 -1 999 Change-Id: Idb27977278f766826045fb7d41929953ec6b1cca Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/66688 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-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;