samd21: Save interrupt pin status before enabling interrupts
[fw/altos] / src / samd21 / ao_exti_samd21.c
1 /*
2  * Copyright © 2022 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_exti.h>
21
22 struct ao_samd21_exti {
23         void                    (*callback)(void);
24         uint8_t                 pmux;
25         uint8_t                 pincfg;
26 };
27
28 static struct ao_samd21_exti ao_samd21_exti[SAMD21_NUM_EIC];
29
30 static uint8_t
31 pin_id(struct samd21_port *port, uint8_t pin)
32 {
33         /* Why, atmel, why? */
34         if (port == &samd21_port_a) {
35                 switch (pin) {
36                 case 24:
37                 case 25:
38                 case 27:
39                         return pin - 12;
40                 case 28:
41                 case 30:
42                 case 31:
43                         return pin - 20;
44                 default:
45                         break;
46                 }
47         }
48
49         /* most pins use exti mapped to their pin number directly (mod 16) */
50         return pin & 0xf;
51 }
52
53
54 void
55 samd21_eic_isr(void)
56 {
57         uint32_t        intflag = samd21_eic.intflag;
58         uint8_t         id;
59
60         for (id = 0; id < SAMD21_NUM_EIC; id++) {
61                 uint32_t mask = (1 << id);
62
63                 if (intflag & mask) {
64                         samd21_eic.intflag = mask;
65                         if (ao_samd21_exti[id].callback)
66                                 (*ao_samd21_exti[id].callback)();
67                 }
68         }
69 }
70
71 static void
72 _ao_exti_set_sense(uint8_t id, uint8_t mode)
73 {
74         uint8_t         sense = mode & (SAMD21_EIC_CONFIG_SENSE_RISE | SAMD21_EIC_CONFIG_SENSE_FALL);
75         uint8_t         n = SAMD21_EIC_CONFIG_N(id);
76         uint32_t        config;
77
78         config = samd21_eic.config[n];
79         config &= ~(SAMD21_EIC_CONFIG_SENSE_MASK << SAMD21_EIC_CONFIG_SENSE(id));
80         config |= (sense << SAMD21_EIC_CONFIG_SENSE(id));
81         samd21_eic.config[n] = config;
82 }
83
84 void
85 ao_exti_setup (struct samd21_port *port, uint8_t pin, uint8_t mode, void (*callback)(void))
86 {
87         uint8_t                 id = pin_id(port,pin);
88         struct ao_samd21_exti   *exti = &ao_samd21_exti[id];
89
90         if (exti->callback)
91                 ao_panic(AO_PANIC_EXTI);
92
93         if (mode & AO_EXTI_PIN_NOCONFIGURE) {
94                 ao_enable_port(port);
95                 samd21_port_dir_set(port, pin, SAMD21_PORT_DIR_IN);
96                 samd21_port_pincfg_set(port, pin,
97                                        (1 << SAMD21_PORT_PINCFG_INEN),
98                                        (1 << SAMD21_PORT_PINCFG_INEN));
99         } else {
100                 ao_enable_input(port, pin, mode);
101         }
102
103         ao_arch_block_interrupts();
104
105         exti->callback = callback;
106
107         /* Set edge triggered */
108         _ao_exti_set_sense(id, mode);
109
110         ao_arch_release_interrupts();
111 }
112
113 void
114 ao_exti_set_mode(struct samd21_port *port, uint8_t pin, uint8_t mode)
115 {
116         uint8_t                 id = pin_id(port,pin);
117
118         ao_arch_block_interrupts();
119         _ao_exti_set_sense(id, mode);
120         ao_arch_release_interrupts();
121 }
122
123 void
124 ao_exti_set_callback(struct samd21_port *port, uint8_t pin, void (*callback)(void))
125 {
126         uint8_t         id = pin_id(port,pin);
127
128         ao_arch_block_interrupts();
129         ao_samd21_exti[id].callback = callback;
130         ao_arch_release_interrupts();
131 }
132
133 void
134 ao_exti_enable(struct samd21_port *port, uint8_t pin)
135 {
136         uint8_t         id = pin_id(port,pin);
137
138         ao_arch_block_interrupts();
139         /* configure gpio to interrupt routing */
140         if ((samd21_eic.intenset & (1 << id)) == 0) {
141                 ao_samd21_exti[id].pmux = samd21_port_pmux_get(port, pin);
142                 ao_samd21_exti[id].pincfg = samd21_port_pincfg_get(port, pin);
143         }
144         samd21_port_pmux_set(port, pin, SAMD21_PORT_PMUX_FUNC_A);
145         samd21_eic.intenset = 1 << id;
146         ao_arch_release_interrupts();
147 }
148
149 void
150 ao_exti_disable(struct samd21_port *port, uint8_t pin)
151 {
152         uint8_t         id = pin_id(port,pin);
153
154         ao_arch_block_interrupts();
155         samd21_eic.intenclr = 1 << id;
156         /* restore gpio config */
157         if (ao_samd21_exti[id].pincfg & (1 << SAMD21_PORT_PINCFG_PMUXEN))
158                 samd21_port_pmux_set(port, pin, ao_samd21_exti[id].pmux);
159         else
160                 samd21_port_pmux_clr(port, pin);
161         ao_arch_release_interrupts();
162 }
163
164 void
165 ao_exti_init(void)
166 {
167         samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_EIC);
168
169         /* Reset */
170         samd21_eic.ctrl = (1 << SAMD21_EIC_CTRL_SWRST);
171
172         while (samd21_eic.status & (1 << SAMD21_EIC_STATUS_SYNCBUSY))
173                 ;
174
175         /* Wire up interrupts */
176         samd21_nvic_set_enable(SAMD21_NVIC_ISR_EIC_POS);
177         samd21_nvic_set_priority(SAMD21_NVIC_ISR_EIC_POS, 3);
178
179         /* Enable */
180         samd21_eic.ctrl = (1 << SAMD21_EIC_CTRL_ENABLE);
181 }