/* 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 "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 & (1 << id)) { /* atomic? */ atomic_clear(&dptf_seen, (1 << id)); return id; } return -1; } /* Return true if any threshold transition occurs. */ static int dpft_check_temp_threshold(int sensor_id, int temp) { int tripped = 0; int max, i; 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, (1 << 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, (1 << 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 (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, (1 << sensor_id)); } else { dptf_threshold[sensor_id][idx].temp = -1; } } #ifdef CONFIG_DPTF_DEVICE_ORIENTATION /* * When tablet mode changes, send an event to ACPI to retrieve * tablet mode value and send an event to the kernel. */ static void dptf_tablet_mode_changed(void) { host_set_single_event(EC_HOST_EVENT_MODE_CHANGE); } DECLARE_HOOK(HOOK_TABLET_MODE_CHANGE, dptf_tablet_mode_changed, HOOK_PRIO_DEFAULT); #endif /*****************************************************************************/ /* 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 |= dpft_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. */ 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)");