| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 |
- /*
- * Copyright (C) 2010 by Ulrich Prinz (uprinz2@netscape.net)
- * (C) 2011, 2012 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/
- */
- /*
- * \verbatim
- * $Id: stm32_gpio.c 3182 2010-10-17 21:46:04Z Astralix $
- * \endverbatim
- */
- #include <cfg/arch.h>
- #include <arch/cm3.h>
- #include <arch/cm3/timer.h>
- #include <arch/cm3/stm/stm32_clk.h>
- #include <cfg/clock.h>
- #include <arch/cm3/stm/vendor/stm32l1xx.h>
- /* Prepare some defaults if configuration is incomplete */
- #if !defined(SYSCLK_SOURCE)
- #define SYSCLK_SOURCE SYSCLK_HSI
- #endif
- static uint32_t SystemCoreClock = 0;
- const uint32_t MSIFreqTable[8] = {65536, 131072, 262144, 524288, 1048000, 2097000, 4194000, 0};
- static const uint8_t APBPrescTable[8] = {1, 1, 1, 1, 2, 4, 8, 16};
- /*---------------- Clock Setup Procedure ------------------------------
- *
- * Clock system ist arranged like this:
- *
- * ,--------------------------- USB
- * | ,--------------- CPU
- * | +--------------- SDIO
- * 1-32MHz HSE ----+---PLLMUL-+-AHBPRES---+-- APB1PRESC--- APB1
- * | | +-- ABP2PRESC--- ABP2
- * 16MHz HSI ------+----------'-------------- ADCPRESC---- ADC
- * |
- * MSI -----------------'
- *
- *
- * ***** 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)
- {
- uint32_t pllmull = 0, plldiv = 0, msirange;
- uint32_t rcc;
- rcc = RCC->CFGR;
- /* Get SYSCLK source -------------------------------------------------------*/
- switch (rcc & RCC_CFGR_SWS)
- {
- case RCC_CFGR_SWS_MSI: /* MSI used as system clock , value depends on RCC_ICSCR/MSIRANGE[2:0]: */
- msirange = RCC->ICSCR & RCC_ICSCR_MSIRANGE ;
- msirange = msirange>>_BI16(RCC_ICSCR_MSIRANGE_1);
- SystemCoreClock = MSIFreqTable[msirange];
- break;
- case RCC_CFGR_SWS_HSI: /* HSI used as system clock */
- SystemCoreClock = HSI_VALUE;
- break;
- case RCC_CFGR_SWS_HSE: /* HSE used as system clock */
- SystemCoreClock = HSE_VALUE;
- break;
- case RCC_CFGR_SWS_PLL:
- /* Assume that values not allowed don't occure*/
- if (rcc & RCC_CFGR_PLLMUL4) pllmull = 4;
- else if (rcc & RCC_CFGR_PLLMUL6) pllmull = 6;
- else if (rcc & RCC_CFGR_PLLMUL8) pllmull = 8;
- else if (rcc & RCC_CFGR_PLLMUL12) pllmull = 12;
- else if (rcc & RCC_CFGR_PLLMUL16) pllmull = 16;
- else if (rcc & RCC_CFGR_PLLMUL24) pllmull = 24;
- else if (rcc & RCC_CFGR_PLLMUL32) pllmull = 32;
- else if (rcc & RCC_CFGR_PLLMUL48) pllmull = 48;
- else pllmull = 3;
- if((rcc & RCC_CFGR_PLLDIV4) == RCC_CFGR_PLLDIV4) plldiv = 4;
- else if (rcc & RCC_CFGR_PLLDIV3) plldiv = 3;
- else if (rcc & RCC_CFGR_PLLDIV2) plldiv = 2;
- else plldiv = 1;
- if (rcc & RCC_CFGR_PLLSRC_HSE)
- SystemCoreClock = HSE_VALUE * pllmull / plldiv;
- else
- SystemCoreClock = HSI_VALUE * pllmull / plldiv;
- }
- /* Compute HCLK clock frequency ----------------*/
- if ((rcc & RCC_CFGR_HPRE_3))
- SystemCoreClock >>= ((rcc & (RCC_CFGR_HPRE_0 | RCC_CFGR_HPRE_1 |RCC_CFGR_HPRE_2)) +1);
- }
- /*!
- * \brief Re/Set RCC register bit and wait for same state of connected RDY bit or timeout
- *
- * \param bbreg Bitband address of the bit to set
- * \param tout timeout in delay units.
- * \return 0 on success, -1 on HSE start failed.
- */
- int rcc_set_and_wait_rdy(__IO uint32_t *bbreg, int value, uint32_t tout)
- {
- int state = (value)?1:0;
- *bbreg = state;
- do {
- tout--;
- } while ((bbreg[1] != state) && (tout > 0));
- return ( bbreg[1] == state)?0:-1;
- }
- /* 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;
- /* switch HSE off to allow to set HSE_BYPASS */
- rc = rcc_set_and_wait_rdy(
- CM3BBADDR(RCC_BASE, RCC_TypeDef, CR, _BI32(RCC_CR_HSEON)), 0, HSE_STARTUP_TIMEOUT);
- if( ena) {
- #if defined(HSE_BYPASS)
- CM3BBSET(RCC_BASE, RCC_TypeDef, CR, _BI32(RCC_CR_HSEBYP));
- #else
- CM3BBCLR(RCC_BASE, RCC_TypeDef, CR, _BI32(RCC_CR_HSEBYP));
- #endif
- #if !defined(RTCPRE) || (RTCPRE <0) || (RTCPRE >3)
- #define RTCPRE 3
- #endif
- RCC->CR &= ~RCC_CR_RTCPRE;
- RCC->CR |= RTCPRE<< _BI32(RCC_CR_RTCPRE_0);
- /* Enable HSE */
- rc = rcc_set_and_wait_rdy(
- CM3BBADDR(RCC_BASE, RCC_TypeDef, CR, _BI32(RCC_CR_HSEON)), 1, HSE_STARTUP_TIMEOUT);
- }
- 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;
- if( ena) {
- /* Enable HSI */
- rc = rcc_set_and_wait_rdy(
- CM3BBADDR(RCC_BASE, RCC_TypeDef, CR, _BI32(RCC_CR_HSION)), 1, HSE_STARTUP_TIMEOUT);
- }
- else {
- /* Disable HSE clock */
- rc = rcc_set_and_wait_rdy(
- CM3BBADDR(RCC_BASE, RCC_TypeDef, CR, _BI32(RCC_CR_HSION)), 0, HSE_STARTUP_TIMEOUT);
- }
- 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;
- if( ena) {
- /* Enable PLL */
- rc = rcc_set_and_wait_rdy(
- CM3BBADDR(RCC_BASE, RCC_TypeDef, CR, _BI32(RCC_CR_PLLON)), 1, HSE_STARTUP_TIMEOUT);
- }
- else {
- /* Disable HSE clock */
- rc = rcc_set_and_wait_rdy(
- CM3BBADDR(RCC_BASE, RCC_TypeDef, CR, _BI32(RCC_CR_PLLON)), 0, HSE_STARTUP_TIMEOUT);
- }
- 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, CFGR, _BI32(RCC_CFGR_PLLSRC));
- }
- }
- else if (src == PLLCLK_HSI) {
- rc = CtlHsiClock(ENABLE);
- /* Select HSI/2 as PLL clock source */
- if (rc==0) {
- CM3BBCLR(RCC_BASE, RCC_TypeDef, CFGR, _BI32(RCC_CFGR_PLLSRC));
- }
- }
- 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;
- /* Fixme: Set MSI source with MSI frequency parameter */
- 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 HSI 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;
- /* Fixme: Allow more flexible Flash Setting
- * For the moment, use 32-bit access with no prefetch . Latency has no meaning
- * for 32-bit access
- */
- 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;
- }
- #else /* (SYSCLK_SOURCE == SYSCLK_HSI) || (SYSCLK_SOURCE == SYSCLK_HSE) */
- #if (PLLCLK_SOURCE==PLLCLK_HSE)
- #define PLLCLK_IN HSE_VALUE
- #else
- #define PLLCLK_IN (HSI_VALUE)
- #endif
- /**
- * @brief Sets System clock frequency to 8MHz and configure HCLK, PCLK2
- * and PCLK1 prescalers.
- * @note This function should be used only after reset.
- * @param None
- * @retval None
- */
- int SetSysClock(void)
- {
- int rc = 0;
- /* FIXME*/
- return rc;
- }
- #endif /* (SYSCLK_SOURCE == SYSCLK_HSI) || (SYSCLK_SOURCE == SYSCLK_HSE) */
- /**
- * @brief Sets RTC clock to selected source.
- *
- * @param source Clock source LSI/LSE/HSE
- * @retval -1 on error, 0 on success
- */
- int SetRTCClock(int source)
- {
- int rc = -1;
- /* Enable PWR Controller and access to the RTC backup domain*/
- CM3BBSET(RCC_BASE, RCC_TypeDef, APB1ENR, _BI32(RCC_APB1ENR_PWREN));
- CM3BBSET(PWR_BASE, PWR_TypeDef, CR, _BI32(PWR_CR_DBP));
- /* Reset RTC to allow selection */
- CM3BBSET(RCC_BASE, RCC_TypeDef, CSR, _BI32(RCC_CSR_RTCRST));
- CM3BBCLR(RCC_BASE, RCC_TypeDef, CSR, _BI32(RCC_CSR_RTCRST));
- switch (source)
- {
- case RTCCLK_LSI:
- rc = rcc_set_and_wait_rdy(
- CM3BBADDR(RCC_BASE, RCC_TypeDef, CSR, _BI32(RCC_CSR_LSION)),
- 1, HSE_STARTUP_TIMEOUT*1000);
- if (rc == -1)
- return rc;
- RCC->CSR &= ~RCC_CSR_RTCSEL;
- RCC->CSR |= RCC_CSR_RTCSEL_LSI;
- break;
- case RTCCLK_LSE:
- /* LSE Bypass can only be written with LSE off*/
- rc = rcc_set_and_wait_rdy(
- CM3BBADDR(RCC_BASE, RCC_TypeDef, CSR, _BI32(RCC_CSR_LSEON)),
- 0, HSE_STARTUP_TIMEOUT*1000);
- if (rc == -1)
- return rc;
- #if defined(LSE_BYPASS)
- CM3BBSET(RCC_BASE, RCC_TypeDef, CSR, _BI32(RCC_CSR_LSEBYP));
- #else
- CM3BBCLR(RCC_BASE, RCC_TypeDef, CSR, _BI32(RCC_CSR_LSEBYP));
- #endif
- rc = rcc_set_and_wait_rdy(
- CM3BBADDR(RCC_BASE, RCC_TypeDef, CSR, _BI32(RCC_CSR_LSEON)),
- 1, HSE_STARTUP_TIMEOUT*1000);
- if (rc == -1)
- return rc;
- RCC->CSR &= ~RCC_CSR_RTCSEL;
- RCC->CSR |= RCC_CSR_RTCSEL_LSE;
- break;
- case RTCCLK_HSE:
- RCC->CSR &= ~RCC_CSR_RTCSEL;
- RCC->CSR |= RCC_CSR_RTCSEL_HSE;
- break;
- }
- return rc;
- }
- /**
- * @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;
- }
- }
|