Refactor to use new protocol module and sipo.
[bertos.git] / bertos / cpu / cortex-m3 / drv / clock_stm32.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief STM32 Clocking driver.
34  *
35  * \author Andrea Righi <arighi@develer.com>
36  */
37
38 #include "clock_stm32.h"
39
40 #include <cfg/compiler.h>
41 #include <cfg/debug.h>
42
43 #include <io/stm32.h>
44
45 struct RCC *RCC;
46
47 INLINE int rcc_get_flag_status(uint32_t flag)
48 {
49         uint32_t id;
50         reg32_t reg;
51
52         /* Get the RCC register index */
53         id = flag >> 5;
54         /* The flag to check is in CR register */
55         if (id == 1)
56                 reg = RCC->CR;
57         /* The flag to check is in BDCR register */
58         else if (id == 2)
59                 reg = RCC->BDCR;
60         /* The flag to check is in CSR register */
61         else
62                 reg = RCC->CSR;
63         /* Get the flag position */
64         id = flag & FLAG_MASK;
65
66         return reg & (1 << id);
67 }
68
69 INLINE uint16_t pll_clock(void)
70 {
71         unsigned int div, mul;
72
73         /* Hopefully this is evaluate at compile time... */
74         for (div = 2; div; div--)
75                 for (mul = 2; mul <= 16; mul++)
76                         if (CPU_FREQ <= (PLL_VCO / div * mul))
77                                 break;
78         return mul << 8 | div;
79 }
80
81 INLINE void rcc_pll_config(void)
82 {
83         reg32_t reg = RCC->CFGR & CFGR_PLL_MASK;
84
85         /* Evaluate clock parameters */
86         uint16_t clock = pll_clock();
87         uint32_t pll_mul = ((clock >> 8) - 2) << 18;
88         uint32_t pll_div = ((clock & 0xff) << 1 | 1) << 16;
89
90         /* Set the PLL configuration bits */
91         reg |= pll_div | pll_mul;
92
93         /* Store the new value */
94         RCC->CFGR = reg;
95
96         /* Enable PLL */
97         *CR_PLLON_BB = 1;
98 }
99
100 INLINE void rcc_set_clock_source(uint32_t source)
101 {
102         reg32_t reg;
103
104         reg = RCC->CFGR & CFGR_SW_MASK;
105         reg |= source;
106         RCC->CFGR = reg;
107 }
108
109 void clock_init(void)
110 {
111         /* Initialize global RCC structure */
112         RCC = (struct RCC *)RCC_BASE;
113
114         /* Enable the internal oscillator */
115         *CR_HSION_BB = 1;
116         while (!rcc_get_flag_status(RCC_FLAG_HSIRDY));
117
118         /* Clock the system from internal HSI RC (8 MHz) */
119         rcc_set_clock_source(RCC_SYSCLK_HSI);
120
121         /* Enable external oscillator */
122         RCC->CR &= CR_HSEON_RESET;
123         RCC->CR &= CR_HSEBYP_RESET;
124         RCC->CR |= CR_HSEON_SET;
125         while (!rcc_get_flag_status(RCC_FLAG_HSERDY));
126
127         /* Initialize PLL according to CPU_FREQ */
128         rcc_pll_config();
129         while(!rcc_get_flag_status(RCC_FLAG_PLLRDY));
130
131         /* Configure USB clock (48MHz) */
132         *CFGR_USBPRE_BB = RCC_USBCLK_PLLCLK_1DIV5;
133         /* Configure ADC clock: PCLK2 (9MHz) */
134         RCC->CFGR &= CFGR_ADCPRE_RESET_MASK;
135         RCC->CFGR |= RCC_PCLK2_DIV8;
136         /* Configure system clock dividers: PCLK2 (72MHz) */
137         RCC->CFGR &= CFGR_PPRE2_RESET_MASK;
138         RCC->CFGR |= RCC_HCLK_DIV1 << 3;
139         /* Configure system clock dividers: PCLK1 (36MHz) */
140         RCC->CFGR &= CFGR_PPRE1_RESET_MASK;
141         RCC->CFGR |= RCC_HCLK_DIV2;
142         /* Configure system clock dividers: HCLK */
143         RCC->CFGR &= CFGR_HPRE_RESET_MASK;
144         RCC->CFGR |= RCC_SYSCLK_DIV1;
145
146         /* Set 1 wait state for the flash memory */
147         *(reg32_t *)FLASH_BASE = 0x12;
148
149         /* Clock the system from the PLL */
150         rcc_set_clock_source(RCC_SYSCLK_PLLCLK);
151 }