Files
OpenCellular/common/thermal.c
Vic Yang 26f0e5d1d2 Revert "Revert "Add thermal engine test""
This reverts commit 89e688a332.

Time-scaling is added back. We can run this test now.

BUG=chrome-os-partner:19236
TEST=Pass the test.
BRANCH=None

Change-Id: Id3dcec6fc12489f5f0602de91c6560a8dfbef9af
Signed-off-by: Vic Yang <victoryang@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/51551
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
2013-05-17 09:52:26 -07:00

388 lines
10 KiB
C

/* Copyright (c) 2012 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.
*/
/* Thermal engine module for Chrome EC */
#include "chipset.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "pwm.h"
#include "task.h"
#include "temp_sensor.h"
#include "thermal.h"
#include "timer.h"
#include "util.h"
/* Console output macros */
#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)
{
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 cpu_down_count;
if (overheated[THRESHOLD_POWER_DOWN]) {
cprintf(CC_CHIPSET,
"[%T critical temperature; shutting down]\n");
chipset_force_shutdown();
host_set_single_event(EC_HOST_EVENT_THERMAL_SHUTDOWN);
return;
}
if (overheated[THRESHOLD_CPU_DOWN]) {
cpu_down_count++;
if (cpu_down_count > 3) {
CPRINTF("[%T overheated; shutting down]\n");
chipset_force_shutdown();
host_set_single_event(EC_HOST_EVENT_THERMAL_SHUTDOWN);
}
} else {
cpu_down_count = 0;
}
if (overheated[THRESHOLD_WARNING]) {
smi_overheated_warning();
chipset_throttle_cpu(1);
} else {
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);
}
overheated_action();
}
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);
/*****************************************************************************/
/* Console commands */
static void print_thermal_config(enum temp_sensor_type type)
{
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;
}
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,
NULL,
"Enable thermal fan control",
NULL);
/*****************************************************************************/
/* Host commands */
static int thermal_command_set_threshold(struct host_cmd_handler_args *args)
{
const struct ec_params_thermal_set_threshold *p = args->params;
if (thermal_set_threshold(p->sensor_type, p->threshold_id, p->value))
return EC_RES_ERROR;
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_THERMAL_SET_THRESHOLD,
thermal_command_set_threshold,
EC_VER_MASK(0));
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);
if (value == -1)
return EC_RES_ERROR;
r->value = value;
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));
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));