diff options
author | Vic Yang <victoryang@chromium.org> | 2013-08-05 11:17:35 +0800 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2013-08-27 23:20:33 +0000 |
commit | 5d014fd2dd5e92ad35a4f2dd1b58e0b1baebb65e (patch) | |
tree | 2078f9831177392f22ba202cbea4b09d69a9a710 /chip/stm32/pwm.c | |
parent | 99f06c39aabd262653c96c589c5cd26fc1fb8389 (diff) | |
download | chrome-ec-5d014fd2dd5e92ad35a4f2dd1b58e0b1baebb65e.tar.gz |
Refactor PWM module
This unifies the PWM module interface for LM4 and STM32. Now PWM
channels are defined in board.h/board.c. Instead of calling functions
named pwm_set_fan_duty(x), one can now use pwm_set_duty(PWM_CH_FAN, x),
which prevents additional functions added when we have a new PWM
channel.
BUG=chrome-os-partner:18343
TEST=Limit input current on Spring.
TEST=Check power LED in S0/S3/S5 on Snow.
TEST=Check keyboard backlight functionality on Link.
TEST=Check fan speed control/detecting on Link.
BRANCH=None
Change-Id: Ibac4d79f72e65c94776d503558a7592f7db859dc
Signed-off-by: Vic Yang <victoryang@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/64450
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Diffstat (limited to 'chip/stm32/pwm.c')
-rw-r--r-- | chip/stm32/pwm.c | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/chip/stm32/pwm.c b/chip/stm32/pwm.c new file mode 100644 index 0000000000..73db55334f --- /dev/null +++ b/chip/stm32/pwm.c @@ -0,0 +1,154 @@ +/* Copyright (c) 2013 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. + */ + +/* PWM control module for STM32 */ + +#include "clock.h" +#include "gpio.h" +#include "hooks.h" +#include "hwtimer.h" +#include "pwm.h" +#include "pwm_data.h" +#include "registers.h" +#include "util.h" + +static int using_pwm[PWM_CH_COUNT]; + +void pwm_set_duty(enum pwm_channel ch, int percent) +{ + const struct pwm_t *pwm = pwm_channels + ch; + timer_ctlr_t *tim = (timer_ctlr_t *)(pwm->tim.base); + + ASSERT((percent >= 0) && (percent <= 100)); + tim->ccr[pwm->channel] = percent; +} + +int pwm_get_duty(enum pwm_channel ch) +{ + const struct pwm_t *pwm = pwm_channels + ch; + timer_ctlr_t *tim = (timer_ctlr_t *)(pwm->tim.base); + return tim->ccr[pwm->channel]; +} + +static void pwm_configure(enum pwm_channel ch) +{ + const struct pwm_t *pwm = pwm_channels + ch; + const struct gpio_info *gpio = gpio_list + pwm->pin; + timer_ctlr_t *tim = (timer_ctlr_t *)(pwm->tim.base); + volatile unsigned *ccmr = NULL; +#ifdef CHIP_FAMILY_stm32f + int mask = gpio->mask; + volatile uint32_t *gpio_cr = NULL; + uint32_t val; +#endif + + if (using_pwm[ch]) + return; + +#ifdef CHIP_FAMILY_stm32f + if (mask < 0x100) { + gpio_cr = &STM32_GPIO_CRL(gpio->port); + } else { + gpio_cr = &STM32_GPIO_CRH(gpio->port); + mask >>= 8; + } + + /* Expand mask from 8-bit to 32-bit */ + mask = mask * mask; + mask = mask * mask; + + /* Set alternate function */ + val = *gpio_cr & ~(mask * 0xf); + val |= mask * 0x9; + *gpio_cr = val; +#else /* stm32l */ + gpio_set_alternate_function(gpio->port, gpio->mask, + GPIO_ALT_TIM(pwm->tim.id)); +#endif + + /* Enable timer */ + __hw_timer_enable_clock(pwm->tim.id, 1); + + /* Disable counter during setup */ + tim->cr1 = 0x0000; + + /* + * CPU clock / PSC determines how fast the counter operates. + * ARR determines the wave period, CCRn determines duty cycle. + * Thus, frequency = cpu_freq / PSC / ARR. so: + * + * frequency = cpu_freq / (cpu_freq/10000) / 100 = 100 Hz. + */ + tim->psc = clock_get_freq() / 10000; + tim->arr = 100; + + if (pwm->channel <= 2) /* Channel ID starts from 1 */ + ccmr = &tim->ccmr1; + else + ccmr = &tim->ccmr2; + + /* Output, PWM mode 1, preload enable */ + if (pwm->channel & 0x1) + *ccmr = (6 << 4) | (1 << 3); + else + *ccmr = (6 << 12) | (1 << 11); + + /* Output enable. Set active high/low. */ + if (pwm->flags & PWM_CONFIG_ACTIVE_LOW) + tim->ccer = 3 << (pwm->channel * 4 - 4); + else + tim->ccer = 1 << (pwm->channel * 4 - 4); + + /* Generate update event to force loading of shadow registers */ + tim->egr |= 1; + + /* Enable auto-reload preload, start counting */ + tim->cr1 |= (1 << 7) | (1 << 0); + + using_pwm[ch] = 1; +} + +static void pwm_disable(enum pwm_channel ch) +{ + const struct pwm_t *pwm = pwm_channels + ch; + timer_ctlr_t *tim = (timer_ctlr_t *)(pwm->tim.base); + + if (using_pwm[ch] == 0) + return; + + /* Disable counter */ + tim->cr1 &= ~0x1; + + /* Disable timer clock */ + __hw_timer_enable_clock(pwm->tim.id, 0); + + using_pwm[ch] = 0; +} + +void pwm_enable(enum pwm_channel ch, int enabled) +{ + if (enabled) + pwm_configure(ch); + else + pwm_disable(ch); +} + +static void pwm_reconfigure(enum pwm_channel ch) +{ + using_pwm[ch] = 0; + pwm_configure(ch); +} + +/** + * Handle clock frequency change + */ +static void pwm_freq_change(void) +{ + int i; + for (i = 0; i < PWM_CH_COUNT; ++i) + if (using_pwm[i]) + pwm_reconfigure(i); +} +DECLARE_HOOK(HOOK_FREQ_CHANGE, pwm_freq_change, HOOK_PRIO_DEFAULT); |