From c7b930606b479d4747da224f6cf62fd8061e6af0 Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Tue, 29 Oct 2013 13:05:11 -0700 Subject: Separate common fan behavior from implementation This looks like a lot, but it's really just moving the non-board-specific stuff from chip/lm4/fan.c into common/fan.c, updating the appropriate headers, and renaming functions to better match the new location. This is entirely code refactoring and renaming. No new functionality. BUG=chrome-os-partner:23530 BRANCH=none TEST=manual make runtests, build all platforms, build and test on Link. Change-Id: I7dc03d6732bad83cf838a86600b42a7cff5aa7aa Signed-off-by: Bill Richardson Reviewed-on: https://chromium-review.googlesource.com/175012 --- board/falco/board.c | 2 +- board/host/build.mk | 1 + board/host/fan.c | 67 ++++++++++ chip/lm4/build.mk | 5 +- chip/lm4/fan.c | 363 +++++++++++++++------------------------------------ chip/lm4/fan_chip.c | 180 ------------------------- chip/lm4/fan_chip.h | 27 ---- chip/lm4/pwm.c | 14 +- common/build.mk | 3 +- common/fan.c | 337 +++++++++++++++++++++++++++++++++++++++++++++++ common/pwm_fan.c | 27 ---- common/thermal.c | 2 +- include/config.h | 2 +- include/fan.h | 32 ++++- test/test_config.h | 2 + test/thermal.c | 2 +- test/thermal_falco.c | 2 +- 17 files changed, 560 insertions(+), 508 deletions(-) create mode 100644 board/host/fan.c delete mode 100644 chip/lm4/fan_chip.c delete mode 100644 chip/lm4/fan_chip.h create mode 100644 common/fan.c delete mode 100644 common/pwm_fan.c diff --git a/board/falco/board.c b/board/falco/board.c index 23da16230d..3e8b88da69 100644 --- a/board/falco/board.c +++ b/board/falco/board.c @@ -255,7 +255,7 @@ int board_discharge_on_ac(int enable) * And never turn it off. Bah. That'll do wonders for battery life. */ #ifdef CONFIG_FAN_RPM_CUSTOM -int pwm_fan_percent_to_rpm(int pct) +int fan_percent_to_rpm(int pct) { const int FAN_MAX = 5050; const int FAN_MIN = 2700; diff --git a/board/host/build.mk b/board/host/build.mk index 21e5383d86..4448f4b0b3 100644 --- a/board/host/build.mk +++ b/board/host/build.mk @@ -11,3 +11,4 @@ CHIP:=host board-y=board.o board-$(HAS_TASK_CHIPSET)+=chipset.o board-$(CONFIG_BATTERY_MOCK)+=battery.o charger.o +board-$(CONFIG_FAN)+=fan.o diff --git a/board/host/fan.c b/board/host/fan.c new file mode 100644 index 0000000000..b7a10de05c --- /dev/null +++ b/board/host/fan.c @@ -0,0 +1,67 @@ +/* Copyright (c) 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. + */ + +/* Mocked fan implementation for tests */ + +#include "fan.h" + +static int mock_enabled; +void fan_set_enabled(int ch, int enabled) +{ + mock_enabled = enabled; +} +int fan_get_enabled(int ch) +{ + return mock_enabled; +} + +static int mock_percent; +void fan_set_duty(int ch, int percent) +{ + mock_percent = percent; +} +int fan_get_duty(int ch) +{ + return mock_percent; +} + +static int mock_rpm_mode; +void fan_set_rpm_mode(int ch, int rpm_mode) +{ + mock_rpm_mode = rpm_mode; +} +int fan_get_rpm_mode(int ch) +{ + return mock_rpm_mode; +} + +static int mock_rpm; +void fan_set_rpm_target(int ch, int rpm) +{ + mock_rpm = rpm; +} +int fan_get_rpm_actual(int ch) +{ + return mock_rpm; +} +int fan_get_rpm_target(int ch) +{ + return mock_rpm; +} + +enum fan_status fan_get_status(int ch) +{ + return FAN_STATUS_LOCKED; +} + +int fan_is_stalled(int ch) +{ + return 0; +} + +void fan_channel_setup(int ch, unsigned int flags) +{ + /* nothing to do */ +} diff --git a/chip/lm4/build.mk b/chip/lm4/build.mk index 551df6e2ac..234c86b669 100644 --- a/chip/lm4/build.mk +++ b/chip/lm4/build.mk @@ -15,12 +15,13 @@ chip-y=clock.o gpio.o hwtimer.o jtag.o system.o uart.o # Optional chip modules chip-$(CONFIG_ADC)+=adc.o chip_temp_sensor.o chip-$(CONFIG_EEPROM)+=eeprom.o -chip-$(CONFIG_FAN)+=fan.o fan_chip.o +chip-$(CONFIG_FAN)+=fan.o chip-$(CONFIG_FLASH)+=flash.o chip-$(CONFIG_I2C)+=i2c.o chip-$(CONFIG_LPC)+=lpc.o chip-$(CONFIG_PECI)+=peci.o -chip-$(CONFIG_PWM)+=pwm.o fan_chip.o +# pwm functions are implemented with the fan functions +chip-$(CONFIG_PWM)+=pwm.o fan.o chip-$(CONFIG_SPI)+=spi.o chip-$(CONFIG_WATCHDOG)+=watchdog.o chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o diff --git a/chip/lm4/fan.c b/chip/lm4/fan.c index e8a77c3e91..a342a49954 100644 --- a/chip/lm4/fan.c +++ b/chip/lm4/fan.c @@ -3,322 +3,173 @@ * found in the LICENSE file. */ -/* Chrome OS fan control */ +/* LM4 fan control module. */ #include "clock.h" -#include "common.h" -#include "console.h" #include "fan.h" -#include "fan_chip.h" #include "gpio.h" #include "hooks.h" -#include "host_command.h" -#include "pwm.h" #include "registers.h" -#include "system.h" -#include "task.h" -#include "timer.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; +/* Maximum RPM for fan controller */ +#define MAX_RPM 0x1fff -static void fan_set_enabled(int enable) -{ - fan_chip_set_enabled(CONFIG_FAN_CH_CPU, enable); +/* Maximum PWM for PWM controller */ +#define MAX_PWM 0x1ff -#ifdef CONFIG_FAN_EN_GPIO - gpio_set_level(CONFIG_FAN_EN_GPIO, enable); -#endif /* CONFIG_FAN_EN_GPIO */ -} +/* + * Scaling factor for requested/actual RPM for CPU fan. We need this because + * the fan controller on Blizzard filters tach pulses that are less than 64 + * 15625Hz ticks apart, which works out to ~7000rpm on an unscaled fan. By + * telling the controller we actually have twice as many edges per revolution, + * the controller can handle fans that actually go twice as fast. See + * crosbug.com/p/7718. + */ +#define RPM_SCALE 2 -static void fan_set_thermal_control_enabled(int enable) -{ - thermal_control_enabled = enable; - /* If controlling the fan, need it in RPM-control mode */ - if (enable) - fan_chip_channel_setup(CONFIG_FAN_CH_CPU, - FAN_CHIP_USE_RPM_MODE); +void fan_set_enabled(int ch, int enabled) +{ + if (enabled) + LM4_FAN_FANCTL |= (1 << ch); + else + LM4_FAN_FANCTL &= ~(1 << ch); } -/* The thermal task will only call this function with pct in [0,100]. */ -test_mockable void pwm_fan_set_percent_needed(int pct) +int fan_get_enabled(int ch) { - int rpm; - - if (!thermal_control_enabled) - return; - - rpm = pwm_fan_percent_to_rpm(pct); - - fan_chip_set_rpm_target(CONFIG_FAN_CH_CPU, rpm); + return (LM4_FAN_FANCTL & (1 << ch)) ? 1 : 0; } -static void fan_set_duty_cycle(int percent) +void fan_set_duty(int ch, int percent) { - /* Move the fan to manual control */ - fan_chip_set_rpm_mode(CONFIG_FAN_CH_CPU, 0); + int duty; + + if (percent < 0) + percent = 0; + else if (percent > 100) + percent = 100; - /* Always enable the fan */ - fan_set_enabled(1); + duty = (MAX_PWM * percent + 50) / 100; - /* Disable thermal engine automatic fan control. */ - fan_set_thermal_control_enabled(0); + /* Always enable the channel */ + fan_set_enabled(ch, 1); /* Set the duty cycle */ - fan_chip_set_duty(CONFIG_FAN_CH_CPU, percent); + LM4_FAN_FANCMD(ch) = duty << 16; } -/*****************************************************************************/ -/* Console commands */ - -static int cc_fanauto(int argc, char **argv) +int fan_get_duty(int ch) { - fan_set_thermal_control_enabled(1); - return EC_SUCCESS; + return ((LM4_FAN_FANCMD(ch) >> 16) * 100 + MAX_PWM / 2) / MAX_PWM; } -DECLARE_CONSOLE_COMMAND(fanauto, cc_fanauto, - NULL, - "Enable thermal fan control", - NULL); -static int cc_faninfo(int argc, char **argv) +int fan_get_rpm_mode(int ch) { - static const char * const human_status[] = { - "not spinning", "changing", "locked", "frustrated" - }; - int tmp; - - ccprintf("Actual: %4d rpm\n", - fan_chip_get_rpm_actual(CONFIG_FAN_CH_CPU)); - ccprintf("Target: %4d rpm\n", - fan_chip_get_rpm_target(CONFIG_FAN_CH_CPU)); - ccprintf("Duty: %d%%\n", - fan_chip_get_duty(CONFIG_FAN_CH_CPU)); - tmp = fan_chip_get_status(CONFIG_FAN_CH_CPU); - ccprintf("Status: %d (%s)\n", tmp, human_status[tmp]); - ccprintf("Mode: %s\n", - fan_chip_get_rpm_mode(CONFIG_FAN_CH_CPU) ? "rpm" : "duty"); - ccprintf("Auto: %s\n", thermal_control_enabled ? "yes" : "no"); - ccprintf("Enable: %s\n", - fan_chip_get_enabled(CONFIG_FAN_CH_CPU) ? "yes" : "no"); -#ifdef CONFIG_FAN_PGOOD_GPIO - ccprintf("Power: %s\n", -#ifdef CONFIG_FAN_EN_GPIO - gpio_get_level(CONFIG_FAN_EN_GPIO) && -#endif - gpio_get_level(CONFIG_FAN_PGOOD_GPIO) ? "yes" : "no"); -#endif - - - return EC_SUCCESS; + return (LM4_FAN_FANCH(ch) & 0x0001) ? 0 : 1; } -DECLARE_CONSOLE_COMMAND(faninfo, cc_faninfo, - NULL, - "Print fan info", - NULL); -static int cc_fanset(int argc, char **argv) +void fan_set_rpm_mode(int ch, int rpm_mode) { - int rpm; - char *e; + int was_enabled = fan_get_enabled(ch); + int was_rpm = fan_get_rpm_mode(ch); - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - rpm = strtoi(argv[1], &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 = pwm_fan_percent_to_rpm(rpm); - } else if (*e) { - return EC_ERROR_PARAM1; + if (!was_rpm && rpm_mode) { + /* Enable RPM control */ + fan_set_enabled(ch, 0); + LM4_FAN_FANCH(ch) &= ~0x0001; + fan_set_enabled(ch, was_enabled); + } else if (was_rpm && !rpm_mode) { + /* Disable RPM mode */ + fan_set_enabled(ch, 0); + LM4_FAN_FANCH(ch) |= 0x0001; + fan_set_enabled(ch, was_enabled); } - - /* Move the fan to automatic control */ - fan_chip_set_rpm_mode(CONFIG_FAN_CH_CPU, 1); - - /* Always enable the fan */ - fan_set_enabled(1); - - /* Disable thermal engine automatic fan control. */ - fan_set_thermal_control_enabled(0); - - fan_chip_set_rpm_target(CONFIG_FAN_CH_CPU, rpm); - - ccprintf("Setting fan rpm target to %d\n", rpm); - - return EC_SUCCESS; } -DECLARE_CONSOLE_COMMAND(fanset, cc_fanset, - "rpm | pct%", - "Set fan speed", - NULL); -static int cc_fanduty(int argc, char **argv) +int fan_get_rpm_actual(int ch) { - int percent = 0; - char *e; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - percent = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - ccprintf("Setting fan duty cycle to %d%%\n", percent); - fan_set_duty_cycle(percent); - - return EC_SUCCESS; + return (LM4_FAN_FANCST(ch) & MAX_RPM) * RPM_SCALE; } -DECLARE_CONSOLE_COMMAND(fanduty, cc_fanduty, - "percent", - "Set fan duty cycle", - NULL); - -/*****************************************************************************/ -/* Host commands */ -static int hc_pwm_get_fan_target_rpm(struct host_cmd_handler_args *args) +int fan_get_rpm_target(int ch) { - struct ec_response_pwm_get_fan_rpm *r = args->response; - - r->rpm = fan_chip_get_rpm_target(CONFIG_FAN_CH_CPU); - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; + return (LM4_FAN_FANCMD(ch) & MAX_RPM) * RPM_SCALE; } -DECLARE_HOST_COMMAND(EC_CMD_PWM_GET_FAN_TARGET_RPM, - hc_pwm_get_fan_target_rpm, - EC_VER_MASK(0)); -static int hc_pwm_set_fan_target_rpm(struct host_cmd_handler_args *args) +void fan_set_rpm_target(int ch, int rpm) { - const struct ec_params_pwm_set_fan_target_rpm *p = args->params; + /* Apply fan scaling */ + if (rpm > 0) + rpm /= RPM_SCALE; - fan_set_thermal_control_enabled(0); - fan_chip_set_rpm_mode(CONFIG_FAN_CH_CPU, 1); - fan_chip_set_rpm_target(CONFIG_FAN_CH_CPU, p->rpm); + /* Treat out-of-range requests as requests for maximum fan speed */ + if (rpm < 0 || rpm > MAX_RPM) + rpm = MAX_RPM; - return EC_RES_SUCCESS; + LM4_FAN_FANCMD(ch) = rpm; } -DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_FAN_TARGET_RPM, - hc_pwm_set_fan_target_rpm, - EC_VER_MASK(0)); -static int hc_pwm_set_fan_duty(struct host_cmd_handler_args *args) +/* The LM4 status is the original definition of enum fan_status */ +enum fan_status fan_get_status(int ch) { - const struct ec_params_pwm_set_fan_duty *p = args->params; - fan_set_duty_cycle(p->percent); - - return EC_RES_SUCCESS; + return (LM4_FAN_FANSTS >> (2 * ch)) & 0x03; } -DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_FAN_DUTY, - hc_pwm_set_fan_duty, - EC_VER_MASK(0)); - -static int hc_thermal_auto_fan_ctrl(struct host_cmd_handler_args *args) -{ - fan_set_thermal_control_enabled(1); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_THERMAL_AUTO_FAN_CTRL, - hc_thermal_auto_fan_ctrl, - EC_VER_MASK(0)); - -/*****************************************************************************/ -/* Hooks */ - -#define PWMFAN_SYSJUMP_TAG 0x5046 /* "PF" */ -#define PWM_HOOK_VERSION 1 -/* Saved PWM state across sysjumps */ -struct pwm_fan_state { - uint16_t fan_rpm; - uint8_t fan_en; - char pad; /* Pad to multiple of 4 bytes. */ -}; - -static void pwm_fan_init(void) +/** + * Return non-zero if fan is enabled but stalled. + */ +int fan_is_stalled(int ch) { - const struct pwm_fan_state *prev; - uint16_t *mapped; - int version, size; - int i; + /* Must be enabled with non-zero target to stall */ + if (!fan_get_enabled(ch) || fan_get_rpm_target(ch) == 0) + return 0; - gpio_config_module(MODULE_PWM_FAN, 1); - - prev = (const struct pwm_fan_state *) - system_get_jump_tag(PWMFAN_SYSJUMP_TAG, &version, &size); - if (prev && version == PWM_HOOK_VERSION && size == sizeof(*prev)) { - /* Restore previous state. */ - fan_chip_set_enabled(CONFIG_FAN_CH_CPU, prev->fan_en); - fan_chip_set_rpm_target(CONFIG_FAN_CH_CPU, prev->fan_rpm); - } else { - /* Set initial fan speed to maximum */ - fan_chip_set_duty(CONFIG_FAN_CH_CPU, 100); - } - - fan_set_thermal_control_enabled(1); - - /* 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; + /* Check for stall condition */ + return (((LM4_FAN_FANSTS >> (2 * ch)) & 0x03) == 0) ? 1 : 0; } -DECLARE_HOOK(HOOK_INIT, pwm_fan_init, HOOK_PRIO_DEFAULT + 1); -static void pwm_fan_second(void) +void fan_channel_setup(int ch, unsigned int flags) { - uint16_t *mapped = (uint16_t *)host_get_memmap(EC_MEMMAP_FAN); - - if (fan_chip_is_stalled(CONFIG_FAN_CH_CPU)) { - mapped[0] = EC_FAN_SPEED_STALLED; + if (flags & FAN_USE_RPM_MODE) { /* - * Issue warning. As we have thermal shutdown - * protection, issuing warning here should be enough. + * Configure automatic/feedback mode: + * 0x8000 = bit 15 = auto-restart + * 0x0000 = bit 14 = slow acceleration + * 0x0000 = bits 13:11 = no hysteresis + * 0x0000 = bits 10:8 = start period (2<<0) edges + * 0x0000 = bits 7:6 = no fast start + * 0x0020 = bits 5:4 = average 4 edges when + * calculating RPM + * 0x000c = bits 3:2 = 8 pulses per revolution + * (see note at top of file) + * 0x0000 = bit 0 = automatic control */ - host_set_single_event(EC_HOST_EVENT_THERMAL); - cprintf(CC_PWM, "[%T Fan stalled!]\n"); + LM4_FAN_FANCH(ch) = 0x802c; } else { - mapped[0] = fan_chip_get_rpm_actual(CONFIG_FAN_CH_CPU); + /* + * Configure drive-only mode: + * 0x0000 = bit 15 = no auto-restart + * 0x0000 = bit 14 = slow acceleration + * 0x0000 = bits 13:11 = no hysteresis + * 0x0000 = bits 10:8 = start period (2<<0) edges + * 0x0000 = bits 7:6 = no fast start + * 0x0000 = bits 5:4 = no RPM averaging + * 0x0000 = bits 3:2 = 1 pulses per revolution + * 0x0001 = bit 0 = manual control + */ + LM4_FAN_FANCH(ch) = 0x0001; } } -DECLARE_HOOK(HOOK_SECOND, pwm_fan_second, HOOK_PRIO_DEFAULT); - -static void pwm_fan_preserve_state(void) -{ - struct pwm_fan_state state; - - state.fan_en = fan_chip_get_enabled(CONFIG_FAN_CH_CPU); - state.fan_rpm = fan_chip_get_rpm_target(CONFIG_FAN_CH_CPU); - - 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_resume(void) +static void fan_init(void) { - fan_chip_set_enabled(CONFIG_FAN_CH_CPU, 1); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, pwm_fan_resume, HOOK_PRIO_DEFAULT); + /* Enable the fan module and delay a few clocks */ + clock_enable_peripheral(CGC_OFFSET_FAN, 0x1, + CGC_MODE_RUN | CGC_MODE_SLEEP); -static void pwm_fan_S3_S5(void) -{ - /* Take back fan control when the processor shuts down */ - fan_set_thermal_control_enabled(1); - /* For now don't do anything with it. We'll have to turn it on again if - * we need active cooling during heavy battery charging or something. - */ - fan_chip_set_rpm_target(CONFIG_FAN_CH_CPU, 0); - fan_chip_set_enabled(CONFIG_FAN_CH_CPU, 0); /* crosbug.com/p/8097 */ + /* Disable all fans */ + LM4_FAN_FANCTL = 0; } -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pwm_fan_S3_S5, HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pwm_fan_S3_S5, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_INIT, fan_init, HOOK_PRIO_INIT_PWM); diff --git a/chip/lm4/fan_chip.c b/chip/lm4/fan_chip.c deleted file mode 100644 index cef39b9b3d..0000000000 --- a/chip/lm4/fan_chip.c +++ /dev/null @@ -1,180 +0,0 @@ -/* Copyright (c) 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. - */ - -/* LM4 fan control module. */ - -#include "clock.h" -#include "fan_chip.h" -#include "gpio.h" -#include "hooks.h" -#include "registers.h" -#include "util.h" - -/* Maximum RPM for fan controller */ -#define MAX_RPM 0x1fff - -/* Maximum PWM for PWM controller */ -#define MAX_PWM 0x1ff - -/* - * Scaling factor for requested/actual RPM for CPU fan. We need this because - * the fan controller on Blizzard filters tach pulses that are less than 64 - * 15625Hz ticks apart, which works out to ~7000rpm on an unscaled fan. By - * telling the controller we actually have twice as many edges per revolution, - * the controller can handle fans that actually go twice as fast. See - * crosbug.com/p/7718. - */ -#define RPM_SCALE 2 - - -void fan_chip_set_enabled(int ch, int enabled) -{ - if (enabled) - LM4_FAN_FANCTL |= (1 << ch); - else - LM4_FAN_FANCTL &= ~(1 << ch); -} - -int fan_chip_get_enabled(int ch) -{ - return (LM4_FAN_FANCTL & (1 << ch)) ? 1 : 0; -} - -void fan_chip_set_duty(int ch, int percent) -{ - int duty; - - if (percent < 0) - percent = 0; - else if (percent > 100) - percent = 100; - - duty = (MAX_PWM * percent + 50) / 100; - - /* Always enable the channel */ - fan_chip_set_enabled(ch, 1); - - /* Set the duty cycle */ - LM4_FAN_FANCMD(ch) = duty << 16; -} - -int fan_chip_get_duty(enum pwm_channel ch) -{ - return ((LM4_FAN_FANCMD(ch) >> 16) * 100 + MAX_PWM / 2) / MAX_PWM; -} - -int fan_chip_get_rpm_mode(int ch) -{ - return (LM4_FAN_FANCH(ch) & 0x0001) ? 0 : 1; -} - -void fan_chip_set_rpm_mode(int ch, int rpm_mode) -{ - int was_enabled = fan_chip_get_enabled(ch); - int was_rpm = fan_chip_get_rpm_mode(ch); - - if (!was_rpm && rpm_mode) { - /* Enable RPM control */ - fan_chip_set_enabled(ch, 0); - LM4_FAN_FANCH(ch) &= ~0x0001; - fan_chip_set_enabled(ch, was_enabled); - } else if (was_rpm && !rpm_mode) { - /* Disable RPM mode */ - fan_chip_set_enabled(ch, 0); - LM4_FAN_FANCH(ch) |= 0x0001; - fan_chip_set_enabled(ch, was_enabled); - } -} - -int fan_chip_get_rpm_actual(int ch) -{ - return (LM4_FAN_FANCST(ch) & MAX_RPM) * RPM_SCALE; -} - -int fan_chip_get_rpm_target(int ch) -{ - return (LM4_FAN_FANCMD(ch) & MAX_RPM) * RPM_SCALE; -} - -void fan_chip_set_rpm_target(int ch, int rpm) -{ - /* Apply fan scaling */ - if (rpm > 0) - rpm /= RPM_SCALE; - - /* Treat out-of-range requests as requests for maximum fan speed */ - if (rpm < 0 || rpm > MAX_RPM) - rpm = MAX_RPM; - - LM4_FAN_FANCMD(ch) = rpm; -} - -/* - * 0 = stopped - * 1 = changing - * 2 = locked - * 3 = unable to lock - */ -int fan_chip_get_status(int ch) -{ - return (LM4_FAN_FANSTS >> (2 * ch)) & 0x03; -} - -/** - * Return non-zero if fan is enabled but stalled. - */ -int fan_chip_is_stalled(int ch) -{ - /* Must be enabled with non-zero target to stall */ - if (!fan_chip_get_enabled(ch) || fan_chip_get_rpm_target(ch) == 0) - return 0; - - /* Check for stall condition */ - return (((LM4_FAN_FANSTS >> (2 * ch)) & 0x03) == 0) ? 1 : 0; -} - -void fan_chip_channel_setup(int ch, unsigned int flags) -{ - if (flags & FAN_CHIP_USE_RPM_MODE) { - /* - * Configure automatic/feedback mode: - * 0x8000 = bit 15 = auto-restart - * 0x0000 = bit 14 = slow acceleration - * 0x0000 = bits 13:11 = no hysteresis - * 0x0000 = bits 10:8 = start period (2<<0) edges - * 0x0000 = bits 7:6 = no fast start - * 0x0020 = bits 5:4 = average 4 edges when - * calculating RPM - * 0x000c = bits 3:2 = 8 pulses per revolution - * (see note at top of file) - * 0x0000 = bit 0 = automatic control - */ - LM4_FAN_FANCH(ch) = 0x802c; - } else { - /* - * Configure drive-only mode: - * 0x0000 = bit 15 = no auto-restart - * 0x0000 = bit 14 = slow acceleration - * 0x0000 = bits 13:11 = no hysteresis - * 0x0000 = bits 10:8 = start period (2<<0) edges - * 0x0000 = bits 7:6 = no fast start - * 0x0000 = bits 5:4 = no RPM averaging - * 0x0000 = bits 3:2 = 1 pulses per revolution - * 0x0001 = bit 0 = manual control - */ - LM4_FAN_FANCH(ch) = 0x0001; - } -} - -static void fan_chip_init(void) -{ - /* Enable the fan module and delay a few clocks */ - clock_enable_peripheral(CGC_OFFSET_FAN, 0x1, - CGC_MODE_RUN | CGC_MODE_SLEEP); - - /* Disable all fans */ - LM4_FAN_FANCTL = 0; -} -DECLARE_HOOK(HOOK_INIT, fan_chip_init, HOOK_PRIO_INIT_PWM); diff --git a/chip/lm4/fan_chip.h b/chip/lm4/fan_chip.h deleted file mode 100644 index 0ff92b1740..0000000000 --- a/chip/lm4/fan_chip.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (c) 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. - */ - -/* LM4-specific fan control module */ - -#ifndef __CROS_EC_LM4_FAN_CHIP_H -#define __CROS_EC_LM4_FAN_CHIP_H - -void fan_chip_set_enabled(int ch, int enabled); -int fan_chip_get_enabled(int ch); -void fan_chip_set_duty(int ch, int percent); -int fan_chip_get_duty(enum pwm_channel ch); -int fan_chip_get_rpm_mode(int ch); -void fan_chip_set_rpm_mode(int ch, int rpm_mode); -int fan_chip_get_rpm_actual(int ch); -int fan_chip_get_rpm_target(int ch); -void fan_chip_set_rpm_target(int ch, int rpm); -int fan_chip_get_status(int ch); -int fan_chip_is_stalled(int ch); - -/* Maintain target RPM using tach input */ -#define FAN_CHIP_USE_RPM_MODE (1 << 0) -void fan_chip_channel_setup(int ch, unsigned int flags); - -#endif /* __CROS_EC_LM4_FAN_CHIP_H */ diff --git a/chip/lm4/pwm.c b/chip/lm4/pwm.c index 943aa0210f..c60a6be296 100644 --- a/chip/lm4/pwm.c +++ b/chip/lm4/pwm.c @@ -9,7 +9,7 @@ */ #include "clock.h" -#include "fan_chip.h" +#include "fan.h" #include "gpio.h" #include "hooks.h" #include "pwm.h" @@ -19,12 +19,12 @@ void pwm_enable(enum pwm_channel ch, int enabled) { - fan_chip_set_enabled(pwm_channels[ch].channel, enabled); + fan_set_enabled(pwm_channels[ch].channel, enabled); } int pwm_get_enabled(enum pwm_channel ch) { - return fan_chip_get_enabled(pwm_channels[ch].channel); + return fan_get_enabled(pwm_channels[ch].channel); } void pwm_set_duty(enum pwm_channel ch, int percent) @@ -42,12 +42,12 @@ void pwm_set_duty(enum pwm_channel ch, int percent) pwm_enable(ch, 1); /* Set the duty cycle */ - fan_chip_set_duty(pwm_channels[ch].channel, percent); + fan_set_duty(pwm_channels[ch].channel, percent); } int pwm_get_duty(enum pwm_channel ch) { - int percent = fan_chip_get_duty(pwm_channels[ch].channel); + int percent = fan_get_duty(pwm_channels[ch].channel); if (pwm_channels[ch].flags & PWM_CONFIG_ACTIVE_LOW) percent = 100 - percent; @@ -60,10 +60,10 @@ static void pwm_init(void) int i; for (i = 0; i < PWM_CH_COUNT; ++i) - fan_chip_channel_setup(pwm_channels[i].channel, + fan_channel_setup(pwm_channels[i].channel, (pwm_channels[i].flags & PWM_CONFIG_HAS_RPM_MODE) - ? FAN_CHIP_USE_RPM_MODE : 0); + ? FAN_USE_RPM_MODE : 0); } /* The chip-specific fan module initializes before this. */ diff --git a/common/build.mk b/common/build.mk index 15669a5dfe..98ef604e27 100644 --- a/common/build.mk +++ b/common/build.mk @@ -28,8 +28,7 @@ common-$(CONFIG_EXTPOWER_GPIO)+=extpower_gpio.o common-$(CONFIG_EXTPOWER_KIRBY)+=extpower_kirby.o common-$(CONFIG_EXTPOWER_SNOW)+=extpower_snow.o common-$(CONFIG_EXTPOWER_SPRING)+=extpower_spring.o -# TODO(rspangler): Rename to CONFIG_PWM_FAN. -common-$(CONFIG_FAN)+=pwm_fan.o +common-$(CONFIG_FAN)+=fan.o common-$(CONFIG_FLASH)+=flash.o common-$(CONFIG_FMAP)+=fmap.o common-$(CONFIG_I2C)+=i2c.o diff --git a/common/fan.c b/common/fan.c new file mode 100644 index 0000000000..635f541e02 --- /dev/null +++ b/common/fan.c @@ -0,0 +1,337 @@ +/* Copyright (c) 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 "common.h" +#include "console.h" +#include "fan.h" +#include "gpio.h" +#include "hooks.h" +#include "host_command.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; + +#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 pct) +{ + int rpm; + + if (!pct) + rpm = 0; + else + rpm = ((pct - 1) * CONFIG_FAN_RPM_MAX + + (100 - pct) * CONFIG_FAN_RPM_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 pct) +{ + int rpm; + + if (!thermal_control_enabled) + return; + + rpm = fan_percent_to_rpm(pct); + + fan_set_rpm_target(CONFIG_FAN_CH_CPU, rpm); +} + +static void set_enabled(int enable) +{ + fan_set_enabled(CONFIG_FAN_CH_CPU, enable); + +#ifdef CONFIG_FAN_EN_GPIO + gpio_set_level(CONFIG_FAN_EN_GPIO, enable); +#endif /* CONFIG_FAN_EN_GPIO */ +} + +static void set_thermal_control_enabled(int enable) +{ + thermal_control_enabled = enable; + + /* If controlling the fan, need it in RPM-control mode */ + if (enable) + fan_set_rpm_mode(CONFIG_FAN_CH_CPU, 1); +} + +static void set_duty_cycle(int percent) +{ + /* Move the fan to manual control */ + fan_set_rpm_mode(CONFIG_FAN_CH_CPU, 0); + + /* Always enable the fan */ + set_enabled(1); + + /* Disable thermal engine automatic fan control. */ + set_thermal_control_enabled(0); + + /* Set the duty cycle */ + fan_set_duty(CONFIG_FAN_CH_CPU, percent); +} + +/*****************************************************************************/ +/* Console commands */ + +static int cc_fanauto(int argc, char **argv) +{ + set_thermal_control_enabled(1); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(fanauto, cc_fanauto, + NULL, + "Enable thermal fan control", + NULL); + +static int cc_faninfo(int argc, char **argv) +{ + static const char * const human_status[] = { + "not spinning", "changing", "locked", "frustrated" + }; + int tmp; + + ccprintf("Actual: %4d rpm\n", + fan_get_rpm_actual(CONFIG_FAN_CH_CPU)); + ccprintf("Target: %4d rpm\n", + fan_get_rpm_target(CONFIG_FAN_CH_CPU)); + ccprintf("Duty: %d%%\n", + fan_get_duty(CONFIG_FAN_CH_CPU)); + tmp = fan_get_status(CONFIG_FAN_CH_CPU); + ccprintf("Status: %d (%s)\n", tmp, human_status[tmp]); + ccprintf("Mode: %s\n", + fan_get_rpm_mode(CONFIG_FAN_CH_CPU) ? "rpm" : "duty"); + ccprintf("Auto: %s\n", thermal_control_enabled ? "yes" : "no"); + ccprintf("Enable: %s\n", + fan_get_enabled(CONFIG_FAN_CH_CPU) ? "yes" : "no"); +#ifdef CONFIG_FAN_PGOOD_GPIO + ccprintf("Power: %s\n", +#ifdef CONFIG_FAN_EN_GPIO + gpio_get_level(CONFIG_FAN_EN_GPIO) && +#endif + gpio_get_level(CONFIG_FAN_PGOOD_GPIO) ? "yes" : "no"); +#endif + + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(faninfo, cc_faninfo, + NULL, + "Print fan info", + NULL); + +static int cc_fanset(int argc, char **argv) +{ + int rpm; + char *e; + + if (argc < 2) + return EC_ERROR_PARAM_COUNT; + + rpm = strtoi(argv[1], &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(rpm); + } else if (*e) { + return EC_ERROR_PARAM1; + } + + /* Move the fan to automatic control */ + fan_set_rpm_mode(CONFIG_FAN_CH_CPU, 1); + + /* Always enable the fan */ + set_enabled(1); + + /* Disable thermal engine automatic fan control. */ + set_thermal_control_enabled(0); + + fan_set_rpm_target(CONFIG_FAN_CH_CPU, rpm); + + ccprintf("Setting fan rpm target to %d\n", rpm); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(fanset, cc_fanset, + "rpm | pct%", + "Set fan speed", + NULL); + +static int cc_fanduty(int argc, char **argv) +{ + int percent = 0; + char *e; + + if (argc < 2) + return EC_ERROR_PARAM_COUNT; + + percent = strtoi(argv[1], &e, 0); + if (*e) + return EC_ERROR_PARAM1; + + ccprintf("Setting fan duty cycle to %d%%\n", percent); + set_duty_cycle(percent); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(fanduty, cc_fanduty, + "percent", + "Set fan duty cycle", + NULL); + +/*****************************************************************************/ +/* Host commands */ + +static int hc_pwm_get_fan_target_rpm(struct host_cmd_handler_args *args) +{ + struct ec_response_pwm_get_fan_rpm *r = args->response; + + r->rpm = fan_get_rpm_target(CONFIG_FAN_CH_CPU); + 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 int hc_pwm_set_fan_target_rpm(struct host_cmd_handler_args *args) +{ + const struct ec_params_pwm_set_fan_target_rpm *p = args->params; + + set_thermal_control_enabled(0); + fan_set_rpm_mode(CONFIG_FAN_CH_CPU, 1); + fan_set_rpm_target(CONFIG_FAN_CH_CPU, p->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)); + +static int hc_pwm_set_fan_duty(struct host_cmd_handler_args *args) +{ + const struct ec_params_pwm_set_fan_duty *p = args->params; + set_duty_cycle(p->percent); + + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_FAN_DUTY, + hc_pwm_set_fan_duty, + EC_VER_MASK(0)); + +static int hc_thermal_auto_fan_ctrl(struct host_cmd_handler_args *args) +{ + set_thermal_control_enabled(1); + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_THERMAL_AUTO_FAN_CTRL, + hc_thermal_auto_fan_ctrl, + EC_VER_MASK(0)); + + +/*****************************************************************************/ +/* Hooks */ + +#define PWMFAN_SYSJUMP_TAG 0x5046 /* "PF" */ +#define PWM_HOOK_VERSION 1 +/* Saved PWM state across sysjumps */ +struct pwm_fan_state { + uint16_t fan_rpm; + uint8_t fan_en; + char pad; /* Pad to multiple of 4 bytes. */ +}; + +static void pwm_fan_init(void) +{ + const struct pwm_fan_state *prev; + uint16_t *mapped; + int version, size; + int i; + + gpio_config_module(MODULE_PWM_FAN, 1); + + prev = (const struct pwm_fan_state *) + system_get_jump_tag(PWMFAN_SYSJUMP_TAG, &version, &size); + if (prev && version == PWM_HOOK_VERSION && size == sizeof(*prev)) { + /* Restore previous state. */ + fan_set_enabled(CONFIG_FAN_CH_CPU, prev->fan_en); + fan_set_rpm_target(CONFIG_FAN_CH_CPU, prev->fan_rpm); + } else { + /* Set initial fan speed to maximum */ + fan_set_duty(CONFIG_FAN_CH_CPU, 100); + } + + set_thermal_control_enabled(1); + + /* 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 + 1); + +static void pwm_fan_second(void) +{ + uint16_t *mapped = (uint16_t *)host_get_memmap(EC_MEMMAP_FAN); + + if (fan_is_stalled(CONFIG_FAN_CH_CPU)) { + mapped[0] = EC_FAN_SPEED_STALLED; + /* + * Issue warning. As we have thermal shutdown + * protection, issuing warning here should be enough. + */ + host_set_single_event(EC_HOST_EVENT_THERMAL); + cprintf(CC_PWM, "[%T Fan stalled!]\n"); + } else { + mapped[0] = fan_get_rpm_actual(CONFIG_FAN_CH_CPU); + } +} +DECLARE_HOOK(HOOK_SECOND, pwm_fan_second, HOOK_PRIO_DEFAULT); + +static void pwm_fan_preserve_state(void) +{ + struct pwm_fan_state state; + + state.fan_en = fan_get_enabled(CONFIG_FAN_CH_CPU); + state.fan_rpm = fan_get_rpm_target(CONFIG_FAN_CH_CPU); + + 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_resume(void) +{ + fan_set_enabled(CONFIG_FAN_CH_CPU, 1); +} +DECLARE_HOOK(HOOK_CHIPSET_RESUME, pwm_fan_resume, HOOK_PRIO_DEFAULT); + +static void pwm_fan_S3_S5(void) +{ + /* Take back fan control when the processor shuts down */ + set_thermal_control_enabled(1); + /* For now don't do anything with it. We'll have to turn it on again if + * we need active cooling during heavy battery charging or something. + */ + fan_set_rpm_target(CONFIG_FAN_CH_CPU, 0); + fan_set_enabled(CONFIG_FAN_CH_CPU, 0); /* crosbug.com/p/8097 */ +} +DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pwm_fan_S3_S5, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pwm_fan_S3_S5, HOOK_PRIO_DEFAULT); diff --git a/common/pwm_fan.c b/common/pwm_fan.c deleted file mode 100644 index f84159a7ad..0000000000 --- a/common/pwm_fan.c +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (c) 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. - */ - -#include "common.h" -#include "fan.h" - -#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 pwm_fan_percent_to_rpm(int pct) -{ - int rpm; - - if (!pct) - rpm = 0; - else - rpm = ((pct - 1) * CONFIG_FAN_RPM_MAX + - (100 - pct) * CONFIG_FAN_RPM_MIN) / 99; - - return rpm; -} -#endif /* CONFIG_FAN_RPM_CUSTOM */ diff --git a/common/thermal.c b/common/thermal.c index 26775f62eb..6d4708c0ef 100644 --- a/common/thermal.c +++ b/common/thermal.c @@ -156,7 +156,7 @@ static void thermal_control(void) #ifdef CONFIG_FAN /* Max fan needed is what's needed. */ - pwm_fan_set_percent_needed(fmax); + fan_set_percent_needed(fmax); #endif } diff --git a/include/config.h b/include/config.h index a82af12158..6df1558e19 100644 --- a/include/config.h +++ b/include/config.h @@ -351,7 +351,7 @@ /* * Replace the default fan mapping with a board-specific function in board.c: * - * int pwm_fan_percent_to_rpm(int pct); + * int fan_percent_to_rpm(int pct); * */ #undef CONFIG_FAN_RPM_CUSTOM diff --git a/include/fan.h b/include/fan.h index 7af956baae..acae150fc0 100644 --- a/include/fan.h +++ b/include/fan.h @@ -14,7 +14,7 @@ * * @param pct Percentage of cooling effort needed (0 - 100) */ -void pwm_fan_set_percent_needed(int pct); +void fan_set_percent_needed(int pct); /** * This function translates the percentage of cooling needed into a target RPM. @@ -24,6 +24,34 @@ void pwm_fan_set_percent_needed(int pct); * @param pct Percentage of cooling effort needed (always in [0,100]) * Return Target RPM for fan */ -int pwm_fan_percent_to_rpm(int pct); +int fan_percent_to_rpm(int pct); + + +/** + * These functions require chip-specific implementations. + */ + +void fan_set_enabled(int ch, int enabled); +int fan_get_enabled(int ch); +void fan_set_duty(int ch, int percent); +int fan_get_duty(int ch); +void fan_set_rpm_mode(int ch, int rpm_mode); +int fan_get_rpm_mode(int ch); +void fan_set_rpm_target(int ch, int rpm); +int fan_get_rpm_actual(int ch); +int fan_get_rpm_target(int ch); +int fan_is_stalled(int ch); + +enum fan_status { + FAN_STATUS_STOPPED = 0, + FAN_STATUS_CHANGING = 1, + FAN_STATUS_LOCKED = 2, + FAN_STATUS_FRUSTRATED = 3 +}; +enum fan_status fan_get_status(int ch); + +/* Maintain target RPM using tach input */ +#define FAN_USE_RPM_MODE (1 << 0) +void fan_channel_setup(int ch, unsigned int flags); #endif /* __CROS_EC_FAN_H */ diff --git a/test/test_config.h b/test/test_config.h index 53144073e4..c18700e25e 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -52,6 +52,7 @@ int board_discharge_on_ac(int enabled); #ifdef TEST_thermal #define CONFIG_CHIPSET_CAN_THROTTLE #define CONFIG_FAN +#define CONFIG_FAN_CH_CPU 0 #define CONFIG_FAN_RPM_MAX 5000 #define CONFIG_FAN_RPM_MIN 1000 #define CONFIG_TEMP_SENSOR @@ -65,6 +66,7 @@ int board_discharge_on_ac(int enabled); #define CONFIG_CHIPSET_CAN_THROTTLE #define CONFIG_EXTPOWER_FALCO #define CONFIG_FAN +#define CONFIG_FAN_CH_CPU 0 #define CONFIG_FAN_RPM_MAX 5000 #define CONFIG_FAN_RPM_MIN 1000 #define CONFIG_TEMP_SENSOR diff --git a/test/thermal.c b/test/thermal.c index fb10b1cd1e..deaac2f631 100644 --- a/test/thermal.c +++ b/test/thermal.c @@ -62,7 +62,7 @@ void host_throttle_cpu(int throttled) host_throttled = throttled; } -void pwm_fan_set_percent_needed(int pct) +void fan_set_percent_needed(int pct) { fan_pct = pct; } diff --git a/test/thermal_falco.c b/test/thermal_falco.c index 87a00f57bb..1452a12692 100644 --- a/test/thermal_falco.c +++ b/test/thermal_falco.c @@ -81,7 +81,7 @@ void host_throttle_cpu(int throttled) host_throttled = throttled; } -void pwm_fan_set_percent_needed(int pct) +void fan_set_percent_needed(int pct) { fan_pct = pct; } -- cgit v1.2.1