altos/stm32f4: Add -mfloat-abi=hard compile option
[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() {
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, void *start)
86 {
87         uint32_t        *sp = &task->stack32[AO_STACK_SIZE>>2];
88         uint32_t        a = (uint32_t) start;
89         int             i;
90
91         /* Return address (goes into LR) */
92         ARM_PUSH32(sp, a);
93
94         /* Clear register values r0-r12 */
95         i = 13;
96         while (i--)
97                 ARM_PUSH32(sp, 0);
98
99         /* APSR */
100         ARM_PUSH32(sp, 0);
101
102         /* Clear register values s0-s31 */
103         i = 32;
104         while (i--)
105                 ARM_PUSH32(sp, 0);
106
107         /* FPSCR */
108         ARM_PUSH32(sp, 0);
109
110         /* BASEPRI with interrupts enabled */
111         ARM_PUSH32(sp, 0);
112
113         task->sp32 = sp;
114 }
115
116 static inline void ao_arch_save_regs(void) {
117         /* Save general registers */
118         asm("push {r0-r12,lr}");
119
120         /* Save APSR */
121         asm("mrs r0,apsr");
122         asm("push {r0}");
123
124         /* Save FPU registers */
125         asm("vpush {s0-s15}");
126         asm("vpush {s16-s31}");
127
128         /* Save FPSCR */
129         asm("vmrs r0,fpscr");
130         asm("push {r0}");
131
132 #ifdef AO_NONMASK_INTERRUPTS
133         /* Save BASEPRI */
134         asm("mrs r0,basepri");
135 #else
136         /* Save PRIMASK */
137         asm("mrs r0,primask");
138 #endif
139         asm("push {r0}");
140 }
141
142 static inline void ao_arch_save_stack(void) {
143         uint32_t        *sp;
144         asm("mov %0,sp" : "=&r" (sp) );
145         ao_cur_task->sp32 = (sp);
146 }
147
148 static inline void ao_arch_restore_stack(void) {
149         /* Switch stacks */
150         asm("mov sp, %0" : : "r" (ao_cur_task->sp32) );
151
152 #ifdef AO_NONMASK_INTERRUPTS
153         /* Restore BASEPRI */
154         asm("pop {r0}");
155         asm("msr basepri,r0");
156 #else
157         /* Restore PRIMASK */
158         asm("pop {r0}");
159         asm("msr primask,r0");
160 #endif
161
162         /* Restore FPSCR */
163         asm("pop {r0}");
164         asm("vmsr fpscr,r0");
165
166         /* Restore FPU registers */
167         asm("vpop {s16-s31}");
168         asm("vpop {s0-s15}");
169
170         /* Restore APSR */
171         asm("pop {r0}");
172         asm("msr apsr_nczvq,r0");
173
174         /* Restore general registers */
175         asm("pop {r0-r12,lr}\n");
176
177         /* Return to calling function */
178         asm("bx lr");
179 }
180
181 #ifndef HAS_SAMPLE_PROFILE
182 #define HAS_SAMPLE_PROFILE 0
183 #endif
184
185 #if DEBUG
186 #define HAS_ARCH_VALIDATE_CUR_STACK     1
187
188 static inline void
189 ao_validate_cur_stack(void)
190 {
191         uint8_t         *psp;
192
193         asm("mrs %0,psp" : "=&r" (psp));
194         if (ao_cur_task &&
195             psp <= ao_cur_task->stack &&
196             psp >= ao_cur_task->stack - 256)
197                 ao_panic(AO_PANIC_STACK);
198 }
199 #endif
200
201 #if !HAS_SAMPLE_PROFILE
202 #define HAS_ARCH_START_SCHEDULER        1
203
204 static inline void ao_arch_start_scheduler(void) {
205         uint32_t        sp;
206         uint32_t        control;
207
208         asm("mrs %0,msp" : "=&r" (sp));
209         asm("msr psp,%0" : : "r" (sp));
210         asm("mrs %0,control" : "=r" (control));
211         control |= (1 << 1);
212         asm("msr control,%0" : : "r" (control));
213         asm("isb");
214 }
215 #endif
216
217 #define ao_arch_isr_stack()
218
219 #endif
220
221 static inline void
222 ao_arch_wait_interrupt(void) {
223 #ifdef AO_NONMASK_INTERRUPTS
224         asm(
225             "dsb\n"                     /* Serialize data */
226             "isb\n"                     /* Serialize instructions */
227             "cpsid i\n"                 /* Block all interrupts */
228             "msr basepri,%0\n"          /* Allow all interrupts through basepri */
229             "wfi\n"                     /* Wait for an interrupt */
230             "cpsie i\n"                 /* Allow all interrupts */
231             "msr basepri,%1\n"          /* Block interrupts through basepri */
232             : : "r" (0), "r" (AO_STM_NVIC_BASEPRI_MASK));
233 #else
234         asm("\twfi\n");
235         ao_arch_release_interrupts();
236         ao_arch_block_interrupts();
237 #endif
238 }
239
240 #define ao_arch_critical(b) do {                        \
241                 uint32_t __mask = ao_arch_irqsave();    \
242                 do { b } while (0);                     \
243                 ao_arch_irqrestore(__mask);             \
244         } while (0)
245
246 /* GPIO functions */
247
248 #define ao_power_register(gpio)
249 #define ao_power_unregister(gpio)
250
251 static inline void ao_enable_port(struct stm_gpio *port)
252 {
253         if ((port) == &stm_gpioa) {
254                 stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPAEN);
255                 ao_power_register(&ao_power_gpioa);
256         } else if ((port) == &stm_gpiob) {
257                 stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPBEN);
258                 ao_power_register(&ao_power_gpiob);
259         } else if ((port) == &stm_gpioc) {
260                 stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPCEN);
261                 ao_power_register(&ao_power_gpioc);
262         } else if ((port) == &stm_gpiod) {
263                 stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPDEN);
264                 ao_power_register(&ao_power_gpiod);
265         } else if ((port) == &stm_gpioe) {
266                 stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPEEN);
267                 ao_power_register(&ao_power_gpioe);
268         } else if ((port) == &stm_gpiof) {
269                 stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPFEN);
270                 ao_power_register(&ao_power_gpiof);
271         } else if ((port) == &stm_gpiog) {
272                 stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPGEN);
273                 ao_power_register(&ao_power_gpiog);
274         } else if ((port) == &stm_gpioh) {
275                 stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPHEN);
276                 ao_power_register(&ao_power_gpioh);
277         }
278 }
279
280 static inline void ao_disable_port(struct stm_gpio *port)
281 {
282         if ((port) == &stm_gpioa) {
283                 stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPAEN);
284                 ao_power_unregister(&ao_power_gpioa);
285         } else if ((port) == &stm_gpiob) {
286                 stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPBEN);
287                 ao_power_unregister(&ao_power_gpiob);
288         } else if ((port) == &stm_gpioc) {
289                 stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPCEN);
290                 ao_power_unregister(&ao_power_gpioc);
291         } else if ((port) == &stm_gpiod) {
292                 stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPDEN);
293                 ao_power_unregister(&ao_power_gpiod);
294         } else if ((port) == &stm_gpioe) {
295                 stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPEEN);
296                 ao_power_unregister(&ao_power_gpioe);
297         } else if ((port) == &stm_gpiof) {
298                 stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPFEN);
299                 ao_power_unregister(&ao_power_gpiof);
300         } else if ((port) == &stm_gpiog) {
301                 stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPGEN);
302                 ao_power_unregister(&ao_power_gpiog);
303         } else if ((port) == &stm_gpioh) {
304                 stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPHEN);
305                 ao_power_unregister(&ao_power_gpioh);
306         }
307 }
308
309 #define ao_gpio_set(port, bit, v) stm_gpio_set(port, bit, v)
310
311 #define ao_gpio_get(port, bit) stm_gpio_get(port, bit)
312
313 #define ao_enable_output(port,bit,v) do {                       \
314                 ao_enable_port(port);                           \
315                 ao_gpio_set(port, bit, v);                      \
316                 stm_moder_set(port, bit, STM_MODER_OUTPUT);\
317         } while (0)
318
319 #define ao_gpio_set_mode(port,bit,mode) do {                            \
320                 if (mode == AO_EXTI_MODE_PULL_UP)                       \
321                         stm_pupdr_set(port, bit, STM_PUPDR_PULL_UP);    \
322                 else if (mode == AO_EXTI_MODE_PULL_DOWN)                \
323                         stm_pupdr_set(port, bit, STM_PUPDR_PULL_DOWN);  \
324                 else                                                    \
325                         stm_pupdr_set(port, bit, STM_PUPDR_NONE);       \
326         } while (0)
327
328 #define ao_enable_input(port,bit,mode) do {                             \
329                 ao_enable_port(port);                                   \
330                 stm_moder_set(port, bit, STM_MODER_INPUT);              \
331                 ao_gpio_set_mode(port, bit, mode);                      \
332         } while (0)
333
334 /* usart */
335
336 void
337 ao_usart_init(void);
338
339
340 #endif /* _AO_ARCH_FUNCS_H_ */