8ef93aa4383fd12b9aa0ced14f2e882339c84fec
[fw/altos] / src / samd21 / ao_arch_funcs.h
1 /*
2  * Copyright © 2019 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 #ifndef _AO_ARCH_FUNCS_H_
20 #define _AO_ARCH_FUNCS_H_
21
22 #define AO_MODE_PULL_NONE       0
23 #define AO_MODE_PULL_UP         1
24 #define AO_MODE_PULL_DOWN       2
25
26 static inline void ao_enable_port(struct samd21_port *port)
27 {
28         (void) port;
29         samd21_pm.apbbmask |= (1UL << SAMD21_PM_APBBMASK_PORT);
30 }
31
32 static inline void ao_disable_port(struct samd21_port *port)
33 {
34         (void) port;
35         samd21_pm.apbbmask &= ~(1UL << SAMD21_PM_APBBMASK_PORT);
36 }
37
38 static inline void
39 ao_gpio_set(struct samd21_port *port, uint8_t bit, uint8_t v)
40 {
41         if (v)
42                 port->outset = (1 << bit);
43         else
44                 port->outclr = (1 << bit);
45 }
46
47 static inline uint8_t
48 ao_gpio_get(struct samd21_port *port, uint8_t bit)
49 {
50         return (port->in >> bit) & 1;
51 }
52
53 static inline void
54 ao_gpio_dir_set(struct samd21_port *port, uint8_t bit, bool output)
55 {
56         if (output)
57                 port->dirset = (1 << bit);
58         else
59                 port->dirclr = (1 << bit);
60 }
61
62 static inline void
63 ao_gpio_set_mode(struct samd21_port *port, uint8_t bit, uint32_t mode)
64 {
65         uint8_t pincfg = 0;
66
67         if (mode != AO_MODE_PULL_NONE) {
68                 pincfg |= (1 << SAMD21_PORT_PINCFG_PULLEN);
69                 ao_gpio_set(port, bit, mode == AO_MODE_PULL_UP);
70         }
71
72         samd21_port_pincfg_set(port, bit,
73                                (0 << SAMD21_PORT_PINCFG_DRVSTR) |
74                                (1 << SAMD21_PORT_PINCFG_PULLEN) |
75                                (0 << SAMD21_PORT_PINCFG_INEN) |
76                                (0 << SAMD21_PORT_PINCFG_PMUXEN),
77                                pincfg);
78 }
79
80 static inline void
81 ao_enable_output(struct samd21_port *port, uint8_t pin, uint8_t v)
82 {
83         ao_enable_port(port);
84         ao_gpio_set(port, pin, v);
85         samd21_port_dir_set(port, pin, SAMD21_PORT_DIR_OUT);
86         samd21_port_pincfg_set(port, pin,
87                                (1 << SAMD21_PORT_PINCFG_DRVSTR) |
88                                (1 << SAMD21_PORT_PINCFG_PULLEN) |
89                                (1 << SAMD21_PORT_PINCFG_INEN),
90                                (0 << SAMD21_PORT_PINCFG_DRVSTR) |
91                                (0 << SAMD21_PORT_PINCFG_PULLEN) |
92                                (0 << SAMD21_PORT_PINCFG_INEN));
93 }
94
95 static inline void
96 ao_enable_input(struct samd21_port *port, uint8_t pin, uint32_t mode)
97 {
98         ao_enable_port(port);
99         samd21_port_dir_set(port, pin, SAMD21_PORT_DIR_IN);
100         uint8_t pincfg;
101
102         pincfg = ((0 << SAMD21_PORT_PINCFG_DRVSTR) |
103                   (0 << SAMD21_PORT_PINCFG_PULLEN) |
104                   (1 << SAMD21_PORT_PINCFG_INEN) |
105                   (0 << SAMD21_PORT_PINCFG_PMUXEN));
106
107         if (mode != AO_MODE_PULL_NONE) {
108                 pincfg |= (1 << SAMD21_PORT_PINCFG_PULLEN);
109                 ao_gpio_set(port, pin, mode == AO_MODE_PULL_UP);
110         }
111
112         samd21_port_pincfg_set(port, pin,
113                                (1 << SAMD21_PORT_PINCFG_DRVSTR) |
114                                (1 << SAMD21_PORT_PINCFG_PULLEN) |
115                                (1 << SAMD21_PORT_PINCFG_INEN) |
116                                (1 << SAMD21_PORT_PINCFG_PMUXEN),
117                                pincfg);
118 }
119
120 static inline void
121 ao_enable_cs(struct samd21_port *port, uint8_t pin)
122 {
123         ao_enable_output(port, pin, 1);
124 }
125
126 #define ARM_PUSH32(stack, val)  (*(--(stack)) = (val))
127
128 typedef uint32_t        ao_arch_irq_t;
129
130 static inline uint32_t
131 ao_arch_irqsave(void) {
132         uint32_t        primask;
133         asm("mrs %0,primask" : "=&r" (primask));
134         ao_arch_block_interrupts();
135         return primask;
136 }
137
138 static inline void
139 ao_arch_irqrestore(uint32_t primask) {
140         asm("msr primask,%0" : : "r" (primask));
141 }
142
143 static inline void
144 ao_arch_memory_barrier(void) {
145         asm volatile("" ::: "memory");
146 }
147
148 #if HAS_TASK
149 static inline void
150 ao_arch_init_stack(struct ao_task *task, uint32_t *sp, void *start)
151 {
152         uint32_t        a = (uint32_t) start;
153         int             i;
154
155         /* Return address (goes into LR) */
156         ARM_PUSH32(sp, a);
157
158         /* Clear register values r0-r7 */
159         i = 8;
160         while (i--)
161                 ARM_PUSH32(sp, 0);
162
163         /* APSR */
164         ARM_PUSH32(sp, 0);
165
166         /* PRIMASK with interrupts enabled */
167         ARM_PUSH32(sp, 0);
168
169         task->sp32 = sp;
170 }
171
172 static inline void ao_arch_save_regs(void) {
173         /* Save general registers */
174         asm("push {r0-r7,lr}\n");
175
176         /* Save APSR */
177         asm("mrs r0,apsr");
178         asm("push {r0}");
179
180         /* Save PRIMASK */
181         asm("mrs r0,primask");
182         asm("push {r0}");
183 }
184
185 static inline void ao_arch_save_stack(void) {
186         uint32_t        *sp;
187         asm("mov %0,sp" : "=&r" (sp) );
188         ao_cur_task->sp32 = (sp);
189         if (sp < &ao_cur_task->stack32[0])
190                 ao_panic (AO_PANIC_STACK);
191 }
192
193 static inline void ao_arch_restore_stack(void) {
194         /* Switch stacks */
195         asm("mov sp, %0" : : "r" (ao_cur_task->sp32) );
196
197         /* Restore PRIMASK */
198         asm("pop {r0}");
199         asm("msr primask,r0");
200
201         /* Restore APSR */
202         asm("pop {r0}");
203         asm("msr apsr_nczvq,r0");
204
205         /* Restore general registers */
206         asm("pop {r0-r7,pc}\n");
207 }
208
209 #ifndef HAS_SAMPLE_PROFILE
210 #define HAS_SAMPLE_PROFILE 0
211 #endif
212
213 #if !HAS_SAMPLE_PROFILE
214 #define HAS_ARCH_START_SCHEDULER        1
215
216 static inline void ao_arch_start_scheduler(void) {
217         uint32_t        sp;
218         uint32_t        control;
219
220         asm("mrs %0,msp" : "=&r" (sp));
221         asm("msr psp,%0" : : "r" (sp));
222         asm("mrs %0,control" : "=&r" (control));
223         control |= (1 << 1);
224         asm("msr control,%0" : : "r" (control));
225         asm("isb");
226 }
227 #endif
228
229 #define ao_arch_isr_stack()
230
231 #endif
232
233 #define ao_arch_wait_interrupt() do {                           \
234                 asm("\twfi\n");                                 \
235                 ao_arch_release_interrupts();                   \
236                 asm(".global ao_idle_loc\nao_idle_loc:");       \
237                 ao_arch_block_interrupts();                     \
238         } while (0)
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 /* ao_serial_samd21.c */
247
248 #if USE_SERIAL_0_FLOW && USE_SERIAL_0_SW_FLOW || USE_SERIAL_1_FLOW && USE_SERIAL_1_SW_FLOW
249 #define HAS_SERIAL_SW_FLOW      1
250 #else
251 #define HAS_SERIAL_SW_FLOW      0
252 #endif
253
254 #if USE_SERIAL_1_FLOW && !USE_SERIAL_1_SW_FLOW
255 #define USE_SERIAL_1_HW_FLOW    1
256 #endif
257
258 #if USE_SERIAL_0_FLOW && !USE_SERIAL_0_SW_FLOW
259 #define USE_SERIAL_0_HW_FLOW    1
260 #endif
261
262 #if USE_SERIAL_0_HW_FLOW || USE_SERIAL_1_HW_FLOW
263 #define HAS_SERIAL_HW_FLOW      1
264 #else
265 #define HAS_SERIAL_HW_FLOW      0
266 #endif
267
268 struct ao_samd21_usart {
269         struct ao_fifo          rx_fifo;
270         struct ao_fifo          tx_fifo;
271         struct samd21_sercom    *reg;
272         uint8_t                 tx_running;
273         uint8_t                 draining;
274 #if HAS_SERIAL_SW_FLOW
275         /* RTS - 0 if we have FIFO space, 1 if not
276          * CTS - 0 if we can send, 0 if not
277          */
278         struct samd21_port      *gpio_rts;
279         struct samd21_port      *gpio_cts;
280         uint8_t                 pin_rts;
281         uint8_t                 pin_cts;
282         uint8_t                 rts;
283 #endif
284 };
285
286 #if HAS_USART_0
287 extern struct ao_samd21_usart   ao_samd21_usart0;
288 #endif
289
290 void
291 ao_serial_init(void);
292
293 /* ao_usb_samd21.c */
294
295 #if AO_USB_OUT_HOOK
296 void
297 ao_usb_out_hook(uint8_t *buffer, uint16_t count);
298 #endif
299
300 void start(void);
301
302 #endif /* _AO_ARCH_FUNCS_H_ */