stm32l4: rcc: Implement PLL helper as seen in other MCU families
Reviewed-by: Karl Palsson <karlp@tweak.net.au>
This commit is contained in:
committed by
Karl Palsson
parent
757a0a14eb
commit
3122c0b33f
@@ -39,16 +39,25 @@
|
||||
#ifndef LIBOPENCM3_RCC_H
|
||||
#define LIBOPENCM3_RCC_H
|
||||
|
||||
/* --- RCC registers ------------------------------------------------------- */
|
||||
#include <libopencm3/stm32/pwr.h>
|
||||
|
||||
/** @defgroup rcc_registers RCC Registers
|
||||
* @brief Reset / Clock Control Registers
|
||||
@{*/
|
||||
/** Clock control register */
|
||||
#define RCC_CR MMIO32(RCC_BASE + 0x00)
|
||||
#define RCC_ICSCR MMIO32(RCC_BASE + 0x04)
|
||||
/** Clock Configuration register */
|
||||
#define RCC_CFGR MMIO32(RCC_BASE + 0x08)
|
||||
/** PLL Configuration register */
|
||||
#define RCC_PLLCFGR MMIO32(RCC_BASE + 0x0c)
|
||||
#define RCC_PLLSAI1_CFGR MMIO32(RCC_BASE + 0x10)
|
||||
#define RCC_PLLSAI2_CFGR MMIO32(RCC_BASE + 0x14)
|
||||
/** Clock interrupt enable register */
|
||||
#define RCC_CIER MMIO32(RCC_BASE + 0x18)
|
||||
/** Clock interrupt flag resiger */
|
||||
#define RCC_CIFR MMIO32(RCC_BASE + 0x1c)
|
||||
/** Clock interrupt clear register */
|
||||
#define RCC_CICR MMIO32(RCC_BASE + 0x20)
|
||||
#define RCC_AHB1RSTR_OFFSET 0x28
|
||||
#define RCC_AHB1RSTR MMIO32(RCC_BASE + RCC_AHB1RSTR_OFFSET)
|
||||
@@ -87,13 +96,18 @@
|
||||
#define RCC_APB2SMENR_OFFSET 0x80
|
||||
#define RCC_APB2SMENR MMIO32(RCC_BASE + RCC_APB2SMENR_OFFSET)
|
||||
#define RCC_CCIPR MMIO32(RCC_BASE + 0x88)
|
||||
/** Backup Domain control register */
|
||||
#define RCC_BDCR MMIO32(RCC_BASE + 0x90)
|
||||
/** Clock control and status register */
|
||||
#define RCC_CSR MMIO32(RCC_BASE + 0x94)
|
||||
#define RCC_CRRCR MMIO32(RCC_BASE + 0x98)
|
||||
#define RCC_CCIPR2 MMIO32(RCC_BASE + 0x9C)
|
||||
/** @}*/
|
||||
|
||||
/* --- RCC_CR values ------------------------------------------------------- */
|
||||
|
||||
/** @defgroup rcc_cr_values RCC_CR values
|
||||
* @ingroup rcc_registers
|
||||
* @brief Clock Control register values
|
||||
@{*/
|
||||
#define RCC_CR_PLLSAI2RDY (1 << 29)
|
||||
#define RCC_CR_PLLSAI2ON (1 << 28)
|
||||
#define RCC_CR_PLLSAI1RDY (1 << 27)
|
||||
@@ -108,6 +122,8 @@
|
||||
#define RCC_CR_HSIRDY (1 << 10)
|
||||
#define RCC_CR_HSIKERON (1 << 9)
|
||||
#define RCC_CR_HSION (1 << 8)
|
||||
/** @}*/
|
||||
|
||||
/** @defgroup rcc_cr_msirange MSI Range
|
||||
* @ingroup STM32L4xx_rcc_defines
|
||||
* @brief Range of the MSI oscillator
|
||||
@@ -180,36 +196,35 @@ Twelve frequency ranges are available: 100 kHz, 200 kHz, 400 kHz, 800 kHz,
|
||||
#define RCC_CFGR_STOPWUCK_MSI (0 << 15)
|
||||
#define RCC_CFGR_STOPWUCK_HSI16 (1 << 15)
|
||||
|
||||
/* PPRE2: APB high-speed prescaler (APB2) */
|
||||
#define RCC_CFGR_PPRE2_NODIV 0x0
|
||||
#define RCC_CFGR_PPRE2_DIV2 0x4
|
||||
#define RCC_CFGR_PPRE2_DIV4 0x5
|
||||
#define RCC_CFGR_PPRE2_DIV8 0x6
|
||||
#define RCC_CFGR_PPRE2_DIV16 0x7
|
||||
#define RCC_CFGR_PPRE2_MASK 0x7
|
||||
#define RCC_CFGR_PPRE2_SHIFT 11
|
||||
#define RCC_CFGR_PPRE1_SHIFT 8
|
||||
#define RCC_CFGR_PPRE1_MASK 0x7
|
||||
#define RCC_CFGR_PPRE2_SHIFT 11
|
||||
#define RCC_CFGR_PPRE2_MASK 0x7
|
||||
/** @defgroup rcc_cfgr_apbxpre RCC_CFGR APBx prescale factors
|
||||
* These can be used for both APB1 and APB2 prescaling
|
||||
* @{
|
||||
*/
|
||||
#define RCC_CFGR_PPRE_NODIV 0x0
|
||||
#define RCC_CFGR_PPRE_DIV2 0x4
|
||||
#define RCC_CFGR_PPRE_DIV4 0x5
|
||||
#define RCC_CFGR_PPRE_DIV8 0x6
|
||||
#define RCC_CFGR_PPRE_DIV16 0x7
|
||||
/**@}*/
|
||||
|
||||
/* PPRE1: APB low-speed prescaler (APB1) */
|
||||
#define RCC_CFGR_PPRE1_NODIV 0x0
|
||||
#define RCC_CFGR_PPRE1_DIV2 0x4
|
||||
#define RCC_CFGR_PPRE1_DIV4 0x5
|
||||
#define RCC_CFGR_PPRE1_DIV8 0x6
|
||||
#define RCC_CFGR_PPRE1_DIV16 0x7
|
||||
#define RCC_CFGR_PPRE1_MASK 0x7
|
||||
#define RCC_CFGR_PPRE1_SHIFT 8
|
||||
|
||||
/* HPRE: AHB prescaler */
|
||||
#define RCC_CFGR_HPRE_NODIV 0x0
|
||||
#define RCC_CFGR_HPRE_DIV2 0x8
|
||||
#define RCC_CFGR_HPRE_DIV4 0x9
|
||||
#define RCC_CFGR_HPRE_DIV8 0xa
|
||||
#define RCC_CFGR_HPRE_DIV16 0xb
|
||||
#define RCC_CFGR_HPRE_DIV64 0xc
|
||||
#define RCC_CFGR_HPRE_DIV128 0xd
|
||||
#define RCC_CFGR_HPRE_DIV256 0xe
|
||||
#define RCC_CFGR_HPRE_DIV512 0xf
|
||||
#define RCC_CFGR_HPRE_MASK 0xf
|
||||
#define RCC_CFGR_HPRE_SHIFT 4
|
||||
#define RCC_CFGR_HPRE_SHIFT 4
|
||||
#define RCC_CFGR_HPRE_MASK 0xf
|
||||
/** @defgroup rcc_cfgr_ahbpre RCC_CFGR AHB prescale factors
|
||||
@{*/
|
||||
#define RCC_CFGR_HPRE_NODIV 0x0
|
||||
#define RCC_CFGR_HPRE_DIV2 (0x8 + 0)
|
||||
#define RCC_CFGR_HPRE_DIV4 (0x8 + 1)
|
||||
#define RCC_CFGR_HPRE_DIV8 (0x8 + 2)
|
||||
#define RCC_CFGR_HPRE_DIV16 (0x8 + 3)
|
||||
#define RCC_CFGR_HPRE_DIV64 (0x8 + 4)
|
||||
#define RCC_CFGR_HPRE_DIV128 (0x8 + 5)
|
||||
#define RCC_CFGR_HPRE_DIV256 (0x8 + 6)
|
||||
#define RCC_CFGR_HPRE_DIV512 (0x8 + 7)
|
||||
/**@}*/
|
||||
|
||||
/* SWS: System clock switch status */
|
||||
#define RCC_CFGR_SWS_MSI 0x0
|
||||
@@ -725,6 +740,30 @@ Twelve frequency ranges are available: 100 kHz, 200 kHz, 400 kHz, 800 kHz,
|
||||
#define RCC_CSR_LSIRDY (1 << 1)
|
||||
#define RCC_CSR_LSION (1 << 0)
|
||||
|
||||
struct rcc_clock_scale {
|
||||
uint8_t pllm;
|
||||
uint16_t plln;
|
||||
uint8_t pllp;
|
||||
uint8_t pllq;
|
||||
uint8_t pllr;
|
||||
uint8_t pll_source;
|
||||
uint32_t flash_config;
|
||||
uint8_t hpre;
|
||||
uint8_t ppre1;
|
||||
uint8_t ppre2;
|
||||
enum pwr_vos_scale voltage_scale;
|
||||
uint32_t ahb_frequency;
|
||||
uint32_t apb1_frequency;
|
||||
uint32_t apb2_frequency;
|
||||
};
|
||||
|
||||
enum rcc_clock_config_entry {
|
||||
RCC_CLOCK_VRANGE1_80MHZ,
|
||||
RCC_CLOCK_CONFIG_END
|
||||
};
|
||||
|
||||
extern const struct rcc_clock_scale rcc_hsi16_configs[RCC_CLOCK_CONFIG_END];
|
||||
|
||||
/* --- Variable definitions ------------------------------------------------ */
|
||||
|
||||
extern uint32_t rcc_ahb_frequency;
|
||||
@@ -981,6 +1020,7 @@ void rcc_set_ppre1(uint32_t ppre1);
|
||||
void rcc_set_hpre(uint32_t hpre);
|
||||
void rcc_set_main_pll(uint32_t source, uint32_t pllm, uint32_t plln, uint32_t pllp, uint32_t pllq, uint32_t pllr);
|
||||
uint32_t rcc_system_clock_source(void);
|
||||
void rcc_clock_setup_pll(const struct rcc_clock_scale *clock);
|
||||
void rcc_set_msi_range(uint32_t msi_range);
|
||||
void rcc_set_msi_range_standby(uint32_t msi_range);
|
||||
void rcc_pll_output_enable(uint32_t pllout);
|
||||
|
||||
@@ -38,12 +38,34 @@
|
||||
/**@{*/
|
||||
#include <libopencm3/cm3/assert.h>
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
#include <libopencm3/stm32/flash.h>
|
||||
#include <libopencm3/stm32/pwr.h>
|
||||
|
||||
/* Set the default clock frequencies after reset. */
|
||||
uint32_t rcc_ahb_frequency = 4000000;
|
||||
uint32_t rcc_apb1_frequency = 4000000;
|
||||
uint32_t rcc_apb2_frequency = 4000000;
|
||||
|
||||
const struct rcc_clock_scale rcc_hsi16_configs[RCC_CLOCK_CONFIG_END] = {
|
||||
{ /* 80MHz PLL from HSI16 VR1 */
|
||||
.pllm = 4,
|
||||
.plln = 40,
|
||||
.pllp = RCC_PLLCFGR_PLLP_DIV7,
|
||||
.pllq = RCC_PLLCFGR_PLLQ_DIV6,
|
||||
.pllr = RCC_PLLCFGR_PLLR_DIV2,
|
||||
.pll_source = RCC_PLLCFGR_PLLSRC_HSI16,
|
||||
.hpre = RCC_CFGR_HPRE_NODIV,
|
||||
.ppre1 = RCC_CFGR_PPRE_NODIV,
|
||||
.ppre2 = RCC_CFGR_PPRE_NODIV,
|
||||
.voltage_scale = PWR_SCALE1,
|
||||
.flash_config = FLASH_ACR_DCEN | FLASH_ACR_ICEN |
|
||||
FLASH_ACR_LATENCY_4WS,
|
||||
.ahb_frequency = 80000000,
|
||||
.apb1_frequency = 80000000,
|
||||
.apb2_frequency = 80000000,
|
||||
},
|
||||
};
|
||||
|
||||
void rcc_osc_ready_int_clear(enum rcc_osc osc)
|
||||
{
|
||||
switch (osc) {
|
||||
@@ -341,6 +363,82 @@ uint32_t rcc_system_clock_source(void)
|
||||
return (RCC_CFGR >> RCC_CFGR_SWS_SHIFT) & RCC_CFGR_SWS_MASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup clocks to run from PLL.
|
||||
*
|
||||
* The arguments provide the pll source, multipliers, dividers, all that's
|
||||
* needed to establish a system clock.
|
||||
*
|
||||
* @param clock clock information structure.
|
||||
*/
|
||||
void rcc_clock_setup_pll(const struct rcc_clock_scale *clock)
|
||||
{
|
||||
/* Enable internal high-speed oscillator (HSI16). */
|
||||
rcc_osc_on(RCC_HSI16);
|
||||
rcc_wait_for_osc_ready(RCC_HSI16);
|
||||
|
||||
/* Select HSI16 as SYSCLK source. */
|
||||
rcc_set_sysclk_source(RCC_PLLCFGR_PLLSRC_HSI16);
|
||||
|
||||
/* Enable external high-speed oscillator (HSE). */
|
||||
if (clock->pll_source == RCC_PLLCFGR_PLLSRC_HSE) {
|
||||
rcc_osc_on(RCC_HSE);
|
||||
rcc_wait_for_osc_ready(RCC_HSE);
|
||||
}
|
||||
|
||||
/* Set the VOS scale mode */
|
||||
rcc_periph_clock_enable(RCC_PWR);
|
||||
pwr_set_vos_scale(clock->voltage_scale);
|
||||
|
||||
/*
|
||||
* Set prescalers for AHB, ADC, APB1, APB2.
|
||||
* Do this before touching the PLL (TODO: why?).
|
||||
*/
|
||||
rcc_set_hpre(clock->hpre);
|
||||
rcc_set_ppre1(clock->ppre1);
|
||||
rcc_set_ppre2(clock->ppre2);
|
||||
|
||||
/* Disable PLL oscillator before changing its configuration. */
|
||||
rcc_osc_off(RCC_PLL);
|
||||
|
||||
/* Configure the PLL oscillator. */
|
||||
rcc_set_main_pll(clock->pll_source, clock->pllm, clock->plln,
|
||||
clock->pllp, clock->pllq, clock->pllr);
|
||||
|
||||
/* Enable PLL oscillator and wait for it to stabilize. */
|
||||
rcc_osc_on(RCC_PLL);
|
||||
rcc_wait_for_osc_ready(RCC_PLL);
|
||||
|
||||
/* Configure flash settings. */
|
||||
if (clock->flash_config & FLASH_ACR_DCEN) {
|
||||
flash_dcache_enable();
|
||||
} else {
|
||||
flash_dcache_disable();
|
||||
}
|
||||
if (clock->flash_config & FLASH_ACR_ICEN) {
|
||||
flash_icache_enable();
|
||||
} else {
|
||||
flash_icache_disable();
|
||||
}
|
||||
flash_set_ws(clock->flash_config);
|
||||
|
||||
/* Select PLL as SYSCLK source. */
|
||||
rcc_set_sysclk_source(RCC_CFGR_SW_PLL);
|
||||
|
||||
/* Wait for PLL clock to be selected. */
|
||||
rcc_wait_for_sysclk_status(RCC_PLL);
|
||||
|
||||
/* Set the peripheral clock frequencies used. */
|
||||
rcc_ahb_frequency = clock->ahb_frequency;
|
||||
rcc_apb1_frequency = clock->apb1_frequency;
|
||||
rcc_apb2_frequency = clock->apb2_frequency;
|
||||
|
||||
/* Disable internal high-speed oscillator. */
|
||||
if (clock->pll_source == RCC_PLLCFGR_PLLSRC_HSE) {
|
||||
rcc_osc_off(RCC_HSI16);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the msi run time range.
|
||||
* Can only be called when MSI is either OFF, or when MSI is on _and_
|
||||
@@ -497,11 +595,11 @@ uint32_t rcc_get_timer_clk_freq(uint32_t timer)
|
||||
}
|
||||
} else if (timer >= TIM2_BASE && timer <= TIM7_BASE) {
|
||||
uint8_t ppre1 = (RCC_CFGR >> RCC_CFGR_PPRE1_SHIFT) & RCC_CFGR_PPRE1_MASK;
|
||||
return (ppre1 == RCC_CFGR_PPRE1_NODIV) ? rcc_apb1_frequency
|
||||
return (ppre1 == RCC_CFGR_PPRE_NODIV) ? rcc_apb1_frequency
|
||||
: 2 * rcc_apb1_frequency;
|
||||
} else {
|
||||
uint8_t ppre2 = (RCC_CFGR >> RCC_CFGR_PPRE2_SHIFT) & RCC_CFGR_PPRE2_MASK;
|
||||
return (ppre2 == RCC_CFGR_PPRE2_NODIV) ? rcc_apb2_frequency
|
||||
return (ppre2 == RCC_CFGR_PPRE_NODIV) ? rcc_apb2_frequency
|
||||
: 2 * rcc_apb2_frequency;
|
||||
}
|
||||
cm3_assert_not_reached();
|
||||
|
||||
Reference in New Issue
Block a user