summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/arm/stm32f4/startup/bspstart.c
diff options
context:
space:
mode:
authorTomasz Gregorek <tomasz.gregorek@gmail.com>2014-09-21 20:07:41 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2014-10-02 07:54:52 +0200
commit7db695311b6e0c1ddd893a5a1e9a283c0235eebc (patch)
tree72748bf2e9bd3a4d560e9bb51ea48dc2a8b5c7f7 /c/src/lib/libbsp/arm/stm32f4/startup/bspstart.c
parentFix assertion "BUF != NULL" for i386 (diff)
downloadrtems-7db695311b6e0c1ddd893a5a1e9a283c0235eebc.tar.bz2
bsp/stm32f4XXXX: System clock configuration
Added simple math to caclulate register values for the PLL and for the prescalers. It will try to keep 48MHz for the USB OTG FS. Also it will set latency on the Flash memory for the high speeds. Limitations: It is assumed that 1MHz resolution is enough. Best fits for the clocks are achieved with multiplies of 42MHz. Even though APB1, APB2 and AHB are calculated user is still required to provide correct values for the bsp configuration for the: STM32F4_PCLK1 STM32F4_PCLK2 STM32F4_HCLK (= system clock) as those are used for the peripheral clocking calculations.
Diffstat (limited to '')
-rw-r--r--c/src/lib/libbsp/arm/stm32f4/startup/bspstart.c280
1 files changed, 278 insertions, 2 deletions
diff --git a/c/src/lib/libbsp/arm/stm32f4/startup/bspstart.c b/c/src/lib/libbsp/arm/stm32f4/startup/bspstart.c
index d337d3aa2c..0d08458c2e 100644
--- a/c/src/lib/libbsp/arm/stm32f4/startup/bspstart.c
+++ b/c/src/lib/libbsp/arm/stm32f4/startup/bspstart.c
@@ -17,10 +17,286 @@
#include <bsp/irq.h>
#include <bsp/bootcard.h>
#include <bsp/irq-generic.h>
+#include <assert.h>
+#include <bsp/stm32f4.h>
-void bsp_start(void)
+#ifdef STM32F4_FAMILY_F4XXXX
+
+#include <bsp/stm32f4xxxx_rcc.h>
+#include <bsp/stm32f4xxxx_flash.h>
+
+static rtems_status_code set_system_clk(
+ uint32_t sys_clk,
+ uint32_t hse_clk,
+ uint32_t hse_flag
+);
+
+static void init_main_osc( void )
+{
+ volatile stm32f4_rcc *rcc = STM32F4_RCC;
+ rtems_status_code status;
+
+ /* Revert to reset values */
+ rcc->cr |= RCC_CR_HSION; /* turn on HSI */
+
+ while ( !( rcc->cr & RCC_CR_HSIRDY ) ) ;
+
+ rcc->cfgr &= 0x00000300; /* all prescalers to 0, clock source to HSI */
+
+ rcc->cr &= 0xF0F0FFFD; /* turn off all clocks and PLL except HSI */
+
+ status = set_system_clk( STM32F4_SYSCLK / 1000000L,
+ STM32F4_HSE_OSCILLATOR / 1000000L,
+ 1 );
+
+ assert( rtems_is_status_successful( status ) );
+}
+
+/**
+ * @brief Sets up clocks configuration.
+ *
+ * Set up clocks configuration to achieve desired system clock
+ * as close as possible with simple math.
+ *
+ * Limitations:
+ * It is assumed that 1MHz resolution is enough.
+ * Best fits for the clocks are achieved with multiplies of 42MHz.
+ * Even though APB1, APB2 and AHB are calculated user is still required
+ * to provide correct values for the bsp configuration for the:
+ * STM32F4_PCLK1
+ * STM32F4_PCLK2
+ * STM32F4_HCLK
+ * as those are used for the peripheral clocking calculations.
+ *
+ * @param sys_clk Desired system clock in MHz.
+ * @param hse_clk External clock speed in MHz.
+ * @param hse_flag Flag determining which clock source to use, 1 for HSE,
+ * 0 for HSI.
+ *
+ * @retval RTEMS_SUCCESSFUL Configuration has been succesfully aplied for the
+ * requested clock speed.
+ * @retval RTEMS_TIMEOUT HSE clock didn't start or PLL didn't lock.
+ * @retval RTEMS_INVALID_NUMBER Requested clock speed is out of range.
+ */
+static rtems_status_code set_system_clk(
+ uint32_t sys_clk,
+ uint32_t hse_clk,
+ uint32_t hse_flag
+)
+{
+ volatile stm32f4_rcc *rcc = STM32F4_RCC;
+ volatile stm32f4_flash *flash = STM32F4_FLASH;
+ long timeout = 0;
+
+ int src_clk = 0;
+
+ uint32_t pll_m = 0;
+ uint32_t pll_n = 0;
+ uint32_t pll_p = 0;
+ uint32_t pll_q = 0;
+
+ uint32_t ahbpre = 0;
+ uint32_t apbpre1 = 0;
+ uint32_t apbpre2 = 0;
+
+ if ( sys_clk == 16 && hse_clk != 16 ) {
+ /* Revert to reset values */
+ rcc->cr |= RCC_CR_HSION; /* turn on HSI */
+
+ while ( !( rcc->cr & RCC_CR_HSIRDY ) ) ;
+
+ /* all prescalers to 0, clock source to HSI */
+ rcc->cfgr &= 0x00000300 | RCC_CFGR_SW_HSI;
+ rcc->cr &= 0xF0F0FFFD; /* turn off all clocks and PLL except HSI */
+ flash->acr = 0; /* slow clock so no cache, no prefetch, no latency */
+
+ return RTEMS_SUCCESSFUL;
+ }
+
+ if ( sys_clk == hse_clk ) {
+ /* Revert to reset values */
+ rcc->cr |= RCC_CR_HSEON; /* turn on HSE */
+ timeout = 400;
+
+ while ( !( rcc->cr & RCC_CR_HSERDY ) && --timeout ) ;
+
+ assert( timeout != 0 );
+
+ if ( timeout == 0 ) {
+ return RTEMS_TIMEOUT;
+ }
+
+ /* all prescalers to 0, clock source to HSE */
+ rcc->cfgr &= 0x00000300;
+ rcc->cfgr |= RCC_CFGR_SW_HSE;
+ /* turn off all clocks and PLL except HSE */
+ rcc->cr &= 0xF0F0FFFC | RCC_CR_HSEON;
+ flash->acr = 0; /* slow clock so no cache, no prefetch, no latency */
+
+ return RTEMS_SUCCESSFUL;
+ }
+
+ /*
+ * Lets use 1MHz input for PLL so we get higher VCO output
+ * this way we get better value for the PLL_Q divader for the USB
+ *
+ * Though you might want to use 2MHz as per CPU specification:
+ *
+ * Caution:The software has to set these bits correctly to ensure
+ * that the VCO input frequency ranges from 1 to 2 MHz.
+ * It is recommended to select a frequency of 2 MHz to limit PLL jitter.
+ */
+
+ if ( sys_clk > 180 ) {
+ return RTEMS_INVALID_NUMBER;
+ } else if ( sys_clk >= 96 ) {
+ pll_n = sys_clk << 1;
+ pll_p = RCC_PLLCFGR_PLLP_BY_2;
+ } else if ( sys_clk >= 48 ) {
+ pll_n = sys_clk << 2;
+ pll_p = RCC_PLLCFGR_PLLP_BY_4;
+ } else if ( sys_clk >= 24 ) {
+ pll_n = sys_clk << 3;
+ pll_p = RCC_PLLCFGR_PLLP_BY_8;
+ } else {
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ if ( hse_clk == 0 || hse_flag == 0 ) {
+ src_clk = 16;
+ hse_flag = 0;
+ } else {
+ src_clk = hse_clk;
+ }
+
+ pll_m = src_clk; /* divide by the oscilator speed in MHz */
+
+ /* pll_q is a prescaler from VCO for the USB OTG FS, SDIO and RNG,
+ * best if results in the 48MHz for the USB
+ */
+ pll_q = ( (long) ( src_clk * pll_n + src_clk * pll_n / 2 ) ) / pll_m / 48;
+
+ if ( pll_q < 2 ) {
+ pll_q = 2;
+ }
+
+ /* APB1 prescaler, APB1 clock must be < 42MHz */
+ apbpre1 = ( sys_clk * 100 ) / 42;
+
+ if ( apbpre1 <= 100 ) {
+ apbpre1 = RCC_CFGR_PPRE1_BY_1;
+ } else if ( apbpre1 <= 200 ) {
+ apbpre1 = RCC_CFGR_PPRE1_BY_2;
+ } else if ( apbpre1 <= 400 ) {
+ apbpre1 = RCC_CFGR_PPRE1_BY_4;
+ } else if ( apbpre1 <= 800 ) {
+ apbpre1 = RCC_CFGR_PPRE1_BY_8;
+ } else if ( apbpre1 ) {
+ apbpre1 = RCC_CFGR_PPRE1_BY_16;
+ }
+
+ /* APB2 prescaler, APB2 clock must be < 84MHz */
+ apbpre2 = ( sys_clk * 100 ) / 84;
+
+ if ( apbpre2 <= 100 ) {
+ apbpre2 = RCC_CFGR_PPRE2_BY_1;
+ } else if ( apbpre2 <= 200 ) {
+ apbpre2 = RCC_CFGR_PPRE2_BY_2;
+ } else if ( apbpre2 <= 400 ) {
+ apbpre2 = RCC_CFGR_PPRE2_BY_4;
+ } else if ( apbpre2 <= 800 ) {
+ apbpre2 = RCC_CFGR_PPRE2_BY_8;
+ } else {
+ apbpre2 = RCC_CFGR_PPRE2_BY_16;
+ }
+
+ rcc->cr |= RCC_CR_HSION; /* turn on HSI */
+
+ while ( ( !( rcc->cr & RCC_CR_HSIRDY ) ) ) ;
+
+ /* all prescalers to 0, clock source to HSI */
+ rcc->cfgr &= 0x00000300;
+ rcc->cfgr |= RCC_CFGR_SW_HSI;
+
+ while ( ( ( rcc->cfgr & RCC_CFGR_SWS_MSK ) != RCC_CFGR_SWS_HSI ) ) ;
+
+ /* turn off PLL */
+ rcc->cr &= ~( RCC_CR_PLLON | RCC_CR_PLLRDY );
+
+ /* turn on HSE */
+ if ( hse_flag ) {
+ rcc->cr |= RCC_CR_HSEON;
+ timeout = 400;
+
+ while ( ( !( rcc->cr & RCC_CR_HSERDY ) ) && timeout-- ) ;
+
+ assert( timeout != 0 );
+
+ if ( timeout == 0 ) {
+ return RTEMS_TIMEOUT;
+ }
+ }
+
+ rcc->pllcfgr &= 0xF0BC8000; /* clear PLL prescalers */
+
+ /* set pll parameters */
+ rcc->pllcfgr |= RCC_PLLCFGR_PLLM( pll_m ) | /* input divider */
+ RCC_PLLCFGR_PLLN( pll_n ) | /* multiplier */
+ pll_p | /* output divider from table */
+ /* HSE v HSI */
+ ( hse_flag ? RCC_PLLCFGR_PLLSRC_HSE : RCC_PLLCFGR_PLLSRC_HSI )
+ |
+ RCC_PLLCFGR_PLLQ( pll_q ); /* PLLQ divider */
+
+ /* set prescalers for the internal busses */
+ rcc->cfgr |= apbpre1 |
+ apbpre2 |
+ ahbpre;
+
+ /*
+ * Set flash parameters, hard coded for now for fast system clocks.
+ * TODO implement some math to use flash on as low latancy as possible
+ */
+ flash->acr = FLASH_ACR_LATENCY( 5 ) | /* latency */
+ FLASH_ACR_ICEN | /* instruction cache */
+ FLASH_ACR_DCEN; /* data cache */
+
+ /* turn on PLL */
+ rcc->cr |= RCC_CR_PLLON;
+ timeout = 40000;
+
+ while ( ( !( rcc->cr & RCC_CR_PLLRDY ) ) && --timeout ) ;
+
+ assert( timeout != 0 );
+
+ if ( timeout == 0 ) {
+ return RTEMS_TIMEOUT;
+ }
+
+ /* clock source to PLL */
+ rcc->cfgr = ( rcc->cfgr & ~RCC_CFGR_SW_MSK ) | RCC_CFGR_SW_PLL;
+
+ while ( ( ( rcc->cfgr & RCC_CFGR_SWS_MSK ) != RCC_CFGR_SWS_PLL ) ) ;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+#endif /* STM32F4_FAMILY_F4XXXX */
+
+#ifdef STM32F4_FAMILY_F10XXX
+
+static void init_main_osc( void )
+{
+
+}
+
+#endif /* STM32F4_FAMILY_F10XXX */
+
+void bsp_start( void )
{
- stm32f4_gpio_set_config_array(&stm32f4_start_config_gpio [0]);
+ init_main_osc();
+
+ stm32f4_gpio_set_config_array( &stm32f4_start_config_gpio[ 0 ] );
bsp_interrupt_initialize();
}