| /* 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. |
| */ |
| |
| /* MEC1322 fan control module. */ |
| |
| /* This assumes 2-pole fan. For each rotation, 5 edges are measured. */ |
| |
| #include "fan.h" |
| #include "registers.h" |
| #include "util.h" |
| |
| /* Maximum tach reading/target value */ |
| #define MAX_TACH 0x1fff |
| |
| /* Tach target value for disable fan */ |
| #define FAN_OFF_TACH 0xfff8 |
| |
| /* |
| * RPM = (n - 1) * m * f * 60 / poles / TACH |
| * n = number of edges = 5 |
| * m = multiplier defined by RANGE = 2 in our case |
| * f = 32.768K |
| * poles = 2 |
| */ |
| #define RPM_TO_TACH(rpm) MIN((7864320 / MAX((rpm), 1)), MAX_TACH) |
| #define TACH_TO_RPM(tach) (7864320 / MAX((tach), 1)) |
| |
| static int rpm_setting; |
| static int duty_setting; |
| static int in_rpm_mode = 1; |
| |
| |
| static void clear_status(void) |
| { |
| /* Clear DRIVE_FAIL, FAN_SPIN, and FAN_STALL bits */ |
| MEC1322_FAN_STATUS = 0x23; |
| } |
| |
| void fan_set_enabled(int ch, int enabled) |
| { |
| if (in_rpm_mode) { |
| if (enabled) |
| fan_set_rpm_target(ch, rpm_setting); |
| else |
| MEC1322_FAN_TARGET = FAN_OFF_TACH; |
| } else { |
| if (enabled) |
| fan_set_duty(ch, duty_setting); |
| else |
| MEC1322_FAN_SETTING = 0; |
| } |
| clear_status(); |
| } |
| |
| int fan_get_enabled(int ch) |
| { |
| if (in_rpm_mode) |
| return (MEC1322_FAN_TARGET & 0xff00) != 0xff00; |
| else |
| return !!MEC1322_FAN_SETTING; |
| } |
| |
| void fan_set_duty(int ch, int percent) |
| { |
| if (percent < 0) |
| percent = 0; |
| else if (percent > 100) |
| percent = 100; |
| |
| duty_setting = percent; |
| MEC1322_FAN_SETTING = percent * 255 / 100; |
| clear_status(); |
| } |
| |
| int fan_get_duty(int ch) |
| { |
| return duty_setting; |
| } |
| |
| int fan_get_rpm_mode(int ch) |
| { |
| return !!(MEC1322_FAN_CFG1 & (1 << 7)); |
| } |
| |
| void fan_set_rpm_mode(int ch, int rpm_mode) |
| { |
| if (rpm_mode) |
| MEC1322_FAN_CFG1 |= 1 << 7; |
| else |
| MEC1322_FAN_CFG1 &= ~(1 << 7); |
| clear_status(); |
| } |
| |
| int fan_get_rpm_actual(int ch) |
| { |
| if ((MEC1322_FAN_READING >> 8) == 0xff) |
| return 0; |
| else |
| return TACH_TO_RPM(MEC1322_FAN_READING >> 3); |
| } |
| |
| int fan_get_rpm_target(int ch) |
| { |
| return rpm_setting; |
| } |
| |
| void fan_set_rpm_target(int ch, int rpm) |
| { |
| rpm_setting = rpm; |
| MEC1322_FAN_TARGET = RPM_TO_TACH(rpm) << 3; |
| clear_status(); |
| } |
| |
| enum fan_status fan_get_status(int ch) |
| { |
| uint8_t sts = MEC1322_FAN_STATUS; |
| |
| if (sts & ((1 << 5) | (1 << 1))) |
| return FAN_STATUS_FRUSTRATED; |
| if (fan_get_rpm_actual(ch) == 0) |
| return FAN_STATUS_STOPPED; |
| return FAN_STATUS_LOCKED; |
| } |
| |
| int fan_is_stalled(int ch) |
| { |
| uint8_t sts = MEC1322_FAN_STATUS; |
| if (fan_get_rpm_actual(ch)) { |
| MEC1322_FAN_STATUS = 0x1; |
| return 0; |
| } |
| return sts & 0x1; |
| } |
| |
| void fan_channel_setup(int ch, unsigned int flags) |
| { |
| /* |
| * Fan configuration 1 register: |
| * 0x80 = bit 7 = RPM mode (0x00 if FAN_USE_RPM_MODE not set) |
| * 0x20 = bits 6:5 = min 1000 RPM, multiplier = 2 |
| * 0x08 = bits 4:3 = 5 edges, 2 poles |
| * 0x03 = bits 2:0 = 400 ms update time |
| * |
| * Fan configuration 2 register: |
| * 0x00 = bit 6 = Ramp control disabled |
| * 0x00 = bit 5 = Glitch filter enabled |
| * 0x18 = bits 4:3 = Using both derivative options |
| * 0x02 = bits 2:1 = error range is 50 RPM |
| * 0x00 = bits 0 = normal polarity |
| */ |
| if (flags & FAN_USE_RPM_MODE) |
| MEC1322_FAN_CFG1 = 0xab; |
| else |
| MEC1322_FAN_CFG1 = 0x2b; |
| MEC1322_FAN_CFG2 = 0x1a; |
| clear_status(); |
| } |