Merge branch 'master'
authorKeith Packard <keithp@keithp.com>
Sat, 26 Nov 2022 17:52:37 +0000 (09:52 -0800)
committerKeith Packard <keithp@keithp.com>
Sat, 26 Nov 2022 17:53:56 +0000 (09:53 -0800)
70 files changed:
Releasing
altoslib/AltosConfigData.java
altoslib/AltosIdleFetch.java
altosui/Makefile.am
altosui/altos-windows.nsi.in
ao-tools/ao-flash/Makefile.am
ao-tools/ao-flash/ao-flash-samd21 [new file with mode: 0755]
ao-tools/ao-flash/ao-flash-samd21.1 [new file with mode: 0644]
src/Makefile.defs
src/attiny/ao_arch_funcs.h
src/drivers/ao_cc1200.c
src/drivers/ao_gps_skytraq.c
src/drivers/ao_gps_ublox.c
src/drivers/ao_led.c
src/drivers/ao_ms5607.c
src/kernel/ao.h
src/kernel/ao_task.c
src/kernel/ao_telemetry.c
src/metro-m0/ao_pins.h [new file with mode: 0644]
src/metro-m0/flash-loader/ao_pins.h [new file with mode: 0644]
src/metro-m0/metro-m0.c [new file with mode: 0644]
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-128-16.ld [new file with mode: 0644]
src/samd21/altos-256-32.ld [new file with mode: 0644]
src/samd21/altos-loader.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_beep_samd21.c [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_exti_samd21.c [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_spi_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/registers.ld [new file with mode: 0644]
src/samd21/samd21.h [new file with mode: 0644]
src/snekboard/Makefile [new file with mode: 0644]
src/snekboard/ao_pins.h [new file with mode: 0644]
src/snekboard/flash-loader/Makefile [new file with mode: 0644]
src/snekboard/flash-loader/ao_pins.h [new file with mode: 0644]
src/snekboard/snekboard.c [new file with mode: 0644]
src/stm32l0/ao_arch_funcs.h
src/telebt-v4.0/ao_pins.h
src/telemetrum-v4.0/Makefile [new file with mode: 0644]
src/telemetrum-v4.0/ao_pins.h [new file with mode: 0644]
src/telemetrum-v4.0/ao_telemetrum.c [new file with mode: 0644]
src/telemetrum-v4.0/flash-loader/Makefile [new file with mode: 0644]
src/telemetrum-v4.0/flash-loader/ao_pins.h [new file with mode: 0644]

index 7bfb3ae8bcbeab276d96ebe4c090181356124c5b..939b10b71eb7872e01f8d8542e51fa1675d41c3c 100644 (file)
--- a/Releasing
+++ b/Releasing
@@ -119,7 +119,7 @@ These are Bdale's notes on how to do a release.
           src/teledongle-v3.0/{*.elf,*.ihx,*.map} \
           src/telegps-v[1-2].0/{*.elf,*.ihx,*.map} \
           src/telemega-v[1-5].0/{*.elf,*.ihx,*.map} \
-          src/telemetrum-v[2-3].0/{*.elf,*.ihx,*.map} \
+          src/telemetrum-v[2-4].0/{*.elf,*.ihx,*.map} \
           src/telemini-v3.0/{*.elf,*.ihx,*.map} \
           src/telelco-v2.0/{*.elf,*.ihx,*.map} \
           src/telefireeight-v[1-2].0/{*.elf,*.ihx,*.map} \
@@ -133,7 +133,7 @@ These are Bdale's notes on how to do a release.
           src/teledongle-v3.0/flash-loader/*.elf \
           src/telegps-v[1-2].0/flash-loader/{*.elf,*.bin,*.map} \
           src/telemega-v[1-5].0/flash-loader/*.elf \
-          src/telemetrum-v[2-3].0/flash-loader/*.elf \
+          src/telemetrum-v[2-4].0/flash-loader/*.elf \
           src/telemini-v3.0/flash-loader/{*.elf,*.bin,*.map} \
           src/telelco-v2.0/flash-loader/*.elf \
           src/telefireeight-v[1-2].0/flash-loader/*.elf \
index 5b9ee03120689d829d7f789a40218c143837daa8..5797f6325c64071226ccdb256eb059d1f90242b3 100644 (file)
@@ -638,6 +638,8 @@ public class AltosConfigData {
                        return false;
                if (product.startsWith("TeleMetrum-v3"))
                        return false;
+               if (product.startsWith("TeleMetrum-v4"))
+                       return true;
                if (product.startsWith("EasyMega"))
                        return false;
                return true;
@@ -664,6 +666,8 @@ public class AltosConfigData {
                                return true;
                        if (product.startsWith("TeleMetrum-v3"))
                                return true;
+                       if (product.startsWith("TeleMetrum-v4"))
+                               return true;
                        if (product.startsWith("TeleMega-v4"))
                                return true;
                        if (product.startsWith("EasyMotor-v2"))
@@ -680,6 +684,8 @@ public class AltosConfigData {
                                return AltosAdxl375.X_AXIS;
                        if (product.startsWith("TeleMetrum-v3"))
                                return AltosAdxl375.X_AXIS;
+                       if (product.startsWith("TeleMetrum-v4"))
+                               return AltosAdxl375.X_AXIS;
                        if (product.startsWith("TeleMega-v4"))
                                return AltosAdxl375.X_AXIS;
                        if (product.startsWith("EasyMotor-v2"))
index b2fd3c6f56833b938cc91392f12fa7c48f87f951..b2552b24bdf73d561378585808c8c8660c61621f 100644 (file)
@@ -182,6 +182,12 @@ public class AltosIdleFetch implements AltosDataProvider {
                               AltosIdler.idle_ms5607,
                               AltosIdler.idle_sensor_metrum),
 
+               new AltosIdler("TeleMetrum-v4",
+                              AltosIdler.idle_gps,
+                              AltosIdler.idle_adxl375,
+                              AltosIdler.idle_ms5607,
+                              AltosIdler.idle_sensor_metrum),
+
                new AltosIdler("TeleMega-v0",
                               AltosIdler.idle_gps,
                               AltosIdler.idle_mma655x,
index 41f0d6ad4085de9ecc283b5cdd1cd19f27a9a3e4..765ad58c3cca4298cc4519b024fcb2e233ae50a9 100644 (file)
@@ -124,7 +124,8 @@ FIRMWARE_TD=$(FIRMWARE_TD_3_0)
 
 FIRMWARE_TM_2_0=$(top_srcdir)/src/telemetrum-v2.0/telemetrum-v2.0-$(VERSION).ihx
 FIRMWARE_TM_3_0=$(top_srcdir)/src/telemetrum-v3.0/telemetrum-v3.0-$(VERSION).ihx
-FIRMWARE_TM=$(FIRMWARE_TM_2_0) $(FIRMWARE_TM_3_0)
+FIRMWARE_TM_4_0=$(top_srcdir)/src/telemetrum-v4.0/telemetrum-v4.0-$(VERSION).ihx
+FIRMWARE_TM=$(FIRMWARE_TM_2_0) $(FIRMWARE_TM_3_0) $(FIRMWARE_TM_4_0)
 
 FIRMWARE_TELEMINI_3_0=$(top_srcdir)/src/telemini-v3.0/telemini-v3.0-$(VERSION).ihx
 FIRMWARE_TELEMINI=$(FIRMWARE_TELEMINI_3_0)
index 7abd4d03f15ce4d2205ae0b1d7dd097fa41fe322..6cb9cd42d024c40cae236d84dc44004107808f12 100644 (file)
@@ -122,6 +122,7 @@ Section "Firmware"
 
        File "../src/telemetrum-v2.0/telemetrum-v2.0-${VERSION}.ihx"
        File "../src/telemetrum-v3.0/telemetrum-v3.0-${VERSION}.ihx"
+       File "../src/telemetrum-v4.0/telemetrum-v4.0-${VERSION}.ihx"
        File "../src/telemini-v3.0/telemini-v3.0-${VERSION}.ihx"
        File "../src/telegps-v1.0/telegps-v1.0-${VERSION}.ihx"
        File "../src/telegps-v2.0/telegps-v2.0-${VERSION}.ihx"
index ed9284397dc3a817b4dad9139ffdb33a242b5633..c9174d6a8d520ef6be1ca50af6d9e95b010b3641 100644 (file)
@@ -1,3 +1,3 @@
-bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x ao-reset-lpc
+bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x ao-reset-lpc ao-flash-samd21
 
-man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1 ao-reset-lpc.1
+man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1 ao-reset-lpc.1 ao-flash-samd21.1
diff --git a/ao-tools/ao-flash/ao-flash-samd21 b/ao-tools/ao-flash/ao-flash-samd21
new file mode 100755 (executable)
index 0000000..f20d526
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+case "$#" in
+1)
+    ;;
+*)
+    echo "usage: $0 <filename> ..."
+    exit 1
+    ;;
+esac
+openocd -f interface/stlink.cfg \
+        -c 'transport select hla_swd' \
+       -c 'set CHIPNAME at91samd21g18' \
+       -c 'set CPUTAPID 0x0bc11477' \
+       -f target/at91samdXX.cfg \
+       -c init \
+       -c 'reset halt' \
+       -c 'at91samd bootloader 0' \
+       -c "flash write_image erase unlock $1" \
+       -c "shutdown"
diff --git a/ao-tools/ao-flash/ao-flash-samd21.1 b/ao-tools/ao-flash/ao-flash-samd21.1
new file mode 100644 (file)
index 0000000..d624fe0
--- /dev/null
@@ -0,0 +1,36 @@
+.\"
+.\" 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.
+.\"
+.\"
+.TH AO-FLASH-LPC 1 "ao-flash-samd21" ""
+.SH NAME
+ao-flash-samd21 \- flash a program to an SAMD21-based AltOS device using openocd
+.SH SYNOPSIS
+.B "ao-flash-samd21"
+\fIfile.elf\fP
+.SH DESCRIPTION
+.I ao-flash-samd21
+loads the specified .elf file into the target device flash memory.
+.SH USAGE
+.I ao-flash-samd21
+is a simple script that passes the correct arguments to openocd to
+load a file into the target device via a connected STlink
+debugging dongle.
+.SH "SEE ALSO"
+openocd(1)
+.SH AUTHOR
+Keith Packard
index d9b719973da61e5c52fabe871d8b3111318b7630..c2d5247cd1ea1cfcbf4129b2e7c291e2f87c3ed7 100644 (file)
@@ -23,6 +23,7 @@ OPT=-Os -Wl,-Map=$(PROGNAME)-$(VERSION).map
 
 PICOLIBC_CFLAGS= \
        -specs=picolibc.specs \
+       -Wl,--gc-sections \
        $(PICOLIBC_PRINTF_CFLAGS)
 
 AO_CFLAGS=\
index 1f422a37645bd92d7d7c2764a29c5f3b26a7efde..e3374ac59ee4b501665675f0c025d9dcb10a758e 100644 (file)
@@ -65,6 +65,8 @@ ao_spi_recv_bus(void *block, uint16_t len);
 #define ao_spi_send(block, len, bus) ao_spi_send_bus(block, len)
 #define ao_spi_recv(block, len, bus) ao_spi_recv_bus(block, len)
 
+#define AO_SPI_DUPLEX  0
+
 void
 ao_spi_init(void);
 
index 4ddc96ce09b2dec483e722ea4b03c77ac9858527..727097441007fee784b2e570fd57d7d315ec7466 100644 (file)
@@ -204,6 +204,7 @@ ao_radio_status(void)
 void
 ao_radio_recv_abort(void)
 {
+       ao_exti_disable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
        ao_radio_abort = 1;
        ao_wakeup(&ao_radio_wake);
 }
@@ -213,7 +214,6 @@ ao_radio_recv_abort(void)
 static void
 ao_radio_isr(void)
 {
-       ao_exti_disable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
        ao_radio_wake = 1;
        ao_wakeup(&ao_radio_wake);
 }
@@ -221,14 +221,12 @@ ao_radio_isr(void)
 static void
 ao_radio_start_tx(void)
 {
-       ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
        ao_radio_strobe(CC1200_STX);
 }
 
 static void
 ao_radio_start_rx(void)
 {
-       ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
        ao_radio_strobe(CC1200_SRX);
 }
 
@@ -575,8 +573,7 @@ static uint16_t ao_radio_mode;
 
 #define AO_RADIO_MODE_BITS_PACKET      1
 #define AO_RADIO_MODE_BITS_TX_BUF      4
-#define AO_RADIO_MODE_BITS_TX_FINISH   8
-#define AO_RADIO_MODE_BITS_RX          16
+#define AO_RADIO_MODE_BITS_FINISH      8
 #define AO_RADIO_MODE_BITS_RDF         32
 #define AO_RADIO_MODE_BITS_APRS                64
 #define AO_RADIO_MODE_BITS_TEST                128
@@ -584,12 +581,12 @@ static uint16_t ao_radio_mode;
 #define AO_RADIO_MODE_BITS_FIXED       512
 
 #define AO_RADIO_MODE_NONE             0
-#define AO_RADIO_MODE_PACKET_TX                (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_TX_FINISH)
-#define AO_RADIO_MODE_PACKET_RX                (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_RX)
-#define AO_RADIO_MODE_RDF              (AO_RADIO_MODE_BITS_RDF    | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_TX_FINISH)
+#define AO_RADIO_MODE_PACKET_TX                (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_FINISH)
+#define AO_RADIO_MODE_PACKET_RX                (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_FINISH)
+#define AO_RADIO_MODE_RDF              (AO_RADIO_MODE_BITS_RDF    | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_FINISH)
 #define AO_RADIO_MODE_APRS_BUF         (AO_RADIO_MODE_BITS_APRS   | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF)
 #define AO_RADIO_MODE_APRS_LAST_BUF    (AO_RADIO_MODE_BITS_APRS   | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_TX_BUF)
-#define AO_RADIO_MODE_APRS_FINISH      (AO_RADIO_MODE_BITS_APRS   | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_TX_FINISH)
+#define AO_RADIO_MODE_APRS_FINISH      (AO_RADIO_MODE_BITS_APRS   | AO_RADIO_MODE_BITS_FIXED    | AO_RADIO_MODE_BITS_FINISH)
 #define AO_RADIO_MODE_TEST             (AO_RADIO_MODE_BITS_TEST   | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF)
 
 static void
@@ -642,17 +639,10 @@ ao_radio_set_mode(uint16_t new_mode)
 
        if (changes & AO_RADIO_MODE_BITS_TX_BUF) {
                ao_radio_reg_write(AO_CC1200_INT_GPIO_IOCFG, CC1200_IOCFG_GPIO_CFG_TXFIFO_THR);
-               ao_exti_set_mode(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH);
        }
 
-       if (changes & AO_RADIO_MODE_BITS_TX_FINISH) {
+       if (changes & AO_RADIO_MODE_BITS_FINISH) {
                ao_radio_reg_write(AO_CC1200_INT_GPIO_IOCFG, CC1200_IOCFG_GPIO_CFG_PKT_SYNC_RXTX);
-               ao_exti_set_mode(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH);
-       }
-
-       if (changes & AO_RADIO_MODE_BITS_RX) {
-               ao_radio_reg_write(AO_CC1200_INT_GPIO_IOCFG, CC1200_IOCFG_GPIO_CFG_MARC_MCU_WAKEUP);
-               ao_exti_set_mode(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN, AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_HIGH);
        }
 
        if (changes & AO_RADIO_MODE_BITS_RDF)
@@ -772,12 +762,18 @@ ao_radio_show_state(char *where)
 /* Wait for the radio to signal an interrupt
  */
 static void
-ao_radio_wait_isr(AO_TICK_TYPE timeout)
+_ao_radio_wait_isr(AO_TICK_TYPE timeout)
 {
-       ao_arch_block_interrupts();
        while (!ao_radio_wake && !ao_radio_abort)
                if (ao_sleep_for(&ao_radio_wake, timeout))
                        ao_radio_abort = 1;
+}
+
+static void
+ao_radio_wait_isr(AO_TICK_TYPE timeout)
+{
+       ao_arch_block_interrupts();
+       _ao_radio_wait_isr(timeout);
        ao_arch_release_interrupts();
 }
 
@@ -943,11 +939,12 @@ ao_radio_send_aprs(ao_radio_fill_func fill)
                        ao_radio_set_len((uint8_t) (total & 0xff));
 
                /* Wait for some space in the fifo */
+               ao_arch_block_interrupts();
                while (started && ao_radio_int_pin() != 0 && !ao_radio_abort) {
                        ao_radio_wake = 0;
-                       ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
-                       ao_radio_wait_isr(AO_MS_TO_TICKS(1000));
+                       _ao_radio_wait_isr(AO_MS_TO_TICKS(1000));
                }
+               ao_arch_release_interrupts();
                if (ao_radio_abort)
                        break;
 
@@ -963,11 +960,12 @@ ao_radio_send_aprs(ao_radio_fill_func fill)
                }
        }
        /* Wait for the transmitter to go idle */
+       ao_arch_block_interrupts();
        while (started && ao_radio_int_pin() != 0 && !ao_radio_abort) {
                ao_radio_wake = 0;
-               ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
-               ao_radio_wait_isr(AO_MS_TO_TICKS(1000));
+               _ao_radio_wait_isr(AO_MS_TO_TICKS(1000));
        }
+       ao_arch_release_interrupts();
        if (ao_radio_abort)
                ao_radio_idle();
        ao_radio_put();
@@ -1033,6 +1031,8 @@ ao_radio_recv(void *d, uint8_t size, AO_TICK_TYPE timeout)
 
        while (!ao_radio_abort) {
                ao_radio_wait_isr(timeout);
+               if (ao_radio_abort)
+                       break;
                if (ao_radio_wake) {
                        uint8_t         marc_status1 = ao_radio_reg_read(CC1200_MARC_STATUS1);
 
@@ -1481,5 +1481,7 @@ ao_radio_init(void)
                      AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
                      ao_radio_isr);
 
+       ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
+
        ao_cmd_register(&ao_radio_cmds[0]);
 }
index 3a253eea69559fb19731824c46866995e5b75baa..00c6932fc646e954d4f65c5263501632c5f4fd47 100644 (file)
@@ -40,6 +40,7 @@ static uint8_t ao_gps_cksum;
 static uint8_t ao_gps_error;
 
 AO_TICK_TYPE ao_gps_tick;
+AO_TICK_TYPE ao_gps_utc_tick;
 struct ao_telemetry_location   ao_gps_data;
 struct ao_telemetry_satellite  ao_gps_tracking_data;
 
@@ -296,7 +297,7 @@ ao_nmea_gga(void)
        if (!ao_gps_error) {
                ao_mutex_get(&ao_gps_mutex);
                ao_gps_new |= AO_GPS_NEW_DATA;
-               ao_gps_tick = ao_gps_next_tick;
+               ao_gps_tick = ao_gps_utc_tick = ao_gps_next_tick;
                memcpy(&ao_gps_data, &ao_gps_next, sizeof (ao_gps_data));
                ao_mutex_put(&ao_gps_mutex);
                ao_wakeup(&ao_gps_new);
index 57cbf22a77b50245975955925096ba85ac9723f5..3d615e9c933f14b260f3cd0fb75faeec68073a3c 100644 (file)
@@ -29,6 +29,7 @@
 uint8_t ao_gps_new;
 uint8_t ao_gps_mutex;
 AO_TICK_TYPE ao_gps_tick;
+AO_TICK_TYPE ao_gps_utc_tick;
 struct ao_telemetry_location   ao_gps_data;
 struct ao_telemetry_satellite  ao_gps_tracking_data;
 
@@ -63,12 +64,13 @@ static uint16_t ao_ublox_len;
 
 #if AO_UBLOX_DEBUG
 
-static uint8_t ao_gps_dbg_enable;
 
 #define DBG_PROTO      1
 #define DBG_CHAR       2
 #define DBG_INIT       4
 
+static uint8_t ao_gps_dbg_enable = DBG_PROTO|DBG_CHAR|DBG_INIT;
+
 static void ao_gps_dbg(int level, char *format, ...) {
        va_list a;
 
@@ -397,6 +399,7 @@ ao_ublox_parse_nav_svinfo(void)
  * NAV-TIMEUTC message parsing
  */
 static struct nav_timeutc {
+       int32_t         nano;
        uint16_t        year;
        uint8_t         month;
        uint8_t         day;
@@ -411,7 +414,8 @@ static struct nav_timeutc {
 #define NAV_TIMEUTC_VALID_UTC  2
 
 static const struct ublox_packet_parse nav_timeutc_packet[] = {
-       { UBLOX_DISCARD, 12 },                                          /* 0 iTOW, tAcc, nano */
+       { UBLOX_DISCARD, 8 },                                           /* 0 iTOW, tAcc */
+       { UBLOX_U32, offsetof(struct nav_timeutc, nano) },              /* 8 nano */
        { UBLOX_U16, offsetof(struct nav_timeutc, year) },              /* 12 year */
        { UBLOX_U8, offsetof(struct nav_timeutc, month) },              /* 14 month */
        { UBLOX_U8, offsetof(struct nav_timeutc, day) },                /* 15 day */
@@ -721,7 +725,7 @@ ao_gps(void)
                        case UBLOX_NAV_TIMEUTC:
                                ao_mutex_get(&ao_gps_mutex);
                                ao_gps_tick = solution_tick;
-
+                               ao_gps_utc_tick = packet_start_tick + (AO_TICK_TYPE) AO_NS_TO_TICKS(nav_timeutc.nano);
                                ao_gps_data.flags = 0;
                                ao_gps_data.flags |= AO_GPS_RUNNING;
                                if (nav_sol.gps_fix & (1 << NAV_SOL_FLAGS_GPSFIXOK)) {
@@ -788,7 +792,7 @@ ao_gps(void)
 #if AO_UBLOX_DEBUG
 static void ao_gps_option(void)
 {
-       uint16_t r = ao_cmd_hex();
+       uint8_t r = (uint8_t) ao_cmd_hex();
        if (ao_cmd_status != ao_cmd_success) {
                ao_cmd_status = ao_cmd_success;
                ao_gps_show();
index b32034e7d3f2f0903a03751e7c8210ffc17462ef..009e9bc23e434cd8496cd5177eb62257c299c805 100644 (file)
@@ -20,7 +20,7 @@
 
 static const struct {
        void            *port;
-       uint16_t        pin;
+       uint8_t         pin;
 } ao_leds[] = {
 #ifdef LED_0_PORT
     [0] { LED_0_PORT, LED_0_PIN },
index f73b095622cd5a4eccf9f1d0b7e28a7ade50caf7..1cddcaf570e540656a2e42b2550a299167c82422 100644 (file)
@@ -154,8 +154,7 @@ ao_ms5607_isr(void)
 
 static uint32_t
 ao_ms5607_get_sample(uint8_t cmd) {
-       uint8_t reply[3];
-       uint8_t read;
+       uint8_t reply[4];
 
        ao_ms5607_done = 0;
 
@@ -173,18 +172,22 @@ ao_ms5607_get_sample(uint8_t cmd) {
                ao_sleep((void *) &ao_ms5607_done);
        ao_arch_release_interrupts();
 #if AO_MS5607_PRIVATE_PINS
-       stm_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 1);
+       ao_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 1);
 #else
        ao_ms5607_stop();
 #endif
 
        ao_ms5607_start();
-       read = AO_MS5607_ADC_READ;
-       ao_spi_send(&read, 1, AO_MS5607_SPI_INDEX);
-       ao_spi_recv(&reply, 3, AO_MS5607_SPI_INDEX);
+       reply[0] = AO_MS5607_ADC_READ;
+#if defined(AO_SPI_DUPLEX) && AO_SPI_DUPLEX == 0
+       ao_spi_send(reply, 1, AO_MS5607_SPI_INDEX);
+       ao_spi_recv(reply+1, 3, AO_MS5607_SPI_INDEX);
+#else
+       ao_spi_duplex(&reply, &reply, 4, AO_MS5607_SPI_INDEX);
+#endif
        ao_ms5607_stop();
 
-       return ((uint32_t) reply[0] << 16) | ((uint32_t) reply[1] << 8) | (uint32_t) reply[2];
+       return ((uint32_t) reply[1] << 16) | ((uint32_t) reply[2] << 8) | (uint32_t) reply[3];
 }
 
 #ifndef AO_MS5607_BARO_OVERSAMPLE
index 9ca2c60eac711c94734d199eca9603ac98ec5169..d714cecbd15e508c3705c0e6751ecae5db2bb475 100644 (file)
@@ -113,6 +113,7 @@ extern volatile AO_TICK_TYPE ao_tick_count;
 #endif
 #define AO_MS_TO_TICKS(ms)     ((ms) / (1000 / AO_HERTZ))
 #define AO_SEC_TO_TICKS(s)     ((AO_TICK_TYPE) (s) * AO_HERTZ)
+#define AO_NS_TO_TICKS(ns)     ((ns) / (1000000000L / AO_HERTZ))
 
 /* Returns the current time in ticks */
 AO_TICK_TYPE
@@ -390,6 +391,7 @@ ao_spi_slave(void);
 
 extern uint8_t ao_gps_new;
 extern AO_TICK_TYPE ao_gps_tick;
+extern AO_TICK_TYPE ao_gps_utc_tick;
 extern uint8_t ao_gps_mutex;
 extern struct ao_telemetry_location ao_gps_data;
 extern struct ao_telemetry_satellite ao_gps_tracking_data;
index dd278bde064740f3ed0ac1a6ef11d90ccb85ab7c..b50454167b67d70fdcea806f75ac3a21afb74b9d 100644 (file)
@@ -57,7 +57,17 @@ static inline void ao_check_stack(void) {
 #endif
 
 #ifndef SLEEP_HASH_SIZE
+#ifdef __ARM_FEATURE_IDIV__
 #define SLEEP_HASH_SIZE        17
+#else
+#define SLEEP_HASH_SIZE 16
+#endif
+#endif
+
+#if SLEEP_HASH_SIZE & (SLEEP_HASH_SIZE - 1)
+#define SLEEP_HASH_SHIFT 0
+#else
+#define SLEEP_HASH_SHIFT 2
 #endif
 
 static struct ao_list  run_queue;
@@ -75,7 +85,7 @@ _ao_task_to_run_queue(struct ao_task *task)
 static struct ao_list *
 ao_task_sleep_queue(void *wchan)
 {
-       return &ao_sleep_queue[(uintptr_t) wchan % SLEEP_HASH_SIZE];
+       return &ao_sleep_queue[(((uintptr_t) wchan) >> SLEEP_HASH_SHIFT) % SLEEP_HASH_SIZE];
 }
 
 static void
index 1fd4037f540b8d0d01618b6a22672cd613bb187b..e0f4d8c22aea4f6399cf03c4b46ad3915b63b335 100644 (file)
@@ -58,7 +58,7 @@ static AO_TICK_TYPE ao_aprs_time;
 #define AO_SEND_MEGA   1
 #endif
 
-#if defined (TELEMETRUM_V_2_0) || defined (TELEMETRUM_V_3_0)
+#if defined (TELEMETRUM_V_2_0) || defined (TELEMETRUM_V_3_0) || defined (TELEMETRUM_V_4_0)
 #define AO_SEND_METRUM 1
 #endif
 
@@ -490,7 +490,10 @@ ao_set_aprs_time(void)
                } else {
                        delta = second - ao_gps_data.second;
                }
-               ao_aprs_time = ao_gps_tick + AO_SEC_TO_TICKS(delta);
+               if (delta < (interval >> 1))
+                       delta += interval;
+
+               ao_aprs_time = ao_gps_utc_tick + AO_SEC_TO_TICKS(delta);
        } else {
                ao_aprs_time += AO_SEC_TO_TICKS(ao_config.aprs_interval);
        }
diff --git a/src/metro-m0/ao_pins.h b/src/metro-m0/ao_pins.h
new file mode 100644 (file)
index 0000000..0f276c1
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * 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_PINS_H_
+#define _AO_PINS_H_
+
+#define LED_0_PORT     (&samd21_port_a)
+#define LED_0_PIN      2
+
+#define LED_BLUE       (1 << 0)
+
+#define AO_LED_PANIC   LED_BLUE
+
+#define HAS_USB                1
+#define USE_USB_STDIO  1
+
+#define HAS_LED                1
+
+#define AO_DFLL48M             48000000
+#define AO_XOSC32K             32768
+
+#define AO_AHB_PRESCALER       1
+#define AO_APBA_PRESCALER      1
+
+#define HAS_SPI_0              1
+#define HAS_SPI_5              1
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS          1
+
+#if 1
+#define AO_M25_SPI_CS_PORT     (&samd21_port_a)
+#define AO_M25_SPI_CS_MASK     (1 << 13)
+#define AO_M25_SPI_BUS         AO_SPI_5_PB22_PB23_PB03
+#else
+
+#define AO_M25_SPI_CS_PORT     (&samd21_port_a)
+#define AO_M25_SPI_CS_MASK     (1 << 14) /* D2 */
+#define AO_M25_SPI_BUS         AO_SPI_4_PB10_PB11_PA12
+
+#endif
+
+/*
+ * Beeper
+ */
+
+#define HAS_BEEP               1
+
+/* Beep on PA11 function F TCC0.3 */
+
+#define AO_BEEP_TCC    (&samd21_tcc0)
+#define AO_BEEP_TCC_APBC_MASK  SAMD21_PM_APBCMASK_TCC0
+#define AO_BEEP_PORT   (&samd21_port_a)
+#define AO_BEEP_PIN    (11)
+#define AO_BEEP_FUNC   SAMD21_PORT_PMUX_FUNC_F
+
+/* ADC */
+#define AO_DATA_RING           32
+
+#define HAS_ADC                        1
+
+struct ao_adc {
+       int16_t                 a[6];
+       int16_t                 temp;
+};
+
+#define AO_NUM_ADC_PIN 6
+#define AO_NUM_ADC     (AO_NUM_ADC_PIN + 1)
+
+#define AO_ADC_DUMP(p)                                                 \
+       printf("tick: %5lu a0: %5d a1: %5d a2: %5d a3: %5d a4: %5d a5: %5d temp: %5d\n", \
+              (p)->tick,                                               \
+              (p)->adc.a[0], (p)->adc.a[1], (p)->adc.a[2],             \
+              (p)->adc.a[3], (p)->adc.a[4], (p)->adc.a[5],             \
+              (p)->adc.temp);
+
+#define AO_ADC_PIN0_PORT       (&samd21_port_a)
+#define AO_ADC_PIN0_PIN                2
+#define AO_ADC_SQ0             0
+
+#define AO_ADC_PIN1_PORT       (&samd21_port_b)
+#define AO_ADC_PIN1_PIN                8
+#define AO_ADC_SQ1             2
+
+#define AO_ADC_PIN2_PORT       (&samd21_port_b)
+#define AO_ADC_PIN2_PIN                9
+#define AO_ADC_SQ2             3
+
+#define AO_ADC_PIN3_PORT       (&samd21_port_a)
+#define AO_ADC_PIN3_PIN                4
+#define AO_ADC_SQ3             4
+
+#define AO_ADC_PIN4_PORT       (&samd21_port_a)
+#define AO_ADC_PIN4_PIN                5
+#define AO_ADC_SQ4             5
+
+#define AO_ADC_PIN5_PORT       (&samd21_port_b)
+#define AO_ADC_PIN5_PIN                2
+#define AO_ADC_SQ5             10
+
+#define AO_ADC_SQ6             SAMD21_ADC_INPUTCTRL_MUXPOS_TEMP
+
+/* GPS */
+#define HAS_GPS                        1
+
+#define AO_SERIAL_SPEED_UBLOX  AO_SERIAL_SPEED_9600
+
+#define HAS_SERIAL_0           1
+#define USE_SERIAL_0_STDIN     0
+#define SERIAL_0_PA08_PA09     1
+
+#define ao_gps_getchar         ao_serial0_getchar
+#define ao_gps_putchar         ao_serial0_putchar
+#define ao_gps_set_speed       ao_serial0_set_speed
+#define ao_gps_fifo            (ao_samd21_usart0.rx_fifo)
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/metro-m0/flash-loader/ao_pins.h b/src/metro-m0/flash-loader/ao_pins.h
new file mode 100644 (file)
index 0000000..9a62148
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2013 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_PINS_H_
+#define _AO_PINS_H_
+
+#include <ao_flash_samd21_pins.h>
+
+/* A0 to gnd for boot loader mode */
+
+#define AO_BOOT_PIN                    1
+#define AO_BOOT_APPLICATION_GPIO       (samd21_port_a)
+#define AO_BOOT_APPLICATION_PIN                2
+#define AO_BOOT_APPLICATION_VALUE      1
+#define AO_BOOT_APPLICATION_MODE       AO_MODE_PULL_UP
+
+/* USB */
+#define HAS_USB                        1
+
+#define AO_DFLL48M             48000000
+#define AO_XOSC32K             32768
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/metro-m0/metro-m0.c b/src/metro-m0/metro-m0.c
new file mode 100644 (file)
index 0000000..5fe7723
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2016 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_led.h>
+#include <ao_dma_samd21.h>
+#include <ao_exti.h>
+
+static int     pressed;
+
+static void
+ao_button_callback(void)
+{
+       pressed = 1;
+       ao_wakeup(&pressed);
+}
+
+static void
+ao_beep_test(void)
+{
+       AO_BEEP_TCC->ctrlbset = (SAMD21_TCC_CTRLB_CMD_READSYNC << SAMD21_TCC_CTRLB_CMD);
+       printf("pressed timer %ld\n", AO_BEEP_TCC->count);
+       fflush(stdout);
+       ao_beep_for(AO_BEEP_MID_DEFAULT, AO_MS_TO_TICKS(200));
+}
+
+static void
+ao_button(void)
+{
+       ao_exti_setup(&samd21_port_a, 10, AO_EXTI_MODE_FALLING | AO_EXTI_MODE_PULL_UP, ao_button_callback);
+       ao_exti_enable(&samd21_port_a, 10);
+       for (;;) {
+               ao_arch_block_interrupts();
+               pressed = 0;
+               while (!pressed)
+                       ao_sleep(&pressed);
+               ao_arch_release_interrupts();
+               ao_beep_test();
+       }
+}
+
+static struct ao_task ao_button_task;
+
+const struct ao_cmds ao_test_cmds[] = {
+       { ao_beep_test, "b \0beep" },
+       { 0, NULL },
+};
+
+int main(void)
+{
+       ao_led_init();
+       ao_clock_init();
+       ao_task_init();
+       ao_timer_init();
+       ao_dma_init();
+       ao_exti_init();
+       ao_spi_init();
+       ao_adc_init();
+       ao_serial_init();
+       ao_gps_init();
+       ao_beep_init();
+       ao_usb_init();
+       ao_storage_init();
+       ao_cmd_init();
+
+       ao_cmd_register(ao_test_cmds);
+       ao_add_task(&ao_button_task, ao_button, "button");
+       ao_start_scheduler();
+
+       return 0;
+}
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..d58db93
--- /dev/null
@@ -0,0 +1,11 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/samd21/Makefile-samd21.defs
+
+SAMD21_LINKER_SCRIPT=altos-$(SAMD21_ROM)-$(SAMD21_RAM).ld 
+
+LDFLAGS=-nostartfiles $(CFLAGS) -L$(TOPDIR)/samd21 -T$(SAMD21_LINKER_SCRIPT) -n
+
+.DEFAULT_GOAL=all
diff --git a/src/samd21/altos-128-16.ld b/src/samd21/altos-128-16.ld
new file mode 100644 (file)
index 0000000..e07a6a4
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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 = 16k;
+__stack_size = 256;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
diff --git a/src/samd21/altos-256-32.ld b/src/samd21/altos-256-32.ld
new file mode 100644 (file)
index 0000000..4be7d67
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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 = 256 - 4K;
+__ram = 0x20000000;
+__ram_size = 32k;
+__stack_size = 256;
+
+INCLUDE registers.ld
+INCLUDE picolibc.ld
diff --git a/src/samd21/altos-loader.ld b/src/samd21/altos-loader.ld
new file mode 100644 (file)
index 0000000..a06d28b
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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 = 4k;
+__stack_size = 256;
+
+INCLUDE registers.ld
+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..6d67eda
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * 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 uint8_t ao_adc_sequence;
+static uint8_t ao_adc_ready;
+
+static uint8_t ao_adc_mux[AO_NUM_ADC] = {
+#if AO_NUM_ADC > 0
+       AO_ADC_SQ0,
+#endif
+#if AO_NUM_ADC > 1
+       AO_ADC_SQ1,
+#endif
+#if AO_NUM_ADC > 2
+       AO_ADC_SQ2,
+#endif
+#if AO_NUM_ADC > 3
+       AO_ADC_SQ3,
+#endif
+#if AO_NUM_ADC > 4
+       AO_ADC_SQ4,
+#endif
+#if AO_NUM_ADC > 5
+       AO_ADC_SQ5,
+#endif
+#if AO_NUM_ADC > 6
+       AO_ADC_SQ6,
+#endif
+#if AO_NUM_ADC > 7
+       AO_ADC_SQ7,
+#endif
+#if AO_NUM_ADC > 8
+       AO_ADC_SQ8,
+#endif
+#if AO_NUM_ADC > 9
+#error set up more ADC
+#endif
+};
+
+static void
+ao_adc_start(void)
+{
+       uint8_t mux = ao_adc_mux[ao_adc_sequence];
+       samd21_adc.inputctrl = ((mux << 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));
+       samd21_adc.swtrig = (1UL << SAMD21_ADC_SWTRIG_START);
+}
+
+void
+samd21_adc_isr(void)
+{
+       uint16_t        *out;
+
+       /* Store converted value in packet */
+       out = (uint16_t *) &ao_data_ring[ao_data_head].adc;
+       out[ao_adc_sequence] = (uint16_t) samd21_adc.result;
+       if (++ao_adc_sequence < AO_NUM_ADC) {
+               ao_adc_start();
+               return;
+       }
+
+       AO_DATA_PRESENT(AO_DATA_ADC);
+       ao_data_fill(ao_data_head);
+       ao_adc_ready = 1;
+}
+
+void
+ao_adc_poll(void)
+{
+       if (!ao_adc_ready)
+               return;
+       ao_adc_ready = 0;
+       ao_adc_sequence = 0;
+       ao_adc_start();
+}
+
+static void
+ao_adc_dump(void)
+{
+       struct ao_data  packet;
+
+       ao_data_get(&packet);
+       AO_ADC_DUMP(&packet);
+}
+
+const struct ao_cmds ao_adc_cmds[] = {
+       { ao_adc_dump,  "a\0Display current ADC values" },
+       { 0, NULL },
+};
+
+static inline void
+set_adc(struct samd21_port *port, uint8_t pin)
+{
+       samd21_port_pmux_set(port, pin, SAMD21_PORT_PMUX_FUNC_B);
+       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) |
+                              (1 << SAMD21_PORT_PINCFG_INEN));
+}
+
+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 = (uint16_t) ((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.intenset = (1UL << SAMD21_ADC_INTFLAG_RESRDY);
+
+       samd21_adc.ctrla = (1 << SAMD21_ADC_CTRLA_ENABLE);
+
+       /* configure interrupts */
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_ADC_POS);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_ADC_POS, 0);
+
+       ao_cmd_register(&ao_adc_cmds[0]);
+
+       /* configure pins */
+#if AO_NUM_ADC_PIN > 0
+       set_adc(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 1
+       set_adc(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 2
+       set_adc(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 3
+       set_adc(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 4
+       set_adc(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 5
+       set_adc(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 6
+       set_adc(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 7
+       set_adc(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 8
+       set_adc(AO_ADC_PIN8_PORT, AO_ADC_PIN8_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 9
+#error set up more ADC bits
+#endif
+
+       ao_adc_ready = 1;
+}
diff --git a/src/samd21/ao_adc_samd21.h b/src/samd21/ao_adc_samd21.h
new file mode 100644 (file)
index 0000000..3d1658b
--- /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_
+
+void
+ao_adc_poll(void);
+
+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..a5f68da
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * 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
+
+#ifndef AO_STACK_SIZE
+#define AO_STACK_SIZE          512
+#endif
+
+#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..32670b4
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * 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);
+}
+
+/* ao_spi_samd21.c */
+
+#define AO_SPI_INDEX_BIT       0
+#define AO_SPI_INDEX_MASK      0x07
+
+#define AO_SPI_CONFIG_BIT      4
+#define AO_SPI_CONFIG_MASK     (3 << AO_SPI_CONFIG_BIT)
+
+#define AO_SPI_CPOL_BIT                6
+#define AO_SPI_CPHA_BIT                7
+
+#define AO_SPI_DOPO_BIT                8
+#define  AO_SPI_DOPO_MOSI_0_SCLK_1     (0 << AO_SPI_DOPO_BIT)
+#define  AO_SPI_DOPO_MOSI_2_SCLK_3     (1 << AO_SPI_DOPO_BIT)
+#define  AO_SPI_DOPO_MOSI_3_SCLK_1     (2 << AO_SPI_DOPO_BIT)
+#define  AO_SPI_DOPO_MOSI_0_SCLK_3     (3 << AO_SPI_DOPO_BIT)
+#define  AO_SPI_DOPO_MASK              (3 << AO_SPI_DOPO_BIT)
+
+#define AO_SPI_DIPO_BIT                10
+#define  AO_SPI_DIPO_MISO_0            (0 << AO_SPI_DIPO_BIT)
+#define  AO_SPI_DIPO_MISO_1            (1 << AO_SPI_DIPO_BIT)
+#define  AO_SPI_DIPO_MISO_2            (2 << AO_SPI_DIPO_BIT)
+#define  AO_SPI_DIPO_MISO_3            (3 << AO_SPI_DIPO_BIT)
+#define  AO_SPI_DIPO_MASK              (3 << AO_SPI_DIPO_MASK)
+
+#define AO_SPI_CONFIG_0                (0 << AO_SPI_CONFIG_BIT)
+#define AO_SPI_CONFIG_1                (1 << AO_SPI_CONFIG_BIT)
+#define AO_SPI_CONFIG_2                (2 << AO_SPI_CONFIG_BIT)
+#define AO_SPI_CONFIG_3                (3 << AO_SPI_CONFIG_BIT)
+
+#define AO_SPI_INDEX(id)       ((uint8_t) ((id) & AO_SPI_INDEX_MASK))
+#define AO_SPI_CONFIG(id)      ((id) & AO_SPI_CONFIG_MASK)
+#define AO_SPI_PIN_CONFIG(id)  ((id) & (AO_SPI_INDEX_MASK | AO_SPI_CONFIG_MASK))
+#define AO_SPI_CPOL(id)                ((uint32_t) (((id) >> AO_SPI_CPOL_BIT) & 1))
+#define AO_SPI_CPHA(id)                ((uint32_t) (((id) >> AO_SPI_CPHA_BIT) & 1))
+#define AO_SPI_DOPO(id)                ((uint32_t) (((id) >> AO_SPI_DOPO_BIT) & 3))
+#define AO_SPI_DIPO(id)                ((uint32_t) (((id) >> AO_SPI_DIPO_BIT) & 3))
+
+#define AO_SPI_MAKE_MODE(pol,pha)      (((pol) << AO_SPI_CPOL_BIT) | ((pha) << AO_SPI_CPHA_BIT))
+#define AO_SPI_MODE_0          AO_SPI_MAKE_MODE(0,0)
+#define AO_SPI_MODE_1          AO_SPI_MAKE_MODE(0,1)
+#define AO_SPI_MODE_2          AO_SPI_MAKE_MODE(1,0)
+#define AO_SPI_MODE_3          AO_SPI_MAKE_MODE(1,1)
+
+#if HAS_SPI_0
+/*
+ * PA08 SERCOM0.0 -> MOSI      (DOPO 0)
+ * PA09 SERCOM0.1 -> SCLK      (DOPO 0)
+ * PA10 SERCOM0.2 -> MISO      (DIPO 2)
+ */
+#define AO_SPI_0_PA08_PA09_PA10        (0 | AO_SPI_CONFIG_0 |          \
+                                AO_SPI_DOPO_MOSI_0_SCLK_1 |    \
+                                AO_SPI_DIPO_MISO_2)
+/*
+ * PA04 SERCOM0.0 -> MOSI      (DOPO 0)
+ * PA05 SERCOM0.1 -> SCLK      (DOPO 0)
+ * PA06 SERCOM0.2 -> MISO      (DIPO 2)
+ */
+#define AO_SPI_0_PA04_PA05_PA06        (0 | AO_SPI_CONFIG_1 |          \
+                                AO_SPI_DOPO_MOSI_0_SCLK_1 |    \
+                                AO_SPI_DIPO_MISO_2)
+#endif /* HAS_SPI_0 */
+
+#if HAS_SPI_3
+/*
+ * PA22 SERCOM3.0 -> MOSI      (DOPO 0)
+ * PA23 SERCOM3.1 -> SCLK      (DOPO 0)
+ * PA20 SERCOM3.2 -> MISO      (DIPO 2)
+ */
+#define AO_SPI_3_PA22_PA23_PA20        (3 | AO_SPI_CONFIG_0 |          \
+                                AO_SPI_DOPO_MOSI_0_SCLK_1 |    \
+                                AO_SPI_DIPO_MISO_2)
+#endif /* HAS_SPI_3 */
+
+#if HAS_SPI_4
+/*
+ * PA04 SERCOM0.0 -> MOSI      (DOPO 0)
+ * PA05 SERCOM0.1 -> SCLK      (DOPO 0)
+ * PA16 SERCOM0.2 -> MISO      (DIPO 2)
+ */
+#define AO_SPI_CONFIG_PA04_PA05_PA06   (0 | AO_SPI_CONFIG_1 |          \
+                                        AO_SPI_DOPO_MOSI_0_SCLK_1 |    \
+                                        AO_SPI_DIPO_MISO_2)
+
+/*
+ * PB10 SERCOM4.2 -> MOSI      (DOPO 1)
+ * PB11 SERCOM4.3 -> SCLK      (DOPO 1)
+ * PA12        SERCOM4.0 -> MISO       (DIPO 0)
+ */
+#define AO_SPI_4_PB10_PB11_PA12        (4 | AO_SPI_CONFIG_0 |          \
+                                AO_SPI_DOPO_MOSI_2_SCLK_3 |    \
+                                AO_SPI_DIPO_MISO_0)
+#endif /* HAS_SPI_4 */
+
+#if HAS_SPI_5
+/*
+ * PB22 SERCOM5.2 -> MOSI      (DOPO 1)
+ * PB23 SERCOM5.3 -> SCLK      (DOPO 1)
+ * PB03 SERCOM5.1 -> MISO      (DIPO 1)
+ */
+#define AO_SPI_5_PB22_PB23_PB03        (5 | AO_SPI_CONFIG_0 |          \
+                                AO_SPI_DOPO_MOSI_2_SCLK_3 |    \
+                                AO_SPI_DIPO_MISO_1)
+#endif /* HAS_SPI_5 */
+
+void
+ao_spi_send(const void *block, uint16_t len, uint16_t spi_index);
+
+void
+ao_spi_send_fixed(uint8_t data, uint16_t len, uint16_t spi_index);
+
+void
+ao_spi_recv(void *block, uint16_t len, uint16_t spi_index);
+
+void
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint16_t spi_index);
+
+void
+ao_spi_get(uint16_t spi_index, uint32_t speed);
+
+void
+ao_spi_put(uint16_t spi_index);
+
+void
+ao_spi_init(void);
+
+#define ao_spi_set_cs(reg,mask) do {           \
+               reg->outclr = mask;             \
+       } while(0)
+
+#define ao_spi_clr_cs(reg,mask) do {           \
+               reg->outset = mask;             \
+       } while(0)
+
+#define ao_spi_get_mask(reg,mask,spi_index, speed) do {                \
+               ao_spi_get(spi_index, speed);                           \
+               ao_spi_set_cs(reg,mask);                        \
+       } while (0)
+
+#define ao_spi_put_mask(reg,mask,spi_index) do {       \
+               ao_spi_clr_cs(reg,mask);        \
+               ao_spi_put(spi_index);          \
+       } while (0)
+
+static inline void
+ao_spi_get_bit(struct samd21_port *port, uint8_t bit, uint16_t spi_index, uint32_t speed)
+{
+       ao_spi_get(spi_index, speed);
+       ao_gpio_set(port, bit, 0);
+}
+
+static inline void
+ao_spi_put_bit(struct samd21_port *port, uint8_t bit, uint16_t spi_index)
+{
+       ao_gpio_set(port, bit, 1);
+       ao_spi_put(spi_index);
+}
+
+static inline uint8_t
+ao_spi_speed(uint32_t hz)
+{
+       int32_t baud = (int32_t) (AO_SYSCLK / (2 * hz)) - 1;
+
+       if (baud < 1)
+               baud = 1;
+       if (baud > 255)
+               baud = 255;
+       return (uint8_t) baud;
+}
+
+#define ao_spi_init_cs(port, mask) do {                                        \
+               uint8_t __bit__;                                        \
+               for (__bit__ = 0; __bit__ < 32; __bit__++) {            \
+                       if (mask & (1 << __bit__))                      \
+                               ao_enable_output(port, __bit__, 1); \
+               }                                                       \
+       } while (0)
+
+#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_beep_samd21.c b/src/samd21/ao_beep_samd21.c
new file mode 100644 (file)
index 0000000..7fbbcba
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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_beep.h"
+
+#define BEEP_SCALE     (AO_SYSCLK / 750000)
+
+void
+ao_beep(uint8_t beep)
+{
+       struct samd21_tcc *tcc = AO_BEEP_TCC;
+       if (beep) {
+               tcc->per = (beep * BEEP_SCALE);
+               tcc->ctrla = (1 << SAMD21_TCC_CTRLA_ENABLE);
+       } else {
+               tcc->ctrla = (0 << SAMD21_TCC_CTRLA_ENABLE);
+       }
+}
+
+void
+ao_beep_for(uint8_t beep, AO_TICK_TYPE ticks)
+{
+       ao_beep(beep);
+       ao_delay(ticks);
+       ao_beep(0);
+}
+
+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 = 94 * BEEP_SCALE;
+
+       tcc->wave = ((SAMD21_TCC_WAVE_WAVEGEN_NFRQ << 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->dbgctrl = (1 << SAMD21_TCC_DBGCTRL_DBGRUN);
+}
+
+void
+ao_beep_init(void)
+{
+       struct samd21_port      *port = AO_BEEP_PORT;
+       uint8_t                 pin = AO_BEEP_PIN;
+       struct samd21_tcc       *tcc = AO_BEEP_TCC;
+       uint32_t                apbc_mask = 1UL << AO_BEEP_TCC_APBC_MASK;
+
+       if (tcc == &samd21_tcc0 || tcc == &samd21_tcc1) {
+               samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC0_TCC1);
+       } else {
+               samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC2_TC3);
+       }
+
+       ao_tcc_init(tcc, apbc_mask);
+
+       ao_enable_output(port, pin, 0);
+
+       samd21_port_pmux_set(port, pin, AO_BEEP_FUNC);
+}
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..5bf9470
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * 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 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;
+
+       if (intpend & 0x0700) {
+               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 %04lx\n",
+              where,
+              samd21_dmac.ctrl,
+              samd21_dmac.intpend,
+              samd21_dmac.intstatus);
+       fflush(stdout);
+       printf(" busych %04lx pendch %04lx active %08lx chctrla %02x\n",
+              samd21_dmac.busych,
+              samd21_dmac.pendch,
+              samd21_dmac.active,
+              samd21_dmac.chctrla);
+       fflush(stdout);
+       printf(" chctrlb %08lx chintflag %02x chstatus %02x\n",
+              samd21_dmac.chctrlb,
+              samd21_dmac.chintflag,
+              samd21_dmac.chstatus);
+       fflush(stdout);
+       printf(" btctrl %04x btcnt %04x srcaddr %08lx dstaddr %08lx descaddr %08lx\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);
+}
+
+void
+_ao_dma_start_transfer(uint8_t         id,
+                      const 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 1
+       /* 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..57f5476
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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,
+                      const 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);
+
+/*
+ * DMA is only used for SERCOM
+ */
+
+#define AO_SERCOM_DMA_BASE                     0U
+#define AO_SERCOM_INPUT_DMA_ID(id)             ((uint8_t) ((id) * 2U + 0U + AO_SERCOM_DMA_BASE))
+#define AO_SERCOM_OUTPUT_DMA_ID(id)            ((uint8_t) ((id) * 2U + 1U + AO_SERCOM_DMA_BASE))
+
+#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..1bd14a0
--- /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    SAMD21_EIC_CONFIG_SENSE_RISE
+#define AO_EXTI_MODE_FALLING   SAMD21_EIC_CONFIG_SENSE_FALL
+#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_exti_samd21.c b/src/samd21/ao_exti_samd21.c
new file mode 100644 (file)
index 0000000..688f643
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * 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>
+
+struct ao_samd21_exti {
+       void                    (*callback)(void);
+       uint8_t                 pmux;
+       uint8_t                 pincfg;
+};
+
+static struct ao_samd21_exti ao_samd21_exti[SAMD21_NUM_EIC];
+
+static uint8_t
+pin_id(struct samd21_port *port, uint8_t pin)
+{
+       /* Why, atmel, why? */
+       if (port == &samd21_port_a) {
+               switch (pin) {
+               case 24:
+               case 25:
+               case 27:
+                       return pin - 12;
+               case 28:
+               case 30:
+               case 31:
+                       return pin - 20;
+               default:
+                       break;
+               }
+       }
+
+       /* most pins use exti mapped to their pin number directly (mod 16) */
+       return pin & 0xf;
+}
+
+
+void
+samd21_eic_isr(void)
+{
+       uint32_t        intflag = samd21_eic.intflag;
+       uint8_t         id;
+
+       for (id = 0; id < SAMD21_NUM_EIC; id++) {
+               uint32_t mask = (1 << id);
+
+               if (intflag & mask) {
+                       samd21_eic.intflag = mask;
+                       if (ao_samd21_exti[id].callback)
+                               (*ao_samd21_exti[id].callback)();
+               }
+       }
+}
+
+static void
+_ao_exti_set_sense(uint8_t id, uint8_t mode)
+{
+       uint8_t         sense = mode & (SAMD21_EIC_CONFIG_SENSE_RISE | SAMD21_EIC_CONFIG_SENSE_FALL);
+       uint8_t         n = SAMD21_EIC_CONFIG_N(id);
+       uint32_t        config;
+
+       config = samd21_eic.config[n];
+       config &= ~(SAMD21_EIC_CONFIG_SENSE_MASK << SAMD21_EIC_CONFIG_SENSE(id));
+       config |= (sense << SAMD21_EIC_CONFIG_SENSE(id));
+       samd21_eic.config[n] = config;
+}
+
+void
+ao_exti_setup (struct samd21_port *port, uint8_t pin, uint8_t mode, void (*callback)(void))
+{
+       uint8_t                 id = pin_id(port,pin);
+       struct ao_samd21_exti   *exti = &ao_samd21_exti[id];
+
+       if (exti->callback)
+               ao_panic(AO_PANIC_EXTI);
+
+       if (mode & AO_EXTI_PIN_NOCONFIGURE) {
+               ao_enable_port(port);
+               samd21_port_dir_set(port, pin, SAMD21_PORT_DIR_IN);
+               samd21_port_pincfg_set(port, pin,
+                                      (1 << SAMD21_PORT_PINCFG_INEN),
+                                      (1 << SAMD21_PORT_PINCFG_INEN));
+       } else {
+               ao_enable_input(port, pin, mode);
+       }
+
+       ao_arch_block_interrupts();
+
+       exti->callback = callback;
+
+       /* Set edge triggered */
+       _ao_exti_set_sense(id, mode);
+
+       ao_arch_release_interrupts();
+}
+
+void
+ao_exti_set_mode(struct samd21_port *port, uint8_t pin, uint8_t mode)
+{
+       uint8_t                 id = pin_id(port,pin);
+
+       ao_arch_block_interrupts();
+       _ao_exti_set_sense(id, mode);
+       ao_arch_release_interrupts();
+}
+
+void
+ao_exti_set_callback(struct samd21_port *port, uint8_t pin, void (*callback)(void))
+{
+       uint8_t         id = pin_id(port,pin);
+
+       ao_arch_block_interrupts();
+       ao_samd21_exti[id].callback = callback;
+       ao_arch_release_interrupts();
+}
+
+void
+ao_exti_enable(struct samd21_port *port, uint8_t pin)
+{
+       uint8_t         id = pin_id(port,pin);
+
+       ao_arch_block_interrupts();
+       /* configure gpio to interrupt routing */
+       if ((samd21_eic.intenset & (1 << id)) == 0) {
+               ao_samd21_exti[id].pmux = samd21_port_pmux_get(port, pin);
+               ao_samd21_exti[id].pincfg = samd21_port_pincfg_get(port, pin);
+       }
+       samd21_port_pmux_set(port, pin, SAMD21_PORT_PMUX_FUNC_A);
+       samd21_eic.intenset = 1 << id;
+       ao_arch_release_interrupts();
+}
+
+void
+ao_exti_disable(struct samd21_port *port, uint8_t pin)
+{
+       uint8_t         id = pin_id(port,pin);
+
+       ao_arch_block_interrupts();
+       samd21_eic.intenclr = 1 << id;
+       /* restore gpio config */
+       if (ao_samd21_exti[id].pincfg & (1 << SAMD21_PORT_PINCFG_PMUXEN))
+               samd21_port_pmux_set(port, pin, ao_samd21_exti[id].pmux);
+       else
+               samd21_port_pmux_clr(port, pin);
+       ao_arch_release_interrupts();
+}
+
+void
+ao_exti_init(void)
+{
+       samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_EIC);
+
+       /* Reset */
+       samd21_eic.ctrl = (1 << SAMD21_EIC_CTRL_SWRST);
+
+       while (samd21_eic.status & (1 << SAMD21_EIC_STATUS_SYNCBUSY))
+               ;
+
+       /* Wire up interrupts */
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_EIC_POS);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_EIC_POS, 3);
+
+       /* Enable */
+       samd21_eic.ctrl = (1 << SAMD21_EIC_CTRL_ENABLE);
+}
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..502ff35
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * 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)
+{
+       uint32_t lock_region = samd21_nvmctrl_lock_region();
+       uintptr_t a = (uintptr_t) addr;
+
+       while (lock_region) {
+               a >>= 1;
+               lock_region >>= 1;
+       }
+
+       return (uint8_t) a;
+}
+
+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;
+
+       if ((uintptr_t) page & (row_size - 1))
+               return;
+
+       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_shift = samd21_nvmctrl_page_shift();
+       uint32_t        pages = 256 >> page_shift;
+       uint32_t        i;
+       uint32_t        per_page = 1 << (page_shift - 2);
+
+       ao_flash_erase_page(page);
+
+       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..1120f65
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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 struct ao_samd21_usart  ao_samd21_usart0;
+
+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 struct ao_samd21_usart  ao_samd21_usart1;
+
+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 struct ao_samd21_usart  ao_samd21_usart2;
+
+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 struct ao_samd21_usart  ao_samd21_usart3;
+
+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
+
+#if HAS_SERIAL_4
+extern struct ao_samd21_usart  ao_samd21_usart4;
+
+char
+ao_serial4_getchar(void);
+
+int
+_ao_serial4_pollchar(void);
+
+uint8_t
+_ao_serial4_sleep_for(uint16_t timeout);
+
+void
+ao_serial4_putchar(char c);
+
+void
+ao_serial4_drain(void);
+
+void
+ao_serial4_set_speed(uint8_t speed);
+#endif
+
+#if HAS_SERIAL_5
+extern struct ao_samd21_usart  ao_samd21_usart5;
+
+char
+ao_serial5_getchar(void);
+
+int
+_ao_serial5_pollchar(void);
+
+uint8_t
+_ao_serial5_sleep_for(uint16_t timeout);
+
+void
+ao_serial5_putchar(char c);
+
+void
+ao_serial5_drain(void);
+
+void
+ao_serial5_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..97d8379
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * 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);
+                       ao_fifo_remove(usart->tx_fifo, usart->reg->data);
+                       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)) {
+               uint8_t data = (uint8_t) usart->reg->data;
+               if (!ao_fifo_full(usart->rx_fifo)) {
+                       ao_fifo_insert(usart->rx_fifo, 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
+               }
+       }
+}
+
+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)
+{
+       struct samd21_sercom *reg = usart->reg;
+       uint64_t        top = (uint64_t) ao_usart_speeds[speed] << (4 + 16);
+       uint16_t        baud = (uint16_t) (65536 - (top + AO_SYSCLK/2) / AO_SYSCLK);
+       uint32_t        ctrla = reg->ctrla;
+
+       if (ctrla & (1UL << SAMD21_SERCOM_CTRLA_ENABLE)) {
+               usart->reg->ctrla = ctrla & ~(1UL << SAMD21_SERCOM_CTRLA_ENABLE);
+               while (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
+                       ;
+       }
+       usart->reg->baud = baud;
+       if (ctrla & (1UL << SAMD21_SERCOM_CTRLA_ENABLE)) {
+               usart->reg->ctrla = ctrla;
+               while (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
+                       ;
+       }
+}
+
+static void
+ao_usart_init(struct ao_samd21_usart *usart, bool hw_flow, uint8_t id, uint8_t txpo, uint8_t rxpo)
+{
+       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) |
+                     (txpo << SAMD21_SERCOM_CTRLA_TXPO) |      /* pad[2] */
+                     (rxpo << 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);
+
+       while (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
+               ;
+
+}
+
+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;
+               ao_fifo_remove(usart->rx_fifo, u);
+#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 */
+
+#if HAS_SERIAL_1
+
+struct ao_samd21_usart ao_samd21_usart1;
+
+void samd21_sercom1_isr(void) { ao_usart_isr(&ao_samd21_usart1, USE_SERIAL_1_STDIN); }
+
+char
+ao_serial1_getchar(void)
+{
+       return ao_usart_getchar(&ao_samd21_usart1);
+}
+
+void
+ao_serial1_putchar(char c)
+{
+       ao_usart_putchar(&ao_samd21_usart1, c);
+}
+
+int
+_ao_serial1_pollchar(void)
+{
+       return _ao_usart_pollchar(&ao_samd21_usart1);
+}
+
+void
+ao_serial1_drain(void)
+{
+       ao_usart_drain(&ao_samd21_usart1);
+}
+
+void
+ao_serial1_set_speed(uint8_t speed)
+{
+       ao_usart_drain(&ao_samd21_usart1);
+       ao_usart_set_speed(&ao_samd21_usart1, speed);
+}
+#endif /* HAS_SERIAL_1 */
+
+void
+ao_serial_init(void)
+{
+       uint8_t txpo, rxpo;
+#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);
+       txpo = SAMD21_SERCOM_CTRLA_TXPO_TX_2; /* pad 2 */
+       rxpo = SAMD21_SERCOM_CTRLA_RXPO_RX_3; /* pad 3 */
+#elif SERIAL_0_PA08_PA09
+       /* Pin settings */
+       ao_enable_port(&samd21_port_a);
+       samd21_port_pmux_set(&samd21_port_a, 8, SAMD21_PORT_PMUX_FUNC_C);
+       samd21_port_pmux_set(&samd21_port_a, 9, SAMD21_PORT_PMUX_FUNC_C);
+       txpo = SAMD21_SERCOM_CTRLA_TXPO_TX_0; /* pad 0 */
+       rxpo = SAMD21_SERCOM_CTRLA_RXPO_RX_1; /* pad 1 */
+#else
+#error "No SERIAL_0 port configuration specified"
+#endif
+
+       ao_samd21_usart0.reg = &samd21_sercom0;
+       ao_usart_init(&ao_samd21_usart0, 0, 0, txpo, rxpo);
+
+#if USE_SERIAL_0_STDIN
+       ao_add_stdio(_ao_serial0_pollchar,
+                    ao_serial0_putchar,
+                    NULL);
+#endif
+#endif
+#if HAS_SERIAL_1
+
+#if SERIAL_1_PA00_PA01
+       /* Pin settings */
+       ao_enable_port(&samd21_port_a);
+       samd21_port_pmux_set(&samd21_port_a, 0, SAMD21_PORT_PMUX_FUNC_D);
+       samd21_port_pmux_set(&samd21_port_a, 1, SAMD21_PORT_PMUX_FUNC_D);
+       txpo = SAMD21_SERCOM_CTRLA_TXPO_TX_0;
+       rxpo = SAMD21_SERCOM_CTRLA_RXPO_RX_1;
+#else
+#error "No SERIAL_1 port configuration specified"
+#endif
+
+       ao_samd21_usart1.reg = &samd21_sercom1;
+       ao_usart_init(&ao_samd21_usart1, 0, 1, txpo, rxpo);
+
+#if USE_SERIAL_1_STDIN
+       ao_add_stdio(_ao_serial1_pollchar,
+                    ao_serial1_putchar,
+                    NULL);
+#endif
+#endif
+}
diff --git a/src/samd21/ao_spi_samd21.c b/src/samd21/ao_spi_samd21.c
new file mode 100644 (file)
index 0000000..aee8cb1
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+ * 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.
+ */
+
+#include <ao.h>
+#include <ao_dma_samd21.h>
+
+static uint8_t         ao_spi_mutex[SAMD21_NUM_SERCOM];
+static uint16_t                ao_spi_pin_config[SAMD21_NUM_SERCOM];
+
+#define SPI_DEBUG      0
+#define SPI_USE_DMA    1
+
+struct ao_spi_samd21_info {
+       struct samd21_sercom    *sercom;
+};
+
+static const struct ao_spi_samd21_info ao_spi_samd21_info[SAMD21_NUM_SERCOM] = {
+       {
+               .sercom = &samd21_sercom0,
+       },
+       {
+               .sercom = &samd21_sercom1,
+       },
+       {
+               .sercom = &samd21_sercom2,
+       },
+       {
+               .sercom = &samd21_sercom3,
+       },
+       {
+               .sercom = &samd21_sercom4,
+       },
+       {
+               .sercom = &samd21_sercom5,
+       },
+};
+
+static uint8_t spi_dev_null;
+
+#if SPI_USE_DMA
+
+static uint8_t ao_spi_done[SAMD21_NUM_SERCOM];
+
+static void
+_ao_spi_recv_dma_done(uint8_t dma_id, void *closure)
+{
+       uint8_t id = (uint8_t) (uintptr_t) closure;
+
+       (void) dma_id;
+       ao_spi_done[id] = 1;
+       ao_wakeup(&ao_spi_done[id]);
+}
+
+static inline uint32_t
+dma_chctrlb(uint8_t id, bool tx)
+{
+       uint32_t        chctrlb = 0;
+
+       /* No complicated actions needed */
+       chctrlb |= SAMD21_DMAC_CHCTRLB_CMD_NOACT << SAMD21_DMAC_CHCTRLB_CMD;
+
+       /* Trigger after each byte transferred */
+       chctrlb |= SAMD21_DMAC_CHCTRLB_TRIGACT_BEAT << SAMD21_DMAC_CHCTRLB_TRIGACT;
+
+       /* Set the trigger source */
+       if (tx)
+               chctrlb |= SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_TX(id) << SAMD21_DMAC_CHCTRLB_TRIGSRC;
+       else
+               chctrlb |= SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_RX(id) << SAMD21_DMAC_CHCTRLB_TRIGSRC;
+
+       /* RX has priority over TX so that we don't drop incoming bytes */
+       if (tx)
+               chctrlb |= SAMD21_DMAC_CHCTRLB_LVL_LVL0 << SAMD21_DMAC_CHCTRLB_LVL;
+       else
+               chctrlb |= SAMD21_DMAC_CHCTRLB_LVL_LVL3 << SAMD21_DMAC_CHCTRLB_LVL;
+
+       /* No events needed */
+       chctrlb |= 0UL << SAMD21_DMAC_CHCTRLB_EVOE;
+       chctrlb |= 0UL << SAMD21_DMAC_CHCTRLB_EVIE;
+
+       /* And no actions either */
+       chctrlb |= SAMD21_DMAC_CHCTRLB_EVACT_NOACT << SAMD21_DMAC_CHCTRLB_EVACT;
+
+       return chctrlb;
+}
+
+static inline uint16_t
+dma_btctrl(bool step, bool tx)
+{
+       uint16_t        btctrl = 0;
+
+       /* Always step by 1 */
+       btctrl |= SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X1 << SAMD21_DMAC_DESC_BTCTRL_STEPSIZE;
+
+       /* Step the source if transmit, otherwise step the dest */
+       if (tx)
+               btctrl |= SAMD21_DMAC_DESC_BTCTRL_STEPSEL_SRC << SAMD21_DMAC_DESC_BTCTRL_STEPSEL;
+       else
+               btctrl |= SAMD21_DMAC_DESC_BTCTRL_STEPSEL_DST << SAMD21_DMAC_DESC_BTCTRL_STEPSEL;
+
+       /* Set the increment if stepping */
+       if (tx) {
+               if (step)
+                       btctrl |= 1UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC;
+               else
+                       btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC;
+               btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC;
+       } else {
+               btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC;
+               if (step)
+                       btctrl |= 1UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC;
+               else
+                       btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC;
+       }
+
+       /* byte at a time please */
+       btctrl |= SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_BYTE << SAMD21_DMAC_DESC_BTCTRL_BEATSIZE;
+
+       /*
+        * Watch for interrupts on RX -- we need to wait for the last byte to get received
+        * to know the SPI bus is idle
+        */
+       if (tx)
+               btctrl |= SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_NOACT << SAMD21_DMAC_DESC_BTCTRL_BLOCKACT;
+       else
+               btctrl |= SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_INT << SAMD21_DMAC_DESC_BTCTRL_BLOCKACT;
+
+       /* don't need any events */
+       btctrl |= SAMD21_DMAC_DESC_BTCTRL_EVOSEL_DISABLE << SAMD21_DMAC_DESC_BTCTRL_EVOSEL;
+
+       /* And make the descriptor valid */
+       btctrl |= 1UL << SAMD21_DMAC_DESC_BTCTRL_VALID;
+
+       return btctrl;
+}
+
+static void
+spi_run(const void *out, void *in, uint16_t len, uint16_t spi_index, bool step_out, bool step_in)
+{
+       const uint8_t           *o = out;
+       uint8_t                 *i = in;
+       uint8_t                 id = AO_SPI_INDEX(spi_index);
+       struct samd21_sercom    *sercom = ao_spi_samd21_info[id].sercom;
+
+       ao_arch_block_interrupts();
+       ao_spi_done[id] = 0;
+
+       /*
+        * Stepped addresses to the DMA engine point past the end of
+        * the block
+        */
+       if (step_out)
+               o += len;
+       if (step_in)
+               i += len;
+
+       /* read any stuck data */
+       (void) sercom->data;
+
+       _ao_dma_start_transfer(AO_SERCOM_INPUT_DMA_ID(id),
+                              (void *) &sercom->data,
+                              i,
+                              len,
+                              dma_chctrlb(id, false),
+                              dma_btctrl(step_in, false),
+
+                              _ao_spi_recv_dma_done,
+                              (void *) (uintptr_t) id
+               );
+
+       _ao_dma_start_transfer(AO_SERCOM_OUTPUT_DMA_ID(id),
+                              o,
+                              (void *) &sercom->data,
+                              len,
+                              dma_chctrlb(id, true),
+                              dma_btctrl(step_out, true),
+                              NULL,
+                              NULL
+               );
+
+       while (ao_spi_done[id] == 0)
+               ao_sleep(&ao_spi_done[id]);
+
+       _ao_dma_done_transfer(AO_SERCOM_OUTPUT_DMA_ID(id));
+       _ao_dma_done_transfer(AO_SERCOM_INPUT_DMA_ID(id));
+       ao_arch_release_interrupts();
+}
+
+#else
+
+static void
+spi_run(const void *out, void *in, uint16_t len, uint16_t spi_index, bool step_out, bool step_in)
+{
+       uint8_t                 id = AO_SPI_INDEX(spi_index);
+       struct samd21_sercom    *sercom = ao_spi_samd21_info[id].sercom;
+       const uint8_t           *o = out;
+       uint8_t                 *i = in;
+
+       while (len--) {
+#if SPI_DEBUG
+               printf("%02x", *o);
+#endif
+               sercom->data = *o;
+               while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0)
+                       ;
+               *i = (uint8_t) sercom->data;
+#if SPI_DEBUG
+               printf("\t%02x\n", *i);
+#endif
+               if (step_out)
+                       o++;
+               if (step_in)
+                       i++;
+       }
+}
+
+#endif
+
+void
+ao_spi_send(const void *block, uint16_t len, uint16_t spi_index)
+{
+       spi_run(block, &spi_dev_null, len, spi_index, true, false);
+}
+
+void
+ao_spi_send_fixed(uint8_t data, uint16_t len, uint16_t spi_index)
+{
+       spi_run(&data, &spi_dev_null, len, spi_index, false, false);
+}
+
+void
+ao_spi_recv(void *block, uint16_t len, uint16_t spi_index)
+{
+       spi_dev_null = 0xff;
+       spi_run(&spi_dev_null, block, len, spi_index, false, true);
+}
+
+
+void
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint16_t spi_index)
+{
+       spi_run(out, in, len, spi_index, true, true);
+}
+
+static void
+ao_spi_disable_pin_config(uint16_t spi_pin_config)
+{
+       switch (spi_pin_config) {
+#if HAS_SPI_0
+       case AO_SPI_PIN_CONFIG(AO_SPI_0_PA08_PA09_PA10):
+               samd21_port_pmux_clr(&samd21_port_a, 8);        /* MOSI */
+               samd21_port_pmux_clr(&samd21_port_a, 9);        /* SCLK */
+               samd21_port_pmux_clr(&samd21_port_a, 10);       /* MISO */
+               break;
+       case AO_SPI_PIN_CONFIG(AO_SPI_0_PA04_PA05_PA06):
+               samd21_port_pmux_clr(&samd21_port_a, 4);        /* MOSI */
+               samd21_port_pmux_clr(&samd21_port_a, 5);        /* SCLK */
+               samd21_port_pmux_clr(&samd21_port_a, 6);        /* MISO */
+               break;
+#endif
+#if HAS_SPI_3
+       case AO_SPI_PIN_CONFIG(AO_SPI_3_PA22_PA23_PA20):
+               samd21_port_pmux_clr(&samd21_port_a, 22);       /* MOSI */
+               samd21_port_pmux_clr(&samd21_port_a, 23);       /* SCLK */
+               samd21_port_pmux_clr(&samd21_port_a, 20);       /* MISO */
+               break;
+#endif
+#if HAS_SPI_4
+       case AO_SPI_PIN_CONFIG(AO_SPI_4_PB10_PB11_PA12):
+               samd21_port_pmux_clr(&samd21_port_b, 10);       /* MOSI */
+               samd21_port_pmux_clr(&samd21_port_b, 11);       /* SCLK */
+               samd21_port_pmux_clr(&samd21_port_a, 12);       /* MISO */
+               break;
+#endif
+#if HAS_SPI_5
+       case AO_SPI_PIN_CONFIG(AO_SPI_5_PB22_PB23_PB03):
+               samd21_port_pmux_clr(&samd21_port_b, 22);       /* MOSI */
+               samd21_port_pmux_clr(&samd21_port_b, 23);       /* SCLK */
+               samd21_port_pmux_clr(&samd21_port_b, 3);        /* MISO */
+               break;
+#endif
+       case 0xffff:
+               break;
+       }
+}
+
+static void
+ao_spi_enable_pin_config(uint16_t spi_pin_config)
+{
+       switch (spi_pin_config) {
+#if HAS_SPI_0
+       case AO_SPI_PIN_CONFIG(AO_SPI_0_PA08_PA09_PA10):
+               ao_enable_output(&samd21_port_a, 8, 1);
+               ao_enable_output(&samd21_port_a, 9, 1);
+               ao_enable_input(&samd21_port_a, 10, AO_MODE_PULL_NONE);
+
+               samd21_port_pmux_set(&samd21_port_a, 8, SAMD21_PORT_PMUX_FUNC_C);       /* MOSI */
+               samd21_port_pmux_set(&samd21_port_a, 9, SAMD21_PORT_PMUX_FUNC_C);       /* SCLK */
+               samd21_port_pmux_set(&samd21_port_a, 10, SAMD21_PORT_PMUX_FUNC_C);      /* MISO */
+               break;
+       case AO_SPI_PIN_CONFIG(AO_SPI_0_PA04_PA05_PA06):
+               ao_enable_output(&samd21_port_a, 4, 1);
+               ao_enable_output(&samd21_port_a, 5, 1);
+               ao_enable_input(&samd21_port_a, 6, AO_MODE_PULL_NONE);
+
+               samd21_port_pmux_set(&samd21_port_a, 4, SAMD21_PORT_PMUX_FUNC_D);       /* MOSI */
+               samd21_port_pmux_set(&samd21_port_a, 5, SAMD21_PORT_PMUX_FUNC_D);       /* SCLK */
+               samd21_port_pmux_set(&samd21_port_a, 6, SAMD21_PORT_PMUX_FUNC_D);       /* MISO */
+               break;
+#endif
+#if HAS_SPI_3
+       case AO_SPI_PIN_CONFIG(AO_SPI_3_PA22_PA23_PA20):
+               ao_enable_output(&samd21_port_a, 22, 1);
+               ao_enable_output(&samd21_port_a, 23, 1);
+               ao_enable_input(&samd21_port_a, 20, AO_MODE_PULL_NONE);
+
+               samd21_port_pmux_set(&samd21_port_a, 22, SAMD21_PORT_PMUX_FUNC_C);      /* MOSI */
+               samd21_port_pmux_set(&samd21_port_a, 23, SAMD21_PORT_PMUX_FUNC_C);      /* SCLK */
+               samd21_port_pmux_set(&samd21_port_a, 20, SAMD21_PORT_PMUX_FUNC_D);      /* MISO */
+               break;
+#endif
+#if HAS_SPI_4
+       case AO_SPI_PIN_CONFIG(AO_SPI_4_PB10_PB11_PA12):
+               ao_enable_output(&samd21_port_b, 10, 1);
+               ao_enable_output(&samd21_port_b, 11, 1);
+               ao_enable_input(&samd21_port_a, 12, AO_MODE_PULL_NONE);
+
+               samd21_port_pmux_set(&samd21_port_b, 10, SAMD21_PORT_PMUX_FUNC_D);      /* MOSI */
+               samd21_port_pmux_set(&samd21_port_b, 11, SAMD21_PORT_PMUX_FUNC_D);      /* SCLK */
+               samd21_port_pmux_set(&samd21_port_a, 12, SAMD21_PORT_PMUX_FUNC_D);      /* MISO */
+               break;
+#endif
+#if HAS_SPI_5
+       case AO_SPI_PIN_CONFIG(AO_SPI_5_PB22_PB23_PB03):
+               ao_enable_output(&samd21_port_b, 22, 1);
+               ao_enable_output(&samd21_port_b, 23, 1);
+               ao_enable_input(&samd21_port_b, 3, AO_MODE_PULL_NONE);
+
+               samd21_port_pmux_set(&samd21_port_b, 22, SAMD21_PORT_PMUX_FUNC_D);      /* 5.2 MOSI */
+               samd21_port_pmux_set(&samd21_port_b, 23, SAMD21_PORT_PMUX_FUNC_D);      /* 5.3 SCLK */
+               samd21_port_pmux_set(&samd21_port_b, 3, SAMD21_PORT_PMUX_FUNC_D);       /* 5.1 MISO */
+               break;
+#endif
+       default:
+               ao_panic(AO_PANIC_SPI);
+               break;
+       }
+}
+
+static void
+ao_spi_config(uint16_t spi_index, uint32_t baud)
+{
+       uint16_t                spi_pin_config = AO_SPI_PIN_CONFIG(spi_index);
+       uint8_t                 id = AO_SPI_INDEX(spi_index);
+       struct samd21_sercom    *sercom = ao_spi_samd21_info[id].sercom;
+
+       if (spi_pin_config != ao_spi_pin_config[id]) {
+               ao_spi_disable_pin_config(ao_spi_pin_config[id]);
+               ao_spi_enable_pin_config(spi_pin_config);
+               ao_spi_pin_config[id] = spi_pin_config;
+       }
+
+       sercom->baud = (uint16_t) baud;
+
+       /* Set spi mode */
+       uint32_t ctrla = sercom->ctrla;
+       ctrla &= ~((1UL << SAMD21_SERCOM_CTRLA_CPOL) |
+                  (1UL << SAMD21_SERCOM_CTRLA_CPHA) |
+                  (SAMD21_SERCOM_CTRLA_DOPO_MASK << SAMD21_SERCOM_CTRLA_DOPO) |
+                  (SAMD21_SERCOM_CTRLA_DIPO_MASK << SAMD21_SERCOM_CTRLA_DIPO));
+       ctrla |= ((AO_SPI_CPOL(spi_index) << SAMD21_SERCOM_CTRLA_CPOL) |
+                 (AO_SPI_CPHA(spi_index) << SAMD21_SERCOM_CTRLA_CPHA) |
+                 (AO_SPI_DOPO(spi_index) << SAMD21_SERCOM_CTRLA_DOPO) |
+                 (AO_SPI_DIPO(spi_index) << SAMD21_SERCOM_CTRLA_DIPO));
+
+       /* finish setup and enable the hardware */
+       ctrla |= (1 << SAMD21_SERCOM_CTRLA_ENABLE);
+
+#if SPI_DEBUG
+       printf("ctrla %08lx\n", ctrla);
+#endif
+
+       sercom->ctrla = ctrla;
+
+       while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
+               ;
+}
+
+void
+ao_spi_get(uint16_t spi_index, uint32_t speed)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+
+       ao_mutex_get(&ao_spi_mutex[id]);
+       ao_spi_config(spi_index, speed);
+}
+
+void
+ao_spi_put(uint16_t spi_index)
+{
+       uint8_t                 id = AO_SPI_INDEX(spi_index);
+       struct samd21_sercom    *sercom = ao_spi_samd21_info[id].sercom;
+
+       sercom->ctrla &= ~(1UL << SAMD21_SERCOM_CTRLA_ENABLE);
+       while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
+               ;
+       ao_mutex_put(&ao_spi_mutex[id]);
+}
+
+static void
+ao_spi_init_sercom(uint8_t id)
+{
+       struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
+
+       /* 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 */
+       sercom->ctrla = (1 << SAMD21_SERCOM_CTRLA_SWRST);
+
+       while ((sercom->ctrla & (1 << SAMD21_SERCOM_CTRLA_SWRST)) ||
+              (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_SWRST)))
+               ;
+
+       /* set SPI mode */
+       sercom->ctrla = ((SAMD21_SERCOM_CTRLA_DORD_MSB << SAMD21_SERCOM_CTRLA_DORD) |
+                        (0 << SAMD21_SERCOM_CTRLA_CPOL) |
+                        (0 << SAMD21_SERCOM_CTRLA_CPHA) |
+                        (0 << SAMD21_SERCOM_CTRLA_FORM) |
+                        (2 << SAMD21_SERCOM_CTRLA_DIPO) |
+                        (0 << SAMD21_SERCOM_CTRLA_DOPO) |
+                        (0 << SAMD21_SERCOM_CTRLA_IBON) |
+                        (0 << SAMD21_SERCOM_CTRLA_RUNSTDBY) |
+                        (SAMD21_SERCOM_CTRLA_MODE_SPI_HOST << SAMD21_SERCOM_CTRLA_MODE) |
+                        (0 << SAMD21_SERCOM_CTRLA_ENABLE) |
+                        (0 << SAMD21_SERCOM_CTRLA_SWRST));
+
+       sercom->ctrlb = ((1 << SAMD21_SERCOM_CTRLB_RXEN) |
+                        (0 << SAMD21_SERCOM_CTRLB_AMODE) |
+                        (0 << SAMD21_SERCOM_CTRLB_MSSEN) |
+                        (0 << SAMD21_SERCOM_CTRLB_SSDE) |
+                        (0 << SAMD21_SERCOM_CTRLB_PLOADEN) |
+                        (SAMD21_SERCOM_CTRLB_CHSIZE_8 << SAMD21_SERCOM_CTRLB_CHSIZE));
+
+       ao_spi_pin_config[id] = 0xffff;
+}
+
+void
+ao_spi_init(void)
+{
+#if HAS_SPI_0
+       ao_spi_init_sercom(0);
+#endif
+#if HAS_SPI_1
+       ao_spi_init_sercom(1);
+#endif
+#if HAS_SPI_2
+       ao_spi_init_sercom(2);
+#endif
+#if HAS_SPI_3
+       ao_spi_init_sercom(3);
+#endif
+#if HAS_SPI_4
+       ao_spi_init_sercom(4);
+#endif
+#if HAS_SPI_5
+       ao_spi_init_sercom(5);
+#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..2b431d3
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * 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;
+               ao_task_check_alarm();
+#if AO_DATA_ALL
+               if (++ao_data_count == ao_data_interval && ao_data_interval) {
+                       ao_data_count = 0;
+#if HAS_FAKE_FLIGHT
+                       if (ao_fake_flight_active)
+                               ao_fake_flight_poll();
+                       else
+#endif
+                               ao_adc_poll();
+#if (AO_DATA_ALL & ~(AO_DATA_ADC))
+                       ao_wakeup((void *) &ao_data_count);
+#endif
+               }
+#endif
+#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..bed4f54
--- /dev/null
@@ -0,0 +1,875 @@
+/*
+ * 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
+
+#if USE_USB_STDIO
+#define AO_USB_OUT_SLEEP_ADDR  (&ao_stdin_ready)
+#else
+#define AO_USB_OUT_SLEEP_ADDR  (&ao_usb_out_avail)
+#endif
+
+#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;
+
+                       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);
+               }
+#if AO_USB_OUT_HOOK
+               ao_usb_out_hook(buf, len);
+#endif
+       }
+}
+#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;
+
+       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;
+
+       ao_enable_port(&samd21_port_a);
+
+       /* 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.ctrla & (1 << SAMD21_USB_CTRLA_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;
+
+#if USE_USB_STDIO
+       ao_add_stdio(_ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
+#endif
+}
diff --git a/src/samd21/registers.ld b/src/samd21/registers.ld
new file mode 100644 (file)
index 0000000..4ff885b
--- /dev/null
@@ -0,0 +1,53 @@
+samd21_scs     = 0xe000e000;
+samd21_systick = 0xe000e010;
+samd21_nvic    = 0xe000e100;
+samd21_scb     = 0xe000ed00;
+
+samd21_ahb_apb_a = 0x40000000;
+samd21_pac0    = 0x40000000;
+samd21_pm      = 0x40000400;
+samd21_sysctrl = 0x40000800;
+samd21_gclk    = 0x40000c00;
+samd21_wdt     = 0x40001000;
+samd21_rtc     = 0x40001400;
+samd21_rtc     = 0x40001400;
+samd21_eic     = 0x40001800;
+
+samd21_ahb_apb_b = 0x41000000;
+samd21_pac1    = 0x41000000;
+samd21_dsu     = 0x41002000;
+samd21_nvmctrl = 0x41004000;
+samd21_port_a  = 0x41004400;
+samd21_port_b  = 0x41004480;
+samd21_dmac    = 0x41004800;
+samd21_usb     = 0x41005000;
+samd21_mtb     = 0x41006000;
+
+samd21_ahb_apb_c = 0x42000000;
+samd21_pac2    = 0x42000000;
+samd21_evsys   = 0x42000400;
+samd21_sercom0 = 0x42000800;
+samd21_sercom1 = 0x42000c00;
+samd21_sercom2 = 0x42001000;
+samd21_sercom3 = 0x42001400;
+samd21_sercom4 = 0x42001800;
+samd21_sercom5 = 0x42001c00;
+samd21_tcc0    = 0x42002000;
+samd21_tcc1    = 0x42002400;
+samd21_tcc2    = 0x42002800;
+samd21_tc3     = 0x42002c00;
+samd21_tc4     = 0x42003000;
+samd21_tc5     = 0x42003400;
+samd21_tc6     = 0x42003800;
+samd21_tc7     = 0x42003c00;
+samd21_adc     = 0x42004000;
+samd21_ac      = 0x42004400;
+samd21_dac     = 0x42004800;
+samd21_ptc     = 0x42004c00;
+samd21_i2s     = 0x42005000;
+samd21_ac1     = 0x42005400;
+samd21_tcc3    = 0x42006000;
+
+samd21_aux0    = 0x00804000;
+samd21_aux1    = 0x00806000;
+samd21_serial  = 0x0080a000;
diff --git a/src/samd21/samd21.h b/src/samd21/samd21.h
new file mode 100644 (file)
index 0000000..b101c6b
--- /dev/null
@@ -0,0 +1,2102 @@
+/*
+ * 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_LVL_LVL0          0UL
+#define  SAMD21_DMAC_CHCTRLB_LVL_LVL1          1UL
+#define  SAMD21_DMAC_CHCTRLB_LVL_LVL2          2UL
+#define  SAMD21_DMAC_CHCTRLB_LVL_LVL3          3UL
+#define SAMD21_DMAC_CHCTRLB_TRIGSRC    8
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_DISABLE   0x00UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_RX(n)      (0x01UL + (n) * 2UL)
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_TX(n)      (0x02UL + (n) * 2UL)
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_OVF  0x0dUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC0  0x0eUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC1  0x0fUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC2  0x10UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC3  0x11UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_OVF  0x12UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_MC0  0x13UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_MC1  0x14UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_OVF  0x15UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_MC0  0x16UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_MC1  0x17UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_OVF   0x18UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_MC0   0x19UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_MC1   0x1aUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_OVF   0x1bUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_MC0   0x1cUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_MC1   0x1dUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_OVF   0x1eUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_MC0   0x1fUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_MC1   0x20UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_OVF   0x21UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_MC0   0x22UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_MC1   0x23UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_OVF   0x24UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_MC0   0x25UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_MC1   0x26UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_ADC_RESRDY        0x27UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_DAC_EMPTY 0x28UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_RX_0  0x29UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_RX_1  0x2aUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_TX_0  0x2bUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_TX_1  0x2cUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_OVF  0x2dUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC0  0x2eUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC1  0x2fUL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC2  0x30UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC3  0x31UL
+
+#define SAMD21_DMAC_CHCTRLB_TRIGACT    22
+#define  SAMD21_DMAC_CHCTRLB_TRIGACT_BLOCK             0UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGACT_BEAT              2UL
+#define  SAMD21_DMAC_CHCTRLB_TRIGACT_TRANSACTION       3UL
+
+#define SAMD21_DMAC_CHCTRLB_CMD                24
+#define  SAMD21_DMAC_CHCTRLB_CMD_NOACT         0UL
+#define  SAMD21_DMAC_CHCTRLB_CMD_SUSPEND       1UL
+#define  SAMD21_DMAC_CHCTRLB_CMD_RESUME                2UL
+
+#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_EVOSEL_DISABLE                0UL
+#define  SAMD21_DMAC_DESC_BTCTRL_EVOSEL_BLOCK          1UL
+#define  SAMD21_DMAC_DESC_BTCTRL_EVOSEL_BEAT           3UL
+#define SAMD21_DMAC_DESC_BTCTRL_BLOCKACT       3
+#define  SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_NOACT                0UL
+#define  SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_INT          1UL
+#define  SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_SUSPEND      2UL
+#define  SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_BOTH         3UL
+#define SAMD21_DMAC_DESC_BTCTRL_BEATSIZE       8
+#define  SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_BYTE         0UL
+#define  SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_HWORD                1UL
+#define  SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_WORD         2UL
+#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_STEPSEL_DST           0UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSEL_SRC           1UL
+#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE       13
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X1           0UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X2           1UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X4           2UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X8           3UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X16          4UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X32          5UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X64          6UL
+#define  SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X128         7UL
+
+struct samd21_eic {
+       vuint8_t        ctrl;
+       vuint8_t        status;
+       vuint8_t        nmictrl;
+       vuint8_t        nmiflag;
+       vuint32_t       evctrl;
+       vuint32_t       intenclr;
+       vuint32_t       intenset;
+
+       vuint32_t       intflag;
+       vuint32_t       wakeup;
+       vuint32_t       config[2];
+};
+
+extern struct samd21_eic samd21_eic;
+
+#define samd21_eic     (*(struct samd21_eic *) 0x40001800)
+
+#define SAMD21_NUM_EIC                 16
+
+#define SAMD21_EIC_CTRL_ENABLE         1
+#define SAMD21_EIC_CTRL_SWRST          0
+
+#define SAMD21_EIC_STATUS_SYNCBUSY     7
+
+#define SAMD21_EIC_NMICTRL_NMIFILTEN   3
+#define SAMD21_EIC_NMICTRL_NMISENSE    0
+
+#define SAMD21_EIC_NMIFLAG_NMI         0
+
+#define SAMD21_EIC_EVCTRL_EXTINTEO(n)  (n)
+
+#define SAMD21_EIC_INTENCLR_EXTINT(n)  (n)
+
+#define SAMD21_EIC_INTENSET_EXTINT(n)  (n)
+
+#define SAMD21_EIC_INTFLAG_EXTINT(n)   (n)
+#define SAMD21_EIC_WAKEUP_WAKEUPEN(n)  (n)
+#define SAMD21_EIC_CONFIG_N(n)         ((n) >> 3)
+#define SAMD21_EIC_CONFIG_SENSE(n)     (((n) & 7) << 2)
+#define SAMD21_EIC_CONFIG_FILTEN(n)    (SAMD21_EIC_CONFIG_SENSE(n) + 3)
+#define  SAMD21_EIC_CONFIG_SENSE_NONE          0
+#define  SAMD21_EIC_CONFIG_SENSE_RISE          1
+#define  SAMD21_EIC_CONFIG_SENSE_FALL          2
+#define  SAMD21_EIC_CONFIG_SENSE_BOTH          3
+#define  SAMD21_EIC_CONFIG_SENSE_HIGH          4
+#define  SAMD21_EIC_CONFIG_SENSE_LOW           5
+#define  SAMD21_EIC_CONFIG_SENSE_MASK          7UL
+
+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_shift(void)
+{
+       return(3 + ((samd21_nvmctrl.param >> SAMD21_NVMCTRL_PARAM_PSZ) &
+                   SAMD21_NVMCTRL_PARAM_PSZ_MASK));
+}
+
+static inline uint32_t
+samd21_nvmctrl_page_size(void)
+{
+       return 1 << samd21_nvmctrl_page_shift();
+}
+
+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));
+}
+
+static inline uint8_t
+samd21_port_pmux_get(struct samd21_port *port, uint8_t pin)
+{
+       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) >> bit);
+       return value;
+}
+
+static inline void
+samd21_port_pmux_clr(struct samd21_port *port, uint8_t pin)
+{
+       samd21_port_pincfg_set(port, pin,
+                              (0 << 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_TEMP              0x18
+# 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;
+}
+
+/* evsys */
+
+struct samd21_evsys {
+       vuint8_t        ctrl;
+       vuint8_t        reserved_01;
+       vuint16_t       reserved_02;
+       vuint32_t       channel;
+       vuint16_t       user;
+       vuint16_t       reserved_0a;
+       vuint32_t       chstatus;
+
+       vuint32_t       intenclr;
+       vuint32_t       intenset;
+       vuint32_t       intflag;
+};
+
+extern struct samd21_evsys samd21_evsys;
+
+#define SAMD21_NUM_EVSYS       16
+
+#define samd21_evsys   (*(struct samd21_evsys *) 0x42000400)
+
+#define SAMD21_EVSYS_CONTROL_SWRST     0
+#define SAMD21_EVSYS_CONTROL_GCLKREQ   4
+
+#define SAMD21_EVSYS_CHANNEL_CHANNEL   0
+
+#define SAMD21_EVSYS_CHANNEL_SWEVT     8
+
+#define SAMD21_EVSYS_CHANNEL_EVGEN     16
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_NONE               0x00
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_RTC_CMP(i)         (0x01 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_OVF                        0x03
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_PER(i)             (0x04 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_EXTINT(i)          (0x0c + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_DMAC_CH(i)         (0x1e + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC0_OVF           0x22
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC0_TRG           0x23
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC0_CNT           0x29
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC0_MCX(i)                (0x25 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC1_OVF           0x29
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC1_TRG           0x2a
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC1_CNT           0x2b
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC1_MCX(i)                (0x2c + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC2_OVF           0x2e
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC2_TRG           0x2f
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC2_CNT           0x30
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC2_MCX(i)                (0x31 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC3_OVF            0x33
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC3_MC(i)          (0x34 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC4_OVF            0x36
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC4_MC(i)          (0x37 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC5_OVF            0x39
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC5_MC(i)          (0x3a + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC6_OVF            0x3c
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC6_MC(i)          (0x3d + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC7_OVF            0x3f
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TC7_MC(i)          (0x40 + (i))
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_ADC_RESRDY         0x42
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_ADC_WINMON         0x43
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_AC_COMP0           0x44
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_AC_COMP1           0x45
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_AC_WIN0            0x46
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_DAC_EMPTY          0x47
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_PTC_EOC            0x48
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_PTC_WCOMP          0x49
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_AC_COMP2           0x4a
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_AC_COMP3           0x4b
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_AC_WIN1            0x4c
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC3_OVF           0x4d
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC3_TRG           0x4e
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC3_CNT           0x4f
+#define  SAMD21_EVSYS_CHANNEL_EVGEN_TCC3_MCX(i)                (0x50 + (i))
+
+#define SAMD21_EVSYS_CHANNEL_PATH      24
+#define  SAMD21_EVSYS_CHANNEL_PATH_SYNCHRONOUS         0
+#define  SAMD21_EVSYS_CHANNEL_PATH_RESYNCHRONIZED      1
+#define  SAMD21_EVSYS_CHANNEL_PATH_ASYNCHRONOUS                2
+
+#define SAMD21_EVSYS_CHANNEL_EDGESEL   26
+#define  SAMD21_EVSYS_CHANNEL_EDGESEL_NO_EVT_OUTPUT    0
+#define  SAMD21_EVSYS_CHANNEL_EDGESEL_RISING_EDGE      1
+#define  SAMD21_EVSYS_CHANNEL_EDGESEL_FALLING_EDGE     2
+#define  SAMD21_EVSYS_CHANNEL_EDGESEL_BOTH_EDGES       3
+
+#define SAMD21_EVSYS_USER_USER         0
+#define  SAMD21_EVSYS_USER_USER_DMAC_CH(n)     (0x00 + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC0_EV(n)     (0x04 + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC0_MC(n)     (0x06 + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC1_EV(n)     (0x0a + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC1_MC(n)     (0x0c + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC2_EV(n)     (0x0e + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC2_MC(n)     (0x10 + (n))
+#define  SAMD21_EVSYS_USER_USER_TC(n)          (0x12 + (n))
+#define  SAMD21_EVSYS_USER_USER_ADC_START      (0x17)
+#define  SAMD21_EVSYS_USER_USER_ADC_SYNC       0x18
+#define  SAMD21_EVSYS_USER_USER_AC_COMP0       0x19
+#define  SAMD21_EVSYS_USER_USER_AC_COMP1       0x1a
+#define  SAMD21_EVSYS_USER_USER_DAC_START      0x1b
+#define  SAMD21_EVSYS_USER_USER_PTC_STCONV     0x1c
+#define  SAMD21_EVSYS_USER_USER_AC_COMP2       0x1d
+#define  SAMD21_EVSYS_USER_USER_AC_COMP3       0x1e
+#define  SAMD21_EVSYS_USER_USER_TCC3_EV(n)     (0x1f + (n))
+#define  SAMD21_EVSYS_USER_USER_TCC3_MC(n)     (0x21 + (n))
+
+#define SAMD21_EVSYS_USER_CHANNEL      8
+#define  SAMD21_EVSYS_USER_CHANNEL_NONE                0
+#define  SAMD21_EVSYS_USER_CHANNEL_NUM(n)      ((n) + 1)
+
+
+#define SAMD21_EVSYS_CHSTATUS_USRRDY(n)        (((n) & 7) | (((n) & 8) << 1))
+#define SAMD21_EVSYS_CHSTATUS_CHBUSY(n)        (((n) & 7) | (((n) & 8) << 1) | 8)
+
+/* 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_NUM_SERCOM      6
+
+#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_SPI_CLIENT   2
+# define SAMD21_SERCOM_CTRLA_MODE_SPI_HOST     3
+# define SAMD21_SERCOM_CTRLA_MODE_I2C_CLIENT   4
+# define SAMD21_SERCOM_CTRLA_MODE_I2C_HOST     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_TXPO_TX_0                 0
+#define  SAMD21_SERCOM_CTRLA_TXPO_TX_2                 1
+#define  SAMD21_SERCOM_CTRLA_TXPO_TX_0_RTS_2_CTS_3     2
+#define SAMD21_SERCOM_CTRLA_RXPO       20
+#define  SAMD21_SERCOM_CTRLA_RXPO_RX_0                 0
+#define  SAMD21_SERCOM_CTRLA_RXPO_RX_1                 1
+#define  SAMD21_SERCOM_CTRLA_RXPO_RX_2                 2
+#define  SAMD21_SERCOM_CTRLA_RXPO_RX_3                 3
+#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
+
+/* SPI controller mode */
+#define SAMD21_SERCOM_CTRLA_DOPO       16
+#define  SAMD21_SERCOM_CTRLA_DOPO_MOSI_0_SCLK_1        0UL
+#define  SAMD21_SERCOM_CTRLA_DOPO_MOSI_2_SCLK_3        1UL
+#define  SAMD21_SERCOM_CTRLA_DOPO_MOSI_3_SCLK_1        2UL
+#define  SAMD21_SERCOM_CTRLA_DOPO_MOSI_0_SCLK_3        3UL
+#define  SAMD21_SERCOM_CTRLA_DOPO_MASK         3UL
+
+#define SAMD21_SERCOM_CTRLA_DIPO       20
+#define  SAMD21_SERCOM_CTRLA_DIPO_MISO_0       0UL
+#define  SAMD21_SERCOM_CTRLA_DIPO_MISO_1       1UL
+#define  SAMD21_SERCOM_CTRLA_DIPO_MISO_2       2UL
+#define  SAMD21_SERCOM_CTRLA_DIPO_MISO_3       3UL
+#define  SAMD21_SERCOM_CTRLA_DIPO_MASK         3UL
+
+#define SAMD21_SERCOM_CTRLA_FORM       24
+#define SAMD21_SERCOM_CTRLA_CPHA       28
+#define SAMD21_SERCOM_CTRLA_CPOL       29
+#define SAMD21_SERCOM_CTRLA_DORD       30
+#define  SAMD21_SERCOM_CTRLA_DORD_LSB  1
+#define  SAMD21_SERCOM_CTRLA_DORD_MSB  0
+
+/* 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
+
+/* SPI mode */
+#define SAMD21_SERCOM_CTRLB_CHSIZE     0
+# define SAMD21_SERCOM_CTRLB_CHSIZE_8          0
+#define SAMD21_SERCOM_CTRLB_PLOADEN    6
+#define SAMD21_SERCOM_CTRLB_SSDE       9
+#define SAMD21_SERCOM_CTRLB_MSSEN      13
+#define SAMD21_SERCOM_CTRLB_AMODE      14
+#define SAMD21_SERCOM_CTRLB_RXEN       17
+
+/* 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
+
+/* SPI mode */
+#define SAMD21_SERCOM_INTFLAG_SSL      3
+
+#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_ */
diff --git a/src/snekboard/Makefile b/src/snekboard/Makefile
new file mode 100644 (file)
index 0000000..5d4f863
--- /dev/null
@@ -0,0 +1,71 @@
+#
+# AltOS build
+#
+#
+
+include ../samd21/Makefile.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_boot.h \
+       ao_pins.h \
+       ao_product.h \
+       ao_task.h \
+       samd21.h \
+       Makefile
+
+ALTOS_SRC = \
+       ao_boot_chain.c \
+       ao_romconfig.c \
+       ao_interrupt.c \
+       ao_product.c \
+       ao_cmd.c \
+       ao_task.c \
+       ao_led.c \
+       ao_stdio.c \
+       ao_panic.c \
+       ao_timer.c \
+       ao_mutex.c \
+       ao_dma_samd21.c \
+       ao_usb_samd21.c \
+       ao_spi_samd21.c
+
+# SAMD21G18A
+
+SAMD21_ROM=256
+SAMD21_RAM=32
+
+PRODUCT=SnekBoard
+PRODUCT_DEF=-DSNEKBOARD
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(SAMD21_CFLAGS)
+
+PROGNAME=snekboard
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) snekboard.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) 
+       $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+load: $(PROG)
+       stm-load $(PROG)
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/snekboard/ao_pins.h b/src/snekboard/ao_pins.h
new file mode 100644 (file)
index 0000000..80b2cb7
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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_PINS_H_
+#define _AO_PINS_H_
+
+#define LED_0_PORT     (&samd21_port_a)
+#define LED_0_PIN      2
+
+#define LED_BLUE       (1 << 0)
+
+#define AO_LED_PANIC   LED_BLUE
+
+#define HAS_BEEP       0
+
+#define HAS_USB                1
+#define USE_USB_STDIO  1
+
+#define HAS_LED                1
+
+#define AO_XOSC                        1
+#define AO_XOSC_FREQ           16000000
+#define AO_XOSC_DIV            256
+#define AO_XOSC_MUL            768
+
+#define AO_AHB_PRESCALER       1
+#define AO_APBA_PRESCALER      1
+
+#define HAS_SPI_0              1
+#define SPI_0_PA08_PA09_PA10   1
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/snekboard/flash-loader/Makefile b/src/snekboard/flash-loader/Makefile
new file mode 100644 (file)
index 0000000..12330d3
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=snekboard
+include $(TOPDIR)/samd21/Makefile-flash.defs
diff --git a/src/snekboard/flash-loader/ao_pins.h b/src/snekboard/flash-loader/ao_pins.h
new file mode 100644 (file)
index 0000000..fb39981
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2013 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_PINS_H_
+#define _AO_PINS_H_
+
+#include <ao_flash_samd21_pins.h>
+
+/* ANALOG1 to gnd for boot loader mode */
+
+#define AO_BOOT_PIN                    1
+#define AO_BOOT_APPLICATION_GPIO       (samd21_port_b)
+#define AO_BOOT_APPLICATION_PIN                8
+#define AO_BOOT_APPLICATION_VALUE      1
+#define AO_BOOT_APPLICATION_MODE       AO_MODE_PULL_UP
+
+/* USB */
+#define HAS_USB                        1
+
+#define AO_XOSC                        1
+#define AO_XOSC_FREQ           16000000
+#define AO_XOSC_DIV            256
+#define AO_XOSC_MUL            768
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/snekboard/snekboard.c b/src/snekboard/snekboard.c
new file mode 100644 (file)
index 0000000..719ea01
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2016 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_led.h>
+#include <ao_dma_samd21.h>
+
+#define SNEK_CS_PORT   (&samd21_port_a)
+#define SNEK_CS_PIN    (11)
+#define SNEK_SPI_INDEX AO_SPI_0_PA08_PA09_PA10
+#define SNEK_SPI_SPEED ao_spi_speed(1000000)
+
+static const uint8_t spi_test[] = {
+       0x55,
+};
+
+static void
+ao_spi_test(void)
+{
+       ao_spi_get_bit(SNEK_CS_PORT, SNEK_CS_PIN, SNEK_SPI_INDEX, SNEK_SPI_SPEED);
+       ao_spi_send(spi_test, sizeof(spi_test), SNEK_SPI_INDEX);
+       ao_spi_put_bit(SNEK_CS_PORT, SNEK_CS_PIN, SNEK_SPI_INDEX);
+}
+
+const struct ao_cmds ao_spi_cmds[] = {
+       { ao_spi_test,  "s \0Send some bytes over spi" },
+       { 0, NULL },
+};
+
+int main(void)
+{
+       ao_led_init();
+       ao_clock_init();
+       ao_task_init();
+       ao_timer_init();
+       ao_dma_init();
+       ao_spi_init();
+       ao_usb_init();
+       ao_cmd_register(ao_spi_cmds);
+       ao_spi_init_cs(&samd21_port_a, 1 << 11); /* analog 8 for CS */
+       ao_cmd_init();
+       ao_start_scheduler();
+       return 0;
+}
index f03d96957802674ffc22d54b009be87805c15119..385fe0e32b192d6c815ec0fd32bdb96f8d69abe0 100644 (file)
@@ -139,8 +139,7 @@ ao_spi_recv_byte(uint8_t spi_index)
 void
 ao_spi_recv(void *block, uint16_t len, uint8_t spi_index);
 
-void
-ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index);
+#define AO_SPI_DUPLEX  0
 
 void
 ao_spi_init(void);
index ce2d949f34d9702995825edc2253213e7070b080..6efa407d15043010e7185198f91a62c26b28a77e 100644 (file)
@@ -178,50 +178,10 @@ struct ao_adc {
 #define AO_RN_SW_BTN_PORT      (&stm_gpioc)
 #define AO_RN_SW_BTN_PIN       14
 
-/* Pin 9. BM70 P2_3 */
-#define AO_RN_WAKEUP_PORT      (&stm_gpiob)
-#define AO_RN_WAKEUP_PIN       9
-
-/* Pin 11. BM70 P2_7/tx_ind. Status indication along with P1_5 */
-#define AO_RN_P0_4_PORT                (&stm_gpioc)
-#define AO_RN_P0_4_PIN         13
-
-/* Pin 12. BM70 P1_1. Status indication along with P0_4 */
+/* Pin 12. BM70 P1_5. Status indication along with P0_4 */
 #define AO_RN_P1_5_PORT                (&stm_gpiob)
 #define AO_RN_P1_5_PIN         6
 
-/* Pin 13. BM70 P1_2. Also I2C SCL */
-#define AO_RN_P1_2_PORT                (&stm_gpiob)
-#define AO_RN_P1_2_PIN         7
-
-/* Pin 14. BM70 P1_3. Also I2C SDA */
-#define AO_RN_P1_3_PORT                (&stm_gpiob)
-#define AO_RN_P1_3_PIN         8
-
-/* Pin 15. BM70 P0_0/cts. */
-#define AO_RN_CTS_PORT         (&stm_gpioa)
-#define AO_RN_CTS_PIN          1
-
-/* Pin 16. BM70 P1_0. */
-#define AO_RN_P0_5_PORT                (&stm_gpiob)
-#define AO_RN_P0_5_PIN         5
-
-/* Pin 17. BM70 P3_6. */
-#define AO_RN_RTS_PORT         (&stm_gpioa)
-#define AO_RN_RTS_PIN          0
-
-/* Pin 18. BM70 P2_0. */
-#define AO_RN_P2_0_PORT                (&stm_gpiob)
-#define AO_RN_P2_0_PIN         3
-
-/* Pin 19. BM70 P2_4. */
-#define AO_RN_P2_4_PORT                (&stm_gpioa)
-#define AO_RN_P2_4_PIN         10
-
-/* Pin 20. BM70 NC. */
-#define AO_RN_EAN_PORT
-#define AO_RN_EAN_PIN
-
 /* Pin 21. BM70 RST_N. */
 #define AO_RN_RST_N_PORT       (&stm_gpioa)
 #define AO_RN_RST_N_PIN                15
@@ -238,22 +198,6 @@ struct ao_adc {
 #define AO_RN_P3_1_PORT                (&stm_gpiob)
 #define AO_RN_P3_1_PIN         2
 
-/* Pin 25. BM70 P3_2/LINK_DROP. */
-#define AO_RN_P3_2_PORT                (&stm_gpioa)
-#define AO_RN_P3_2_PIN         8
-
-/* Pin 26. BM70 P3_3/UART_RX_IND. */
-#define AO_RN_P3_3_PORT                (&stm_gpiob)
-#define AO_RN_P3_3_PIN         15
-
-/* Pin 27. BM70 P3_4/PAIRING_KEY. */
-#define AO_RN_P3_4_PORT                (&stm_gpiob)
-#define AO_RN_P3_4_PIN         14
-
-/* Pin 28. BM70 P3_5. */
-#define AO_RN_P3_6_PORT                (&stm_gpiob)
-#define AO_RN_P3_6_PIN         13
-
 /* Pin 29. BM70 P0_7. */
 #define AO_RN_P3_7_PORT                (&stm_gpiob)
 #define AO_RN_P3_7_PIN         12
diff --git a/src/telemetrum-v4.0/Makefile b/src/telemetrum-v4.0/Makefile
new file mode 100644 (file)
index 0000000..5191640
--- /dev/null
@@ -0,0 +1,122 @@
+#
+# AltOS build
+#
+#
+
+include ../samd21/Makefile.defs
+
+INC = \
+        ao.h \
+        ao_arch.h \
+        ao_arch_funcs.h \
+        ao_boot.h \
+        ao_companion.h \
+        ao_data.h \
+        ao_sample.h \
+        ao_pins.h \
+        altitude-pa.h \
+        ao_kalman.h \
+        ao_product.h \
+        ao_ms5607.h \
+        ao_adxl375.h \
+        ao_cc1200_CC1200.h \
+        ao_task.h \
+        ao_whiten.h \
+       samd21.h \
+       Makefile
+
+# SAMD21G17D
+
+SAMD21_ROM=128
+SAMD21_RAM=16
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+#SAMPLE_PROFILE=ao_sample_profile.c \
+#       ao_sample_profile_timer.c
+#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1
+
+#STACK_GUARD=ao_mpu_stm.c
+#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
+
+ALTOS_SRC = \
+       ao_boot_chain.c \
+        ao_interrupt.c \
+        ao_product.c \
+        ao_romconfig.c \
+        ao_cmd.c \
+        ao_config.c \
+        ao_task.c \
+        ao_led.c \
+        ao_stdio.c \
+        ao_panic.c \
+        ao_timer.c \
+        ao_mutex.c \
+        ao_serial_samd21.c \
+        ao_gps_ublox.c \
+        ao_gps_show.c \
+        ao_gps_report_metrum.c \
+        ao_ignite.c \
+        ao_freq.c \
+        ao_dma_samd21.c \
+        ao_spi_samd21.c \
+        ao_cc1200.c \
+        ao_data.c \
+        ao_ms5607.c \
+        ao_adxl375.c \
+        ao_adc_samd21.c \
+        ao_beep_samd21.c \
+        ao_storage.c \
+        ao_m25.c \
+       ao_usb_samd21.c \
+       ao_exti_samd21.c \
+        ao_report.c \
+        ao_convert_pa.c \
+        ao_convert_volt.c \
+        ao_log.c \
+        ao_log_metrum.c \
+        ao_sample.c \
+        ao_kalman.c \
+        ao_flight.c \
+        ao_telemetry.c \
+        ao_packet_slave.c \
+        ao_packet.c \
+        ao_companion.c \
+        ao_aprs.c \
+        $(PROFILE) \
+        $(SAMPLE_PROFILE) \
+        $(STACK_GUARD)
+
+PRODUCT=TeleMetrum-v4.0
+PRODUCT_DEF=-DTELEMETRUM_V_4_0
+IDPRODUCT=0x000b
+
+CFLAGS = $(PRODUCT_DEF) $(SAMD21_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF)
+
+PROGNAME=telemetrum-v4.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_telemetrum.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ)
+       $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+load: $(PROG)
+       stm-load $(PROG)
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/telemetrum-v4.0/ao_pins.h b/src/telemetrum-v4.0/ao_pins.h
new file mode 100644 (file)
index 0000000..bc05761
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * 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_PINS_H_
+#define _AO_PINS_H_
+
+#define AO_XOSC                        1
+#define AO_XOSC_FREQ           16000000
+#define AO_XOSC_DIV            256
+#define AO_XOSC_MUL            768
+
+#define AO_AHB_PRESCALER       1
+#define AO_APBA_PRESCALER      1
+
+#define HAS_SERIAL_1           1
+#define USE_SERIAL_1_STDIN     0
+
+#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX       (512 * 1024)
+#define AO_CONFIG_MAX_SIZE                     1024
+#define LOG_ERASE_MARK                         0x55
+#define LOG_MAX_ERASE                          128
+#define AO_LOG_FORMAT                          AO_LOG_FORMAT_TELEMETRUM
+
+#define HAS_EEPROM             1
+#define USE_INTERNAL_FLASH     0
+#define USE_EEPROM_CONFIG      0
+#define USE_STORAGE_CONFIG     1
+#define HAS_USB                        1
+#define USE_USB_STDIO  1
+#define HAS_BATTERY_REPORT     1
+#define BEEPER_CHANNEL         4
+#define BEEPER_TIMER           3
+#define BEEPER_PORT            (&samd21_port_a)
+#define BEEPER_PIN             16
+#define HAS_RADIO              1
+#define HAS_RADIO_10MW         1
+#define HAS_TELEMETRY          1
+#define HAS_APRS               1
+#define HAS_COMPANION          1
+
+#define HAS_SPI_0              1
+#define HAS_SPI_3              1
+#define HAS_SPI_5              1
+
+#define PACKET_HAS_SLAVE       1
+#define PACKET_HAS_MASTER      0
+
+#define LOW_LEVEL_DEBUG                0
+
+#define HAS_LED                        1
+#define LED_0_PORT             (&samd21_port_b)
+#define LED_0_PIN              10
+#define LED_1_PORT             (&samd21_port_b)
+#define LED_1_PIN              11
+#define AO_LED_RED             (1 << 0)
+#define AO_LED_GREEN           (1 << 1)
+
+#define HAS_GPS                        1
+#define HAS_FLIGHT             1
+#define HAS_ADC                        1
+#define HAS_ADC_TEMP           1
+#define HAS_LOG                        1
+
+/*
+ * Beeper
+ */
+
+#define HAS_BEEP               1
+/* Beep on PA16 function E TCC2.0 */
+
+#define AO_BEEP_TCC            (&samd21_tcc2)
+#define AO_BEEP_TCC_APBC_MASK  SAMD21_PM_APBCMASK_TCC2
+#define AO_BEEP_PORT           (&samd21_port_a)
+#define AO_BEEP_PIN            (16)
+#define AO_BEEP_FUNC           SAMD21_PORT_PMUX_FUNC_E
+
+/*
+ * Igniter
+ */
+
+#define HAS_IGNITE             1
+#define HAS_IGNITE_REPORT      1
+
+#define AO_SENSE_DROGUE(p)     ((p)->adc.sense_a)
+#define AO_SENSE_MAIN(p)       ((p)->adc.sense_m)
+#define AO_IGNITER_CLOSED      400
+#define AO_IGNITER_OPEN                60
+
+/* Drogue */
+#define AO_IGNITER_DROGUE_PORT (&samd21_port_a)
+#define AO_IGNITER_DROGUE_PIN  19
+
+/* Main */
+#define AO_IGNITER_MAIN_PORT   (&samd21_port_a)
+#define AO_IGNITER_MAIN_PIN    18
+
+/*
+ * ADC
+ */
+#define AO_DATA_RING           32
+#define AO_ADC_NUM_SENSE       2
+
+struct ao_adc {
+       int16_t                 sense_a;
+       int16_t                 sense_m;
+       int16_t                 v_batt;
+       int16_t                 temp;
+};
+
+#define AO_ADC_DUMP(p) \
+       printf("tick: %5lu drogue: %5d main: %5d batt: %5d\n", \
+              (p)->tick, \
+              (p)->adc.sense_a, (p)->adc.sense_m, \
+              (p)->adc.v_batt);
+
+#define AO_ADC_SENSE_DROGUE            18
+#define AO_ADC_SENSE_DROGUE_PORT       (&samd21_port_a)
+#define AO_ADC_SENSE_DROGUE_PIN                10
+
+#define AO_ADC_SENSE_MAIN              19
+#define AO_ADC_SENSE_MAIN_PORT         (&samd21_port_a)
+#define AO_ADC_SENSE_MAIN_PIN          11
+
+#define AO_ADC_V_BATT                  17
+#define AO_ADC_V_BATT_PORT             (&samd21_port_a)
+#define AO_ADC_V_BATT_PIN              9
+
+#define AO_ADC_TEMP                    SAMD21_ADC_INPUTCTRL_MUXPOS_TEMP
+
+#define AO_NUM_ADC_PIN                 3
+
+#define AO_ADC_PIN0_PORT       AO_ADC_SENSE_DROGUE_PORT
+#define AO_ADC_PIN0_PIN                AO_ADC_SENSE_DROGUE_PIN
+#define AO_ADC_PIN1_PORT       AO_ADC_SENSE_MAIN_PORT
+#define AO_ADC_PIN1_PIN                AO_ADC_SENSE_MAIN_PIN
+#define AO_ADC_PIN2_PORT       AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN2_PIN                AO_ADC_V_BATT_PIN
+
+#define AO_NUM_ADC             (AO_NUM_ADC_PIN + 1)
+
+#define AO_ADC_SQ0             AO_ADC_SENSE_DROGUE
+#define AO_ADC_SQ1             AO_ADC_SENSE_MAIN
+#define AO_ADC_SQ2             AO_ADC_V_BATT
+#define AO_ADC_SQ3             AO_ADC_TEMP
+
+/*
+ * Voltage divider on ADC battery sampler
+ */
+#define AO_BATTERY_DIV_PLUS    56      /* 5.6k */
+#define AO_BATTERY_DIV_MINUS   100     /* 10k */
+
+/*
+ * Voltage divider on ADC igniter samplers
+ */
+#define AO_IGNITE_DIV_PLUS     100     /* 100k */
+#define AO_IGNITE_DIV_MINUS    27      /* 27k */
+
+/*
+ * ADC reference in decivolts
+ */
+#define AO_ADC_REFERENCE_DV    33
+
+/*
+ * GPS
+ */
+
+#define AO_SERIAL_SPEED_UBLOX  AO_SERIAL_SPEED_9600
+
+#define HAS_SERIAL_1           1
+#define USE_SERIAL_1_STDIN     0
+#define SERIAL_1_PA00_PA01     1
+
+#define ao_gps_getchar         ao_serial1_getchar
+#define ao_gps_putchar         ao_serial1_putchar
+#define ao_gps_set_speed       ao_serial1_set_speed
+#define ao_gps_fifo            (ao_samd21_usart1.rx_fifo)
+
+/*
+ * Pressure sensor settings
+ */
+#define HAS_MS5607             1
+#define HAS_MS5611             0
+#define AO_MS5607_PRIVATE_PINS 0
+#define AO_MS5607_CS_PORT      (&samd21_port_a)
+#define AO_MS5607_CS_PIN       21
+#define AO_MS5607_MISO_PORT    (&samd21_port_a)
+#define AO_MS5607_MISO_PIN     20
+#define AO_MS5607_SPI_INDEX    AO_SPI_3_PA22_PA23_PA20
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS          1
+#define AO_M25_SPI_CS_PORT     (&samd21_port_a)
+#define AO_M25_SPI_CS_MASK     (1 << 27)
+#define AO_M25_SPI_BUS         AO_SPI_5_PB22_PB23_PB03
+
+
+/*
+ * Radio (cc1200)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT   5695733
+
+#define AO_CC1200_SPI_CS_PORT  (&samd21_port_a)
+#define AO_CC1200_SPI_CS_PIN   7
+#define AO_CC1200_SPI_BUS      AO_SPI_5_PB22_PB23_PB03
+
+#define AO_CC1200_INT_PORT             (&samd21_port_b)
+#define AO_CC1200_INT_PIN              (8)
+#define AO_CC1200_MCU_WAKEUP_PORT      (&samd21_port_b)
+#define AO_CC1200_MCU_WAKEUP_PIN       (9)
+
+#define AO_CC1200_INT_GPIO     2
+#define AO_CC1200_INT_GPIO_IOCFG       CC1200_IOCFG2
+
+#define AO_CC1200_MARC_GPIO    3
+#define AO_CC1200_MARC_GPIO_IOCFG      CC1200_IOCFG3
+
+#define HAS_BOOT_RADIO         0
+
+#define HAS_HIGHG_ACCEL                1
+
+/* ADXL375 */
+
+#define HAS_ADXL375            1
+#define AO_ADXL375_CS_PORT     (&samd21_port_a)
+#define AO_ADXL375_CS_PIN      8
+#define AO_ADXL375_SPI_INDEX   (AO_SPI_0_PA04_PA05_PA06 | AO_SPI_MODE_3)
+
+#define AO_ADXL375_AXIS                x
+#define AO_ADXL375_INVERT      1
+
+#define NUM_CMDS               16
+
+/*
+ * Companion
+ */
+
+#define AO_COMPANION_CS_PORT   (&samd21_port_a)
+#define AO_COMPANION_CS_PIN    (13)
+#define AO_COMPANION_SPI_BUS   AO_SPI_5_PB22_PB23_PB03
+
+/*
+ * Monitor
+ */
+
+#define HAS_MONITOR            0
+#define LEGACY_MONITOR         0
+#define HAS_MONITOR_PUT                1
+#define AO_MONITOR_LED         0
+#define HAS_RSSI               0
+
+/*
+ * Profiling Viterbi decoding
+ */
+
+#ifndef AO_PROFILE
+#define AO_PROFILE             0
+#endif
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telemetrum-v4.0/ao_telemetrum.c b/src/telemetrum-v4.0/ao_telemetrum.c
new file mode 100644 (file)
index 0000000..db0fc6a
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2016 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_ms5607.h>
+#include <ao_adxl375.h>
+#include <ao_log.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_eeprom.h>
+#if HAS_SAMPLE_PROFILE
+#include <ao_profile.h>
+#include <ao_sample_profile.h>
+#endif
+#if HAS_STACK_GUARD
+#include <ao_mpu.h>
+#endif
+#include <ao_dma_samd21.h>
+
+int
+main(void)
+{
+       ao_clock_init();
+
+#if HAS_STACK_GUARD
+       ao_mpu_init();
+#endif
+
+       ao_task_init();
+       ao_serial_init();
+       ao_led_init();
+       ao_led_on(LEDS_AVAILABLE);
+       ao_timer_init();
+
+       ao_spi_init();
+       ao_dma_init();
+       ao_exti_init();
+
+       ao_adc_init();
+       ao_beep_init();
+       ao_cmd_init();
+
+       ao_ms5607_init();
+       ao_adxl375_init();
+
+       ao_storage_init();
+
+       ao_flight_init();
+       ao_log_init();
+       ao_report_init();
+
+       ao_usb_init();
+       ao_gps_init();
+       ao_gps_report_metrum_init();
+       ao_telemetry_init();
+       ao_radio_init();
+       ao_packet_slave_init(false);
+       ao_igniter_init();
+       ao_companion_init();
+
+       ao_config_init();
+#if AO_PROFILE
+       ao_profile_init();
+#endif
+#if HAS_SAMPLE_PROFILE
+       ao_sample_profile_init();
+#endif
+       ao_led_off(LEDS_AVAILABLE);
+
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/telemetrum-v4.0/flash-loader/Makefile b/src/telemetrum-v4.0/flash-loader/Makefile
new file mode 100644 (file)
index 0000000..5fabbc0
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telemetrum-v4.0
+include $(TOPDIR)/samd21/Makefile-flash.defs
diff --git a/src/telemetrum-v4.0/flash-loader/ao_pins.h b/src/telemetrum-v4.0/flash-loader/ao_pins.h
new file mode 100644 (file)
index 0000000..5653c24
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2022 Bdale Garbee <bdale@gag.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_PINS_H_
+#define _AO_PINS_H_
+
+#include <ao_flash_samd21_pins.h>
+
+/* cs_comp_0 (companion port pin 6) to gnd for boot loader mode */
+
+#define AO_BOOT_PIN                    1
+#define AO_BOOT_APPLICATION_GPIO       (samd21_port_a)
+#define AO_BOOT_APPLICATION_PIN                13
+#define AO_BOOT_APPLICATION_VALUE      1
+#define AO_BOOT_APPLICATION_MODE       AO_MODE_PULL_UP
+
+/* USB */
+#define HAS_USB                        1
+
+#define AO_XOSC                        1
+#define AO_XOSC_FREQ           16000000
+#define AO_XOSC_DIV            256
+#define AO_XOSC_MUL            768
+
+#endif /* _AO_PINS_H_ */