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} \
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 \
return false;
if (product.startsWith("TeleMetrum-v3"))
return false;
+ if (product.startsWith("TeleMetrum-v4"))
+ return true;
if (product.startsWith("EasyMega"))
return false;
return true;
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"))
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"))
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,
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)
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"
-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
--- /dev/null
+#!/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"
--- /dev/null
+.\"
+.\" 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
PICOLIBC_CFLAGS= \
-specs=picolibc.specs \
+ -Wl,--gc-sections \
$(PICOLIBC_PRINTF_CFLAGS)
AO_CFLAGS=\
#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);
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);
}
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);
}
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);
}
#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
#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
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)
/* 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();
}
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;
}
}
/* 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();
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);
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]);
}
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;
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);
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;
#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;
* NAV-TIMEUTC message parsing
*/
static struct nav_timeutc {
+ int32_t nano;
uint16_t year;
uint8_t month;
uint8_t day;
#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 */
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)) {
#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();
static const struct {
void *port;
- uint16_t pin;
+ uint8_t pin;
} ao_leds[] = {
#ifdef LED_0_PORT
[0] { LED_0_PORT, LED_0_PIN },
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;
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
#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
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;
#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;
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
#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
} 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);
}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+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:
--- /dev/null
+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)
--- /dev/null
+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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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();
+}
+
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+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;
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+#
+# 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:
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=snekboard
+include $(TOPDIR)/samd21/Makefile-flash.defs
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
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);
#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
#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
--- /dev/null
+#
+# 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:
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telemetrum-v4.0
+include $(TOPDIR)/samd21/Makefile-flash.defs
--- /dev/null
+/*
+ * 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_ */