diff options
author | Dino Li <dino.li@ite.com.tw> | 2015-06-24 11:43:29 +0800 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-06-25 05:32:30 +0000 |
commit | d5c43a880c7dadf76df152422f8b0f5e68455f61 (patch) | |
tree | c34a9357877ac5ae4b625b3a8c96824d68d5697f /chip | |
parent | 52ee6aa1312b71472196015a4bf70f384f9f27d4 (diff) | |
download | chrome-ec-d5c43a880c7dadf76df152422f8b0f5e68455f61.tar.gz |
it8380dev: add fan control module
1. pwm, add frequency select function for pwm channels.
2. timer, add external timer 3~8 apis.
3. add fan control module for emulation board.
Signed-off-by: Dino Li <dino.li@ite.com.tw>
BRANCH=none
BUG=none
TEST=console command "faninfo, fanset, fanduty, and fanauto"
fanset 3333
Setting fan 0 rpm target to 3333
faninfo
Actual: 3390 rpm
Target: 3333 rpm
Duty: 35%
Status: 1 (changing)
Mode: rpm
Auto: no
Enable: yes
faninfo
Actual: 3301 rpm
Target: 3333 rpm
Duty: 34%
Status: 2 (locked)
Mode: rpm
Auto: no
Enable: yes
fanduty 80
Setting fan 0 duty cycle to 80%
faninfo
Actual: 5952 rpm
Target: 3333 rpm
Duty: 80%
Status: 2 (locked)
Mode: duty
Auto: no
Enable: yes
faninfo
Actual: 5971 rpm
Target: 3333 rpm
Duty: 80%
Status: 2 (locked)
Mode: duty
Auto: no
Enable: yes
fanauto
faninfo
Actual: 3330 rpm
Target: 3333 rpm
Duty: 36%
Status: 2 (locked)
Mode: rpm
Auto: yes
Enable: yes
fanset 8000
Setting fan 0 rpm target to 8000
faninfo
Actual: 6793 rpm
Target: 8000 rpm
Duty: 100%
Status: 3 (frustrated)
Mode: rpm
Auto: no
Enable: yes
fanset 3456
Setting fan 0 rpm target to 3456
faninfo
Actual: 5053 rpm
Target: 3456 rpm
Duty: 56%
Status: 1 (changing)
Mode: rpm
Auto: no
Enable: yes
faninfo
Actual: 3440 rpm
Target: 3456 rpm
Duty: 34%
Status: 2 (locked)
Mode: rpm
Auto: no
Enable: yes
/* force stop the fan */
[87.035136 Fan 0 stalled!]
[87.035520 event set 0x00000400]
[88.035712 Fan 0 stalled!]
[89.036288 Fan 0 stalled!]
[90.036864 Fan 0 stalled!]
[91.037440 Fan 0 stalled!]
[92.038016 Fan 0 stalled!]
[93.038592 Fan 0 stalled!]
[94.039168 Fan 0 stalled!]
/* release */
faninfo
Actual: 3427 rpm
Target: 3456 rpm
Duty: 35%
Status: 2 (locked)
Mode: rpm
Auto: no
Enable: yes
Change-Id: Icbe1917902d033a8be42b8d834ffc6045d08b985
Reviewed-on: https://chromium-review.googlesource.com/266625
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Commit-Queue: Dino Li <dino.li@ite.com.tw>
Tested-by: Dino Li <dino.li@ite.com.tw>
Diffstat (limited to 'chip')
-rw-r--r-- | chip/it83xx/build.mk | 1 | ||||
-rw-r--r-- | chip/it83xx/fan.c | 475 | ||||
-rw-r--r-- | chip/it83xx/hwtimer.c | 128 | ||||
-rw-r--r-- | chip/it83xx/hwtimer_chip.h | 54 | ||||
-rw-r--r-- | chip/it83xx/pwm.c | 134 | ||||
-rw-r--r-- | chip/it83xx/pwm_chip.h | 52 | ||||
-rw-r--r-- | chip/it83xx/registers.h | 36 |
7 files changed, 855 insertions, 25 deletions
diff --git a/chip/it83xx/build.mk b/chip/it83xx/build.mk index 9cd0b5ff4a..280c6c5de8 100644 --- a/chip/it83xx/build.mk +++ b/chip/it83xx/build.mk @@ -16,6 +16,7 @@ chip-y=hwtimer.o uart.o gpio.o system.o jtag.o clock.o irq.o intc.o # Optional chip modules chip-$(CONFIG_WATCHDOG)+=watchdog.o +chip-$(CONFIG_FANS)+=fan.o pwm.o chip-$(CONFIG_PWM)+=pwm.o chip-$(CONFIG_ADC)+=adc.o chip-$(CONFIG_EC2I)+=ec2i.o diff --git a/chip/it83xx/fan.c b/chip/it83xx/fan.c new file mode 100644 index 0000000000..4e0e7ab6bd --- /dev/null +++ b/chip/it83xx/fan.c @@ -0,0 +1,475 @@ +/* Copyright 2015 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Fan control module. */ + +#include "clock.h" +#include "fan.h" +#include "gpio.h" +#include "hooks.h" +#include "registers.h" +#include "util.h" +#include "pwm.h" +#include "task.h" +#include "math_util.h" +#include "hwtimer_chip.h" +#include "pwm_chip.h" + +#define TACH_EC_FREQ 8000000 +#define FAN_CTRL_BASED_MS 10 +#define FAN_CTRL_INTERVAL_MAX_MS 60 + +/* The sampling rate (fs) is FreqEC / 128 */ +#define TACH_DATA_VALID_TIMEOUT_MS (0xFFFF * 128 / (TACH_EC_FREQ / 1000)) + +/* + * Fan Speed (RPM) = 60 / (1/fs sec * {FnTMRR, FnTLRR} * P) + * n denotes 1 or 2. + * P denotes the numbers of square pulses per revolution. + * And {FnTMRR, FnTLRR} = 0000h denotes Fan Speed is zero. + * The sampling rate (fs) is FreqEC / 128. + */ +/* pulse, the numbers of square pulses per revolution. */ +#define TACH0_TO_RPM(pulse, raw) (60 * TACH_EC_FREQ / 128 / pulse / raw) +#ifdef CHIP_FAMILY_IT839X +#define TACH1_TO_RPM(pulse, raw) (raw * 120 / (pulse * 2)) +#else +#define TACH1_TO_RPM(pulse, raw) (60 * TACH_EC_FREQ / 128 / pulse / raw) +#endif + +enum fan_output_s { + FAN_DUTY_I = 0x01, + FAN_DUTY_R = 0x02, + FAN_DUTY_OV = 0x03, + FAN_DUTY_DONE = 0x04, +}; + +struct fan_info { + unsigned int flags; + int fan_mode; + int fan_p; + int rpm_target; + int rpm_actual; + int tach_valid_ms; + int rpm_re; + int fan_ms; + int fan_ms_idx; + int startup_duty; + enum fan_status fan_sts; + int enabled; +}; +static struct fan_info fan_info_data[TACH_CH_COUNT]; + +static enum tach_ch_sel tach_bind(int ch) +{ + return fan_tach[pwm_channels[ch].channel].ch_tach; +} + +static void fan_set_interval(int ch) +{ + int diff, fan_ms; + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + diff = ABS(fan_info_data[tach_ch].rpm_target - + fan_info_data[tach_ch].rpm_actual) / 100; + + fan_ms = FAN_CTRL_INTERVAL_MAX_MS; + + fan_ms -= diff; + if (fan_ms < FAN_CTRL_BASED_MS) + fan_ms = FAN_CTRL_BASED_MS; + + fan_info_data[tach_ch].fan_ms = fan_ms; +} + +static void fan_init_start(int ch) +{ + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + if (tach_ch < TACH_CH_COUNT) + fan_set_duty(ch, fan_info_data[tach_ch].startup_duty); +} + +static int fan_all_disabled(void) +{ + int fan, all_disabled = 0; + + for (fan = 0; fan < CONFIG_FANS; fan++) { + if (!fan_get_enabled(fans[fan].ch)) + all_disabled++; + } + + if (all_disabled >= CONFIG_FANS) + return 1; + + return 0; +} + +void fan_set_enabled(int ch, int enabled) +{ + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + /* enable */ + if (enabled) { + if (tach_ch < TACH_CH_COUNT) + fan_info_data[tach_ch].fan_sts = FAN_STATUS_CHANGING; + + /* enable timer interrupt for fan control */ + ext_timer_start(FAN_CTRL_EXT_TIMER, 1); + /* disable */ + } else { + fan_set_duty(ch, 0); + + if (tach_ch < TACH_CH_COUNT) { + fan_info_data[tach_ch].rpm_actual = 0; + fan_info_data[tach_ch].fan_sts = FAN_STATUS_STOPPED; + } + /* disable timer interrupt if all fan off. */ + if (fan_all_disabled()) + ext_timer_stop(FAN_CTRL_EXT_TIMER, 1); + } + + /* on/off */ + if (tach_ch < TACH_CH_COUNT) { + fan_info_data[tach_ch].enabled = enabled; + fan_info_data[tach_ch].tach_valid_ms = 0; + } + + pwm_enable(ch, enabled); +} + +int fan_get_enabled(int ch) +{ + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + if (tach_ch < TACH_CH_COUNT) + return pwm_get_enabled(ch) && fan_info_data[tach_ch].enabled; + else + return 0; +} + +void fan_set_duty(int ch, int percent) +{ + pwm_set_duty(ch, percent); +} + +int fan_get_duty(int ch) +{ + return pwm_get_duty(ch); +} + +int fan_get_rpm_mode(int ch) +{ + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + if (tach_ch < TACH_CH_COUNT) + return fan_info_data[tach_ch].fan_mode; + else + return EC_ERROR_UNKNOWN; +} + +void fan_set_rpm_mode(int ch, int rpm_mode) +{ + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + if (tach_ch < TACH_CH_COUNT) + fan_info_data[tach_ch].fan_mode = rpm_mode; +} + +int fan_get_rpm_actual(int ch) +{ + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + if (tach_ch < TACH_CH_COUNT) + return fan_info_data[tach_ch].rpm_actual; + else + return EC_ERROR_UNKNOWN; +} + +int fan_get_rpm_target(int ch) +{ + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + if (tach_ch < TACH_CH_COUNT) + return fan_info_data[tach_ch].rpm_target; + else + return EC_ERROR_UNKNOWN; +} + +test_mockable void fan_set_rpm_target(int ch, int rpm) +{ + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + if (tach_ch < TACH_CH_COUNT) + fan_info_data[tach_ch].rpm_target = rpm; +} + +enum fan_status fan_get_status(int ch) +{ + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + if (tach_ch < TACH_CH_COUNT) + return fan_info_data[tach_ch].fan_sts; + else + return FAN_STATUS_STOPPED; +} + +/** + * Return non-zero if fan is enabled but stalled. + */ +int fan_is_stalled(int ch) +{ + /* Must be enabled with non-zero target to stall */ + if (!fan_get_enabled(ch) || + fan_get_rpm_target(ch) == 0 || + !fan_get_duty(ch)) + return 0; + + /* Check for stall condition */ + return fan_get_status(ch) == FAN_STATUS_STOPPED; +} + +void fan_channel_setup(int ch, unsigned int flags) +{ + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + if (tach_ch < TACH_CH_COUNT) + fan_info_data[tach_ch].flags = flags; +} + +static void fan_ctrl(int ch) +{ + int status = -1, adjust = 0; + int rpm_actual, rpm_target, rpm_re, duty; + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + fan_info_data[tach_ch].fan_ms_idx += FAN_CTRL_BASED_MS; + if (fan_info_data[tach_ch].fan_ms_idx > + fan_info_data[tach_ch].fan_ms) { + fan_info_data[tach_ch].fan_ms_idx = 0x00; + adjust = 1; + } + + if (adjust) { + /* get current pwm output duty */ + duty = fan_get_duty(ch); + + /* rpm mode */ + if (fan_info_data[tach_ch].fan_mode) { + rpm_actual = fan_info_data[tach_ch].rpm_actual; + rpm_target = fan_info_data[tach_ch].rpm_target; + rpm_re = fan_info_data[tach_ch].rpm_re; + + if (rpm_actual < (rpm_target - rpm_re)) { + if (duty == 100) { + status = FAN_DUTY_OV; + } else { + if (duty == 0) + fan_init_start(ch); + + pwm_duty_inc(ch); + status = FAN_DUTY_I; + } + } else if (rpm_actual > (rpm_target + rpm_re)) { + if (duty == 0) { + status = FAN_DUTY_OV; + } else { + pwm_duty_reduce(ch); + status = FAN_DUTY_R; + } + } else { + status = FAN_DUTY_DONE; + } + } else { + fan_info_data[tach_ch].fan_sts = FAN_STATUS_LOCKED; + } + + if (status == FAN_DUTY_DONE) { + fan_info_data[tach_ch].fan_sts = FAN_STATUS_LOCKED; + } else if ((status == FAN_DUTY_I) || (status == FAN_DUTY_R)) { + fan_info_data[tach_ch].fan_sts = FAN_STATUS_CHANGING; + } else if (status == FAN_DUTY_OV) { + fan_info_data[tach_ch].fan_sts = FAN_STATUS_FRUSTRATED; + + if (!fan_info_data[tach_ch].rpm_actual && duty) + fan_info_data[tach_ch].fan_sts = + FAN_STATUS_STOPPED; + } + } +} + +static int tach_ch_valid(enum tach_ch_sel tach_ch) +{ + int valid = 0; + + switch (tach_ch) { + case TACH_CH_TACH0A: + if ((IT83XX_PWM_TSWCTRL & 0x0C) == 0x08) + valid = 1; + break; + case TACH_CH_TACH1A: + if ((IT83XX_PWM_TSWCTRL & 0x03) == 0x02) + valid = 1; + break; + case TACH_CH_TACH0B: + if ((IT83XX_PWM_TSWCTRL & 0x0C) == 0x0C) + valid = 1; + break; + case TACH_CH_TACH1B: + if ((IT83XX_PWM_TSWCTRL & 0x03) == 0x03) + valid = 1; + break; + default: + break; + } + + return valid; +} + +static int get_tach0_rpm(int fan_p) +{ + uint16_t rpm; + + /* TACH0A / TACH0B data is valid */ + if (IT83XX_PWM_TSWCTRL & 0x08) { + rpm = (IT83XX_PWM_F1TMRR << 8) | IT83XX_PWM_F1TLRR; + + if (rpm) + rpm = TACH0_TO_RPM(fan_p, rpm); + + /* W/C */ + IT83XX_PWM_TSWCTRL |= 0x08; + return rpm; + } + return -1; +} + +static int get_tach1_rpm(int fan_p) +{ + uint16_t rpm; + + /* TACH1A / TACH1B data is valid */ + if (IT83XX_PWM_TSWCTRL & 0x02) { + rpm = (IT83XX_PWM_F2TMRR << 8) | IT83XX_PWM_F2TLRR; + + if (rpm) + rpm = TACH1_TO_RPM(fan_p, rpm); + + /* W/C */ + IT83XX_PWM_TSWCTRL |= 0x02; + return rpm; + } + return -1; +} + +static void proc_tach(int ch) +{ + int t_rpm; + enum tach_ch_sel tach_ch; + + tach_ch = tach_bind(ch); + + /* tachometer data valid */ + if (tach_ch_valid(tach_ch)) { + if ((tach_ch == TACH_CH_TACH0A) || (tach_ch == TACH_CH_TACH0B)) + t_rpm = get_tach0_rpm(fan_info_data[tach_ch].fan_p); + else + t_rpm = get_tach1_rpm(fan_info_data[tach_ch].fan_p); + + fan_info_data[tach_ch].rpm_actual = t_rpm; + fan_set_interval(ch); + fan_info_data[tach_ch].tach_valid_ms = 0; + } else { + fan_info_data[tach_ch].tach_valid_ms += FAN_CTRL_BASED_MS; + if (fan_info_data[tach_ch].tach_valid_ms > + TACH_DATA_VALID_TIMEOUT_MS) + fan_info_data[tach_ch].rpm_actual = 0; + } +} + +void fan_ext_timer_interrupt(void) +{ + int fan; + + task_clear_pending_irq(et_ctrl_regs[FAN_CTRL_EXT_TIMER].irq); + + for (fan = 0; fan < CONFIG_FANS; fan++) { + if (fan_get_enabled(fans[fan].ch)) { + proc_tach(fans[fan].ch); + fan_ctrl(fans[fan].ch); + } + } +} + +static void fan_init(void) +{ + int ch, rpm_re, fan_p, s_duty; + enum tach_ch_sel tach_ch; + + for (ch = 0; ch < CONFIG_FANS; ch++) { + + rpm_re = fan_tach[pwm_channels[fans[ch].ch].channel].rpm_re; + fan_p = fan_tach[pwm_channels[fans[ch].ch].channel].fan_p; + s_duty = fan_tach[pwm_channels[fans[ch].ch].channel].s_duty; + tach_ch = tach_bind(fans[ch].ch); + + if (tach_ch < TACH_CH_COUNT) { + + if (tach_ch == TACH_CH_TACH0B) { + /* GPJ2 will select TACH0B as its alt. */ + IT83XX_GPIO_GRC5 |= 0x01; + /* bit2, to select TACH0B */ + IT83XX_PWM_TSWCTRL |= 0x04; + } else if (tach_ch == TACH_CH_TACH1B) { + /* GPJ3 will select TACH1B as its alt. */ + IT83XX_GPIO_GRC5 |= 0x02; + /* bit0, to select TACH1B */ + IT83XX_PWM_TSWCTRL |= 0x01; + } + + fan_info_data[tach_ch].flags = 0; + fan_info_data[tach_ch].fan_mode = 0; + fan_info_data[tach_ch].rpm_target = 0; + fan_info_data[tach_ch].rpm_actual = 0; + fan_info_data[tach_ch].tach_valid_ms = 0; + fan_info_data[tach_ch].fan_ms_idx = 0; + fan_info_data[tach_ch].enabled = 0; + fan_info_data[tach_ch].fan_p = fan_p; + fan_info_data[tach_ch].rpm_re = rpm_re; + fan_info_data[tach_ch].fan_ms = FAN_CTRL_BASED_MS; + fan_info_data[tach_ch].fan_sts = FAN_STATUS_STOPPED; + fan_info_data[tach_ch].startup_duty = s_duty; + } + } + + /* init external timer for fan control */ + ext_timer_ms(FAN_CTRL_EXT_TIMER, EXT_PSR_32P768K_HZ, 0, 0, + FAN_CTRL_BASED_MS, 1); +} +DECLARE_HOOK(HOOK_INIT, fan_init, HOOK_PRIO_INIT_PWM); diff --git a/chip/it83xx/hwtimer.c b/chip/it83xx/hwtimer.c index 3493843adc..1a5643ea2e 100644 --- a/chip/it83xx/hwtimer.c +++ b/chip/it83xx/hwtimer.c @@ -14,6 +14,7 @@ #include "timer.h" #include "util.h" #include "watchdog.h" +#include "hwtimer_chip.h" /* 128us (2^7 us) between 2 ticks */ #define TICK_INTERVAL_LOG2 7 @@ -21,6 +22,8 @@ #define TICK_INTERVAL (1 << TICK_INTERVAL_LOG2) #define TICK_INTERVAL_MASK (TICK_INTERVAL - 1) +#define MS_TO_COUNT(hz, ms) ((hz) * (ms) / 1000) + /* * Tick interval must fit in one byte, and must be greater than two * so that the duty cycle does not equal the cycle time (IT83XX_TMR_DCR_B0 must @@ -37,6 +40,22 @@ static volatile uint32_t time_us; */ static uint32_t next_event_time; +const struct ext_timer_ctrl_t et_ctrl_regs[] = { + {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, 0x08, + IT83XX_IRQ_EXT_TIMER3}, + {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, 0x10, + IT83XX_IRQ_EXT_TIMER4}, + {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, 0x20, + IT83XX_IRQ_EXT_TIMER5}, + {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, 0x40, + IT83XX_IRQ_EXT_TIMER6}, + {&IT83XX_INTC_IELMR19, &IT83XX_INTC_IPOLR19, 0x80, + IT83XX_IRQ_EXT_TIMER7}, + {&IT83XX_INTC_IELMR10, &IT83XX_INTC_IPOLR10, 0x01, + IT83XX_IRQ_EXT_TMR8}, +}; +BUILD_ASSERT(ARRAY_SIZE(et_ctrl_regs) == EXT_TIMER_COUNT); + void __hw_clock_event_set(uint32_t deadline) { next_event_time = deadline; @@ -65,7 +84,7 @@ void __hw_clock_source_set(uint32_t ts) static void __hw_clock_source_irq(void) { -#ifdef CONFIG_WATCHDOG +#if defined(CONFIG_WATCHDOG) || defined(CONFIG_FANS) /* Determine interrupt number. */ int irq = IT83XX_INTC_IVCT3 - 16; #endif @@ -91,7 +110,17 @@ static void __hw_clock_source_irq(void) } #endif - /* clear interrupt status */ +#ifdef CONFIG_FANS + if (irq == et_ctrl_regs[FAN_CTRL_EXT_TIMER].irq) { + fan_ext_timer_interrupt(); + return; + } +#endif + + /* + * If we're still here, this is actually a hardware interrupt for the + * clock source. Clear its interrupt status and update time_us. + */ task_clear_pending_irq(IT83XX_IRQ_TMR_B0); time_us += TICK_INTERVAL; @@ -186,3 +215,98 @@ void udelay(unsigned us) while (waits-- >= 0) IT83XX_GCTRL_WNCKR = 0; } + +void ext_timer_start(enum ext_timer_sel ext_timer, int en_irq) +{ + /* enable external timer n */ + IT83XX_ETWD_ETXCTRL(ext_timer) |= 0x01; + + if (en_irq) { + task_clear_pending_irq(et_ctrl_regs[ext_timer].irq); + task_enable_irq(et_ctrl_regs[ext_timer].irq); + } +} + +void ext_timer_stop(enum ext_timer_sel ext_timer, int dis_irq) +{ + /* disable external timer n */ + IT83XX_ETWD_ETXCTRL(ext_timer) &= ~0x01; + + if (dis_irq) + task_disable_irq(et_ctrl_regs[ext_timer].irq); +} + +static void ext_timer_ctrl(enum ext_timer_sel ext_timer, + enum ext_timer_clock_source ext_timer_clock, + int start, + int with_int, + int32_t count) +{ + uint8_t intc_mask; + + /* rising-edge-triggered */ + intc_mask = et_ctrl_regs[ext_timer].mask; + *et_ctrl_regs[ext_timer].mode |= intc_mask; + *et_ctrl_regs[ext_timer].polarity &= ~intc_mask; + + /* clear interrupt status */ + task_clear_pending_irq(et_ctrl_regs[ext_timer].irq); + + /* These bits control the clock input source to the exttimer 3 - 8 */ + IT83XX_ETWD_ETXPSR(ext_timer) = ext_timer_clock; + + /* The count number of external timer n. */ + IT83XX_ETWD_ETXCNTLH2R(ext_timer) = (count >> 16) & 0xFF; + IT83XX_ETWD_ETXCNTLHR(ext_timer) = (count >> 8) & 0xFF; + IT83XX_ETWD_ETXCNTLLR(ext_timer) = count & 0xFF; + + ext_timer_stop(ext_timer, 0); + if (start) + ext_timer_start(ext_timer, 0); + + if (with_int) + task_enable_irq(et_ctrl_regs[ext_timer].irq); + else + task_disable_irq(et_ctrl_regs[ext_timer].irq); +} + +int ext_timer_ms(enum ext_timer_sel ext_timer, + enum ext_timer_clock_source ext_timer_clock, + int start, + int with_int, + int32_t ms, + int first_time_enable) +{ + uint32_t count; + + if (ext_timer_clock == EXT_PSR_32P768K_HZ) + count = MS_TO_COUNT(32768, ms); + else if (ext_timer_clock == EXT_PSR_1P024K_HZ) + count = MS_TO_COUNT(1024, ms); + else if (ext_timer_clock == EXT_PSR_32_HZ) + count = MS_TO_COUNT(32, ms); + else if (ext_timer_clock == EXT_PSR_8M_HZ) + count = 8000 * ms; + else + return -1; + + /* + * IT838X support 24-bits external timer only, + * IT839X support three(4, 6, and 8) 32-bit external timers, + * implemented later. + */ + if (count >> 24) + return -2; + + if (count == 0) + return -3; + + if (first_time_enable) { + ext_timer_start(ext_timer, 0); + ext_timer_stop(ext_timer, 0); + } + + ext_timer_ctrl(ext_timer, ext_timer_clock, start, with_int, count); + + return 0; +} diff --git a/chip/it83xx/hwtimer_chip.h b/chip/it83xx/hwtimer_chip.h new file mode 100644 index 0000000000..9217545c5b --- /dev/null +++ b/chip/it83xx/hwtimer_chip.h @@ -0,0 +1,54 @@ +/* Copyright 2015 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* External timers control module for IT83xx. */ + +#ifndef __CROS_EC_HWTIMER_CHIP_H +#define __CROS_EC_HWTIMER_CHIP_H + +#define FAN_CTRL_EXT_TIMER EXT_TIMER_5 + +enum ext_timer_clock_source { + EXT_PSR_32P768K_HZ = 0, + EXT_PSR_1P024K_HZ = 1, + EXT_PSR_32_HZ = 2, + EXT_PSR_8M_HZ = 3 +}; + +enum ext_timer_sel { + /* For WDT capture important state information before being reset */ + EXT_TIMER_3 = 0, + /* reserved */ + EXT_TIMER_4, + /* For fan control */ + EXT_TIMER_5, + /* reserved */ + EXT_TIMER_6, + /* reserved */ + EXT_TIMER_7, + /* reserved */ + EXT_TIMER_8, + EXT_TIMER_COUNT, +}; + +struct ext_timer_ctrl_t { + volatile uint8_t *mode; + volatile uint8_t *polarity; + uint8_t mask; + uint8_t irq; +}; + +extern const struct ext_timer_ctrl_t et_ctrl_regs[]; +void ext_timer_start(enum ext_timer_sel ext_timer, int en_irq); +void ext_timer_stop(enum ext_timer_sel ext_timer, int dis_irq); +void fan_ext_timer_interrupt(void); +int ext_timer_ms(enum ext_timer_sel ext_timer, + enum ext_timer_clock_source ext_timer_clock, + int start, + int et_int, + int32_t ms, + int first_time_enable); + +#endif /* __CROS_EC_HWTIMER_CHIP_H */ diff --git a/chip/it83xx/pwm.c b/chip/it83xx/pwm.c index 6d705f5c46..d027a366cc 100644 --- a/chip/it83xx/pwm.c +++ b/chip/it83xx/pwm.c @@ -13,6 +13,10 @@ #include "pwm_chip.h" #include "registers.h" #include "util.h" +#include "math_util.h" + +#define PWM_CTRX_MIN 120 +#define PWM_EC_FREQ 8000000 const struct pwm_ctrl_t pwm_ctrl_regs[] = { { &IT83XX_PWM_DCR0, &IT83XX_PWM_PCSSGL, &IT83XX_GPIO_GPCRA0}, @@ -36,6 +40,31 @@ const struct pwm_ctrl_t2 pwm_clock_ctrl_regs[] = { &IT83XX_PWM_PCFSR, 0x08}, }; +static int pwm_get_cycle_time(enum pwm_channel ch) +{ + int pcs_shift; + int pcs_mask; + int pcs_reg; + int cycle_time_setting; + + /* pwm channel mapping */ + ch = pwm_channels[ch].channel; + + /* bit shift for "Prescaler Clock Source Select Group" register. */ + pcs_shift = (ch % 4) * 2; + + /* setting of "Prescaler Clock Source Select Group" register. */ + pcs_reg = *pwm_ctrl_regs[ch].pwm_clock_source; + + /* only bit0 bit1 information. */ + pcs_mask = (pcs_reg >> pcs_shift) & 0x03; + + /* get cycle time setting of PWM channel x. */ + cycle_time_setting = *pwm_clock_ctrl_regs[pcs_mask].pwm_cycle_time; + + return cycle_time_setting; +} + void pwm_enable(enum pwm_channel ch, int enabled) { /* pwm channel mapping */ @@ -73,6 +102,9 @@ void pwm_set_duty(enum pwm_channel ch, int percent) else if (percent > 100) percent = 100; + if (pwm_channels[ch].flags & PWM_CONFIG_ACTIVE_LOW) + percent = 100 - percent; + /* pwm channel mapping */ ch = pwm_channels[ch].channel; @@ -88,9 +120,6 @@ void pwm_set_duty(enum pwm_channel ch, int percent) /* get cycle time setting of PWM channel x. */ cycle_time_setting = *pwm_clock_ctrl_regs[pcs_mask].pwm_cycle_time; - if (pwm_channels[ch].flags & PWM_CONFIG_ACTIVE_LOW) - percent = 100 - percent; - /* to update PWM DCRx depend on CTRx setting. */ if (percent == 100) { *pwm_ctrl_regs[ch].pwm_duty = cycle_time_setting; @@ -106,6 +135,9 @@ int pwm_get_duty(enum pwm_channel ch) int pcs_reg; int cycle_time_setting; int percent; + int ch_idx; + + ch_idx = ch; /* pwm channel mapping */ ch = pwm_channels[ch].channel; @@ -121,23 +153,105 @@ int pwm_get_duty(enum pwm_channel ch) percent = *pwm_ctrl_regs[ch].pwm_duty * 100 / cycle_time_setting; - if (pwm_channels[ch].flags & PWM_CONFIG_ACTIVE_LOW) + if (pwm_channels[ch_idx].flags & PWM_CONFIG_ACTIVE_LOW) percent = 100 - percent; /* output signal duty cycle. */ return percent; } +void pwm_duty_inc(enum pwm_channel ch) +{ + int cycle_time, pwm_ch; + + /* pwm channel mapping */ + pwm_ch = pwm_channels[ch].channel; + + cycle_time = pwm_get_cycle_time(ch); + + if (pwm_channels[ch].flags & PWM_CONFIG_ACTIVE_LOW) { + if (*pwm_ctrl_regs[pwm_ch].pwm_duty > 0) + *pwm_ctrl_regs[pwm_ch].pwm_duty -= 1; + } else { + if (*pwm_ctrl_regs[pwm_ch].pwm_duty < cycle_time) + *pwm_ctrl_regs[pwm_ch].pwm_duty += 1; + } +} + +void pwm_duty_reduce(enum pwm_channel ch) +{ + int cycle_time, pwm_ch; + + /* pwm channel mapping */ + pwm_ch = pwm_channels[ch].channel; + + cycle_time = pwm_get_cycle_time(ch); + + if (pwm_channels[ch].flags & PWM_CONFIG_ACTIVE_LOW) { + if (*pwm_ctrl_regs[pwm_ch].pwm_duty < cycle_time) + *pwm_ctrl_regs[pwm_ch].pwm_duty += 1; + } else { + if (*pwm_ctrl_regs[pwm_ch].pwm_duty > 0) + *pwm_ctrl_regs[pwm_ch].pwm_duty -= 1; + } +} + +static int pwm_ch_freq(enum pwm_channel ch) +{ + int actual_freq = -1, targe_freq, deviation; + int pcfsr, ctr, pcfsr_sel, pcs_shift, pcs_mask; + + targe_freq = pwm_channels[ch].freq_hz; + deviation = (targe_freq / 100) + 1; + + for (ctr = 0xFF; ctr > PWM_CTRX_MIN; ctr--) { + pcfsr = (PWM_EC_FREQ / (ctr + 1) / targe_freq) - 1; + if (pcfsr >= 0) { + actual_freq = PWM_EC_FREQ / (ctr + 1) / (pcfsr + 1); + if (ABS(actual_freq - targe_freq) < deviation) + break; + } + } + + if (ctr < PWM_CTRX_MIN) { + actual_freq = -1; + } else { + pcfsr_sel = pwm_channels[ch].pcfsr_sel; + *pwm_clock_ctrl_regs[pcfsr_sel].pwm_cycle_time = ctr; + /* ec clock 8MHz */ + *pwm_clock_ctrl_regs[pcfsr_sel].pwm_pcfsr_reg |= + pwm_clock_ctrl_regs[pcfsr_sel].pwm_pcfsr_ctrl; + + /* pwm channel mapping */ + ch = pwm_channels[ch].channel; + + /* + * bit shift for "Prescaler Clock Source Select Group" + * register. + */ + pcs_shift = (ch % 4) * 2; + pcs_mask = pcfsr_sel << pcs_shift; + + *pwm_ctrl_regs[ch].pwm_clock_source &= ~(0x3 << pcs_shift); + *pwm_ctrl_regs[ch].pwm_clock_source |= pcs_mask; + + *pwm_clock_ctrl_regs[pcfsr_sel].pwm_cpr_lsb = pcfsr & 0xFF; + *pwm_clock_ctrl_regs[pcfsr_sel].pwm_cpr_msb = + (pcfsr >> 8) & 0xFF; + } + + return actual_freq; +} + static void pwm_init(void) { + int ch; + + for (ch = 0; ch < PWM_CH_COUNT; ch++) + pwm_ch_freq(ch); + /* enable PWMs clock counter. */ IT83XX_PWM_ZTIER |= 0x02; - - /* 0.5 resolution */ - IT83XX_PWM_CTR = 200; - IT83XX_PWM_CTR1 = 200; - IT83XX_PWM_CTR2 = 200; - IT83XX_PWM_CTR3 = 200; } /* The chip PWM module initialization. */ diff --git a/chip/it83xx/pwm_chip.h b/chip/it83xx/pwm_chip.h index 3c17b00125..22f2076432 100644 --- a/chip/it83xx/pwm_chip.h +++ b/chip/it83xx/pwm_chip.h @@ -8,6 +8,40 @@ #ifndef __CROS_EC_PWM_CHIP_H #define __CROS_EC_PWM_CHIP_H +enum pwm_pcfsr_sel { + PWM_PRESCALER_C4 = 1, + PWM_PRESCALER_C6 = 2, + PWM_PRESCALER_C7 = 3, +}; + +enum pwm_hw_channel { + PWM_HW_CH_DCR0 = 0, + PWM_HW_CH_DCR1, + PWM_HW_CH_DCR2, + PWM_HW_CH_DCR3, + PWM_HW_CH_DCR4, + PWM_HW_CH_DCR5, + PWM_HW_CH_DCR6, + PWM_HW_CH_DCR7, + + PWM_HW_CH_TOTAL, +}; + +enum tach_ch_sel { + /* Pin GPIOD.6 */ + TACH_CH_TACH0A = 0, + /* Pin GPIOD.7 */ + TACH_CH_TACH1A, + /* Pin GPIOJ.2 */ + TACH_CH_TACH0B, + /* Pin GPIOJ.3 */ + TACH_CH_TACH1B, + /* Number of TACH channels */ + TACH_CH_COUNT, + + TACH_CH_NULL = 0xFF, +}; + /* Data structure to define PWM channel control registers. */ struct pwm_ctrl_t { /* PWM channel output duty register. */ @@ -38,8 +72,26 @@ struct pwm_t { int channel; /* PWM channel flags. See include/pwm.h */ uint32_t flags; + int freq_hz; + enum pwm_pcfsr_sel pcfsr_sel; +}; + +/* Tachometer channel of each physical fan */ +struct fan_tach_t { + enum tach_ch_sel ch_tach; + /* the numbers of square pulses per revolution of fan. */ + int fan_p; + /* allow actual rpm ~= targe rpm +- rpm_re */ + int rpm_re; + /* startup duty of fan */ + int s_duty; }; extern const struct pwm_t pwm_channels[]; +/* The list of tachometer channel of fans is instantiated in board.c. */ +extern const struct fan_tach_t fan_tach[]; + +void pwm_duty_inc(enum pwm_channel ch); +void pwm_duty_reduce(enum pwm_channel ch); #endif /* __CROS_EC_PWM_CHIP_H */ diff --git a/chip/it83xx/registers.h b/chip/it83xx/registers.h index c7d4e58c0b..bc7ddeee44 100644 --- a/chip/it83xx/registers.h +++ b/chip/it83xx/registers.h @@ -358,6 +358,11 @@ #define IT83XX_INTC_ISR19 REG8(IT83XX_INTC_BASE+0x50) #define IT83XX_INTC_ISR20 REG8(IT83XX_INTC_BASE+0x54) +#define IT83XX_INTC_IELMR10 REG8(IT83XX_INTC_BASE+0x2E) +#define IT83XX_INTC_IPOLR10 REG8(IT83XX_INTC_BASE+0x2F) +#define IT83XX_INTC_IELMR19 REG8(IT83XX_INTC_BASE+0x52) +#define IT83XX_INTC_IPOLR19 REG8(IT83XX_INTC_BASE+0x53) + #define IT83XX_INTC_EXT_IER0 REG8(IT83XX_INTC_BASE+0x60) #define IT83XX_INTC_EXT_IER1 REG8(IT83XX_INTC_BASE+0x61) #define IT83XX_INTC_EXT_IER2 REG8(IT83XX_INTC_BASE+0x62) @@ -571,19 +576,24 @@ enum clock_gate_offsets { /* --- External Timer and Watchdog (ETWD) --- */ #define IT83XX_ETWD_BASE 0x00F01F00 -#define IT83XX_ETWD_ETWCFG REG8(IT83XX_ETWD_BASE+0x01) -#define IT83XX_ETWD_ET1PSR REG8(IT83XX_ETWD_BASE+0x02) -#define IT83XX_ETWD_ET1CNTLHR REG8(IT83XX_ETWD_BASE+0x03) -#define IT83XX_ETWD_ET1CNTLLR REG8(IT83XX_ETWD_BASE+0x04) -#define IT83XX_ETWD_ETWCTRL REG8(IT83XX_ETWD_BASE+0x05) -#define IT83XX_ETWD_EWDCNTLLR REG8(IT83XX_ETWD_BASE+0x06) -#define IT83XX_ETWD_EWDKEYR REG8(IT83XX_ETWD_BASE+0x07) -#define IT83XX_ETWD_EWDCNTLHR REG8(IT83XX_ETWD_BASE+0x09) -#define IT83XX_ETWD_ET3CTRL REG8(IT83XX_ETWD_BASE+0x10) -#define IT83XX_ETWD_ET3PSR REG8(IT83XX_ETWD_BASE+0x11) -#define IT83XX_ETWD_ET3CNTLLR REG8(IT83XX_ETWD_BASE+0x14) -#define IT83XX_ETWD_ET3CNTLHR REG8(IT83XX_ETWD_BASE+0x15) -#define IT83XX_ETWD_ET3CNTLH2R REG8(IT83XX_ETWD_BASE+0x16) +#define IT83XX_ETWD_ETWCFG REG8(IT83XX_ETWD_BASE+0x01) +#define IT83XX_ETWD_ET1PSR REG8(IT83XX_ETWD_BASE+0x02) +#define IT83XX_ETWD_ET1CNTLHR REG8(IT83XX_ETWD_BASE+0x03) +#define IT83XX_ETWD_ET1CNTLLR REG8(IT83XX_ETWD_BASE+0x04) +#define IT83XX_ETWD_ETWCTRL REG8(IT83XX_ETWD_BASE+0x05) +#define IT83XX_ETWD_EWDCNTLLR REG8(IT83XX_ETWD_BASE+0x06) +#define IT83XX_ETWD_EWDKEYR REG8(IT83XX_ETWD_BASE+0x07) +#define IT83XX_ETWD_EWDCNTLHR REG8(IT83XX_ETWD_BASE+0x09) +#define IT83XX_ETWD_ET3CTRL REG8(IT83XX_ETWD_BASE+0x10) +#define IT83XX_ETWD_ET3PSR REG8(IT83XX_ETWD_BASE+0x11) +#define IT83XX_ETWD_ET3CNTLLR REG8(IT83XX_ETWD_BASE+0x14) +#define IT83XX_ETWD_ET3CNTLHR REG8(IT83XX_ETWD_BASE+0x15) +#define IT83XX_ETWD_ET3CNTLH2R REG8(IT83XX_ETWD_BASE+0x16) +#define IT83XX_ETWD_ETXCTRL(n) REG8(IT83XX_ETWD_BASE + 0x10 + (n << 3)) +#define IT83XX_ETWD_ETXPSR(n) REG8(IT83XX_ETWD_BASE + 0x11 + (n << 3)) +#define IT83XX_ETWD_ETXCNTLLR(n) REG8(IT83XX_ETWD_BASE + 0x14 + (n << 3)) +#define IT83XX_ETWD_ETXCNTLHR(n) REG8(IT83XX_ETWD_BASE + 0x15 + (n << 3)) +#define IT83XX_ETWD_ETXCNTLH2R(n) REG8(IT83XX_ETWD_BASE + 0x16 + (n << 3)) /* --- General Control (GCTRL) --- */ #define IT83XX_GCTRL_BASE 0x00F02000 |