mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-11 02:15:14 +00:00
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>
This commit is contained in:
committed by
ChromeBot
parent
0e024b2bae
commit
fcce7223a5
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/* Check for stall condition */
|
||||
return (((LM4_FAN_FANSTS >> (2 * FAN_CH_CPU)) & 0x03) == 0) ? 1 : 0;
|
||||
fan_set_duty_raw(pwm);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Console commands */
|
||||
|
||||
static int command_fan_info(int argc, char **argv)
|
||||
static int cc_fanauto(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? */
|
||||
fan_set_thermal_control_enabled(1);
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
DECLARE_CONSOLE_COMMAND(fanauto, cc_fanauto,
|
||||
NULL,
|
||||
"Enable thermal fan control",
|
||||
NULL);
|
||||
|
||||
|
||||
static int cc_faninfo(int argc, char **argv)
|
||||
{
|
||||
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);
|
||||
|
||||
pwm_set_fan_target_rpm(rpm);
|
||||
fan_set_rpm_target(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);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "registers.h"
|
||||
#include "system.h"
|
||||
#include "task.h"
|
||||
#include "thermal.h"
|
||||
#include "timer.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
27
common/pwm_fan.c
Normal file
27
common/pwm_fan.c
Normal file
@@ -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 */
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
506
common/thermal.c
506
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
int thermal_get_threshold(enum temp_sensor_type type, int threshold_id)
|
||||
{
|
||||
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];
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void smi_overheated_warning(void)
|
||||
{
|
||||
host_set_single_event(EC_HOST_EVENT_THERMAL_OVERLOAD);
|
||||
}
|
||||
|
||||
static void smi_sensor_failure_warning(void)
|
||||
test_mockable_static void smi_sensor_failure_warning(void)
|
||||
{
|
||||
CPRINTF("[%T can't read any temp sensors!]\n");
|
||||
host_set_single_event(EC_HOST_EVENT_THERMAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 fan_percent(int low, int high, int cur)
|
||||
{
|
||||
static int cpu_down_count;
|
||||
if (cur < low)
|
||||
return 0;
|
||||
if (cur > high)
|
||||
return 100;
|
||||
return 100 * (cur - low) / (high - low);
|
||||
}
|
||||
|
||||
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);
|
||||
/* 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);
|
||||
|
||||
/* Keep track of which thresholds have triggered */
|
||||
static cond_t cond_hot[EC_TEMP_THRESH_COUNT];
|
||||
|
||||
static void thermal_control(void)
|
||||
{
|
||||
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) {
|
||||
|
||||
/* 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]++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 (!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_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;
|
||||
/* 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 (overheated[THRESHOLD_WARNING]) {
|
||||
smi_overheated_warning();
|
||||
|
||||
/* What do we do about it? (note hard-coded logic). */
|
||||
|
||||
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.
|
||||
*/
|
||||
CPRINTF("[%T thermal no longer shutdown]\n");
|
||||
}
|
||||
|
||||
if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HIGH])) {
|
||||
CPRINTF("[%T thermal HIGH]\n");
|
||||
chipset_throttle_cpu(1);
|
||||
} else {
|
||||
} else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_HIGH])) {
|
||||
CPRINTF("[%T thermal no longer high]\n");
|
||||
chipset_throttle_cpu(0);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
overheated[threshold_id] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
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_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);
|
||||
}
|
||||
|
||||
overheated_action();
|
||||
/* Max fan needed is what's needed. */
|
||||
pwm_fan_set_percent_needed(fmax);
|
||||
}
|
||||
|
||||
void thermal_task(void)
|
||||
{
|
||||
while (1) {
|
||||
thermal_process();
|
||||
usleep(SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
static void thermal_shutdown(void)
|
||||
{
|
||||
/* Take back fan control when the processor shuts down */
|
||||
thermal_control_fan(1);
|
||||
}
|
||||
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",
|
||||
NULL);
|
||||
|
||||
static int command_fan_config(int argc, char **argv)
|
||||
{
|
||||
char *e;
|
||||
int sensor_type, stepping_id, value;
|
||||
|
||||
if (argc != 2 && argc != 4)
|
||||
return EC_ERROR_PARAM_COUNT;
|
||||
|
||||
sensor_type = strtoi(argv[1], &e, 0);
|
||||
if ((e && *e) || sensor_type < 0 ||
|
||||
sensor_type >= TEMP_SENSOR_TYPE_COUNT)
|
||||
return EC_ERROR_PARAM1;
|
||||
|
||||
if (argc == 2) {
|
||||
print_fan_stepping(sensor_type);
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
DECLARE_CONSOLE_COMMAND(thermalfan, command_fan_config,
|
||||
"sensortype [threshold_id rpm]",
|
||||
"Get/set thermal threshold fan rpm",
|
||||
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,
|
||||
DECLARE_CONSOLE_COMMAND(thermalget, command_thermalget,
|
||||
NULL,
|
||||
"Enable thermal fan control",
|
||||
"Print thermal parameters (degrees Kelvin)",
|
||||
NULL);
|
||||
|
||||
|
||||
static int command_thermalset(int argc, char **argv)
|
||||
{
|
||||
unsigned int n;
|
||||
int i, val;
|
||||
char *e;
|
||||
|
||||
if (argc < 3 || argc > 7)
|
||||
return EC_ERROR_PARAM_COUNT;
|
||||
|
||||
n = (unsigned int)strtoi(argv[1], &e, 0);
|
||||
if (*e)
|
||||
return EC_ERROR_PARAM1;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
command_thermalget(0, 0);
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
DECLARE_CONSOLE_COMMAND(thermalset, command_thermalset,
|
||||
"sensor warn [high [shutdown [fan_off [fan_max]]]]",
|
||||
"Set thermal parameters (degrees Kelvin)."
|
||||
" Use -1 to skip.",
|
||||
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));
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
34
include/fan.h
Normal file
34
include/fan.h
Normal file
@@ -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 */
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
/* 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.
|
||||
*/
|
||||
#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
|
||||
*/
|
||||
void thermal_control_fan(int enable);
|
||||
extern struct ec_thermal_config thermal_params[];
|
||||
|
||||
#endif /* __CROS_EC_THERMAL_H */
|
||||
|
||||
@@ -23,4 +23,8 @@
|
||||
#define CONFIG_EXTPOWER_FALCO
|
||||
#endif
|
||||
|
||||
#ifdef TEST_thermal
|
||||
#define CONFIG_TEMP_SENSOR
|
||||
#endif
|
||||
|
||||
#endif /* __CROS_EC_TEST_CONFIG_H */
|
||||
|
||||
648
test/thermal.c
648
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;
|
||||
}
|
||||
|
||||
void host_throttle_cpu(int throttled)
|
||||
{
|
||||
host_throttled = throttled;
|
||||
}
|
||||
|
||||
void pwm_fan_set_percent_needed(int pct)
|
||||
{
|
||||
fan_pct = pct;
|
||||
}
|
||||
|
||||
void smi_sensor_failure_warning(void)
|
||||
{
|
||||
no_temps_read = 1;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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)
|
||||
static void set_temps(int t0, int t1, int t2, int t3)
|
||||
{
|
||||
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;
|
||||
}
|
||||
mock_temp[0] = t0;
|
||||
mock_temp[1] = t1;
|
||||
mock_temp[2] = t2;
|
||||
mock_temp[3] = t3;
|
||||
}
|
||||
|
||||
static int wait_fan_rpm(int rpm, int timeout_secs)
|
||||
static void all_temps(int t)
|
||||
{
|
||||
do {
|
||||
if (fan_rpm == rpm)
|
||||
return 1;
|
||||
usleep(SECOND);
|
||||
} while (timeout_secs--);
|
||||
|
||||
return 0;
|
||||
set_temps(t, t, t, t);
|
||||
}
|
||||
|
||||
static int wait_value(int *v, int target, int timeout_secs)
|
||||
static void reset_mocks(void)
|
||||
{
|
||||
do {
|
||||
if (*v == target)
|
||||
return 1;
|
||||
usleep(SECOND);
|
||||
} while (timeout_secs--);
|
||||
/* Ignore all sensors */
|
||||
memset(thermal_params, 0, sizeof(thermal_params));
|
||||
|
||||
return 0;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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. */
|
||||
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);
|
||||
|
||||
sleep(2);
|
||||
|
||||
TEST_ASSERT(host_throttled == 0);
|
||||
TEST_ASSERT(cpu_throttled == 0);
|
||||
TEST_ASSERT(cpu_shutdown == 0);
|
||||
TEST_ASSERT(fan_pct == 0);
|
||||
TEST_ASSERT(no_temps_read);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_cpu_fan(void)
|
||||
static int test_sensors_can_be_read(void)
|
||||
{
|
||||
reset_mock_temp();
|
||||
reset_mocks();
|
||||
mock_temp[2] = 100;
|
||||
|
||||
/*
|
||||
* 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));
|
||||
sleep(2);
|
||||
|
||||
/* 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 == 0);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_safety(void)
|
||||
|
||||
static int test_one_fan(void)
|
||||
{
|
||||
reset_mock_temp();
|
||||
reset_mocks();
|
||||
thermal_params[2].temp_fan_off = 100;
|
||||
thermal_params[2].temp_fan_max = 200;
|
||||
|
||||
/* 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));
|
||||
all_temps(50);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 0);
|
||||
|
||||
/* Lower temperature. CPU not throttled anymore. */
|
||||
mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_WARNING) - 5;
|
||||
TEST_ASSERT(wait_clear(&cpu_throttled, 2));
|
||||
all_temps(100);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 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));
|
||||
all_temps(101);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 1);
|
||||
|
||||
mock_temp[T_CPU] = 0;
|
||||
usleep(SECOND);
|
||||
cpu_down = 0;
|
||||
all_temps(130);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 30);
|
||||
|
||||
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));
|
||||
all_temps(150);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 50);
|
||||
|
||||
mock_temp[T_CPU] = 0;
|
||||
cpu_down = 0;
|
||||
all_temps(170);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 70);
|
||||
|
||||
all_temps(200);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 100);
|
||||
|
||||
all_temps(300);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 100);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_sensor_failure(void)
|
||||
static int test_two_fans(void)
|
||||
{
|
||||
reset_mock_temp();
|
||||
reset_mocks();
|
||||
|
||||
/* 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)));
|
||||
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;
|
||||
|
||||
/* 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(50);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 0);
|
||||
|
||||
all_temps(100);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 0);
|
||||
|
||||
all_temps(101);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 1);
|
||||
|
||||
all_temps(130);
|
||||
sleep(2);
|
||||
/* fan 2 is still higher */
|
||||
TEST_ASSERT(fan_pct == 30);
|
||||
|
||||
all_temps(150);
|
||||
sleep(2);
|
||||
/* now fan 1 is higher: 150 = 75% of [120-160] */
|
||||
TEST_ASSERT(fan_pct == 75);
|
||||
|
||||
all_temps(170);
|
||||
sleep(2);
|
||||
/* fan 1 is maxed now */
|
||||
TEST_ASSERT(fan_pct == 100);
|
||||
|
||||
all_temps(200);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 100);
|
||||
|
||||
all_temps(300);
|
||||
sleep(2);
|
||||
TEST_ASSERT(fan_pct == 100);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_sensor_info(void)
|
||||
static int test_all_fans(void)
|
||||
{
|
||||
struct ec_params_temp_sensor_get_info params;
|
||||
struct ec_response_temp_sensor_get_info resp;
|
||||
int i;
|
||||
reset_mocks();
|
||||
|
||||
for (i = 0; i < TEMP_SENSOR_COUNT; ++i) {
|
||||
params.id = i;
|
||||
TEST_ASSERT(test_send_host_command(
|
||||
EC_CMD_TEMP_SENSOR_GET_INFO,
|
||||
0, ¶ms, sizeof(params),
|
||||
&resp, sizeof(resp)) == EC_RES_SUCCESS);
|
||||
TEST_ASSERT_ARRAY_EQ(resp.sensor_name,
|
||||
temp_sensors[i].name,
|
||||
strlen(resp.sensor_name));
|
||||
TEST_ASSERT(resp.sensor_type == temp_sensors[i].type);
|
||||
}
|
||||
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;
|
||||
|
||||
params.id = TEMP_SENSOR_COUNT;
|
||||
TEST_ASSERT(test_send_host_command(
|
||||
EC_CMD_TEMP_SENSOR_GET_INFO,
|
||||
0, ¶ms, sizeof(params),
|
||||
&resp, sizeof(resp)) != EC_RES_SUCCESS);
|
||||
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 set_threshold(int type, int threshold_id, int val)
|
||||
static int test_one_limit(void)
|
||||
{
|
||||
struct ec_params_thermal_set_threshold params;
|
||||
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;
|
||||
|
||||
params.sensor_type = type;
|
||||
params.threshold_id = threshold_id;
|
||||
params.value = val;
|
||||
all_temps(50);
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 0);
|
||||
TEST_ASSERT(cpu_throttled == 0);
|
||||
TEST_ASSERT(cpu_shutdown == 0);
|
||||
|
||||
return test_send_host_command(EC_CMD_THERMAL_SET_THRESHOLD, 0, ¶ms,
|
||||
sizeof(params), NULL, 0);
|
||||
}
|
||||
all_temps(100);
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 0);
|
||||
TEST_ASSERT(cpu_throttled == 0);
|
||||
TEST_ASSERT(cpu_shutdown == 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(101);
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 1);
|
||||
TEST_ASSERT(cpu_throttled == 0);
|
||||
TEST_ASSERT(cpu_shutdown == 0);
|
||||
|
||||
params.sensor_type = type;
|
||||
params.threshold_id = threshold_id;
|
||||
all_temps(100);
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 1);
|
||||
TEST_ASSERT(cpu_throttled == 0);
|
||||
TEST_ASSERT(cpu_shutdown == 0);
|
||||
|
||||
rv = test_send_host_command(EC_CMD_THERMAL_GET_THRESHOLD, 0, ¶ms,
|
||||
sizeof(params), &resp, sizeof(resp));
|
||||
if (rv != EC_RES_SUCCESS)
|
||||
return rv;
|
||||
all_temps(99);
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 0);
|
||||
TEST_ASSERT(cpu_throttled == 0);
|
||||
TEST_ASSERT(cpu_shutdown == 0);
|
||||
|
||||
*val = resp.value;
|
||||
return EC_RES_SUCCESS;
|
||||
}
|
||||
all_temps(199);
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 1);
|
||||
TEST_ASSERT(cpu_throttled == 0);
|
||||
TEST_ASSERT(cpu_shutdown == 0);
|
||||
|
||||
static int verify_threshold(int type, int threshold_id, int val)
|
||||
{
|
||||
int actual_val;
|
||||
all_temps(200);
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 1);
|
||||
TEST_ASSERT(cpu_throttled == 0);
|
||||
TEST_ASSERT(cpu_shutdown == 0);
|
||||
|
||||
if (get_threshold(type, threshold_id, &actual_val) != EC_RES_SUCCESS)
|
||||
return 0;
|
||||
return val == actual_val;
|
||||
}
|
||||
all_temps(201);
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 1);
|
||||
TEST_ASSERT(cpu_throttled == 1);
|
||||
TEST_ASSERT(cpu_shutdown == 0);
|
||||
|
||||
static int test_threshold_hostcmd(void)
|
||||
{
|
||||
reset_mock_temp();
|
||||
all_temps(200);
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 1);
|
||||
TEST_ASSERT(cpu_throttled == 1);
|
||||
TEST_ASSERT(cpu_shutdown == 0);
|
||||
|
||||
/* 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)));
|
||||
all_temps(199);
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 1);
|
||||
TEST_ASSERT(cpu_throttled == 0);
|
||||
TEST_ASSERT(cpu_shutdown == 0);
|
||||
|
||||
/* 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));
|
||||
all_temps(99);
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 0);
|
||||
TEST_ASSERT(cpu_throttled == 0);
|
||||
TEST_ASSERT(cpu_shutdown == 0);
|
||||
|
||||
/* 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));
|
||||
all_temps(201);
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 1);
|
||||
TEST_ASSERT(cpu_throttled == 1);
|
||||
TEST_ASSERT(cpu_shutdown == 0);
|
||||
|
||||
/* Clear */
|
||||
mock_temp[T_CPU] = 0;
|
||||
TEST_ASSERT(wait_clear(&cpu_throttled, 2));
|
||||
cpu_down = 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 test_threshold_console_cmd(void)
|
||||
static int test_several_limits(void)
|
||||
{
|
||||
char buf[100];
|
||||
reset_mocks();
|
||||
|
||||
reset_mock_temp();
|
||||
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;
|
||||
|
||||
/* 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));
|
||||
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;
|
||||
|
||||
/* 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));
|
||||
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;
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
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);
|
||||
|
||||
static int test_invalid_hostcmd(void)
|
||||
{
|
||||
int dummy;
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
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);
|
||||
|
||||
static int test_auto_fan_ctrl(void)
|
||||
{
|
||||
reset_mock_temp();
|
||||
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);
|
||||
|
||||
/* 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));
|
||||
all_temps(0); /* reset from shutdown */
|
||||
sleep(2);
|
||||
TEST_ASSERT(host_throttled == 0);
|
||||
TEST_ASSERT(cpu_throttled == 0);
|
||||
|
||||
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
426
test/thermal_old.c
Normal file
426
test/thermal_old.c
Normal file
@@ -0,0 +1,426 @@
|
||||
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
* Test thermal engine.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "console.h"
|
||||
#include "hooks.h"
|
||||
#include "host_command.h"
|
||||
#include "printf.h"
|
||||
#include "temp_sensor.h"
|
||||
#include "test_util.h"
|
||||
#include "thermal.h"
|
||||
#include "timer.h"
|
||||
#include "util.h"
|
||||
|
||||
static int mock_temp[TEMP_SENSOR_COUNT];
|
||||
static int fan_rpm;
|
||||
static int fan_rpm_mode = 1;
|
||||
static int cpu_throttled;
|
||||
static int cpu_down;
|
||||
|
||||
extern struct thermal_config_t thermal_config[TEMP_SENSOR_TYPE_COUNT];
|
||||
extern const int fan_speed[THERMAL_FAN_STEPS + 1];
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Mock functions */
|
||||
|
||||
int temp_sensor_read(enum temp_sensor_id id, int *temp_ptr)
|
||||
{
|
||||
if (mock_temp[id] >= 0) {
|
||||
*temp_ptr = mock_temp[id];
|
||||
return EC_SUCCESS;
|
||||
} else {
|
||||
return -mock_temp[id];
|
||||
}
|
||||
}
|
||||
|
||||
void pwm_set_fan_rpm_mode(int rpm_mode)
|
||||
{
|
||||
fan_rpm_mode = rpm_mode;
|
||||
}
|
||||
|
||||
void pwm_set_fan_target_rpm(int rpm)
|
||||
{
|
||||
fan_rpm = rpm;
|
||||
}
|
||||
|
||||
void chipset_force_shutdown(void)
|
||||
{
|
||||
cpu_down = 1;
|
||||
}
|
||||
|
||||
void chipset_throttle_cpu(int throttled)
|
||||
{
|
||||
cpu_throttled = throttled;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Test utilities */
|
||||
|
||||
/* Test shorthands */
|
||||
#define T_CPU TEMP_SENSOR_CPU
|
||||
#define T_BOARD TEMP_SENSOR_BOARD
|
||||
#define T_CASE TEMP_SENSOR_CASE
|
||||
#define THRESHOLD(x, y) (thermal_config[x].thresholds[y])
|
||||
#define FAN_THRESHOLD(x, y) THRESHOLD(x, THRESHOLD_COUNT + (y))
|
||||
|
||||
static void reset_mock_temp(void)
|
||||
{
|
||||
int i;
|
||||
enum temp_sensor_type type;
|
||||
for (i = 0; i < TEMP_SENSOR_COUNT; ++i) {
|
||||
type = temp_sensors[i].type;
|
||||
mock_temp[i] = FAN_THRESHOLD(type, 0) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int wait_fan_rpm(int rpm, int timeout_secs)
|
||||
{
|
||||
do {
|
||||
if (fan_rpm == rpm)
|
||||
return 1;
|
||||
usleep(SECOND);
|
||||
} while (timeout_secs--);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_value(int *v, int target, int timeout_secs)
|
||||
{
|
||||
do {
|
||||
if (*v == target)
|
||||
return 1;
|
||||
usleep(SECOND);
|
||||
} while (timeout_secs--);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_set(int *v, int timeout_secs)
|
||||
{
|
||||
return wait_value(v, 1, timeout_secs);
|
||||
}
|
||||
|
||||
static int wait_clear(int *v, int timeout_secs)
|
||||
{
|
||||
return wait_value(v, 0, timeout_secs);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Tests */
|
||||
|
||||
static int test_init_val(void)
|
||||
{
|
||||
/* Initial mock temperature values are all zero. */
|
||||
TEST_ASSERT(cpu_throttled == 0);
|
||||
TEST_ASSERT(cpu_down == 0);
|
||||
TEST_ASSERT(!(host_get_events() &
|
||||
EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_OVERLOAD)));
|
||||
TEST_ASSERT(!(host_get_events() &
|
||||
EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN)));
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_cpu_fan(void)
|
||||
{
|
||||
reset_mock_temp();
|
||||
|
||||
/*
|
||||
* Increase CPU temperature to first fan step and check if
|
||||
* the fan comes up.
|
||||
*/
|
||||
mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 0);
|
||||
TEST_ASSERT(wait_fan_rpm(fan_speed[1], 11));
|
||||
|
||||
/* Increase CPU temperature to second fan step */
|
||||
mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 1);
|
||||
TEST_ASSERT(wait_fan_rpm(fan_speed[2], 11));
|
||||
|
||||
/* Test threshold hysteresis */
|
||||
mock_temp[T_CPU]--;
|
||||
usleep(15 * SECOND);
|
||||
TEST_ASSERT(fan_rpm == fan_speed[2]);
|
||||
|
||||
/* Test action delay */
|
||||
mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 4);
|
||||
usleep((temp_sensors[T_CPU].action_delay_sec - 1) * SECOND);
|
||||
TEST_ASSERT(fan_rpm == fan_speed[2]);
|
||||
mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 0);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_safety(void)
|
||||
{
|
||||
reset_mock_temp();
|
||||
|
||||
/* Trigger CPU throttling */
|
||||
mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_WARNING);
|
||||
TEST_ASSERT(wait_set(&cpu_throttled, 11));
|
||||
TEST_ASSERT(host_get_events() &
|
||||
EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_OVERLOAD));
|
||||
|
||||
/* Lower temperature. CPU not throttled anymore. */
|
||||
mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_WARNING) - 5;
|
||||
TEST_ASSERT(wait_clear(&cpu_throttled, 2));
|
||||
|
||||
/* Thermal shutdown */
|
||||
mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_CPU_DOWN);
|
||||
TEST_ASSERT(wait_set(&cpu_down, 11));
|
||||
TEST_ASSERT(host_get_events() &
|
||||
EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN));
|
||||
|
||||
mock_temp[T_CPU] = 0;
|
||||
usleep(SECOND);
|
||||
cpu_down = 0;
|
||||
|
||||
mock_temp[T_CPU] = THRESHOLD(T_CPU, THRESHOLD_POWER_DOWN);
|
||||
TEST_ASSERT(wait_set(&cpu_down, 11));
|
||||
TEST_ASSERT(host_get_events() &
|
||||
EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN));
|
||||
|
||||
mock_temp[T_CPU] = 0;
|
||||
cpu_down = 0;
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_sensor_failure(void)
|
||||
{
|
||||
reset_mock_temp();
|
||||
|
||||
/* Failure due to sensor not powered should be ignored */
|
||||
mock_temp[T_CPU] = -EC_ERROR_NOT_POWERED;
|
||||
usleep(5 * SECOND);
|
||||
TEST_ASSERT(!(host_get_events() &
|
||||
EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL)));
|
||||
|
||||
/* Other failure should be pumped up to host */
|
||||
mock_temp[T_CPU] = -EC_ERROR_UNKNOWN;
|
||||
usleep(5 * SECOND);
|
||||
TEST_ASSERT(host_get_events() &
|
||||
EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL));
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_sensor_info(void)
|
||||
{
|
||||
struct ec_params_temp_sensor_get_info params;
|
||||
struct ec_response_temp_sensor_get_info resp;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TEMP_SENSOR_COUNT; ++i) {
|
||||
params.id = i;
|
||||
TEST_ASSERT(test_send_host_command(
|
||||
EC_CMD_TEMP_SENSOR_GET_INFO,
|
||||
0, ¶ms, sizeof(params),
|
||||
&resp, sizeof(resp)) == EC_RES_SUCCESS);
|
||||
TEST_ASSERT_ARRAY_EQ(resp.sensor_name,
|
||||
temp_sensors[i].name,
|
||||
strlen(resp.sensor_name));
|
||||
TEST_ASSERT(resp.sensor_type == temp_sensors[i].type);
|
||||
}
|
||||
|
||||
params.id = TEMP_SENSOR_COUNT;
|
||||
TEST_ASSERT(test_send_host_command(
|
||||
EC_CMD_TEMP_SENSOR_GET_INFO,
|
||||
0, ¶ms, sizeof(params),
|
||||
&resp, sizeof(resp)) != EC_RES_SUCCESS);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int set_threshold(int type, int threshold_id, int val)
|
||||
{
|
||||
struct ec_params_thermal_set_threshold params;
|
||||
|
||||
params.sensor_type = type;
|
||||
params.threshold_id = threshold_id;
|
||||
params.value = val;
|
||||
|
||||
return test_send_host_command(EC_CMD_THERMAL_SET_THRESHOLD, 0, ¶ms,
|
||||
sizeof(params), NULL, 0);
|
||||
}
|
||||
|
||||
static int get_threshold(int type, int threshold_id, int *val)
|
||||
{
|
||||
struct ec_params_thermal_get_threshold params;
|
||||
struct ec_response_thermal_get_threshold resp;
|
||||
int rv;
|
||||
|
||||
params.sensor_type = type;
|
||||
params.threshold_id = threshold_id;
|
||||
|
||||
rv = test_send_host_command(EC_CMD_THERMAL_GET_THRESHOLD, 0, ¶ms,
|
||||
sizeof(params), &resp, sizeof(resp));
|
||||
if (rv != EC_RES_SUCCESS)
|
||||
return rv;
|
||||
|
||||
*val = resp.value;
|
||||
return EC_RES_SUCCESS;
|
||||
}
|
||||
|
||||
static int verify_threshold(int type, int threshold_id, int val)
|
||||
{
|
||||
int actual_val;
|
||||
|
||||
if (get_threshold(type, threshold_id, &actual_val) != EC_RES_SUCCESS)
|
||||
return 0;
|
||||
return val == actual_val;
|
||||
}
|
||||
|
||||
static int test_threshold_hostcmd(void)
|
||||
{
|
||||
reset_mock_temp();
|
||||
|
||||
/* Verify thresholds */
|
||||
TEST_ASSERT(verify_threshold(T_CPU, THRESHOLD_WARNING,
|
||||
THRESHOLD(T_CPU, THRESHOLD_WARNING)));
|
||||
TEST_ASSERT(verify_threshold(T_BOARD, THRESHOLD_WARNING,
|
||||
THRESHOLD(T_BOARD, THRESHOLD_WARNING)));
|
||||
TEST_ASSERT(verify_threshold(T_CPU, THRESHOLD_CPU_DOWN,
|
||||
THRESHOLD(T_CPU, THRESHOLD_CPU_DOWN)));
|
||||
|
||||
/* Lower CPU throttling threshold and trigger */
|
||||
TEST_ASSERT(set_threshold(T_CPU, THRESHOLD_WARNING, 350) ==
|
||||
EC_RES_SUCCESS);
|
||||
mock_temp[T_CPU] = 355;
|
||||
TEST_ASSERT(wait_set(&cpu_throttled, 11));
|
||||
TEST_ASSERT(host_get_events() &
|
||||
EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_OVERLOAD));
|
||||
|
||||
/* Lower thermal shutdown threshold */
|
||||
TEST_ASSERT(set_threshold(T_CPU, THRESHOLD_CPU_DOWN, 353) ==
|
||||
EC_RES_SUCCESS);
|
||||
TEST_ASSERT(wait_set(&cpu_down, 11));
|
||||
TEST_ASSERT(host_get_events() &
|
||||
EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN));
|
||||
|
||||
/* Clear */
|
||||
mock_temp[T_CPU] = 0;
|
||||
TEST_ASSERT(wait_clear(&cpu_throttled, 2));
|
||||
cpu_down = 0;
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_threshold_console_cmd(void)
|
||||
{
|
||||
char buf[100];
|
||||
|
||||
reset_mock_temp();
|
||||
|
||||
/* Lower CPU threshold and trigger */
|
||||
snprintf(buf, 100, "thermalconf %d %d 330\n", T_CPU, THRESHOLD_WARNING);
|
||||
UART_INJECT(buf);
|
||||
msleep(100);
|
||||
mock_temp[T_CPU] = 335;
|
||||
TEST_ASSERT(wait_set(&cpu_throttled, 11));
|
||||
TEST_ASSERT(host_get_events() &
|
||||
EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_OVERLOAD));
|
||||
|
||||
/* Set first fan step to 280 K */
|
||||
snprintf(buf, 100, "thermalfan %d 0 280\n", T_CPU);
|
||||
UART_INJECT(buf);
|
||||
msleep(100);
|
||||
mock_temp[T_CPU] = 280;
|
||||
TEST_ASSERT(wait_fan_rpm(fan_speed[1], 11));
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_invalid_hostcmd(void)
|
||||
{
|
||||
int dummy;
|
||||
|
||||
TEST_ASSERT(set_threshold(TEMP_SENSOR_TYPE_COUNT, THRESHOLD_WARNING,
|
||||
100) != EC_RES_SUCCESS);
|
||||
TEST_ASSERT(set_threshold(T_CPU, THRESHOLD_COUNT + THERMAL_FAN_STEPS,
|
||||
100) != EC_RES_SUCCESS);
|
||||
TEST_ASSERT(get_threshold(TEMP_SENSOR_TYPE_COUNT, THRESHOLD_WARNING,
|
||||
&dummy) != EC_RES_SUCCESS);
|
||||
TEST_ASSERT(get_threshold(T_CPU, THRESHOLD_COUNT + THERMAL_FAN_STEPS,
|
||||
&dummy) != EC_RES_SUCCESS);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_auto_fan_ctrl(void)
|
||||
{
|
||||
reset_mock_temp();
|
||||
|
||||
/* Disable fan control */
|
||||
pwm_set_fan_rpm_mode(0);
|
||||
thermal_control_fan(0);
|
||||
|
||||
/*
|
||||
* Increase CPU temperature to first fan step and check the fan
|
||||
* doesn't come up.
|
||||
*/
|
||||
mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 0);
|
||||
TEST_ASSERT(!wait_fan_rpm(fan_speed[1], 11));
|
||||
|
||||
/* Enable fan control */
|
||||
TEST_ASSERT(test_send_host_command(EC_CMD_THERMAL_AUTO_FAN_CTRL, 0,
|
||||
NULL, 0, NULL, 0) == EC_RES_SUCCESS);
|
||||
TEST_ASSERT(fan_rpm_mode == 1);
|
||||
TEST_ASSERT(wait_fan_rpm(fan_speed[1], 11));
|
||||
|
||||
/* Disable fan control */
|
||||
pwm_set_fan_rpm_mode(0);
|
||||
thermal_control_fan(0);
|
||||
|
||||
/* Increase CPU temperature to second fan step */
|
||||
mock_temp[T_CPU] = FAN_THRESHOLD(T_CPU, 1);
|
||||
TEST_ASSERT(!wait_fan_rpm(fan_speed[2], 11));
|
||||
|
||||
/* Enable fan control by console command */
|
||||
UART_INJECT("autofan\n");
|
||||
msleep(100);
|
||||
TEST_ASSERT(fan_rpm_mode == 1);
|
||||
TEST_ASSERT(wait_fan_rpm(fan_speed[2], 11));
|
||||
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int check_assumption(void)
|
||||
{
|
||||
TEST_ASSERT((int)TEMP_SENSOR_CPU == (int)TEMP_SENSOR_TYPE_CPU);
|
||||
TEST_ASSERT((int)TEMP_SENSOR_BOARD == (int)TEMP_SENSOR_TYPE_BOARD);
|
||||
TEST_ASSERT((int)TEMP_SENSOR_CASE == (int)TEMP_SENSOR_TYPE_CASE);
|
||||
|
||||
TEST_ASSERT(temp_sensors[T_CPU].action_delay_sec != 0);
|
||||
|
||||
TEST_ASSERT(thermal_config[T_CPU].config_flags &
|
||||
THERMAL_CONFIG_WARNING_ON_FAIL);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
void run_test(void)
|
||||
{
|
||||
test_reset();
|
||||
|
||||
/* Test assumptions */
|
||||
RUN_TEST(check_assumption);
|
||||
|
||||
RUN_TEST(test_init_val);
|
||||
RUN_TEST(test_cpu_fan);
|
||||
/* No tests for board and case temp sensors as they are ignored. */
|
||||
RUN_TEST(test_safety);
|
||||
RUN_TEST(test_sensor_failure);
|
||||
RUN_TEST(test_auto_fan_ctrl);
|
||||
RUN_TEST(test_sensor_info);
|
||||
RUN_TEST(test_threshold_hostcmd);
|
||||
RUN_TEST(test_invalid_hostcmd);
|
||||
RUN_TEST(test_threshold_console_cmd);
|
||||
|
||||
test_print_result();
|
||||
}
|
||||
170
util/ectool.c
170
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;
|
||||
|
||||
Reference in New Issue
Block a user