summaryrefslogtreecommitdiff
path: root/chip/stm32/pwm.c
diff options
context:
space:
mode:
authorVic Yang <victoryang@chromium.org>2013-08-05 11:17:35 +0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2013-08-27 23:20:33 +0000
commit5d014fd2dd5e92ad35a4f2dd1b58e0b1baebb65e (patch)
tree2078f9831177392f22ba202cbea4b09d69a9a710 /chip/stm32/pwm.c
parent99f06c39aabd262653c96c589c5cd26fc1fb8389 (diff)
downloadchrome-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.c154
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);