diff options
author | Mulin Chao <mlchao@nuvoton.com> | 2015-09-19 08:59:29 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2015-09-29 21:11:41 -0700 |
commit | 7e7dd8cd1aa232e2790ddfe09bffbe908f3d0230 (patch) | |
tree | 5aa2e13b38519396e04b5ca679ccaac071dab50b | |
parent | dbef9a6fed88c78795a5060dead823978394cf6f (diff) | |
download | chrome-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.c | 49 | ||||
-rw-r--r-- | board/npcx_evb/board.h | 3 | ||||
-rw-r--r-- | board/npcx_evb/gpio.inc | 3 | ||||
-rw-r--r-- | board/npcx_evb_arm/board.c | 49 | ||||
-rw-r--r-- | board/npcx_evb_arm/board.h | 3 | ||||
-rw-r--r-- | board/npcx_evb_arm/gpio.inc | 5 | ||||
-rw-r--r-- | chip/npcx/adc.c | 29 | ||||
-rw-r--r-- | chip/npcx/adc_chip.h | 12 | ||||
-rw-r--r-- | chip/npcx/build.mk | 2 | ||||
-rw-r--r-- | chip/npcx/fan.c | 475 | ||||
-rw-r--r-- | chip/npcx/fan_chip.h | 45 | ||||
-rw-r--r-- | chip/npcx/pwm.c | 193 | ||||
-rw-r--r-- | chip/npcx/pwm_chip.h | 10 | ||||
-rw-r--r-- | chip/npcx/registers.h | 36 |
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 |