samd21: Wire up pin interrupts
[fw/altos] / src / samd21 / ao_exti_samd21.c
index aa865cc2d2aef6103466ab3a019f54fb05a3ddac..962197837713975d0950d9e7db048fe0ae0f5050 100644 (file)
 #include <ao.h>
 #include <ao_exti.h>
 
-struct samd21_exti {
-       void    (*callback)(void);
-       uint8_t port;
-       uint8_t pin;
+struct ao_samd21_exti {
+       void                    (*callback)(void);
+       struct samd21_port      *port;
+       uint8_t                 pin;
 };
 
-#if 0
-static struct samd21_exti ao_samd_exti[SAMD21_NUM_EVSYS];
+static struct ao_samd21_exti ao_samd21_exti[SAMD21_NUM_EIC];
+
+static uint8_t
+pin_id(struct samd21_port *port, uint8_t pin)
+{
+       /* Why, atmel, why? */
+       if (port == &samd21_port_a) {
+               switch (pin) {
+               case 24:
+               case 25:
+               case 27:
+                       return pin - 12;
+               case 28:
+               case 30:
+               case 31:
+                       return pin - 20;
+               default:
+                       break;
+               }
+       }
+
+       /* most pins use exti mapped to their pin number directly (mod 16) */
+       return pin & 0xf;
+}
 
-static uint32_t ao_exti_inuse;
-#endif
 
 void
-samd21_evsys_isr(void)
+samd21_eic_isr(void)
 {
+       uint32_t        intflag = samd21_eic.intflag;
+       uint8_t         id;
+
+       for (id = 0; id < SAMD21_NUM_EIC; id++) {
+               uint32_t mask = (1 << id);
+
+               if (intflag & mask) {
+                       samd21_eic.intflag = mask;
+                       if (ao_samd21_exti[id].callback)
+                               (*ao_samd21_exti[id].callback)();
+               }
+       }
+}
+
+static void
+_ao_exti_set_sense(uint8_t id, uint8_t mode)
+{
+       uint8_t         sense = mode & (SAMD21_EIC_CONFIG_SENSE_RISE | SAMD21_EIC_CONFIG_SENSE_FALL);
+       uint8_t         n = SAMD21_EIC_CONFIG_N(id);
+       uint32_t        config;
+
+       config = samd21_eic.config[n];
+       config &= ~(SAMD21_EIC_CONFIG_SENSE_MASK << SAMD21_EIC_CONFIG_SENSE(id));
+       config |= (sense << SAMD21_EIC_CONFIG_SENSE(id));
+       samd21_eic.config[n] = config;
 }
 
 void
 ao_exti_setup (struct samd21_port *port, uint8_t pin, uint8_t mode, void (*callback)(void))
 {
-       (void) port;
-       (void) pin;
-       (void) mode;
-       (void) callback;
-#if 0
-       uint8_t         id = pin_id(port,pin);
-       uint8_t         pint;
-       uint8_t         mask;
-       uint8_t         prio;
-
-       for (pint = 0; pint < SAMD21_NUM_EVSYS; pint++)
-               if ((ao_exti_inuse & (1 << pint)) == 0)
-                       break;
+       uint8_t                 id = pin_id(port,pin);
+       struct ao_samd21_exti   *exti = &ao_samd21_exti[id];
 
-       if (pint == SAMD21_NUM_EVSYS)
+       if (exti->port)
                ao_panic(AO_PANIC_EXTI);
 
        if (!(mode & AO_EXTI_PIN_NOCONFIGURE))
                ao_enable_input(port, pin, mode);
 
        ao_arch_block_interrupts();
-       mask = (1 << pint);
-       ao_exti_inuse |= mask;
-       ao_pint_enabled &= (uint8_t) ~mask;
-
-       ao_pint_map[id] = pint;
-       ao_exti_callback[pint] = callback;
 
-       /* configure gpio to interrupt routing */
-       lpc_scb.pintsel[pint] = id;
+       exti->port = port;
+       exti->pin = pin;
+       exti->callback = callback;
 
        /* Set edge triggered */
-       lpc_gpio_pin.isel &= ~mask;
+       _ao_exti_set_sense(id, mode);
 
-       ao_pint_enabled &= (uint8_t) ~mask;
-       ao_pint_mode[pint] = mode;
-       _ao_exti_set_enable(pint);
-
-       /* Set interrupt mask and rising/falling mode */
-
-       prio = AO_LPC_NVIC_MED_PRIORITY;
-       if (mode & AO_EXTI_PRIORITY_LOW)
-               prio = AO_LPC_NVIC_LOW_PRIORITY;
-       else if (mode & AO_EXTI_PRIORITY_HIGH)
-               prio = AO_LPC_NVIC_HIGH_PRIORITY;
-
-       /* Set priority and enable */
-       lpc_nvic_set_priority(LPC_ISR_PIN_INT0_POS + pint, prio);
-       lpc_nvic_set_enable(LPC_ISR_PIN_INT0_POS + pint);
        ao_arch_release_interrupts();
-#endif
 }
 
 void
 ao_exti_set_mode(struct samd21_port *port, uint8_t pin, uint8_t mode)
 {
-       (void) port;
-       (void) pin;
-       (void) mode;
-#if 0
-       uint8_t         id = pin_id(port,pin);
-       uint8_t         pint = ao_pint_map[id];
+       uint8_t                 id = pin_id(port,pin);
 
        ao_arch_block_interrupts();
-       ao_pint_mode[pint] = mode;
-       _ao_exti_set_enable(pint);
+       _ao_exti_set_sense(id, mode);
        ao_arch_release_interrupts();
-#endif
 }
 
 void
 ao_exti_set_callback(struct samd21_port *port, uint8_t pin, void (*callback)(void))
 {
-       (void) port;
-       (void) pin;
-       (void) callback;
-#if 0
        uint8_t         id = pin_id(port,pin);
-       uint8_t         pint = ao_pint_map[id];
 
-       ao_exti_callback[pint] = callback;
-#endif
+       ao_arch_block_interrupts();
+       ao_samd21_exti[id].callback = callback;
+       ao_arch_release_interrupts();
 }
 
 void
 ao_exti_enable(struct samd21_port *port, uint8_t pin)
 {
-       (void) port;
-       (void) pin;
-#if 0
        uint8_t         id = pin_id(port,pin);
-       uint8_t         pint = ao_pint_map[id];
-       uint8_t         mask = 1 << pint;
 
        ao_arch_block_interrupts();
-       ao_pint_enabled |= mask;
-       _ao_exti_set_enable(pint);
+       samd21_eic.intenset = 1 << id;
+       /* configure gpio to interrupt routing */
+       samd21_port_pmux_set(port, pin, SAMD21_PORT_PMUX_FUNC_A);
        ao_arch_release_interrupts();
-#endif
 }
 
 void
 ao_exti_disable(struct samd21_port *port, uint8_t pin)
 {
-       (void) port;
-       (void) pin;
-#if 0
        uint8_t         id = pin_id(port,pin);
-       uint8_t         pint = ao_pint_map[id];
-       uint8_t         mask = 1 << pint;
 
        ao_arch_block_interrupts();
-       ao_pint_enabled &= (uint8_t) ~mask;
-       _ao_exti_set_enable(pint);
+       samd21_eic.intenclr = 1 << id;
+       /* configure gpio to interrupt routing */
+       samd21_port_pmux_clr(port, pin);
        ao_arch_release_interrupts();
-#endif
 }
 
 void
 ao_exti_init(void)
 {
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_EIC);
+
+       /* Reset */
+       samd21_eic.ctrl = (1 << SAMD21_EIC_CTRL_SWRST);
+
+       while (samd21_eic.status & (1 << SAMD21_EIC_STATUS_SYNCBUSY))
+               ;
+
+       /* Wire up interrupts */
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_EIC_POS);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_EIC_POS, 3);
+
+       /* Enable */
+       samd21_eic.ctrl = (1 << SAMD21_EIC_CTRL_ENABLE);
 }