altos/test: Adjust CRC error rate after FEC fix
[fw/altos] / src / stm32f4 / ao_arch_funcs.h
1 /*
2  * Copyright © 2018 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
15 #ifndef _AO_ARCH_FUNCS_H_
16 #define _AO_ARCH_FUNCS_H_
17
18 /* task functions */
19
20 #define ARM_PUSH32(stack, val)  (*(--(stack)) = (val))
21
22 typedef uint32_t        ao_arch_irq_t;
23
24 static inline void
25 ao_arch_block_interrupts(void) {
26 #ifdef AO_NONMASK_INTERRUPTS
27         asm("msr basepri,%0" : : "r" (AO_STM_NVIC_BASEPRI_MASK));
28 #else
29         asm("cpsid i");
30 #endif
31 }
32
33 static inline void
34 ao_arch_release_interrupts(void) {
35 #ifdef AO_NONMASK_INTERRUPTS
36         asm("msr basepri,%0" : : "r" (0x0));
37 #else
38         asm("cpsie i");
39 #endif
40 }
41
42 static inline uint32_t
43 ao_arch_irqsave(void) {
44         uint32_t        val;
45 #ifdef AO_NONMASK_INTERRUPTS
46         asm("mrs %0,basepri" : "=r" (val));
47 #else
48         asm("mrs %0,primask" : "=r" (val));
49 #endif
50         ao_arch_block_interrupts();
51         return val;
52 }
53
54 static inline void
55 ao_arch_irqrestore(uint32_t basepri) {
56 #ifdef AO_NONMASK_INTERRUPTS
57         asm("msr basepri,%0" : : "r" (basepri));
58 #else
59         asm("msr primask,%0" : : "r" (basepri));
60 #endif
61 }
62
63 static inline void
64 ao_arch_memory_barrier(void) {
65         asm volatile("" ::: "memory");
66 }
67
68 static inline void
69 ao_arch_irq_check(void) {
70 #ifdef AO_NONMASK_INTERRUPTS
71         uint32_t        basepri;
72         asm("mrs %0,basepri" : "=r" (basepri));
73         if (basepri == 0)
74                 ao_panic(AO_PANIC_IRQ);
75 #else
76         uint32_t        primask;
77         asm("mrs %0,primask" : "=r" (primask));
78         if ((primask & 1) == 0)
79                 ao_panic(AO_PANIC_IRQ);
80 #endif
81 }
82
83 #if HAS_TASK
84 static inline void
85 ao_arch_init_stack(struct ao_task *task, uint32_t *sp, void *start)
86 {
87         uint32_t        a = (uint32_t) start;
88         int             i;
89
90         /* Return address (goes into LR) */
91         ARM_PUSH32(sp, a);
92
93         /* Clear register values r0-r12 */
94         i = 13;
95         while (i--)
96                 ARM_PUSH32(sp, 0);
97
98         /* APSR */
99         ARM_PUSH32(sp, 0);
100
101         /* Clear register values s0-s31 */
102         i = 32;
103         while (i--)
104                 ARM_PUSH32(sp, 0);
105
106         /* FPSCR */
107         ARM_PUSH32(sp, 0);
108
109         /* BASEPRI with interrupts enabled */
110         ARM_PUSH32(sp, 0);
111
112         task->sp32 = sp;
113 }
114
115 static inline void ao_arch_save_regs(void) {
116         /* Save general registers */
117         asm("push {r0-r12,lr}");
118
119         /* Save APSR */
120         asm("mrs r0,apsr");
121         asm("push {r0}");
122
123         /* Save FPU registers */
124         asm("vpush {s0-s15}");
125         asm("vpush {s16-s31}");
126
127         /* Save FPSCR */
128         asm("vmrs r0,fpscr");
129         asm("push {r0}");
130
131 #ifdef AO_NONMASK_INTERRUPTS
132         /* Save BASEPRI */
133         asm("mrs r0,basepri");
134 #else
135         /* Save PRIMASK */
136         asm("mrs r0,primask");
137 #endif
138         asm("push {r0}");
139 }
140
141 static inline void ao_arch_save_stack(void) {
142         uint32_t        *sp;
143         asm("mov %0,sp" : "=&r" (sp) );
144         ao_cur_task->sp32 = (sp);
145 }
146
147 static inline void ao_arch_restore_stack(void) {
148         /* Switch stacks */
149         asm("mov sp, %0" : : "r" (ao_cur_task->sp32) );
150
151 #ifdef AO_NONMASK_INTERRUPTS
152         /* Restore BASEPRI */
153         asm("pop {r0}");
154         asm("msr basepri,r0");
155 #else
156         /* Restore PRIMASK */
157         asm("pop {r0}");
158         asm("msr primask,r0");
159 #endif
160
161         /* Restore FPSCR */
162         asm("pop {r0}");
163         asm("vmsr fpscr,r0");
164
165         /* Restore FPU registers */
166         asm("vpop {s16-s31}");
167         asm("vpop {s0-s15}");
168
169         /* Restore APSR */
170         asm("pop {r0}");
171         asm("msr apsr_nczvq,r0");
172
173         /* Restore general registers */
174         asm("pop {r0-r12,lr}\n");
175
176         /* Return to calling function */
177         asm("bx lr");
178 }
179
180 #ifndef HAS_SAMPLE_PROFILE
181 #define HAS_SAMPLE_PROFILE 0
182 #endif
183
184 #if DEBUG
185 #define HAS_ARCH_VALIDATE_CUR_STACK     1
186
187 static inline void
188 ao_validate_cur_stack(void)
189 {
190         uint8_t         *psp;
191
192         asm("mrs %0,psp" : "=&r" (psp));
193         if (ao_cur_task &&
194             psp <= ao_cur_task->stack &&
195             psp >= ao_cur_task->stack - 256)
196                 ao_panic(AO_PANIC_STACK);
197 }
198 #endif
199
200 #if !HAS_SAMPLE_PROFILE
201 #define HAS_ARCH_START_SCHEDULER        1
202
203 static inline void ao_arch_start_scheduler(void) {
204         uint32_t        sp;
205         uint32_t        control;
206
207         asm("mrs %0,msp" : "=&r" (sp));
208         asm("msr psp,%0" : : "r" (sp));
209         asm("mrs %0,control" : "=r" (control));
210         control |= (1 << 1);
211         asm("msr control,%0" : : "r" (control));
212         asm("isb");
213 }
214 #endif
215
216 #define ao_arch_isr_stack()
217
218 #endif
219
220 static inline void
221 ao_arch_wait_interrupt(void) {
222 #ifdef AO_NONMASK_INTERRUPTS
223         asm(
224             "dsb\n"                     /* Serialize data */
225             "isb\n"                     /* Serialize instructions */
226             "cpsid i\n"                 /* Block all interrupts */
227             "msr basepri,%0\n"          /* Allow all interrupts through basepri */
228             "wfi\n"                     /* Wait for an interrupt */
229             "cpsie i\n"                 /* Allow all interrupts */
230             "msr basepri,%1\n"          /* Block interrupts through basepri */
231             : : "r" (0), "r" (AO_STM_NVIC_BASEPRI_MASK));
232 #else
233         asm("\twfi\n");
234         ao_arch_release_interrupts();
235         ao_arch_block_interrupts();
236 #endif
237 }
238
239 #define ao_arch_critical(b) do {                        \
240                 uint32_t __mask = ao_arch_irqsave();    \
241                 do { b } while (0);                     \
242                 ao_arch_irqrestore(__mask);             \
243         } while (0)
244
245 /* GPIO functions */
246
247 #define ao_power_register(gpio)
248 #define ao_power_unregister(gpio)
249
250 static inline void ao_enable_port(struct stm_gpio *port)
251 {
252         if ((port) == &stm_gpioa) {
253                 stm_rcc_ahb1_clk_enable(1 << STM_RCC_AHB1ENR_IOPAEN);
254                 ao_power_register(&ao_power_gpioa);
255         } else if ((port) == &stm_gpiob) {
256                 stm_rcc_ahb1_clk_enable(1 << STM_RCC_AHB1ENR_IOPBEN);
257                 ao_power_register(&ao_power_gpiob);
258         } else if ((port) == &stm_gpioc) {
259                 stm_rcc_ahb1_clk_enable(1 << STM_RCC_AHB1ENR_IOPCEN);
260                 ao_power_register(&ao_power_gpioc);
261         } else if ((port) == &stm_gpiod) {
262                 stm_rcc_ahb1_clk_enable(1 << STM_RCC_AHB1ENR_IOPDEN);
263                 ao_power_register(&ao_power_gpiod);
264         } else if ((port) == &stm_gpioe) {
265                 stm_rcc_ahb1_clk_enable(1 << STM_RCC_AHB1ENR_IOPEEN);
266                 ao_power_register(&ao_power_gpioe);
267         } else if ((port) == &stm_gpiof) {
268                 stm_rcc_ahb1_clk_enable(1 << STM_RCC_AHB1ENR_IOPFEN);
269                 ao_power_register(&ao_power_gpiof);
270         } else if ((port) == &stm_gpiog) {
271                 stm_rcc_ahb1_clk_enable(1 << STM_RCC_AHB1ENR_IOPGEN);
272                 ao_power_register(&ao_power_gpiog);
273         } else if ((port) == &stm_gpioh) {
274                 stm_rcc_ahb1_clk_enable(1 << STM_RCC_AHB1ENR_IOPHEN);
275                 ao_power_register(&ao_power_gpioh);
276         }
277 }
278
279 static inline void ao_disable_port(struct stm_gpio *port)
280 {
281         if ((port) == &stm_gpioa) {
282                 stm_rcc_ahb1_clk_disable(1 << STM_RCC_AHB1ENR_IOPAEN);
283                 ao_power_unregister(&ao_power_gpioa);
284         } else if ((port) == &stm_gpiob) {
285                 stm_rcc_ahb1_clk_disable(1 << STM_RCC_AHB1ENR_IOPBEN);
286                 ao_power_unregister(&ao_power_gpiob);
287         } else if ((port) == &stm_gpioc) {
288                 stm_rcc_ahb1_clk_disable(1 << STM_RCC_AHB1ENR_IOPCEN);
289                 ao_power_unregister(&ao_power_gpioc);
290         } else if ((port) == &stm_gpiod) {
291                 stm_rcc_ahb1_clk_disable(1 << STM_RCC_AHB1ENR_IOPDEN);
292                 ao_power_unregister(&ao_power_gpiod);
293         } else if ((port) == &stm_gpioe) {
294                 stm_rcc_ahb1_clk_disable(1 << STM_RCC_AHB1ENR_IOPEEN);
295                 ao_power_unregister(&ao_power_gpioe);
296         } else if ((port) == &stm_gpiof) {
297                 stm_rcc_ahb1_clk_disable(1 << STM_RCC_AHB1ENR_IOPFEN);
298                 ao_power_unregister(&ao_power_gpiof);
299         } else if ((port) == &stm_gpiog) {
300                 stm_rcc_ahb1_clk_disable(1 << STM_RCC_AHB1ENR_IOPGEN);
301                 ao_power_unregister(&ao_power_gpiog);
302         } else if ((port) == &stm_gpioh) {
303                 stm_rcc_ahb1_clk_disable(1 << STM_RCC_AHB1ENR_IOPHEN);
304                 ao_power_unregister(&ao_power_gpioh);
305         }
306 }
307
308 #define ao_gpio_set(port, bit, v) stm_gpio_set(port, bit, v)
309
310 #define ao_gpio_get(port, bit) stm_gpio_get(port, bit)
311
312 #define ao_enable_output(port,bit,v) do {                       \
313                 ao_enable_port(port);                           \
314                 ao_gpio_set(port, bit, v);                      \
315                 stm_moder_set(port, bit, STM_MODER_OUTPUT);\
316         } while (0)
317
318 #define ao_gpio_set_mode(port,bit,mode) do {                            \
319                 if (mode == AO_EXTI_MODE_PULL_UP)                       \
320                         stm_pupdr_set(port, bit, STM_PUPDR_PULL_UP);    \
321                 else if (mode == AO_EXTI_MODE_PULL_DOWN)                \
322                         stm_pupdr_set(port, bit, STM_PUPDR_PULL_DOWN);  \
323                 else                                                    \
324                         stm_pupdr_set(port, bit, STM_PUPDR_NONE);       \
325         } while (0)
326
327 #define ao_enable_input(port,bit,mode) do {                             \
328                 ao_enable_port(port);                                   \
329                 stm_moder_set(port, bit, STM_MODER_INPUT);              \
330                 ao_gpio_set_mode(port, bit, mode);                      \
331         } while (0)
332
333 /* usart */
334
335 void
336 ao_usart_init(void);
337
338 void
339 start(void);
340
341 char
342 ao_serial6_getchar(void);
343
344 void
345 ao_serial6_putchar(char c);
346
347 int
348 _ao_serial6_pollchar(void);
349
350 uint8_t
351 _ao_serial6_sleep_for(uint16_t timeout);
352
353 void
354 ao_serial6_set_speed(uint32_t speed);
355
356 void
357 ao_serial6_drain(void);
358
359 #endif /* _AO_ARCH_FUNCS_H_ */