/* * 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 #include #include #include #include #include /* 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; } }