| /* 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)"); |