1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/stm32f1/stmclk.c

210 lines
5.8 KiB
C
Raw Normal View History

/*
* Copyright (C) 2017 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_stm32f1
* @{
*
* @file
* @brief Implementation of STM32F1 clock configuration
*
* @author Stefan Pfeiffer <stefan.pfeiffer@fu-berlin.de>
* @author Alaeddine Weslati <alaeddine.weslati@inria.fr>
* @author Thomas Eichinger <thomas.eichinger@fu-berlin.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Nick van IJzendoorn <nijzendoorn@engineering-spirit.nl>
* @author Víctor Ariño <victor.arino@zii.aero>
*
* @}
*/
#include "irq.h"
#include "cpu.h"
#include "stmclk.h"
#include "periph_conf.h"
/* make sure we have all needed information about the clock configuration */
#ifndef CLOCK_HSE
#error "Please provide CLOCK_HSE in your board's perhip_conf.h"
#endif
#ifndef CLOCK_LSE
#error "Please provide CLOCK_LSE in your board's periph_conf.h"
#endif
#ifndef CLOCK_CORECLOCK
#error "Please provide CLOCK_CORECLOCK in your board's periph_conf.h"
#endif
#if !defined(CLOCK_PLL_MUL) || !defined(CLOCK_PLL_DIV)
#error "Please provide a valid PLL configuration in your board's periph_conf.h"
#endif
#if !defined(CLOCK_AHB_DIV) || !defined(CLOCK_AHB) || \
!defined(CLOCK_APB1_DIV) || !defined(CLOCK_APB1) || \
!defined(CLOCK_APB2_DIV) || !defined(CLOCK_APB2)
#error "Please provide a AHB and APBx configuration in your board configuration"
#endif
/* make sure the selected system clock is valid */
#if (CLOCK_CORECLOCK > 72000000)
#error "clock config: the selected system clock exceeds 72MHz"
#endif
/* figure out which base block to use */
#if CLOCK_HSE
#if (CLOCK_HSE < 4000000) || (CLOCK_HSE > 16000000)
#error "clock config: HSE value is out of valid range"
#endif
#define BASECLK (CLOCK_HSE)
#else
#define BASECLK (8000000) /* HSI is alway 8MHz */
#endif
/* if PLL is configured, verify its parameters */
#if (CLOCK_PLL_DIV && CLOCK_PLL_MUL)
#define USEPLL
/* check clock config */
#if (CLOCK_CORECLOCK != ((BASECLK / CLOCK_PLL_DIV) * CLOCK_PLL_MUL))
#error "clock config: PLL configuration does not yield expected system clock"
#endif
/* make sure PLL_MUL is in range */
#if (CLOCK_PLL_MUL < 2) || (CLOCK_PLL_MUL > 17)
#error "clock config: CLOCK_PLL_MUL is out of range"
#endif
/* make sure PLL_DIV is 2 when using HSI as input */
#if (!CLOCK_HSE) && (CLOCK_PLL_DIV != 2)
#error "clock config: CLOCK_PLL_DIV must be 2 when using HSI oscillator"
#endif
/* and produce the actual PLL configuration */
#if CLOCK_HSE
#define PLLSRC (RCC_CFGR_PLLSRC)
#if (CLOCK_PLL_DIV == 2)
#define PLLDIV (RCC_CFGR_PLLXTPRE)
#else
#define PLLDIV (0)
#endif
#else
#define PLLSRC (0)
#define PLLDIV (0)
#endif
#define PLLMUL ((CLOCK_PLL_MUL - 2) << 18)
/* and join it for writing the the CFGR register */
#define PLLCFG (PLLMUL | PLLDIV | PLLSRC)
#else
#define PLLCFG (0)
#endif
/* now we need to select the system clock source configuration */
#ifdef USEPLL
#define SYSCLK_SRC RCC_CFGR_SW_PLL
#define SYSCLK_BSY RCC_CFGR_SWS_PLL
#elif CLK_HSE
#define SYSCLK_SRC RCC_CFGR_SW_HSE
#define SYSCLK_BSY RCC_CFGR_SWS_HSE
#elif
#define SYSCLK_SRC RCC_CFGR_SW_HSI
#define SYSCLK_BSY RCC_CFGR_SWS_HSI
#endif
/* Configuration of flash access cycles */
#define FLASH_WAITSTATES ((CLOCK_CORECLOCK - 1) / 24000000U)
/* define some bitfields */
#define HSITRIM (1 << 7)
#define SWSHSI (0)
void stmclk_init_sysclk(void)
{
/* disable any IRQs */
unsigned is = irq_disable();
RCC->CIR = 0;
/* enable HSI and use it as system clock */
stmclk_enable_hsi();
RCC->CFGR &= ~(RCC_CFGR_SW);
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI) {}
/* its safe now to program the flash wait states */
FLASH->ACR = (FLASH_ACR_PRFTBE | FLASH_WAITSTATES);
/* now we are in a defined state and can stop all other clocks */
RCC->CR = (HSITRIM | RCC_CR_HSION);
/* next we put in the desired PLL and peripheral bus configuration */
RCC->CFGR = (CLOCK_AHB_DIV | CLOCK_APB1_DIV | CLOCK_APB2_DIV | PLLCFG);
/* now we need to (re-)enable the used clocks */
#if CLOCK_HSE
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY)) {}
#endif
#ifdef USEPLL
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY)) {}
#endif
/* leaves switching the system clock */
RCC->CFGR |= SYSCLK_SRC;
while ((RCC->CFGR & RCC_CFGR_SWS) != SYSCLK_BSY) {}
/* disable HSI (if not used) */
stmclk_disable_hsi();
/* re-enable IRQs */
irq_restore(is);
}
void stmclk_enable_hsi(void)
{
RCC->CR |= RCC_CR_HSION;
while (!(RCC->CR & RCC_CR_HSIRDY)) {}
}
void stmclk_disable_hsi(void)
{
/* we only disable the HSI clock if not used as input for the PLL and if
* not used directly as system clock */
#ifdef CLOCK_HSE
if ((RCC->CFGR & RCC_CFGR_SWS) != SWSHSI) {
RCC->CR &= ~(RCC_CR_HSION);
}
#endif
}
void stmclk_enable_lfclk(void)
{
#if CLOCK_LSE
stmclk_bdp_unlock();
RCC->BDCR |= RCC_BDCR_LSEON;
while (!(RCC->BDCR & RCC_BDCR_LSERDY)) {}
stmclk_bdp_lock();
#else
RCC->CSR |= RCC_CSR_LSION;
while (!(RCC->CSR & RCC_CSR_LSIRDY)) {}
#endif
}
void stmclk_disable_lfclk(void)
{
#if CLOCK_LSE
stmclk_bdp_unlock();
RCC->BDCR &= ~(RCC_BDCR_LSEON);
stmclk_bdp_lock();
#else
RCC->CSR &= ~(RCC_CSR_LSION);
#endif
}
void stmclk_bdp_unlock(void)
{
periph_clk_en(APB1, RCC_APB1ENR_PWREN);
PWR->CR |= PWR_CR_DBP;
}
void stmclk_bdp_lock(void)
{
PWR->CR &= ~(PWR_CR_DBP);
periph_clk_dis(APB1, RCC_APB1ENR_PWREN);
}