summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMulin Chao <mlchao@nuvoton.com>2015-09-19 08:59:29 +0800
committerchrome-bot <chrome-bot@chromium.org>2015-09-29 21:11:41 -0700
commit7e7dd8cd1aa232e2790ddfe09bffbe908f3d0230 (patch)
tree5aa2e13b38519396e04b5ca679ccaac071dab50b
parentdbef9a6fed88c78795a5060dead823978394cf6f (diff)
downloadchrome-ec-7e7dd8cd1aa232e2790ddfe09bffbe908f3d0230.tar.gz
nuc: Simplify adc/pwm/fan drivers and related structures in boards
Modified drivers: 1. register.h: Add marco field operation funcs for muti-bits field of register. 2. adc.c/fan.c/pwm.c: Simplify field operations by marco funcs. 3. adc.c: Add support for ADC_CH3/4 4. pwm.c: Add PWM_CONFIG_DSLEEP_CLK flag 6. fan.c: Support multi-fans mechanism BUG=chrome-os-partner:34346 TEST=make buildall -j; test nuvoton IC specific drivers BRANCH=none Change-Id: Iaaeb6c4ae8d55b4245a1cefb9c20feae4c0fdec2 Reviewed-on: https://chromium-review.googlesource.com/300673 Commit-Ready: Mulin Chao <mlchao@nuvoton.com> Tested-by: Mulin Chao <mlchao@nuvoton.com> Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--board/npcx_evb/board.c49
-rw-r--r--board/npcx_evb/board.h3
-rw-r--r--board/npcx_evb/gpio.inc3
-rw-r--r--board/npcx_evb_arm/board.c49
-rw-r--r--board/npcx_evb_arm/board.h3
-rw-r--r--board/npcx_evb_arm/gpio.inc5
-rw-r--r--chip/npcx/adc.c29
-rw-r--r--chip/npcx/adc_chip.h12
-rw-r--r--chip/npcx/build.mk2
-rw-r--r--chip/npcx/fan.c475
-rw-r--r--chip/npcx/fan_chip.h45
-rw-r--r--chip/npcx/pwm.c193
-rw-r--r--chip/npcx/pwm_chip.h10
-rw-r--r--chip/npcx/registers.h36
14 files changed, 353 insertions, 561 deletions
diff --git a/board/npcx_evb/board.c b/board/npcx_evb/board.c
index 3e438bb6b0..e5b70d7f7d 100644
--- a/board/npcx_evb/board.c
+++ b/board/npcx_evb/board.c
@@ -36,43 +36,17 @@
/******************************************************************************/
/* ADC channels. Must be in the exactly same order as in enum adc_channel. */
const struct adc_t adc_channels[] = {
- [ADC_CH_0] = {"ADC0", NPCX_ADC_INPUT_CH0, ADC_MAX_VOLT,
- ADC_READ_MAX+1, 0},
- [ADC_CH_1] = {"ADC1", NPCX_ADC_INPUT_CH1, ADC_MAX_VOLT,
- ADC_READ_MAX+1, 0},
- [ADC_CH_2] = {"ADC2", NPCX_ADC_INPUT_CH2, ADC_MAX_VOLT,
- ADC_READ_MAX+1, 0},
+ [ADC_CH_0] = {"ADC0", NPCX_ADC_CH0, ADC_MAX_VOLT, ADC_READ_MAX+1, 0},
+ [ADC_CH_1] = {"ADC1", NPCX_ADC_CH1, ADC_MAX_VOLT, ADC_READ_MAX+1, 0},
+ [ADC_CH_2] = {"ADC2", NPCX_ADC_CH2, ADC_MAX_VOLT, ADC_READ_MAX+1, 0},
};
BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);
/******************************************************************************/
/* PWM channels. Must be in the exactly same order as in enum pwm_channel. */
const struct pwm_t pwm_channels[] = {
- [PWM_CH_FAN] = {
- .channel = 0,
- /*
- * flags can reverse the PWM output signal according to
- * the board design
- */
- .flags = PWM_CONFIG_ACTIVE_LOW,
- /*
- * freq_operation = freq_input / prescaler_divider
- * freq_output = freq_operation / cycle_pulses
- * and freq_output <= freq_mft
- */
- .freq = 34,
- /*
- * cycle_pulses = (cycle_pulses * freq_output) *
- * RPM_EDGES * RPM_SCALE * 60 / poles / rpm_min
- */
- .cycle_pulses = 480,
- },
- [PWM_CH_KBLIGHT] = {
- .channel = 1,
- .flags = 0,
- .freq = 10000,
- .cycle_pulses = 100,
- },
+ [PWM_CH_FAN] = { 0, PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP_CLK, 100},
+ [PWM_CH_KBLIGHT] = { 1, 0, 10000 },
};
BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT);
@@ -84,7 +58,7 @@ const struct fan_t fans[] = {
.rpm_min = 1020,
.rpm_start = 1020,
.rpm_max = 8190,
- .ch = 0,/* Use PWM/MFT to control fan */
+ .ch = 0,/* Use MFT id to control fan */
.pgood_gpio = GPIO_PGOOD_FAN,
.enable_gpio = -1,
},
@@ -94,16 +68,7 @@ BUILD_ASSERT(ARRAY_SIZE(fans) == FAN_CH_COUNT);
/******************************************************************************/
/* MFT channels. These are logically separate from mft_channels. */
const struct mft_t mft_channels[] = {
- [MFT_CH_0] = {
- .module = NPCX_MFT_MODULE_1,
- .port = NPCX_MFT_MODULE_PORT_TA,
- .default_count = 0xFFFF,
-#ifdef NPCX_MFT_INPUT_LFCLK
- .freq = 32768,
-#else
- .freq = 2000000,
-#endif
- },
+ [MFT_CH_0] = { NPCX_MFT_MODULE_1, 0xFFFF, TCKC_LFCLK, PWM_CH_FAN},
};
BUILD_ASSERT(ARRAY_SIZE(mft_channels) == MFT_CH_COUNT);
diff --git a/board/npcx_evb/board.h b/board/npcx_evb/board.h
index fd0ff65e75..316ca3ede7 100644
--- a/board/npcx_evb/board.h
+++ b/board/npcx_evb/board.h
@@ -10,7 +10,6 @@
/* Optional modules */
#define CONFIG_ADC
-#define CONFIG_PECI
#define CONFIG_PWM
#define CONFIG_SPI
#define CONFIG_LPC /* Used in Intel-based platform for host interface */
@@ -41,8 +40,6 @@
#define CONFIG_FANS 1
/* Optional feature - used by nuvoton */
-#define NPCX_PWM_INPUT_LFCLK /* PWM use LFCLK for input clock */
-#define NPCX_MFT_INPUT_LFCLK /* MFT use LFCLK for input clock */
#define NPCX_UART_MODULE2 0 /* 0:GPIO10/11 1:GPIO64/65 as UART */
#define NPCX_JTAG_MODULE2 0 /* 0:GPIO21/17/16/20 1:GPIOD5/E2/D4/E5 as JTAG*/
#define NPCX_TACH_SEL2 0 /* 0:GPIO40/A4 1:GPIO93/D3 as TACH */
diff --git a/board/npcx_evb/gpio.inc b/board/npcx_evb/gpio.inc
index 7c7ae2f10d..027855620b 100644
--- a/board/npcx_evb/gpio.inc
+++ b/board/npcx_evb/gpio.inc
@@ -62,9 +62,12 @@ ALTERNATE(PIN_MASK(4, 0x38), 1, MODULE_ADC, 0) /* ADC
ALTERNATE(PIN_MASK(A, 0x0A), 1, MODULE_SPI, 0) /* SPIP_MOSI/SPIP_SCLK GPIOA3/A1 */
ALTERNATE(PIN_MASK(9, 0x20), 1, MODULE_SPI, 0) /* SPIP_MISO GPIO95 */
ALTERNATE(PIN_MASK(C, 0x04), 3, MODULE_PWM_KBLIGHT, 0) /* PWM1 for PWM/KBLIGHT Test GPIOC2 */
+/* Alternative functionality for FANS */
+#ifdef CONFIG_FANS
ALTERNATE(PIN_MASK(C, 0x08), 7, MODULE_PWM_FAN, 0) /* PWM0 for PWM/FAN Test GPIOC3 */
#if NPCX_TACH_SEL2
ALTERNATE(PIN_MASK(9, 0x08), 3, MODULE_PWM_FAN, 0) /* MFT-1/TA1_TACH1 for FAN GPIO93 */
#else
ALTERNATE(PIN_MASK(4, 0x01), 3, MODULE_PWM_FAN, 0) /* MFT-1/TA1_TACH1 for FAN Test GPIO40 */
#endif
+#endif
diff --git a/board/npcx_evb_arm/board.c b/board/npcx_evb_arm/board.c
index 6661ff6e11..037e3d82e5 100644
--- a/board/npcx_evb_arm/board.c
+++ b/board/npcx_evb_arm/board.c
@@ -36,43 +36,17 @@
/******************************************************************************/
/* ADC channels. Must be in the exactly same order as in enum adc_channel. */
const struct adc_t adc_channels[] = {
- [ADC_CH_0] = {"ADC0", NPCX_ADC_INPUT_CH0, ADC_MAX_VOLT,
- ADC_READ_MAX+1, 0},
- [ADC_CH_1] = {"ADC1", NPCX_ADC_INPUT_CH1, ADC_MAX_VOLT,
- ADC_READ_MAX+1, 0},
- [ADC_CH_2] = {"ADC2", NPCX_ADC_INPUT_CH2, ADC_MAX_VOLT,
- ADC_READ_MAX+1, 0},
+ [ADC_CH_0] = {"ADC0", NPCX_ADC_CH0, ADC_MAX_VOLT, ADC_READ_MAX+1, 0},
+ [ADC_CH_1] = {"ADC1", NPCX_ADC_CH1, ADC_MAX_VOLT, ADC_READ_MAX+1, 0},
+ [ADC_CH_2] = {"ADC2", NPCX_ADC_CH2, ADC_MAX_VOLT, ADC_READ_MAX+1, 0},
};
BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);
/******************************************************************************/
/* PWM channels. Must be in the exactly same order as in enum pwm_channel. */
const struct pwm_t pwm_channels[] = {
- [PWM_CH_FAN] = {
- .channel = 0,
- /*
- * flags can reverse the PWM output signal according to
- * the board design
- */
- .flags = PWM_CONFIG_ACTIVE_LOW,
- /*
- * freq_operation = freq_input / prescaler_divider
- * freq_output = freq_operation / cycle_pulses
- * and freq_output <= freq_mft
- */
- .freq = 34,
- /*
- * cycle_pulses = (cycle_pulses * freq_output) *
- * RPM_EDGES * RPM_SCALE * 60 / poles / rpm_min
- */
- .cycle_pulses = 480,
- },
- [PWM_CH_KBLIGHT] = {
- .channel = 1,
- .flags = 0,
- .freq = 10000,
- .cycle_pulses = 100,
- },
+ [PWM_CH_FAN] = { 0, PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP_CLK, 100},
+ [PWM_CH_KBLIGHT] = { 1, 0, 10000 },
};
BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT);
@@ -84,7 +58,7 @@ const struct fan_t fans[] = {
.rpm_min = 1020,
.rpm_start = 1020,
.rpm_max = 8190,
- .ch = 0,/* Use PWM/MFT to control fan */
+ .ch = 0,/* Use MFT id to control fan */
.pgood_gpio = GPIO_PGOOD_FAN,
.enable_gpio = -1,
},
@@ -94,16 +68,7 @@ BUILD_ASSERT(ARRAY_SIZE(fans) == FAN_CH_COUNT);
/******************************************************************************/
/* MFT channels. These are logically separate from mft_channels. */
const struct mft_t mft_channels[] = {
- [MFT_CH_0] = {
- .module = NPCX_MFT_MODULE_1,
- .port = NPCX_MFT_MODULE_PORT_TA,
- .default_count = 0xFFFF,
-#ifdef NPCX_MFT_INPUT_LFCLK
- .freq = 32768,
-#else
- .freq = 2000000,
-#endif
- },
+ [MFT_CH_0] = { NPCX_MFT_MODULE_1, 0xFFFF, TCKC_LFCLK, PWM_CH_FAN},
};
BUILD_ASSERT(ARRAY_SIZE(mft_channels) == MFT_CH_COUNT);
diff --git a/board/npcx_evb_arm/board.h b/board/npcx_evb_arm/board.h
index 1a8c85ea93..b455f40473 100644
--- a/board/npcx_evb_arm/board.h
+++ b/board/npcx_evb_arm/board.h
@@ -37,13 +37,12 @@
#define CONFIG_FANS 1
/* Optional feature - used by nuvoton */
-#define NPCX_PWM_INPUT_LFCLK /* PWM use LFCLK for input clock */
-#define NPCX_MFT_INPUT_LFCLK /* MFT use LFCLK for input clock */
#define NPCX_UART_MODULE2 0 /* 0:GPIO10/11 1:GPIO64/65 as UART */
#define NPCX_JTAG_MODULE2 0 /* 0:GPIO21/17/16/20 1:GPIOD5/E2/D4/E5 as JTAG*/
#define NPCX_TACH_SEL2 0 /* 0:GPIO40/A4 1:GPIO93/D3 as TACH */
/* Optional for testing */
+#undef CONFIG_PECI
#undef CONFIG_PSTORE
#undef CONFIG_LOW_POWER_IDLE /* Deep Sleep Support */
diff --git a/board/npcx_evb_arm/gpio.inc b/board/npcx_evb_arm/gpio.inc
index 18d50564ff..1d1667ba06 100644
--- a/board/npcx_evb_arm/gpio.inc
+++ b/board/npcx_evb_arm/gpio.inc
@@ -20,7 +20,7 @@ GPIO_INT(LID_OPEN, PIN(3, 3), GPIO_PULL_DOWN | GPIO_INT_BOTH, lid_interr
GPIO(ENTERING_RW, PIN(3, 6), GPIO_OUT_LOW) /* Indicate when EC is entering RW code */
GPIO(PCH_WAKE_L, PIN(5, 0), GPIO_OUT_HIGH) /* Wake signal output to PCH */
/* For testing keyboard mkbp */
-GPIO(EC_INT_L, PIN(7, 4), GPIO_ODR_HIGH) /* Interrupt pin for keyboard mkbp */
+GPIO(EC_INT_L, PIN(7, 4), GPIO_ODR_HIGH) /* Interrupt pin for keyboard mkbp */
/* Used for module testing */
GPIO(PGOOD_FAN, PIN(C, 7), GPIO_PULL_UP | GPIO_INPUT) /* Power Good for FAN test */
@@ -63,9 +63,12 @@ ALTERNATE(PIN_MASK(4, 0x38), 1, MODULE_ADC, 0) /* ADC
ALTERNATE(PIN_MASK(A, 0x0A), 1, MODULE_SPI, 0) /* SPIP_MOSI/SPIP_SCLK GPIOA3/A1 */
ALTERNATE(PIN_MASK(9, 0x20), 1, MODULE_SPI, 0) /* SPIP_MISO GPIO95 */
ALTERNATE(PIN_MASK(C, 0x04), 3, MODULE_PWM_KBLIGHT, 0) /* PWM1 for PWM/KBLIGHT Test GPIOC2 */
+/* Alternative functionality for FANS */
+#ifdef CONFIG_FANS
ALTERNATE(PIN_MASK(C, 0x08), 7, MODULE_PWM_FAN, 0) /* PWM0 for PWM/FAN Test GPIOC3 */
#if NPCX_TACH_SEL2
ALTERNATE(PIN_MASK(9, 0x08), 3, MODULE_PWM_FAN, 0) /* MFT-1/TA1_TACH1 for FAN GPIO93 */
#else
ALTERNATE(PIN_MASK(4, 0x01), 3, MODULE_PWM_FAN, 0) /* MFT-1/TA1_TACH1 for FAN Test GPIO40 */
#endif
+#endif
diff --git a/chip/npcx/adc.c b/chip/npcx/adc.c
index 9d498be555..ba80162f45 100644
--- a/chip/npcx/adc.c
+++ b/chip/npcx/adc.c
@@ -33,13 +33,6 @@ enum npcx_adc_conversion_mode {
ADC_SCAN_CONVERSION_MODE = 1
};
-/* ADC repetitive mode */
-enum npcx_adc_repetitive_mode {
- ADC_ONE_SHOT_CONVERSION_TYPE = 0,
- ADC_REPETITIVE_CONVERSION_TYPE = 1
-};
-
-
/* Global variables */
static task_id_t task_waiting;
@@ -62,8 +55,7 @@ void adc_freq_changed(void)
prescaler_divider = 0x3F;
/* Set Core Clock Division Factor in order to obtain the ADC clock */
- NPCX_ATCTL = (NPCX_ATCTL & (~(((1<<6)-1)<<NPCX_ATCTL_SCLKDIV)))
- |(prescaler_divider<<NPCX_ATCTL_SCLKDIV);
+ SET_FIELD(NPCX_ATCTL, NPCX_ATCTL_SCLKDIV_FIELD, prescaler_divider);
}
DECLARE_HOOK(HOOK_FREQ_CHANGE, adc_freq_changed, HOOK_PRIO_DEFAULT);
@@ -75,7 +67,7 @@ DECLARE_HOOK(HOOK_FREQ_CHANGE, adc_freq_changed, HOOK_PRIO_DEFAULT);
*/
static int get_channel_data(enum npcx_adc_input_channel input_ch)
{
- return (NPCX_CHNDAT(input_ch)>>NPCX_CHNDAT_CHDAT) & ((1<<10)-1);
+ return GET_FIELD(NPCX_CHNDAT(input_ch), NPCX_CHNDAT_CHDAT_FIELD);
}
/**
@@ -94,16 +86,14 @@ static int start_single_and_wait(enum npcx_adc_input_channel input_ch
task_waiting = task_get_current();
/* Set ADC conversion code to SW conversion mode */
- NPCX_ADCCNF = (NPCX_ADCCNF & (~(((1<<2)-1)<<NPCX_ADCCNF_ADCMD)))
- |(ADC_CHN_CONVERSION_MODE<<NPCX_ADCCNF_ADCMD);
+ SET_FIELD(NPCX_ADCCNF, NPCX_ADCCNF_ADCMD_FIELD,
+ ADC_CHN_CONVERSION_MODE);
/* Set conversion type to one-shot type */
- NPCX_ADCCNF = (NPCX_ADCCNF & (~(((1<<1)-1)<<NPCX_ADCCNF_ADCRPTC)))
- |(ADC_ONE_SHOT_CONVERSION_TYPE<<NPCX_ADCCNF_ADCRPTC);
+ CLEAR_BIT(NPCX_ADCCNF, NPCX_ADCCNF_ADCRPTC);
/* Update number of channel to be converted */
- NPCX_ASCADD = (NPCX_ASCADD & (~(((1<<5)-1)<<NPCX_ASCADD_SADDR)))
- |(input_ch<<NPCX_ASCADD_SADDR);
+ SET_FIELD(NPCX_ASCADD, NPCX_ASCADD_SADDR_FIELD, input_ch);
/* Clear End-of-Conversion Event status */
SET_BIT(NPCX_ADCSTS, NPCX_ADCSTS_EOCEV);
@@ -139,7 +129,7 @@ int adc_read_channel(enum adc_channel ch)
if (start_single_and_wait(adc->input_ch, ADC_TIMEOUT_US)) {
if ((adc->input_ch ==
- ((NPCX_ASCADD>>NPCX_ASCADD_SADDR)&((1<<5)-1)))
+ GET_FIELD(NPCX_ASCADD, NPCX_ASCADD_SADDR_FIELD))
&& (IS_BIT_SET(NPCX_CHNDAT(adc->input_ch),
NPCX_CHNDAT_NEW))) {
value = get_channel_data(adc->input_ch) *
@@ -223,8 +213,9 @@ static void adc_init(void)
adc_freq_changed();
/* Set regular speed */
- NPCX_ATCTL = (NPCX_ATCTL & (~(((1<<3)-1)<<NPCX_ATCTL_DLY)))
- |((ADC_REGULAR_DLY - 1)<<NPCX_ATCTL_DLY);
+ SET_FIELD(NPCX_ATCTL, NPCX_ATCTL_DLY_FIELD, (ADC_REGULAR_DLY - 1));
+
+ /* Set the other ADC settings */
NPCX_ADCCNF2 = ADC_REGULAR_ADCCNF2;
NPCX_GENDLY = ADC_REGULAR_GENDLY;
NPCX_MEAST = ADC_REGULAR_MEAST;
diff --git a/chip/npcx/adc_chip.h b/chip/npcx/adc_chip.h
index fda98a7d74..8ee59519af 100644
--- a/chip/npcx/adc_chip.h
+++ b/chip/npcx/adc_chip.h
@@ -11,14 +11,16 @@
/* Minimum and maximum values returned by raw ADC read. */
#define ADC_READ_MIN 0
#define ADC_READ_MAX 1023
-#define ADC_MAX_VOLT 3260
+#define ADC_MAX_VOLT 3300
/* ADC input channel select */
enum npcx_adc_input_channel {
- NPCX_ADC_INPUT_CH0 = 0,
- NPCX_ADC_INPUT_CH1,
- NPCX_ADC_INPUT_CH2,
- NPCX_ADC_INPUT_CH_COUNT
+ NPCX_ADC_CH0 = 0,
+ NPCX_ADC_CH1,
+ NPCX_ADC_CH2,
+ NPCX_ADC_CH3,
+ NPCX_ADC_CH4,
+ NPCX_ADC_CH_COUNT
};
/* Data structure to define ADC channels. */
diff --git a/chip/npcx/build.mk b/chip/npcx/build.mk
index 6a5cd5e213..0fd97d33b0 100644
--- a/chip/npcx/build.mk
+++ b/chip/npcx/build.mk
@@ -23,7 +23,7 @@ chip-$(CONFIG_LPC)+=lpc.o
chip-$(CONFIG_PECI)+=peci.o
chip-$(CONFIG_SHI)+=shi.o
# pwm functions are implemented with the fan functions
-chip-$(CONFIG_PWM)+=pwm.o fan.o
+chip-$(CONFIG_PWM)+=pwm.o
chip-$(CONFIG_SPI)+=spi.o
chip-$(CONFIG_WATCHDOG)+=watchdog.o
chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o
diff --git a/chip/npcx/fan.c b/chip/npcx/fan.c
index 20208dccdc..6b4e24685a 100644
--- a/chip/npcx/fan.c
+++ b/chip/npcx/fan.c
@@ -25,14 +25,6 @@
#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,
@@ -44,17 +36,39 @@ enum npcx_mft_mdsel {
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
+/* Tacho measurement state */
+enum tacho_measure_state {
+ /* Tacho init state */
+ TACHO_IN_IDLE = 0,
+ /* Tacho first edge state */
+ TACHO_WAIT_FOR_1_EDGE,
+ /* Tacho second edge state */
+ TACHO_WAIT_FOR_2_EDGE,
+ /* Tacho underflow state */
+ TACHO_UNDERFLOW
+};
+
+/* Fan status data structure */
+struct fan_status_t {
+ /* Current state of the measurement */
+ enum tacho_measure_state cur_state;
+ /* MFT sampling freq*/
+ uint32_t mft_freq;
+ /* Actual rpm */
+ int rpm_actual;
+ /* Target rpm */
+ int rpm_target;
};
-#define RPM_SCALE 1 /* Fan RPM is multiplier of actual RPM */
-#define RPM_EDGES 1 /* Fan number of edges - 1 */
+/* Global variables */
+static volatile struct fan_status_t fan_status[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)
@@ -62,46 +76,9 @@ enum npcx_mft_clk_src {
* 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(ch, tach) \
+ ((fan_status[ch].mft_freq * ROUNDS) / MAX((tach), 1))
-#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.
@@ -109,36 +86,13 @@ static int fan_op_ch(int ch, enum npcx_fan_op_module op_module)
* @param ch operation channel
* @return none
*/
-static void mft_startmeasure(int ch)
+static void mft_start_measure(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
+ int mdl = mft_channels[ch].module;
+
+ /* Set the clock source type and start counting */
+ SET_FIELD(NPCX_TCKC(mdl), NPCX_TCKC_C1CSEL_FIELD,
+ mft_channels[ch].clk_src);
}
/**
@@ -147,76 +101,60 @@ static void mft_startmeasure(int ch)
* @param ch operation channel
* @return none
*/
-static void mft_stopmeasure(int ch)
+static void mft_stop_measure(int ch)
{
- int mft_ch = fan_op_ch(ch, NPCX_FAN_OP_MFT);
+ int mdl = mft_channels[ch].module;
/* 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);
+ NPCX_TECLR(mdl) = NPCX_TECTRL(mdl);
+
+ /* Stop the timer and capture events for TCNT2 */
+ SET_FIELD(NPCX_TCKC(mdl), NPCX_TCKC_C1CSEL_FIELD, TCKC_NOCLK);
}
/**
* MFT final measure.
*
* @param ch operation channel
- * @return none
+ * @return actual rpm
*/
-static void mft_finalmeasure(int ch)
+static int mft_final_measure(int ch)
{
- int mft_ch = fan_op_ch(ch, NPCX_FAN_OP_MFT);
-
+ int mdl = mft_channels[ch].module;
+ int tacho;
/*
* 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));
+ tacho = mft_channels[ch].default_count - NPCX_TCRA(mdl);
+ CPRINTS("tacho=%x", tacho);
+
+ /* Transfer tacho to actual rpm */
+ return (tacho > 0) ? (TACH_TO_RPM(ch, tacho)) : 0;
}
/**
- * Preset fan operation clock.
+ * Set fan prescaler based on apb1 clock
*
* @param none
* @return none
* @notes changed when initial or HOOK_FREQ_CHANGE command
*/
-#ifndef NPCX_MFT_INPUT_LFCLK
-void mft_freq_changed(void)
+void mft_set_apb1_prescaler(int ch)
{
- uint16_t prescaler_divider = 0;
- int mft_ch = fan_op_ch(fan_init_ch, NPCX_FAN_OP_MFT);
+ int mdl = mft_channels[ch].module;
+ uint16_t prescaler_divider = 0;
/* Set clock prescaler divider to MFT module*/
prescaler_divider = (uint16_t)(clock_get_apb1_freq()
- /mft_channels[mft_ch].freq);
+ / fan_status[ch].mft_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;
+ NPCX_TPRSC(mdl) = (uint8_t) prescaler_divider;
}
-DECLARE_HOOK(HOOK_FREQ_CHANGE, mft_freq_changed, HOOK_PRIO_DEFAULT);
-#endif
/**
* Fan configuration.
@@ -227,117 +165,89 @@ DECLARE_HOOK(HOOK_FREQ_CHANGE, mft_freq_changed, HOOK_PRIO_DEFAULT);
*/
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);
+ int mdl = mft_channels[ch].module;
+ int pwm_id = mft_channels[ch].pwm_id;
+ enum npcx_mft_clk_src clk_src = mft_channels[ch].clk_src;
+ volatile struct fan_status_t *p_status = fan_status + ch;
/* Configure pins from GPIOs to FAN */
gpio_config_module(MODULE_PWM_FAN, 1);
+ /* Setup pwm with fan spec. */
+ pwm_config(pwm_id);
+
+ /* Need to initialize MFT or not */
if (enable_mft_read_rpm) {
+
+ /* Initialize tacho sampling rate */
+ if (clk_src == TCKC_LFCLK)
+ p_status->mft_freq = INT_32K_CLOCK;
+ else if (clk_src == TCKC_PRESCALE_APB1_CLK)
+ p_status->mft_freq = clock_get_apb1_freq();
+ else
+ p_status->mft_freq = 0;
+
/* 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);
- }
+ SET_FIELD(NPCX_TMCTRL(mdl), NPCX_TMCTRL_MDSEL_FIELD,
+ NPCX_MFT_MDSEL_5);
+
+ /* Set MFT operation frequency */
+ if (clk_src == TCKC_PRESCALE_APB1_CLK)
+ mft_set_apb1_prescaler(ch);
+
+ /* Set the low power mode or not. */
+ UPDATE_BIT(NPCX_TCKC(mdl), NPCX_TCKC_LOW_PWR,
+ clk_src == TCKC_LFCLK);
+
+ /* Set the default count-down timer. */
+ NPCX_TCNT1(mdl) = mft_channels[ch].default_count;
+ NPCX_TCRA(mdl) = mft_channels[ch].default_count;
+
+ /* Set the edge polarity to rising. */
+ SET_BIT(NPCX_TMCTRL(mdl), NPCX_TMCTRL_TAEDG);
+ /* Enable capture TCNT1 into TCRA and preset TCNT1. */
+ SET_BIT(NPCX_TMCTRL(mdl), NPCX_TMCTRL_TAEN);
+ /* Enable input debounce logic into TA. */
+ SET_BIT(NPCX_TCFG(mdl), NPCX_TCFG_TADBEN);
+
+ /* Set the no clock to TCNT1. */
+ SET_FIELD(NPCX_TCKC(mdl), NPCX_TCKC_C1CSEL_FIELD,
+ TCKC_NOCLK);
+ /* Set timer wake-up enable */
+ SET_BIT(NPCX_TWUEN(mdl), NPCX_TWUEN_TAWEN);
+ SET_BIT(NPCX_TWUEN(mdl), NPCX_TWUEN_TCWEN);
+
}
/* Back to Idle mode*/
- tacho_status.cur_state = TACHO_IN_IDLE;
+ p_status->cur_state = TACHO_IN_IDLE;
}
/**
- * Set fan enabled.
+ * Get percentage of duty cycle from rpm
*
* @param ch operation channel
- * @param enabled enabled flag
+ * @param rpm target rpm
* @return none
*/
-void fan_set_enabled(int ch, int enabled)
+int fan_rpm_to_percent(int ch, int rpm)
{
- int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM);
+ int pct, max, min;
- 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);
+ if (!rpm) {
+ pct = 0;
+ } else {
+ min = fans[ch].rpm_min;
+ max = fans[ch].rpm_max;
+ pct = (99*rpm + max - 100*min) / (max-min);
+ }
- return pwm_get_enabled(pwm_ch);
+ return pct;
}
+/*****************************************************************************/
+/* IC specific low-level driver */
+
/**
* Set fan duty cycle.
*
@@ -347,12 +257,15 @@ int fan_get_enabled(int ch)
*/
void fan_set_duty(int ch, int percent)
{
- int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM);
+ int pwm_id = mft_channels[ch].pwm_id;
CPRINTS("set duty percent=%d", percent);
+ /* Set the duty cycle of PWM */
+ pwm_set_duty(pwm_id, percent);
- /* Set the duty cycle */
- pwm_set_duty(pwm_ch, percent);
+ /* Start measurement again */
+ if (percent != 0 && fan_status[ch].cur_state == TACHO_UNDERFLOW)
+ fan_status[ch].cur_state = TACHO_IN_IDLE;
}
/**
@@ -363,12 +276,11 @@ void fan_set_duty(int ch, int percent)
*/
int fan_get_duty(int ch)
{
- int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM);
+ int pwm_id = mft_channels[ch].pwm_id;
/* Return percent */
- return pwm_get_duty(pwm_ch);
+ return pwm_get_duty(pwm_id);
}
-
/**
* Check fan is rpm operation mode.
*
@@ -403,59 +315,44 @@ void fan_set_rpm_mode(int ch, int rpm_mode)
*/
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*/
+ int mdl = mft_channels[ch].module;
+ volatile struct fan_status_t *p_status = fan_status + ch;
+
+ /* 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))) {
+ if (p_status->cur_state == TACHO_IN_IDLE) {
CPRINTS("mft_startmeasure");
- if ((0 == rpm_actual) || (-1 == rpm_actual))
- rpm_actual = fans[ch].rpm_min;
+ if (p_status->rpm_actual <= 0)
+ p_status->rpm_actual = fans[ch].rpm_min;
/* Clear all pending flags */
- NPCX_TECLR(mft_channels[mft_ch].module) =
- NPCX_TECTRL(mft_channels[mft_ch].module);
+ NPCX_TECLR(mdl) = NPCX_TECTRL(mdl);
/* Start from first edge state */
- tacho_status.cur_state = TACHO_WAIT_FOR_1_EDGE;
+ p_status->cur_state = TACHO_WAIT_FOR_1_EDGE;
/* Start measure */
- mft_startmeasure(ch);
+ mft_start_measure(ch);
}
/* Check whether MFT underflow flag is occurred */
- else if (IS_BIT_SET(NPCX_TECTRL(mft_channels[mft_ch].module),
- underflow_pnd)) {
+ else if (IS_BIT_SET(NPCX_TECTRL(mdl), NPCX_TECTRL_TCPND)) {
/* Measurement is active - stop the measurement */
- mft_stopmeasure(fan_init_ch);
+ mft_stop_measure(ch);
/* Need to avoid underflow state happen */
- rpm_actual = 0;
+ p_status->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;
+ p_status->cur_state = TACHO_UNDERFLOW;
CPRINTS("TACHO_UNDERFLOW");
/* Clear pending flags */
- SET_BIT(NPCX_TECLR(mft_channels[mft_ch].module),
- underflow_clr);
+ SET_BIT(NPCX_TECLR(mdl), NPCX_TECLR_TCCLR);
}
/* Check whether MFT signal detection flag is occurred */
- else if (IS_BIT_SET(NPCX_TECTRL(mft_channels[mft_ch].module),
- capture_pnd)) {
+ else if (IS_BIT_SET(NPCX_TECTRL(mdl), NPCX_TECTRL_TAPND)) {
/* Start of tacho cycle is detected */
- switch (tacho_status.cur_state) {
+ switch (p_status->cur_state) {
case TACHO_WAIT_FOR_1_EDGE:
CPRINTS("TACHO_WAIT_FOR_1_EDGE");
/*
@@ -463,44 +360,61 @@ int fan_get_rpm_actual(int ch)
* and wait for the second tacho cycle
* (second edge)
*/
- tacho_status.cur_state = TACHO_WAIT_FOR_2_EDGE;
+ p_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);
+ /* Complete measure tacho and get actual rpm */
+ p_status->rpm_actual = mft_final_measure(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;
+ mft_stop_measure(ch);
+
/* Back to Idle mode*/
- tacho_status.cur_state = TACHO_IN_IDLE;
+ p_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);
+ CPRINTS("rpm_actual=%d", p_status->rpm_actual);
break;
default:
break;
}
/* Clear pending flags */
- SET_BIT(NPCX_TECLR(mft_channels[mft_ch].module),
- capture_clr);
+ SET_BIT(NPCX_TECLR(mdl), NPCX_TECLR_TACLR);
}
} else {
CPRINTS("preset rpm");
/* Send preset rpm before fan is working */
if (fan_get_enabled(ch))
- rpm_actual = fans[ch].rpm_min;
+ p_status->rpm_actual = fans[ch].rpm_min;
else
- rpm_actual = 0;
+ p_status->rpm_actual = 0;
- tacho_status.cur_state = TACHO_IN_IDLE;
+ p_status->cur_state = TACHO_IN_IDLE;
}
- pre_duty = fan_get_duty(ch);
- return rpm_actual;
+ return p_status->rpm_actual;
+}
+
+/**
+ * Check fan enabled.
+ *
+ * @param ch operation channel
+ * @return enabled or not
+ */
+int fan_get_enabled(int ch)
+{
+ int pwm_id = mft_channels[ch].pwm_id;
+ return pwm_get_enabled(pwm_id);
+}
+/**
+ * Set fan enabled.
+ *
+ * @param ch operation channel
+ * @param enabled enabled flag
+ * @return none
+ */
+void fan_set_enabled(int ch, int enabled)
+{
+ int pwm_id = mft_channels[ch].pwm_id;
+ pwm_enable(pwm_id, enabled);
}
/**
@@ -511,7 +425,7 @@ int fan_get_rpm_actual(int ch)
*/
int fan_get_rpm_target(int ch)
{
- return rpm_target;
+ return fan_status[ch].rpm_target;
}
/**
@@ -523,23 +437,18 @@ int fan_get_rpm_target(int ch)
*/
void fan_set_rpm_target(int ch, int rpm)
{
- uint32_t percent = 0;
- int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM);
+ int percent = 0;
- 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);
+ fan_status[ch].rpm_target = rpm;
+ /* Transfer rpm to percentage of duty cycle */
+ percent = fan_rpm_to_percent(ch, rpm);
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);
+ fan_set_duty(ch, percent);
}
/**
@@ -604,13 +513,7 @@ void fan_channel_setup(int ch, unsigned int flags)
*/
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);
diff --git a/chip/npcx/fan_chip.h b/chip/npcx/fan_chip.h
index 02c86bc9d4..3280cd1920 100644
--- a/chip/npcx/fan_chip.h
+++ b/chip/npcx/fan_chip.h
@@ -10,51 +10,30 @@
/* MFT module select */
enum npcx_mft_module {
- NPCX_MFT_MODULE_1 = 0,
- NPCX_MFT_MODULE_2 = 0,
- NPCX_MFT_MODULE_3 = 0,
+ NPCX_MFT_MODULE_1,
+ NPCX_MFT_MODULE_2,
+ NPCX_MFT_MODULE_3,
/* Number of MFT modules */
NPCX_MFT_MODULE_COUNT
};
-/* MFT module port */
-enum npcx_mft_module_port {
- NPCX_MFT_MODULE_PORT_TA,
- NPCX_MFT_MODULE_PORT_TB,
- /* Number of MFT module ports */
- NPCX_MFT_MODULE_PORT_COUNT
+/* MFT clock source */
+enum npcx_mft_clk_src {
+ TCKC_NOCLK = 0,
+ TCKC_PRESCALE_APB1_CLK = 1,
+ TCKC_LFCLK = 5,
};
/* Data structure to define MFT channels. */
struct mft_t {
/* MFT module ID */
enum npcx_mft_module module;
- /* MFT port */
- enum npcx_mft_module_port port;
/* MFT TCNT default count */
uint32_t default_count;
- /* MFT freq */
- uint32_t freq;
-};
-
-/* Tacho measurement state */
-enum tacho_measure_state {
- /* Tacho init state */
- TACHO_IN_IDLE = 0,
- /* Tacho first edge state */
- TACHO_WAIT_FOR_1_EDGE,
- /* Tacho second edge state */
- TACHO_WAIT_FOR_2_EDGE,
- /* Tacho underflow state */
- TACHO_UNDERFLOW
-};
-
-/* Tacho status data structure */
-struct tacho_status_t {
- /* Current state of the measurement */
- enum tacho_measure_state cur_state;
- /* Pulse counter value between edge1 and edge2 */
- uint32_t edge_interval;
+ /* MFT clock source */
+ enum npcx_mft_clk_src clk_src;
+ /* PWM id */
+ int pwm_id;
};
extern const struct mft_t mft_channels[];
diff --git a/chip/npcx/pwm.c b/chip/npcx/pwm.c
index 4a8ba4dace..d89ff365c3 100644
--- a/chip/npcx/pwm.c
+++ b/chip/npcx/pwm.c
@@ -30,7 +30,7 @@ enum npcx_pwm_source_clock {
NPCX_PWM_CLOCK_APB2_LFCLK = 0,
NPCX_PWM_CLOCK_FX = 1,
NPCX_PWM_CLOCK_FR = 2,
- NPCX_PWM_CLOCK_RESERVED = 0x3,
+ NPCX_PWM_CLOCK_RESERVED = 3,
NPCX_PWM_CLOCK_UNDEF = 0xFF
};
@@ -43,53 +43,53 @@ enum npcx_pwm_heartbeat_mode {
NPCX_PWM_HBM_UNDEF = 0xFF
};
-/* Global variables */
-static int pwm_init_ch;
+/* Default duty cycle resolution */
+#define DUTY_CYCLE_RESOLUTION 100
/**
- * Preset PWM operation clock.
+ * Set PWM operation clock.
*
- * @param none
- * @return none
- * @notes changed when initial or HOOK_FREQ_CHANGE command
+ * @param ch operation channel
+ * @param freq desired PWM frequency
+ * @param res resolution for duty cycle
+ * @notes changed when initialization
*/
-void pwm_freq_changed(void)
+void pwm_set_freq(enum pwm_channel ch, uint32_t freq, uint32_t res)
{
+ int mdl = pwm_channels[ch].channel;
uint32_t prescaler_divider = 0;
+ uint32_t clock;
/* Disable PWM for module configuration */
- pwm_enable(pwm_init_ch, 0);
-
- if (pwm_init_ch == PWM_CH_FAN) {
- /*
- * Using PWM Frequency and Resolution we calculate
- * prescaler for input clock
- */
-#ifdef NPCX_PWM_INPUT_LFCLK
- prescaler_divider = (uint32_t)(32768 /
- (pwm_channels[pwm_init_ch].freq)
- /(pwm_channels[pwm_init_ch].cycle_pulses));
-#else
- prescaler_divider = (uint32_t)(
- clock_get_apb2_freq() / pwm_channels[pwm_init_ch].freq
- / (pwm_channels[pwm_init_ch].cycle_pulses));
-#endif
- } else {
- prescaler_divider = (uint32_t)(
- clock_get_apb2_freq() / pwm_channels[pwm_init_ch].freq
- / (pwm_channels[pwm_init_ch].cycle_pulses));
- }
- /* Set clock prescalre divider to ADC module*/
+ pwm_enable(ch, 0);
+
+ /* Get PWM clock frequency */
+ if (pwm_channels[ch].flags & PWM_CONFIG_DSLEEP_CLK)
+ clock = INT_32K_CLOCK;
+ else
+ clock = clock_get_apb2_freq();
+
+ /*
+ * Using PWM Frequency and Resolution we calculate
+ * prescaler for input clock
+ */
+ prescaler_divider = ((clock / freq)/res);
+
+ /* Set clock prescaler divider to PWM module*/
if (prescaler_divider >= 1)
prescaler_divider = prescaler_divider - 1;
if (prescaler_divider > 0xFFFF)
prescaler_divider = 0xFFFF;
/* Configure computed prescaler and resolution */
- NPCX_PRSC(pwm_channels[pwm_init_ch].channel) =
- (uint16_t)prescaler_divider;
+ NPCX_PRSC(mdl) = (uint16_t)prescaler_divider - 1;
+
+ /* Set PWM cycle time */
+ NPCX_CTR(mdl) = res - 1;
+
+ /* Set the duty cycle to 0% since DCR > CTR */
+ NPCX_DCR(mdl) = res;
}
-DECLARE_HOOK(HOOK_FREQ_CHANGE, pwm_freq_changed, HOOK_PRIO_DEFAULT);
/**
* Set PWM enabled.
@@ -100,12 +100,9 @@ DECLARE_HOOK(HOOK_FREQ_CHANGE, pwm_freq_changed, HOOK_PRIO_DEFAULT);
*/
void pwm_enable(enum pwm_channel ch, int enabled)
{
+ int mdl = pwm_channels[ch].channel;
/* Start or close PWM module */
- if (enabled)
- SET_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), NPCX_PWMCTL_PWR);
- else
- CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel),
- NPCX_PWMCTL_PWR);
+ UPDATE_BIT(NPCX_PWMCTL(mdl), NPCX_PWMCTL_PWR, enabled);
}
/**
@@ -116,8 +113,8 @@ void pwm_enable(enum pwm_channel ch, int enabled)
*/
int pwm_get_enabled(enum pwm_channel ch)
{
- return IS_BIT_SET(NPCX_PWMCTL(pwm_channels[ch].channel),
- NPCX_PWMCTL_PWR);
+ int mdl = pwm_channels[ch].channel;
+ return IS_BIT_SET(NPCX_PWMCTL(mdl), NPCX_PWMCTL_PWR);
}
/**
@@ -129,37 +126,38 @@ int pwm_get_enabled(enum pwm_channel ch)
*/
void pwm_set_duty(enum pwm_channel ch, int percent)
{
- uint32_t resolution = 0;
- uint16_t duty_cycle = 0;
-
- CPRINTS("pwm0=%d", percent);
- /* Assume the fan control is active high and invert it ourselves */
- if (pwm_channels[ch].flags & PWM_CONFIG_ACTIVE_LOW)
- SET_BIT(NPCX_PWMCTL(pwm_channels[ch].channel),
- NPCX_PWMCTL_INVP);
- else
- CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel),
- NPCX_PWMCTL_INVP);
+ int mdl = pwm_channels[ch].channel;
+ uint32_t dc_res = 0;
+ uint16_t dc_cnt = 0;
+ /* Checking duty value first */
if (percent < 0)
percent = 0;
else if (percent > 100)
percent = 100;
- CPRINTS("pwm1duty=%d", percent);
+ CPRINTS("pwm%d, set duty=%d", mdl, percent);
- resolution = NPCX_CTR(pwm_channels[ch].channel) + 1;
- duty_cycle = percent*resolution/100;
+ /* Assume the fan control is active high and invert it ourselves */
+ UPDATE_BIT(NPCX_PWMCTL(mdl), NPCX_PWMCTL_INVP,
+ (pwm_channels[ch].flags & PWM_CONFIG_ACTIVE_LOW));
+
+ dc_res = NPCX_CTR(mdl) + 1;
+ dc_cnt = (percent*dc_res)/100;
CPRINTS("freq=0x%x", pwm_channels[ch].freq);
- CPRINTS("resolution=%d", resolution);
- CPRINTS("duty_cycle=%d", duty_cycle);
- if (percent*resolution > (duty_cycle*100))
- duty_cycle += 1;
+ CPRINTS("duty_cycle_res=%d", dc_res);
+ CPRINTS("duty_cycle_cnt=%d", dc_cnt);
+
+
/* Set the duty cycle */
- if (duty_cycle > 0) {
- NPCX_DCR(pwm_channels[ch].channel) = (duty_cycle - 1);
+ if (percent > 0) {
+ if (percent == 100)
+ NPCX_DCR(mdl) = NPCX_CTR(mdl);
+ else
+ NPCX_DCR(mdl) = (dc_cnt - 1);
pwm_enable(ch, 1);
} else {
- NPCX_DCR(pwm_channels[ch].channel) = resolution;
+ /* Output low since DCR > CTR */
+ NPCX_DCR(mdl) = NPCX_CTR(mdl) + 1;
pwm_enable(ch, 0);
}
}
@@ -172,74 +170,45 @@ void pwm_set_duty(enum pwm_channel ch, int percent)
*/
int pwm_get_duty(enum pwm_channel ch)
{
+ int mdl = pwm_channels[ch].channel;
/* Return percent */
- if ((0 == pwm_get_enabled(ch)) || (NPCX_DCR(pwm_channels[ch].channel)
- > NPCX_CTR(pwm_channels[ch].channel)))
+ if ((!pwm_get_enabled(ch)) || (NPCX_DCR(mdl) > NPCX_CTR(mdl)))
return 0;
else
- return (((NPCX_DCR(pwm_channels[ch].channel) + 1) * 100)
- / (NPCX_CTR(pwm_channels[ch].channel) + 1));
+ return ((NPCX_DCR(mdl) + 1) * 100) / (NPCX_CTR(mdl) + 1);
}
/**
* PWM configuration.
*
- * @param ch operation channel
+ * @param ch operation channel
* @return none
*/
void pwm_config(enum pwm_channel ch)
{
- pwm_init_ch = ch;
-
- /* Configure pins from GPIOs to PWM */
- if (ch == PWM_CH_FAN)
- gpio_config_module(MODULE_PWM_FAN, 1);
- else
- gpio_config_module(MODULE_PWM_KBLIGHT, 1);
+ int mdl = pwm_channels[ch].channel;
/* Disable PWM for module configuration */
- pwm_enable(ch, 0);
+ pwm_enable(mdl, 0);
- /* Set PWM heartbeat mode is no heartbeat*/
- NPCX_PWMCTL(pwm_channels[ch].channel) =
- (NPCX_PWMCTL(pwm_channels[ch].channel)
- & (~(((1<<2)-1) << NPCX_PWMCTL_HB_DC_CTL)))
- | (NPCX_PWM_HBM_NORMAL << NPCX_PWMCTL_HB_DC_CTL);
+ /* Set PWM heartbeat mode is no heartbeat */
+ SET_FIELD(NPCX_PWMCTL(mdl), NPCX_PWMCTL_HB_DC_CTL_FIELD,
+ NPCX_PWM_HBM_NORMAL);
- /* Set PWM operation frequence */
- pwm_freq_changed();
+ /* Select default CLK or LFCLK clock input to PWM module */
+ SET_FIELD(NPCX_PWMCTLEX(mdl), NPCX_PWMCTLEX_FCK_SEL_FIELD,
+ NPCX_PWM_CLOCK_APB2_LFCLK);
- /* Set PWM cycle time */
- NPCX_CTR(pwm_channels[ch].channel) =
- (pwm_channels[ch].cycle_pulses - 1);
+ /* Set PWM polarity normal first */
+ CLEAR_BIT(NPCX_PWMCTL(mdl), NPCX_PWMCTL_INVP);
- /* Set the duty cycle */
- NPCX_DCR(pwm_channels[ch].channel) = pwm_channels[ch].cycle_pulses;
+ /* Select PWM clock source */
+ UPDATE_BIT(NPCX_PWMCTL(mdl), NPCX_PWMCTL_CKSEL,
+ (pwm_channels[ch].flags & PWM_CONFIG_DSLEEP_CLK));
- /* Set PWM polarity is normal*/
- CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), NPCX_PWMCTL_INVP);
+ /* Set PWM operation frequency */
+ pwm_set_freq(ch, pwm_channels[ch].freq, DUTY_CYCLE_RESOLUTION);
- /* Select default CLK or LFCLK clock input to PWM module */
- NPCX_PWMCTLEX(pwm_channels[ch].channel) =
- (NPCX_PWMCTLEX(pwm_channels[ch].channel)
- & (~(((1<<2)-1)<<NPCX_PWMCTLEX_FCK_SEL)))
- | (NPCX_PWM_CLOCK_APB2_LFCLK<<NPCX_PWMCTLEX_FCK_SEL);
-
- if (ch == PWM_CH_FAN) {
-#ifdef NPCX_PWM_INPUT_LFCLK
- /* Select default LFCLK clock input to PWM module */
- SET_BIT(NPCX_PWMCTL(pwm_channels[ch].channel),
- NPCX_PWMCTL_CKSEL);
-#else
- /* Select default core clock input to PWM module */
- CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel),
- NPCX_PWMCTL_CKSEL);
-#endif
- } else {
- /* Select default core clock input to PWM module */
- CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel),
- NPCX_PWMCTL_CKSEL);
- }
}
/**
@@ -252,14 +221,8 @@ static void pwm_init(void)
{
int i;
-#ifdef CONFIG_PWM_DSLEEP
/* Enable the PWM module and delay a few clocks */
clock_enable_peripheral(CGC_OFFSET_PWM, CGC_PWM_MASK, CGC_MODE_ALL);
-#else
- /* Enable the PWM module and delay a few clocks */
- clock_enable_peripheral(CGC_OFFSET_PWM, CGC_PWM_MASK,
- CGC_MODE_RUN | CGC_MODE_SLEEP);
-#endif
for (i = 0; i < PWM_CH_COUNT; i++)
pwm_config(i);
diff --git a/chip/npcx/pwm_chip.h b/chip/npcx/pwm_chip.h
index 201f628106..530e221a1d 100644
--- a/chip/npcx/pwm_chip.h
+++ b/chip/npcx/pwm_chip.h
@@ -8,6 +8,14 @@
#ifndef __CROS_EC_PWM_CHIP_H
#define __CROS_EC_PWM_CHIP_H
+/* Optional npcx flags for PWM config table */
+
+/**
+ * Use internal 32K as PWM clock source.
+ * It can keep PWM is active during ec enter deep idle.
+ */
+#define PWM_CONFIG_DSLEEP_CLK (1 << 31)
+
/* Data structure to define PWM channels. */
struct pwm_t {
/* PWM channel ID */
@@ -16,8 +24,6 @@ struct pwm_t {
uint32_t flags;
/* PWM freq. */
uint32_t freq;
- /* PWM pulses each cycle. */
- uint32_t cycle_pulses;
};
extern const struct pwm_t pwm_channels[];
diff --git a/chip/npcx/registers.h b/chip/npcx/registers.h
index fbcf7991dc..df0fb9256b 100644
--- a/chip/npcx/registers.h
+++ b/chip/npcx/registers.h
@@ -13,6 +13,7 @@
/*
* Macro Functions
*/
+/* Bit functions */
#define SET_BIT(reg, bit) ((reg) |= (0x1 << (bit)))
#define CLEAR_BIT(reg, bit) ((reg) &= (~(0x1 << (bit))))
#define IS_BIT_SET(reg, bit) ((reg >> bit) & (0x1))
@@ -20,6 +21,21 @@
SET_BIT(reg, bit); \
else \
CLEAR_BIT(reg, bit); }
+/* Field functions */
+#define GET_POS_FIELD(pos, size) pos
+#define GET_SIZE_FIELD(pos, size) size
+#define FIELD_POS(field) GET_POS_##field
+#define FIELD_SIZE(field) GET_SIZE_##field
+/* Read field functions */
+#define GET_FIELD(reg, field) \
+ _GET_FIELD_(reg, FIELD_POS(field), FIELD_SIZE(field))
+#define _GET_FIELD_(reg, f_pos, f_size) (((reg)>>(f_pos)) & ((1<<(f_size))-1))
+/* Write field functions */
+#define SET_FIELD(reg, field, value) \
+ _SET_FIELD_(reg, FIELD_POS(field), FIELD_SIZE(field), value)
+#define _SET_FIELD_(reg, f_pos, f_size, value) \
+ ((reg) = ((reg) & (~(((1 << (f_size))-1) << (f_pos)))) \
+ | ((value) << (f_pos)))
/******************************************************************************/
/*
@@ -1008,17 +1024,17 @@ enum PM_CHANNEL_T {
#define NPCX_MEAST REG16(NPCX_ADC_BASE_ADDR + 0x026)
/* ADC register fields */
-#define NPCX_ATCTL_SCLKDIV 0
-#define NPCX_ATCTL_DLY 8
-#define NPCX_ASCADD_SADDR 0
+#define NPCX_ATCTL_SCLKDIV_FIELD FIELD(0, 6)
+#define NPCX_ATCTL_DLY_FIELD FIELD(8, 3)
+#define NPCX_ASCADD_SADDR_FIELD FIELD(0, 5)
#define NPCX_ADCSTS_EOCEV 0
-#define NPCX_ADCCNF_ADCMD 1
+#define NPCX_ADCCNF_ADCMD_FIELD FIELD(1, 2)
#define NPCX_ADCCNF_ADCRPTC 3
#define NPCX_ADCCNF_INTECEN 6
#define NPCX_ADCCNF_START 4
#define NPCX_ADCCNF_ADCEN 0
#define NPCX_ADCCNF_STOP 11
-#define NPCX_CHNDAT_CHDAT 0
+#define NPCX_CHNDAT_CHDAT_FIELD FIELD(0, 10)
#define NPCX_CHNDAT_NEW 15
/******************************************************************************/
/* SPI Register */
@@ -1076,9 +1092,9 @@ enum PM_CHANNEL_T {
/* PWM register fields */
#define NPCX_PWMCTL_INVP 0
#define NPCX_PWMCTL_CKSEL 1
-#define NPCX_PWMCTL_HB_DC_CTL 2
+#define NPCX_PWMCTL_HB_DC_CTL_FIELD FIELD(2, 2)
#define NPCX_PWMCTL_PWR 7
-#define NPCX_PWMCTLEX_FCK_SEL 4
+#define NPCX_PWMCTLEX_FCK_SEL_FIELD FIELD(4, 2)
#define NPCX_PWMCTLEX_OD_OUT 7
/******************************************************************************/
/* MFT Registers */
@@ -1096,11 +1112,11 @@ enum PM_CHANNEL_T {
#define NPCX_TCFG(n) REG8(NPCX_MFT_BASE_ADDR(n) + 0x01C)
/* MFT register fields */
-#define NPCX_TMCTRL_MDSEL 0
+#define NPCX_TMCTRL_MDSEL_FIELD FIELD(0, 3)
#define NPCX_TCKC_LOW_PWR 7
#define NPCX_TCKC_PLS_ACC_CLK 6
-#define NPCX_TCKC_C1CSEL 0
-#define NPCX_TCKC_C2CSEL 3
+#define NPCX_TCKC_C1CSEL_FIELD FIELD(0, 3)
+#define NPCX_TCKC_C2CSEL_FIELD FIELD(3, 3)
#define NPCX_TMCTRL_TAEN 5
#define NPCX_TMCTRL_TBEN 6
#define NPCX_TMCTRL_TAEDG 3