coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/stm32/pwm.c

164 lines
3.6 KiB
C

/* Copyright 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_chip.h"
#include "registers.h"
#include "system.h"
#include "util.h"
/* Bitmap of currently active PWM channels. 1 bit per channel. */
static uint32_t using_pwm;
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;
timer_ctlr_t *tim = (timer_ctlr_t *)(pwm->tim.base);
volatile unsigned *ccmr = NULL;
/* Default frequency = 100 Hz */
int frequency = pwm->frequency ? pwm->frequency : 100;
uint16_t ccer;
if (using_pwm & BIT(ch))
return;
/* 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 + 1) / (99 + 1) = 100 Hz.
*/
tim->psc = clock_get_freq() / (frequency * 100) - 1;
tim->arr = 99;
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) | BIT(3);
else
*ccmr = (6 << 12) | BIT(11);
/* Output enable. Set active high/low. */
if (pwm->flags & PWM_CONFIG_ACTIVE_LOW)
ccer = 3 << (pwm->channel * 4 - 4);
else
ccer = 1 << (pwm->channel * 4 - 4);
/* Enable complementary output, if present. */
if (pwm->flags & PWM_CONFIG_COMPLEMENTARY_OUTPUT)
ccer |= (ccer << 2);
tim->ccer = ccer;
/*
* Main output enable.
* TODO(shawnn): BDTR is undocumented on STM32L. Verify this isn't
* harmful on STM32L.
*/
tim->bdtr |= BIT(15);
/* Generate update event to force loading of shadow registers */
tim->egr |= 1;
/* Enable auto-reload preload, start counting */
tim->cr1 |= BIT(7) | BIT(0);
atomic_or(&using_pwm, 1 << ch);
/* Prevent sleep */
disable_sleep(SLEEP_MASK_PWM);
}
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 & BIT(ch)) == 0)
return;
/* Main output disable */
tim->bdtr &= ~BIT(15);
/* Disable counter */
tim->cr1 &= ~0x1;
/* Disable timer clock */
__hw_timer_enable_clock(pwm->tim.id, 0);
/* Allow sleep */
enable_sleep(SLEEP_MASK_PWM);
atomic_clear(&using_pwm, 1 << ch);
/* Unless another PWM is active... Then prevent sleep */
if (using_pwm)
disable_sleep(SLEEP_MASK_PWM);
}
void pwm_enable(enum pwm_channel ch, int enabled)
{
if (enabled)
pwm_configure(ch);
else
pwm_disable(ch);
}
int pwm_get_enabled(enum pwm_channel ch)
{
return using_pwm & BIT(ch);
}
static void pwm_reconfigure(enum pwm_channel ch)
{
atomic_clear(&using_pwm, 1 << ch);
pwm_configure(ch);
}
/**
* Handle clock frequency change
*/
static void pwm_freq_change(void)
{
int i;
for (i = 0; i < PWM_CH_COUNT; ++i)
if (pwm_get_enabled(i))
pwm_reconfigure(i);
}
DECLARE_HOOK(HOOK_FREQ_CHANGE, pwm_freq_change, HOOK_PRIO_DEFAULT);