diff options
Diffstat (limited to 'chip/lm4/fan_chip.c')
-rw-r--r-- | chip/lm4/fan_chip.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/chip/lm4/fan_chip.c b/chip/lm4/fan_chip.c new file mode 100644 index 0000000000..cef39b9b3d --- /dev/null +++ b/chip/lm4/fan_chip.c @@ -0,0 +1,180 @@ +/* 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); |