| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563 |
- /*
- * Copyright (C) 2013,2014 by Uwe Bonnes
- * (bon@elektron.ikp.physik.tu-darmstadt.de)
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- */
- #include <cfg/arch.h>
- #include <arch/cm3.h>
- #include <arch/cm3/timer.h>
- #include <arch/cm3/stm/stm32_clk.h>
- #include <cfg/clock.h>
- #if defined(MCU_STM32F0) || defined(MCU_STM32F3)
- #include <arch/cm3/stm/stm32xxxx.h>
- #else
- #warning "Unknown STM32 family"
- #endif
- /* Prepare some defaults if configuration is incomplete */
- #if !defined(SYSCLK_SOURCE)
- #define SYSCLK_SOURCE SYSCLK_HSI
- #endif
- static uint32_t SystemCoreClock = 0;
- static const uint8_t AHBPrescTable[16] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
- static const uint8_t APBPrescTable[8] = {1, 1, 1, 1, 2, 4, 8, 16};
- /* Equalize missing STM32F0 register bits */
- #if !defined(RCC_CFGR_PPRE2)
- #define RCC_CFGR_PPRE2 0
- #endif
- #if !defined(RCC_CFGR_PPRE2_0)
- #define RCC_CFGR_PPRE2_0 0
- #endif
- #if defined( RCC_CFGR_PPRE) && !defined( RCC_CFGR_PPRE1)
- #define RCC_CFGR_PPRE1 RCC_CFGR_PPRE
- #endif
- #if defined (RCC_CFGR_PPRE_DIV1) && !defined(RCC_CFGR_PPRE1_DIV1)
- #define RCC_CFGR_PPRE1_DIV1 RCC_CFGR_PPRE_DIV1
- #endif
- #if defined (RCC_CFGR_PPRE_DIV2) && !defined(RCC_CFGR_PPRE1_DIV2)
- #define RCC_CFGR_PPRE1_DIV2 RCC_CFGR_PPRE_DIV2
- #endif
- #if !defined(RCC_CFGR_PPRE2_DIV1)
- #define RCC_CFGR_PPRE2_DIV1 0
- #endif
- #if !defined(RCC_CFGR_PPRE2_DIV2)
- #define RCC_CFGR_PPRE2_DIV2 0
- #endif
- #if defined(FLASH_ACR_LATENCY) && !defined(FLASH_ACR_LATENCY_0)
- #define FLASH_ACR_LATENCY_0 FLASH_ACR_LATENCY
- #endif
- #if defined(RCC_CFGR_PPRE_0) && !defined(RCC_CFGR_PPRE1_0)
- #define RCC_CFGR_PPRE1_0 RCC_CFGR_PPRE_0
- #endif
- /*---------------- Clock Setup Procedure ------------------------------
- *
- * Clock system ist arranged like this:
- *
- * /Q------------------------------ USB
- * | ,--------------- CPU
- * | +--------------- SDIO
- * (1)4-32MHz HSE-+--/M*N+---+-AHBPRES---+-- APB1PRESC--- APB1
- * | | +-- ABP2PRESC--- ABP2
- * 8MHz HSI ------+----------' '-- ADCPRESC---- ADC
- * +-- Flash
- * '-- Option byte Loader
- *
- * M = 1..16
- * N = 2..16
- * Q = 1/1.5
- *
- * ***** Setup of system clock configuration *****
- *
- * 1) Select system clock sources
- *
- * To setup system to use HSI call: SetSysClockSource( SYSCLK_HSI);
- * To setup system to use HSE call: SetSysClockSource( SYSCLK_HSE);
- *
- * To setup system to use the PLL output, first setup the PLL source:
- * SetPllClockSource( PLLCLK_HSI);
- * or
- * SetPllClockSource( PLLCLK_HSE);
- * Then call SetSysClockSource( SYSCLK_PLL);
- *
- * 2) Configure prescalers
- * After selecting the right clock sources, the prescalers need to
- * be configured:
- * Call SetSysClock(); to do this automatically.
- *
- */
- /*!
- * \brief Update SystemCoreClock according to Clock Register Values
- *
- * This function reads out the CPUs clock and PLL registers and assembles
- * the actual clock speed values into the SystemCoreClock local variable.
- */
- void SystemCoreClockUpdate(void)
- {
- RCC_TypeDef *rcc = (RCC_TypeDef*) RCC_BASE;
- uint32_t cfgr;
- uint32_t tmp = 0;
- uint32_t hpre;
- /* Get SYSCLK source ---------------------------------------------------*/
- cfgr = RCC->CFGR;
- switch(cfgr & RCC_CFGR_SWS) {
- case RCC_CFGR_SWS_HSE:
- tmp = HSE_VALUE;
- break;
- case RCC_CFGR_SWS_PLL: {
- uint32_t cfgr2 = rcc->CFGR2;
- uint32_t prediv;
- uint32_t pllmull;
- prediv = (cfgr2 & RCC_CFGR2_PREDIV1) >> _BI32(RCC_CFGR2_PREDIV1_0);
- prediv += 1;
- pllmull = (cfgr & RCC_CFGR_PLLMULL) >> _BI32(RCC_CFGR_PLLMULL_0);
- pllmull += 2;
- if (pllmull > 16)
- pllmull = 16;
- if ((cfgr & RCC_CFGR_PLLSRC ) == RCC_CFGR_PLLSRC )
- tmp = HSE_VALUE;
- else
- tmp = HSI_VALUE / 2;
- tmp = (tmp / prediv) * pllmull;
- break;
- }
- default:
- tmp = HSI_VALUE;
- }
- hpre = (cfgr & RCC_CFGR_HPRE) >> _BI32(RCC_CFGR_HPRE_0);
- SystemCoreClock = tmp >> AHBPrescTable[hpre];
- }
- /* Functional same as F1 */
- /*!
- * \brief Control HSE clock.
- *
- * \param ena 0 disable clock, any other value enable it.
- * \return 0 on success, -1 on HSE start failed.
- */
- int CtlHseClock( uint8_t ena)
- {
- int rc = 0;
- uint32_t tout = HSE_STARTUP_TIMEOUT;
- volatile uint32_t HSEStatus = 0;
- if( ena) {
- /* Enable HSE */
- RCC->CR |= RCC_CR_HSEON;
- #if defined(HSE_BYPASS)
- /* Assume HSE if off */
- RCC->CR |= RCC_CR_HSEBYP;
- #else
- RCC->CR &= ~RCC_CR_HSEBYP;
- #endif
- /* Wait till HSE is ready or time out is reached */
- do {
- tout--;
- HSEStatus = RCC->CR & RCC_CR_HSERDY;
- } while((HSEStatus == 0) && (tout > 0));
- if ((RCC->CR & RCC_CR_HSERDY) == RESET) {
- /* HSE failed to start */
- rc = -1;
- }
- }
- else {
- /* Disable HSE clock */
- RCC->CR &= ~RCC_CR_HSEON;
- }
- return rc;
- }
- /* Functional same as F1 */
- /*!
- * \brief Control HSI clock.
- *
- * \param ena 0 disable clock, any other value enable it.
- * \return 0 on success, -1 on HSI start failed.
- */
- int CtlHsiClock( uint8_t ena)
- {
- int rc = 0;
- uint32_t tout = HSE_STARTUP_TIMEOUT;
- volatile uint32_t HSIStatus = 0;
- if( ena) {
- /* Enable HSI */
- RCC->CR |= RCC_CR_HSION;
- /* Wait till HSI is ready or time out is reached */
- do {
- tout--;
- HSIStatus = RCC->CR & RCC_CR_HSIRDY;
- } while((HSIStatus == 0) && (tout > 0));
- if ((RCC->CR & RCC_CR_HSIRDY) == RESET) {
- /* HSI failed to start */
- rc = -1;
- }
- }
- else {
- /* Disable HSE clock */
- RCC->CR &= ~RCC_CR_HSION;
- }
- return rc;
- }
- /* Functional same as F1 */
- /*!
- * \brief Control PLL clock.
- *
- * \param ena 0 disable clock, any other value enable it.
- * \return 0 on success, -1 on PLL start failed.
- */
- int CtlPllClock( uint8_t ena)
- {
- int rc = 0;
- uint32_t tout = HSE_STARTUP_TIMEOUT;
- volatile uint32_t PLLStatus = 0;
- if( ena) {
- /* Enable PLL */
- RCC->CR |= RCC_CR_PLLON;
- /* Wait till PLL is ready or time out is reached */
- do {
- tout--;
- PLLStatus = RCC->CR & RCC_CR_PLLRDY;
- } while((PLLStatus == 0) && (tout > 0));
- if ((RCC->CR & RCC_CR_PLLRDY) == RESET) {
- /* PLL failed to start */
- rc = -1;
- }
- }
- else {
- /* Disable HSE clock */
- RCC->CR &= ~RCC_CR_PLLON;
- }
- return rc;
- }
- /*!
- * \brief Configures the System clock source: HSE or HSI.
- * \note This function should be used with PLL disables
- * \param src is one of PLLCLK_HSE, PLLCLK_HSI.
- * \return 0 if clock is running ale -1.
- */
- int SetPllClockSource( int src)
- {
- int rc = -1;
- if (src == PLLCLK_HSE) {
- rc = CtlHseClock(ENABLE);
- if (rc==0) {
- CM3BBSET(RCC_BASE, RCC_TypeDef, CFGR2, _BI32(RCC_CFGR_PLLSRC_PREDIV1));
- }
- }
- else if (src == PLLCLK_HSI) {
- rc = CtlHsiClock(ENABLE);
- /* Select HSI/2 as PLL clock source */
- if (rc==0) {
- CM3BBCLR(RCC_BASE, RCC_TypeDef, CFGR2, _BI32(RCC_CFGR_PLLSRC_PREDIV1));
- }
- }
- return rc;
- }
- /*!
- * \brief Configures the System clock source: HSI, HS or PLL.
- * \note This function should be used only after reset.
- * \param src is one of SYSCLK_HSE, SYSCLK_HSI or SYSCLK_PLL.
- * \return 0 if selected clock is running else -1.
- */
- int SetSysClockSource( int src)
- {
- int rc = -1;
- if (src == SYSCLK_HSE) {
- rc = CtlHseClock(ENABLE);
- if (rc == 0) {
- /* Select HSE as system clock source */
- RCC->CFGR &= ~RCC_CFGR_SW;
- RCC->CFGR |= RCC_CFGR_SW_HSE;
- /* Wait till HSE is used as system clock source */
- while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSE);
- }
- }
- else if (src == SYSCLK_HSI) {
- rc = CtlHsiClock(ENABLE);
- if (rc == 0) {
- /* Select HSI as system clock source */
- RCC->CFGR &= ~RCC_CFGR_SW;
- RCC->CFGR |= RCC_CFGR_SW_HSI;
- /* Wait till HSI is used as system clock source */
- while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI);
- }
- }
- else if (src == SYSCLK_PLL) {
- rc = CtlPllClock(ENABLE);
- if (rc == 0) {
- /* Select HSI as system clock source */
- RCC->CFGR &= ~RCC_CFGR_SW;
- RCC->CFGR |= RCC_CFGR_SW_PLL;
- /* Wait till PLL is used as system clock source */
- while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
- }
- }
- /* Update core clock information */
- SystemCoreClockUpdate();
- return rc;
- }
- #if (SYSCLK_SOURCE == SYSCLK_HSI) || (SYSCLK_SOURCE == SYSCLK_HSE)
- /*!
- * \brief Configures the System clock coming from HSE or HSI oscillator.
- *
- * Enable HSI/HSE clock and setup HCLK, PCLK2 and PCLK1 prescalers.
- *
- * \param None.
- * \return 0 on success, -1 on fault of HSE.
- */
- int SetSysClock(void)
- {
- int rc = 0;
- register uint32_t cfgr;
- /* Todo: Check Voltage range! Here 2.7-3.6 Volt is assumed */
- /* For 2.7-3.6 Volt up to 30 MHz no Wait state required */
- cfgr = RCC->CFGR;
- cfgr &= ~(RCC_CFGR_HPRE|RCC_CFGR_PPRE1|RCC_CFGR_PPRE2);
- /* HCLK = SYSCLK */
- cfgr |= (uint32_t)RCC_CFGR_HPRE_DIV1;
- /* PCLK2 = HCLK */
- cfgr |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
- /* PCLK1 = HCLK */
- cfgr |= (uint32_t)RCC_CFGR_PPRE1_DIV1;
- RCC->CFGR = cfgr;
- rc = SetSysClockSource(SYSCLK_SOURCE);
- return rc;
- }
- #elif (SYSCLK_SOURCE == SYSCLK_PLL)
- /**
- * @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2
- * and PCLK1 prescalers.
- * @note This function should be used only after reset.
- * @param None
- * @retval None
- */
- #if (PLLCLK_SOURCE==PLLCLK_HSE)
- #define PLLCLK_IN HSE_VALUE
- #if (PLLCLK_MULT == 0 ) && (PLLCLK_DIV == 0)
- #undef PLLCLK_DIV
- #if (((PLLCLK_IN % 24000000) == 0) && ((SYSCLK_FREQ % 24000000) == 0))
- #define PLLCLK_DIV (PLLCLK_IN / 24000000)
- #elif (((PLLCLK_IN % 16000000) == 0) && ((SYSCLK_FREQ % 16000000) == 0))
- #define PLLCLK_DIV (PLLCLK_IN / 16000000)
- #elif (((PLLCLK_IN % 14400000) == 0) && ((SYSCLK_FREQ % 14400000) == 0))
- #define PLLCLK_DIV (PLLCLK_IN / 14400000)
- #elif (((PLLCLK_IN % 12000000) == 0) && ((SYSCLK_FREQ % 12000000) == 0))
- #define PLLCLK_DIV (PLLCLK_IN / 12000000)
- #elif (((PLLCLK_IN % 9600000) == 0) && ((SYSCLK_FREQ % 9600000) == 0))
- #define PLLCLK_DIV (PLLCLK_IN / 9600000)
- #elif (((PLLCLK_IN % 8000000) == 0) && ((SYSCLK_FREQ % 8000000) == 0))
- #define PLLCLK_DIV (PLLCLK_IN / 8000000)
- #elif (((PLLCLK_IN % 6000000) == 0) && ((SYSCLK_FREQ % 6000000) == 0))
- #define PLLCLK_DIV (PLLCLK_IN / 6000000)
- #elif (((PLLCLK_IN % 4800000) == 0) && ((SYSCLK_FREQ % 4800000) == 0))
- #define PLLCLK_DIV (PLLCLK_IN / 4800000)
- #elif (((PLLCLK_IN % 4000000) == 0) && ((SYSCLK_FREQ % 4000000) == 0))
- #define PLLCLK_DIV (PLLCLK_IN / 4000000)
- #elif (((PLLCLK_IN % 3000000) == 0) && ((SYSCLK_FREQ % 3000000) == 0))
- #define PLLCLK_DIV (PLLCLK_IN / 3000000)
- #endif
- #endif
- #else /* HSI Used*/
- /* HSI_VALUE value from vendor provided file as illegal type */
- #define PLLCLK_IN 4000000
- #if (PLLCLK_MULT == 0 ) && (PLLCLK_DIV == 0)
- #undef PLLCLK_DIV
- #define PLLCLK_DIV 1
- #elif (PLLCLK_DIV != 0)
- #warning "HSI/2 Input has no clock divider"
- #endif
- #endif
- #if (PLLCLK_MULT == 0 ) && (PLLCLK_DIV > 0)
- #undef PLLCLK_MULT
- #define PLLCLK_MULT (SYSCLK_FREQ/(PLLCLK_IN / PLLCLK_DIV))
- #endif
- #if (PLLCLK_DIV < 1) || (PLLCLK_DIV > 16)
- # warning "Illegal PLLCLK_DIV value"
- #endif
- #if (PLLCLK_MULT < 2) || (PLLCLK_MULT > 16)
- # warning "Illegal PLLCLK_MULT value"
- #endif
- #if (PLLCLK_IN /PLLCLK_DIV * PLLCLK_MULT > 72000000)
- #warning "Clock frequency too high"
- #elif (PLLCLK_IN /PLLCLK_DIV * PLLCLK_MULT >48000000)
- #define NUT_FLASH_LATENCY FLASH_ACR_LATENCY_1
- #if defined(MCU_STM32F0)
- #warning "Clock frequency too high"
- #endif
- #elif (PLLCLK_IN /PLLCLK_DIV * PLLCLK_MULT >24000000)
- #define NUT_FLASH_LATENCY FLASH_ACR_LATENCY_0
- #else
- #define NUT_FLASH_LATENCY 0
- #endif
- /*
- Easy Approach:
- Try to reach fpll = 72 Mhz with M/N.
- */
- int SetSysClock(void)
- {
- int rc = 0;
- uint32_t rcc_reg;
- RCC->APB1ENR |= RCC_APB1ENR_PWREN;
- rcc_reg = RCC->CFGR;
- rcc_reg &= ~(RCC_CFGR_PLLMULL |RCC_CFGR_PLLSRC |RCC_CFGR_PPRE2 | RCC_CFGR_PPRE1 |RCC_CFGR_HPRE);
- #if defined(MCU_STM32F0)
- /* APB Bus can run with full SYSCLK speed (48 MHz) */
- rcc_reg |= ((PLLCLK_MULT -2) * RCC_CFGR_PLLMULL_0);
- #else
- /* APB1 Bus (Slow APB) bus can only run with half SYSCLK speed 36 MHz) */
- rcc_reg |= ((PLLCLK_MULT -2) * RCC_CFGR_PLLMULL_0) | RCC_CFGR_PPRE1_DIV2;
- #endif
- #if (PLLCLK_SOURCE == PLLCLK_HSE)
- if (CtlHseClock(ENABLE) != 0)
- return -1;
- rcc_reg |= RCC_CFGR_PLLSRC;
- #else
- if (CtlHsiClock(ENABLE) != 0)
- return -1;
- #endif
- RCC->CFGR = rcc_reg;
- rcc_reg = FLASH->ACR;
- rcc_reg &= ~FLASH_ACR_LATENCY;
- rcc_reg |= NUT_FLASH_LATENCY | FLASH_ACR_PRFTBE ;
- FLASH->ACR = rcc_reg;
- rcc_reg = RCC->CFGR2;
- rcc_reg &= ~(RCC_CFGR2_PREDIV1);
- /* HCLK = SYSCLK, PCLK2 = HCLK , PCLK1 = HCLK/2 */
- rcc_reg |= (PLLCLK_DIV - 1);
- RCC->CFGR2 = rcc_reg;
- /* Start PLL, wait ready and switch to it as clock source */
- rc = SetSysClockSource(SYSCLK_SOURCE);
- if (rc) {
- /* Something went wrong with the PLL startup! */
- SetSysClockSource(SYSCLK_HSI);
- return rc;
- }
- return rc;
- }
- #endif /* (SYSCLK_SOURCE == SYSCLK_HSI) || (SYSCLK_SOURCE == SYSCLK_HSE) */
- /**
- * @brief requests System clock frequency
- *
- * @note This function should be used only after reset.
- * @param None
- * @retval None
- */
- uint32_t SysCtlClockGet(void)
- {
- SystemCoreClockUpdate();
- return SystemCoreClock;
- }
- /**
- * @brief requests frequency of the given clock
- *
- * @param idx NUT_HWCLK Index
- * @retval clock or 0 if idx points to an invalid clock
- */
- uint32_t STM_ClockGet(int idx)
- {
- SystemCoreClockUpdate();
- switch(idx) {
- case NUT_HWCLK_CPU:
- return SystemCoreClock;
- break;
- case NUT_HWCLK_PCLK1: {
- uint32_t tmp = (RCC->CFGR & RCC_CFGR_PPRE1) >> _BI32( RCC_CFGR_PPRE1_0);
- return SystemCoreClock/APBPrescTable[tmp];
- break;
- }
- case NUT_HWCLK_PCLK2: {
- uint32_t tmp = (RCC->CFGR & RCC_CFGR_PPRE2) >> _BI32( RCC_CFGR_PPRE2_0);
- return SystemCoreClock/APBPrescTable[tmp];
- break;
- }
- default:
- return 0;
- break;
- }
- }
|