mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-09 00:51:29 +00:00
Add npcx_evb_arm board-level driver for arm-based platform. Add header.c: for booting from NPCX5M5G A3 Booter. Remove lfw folder due to those functionalitie have been replaced with Booter Modified drivers for Patch Set 1: 1. flash.c: Implement UMA lock, tri-state and selection register lock functionalities 2. hwtimer.c: Add ITIM32 for hwtimer 3. lpc.c: Add checking for LRESET 4. system.c: Modified CODERAM_ARCH functions for NPCX5M5G A3 Booter. 5. uart.c: Add support for module 2 Patch Set 2: 6. lpc.c: Modified lpc_get_pltrst_asserted() func Patch Set 3: 7. minimize the changes for CONFIG_CODERAM_ARCH in common layer 8. comments of Patch Set1/2 Patch Set 4: 9. Modified CONFIG_RO_MEM_OFF point to ro image and keep header as a part of ec.RO.flat. 10. Fixed RO_FRID and RW_FRID issues which caused by CONFIG_CODERAM_ARCH. Patch Set 5: 11. Modified system.c in common folder for supporting *_STORAGE_OFF. 12. Use *_STORAGE_OFF in firmware_image.lds.S to indicate flat file layout in flash. Patch Set 6: 13. rebase to newest version 14. system.c: Modified for the newest include/system.h Patch Set 7: 15. Merge from version 0625 BUG=chrome-os-partner:34346 TEST=make buildall -j; test nuvoton IC specific drivers BRANCH=none Change-Id: Ifd7c10b81b5781ccd75bb2558dc236486976e8ed Signed-off-by: Ian Chao <mlchao@nuvoton.com> Reviewed-on: https://chromium-review.googlesource.com/272034 Reviewed-by: Shawn N <shawnn@chromium.org> Tested-by: Shawn N <shawnn@chromium.org> Commit-Queue: Shawn N <shawnn@chromium.org>
617 lines
16 KiB
C
617 lines
16 KiB
C
/* Copyright (c) 2014 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.
|
|
*/
|
|
|
|
/* NPCX fan control module. */
|
|
|
|
#include "clock.h"
|
|
#include "clock_chip.h"
|
|
#include "fan.h"
|
|
#include "fan_chip.h"
|
|
#include "gpio.h"
|
|
#include "hooks.h"
|
|
#include "registers.h"
|
|
#include "util.h"
|
|
#include "pwm.h"
|
|
#include "pwm_chip.h"
|
|
#include "console.h"
|
|
#include "timer.h"
|
|
#include "task.h"
|
|
|
|
#if !(DEBUG_FAN)
|
|
#define CPRINTS(...)
|
|
#else
|
|
#define CPRINTS(format, args...) cprints(CC_PWM, format, ## args)
|
|
#endif
|
|
|
|
/* Fan operation module */
|
|
enum npcx_fan_op_module {
|
|
NPCX_FAN_OP_PWM,
|
|
NPCX_FAN_OP_MFT,
|
|
/* Number of FAN module operations */
|
|
NPCX_FAN_OP_COUNT
|
|
};
|
|
|
|
/* MFT model select */
|
|
enum npcx_mft_mdsel {
|
|
NPCX_MFT_MDSEL_1,
|
|
NPCX_MFT_MDSEL_2,
|
|
NPCX_MFT_MDSEL_3,
|
|
NPCX_MFT_MDSEL_4,
|
|
NPCX_MFT_MDSEL_5,
|
|
/* Number of MFT modes */
|
|
NPCX_MFT_MDSEL_COUNT
|
|
};
|
|
|
|
/* MFT clock source */
|
|
enum npcx_mft_clk_src {
|
|
TCKC_NOCLK = 0,
|
|
TCKC_PRESCALE_APB1_CLK,
|
|
TCKC_EXTERNAL,
|
|
TCKC_PULSE_ACC,
|
|
TCKC_LFCLK
|
|
};
|
|
|
|
#define RPM_SCALE 1 /* Fan RPM is multiplier of actual RPM */
|
|
#define RPM_EDGES 1 /* Fan number of edges - 1 */
|
|
/*
|
|
* 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
|
|
*/
|
|
#define RPM_TO_TACH(pwm_channel, rpm) \
|
|
MIN(((uint32_t)(pwm_channels[pwm_channel].freq) \
|
|
*(pwm_channels[pwm_channel].cycle_pulses)*30*RPM_EDGES*RPM_SCALE \
|
|
/MAX((rpm), 1)), (pwm_channels[pwm_channel].cycle_pulses))
|
|
|
|
#define TACH_TO_RPM(mft_channel, tach) \
|
|
((mft_channels[mft_channel].freq)*30*RPM_EDGES*RPM_SCALE \
|
|
/MAX((tach), 1))
|
|
|
|
/* Global variables */
|
|
static volatile struct tacho_status_t tacho_status;
|
|
static int rpm_target;
|
|
static int pre_duty;
|
|
static int rpm_actual = -1;
|
|
static int fan_init_ch;
|
|
/**
|
|
* Select fan operation channel by module.
|
|
*
|
|
* @param none
|
|
* @param op_module npcx operation module
|
|
* @return npcx operation channel by module
|
|
* @notes Fan is controlled by PWM/MFT module in npcx chip
|
|
*/
|
|
static int fan_op_ch(int ch, enum npcx_fan_op_module op_module)
|
|
{
|
|
uint8_t op_ch;
|
|
|
|
switch (ch) {
|
|
case 0:
|
|
if (op_module == NPCX_FAN_OP_PWM)
|
|
op_ch = PWM_CH_FAN;
|
|
else
|
|
op_ch = MFT_CH_0;
|
|
break;
|
|
default:
|
|
op_ch = 0;
|
|
break;
|
|
}
|
|
return op_ch;
|
|
}
|
|
|
|
/**
|
|
* MFT start measure.
|
|
*
|
|
* @param ch operation channel
|
|
* @return none
|
|
*/
|
|
static void mft_startmeasure(int ch)
|
|
{
|
|
int mft_ch = fan_op_ch(ch, NPCX_FAN_OP_MFT);
|
|
|
|
/* Start measurement */
|
|
#ifdef NPCX_MFT_INPUT_LFCLK
|
|
/* Set the LFCLK clock. */
|
|
if (NPCX_MFT_MODULE_PORT_TB == mft_channels[mft_ch].port)
|
|
NPCX_TCKC(mft_channels[mft_ch].module) =
|
|
(NPCX_TCKC(mft_channels[mft_ch].module)
|
|
&(~(((1<<3)-1)<<NPCX_TCKC_C2CSEL)))
|
|
|(TCKC_LFCLK<<NPCX_TCKC_C2CSEL);
|
|
else
|
|
NPCX_TCKC(mft_channels[mft_ch].module) =
|
|
(NPCX_TCKC(mft_channels[mft_ch].module)
|
|
&(~(((1<<3)-1)<<NPCX_TCKC_C1CSEL)))
|
|
|(TCKC_LFCLK<<NPCX_TCKC_C1CSEL);
|
|
#else
|
|
/* Set the core clock. */
|
|
if (NPCX_MFT_MODULE_PORT_TB == mft_channels[mft_ch].port)
|
|
NPCX_TCKC(mft_channels[mft_ch].module) =
|
|
(NPCX_TCKC(mft_channels[mft_ch].module)
|
|
&(~(((1<<3)-1)<<NPCX_TCKC_C2CSEL)))
|
|
|(TCKC_PRESCALE_APB1_CLK<<NPCX_TCKC_C2CSEL);
|
|
else
|
|
NPCX_TCKC(mft_channels[mft_ch].module) =
|
|
(NPCX_TCKC(mft_channels[mft_ch].module)
|
|
&(~(((1<<3)-1)<<NPCX_TCKC_C1CSEL)))
|
|
|(TCKC_PRESCALE_APB1_CLK<<NPCX_TCKC_C1CSEL);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* MFT stop measure.
|
|
*
|
|
* @param ch operation channel
|
|
* @return none
|
|
*/
|
|
static void mft_stopmeasure(int ch)
|
|
{
|
|
int mft_ch = fan_op_ch(ch, NPCX_FAN_OP_MFT);
|
|
|
|
/* Clear all pending flag */
|
|
NPCX_TECLR(mft_channels[mft_ch].module) =
|
|
NPCX_TECTRL(mft_channels[mft_ch].module);
|
|
|
|
/* Stop the timer */
|
|
if (NPCX_MFT_MODULE_PORT_TB == mft_channels[mft_ch].port)
|
|
NPCX_TCKC(mft_channels[mft_ch].module) =
|
|
(NPCX_TCKC(mft_channels[mft_ch].module)
|
|
&(~(((1<<3)-1)<<NPCX_TCKC_C2CSEL)))
|
|
|(TCKC_NOCLK<<NPCX_TCKC_C2CSEL);
|
|
else
|
|
NPCX_TCKC(mft_channels[mft_ch].module) =
|
|
(NPCX_TCKC(mft_channels[mft_ch].module)
|
|
&(~(((1<<3)-1)<<NPCX_TCKC_C1CSEL)))
|
|
|(TCKC_NOCLK<<NPCX_TCKC_C1CSEL);
|
|
}
|
|
|
|
/**
|
|
* MFT final measure.
|
|
*
|
|
* @param ch operation channel
|
|
* @return none
|
|
*/
|
|
static void mft_finalmeasure(int ch)
|
|
{
|
|
int mft_ch = fan_op_ch(ch, NPCX_FAN_OP_MFT);
|
|
|
|
/*
|
|
* Start of the last tacho cycle is detected -
|
|
* calculated tacho cycle duration
|
|
*/
|
|
if (NPCX_MFT_MODULE_PORT_TB == mft_channels[mft_ch].port)
|
|
tacho_status.edge_interval =
|
|
(uint32_t)(mft_channels[mft_ch].default_count
|
|
- NPCX_TCRB(mft_channels[mft_ch].module));
|
|
else
|
|
tacho_status.edge_interval =
|
|
(uint32_t)(mft_channels[mft_ch].default_count
|
|
- NPCX_TCRA(mft_channels[mft_ch].module));
|
|
}
|
|
|
|
/**
|
|
* Preset fan operation clock.
|
|
*
|
|
* @param none
|
|
* @return none
|
|
* @notes changed when initial or HOOK_FREQ_CHANGE command
|
|
*/
|
|
#ifndef NPCX_MFT_INPUT_LFCLK
|
|
void mft_freq_changed(void)
|
|
{
|
|
uint16_t prescaler_divider = 0;
|
|
int mft_ch = fan_op_ch(fan_init_ch, NPCX_FAN_OP_MFT);
|
|
|
|
/* Set clock prescaler divider to MFT module*/
|
|
prescaler_divider = (uint16_t)(clock_get_apb1_freq()
|
|
/mft_channels[mft_ch].freq);
|
|
if (prescaler_divider >= 1)
|
|
prescaler_divider = prescaler_divider - 1;
|
|
if (prescaler_divider > 0xFF)
|
|
prescaler_divider = 0xFF;
|
|
|
|
NPCX_TPRSC(mft_channels[mft_ch].module) = (uint8_t)prescaler_divider;
|
|
}
|
|
DECLARE_HOOK(HOOK_FREQ_CHANGE, mft_freq_changed, HOOK_PRIO_DEFAULT);
|
|
#endif
|
|
|
|
/**
|
|
* Fan configuration.
|
|
*
|
|
* @param ch operation channel
|
|
* @param enable_mft_read_rpm FAN_USE_RPM_MODE enable flag
|
|
* @return none
|
|
*/
|
|
static void fan_config(int ch, int enable_mft_read_rpm)
|
|
{
|
|
int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM);
|
|
int mft_ch = fan_op_ch(ch, NPCX_FAN_OP_MFT);
|
|
|
|
fan_init_ch = ch;
|
|
pwm_config(pwm_ch);
|
|
|
|
/* Configure pins from GPIOs to FAN */
|
|
gpio_config_module(MODULE_PWM_FAN, 1);
|
|
|
|
if (enable_mft_read_rpm) {
|
|
/* Set mode 5 to MFT module*/
|
|
NPCX_TMCTRL(mft_channels[mft_ch].module) =
|
|
(NPCX_TMCTRL(mft_channels[mft_ch].module)
|
|
& (~(((1<<3)-1)<<NPCX_TMCTRL_MDSEL)))
|
|
| (NPCX_MFT_MDSEL_5<<NPCX_TMCTRL_MDSEL);
|
|
|
|
#ifndef NPCX_MFT_INPUT_LFCLK
|
|
/* Set MFT operation frequence */
|
|
mft_freq_changed();
|
|
/* Set the active power mode. */
|
|
CLEAR_BIT(NPCX_TCKC(mft_channels[mft_ch].module),
|
|
NPCX_TCKC_LOW_PWR);
|
|
#else
|
|
/* Set the low power mode. */
|
|
SET_BIT(NPCX_TCKC(mft_channels[mft_ch].module),
|
|
NPCX_TCKC_LOW_PWR);
|
|
#endif
|
|
if (NPCX_MFT_MODULE_PORT_TB == mft_channels[mft_ch].port) {
|
|
/* Set the default count-down timer. */
|
|
NPCX_TCNT2(mft_channels[mft_ch].module) =
|
|
mft_channels[mft_ch].default_count;
|
|
NPCX_TCRB(mft_channels[mft_ch].module) =
|
|
mft_channels[mft_ch].default_count;
|
|
/* Set the edge polarity to rising. */
|
|
SET_BIT(NPCX_TMCTRL(mft_channels[mft_ch].module),
|
|
NPCX_TMCTRL_TBEDG);
|
|
/* Enable capture TCNT2 into TCRB and preset TCNT2. */
|
|
SET_BIT(NPCX_TMCTRL(mft_channels[mft_ch].module),
|
|
NPCX_TMCTRL_TBEN);
|
|
/* Enable input debounce logic into TB. */
|
|
SET_BIT(NPCX_TCFG(mft_channels[mft_ch].module),
|
|
NPCX_TCFG_TBDBEN);
|
|
/* Set the no clock to TCNT2. */
|
|
NPCX_TCKC(mft_channels[mft_ch].module) =
|
|
(NPCX_TCKC(mft_channels[mft_ch].module)
|
|
& (~(((1<<3)-1)<<NPCX_TCKC_C2CSEL)))
|
|
| (TCKC_NOCLK<<NPCX_TCKC_C2CSEL);
|
|
/* Set timer wake-up enable */
|
|
SET_BIT(NPCX_TWUEN(mft_channels[mft_ch].module),
|
|
NPCX_TWUEN_TBWEN);
|
|
SET_BIT(NPCX_TWUEN(mft_channels[mft_ch].module),
|
|
NPCX_TWUEN_TDWEN);
|
|
} else {
|
|
/* Set the default count-down timer. */
|
|
NPCX_TCNT1(mft_channels[mft_ch].module) =
|
|
mft_channels[mft_ch].default_count;
|
|
NPCX_TCRA(mft_channels[mft_ch].module) =
|
|
mft_channels[mft_ch].default_count;
|
|
/* Set the edge polarity to rising. */
|
|
SET_BIT(NPCX_TMCTRL(mft_channels[mft_ch].module),
|
|
NPCX_TMCTRL_TAEDG);
|
|
/* Enable capture TCNT1 into TCRA and preset TCNT1. */
|
|
SET_BIT(NPCX_TMCTRL(mft_channels[mft_ch].module),
|
|
NPCX_TMCTRL_TAEN);
|
|
/* Enable input debounce logic into TA. */
|
|
SET_BIT(NPCX_TCFG(mft_channels[mft_ch].module),
|
|
NPCX_TCFG_TADBEN);
|
|
/* Set the no clock to TCNT1. */
|
|
NPCX_TCKC(mft_channels[mft_ch].module) =
|
|
(NPCX_TCKC(mft_channels[mft_ch].module)
|
|
& (~(((1<<3)-1)<<NPCX_TCKC_C1CSEL)))
|
|
| (TCKC_NOCLK<<NPCX_TCKC_C1CSEL);
|
|
/* Set timer wake-up enable */
|
|
SET_BIT(NPCX_TWUEN(mft_channels[mft_ch].module),
|
|
NPCX_TWUEN_TAWEN);
|
|
SET_BIT(NPCX_TWUEN(mft_channels[mft_ch].module),
|
|
NPCX_TWUEN_TCWEN);
|
|
}
|
|
}
|
|
|
|
/* Back to Idle mode*/
|
|
tacho_status.cur_state = TACHO_IN_IDLE;
|
|
}
|
|
|
|
/**
|
|
* Set fan enabled.
|
|
*
|
|
* @param ch operation channel
|
|
* @param enabled enabled flag
|
|
* @return none
|
|
*/
|
|
void fan_set_enabled(int ch, int enabled)
|
|
{
|
|
int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM);
|
|
|
|
pwm_enable(pwm_ch, enabled);
|
|
}
|
|
|
|
/**
|
|
* Check fan enabled.
|
|
*
|
|
* @param ch operation channel
|
|
* @return enabled or not
|
|
*/
|
|
int fan_get_enabled(int ch)
|
|
{
|
|
int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM);
|
|
|
|
return pwm_get_enabled(pwm_ch);
|
|
}
|
|
|
|
/**
|
|
* Set fan duty cycle.
|
|
*
|
|
* @param ch operation channel
|
|
* @param percent duty cycle percent
|
|
* @return none
|
|
*/
|
|
void fan_set_duty(int ch, int percent)
|
|
{
|
|
int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM);
|
|
|
|
CPRINTS("set duty percent=%d", percent);
|
|
|
|
/* Set the duty cycle */
|
|
pwm_set_duty(pwm_ch, percent);
|
|
}
|
|
|
|
/**
|
|
* Get fan duty cycle.
|
|
*
|
|
* @param ch operation channel
|
|
* @return duty cycle
|
|
*/
|
|
int fan_get_duty(int ch)
|
|
{
|
|
int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM);
|
|
|
|
/* Return percent */
|
|
return pwm_get_duty(pwm_ch);
|
|
}
|
|
|
|
/**
|
|
* Check fan is rpm operation mode.
|
|
*
|
|
* @param ch operation channel
|
|
* @return rpm operation mode or not
|
|
* @notes not support in npcx chip
|
|
*/
|
|
int fan_get_rpm_mode(int ch)
|
|
{
|
|
/* TODO: (Benson_TBD_4) not support rpm mode, return 0 always */
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Set fan to rpm operation mode.
|
|
*
|
|
* @param ch operation channel
|
|
* @param rpm_mode rpm operation mode flag
|
|
* @return none
|
|
* @notes not support in npcx chip
|
|
*/
|
|
void fan_set_rpm_mode(int ch, int rpm_mode)
|
|
{
|
|
/* TODO: (Benson_TBD_4) not support rpm mode */
|
|
}
|
|
|
|
/**
|
|
* Get fan actual operation rpm.
|
|
*
|
|
* @param ch operation channel
|
|
* @return actual operation rpm value
|
|
*/
|
|
int fan_get_rpm_actual(int ch)
|
|
{
|
|
int mft_ch = fan_op_ch(ch, NPCX_FAN_OP_MFT);
|
|
uint8_t capture_pnd = NPCX_TECTRL_TBPND,
|
|
underflow_pnd = NPCX_TECTRL_TDPND;
|
|
uint8_t capture_clr = NPCX_TECLR_TBCLR,
|
|
underflow_clr = NPCX_TECLR_TDCLR;
|
|
|
|
/* Init pending/clear flag bit */
|
|
if (NPCX_MFT_MODULE_PORT_TA == mft_channels[mft_ch].port) {
|
|
capture_pnd = NPCX_TECTRL_TAPND;
|
|
underflow_pnd = NPCX_TECTRL_TCPND;
|
|
capture_clr = NPCX_TECLR_TACLR;
|
|
underflow_clr = NPCX_TECLR_TCCLR;
|
|
}
|
|
/* Start measure and return previous value when fan is working*/
|
|
if ((fan_get_enabled(ch)) && (fan_get_duty(ch))) {
|
|
if ((tacho_status.cur_state == TACHO_IN_IDLE)
|
|
|| (pre_duty != fan_get_duty(ch))) {
|
|
CPRINTS("mft_startmeasure");
|
|
if ((0 == rpm_actual) || (-1 == rpm_actual))
|
|
rpm_actual = fans[ch].rpm_min;
|
|
/* Clear all pending flags */
|
|
NPCX_TECLR(mft_channels[mft_ch].module) =
|
|
NPCX_TECTRL(mft_channels[mft_ch].module);
|
|
/* Start from first edge state */
|
|
tacho_status.cur_state = TACHO_WAIT_FOR_1_EDGE;
|
|
/* Start measure */
|
|
mft_startmeasure(ch);
|
|
}
|
|
/* Check whether MFT underflow flag is occurred */
|
|
else if (IS_BIT_SET(NPCX_TECTRL(mft_channels[mft_ch].module),
|
|
underflow_pnd)) {
|
|
/* Measurement is active - stop the measurement */
|
|
mft_stopmeasure(fan_init_ch);
|
|
/* Need to avoid underflow state happen */
|
|
rpm_actual = 0;
|
|
/*
|
|
* Flag TDPND means mft underflow happen,
|
|
* but let MFT still can re-measure actual rpm
|
|
* when user change pwm/fan duty during
|
|
* TACHO_UNDERFLOW state.
|
|
*/
|
|
tacho_status.cur_state = TACHO_UNDERFLOW;
|
|
CPRINTS("TACHO_UNDERFLOW");
|
|
|
|
/* Clear pending flags */
|
|
SET_BIT(NPCX_TECLR(mft_channels[mft_ch].module),
|
|
underflow_clr);
|
|
}
|
|
/* Check whether MFT signal detection flag is occurred */
|
|
else if (IS_BIT_SET(NPCX_TECTRL(mft_channels[mft_ch].module),
|
|
capture_pnd)) {
|
|
/* Start of tacho cycle is detected */
|
|
switch (tacho_status.cur_state) {
|
|
case TACHO_WAIT_FOR_1_EDGE:
|
|
CPRINTS("TACHO_WAIT_FOR_1_EDGE");
|
|
/*
|
|
* Start of the first tacho cycle is detected
|
|
* and wait for the second tacho cycle
|
|
* (second edge)
|
|
*/
|
|
tacho_status.cur_state = TACHO_WAIT_FOR_2_EDGE;
|
|
/* Send previous rpm before complete measure */
|
|
break;
|
|
case TACHO_WAIT_FOR_2_EDGE:
|
|
/* Complete measure tach and get actual tach */
|
|
mft_finalmeasure(fan_init_ch);
|
|
/* Stop the measurement */
|
|
mft_stopmeasure(ch);
|
|
/* Transfer actual tach to actual rpm */
|
|
rpm_actual = (tacho_status.edge_interval > 0) ?
|
|
(TACH_TO_RPM(mft_ch,
|
|
tacho_status.edge_interval)) : 0;
|
|
/* Back to Idle mode*/
|
|
tacho_status.cur_state = TACHO_IN_IDLE;
|
|
CPRINTS("TACHO_WAIT_FOR_2_EDGE");
|
|
CPRINTS("edge_interval=%x",
|
|
tacho_status.edge_interval);
|
|
CPRINTS("rpm_actual=%d", rpm_actual);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
/* Clear pending flags */
|
|
SET_BIT(NPCX_TECLR(mft_channels[mft_ch].module),
|
|
capture_clr);
|
|
}
|
|
} else {
|
|
CPRINTS("preset rpm");
|
|
/* Send preset rpm before fan is working */
|
|
if (fan_get_enabled(ch))
|
|
rpm_actual = fans[ch].rpm_min;
|
|
else
|
|
rpm_actual = 0;
|
|
|
|
tacho_status.cur_state = TACHO_IN_IDLE;
|
|
}
|
|
pre_duty = fan_get_duty(ch);
|
|
return rpm_actual;
|
|
}
|
|
|
|
/**
|
|
* Get fan setting rpm.
|
|
*
|
|
* @param ch operation channel
|
|
* @return setting rpm value
|
|
*/
|
|
int fan_get_rpm_target(int ch)
|
|
{
|
|
return rpm_target;
|
|
}
|
|
|
|
/**
|
|
* Set fan setting rpm.
|
|
*
|
|
* @param ch operation channel
|
|
* @param rpm setting rpm value
|
|
* @return none
|
|
*/
|
|
void fan_set_rpm_target(int ch, int rpm)
|
|
{
|
|
uint32_t percent = 0;
|
|
int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM);
|
|
|
|
rpm_target = rpm;
|
|
/* Transfer rpm to tach then calculate percentage */
|
|
percent = (RPM_TO_TACH(pwm_ch, rpm_target)*100)
|
|
/(pwm_channels[pwm_ch].cycle_pulses);
|
|
|
|
if (percent < 0)
|
|
percent = 0;
|
|
else if (percent > 100)
|
|
percent = 100;
|
|
|
|
/* RPM is inverse ratio to tach and percentage */
|
|
percent = 100 - percent;
|
|
|
|
pwm_set_duty(pwm_ch, percent);
|
|
}
|
|
|
|
/**
|
|
* Check fan operation status.
|
|
*
|
|
* @param ch operation channel
|
|
* @return fan_status fan operation status
|
|
*/
|
|
enum fan_status fan_get_status(int ch)
|
|
{
|
|
int rpm_actual;
|
|
|
|
rpm_actual = fan_get_rpm_actual(ch);
|
|
|
|
if (((fan_get_duty(ch)) && (rpm_actual < fans[ch].rpm_min))
|
|
|| ((!fan_get_duty(ch)) && (rpm_actual)))
|
|
return FAN_STATUS_FRUSTRATED;
|
|
else if ((rpm_actual == 0) && (!fan_get_duty(ch)))
|
|
return FAN_STATUS_STOPPED;
|
|
else
|
|
return FAN_STATUS_LOCKED;
|
|
}
|
|
|
|
/**
|
|
* Check fan is stall condition.
|
|
*
|
|
* @param ch operation channel
|
|
* @return non-zero if fan is enabled but stalled
|
|
*/
|
|
int fan_is_stalled(int ch)
|
|
{
|
|
int rpm_actual;
|
|
|
|
rpm_actual = fan_get_rpm_actual(ch);
|
|
|
|
/* Check for normal condition, others are stall condition */
|
|
if ((!fan_get_enabled(ch)) || ((fan_get_duty(ch))
|
|
&& (rpm_actual >= fans[ch].rpm_min))
|
|
|| ((!fan_get_duty(ch)) && (!rpm_actual)))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Fan channel setup.
|
|
*
|
|
* @param ch operation channel
|
|
* @param flags input flags
|
|
* @return none
|
|
*/
|
|
void fan_channel_setup(int ch, unsigned int flags)
|
|
{
|
|
fan_config(ch, (flags & FAN_USE_RPM_MODE));
|
|
}
|
|
|
|
/**
|
|
* Fan initial.
|
|
*
|
|
* @param none
|
|
* @return none
|
|
*/
|
|
static void fan_init(void)
|
|
{
|
|
#ifdef CONFIG_PWM_DSLEEP
|
|
/* Enable the fan module and delay a few clocks */
|
|
clock_enable_peripheral(CGC_OFFSET_FAN, CGC_FAN_MASK, CGC_MODE_ALL);
|
|
#else
|
|
/* Enable the fan module and delay a few clocks */
|
|
clock_enable_peripheral(CGC_OFFSET_FAN, CGC_FAN_MASK,
|
|
CGC_MODE_RUN | CGC_MODE_SLEEP);
|
|
#endif
|
|
}
|
|
DECLARE_HOOK(HOOK_INIT, fan_init, HOOK_PRIO_INIT_PWM);
|