summaryrefslogtreecommitdiff
path: root/common/fan.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/fan.c')
-rw-r--r--common/fan.c622
1 files changed, 0 insertions, 622 deletions
diff --git a/common/fan.c b/common/fan.c
deleted file mode 100644
index 636bec04f9..0000000000
--- a/common/fan.c
+++ /dev/null
@@ -1,622 +0,0 @@
-/* Copyright 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.
- */
-
-/* Basic Chrome OS fan control */
-
-#include "assert.h"
-#include "chipset.h"
-#include "common.h"
-#include "console.h"
-#include "fan.h"
-#include "gpio.h"
-#include "hooks.h"
-#include "host_command.h"
-#include "printf.h"
-#include "system.h"
-#include "util.h"
-
-/* True if we're listening to the thermal control task. False if we're setting
- * things manually. */
-static int thermal_control_enabled[CONFIG_FANS];
-
-int is_thermal_control_enabled(int idx)
-{
- return thermal_control_enabled[idx];
-}
-
-#ifdef CONFIG_FAN_UPDATE_PERIOD
-/* Should we ignore the fans for a while? */
-static int fan_update_counter[CONFIG_FANS];
-#endif
-
-/*
- * Number of fans.
- *
- * Use fan_get_count and fan_set_count to access it. It should be set only
- * before HOOK_INIT/HOOK_PRIO_DEFAULT.
- */
-static int fan_count = CONFIG_FANS;
-
-int fan_get_count(void)
-{
- return fan_count;
-}
-
-void fan_set_count(int count)
-{
- /* You can only decrease the count. */
- assert(count <= CONFIG_FANS);
- fan_count = count;
-}
-
-#ifndef CONFIG_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 fan_percent_to_rpm(int fan, int pct)
-{
- int rpm, max, min;
-
- if (!pct) {
- rpm = 0;
- } else {
- min = fans[fan].rpm->rpm_min;
- max = fans[fan].rpm->rpm_max;
- rpm = ((pct - 1) * max + (100 - pct) * min) / 99;
- }
-
- return rpm;
-}
-#endif /* CONFIG_FAN_RPM_CUSTOM */
-
-/* The thermal task will only call this function with pct in [0,100]. */
-test_mockable void fan_set_percent_needed(int fan, int pct)
-{
- int actual_rpm, new_rpm;
-
- if (!is_thermal_control_enabled(fan))
- return;
-
-#ifdef CONFIG_FAN_UPDATE_PERIOD
- /* Only set each fan every so often, to avoid rapid changes. */
- fan_update_counter[fan] %= CONFIG_FAN_UPDATE_PERIOD;
- if (fan_update_counter[fan]++)
- return;
-#endif
-
- new_rpm = fan_percent_to_rpm(fan, pct);
- actual_rpm = fan_get_rpm_actual(FAN_CH(fan));
-
- /* If we want to turn and the fans are currently significantly below
- * the minimum turning speed, we should turn at least as fast as the
- * necessary start speed instead. */
- if (new_rpm &&
- actual_rpm < fans[fan].rpm->rpm_min * 9 / 10 &&
- new_rpm < fans[fan].rpm->rpm_start)
- new_rpm = fans[fan].rpm->rpm_start;
-
- fan_set_rpm_target(FAN_CH(fan), new_rpm);
-}
-
-static void set_enabled(int fan, int enable)
-{
- fan_set_enabled(FAN_CH(fan), enable);
-
- if (fans[fan].conf->enable_gpio >= 0)
- gpio_set_level(fans[fan].conf->enable_gpio, enable);
-}
-
-test_export_static void set_thermal_control_enabled(int fan, int enable)
-{
- thermal_control_enabled[fan] = enable;
-
- /* If controlling the fan, need it in RPM-control mode */
- if (enable)
- fan_set_rpm_mode(FAN_CH(fan), 1);
-}
-
-static void set_duty_cycle(int fan, int percent)
-{
- /* Move the fan to manual control */
- fan_set_rpm_mode(FAN_CH(fan), 0);
-
- /* enable the fan when non-zero duty */
- set_enabled(fan, (percent > 0) ? 1 : 0);
-
- /* Disable thermal engine automatic fan control. */
- set_thermal_control_enabled(fan, 0);
-
- /* Set the duty cycle */
- fan_set_duty(FAN_CH(fan), percent);
-}
-
-/*****************************************************************************/
-/* Console commands */
-
-static int cc_fanauto(int argc, char **argv)
-{
- char *e;
- int fan = 0;
-
- if (fan_count > 1) {
- if (argc < 2) {
- ccprintf("fan number is required as the first arg\n");
- return EC_ERROR_PARAM_COUNT;
- }
- fan = strtoi(argv[1], &e, 0);
- if (*e || fan >= fan_count)
- return EC_ERROR_PARAM1;
- argc--;
- argv++;
- }
-
- set_thermal_control_enabled(fan, 1);
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(fanauto, cc_fanauto,
- "{fan}",
- "Enable thermal fan control");
-
-/* Return 0 for off, 1 for on, -1 for unknown */
-static int is_powered(int fan)
-{
- int is_pgood = -1;
-
- /* If we have an enable output, see if it's on or off. */
- if (fans[fan].conf->enable_gpio >= 0)
- is_pgood = gpio_get_level(fans[fan].conf->enable_gpio);
- /* If we have a pgood input, it overrides any enable output. */
- if (fans[fan].conf->pgood_gpio >= 0)
- is_pgood = gpio_get_level(fans[fan].conf->pgood_gpio);
-
- return is_pgood;
-}
-
-static int cc_faninfo(int argc, char **argv)
-{
- static const char * const human_status[] = {
- "not spinning", "changing", "locked", "frustrated"
- };
- int tmp, is_pgood;
- int fan;
- char leader[20] = "";
- for (fan = 0; fan < fan_count; fan++) {
- if (fan_count > 1)
- snprintf(leader, sizeof(leader), "Fan %d ", fan);
- if (fan)
- ccprintf("\n");
- ccprintf("%sActual: %4d rpm\n", leader,
- fan_get_rpm_actual(FAN_CH(fan)));
- ccprintf("%sTarget: %4d rpm\n", leader,
- fan_get_rpm_target(FAN_CH(fan)));
- ccprintf("%sDuty: %d%%\n", leader,
- fan_get_duty(FAN_CH(fan)));
- tmp = fan_get_status(FAN_CH(fan));
- ccprintf("%sStatus: %d (%s)\n", leader,
- tmp, human_status[tmp]);
- ccprintf("%sMode: %s\n", leader,
- fan_get_rpm_mode(FAN_CH(fan)) ? "rpm" : "duty");
- ccprintf("%sAuto: %s\n", leader,
- is_thermal_control_enabled(fan) ? "yes" : "no");
- ccprintf("%sEnable: %s\n", leader,
- fan_get_enabled(FAN_CH(fan)) ? "yes" : "no");
- is_pgood = is_powered(fan);
- if (is_pgood >= 0)
- ccprintf("%sPower: %s\n", leader,
- is_pgood ? "yes" : "no");
- }
-
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(faninfo, cc_faninfo,
- NULL,
- "Print fan info");
-
-static int cc_fanset(int argc, char **argv)
-{
- const char *rpm_str;
- int rpm;
- char *e;
- int fan = 0;
-
- if (fan_count == 0) {
- ccprintf("Fan count is zero\n");
- return EC_ERROR_INVAL;
- }
-
- if (fan_count > 1) {
- if (argc < 3) {
- ccprintf("fan number is required as the first arg\n");
- return EC_ERROR_PARAM_COUNT;
- }
- }
-
- if (argc == 3) {
- fan = strtoi(argv[1], &e, 0);
- if (*e || fan >= fan_count)
- return EC_ERROR_PARAM1;
- rpm_str = argv[2];
- } else if (argc == 2) {
- rpm_str = argv[1];
- } else {
- return EC_ERROR_PARAM_COUNT;
- }
-
- rpm = strtoi(rpm_str, &e, 0);
- 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 = fan_percent_to_rpm(fan, rpm);
- } else if (*e) {
- return EC_ERROR_PARAM1;
- }
-
- /* Move the fan to automatic control */
- fan_set_rpm_mode(FAN_CH(fan), 1);
-
- /* enable the fan when non-zero rpm */
- set_enabled(fan, (rpm > 0) ? 1 : 0);
-
- /* Disable thermal engine automatic fan control. */
- set_thermal_control_enabled(fan, 0);
-
- fan_set_rpm_target(FAN_CH(fan), rpm);
-
- ccprintf("Setting fan %d rpm target to %d\n", fan, rpm);
-
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(fanset, cc_fanset,
- "[fan] (rpm | pct%)",
- "Set fan speed");
-
-static int cc_fanduty(int argc, char **argv)
-{
- const char *percent_str;
- int percent = 0;
- char *e;
- int fan = 0;
-
- if (fan_count == 0) {
- ccprintf("Fan count is zero\n");
- return EC_ERROR_INVAL;
- }
-
- if (fan_count > 1) {
- if (argc < 3) {
- ccprintf("fan number is required as the first arg\n");
- return EC_ERROR_PARAM_COUNT;
- }
- }
-
- if (argc == 3) {
- fan = strtoi(argv[1], &e, 0);
- if (*e || fan >= fan_count)
- return EC_ERROR_PARAM1;
- percent_str = argv[2];
- } else if (argc == 2) {
- percent_str = argv[1];
- } else {
- return EC_ERROR_PARAM_COUNT;
- }
-
- percent = strtoi(percent_str, &e, 0);
- if (*e)
- return EC_ERROR_PARAM1;
-
- ccprintf("Setting fan %d duty cycle to %d%%\n", fan, percent);
- set_duty_cycle(fan, percent);
-
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(fanduty, cc_fanduty,
- "[fan] percent",
- "Set fan duty cycle");
-
-/*****************************************************************************/
-/* DPTF interface functions */
-
-/* 0-100% if in duty mode. -1 if not */
-int dptf_get_fan_duty_target(void)
-{
- int fan = 0; /* TODO(crosbug.com/p/23803) */
-
- if (fan_count == 0)
- return -1;
-
- if (is_thermal_control_enabled(fan) || fan_get_rpm_mode(FAN_CH(fan)))
- return -1;
-
- return fan_get_duty(FAN_CH(fan));
-}
-
-/* 0-100% sets duty, out of range means let the EC drive */
-void dptf_set_fan_duty_target(int pct)
-{
- int fan;
-
- if (pct < 0 || pct > 100) {
- /* TODO(crosbug.com/p/23803) */
- for (fan = 0; fan < fan_count; fan++)
- set_thermal_control_enabled(fan, 1);
- } else {
- /* TODO(crosbug.com/p/23803) */
- for (fan = 0; fan < fan_count; fan++)
- set_duty_cycle(fan, pct);
- }
-}
-
-/*****************************************************************************/
-/* Host commands */
-
-static enum ec_status
-hc_pwm_get_fan_target_rpm(struct host_cmd_handler_args *args)
-{
- struct ec_response_pwm_get_fan_rpm *r = args->response;
-
- if (fan_count == 0)
- return EC_RES_ERROR;
-
- /* TODO(crosbug.com/p/23803) */
- r->rpm = fan_get_rpm_target(FAN_CH(0));
- args->response_size = sizeof(*r);
-
- return EC_RES_SUCCESS;
-}
-DECLARE_HOST_COMMAND(EC_CMD_PWM_GET_FAN_TARGET_RPM,
- hc_pwm_get_fan_target_rpm,
- EC_VER_MASK(0));
-
-static enum ec_status
-hc_pwm_set_fan_target_rpm(struct host_cmd_handler_args *args)
-{
- const struct ec_params_pwm_set_fan_target_rpm_v1 *p_v1 = args->params;
- const struct ec_params_pwm_set_fan_target_rpm_v0 *p_v0 = args->params;
- int fan;
-
- if (args->version == 0) {
- for (fan = 0; fan < fan_count; fan++) {
- /* enable the fan if rpm is non-zero */
- set_enabled(fan, (p_v0->rpm > 0) ? 1 : 0);
-
- set_thermal_control_enabled(fan, 0);
- fan_set_rpm_mode(FAN_CH(fan), 1);
- fan_set_rpm_target(FAN_CH(fan), p_v0->rpm);
- }
-
- return EC_RES_SUCCESS;
- }
-
- fan = p_v1->fan_idx;
- if (fan >= fan_count)
- return EC_RES_ERROR;
-
- /* enable the fan if rpm is non-zero */
- set_enabled(fan, (p_v1->rpm > 0) ? 1 :0);
-
- set_thermal_control_enabled(fan, 0);
- fan_set_rpm_mode(FAN_CH(fan), 1);
- fan_set_rpm_target(FAN_CH(fan), p_v1->rpm);
-
- return EC_RES_SUCCESS;
-}
-DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_FAN_TARGET_RPM,
- hc_pwm_set_fan_target_rpm,
- EC_VER_MASK(0) | EC_VER_MASK(1));
-
-static enum ec_status hc_pwm_set_fan_duty(struct host_cmd_handler_args *args)
-{
- const struct ec_params_pwm_set_fan_duty_v1 *p_v1 = args->params;
- const struct ec_params_pwm_set_fan_duty_v0 *p_v0 = args->params;
- int fan;
-
- if (args->version == 0) {
- for (fan = 0; fan < fan_count; fan++)
- set_duty_cycle(fan, p_v0->percent);
-
- return EC_RES_SUCCESS;
- }
-
- fan = p_v1->fan_idx;
- if (fan >= fan_count)
- return EC_RES_ERROR;
-
- set_duty_cycle(fan, p_v1->percent);
-
- return EC_RES_SUCCESS;
-}
-DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_FAN_DUTY,
- hc_pwm_set_fan_duty,
- EC_VER_MASK(0) | EC_VER_MASK(1));
-
-static enum ec_status
-hc_thermal_auto_fan_ctrl(struct host_cmd_handler_args *args)
-{
- int fan;
- const struct ec_params_auto_fan_ctrl_v1 *p_v1 = args->params;
-
- if (args->version == 0) {
- for (fan = 0; fan < fan_count; fan++)
- set_thermal_control_enabled(fan, 1);
-
- return EC_RES_SUCCESS;
- }
-
- fan = p_v1->fan_idx;
- if (fan >= fan_count)
- return EC_RES_ERROR;
-
- set_thermal_control_enabled(fan, 1);
-
- return EC_RES_SUCCESS;
-}
-DECLARE_HOST_COMMAND(EC_CMD_THERMAL_AUTO_FAN_CTRL,
- hc_thermal_auto_fan_ctrl,
- EC_VER_MASK(0)|EC_VER_MASK(1));
-
-
-/*****************************************************************************/
-/* Hooks */
-
-/* We only have a limited number of memory-mapped slots to report fan speed to
- * the AP. If we have more fans than that, some will be inaccessible. But
- * if we're using that many fans, we probably have bigger problems.
- */
-BUILD_ASSERT(CONFIG_FANS <= EC_FAN_SPEED_ENTRIES);
-
-#define PWMFAN_SYSJUMP_TAG 0x5046 /* "PF" */
-#define PWM_HOOK_VERSION 1
-/* Saved PWM state across sysjumps */
-struct pwm_fan_state {
- /* TODO(crosbug.com/p/23530): Still treating all fans as one. */
- uint16_t rpm;
- uint8_t flag; /* FAN_STATE_FLAG_* */
-};
-
-/* For struct pwm_fan_state.flag */
-#define FAN_STATE_FLAG_ENABLED BIT(0)
-#define FAN_STATE_FLAG_THERMAL BIT(1)
-
-static void pwm_fan_init(void)
-{
- const struct pwm_fan_state *prev;
- struct pwm_fan_state state;
- uint16_t *mapped;
- int version, size;
- int i;
- int fan;
-
- if (fan_count == 0)
- return;
-
- for (fan = 0; fan < fan_count; fan++)
- fan_channel_setup(FAN_CH(fan), fans[fan].conf->flags);
-
- /* Restore previous state. */
- prev = (const struct pwm_fan_state *)
- system_get_jump_tag(PWMFAN_SYSJUMP_TAG, &version, &size);
- if (prev && version == PWM_HOOK_VERSION && size == sizeof(*prev)) {
- memcpy(&state, prev, sizeof(state));
- } else {
- memset(&state, 0, sizeof(state));
- }
-
- for (fan = 0; fan < fan_count; fan++) {
- fan_set_enabled(FAN_CH(fan),
- state.flag & FAN_STATE_FLAG_ENABLED);
- fan_set_rpm_target(FAN_CH(fan), state.rpm);
- set_thermal_control_enabled(
- fan, state.flag & FAN_STATE_FLAG_THERMAL);
- }
-
- /* Initialize memory-mapped data */
- mapped = (uint16_t *)host_get_memmap(EC_MEMMAP_FAN);
- for (i = 0; i < EC_FAN_SPEED_ENTRIES; i++)
- mapped[i] = EC_FAN_SPEED_NOT_PRESENT;
-}
-DECLARE_HOOK(HOOK_INIT, pwm_fan_init, HOOK_PRIO_DEFAULT);
-
-static void pwm_fan_second(void)
-{
- uint16_t *mapped = (uint16_t *)host_get_memmap(EC_MEMMAP_FAN);
- uint16_t rpm;
- int stalled = 0;
- int fan;
-
- for (fan = 0; fan < fan_count; fan++) {
- if (fan_is_stalled(FAN_CH(fan))) {
- rpm = EC_FAN_SPEED_STALLED;
- stalled = 1;
- cprints(CC_PWM, "Fan %d stalled!", fan);
- } else {
- rpm = fan_get_rpm_actual(FAN_CH(fan));
- }
-
- mapped[fan] = rpm;
- }
-
- /*
- * Issue warning. As we have thermal shutdown
- * protection, issuing warning here should be enough.
- */
- if (stalled)
- host_set_single_event(EC_HOST_EVENT_THERMAL);
-}
-DECLARE_HOOK(HOOK_SECOND, pwm_fan_second, HOOK_PRIO_DEFAULT);
-
-static void pwm_fan_preserve_state(void)
-{
- struct pwm_fan_state state = {0};
- int fan = 0;
-
- if (fan_count == 0)
- return;
-
- /* TODO(crosbug.com/p/23530): Still treating all fans as one. */
- if (fan_get_enabled(FAN_CH(fan)))
- state.flag |= FAN_STATE_FLAG_ENABLED;
- if (is_thermal_control_enabled(fan))
- state.flag |= FAN_STATE_FLAG_THERMAL;
- state.rpm = fan_get_rpm_target(FAN_CH(fan));
-
- system_add_jump_tag(PWMFAN_SYSJUMP_TAG, PWM_HOOK_VERSION,
- sizeof(state), &state);
-}
-DECLARE_HOOK(HOOK_SYSJUMP, pwm_fan_preserve_state, HOOK_PRIO_DEFAULT);
-
-static void pwm_fan_control(int enable)
-{
- int fan;
-
- /* TODO(crosbug.com/p/23530): Still treating all fans as one. */
- for (fan = 0; fan < fan_count; fan++) {
- set_thermal_control_enabled(fan, enable);
- fan_set_rpm_target(FAN_CH(fan), enable ?
- fan_percent_to_rpm(FAN_CH(fan), CONFIG_FAN_INIT_SPEED) :
- 0);
- set_enabled(fan, enable);
- }
-}
-
-static void pwm_fan_stop(void)
-{
- /*
- * There is no need to cool CPU in S3 or S5. We currently don't
- * have fans for battery or charger chip. Battery systems will
- * control charge current based on its own temperature readings.
- * Thus, we do not need to keep fans running in S3 or S5.
- *
- * Even with a fan on charging system, it's questionable to run
- * a fan in S3/S5. Under an extreme heat condition, spinning a
- * fan would create more heat as it draws current from a
- * battery and heat would come from ambient air instead of CPU.
- *
- * Thermal control may be already disabled if DPTF is used.
- */
- pwm_fan_control(0); /* crosbug.com/p/8097 */
-}
-DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pwm_fan_stop, HOOK_PRIO_DEFAULT);
-DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pwm_fan_stop, HOOK_PRIO_DEFAULT);
-
-static void pwm_fan_start(void)
-{
- /*
- * Even if the DPTF is enabled, enable thermal control here.
- * Upon booting to S0, if needed AP will disable/throttle it using
- * host commands.
- */
- if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ON))
- pwm_fan_control(1);
-}
-/* On Fizz, CHIPSET_RESUME isn't triggered when AP warm resets.
- * So we hook CHIPSET_RESET instead.
- */
-DECLARE_HOOK(HOOK_CHIPSET_RESET, pwm_fan_start, HOOK_PRIO_FIRST);
-DECLARE_HOOK(HOOK_CHIPSET_RESUME, pwm_fan_start, HOOK_PRIO_DEFAULT);