altos/test: Adjust CRC error rate after FEC fix
[fw/altos] / src / stmf0 / ao_timer.c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include "ao.h"
20 #include <ao_task.h>
21 #if HAS_FAKE_FLIGHT
22 #include <ao_fake_flight.h>
23 #endif
24
25 #ifndef HAS_TICK
26 #define HAS_TICK 1
27 #endif
28
29 #if HAS_TICK
30 volatile AO_TICK_TYPE ao_tick_count;
31
32 AO_TICK_TYPE
33 ao_time(void)
34 {
35         return ao_tick_count;
36 }
37
38 uint64_t
39 ao_time_ns(void)
40 {
41         AO_TICK_TYPE    before, after;
42         uint32_t        cvr;
43
44         do {
45                 before = ao_tick_count;
46                 cvr = stm_systick.cvr;
47                 after = ao_tick_count;
48         } while (before != after);
49
50         return (uint64_t) after * (1000000000ULL / AO_HERTZ) +
51                 (uint64_t) cvr * (1000000000ULL / AO_SYSTICK);
52 }
53
54 #if AO_DATA_ALL
55 volatile uint8_t        ao_data_interval = 1;
56 volatile uint8_t        ao_data_count;
57 #endif
58
59 void stm_systick_isr(void)
60 {
61         if (stm_systick.csr & (1 << STM_SYSTICK_CSR_COUNTFLAG)) {
62                 ++ao_tick_count;
63                 ao_task_check_alarm();
64 #if AO_DATA_ALL
65                 if (++ao_data_count == ao_data_interval && ao_data_interval) {
66                         ao_data_count = 0;
67 #if HAS_ADC
68 #if HAS_FAKE_FLIGHT
69                         if (ao_fake_flight_active)
70                                 ao_fake_flight_poll();
71                         else
72 #endif
73                                 ao_adc_poll();
74 #endif
75 #if (AO_DATA_ALL & ~(AO_DATA_ADC))
76                         ao_wakeup((void *) &ao_data_count);
77 #endif
78                 }
79 #endif
80 #ifdef AO_TIMER_HOOK
81                 AO_TIMER_HOOK;
82 #endif
83         }
84 }
85
86 #if HAS_ADC
87 void
88 ao_timer_set_adc_interval(uint8_t interval)
89 {
90         ao_arch_critical(
91                 ao_data_interval = interval;
92                 ao_data_count = 0;
93                 );
94 }
95 #endif
96
97 #define SYSTICK_RELOAD (AO_SYSTICK / 100 - 1)
98
99 void
100 ao_timer_init(void)
101 {
102         stm_systick.csr = 0;
103         stm_systick.rvr = SYSTICK_RELOAD;
104         stm_systick.cvr = 0;
105         stm_systick.csr = ((1 << STM_SYSTICK_CSR_ENABLE) |
106                            (1 << STM_SYSTICK_CSR_TICKINT) |
107                            (STM_SYSTICK_CSR_CLKSOURCE_HCLK_8 << STM_SYSTICK_CSR_CLKSOURCE));
108 }
109
110 #endif
111
112 #if AO_HSI48
113 static void
114 ao_clock_enable_crs(void)
115 {
116         /* Enable crs interface clock */
117         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_CRSEN);
118
119         /* Disable error counter */
120         stm_crs.cr = ((stm_crs.cr & (1 << 4)) |
121                       (32 << STM_CRS_CR_TRIM) |
122                       (0 << STM_CRS_CR_SWSYNC) |
123                       (0 << STM_CRS_CR_AUTOTRIMEN) |
124                       (0 << STM_CRS_CR_CEN) |
125                       (0 << STM_CRS_CR_ESYNCIE) |
126                       (0 << STM_CRS_CR_ERRIE) |
127                       (0 << STM_CRS_CR_SYNCWARNIE) |
128                       (0 << STM_CRS_CR_SYNCOKIE));
129
130         /* Configure for USB source */
131         stm_crs.cfgr = ((stm_crs.cfgr & ((1 << 30) | (1 << 27))) |
132                         (0 << STM_CRS_CFGR_SYNCPOL) |
133                         (STM_CRS_CFGR_SYNCSRC_USB << STM_CRS_CFGR_SYNCSRC) |
134                         (STM_CRS_CFGR_SYNCDIV_1 << STM_CRS_CFGR_SYNCDIV) |
135                         (0x22 << STM_CRS_CFGR_FELIM) |
136                         (((48000000 / 1000) - 1) << STM_CRS_CFGR_RELOAD));
137
138         /* Enable error counter, set auto trim */
139         stm_crs.cr = ((stm_crs.cr & (1 << 4)) |
140                       (32 << STM_CRS_CR_TRIM) |
141                       (0 << STM_CRS_CR_SWSYNC) |
142                       (1 << STM_CRS_CR_AUTOTRIMEN) |
143                       (1 << STM_CRS_CR_CEN) |
144                       (0 << STM_CRS_CR_ESYNCIE) |
145                       (0 << STM_CRS_CR_ERRIE) |
146                       (0 << STM_CRS_CR_SYNCWARNIE) |
147                       (0 << STM_CRS_CR_SYNCOKIE));
148 }
149 #endif
150
151 static void
152 ao_clock_hsi(void)
153 {
154         stm_rcc.cr |= (1 << STM_RCC_CR_HSION);
155         while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
156                 ao_arch_nop();
157
158         stm_rcc.cfgr = (stm_rcc.cfgr & ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW)) |
159                 (STM_RCC_CFGR_SW_HSI << STM_RCC_CFGR_SW);
160
161         /* wait for system to switch to HSI */
162         while ((stm_rcc.cfgr & (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS)) !=
163                (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS))
164                 ao_arch_nop();
165
166         /* reset the clock config, leaving us running on the HSI */
167         stm_rcc.cfgr &= (uint32_t)0x0000000f;
168
169         /* reset PLLON, CSSON, HSEBYP, HSEON */
170         stm_rcc.cr &= 0x0000ffff;
171 }
172
173 static void
174 ao_clock_normal_start(void)
175 {
176 #if AO_HSE
177         uint32_t        cfgr;
178 #define STM_RCC_CFGR_SWS_TARGET_CLOCK           STM_RCC_CFGR_SWS_PLL
179 #define STM_RCC_CFGR_SW_TARGET_CLOCK            STM_RCC_CFGR_SW_PLL
180 #define STM_PLLSRC                              AO_HSE
181 #define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK        STM_RCC_CFGR_PLLSRC_HSE
182
183 #if AO_HSE_BYPASS
184         stm_rcc.cr |= (1 << STM_RCC_CR_HSEBYP);
185 #else
186         stm_rcc.cr &= ~(1UL << STM_RCC_CR_HSEBYP);
187 #endif
188         /* Enable HSE clock */
189         stm_rcc.cr |= (1 << STM_RCC_CR_HSEON);
190         while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSERDY)))
191                 asm("nop");
192
193         /* Disable the PLL */
194         stm_rcc.cr &= ~(1UL << STM_RCC_CR_PLLON);
195         while (stm_rcc.cr & (1UL << STM_RCC_CR_PLLRDY))
196                 asm("nop");
197
198         /* Set multiplier */
199         cfgr = stm_rcc.cfgr;
200         cfgr &= ~(STM_RCC_CFGR_PLLMUL_MASK << STM_RCC_CFGR_PLLMUL);
201         cfgr |= (AO_RCC_CFGR_PLLMUL << STM_RCC_CFGR_PLLMUL);
202
203         /* PLL source */
204         cfgr &= ~(1UL << STM_RCC_CFGR_PLLSRC);
205         cfgr |= (STM_RCC_CFGR_PLLSRC_TARGET_CLOCK  << STM_RCC_CFGR_PLLSRC);
206         stm_rcc.cfgr = cfgr;
207
208         /* Set pre divider */
209         stm_rcc.cfgr2 = (AO_RCC_CFGR2_PLLDIV << STM_RCC_CFGR2_PREDIV);
210
211         /* Enable the PLL and wait for it */
212         stm_rcc.cr |= (1 << STM_RCC_CR_PLLON);
213         while (!(stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY)))
214                 asm("nop");
215
216 #endif
217
218 #if AO_HSI48
219 #define STM_RCC_CFGR_SWS_TARGET_CLOCK           STM_RCC_CFGR_SWS_HSI48
220 #define STM_RCC_CFGR_SW_TARGET_CLOCK            STM_RCC_CFGR_SW_HSI48
221
222         /* Turn HSI48 clock on */
223         stm_rcc.cr2 |= (1 << STM_RCC_CR2_HSI48ON);
224
225         /* Wait for clock to stabilize */
226         while ((stm_rcc.cr2 & (1 << STM_RCC_CR2_HSI48RDY)) == 0)
227                 ao_arch_nop();
228
229         ao_clock_enable_crs();
230 #endif
231
232 #ifndef STM_RCC_CFGR_SWS_TARGET_CLOCK
233 #define STM_HSI                                 16000000
234 #define STM_RCC_CFGR_SWS_TARGET_CLOCK           STM_RCC_CFGR_SWS_HSI
235 #define STM_RCC_CFGR_SW_TARGET_CLOCK            STM_RCC_CFGR_SW_HSI
236 #define STM_PLLSRC                              STM_HSI
237 #define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK        0
238 #endif
239
240
241 }
242
243 static void
244 ao_clock_normal_switch(void)
245 {
246         uint32_t        cfgr;
247
248         cfgr = stm_rcc.cfgr;
249         cfgr &= ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW);
250         cfgr |= (STM_RCC_CFGR_SW_TARGET_CLOCK << STM_RCC_CFGR_SW);
251         stm_rcc.cfgr = cfgr;
252         for (;;) {
253                 uint32_t        c, part, mask, val;
254
255                 c = stm_rcc.cfgr;
256                 mask = (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS);
257                 val = (STM_RCC_CFGR_SWS_TARGET_CLOCK << STM_RCC_CFGR_SWS);
258                 part = c & mask;
259                 if (part == val)
260                         break;
261         }
262 #if !AO_HSI && !AO_NEED_HSI
263         /* Turn off the HSI clock */
264         stm_rcc.cr &= ~(1UL << STM_RCC_CR_HSION);
265 #endif
266 #ifdef STM_PLLSRC
267         /* USB PLL source */
268         stm_rcc.cfgr3 |= (1 << STM_RCC_CFGR3_USBSW);
269 #endif
270 }
271
272 void
273 ao_clock_init(void)
274 {
275         uint32_t        cfgr;
276
277         /* Switch to HSI while messing about */
278         ao_clock_hsi();
279
280         /* Disable all interrupts */
281         stm_rcc.cir = 0;
282
283         /* Start high speed clock */
284         ao_clock_normal_start();
285
286         /* Set flash latency to tolerate 48MHz SYSCLK  -> 1 wait state */
287
288         /* Enable prefetch */
289         stm_flash.acr |= (1 << STM_FLASH_ACR_PRFTBE);
290
291         /* Enable 1 wait state so the CPU can run at 48MHz */
292         stm_flash.acr |= (STM_FLASH_ACR_LATENCY_1 << STM_FLASH_ACR_LATENCY);
293
294         /* HCLK to 48MHz -> AHB prescaler = /1 */
295         cfgr = stm_rcc.cfgr;
296         cfgr &= ~(STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE);
297         cfgr |= (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE);
298         stm_rcc.cfgr = cfgr;
299         while ((stm_rcc.cfgr & (STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE)) !=
300                (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE))
301                 ao_arch_nop();
302
303         /* APB Prescaler = AO_APB_PRESCALER */
304         cfgr = stm_rcc.cfgr;
305         cfgr &= ~(STM_RCC_CFGR_PPRE_MASK << STM_RCC_CFGR_PPRE);
306         cfgr |= (AO_RCC_CFGR_PPRE_DIV << STM_RCC_CFGR_PPRE);
307         stm_rcc.cfgr = cfgr;
308
309         /* Switch to the desired system clock */
310         ao_clock_normal_switch();
311
312         /* Clear reset flags */
313         stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
314
315 #ifdef AO_MCO_PORT
316         cfgr = stm_rcc.cfgr;
317
318         /* Send PLL clock to MCO */
319         cfgr &= ~(STM_RCC_CFGR_MCO_MASK << STM_RCC_CFGR_MCO);
320         cfgr |= (STM_RCC_CFGR_MCO_PLLCLK << STM_RCC_CFGR_MCO);
321
322         /* Divide by 1 */
323         cfgr &= ~(STM_RCC_CFGR_MCOPRE_DIV_MASK << STM_RCC_CFGR_MCOPRE);
324         cfgr |= (STM_RCC_CFGR_MCOPRE_DIV_1 << STM_RCC_CFGR_MCOPRE);
325
326         /* Don't divide PLL */
327         cfgr |= (1 << STM_RCC_CFGR_PLL_NODIV);
328
329         stm_rcc.cfgr = cfgr;
330
331         ao_enable_port(AO_MCO_PORT);
332         stm_ospeedr_set(AO_MCO_PORT, AO_MCO_PIN, STM_OSPEEDR_HIGH);
333         stm_afr_set(AO_MCO_PORT, AO_MCO_PIN, AO_MCO_AF);
334 #endif
335
336 #if DEBUG_THE_CLOCK
337         /* Output SYSCLK on PA8 for measurments */
338
339         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
340
341         stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0);
342         stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_HIGH);
343
344         stm_rcc.cfgr |= (STM_RCC_CFGR_MCOPRE_DIV_1 << STM_RCC_CFGR_MCOPRE);
345         stm_rcc.cfgr |= (STM_RCC_CFGR_MCOSEL_HSE << STM_RCC_CFGR_MCOSEL);
346 #endif
347 }
348
349 #if AO_POWER_MANAGEMENT
350 void
351 ao_clock_suspend(void)
352 {
353         ao_clock_hsi();
354 }
355
356 void
357 ao_clock_resume(void)
358 {
359         ao_clock_normal_start();
360         ao_clock_normal_switch();
361 }
362 #endif