201 lines
5.3 KiB
C
201 lines
5.3 KiB
C
/* Copyright 2016 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 "atomic.h"
|
|
#include "chipset.h"
|
|
#include "common.h"
|
|
#include "console.h"
|
|
#include "dptf.h"
|
|
#include "hooks.h"
|
|
#include "host_command.h"
|
|
#include "temp_sensor.h"
|
|
#include "util.h"
|
|
|
|
/* Console output macros */
|
|
#define CPUTS(outstr) cputs(CC_DPTF, outstr)
|
|
#define CPRINTS(format, args...) cprints(CC_DPTF, format, ## args)
|
|
|
|
/*****************************************************************************/
|
|
/* DPTF temperature thresholds */
|
|
|
|
static struct {
|
|
int temp; /* degrees K, negative for disabled */
|
|
cond_t over; /* watch for crossings */
|
|
} dptf_threshold[TEMP_SENSOR_COUNT][DPTF_THRESHOLDS_PER_SENSOR];
|
|
|
|
static void dptf_init(void)
|
|
{
|
|
int id, t;
|
|
|
|
for (id = 0; id < TEMP_SENSOR_COUNT; id++)
|
|
for (t = 0; t < DPTF_THRESHOLDS_PER_SENSOR; t++) {
|
|
dptf_threshold[id][t].temp = -1;
|
|
cond_init(&dptf_threshold[id][t].over, 0);
|
|
}
|
|
|
|
}
|
|
DECLARE_HOOK(HOOK_INIT, dptf_init, HOOK_PRIO_DEFAULT);
|
|
|
|
/* Keep track of which triggered sensor thresholds the AP has seen */
|
|
static uint32_t dptf_seen;
|
|
|
|
int dptf_query_next_sensor_event(void)
|
|
{
|
|
int id;
|
|
|
|
for (id = 0; id < TEMP_SENSOR_COUNT; id++)
|
|
if (dptf_seen & BIT(id)) { /* atomic? */
|
|
atomic_clear(&dptf_seen, BIT(id));
|
|
return id;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Return true if any threshold transition occurs. */
|
|
static int dptf_check_temp_threshold(int sensor_id, int temp)
|
|
{
|
|
int tripped = 0;
|
|
int max, i;
|
|
|
|
if (sensor_id >= TEMP_SENSOR_COUNT) {
|
|
CPRINTS("DPTF: Invalid sensor ID");
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < DPTF_THRESHOLDS_PER_SENSOR; i++) {
|
|
|
|
max = dptf_threshold[sensor_id][i].temp;
|
|
if (max < 0) /* disabled? */
|
|
continue;
|
|
|
|
if (temp >= max)
|
|
cond_set_true(&dptf_threshold[sensor_id][i].over);
|
|
else if (temp <= max - DPTF_THRESHOLD_HYSTERESIS)
|
|
cond_set_false(&dptf_threshold[sensor_id][i].over);
|
|
|
|
if (cond_went_true(&dptf_threshold[sensor_id][i].over)) {
|
|
CPRINTS("DPTF over threshold [%d][%d",
|
|
sensor_id, i);
|
|
atomic_or(&dptf_seen, BIT(sensor_id));
|
|
tripped = 1;
|
|
}
|
|
if (cond_went_false(&dptf_threshold[sensor_id][i].over)) {
|
|
CPRINTS("DPTF under threshold [%d][%d",
|
|
sensor_id, i);
|
|
atomic_or(&dptf_seen, BIT(sensor_id));
|
|
tripped = 1;
|
|
}
|
|
}
|
|
|
|
return tripped;
|
|
}
|
|
|
|
void dptf_set_temp_threshold(int sensor_id, int temp, int idx, int enable)
|
|
{
|
|
CPRINTS("DPTF sensor %d, threshold %d C, index %d, %sabled",
|
|
sensor_id, K_TO_C(temp), idx, enable ? "en" : "dis");
|
|
|
|
if ((sensor_id >= TEMP_SENSOR_COUNT) ||
|
|
(idx >= DPTF_THRESHOLDS_PER_SENSOR)) {
|
|
CPRINTS("DPTF: Invalid sensor ID");
|
|
return;
|
|
}
|
|
|
|
if (enable) {
|
|
/* Don't update threshold condition if already enabled */
|
|
if (dptf_threshold[sensor_id][idx].temp == -1)
|
|
cond_init(&dptf_threshold[sensor_id][idx].over, 0);
|
|
dptf_threshold[sensor_id][idx].temp = temp;
|
|
atomic_clear(&dptf_seen, BIT(sensor_id));
|
|
} else {
|
|
dptf_threshold[sensor_id][idx].temp = -1;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* EC-specific thermal controls */
|
|
|
|
test_mockable_static void smi_sensor_failure_warning(void)
|
|
{
|
|
CPRINTS("can't read any temp sensors!");
|
|
host_set_single_event(EC_HOST_EVENT_THERMAL);
|
|
}
|
|
|
|
static void thermal_control_dptf(void)
|
|
{
|
|
int i, t, rv;
|
|
int dptf_tripped;
|
|
int num_sensors_read;
|
|
|
|
dptf_tripped = 0;
|
|
num_sensors_read = 0;
|
|
|
|
/* go through all the sensors */
|
|
for (i = 0; i < TEMP_SENSOR_COUNT; ++i) {
|
|
rv = temp_sensor_read(i, &t);
|
|
if (rv != EC_SUCCESS)
|
|
continue;
|
|
else
|
|
num_sensors_read++;
|
|
/* and check the dptf thresholds */
|
|
dptf_tripped |= dptf_check_temp_threshold(i, t);
|
|
}
|
|
|
|
if (!num_sensors_read) {
|
|
/*
|
|
* Trigger a SMI event if we can't read any sensors.
|
|
*
|
|
* In theory we could do something more elaborate like forcing
|
|
* the system to shut down if no sensors are available after
|
|
* several retries. This is a very unlikely scenario -
|
|
* particularly on LM4-based boards, since the LM4 has its own
|
|
* internal temp sensor. It's most likely to occur during
|
|
* bringup of a new board, where we haven't debugged the I2C
|
|
* bus to the sensors; forcing a shutdown in that case would
|
|
* merely hamper board bringup.
|
|
*/
|
|
if (!chipset_in_state(CHIPSET_STATE_HARD_OFF))
|
|
smi_sensor_failure_warning();
|
|
}
|
|
|
|
/* Don't forget to signal any DPTF thresholds */
|
|
if (dptf_tripped)
|
|
host_set_single_event(EC_HOST_EVENT_THERMAL_THRESHOLD);
|
|
}
|
|
|
|
/* Wait until after the sensors have been read */
|
|
DECLARE_HOOK(HOOK_SECOND, thermal_control_dptf, HOOK_PRIO_TEMP_SENSOR_DONE);
|
|
|
|
/*****************************************************************************/
|
|
/* Console commands */
|
|
|
|
static int command_dptftemp(int argc, char **argv)
|
|
{
|
|
int id, t;
|
|
int temp, trig;
|
|
|
|
ccprintf("sensor thresh0 thresh1\n");
|
|
for (id = 0; id < TEMP_SENSOR_COUNT; id++) {
|
|
ccprintf(" %2d", id);
|
|
for (t = 0; t < DPTF_THRESHOLDS_PER_SENSOR; t++) {
|
|
temp = dptf_threshold[id][t].temp;
|
|
trig = cond_is_true(&dptf_threshold[id][t].over);
|
|
if (temp < 0)
|
|
ccprintf(" --- ");
|
|
else
|
|
ccprintf(" %3d%c", temp,
|
|
trig ? '*' : ' ');
|
|
}
|
|
ccprintf(" %s\n", temp_sensors[id].name);
|
|
}
|
|
|
|
ccprintf("AP seen mask: 0x%08x\n", dptf_seen);
|
|
return EC_SUCCESS;
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(dptftemp, command_dptftemp,
|
|
NULL,
|
|
"Print DPTF thermal parameters (degrees Kelvin)");
|