2 * Copyright © 2022 Keith Packard <keithp@keithp.com>
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.
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.
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.
22 struct ao_samd21_exti {
23 void (*callback)(void);
28 static struct ao_samd21_exti ao_samd21_exti[SAMD21_NUM_EIC];
31 pin_id(struct samd21_port *port, uint8_t pin)
33 /* Why, atmel, why? */
34 if (port == &samd21_port_a) {
49 /* most pins use exti mapped to their pin number directly (mod 16) */
57 uint32_t intflag = samd21_eic.intflag;
60 for (id = 0; id < SAMD21_NUM_EIC; id++) {
61 uint32_t mask = (1 << id);
64 samd21_eic.intflag = mask;
65 if (ao_samd21_exti[id].callback)
66 (*ao_samd21_exti[id].callback)();
72 _ao_exti_set_sense(uint8_t id, uint8_t mode)
74 uint8_t sense = mode & (SAMD21_EIC_CONFIG_SENSE_RISE | SAMD21_EIC_CONFIG_SENSE_FALL);
75 uint8_t n = SAMD21_EIC_CONFIG_N(id);
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;
85 ao_exti_setup (struct samd21_port *port, uint8_t pin, uint8_t mode, void (*callback)(void))
87 uint8_t id = pin_id(port,pin);
88 struct ao_samd21_exti *exti = &ao_samd21_exti[id];
91 ao_panic(AO_PANIC_EXTI);
93 if (mode & AO_EXTI_PIN_NOCONFIGURE) {
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));
100 ao_enable_input(port, pin, mode);
103 ao_arch_block_interrupts();
105 exti->callback = callback;
107 /* Set edge triggered */
108 _ao_exti_set_sense(id, mode);
110 ao_arch_release_interrupts();
114 ao_exti_set_mode(struct samd21_port *port, uint8_t pin, uint8_t mode)
116 uint8_t id = pin_id(port,pin);
118 ao_arch_block_interrupts();
119 _ao_exti_set_sense(id, mode);
120 ao_arch_release_interrupts();
124 ao_exti_set_callback(struct samd21_port *port, uint8_t pin, void (*callback)(void))
126 uint8_t id = pin_id(port,pin);
128 ao_arch_block_interrupts();
129 ao_samd21_exti[id].callback = callback;
130 ao_arch_release_interrupts();
134 ao_exti_enable(struct samd21_port *port, uint8_t pin)
136 uint8_t id = pin_id(port,pin);
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);
144 samd21_port_pmux_set(port, pin, SAMD21_PORT_PMUX_FUNC_A);
145 samd21_eic.intenset = 1 << id;
146 ao_arch_release_interrupts();
150 ao_exti_disable(struct samd21_port *port, uint8_t pin)
152 uint8_t id = pin_id(port,pin);
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);
160 samd21_port_pmux_clr(port, pin);
161 ao_arch_release_interrupts();
167 samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_EIC);
170 samd21_eic.ctrl = (1 << SAMD21_EIC_CTRL_SWRST);
172 while (samd21_eic.status & (1 << SAMD21_EIC_STATUS_SYNCBUSY))
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);
180 samd21_eic.ctrl = (1 << SAMD21_EIC_CTRL_ENABLE);