beb8d1262c796b47346a58999d03c85a788eca56
[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 /* ao_spi_samd21.c */
127
128 #define AO_SPI_INDEX_BIT        0
129 #define AO_SPI_INDEX_MASK       0x07
130
131 #define AO_SPI_CONFIG_BIT       4
132 #define AO_SPI_CONFIG_MASK      (3 << AO_SPI_CONFIG_BIT)
133
134 #define AO_SPI_CPOL_BIT         6
135 #define AO_SPI_CPHA_BIT         7
136
137 #define AO_SPI_DOPO_BIT         8
138 #define  AO_SPI_DOPO_MOSI_0_SCLK_1      (0 << AO_SPI_DOPO_BIT)
139 #define  AO_SPI_DOPO_MOSI_2_SCLK_3      (1 << AO_SPI_DOPO_BIT)
140 #define  AO_SPI_DOPO_MOSI_3_SCLK_1      (2 << AO_SPI_DOPO_BIT)
141 #define  AO_SPI_DOPO_MOSI_0_SCLK_3      (3 << AO_SPI_DOPO_BIT)
142 #define  AO_SPI_DOPO_MASK               (3 << AO_SPI_DOPO_BIT)
143
144 #define AO_SPI_DIPO_BIT         10
145 #define  AO_SPI_DIPO_MISO_0             (0 << AO_SPI_DIPO_BIT)
146 #define  AO_SPI_DIPO_MISO_1             (1 << AO_SPI_DIPO_BIT)
147 #define  AO_SPI_DIPO_MISO_2             (2 << AO_SPI_DIPO_BIT)
148 #define  AO_SPI_DIPO_MISO_3             (3 << AO_SPI_DIPO_BIT)
149 #define  AO_SPI_DIPO_MASK               (3 << AO_SPI_DIPO_MASK)
150
151 #define AO_SPI_CONFIG_0         (0 << AO_SPI_CONFIG_BIT)
152 #define AO_SPI_CONFIG_1         (1 << AO_SPI_CONFIG_BIT)
153 #define AO_SPI_CONFIG_2         (2 << AO_SPI_CONFIG_BIT)
154 #define AO_SPI_CONFIG_3         (3 << AO_SPI_CONFIG_BIT)
155
156 #define AO_SPI_INDEX(id)        ((uint8_t) ((id) & AO_SPI_INDEX_MASK))
157 #define AO_SPI_CONFIG(id)       ((id) & AO_SPI_CONFIG_MASK)
158 #define AO_SPI_PIN_CONFIG(id)   ((id) & (AO_SPI_INDEX_MASK | AO_SPI_CONFIG_MASK))
159 #define AO_SPI_CPOL(id)         ((uint32_t) (((id) >> AO_SPI_CPOL_BIT) & 1))
160 #define AO_SPI_CPHA(id)         ((uint32_t) (((id) >> AO_SPI_CPHA_BIT) & 1))
161 #define AO_SPI_DOPO(id)         ((uint32_t) (((id) >> AO_SPI_DOPO_BIT) & 3))
162 #define AO_SPI_DIPO(id)         ((uint32_t) (((id) >> AO_SPI_DIPO_BIT) & 3))
163
164 #define AO_SPI_MAKE_MODE(pol,pha)       (((pol) << AO_SPI_CPOL_BIT) | ((pha) << AO_SPI_CPHA_BIT))
165 #define AO_SPI_MODE_0           AO_SPI_MAKE_MODE(0,0)
166 #define AO_SPI_MODE_1           AO_SPI_MAKE_MODE(0,1)
167 #define AO_SPI_MODE_2           AO_SPI_MAKE_MODE(1,0)
168 #define AO_SPI_MODE_3           AO_SPI_MAKE_MODE(1,1)
169
170 #if HAS_SPI_0
171 /*
172  * PA08 SERCOM0.0 -> MOSI       (DOPO 0)
173  * PA09 SERCOM0.1 -> SCLK       (DOPO 0)
174  * PA10 SERCOM0.2 -> MISO       (DIPO 2)
175  */
176 #define AO_SPI_0_PA08_PA09_PA10 (0 | AO_SPI_CONFIG_0 |          \
177                                  AO_SPI_DOPO_MOSI_0_SCLK_1 |    \
178                                  AO_SPI_DIPO_MISO_2)
179 /*
180  * PA04 SERCOM0.0 -> MOSI       (DOPO 0)
181  * PA05 SERCOM0.1 -> SCLK       (DOPO 0)
182  * PA06 SERCOM0.2 -> MISO       (DIPO 2)
183  */
184 #define AO_SPI_0_PA04_PA05_PA06 (0 | AO_SPI_CONFIG_1 |          \
185                                  AO_SPI_DOPO_MOSI_0_SCLK_1 |    \
186                                  AO_SPI_DIPO_MISO_2)
187 #endif /* HAS_SPI_0 */
188
189 #if HAS_SPI_3
190 /*
191  * PA22 SERCOM3.0 -> MOSI       (DOPO 0)
192  * PA23 SERCOM3.1 -> SCLK       (DOPO 0)
193  * PA20 SERCOM3.2 -> MISO       (DIPO 2)
194  */
195 #define AO_SPI_3_PA22_PA23_PA20 (3 | AO_SPI_CONFIG_0 |          \
196                                  AO_SPI_DOPO_MOSI_0_SCLK_1 |    \
197                                  AO_SPI_DIPO_MISO_2)
198 #endif /* HAS_SPI_3 */
199
200 #if HAS_SPI_4
201 /*
202  * PA04 SERCOM0.0 -> MOSI       (DOPO 0)
203  * PA05 SERCOM0.1 -> SCLK       (DOPO 0)
204  * PA16 SERCOM0.2 -> MISO       (DIPO 2)
205  */
206 #define AO_SPI_CONFIG_PA04_PA05_PA06    (0 | AO_SPI_CONFIG_1 |          \
207                                          AO_SPI_DOPO_MOSI_0_SCLK_1 |    \
208                                          AO_SPI_DIPO_MISO_2)
209
210 /*
211  * PB10 SERCOM4.2 -> MOSI       (DOPO 1)
212  * PB11 SERCOM4.3 -> SCLK       (DOPO 1)
213  * PA12 SERCOM4.0 -> MISO       (DIPO 0)
214  */
215 #define AO_SPI_4_PB10_PB11_PA12 (4 | AO_SPI_CONFIG_0 |          \
216                                  AO_SPI_DOPO_MOSI_2_SCLK_3 |    \
217                                  AO_SPI_DIPO_MISO_0)
218 #endif /* HAS_SPI_4 */
219
220 #if HAS_SPI_5
221 /*
222  * PB22 SERCOM5.2 -> MOSI       (DOPO 1)
223  * PB23 SERCOM5.3 -> SCLK       (DOPO 1)
224  * PB03 SERCOM5.1 -> MISO       (DIPO 1)
225  */
226 #define AO_SPI_5_PB22_PB23_PB03 (5 | AO_SPI_CONFIG_0 |          \
227                                  AO_SPI_DOPO_MOSI_2_SCLK_3 |    \
228                                  AO_SPI_DIPO_MISO_1)
229 #endif /* HAS_SPI_5 */
230
231 void
232 ao_spi_send(const void *block, uint16_t len, uint16_t spi_index);
233
234 void
235 ao_spi_send_fixed(uint8_t data, uint16_t len, uint16_t spi_index);
236
237 void
238 ao_spi_recv(void *block, uint16_t len, uint16_t spi_index);
239
240 void
241 ao_spi_duplex(const void *out, void *in, uint16_t len, uint16_t spi_index);
242
243 void
244 ao_spi_get(uint16_t spi_index, uint32_t speed);
245
246 void
247 ao_spi_put(uint16_t spi_index);
248
249 void
250 ao_spi_init(void);
251
252 #define ao_spi_set_cs(reg,mask) do {            \
253                 reg->outclr = mask;             \
254         } while(0)
255
256 #define ao_spi_clr_cs(reg,mask) do {            \
257                 reg->outset = mask;             \
258         } while(0)
259
260 #define ao_spi_get_mask(reg,mask,spi_index, speed) do {         \
261                 ao_spi_get(spi_index, speed);                           \
262                 ao_spi_set_cs(reg,mask);                        \
263         } while (0)
264
265 #define ao_spi_put_mask(reg,mask,spi_index) do {        \
266                 ao_spi_clr_cs(reg,mask);        \
267                 ao_spi_put(spi_index);          \
268         } while (0)
269
270 static inline void
271 ao_spi_get_bit(struct samd21_port *port, uint8_t bit, uint16_t spi_index, uint32_t speed)
272 {
273         ao_spi_get(spi_index, speed);
274         ao_gpio_set(port, bit, 0);
275 }
276
277 static inline void
278 ao_spi_put_bit(struct samd21_port *port, uint8_t bit, uint16_t spi_index)
279 {
280         ao_gpio_set(port, bit, 1);
281         ao_spi_put(spi_index);
282 }
283
284 static inline uint8_t
285 ao_spi_speed(uint32_t hz)
286 {
287         int32_t baud = (int32_t) (AO_SYSCLK / (2 * hz)) - 1;
288
289         if (baud < 0)
290                 baud = 0;
291         if (baud > 255)
292                 baud = 255;
293         return (uint8_t) baud;
294 }
295
296 #define ao_spi_init_cs(port, mask) do {                                 \
297                 uint8_t __bit__;                                        \
298                 for (__bit__ = 0; __bit__ < 32; __bit__++) {            \
299                         if (mask & (1 << __bit__))                      \
300                                 ao_enable_output(port, __bit__, 1); \
301                 }                                                       \
302         } while (0)
303
304 #define ARM_PUSH32(stack, val)  (*(--(stack)) = (val))
305
306 typedef uint32_t        ao_arch_irq_t;
307
308 static inline uint32_t
309 ao_arch_irqsave(void) {
310         uint32_t        primask;
311         asm("mrs %0,primask" : "=&r" (primask));
312         ao_arch_block_interrupts();
313         return primask;
314 }
315
316 static inline void
317 ao_arch_irqrestore(uint32_t primask) {
318         asm("msr primask,%0" : : "r" (primask));
319 }
320
321 static inline void
322 ao_arch_memory_barrier(void) {
323         asm volatile("" ::: "memory");
324 }
325
326 #if HAS_TASK
327 static inline void
328 ao_arch_init_stack(struct ao_task *task, uint32_t *sp, void *start)
329 {
330         uint32_t        a = (uint32_t) start;
331         int             i;
332
333         /* Return address (goes into LR) */
334         ARM_PUSH32(sp, a);
335
336         /* Clear register values r0-r7 */
337         i = 8;
338         while (i--)
339                 ARM_PUSH32(sp, 0);
340
341         /* APSR */
342         ARM_PUSH32(sp, 0);
343
344         /* PRIMASK with interrupts enabled */
345         ARM_PUSH32(sp, 0);
346
347         task->sp32 = sp;
348 }
349
350 static inline void ao_arch_save_regs(void) {
351         /* Save general registers */
352         asm("push {r0-r7,lr}\n");
353
354         /* Save APSR */
355         asm("mrs r0,apsr");
356         asm("push {r0}");
357
358         /* Save PRIMASK */
359         asm("mrs r0,primask");
360         asm("push {r0}");
361 }
362
363 static inline void ao_arch_save_stack(void) {
364         uint32_t        *sp;
365         asm("mov %0,sp" : "=&r" (sp) );
366         ao_cur_task->sp32 = (sp);
367         if (sp < &ao_cur_task->stack32[0])
368                 ao_panic (AO_PANIC_STACK);
369 }
370
371 static inline void ao_arch_restore_stack(void) {
372         /* Switch stacks */
373         asm("mov sp, %0" : : "r" (ao_cur_task->sp32) );
374
375         /* Restore PRIMASK */
376         asm("pop {r0}");
377         asm("msr primask,r0");
378
379         /* Restore APSR */
380         asm("pop {r0}");
381         asm("msr apsr_nczvq,r0");
382
383         /* Restore general registers */
384         asm("pop {r0-r7,pc}\n");
385 }
386
387 #ifndef HAS_SAMPLE_PROFILE
388 #define HAS_SAMPLE_PROFILE 0
389 #endif
390
391 #if !HAS_SAMPLE_PROFILE
392 #define HAS_ARCH_START_SCHEDULER        1
393
394 static inline void ao_arch_start_scheduler(void) {
395         uint32_t        sp;
396         uint32_t        control;
397
398         asm("mrs %0,msp" : "=&r" (sp));
399         asm("msr psp,%0" : : "r" (sp));
400         asm("mrs %0,control" : "=&r" (control));
401         control |= (1 << 1);
402         asm("msr control,%0" : : "r" (control));
403         asm("isb");
404 }
405 #endif
406
407 #define ao_arch_isr_stack()
408
409 #endif
410
411 #define ao_arch_wait_interrupt() do {                           \
412                 asm("\twfi\n");                                 \
413                 ao_arch_release_interrupts();                   \
414                 asm(".global ao_idle_loc\nao_idle_loc:");       \
415                 ao_arch_block_interrupts();                     \
416         } while (0)
417
418 #define ao_arch_critical(b) do {                        \
419                 uint32_t __mask = ao_arch_irqsave();    \
420                 do { b } while (0);                     \
421                 ao_arch_irqrestore(__mask);             \
422         } while (0)
423
424 /* ao_serial_samd21.c */
425
426 #if USE_SERIAL_0_FLOW && USE_SERIAL_0_SW_FLOW || USE_SERIAL_1_FLOW && USE_SERIAL_1_SW_FLOW
427 #define HAS_SERIAL_SW_FLOW      1
428 #else
429 #define HAS_SERIAL_SW_FLOW      0
430 #endif
431
432 #if USE_SERIAL_1_FLOW && !USE_SERIAL_1_SW_FLOW
433 #define USE_SERIAL_1_HW_FLOW    1
434 #endif
435
436 #if USE_SERIAL_0_FLOW && !USE_SERIAL_0_SW_FLOW
437 #define USE_SERIAL_0_HW_FLOW    1
438 #endif
439
440 #if USE_SERIAL_0_HW_FLOW || USE_SERIAL_1_HW_FLOW
441 #define HAS_SERIAL_HW_FLOW      1
442 #else
443 #define HAS_SERIAL_HW_FLOW      0
444 #endif
445
446 struct ao_samd21_usart {
447         struct ao_fifo          rx_fifo;
448         struct ao_fifo          tx_fifo;
449         struct samd21_sercom    *reg;
450         uint8_t                 tx_running;
451         uint8_t                 draining;
452 #if HAS_SERIAL_SW_FLOW
453         /* RTS - 0 if we have FIFO space, 1 if not
454          * CTS - 0 if we can send, 0 if not
455          */
456         struct samd21_port      *gpio_rts;
457         struct samd21_port      *gpio_cts;
458         uint8_t                 pin_rts;
459         uint8_t                 pin_cts;
460         uint8_t                 rts;
461 #endif
462 };
463
464 #if HAS_USART_0
465 extern struct ao_samd21_usart   ao_samd21_usart0;
466 #endif
467
468 void
469 ao_serial_init(void);
470
471 /* ao_usb_samd21.c */
472
473 #if AO_USB_OUT_HOOK
474 void
475 ao_usb_out_hook(uint8_t *buffer, uint16_t count);
476 #endif
477
478 void start(void);
479
480 #endif /* _AO_ARCH_FUNCS_H_ */