summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMulin Chao <mlchao@nuvoton.com>2017-04-10 12:00:55 +0800
committerchrome-bot <chrome-bot@chromium.org>2017-04-18 20:49:50 -0700
commit31efc1e864da2390d98abda1fedc479448e75942 (patch)
tree7748e534ca65e255648bc6ae2db2ce91094427a5
parenteca98d30ff6b6bd66d86862b0233779e51e8cdeb (diff)
downloadchrome-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.c140
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.