diff options
author | Mulin Chao <mlchao@nuvoton.com> | 2017-04-10 12:00:55 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-04-18 20:49:50 -0700 |
commit | 31efc1e864da2390d98abda1fedc479448e75942 (patch) | |
tree | 7748e534ca65e255648bc6ae2db2ce91094427a5 | |
parent | eca98d30ff6b6bd66d86862b0233779e51e8cdeb (diff) | |
download | chrome-ec-31efc1e864da2390d98abda1fedc479448e75942.tar.gz |
npcx: fan: Simplified TACH_TO_RPM formula and fixed bugs.
This CL simplified TACH_TO_RPM formula and abstracted two definitions
(PULSES_ROUND and RPM_DEVIATION) for the pwm-type fan used on boards.
The developers can modify them in board-level driver if fan doesn't meet
the default spec.
In this CL, it also fixed:
1. Declare rpm_pre as array if FAN_CH_COUNT > 1.
2. Add checking for the value of next duty of pwm.
3. Use TAPND pending bit to make sure TCRA is valid.
BRANCH=none
BUG=none
TEST=test fan used in kahlee and Sunon fan with fanset command on
npcx_evb and use faninfo for verifying. Measure the actual rpm by scope.
Change-Id: Ieb07482eb359912286414ccb9738341d98ea99e4
Signed-off-by: Mulin Chao <mlchao@nuvoton.com>
Reviewed-on: https://chromium-review.googlesource.com/472289
Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r-- | chip/npcx/fan.c | 140 |
1 files changed, 92 insertions, 48 deletions
diff --git a/chip/npcx/fan.c b/chip/npcx/fan.c index 84cbd0e6f3..ca0980a91a 100644 --- a/chip/npcx/fan.c +++ b/chip/npcx/fan.c @@ -73,29 +73,38 @@ struct fan_status_t { /* Global variables */ static volatile struct fan_status_t fan_status[FAN_CH_COUNT]; +static int rpm_pre[FAN_CH_COUNT]; -/* Fan encoder spec. */ -#define RPM_SCALE 1 /* Fan RPM is multiplier of actual RPM */ -#define RPM_EDGES 1 /* Fan number of edges - 1 */ -#define POLES 2 /* Pole number of fan */ -/* Rounds per second */ -#define ROUNDS ((60 / POLES) * RPM_EDGES * RPM_SCALE) /* - * RPM = (n - 1) * m * f * 60 / poles / TACH - * n = Fan number of edges = (RPM_EDGES + 1) - * m = Fan multiplier defined by RANGE - * f = PWM and MFT operation freq - * poles = 2 + * Fan specifications. If they (PULSES_ROUND and RPM_DEVIATION) cannot meet + * the followings, please replace them with correct one in board-level driver. + */ + +/* Pulses per round */ +#ifndef PULSES_ROUND +#define PULSES_ROUND 2 /* 4-phases pwm-type fan. (2-phases should be 1) */ +#endif + +/* Rpm deviation (Unit:percent) */ +#ifndef RPM_DEVIATION +#define RPM_DEVIATION 7 +#endif + +/* + * RPM = 60 * f / (n * TACH) + * n = Pulses per round + * f = Tachometer (MFT) operation freq + * TACH = Counts of tachometer */ #define TACH_TO_RPM(ch, tach) \ - ((fan_status[ch].mft_freq * ROUNDS) / MAX((tach), 1)) + ((fan_status[ch].mft_freq * 60 / PULSES_ROUND) / MAX((tach), 1)) /* MFT TCNT default count */ -#define TACHO_MAX_CNT ((1<<16)-1) +#define TACHO_MAX_CNT ((1 << 16) - 1) + +/* Margin of target rpm */ +#define RPM_MARGIN(rpm_target) (((rpm_target) * RPM_DEVIATION) / 100) -/* Smart fan control settings */ -#define RPM_MARGIN_PERCENT 3 -#define RPM_MARGIN_THRES(rpm_target) (((rpm_target)*RPM_MARGIN_PERCENT)/100) /** * MFT get fan rpm value * @@ -113,8 +122,6 @@ static int mft_fan_rpm(int ch) /* Clear pending flags */ SET_BIT(NPCX_TECLR(mdl), NPCX_TECLR_TCCLR); - /* Need to avoid underflow state happen */ - p_status->rpm_actual = 0; /* * Flag TDPND means mft underflow happen, * but let MFT still can re-measure actual rpm @@ -128,6 +135,13 @@ static int mft_fan_rpm(int ch) return 0; } + /* Check whether MFT capture flag is set, else return previous rpm */ + if (IS_BIT_SET(NPCX_TECTRL(mdl), NPCX_TECTRL_TAPND)) + /* Clear pending flags */ + SET_BIT(NPCX_TECLR(mdl), NPCX_TECLR_TACLR); + else + return p_status->rpm_actual; + p_status->cur_state = TACHO_NORMAL; /* * Start of the last tacho cycle is detected - @@ -224,6 +238,55 @@ static void fan_config(int ch, int enable_mft_read_rpm) } /** + * Check all fans are stopped + * + * @return 1: all fans are stopped. 0: else. + */ +static int fan_all_disabled(void) +{ + int ch; + + for (ch = 0; ch < CONFIG_FANS; ch++) + if (fan_status[ch].auto_status != FAN_STATUS_STOPPED) + return 0; + return 1; +} + +/** + * Adjust fan duty by difference between target and actual rpm + * + * @param ch operation channel + * @param rpm_diff difference between target and actual rpm + * @param duty current fan duty + */ +static void fan_adjust_duty(int ch, int rpm_diff, int duty) +{ + int duty_step = 0; + + /* Find suitable duty step */ + if (ABS(rpm_diff) >= 2000) + duty_step = 20; + else if (ABS(rpm_diff) >= 1000) + duty_step = 10; + else if (ABS(rpm_diff) >= 500) + duty_step = 5; + else if (ABS(rpm_diff) >= 250) + duty_step = 3; + else + duty_step = 1; + + /* Adjust fan duty step by step */ + if (rpm_diff > 0) + duty = MIN(duty + duty_step, 100); + else + duty = MAX(duty - duty_step, 1); + + fan_set_duty(ch, duty); + + CPRINTS("fan%d: duty %d, rpm_diff %d", ch, duty, rpm_diff); +} + +/** * Smart fan control function. * * @param ch operation channel @@ -233,53 +296,36 @@ static void fan_config(int ch, int enable_mft_read_rpm) */ enum fan_status fan_smart_control(int ch, int rpm_actual, int rpm_target) { - static int rpm_pre; - int duty, duty_diff, rpm_diff; + int duty, rpm_diff; /* wait rpm is stable */ - if (ABS(rpm_actual - rpm_pre) > RPM_MARGIN_THRES(rpm_target)/2) { - rpm_pre = rpm_actual; + if (ABS(rpm_actual - rpm_pre[ch]) > RPM_MARGIN(rpm_actual)) { + rpm_pre[ch] = rpm_actual; return FAN_STATUS_CHANGING; } /* Record previous rpm */ - rpm_pre = rpm_actual; - - /* Find suitable duty step */ - rpm_diff = rpm_target - rpm_actual; - if (ABS(rpm_diff) >= 2000) - duty_diff = 20; - else if (ABS(rpm_diff) >= 1000) - duty_diff = 10; - else if (ABS(rpm_diff) >= 500) - duty_diff = 5; - else if (ABS(rpm_diff) >= 250) - duty_diff = 3; - else - duty_diff = 1; + rpm_pre[ch] = rpm_actual; /* Adjust PWM duty */ + rpm_diff = rpm_target - rpm_actual; duty = fan_get_duty(ch); if (duty == 0 && rpm_target == 0) return FAN_STATUS_STOPPED; /* Increase PWM duty */ - if (rpm_diff > RPM_MARGIN_THRES(rpm_target)) { + if (rpm_diff > RPM_MARGIN(rpm_target)) { if (duty == 100) return FAN_STATUS_FRUSTRATED; - fan_set_duty(ch, duty + duty_diff); - CPRINTS("duty=%d, threshold %d, rpm target %d, actual %d", duty, - RPM_MARGIN_THRES(rpm_target), rpm_target, rpm_actual); + fan_adjust_duty(ch, rpm_diff, duty); return FAN_STATUS_CHANGING; /* Decrease PWM duty */ - } else if (rpm_diff < -RPM_MARGIN_THRES(rpm_target)) { + } else if (rpm_diff < -RPM_MARGIN(rpm_target)) { if (duty == 1 && rpm_target != 0) return FAN_STATUS_FRUSTRATED; - fan_set_duty(ch, duty - duty_diff); - CPRINTS("duty=%d, threshold %d, rpm target %d, actual %d", duty, - RPM_MARGIN_THRES(rpm_target), rpm_target, rpm_actual); + fan_adjust_duty(ch, rpm_diff, duty); return FAN_STATUS_CHANGING; } @@ -328,16 +374,14 @@ void fan_set_duty(int ch, int percent) /* duty is zero */ if (!percent) { fan_status[ch].auto_status = FAN_STATUS_STOPPED; - enable_sleep(SLEEP_MASK_FAN); + if (fan_all_disabled()) + enable_sleep(SLEEP_MASK_FAN); } else disable_sleep(SLEEP_MASK_FAN); /* Set the duty cycle of PWM */ pwm_set_duty(pwm_id, percent); } -/* ensure that only one fan used since ec enables sleep bit if duty is zero */ -BUILD_ASSERT(CONFIG_FANS <= 1); - /** * Get fan duty cycle. |