altos: Add SAMD21 bits
authorKeith Packard <keithp@keithp.com>
Sun, 25 Sep 2022 20:55:43 +0000 (13:55 -0700)
committerKeith Packard <keithp@keithp.com>
Sun, 25 Sep 2022 20:55:43 +0000 (13:55 -0700)
Brought back from snek, these bits provide Altos support for the Atmel
SAMD21 family of chips.

Signed-off-by: Keith Packard <keithp@keithp.com>
32 files changed:
src/samd21/Makefile-flash.defs [new file with mode: 0644]
src/samd21/Makefile-samd21.defs [new file with mode: 0644]
src/samd21/Makefile.defs [new file with mode: 0644]
src/samd21/altos-loader.ld [new file with mode: 0644]
src/samd21/altos.ld [new file with mode: 0644]
src/samd21/ao_adc_samd21.c [new file with mode: 0644]
src/samd21/ao_adc_samd21.h [new file with mode: 0644]
src/samd21/ao_apa102.c [new file with mode: 0644]
src/samd21/ao_arch.h [new file with mode: 0644]
src/samd21/ao_arch_funcs.h [new file with mode: 0644]
src/samd21/ao_boot_chain.c [new file with mode: 0644]
src/samd21/ao_boot_pin.c [new file with mode: 0644]
src/samd21/ao_dac_samd21.c [new file with mode: 0644]
src/samd21/ao_dac_samd21.h [new file with mode: 0644]
src/samd21/ao_dma_samd21.c [new file with mode: 0644]
src/samd21/ao_dma_samd21.h [new file with mode: 0644]
src/samd21/ao_exti.h [new file with mode: 0644]
src/samd21/ao_flash.h [new file with mode: 0644]
src/samd21/ao_flash_loader_samd21.c [new file with mode: 0644]
src/samd21/ao_flash_samd21.c [new file with mode: 0644]
src/samd21/ao_flash_samd21_pins.h [new file with mode: 0644]
src/samd21/ao_interrupt.c [new file with mode: 0644]
src/samd21/ao_neopixel.c [new file with mode: 0644]
src/samd21/ao_serial.h [new file with mode: 0644]
src/samd21/ao_serial_samd21.c [new file with mode: 0644]
src/samd21/ao_tc_samd21.c [new file with mode: 0644]
src/samd21/ao_tc_samd21.h [new file with mode: 0644]
src/samd21/ao_tcc_samd21.c [new file with mode: 0644]
src/samd21/ao_tcc_samd21.h [new file with mode: 0644]
src/samd21/ao_timer.c [new file with mode: 0644]
src/samd21/ao_usb_samd21.c [new file with mode: 0644]
src/samd21/samd21.h [new file with mode: 0644]

diff --git a/src/samd21/Makefile-flash.defs b/src/samd21/Makefile-flash.defs
new file mode 100644 (file)
index 0000000..d489bf1
--- /dev/null
@@ -0,0 +1,58 @@
+include $(TOPDIR)/samd21/Makefile-samd21.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_flash_pins.h \
+       ao_flash_samd21_pins.h \
+       ao_flash_task.h \
+       ao_pins.h \
+       ao_product.h \
+       Makefile
+
+#
+# Common AltOS sources
+#
+SRC = \
+       ao_interrupt.c \
+       ao_romconfig.c \
+       ao_boot_chain.c \
+       ao_boot_pin.c \
+       ao_product.c \
+       ao_notask.c \
+       ao_timer.c \
+       ao_usb_samd21.c \
+       ao_flash_samd21.c \
+       ao_flash_task.c \
+       ao_flash_loader_samd21.c
+
+OBJ=$(SRC:.c=.o)
+
+PRODUCT=AltosFlash
+PRODUCT_DEF=-DALTOS_FLASH
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(SAMD21_CFLAGS)
+
+LDFLAGS=-nostartfiles $(CFLAGS) -L$(TOPDIR)/samd21 -Taltos-loader.ld -n
+
+PROGNAME=$(HARDWARE)-altos-flash
+PROG=$(PROGNAME)-$(VERSION).elf
+
+$(PROG): Makefile $(OBJ) altos-loader.ld
+       $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+all: $(PROG)
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.map
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/samd21/Makefile-samd21.defs b/src/samd21/Makefile-samd21.defs
new file mode 100644 (file)
index 0000000..ef8713c
--- /dev/null
@@ -0,0 +1,12 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/Makefile.defs
+
+vpath % $(TOPDIR)/samd21:$(AO_VPATH)
+
+CC=$(ARM_CC)
+
+SAMD21_CFLAGS=-mlittle-endian -mcpu=cortex-m0 -mthumb \
+       -I$(TOPDIR)/samd21 $(AO_CFLAGS) $(PICOLIBC_CFLAGS)
diff --git a/src/samd21/Makefile.defs b/src/samd21/Makefile.defs
new file mode 100644 (file)
index 0000000..eb6a267
--- /dev/null
@@ -0,0 +1,9 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/samd21/Makefile-samd21.defs
+
+LDFLAGS=-nostartfiles $(CFLAGS) -L$(TOPDIR)/samd21 -Taltos.ld -n
+
+.DEFAULT_GOAL=all
diff --git a/src/samd21/altos-loader.ld b/src/samd21/altos-loader.ld
new file mode 100644 (file)
index 0000000..6c12d32
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+__flash = 0x00000000;
+__flash_size = 4K;
+__ram = 0x20000000;
+__ram_size = 32k;
+__stack_size = 256;
+
+INCLUDE picolibc.ld
diff --git a/src/samd21/altos.ld b/src/samd21/altos.ld
new file mode 100644 (file)
index 0000000..b5cb413
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+__flash = 0x00001000;
+__flash_size = 128K - 4K;
+__ram = 0x20000000;
+__ram_size = 32k;
+__stack_size = 256;
+
+INCLUDE picolibc.ld
diff --git a/src/samd21/ao_adc_samd21.c b/src/samd21/ao_adc_samd21.c
new file mode 100644 (file)
index 0000000..b2e6aa7
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_adc-samd21.h>
+
+static void
+ao_adc_sync(void)
+{
+       while (samd21_adc.status & (1 << SAMD21_ADC_STATUS_SYNCBUSY))
+               ;
+}
+
+static uint16_t
+ao_adc_do_conversion(void)
+{
+       ao_adc_sync();
+       samd21_adc.swtrig = (1 << SAMD21_ADC_SWTRIG_START);
+       ao_adc_sync();
+       while ((samd21_adc.intflag & (1 << SAMD21_ADC_INTFLAG_RESRDY)) == 0)
+               ao_adc_sync();
+       ao_adc_sync();
+       return samd21_adc.result;
+}
+
+uint16_t
+ao_adc_read(uint8_t channel)
+{
+       ao_adc_sync();
+       samd21_adc.inputctrl = ((channel << SAMD21_ADC_INPUTCTRL_MUXPOS) |
+                               (SAMD21_ADC_INPUTCTRL_MUXNEG_GND << SAMD21_ADC_INPUTCTRL_MUXNEG) |
+                               (0 << SAMD21_ADC_INPUTCTRL_INPUTSCAN) |
+                               (0 << SAMD21_ADC_INPUTCTRL_INPUTOFFSET) |
+                               (SAMD21_ADC_INPUTCTRL_GAIN_DIV2 << SAMD21_ADC_INPUTCTRL_GAIN));
+
+       /* Read twice and discard the first value as recommended by app note
+        * http://www.atmel.com/images/Atmel-42645-ADC-Configurations-with-Examples_ApplicationNote_AT11481.pdf
+        */
+       (void) ao_adc_do_conversion();
+       return ao_adc_do_conversion();
+}
+
+void
+ao_adc_init(void)
+{
+       /* supply a clock */
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_ADC);
+
+       /* enable the device */
+       samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_ADC);
+
+       /* Reset */
+       samd21_adc.ctrla = (1 << SAMD21_ADC_CTRLA_SWRST);
+
+       ao_adc_sync();
+
+       while ((samd21_adc.ctrla & (1 << SAMD21_ADC_CTRLA_SWRST)) != 0 ||
+              (samd21_adc.status & (1 << SAMD21_ADC_STATUS_SYNCBUSY)) != 0)
+               ao_adc_sync();
+
+       /* Load ADC calibration values */
+       uint32_t b = (samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_ADC_BIASCAL) & SAMD21_AUX1_CALIBRATION_ADC_BIASCAL_MASK;
+       uint32_t l = (samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_ADC_LINEARITY) & SAMD21_AUX1_CALIBRATION_ADC_LINEARITY_MASK;
+
+       samd21_adc.calib = ((b << SAMD21_ADC_CALIB_BIAS_CAL) |
+                           (l << SAMD21_ADC_CALIB_LINEARITY_CAL));
+
+
+       ao_adc_sync();
+
+       samd21_adc.ctrlb = ((0 << SAMD21_ADC_CTRLB_DIFFMODE) |
+                           (0 << SAMD21_ADC_CTRLB_LEFTADJ) |
+                           (0 << SAMD21_ADC_CTRLB_FREERUN) |
+                           (0 << SAMD21_ADC_CTRLB_CORREN) |
+                           (SAMD21_ADC_CTRLB_RESSEL_12BIT << SAMD21_ADC_CTRLB_RESSEL) |
+                           (SAMD21_ADC_CTRLB_PRESCALER_DIV512 << SAMD21_ADC_CTRLB_PRESCALER));
+
+       ao_adc_sync();
+
+       samd21_adc.sampctrl = 0x1f;
+
+       ao_adc_sync();
+
+       samd21_adc.refctrl = (SAMD21_ADC_REFCTRL_REFSEL_INTVCC1 << SAMD21_ADC_REFCTRL_REFSEL);
+
+       ao_adc_sync();
+
+       samd21_adc.ctrla = (1 << SAMD21_ADC_CTRLA_ENABLE);
+}
diff --git a/src/samd21/ao_adc_samd21.h b/src/samd21/ao_adc_samd21.h
new file mode 100644 (file)
index 0000000..dd562a0
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_ADC_SAMD21_H_
+#define _AO_ADC_SAMD21_H_
+
+uint16_t
+ao_adc_read(uint8_t channel);
+
+void
+ao_adc_init(void);
+
+#endif /* _AO_ADC_SAMD21_H_ */
diff --git a/src/samd21/ao_apa102.c b/src/samd21/ao_apa102.c
new file mode 100644 (file)
index 0000000..e391f96
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <ao.h>
+#include <ao_snek.h>
+
+#define bit(v) do {                            \
+               port_c->outtgl = (1 << pin_c);  \
+               ao_arch_nop(); \
+               ao_gpio_set(port_d, pin_d, v);  \
+               ao_arch_nop(); \
+               port_c->outtgl = (1 << pin_c);  \
+               ao_arch_nop(); \
+       } while (0)
+
+#define byte(v)        do {                            \
+               uint8_t _bit_ = 0x80;           \
+               while (_bit_) {                 \
+                       bit(!!((v) & _bit_));   \
+                       _bit_ >>= 1;            \
+               }                               \
+       } while(0)
+
+#define repeat(v,c) do {                       \
+               uint8_t _i_;                    \
+               for (_i_ = 0; _i_ < (c); _i_++) \
+                       bit(v);                 \
+       } while (0)
+
+void
+ao_snek_apa102_write(void *gpio_d, uint8_t pin_d,
+                    void *gpio_c, uint8_t pin_c,
+                    int npixel,
+                    struct snek_neopixel *pixels)
+{
+       struct samd21_port *port_d = gpio_d;
+       struct samd21_port *port_c = gpio_c;
+
+       ao_gpio_set(port_c, pin_c, 1);
+       int i;
+       for (i = 0; i < 32; i++)
+               ao_arch_nop();
+       repeat(0, 32);
+       while (npixel--) {
+               byte(0xff);
+               byte(pixels->b);
+               byte(pixels->g);
+               byte(pixels->r);
+       }
+       repeat(1, 32);
+}
diff --git a/src/samd21/ao_arch.h b/src/samd21/ao_arch.h
new file mode 100644 (file)
index 0000000..af2ca3e
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_ARCH_H_
+#define _AO_ARCH_H_
+
+#include <stdio.h>
+#include <samd21.h>
+
+/*
+ * Samd21 definitions and code fragments for AltOS
+ */
+
+#define AO_PORT_TYPE   uint32_t
+
+#define AO_LED_TYPE    AO_PORT_TYPE
+
+#define ao_arch_naked_declare  __attribute__((naked))
+#define ao_arch_naked_define
+
+#define ao_arch_reboot() \
+       (samd21_scb.aircr = ((SAMD21_SCB_AIRCR_VECTKEY_KEY << SAMD21_SCB_AIRCR_VECTKEY) | \
+                         (1 << SAMD21_SCB_AIRCR_SYSRESETREQ)))
+
+#define ao_arch_nop()          asm("nop")
+#define ao_arch_interrupt(n)   /* nothing */
+#define ao_arch_block_interrupts()     asm("cpsid i")
+#define ao_arch_release_interrupts()   asm("cpsie i")
+
+/* ao_romconfig.c */
+#define AO_ROMCONFIG_SYMBOL __attribute__((section(".init.1"))) const
+#define AO_USBCONFIG_SYMBOL __attribute__((section(".init.2"))) const
+
+/*
+ * ao_timer.c
+ *
+ * For the samd21, we want to use the DFLL48 clock
+ */
+
+/* GCLK 0 is always the system clock source */
+
+#define AO_GCLK_SYSCLK 0
+
+/* If there's a 32kHz xtal, use that for the 32kHz oscillator */
+#ifdef AO_XOSC32K
+# ifndef AO_GCLK_XOSC32K
+#  define AO_GCLK_XOSC32K      1
+# endif
+#endif
+
+/* If there's a high-freq xtal, use that */
+#ifdef AO_XOSC
+# ifndef AO_GCLK_XOSC
+#  define AO_GCLK_XOSC 1
+# endif
+
+# ifndef AO_XOSC_GCLK_DIV
+#  define AO_XOSC_GCLK_DIV 1
+# endif
+
+# define AO_FDPLL96M   ((AO_XOSC_FREQ / AO_XOSC_DIV * AO_XOSC_MUL) / AO_XOSC_GCLK_DIV)
+
+/* By default, use the xosc for the system, but allow the dfll48m to
+ * drive USB if desired.
+ */
+
+# ifndef AO_GCLK_FDPLL96M
+#  define AO_SYSCLK            AO_FDPLL96M
+#  define AO_GCLK_FDPLL96M     AO_GCLK_SYSCLK
+#  define AO_GCLK_DFLL48M      2
+# endif
+
+#endif /* AO_XOSC */
+
+#if AO_DFLL48M
+# ifndef AO_GCLK_DFLL48M
+#  define AO_SYSCLK    AO_DFLL48M
+#  define AO_GCLK_DFLL48M AO_GCLK_SYSCLK
+# endif
+#endif
+
+#ifndef AO_GCLK_USB
+# if AO_DFLL48M
+#  define AO_GCLK_USB AO_GCLK_DFLL48M
+# else
+#  define AO_GCLK_USB AO_GCLK_SYSCLK
+# endif
+#endif
+
+/*
+ * ao_timer.c
+ *
+ * For the samd21, we want to use the DFLL48 clock
+ */
+
+/* GCLK 0 is always the system clock source */
+
+#define AO_GCLK_SYSCLK 0
+
+/* If there's a 32kHz xtal, use that for the 32kHz oscillator */
+#ifdef AO_XOSC32K
+# ifndef AO_GCLK_XOSC32K
+#  define AO_GCLK_XOSC32K      1
+# endif
+#endif
+
+/* If there's a high-freq xtal, use that */
+#ifdef AO_XOSC
+# ifndef AO_GCLK_XOSC
+#  define AO_GCLK_XOSC 1
+# endif
+
+# ifndef AO_XOSC_GCLK_DIV
+#  define AO_XOSC_GCLK_DIV 1
+# endif
+
+# define AO_FDPLL96M   ((AO_XOSC_FREQ / AO_XOSC_DIV * AO_XOSC_MUL) / AO_XOSC_GCLK_DIV)
+
+/* By default, use the xosc for the system, but allow the dfll48m to
+ * drive USB if desired.
+ */
+
+# ifndef AO_GCLK_FDPLL96M
+#  define AO_SYSCLK            AO_FDPLL96M
+#  define AO_GCLK_FDPLL96M     AO_GCLK_SYSCLK
+#  define AO_GCLK_DFLL48M      2
+# endif
+
+#endif /* AO_XOSC */
+
+#if AO_DFLL48M
+# ifndef AO_GCLK_DFLL48M
+#  define AO_SYSCLK    AO_DFLL48M
+#  define AO_GCLK_DFLL48M AO_GCLK_SYSCLK
+# endif
+#endif
+
+#ifndef AO_GCLK_USB
+# if AO_DFLL48M
+#  define AO_GCLK_USB AO_GCLK_DFLL48M
+# else
+#  define AO_GCLK_USB AO_GCLK_SYSCLK
+# endif
+#endif
+
+#define AO_HCLK                (AO_SYSCLK / AO_AHB_PRESCALER)
+
+#define AO_HCLK                (AO_SYSCLK / AO_AHB_PRESCALER)
+#define AO_PCLK                (AO_HCLK / AO_APB_PRESCALER)
+#define AO_SYSTICK     (AO_HCLK)
+#define AO_PANIC_DELAY_SCALE  (AO_SYSCLK / 12000000)
+
+#define AO_SAMD21_NVIC_HIGH_PRIORITY   (0 << 6)
+#define AO_SAMD21_NVIC_CLOCK_PRIORITY  (1 << 6)
+#define AO_SAMD21_NVIC_MED_PRIORITY    (2 << 6)
+#define AO_SAMD21_NVIC_LOW_PRIORITY    (3 << 6)
+
+/* ADC maximum reported value */
+#define AO_ADC_MAX                     4095
+
+#define AO_SAMD21_NVIC_HIGH_PRIORITY   (0 << 6)
+#define AO_SAMD21_NVIC_CLOCK_PRIORITY  (1 << 6)
+#define AO_SAMD21_NVIC_MED_PRIORITY    (2 << 6)
+#define AO_SAMD21_NVIC_LOW_PRIORITY    (3 << 6)
+
+/* ADC maximum reported value */
+#define AO_ADC_MAX                     4095
+
+/* This has to be 65536 so that TCC and TC match; TC isn't configurable */
+#define AO_TCC_PERIOD          65536
+#define SNEK_PWM_MAX           (AO_TCC_PERIOD-1)
+
+#define AO_TICK_TYPE           uint32_t
+#define AO_TICK_SIGNED         int32_t
+
+bool
+ao_usb_waiting(void);
+
+#define AO_CMD_LEN             128
+#define AO_STACK_SIZE          2048
+
+#ifndef HAS_BOOT_LOADER
+#define HAS_BOOT_LOADER                        1
+#endif
+
+#if HAS_BOOT_LOADER
+#define AO_BOOT_APPLICATION_BASE       ((uint32_t *) 0x00001000)
+#ifndef AO_BOOT_APPLICATION_BOUND
+#define AO_BOOT_APPLICATION_BOUND      ((uint32_t *) (0x00000000 + samd21_flash_size()))
+#endif
+#define AO_BOOT_LOADER_BASE            ((uint32_t *) 0x00000000)
+#endif
+
+#endif /* _AO_ARCH_H_ */
diff --git a/src/samd21/ao_arch_funcs.h b/src/samd21/ao_arch_funcs.h
new file mode 100644 (file)
index 0000000..8ef93aa
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_ARCH_FUNCS_H_
+#define _AO_ARCH_FUNCS_H_
+
+#define AO_MODE_PULL_NONE      0
+#define AO_MODE_PULL_UP                1
+#define AO_MODE_PULL_DOWN      2
+
+static inline void ao_enable_port(struct samd21_port *port)
+{
+       (void) port;
+       samd21_pm.apbbmask |= (1UL << SAMD21_PM_APBBMASK_PORT);
+}
+
+static inline void ao_disable_port(struct samd21_port *port)
+{
+       (void) port;
+       samd21_pm.apbbmask &= ~(1UL << SAMD21_PM_APBBMASK_PORT);
+}
+
+static inline void
+ao_gpio_set(struct samd21_port *port, uint8_t bit, uint8_t v)
+{
+       if (v)
+               port->outset = (1 << bit);
+       else
+               port->outclr = (1 << bit);
+}
+
+static inline uint8_t
+ao_gpio_get(struct samd21_port *port, uint8_t bit)
+{
+       return (port->in >> bit) & 1;
+}
+
+static inline void
+ao_gpio_dir_set(struct samd21_port *port, uint8_t bit, bool output)
+{
+       if (output)
+               port->dirset = (1 << bit);
+       else
+               port->dirclr = (1 << bit);
+}
+
+static inline void
+ao_gpio_set_mode(struct samd21_port *port, uint8_t bit, uint32_t mode)
+{
+       uint8_t pincfg = 0;
+
+       if (mode != AO_MODE_PULL_NONE) {
+               pincfg |= (1 << SAMD21_PORT_PINCFG_PULLEN);
+               ao_gpio_set(port, bit, mode == AO_MODE_PULL_UP);
+       }
+
+       samd21_port_pincfg_set(port, bit,
+                              (0 << SAMD21_PORT_PINCFG_DRVSTR) |
+                              (1 << SAMD21_PORT_PINCFG_PULLEN) |
+                              (0 << SAMD21_PORT_PINCFG_INEN) |
+                              (0 << SAMD21_PORT_PINCFG_PMUXEN),
+                              pincfg);
+}
+
+static inline void
+ao_enable_output(struct samd21_port *port, uint8_t pin, uint8_t v)
+{
+       ao_enable_port(port);
+       ao_gpio_set(port, pin, v);
+       samd21_port_dir_set(port, pin, SAMD21_PORT_DIR_OUT);
+       samd21_port_pincfg_set(port, pin,
+                              (1 << SAMD21_PORT_PINCFG_DRVSTR) |
+                              (1 << SAMD21_PORT_PINCFG_PULLEN) |
+                              (1 << SAMD21_PORT_PINCFG_INEN),
+                              (0 << SAMD21_PORT_PINCFG_DRVSTR) |
+                              (0 << SAMD21_PORT_PINCFG_PULLEN) |
+                              (0 << SAMD21_PORT_PINCFG_INEN));
+}
+
+static inline void
+ao_enable_input(struct samd21_port *port, uint8_t pin, uint32_t mode)
+{
+       ao_enable_port(port);
+       samd21_port_dir_set(port, pin, SAMD21_PORT_DIR_IN);
+       uint8_t pincfg;
+
+       pincfg = ((0 << SAMD21_PORT_PINCFG_DRVSTR) |
+                 (0 << SAMD21_PORT_PINCFG_PULLEN) |
+                 (1 << SAMD21_PORT_PINCFG_INEN) |
+                 (0 << SAMD21_PORT_PINCFG_PMUXEN));
+
+       if (mode != AO_MODE_PULL_NONE) {
+               pincfg |= (1 << SAMD21_PORT_PINCFG_PULLEN);
+               ao_gpio_set(port, pin, mode == AO_MODE_PULL_UP);
+       }
+
+       samd21_port_pincfg_set(port, pin,
+                              (1 << SAMD21_PORT_PINCFG_DRVSTR) |
+                              (1 << SAMD21_PORT_PINCFG_PULLEN) |
+                              (1 << SAMD21_PORT_PINCFG_INEN) |
+                              (1 << SAMD21_PORT_PINCFG_PMUXEN),
+                              pincfg);
+}
+
+static inline void
+ao_enable_cs(struct samd21_port *port, uint8_t pin)
+{
+       ao_enable_output(port, pin, 1);
+}
+
+#define ARM_PUSH32(stack, val) (*(--(stack)) = (val))
+
+typedef uint32_t       ao_arch_irq_t;
+
+static inline uint32_t
+ao_arch_irqsave(void) {
+       uint32_t        primask;
+       asm("mrs %0,primask" : "=&r" (primask));
+       ao_arch_block_interrupts();
+       return primask;
+}
+
+static inline void
+ao_arch_irqrestore(uint32_t primask) {
+       asm("msr primask,%0" : : "r" (primask));
+}
+
+static inline void
+ao_arch_memory_barrier(void) {
+       asm volatile("" ::: "memory");
+}
+
+#if HAS_TASK
+static inline void
+ao_arch_init_stack(struct ao_task *task, uint32_t *sp, void *start)
+{
+       uint32_t        a = (uint32_t) start;
+       int             i;
+
+       /* Return address (goes into LR) */
+       ARM_PUSH32(sp, a);
+
+       /* Clear register values r0-r7 */
+       i = 8;
+       while (i--)
+               ARM_PUSH32(sp, 0);
+
+       /* APSR */
+       ARM_PUSH32(sp, 0);
+
+       /* PRIMASK with interrupts enabled */
+       ARM_PUSH32(sp, 0);
+
+       task->sp32 = sp;
+}
+
+static inline void ao_arch_save_regs(void) {
+       /* Save general registers */
+       asm("push {r0-r7,lr}\n");
+
+       /* Save APSR */
+       asm("mrs r0,apsr");
+       asm("push {r0}");
+
+       /* Save PRIMASK */
+       asm("mrs r0,primask");
+       asm("push {r0}");
+}
+
+static inline void ao_arch_save_stack(void) {
+       uint32_t        *sp;
+       asm("mov %0,sp" : "=&r" (sp) );
+       ao_cur_task->sp32 = (sp);
+       if (sp < &ao_cur_task->stack32[0])
+               ao_panic (AO_PANIC_STACK);
+}
+
+static inline void ao_arch_restore_stack(void) {
+       /* Switch stacks */
+       asm("mov sp, %0" : : "r" (ao_cur_task->sp32) );
+
+       /* Restore PRIMASK */
+       asm("pop {r0}");
+       asm("msr primask,r0");
+
+       /* Restore APSR */
+       asm("pop {r0}");
+       asm("msr apsr_nczvq,r0");
+
+       /* Restore general registers */
+       asm("pop {r0-r7,pc}\n");
+}
+
+#ifndef HAS_SAMPLE_PROFILE
+#define HAS_SAMPLE_PROFILE 0
+#endif
+
+#if !HAS_SAMPLE_PROFILE
+#define HAS_ARCH_START_SCHEDULER       1
+
+static inline void ao_arch_start_scheduler(void) {
+       uint32_t        sp;
+       uint32_t        control;
+
+       asm("mrs %0,msp" : "=&r" (sp));
+       asm("msr psp,%0" : : "r" (sp));
+       asm("mrs %0,control" : "=&r" (control));
+       control |= (1 << 1);
+       asm("msr control,%0" : : "r" (control));
+       asm("isb");
+}
+#endif
+
+#define ao_arch_isr_stack()
+
+#endif
+
+#define ao_arch_wait_interrupt() do {                          \
+               asm("\twfi\n");                                 \
+               ao_arch_release_interrupts();                   \
+               asm(".global ao_idle_loc\nao_idle_loc:");       \
+               ao_arch_block_interrupts();                     \
+       } while (0)
+
+#define ao_arch_critical(b) do {                       \
+               uint32_t __mask = ao_arch_irqsave();    \
+               do { b } while (0);                     \
+               ao_arch_irqrestore(__mask);             \
+       } while (0)
+
+/* ao_serial_samd21.c */
+
+#if USE_SERIAL_0_FLOW && USE_SERIAL_0_SW_FLOW || USE_SERIAL_1_FLOW && USE_SERIAL_1_SW_FLOW
+#define HAS_SERIAL_SW_FLOW     1
+#else
+#define HAS_SERIAL_SW_FLOW     0
+#endif
+
+#if USE_SERIAL_1_FLOW && !USE_SERIAL_1_SW_FLOW
+#define USE_SERIAL_1_HW_FLOW   1
+#endif
+
+#if USE_SERIAL_0_FLOW && !USE_SERIAL_0_SW_FLOW
+#define USE_SERIAL_0_HW_FLOW   1
+#endif
+
+#if USE_SERIAL_0_HW_FLOW || USE_SERIAL_1_HW_FLOW
+#define HAS_SERIAL_HW_FLOW     1
+#else
+#define HAS_SERIAL_HW_FLOW     0
+#endif
+
+struct ao_samd21_usart {
+       struct ao_fifo          rx_fifo;
+       struct ao_fifo          tx_fifo;
+       struct samd21_sercom    *reg;
+       uint8_t                 tx_running;
+       uint8_t                 draining;
+#if HAS_SERIAL_SW_FLOW
+       /* RTS - 0 if we have FIFO space, 1 if not
+        * CTS - 0 if we can send, 0 if not
+        */
+       struct samd21_port      *gpio_rts;
+       struct samd21_port      *gpio_cts;
+       uint8_t                 pin_rts;
+       uint8_t                 pin_cts;
+       uint8_t                 rts;
+#endif
+};
+
+#if HAS_USART_0
+extern struct ao_samd21_usart  ao_samd21_usart0;
+#endif
+
+void
+ao_serial_init(void);
+
+/* ao_usb_samd21.c */
+
+#if AO_USB_OUT_HOOK
+void
+ao_usb_out_hook(uint8_t *buffer, uint16_t count);
+#endif
+
+void start(void);
+
+#endif /* _AO_ARCH_FUNCS_H_ */
diff --git a/src/samd21/ao_boot_chain.c b/src/samd21/ao_boot_chain.c
new file mode 100644 (file)
index 0000000..551af78
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_boot.h>
+
+void
+ao_boot_chain(uint32_t *base)
+{
+       uint32_t        sp;
+       uint32_t        pc;
+
+       sp = base[0];
+       pc = base[1];
+       if (0x00000100 <= pc && pc <= 0x00200000 && (pc & 1) == 1) {
+               asm ("mov sp, %0" : : "r" (sp));
+               asm ("mov lr, %0" : : "r" (pc));
+               asm ("bx lr");
+       }
+}
+
+#define AO_BOOT_SIGNAL 0x5a5aa5a5
+#define AO_BOOT_CHECK  0xc3c33c3c
+
+struct ao_boot {
+       uint32_t        *base;
+       uint32_t        signal;
+       uint32_t        check;
+};
+
+struct ao_boot ao_boot __attribute__((section(".preserve.2")));
+
+int
+ao_boot_check_chain(void)
+{
+       if (ao_boot.signal == AO_BOOT_SIGNAL && ao_boot.check == AO_BOOT_CHECK) {
+               ao_boot.signal = 0;
+               ao_boot.check = 0;
+               if (ao_boot.base == AO_BOOT_FORCE_LOADER)
+                       return 0;
+               ao_boot_chain(ao_boot.base);
+       }
+       return 1;
+}
+
+void
+ao_boot_reboot(uint32_t *base)
+{
+       ao_boot.base = base;
+       ao_boot.signal = AO_BOOT_SIGNAL;
+       ao_boot.check = AO_BOOT_CHECK;
+       ao_arch_reboot();
+}
diff --git a/src/samd21/ao_boot_pin.c b/src/samd21/ao_boot_pin.c
new file mode 100644 (file)
index 0000000..baabe81
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_boot.h>
+#include <ao_exti.h>
+
+int
+ao_boot_check_pin(void)
+{
+       uint16_t v;
+
+       /* Enable the input pin */
+       ao_enable_input(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN,
+                       AO_BOOT_APPLICATION_MODE);
+
+       for (v = 0; v < 100; v++)
+               ao_arch_nop();
+
+       /* Read the value */
+       v = ao_gpio_get(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN);
+
+       /* Reset the chip to turn off the port and the power interface clock */
+       ao_gpio_set_mode(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN, 0);
+       ao_disable_port(&AO_BOOT_APPLICATION_GPIO);
+       return v == AO_BOOT_APPLICATION_VALUE;
+}
diff --git a/src/samd21/ao_dac_samd21.c b/src/samd21/ao_dac_samd21.c
new file mode 100644 (file)
index 0000000..7dc00c1
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright © 2020 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <snek.h>
+#include <ao.h>
+#include <ao_dac-samd21.h>
+#include <ao_tcc-samd21.h>
+
+/* Max DAC output value. We're using left-adjusted values */
+#define SNEK_DAC_MAX           65535
+
+#ifdef SNEK_SAMD21_DAC_TIMER
+/*
+ * If there's a timer available, we can use that
+ * to implement the 'tone' function
+ */
+
+#include "sine.h"
+
+#define NSINE  (sizeof(sine) / sizeof(sine[0]))
+
+static uint16_t current_power;
+static uint16_t power;
+static uint32_t        phase;
+static uint32_t phase_step;
+static volatile bool dac_running;
+
+#define _paste2(x,y)   x ## y
+#define _paste3(x,y,z) x ## y ## z
+#define paste2(x,y)    _paste2(x,y)
+#define paste3(x,y,z)  _paste3(x,y,z)
+#define SAMD21_TCC     paste2(samd21_tcc, SNEK_SAMD21_DAC_TIMER)
+#define SAMD21_TCC_ISR paste3(samd21_tcc, SNEK_SAMD21_DAC_TIMER, _isr)
+
+#define AO_DAC_RATE    24000
+
+#define UINT_TO_FIXED(u)       ((uint32_t) (u) << 16)
+#define FIXED_TO_UINT(u)       ((u) >> 16)
+
+void
+SAMD21_TCC_ISR(void)
+{
+       uint32_t intflag = SAMD21_TCC.intflag;
+       SAMD21_TCC.intflag = intflag;
+       if (intflag & (1 << SAMD21_TCC_INTFLAG_OVF)) {
+               if (phase_step) {
+                       samd21_dac.data = ((uint32_t) sine[FIXED_TO_UINT(phase)] * current_power) >> 16;
+                       if ((phase += phase_step) >= UINT_TO_FIXED(NSINE)) {
+                               phase -= UINT_TO_FIXED(NSINE);
+
+                               current_power = power;
+
+                               /* Stop output at zero crossing when no longer outputing tone */
+                               if (!dac_running) {
+                                       phase_step = 0;
+                                       phase = 0;
+                                       SAMD21_TCC.intenclr = (1 << SAMD21_TCC_INTFLAG_OVF);
+                               }
+                       }
+               }
+       }
+}
+
+void
+ao_dac_set_hz(float hz)
+{
+       /* samples/second = AC_DAC_RATE
+        *
+        * cycles/second = hz
+        *
+        * samples/cycle = AC_DAC_RATE / hz
+        *
+        * step/cycle = 256
+        *
+        * step/sample = step/cycle * cycle/samples
+        *             = TWO_PI * hz / AC_DAC_RATE;
+        */
+       uint32_t new_phase_step = (float) UINT_TO_FIXED(NSINE) * hz / (float) AO_DAC_RATE;
+       ao_arch_critical(
+               if (new_phase_step) {
+                       dac_running = true;
+                       phase_step = new_phase_step;
+                       SAMD21_TCC.intenset = (1 << SAMD21_TCC_INTFLAG_OVF);
+               } else {
+                       dac_running = false;
+               });
+}
+
+static void
+ao_dac_timer_init(void)
+{
+       /* Adjust timer to interrupt once per sample period */
+       SAMD21_TCC.per = AO_HCLK / AO_DAC_RATE;
+
+       /* Enable timer interrupts */
+       samd21_nvic_set_enable(paste3(SAMD21_NVIC_ISR_TCC, SNEK_SAMD21_DAC_TIMER, _POS));
+       samd21_nvic_set_priority(paste3(SAMD21_NVIC_ISR_TCC, SNEK_SAMD21_DAC_TIMER, _POS), 3);
+}
+#else
+#define ao_dac_timer_init()
+#endif
+
+static void
+ao_dac_sync(void)
+{
+       while (samd21_dac.status & (1 << SAMD21_DAC_STATUS_SYNCBUSY))
+               ;
+}
+
+void
+ao_dac_set(uint16_t new_power)
+{
+#if SNEK_DAC_MAX != SNEK_PWM_MAX
+       new_power = (uint16_t) ((uint32_t) new_power * SNEK_DAC_MAX) / SNEK_PWM_MAX;
+#endif
+
+       ao_arch_critical(
+#ifdef SNEK_SAMD21_DAC_TIMER
+               power = new_power;
+               /*
+                * When not generating a tone, just set the DAC
+                * output to the requested level
+                */
+               if (!phase_step) {
+                       current_power = new_power;
+                       samd21_dac.data = new_power;
+               }
+#else
+               samd21_dac.data = new_power;
+#endif
+               );
+}
+
+void
+ao_dac_init(void)
+{
+       /* supply a clock */
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_DAC);
+
+       /* enable the device */
+       samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_DAC);
+
+       /* reset */
+       samd21_dac.ctrla = (1 << SAMD21_DAC_CTRLA_SWRST);
+
+       while ((samd21_dac.ctrla & (1 << SAMD21_DAC_CTRLA_SWRST)) != 0 ||
+              (samd21_dac.status & (1 << SAMD21_DAC_STATUS_SYNCBUSY)) != 0)
+               ao_arch_nop();
+
+       /* Configure using VDD as reference */
+       samd21_dac.ctrlb = ((1 << SAMD21_DAC_CTRLB_EOEN) |
+                           (0 << SAMD21_DAC_CTRLB_IOEN) |
+                           (1 << SAMD21_DAC_CTRLB_LEFTADJ) |
+                           (0 << SAMD21_DAC_CTRLB_VPD) |
+                           (1 << SAMD21_DAC_CTRLB_BDWP) |
+                           (SAMD21_DAC_CTRLB_REFSEL_VDDANA << SAMD21_DAC_CTRLB_REFSEL));
+
+       ao_dac_sync();
+
+       samd21_dac.ctrla = (1 << SAMD21_DAC_CTRLA_ENABLE);
+
+       ao_dac_sync();
+
+       ao_dac_timer_init();
+}
diff --git a/src/samd21/ao_dac_samd21.h b/src/samd21/ao_dac_samd21.h
new file mode 100644 (file)
index 0000000..7939f4b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright © 2020 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _AO_DAC_SAMD21_H_
+#define _AO_DAC_SAMD21_H_
+
+void
+ao_dac_set(uint16_t value);
+
+#ifdef SNEK_SAMD21_DAC_TIMER
+void
+ao_dac_set_hz(float hz);
+#endif
+
+void
+ao_dac_init(void);
+
+#endif /* _AO_DAC_SAMD21_H_ */
diff --git a/src/samd21/ao_dma_samd21.c b/src/samd21/ao_dma_samd21.c
new file mode 100644 (file)
index 0000000..60bf34f
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <ao.h>
+#include <ao_dma-samd21.h>
+
+static struct samd21_dmac_desc samd21_dmac_desc[SAMD21_DMAC_NCHAN]  __attribute__((aligned(16)));
+static struct samd21_dmac_desc samd21_dmac_wrb[SAMD21_DMAC_NCHAN]  __attribute__((aligned(16)));
+
+static volatile uint16_t       saved_intpend;
+static volatile int            interrupts;
+
+static struct {
+       void (*callback)(uint8_t id, void *closure);
+       void *closure;
+} dmac_callback[SAMD21_DMAC_NCHAN];
+
+void
+samd21_dmac_isr(void)
+{
+       uint16_t        intpend = samd21_dmac.intpend;
+
+       ++interrupts;
+       saved_intpend = intpend;
+       if (intpend & 0xff00) {
+               uint8_t id = (intpend >> SAMD21_DMAC_INTPEND_ID) & SAMD21_DMAC_INTPEND_ID_MASK;
+               samd21_dmac.intpend = intpend;
+               if (intpend & (1 << SAMD21_DMAC_INTPEND_TCMPL)) {
+                       if (dmac_callback[id].callback)
+                               (*dmac_callback[id].callback)(id, dmac_callback[id].closure);
+               }
+       }
+}
+
+void
+ao_dma_dump(char *where)
+{
+       printf("DMA %s ctrl %04x intpend %04x intstatus %04x\n",
+              where,
+              samd21_dmac.ctrl,
+              samd21_dmac.intpend,
+              samd21_dmac.intstatus);
+       fflush(stdout);
+       printf(" busych %04x pendch %04x active %08x chctrla %02x\n",
+              samd21_dmac.busych,
+              samd21_dmac.pendch,
+              samd21_dmac.active,
+              samd21_dmac.chctrla);
+       fflush(stdout);
+       printf(" chctrlb %08x chintflag %02x chstatus %02x\n",
+              samd21_dmac.chctrlb,
+              samd21_dmac.chintflag,
+              samd21_dmac.chstatus);
+       fflush(stdout);
+       printf(" btctrl %04x btcnt %04x srcaddr %08x dstaddr %08x descaddr %08x\n",
+              samd21_dmac_desc[0].btctrl,
+              samd21_dmac_desc[0].btcnt,
+              samd21_dmac_desc[0].srcaddr,
+              samd21_dmac_desc[0].dstaddr,
+              samd21_dmac_desc[0].descaddr);
+       fflush(stdout);
+       printf("intpend %04x interrupts %d\n", saved_intpend, interrupts);
+}
+
+void
+_ao_dma_start_transfer(uint8_t         id,
+                      void             *src,
+                      void             *dst,
+                      uint16_t         count,
+                      uint32_t         chctrlb,
+                      uint16_t         btctrl,
+                      void             (*callback)(uint8_t id, void *closure),
+                      void             *closure)
+{
+       /* Set up the callback */
+       dmac_callback[id].closure = closure;
+       dmac_callback[id].callback = callback;
+
+       /* Set up the descriptor */
+       samd21_dmac_desc[id].btctrl = btctrl;
+       samd21_dmac_desc[id].btcnt = count;
+       samd21_dmac_desc[id].srcaddr = (uint32_t) src;
+       samd21_dmac_desc[id].dstaddr = (uint32_t) dst;
+       samd21_dmac_desc[id].descaddr = 0;
+
+       /* Configure the channel and enable it */
+       samd21_dmac.chid = id;
+       samd21_dmac.chctrlb = chctrlb;
+       samd21_dmac.chctrla = (1 << SAMD21_DMAC_CHCTRLA_ENABLE);
+}
+
+void
+_ao_dma_done_transfer(uint8_t id)
+{
+       /* Disable channel */
+       samd21_dmac.chid = id;
+       samd21_dmac.chctrla = 0;
+}
+
+void
+ao_dma_init(void)
+{
+       uint8_t ch;
+
+       /* Enable DMAC clocks */
+       samd21_pm.ahbmask |= (1 << SAMD21_PM_AHBMASK_DMAC);
+       samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_DMAC);
+
+#if 0
+       /* Enable HPB clocks so we can talk to peripherals */
+       samd21_pm.ahbmask |= ((1 << SAMD21_PM_AHBMASK_HPB0) |
+                             (1 << SAMD21_PM_AHBMASK_HPB1) |
+                             (1 << SAMD21_PM_AHBMASK_HPB2));
+#endif
+
+       samd21_pm.apbamask |= (1 << SAMD21_PM_APBAMASK_PAC0);
+       samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_PAC1);
+       samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_PAC2);
+
+       /* Reset DMAC device */
+       samd21_dmac.ctrl = 0;
+       samd21_dmac.ctrl = (1 << SAMD21_DMAC_CTRL_SWRST);
+       while (samd21_dmac.ctrl & (1 << SAMD21_DMAC_CTRL_SWRST))
+               ;
+
+       samd21_dmac.baseaddr = (uint32_t) &samd21_dmac_desc[0];
+       samd21_dmac.wrbaddr = (uint32_t) &samd21_dmac_wrb[0];
+
+       samd21_dmac.swtrigctrl = 0;
+
+       /* Set QoS to highest value */
+       samd21_dmac.qosctrl = ((SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_DQOS) |
+                              (SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_FQOS) |
+                              (SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_WRBQOS));
+
+       /* Enable DMAC controller with all priority levels */
+       samd21_dmac.ctrl = ((1 << SAMD21_DMAC_CTRL_DMAENABLE) |
+                           (1 << SAMD21_DMAC_CTRL_LVLEN(0)) |
+                           (1 << SAMD21_DMAC_CTRL_LVLEN(1)) |
+                           (1 << SAMD21_DMAC_CTRL_LVLEN(2)) |
+                           (1 << SAMD21_DMAC_CTRL_LVLEN(3)));
+
+       /* Reset all DMAC channels */
+       for (ch = 0; ch < SAMD21_DMAC_NCHAN; ch++) {
+               samd21_dmac.chid = ch;
+               samd21_dmac.chctrla = (1 << SAMD21_DMAC_CHCTRLA_SWRST);
+               while (samd21_dmac.chctrla & (1 << SAMD21_DMAC_CHCTRLA_SWRST))
+                       ;
+               samd21_dmac.chintenset = (1 << SAMD21_DMAC_CHINTFLAG_TCMPL);
+       }
+
+       /* configure interrupts */
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_DMAC_POS);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_DMAC_POS, 3);
+}
diff --git a/src/samd21/ao_dma_samd21.h b/src/samd21/ao_dma_samd21.h
new file mode 100644 (file)
index 0000000..725e621
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _AO_DMA_SAM21_H_
+#define _AO_DMA_SAM21_H_
+
+void
+ao_dma_init(void);
+
+void
+_ao_dma_start_transfer(uint8_t         id,
+                      void             *src,
+                      void             *dst,
+                      uint16_t         count,
+                      uint32_t         chctrlb,
+                      uint16_t         btctrl,
+                      void             (*callback)(uint8_t id, void *closure),
+                      void             *closure);
+
+void
+_ao_dma_done_transfer(uint8_t id);
+
+void
+ao_dma_dump(char *where);
+
+#endif /* _AO_DMA_SAM21_H_ */
diff --git a/src/samd21/ao_exti.h b/src/samd21/ao_exti.h
new file mode 100644 (file)
index 0000000..43cfcb2
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_EXTI_H_
+#define _AO_EXTI_H_
+
+#define AO_EXTI_MODE_RISING    1
+#define AO_EXTI_MODE_FALLING   2
+#define AO_EXTI_MODE_PULL_NONE 0
+#define AO_EXTI_MODE_PULL_UP   4
+#define AO_EXTI_MODE_PULL_DOWN 8
+#define AO_EXTI_PRIORITY_LOW   16
+#define AO_EXTI_PRIORITY_MED   0
+#define AO_EXTI_PRIORITY_HIGH  32
+#define AO_EXTI_PIN_NOCONFIGURE        64
+
+void
+ao_exti_setup(struct samd21_port *gpio, uint8_t pin, uint8_t mode, void (*callback)(void));
+
+void
+ao_exti_set_mode(struct samd21_port *gpio, uint8_t pin, uint8_t mode);
+
+void
+ao_exti_set_callback(struct samd21_port *gpio, uint8_t pin, void (*callback)(void));
+
+void
+ao_exti_enable(struct samd21_port *gpio, uint8_t pin);
+
+void
+ao_exti_disable(struct samd21_port *gpio, uint8_t pin);
+
+void
+ao_exti_init(void);
+
+#endif /* _AO_EXTI_H_ */
diff --git a/src/samd21/ao_flash.h b/src/samd21/ao_flash.h
new file mode 100644 (file)
index 0000000..5922625
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_FLASH_H_
+#define _AO_FLASH_H_
+
+void
+ao_flash_erase_page(uint32_t *page);
+
+void
+ao_flash_page(uint32_t *page, uint32_t *src);
+
+#endif /* _AO_FLASH_H_ */
diff --git a/src/samd21/ao_flash_loader_samd21.c b/src/samd21/ao_flash_loader_samd21.c
new file mode 100644 (file)
index 0000000..8b08ab2
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_exti.h>
+#include <ao_boot.h>
+#include <ao_flash_task.h>
+
+int
+main(void)
+{
+       ao_clock_init();
+
+       ao_usb_init();
+
+#if HAS_TICK
+       ao_timer_init();
+#endif
+
+#ifdef AO_FLASH_LOADER_INIT
+       AO_FLASH_LOADER_INIT;
+#endif
+       ao_flash_task();
+       return 0;
+}
diff --git a/src/samd21/ao_flash_samd21.c b/src/samd21/ao_flash_samd21.c
new file mode 100644 (file)
index 0000000..c1cee3f
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_flash.h>
+#include <stdio.h>
+
+/* Erase rows are four pages */
+static uint32_t
+samd21_nvmctrl_row_size(void)
+{
+       return samd21_nvmctrl_page_size() * 4;
+}
+
+/* size of a lock region. That's just total flash size / 16 */
+static uint32_t
+samd21_nvmctrl_lock_region(void)
+{
+       return samd21_flash_size() >> 4;
+}
+
+/* Find the bit index of an address within the lock word */
+static uint8_t
+ao_flash_lock_region_bit(void *addr)
+{
+       return (uint8_t) (((uintptr_t) addr) / samd21_nvmctrl_lock_region());
+}
+
+static uint8_t
+ao_flash_is_locked(void *addr)
+{
+       return (samd21_nvmctrl.lock >> ao_flash_lock_region_bit(addr)) & 1;
+}
+
+/* Execute a single flash operation, waiting for it to complete. This
+ * bit of code must be in ram
+ */
+static void __attribute__ ((section(".sdata2.flash"), noinline))
+_ao_flash_execute(uint16_t cmd)
+{
+       while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0)
+               ;
+       samd21_nvmctrl.ctrla = ((cmd << SAMD21_NVMCTRL_CTRLA_CMD) |
+                               (SAMD21_NVMCTRL_CTRLA_CMDEX_KEY << SAMD21_NVMCTRL_CTRLA_CMDEX));
+       while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0)
+               ;
+       samd21_nvmctrl.intflag = ((1 << SAMD21_NVMCTRL_INTFLAG_READY) |
+                                 (1 << SAMD21_NVMCTRL_INTFLAG_ERROR));
+}
+
+/* Set the address of the next flash operation */
+static void
+_ao_flash_set_addr(void *addr)
+{
+       while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0)
+               ;
+       samd21_nvmctrl.addr = ((uint32_t) addr) >> 1;
+       while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0)
+               ;
+}
+
+/* Unlock a region of flash */
+static void
+_ao_flash_unlock(void *addr)
+{
+       if (!ao_flash_is_locked(addr))
+               return;
+
+       _ao_flash_set_addr(addr);
+       _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_UR);
+}
+
+/* Erase a row of flash */
+static void
+_ao_flash_erase_row(void *row)
+{
+       _ao_flash_unlock(row);
+       _ao_flash_set_addr(row);
+       _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_ER);
+}
+
+void
+ao_flash_erase_page(uint32_t *page)
+{
+       uint8_t *row = (uint8_t *) page;
+       uint32_t row_size = samd21_nvmctrl_row_size();
+       uint32_t rows = (row_size + 255) / 256;
+
+       ao_arch_block_interrupts();
+
+       if (((uintptr_t) row & (row_size - 1)) == 0) {
+               while (rows--) {
+                       _ao_flash_erase_row(row);
+                       row += row_size;
+               }
+       }
+
+       ao_arch_release_interrupts();
+}
+
+void
+ao_flash_page(uint32_t *page, uint32_t *src)
+{
+       uint32_t        page_size = samd21_nvmctrl_page_size();
+       uint32_t        pages = 256 / page_size;
+       uint32_t        i;
+       uint32_t        per_page = page_size / sizeof(uint32_t);
+
+       ao_arch_block_interrupts();
+
+       while(pages--) {
+               /* Clear write buffer */
+               _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_PBC);
+               _ao_flash_set_addr(page);
+               for (i = 0; i < per_page; i++)
+                       *page++ = *src++;
+               _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_WP);
+       }
+
+       ao_arch_release_interrupts();
+}
+
diff --git a/src/samd21/ao_flash_samd21_pins.h b/src/samd21/ao_flash_samd21_pins.h
new file mode 100644 (file)
index 0000000..2293968
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2022 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_FLASH_SAMD21_PINS_H_
+#define _AO_FLASH_SAMD21_PINS_H_
+
+#include <ao_flash_pins.h>
+
+#define AO_AHB_PRESCALER       1
+#define AO_APBA_PRESCALER      1
+
+#define HAS_USB                1
+
+#endif /* _AO_FLASH_SAMD21_PINS_H_ */
diff --git a/src/samd21/ao_interrupt.c b/src/samd21/ao_interrupt.c
new file mode 100644 (file)
index 0000000..e425520
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_boot.h>
+
+/* Interrupt functions */
+
+void samd21_halt_isr(void)
+{
+       ao_panic(AO_PANIC_CRASH);
+}
+
+void samd21_ignore_isr(void)
+{
+}
+
+uint32_t
+samd21_flash_size(void)
+{
+       uint32_t        nvmp = (samd21_nvmctrl.param >> SAMD21_NVMCTRL_PARAM_NVMP) & SAMD21_NVMCTRL_PARAM_NVMP_MASK;
+       uint32_t        psz = (samd21_nvmctrl.param >> SAMD21_NVMCTRL_PARAM_PSZ) & SAMD21_NVMCTRL_PARAM_PSZ_MASK;
+
+       /* page size is 2**(3 + psz) */
+       return nvmp << (3 + psz);
+}
+
+#define STRINGIFY(x) #x
+
+#define isr(name) \
+       void __attribute__ ((weak)) samd21_ ## name ## _isr(void); \
+       _Pragma(STRINGIFY(weak samd21_ ## name ## _isr = samd21_ignore_isr))
+
+#define isr_halt(name) \
+       void __attribute__ ((weak)) samd21_ ## name ## _isr(void); \
+       _Pragma(STRINGIFY(weak samd21_ ## name ## _isr = samd21_halt_isr))
+
+isr(nmi);
+isr_halt(hardfault);
+isr_halt(memmanage);
+isr_halt(busfault);
+isr_halt(usagefault);
+isr(svc);
+isr(debugmon);
+isr(pendsv);
+isr(systick);
+isr(pm);               /* IRQ0 */
+isr(sysctrl);
+isr(wdt);
+isr(rtc);
+isr(eic);
+isr(nvmctrl);
+isr(dmac);
+isr(usb);
+isr(evsys);
+isr(sercom0);
+isr(sercom1);
+isr(sercom2);
+isr(sercom3);
+isr(sercom4);
+isr(sercom5);
+isr(tcc0);
+isr(tcc1);
+isr(tcc2);
+isr(tc3);
+isr(tc4);
+isr(tc5);
+isr(tc6);
+isr(tc7);
+isr(adc);
+isr(ac);
+isr(dac);
+isr(ptc);
+isr(i2s);
+isr(ac1);
+isr(tcc3);
+
+#undef isr
+#undef isr_halt
+
+#define i(addr,name)   [(addr)/4] = samd21_ ## name ## _isr
+
+extern char __stack[];
+void _start(void) __attribute__((__noreturn__));
+void main(void) __attribute__((__noreturn__));
+
+__attribute__ ((section(".init")))
+void (*const __interrupt_vector[])(void) __attribute((aligned(128))) = {
+       [0] = (void *) __stack,
+       [1] = _start,
+       i(0x08, nmi),
+       i(0x0c, hardfault),
+       i(0x2c, svc),
+       i(0x30, debugmon),
+       i(0x38, pendsv),
+       i(0x3c, systick),
+
+       i(0x40, pm),            /* IRQ0 */
+       i(0x44, sysctrl),
+       i(0x48, wdt),
+       i(0x4c, rtc),
+       i(0x50, eic),
+       i(0x54, nvmctrl),
+       i(0x58, dmac),
+       i(0x5c, usb),
+       i(0x60, evsys),
+       i(0x64, sercom0),
+       i(0x68, sercom1),
+       i(0x6c, sercom2),
+       i(0x70, sercom3),
+       i(0x74, sercom4),
+       i(0x78, sercom5),
+       i(0x7c, tcc0),
+       i(0x80, tcc1),
+       i(0x84, tcc2),
+       i(0x88, tc3),
+       i(0x8c, tc4),
+       i(0x90, tc5),
+       i(0x94, tc6),
+       i(0x98, tc7),
+       i(0x9c, adc),
+       i(0xa0, ac),
+       i(0xa4, dac),
+       i(0xa8, ptc),
+       i(0xac, i2s),
+       i(0xb0, ac1),
+       i(0xb4, tcc3),
+};
+
+extern char __data_source[];
+extern char __data_start[];
+extern char __data_size[];
+extern char __bss_start[];
+extern char __bss_size[];
+
+void _start(void)
+{
+       memcpy(__data_start, __data_source, (uintptr_t) __data_size);
+       memset(__bss_start, '\0', (uintptr_t) __bss_size);
+
+#if AO_BOOT_CHAIN
+       if (ao_boot_check_chain()) {
+#if AO_BOOT_PIN
+               if (ao_boot_check_pin())
+#endif
+               {
+                       ao_boot_chain(AO_BOOT_APPLICATION_BASE);
+               }
+       }
+#endif
+
+       /* Turn on sysctrl */
+       samd21_pm.apbamask |= (1 << SAMD21_PM_APBAMASK_SYSCTRL);
+       /* Set interrupt vector */
+       samd21_scb.vtor = (uint32_t) &__interrupt_vector;
+
+       main();
+}
diff --git a/src/samd21/ao_neopixel.c b/src/samd21/ao_neopixel.c
new file mode 100644 (file)
index 0000000..818910c
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <ao.h>
+#include <ao_snek.h>
+
+void
+ao_snek_neopixel_write(void *gpio, uint8_t pin, int npixel, const struct snek_neopixel *pixels)
+{
+       volatile uint32_t *outtgl = &(((struct samd21_port *) gpio)->outtgl);
+       uint32_t value = ((uint32_t) 1 << pin);
+
+       while (npixel--) {
+               int32_t p = pixels->p;
+               uint8_t bit;
+               pixels++;
+
+               ao_arch_block_interrupts();
+               for (bit = 0; bit < 24; bit++) {
+                       *outtgl = value;
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       if (p < 0) {
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                               *outtgl = value;
+                       } else {
+                               *outtgl = value;
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                               ao_arch_nop();
+                               ao_arch_nop();
+                               ao_arch_nop();
+
+                       }
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+                       ao_arch_nop();
+
+                       ao_arch_nop();
+
+                       p <<= 1;
+               }
+               ao_arch_release_interrupts();
+       }
+}
diff --git a/src/samd21/ao_serial.h b/src/samd21/ao_serial.h
new file mode 100644 (file)
index 0000000..440b562
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_SERIAL_H_
+#define _AO_SERIAL_H_
+
+#define AO_SERIAL_SPEED_4800   0
+#define AO_SERIAL_SPEED_9600   1
+#define AO_SERIAL_SPEED_19200  2
+#define AO_SERIAL_SPEED_57600  3
+#define AO_SERIAL_SPEED_115200 4
+
+#if HAS_SERIAL_0
+extern volatile struct ao_fifo ao_serial0_rx_fifo;
+extern volatile struct ao_fifo ao_serial0_tx_fifo;
+
+char
+ao_serial0_getchar(void);
+
+int
+_ao_serial0_pollchar(void);
+
+uint8_t
+_ao_serial0_sleep_for(uint16_t timeout);
+
+void
+ao_serial0_putchar(char c);
+
+void
+ao_serial0_drain(void);
+
+void
+ao_serial0_set_speed(uint8_t speed);
+#endif
+
+#if HAS_SERIAL_1
+extern volatile struct ao_fifo ao_serial1_rx_fifo;
+extern volatile struct ao_fifo ao_serial1_tx_fifo;
+
+char
+ao_serial1_getchar(void);
+
+int
+_ao_serial1_pollchar(void);
+
+uint8_t
+_ao_serial1_sleep_for(uint16_t timeout);
+
+void
+ao_serial1_putchar(char c);
+
+void
+ao_serial1_drain(void);
+
+void
+ao_serial1_set_speed(uint8_t speed);
+#endif
+
+#if HAS_SERIAL_2
+extern volatile struct ao_fifo ao_serial2_rx_fifo;
+extern volatile struct ao_fifo ao_serial2_tx_fifo;
+
+char
+ao_serial2_getchar(void);
+
+int
+_ao_serial2_pollchar(void);
+
+uint8_t
+_ao_serial2_sleep_for(uint16_t timeout);
+
+void
+ao_serial2_putchar(char c);
+
+void
+ao_serial2_drain(void);
+
+void
+ao_serial2_set_speed(uint8_t speed);
+#endif
+
+#if HAS_SERIAL_3
+extern volatile struct ao_fifo ao_serial3_rx_fifo;
+extern volatile struct ao_fifo ao_serial3_tx_fifo;
+
+char
+ao_serial3_getchar(void);
+
+int
+_ao_serial3_pollchar(void);
+
+uint8_t
+_ao_serial3_sleep_for(uint16_t timeout);
+
+void
+ao_serial3_putchar(char c);
+
+void
+ao_serial3_drain(void);
+
+void
+ao_serial3_set_speed(uint8_t speed);
+#endif
+
+void
+ao_serial_init(void);
+
+#endif /* _AO_SERIAL_H_ */
diff --git a/src/samd21/ao_serial_samd21.c b/src/samd21/ao_serial_samd21.c
new file mode 100644 (file)
index 0000000..a52966c
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+
+static int
+_ao_usart_tx_start(struct ao_samd21_usart *usart)
+{
+       if (!ao_fifo_empty(&usart->tx_fifo)) {
+#if HAS_SERIAL_SW_FLOW
+               if (usart->gpio_cts && ao_gpio_get(usart->gpio_cts, usart->pin_cts, foo) == 1) {
+                       ao_exti_enable(usart->gpio_cts, usart->pin_cts);
+                       return 0;
+               }
+#endif
+               if (usart->reg->intflag & (1 << SAMD21_SERCOM_INTFLAG_DRE))
+               {
+                       usart->tx_running = 1;
+                       usart->reg->intenset = (1 << SAMD21_SERCOM_INTFLAG_DRE) | (1 << SAMD21_SERCOM_INTFLAG_TXC);
+                       usart->reg->data = ao_fifo_remove(&usart->tx_fifo);
+                       ao_wakeup(&usart->tx_fifo);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static void
+_ao_usart_rx(struct ao_samd21_usart *usart, int is_stdin)
+{
+       if (usart->reg->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) {
+               if (!ao_fifo_full(&usart->rx_fifo)) {
+                       ao_fifo_insert(&usart->rx_fifo, usart->reg->data);
+                       ao_wakeup(&usart->rx_fifo);
+                       if (is_stdin)
+                               ao_wakeup(&ao_stdin_ready);
+#if HAS_SERIAL_SW_FLOW
+                       /* If the fifo is nearly full, turn off RTS and wait
+                        * for it to drain a bunch
+                        */
+                       if (usart->gpio_rts && ao_fifo_mostly(usart->rx_fifo)) {
+                               ao_gpio_set(usart->gpio_rts, usart->pin_rts, usart->pin_rts, 1);
+                               usart->rts = 0;
+                       }
+#endif
+               } else {
+                       usart->reg->intenclr = (1 << SAMD21_SERCOM_INTFLAG_RXC);
+               }
+       }
+}
+
+static void
+ao_usart_isr(struct ao_samd21_usart *usart, int is_stdin)
+{
+       _ao_usart_rx(usart, is_stdin);
+
+       if (!_ao_usart_tx_start(usart))
+               usart->reg->intenclr = (1 << SAMD21_SERCOM_INTFLAG_DRE);
+
+       if (usart->reg->intflag & (1 << SAMD21_SERCOM_INTFLAG_TXC)) {
+               usart->tx_running = 0;
+               usart->reg->intenclr = (1 << SAMD21_SERCOM_INTFLAG_TXC);
+               if (usart->draining) {
+                       usart->draining = 0;
+                       ao_wakeup(&usart->tx_fifo);
+               }
+       }
+}
+
+static const uint32_t ao_usart_speeds[] = {
+       [AO_SERIAL_SPEED_4800] = 4800,
+       [AO_SERIAL_SPEED_9600] = 9600,
+       [AO_SERIAL_SPEED_19200] = 19200,
+       [AO_SERIAL_SPEED_57600] = 57600,
+       [AO_SERIAL_SPEED_115200] = 115200,
+};
+
+static void
+ao_usart_set_speed(struct ao_samd21_usart *usart, uint8_t speed)
+{
+       uint64_t        top = (uint64_t) ao_usart_speeds[speed] << (4 + 16);
+       uint16_t        baud = 65536 - (top + AO_SYSCLK/2) / AO_SYSCLK;
+
+       usart->reg->baud = baud;
+}
+
+static void
+ao_usart_init(struct ao_samd21_usart *usart, int hw_flow, int id)
+{
+       struct samd21_sercom *reg = usart->reg;
+
+       (void) hw_flow;
+
+       /* Send a clock along */
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_SERCOM0_CORE + id);
+
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_SERCOM0_POS + id);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_SERCOM0_POS + id, 4);
+
+       /* enable */
+       samd21_pm.apbcmask |= (1 << (SAMD21_PM_APBCMASK_SERCOM0 + id));
+
+       /* Reset */
+       reg->ctrla = (1 << SAMD21_SERCOM_CTRLA_SWRST);
+
+       while ((reg->ctrla & (1 << SAMD21_SERCOM_CTRLA_SWRST)) ||
+              (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_SWRST)))
+               ;
+
+       reg->ctrlb = ((0 << SAMD21_SERCOM_CTRLB_CHSIZE) |
+                     (0 << SAMD21_SERCOM_CTRLB_SBMODE) |
+                     (0 << SAMD21_SERCOM_CTRLB_COLDEN) |
+                     (0 << SAMD21_SERCOM_CTRLB_SFDE) |
+                     (0 << SAMD21_SERCOM_CTRLB_ENC) |
+                     (0 << SAMD21_SERCOM_CTRLB_PMODE) |
+                     (1 << SAMD21_SERCOM_CTRLB_TXEN) |
+                     (1 << SAMD21_SERCOM_CTRLB_RXEN) |
+                     (3 << SAMD21_SERCOM_CTRLB_FIFOCLR));
+
+       ao_usart_set_speed(usart, AO_SERIAL_SPEED_9600);
+
+       /* finish setup and enable the hardware */
+       reg->ctrla = ((0 << SAMD21_SERCOM_CTRLA_SWRST) |
+                     (1 << SAMD21_SERCOM_CTRLA_ENABLE) |
+                     (1 << SAMD21_SERCOM_CTRLA_MODE) |
+                     (1 << SAMD21_SERCOM_CTRLA_RUNSTDBY) |
+                     (0 << SAMD21_SERCOM_CTRLA_IBON) |
+                     (0 << SAMD21_SERCOM_CTRLA_SAMPR) |
+                     (1 << SAMD21_SERCOM_CTRLA_TXPO) | /* pad[2] */
+                     (3 << SAMD21_SERCOM_CTRLA_RXPO) | /* pad[3] */
+                     (0 << SAMD21_SERCOM_CTRLA_SAMPA) |
+                     (0 << SAMD21_SERCOM_CTRLA_FORM) | /* no parity */
+                     (0 << SAMD21_SERCOM_CTRLA_CMODE) | /* async */
+                     (0 << SAMD21_SERCOM_CTRLA_CPOL) |
+                     (1 << SAMD21_SERCOM_CTRLA_DORD)); /* LSB first */
+
+       /* Enable receive interrupt */
+       reg->intenset = (1 << SAMD21_SERCOM_INTFLAG_RXC);
+}
+
+static int
+_ao_usart_pollchar(struct ao_samd21_usart *usart)
+{
+       int     c;
+
+       if (ao_fifo_empty(&usart->rx_fifo))
+               c = AO_READ_AGAIN;
+       else {
+               uint8_t u;
+               u = ao_fifo_remove(&usart->rx_fifo);
+               if ((usart->reg->intenset & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0) {
+                       if (ao_fifo_barely(&usart->rx_fifo))
+                               usart->reg->intenset = (1 << SAMD21_SERCOM_INTFLAG_RXC);
+               }
+#if HAS_SERIAL_SW_FLOW
+               /* If we've cleared RTS, check if there's space now and turn it back on */
+               if (usart->gpio_rts && usart->rts == 0 && ao_fifo_barely(usart->rx_fifo)) {
+                       ao_gpio_set(usart->gpio_rts, usart->pin_rts, foo, 0);
+                       usart->rts = 1;
+               }
+#endif
+               c = u;
+       }
+       return c;
+}
+
+static char
+ao_usart_getchar(struct ao_samd21_usart *usart)
+{
+       int c;
+       ao_arch_block_interrupts();
+       while ((c = _ao_usart_pollchar(usart)) == AO_READ_AGAIN)
+               ao_sleep(&usart->rx_fifo);
+       ao_arch_release_interrupts();
+       return (char) c;
+}
+
+static void
+ao_usart_putchar(struct ao_samd21_usart *usart, char c)
+{
+       ao_arch_block_interrupts();
+       while (ao_fifo_full(&usart->tx_fifo))
+               ao_sleep(&usart->tx_fifo);
+       ao_fifo_insert(&usart->tx_fifo, c);
+       _ao_usart_tx_start(usart);
+       ao_arch_release_interrupts();
+}
+
+static void
+ao_usart_drain(struct ao_samd21_usart *usart)
+{
+       ao_arch_block_interrupts();
+       while (!ao_fifo_empty(&usart->tx_fifo) || usart->tx_running) {
+               usart->draining = 1;
+               ao_sleep(&usart->tx_fifo);
+       }
+       ao_arch_release_interrupts();
+}
+
+#if HAS_SERIAL_0
+
+struct ao_samd21_usart ao_samd21_usart0;
+
+void samd21_sercom0_isr(void) { ao_usart_isr(&ao_samd21_usart0, USE_SERIAL_0_STDIN); }
+
+char
+ao_serial0_getchar(void)
+{
+       return ao_usart_getchar(&ao_samd21_usart0);
+}
+
+void
+ao_serial0_putchar(char c)
+{
+       ao_usart_putchar(&ao_samd21_usart0, c);
+}
+
+int
+_ao_serial0_pollchar(void)
+{
+       return _ao_usart_pollchar(&ao_samd21_usart0);
+}
+
+void
+ao_serial0_drain(void)
+{
+       ao_usart_drain(&ao_samd21_usart0);
+}
+
+void
+ao_serial0_set_speed(uint8_t speed)
+{
+       ao_usart_drain(&ao_samd21_usart0);
+       ao_usart_set_speed(&ao_samd21_usart0, speed);
+}
+#endif /* HAS_SERIAL_0 */
+
+void
+ao_serial_init(void)
+{
+#if HAS_SERIAL_0
+
+#if SERIAL_0_PA10_PA11
+       /* Pin settings */
+       ao_enable_port(&samd21_port_a);
+       samd21_port_pmux_set(&samd21_port_a, 10, SAMD21_PORT_PMUX_FUNC_C);
+       samd21_port_pmux_set(&samd21_port_a, 11, SAMD21_PORT_PMUX_FUNC_C);
+#else
+#error "No SERIAL_0 port configuration specified"
+#endif
+
+       ao_samd21_usart0.reg = &samd21_sercom0;
+       ao_usart_init(&ao_samd21_usart0, 0, 0);
+
+#if USE_SERIAL_0_STDIN
+       ao_add_stdio(_ao_serial0_pollchar,
+                    ao_serial0_putchar,
+                    NULL);
+#endif
+#endif
+}
diff --git a/src/samd21/ao_tc_samd21.c b/src/samd21/ao_tc_samd21.c
new file mode 100644 (file)
index 0000000..f08c738
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_tc-samd21.h>
+
+void
+ao_tc_set(struct samd21_tc *tc, uint8_t channel, uint32_t value)
+{
+       tc->mode_16.cc[channel] = value;
+}
+
+static void
+ao_tc_init(struct samd21_tc *tc, uint32_t apbcmask)
+{
+       samd21_pm.apbcmask |= apbcmask;
+
+       /* Reset the device */
+       tc->ctrla = (1 << SAMD21_TC_CTRLA_SWRST);
+
+       while ((tc->ctrla & (1 << SAMD21_TC_CTRLA_SWRST)) != 0 ||
+              (tc->status & (1 << SAMD21_TC_STATUS_SYNCBUSY)) != 0)
+               ;
+
+       tc->ctrla = ((SAMD21_TC_CTRLA_PRESCSYNC_GCLK << SAMD21_TC_CTRLA_PRESCSYNC) |
+                    (SAMD21_TC_CTRLA_PRESCALER_DIV1 << SAMD21_TC_CTRLA_PRESCALER) |
+                    (SAMD21_TC_CTRLA_WAVEGEN_NPWM << SAMD21_TC_CTRLA_WAVEGEN) |
+                    (SAMD21_TC_CTRLA_MODE_COUNT16) |
+                    (1 << SAMD21_TC_CTRLA_ENABLE));
+       tc->dbgctrl = (1 << SAMD21_TC_DBGCTRL_DBGRUN);
+}
+
+void
+ao_tc_samd21_init(void)
+{
+       /* SAMD21G18 has only TC3-TC5 */
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC2_TC3);
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TC4_TC5);
+
+       ao_tc_init(&samd21_tc3, 1 << SAMD21_PM_APBCMASK_TC3);
+       ao_tc_init(&samd21_tc4, 1 << SAMD21_PM_APBCMASK_TC4);
+       ao_tc_init(&samd21_tc5, 1 << SAMD21_PM_APBCMASK_TC5);
+}
diff --git a/src/samd21/ao_tc_samd21.h b/src/samd21/ao_tc_samd21.h
new file mode 100644 (file)
index 0000000..1d6cf41
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_TC_SAMD21_H_
+#define _AO_TC_SAMD21_H_
+
+void
+ao_tc_set(struct samd21_tc *tc, uint8_t channel, uint32_t value);
+
+void
+ao_tc_samd21_init(void);
+
+#endif /* _AO_TC_SAMD21_H_ */
diff --git a/src/samd21/ao_tcc_samd21.c b/src/samd21/ao_tcc_samd21.c
new file mode 100644 (file)
index 0000000..9cb45c0
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include <ao_tcc-samd21.h>
+
+void
+ao_tcc_set(struct samd21_tcc *tcc, uint8_t channel, uint32_t value)
+{
+       tcc->cc[channel] = value;
+}
+
+static void
+ao_tcc_init(struct samd21_tcc *tcc, uint32_t apbcmask)
+{
+       samd21_pm.apbcmask |= apbcmask;
+
+       /* Reset the device */
+       tcc->ctrla = (1 << SAMD21_TCC_CTRLA_SWRST);
+
+       while ((tcc->ctrla & (1 << SAMD21_TCC_CTRLA_SWRST)) != 0 ||
+              (tcc->syncbusy & (1 << SAMD21_TCC_SYNCBUSY_SWRST)) != 0)
+               ;
+
+       tcc->per = AO_TCC_PERIOD - 1;
+
+#if 0
+       tcc->evctrl = ((1 << SAMD21_TCC_EVCTRL_MCEO(0)) |
+                      (1 << SAMD21_TCC_EVCTRL_MCEO(1)) |
+                      (1 << SAMD21_TCC_EVCTRL_MCEO(2)) |
+                      (1 << SAMD21_TCC_EVCTRL_MCEO(3)));
+#endif
+
+       tcc->wave = ((SAMD21_TCC_WAVE_WAVEGEN_NPWM << SAMD21_TCC_WAVE_WAVEGEN) |
+                    (0 << SAMD21_TCC_WAVE_RAMP) |
+                    (0 << SAMD21_TCC_WAVE_CIPEREN) |
+                    (0 << SAMD21_TCC_WAVE_CCCEN(0)) |
+                    (0 << SAMD21_TCC_WAVE_CCCEN(1)) |
+                    (0 << SAMD21_TCC_WAVE_CCCEN(2)) |
+                    (0 << SAMD21_TCC_WAVE_CCCEN(3)) |
+                    (0 << SAMD21_TCC_WAVE_POL(0)) |
+                    (0 << SAMD21_TCC_WAVE_POL(1)) |
+                    (0 << SAMD21_TCC_WAVE_POL(1)) |
+                    (0 << SAMD21_TCC_WAVE_POL(3)) |
+                    (0 << SAMD21_TCC_WAVE_SWAP(0)) |
+                    (0 << SAMD21_TCC_WAVE_SWAP(1)) |
+                    (0 << SAMD21_TCC_WAVE_SWAP(1)) |
+                    (0 << SAMD21_TCC_WAVE_SWAP(3)));
+
+       tcc->ctrla = (1 << SAMD21_TCC_CTRLA_ENABLE);
+       tcc->dbgctrl = (1 << SAMD21_TCC_DBGCTRL_DBGRUN);
+}
+
+void
+ao_tcc_samd21_init(void)
+{
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC0_TCC1);
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC2_TC3);
+
+       ao_tcc_init(&samd21_tcc0, 1 << SAMD21_PM_APBCMASK_TCC0);
+       ao_tcc_init(&samd21_tcc1, 1 << SAMD21_PM_APBCMASK_TCC1);
+       ao_tcc_init(&samd21_tcc2, 1 << SAMD21_PM_APBCMASK_TCC2);
+}
diff --git a/src/samd21/ao_tcc_samd21.h b/src/samd21/ao_tcc_samd21.h
new file mode 100644 (file)
index 0000000..a8f41a8
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_TCC_SAMD21_H_
+#define _AO_TCC_SAMD21_H_
+
+void
+ao_tcc_set(struct samd21_tcc *tcc, uint8_t channel, uint32_t value);
+
+void
+ao_tcc_samd21_init(void);
+
+#endif /* _AO_TCC_SAMD21_H_ */
diff --git a/src/samd21/ao_timer.c b/src/samd21/ao_timer.c
new file mode 100644 (file)
index 0000000..0eb0dbe
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+
+#ifndef HAS_TICK
+#define HAS_TICK 1
+#endif
+
+#if HAS_TICK
+volatile AO_TICK_TYPE ao_tick_count;
+
+AO_TICK_TYPE
+ao_time(void)
+{
+       return ao_tick_count;
+}
+
+uint64_t
+ao_time_ns(void)
+{
+       AO_TICK_TYPE    before, after;
+       uint32_t        cvr;
+
+       do {
+               before = ao_tick_count;
+               cvr = samd21_systick.cvr;
+               after = ao_tick_count;
+       } while (before != after);
+
+       return (uint64_t) after * (1000000000ULL / AO_HERTZ) +
+               (uint64_t) cvr * (1000000000ULL / AO_SYSTICK);
+}
+
+#if AO_DATA_ALL
+volatile uint8_t       ao_data_interval = 1;
+volatile uint8_t       ao_data_count;
+#endif
+
+void samd21_systick_isr(void)
+{
+       ao_arch_release_interrupts();
+       if (samd21_systick.csr & (1 << SAMD21_SYSTICK_CSR_COUNTFLAG)) {
+               ++ao_tick_count;
+#ifdef AO_TIMER_HOOK
+               AO_TIMER_HOOK;
+#endif
+       }
+}
+
+#if HAS_ADC
+void
+ao_timer_set_adc_interval(uint8_t interval)
+{
+       ao_arch_critical(
+               ao_data_interval = interval;
+               ao_data_count = 0;
+               );
+}
+#endif
+
+#define SYSTICK_RELOAD (AO_SYSTICK / AO_HERTZ - 1)
+
+void
+ao_timer_init(void)
+{
+       samd21_systick.csr = 0;
+       samd21_systick.rvr = SYSTICK_RELOAD;
+       samd21_systick.cvr = 0;
+       samd21_systick.csr = ((1 << SAMD21_SYSTICK_CSR_ENABLE) |
+                          (1 << SAMD21_SYSTICK_CSR_TICKINT) |
+                          (SAMD21_SYSTICK_CSR_CLKSOURCE_HCLK_8 << SAMD21_SYSTICK_CSR_CLKSOURCE));
+       /* Set clock to lowest priority */
+       samd21_scb.shpr3 |= 3UL << 30;
+}
+
+#endif
+
+
+void
+ao_clock_init(void)
+{
+       /* Set flash wait state to tolerate 48MHz */
+       samd21_nvmctrl.ctrlb |= (1 << SAMD21_NVMCTRL_CTRLB_RWS);
+
+       samd21_pm.apbamask |= ((1 << SAMD21_PM_APBAMASK_GCLK) |
+                              (1 << SAMD21_PM_APBAMASK_SYSCTRL));
+
+       /* Reset gclk */
+       samd21_gclk.ctrl = (1 << SAMD21_GCLK_CTRL_SWRST)
+               ;
+
+       /* Wait for reset to complete */
+       while ((samd21_gclk.ctrl & (1 << SAMD21_GCLK_CTRL_SWRST)) &&
+              (samd21_gclk.status & (1 << SAMD21_GCLK_STATUS_SYNCBUSY)))
+               ;
+
+#ifdef AO_XOSC
+       /* Enable xosc (external xtal oscillator) */
+       samd21_sysctrl.xosc = ((SAMD21_SYSCTRL_XOSC_STARTUP_8192 << SAMD21_SYSCTRL_XOSC_STARTUP) |
+                              (0 << SAMD21_SYSCTRL_XOSC_AMPGC) |
+                              (SAMD21_SYSCTRL_XOSC_GAIN_16MHz << SAMD21_SYSCTRL_XOSC_GAIN) |
+                              (0 << SAMD21_SYSCTRL_XOSC_ONDEMAND) |
+                              (1 << SAMD21_SYSCTRL_XOSC_RUNSTDBY) |
+                              (1 << SAMD21_SYSCTRL_XOSC_XTALEN));
+       samd21_sysctrl.xosc |= ((1 << SAMD21_SYSCTRL_XOSC_ENABLE));
+
+       /* Wait for xosc */
+       while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_XOSCRDY)) == 0)
+               ;
+
+       /* program DPLL */
+
+       /* Divide down */
+       samd21_sysctrl.dpllctrlb = (((AO_XOSC_DIV/2 - 1) << SAMD21_SYSCTRL_DPLLCTRLB_DIV) |
+                                   (0 << SAMD21_SYSCTRL_DPLLCTRLB_LBYPASS) |
+                                   (SAMD21_SYSCTRL_DPLLCTRLB_LTIME_DEFAULT << SAMD21_SYSCTRL_DPLLCTRLB_LTIME) |
+                                   (SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_XOSC << SAMD21_SYSCTRL_DPLLCTRLB_REFCLK) |
+                                   (0 << SAMD21_SYSCTRL_DPLLCTRLB_WUF) |
+                                   (1 << SAMD21_SYSCTRL_DPLLCTRLB_LPEN) |
+                                   (SAMD21_SYSCTRL_DPLLCTRLB_FILTER_DEFAULT << SAMD21_SYSCTRL_DPLLCTRLB_FILTER));
+
+       /* Multiply up */
+       samd21_sysctrl.dpllratio = ((AO_XOSC_MUL - 1) << SAMD21_SYSCTRL_DPLLRATIO_LDR);
+
+       /* Always on in run mode, off in standby mode */
+       samd21_sysctrl.dpllctrla = ((0 << SAMD21_SYSCTRL_DPLLCTRLA_ONDEMAND) |
+                                   (0 << SAMD21_SYSCTRL_DPLLCTRLA_RUNSTDBY));
+
+       /* Enable DPLL */
+       samd21_sysctrl.dpllctrla |= (1 << SAMD21_SYSCTRL_DPLLCTRLA_ENABLE);
+
+       /* Wait for the DPLL to be enabled */
+       while ((samd21_sysctrl.dpllstatus & (1 << SAMD21_SYSCTRL_DPLLSTATUS_ENABLE)) == 0)
+               ;
+
+       /* Wait for the DPLL to be ready */
+       while ((samd21_sysctrl.dpllstatus & (1 << SAMD21_SYSCTRL_DPLLSTATUS_CLKRDY)) == 0)
+               ;
+
+       samd21_gclk_wait_sync();
+
+       /*
+        * Switch generator 0 (CPU clock) to DPLL
+        */
+
+       /* divide by 1 */
+       samd21_gclk_gendiv(AO_GCLK_SYSCLK, AO_XOSC_GCLK_DIV);
+
+       /* select DPLL as source */
+       samd21_gclk_genctrl(SAMD21_GCLK_GENCTRL_SRC_FDPLL96M, AO_GCLK_FDPLL96M);
+#endif
+
+#ifdef AO_DFLL48M
+
+       /*
+        * Enable DFLL48M clock
+        */
+
+       samd21_sysctrl.dfllctrl = (1 << SAMD21_SYSCTRL_DFLLCTRL_ENABLE);
+       samd21_dfll_wait_sync();
+
+#ifdef AO_XOSC32K
+#define AO_GCLK_XOSC32K        1
+
+       /* Enable xosc32k (external 32.768kHz oscillator) */
+       samd21_sysctrl.xosc32k = ((6 << SAMD21_SYSCTRL_XOSC32K_STARTUP) |
+                                 (1 << SAMD21_SYSCTRL_XOSC32K_XTALEN) |
+                                 (1 << SAMD21_SYSCTRL_XOSC32K_EN32K));
+
+       /* requires separate store */
+       samd21_sysctrl.xosc32k |= (1 << SAMD21_SYSCTRL_XOSC32K_ENABLE);
+
+       /* Wait for osc */
+       while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_XOSC32KRDY)) == 0)
+               ;
+
+       /*
+        * Use xosc32k as source of gclk generator AO_GCLK_XOSC32K
+        */
+
+       samd21_gclk_gendiv(AO_GCLK_XOSC32K, 1);
+       samd21_gclk_genctrl(SAMD21_GCLK_GENCTRL_SRC_XOSC32K, AO_GCLK_XOSC32K);
+
+       /*
+        * Use generator as source for dfm48m reference
+        */
+
+       samd21_gclk_clkctrl(AO_GCLK_XOSC32K, SAMD21_GCLK_CLKCTRL_ID_DFLL48M_REF);
+
+       /* Set multiplier to get as close to 48MHz as we can without going over */
+       samd21_sysctrl.dfllmul = (((31/4) << SAMD21_SYSCTRL_DFLLMUL_CSTEP) |
+                                 ((255/4) << SAMD21_SYSCTRL_DFLLMUL_FSTEP) |
+                                 ((AO_DFLL48M / AO_XOSC32K) << SAMD21_SYSCTRL_DFLLMUL_MUL));
+
+       /* pull out coarse calibration value from rom */
+       uint32_t coarse = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL) &
+                          SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL_MASK);
+
+       samd21_sysctrl.dfllval = ((coarse << SAMD21_SYSCTRL_DFLLVAL_COARSE) |
+                                 (512 << SAMD21_SYSCTRL_DFLLVAL_FINE));
+
+       samd21_sysctrl.dfllctrl = 0;
+       samd21_dfll_wait_sync();
+
+       samd21_sysctrl.dfllctrl = ((1 << SAMD21_SYSCTRL_DFLLCTRL_MODE) |
+                                  (1 << SAMD21_SYSCTRL_DFLLCTRL_ENABLE));
+
+       samd21_dfll_wait_sync();
+       samd21_gclk_wait_sync();
+
+       /* wait for fine lock */
+       while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_DFLLLCKC)) == 0 ||
+              (samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_DFLLLCKF)) == 0)
+               ;
+#else
+       samd21_sysctrl.dfllmul = (((31/4) << SAMD21_SYSCTRL_DFLLMUL_CSTEP) |
+                                 ((255/4) << SAMD21_SYSCTRL_DFLLMUL_FSTEP) |
+                                 ((AO_DFLL48M / 1000) << SAMD21_SYSCTRL_DFLLMUL_MUL));
+
+       /* pull out coarse calibration value from rom */
+       uint32_t coarse = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL) &
+                          SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL_MASK);
+
+       samd21_sysctrl.dfllval = ((coarse << SAMD21_SYSCTRL_DFLLVAL_COARSE) |
+                                 (512 << SAMD21_SYSCTRL_DFLLVAL_FINE));
+
+       samd21_sysctrl.dfllctrl = 0;
+       samd21_dfll_wait_sync();
+
+       samd21_sysctrl.dfllctrl = ((1 << SAMD21_SYSCTRL_DFLLCTRL_MODE) |
+                                  (1 << SAMD21_SYSCTRL_DFLLCTRL_BPLCKC) |
+                                  (1 << SAMD21_SYSCTRL_DFLLCTRL_CCDIS) |
+                                  (1 << SAMD21_SYSCTRL_DFLLCTRL_USBCRM) |
+                                  (1 << SAMD21_SYSCTRL_DFLLCTRL_ENABLE));
+
+       samd21_dfll_wait_sync();
+       samd21_gclk_wait_sync();
+#endif
+
+       /*
+        * Switch generator to DFLL48M
+        */
+
+       /* divide by 1 */
+       samd21_gclk_gendiv(AO_GCLK_SYSCLK, 1);
+
+       /* select DFLL48M as source */
+       samd21_gclk_genctrl(SAMD21_GCLK_GENCTRL_SRC_DFLL48M, AO_GCLK_DFLL48M);
+#endif
+
+       /* Set up all of the clocks to be /1 */
+
+       samd21_pm.cpusel = ((0 << SAMD21_PM_CPUSEL_CPUDIV));
+       samd21_pm.apbasel = ((0 << SAMD21_PM_APBASEL_APBADIV));
+       samd21_pm.apbbsel = ((0 << SAMD21_PM_APBBSEL_APBBDIV));
+       samd21_pm.apbcsel = ((0 << SAMD21_PM_APBCSEL_APBCDIV));
+
+       /* Disable OSC8M */
+       samd21_sysctrl.osc8m &= ~(1UL << SAMD21_SYSCTRL_OSC8M_ENABLE);
+
+       /* Additional misc configuration stuff */
+
+       /* Disable automatic NVM write operations */
+       samd21_nvmctrl.ctrlb |= (1UL << SAMD21_NVMCTRL_CTRLB_MANW);
+}
diff --git a/src/samd21/ao_usb_samd21.c b/src/samd21/ao_usb_samd21.c
new file mode 100644 (file)
index 0000000..7f6d2d3
--- /dev/null
@@ -0,0 +1,867 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_usb.h"
+#include "ao_product.h"
+
+#ifndef AO_POWER_MANAGEMENT
+#define AO_POWER_MANAGEMENT    0
+#endif
+
+#ifndef AO_USB_DEVICE_ID_SERIAL
+#define AO_USB_DEVICE_ID_SERIAL 0
+#endif
+
+#if USE_USB_FIFO
+static struct ao_fifo  ao_usb_rx_fifo;
+#endif
+
+#define AO_USB_OUT_SLEEP_ADDR  (&ao_usb_out_avail)
+
+#define SAMD21_USB_ALIGN       __attribute__ ((aligned(4)))
+
+struct ao_usb_setup {
+       uint8_t         dir_type_recip;
+       uint8_t         request;
+       uint16_t        value;
+       uint16_t        index;
+       uint16_t        length;
+} ao_usb_setup;
+
+static uint8_t         ao_usb_ep0_state;
+
+struct samd21_usb_desc samd21_usb_desc[8] SAMD21_USB_ALIGN;
+
+/* Pending EP0 IN data */
+static uint8_t         ao_usb_ep0_in_tmp[2];   /* small buffer */
+static const uint8_t   *ao_usb_ep0_in_data;    /* Remaining data */
+static uint8_t                 ao_usb_ep0_in_len;      /* Remaining amount */
+
+/* Pending EP0 OUT data */
+static uint8_t *ao_usb_ep0_out_data;
+static uint8_t         ao_usb_ep0_out_len;
+
+/* Endpoint 0 buffers */
+static uint8_t ao_usb_ep0_in_buf[AO_USB_CONTROL_SIZE] SAMD21_USB_ALIGN;
+static uint8_t ao_usb_ep0_out_buf[AO_USB_CONTROL_SIZE]  SAMD21_USB_ALIGN;
+
+#if AO_USB_HAS_INT
+/* Pointer to interrupt buffer in USB memory */
+static uint8_t ao_usb_int_buf[AO_USB_INT_SIZE]  SAMD21_USB_ALIGN;
+#endif
+
+/* Buffers in DRAM */
+#if AO_USB_HAS_IN
+static uint8_t ao_usb_in_tx_which;
+static uint8_t ao_usb_tx_count;
+static uint8_t ao_usb_in_buf[2][AO_USB_IN_SIZE]  SAMD21_USB_ALIGN;
+
+#endif
+#if AO_USB_HAS_OUT
+static uint8_t ao_usb_out_rx_which;
+#if !USE_USB_FIFO
+static uint8_t ao_usb_rx_count, ao_usb_rx_pos;
+#endif
+static uint8_t ao_usb_out_buf[2][AO_USB_OUT_SIZE]  SAMD21_USB_ALIGN;
+
+#endif
+
+/* Marks when we don't need to send an IN packet.
+ * This happens only when the last IN packet is not full,
+ * otherwise the host will expect to keep seeing packets.
+ * Send a zero-length packet as required
+ */
+static uint8_t ao_usb_in_flushed;
+
+/* Marks when we have delivered an IN packet to the hardware
+ * and it has not been received yet. ao_sleep on this address
+ * to wait for it to be delivered.
+ */
+static uint8_t ao_usb_in_pending;
+
+/* Marks when an OUT packet has been received by the hardware
+ * but not pulled to the shadow buffer.
+ */
+static uint8_t ao_usb_out_avail;
+uint8_t                ao_usb_running;
+static uint8_t ao_usb_configuration;
+
+#define AO_USB_EP0_GOT_SETUP   1
+#define AO_USB_EP0_GOT_RX_DATA 2
+#define AO_USB_EP0_GOT_TX_ACK  4
+
+static uint8_t ao_usb_ep0_receive;
+static uint8_t ao_usb_address;
+static uint8_t ao_usb_address_pending;
+
+/*
+ * Set current device address and mark the
+ * interface as active
+ */
+static void
+ao_usb_set_address(uint8_t address)
+{
+       samd21_usb.dadd = (1 << SAMD21_USB_DADD_ADDEN) | (address << SAMD21_USB_DADD_DADD);
+       ao_usb_address_pending = 0;
+}
+
+/*
+ * Initialize an entpoint
+ */
+
+static void
+ao_usb_init_bank(struct samd21_usb_desc_bank *bank,
+                uint8_t *buf,
+                uint16_t size)
+{
+       bank->addr = (uint32_t) buf;
+
+       uint32_t size_bits = 0;
+       switch (size) {
+       case 8: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_8; break;
+       case 16: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_16; break;
+       case 32: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_32; break;
+       case 64: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_64; break;
+       case 128: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_128; break;
+       case 256: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_256; break;
+       case 512: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_512; break;
+       case 1023: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_1023; break;
+       }
+       bank->pcksize = ((0 << SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT) |
+                        (0 << SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE) |
+                        (size_bits << SAMD21_USB_DESC_PCKSIZE_SIZE) |
+                        (0 << SAMD21_USB_DESC_PCKSIZE_AUTO_ZLP));
+}
+
+static void
+ao_usb_init_ep(uint8_t ep,
+              uint8_t type_out, void *out_buf, uint16_t out_size,
+              uint8_t type_in, void *in_buf, uint16_t in_size)
+{
+       /* set up descriptors */
+       ao_usb_init_bank(&samd21_usb_desc[ep].bank[0],
+                        out_buf, out_size);
+
+       ao_usb_init_bank(&samd21_usb_desc[ep].bank[1],
+                        in_buf, in_size);
+
+       samd21_usb.ep[ep].epcfg = (uint8_t) ((type_out << SAMD21_USB_EP_EPCFG_EP_TYPE_OUT) |
+                                            (type_in << SAMD21_USB_EP_EPCFG_EP_TYPE_IN));
+
+       /* Clear all status bits */
+       samd21_usb.ep[ep].epstatusclr = 0xff;
+
+       /* Select interrupts */
+       uint8_t epinten = 0;
+       if (out_buf)
+               epinten |= (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT0);
+       if (in_buf)
+               epinten |= (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT1);
+       if (ep == 0)
+               epinten |= (1 << SAMD21_USB_EP_EPINTFLAG_RXSTP);
+       samd21_usb.ep[ep].epintenset = epinten;
+}
+
+static void
+ao_usb_set_ep0(void)
+{
+       ao_usb_init_ep(AO_USB_CONTROL_EP,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_CONTROL,
+                      ao_usb_ep0_out_buf, AO_USB_CONTROL_SIZE,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_IN_CONTROL,
+                      ao_usb_ep0_in_buf, AO_USB_CONTROL_SIZE);
+
+       ao_usb_running = 0;
+
+       /* Reset our internal state
+        */
+
+       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+       ao_usb_ep0_in_data = NULL;
+       ao_usb_ep0_in_len = 0;
+
+       ao_usb_ep0_out_data = NULL;
+       ao_usb_ep0_out_len = 0;
+}
+
+static void
+ao_usb_set_configuration(void)
+{
+#if AO_USB_HAS_INT
+       ao_usb_init_ep(AO_USB_INT_EP,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DISABLED,
+                      NULL, 0,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_IN_INTERRUPT,
+                      ao_usb_int_buf, AO_USB_INT_SIZE);
+#endif
+
+#if AO_USB_HAS_OUT
+       ao_usb_init_ep(AO_USB_OUT_EP,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_BULK,
+                      &ao_usb_out_buf[0][0], AO_USB_OUT_SIZE,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_IN_DUAL_BANK,
+                      &ao_usb_out_buf[1][0], AO_USB_OUT_SIZE);
+
+       /* At first receive, we'll flip this back to 0 */
+       ao_usb_out_rx_which = 1;
+#endif
+
+#if AO_USB_HAS_IN
+       /* Set up the IN end point */
+       ao_usb_init_ep(AO_USB_IN_EP,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DUAL_BANK,
+                      &ao_usb_in_buf[0][0], AO_USB_IN_SIZE,
+                      SAMD21_USB_EP_EPCFG_EP_TYPE_IN_BULK,
+                      &ao_usb_in_buf[1][0], AO_USB_IN_SIZE);
+
+       /* First transmit data goes to buffer 0 */
+       ao_usb_in_tx_which = 0;
+#endif
+
+       ao_usb_in_flushed = 1;
+       ao_usb_in_pending = 0;
+       ao_wakeup(&ao_usb_in_pending);
+
+       ao_usb_out_avail = 0;
+       ao_usb_configuration = 0;
+
+       ao_wakeup(AO_USB_OUT_SLEEP_ADDR);
+}
+
+/* Send an IN data packet */
+static void
+ao_usb_ep0_flush(void)
+{
+       uint8_t this_len;
+
+       /* Check to see if the endpoint is still busy */
+       if ((samd21_usb.ep[0].epstatus & (1 << (SAMD21_USB_EP_EPSTATUS_BK1RDY))) != 0)
+               return;
+
+       this_len = ao_usb_ep0_in_len;
+       if (this_len > AO_USB_CONTROL_SIZE)
+               this_len = AO_USB_CONTROL_SIZE;
+
+       if (this_len < AO_USB_CONTROL_SIZE)
+               ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+       ao_usb_ep0_in_len -= this_len;
+
+       memcpy(ao_usb_ep0_in_buf, ao_usb_ep0_in_data, this_len);
+       ao_usb_ep0_in_data += this_len;
+
+       /* Mark the endpoint as TX valid to send the packet */
+       samd21_usb_desc_set_byte_count(AO_USB_CONTROL_EP, 1, this_len);
+       samd21_usb_ep_set_ready(AO_USB_CONTROL_EP, 1);
+}
+
+/* Read data from the ep0 OUT fifo */
+static void
+ao_usb_ep0_fill(void)
+{
+       uint16_t        len = samd21_usb_desc_get_byte_count(AO_USB_CONTROL_EP, 0);
+
+       if (len > ao_usb_ep0_out_len)
+               len = ao_usb_ep0_out_len;
+       ao_usb_ep0_out_len -= (uint8_t) len;
+
+       /* Pull all of the data out of the packet */
+       memcpy(ao_usb_ep0_out_data, ao_usb_ep0_out_buf, len);
+       ao_usb_ep0_out_data += len;
+
+       /* ACK the packet */
+       samd21_usb_ep_clr_ready(AO_USB_CONTROL_EP, 0);
+}
+
+static void
+ao_usb_ep0_in_reset(void)
+{
+       ao_usb_ep0_in_data = ao_usb_ep0_in_tmp;
+       ao_usb_ep0_in_len = 0;
+}
+
+static void
+ao_usb_ep0_in_queue_byte(uint8_t a)
+{
+       if (ao_usb_ep0_in_len < sizeof (ao_usb_ep0_in_tmp))
+               ao_usb_ep0_in_tmp[ao_usb_ep0_in_len++] = a;
+}
+
+static void
+ao_usb_ep0_in_set(const uint8_t *data, uint8_t len)
+{
+       ao_usb_ep0_in_data = data;
+       ao_usb_ep0_in_len = len;
+}
+
+static void
+ao_usb_ep0_out_set(uint8_t *data, uint8_t len)
+{
+       ao_usb_ep0_out_data = data;
+       ao_usb_ep0_out_len = len;
+}
+
+static void
+ao_usb_ep0_in_start(uint16_t max)
+{
+       /* Don't send more than asked for */
+       if (ao_usb_ep0_in_len > max)
+               ao_usb_ep0_in_len = (uint8_t) max;
+       ao_usb_ep0_flush();
+}
+
+struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
+
+#if AO_USB_DEVICE_ID_SERIAL
+static uint8_t ao_usb_serial[2 + 64];
+
+/* Convert a 32-bit value to 8 hexidecimal UCS2 characters */
+static void
+hex_to_ucs2(uint32_t in, uint8_t *out)
+{
+       int     i;
+
+       for (i = 28; i >= 0; i -= 4) {
+               uint8_t bits = (in >> i) & 0xf;
+               *out++ = ((bits < 10) ? '0' : ('a' - 10)) + bits;
+               *out++ = 0;
+       }
+}
+
+/* Encode the device ID (128 bits) in hexidecimal to use as a device
+ * serial number
+ */
+static void
+ao_usb_serial_init(void)
+{
+       ao_usb_serial[0] = 66;  /* length */
+       ao_usb_serial[1] = AO_USB_DESC_STRING;
+       hex_to_ucs2(samd21_serial.word0, ao_usb_serial + 2 + 0);
+       hex_to_ucs2(samd21_serial.word1, ao_usb_serial + 2 + 16);
+       hex_to_ucs2(samd21_serial.word2, ao_usb_serial + 2 + 32);
+       hex_to_ucs2(samd21_serial.word3, ao_usb_serial + 2 + 48);
+}
+#endif
+
+/* Walk through the list of descriptors and find a match
+ */
+static void
+ao_usb_get_descriptor(uint16_t value, uint16_t length)
+{
+       const uint8_t           *descriptor;
+       uint8_t         type = (uint8_t) (value >> 8);
+       uint8_t         index = (uint8_t) value;
+
+       descriptor = ao_usb_descriptors;
+       while (descriptor[0] != 0) {
+               if (descriptor[1] == type && index-- == 0) {
+                       uint8_t len;
+                       if (type == AO_USB_DESC_CONFIGURATION)
+                               len = descriptor[2];
+                       else
+                               len = descriptor[0];
+#if AO_USB_DEVICE_ID_SERIAL
+                       /* Slightly hacky - the serial number is string 3 */
+                       if (type == AO_USB_DESC_STRING && (value & 0xff) == 3) {
+                               descriptor = ao_usb_serial;
+                               len = sizeof (ao_usb_serial);
+                       }
+#endif
+                       if (len > length)
+                               len = (uint8_t) length;
+                       ao_usb_ep0_in_set(descriptor, len);
+                       break;
+               }
+               descriptor += descriptor[0];
+       }
+}
+
+static void
+ao_usb_ep0_setup(void)
+{
+       /* Pull the setup packet out of the fifo */
+       ao_usb_ep0_out_set((uint8_t *) &ao_usb_setup, 8);
+       ao_usb_ep0_fill();
+       if (ao_usb_ep0_out_len != 0)
+               return;
+
+       if ((ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) || ao_usb_setup.length == 0)
+               ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+       else
+               ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
+
+       ao_usb_ep0_in_reset();
+
+       switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
+       case AO_USB_TYPE_STANDARD:
+               switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
+               case AO_USB_RECIP_DEVICE:
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_ADDRESS:
+                               ao_usb_address = (uint8_t) ao_usb_setup.value;
+                               ao_usb_address_pending = 1;
+                               break;
+                       case AO_USB_REQ_GET_DESCRIPTOR:
+                               ao_usb_get_descriptor(ao_usb_setup.value, ao_usb_setup.length);
+                               break;
+                       case AO_USB_REQ_GET_CONFIGURATION:
+                               ao_usb_ep0_in_queue_byte(ao_usb_configuration);
+                               break;
+                       case AO_USB_REQ_SET_CONFIGURATION:
+                               ao_usb_configuration = (uint8_t) ao_usb_setup.value;
+                               ao_usb_set_configuration();
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_INTERFACE:
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_GET_INTERFACE:
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_INTERFACE:
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_ENDPOINT:
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       }
+                       break;
+               }
+               break;
+       case AO_USB_TYPE_CLASS:
+               switch (ao_usb_setup.request) {
+               case AO_USB_SET_LINE_CODING:
+                       ao_usb_ep0_out_set((uint8_t *) &ao_usb_line_coding, 7);
+                       break;
+               case AO_USB_GET_LINE_CODING:
+                       ao_usb_ep0_in_set((const uint8_t *) &ao_usb_line_coding, 7);
+                       break;
+               case AO_USB_SET_CONTROL_LINE_STATE:
+                       break;
+               }
+               break;
+       }
+
+       /* If we're not waiting to receive data from the host,
+        * queue an IN response
+        */
+       if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+               ao_usb_ep0_in_start(ao_usb_setup.length);
+}
+
+static void
+ao_usb_ep0_handle(uint8_t receive)
+{
+       ao_usb_ep0_receive = 0;
+       if (receive & AO_USB_EP0_GOT_SETUP) {
+               ao_usb_ep0_setup();
+       }
+       if (receive & AO_USB_EP0_GOT_RX_DATA) {
+               if (ao_usb_ep0_state == AO_USB_EP0_DATA_OUT) {
+                       ao_usb_ep0_fill();
+                       if (ao_usb_ep0_out_len == 0) {
+                               ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+                               ao_usb_ep0_in_start(0);
+                       }
+               }
+       }
+       if (receive & AO_USB_EP0_GOT_TX_ACK) {
+
+#if HAS_FLIGHT && AO_USB_FORCE_IDLE
+               ao_flight_force_idle = 1;
+#endif
+               if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+                       ao_usb_ep0_flush();
+               /* Wait until the IN packet is received from addr 0
+                * before assigning our local address
+                */
+               if (ao_usb_address_pending)
+                       ao_usb_set_address(ao_usb_address);
+       }
+}
+
+#if AO_POWER_MANAGEMENT
+static void
+ao_usb_suspend(void)
+{
+       stm_usb.cntr |= (1 << STM_USB_CNTR_FSUSP);
+       ao_power_suspend();
+       stm_usb.cntr |= (1 << STM_USB_CNTR_LP_MODE);
+       ao_clock_suspend();
+}
+
+static void
+ao_usb_wakeup(void)
+{
+       ao_clock_resume();
+       stm_usb.cntr &= ~(1 << STM_USB_CNTR_FSUSP);
+       ao_power_resume();
+}
+#endif
+
+#if USE_USB_FIFO
+static void
+ao_usb_fifo_check(void)
+{
+       uint8_t next_which = 1 - ao_usb_out_rx_which;
+       if (ao_usb_out_avail & (1 << next_which)) {
+               uint8_t *buf = ao_usb_out_buf[next_which];
+               uint16_t len = samd21_usb_desc_get_byte_count(AO_USB_OUT_EP, next_which);
+
+               if (ao_fifo_has_space(&ao_usb_rx_fifo, len)) {
+                       uint16_t        i;
+
+#if AO_USB_OUT_HOOK
+                       ao_usb_out_hook(buf, len);
+#endif
+                       for (i = 0; i < len; i++)
+                               ao_fifo_insert(&ao_usb_rx_fifo, buf[i]);
+                       samd21_usb_ep_clr_ready(AO_USB_OUT_EP, next_which);
+                       ao_usb_out_avail &= ~(1 << next_which);
+                       ao_usb_out_rx_which = next_which;
+                       ao_wakeup(AO_USB_OUT_SLEEP_ADDR);
+               }
+       }
+}
+#endif
+
+void
+samd21_usb_isr(void)
+{
+       uint16_t        intflag = samd21_usb.intflag;
+       uint16_t        epintsmry = samd21_usb.epintsmry;
+
+       samd21_usb.intflag = intflag;
+
+       if (epintsmry & (1 << 0)) {
+               uint8_t epintflag = samd21_usb.ep[0].epintflag;
+               samd21_usb.ep[0].epintflag = epintflag;
+
+               if (epintflag & (1 << SAMD21_USB_EP_EPINTFLAG_RXSTP))
+                       ao_usb_ep0_receive |= AO_USB_EP0_GOT_SETUP;
+               else if (epintflag & (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT0))
+                       ao_usb_ep0_receive |= AO_USB_EP0_GOT_RX_DATA;
+               if (epintflag & (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT1))
+                       ao_usb_ep0_receive |= AO_USB_EP0_GOT_TX_ACK;
+               ao_usb_ep0_handle(ao_usb_ep0_receive);
+       }
+#if AO_USB_HAS_OUT
+       if (epintsmry & (1 << AO_USB_OUT_EP)) {
+               uint8_t epintflag = samd21_usb.ep[AO_USB_OUT_EP].epintflag;
+               samd21_usb.ep[AO_USB_OUT_EP].epintflag = epintflag;
+               uint8_t avail = (epintflag >> SAMD21_USB_EP_EPINTFLAG_TRCPT0) & 3;
+               if (avail) {
+                       ao_usb_out_avail |= avail;
+                       ao_usb_running = 1;
+#if USE_USB_FIFO
+                       ao_usb_fifo_check();
+#else
+                       ao_wakeup(AO_USB_OUT_SLEEP_ADDR);
+#endif
+               }
+       }
+#endif
+#if AO_USB_HAS_IN
+       if (epintsmry & (1 << AO_USB_IN_EP)) {
+               uint8_t epintflag = samd21_usb.ep[AO_USB_IN_EP].epintflag;
+               samd21_usb.ep[AO_USB_IN_EP].epintflag = epintflag;
+               uint8_t done = (epintflag >> SAMD21_USB_EP_EPINTFLAG_TRCPT0) & 3;
+               if (done) {
+                       ao_usb_in_pending &= (uint8_t) ~done;
+                       ao_wakeup(&ao_usb_in_pending);
+               }
+       }
+#endif
+#if AO_USB_HAS_INT
+       if (epintsmry & (1 << AO_USB_INT_EP)) {
+       }
+#endif
+       if (intflag & (1 << SAMD21_USB_INTFLAG_EORST)) {
+               ao_usb_set_ep0();
+       }
+#if AO_POWER_MANAGEMENT
+       if (istr & (1 << STM_USB_ISTR_SUSP))
+               ao_usb_suspend();
+
+       if (istr & (1 << STM_USB_ISTR_WKUP))
+               ao_usb_wakeup();
+#endif
+}
+
+#if AO_USB_HAS_IN
+/* Queue the current IN buffer for transmission */
+static void
+_ao_usb_in_send(void)
+{
+       ao_usb_in_pending |= (uint8_t) (1 << ao_usb_in_tx_which);
+       if (ao_usb_tx_count != AO_USB_IN_SIZE)
+               ao_usb_in_flushed = 1;
+
+       samd21_usb_desc_set_byte_count(AO_USB_IN_EP, ao_usb_in_tx_which, ao_usb_tx_count);
+       samd21_usb_ep_set_ready(AO_USB_IN_EP, ao_usb_in_tx_which);
+
+       /* Toggle our usage */
+       ao_usb_in_tx_which = 1 - ao_usb_in_tx_which;
+       ao_usb_tx_count = 0;
+}
+
+/* Wait for a free IN buffer. Interrupts are blocked */
+static void
+_ao_usb_in_wait(void)
+{
+       /* Wait for an IN buffer to be ready */
+       while ((ao_usb_in_pending & (1 << ao_usb_in_tx_which)) != 0)
+               ao_sleep(&ao_usb_in_pending);
+}
+
+void
+ao_usb_flush(void)
+{
+       if (!ao_usb_running)
+               return;
+
+       /* Anytime we've sent a character since
+        * the last time we flushed, we'll need
+        * to send a packet -- the only other time
+        * we would send a packet is when that
+        * packet was full, in which case we now
+        * want to send an empty packet
+        */
+       ao_arch_block_interrupts();
+       if (!ao_usb_in_flushed) {
+               _ao_usb_in_wait();
+               if (!ao_usb_in_flushed)
+                       _ao_usb_in_send();
+       }
+       ao_arch_release_interrupts();
+}
+
+void
+ao_usb_putchar(char c)
+{
+       if (!ao_usb_running)
+               return;
+
+       if (c == '\n')
+               ao_usb_putchar('\r');
+       ao_arch_block_interrupts();
+       _ao_usb_in_wait();
+
+       ao_usb_in_flushed = 0;
+       ao_usb_in_buf[ao_usb_in_tx_which][ao_usb_tx_count++] = c;
+
+       /* Send the packet when full */
+       if (ao_usb_tx_count == AO_USB_IN_SIZE)
+               _ao_usb_in_send();
+
+       ao_arch_release_interrupts();
+       if (c == '\n')
+               ao_usb_flush();
+}
+#endif
+
+#if AO_USB_HAS_OUT
+#if !USE_USB_FIFO
+static bool
+_ao_usb_out_recv(void)
+{
+       uint8_t next_which = 1 - ao_usb_out_rx_which;
+
+       if ((ao_usb_out_avail & (1 << next_which)) != 0) {
+               /* switch current buffer */
+               ao_usb_out_rx_which = next_which;
+               ao_usb_out_avail &= (uint8_t) ~(1 << ao_usb_out_rx_which);
+
+               ao_usb_rx_count = (uint8_t) samd21_usb_desc_get_byte_count(AO_USB_OUT_EP, ao_usb_out_rx_which);
+               ao_usb_rx_pos = 0;
+               return true;
+       }
+       return false;
+}
+#endif
+
+static int
+_ao_usb_pollchar(void)
+{
+       uint8_t c;
+
+       if (!ao_usb_running)
+               return AO_READ_AGAIN;
+
+#if USE_USB_FIFO
+       if (ao_fifo_empty(&ao_usb_rx_fifo))
+               return AO_READ_AGAIN;
+       c = ao_fifo_remove(&ao_usb_rx_fifo);
+       ao_usb_fifo_check();
+#else
+       for (;;) {
+               if (ao_usb_rx_pos != ao_usb_rx_count)
+                       break;
+
+               /* Check for packet */
+               if (!_ao_usb_out_recv())
+                       return AO_READ_AGAIN;
+       }
+
+       /* Pull a character out of the fifo */
+       c = ao_usb_out_buf[ao_usb_out_rx_which][ao_usb_rx_pos++];
+
+       if (ao_usb_rx_pos == ao_usb_rx_count)
+               samd21_usb_ep_clr_ready(AO_USB_OUT_EP, ao_usb_out_rx_which);
+#endif
+
+       return c;
+}
+
+char
+ao_usb_getchar(void)
+{
+       int     c;
+
+       ao_arch_block_interrupts();
+       while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN)
+               ao_sleep(AO_USB_OUT_SLEEP_ADDR);
+       ao_arch_release_interrupts();
+       return (char) c;
+}
+
+#endif
+
+void
+ao_usb_disable(void)
+{
+       ao_arch_block_interrupts();
+       samd21_nvic_clear_enable(SAMD21_NVIC_ISR_USB_POS);
+
+       /* Disable USB pull-up */
+       samd21_usb.ctrlb |= (1 << SAMD21_USB_CTRLB_DETACH);
+
+       /* Switch off the device */
+       samd21_usb.ctrla &= (uint8_t) ~(1 << SAMD21_USB_CTRLA_ENABLE);
+
+       /* Disable the interface */
+       samd21_pm.apbbmask &= ~(1UL << SAMD21_PM_APBBMASK_USB);
+       ao_arch_release_interrupts();
+}
+
+void
+ao_usb_enable(void)
+{
+       int     t;
+
+       /* Set up USB DM/DP pins */
+       samd21_port_pmux_set(&samd21_port_a, 24, SAMD21_PORT_PMUX_FUNC_G);
+       samd21_port_pmux_set(&samd21_port_a, 25, SAMD21_PORT_PMUX_FUNC_G);
+
+       /* Assign gclk 0 to USB reference */
+       samd21_gclk_clkctrl(AO_GCLK_USB, SAMD21_GCLK_CLKCTRL_ID_USB);
+
+       /* Enable USB clock */
+       samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_USB);
+
+       /* Reset USB Device */
+
+       samd21_usb.ctrla |= (1 << SAMD21_USB_CTRLA_SWRST);
+
+       while ((samd21_usb.syncbusy & (1 << SAMD21_USB_SYNCBUSY_SWRST)) == 0)
+               ;
+
+       while ((samd21_usb.syncbusy & (1 << SAMD21_USB_SYNCBUSY_SWRST)) != 0)
+               ;
+
+       memset(&samd21_usb_desc, 0, sizeof (samd21_usb_desc));
+
+       /* Detach */
+       samd21_usb.ctrlb |= (1 << SAMD21_USB_CTRLB_DETACH);
+
+       samd21_usb.descadd = (uint32_t) &samd21_usb_desc;
+
+       /* Load calibration values */
+       uint32_t transn = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_USB_TRANSN) &
+                          SAMD21_AUX1_CALIBRATION_USB_TRANSN_MASK);
+       if (transn == 0x1f)
+               transn = 5;
+       uint32_t transp = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_USB_TRANSP) &
+                          SAMD21_AUX1_CALIBRATION_USB_TRANSP_MASK);
+       if (transp == 0x1f)
+               transp = 29;
+       uint32_t trim   = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_USB_TRIM) &
+                          SAMD21_AUX1_CALIBRATION_USB_TRIM_MASK);
+       if (trim == 7)
+               trim = 3;
+
+       samd21_usb.padcal = (uint16_t) ((transn << SAMD21_USB_PADCAL_TRANSN) |
+                                       (transp << SAMD21_USB_PADCAL_TRANSP) |
+                                       (trim << SAMD21_USB_PADCAL_TRIM));
+
+       samd21_usb.qosctrl = ((3 << SAMD21_USB_QOSCTRL_CQOS) |
+                             (3 << SAMD21_USB_QOSCTRL_DQOS));
+
+       ao_arch_block_interrupts();
+
+       /* set full speed */
+       samd21_usb.ctrlb = (SAMD21_USB_CTRLB_SPDCONF_FS << SAMD21_USB_CTRLB_SPDCONF);
+
+       /* Set device mode */
+       samd21_usb.ctrla = ((0 << SAMD21_USB_CTRLA_MODE) |
+                           (1 << SAMD21_USB_CTRLA_RUNSTDBY) |
+                           (1 << SAMD21_USB_CTRLA_ENABLE));
+
+       while ((samd21_usb.syncbusy & (1 << SAMD21_USB_SYNCBUSY_ENABLE)) != 0)
+               ;
+
+       /* configure interrupts */
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_USB_POS);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_USB_POS, 2);
+
+       samd21_usb.intenset = ((1 << SAMD21_USB_INTFLAG_EORST));
+
+       ao_arch_release_interrupts();
+
+       for (t = 0; t < 50000; t++)
+               ao_arch_nop();
+
+       /* Attach */
+       samd21_usb.ctrlb &= (uint16_t) ~(1 << SAMD21_USB_CTRLB_DETACH);
+}
+
+void
+ao_usb_init(void)
+{
+       ao_usb_enable();
+
+#if AO_USB_DEVICE_ID_SERIAL
+       ao_usb_serial_init();
+#endif
+
+       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+}
diff --git a/src/samd21/samd21.h b/src/samd21/samd21.h
new file mode 100644 (file)
index 0000000..73b3db8
--- /dev/null
@@ -0,0 +1,1848 @@
+/*
+ * Copyright © 2019 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _SAMD21_H_
+#define _SAMD21_H_
+
+#include <stdint.h>
+
+typedef volatile uint64_t      vuint64_t;
+typedef volatile uint32_t      vuint32_t;
+typedef volatile void *                vvoid_t;
+typedef volatile uint16_t      vuint16_t;
+typedef volatile uint8_t       vuint8_t;
+
+struct samd21_pac {
+       vuint32_t       wpclr;
+       vuint32_t       wpset;
+};
+
+extern struct samd21_pac samd21_pac0;
+extern struct samd21_pac samd21_pac1;
+extern struct samd21_pac samd21_pac2;
+
+#define samd21_pac0 (*(struct samd21_pac *) 0x40000000)
+#define samd21_pac1 (*(struct samd21_pac *) 0x41000000)
+#define samd21_pac2 (*(struct samd21_pac *) 0x42000000)
+
+struct samd21_gclk {
+       vuint8_t        ctrl;
+       vuint8_t        status;
+       vuint16_t       clkctrl;
+       vuint32_t       genctrl;
+       vuint32_t       gendiv;
+};
+
+extern struct samd21_gclk samd21_gclk;
+
+#define samd21_gclk    (*(struct samd21_gclk *) 0x40000c00)
+
+#define SAMD21_GCLK_CTRL_SWRST         0
+
+#define SAMD21_GCLK_STATUS_SYNCBUSY    7
+
+#define SAMD21_GCLK_CLKCTRL_ID         0
+#define  SAMD21_GCLK_CLKCTRL_ID_DFLL48M_REF            0
+#define  SAMD21_GCLK_CLKCTRL_ID_DPLL                   1
+#define  SAMD21_GCLK_CLKCTRL_ID_DPLL_32K               2
+#define  SAMD21_GCLK_CLKCTRL_ID_WDT                    3
+#define  SAMD21_GCLK_CLKCTRL_ID_RTC                    4
+#define  SAMD21_GCLK_CLKCTRL_ID_EIC                    5
+#define  SAMD21_GCLK_CLKCTRL_ID_USB                    6
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_0                0x07
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_1                0x08
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_2                0x09
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_3                0x0a
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_4                0x0b
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_5                0x0c
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_6                0x0d
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_7                0x0e
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_8                0e0f
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_9                0x10
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_10       0x11
+#define  SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_11       0x12
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOMx_SLOW           0x13
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOM0_CORE           0x14
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOM1_CORE           0x15
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOM2_CORE           0x16
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOM3_CORE           0x17
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOM4_CORE           0x18
+#define  SAMD21_GCLK_CLKCTRL_ID_SERCOM5_CORE           0x19
+#define  SAMD21_GCLK_CLKCTRL_ID_TCC0_TCC1              0x1a
+#define  SAMD21_GCLK_CLKCTRL_ID_TCC2_TC3               0x1b
+#define  SAMD21_GCLK_CLKCTRL_ID_TC4_TC5                        0x1c
+#define  SAMD21_GCLK_CLKCTRL_ID_TC6_TC7                        0x1d
+#define  SAMD21_GCLK_CLKCTRL_ID_ADC                    0x1e
+#define  SAMD21_GCLK_CLKCTRL_ID_AC_DIG                 0x1f
+#define  SAMD21_GCLK_CLKCTRL_ID_AC_ANA                 0x20
+#define  SAMD21_GCLK_CLKCTRL_ID_DAC                    0x21
+#define  SAMD21_GCLK_CLKCTRL_ID_PTC                    0x22
+#define  SAMD21_GCLK_CLKCTRL_ID_I2S_0                  0x23
+#define  SAMD21_GCLK_CLKCTRL_ID_I2S_1                  0x24
+#define  SAMD21_GCLK_CLKCTRL_ID_TCC3                   0x25
+
+#define SAMD21_GCLK_CLKCTRL_GEN                8
+#define SAMD21_GCLK_CLKCTRL_CLKEN      14
+#define SAMD21_GCLK_CLKCTRL_WRTLOCK    15
+
+#define SAMD21_GCLK_GENCTRL_ID         0
+#define SAMD21_GCLK_GENCTRL_SRC                8
+#define  SAMD21_GCLK_GENCTRL_SRC_XOSC          0
+#define  SAMD21_GCLK_GENCTRL_SRC_GCLKIN                1
+#define  SAMD21_GCLK_GENCTRL_SRC_GCLKGEN1      2
+#define  SAMD21_GCLK_GENCTRL_SRC_OSCULP32K     3
+#define  SAMD21_GCLK_GENCTRL_SRC_OSC32K                4
+#define  SAMD21_GCLK_GENCTRL_SRC_XOSC32K       5
+#define  SAMD21_GCLK_GENCTRL_SRC_OSC8M         6
+#define  SAMD21_GCLK_GENCTRL_SRC_DFLL48M       7
+#define  SAMD21_GCLK_GENCTRL_SRC_FDPLL96M      8
+
+#define SAMD21_GCLK_GENCTRL_GENEN      16
+#define SAMD21_GCLK_GENCTRL_IDC                17
+#define SAMD21_GCLK_GENCTRL_OOV                18
+#define SAMD21_GCLK_GENCTRL_OE         19
+#define SAMD21_GCLK_GENCTRL_DIVSEL     20
+#define SAMD21_GCLK_GENCTRL_RUNSTDBY   21
+
+#define SAMD21_GCLK_GENDIV_ID          0
+#define SAMD21_GCLK_GENDIV_DIV         8
+
+struct samd21_pm {
+       vuint8_t        ctrl;
+       vuint8_t        sleep;
+       vuint8_t        reserved_02;
+       vuint8_t        reserved_03;
+       vuint32_t       reserved_04;
+       vuint8_t        cpusel;
+       vuint8_t        apbasel;
+       vuint8_t        apbbsel;
+       vuint8_t        apbcsel;
+       vuint32_t       reserved_0c;
+
+       vuint32_t       reserved_10;
+       vuint32_t       ahbmask;
+       vuint32_t       apbamask;
+       vuint32_t       apbbmask;
+
+       vuint32_t       apbcmask;
+       vuint32_t       reserved_24;
+       vuint32_t       reserved_28;
+       vuint32_t       reserved_2c;
+
+       vuint32_t       reserved_30;
+       vuint8_t        intenclr;
+       vuint8_t        intelset;
+       vuint8_t        intflag;
+       vuint8_t        reserved_37;
+       vuint8_t        rcause;
+};
+
+extern struct samd21_pm samd21_pm;
+
+#define samd21_pm      (*(struct samd21_pm *) 0x40000400)
+
+#define SAMD21_PM_CPUSEL_CPUDIV                0
+#define SAMD21_PM_APBASEL_APBADIV      0
+#define SAMD21_PM_APBBSEL_APBBDIV      0
+#define SAMD21_PM_APBCSEL_APBCDIV      0
+
+#define SAMD21_PM_APBAMASK_PAC0                0
+#define SAMD21_PM_APBAMASK_PM          1
+#define SAMD21_PM_APBAMASK_SYSCTRL     2
+#define SAMD21_PM_APBAMASK_GCLK                3
+#define SAMD21_PM_APBAMASK_WDT         4
+#define SAMD21_PM_APBAMASK_RTC         5
+#define SAMD21_PM_APBAMASK_EIC         6
+
+#define SAMD21_PM_AHBMASK_HPB0         0
+#define SAMD21_PM_AHBMASK_HPB1         1
+#define SAMD21_PM_AHBMASK_HPB2         2
+#define SAMD21_PM_AHBMASK_DSU          3
+#define SAMD21_PM_AHBMASK_NVMCTRL      4
+#define SAMD21_PM_AHBMASK_DMAC         5
+#define SAMD21_PM_AHBMASK_USB          6
+
+#define SAMD21_PM_APBBMASK_PAC1                0
+#define SAMD21_PM_APBBMASK_DSU         1
+#define SAMD21_PM_APBBMASK_NVMCTRL     2
+#define SAMD21_PM_APBBMASK_PORT                3
+#define SAMD21_PM_APBBMASK_DMAC                4
+#define SAMD21_PM_APBBMASK_USB         5
+
+#define SAMD21_PM_APBCMASK_PAC2                0
+#define SAMD21_PM_APBCMASK_EVSYS       1
+#define SAMD21_PM_APBCMASK_SERCOM0     2
+#define SAMD21_PM_APBCMASK_SERCOM1     3
+#define SAMD21_PM_APBCMASK_SERCOM2     4
+#define SAMD21_PM_APBCMASK_SERCOM3     5
+#define SAMD21_PM_APBCMASK_SERCOM4     6
+#define SAMD21_PM_APBCMASK_SERCOM5     7
+#define SAMD21_PM_APBCMASK_TCC0                8
+#define SAMD21_PM_APBCMASK_TCC1                9
+#define SAMD21_PM_APBCMASK_TCC2                10
+#define SAMD21_PM_APBCMASK_TC3         11
+#define SAMD21_PM_APBCMASK_TC4         12
+#define SAMD21_PM_APBCMASK_TC5         13
+#define SAMD21_PM_APBCMASK_TC6         14
+#define SAMD21_PM_APBCMASK_TC7         15
+#define SAMD21_PM_APBCMASK_ADC         16
+#define SAMD21_PM_APBCMASK_AC          17
+#define SAMD21_PM_APBCMASK_DAC         18
+#define SAMD21_PM_APBCMASK_PTC         19
+#define SAMD21_PM_APBCMASK_I2S         20
+#define SAMD21_PM_APBCMASK_AC1         21
+#define SAMD21_PM_APBCMASK_TCC3                24
+
+struct samd21_sysctrl {
+       vuint32_t       intenclr;
+       vuint32_t       intenset;
+       vuint32_t       intflag;
+       vuint32_t       pclksr;
+
+       vuint32_t       xosc;
+       vuint32_t       xosc32k;
+       vuint32_t       osc32k;
+       vuint32_t       osculp32k;
+
+       vuint32_t       osc8m;
+       vuint32_t       dfllctrl;
+       vuint32_t       dfllval;
+       vuint32_t       dfllmul;
+
+       vuint32_t       dfllsync;
+       vuint32_t       bod33;
+       vuint32_t       reserved_38;
+       vuint32_t       vreg;
+
+       vuint32_t       vref;
+       vuint32_t       dpllctrla;
+       vuint32_t       dpllratio;
+       vuint32_t       dpllctrlb;
+
+       vuint32_t       dpllstatus;
+};
+
+extern struct samd21_sysctrl samd21_sysctrl;
+
+#define samd21_sysctrl (*(struct samd21_sysctrl *) 0x40000800)
+
+#define SAMD21_SYSCTRL_PCLKSR_XOSCRDY          0
+#define SAMD21_SYSCTRL_PCLKSR_XOSC32KRDY       1
+#define SAMD21_SYSCTRL_PCLKSR_OSC32KRDY                2
+#define SAMD21_SYSCTRL_PCLKSR_OSC8MRDY         3
+#define SAMD21_SYSCTRL_PCLKSR_DFLLRDY          4
+#define SAMD21_SYSCTRL_PCLKSR_DFLLOOB          5
+#define SAMD21_SYSCTRL_PCLKSR_DFLLLCKF         6
+#define SAMD21_SYSCTRL_PCLKSR_DFLLLCKC         7
+#define SAMD21_SYSCTRL_PCLKSR_DFLLRCS          8
+#define SAMD21_SYSCTRL_PCLKSR_BOD33RDY         9
+#define SAMD21_SYSCTRL_PCLKSR_BOD33DET         10
+#define SAMD21_SYSCTRL_PCLKSR_B33SRDY          11
+#define SAMD21_SYSCTRL_PCLKSR_DBPLLLCKR                15
+#define SAMD21_SYSCTRL_PCLKSR_DPLLLCKF         16
+#define SAMD21_SYSCTRL_PCLKSR_DPLLTO           17
+
+#define SAMD21_SYSCTRL_XOSC_ENABLE             1
+#define SAMD21_SYSCTRL_XOSC_XTALEN             2
+#define SAMD21_SYSCTRL_XOSC_RUNSTDBY           6
+#define SAMD21_SYSCTRL_XOSC_ONDEMAND           7
+#define SAMD21_SYSCTRL_XOSC_GAIN               8
+#define  SAMD21_SYSCTRL_XOSC_GAIN_2MHz                 0
+#define  SAMD21_SYSCTRL_XOSC_GAIN_4MHz                 1
+#define  SAMD21_SYSCTRL_XOSC_GAIN_8MHz                 2
+#define  SAMD21_SYSCTRL_XOSC_GAIN_16MHz                        3
+#define  SAMD21_SYSCTRL_XOSC_GAIN_30MHz                        4
+#define SAMD21_SYSCTRL_XOSC_AMPGC              11
+#define SAMD21_SYSCTRL_XOSC_STARTUP            12
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_1                 0
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_2                         1
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_4                 2
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_8                 3
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_16                        4
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_32                        5
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_64                        6
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_128               7
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_256               8
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_512               9
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_1024              10
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_2048              11
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_4096              12
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_8192              13
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_16384             14
+#define  SAMD21_SYSCTRL_XOSC_STARTUP_32768             15
+
+#define SAMD21_SYSCTRL_XOSC32K_ENABLE  1
+#define SAMD21_SYSCTRL_XOSC32K_XTALEN  2
+#define SAMD21_SYSCTRL_XOSC32K_EN32K   3
+#define SAMD21_SYSCTRL_XOSC32K_AAMPEN  5
+#define SAMD21_SYSCTRL_XOSC32K_RUNSTDBY        6
+#define SAMD21_SYSCTRL_XOSC32K_ONDEMAND        7
+#define SAMD21_SYSCTRL_XOSC32K_STARTUP 8
+#define SAMD21_SYSCTRL_XOSC32K_WRTLOCK 12
+
+#define SAMD21_SYSCTRL_OSC8M_ENABLE    1
+#define SAMD21_SYSCTRL_OSC8M_RUNSTDBY  6
+#define SAMD21_SYSCTRL_OSC8M_ONDEMAND  7
+#define SAMD21_SYSCTRL_OSC8M_PRESC     8
+#define  SAMD21_SYSCTRL_OSC8M_PRESC_1          0
+#define  SAMD21_SYSCTRL_OSC8M_PRESC_2          1
+#define  SAMD21_SYSCTRL_OSC8M_PRESC_4          2
+#define  SAMD21_SYSCTRL_OSC8M_PRESC_8          3
+#define  SAMD21_SYSCTRL_OSC8M_PRESC_MASK       3
+#define SAMD21_SYSCTRL_OSC8M_CALIB     16
+#define SAMD21_SYSCTRL_OSC8M_FRANGE    30
+#define  SAMD21_SYSCTRL_OSC8M_FRANGE_4_6       0
+#define  SAMD21_SYSCTRL_OSC8M_FRANGE_6_8       1
+#define  SAMD21_SYSCTRL_OSC8M_FRANGE_8_11      2
+#define  SAMD21_SYSCTRL_OSC8M_FRANGE_11_15     3
+
+#define SAMD21_SYSCTRL_DFLLCTRL_ENABLE         1
+#define SAMD21_SYSCTRL_DFLLCTRL_MODE           2
+#define SAMD21_SYSCTRL_DFLLCTRL_STABLE         3
+#define SAMD21_SYSCTRL_DFLLCTRL_LLAW           4
+#define SAMD21_SYSCTRL_DFLLCTRL_USBCRM         5
+#define SAMD21_SYSCTRL_DFLLCTRL_RUNSTDBY       6
+#define SAMD21_SYSCTRL_DFLLCTRL_ONDEMAND       7
+#define SAMD21_SYSCTRL_DFLLCTRL_CCDIS          8
+#define SAMD21_SYSCTRL_DFLLCTRL_QLDIS          9
+#define SAMD21_SYSCTRL_DFLLCTRL_BPLCKC         10
+#define SAMD21_SYSCTRL_DFLLCTRL_WAITLOCK       11
+
+#define SAMD21_SYSCTRL_DFLLVAL_FINE            0
+#define SAMD21_SYSCTRL_DFLLVAL_COARSE          10
+#define SAMD21_SYSCTRL_DFLLVAL_DIFF            16
+
+#define SAMD21_SYSCTRL_DFLLMUL_MUL             0
+#define SAMD21_SYSCTRL_DFLLMUL_FSTEP           16
+#define SAMD21_SYSCTRL_DFLLMUL_CSTEP           26
+
+#define SAMD21_SYSCTRL_DFLLSYNC_READREQ                7
+
+#define SAMD21_SYSCTRL_DPLLCTRLA_ENABLE                1
+#define SAMD21_SYSCTRL_DPLLCTRLA_RUNSTDBY      6
+#define SAMD21_SYSCTRL_DPLLCTRLA_ONDEMAND      7
+
+#define SAMD21_SYSCTRL_DPLLRATIO_LDR           0
+#define SAMD21_SYSCTRL_DPLLRATIO_LDRFRAC       0
+
+#define SAMD21_SYSCTRL_DPLLCTRLB_FILTER                0
+#define  SAMD21_SYSCTRL_DPLLCTRLB_FILTER_DEFAULT       0
+#define  SAMD21_SYSCTRL_DPLLCTRLB_FILTER_LBFILT                1
+#define  SAMD21_SYSCTRL_DPLLCTRLB_FILTER_HBFILT                2
+#define  SAMD21_SYSCTRL_DPLLCTRLB_FILTER_HDFILT                3
+#define SAMD21_SYSCTRL_DPLLCTRLB_LPEN          2
+#define SAMD21_SYSCTRL_DPLLCTRLB_WUF           3
+#define SAMD21_SYSCTRL_DPLLCTRLB_REFCLK                4
+#define  SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_XOSC32                0
+#define  SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_XOSC          1
+#define  SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_GCLK_DPLL     2
+#define SAMD21_SYSCTRL_DPLLCTRLB_LTIME         8
+#define  SAMD21_SYSCTRL_DPLLCTRLB_LTIME_DEFAULT                0
+#define  SAMD21_SYSCTRL_DPLLCTRLB_LTIME_8MS            4
+#define  SAMD21_SYSCTRL_DPLLCTRLB_LTIME_9MS            5
+#define  SAMD21_SYSCTRL_DPLLCTRLB_LTIME_10MS           6
+#define  SAMD21_SYSCTRL_DPLLCTRLB_LTIME_11MS           7
+#define SAMD21_SYSCTRL_DPLLCTRLB_LBYPASS       12
+#define SAMD21_SYSCTRL_DPLLCTRLB_DIV           16
+
+#define SAMD21_SYSCTRL_DPLLSTATUS_LOCK         0
+#define SAMD21_SYSCTRL_DPLLSTATUS_CLKRDY       1
+#define SAMD21_SYSCTRL_DPLLSTATUS_ENABLE       2
+#define SAMD21_SYSCTRL_DPLLSTATUS_DIV          3
+
+struct samd21_dmac {
+       vuint16_t       ctrl;
+       vuint16_t       crcctrl;
+       vuint32_t       crcdatain;
+       vuint32_t       crcchksum;
+       vuint8_t        crcstatus;
+       vuint8_t        dbgctrl;
+       vuint8_t        qosctrl;
+       uint8_t         reserved_0f;
+
+       vuint32_t       swtrigctrl;
+       vuint32_t       prictrl0;
+       uint32_t        reserved_18;
+       uint32_t        reserved_1c;
+
+       vuint16_t       intpend;
+       uint16_t        reserved_22;
+       vuint32_t       intstatus;
+       vuint32_t       busych;
+       vuint32_t       pendch;
+
+       vuint32_t       active;
+       vuint32_t       baseaddr;
+       vuint32_t       wrbaddr;
+       uint16_t        reserved_3c;
+       uint8_t         reserved_3e;
+       vuint8_t        chid;
+
+       vuint8_t        chctrla;
+       uint8_t         reserved_41;
+       uint16_t        reserved_42;
+       vuint32_t       chctrlb;
+       uint32_t        reserved_48;
+       vuint8_t        chintenclr;
+       vuint8_t        chintenset;
+       vuint8_t        chintflag;
+       vuint8_t        chstatus;
+};
+
+extern struct samd21_dmac samd21_dmac;
+
+#define samd21_dmac (*(struct samd21_dmac *) 0x41004800)
+
+struct samd21_dmac_desc {
+       vuint16_t       btctrl;
+       vuint16_t       btcnt;
+       vuint32_t       srcaddr;
+       vuint32_t       dstaddr;
+       vuint32_t       descaddr;
+} __attribute__((aligned(8)));
+
+#define SAMD21_DMAC_NCHAN              12
+
+#define SAMD21_DMAC_CTRL_SWRST         0
+#define SAMD21_DMAC_CTRL_DMAENABLE     1
+#define SAMD21_DMAC_CTRL_CRCENABLE     2
+#define SAMD21_DMAC_CTRL_LVLEN(x)      (8 + (x))
+
+#define SAMD21_DMAC_QOSCTRL_WRBQOS     0
+#define SAMD21_DMAC_QOSCTRL_FQOS       2
+#define SAMD21_DMAC_QOSCTRL_DQOS       4
+
+#define SAMD21_DMAC_QOSCTRL_DISABLE    0
+#define SAMD21_DMAC_QOSCTRL_LOW                1
+#define SAMD21_DMAC_QOSCTRL_MEDIUM     2
+#define SAMD21_DMAC_QOSCTRL_HIGH       3
+
+#define SAMD21_DMAC_SWTRIGCTRL_SWTRIG(n)       (0 + (n))
+
+#define SAMD21_DMAC_PRICTRL0_LVLPRI0   0
+#define SAMD21_DMAC_PRICTRL0_RRLVLEN0  7
+#define SAMD21_DMAC_PRICTRL0_LVLPRI1   8
+#define SAMD21_DMAC_PRICTRL0_RRLVLEN1  15
+#define SAMD21_DMAC_PRICTRL0_LVLPRI2   16
+#define SAMD21_DMAC_PRICTRL0_RRLVLEN2  23
+#define SAMD21_DMAC_PRICTRL0_LVLPRI3   24
+#define SAMD21_DMAC_PRICTRL0_RRLVLEN3  31
+
+#define SAMD21_DMAC_INTPEND_ID         0
+#define  SAMD21_DMAC_INTPEND_ID_MASK           0xf
+#define SAMD21_DMAC_INTPEND_TERR       8
+#define SAMD21_DMAC_INTPEND_TCMPL      9
+#define SAMD21_DMAC_INTPEND_SUSP       10
+#define SAMD21_DMAC_INTPEND_FERR       13
+#define SAMD21_DMAC_INTPEND_BUSY       14
+#define SAMD21_DMAC_INTPEND_PEND       15
+
+#define SAMD21_DMAC_INTSTATUS_CHINT(n) (0 + (n))
+
+#define SAMD21_DMAC_BUSYCH_BUSYCH(n)   (0 + (n))
+
+#define SAMD21_DMAC_PENDCH_PENDCH(n)   (0 + (n))
+
+#define SAMD21_DMAC_ACTIVE_LVLEX(x)    (0 + (x))
+#define SAMD21_DMAC_ACTIVE_ID          8
+#define SAMD21_DMAC_ACTIVE_ABUSY       15
+#define SAMD21_DMAC_ACTIVE_BTCNT       16
+
+#define SAMD21_DMAC_CHCTRLA_SWRST      0
+#define SAMD21_DMAC_CHCTRLA_ENABLE     1
+
+#define SAMD21_DMAC_CHCTRLB_EVACT      0
+#define  SAMD21_DMAC_CHCTRLB_EVACT_NOACT       0
+#define  SAMD21_DMAC_CHCTRLB_EVACT_TRIG                1
+#define  SAMD21_DMAC_CHCTRLB_EVACT_CTRIG       2
+#define  SAMD21_DMAC_CHCTRLB_EVACT_CBLOCK      3
+#define  SAMD21_DMAC_CHCTRLB_EVACT_SUSPEND     4
+#define  SAMD21_DMAC_CHCTRLB_EVACT_RESUME      5
+#define  SAMD21_DMAC_CHCTRLB_EVACT_SSKIP       6
+
+#define SAMD21_DMAC_CHCTRLB_EVIE       3
+#define SAMD21_DMAC_CHCTRLB_EVOE       4
+#define SAMD21_DMAC_CHCTRLB_LVL                5
+#define SAMD21_DMAC_CHCTRLB_TRIGSRC    8
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_DISABLE   0x00
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_RX(n)      (0x01 + (n) * 2)
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_TX(n)      (0x02 + (n) * 2)
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_OVF  0x0d
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC0  0x0e
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC1  0x0f
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC2  0x10
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC3  0x11
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_OVF  0x12
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_MC0  0x13
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_MC1  0x14
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_OVF  0x15
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_MC0  0x16
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_MC1  0x17
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_OVF   0x18
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_MC0   0x19
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_MC1   0x1a
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_OVF   0x1b
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_MC0   0x1c
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_MC1   0x1d
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_OVF   0x1e
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_MC0   0x1f
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_MC1   0x20
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_OVF   0x21
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_MC0   0x22
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_MC1   0x23
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_OVF   0x24
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_MC0   0x25
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_MC1   0x26
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_ADC_RESRDY        0x27
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_DAC_EMPTY 0x28
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_RX_0  0x29
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_RX_1  0x2a
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_TX_0  0x2b
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_TX_1  0x2c
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_OVF  0x2d
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC0  0x2e
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC1  0x2f
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC2  0x30
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC3  0x31
+
+#define SAMD21_DMAC_CHCTRLB_TRIGACT    22
+#define  SAMD21_DMAC_CHCTRLB_TRIGACT_BLOCK             0
+#define  SAMD21_DMAC_CHCTRLB_TRIGACT_BEAT              2
+#define  SAMD21_DMAC_CHCTRLB_TRIGACT_TRANSACTION       3
+
+#define SAMD21_DMAC_CHCTRLB_CMD                24
+#define  SAMD21_DMAC_CHCTRLB_CMD_NOACT         0
+#define  SAMD21_DMAC_CHCTRLB_CMD_SUSPEND       1
+#define  SAMD21_DMAC_CHCTRLB_CMD_RESUME                2
+
+#define SAMD21_DMAC_CHINTFLAG_TERR     0
+#define SAMD21_DMAC_CHINTFLAG_TCMPL    1
+#define SAMD21_DMAC_CHINTFLAG_SUSP     2
+
+#define SAMD21_DMAC_CHSTATUS_PEND      0
+#define SAMD21_DMAC_CHSTATUS_BUSY      1
+#define SAMD21_DMAC_CHSTATUS_FERR      2
+
+#define SAMD21_DMAC_DESC_BTCTRL_VALID          0
+#define SAMD21_DMAC_DESC_BTCTRL_EVOSEL         1
+#define SAMD21_DMAC_DESC_BTCTRL_BLOCKACT       3
+#define SAMD21_DMAC_DESC_BTCTRL_BEATSIZE       8
+#define  SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_BYTE         0
+#define  SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_HWORD                1
+#define  SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_WORD         2
+#define SAMD21_DMAC_DESC_BTCTRL_SRCINC         10
+#define SAMD21_DMAC_DESC_BTCTRL_DSTINC         11
+#define SAMD21_DMAC_DESC_BTCTRL_STEPSEL                12
+#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE       13
+
+struct samd21_nvmctrl {
+       vuint32_t       ctrla;
+       vuint32_t       ctrlb;
+       vuint32_t       param;
+       vuint32_t       intenclr;
+
+       vuint32_t       intenset;
+       vuint32_t       intflag;
+       vuint32_t       status;
+       vuint32_t       addr;
+
+       vuint32_t       lock;
+};
+
+extern struct samd21_nvmctrl samd21_nvmctrl;
+
+#define samd21_nvmctrl (*(struct samd21_nvmctrl *) 0x41004000)
+
+#define SAMD21_NVMCTRL_CTRLA_CMD       0
+#define  SAMD21_NVMCTRL_CTRLA_CMD_ER           0x02
+#define  SAMD21_NVMCTRL_CTRLA_CMD_WP           0x04
+#define  SAMD21_NVMCTRL_CTRLA_CMD_EAR          0x05
+#define  SAMD21_NVMCTRL_CTRLA_CMD_WAP          0x06
+#define  SAMD21_NVMCTRL_CTRLA_CMD_RWWEEER      0x1a
+#define  SAMD21_NVMCTRL_CTRLA_CMD_RWEEEWP      0x1c
+#define  SAMD21_NVMCTRL_CTRLA_CMD_LR           0x40
+#define  SAMD21_NVMCTRL_CTRLA_CMD_UR           0x41
+#define  SAMD21_NVMCTRL_CTRLA_CMD_SPRM         0x42
+#define  SAMD21_NVMCTRL_CTRLA_CMD_CPRM         0x43
+#define  SAMD21_NVMCTRL_CTRLA_CMD_PBC          0x44
+#define  SAMD21_NVMCTRL_CTRLA_CMD_SSB          0x45
+#define  SAMD21_NVMCTRL_CTRLA_CMD_INVALL       0x46
+#define  SAMD21_NVMCTRL_CTRLA_CMD_LDR          0x47
+#define  SAMD21_NVMCTRL_CTRLA_CMD_UDR          0x48
+#define SAMD21_NVMCTRL_CTRLA_CMDEX     8
+#define  SAMD21_NVMCTRL_CTRLA_CMDEX_KEY                0xa5
+
+#define SAMD21_NVMCTRL_CTRLB_RWS       1
+#define SAMD21_NVMCTRL_CTRLB_MANW      7
+#define SAMD21_NVMCTRL_CTRLB_SLEEPRM   8
+#define SAMD21_NVMCTRL_CTRLB_READMODE  16
+#define SAMD21_NVMCTRL_CTRLB_CACHEDIS  18
+
+#define SAMD21_NVMCTRL_INTENCLR_READY  0
+#define SAMD21_NVMCTRL_INTENCLR_ERROR  1
+
+#define SAMD21_NVMCTRL_INTENSET_READY  0
+#define SAMD21_NVMCTRL_INTENSET_ERROR  1
+
+#define SAMD21_NVMCTRL_INTFLAG_READY   0
+#define SAMD21_NVMCTRL_INTFLAG_ERROR   1
+
+#define SAMD21_NVMCTRL_STATUS_PRM      0
+#define SAMD21_NVMCTRL_STATUS_LOAD     1
+#define SAMD21_NVMCTRL_STATUS_PROGE    2
+#define SAMD21_NVMCTRL_STATUS_LOCKE    3
+#define SAMD21_NVMCTRL_STATUS_NVME     4
+#define SAMD21_NVMCTRL_STATUS_SB       8
+
+#define SAMD21_NVMCTRL_PARAM_NVMP      0
+#define  SAMD21_NVMCTRL_PARAM_NVMP_MASK                0xffff
+#define SAMD21_NVMCTRL_PARAM_PSZ       16
+#define  SAMD21_NVMCTRL_PARAM_PSZ_MASK         0x7
+#define SAMD21_NVMCTRL_PARAM_RWWEEP    20
+#define  SAMD21_NVMCTRL_PARAM_RWWEEP_MASK      0xfff
+
+static inline uint32_t
+samd21_nvmctrl_page_size(void)
+{
+       return 1 << (3 + ((samd21_nvmctrl.param >> SAMD21_NVMCTRL_PARAM_PSZ) &
+                         SAMD21_NVMCTRL_PARAM_PSZ_MASK));
+}
+
+uint32_t
+samd21_flash_size(void);
+
+struct samd21_port {
+       vuint32_t       dir;
+       vuint32_t       dirclr;
+       vuint32_t       dirset;
+       vuint32_t       dirtgl;
+
+       vuint32_t       out;
+       vuint32_t       outclr;
+       vuint32_t       outset;
+       vuint32_t       outtgl;
+
+       vuint32_t       in;
+       vuint32_t       ctrl;
+       vuint32_t       wrconfig;
+       vuint32_t       reserved_2c;
+
+       vuint8_t        pmux[16];
+
+       vuint8_t        pincfg[32];
+};
+
+extern struct samd21_port samd21_port_a;
+extern struct samd21_port samd21_port_b;
+
+#define samd21_port_a (*(struct samd21_port *) 0x41004400)
+#define samd21_port_b (*(struct samd21_port *) 0x41004480)
+
+#define SAMD21_PORT_PINCFG_PMUXEN      0
+#define SAMD21_PORT_PINCFG_INEN                1
+#define SAMD21_PORT_PINCFG_PULLEN      2
+#define SAMD21_PORT_PINCFG_DRVSTR      6
+
+#define SAMD21_PORT_PMUX_FUNC_A                0
+#define SAMD21_PORT_PMUX_FUNC_B                1
+#define SAMD21_PORT_PMUX_FUNC_C                2
+#define SAMD21_PORT_PMUX_FUNC_D                3
+#define SAMD21_PORT_PMUX_FUNC_E                4
+#define SAMD21_PORT_PMUX_FUNC_F                5
+#define SAMD21_PORT_PMUX_FUNC_G                6
+#define SAMD21_PORT_PMUX_FUNC_H                7
+#define SAMD21_PORT_PMUX_FUNC_I                8
+
+#define SAMD21_PORT_DIR_OUT            1
+#define SAMD21_PORT_DIR_IN             0
+
+static inline void
+samd21_port_dir_set(struct samd21_port *port, uint8_t pin, uint8_t dir)
+{
+       if (dir)
+               port->dirset = (1 << pin);
+       else
+               port->dirclr = (1 << pin);
+}
+
+static inline void
+samd21_port_pincfg_set(struct samd21_port *port, uint8_t pin, uint8_t pincfg_mask, uint8_t pincfg)
+{
+       port->pincfg[pin] = (uint8_t) ((port->pincfg[pin] & ~pincfg_mask) | pincfg);
+}
+
+static inline uint8_t
+samd21_port_pincfg_get(struct samd21_port *port, uint8_t pin)
+{
+       return port->pincfg[pin];
+}
+
+static inline void
+samd21_port_pmux_set(struct samd21_port *port, uint8_t pin, uint8_t func)
+{
+       uint8_t byte = pin >> 1;
+       uint8_t bit = (pin & 1) << 2;
+       uint8_t mask = 0xf << bit;
+       uint8_t value = (uint8_t) ((port->pmux[byte] & ~mask) | (func << bit));
+       port->pmux[byte] = value;
+       samd21_port_pincfg_set(port, pin,
+                              (1 << SAMD21_PORT_PINCFG_PMUXEN),
+                              (1 << SAMD21_PORT_PINCFG_PMUXEN));
+}
+
+struct samd21_adc {
+       vuint8_t        ctrla;
+       vuint8_t        refctrl;
+       vuint8_t        avgctrl;
+       vuint8_t        sampctrl;
+       vuint16_t       ctrlb;
+       vuint16_t       reserved_06;
+       vuint8_t        winctrl;
+       vuint8_t        reserved_09;
+       vuint16_t       reserved_0a;
+       vuint8_t        swtrig;
+       vuint8_t        reserved_0d;
+       vuint16_t       reserved_0e;
+
+       vuint32_t       inputctrl;
+       vuint8_t        evctrl;
+       vuint8_t        reserved_15;
+       vuint8_t        intenclr;
+       vuint8_t        intenset;
+       vuint8_t        intflag;
+       vuint8_t        status;
+       vuint16_t       result;
+       vuint16_t       winlt;
+       vuint16_t       reserved_1e;
+
+       vuint16_t       winut;
+       vuint16_t       reserved_22;
+       vuint16_t       gaincorr;
+       vuint16_t       offsetcorr;
+       vuint16_t       calib;
+       vuint8_t        dbgctrl;
+       vuint8_t        reserved_2b;
+       vuint32_t       reserved_2c;
+};
+
+#define SAMD21_ADC_CTRLA_SWRST         0
+#define SAMD21_ADC_CTRLA_ENABLE                1
+#define SAMD21_ADC_CTRLA_RUNSTDBY      2
+
+#define SAMD21_ADC_REFCTRL_REFSEL      0
+#define  SAMD21_ADC_REFCTRL_REFSEL_INT1V       0
+#define  SAMD21_ADC_REFCTRL_REFSEL_INTVCC0     1
+#define  SAMD21_ADC_REFCTRL_REFSEL_INTVCC1     2
+#define  SAMD21_ADC_REFCTRL_REFSEL_VREFA       3
+#define  SAMD21_ADC_REFCTRL_REFSEL_VREFB       4
+#define SAMD21_ADC_REFCTRL_REFCOMP     7
+
+#define SAMD21_ADC_AVGCTRL_SAMPLENUM   0
+#define SAMD21_ADC_AVGCTRL_ADJRES      4
+
+#define SAMD21_ADC_SAMPCTRL_SAMPLEN    0
+
+#define SAMD21_ADC_CTRLB_DIFFMODE      0
+#define SAMD21_ADC_CTRLB_LEFTADJ       1
+#define SAMD21_ADC_CTRLB_FREERUN       2
+#define SAMD21_ADC_CTRLB_CORREN                3
+#define SAMD21_ADC_CTRLB_RESSEL                4
+#define  SAMD21_ADC_CTRLB_RESSEL_12BIT         0
+#define  SAMD21_ADC_CTRLB_RESSEL_16BIT         1
+#define  SAMD21_ADC_CTRLB_RESSEL_10BIT         2
+#define  SAMD21_ADC_CTRLB_RESSEL_8BIT          3
+#define SAMD21_ADC_CTRLB_PRESCALER     8
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV4       0
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV8       1
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV16      2
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV32      3
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV64      4
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV128     5
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV256     6
+#define  SAMD21_ADC_CTRLB_PRESCALER_DIV512     7
+
+#define SAMD21_ADC_SWTRIG_FLUSH                0
+#define SAMD21_ADC_SWTRIG_START                1
+
+#define SAMD21_ADC_INPUTCTRL_MUXPOS            0
+# define SAMD21_ADC_INPUTCTRL_MUXPOS_BANDGAP           0x19
+# define SAMD21_ADC_INPUTCTRL_MUXPOS_SCALEDCOREVCC     0x1a
+# define SAMD21_ADC_INPUTCTRL_MUXPOS_SCALEDIOVCC       0x1b
+# define SAMD21_ADC_INPUTCTRL_MUXPOS_DAC               0x1c
+#define SAMD21_ADC_INPUTCTRL_MUXNEG            8
+# define SAMD21_ADC_INPUTCTRL_MUXNEG_GND               0x18
+# define SAMD21_ADC_INPUTCTRL_MUXNEG_IOGND             0x19
+#define SAMD21_ADC_INPUTCTRL_INPUTSCAN         16
+#define SAMD21_ADC_INPUTCTRL_INPUTOFFSET       20
+#define SAMD21_ADC_INPUTCTRL_GAIN              24
+#define  SAMD21_ADC_INPUTCTRL_GAIN_1X                  0
+#define  SAMD21_ADC_INPUTCTRL_GAIN_DIV2                        0xf
+
+#define SAMD21_ADC_INTFLAG_RESRDY      0
+#define SAMD21_ADC_INTFLAG_OVERRUN     1
+#define SAMD21_ADC_INTFLAG_WINMON      2
+#define SAMD21_ADC_INTFLAG_SYNCRDY     3
+
+#define SAMD21_ADC_STATUS_SYNCBUSY     7
+
+#define SAMD21_ADC_CALIB_LINEARITY_CAL 0
+#define SAMD21_ADC_CALIB_BIAS_CAL      16
+
+extern struct samd21_adc samd21_adc;
+
+#define samd21_adc (*(struct samd21_adc *) 0x42004000)
+
+struct samd21_dac {
+       vuint8_t        ctrla;
+       vuint8_t        ctrlb;
+       vuint8_t        evctrl;
+       uint8_t         reserved_03;
+
+       vuint8_t        intenclr;
+       vuint8_t        intenset;
+       vuint8_t        intflag;
+       vuint8_t        status;
+
+       vuint16_t       data;
+       uint16_t        reserved_0a;
+
+       vuint16_t       databuf;
+};
+
+#define SAMD21_DAC_CTRLA_SWRST         0
+#define SAMD21_DAC_CTRLA_ENABLE                1
+#define SAMD21_DAC_CTRLA_RUNSTDBY      2
+
+#define SAMD21_DAC_CTRLB_EOEN          0
+#define SAMD21_DAC_CTRLB_IOEN          1
+#define SAMD21_DAC_CTRLB_LEFTADJ       2
+#define SAMD21_DAC_CTRLB_VPD           3
+#define SAMD21_DAC_CTRLB_BDWP          4
+#define SAMD21_DAC_CTRLB_REFSEL                6
+#define  SAMD21_DAC_CTRLB_REFSEL_INTREF                0
+#define  SAMD21_DAC_CTRLB_REFSEL_VDDANA                1
+#define  SAMD21_DAC_CTRLB_REFSEL_VREFA         2
+#define  SAMD21_DAC_CTRLB_REFSEL_MASK          3
+
+#define SAMD21_DAC_EVCTRL_STARTEI      0
+#define SAMD21_DAC_EVCTRL_EMPTYEO      1
+
+#define SAMD21_DAC_INTENCLR_UNDERRUN   0
+#define SAMD21_DAC_INTENCLR_EMPTY      1
+#define SAMD21_DAC_INTENCLR_SYNCRDY    2
+
+#define SAMD21_DAC_INTENSET_UNDERRUN   0
+#define SAMD21_DAC_INTENSET_EMPTY      1
+#define SAMD21_DAC_INTENSET_SYNCRDY    2
+
+#define SAMD21_DAC_INTFLAG_UNDERRUN    0
+#define SAMD21_DAC_INTFLAG_EMPTY       1
+#define SAMD21_DAC_INTFLAG_SYNCRDY     2
+
+#define SAMD21_DAC_STATUS_SYNCBUSY     7
+
+extern struct samd21_dac samd21_dac;
+#define samd21_dac (*(struct samd21_dac *) 0x42004800)
+
+/* TC */
+struct samd21_tc {
+       vuint16_t       ctrla;
+       vuint16_t       readreq;
+       vuint8_t        ctrlbclr;
+       vuint8_t        ctrlbset;
+       vuint8_t        ctrlc;
+       vuint8_t        reserved_07;
+       vuint8_t        dbgctrl;
+       vuint8_t        reserved_09;
+       vuint16_t       evctrl;
+       vuint8_t        intenclr;
+       vuint8_t        intenset;
+       vuint8_t        intflag;
+       vuint8_t        status;
+
+       union {
+               struct {
+                       vuint8_t        count;
+                       vuint8_t        reserved_11;
+                       vuint16_t       reserved_12;
+                       vuint8_t        per;
+                       vuint8_t        reserved_15;
+                       vuint16_t       reserved_16;
+                       vuint8_t        cc[2];
+               } mode_8;
+               struct {
+                       vuint16_t       count;
+                       vuint16_t       reserved_12;
+                       vuint32_t       reserved_14;
+                       vuint16_t       cc[2];
+               } mode_16;
+               struct {
+                       vuint32_t       count;
+                       vuint32_t       reserved_14;
+                       vuint32_t       cc[2];
+               } mode_32;
+       };
+};
+
+extern struct samd21_tc samd21_tc3;
+#define samd21_tc3 (*(struct samd21_tc *) 0x42002c00)
+
+extern struct samd21_tc samd21_tc4;
+#define samd21_tc4 (*(struct samd21_tc *) 0x42003000)
+
+extern struct samd21_tc samd21_tc5;
+#define samd21_tc5 (*(struct samd21_tc *) 0x42003400)
+
+#ifdef ATSAMD21J
+/* Present on all of the samd21j parts and the samd21g16l */
+extern struct samd21_tc samd21_tc6;
+#define samd21_tc6 (*(struct samd21_tc *) 0x42003800)
+
+extern struct samd21_tc samd21_tc7;
+#define samd21_tc7 (*(struct samd21_tc *) 0x42003c00)
+#endif
+
+#define SAMD21_TC_CTRLA_SWRST          0
+#define SAMD21_TC_CTRLA_ENABLE         1
+#define SAMD21_TC_CTRLA_MODE           2
+#define  SAMD21_TC_CTRLA_MODE_COUNT16          0
+#define  SAMD21_TC_CTRLA_MODE_COUNT8           1
+#define  SAMD21_TC_CTRLA_MODE_COUNT32          2
+#define SAMD21_TC_CTRLA_WAVEGEN                5
+#define  SAMD21_TC_CTRLA_WAVEGEN_NFRQ          0
+#define  SAMD21_TC_CTRLA_WAVEGEN_MFRQ          1
+#define  SAMD21_TC_CTRLA_WAVEGEN_NPWM          2
+#define  SAMD21_TC_CTRLA_WAVEGEN_MPWM          3
+#define SAMD21_TC_CTRLA_PRESCALER      8
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV1                0
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV2                1
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV4                2
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV8                3
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV16       4
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV64       5
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV256      6
+#define  SAMD21_TC_CTRLA_PRESCALER_DIV1024     7
+#define SAMD21_TC_CTRLA_RUNSTDBY       11
+#define SAMD21_TC_CTRLA_PRESCSYNC      12
+#define  SAMD21_TC_CTRLA_PRESCSYNC_GCLK                0
+#define  SAMD21_TC_CTRLA_PRESCSYNC_PRSEC       1
+#define  SAMD21_TC_CTRLA_PRESCSYNC_RESYNC      2
+
+#define SAMD21_TC_READREQ_ADDR         0
+#define SAMD21_TC_READREQ_RCONT                14
+#define SAMD21_TC_READREQ_RREQ         15
+#define SAMD21_TC_CTRLB_DIR            0
+#define SAMD21_TC_CTRLB_ONESHOT                2
+#define SAMD21_TC_CTRLB_CMD            6
+#define SAMD21_TC_CTRLC_INVEN(x)       (0 + (x))
+#define SAMD21_TC_CTRLC_CPTEN(x)       (4 + (x))
+#define SAMD21_TC_DBGCTRL_DBGRUN       0
+#define SAMD21_TC_EVCTRL_EVACT         0
+#define SAMD21_TC_EVCTRL_TCINV         4
+#define SAMD21_TC_EVCTRL_TCEI          5
+#define SAMD21_TC_EVCTRL_OVFEO         8
+#define SAMD21_TC_EVCTRL_MCEO(x)       (12 + (x))
+
+#define SAMD21_TC_INTFLAG_MC(x)                (4 + (x))
+#define SAMD21_TC_INTFLAG_SYNCRDY      3
+#define SAMD21_TC_INTFLAG_ERR          1
+#define SAMD21_TC_INTFLAG_OVF          0
+
+#define SAMD21_TC_STATUS_STOP          3
+#define SAMD21_TC_STATUS_FOLLOWER      4
+#define SAMD21_TC_STATUS_SYNCBUSY      7
+
+/* TCC */
+
+struct samd21_tcc {
+       vuint32_t       ctrla;
+       vuint8_t        ctrlbclr;
+       vuint8_t        ctrlbset;
+       vuint16_t       reserved_06;
+       vuint32_t       syncbusy;
+       vuint32_t       fctrla;
+
+       vuint32_t       fctlrb;
+       vuint32_t       wexctrl;
+       vuint32_t       drvctrl;
+       vuint16_t       reserved_1c;
+       vuint8_t        dbgctrl;
+       vuint8_t        reserved_1f;
+
+       vuint32_t       evctrl;
+       vuint32_t       intenclr;
+       vuint32_t       intenset;
+       vuint32_t       intflag;
+
+       vuint32_t       status;
+       vuint32_t       count;
+       vuint16_t       patt;
+       vuint16_t       reserved_3a;
+       vuint32_t       wave;
+
+       vuint32_t       per;
+       vuint32_t       cc[4];
+       vuint32_t       reserved_54;
+       vuint32_t       reserved_58;
+       vuint32_t       reserved_5c;
+
+       vuint32_t       reserved_60;
+       vuint16_t       pattb;
+       vuint16_t       reserved_66;
+       vuint32_t       waveb;
+       vuint32_t       perb;
+
+       vuint32_t       ccb[4];
+};
+
+extern struct samd21_tcc samd21_tcc0;
+#define samd21_tcc0 (*(struct samd21_tcc *) 0x42002000)
+
+extern struct samd21_tcc samd21_tcc1;
+#define samd21_tcc1 (*(struct samd21_tcc *) 0x42002400)
+
+extern struct samd21_tcc samd21_tcc2;
+#define samd21_tcc2 (*(struct samd21_tcc *) 0x42002800)
+
+#ifdef SAMD21E17D
+/* only on the samd21e17d */
+extern struct samd21_tcc samd21_tcc3;
+#define samd21_tcc3 (*(struct samd21_tcc *) 0x42006000)
+#endif
+
+#define SAMD21_TCC_CTRLA_SWRST         0
+#define SAMD21_TCC_CTRLA_ENABLE                1
+#define SAMD21_TCC_CTRLA_RESOLUTION    5
+#define  SAMD21_TCC_CTRLA_RESOLUTION_NONE      0
+#define  SAMD21_TCC_CTRLA_RESOLUTION_DITH4     1
+#define  SAMD21_TCC_CTRLA_RESOLUTION_DITH5     2
+#define  SAMD21_TCC_CTRLA_RESOLUTION_DITH6     3
+#define SAMD21_TCC_CTRLA_PRESCALER     8
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV1       0
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV2       1
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV4       2
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV8       3
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV16      4
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV64      5
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV256     6
+#define  SAMD21_TCC_CTRLA_PRESCALER_DIV1024    7
+#define SAMD21_TCC_CTRLA_RUNSTDBY      11
+#define SAMD21_TCC_CTRLA_PRESYNC       12
+#define  SAMD21_TCC_CTRLA_PRESYNC_GCLK         0
+#define  SAMD21_TCC_CTRLA_PRESYNC_PRESC                1
+#define  SAMD21_TCC_CTRLA_PRESYNC_RESYNC       2
+#define SAMD21_TCC_CTRLA_ALOCK         14
+#define SAMD21_TCC_CTRLA_CPTEN(n)      (24 + (n))
+
+#define SAMD21_TCC_CTRLB_DIR           0
+#define SAMD21_TCC_CTRLB_LUPD          1
+#define SAMD21_TCC_CTRLB_ONESHOT       2
+#define SAMD21_TCC_CTRLB_IDXCMD                3
+#define  SAMD21_TCC_CTRLB_IDXCMD_DISABLE       0
+#define  SAMD21_TCC_CTRLB_IDXCMD_SET           1
+#define  SAMD21_TCC_CTRLB_IDXCMD_CLEAR         2
+#define  SAMD21_TCC_CTRLB_IDXCMD_HOLD          3
+#define SAMD21_TCC_CTRLB_CMD           5
+#define  SAMD21_TCC_CTRLB_CMD_NONE             0
+#define  SAMD21_TCC_CTRLB_CMD_RETRIGGER                1
+#define  SAMD21_TCC_CTRLB_CMD_STOP             2
+#define  SAMD21_TCC_CTRLB_CMD_UPDATE           3
+#define  SAMD21_TCC_CTRLB_CMD_READSYNC         4
+#define  SAMD21_TCC_CTRLB_CMD_DMAOS            5
+
+#define SAMD21_TCC_SYNCBUSY_SWRST      0
+#define SAMD21_TCC_SYNCBUSY_ENABLE     1
+#define SAMD21_TCC_SYNCBUSY_CTRLB      2
+#define SAMD21_TCC_SYNCBUSY_STATUS     3
+#define SAMD21_TCC_SYNCBUSY_COUNT      4
+#define SAMD21_TCC_SYNCBUSY_PATT       5
+#define SAMD21_TCC_SYNCBUSY_WAVE       6
+#define SAMD21_TCC_SYNCBUSY_PER                7
+#define SAMD21_TCC_SYNCBUSY_CC(x)      (8 + (x))
+#define SAMD21_TCC_SYNCBUSY_PATTB      16
+#define SAMD21_TCC_SYNCBUSY_WAVEB      17
+#define SAMD21_TCC_SYNCBUSY_PERB       18
+#define SAMD21_TCC_SYNCBUSY_CCB(x)     ((19 + (x))
+
+#define SAMD21_TCC_DBGCTRL_FDDBD       2
+#define SAMD21_TCC_DBGCTRL_DBGRUN      0
+
+#define SAMD21_TCC_EVCTRL_EVACTO       0
+#define SAMD21_TCC_EVCTRL_EVACT1       3
+#define SAMD21_TCC_EVCTRL_CNTSEL       6
+#define SAMD21_TCC_EVCTRL_OVFEO                8
+#define SAMD21_TCC_EVCTRL_TRGEO                9
+#define SAMD21_TCC_EVCTRL_CNTEO                10
+#define SAMD21_TCC_EVCTRL_TCINV(x)     (12 + (x))
+#define SAMD21_TCC_EVCTRL_MCEI(x)      (16 + (x))
+#define SAMD21_TCC_EVCTRL_MCEO(x)      (24 + (x))
+
+#define SAMD21_TCC_INTFLAG_OVF         0
+#define SAMD21_TCC_INTFLAG_TRG         1
+#define SAMD21_TCC_INTFLAG_CNT         2
+#define SAMD21_TCC_INTFLAG_ERR         3
+#define SAMD21_TCC_INTFLAG_UFS         10
+#define SAMD21_TCC_INTFLAG_DFS         11
+#define SAMD21_TCC_INTFLAG_FAULTA      12
+#define SAMD21_TCC_INTFLAG_FAULTB      13
+#define SAMD21_TCC_INTFLAG_FAULT0      14
+#define SAMD21_TCC_INTFLAG_FAULT1      15
+#define SAMD21_TCC_INTFLAG_MC(x)       (16 + (x))
+
+#define SAMD21_TCC_WAVE_WAVEGEN                0
+#define  SAMD21_TCC_WAVE_WAVEGEN_NFRQ          0
+#define  SAMD21_TCC_WAVE_WAVEGEN_MFRQ          1
+#define  SAMD21_TCC_WAVE_WAVEGEN_NPWM          2
+#define  SAMD21_TCC_WAVE_WAVEGEN_DSCRITICAL    4
+#define  SAMD21_TCC_WAVE_WAVEGEN_DSBOTTOM      5
+#define  SAMD21_TCC_WAVE_WAVEGEN_DSBOTH                6
+#define  SAMD21_TCC_WAVE_WAVEGEN_DSTOP         7
+#define SAMD21_TCC_WAVE_RAMP           4
+#define SAMD21_TCC_WAVE_CIPEREN                7
+#define SAMD21_TCC_WAVE_CCCEN(x)       (8 + (x))
+#define SAMD21_TCC_WAVE_POL(x)         (16 + (x))
+#define SAMD21_TCC_WAVE_SWAP(x)                (24 + (x))
+
+/* USB */
+
+struct samd21_usb {
+       vuint8_t        ctrla;
+       vuint8_t        reserved_01;
+       vuint8_t        syncbusy;
+       vuint8_t        qosctrl;
+
+       vuint32_t       reserved_04;
+       vuint16_t       ctrlb;
+       vuint8_t        dadd;
+       vuint8_t        reserved_0b;
+       vuint8_t        status;
+       vuint8_t        fsmstatus;
+       vuint16_t       reserved_0e;
+
+       vuint16_t       fnum;
+       vuint16_t       reserved_12;
+       vuint16_t       intenclr;
+       vuint16_t       reserved_16;
+       vuint16_t       intenset;
+       vuint16_t       reserved_1a;
+       vuint16_t       intflag;
+       vuint16_t       reserved_1e;
+
+       vuint16_t       epintsmry;
+       vuint16_t       reserved_22;
+
+       vuint32_t       descadd;
+       vuint16_t       padcal;
+       uint8_t         reserved_2a[0x100 - 0x2a];
+
+       struct {
+               vuint8_t        epcfg;
+               vuint8_t        reserved_01;
+               vuint8_t        reserved_02;
+               vuint8_t        binterval;
+               vuint8_t        epstatusclr;
+               vuint8_t        epstatusset;
+               vuint8_t        epstatus;
+               vuint8_t        epintflag;
+               vuint8_t        epintenclr;
+               vuint8_t        epintenset;
+               vuint8_t        reserved_0a[0x20 - 0x0a];
+       } ep[8];
+};
+
+extern struct samd21_usb samd21_usb;
+
+#define samd21_usb (*(struct samd21_usb *) 0x41005000)
+
+#define SAMD21_USB_CTRLA_SWRST         0
+#define SAMD21_USB_CTRLA_ENABLE                1
+#define SAMD21_USB_CTRLA_RUNSTDBY      2
+#define SAMD21_USB_CTRLA_MODE          7
+
+#define SAMD21_USB_SYNCBUSY_SWRST      0
+#define SAMD21_USB_SYNCBUSY_ENABLE     1
+
+#define SAMD21_USB_QOSCTRL_CQOS                0
+#define SAMD21_USB_QOSCTRL_DQOS                2
+
+#define SAMD21_USB_CTRLB_DETACH                0
+#define SAMD21_USB_CTRLB_UPRSM         1
+#define SAMD21_USB_CTRLB_SPDCONF       2
+#define  SAMD21_USB_CTRLB_SPDCONF_FS           0
+#define  SAMD21_USB_CTRLB_SPDCONF_LS           1
+#define  SAMD21_USB_CTRLB_SPDCONF_MASK         0x3
+#define SAMD21_USB_CTRLB_NREPLY                4
+#define SAMD21_USB_CTRLB_GNAK          9
+#define SAMD21_USB_CTRLB_LPMHDSK       10
+#define  SAMD21_USB_CTRLB_LPMHDSK_NONE         0
+#define  SAMD21_USB_CTRLB_LPMHDSK_ACK          1
+#define  SAMD21_USB_CTRLB_LPMHDSK_NYET         2
+#define  SAMD21_USB_CTRLB_LPMHDSK_MASK         3
+
+#define SAMD21_USB_DADD_DADD           0
+#define SAMD21_USB_DADD_ADDEN          7
+
+#define SAMD21_USB_STATUS_SPEED                2
+#define SAMD21_USB_STATUS_LINESTATE    6
+#define SAMD21_USB_FNUM_MFNUM          0
+#define SAMD21_USB_FNUM_FNUM           3
+#define SAMD21_USB_FNUM_FNCERR         15
+#define SAMD21_USB_INTFLAG_SUSPEND     0
+#define SAMD21_USB_INTFLAG_SOF         2
+#define SAMD21_USB_INTFLAG_EORST       3
+#define SAMD21_USB_INTFLAG_WAKEUP      4
+#define SAMD21_USB_INTFLAG_EORSM       5
+#define SAMD21_USB_INTFLAG_UPRSM       6
+#define SAMD21_USB_INTFLAG_RAMACER     7
+#define SAMD21_USB_INTFLAG_LPMNYET     8
+#define SAMD21_USB_INTFLAG_LPMSUSP     9
+
+#define SAMD21_USB_PADCAL_TRANSP       0
+#define SAMD21_USB_PADCAL_TRANSN       6
+#define SAMD21_USB_PADCAL_TRIM         12
+
+#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT                0
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DISABLED      0
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_CONTROL       1
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_ISOCHRONOUS   2
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_BULK          3
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_INTERRUPT     4
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DUAL_BANK     5
+#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN         4
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_IN_DISABLED       0
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_IN_CONTROL        1
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_IN_ISOCHRONOUS    2
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_IN_BULK           3
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_IN_INTERRUPT      4
+#define  SAMD21_USB_EP_EPCFG_EP_TYPE_IN_DUAL_BANK      5
+
+#define SAMD21_USB_EP_EPSTATUS_DTGLOUT                 0
+#define SAMD21_USB_EP_EPSTATUS_DTGLIN                  1
+#define SAMD21_USB_EP_EPSTATUS_CURBK                   2
+#define SAMD21_USB_EP_EPSTATUS_STALLRQ0                        4
+#define SAMD21_USB_EP_EPSTATUS_STALLRQ1                        5
+#define SAMD21_USB_EP_EPSTATUS_BK0RDY                  6
+#define SAMD21_USB_EP_EPSTATUS_BK1RDY                  7
+
+#define SAMD21_USB_EP_EPINTFLAG_TRCPT0                 0
+#define SAMD21_USB_EP_EPINTFLAG_TRCPT1                 1
+#define SAMD21_USB_EP_EPINTFLAG_TRFAIL0                        2
+#define SAMD21_USB_EP_EPINTFLAG_TRFAIL1                        3
+#define SAMD21_USB_EP_EPINTFLAG_RXSTP                  4
+#define SAMD21_USB_EP_EPINTFLAG_STALL                  5
+
+struct samd21_usb_desc_bank {
+       vuint32_t       addr;
+       vuint32_t       pcksize;
+       vuint16_t       extreg;
+       vuint8_t        status_bk;
+       vuint8_t        reserved_0b;
+       vuint32_t       reserved_0c;
+};
+
+struct samd21_usb_desc {
+       struct samd21_usb_desc_bank bank[2];
+};
+
+extern struct samd21_usb_desc  samd21_usb_desc[8];
+
+#define SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT             0
+#define  SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT_MASK               0x3fffU
+#define SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE      14
+#define  SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE_MASK                0x3fffU
+#define SAMD21_USB_DESC_PCKSIZE_SIZE                   28
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_8                                0
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_16                       1
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_32                       2
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_64                       3
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_128                      4
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_256                      5
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_512                      6
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_1023                     7
+#define  SAMD21_USB_DESC_PCKSIZE_SIZE_MASK                     7U
+#define SAMD21_USB_DESC_PCKSIZE_AUTO_ZLP               31
+
+static inline uint16_t
+samd21_usb_desc_get_byte_count(uint8_t ep, uint8_t bank)
+{
+       return ((samd21_usb_desc[ep].bank[bank].pcksize >> SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT) &
+               SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT_MASK);
+}
+
+static inline void
+samd21_usb_desc_set_byte_count(uint8_t ep, uint8_t bank, uint32_t count)
+{
+       uint32_t pcksize = samd21_usb_desc[ep].bank[bank].pcksize;
+
+       pcksize &= ~(SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT_MASK << SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT);
+       pcksize &= ~(SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE_MASK << SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE);
+       pcksize |= (count << SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT);
+       samd21_usb_desc[ep].bank[bank].pcksize = pcksize;
+}
+
+static inline void
+samd21_usb_desc_set_size(uint8_t ep, uint8_t bank, uint32_t size)
+{
+       uint32_t pcksize = samd21_usb_desc[ep].bank[bank].pcksize;
+
+       pcksize &= ~(SAMD21_USB_DESC_PCKSIZE_SIZE_MASK << SAMD21_USB_DESC_PCKSIZE_SIZE);
+
+       uint32_t size_bits = 0;
+       switch (size) {
+       case 8: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_8; break;
+       case 16: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_16; break;
+       case 32: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_32; break;
+       case 64: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_64; break;
+       case 128: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_128; break;
+       case 256: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_256; break;
+       case 512: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_512; break;
+       case 1023: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_1023; break;
+       }
+       pcksize |= (size_bits << SAMD21_USB_DESC_PCKSIZE_SIZE);
+       samd21_usb_desc[ep].bank[bank].pcksize = pcksize;
+}
+
+static inline void
+samd21_usb_ep_set_ready(uint8_t ep, uint8_t bank)
+{
+       samd21_usb.ep[ep].epstatusset = (1 << (SAMD21_USB_EP_EPSTATUS_BK0RDY + bank));
+       samd21_usb.ep[ep].epintflag = (1 << (SAMD21_USB_EP_EPINTFLAG_TRFAIL0 + bank));
+}
+
+static inline void
+samd21_usb_ep_clr_ready(uint8_t ep, uint8_t bank)
+{
+       samd21_usb.ep[ep].epstatusclr = (1 << (SAMD21_USB_EP_EPSTATUS_BK0RDY + bank));
+}
+
+static inline uint8_t
+samd21_usb_ep_ready(uint8_t ep)
+{
+       return (samd21_usb.ep[ep].epstatus >> SAMD21_USB_EP_EPSTATUS_BK0RDY) & 3;
+}
+
+static inline uint8_t
+samd21_usb_ep_curbk(uint8_t ep)
+{
+       return (samd21_usb.ep[ep].epstatus >> SAMD21_USB_EP_EPSTATUS_CURBK) & 1;
+}
+
+/* sercom */
+
+struct samd21_sercom {
+       vuint32_t       ctrla;
+       vuint32_t       ctrlb;
+       vuint32_t       reserved_08;
+       vuint16_t       baud;
+       vuint8_t        rxpl;
+       vuint8_t        reserved_0f;
+
+       vuint32_t       reserved_10;
+       vuint8_t        intenclr;
+       vuint8_t        reserved_15;
+       vuint8_t        intenset;
+       vuint8_t        reserved_17;
+       vuint8_t        intflag;
+       vuint8_t        reserved_19;
+       vuint16_t       status;
+       vuint32_t       syncbusy;
+
+       vuint32_t       reserved_20;
+       vuint32_t       addr;
+       vuint16_t       data;
+       vuint16_t       reserved_2a;
+       vuint32_t       reserved_2c;
+
+       vuint8_t        dbgctrl;
+       vuint8_t        reserved_31;
+       vuint16_t       reserved_32;
+       vuint16_t       fifospace;
+       vuint16_t       fifoptr;
+};
+
+extern struct samd21_sercom samd21_sercom0;
+extern struct samd21_sercom samd21_sercom1;
+extern struct samd21_sercom samd21_sercom2;
+extern struct samd21_sercom samd21_sercom3;
+extern struct samd21_sercom samd21_sercom4;
+extern struct samd21_sercom samd21_sercom5;
+
+#define samd21_sercom0 (*(struct samd21_sercom *) 0x42000800)
+#define samd21_sercom1 (*(struct samd21_sercom *) 0x42000c00)
+#define samd21_sercom2 (*(struct samd21_sercom *) 0x42001000)
+#define samd21_sercom3 (*(struct samd21_sercom *) 0x42001400)
+#define samd21_sercom4 (*(struct samd21_sercom *) 0x42001800)
+#define samd21_sercom5 (*(struct samd21_sercom *) 0x42001c00)
+
+#define SAMD21_SERCOM_CTRLA_SWRST      0
+#define SAMD21_SERCOM_CTRLA_ENABLE     1
+#define SAMD21_SERCOM_CTRLA_MODE       2
+# define SAMD21_SERCOM_CTRLA_MODE_USART                1
+# define SAMD21_SERCOM_CTRLA_MODE_I2C_LEADER   5
+
+#define SAMD21_SERCOM_CTRLA_RUNSTDBY   7
+
+/* USART mode */
+#define SAMD21_SERCOM_CTRLA_IBON       8
+#define SAMD21_SERCOM_CTRLA_SAMPR      13
+#define SAMD21_SERCOM_CTRLA_TXPO       16
+#define SAMD21_SERCOM_CTRLA_RXPO       20
+#define SAMD21_SERCOM_CTRLA_SAMPA      22
+#define SAMD21_SERCOM_CTRLA_FORM       24
+#define SAMD21_SERCOM_CTRLA_CMODE      28
+#define SAMD21_SERCOM_CTRLA_CPOL       29
+#define SAMD21_SERCOM_CTRLA_DORD       30
+
+/* I2C controller mode */
+#define SAMD21_SERCOM_CTRLA_PINOUT     16
+#define SAMD21_SERCOM_CTRLA_SDAHOLD    20
+#define  SAMD21_SERCOM_CTRLA_SDAHOLD_DIS       0
+#define  SAMD21_SERCOM_CTRLA_SDAHOLD_75NS      1
+#define  SAMD21_SERCOM_CTRLA_SDAHOLD_450NS     2
+#define  SAMD21_SERCOM_CTRLA_SDAHOLD_600NS     3
+#define SAMD21_SERCOM_CTRLA_MEXTTOEN   22
+#define SAMD21_SERCOM_CTRLA_SEXTTOEN   23
+#define SAMD21_SERCOM_CTRLA_SPEED      24
+#define  SAMD21_SERCOM_CTRLA_SPEED_STANDARD    0
+#define  SAMD21_SERCOM_CTRLA_SPEED_FAST                1
+#define  SAMD21_SERCOM_CTRLA_SPEED_HIGH                2
+#define SAMD21_SERCOM_CTRLA_SCLSM      27
+#define SAMD21_SERCOM_CTRLA_INACTOUT   28
+#define  SAMD21_SERCOM_CTRLA_INACTOUT_DIS      0
+#define  SAMD21_SERCOM_CTRLA_INACTOUT_55US     1
+#define  SAMD21_SERCOM_CTRLA_INACTOUT_105US    2
+#define  SAMD21_SERCOM_CTRLA_INACTOUT_205US    3
+#define SAMD21_SERCOM_CTRLA_LOWTOUT    30
+
+/* USART mode */
+#define SAMD21_SERCOM_CTRLB_CHSIZE     0
+#define SAMD21_SERCOM_CTRLB_SBMODE     6
+#define SAMD21_SERCOM_CTRLB_COLDEN     8
+#define SAMD21_SERCOM_CTRLB_SFDE       9
+#define SAMD21_SERCOM_CTRLB_ENC                10
+#define SAMD21_SERCOM_CTRLB_PMODE      13
+#define SAMD21_SERCOM_CTRLB_TXEN       16
+#define SAMD21_SERCOM_CTRLB_RXEN       17
+#define SAMD21_SERCOM_CTRLB_FIFOCLR    22
+
+/* I2C mode */
+#define SAMD21_SERCOM_CTRLB_SMEN       8
+#define SAMD21_SERCOM_CTRLB_QCEN       9
+#define SAMD21_SERCOM_CTRLB_CMD                16
+#define  SAMD21_SERCOM_CTRLB_CMD_NOP           0
+#define  SAMD21_SERCOM_CTRLB_CMD_START         1
+#define  SAMD21_SERCOM_CTRLB_CMD_READ          2
+#define  SAMD21_SERCOM_CTRLB_CMD_STOP          3
+#define SAMD21_SERCOM_CTRLB_ACKACT     18
+#define  SAMD21_SERCOM_CTRLB_ACKACT_ACK                0
+#define  SAMD21_SERCOM_CTRLB_ACKACT_NACK       1
+#define SAMD21_SERCOM_CTRLB_FIFOCLR    22
+
+/* USART mode */
+#define SAMD21_SERCOM_INTFLAG_DRE      0
+#define SAMD21_SERCOM_INTFLAG_TXC      1
+#define SAMD21_SERCOM_INTFLAG_RXC      2
+#define SAMD21_SERCOM_INTFLAG_RXS      3
+#define SAMD21_SERCOM_INTFLAG_CTSIC    4
+#define SAMD21_SERCOM_INTFLAG_RXBRK    5
+#define SAMD21_SERCOM_INTFLAG_ERROR    7
+
+/* I2C mode */
+#define SAMD21_SERCOM_INTFLAG_ERROR    7
+#define SAMD21_SERCOM_INTFLAG_RXFF     4
+#define SAMD21_SERCOM_INTFLAG_TXFE     3
+#define SAMD21_SERCOM_INTFLAG_SB       1
+#define SAMD21_SERCOM_INTFLAG_MB       0
+
+#define SAMD21_SERCOM_INTENCLR_DRE     0
+#define SAMD21_SERCOM_INTENCLR_TXC     1
+#define SAMD21_SERCOM_INTENCLR_RXC     2
+#define SAMD21_SERCOM_INTENCLR_RXS     3
+#define SAMD21_SERCOM_INTENCLR_CTSIC   4
+#define SAMD21_SERCOM_INTENCLR_RXBRK   5
+#define SAMD21_SERCOM_INTENCLR_ERROR   7
+
+#define SAMD21_SERCOM_STATUS_PERR      0
+#define SAMD21_SERCOM_STATUS_FERR      1
+#define SAMD21_SERCOM_STATUS_BUFOVF    2
+#define SAMD21_SERCOM_STATUS_CTS               3
+#define SAMD21_SERCOM_STATUS_ISF               4
+#define SAMD21_SERCOM_STATUS_COLL      5
+#define SAMD21_SERCOM_STATUS_TXE               6
+
+#define SAMD21_SERCOM_SYNCBUSY_SWRST   0
+#define SAMD21_SERCOM_SYNCBUSY_ENABLE  1
+#define SAMD21_SERCOM_SYNCBUSY_CTRLB   2
+#define SAMD21_SERCOM_SYNCBUSY_SYSOP   2
+
+#define SAMD21_SERCOM_ADDR_ADDR                0
+#define SAMD21_SERCOM_ADDR_LENEN       13
+#define SAMD21_SERCOM_ADDR_HS          14
+#define SAMD21_SERCOM_ADDR_TENBITEN    15
+#define SAMD21_SERCOM_ADDR_LEN         16
+
+#define SAMD21_SERCOM_DBGCTRL_DBGSTOP  0
+
+#define SAMD21_SERCOM_FIFOSPACE_TXSPACE        0
+#define  SAMD21_SERCOM_FIFOSPACE_TXSPACE_MASK  0x1f
+#define SAMD21_SERCOM_FIFOSPACE_RXSPACE        8
+#define  SAMD21_SERCOM_FIFOSPACE_RXSPACE_MASK  0x1f
+
+#define SAMD21_SERCOM_FIFOPTR_CPUWRPTR 0
+#define  SAMD21_SERCOM_FIFOPTR_CPUWRPTR_MASK   0xf
+#define SAMD21_SERCOM_FIFOPTR_CPURDPTR 8
+#define  SAMD21_SERCOM_FIFOPTR_CPURDPTR_MASK   0xf
+
+/* The SYSTICK starts at 0xe000e010 */
+struct samd21_systick {
+       vuint32_t       csr;
+       vuint32_t       rvr;
+       vuint32_t       cvr;
+       vuint32_t       calib;
+};
+
+extern struct samd21_systick samd21_systick;
+
+#define samd21_systick (*(struct samd21_systick *) 0xe000e010)
+
+#define SAMD21_SYSTICK_CSR_ENABLE      0
+#define SAMD21_SYSTICK_CSR_TICKINT     1
+#define SAMD21_SYSTICK_CSR_CLKSOURCE   2
+#define  SAMD21_SYSTICK_CSR_CLKSOURCE_EXTERNAL         0
+#define  SAMD21_SYSTICK_CSR_CLKSOURCE_HCLK_8           1
+#define SAMD21_SYSTICK_CSR_COUNTFLAG   16
+
+#define SAMD21_SYSTICK_PRI             15
+
+/* The NVIC starts at 0xe000e100, so add that to the offsets to find the absolute address */
+
+struct samd21_nvic {
+       vuint32_t       iser;           /* 0x000 0xe000e100 Set Enable Register */
+
+       uint8_t         _unused020[0x080 - 0x004];
+
+       vuint32_t       icer;           /* 0x080 0xe000e180 Clear Enable Register */
+
+       uint8_t         _unused0a0[0x100 - 0x084];
+
+       vuint32_t       ispr;           /* 0x100 0xe000e200 Set Pending Register */
+
+       uint8_t         _unused120[0x180 - 0x104];
+
+       vuint32_t       icpr;           /* 0x180 0xe000e280 Clear Pending Register */
+
+       uint8_t         _unused1a0[0x300 - 0x184];
+
+       vuint32_t       ipr[8];         /* 0x300 0xe000e400 Priority Register */
+};
+
+extern struct samd21_nvic samd21_nvic;
+
+#define samd21_nvic (*(struct samd21_nvic *) 0xe000e100)
+
+#define SAMD21_NVIC_ISR_PM_POS         0
+#define SAMD21_NVIC_ISR_SYSCTRL_POS    1
+#define SAMD21_NVIC_ISR_WDT_POS                2
+#define SAMD21_NVIC_ISR_RTC_POS                3
+#define SAMD21_NVIC_ISR_EIC_POS                4
+#define SAMD21_NVIC_ISR_NVMCTRL_POS    5
+#define SAMD21_NVIC_ISR_DMAC_POS       6
+#define SAMD21_NVIC_ISR_USB_POS                7
+#define SAMD21_NVIC_ISR_EVSYS_POS      8
+#define SAMD21_NVIC_ISR_SERCOM0_POS    9
+#define SAMD21_NVIC_ISR_SERCOM1_POS    10
+#define SAMD21_NVIC_ISR_SERCOM2_POS    11
+#define SAMD21_NVIC_ISR_SERCOM3_POS    12
+#define SAMD21_NVIC_ISR_SERCOM4_POS    13
+#define SAMD21_NVIC_ISR_SERCOM5_POS    14
+#define SAMD21_NVIC_ISR_TCC0_POS       15
+#define SAMD21_NVIC_ISR_TCC1_POS       16
+#define SAMD21_NVIC_ISR_TCC2_POS       17
+#define SAMD21_NVIC_ISR_TC3_POS                18
+#define SAMD21_NVIC_ISR_TC4_POS                19
+#define SAMD21_NVIC_ISR_TC5_POS                20
+#define SAMD21_NVIC_ISR_TC6_POS                21
+#define SAMD21_NVIC_ISR_TC7_POS                22
+#define SAMD21_NVIC_ISR_ADC_POS                23
+#define SAMD21_NVIC_ISR_AC_POS         24
+#define SAMD21_NVIC_ISR_DAC_POS                25
+#define SAMD21_NVIC_ISR_PTC_POS                26
+#define SAMD21_NVIC_ISR_I2S_POS                27
+#define SAMD21_NVIC_ISR_AC1_POS                28
+#define SAMD21_NVIC_ISR_TCC3_POS       29
+
+#define IRQ_MASK(irq)  (1 << (irq))
+#define IRQ_BOOL(v,irq)        (((v) >> (irq)) & 1)
+
+static inline void
+samd21_nvic_set_enable(int irq) {
+       samd21_nvic.iser = IRQ_MASK(irq);
+}
+
+static inline void
+samd21_nvic_clear_enable(int irq) {
+       samd21_nvic.icer = IRQ_MASK(irq);
+}
+
+static inline int
+samd21_nvic_enabled(int irq) {
+       return IRQ_BOOL(samd21_nvic.iser, irq);
+}
+
+static inline void
+samd21_nvic_set_pending(int irq) {
+       samd21_nvic.ispr = IRQ_MASK(irq);
+}
+
+static inline void
+samd21_nvic_clear_pending(int irq) {
+       samd21_nvic.icpr = IRQ_MASK(irq);
+}
+
+static inline int
+samd21_nvic_pending(int irq) {
+       return IRQ_BOOL(samd21_nvic.ispr, irq);
+}
+
+#define IRQ_PRIO_REG(irq)      ((irq) >> 2)
+#define IRQ_PRIO_BIT(irq)      (((irq) & 3) << 3)
+#define IRQ_PRIO_MASK(irq)     (0xffU << IRQ_PRIO_BIT(irq))
+
+static inline void
+samd21_nvic_set_priority(int irq, uint8_t prio) {
+       int             n = IRQ_PRIO_REG(irq);
+       uint32_t        v;
+
+       v = samd21_nvic.ipr[n];
+       v &= ~IRQ_PRIO_MASK(irq);
+       v |= (prio) << IRQ_PRIO_BIT(irq);
+       samd21_nvic.ipr[n] = v;
+}
+
+static inline uint8_t
+samd21_nvic_get_priority(int irq) {
+       return (samd21_nvic.ipr[IRQ_PRIO_REG(irq)] >> IRQ_PRIO_BIT(irq)) & IRQ_PRIO_MASK(0);
+}
+
+
+
+/* Cortex M0+ SCB */
+
+struct samd21_scb {
+       vuint32_t       cpuid;
+       vuint32_t       icsr;
+       vuint32_t       vtor;
+       vuint32_t       aircr;
+
+       vuint32_t       scr;
+       vuint32_t       ccr;
+       vuint32_t       shpr1;
+       vuint32_t       shpr2;
+
+       vuint32_t       shpr3;
+       vuint32_t       shcrs;
+       vuint32_t       cfsr;
+       vuint32_t       hfsr;
+
+       uint32_t        unused_30;
+       vuint32_t       mmfar;
+       vuint32_t       bfar;
+};
+
+extern struct samd21_scb samd21_scb;
+
+#define samd21_scb (*(struct samd21_scb *) 0xe000ed00)
+
+#define SAMD21_SCB_AIRCR_VECTKEY       16
+#define  SAMD21_SCB_AIRCR_VECTKEY_KEY          0x05fa
+#define SAMD21_SCB_AIRCR_PRIGROUP      8
+#define SAMD21_SCB_AIRCR_SYSRESETREQ   2
+#define SAMD21_SCB_AIRCR_VECTCLRACTIVE 1
+#define SAMD21_SCB_AIRCR_VECTRESET     0
+
+/* The NVM Calibration and auxiliary space starts at 0x00800000 */
+
+struct samd21_aux0 {
+       vuint64_t       userrow;
+};
+
+extern struct samd21_aux0 samd21_aux0;
+
+#define samd21_aux0 (*(struct samd21_aux0 *) 0x00804000)
+
+#define SAMD21_AUX0_USERROW_BOOTPROT   0
+#define SAMD21_AUX0_USERROW_EEPROM             4
+#define SAMD21_AUX0_USERROW_BOD33_LEVEL        8
+#define SAMD21_AUX0_USERROW_BOD33_ENABLE       14
+#define SAMD21_AUX0_USERROW_BOD33_ACTION       15
+#define SAMD21_AUX0_USERROW_WDT_ENABLE 25
+#define SAMD21_AUX0_USERROW_WDT_ALWAYS_ON      26
+#define SAMD21_AUX0_USERROW_WDT_PERIOD 27
+#define SAMD21_AUX0_USERROW_WDT_WINDOW 31
+#define SAMD21_AUX0_USERROW_WDT_EWOFFSET       35
+#define SAMD21_AUX0_USERROW_WDT_WEN            39
+#define SAMD21_AUX0_USERROW_BOD33_HYST 40
+#define SAMD21_AUX0_USERROW_LOCK               48
+
+struct samd21_aux1 {
+       vuint64_t       reserved_00;
+       vuint64_t       device_config;
+
+       vuint64_t       reserved_10;
+       vuint64_t       reserved_18;
+
+       vuint64_t       calibration;
+       vuint64_t       reserved_28;
+};
+
+extern struct samd21_aux1 samd21_aux1;
+
+#define samd21_aux1 (*(struct samd21_aux1 *) 0x00806000)
+
+#define SAMD21_AUX1_CALIBRATION_ADC_LINEARITY  27
+#define  SAMD21_AUX1_CALIBRATION_ADC_LINEARITY_MASK    0xff
+#define SAMD21_AUX1_CALIBRATION_ADC_BIASCAL    35
+#define  SAMD21_AUX1_CALIBRATION_ADC_BIASCAL_MASK      0x7
+#define SAMD21_AUX1_CALIBRATION_OSC32K_CAL     38
+#define SAMD21_AUX1_CALIBRATION_USB_TRANSN     45
+#define  SAMD21_AUX1_CALIBRATION_USB_TRANSN_MASK       0x1f
+#define SAMD21_AUX1_CALIBRATION_USB_TRANSP     50
+#define  SAMD21_AUX1_CALIBRATION_USB_TRANSP_MASK       0x1f
+#define SAMD21_AUX1_CALIBRATION_USB_TRIM       55
+#define  SAMD21_AUX1_CALIBRATION_USB_TRIM_MASK         0x07
+#define SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL     58
+#define  SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL_MASK       0x3f
+
+struct samd21_serial {
+       vuint32_t       reserved_00;
+       vuint32_t       reserved_04;
+       vuint32_t       reserved_08;
+       vuint32_t       word0;
+
+       vuint32_t       reserved_10;
+       vuint32_t       reserved_14;
+       vuint32_t       reserved_18;
+       vuint32_t       reserved_1c;
+
+       vuint32_t       reserved_20;
+       vuint32_t       reserved_24;
+       vuint32_t       reserved_28;
+       vuint32_t       reserved_2c;
+
+       vuint32_t       reserved_30;
+       vuint32_t       reserved_34;
+       vuint32_t       reserved_38;
+       vuint32_t       reserved_3c;
+
+       vuint32_t       word1;
+       vuint32_t       word2;
+       vuint32_t       word3;
+       vuint32_t       reserved_4c;
+};
+
+extern struct samd21_serial samd21_serial;
+
+#define samd21_serial (*(struct samd21_serial *) 0x0080a000)
+
+static inline void
+samd21_gclk_wait_sync(void)
+{
+       while (samd21_gclk.status & (1 << SAMD21_GCLK_STATUS_SYNCBUSY))
+               ;
+}
+
+static inline void
+samd21_dfll_wait_sync(void)
+{
+       while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_DFLLRDY)) == 0)
+               ;
+}
+
+static inline void
+samd21_gclk_gendiv(uint32_t id, uint32_t div)
+{
+       if (div == 1)
+               div = 0;
+       samd21_gclk.gendiv = ((id << SAMD21_GCLK_GENDIV_ID) |
+                             (div << SAMD21_GCLK_GENDIV_DIV));
+       samd21_gclk_wait_sync();
+}
+
+static inline void
+samd21_gclk_genctrl(uint32_t src, uint32_t id)
+{
+       samd21_gclk.genctrl = ((id << SAMD21_GCLK_GENCTRL_ID) |
+                              (src << SAMD21_GCLK_GENCTRL_SRC) |
+                              (0 << SAMD21_GCLK_GENCTRL_OE) |
+                              (1 << SAMD21_GCLK_GENCTRL_GENEN));
+       samd21_gclk_wait_sync();
+}
+
+static inline void
+samd21_gclk_clkctrl(uint32_t gen, uint32_t id)
+{
+       samd21_gclk.clkctrl = (uint16_t) ((gen << SAMD21_GCLK_CLKCTRL_GEN) |
+                                         (id << SAMD21_GCLK_CLKCTRL_ID) |
+                                         (1U << SAMD21_GCLK_CLKCTRL_CLKEN));
+       samd21_gclk_wait_sync();
+}
+
+#define isr_decl(name) \
+       void samd21_ ## name ## _isr(void)
+
+isr_decl(halt);
+isr_decl(ignore);
+isr_decl(nmi);
+isr_decl(hardfault);
+isr_decl(memmanage);
+isr_decl(busfault);
+isr_decl(usagefault);
+isr_decl(svc);
+isr_decl(debugmon);
+isr_decl(pendsv);
+isr_decl(systick);
+isr_decl(pm);          /* IRQ0 */
+isr_decl(sysctrl);
+isr_decl(wdt);
+isr_decl(rtc);
+isr_decl(eic);
+isr_decl(nvmctrl);
+isr_decl(dmac);
+isr_decl(usb);
+isr_decl(evsys);
+isr_decl(sercom0);
+isr_decl(sercom1);
+isr_decl(sercom2);
+isr_decl(sercom3);
+isr_decl(sercom4);
+isr_decl(sercom5);
+isr_decl(tcc0);
+isr_decl(tcc1);
+isr_decl(tcc2);
+isr_decl(tc3);
+isr_decl(tc4);
+isr_decl(tc5);
+isr_decl(tc6);
+isr_decl(tc7);
+isr_decl(adc);
+isr_decl(ac);
+isr_decl(dac);
+isr_decl(ptc);
+isr_decl(i2s);
+isr_decl(ac1);
+isr_decl(tcc3);
+
+#undef isr_decl
+
+#endif /* _SAMD21_H_ */