From: Keith Packard Date: Sat, 26 Nov 2022 17:52:37 +0000 (-0800) Subject: Merge branch 'master' X-Git-Tag: 1.9.13~1^2~26 X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=c1708f3fa4ff412da8817ba0fa58d05fe7ef44f5;hp=414ad2696bbe2a2f245ae7bb2ec8f57bc8cc0097 Merge branch 'master' --- diff --git a/Releasing b/Releasing index 7bfb3ae8..939b10b7 100644 --- a/Releasing +++ b/Releasing @@ -119,7 +119,7 @@ These are Bdale's notes on how to do a release. src/teledongle-v3.0/{*.elf,*.ihx,*.map} \ src/telegps-v[1-2].0/{*.elf,*.ihx,*.map} \ src/telemega-v[1-5].0/{*.elf,*.ihx,*.map} \ - src/telemetrum-v[2-3].0/{*.elf,*.ihx,*.map} \ + src/telemetrum-v[2-4].0/{*.elf,*.ihx,*.map} \ src/telemini-v3.0/{*.elf,*.ihx,*.map} \ src/telelco-v2.0/{*.elf,*.ihx,*.map} \ src/telefireeight-v[1-2].0/{*.elf,*.ihx,*.map} \ @@ -133,7 +133,7 @@ These are Bdale's notes on how to do a release. src/teledongle-v3.0/flash-loader/*.elf \ src/telegps-v[1-2].0/flash-loader/{*.elf,*.bin,*.map} \ src/telemega-v[1-5].0/flash-loader/*.elf \ - src/telemetrum-v[2-3].0/flash-loader/*.elf \ + src/telemetrum-v[2-4].0/flash-loader/*.elf \ src/telemini-v3.0/flash-loader/{*.elf,*.bin,*.map} \ src/telelco-v2.0/flash-loader/*.elf \ src/telefireeight-v[1-2].0/flash-loader/*.elf \ diff --git a/altoslib/AltosConfigData.java b/altoslib/AltosConfigData.java index 5b9ee031..5797f632 100644 --- a/altoslib/AltosConfigData.java +++ b/altoslib/AltosConfigData.java @@ -638,6 +638,8 @@ public class AltosConfigData { return false; if (product.startsWith("TeleMetrum-v3")) return false; + if (product.startsWith("TeleMetrum-v4")) + return true; if (product.startsWith("EasyMega")) return false; return true; @@ -664,6 +666,8 @@ public class AltosConfigData { return true; if (product.startsWith("TeleMetrum-v3")) return true; + if (product.startsWith("TeleMetrum-v4")) + return true; if (product.startsWith("TeleMega-v4")) return true; if (product.startsWith("EasyMotor-v2")) @@ -680,6 +684,8 @@ public class AltosConfigData { return AltosAdxl375.X_AXIS; if (product.startsWith("TeleMetrum-v3")) return AltosAdxl375.X_AXIS; + if (product.startsWith("TeleMetrum-v4")) + return AltosAdxl375.X_AXIS; if (product.startsWith("TeleMega-v4")) return AltosAdxl375.X_AXIS; if (product.startsWith("EasyMotor-v2")) diff --git a/altoslib/AltosIdleFetch.java b/altoslib/AltosIdleFetch.java index b2fd3c6f..b2552b24 100644 --- a/altoslib/AltosIdleFetch.java +++ b/altoslib/AltosIdleFetch.java @@ -182,6 +182,12 @@ public class AltosIdleFetch implements AltosDataProvider { AltosIdler.idle_ms5607, AltosIdler.idle_sensor_metrum), + new AltosIdler("TeleMetrum-v4", + AltosIdler.idle_gps, + AltosIdler.idle_adxl375, + AltosIdler.idle_ms5607, + AltosIdler.idle_sensor_metrum), + new AltosIdler("TeleMega-v0", AltosIdler.idle_gps, AltosIdler.idle_mma655x, diff --git a/altosui/Makefile.am b/altosui/Makefile.am index 41f0d6ad..765ad58c 100644 --- a/altosui/Makefile.am +++ b/altosui/Makefile.am @@ -124,7 +124,8 @@ FIRMWARE_TD=$(FIRMWARE_TD_3_0) FIRMWARE_TM_2_0=$(top_srcdir)/src/telemetrum-v2.0/telemetrum-v2.0-$(VERSION).ihx FIRMWARE_TM_3_0=$(top_srcdir)/src/telemetrum-v3.0/telemetrum-v3.0-$(VERSION).ihx -FIRMWARE_TM=$(FIRMWARE_TM_2_0) $(FIRMWARE_TM_3_0) +FIRMWARE_TM_4_0=$(top_srcdir)/src/telemetrum-v4.0/telemetrum-v4.0-$(VERSION).ihx +FIRMWARE_TM=$(FIRMWARE_TM_2_0) $(FIRMWARE_TM_3_0) $(FIRMWARE_TM_4_0) FIRMWARE_TELEMINI_3_0=$(top_srcdir)/src/telemini-v3.0/telemini-v3.0-$(VERSION).ihx FIRMWARE_TELEMINI=$(FIRMWARE_TELEMINI_3_0) diff --git a/altosui/altos-windows.nsi.in b/altosui/altos-windows.nsi.in index 7abd4d03..6cb9cd42 100644 --- a/altosui/altos-windows.nsi.in +++ b/altosui/altos-windows.nsi.in @@ -122,6 +122,7 @@ Section "Firmware" File "../src/telemetrum-v2.0/telemetrum-v2.0-${VERSION}.ihx" File "../src/telemetrum-v3.0/telemetrum-v3.0-${VERSION}.ihx" + File "../src/telemetrum-v4.0/telemetrum-v4.0-${VERSION}.ihx" File "../src/telemini-v3.0/telemini-v3.0-${VERSION}.ihx" File "../src/telegps-v1.0/telegps-v1.0-${VERSION}.ihx" File "../src/telegps-v2.0/telegps-v2.0-${VERSION}.ihx" diff --git a/ao-tools/ao-flash/Makefile.am b/ao-tools/ao-flash/Makefile.am index ed928439..c9174d6a 100644 --- a/ao-tools/ao-flash/Makefile.am +++ b/ao-tools/ao-flash/Makefile.am @@ -1,3 +1,3 @@ -bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x ao-reset-lpc +bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x ao-reset-lpc ao-flash-samd21 -man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1 ao-reset-lpc.1 +man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1 ao-reset-lpc.1 ao-flash-samd21.1 diff --git a/ao-tools/ao-flash/ao-flash-samd21 b/ao-tools/ao-flash/ao-flash-samd21 new file mode 100755 index 00000000..f20d526d --- /dev/null +++ b/ao-tools/ao-flash/ao-flash-samd21 @@ -0,0 +1,19 @@ +#!/bin/sh +case "$#" in +1) + ;; +*) + echo "usage: $0 ..." + exit 1 + ;; +esac +openocd -f interface/stlink.cfg \ + -c 'transport select hla_swd' \ + -c 'set CHIPNAME at91samd21g18' \ + -c 'set CPUTAPID 0x0bc11477' \ + -f target/at91samdXX.cfg \ + -c init \ + -c 'reset halt' \ + -c 'at91samd bootloader 0' \ + -c "flash write_image erase unlock $1" \ + -c "shutdown" diff --git a/ao-tools/ao-flash/ao-flash-samd21.1 b/ao-tools/ao-flash/ao-flash-samd21.1 new file mode 100644 index 00000000..d624fe08 --- /dev/null +++ b/ao-tools/ao-flash/ao-flash-samd21.1 @@ -0,0 +1,36 @@ +.\" +.\" Copyright © 2022 Keith Packard +.\" +.\" 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 diff --git a/src/Makefile.defs b/src/Makefile.defs index d9b71997..c2d5247c 100644 --- a/src/Makefile.defs +++ b/src/Makefile.defs @@ -23,6 +23,7 @@ OPT=-Os -Wl,-Map=$(PROGNAME)-$(VERSION).map PICOLIBC_CFLAGS= \ -specs=picolibc.specs \ + -Wl,--gc-sections \ $(PICOLIBC_PRINTF_CFLAGS) AO_CFLAGS=\ diff --git a/src/attiny/ao_arch_funcs.h b/src/attiny/ao_arch_funcs.h index 1f422a37..e3374ac5 100644 --- a/src/attiny/ao_arch_funcs.h +++ b/src/attiny/ao_arch_funcs.h @@ -65,6 +65,8 @@ ao_spi_recv_bus(void *block, uint16_t len); #define ao_spi_send(block, len, bus) ao_spi_send_bus(block, len) #define ao_spi_recv(block, len, bus) ao_spi_recv_bus(block, len) +#define AO_SPI_DUPLEX 0 + void ao_spi_init(void); diff --git a/src/drivers/ao_cc1200.c b/src/drivers/ao_cc1200.c index 4ddc96ce..72709744 100644 --- a/src/drivers/ao_cc1200.c +++ b/src/drivers/ao_cc1200.c @@ -204,6 +204,7 @@ ao_radio_status(void) void ao_radio_recv_abort(void) { + ao_exti_disable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN); ao_radio_abort = 1; ao_wakeup(&ao_radio_wake); } @@ -213,7 +214,6 @@ ao_radio_recv_abort(void) static void ao_radio_isr(void) { - ao_exti_disable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN); ao_radio_wake = 1; ao_wakeup(&ao_radio_wake); } @@ -221,14 +221,12 @@ ao_radio_isr(void) static void ao_radio_start_tx(void) { - ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN); ao_radio_strobe(CC1200_STX); } static void ao_radio_start_rx(void) { - ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN); ao_radio_strobe(CC1200_SRX); } @@ -575,8 +573,7 @@ static uint16_t ao_radio_mode; #define AO_RADIO_MODE_BITS_PACKET 1 #define AO_RADIO_MODE_BITS_TX_BUF 4 -#define AO_RADIO_MODE_BITS_TX_FINISH 8 -#define AO_RADIO_MODE_BITS_RX 16 +#define AO_RADIO_MODE_BITS_FINISH 8 #define AO_RADIO_MODE_BITS_RDF 32 #define AO_RADIO_MODE_BITS_APRS 64 #define AO_RADIO_MODE_BITS_TEST 128 @@ -584,12 +581,12 @@ static uint16_t ao_radio_mode; #define AO_RADIO_MODE_BITS_FIXED 512 #define AO_RADIO_MODE_NONE 0 -#define AO_RADIO_MODE_PACKET_TX (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_FINISH) -#define AO_RADIO_MODE_PACKET_RX (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_RX) -#define AO_RADIO_MODE_RDF (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_FINISH) +#define AO_RADIO_MODE_PACKET_TX (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_FINISH) +#define AO_RADIO_MODE_PACKET_RX (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_FINISH) +#define AO_RADIO_MODE_RDF (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_FINISH) #define AO_RADIO_MODE_APRS_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF) #define AO_RADIO_MODE_APRS_LAST_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_BUF) -#define AO_RADIO_MODE_APRS_FINISH (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_FINISH) +#define AO_RADIO_MODE_APRS_FINISH (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_FINISH) #define AO_RADIO_MODE_TEST (AO_RADIO_MODE_BITS_TEST | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF) static void @@ -642,17 +639,10 @@ ao_radio_set_mode(uint16_t new_mode) if (changes & AO_RADIO_MODE_BITS_TX_BUF) { ao_radio_reg_write(AO_CC1200_INT_GPIO_IOCFG, CC1200_IOCFG_GPIO_CFG_TXFIFO_THR); - ao_exti_set_mode(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH); } - if (changes & AO_RADIO_MODE_BITS_TX_FINISH) { + if (changes & AO_RADIO_MODE_BITS_FINISH) { ao_radio_reg_write(AO_CC1200_INT_GPIO_IOCFG, CC1200_IOCFG_GPIO_CFG_PKT_SYNC_RXTX); - ao_exti_set_mode(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH); - } - - if (changes & AO_RADIO_MODE_BITS_RX) { - ao_radio_reg_write(AO_CC1200_INT_GPIO_IOCFG, CC1200_IOCFG_GPIO_CFG_MARC_MCU_WAKEUP); - ao_exti_set_mode(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN, AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_HIGH); } if (changes & AO_RADIO_MODE_BITS_RDF) @@ -772,12 +762,18 @@ ao_radio_show_state(char *where) /* Wait for the radio to signal an interrupt */ static void -ao_radio_wait_isr(AO_TICK_TYPE timeout) +_ao_radio_wait_isr(AO_TICK_TYPE timeout) { - ao_arch_block_interrupts(); while (!ao_radio_wake && !ao_radio_abort) if (ao_sleep_for(&ao_radio_wake, timeout)) ao_radio_abort = 1; +} + +static void +ao_radio_wait_isr(AO_TICK_TYPE timeout) +{ + ao_arch_block_interrupts(); + _ao_radio_wait_isr(timeout); ao_arch_release_interrupts(); } @@ -943,11 +939,12 @@ ao_radio_send_aprs(ao_radio_fill_func fill) ao_radio_set_len((uint8_t) (total & 0xff)); /* Wait for some space in the fifo */ + ao_arch_block_interrupts(); while (started && ao_radio_int_pin() != 0 && !ao_radio_abort) { ao_radio_wake = 0; - ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN); - ao_radio_wait_isr(AO_MS_TO_TICKS(1000)); + _ao_radio_wait_isr(AO_MS_TO_TICKS(1000)); } + ao_arch_release_interrupts(); if (ao_radio_abort) break; @@ -963,11 +960,12 @@ ao_radio_send_aprs(ao_radio_fill_func fill) } } /* Wait for the transmitter to go idle */ + ao_arch_block_interrupts(); while (started && ao_radio_int_pin() != 0 && !ao_radio_abort) { ao_radio_wake = 0; - ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN); - ao_radio_wait_isr(AO_MS_TO_TICKS(1000)); + _ao_radio_wait_isr(AO_MS_TO_TICKS(1000)); } + ao_arch_release_interrupts(); if (ao_radio_abort) ao_radio_idle(); ao_radio_put(); @@ -1033,6 +1031,8 @@ ao_radio_recv(void *d, uint8_t size, AO_TICK_TYPE timeout) while (!ao_radio_abort) { ao_radio_wait_isr(timeout); + if (ao_radio_abort) + break; if (ao_radio_wake) { uint8_t marc_status1 = ao_radio_reg_read(CC1200_MARC_STATUS1); @@ -1481,5 +1481,7 @@ ao_radio_init(void) AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH, ao_radio_isr); + ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN); + ao_cmd_register(&ao_radio_cmds[0]); } diff --git a/src/drivers/ao_gps_skytraq.c b/src/drivers/ao_gps_skytraq.c index 3a253eea..00c6932f 100644 --- a/src/drivers/ao_gps_skytraq.c +++ b/src/drivers/ao_gps_skytraq.c @@ -40,6 +40,7 @@ static uint8_t ao_gps_cksum; static uint8_t ao_gps_error; AO_TICK_TYPE ao_gps_tick; +AO_TICK_TYPE ao_gps_utc_tick; struct ao_telemetry_location ao_gps_data; struct ao_telemetry_satellite ao_gps_tracking_data; @@ -296,7 +297,7 @@ ao_nmea_gga(void) if (!ao_gps_error) { ao_mutex_get(&ao_gps_mutex); ao_gps_new |= AO_GPS_NEW_DATA; - ao_gps_tick = ao_gps_next_tick; + ao_gps_tick = ao_gps_utc_tick = ao_gps_next_tick; memcpy(&ao_gps_data, &ao_gps_next, sizeof (ao_gps_data)); ao_mutex_put(&ao_gps_mutex); ao_wakeup(&ao_gps_new); diff --git a/src/drivers/ao_gps_ublox.c b/src/drivers/ao_gps_ublox.c index 57cbf22a..3d615e9c 100644 --- a/src/drivers/ao_gps_ublox.c +++ b/src/drivers/ao_gps_ublox.c @@ -29,6 +29,7 @@ uint8_t ao_gps_new; uint8_t ao_gps_mutex; AO_TICK_TYPE ao_gps_tick; +AO_TICK_TYPE ao_gps_utc_tick; struct ao_telemetry_location ao_gps_data; struct ao_telemetry_satellite ao_gps_tracking_data; @@ -63,12 +64,13 @@ static uint16_t ao_ublox_len; #if AO_UBLOX_DEBUG -static uint8_t ao_gps_dbg_enable; #define DBG_PROTO 1 #define DBG_CHAR 2 #define DBG_INIT 4 +static uint8_t ao_gps_dbg_enable = DBG_PROTO|DBG_CHAR|DBG_INIT; + static void ao_gps_dbg(int level, char *format, ...) { va_list a; @@ -397,6 +399,7 @@ ao_ublox_parse_nav_svinfo(void) * NAV-TIMEUTC message parsing */ static struct nav_timeutc { + int32_t nano; uint16_t year; uint8_t month; uint8_t day; @@ -411,7 +414,8 @@ static struct nav_timeutc { #define NAV_TIMEUTC_VALID_UTC 2 static const struct ublox_packet_parse nav_timeutc_packet[] = { - { UBLOX_DISCARD, 12 }, /* 0 iTOW, tAcc, nano */ + { UBLOX_DISCARD, 8 }, /* 0 iTOW, tAcc */ + { UBLOX_U32, offsetof(struct nav_timeutc, nano) }, /* 8 nano */ { UBLOX_U16, offsetof(struct nav_timeutc, year) }, /* 12 year */ { UBLOX_U8, offsetof(struct nav_timeutc, month) }, /* 14 month */ { UBLOX_U8, offsetof(struct nav_timeutc, day) }, /* 15 day */ @@ -721,7 +725,7 @@ ao_gps(void) case UBLOX_NAV_TIMEUTC: ao_mutex_get(&ao_gps_mutex); ao_gps_tick = solution_tick; - + ao_gps_utc_tick = packet_start_tick + (AO_TICK_TYPE) AO_NS_TO_TICKS(nav_timeutc.nano); ao_gps_data.flags = 0; ao_gps_data.flags |= AO_GPS_RUNNING; if (nav_sol.gps_fix & (1 << NAV_SOL_FLAGS_GPSFIXOK)) { @@ -788,7 +792,7 @@ ao_gps(void) #if AO_UBLOX_DEBUG static void ao_gps_option(void) { - uint16_t r = ao_cmd_hex(); + uint8_t r = (uint8_t) ao_cmd_hex(); if (ao_cmd_status != ao_cmd_success) { ao_cmd_status = ao_cmd_success; ao_gps_show(); diff --git a/src/drivers/ao_led.c b/src/drivers/ao_led.c index b32034e7..009e9bc2 100644 --- a/src/drivers/ao_led.c +++ b/src/drivers/ao_led.c @@ -20,7 +20,7 @@ static const struct { void *port; - uint16_t pin; + uint8_t pin; } ao_leds[] = { #ifdef LED_0_PORT [0] { LED_0_PORT, LED_0_PIN }, diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index f73b0956..1cddcaf5 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -154,8 +154,7 @@ ao_ms5607_isr(void) static uint32_t ao_ms5607_get_sample(uint8_t cmd) { - uint8_t reply[3]; - uint8_t read; + uint8_t reply[4]; ao_ms5607_done = 0; @@ -173,18 +172,22 @@ ao_ms5607_get_sample(uint8_t cmd) { ao_sleep((void *) &ao_ms5607_done); ao_arch_release_interrupts(); #if AO_MS5607_PRIVATE_PINS - stm_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 1); + ao_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 1); #else ao_ms5607_stop(); #endif ao_ms5607_start(); - read = AO_MS5607_ADC_READ; - ao_spi_send(&read, 1, AO_MS5607_SPI_INDEX); - ao_spi_recv(&reply, 3, AO_MS5607_SPI_INDEX); + reply[0] = AO_MS5607_ADC_READ; +#if defined(AO_SPI_DUPLEX) && AO_SPI_DUPLEX == 0 + ao_spi_send(reply, 1, AO_MS5607_SPI_INDEX); + ao_spi_recv(reply+1, 3, AO_MS5607_SPI_INDEX); +#else + ao_spi_duplex(&reply, &reply, 4, AO_MS5607_SPI_INDEX); +#endif ao_ms5607_stop(); - return ((uint32_t) reply[0] << 16) | ((uint32_t) reply[1] << 8) | (uint32_t) reply[2]; + return ((uint32_t) reply[1] << 16) | ((uint32_t) reply[2] << 8) | (uint32_t) reply[3]; } #ifndef AO_MS5607_BARO_OVERSAMPLE diff --git a/src/kernel/ao.h b/src/kernel/ao.h index 9ca2c60e..d714cecb 100644 --- a/src/kernel/ao.h +++ b/src/kernel/ao.h @@ -113,6 +113,7 @@ extern volatile AO_TICK_TYPE ao_tick_count; #endif #define AO_MS_TO_TICKS(ms) ((ms) / (1000 / AO_HERTZ)) #define AO_SEC_TO_TICKS(s) ((AO_TICK_TYPE) (s) * AO_HERTZ) +#define AO_NS_TO_TICKS(ns) ((ns) / (1000000000L / AO_HERTZ)) /* Returns the current time in ticks */ AO_TICK_TYPE @@ -390,6 +391,7 @@ ao_spi_slave(void); extern uint8_t ao_gps_new; extern AO_TICK_TYPE ao_gps_tick; +extern AO_TICK_TYPE ao_gps_utc_tick; extern uint8_t ao_gps_mutex; extern struct ao_telemetry_location ao_gps_data; extern struct ao_telemetry_satellite ao_gps_tracking_data; diff --git a/src/kernel/ao_task.c b/src/kernel/ao_task.c index dd278bde..b5045416 100644 --- a/src/kernel/ao_task.c +++ b/src/kernel/ao_task.c @@ -57,7 +57,17 @@ static inline void ao_check_stack(void) { #endif #ifndef SLEEP_HASH_SIZE +#ifdef __ARM_FEATURE_IDIV__ #define SLEEP_HASH_SIZE 17 +#else +#define SLEEP_HASH_SIZE 16 +#endif +#endif + +#if SLEEP_HASH_SIZE & (SLEEP_HASH_SIZE - 1) +#define SLEEP_HASH_SHIFT 0 +#else +#define SLEEP_HASH_SHIFT 2 #endif static struct ao_list run_queue; @@ -75,7 +85,7 @@ _ao_task_to_run_queue(struct ao_task *task) static struct ao_list * ao_task_sleep_queue(void *wchan) { - return &ao_sleep_queue[(uintptr_t) wchan % SLEEP_HASH_SIZE]; + return &ao_sleep_queue[(((uintptr_t) wchan) >> SLEEP_HASH_SHIFT) % SLEEP_HASH_SIZE]; } static void diff --git a/src/kernel/ao_telemetry.c b/src/kernel/ao_telemetry.c index 1fd4037f..e0f4d8c2 100644 --- a/src/kernel/ao_telemetry.c +++ b/src/kernel/ao_telemetry.c @@ -58,7 +58,7 @@ static AO_TICK_TYPE ao_aprs_time; #define AO_SEND_MEGA 1 #endif -#if defined (TELEMETRUM_V_2_0) || defined (TELEMETRUM_V_3_0) +#if defined (TELEMETRUM_V_2_0) || defined (TELEMETRUM_V_3_0) || defined (TELEMETRUM_V_4_0) #define AO_SEND_METRUM 1 #endif @@ -490,7 +490,10 @@ ao_set_aprs_time(void) } else { delta = second - ao_gps_data.second; } - ao_aprs_time = ao_gps_tick + AO_SEC_TO_TICKS(delta); + if (delta < (interval >> 1)) + delta += interval; + + ao_aprs_time = ao_gps_utc_tick + AO_SEC_TO_TICKS(delta); } else { ao_aprs_time += AO_SEC_TO_TICKS(ao_config.aprs_interval); } diff --git a/src/metro-m0/ao_pins.h b/src/metro-m0/ao_pins.h new file mode 100644 index 00000000..0f276c1f --- /dev/null +++ b/src/metro-m0/ao_pins.h @@ -0,0 +1,135 @@ +/* + * Copyright © 2022 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_PINS_H_ +#define _AO_PINS_H_ + +#define LED_0_PORT (&samd21_port_a) +#define LED_0_PIN 2 + +#define LED_BLUE (1 << 0) + +#define AO_LED_PANIC LED_BLUE + +#define HAS_USB 1 +#define USE_USB_STDIO 1 + +#define HAS_LED 1 + +#define AO_DFLL48M 48000000 +#define AO_XOSC32K 32768 + +#define AO_AHB_PRESCALER 1 +#define AO_APBA_PRESCALER 1 + +#define HAS_SPI_0 1 +#define HAS_SPI_5 1 + +/* + * SPI Flash memory + */ + +#define M25_MAX_CHIPS 1 + +#if 1 +#define AO_M25_SPI_CS_PORT (&samd21_port_a) +#define AO_M25_SPI_CS_MASK (1 << 13) +#define AO_M25_SPI_BUS AO_SPI_5_PB22_PB23_PB03 +#else + +#define AO_M25_SPI_CS_PORT (&samd21_port_a) +#define AO_M25_SPI_CS_MASK (1 << 14) /* D2 */ +#define AO_M25_SPI_BUS AO_SPI_4_PB10_PB11_PA12 + +#endif + +/* + * Beeper + */ + +#define HAS_BEEP 1 + +/* Beep on PA11 function F TCC0.3 */ + +#define AO_BEEP_TCC (&samd21_tcc0) +#define AO_BEEP_TCC_APBC_MASK SAMD21_PM_APBCMASK_TCC0 +#define AO_BEEP_PORT (&samd21_port_a) +#define AO_BEEP_PIN (11) +#define AO_BEEP_FUNC SAMD21_PORT_PMUX_FUNC_F + +/* ADC */ +#define AO_DATA_RING 32 + +#define HAS_ADC 1 + +struct ao_adc { + int16_t a[6]; + int16_t temp; +}; + +#define AO_NUM_ADC_PIN 6 +#define AO_NUM_ADC (AO_NUM_ADC_PIN + 1) + +#define AO_ADC_DUMP(p) \ + printf("tick: %5lu a0: %5d a1: %5d a2: %5d a3: %5d a4: %5d a5: %5d temp: %5d\n", \ + (p)->tick, \ + (p)->adc.a[0], (p)->adc.a[1], (p)->adc.a[2], \ + (p)->adc.a[3], (p)->adc.a[4], (p)->adc.a[5], \ + (p)->adc.temp); + +#define AO_ADC_PIN0_PORT (&samd21_port_a) +#define AO_ADC_PIN0_PIN 2 +#define AO_ADC_SQ0 0 + +#define AO_ADC_PIN1_PORT (&samd21_port_b) +#define AO_ADC_PIN1_PIN 8 +#define AO_ADC_SQ1 2 + +#define AO_ADC_PIN2_PORT (&samd21_port_b) +#define AO_ADC_PIN2_PIN 9 +#define AO_ADC_SQ2 3 + +#define AO_ADC_PIN3_PORT (&samd21_port_a) +#define AO_ADC_PIN3_PIN 4 +#define AO_ADC_SQ3 4 + +#define AO_ADC_PIN4_PORT (&samd21_port_a) +#define AO_ADC_PIN4_PIN 5 +#define AO_ADC_SQ4 5 + +#define AO_ADC_PIN5_PORT (&samd21_port_b) +#define AO_ADC_PIN5_PIN 2 +#define AO_ADC_SQ5 10 + +#define AO_ADC_SQ6 SAMD21_ADC_INPUTCTRL_MUXPOS_TEMP + +/* GPS */ +#define HAS_GPS 1 + +#define AO_SERIAL_SPEED_UBLOX AO_SERIAL_SPEED_9600 + +#define HAS_SERIAL_0 1 +#define USE_SERIAL_0_STDIN 0 +#define SERIAL_0_PA08_PA09 1 + +#define ao_gps_getchar ao_serial0_getchar +#define ao_gps_putchar ao_serial0_putchar +#define ao_gps_set_speed ao_serial0_set_speed +#define ao_gps_fifo (ao_samd21_usart0.rx_fifo) + +#endif /* _AO_PINS_H_ */ diff --git a/src/metro-m0/flash-loader/ao_pins.h b/src/metro-m0/flash-loader/ao_pins.h new file mode 100644 index 00000000..9a621480 --- /dev/null +++ b/src/metro-m0/flash-loader/ao_pins.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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 + +/* A0 to gnd for boot loader mode */ + +#define AO_BOOT_PIN 1 +#define AO_BOOT_APPLICATION_GPIO (samd21_port_a) +#define AO_BOOT_APPLICATION_PIN 2 +#define AO_BOOT_APPLICATION_VALUE 1 +#define AO_BOOT_APPLICATION_MODE AO_MODE_PULL_UP + +/* USB */ +#define HAS_USB 1 + +#define AO_DFLL48M 48000000 +#define AO_XOSC32K 32768 + +#endif /* _AO_PINS_H_ */ diff --git a/src/metro-m0/metro-m0.c b/src/metro-m0/metro-m0.c new file mode 100644 index 00000000..5fe7723b --- /dev/null +++ b/src/metro-m0/metro-m0.c @@ -0,0 +1,82 @@ +/* + * Copyright © 2016 Keith Packard + * + * 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 +#include +#include +#include + +static int pressed; + +static void +ao_button_callback(void) +{ + pressed = 1; + ao_wakeup(&pressed); +} + +static void +ao_beep_test(void) +{ + AO_BEEP_TCC->ctrlbset = (SAMD21_TCC_CTRLB_CMD_READSYNC << SAMD21_TCC_CTRLB_CMD); + printf("pressed timer %ld\n", AO_BEEP_TCC->count); + fflush(stdout); + ao_beep_for(AO_BEEP_MID_DEFAULT, AO_MS_TO_TICKS(200)); +} + +static void +ao_button(void) +{ + ao_exti_setup(&samd21_port_a, 10, AO_EXTI_MODE_FALLING | AO_EXTI_MODE_PULL_UP, ao_button_callback); + ao_exti_enable(&samd21_port_a, 10); + for (;;) { + ao_arch_block_interrupts(); + pressed = 0; + while (!pressed) + ao_sleep(&pressed); + ao_arch_release_interrupts(); + ao_beep_test(); + } +} + +static struct ao_task ao_button_task; + +const struct ao_cmds ao_test_cmds[] = { + { ao_beep_test, "b \0beep" }, + { 0, NULL }, +}; + +int main(void) +{ + ao_led_init(); + ao_clock_init(); + ao_task_init(); + ao_timer_init(); + ao_dma_init(); + ao_exti_init(); + ao_spi_init(); + ao_adc_init(); + ao_serial_init(); + ao_gps_init(); + ao_beep_init(); + ao_usb_init(); + ao_storage_init(); + ao_cmd_init(); + + ao_cmd_register(ao_test_cmds); + ao_add_task(&ao_button_task, ao_button, "button"); + ao_start_scheduler(); + + return 0; +} diff --git a/src/samd21/Makefile-flash.defs b/src/samd21/Makefile-flash.defs new file mode 100644 index 00000000..d489bf1a --- /dev/null +++ b/src/samd21/Makefile-flash.defs @@ -0,0 +1,58 @@ +include $(TOPDIR)/samd21/Makefile-samd21.defs + +INC = \ + ao.h \ + ao_arch.h \ + ao_arch_funcs.h \ + ao_flash_pins.h \ + ao_flash_samd21_pins.h \ + ao_flash_task.h \ + ao_pins.h \ + ao_product.h \ + Makefile + +# +# Common AltOS sources +# +SRC = \ + ao_interrupt.c \ + ao_romconfig.c \ + ao_boot_chain.c \ + ao_boot_pin.c \ + ao_product.c \ + ao_notask.c \ + ao_timer.c \ + ao_usb_samd21.c \ + ao_flash_samd21.c \ + ao_flash_task.c \ + ao_flash_loader_samd21.c + +OBJ=$(SRC:.c=.o) + +PRODUCT=AltosFlash +PRODUCT_DEF=-DALTOS_FLASH +IDPRODUCT=0x000a + +CFLAGS = $(PRODUCT_DEF) $(SAMD21_CFLAGS) + +LDFLAGS=-nostartfiles $(CFLAGS) -L$(TOPDIR)/samd21 -Taltos-loader.ld -n + +PROGNAME=$(HARDWARE)-altos-flash +PROG=$(PROGNAME)-$(VERSION).elf + +$(PROG): Makefile $(OBJ) altos-loader.ld + $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS) + +$(OBJ): $(INC) + +all: $(PROG) + +distclean: clean + +clean: + rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.map + rm -f ao_product.h + +install: + +uninstall: diff --git a/src/samd21/Makefile-samd21.defs b/src/samd21/Makefile-samd21.defs new file mode 100644 index 00000000..ef8713c6 --- /dev/null +++ b/src/samd21/Makefile-samd21.defs @@ -0,0 +1,12 @@ +ifndef TOPDIR +TOPDIR=.. +endif + +include $(TOPDIR)/Makefile.defs + +vpath % $(TOPDIR)/samd21:$(AO_VPATH) + +CC=$(ARM_CC) + +SAMD21_CFLAGS=-mlittle-endian -mcpu=cortex-m0 -mthumb \ + -I$(TOPDIR)/samd21 $(AO_CFLAGS) $(PICOLIBC_CFLAGS) diff --git a/src/samd21/Makefile.defs b/src/samd21/Makefile.defs new file mode 100644 index 00000000..d58db934 --- /dev/null +++ b/src/samd21/Makefile.defs @@ -0,0 +1,11 @@ +ifndef TOPDIR +TOPDIR=.. +endif + +include $(TOPDIR)/samd21/Makefile-samd21.defs + +SAMD21_LINKER_SCRIPT=altos-$(SAMD21_ROM)-$(SAMD21_RAM).ld + +LDFLAGS=-nostartfiles $(CFLAGS) -L$(TOPDIR)/samd21 -T$(SAMD21_LINKER_SCRIPT) -n + +.DEFAULT_GOAL=all diff --git a/src/samd21/altos-128-16.ld b/src/samd21/altos-128-16.ld new file mode 100644 index 00000000..e07a6a4e --- /dev/null +++ b/src/samd21/altos-128-16.ld @@ -0,0 +1,26 @@ +/* + * Copyright © 2022 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +__flash = 0x00001000; +__flash_size = 128K - 4K; +__ram = 0x20000000; +__ram_size = 16k; +__stack_size = 256; + +INCLUDE registers.ld +INCLUDE picolibc.ld diff --git a/src/samd21/altos-256-32.ld b/src/samd21/altos-256-32.ld new file mode 100644 index 00000000..4be7d676 --- /dev/null +++ b/src/samd21/altos-256-32.ld @@ -0,0 +1,26 @@ +/* + * Copyright © 2022 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +__flash = 0x00001000; +__flash_size = 256 - 4K; +__ram = 0x20000000; +__ram_size = 32k; +__stack_size = 256; + +INCLUDE registers.ld +INCLUDE picolibc.ld diff --git a/src/samd21/altos-loader.ld b/src/samd21/altos-loader.ld new file mode 100644 index 00000000..a06d28b2 --- /dev/null +++ b/src/samd21/altos-loader.ld @@ -0,0 +1,26 @@ +/* + * Copyright © 2012 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +__flash = 0x00000000; +__flash_size = 4K; +__ram = 0x20000000; +__ram_size = 4k; +__stack_size = 256; + +INCLUDE registers.ld +INCLUDE picolibc.ld diff --git a/src/samd21/ao_adc_samd21.c b/src/samd21/ao_adc_samd21.c new file mode 100644 index 00000000..6d67eda7 --- /dev/null +++ b/src/samd21/ao_adc_samd21.c @@ -0,0 +1,216 @@ +/* + * Copyright © 2019 Keith Packard + * + * 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 +#include + +static void +ao_adc_sync(void) +{ + while (samd21_adc.status & (1 << SAMD21_ADC_STATUS_SYNCBUSY)) + ; +} + +static uint8_t ao_adc_sequence; +static uint8_t ao_adc_ready; + +static uint8_t ao_adc_mux[AO_NUM_ADC] = { +#if AO_NUM_ADC > 0 + AO_ADC_SQ0, +#endif +#if AO_NUM_ADC > 1 + AO_ADC_SQ1, +#endif +#if AO_NUM_ADC > 2 + AO_ADC_SQ2, +#endif +#if AO_NUM_ADC > 3 + AO_ADC_SQ3, +#endif +#if AO_NUM_ADC > 4 + AO_ADC_SQ4, +#endif +#if AO_NUM_ADC > 5 + AO_ADC_SQ5, +#endif +#if AO_NUM_ADC > 6 + AO_ADC_SQ6, +#endif +#if AO_NUM_ADC > 7 + AO_ADC_SQ7, +#endif +#if AO_NUM_ADC > 8 + AO_ADC_SQ8, +#endif +#if AO_NUM_ADC > 9 +#error set up more ADC +#endif +}; + +static void +ao_adc_start(void) +{ + uint8_t mux = ao_adc_mux[ao_adc_sequence]; + samd21_adc.inputctrl = ((mux << SAMD21_ADC_INPUTCTRL_MUXPOS) | + (SAMD21_ADC_INPUTCTRL_MUXNEG_GND << SAMD21_ADC_INPUTCTRL_MUXNEG) | + (0 << SAMD21_ADC_INPUTCTRL_INPUTSCAN) | + (0 << SAMD21_ADC_INPUTCTRL_INPUTOFFSET) | + (SAMD21_ADC_INPUTCTRL_GAIN_DIV2 << SAMD21_ADC_INPUTCTRL_GAIN)); + samd21_adc.swtrig = (1UL << SAMD21_ADC_SWTRIG_START); +} + +void +samd21_adc_isr(void) +{ + uint16_t *out; + + /* Store converted value in packet */ + out = (uint16_t *) &ao_data_ring[ao_data_head].adc; + out[ao_adc_sequence] = (uint16_t) samd21_adc.result; + if (++ao_adc_sequence < AO_NUM_ADC) { + ao_adc_start(); + return; + } + + AO_DATA_PRESENT(AO_DATA_ADC); + ao_data_fill(ao_data_head); + ao_adc_ready = 1; +} + +void +ao_adc_poll(void) +{ + if (!ao_adc_ready) + return; + ao_adc_ready = 0; + ao_adc_sequence = 0; + ao_adc_start(); +} + +static void +ao_adc_dump(void) +{ + struct ao_data packet; + + ao_data_get(&packet); + AO_ADC_DUMP(&packet); +} + +const struct ao_cmds ao_adc_cmds[] = { + { ao_adc_dump, "a\0Display current ADC values" }, + { 0, NULL }, +}; + +static inline void +set_adc(struct samd21_port *port, uint8_t pin) +{ + samd21_port_pmux_set(port, pin, SAMD21_PORT_PMUX_FUNC_B); + samd21_port_pincfg_set(port, pin, + (1 << SAMD21_PORT_PINCFG_DRVSTR) | + (1 << SAMD21_PORT_PINCFG_PULLEN) | + (1 << SAMD21_PORT_PINCFG_INEN), + (0 << SAMD21_PORT_PINCFG_DRVSTR) | + (0 << SAMD21_PORT_PINCFG_PULLEN) | + (1 << SAMD21_PORT_PINCFG_INEN)); +} + +void +ao_adc_init(void) +{ + /* supply a clock */ + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_ADC); + + /* enable the device */ + samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_ADC); + + /* Reset */ + samd21_adc.ctrla = (1 << SAMD21_ADC_CTRLA_SWRST); + + ao_adc_sync(); + + while ((samd21_adc.ctrla & (1 << SAMD21_ADC_CTRLA_SWRST)) != 0 || + (samd21_adc.status & (1 << SAMD21_ADC_STATUS_SYNCBUSY)) != 0) + ao_adc_sync(); + + /* Load ADC calibration values */ + uint32_t b = (samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_ADC_BIASCAL) & SAMD21_AUX1_CALIBRATION_ADC_BIASCAL_MASK; + uint32_t l = (samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_ADC_LINEARITY) & SAMD21_AUX1_CALIBRATION_ADC_LINEARITY_MASK; + + samd21_adc.calib = (uint16_t) ((b << SAMD21_ADC_CALIB_BIAS_CAL) | + (l << SAMD21_ADC_CALIB_LINEARITY_CAL)); + + + ao_adc_sync(); + + samd21_adc.ctrlb = ((0 << SAMD21_ADC_CTRLB_DIFFMODE) | + (0 << SAMD21_ADC_CTRLB_LEFTADJ) | + (0 << SAMD21_ADC_CTRLB_FREERUN) | + (0 << SAMD21_ADC_CTRLB_CORREN) | + (SAMD21_ADC_CTRLB_RESSEL_12BIT << SAMD21_ADC_CTRLB_RESSEL) | + (SAMD21_ADC_CTRLB_PRESCALER_DIV512 << SAMD21_ADC_CTRLB_PRESCALER)); + + ao_adc_sync(); + + samd21_adc.sampctrl = 0x1f; + + ao_adc_sync(); + + samd21_adc.refctrl = (SAMD21_ADC_REFCTRL_REFSEL_INTVCC1 << SAMD21_ADC_REFCTRL_REFSEL); + + ao_adc_sync(); + + samd21_adc.intenset = (1UL << SAMD21_ADC_INTFLAG_RESRDY); + + samd21_adc.ctrla = (1 << SAMD21_ADC_CTRLA_ENABLE); + + /* configure interrupts */ + samd21_nvic_set_enable(SAMD21_NVIC_ISR_ADC_POS); + samd21_nvic_set_priority(SAMD21_NVIC_ISR_ADC_POS, 0); + + ao_cmd_register(&ao_adc_cmds[0]); + + /* configure pins */ +#if AO_NUM_ADC_PIN > 0 + set_adc(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN); +#endif +#if AO_NUM_ADC_PIN > 1 + set_adc(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN); +#endif +#if AO_NUM_ADC_PIN > 2 + set_adc(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN); +#endif +#if AO_NUM_ADC_PIN > 3 + set_adc(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN); +#endif +#if AO_NUM_ADC_PIN > 4 + set_adc(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN); +#endif +#if AO_NUM_ADC_PIN > 5 + set_adc(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN); +#endif +#if AO_NUM_ADC_PIN > 6 + set_adc(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN); +#endif +#if AO_NUM_ADC_PIN > 7 + set_adc(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN); +#endif +#if AO_NUM_ADC_PIN > 8 + set_adc(AO_ADC_PIN8_PORT, AO_ADC_PIN8_PIN); +#endif +#if AO_NUM_ADC_PIN > 9 +#error set up more ADC bits +#endif + + ao_adc_ready = 1; +} diff --git a/src/samd21/ao_adc_samd21.h b/src/samd21/ao_adc_samd21.h new file mode 100644 index 00000000..3d1658be --- /dev/null +++ b/src/samd21/ao_adc_samd21.h @@ -0,0 +1,24 @@ +/* + * Copyright © 2019 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _AO_ADC_SAMD21_H_ +#define _AO_ADC_SAMD21_H_ + +void +ao_adc_poll(void); + +void +ao_adc_init(void); + +#endif /* _AO_ADC_SAMD21_H_ */ diff --git a/src/samd21/ao_apa102.c b/src/samd21/ao_apa102.c new file mode 100644 index 00000000..e391f963 --- /dev/null +++ b/src/samd21/ao_apa102.c @@ -0,0 +1,66 @@ +/* + * Copyright © 2019 Keith Packard + * + * 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 +#include + +#define bit(v) do { \ + port_c->outtgl = (1 << pin_c); \ + ao_arch_nop(); \ + ao_gpio_set(port_d, pin_d, v); \ + ao_arch_nop(); \ + port_c->outtgl = (1 << pin_c); \ + ao_arch_nop(); \ + } while (0) + +#define byte(v) do { \ + uint8_t _bit_ = 0x80; \ + while (_bit_) { \ + bit(!!((v) & _bit_)); \ + _bit_ >>= 1; \ + } \ + } while(0) + +#define repeat(v,c) do { \ + uint8_t _i_; \ + for (_i_ = 0; _i_ < (c); _i_++) \ + bit(v); \ + } while (0) + +void +ao_snek_apa102_write(void *gpio_d, uint8_t pin_d, + void *gpio_c, uint8_t pin_c, + int npixel, + struct snek_neopixel *pixels) +{ + struct samd21_port *port_d = gpio_d; + struct samd21_port *port_c = gpio_c; + + ao_gpio_set(port_c, pin_c, 1); + int i; + for (i = 0; i < 32; i++) + ao_arch_nop(); + repeat(0, 32); + while (npixel--) { + byte(0xff); + byte(pixels->b); + byte(pixels->g); + byte(pixels->r); + } + repeat(1, 32); +} diff --git a/src/samd21/ao_arch.h b/src/samd21/ao_arch.h new file mode 100644 index 00000000..a5f68daf --- /dev/null +++ b/src/samd21/ao_arch.h @@ -0,0 +1,212 @@ +/* + * Copyright © 2019 Keith Packard + * + * 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 +#include + +/* + * Samd21 definitions and code fragments for AltOS + */ + +#define AO_PORT_TYPE uint32_t + +#define AO_LED_TYPE AO_PORT_TYPE + +#define ao_arch_naked_declare __attribute__((naked)) +#define ao_arch_naked_define + +#define ao_arch_reboot() \ + (samd21_scb.aircr = ((SAMD21_SCB_AIRCR_VECTKEY_KEY << SAMD21_SCB_AIRCR_VECTKEY) | \ + (1 << SAMD21_SCB_AIRCR_SYSRESETREQ))) + +#define ao_arch_nop() asm("nop") +#define ao_arch_interrupt(n) /* nothing */ +#define ao_arch_block_interrupts() asm("cpsid i") +#define ao_arch_release_interrupts() asm("cpsie i") + +/* ao_romconfig.c */ +#define AO_ROMCONFIG_SYMBOL __attribute__((section(".init.1"))) const +#define AO_USBCONFIG_SYMBOL __attribute__((section(".init.2"))) const + +/* + * ao_timer.c + * + * For the samd21, we want to use the DFLL48 clock + */ + +/* GCLK 0 is always the system clock source */ + +#define AO_GCLK_SYSCLK 0 + +/* If there's a 32kHz xtal, use that for the 32kHz oscillator */ +#ifdef AO_XOSC32K +# ifndef AO_GCLK_XOSC32K +# define AO_GCLK_XOSC32K 1 +# endif +#endif + +/* If there's a high-freq xtal, use that */ +#ifdef AO_XOSC +# ifndef AO_GCLK_XOSC +# define AO_GCLK_XOSC 1 +# endif + +# ifndef AO_XOSC_GCLK_DIV +# define AO_XOSC_GCLK_DIV 1 +# endif + +# define AO_FDPLL96M ((AO_XOSC_FREQ / AO_XOSC_DIV * AO_XOSC_MUL) / AO_XOSC_GCLK_DIV) + +/* By default, use the xosc for the system, but allow the dfll48m to + * drive USB if desired. + */ + +# ifndef AO_GCLK_FDPLL96M +# define AO_SYSCLK AO_FDPLL96M +# define AO_GCLK_FDPLL96M AO_GCLK_SYSCLK +# define AO_GCLK_DFLL48M 2 +# endif + +#endif /* AO_XOSC */ + +#if AO_DFLL48M +# ifndef AO_GCLK_DFLL48M +# define AO_SYSCLK AO_DFLL48M +# define AO_GCLK_DFLL48M AO_GCLK_SYSCLK +# endif +#endif + +#ifndef AO_GCLK_USB +# if AO_DFLL48M +# define AO_GCLK_USB AO_GCLK_DFLL48M +# else +# define AO_GCLK_USB AO_GCLK_SYSCLK +# endif +#endif + +/* + * ao_timer.c + * + * For the samd21, we want to use the DFLL48 clock + */ + +/* GCLK 0 is always the system clock source */ + +#define AO_GCLK_SYSCLK 0 + +/* If there's a 32kHz xtal, use that for the 32kHz oscillator */ +#ifdef AO_XOSC32K +# ifndef AO_GCLK_XOSC32K +# define AO_GCLK_XOSC32K 1 +# endif +#endif + +/* If there's a high-freq xtal, use that */ +#ifdef AO_XOSC +# ifndef AO_GCLK_XOSC +# define AO_GCLK_XOSC 1 +# endif + +# ifndef AO_XOSC_GCLK_DIV +# define AO_XOSC_GCLK_DIV 1 +# endif + +# define AO_FDPLL96M ((AO_XOSC_FREQ / AO_XOSC_DIV * AO_XOSC_MUL) / AO_XOSC_GCLK_DIV) + +/* By default, use the xosc for the system, but allow the dfll48m to + * drive USB if desired. + */ + +# ifndef AO_GCLK_FDPLL96M +# define AO_SYSCLK AO_FDPLL96M +# define AO_GCLK_FDPLL96M AO_GCLK_SYSCLK +# define AO_GCLK_DFLL48M 2 +# endif + +#endif /* AO_XOSC */ + +#if AO_DFLL48M +# ifndef AO_GCLK_DFLL48M +# define AO_SYSCLK AO_DFLL48M +# define AO_GCLK_DFLL48M AO_GCLK_SYSCLK +# endif +#endif + +#ifndef AO_GCLK_USB +# if AO_DFLL48M +# define AO_GCLK_USB AO_GCLK_DFLL48M +# else +# define AO_GCLK_USB AO_GCLK_SYSCLK +# endif +#endif + +#define AO_HCLK (AO_SYSCLK / AO_AHB_PRESCALER) + +#define AO_HCLK (AO_SYSCLK / AO_AHB_PRESCALER) +#define AO_PCLK (AO_HCLK / AO_APB_PRESCALER) +#define AO_SYSTICK (AO_HCLK) +#define AO_PANIC_DELAY_SCALE (AO_SYSCLK / 12000000) + +#define AO_SAMD21_NVIC_HIGH_PRIORITY (0 << 6) +#define AO_SAMD21_NVIC_CLOCK_PRIORITY (1 << 6) +#define AO_SAMD21_NVIC_MED_PRIORITY (2 << 6) +#define AO_SAMD21_NVIC_LOW_PRIORITY (3 << 6) + +/* ADC maximum reported value */ +#define AO_ADC_MAX 4095 + +#define AO_SAMD21_NVIC_HIGH_PRIORITY (0 << 6) +#define AO_SAMD21_NVIC_CLOCK_PRIORITY (1 << 6) +#define AO_SAMD21_NVIC_MED_PRIORITY (2 << 6) +#define AO_SAMD21_NVIC_LOW_PRIORITY (3 << 6) + +/* ADC maximum reported value */ +#define AO_ADC_MAX 4095 + +/* This has to be 65536 so that TCC and TC match; TC isn't configurable */ +#define AO_TCC_PERIOD 65536 +#define SNEK_PWM_MAX (AO_TCC_PERIOD-1) + +#define AO_TICK_TYPE uint32_t +#define AO_TICK_SIGNED int32_t + +bool +ao_usb_waiting(void); + +#define AO_CMD_LEN 128 + +#ifndef AO_STACK_SIZE +#define AO_STACK_SIZE 512 +#endif + +#ifndef HAS_BOOT_LOADER +#define HAS_BOOT_LOADER 1 +#endif + +#if HAS_BOOT_LOADER +#define AO_BOOT_APPLICATION_BASE ((uint32_t *) 0x00001000) +#ifndef AO_BOOT_APPLICATION_BOUND +#define AO_BOOT_APPLICATION_BOUND ((uint32_t *) (0x00000000 + samd21_flash_size())) +#endif +#define AO_BOOT_LOADER_BASE ((uint32_t *) 0x00000000) +#endif + +#endif /* _AO_ARCH_H_ */ diff --git a/src/samd21/ao_arch_funcs.h b/src/samd21/ao_arch_funcs.h new file mode 100644 index 00000000..32670b45 --- /dev/null +++ b/src/samd21/ao_arch_funcs.h @@ -0,0 +1,480 @@ +/* + * Copyright © 2019 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_ARCH_FUNCS_H_ +#define _AO_ARCH_FUNCS_H_ + +#define AO_MODE_PULL_NONE 0 +#define AO_MODE_PULL_UP 1 +#define AO_MODE_PULL_DOWN 2 + +static inline void ao_enable_port(struct samd21_port *port) +{ + (void) port; + samd21_pm.apbbmask |= (1UL << SAMD21_PM_APBBMASK_PORT); +} + +static inline void ao_disable_port(struct samd21_port *port) +{ + (void) port; + samd21_pm.apbbmask &= ~(1UL << SAMD21_PM_APBBMASK_PORT); +} + +static inline void +ao_gpio_set(struct samd21_port *port, uint8_t bit, uint8_t v) +{ + if (v) + port->outset = (1 << bit); + else + port->outclr = (1 << bit); +} + +static inline uint8_t +ao_gpio_get(struct samd21_port *port, uint8_t bit) +{ + return (port->in >> bit) & 1; +} + +static inline void +ao_gpio_dir_set(struct samd21_port *port, uint8_t bit, bool output) +{ + if (output) + port->dirset = (1 << bit); + else + port->dirclr = (1 << bit); +} + +static inline void +ao_gpio_set_mode(struct samd21_port *port, uint8_t bit, uint32_t mode) +{ + uint8_t pincfg = 0; + + if (mode != AO_MODE_PULL_NONE) { + pincfg |= (1 << SAMD21_PORT_PINCFG_PULLEN); + ao_gpio_set(port, bit, mode == AO_MODE_PULL_UP); + } + + samd21_port_pincfg_set(port, bit, + (0 << SAMD21_PORT_PINCFG_DRVSTR) | + (1 << SAMD21_PORT_PINCFG_PULLEN) | + (0 << SAMD21_PORT_PINCFG_INEN) | + (0 << SAMD21_PORT_PINCFG_PMUXEN), + pincfg); +} + +static inline void +ao_enable_output(struct samd21_port *port, uint8_t pin, uint8_t v) +{ + ao_enable_port(port); + ao_gpio_set(port, pin, v); + samd21_port_dir_set(port, pin, SAMD21_PORT_DIR_OUT); + samd21_port_pincfg_set(port, pin, + (1 << SAMD21_PORT_PINCFG_DRVSTR) | + (1 << SAMD21_PORT_PINCFG_PULLEN) | + (1 << SAMD21_PORT_PINCFG_INEN), + (0 << SAMD21_PORT_PINCFG_DRVSTR) | + (0 << SAMD21_PORT_PINCFG_PULLEN) | + (0 << SAMD21_PORT_PINCFG_INEN)); +} + +static inline void +ao_enable_input(struct samd21_port *port, uint8_t pin, uint32_t mode) +{ + ao_enable_port(port); + samd21_port_dir_set(port, pin, SAMD21_PORT_DIR_IN); + uint8_t pincfg; + + pincfg = ((0 << SAMD21_PORT_PINCFG_DRVSTR) | + (0 << SAMD21_PORT_PINCFG_PULLEN) | + (1 << SAMD21_PORT_PINCFG_INEN) | + (0 << SAMD21_PORT_PINCFG_PMUXEN)); + + if (mode != AO_MODE_PULL_NONE) { + pincfg |= (1 << SAMD21_PORT_PINCFG_PULLEN); + ao_gpio_set(port, pin, mode == AO_MODE_PULL_UP); + } + + samd21_port_pincfg_set(port, pin, + (1 << SAMD21_PORT_PINCFG_DRVSTR) | + (1 << SAMD21_PORT_PINCFG_PULLEN) | + (1 << SAMD21_PORT_PINCFG_INEN) | + (1 << SAMD21_PORT_PINCFG_PMUXEN), + pincfg); +} + +static inline void +ao_enable_cs(struct samd21_port *port, uint8_t pin) +{ + ao_enable_output(port, pin, 1); +} + +/* ao_spi_samd21.c */ + +#define AO_SPI_INDEX_BIT 0 +#define AO_SPI_INDEX_MASK 0x07 + +#define AO_SPI_CONFIG_BIT 4 +#define AO_SPI_CONFIG_MASK (3 << AO_SPI_CONFIG_BIT) + +#define AO_SPI_CPOL_BIT 6 +#define AO_SPI_CPHA_BIT 7 + +#define AO_SPI_DOPO_BIT 8 +#define AO_SPI_DOPO_MOSI_0_SCLK_1 (0 << AO_SPI_DOPO_BIT) +#define AO_SPI_DOPO_MOSI_2_SCLK_3 (1 << AO_SPI_DOPO_BIT) +#define AO_SPI_DOPO_MOSI_3_SCLK_1 (2 << AO_SPI_DOPO_BIT) +#define AO_SPI_DOPO_MOSI_0_SCLK_3 (3 << AO_SPI_DOPO_BIT) +#define AO_SPI_DOPO_MASK (3 << AO_SPI_DOPO_BIT) + +#define AO_SPI_DIPO_BIT 10 +#define AO_SPI_DIPO_MISO_0 (0 << AO_SPI_DIPO_BIT) +#define AO_SPI_DIPO_MISO_1 (1 << AO_SPI_DIPO_BIT) +#define AO_SPI_DIPO_MISO_2 (2 << AO_SPI_DIPO_BIT) +#define AO_SPI_DIPO_MISO_3 (3 << AO_SPI_DIPO_BIT) +#define AO_SPI_DIPO_MASK (3 << AO_SPI_DIPO_MASK) + +#define AO_SPI_CONFIG_0 (0 << AO_SPI_CONFIG_BIT) +#define AO_SPI_CONFIG_1 (1 << AO_SPI_CONFIG_BIT) +#define AO_SPI_CONFIG_2 (2 << AO_SPI_CONFIG_BIT) +#define AO_SPI_CONFIG_3 (3 << AO_SPI_CONFIG_BIT) + +#define AO_SPI_INDEX(id) ((uint8_t) ((id) & AO_SPI_INDEX_MASK)) +#define AO_SPI_CONFIG(id) ((id) & AO_SPI_CONFIG_MASK) +#define AO_SPI_PIN_CONFIG(id) ((id) & (AO_SPI_INDEX_MASK | AO_SPI_CONFIG_MASK)) +#define AO_SPI_CPOL(id) ((uint32_t) (((id) >> AO_SPI_CPOL_BIT) & 1)) +#define AO_SPI_CPHA(id) ((uint32_t) (((id) >> AO_SPI_CPHA_BIT) & 1)) +#define AO_SPI_DOPO(id) ((uint32_t) (((id) >> AO_SPI_DOPO_BIT) & 3)) +#define AO_SPI_DIPO(id) ((uint32_t) (((id) >> AO_SPI_DIPO_BIT) & 3)) + +#define AO_SPI_MAKE_MODE(pol,pha) (((pol) << AO_SPI_CPOL_BIT) | ((pha) << AO_SPI_CPHA_BIT)) +#define AO_SPI_MODE_0 AO_SPI_MAKE_MODE(0,0) +#define AO_SPI_MODE_1 AO_SPI_MAKE_MODE(0,1) +#define AO_SPI_MODE_2 AO_SPI_MAKE_MODE(1,0) +#define AO_SPI_MODE_3 AO_SPI_MAKE_MODE(1,1) + +#if HAS_SPI_0 +/* + * PA08 SERCOM0.0 -> MOSI (DOPO 0) + * PA09 SERCOM0.1 -> SCLK (DOPO 0) + * PA10 SERCOM0.2 -> MISO (DIPO 2) + */ +#define AO_SPI_0_PA08_PA09_PA10 (0 | AO_SPI_CONFIG_0 | \ + AO_SPI_DOPO_MOSI_0_SCLK_1 | \ + AO_SPI_DIPO_MISO_2) +/* + * PA04 SERCOM0.0 -> MOSI (DOPO 0) + * PA05 SERCOM0.1 -> SCLK (DOPO 0) + * PA06 SERCOM0.2 -> MISO (DIPO 2) + */ +#define AO_SPI_0_PA04_PA05_PA06 (0 | AO_SPI_CONFIG_1 | \ + AO_SPI_DOPO_MOSI_0_SCLK_1 | \ + AO_SPI_DIPO_MISO_2) +#endif /* HAS_SPI_0 */ + +#if HAS_SPI_3 +/* + * PA22 SERCOM3.0 -> MOSI (DOPO 0) + * PA23 SERCOM3.1 -> SCLK (DOPO 0) + * PA20 SERCOM3.2 -> MISO (DIPO 2) + */ +#define AO_SPI_3_PA22_PA23_PA20 (3 | AO_SPI_CONFIG_0 | \ + AO_SPI_DOPO_MOSI_0_SCLK_1 | \ + AO_SPI_DIPO_MISO_2) +#endif /* HAS_SPI_3 */ + +#if HAS_SPI_4 +/* + * PA04 SERCOM0.0 -> MOSI (DOPO 0) + * PA05 SERCOM0.1 -> SCLK (DOPO 0) + * PA16 SERCOM0.2 -> MISO (DIPO 2) + */ +#define AO_SPI_CONFIG_PA04_PA05_PA06 (0 | AO_SPI_CONFIG_1 | \ + AO_SPI_DOPO_MOSI_0_SCLK_1 | \ + AO_SPI_DIPO_MISO_2) + +/* + * PB10 SERCOM4.2 -> MOSI (DOPO 1) + * PB11 SERCOM4.3 -> SCLK (DOPO 1) + * PA12 SERCOM4.0 -> MISO (DIPO 0) + */ +#define AO_SPI_4_PB10_PB11_PA12 (4 | AO_SPI_CONFIG_0 | \ + AO_SPI_DOPO_MOSI_2_SCLK_3 | \ + AO_SPI_DIPO_MISO_0) +#endif /* HAS_SPI_4 */ + +#if HAS_SPI_5 +/* + * PB22 SERCOM5.2 -> MOSI (DOPO 1) + * PB23 SERCOM5.3 -> SCLK (DOPO 1) + * PB03 SERCOM5.1 -> MISO (DIPO 1) + */ +#define AO_SPI_5_PB22_PB23_PB03 (5 | AO_SPI_CONFIG_0 | \ + AO_SPI_DOPO_MOSI_2_SCLK_3 | \ + AO_SPI_DIPO_MISO_1) +#endif /* HAS_SPI_5 */ + +void +ao_spi_send(const void *block, uint16_t len, uint16_t spi_index); + +void +ao_spi_send_fixed(uint8_t data, uint16_t len, uint16_t spi_index); + +void +ao_spi_recv(void *block, uint16_t len, uint16_t spi_index); + +void +ao_spi_duplex(const void *out, void *in, uint16_t len, uint16_t spi_index); + +void +ao_spi_get(uint16_t spi_index, uint32_t speed); + +void +ao_spi_put(uint16_t spi_index); + +void +ao_spi_init(void); + +#define ao_spi_set_cs(reg,mask) do { \ + reg->outclr = mask; \ + } while(0) + +#define ao_spi_clr_cs(reg,mask) do { \ + reg->outset = mask; \ + } while(0) + +#define ao_spi_get_mask(reg,mask,spi_index, speed) do { \ + ao_spi_get(spi_index, speed); \ + ao_spi_set_cs(reg,mask); \ + } while (0) + +#define ao_spi_put_mask(reg,mask,spi_index) do { \ + ao_spi_clr_cs(reg,mask); \ + ao_spi_put(spi_index); \ + } while (0) + +static inline void +ao_spi_get_bit(struct samd21_port *port, uint8_t bit, uint16_t spi_index, uint32_t speed) +{ + ao_spi_get(spi_index, speed); + ao_gpio_set(port, bit, 0); +} + +static inline void +ao_spi_put_bit(struct samd21_port *port, uint8_t bit, uint16_t spi_index) +{ + ao_gpio_set(port, bit, 1); + ao_spi_put(spi_index); +} + +static inline uint8_t +ao_spi_speed(uint32_t hz) +{ + int32_t baud = (int32_t) (AO_SYSCLK / (2 * hz)) - 1; + + if (baud < 1) + baud = 1; + if (baud > 255) + baud = 255; + return (uint8_t) baud; +} + +#define ao_spi_init_cs(port, mask) do { \ + uint8_t __bit__; \ + for (__bit__ = 0; __bit__ < 32; __bit__++) { \ + if (mask & (1 << __bit__)) \ + ao_enable_output(port, __bit__, 1); \ + } \ + } while (0) + +#define ARM_PUSH32(stack, val) (*(--(stack)) = (val)) + +typedef uint32_t ao_arch_irq_t; + +static inline uint32_t +ao_arch_irqsave(void) { + uint32_t primask; + asm("mrs %0,primask" : "=&r" (primask)); + ao_arch_block_interrupts(); + return primask; +} + +static inline void +ao_arch_irqrestore(uint32_t primask) { + asm("msr primask,%0" : : "r" (primask)); +} + +static inline void +ao_arch_memory_barrier(void) { + asm volatile("" ::: "memory"); +} + +#if HAS_TASK +static inline void +ao_arch_init_stack(struct ao_task *task, uint32_t *sp, void *start) +{ + uint32_t a = (uint32_t) start; + int i; + + /* Return address (goes into LR) */ + ARM_PUSH32(sp, a); + + /* Clear register values r0-r7 */ + i = 8; + while (i--) + ARM_PUSH32(sp, 0); + + /* APSR */ + ARM_PUSH32(sp, 0); + + /* PRIMASK with interrupts enabled */ + ARM_PUSH32(sp, 0); + + task->sp32 = sp; +} + +static inline void ao_arch_save_regs(void) { + /* Save general registers */ + asm("push {r0-r7,lr}\n"); + + /* Save APSR */ + asm("mrs r0,apsr"); + asm("push {r0}"); + + /* Save PRIMASK */ + asm("mrs r0,primask"); + asm("push {r0}"); +} + +static inline void ao_arch_save_stack(void) { + uint32_t *sp; + asm("mov %0,sp" : "=&r" (sp) ); + ao_cur_task->sp32 = (sp); + if (sp < &ao_cur_task->stack32[0]) + ao_panic (AO_PANIC_STACK); +} + +static inline void ao_arch_restore_stack(void) { + /* Switch stacks */ + asm("mov sp, %0" : : "r" (ao_cur_task->sp32) ); + + /* Restore PRIMASK */ + asm("pop {r0}"); + asm("msr primask,r0"); + + /* Restore APSR */ + asm("pop {r0}"); + asm("msr apsr_nczvq,r0"); + + /* Restore general registers */ + asm("pop {r0-r7,pc}\n"); +} + +#ifndef HAS_SAMPLE_PROFILE +#define HAS_SAMPLE_PROFILE 0 +#endif + +#if !HAS_SAMPLE_PROFILE +#define HAS_ARCH_START_SCHEDULER 1 + +static inline void ao_arch_start_scheduler(void) { + uint32_t sp; + uint32_t control; + + asm("mrs %0,msp" : "=&r" (sp)); + asm("msr psp,%0" : : "r" (sp)); + asm("mrs %0,control" : "=&r" (control)); + control |= (1 << 1); + asm("msr control,%0" : : "r" (control)); + asm("isb"); +} +#endif + +#define ao_arch_isr_stack() + +#endif + +#define ao_arch_wait_interrupt() do { \ + asm("\twfi\n"); \ + ao_arch_release_interrupts(); \ + asm(".global ao_idle_loc\nao_idle_loc:"); \ + ao_arch_block_interrupts(); \ + } while (0) + +#define ao_arch_critical(b) do { \ + uint32_t __mask = ao_arch_irqsave(); \ + do { b } while (0); \ + ao_arch_irqrestore(__mask); \ + } while (0) + +/* ao_serial_samd21.c */ + +#if USE_SERIAL_0_FLOW && USE_SERIAL_0_SW_FLOW || USE_SERIAL_1_FLOW && USE_SERIAL_1_SW_FLOW +#define HAS_SERIAL_SW_FLOW 1 +#else +#define HAS_SERIAL_SW_FLOW 0 +#endif + +#if USE_SERIAL_1_FLOW && !USE_SERIAL_1_SW_FLOW +#define USE_SERIAL_1_HW_FLOW 1 +#endif + +#if USE_SERIAL_0_FLOW && !USE_SERIAL_0_SW_FLOW +#define USE_SERIAL_0_HW_FLOW 1 +#endif + +#if USE_SERIAL_0_HW_FLOW || USE_SERIAL_1_HW_FLOW +#define HAS_SERIAL_HW_FLOW 1 +#else +#define HAS_SERIAL_HW_FLOW 0 +#endif + +struct ao_samd21_usart { + struct ao_fifo rx_fifo; + struct ao_fifo tx_fifo; + struct samd21_sercom *reg; + uint8_t tx_running; + uint8_t draining; +#if HAS_SERIAL_SW_FLOW + /* RTS - 0 if we have FIFO space, 1 if not + * CTS - 0 if we can send, 0 if not + */ + struct samd21_port *gpio_rts; + struct samd21_port *gpio_cts; + uint8_t pin_rts; + uint8_t pin_cts; + uint8_t rts; +#endif +}; + +#if HAS_USART_0 +extern struct ao_samd21_usart ao_samd21_usart0; +#endif + +void +ao_serial_init(void); + +/* ao_usb_samd21.c */ + +#if AO_USB_OUT_HOOK +void +ao_usb_out_hook(uint8_t *buffer, uint16_t count); +#endif + +void start(void); + +#endif /* _AO_ARCH_FUNCS_H_ */ diff --git a/src/samd21/ao_beep_samd21.c b/src/samd21/ao_beep_samd21.c new file mode 100644 index 00000000..7fbbcba1 --- /dev/null +++ b/src/samd21/ao_beep_samd21.c @@ -0,0 +1,96 @@ +/* + * Copyright © 2022 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" +#include "ao_beep.h" + +#define BEEP_SCALE (AO_SYSCLK / 750000) + +void +ao_beep(uint8_t beep) +{ + struct samd21_tcc *tcc = AO_BEEP_TCC; + if (beep) { + tcc->per = (beep * BEEP_SCALE); + tcc->ctrla = (1 << SAMD21_TCC_CTRLA_ENABLE); + } else { + tcc->ctrla = (0 << SAMD21_TCC_CTRLA_ENABLE); + } +} + +void +ao_beep_for(uint8_t beep, AO_TICK_TYPE ticks) +{ + ao_beep(beep); + ao_delay(ticks); + ao_beep(0); +} + +static void +ao_tcc_init(struct samd21_tcc *tcc, uint32_t apbcmask) +{ + samd21_pm.apbcmask |= apbcmask; + + /* Reset the device */ + tcc->ctrla = (1 << SAMD21_TCC_CTRLA_SWRST); + + while ((tcc->ctrla & (1 << SAMD21_TCC_CTRLA_SWRST)) != 0 || + (tcc->syncbusy & (1 << SAMD21_TCC_SYNCBUSY_SWRST)) != 0) + ; + + tcc->per = 94 * BEEP_SCALE; + + tcc->wave = ((SAMD21_TCC_WAVE_WAVEGEN_NFRQ << SAMD21_TCC_WAVE_WAVEGEN) | + (0 << SAMD21_TCC_WAVE_RAMP) | + (0 << SAMD21_TCC_WAVE_CIPEREN) | + (0 << SAMD21_TCC_WAVE_CCCEN(0)) | + (0 << SAMD21_TCC_WAVE_CCCEN(1)) | + (0 << SAMD21_TCC_WAVE_CCCEN(2)) | + (0 << SAMD21_TCC_WAVE_CCCEN(3)) | + (0 << SAMD21_TCC_WAVE_POL(0)) | + (0 << SAMD21_TCC_WAVE_POL(1)) | + (0 << SAMD21_TCC_WAVE_POL(1)) | + (0 << SAMD21_TCC_WAVE_POL(3)) | + (0 << SAMD21_TCC_WAVE_SWAP(0)) | + (0 << SAMD21_TCC_WAVE_SWAP(1)) | + (0 << SAMD21_TCC_WAVE_SWAP(1)) | + (0 << SAMD21_TCC_WAVE_SWAP(3))); + + tcc->dbgctrl = (1 << SAMD21_TCC_DBGCTRL_DBGRUN); +} + +void +ao_beep_init(void) +{ + struct samd21_port *port = AO_BEEP_PORT; + uint8_t pin = AO_BEEP_PIN; + struct samd21_tcc *tcc = AO_BEEP_TCC; + uint32_t apbc_mask = 1UL << AO_BEEP_TCC_APBC_MASK; + + if (tcc == &samd21_tcc0 || tcc == &samd21_tcc1) { + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC0_TCC1); + } else { + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC2_TC3); + } + + ao_tcc_init(tcc, apbc_mask); + + ao_enable_output(port, pin, 0); + + samd21_port_pmux_set(port, pin, AO_BEEP_FUNC); +} diff --git a/src/samd21/ao_boot_chain.c b/src/samd21/ao_boot_chain.c new file mode 100644 index 00000000..551af783 --- /dev/null +++ b/src/samd21/ao_boot_chain.c @@ -0,0 +1,68 @@ +/* + * Copyright © 2022 Keith Packard + * + * 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 +#include + +void +ao_boot_chain(uint32_t *base) +{ + uint32_t sp; + uint32_t pc; + + sp = base[0]; + pc = base[1]; + if (0x00000100 <= pc && pc <= 0x00200000 && (pc & 1) == 1) { + asm ("mov sp, %0" : : "r" (sp)); + asm ("mov lr, %0" : : "r" (pc)); + asm ("bx lr"); + } +} + +#define AO_BOOT_SIGNAL 0x5a5aa5a5 +#define AO_BOOT_CHECK 0xc3c33c3c + +struct ao_boot { + uint32_t *base; + uint32_t signal; + uint32_t check; +}; + +struct ao_boot ao_boot __attribute__((section(".preserve.2"))); + +int +ao_boot_check_chain(void) +{ + if (ao_boot.signal == AO_BOOT_SIGNAL && ao_boot.check == AO_BOOT_CHECK) { + ao_boot.signal = 0; + ao_boot.check = 0; + if (ao_boot.base == AO_BOOT_FORCE_LOADER) + return 0; + ao_boot_chain(ao_boot.base); + } + return 1; +} + +void +ao_boot_reboot(uint32_t *base) +{ + ao_boot.base = base; + ao_boot.signal = AO_BOOT_SIGNAL; + ao_boot.check = AO_BOOT_CHECK; + ao_arch_reboot(); +} diff --git a/src/samd21/ao_boot_pin.c b/src/samd21/ao_boot_pin.c new file mode 100644 index 00000000..baabe810 --- /dev/null +++ b/src/samd21/ao_boot_pin.c @@ -0,0 +1,42 @@ +/* + * Copyright © 2022 Keith Packard + * + * 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 +#include +#include + +int +ao_boot_check_pin(void) +{ + uint16_t v; + + /* Enable the input pin */ + ao_enable_input(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN, + AO_BOOT_APPLICATION_MODE); + + for (v = 0; v < 100; v++) + ao_arch_nop(); + + /* Read the value */ + v = ao_gpio_get(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN); + + /* Reset the chip to turn off the port and the power interface clock */ + ao_gpio_set_mode(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN, 0); + ao_disable_port(&AO_BOOT_APPLICATION_GPIO); + return v == AO_BOOT_APPLICATION_VALUE; +} diff --git a/src/samd21/ao_dac_samd21.c b/src/samd21/ao_dac_samd21.c new file mode 100644 index 00000000..7dc00c1d --- /dev/null +++ b/src/samd21/ao_dac_samd21.c @@ -0,0 +1,180 @@ +/* + * Copyright © 2020 Keith Packard + * + * 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 +#include +#include +#include + +/* Max DAC output value. We're using left-adjusted values */ +#define SNEK_DAC_MAX 65535 + +#ifdef SNEK_SAMD21_DAC_TIMER +/* + * If there's a timer available, we can use that + * to implement the 'tone' function + */ + +#include "sine.h" + +#define NSINE (sizeof(sine) / sizeof(sine[0])) + +static uint16_t current_power; +static uint16_t power; +static uint32_t phase; +static uint32_t phase_step; +static volatile bool dac_running; + +#define _paste2(x,y) x ## y +#define _paste3(x,y,z) x ## y ## z +#define paste2(x,y) _paste2(x,y) +#define paste3(x,y,z) _paste3(x,y,z) +#define SAMD21_TCC paste2(samd21_tcc, SNEK_SAMD21_DAC_TIMER) +#define SAMD21_TCC_ISR paste3(samd21_tcc, SNEK_SAMD21_DAC_TIMER, _isr) + +#define AO_DAC_RATE 24000 + +#define UINT_TO_FIXED(u) ((uint32_t) (u) << 16) +#define FIXED_TO_UINT(u) ((u) >> 16) + +void +SAMD21_TCC_ISR(void) +{ + uint32_t intflag = SAMD21_TCC.intflag; + SAMD21_TCC.intflag = intflag; + if (intflag & (1 << SAMD21_TCC_INTFLAG_OVF)) { + if (phase_step) { + samd21_dac.data = ((uint32_t) sine[FIXED_TO_UINT(phase)] * current_power) >> 16; + if ((phase += phase_step) >= UINT_TO_FIXED(NSINE)) { + phase -= UINT_TO_FIXED(NSINE); + + current_power = power; + + /* Stop output at zero crossing when no longer outputing tone */ + if (!dac_running) { + phase_step = 0; + phase = 0; + SAMD21_TCC.intenclr = (1 << SAMD21_TCC_INTFLAG_OVF); + } + } + } + } +} + +void +ao_dac_set_hz(float hz) +{ + /* samples/second = AC_DAC_RATE + * + * cycles/second = hz + * + * samples/cycle = AC_DAC_RATE / hz + * + * step/cycle = 256 + * + * step/sample = step/cycle * cycle/samples + * = TWO_PI * hz / AC_DAC_RATE; + */ + uint32_t new_phase_step = (float) UINT_TO_FIXED(NSINE) * hz / (float) AO_DAC_RATE; + ao_arch_critical( + if (new_phase_step) { + dac_running = true; + phase_step = new_phase_step; + SAMD21_TCC.intenset = (1 << SAMD21_TCC_INTFLAG_OVF); + } else { + dac_running = false; + }); +} + +static void +ao_dac_timer_init(void) +{ + /* Adjust timer to interrupt once per sample period */ + SAMD21_TCC.per = AO_HCLK / AO_DAC_RATE; + + /* Enable timer interrupts */ + samd21_nvic_set_enable(paste3(SAMD21_NVIC_ISR_TCC, SNEK_SAMD21_DAC_TIMER, _POS)); + samd21_nvic_set_priority(paste3(SAMD21_NVIC_ISR_TCC, SNEK_SAMD21_DAC_TIMER, _POS), 3); +} +#else +#define ao_dac_timer_init() +#endif + +static void +ao_dac_sync(void) +{ + while (samd21_dac.status & (1 << SAMD21_DAC_STATUS_SYNCBUSY)) + ; +} + +void +ao_dac_set(uint16_t new_power) +{ +#if SNEK_DAC_MAX != SNEK_PWM_MAX + new_power = (uint16_t) ((uint32_t) new_power * SNEK_DAC_MAX) / SNEK_PWM_MAX; +#endif + + ao_arch_critical( +#ifdef SNEK_SAMD21_DAC_TIMER + power = new_power; + /* + * When not generating a tone, just set the DAC + * output to the requested level + */ + if (!phase_step) { + current_power = new_power; + samd21_dac.data = new_power; + } +#else + samd21_dac.data = new_power; +#endif + ); +} + +void +ao_dac_init(void) +{ + /* supply a clock */ + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_DAC); + + /* enable the device */ + samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_DAC); + + /* reset */ + samd21_dac.ctrla = (1 << SAMD21_DAC_CTRLA_SWRST); + + while ((samd21_dac.ctrla & (1 << SAMD21_DAC_CTRLA_SWRST)) != 0 || + (samd21_dac.status & (1 << SAMD21_DAC_STATUS_SYNCBUSY)) != 0) + ao_arch_nop(); + + /* Configure using VDD as reference */ + samd21_dac.ctrlb = ((1 << SAMD21_DAC_CTRLB_EOEN) | + (0 << SAMD21_DAC_CTRLB_IOEN) | + (1 << SAMD21_DAC_CTRLB_LEFTADJ) | + (0 << SAMD21_DAC_CTRLB_VPD) | + (1 << SAMD21_DAC_CTRLB_BDWP) | + (SAMD21_DAC_CTRLB_REFSEL_VDDANA << SAMD21_DAC_CTRLB_REFSEL)); + + ao_dac_sync(); + + samd21_dac.ctrla = (1 << SAMD21_DAC_CTRLA_ENABLE); + + ao_dac_sync(); + + ao_dac_timer_init(); +} diff --git a/src/samd21/ao_dac_samd21.h b/src/samd21/ao_dac_samd21.h new file mode 100644 index 00000000..7939f4bb --- /dev/null +++ b/src/samd21/ao_dac_samd21.h @@ -0,0 +1,33 @@ +/* + * Copyright © 2020 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _AO_DAC_SAMD21_H_ +#define _AO_DAC_SAMD21_H_ + +void +ao_dac_set(uint16_t value); + +#ifdef SNEK_SAMD21_DAC_TIMER +void +ao_dac_set_hz(float hz); +#endif + +void +ao_dac_init(void); + +#endif /* _AO_DAC_SAMD21_H_ */ diff --git a/src/samd21/ao_dma_samd21.c b/src/samd21/ao_dma_samd21.c new file mode 100644 index 00000000..5bf94709 --- /dev/null +++ b/src/samd21/ao_dma_samd21.c @@ -0,0 +1,164 @@ +/* + * Copyright © 2019 Keith Packard + * + * 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 +#include + +static struct samd21_dmac_desc samd21_dmac_desc[SAMD21_DMAC_NCHAN] __attribute__((aligned(16))); +static struct samd21_dmac_desc samd21_dmac_wrb[SAMD21_DMAC_NCHAN] __attribute__((aligned(16))); + +static struct { + void (*callback)(uint8_t id, void *closure); + void *closure; +} dmac_callback[SAMD21_DMAC_NCHAN]; + +void +samd21_dmac_isr(void) +{ + uint16_t intpend = samd21_dmac.intpend; + + if (intpend & 0x0700) { + uint8_t id = (intpend >> SAMD21_DMAC_INTPEND_ID) & SAMD21_DMAC_INTPEND_ID_MASK; + samd21_dmac.intpend = intpend; + if (intpend & (1 << SAMD21_DMAC_INTPEND_TCMPL)) { + if (dmac_callback[id].callback) + (*dmac_callback[id].callback)(id, dmac_callback[id].closure); + } + } +} + +void +ao_dma_dump(char *where) +{ + printf("DMA %s ctrl %04x intpend %04x intstatus %04lx\n", + where, + samd21_dmac.ctrl, + samd21_dmac.intpend, + samd21_dmac.intstatus); + fflush(stdout); + printf(" busych %04lx pendch %04lx active %08lx chctrla %02x\n", + samd21_dmac.busych, + samd21_dmac.pendch, + samd21_dmac.active, + samd21_dmac.chctrla); + fflush(stdout); + printf(" chctrlb %08lx chintflag %02x chstatus %02x\n", + samd21_dmac.chctrlb, + samd21_dmac.chintflag, + samd21_dmac.chstatus); + fflush(stdout); + printf(" btctrl %04x btcnt %04x srcaddr %08lx dstaddr %08lx descaddr %08lx\n", + samd21_dmac_desc[0].btctrl, + samd21_dmac_desc[0].btcnt, + samd21_dmac_desc[0].srcaddr, + samd21_dmac_desc[0].dstaddr, + samd21_dmac_desc[0].descaddr); + fflush(stdout); +} + +void +_ao_dma_start_transfer(uint8_t id, + const void *src, + void *dst, + uint16_t count, + uint32_t chctrlb, + uint16_t btctrl, + void (*callback)(uint8_t id, void *closure), + void *closure) +{ + /* Set up the callback */ + dmac_callback[id].closure = closure; + dmac_callback[id].callback = callback; + + /* Set up the descriptor */ + samd21_dmac_desc[id].btctrl = btctrl; + samd21_dmac_desc[id].btcnt = count; + samd21_dmac_desc[id].srcaddr = (uint32_t) src; + samd21_dmac_desc[id].dstaddr = (uint32_t) dst; + samd21_dmac_desc[id].descaddr = 0; + + /* Configure the channel and enable it */ + samd21_dmac.chid = id; + samd21_dmac.chctrlb = chctrlb; + samd21_dmac.chctrla = (1 << SAMD21_DMAC_CHCTRLA_ENABLE); +} + +void +_ao_dma_done_transfer(uint8_t id) +{ + /* Disable channel */ + samd21_dmac.chid = id; + samd21_dmac.chctrla = 0; +} + +void +ao_dma_init(void) +{ + uint8_t ch; + + /* Enable DMAC clocks */ + samd21_pm.ahbmask |= (1 << SAMD21_PM_AHBMASK_DMAC); + samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_DMAC); + +#if 1 + /* Enable HPB clocks so we can talk to peripherals */ + samd21_pm.ahbmask |= ((1 << SAMD21_PM_AHBMASK_HPB0) | + (1 << SAMD21_PM_AHBMASK_HPB1) | + (1 << SAMD21_PM_AHBMASK_HPB2)); +#endif + + samd21_pm.apbamask |= (1 << SAMD21_PM_APBAMASK_PAC0); + samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_PAC1); + samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_PAC2); + + /* Reset DMAC device */ + samd21_dmac.ctrl = 0; + samd21_dmac.ctrl = (1 << SAMD21_DMAC_CTRL_SWRST); + while (samd21_dmac.ctrl & (1 << SAMD21_DMAC_CTRL_SWRST)) + ; + + samd21_dmac.baseaddr = (uint32_t) &samd21_dmac_desc[0]; + samd21_dmac.wrbaddr = (uint32_t) &samd21_dmac_wrb[0]; + + samd21_dmac.swtrigctrl = 0; + + /* Set QoS to highest value */ + samd21_dmac.qosctrl = ((SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_DQOS) | + (SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_FQOS) | + (SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_WRBQOS)); + + /* Enable DMAC controller with all priority levels */ + samd21_dmac.ctrl = ((1 << SAMD21_DMAC_CTRL_DMAENABLE) | + (1 << SAMD21_DMAC_CTRL_LVLEN(0)) | + (1 << SAMD21_DMAC_CTRL_LVLEN(1)) | + (1 << SAMD21_DMAC_CTRL_LVLEN(2)) | + (1 << SAMD21_DMAC_CTRL_LVLEN(3))); + + /* Reset all DMAC channels */ + for (ch = 0; ch < SAMD21_DMAC_NCHAN; ch++) { + samd21_dmac.chid = ch; + samd21_dmac.chctrla = (1 << SAMD21_DMAC_CHCTRLA_SWRST); + while (samd21_dmac.chctrla & (1 << SAMD21_DMAC_CHCTRLA_SWRST)) + ; + samd21_dmac.chintenset = (1 << SAMD21_DMAC_CHINTFLAG_TCMPL); + } + + /* configure interrupts */ + samd21_nvic_set_enable(SAMD21_NVIC_ISR_DMAC_POS); + samd21_nvic_set_priority(SAMD21_NVIC_ISR_DMAC_POS, 3); +} diff --git a/src/samd21/ao_dma_samd21.h b/src/samd21/ao_dma_samd21.h new file mode 100644 index 00000000..57f54769 --- /dev/null +++ b/src/samd21/ao_dma_samd21.h @@ -0,0 +1,49 @@ +/* + * Copyright © 2019 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _AO_DMA_SAM21_H_ +#define _AO_DMA_SAM21_H_ + +void +ao_dma_init(void); + +void +_ao_dma_start_transfer(uint8_t id, + const void *src, + void *dst, + uint16_t count, + uint32_t chctrlb, + uint16_t btctrl, + void (*callback)(uint8_t id, void *closure), + void *closure); + +void +_ao_dma_done_transfer(uint8_t id); + +void +ao_dma_dump(char *where); + +/* + * DMA is only used for SERCOM + */ + +#define AO_SERCOM_DMA_BASE 0U +#define AO_SERCOM_INPUT_DMA_ID(id) ((uint8_t) ((id) * 2U + 0U + AO_SERCOM_DMA_BASE)) +#define AO_SERCOM_OUTPUT_DMA_ID(id) ((uint8_t) ((id) * 2U + 1U + AO_SERCOM_DMA_BASE)) + +#endif /* _AO_DMA_SAM21_H_ */ diff --git a/src/samd21/ao_exti.h b/src/samd21/ao_exti.h new file mode 100644 index 00000000..1bd14a03 --- /dev/null +++ b/src/samd21/ao_exti.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2022 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_EXTI_H_ +#define _AO_EXTI_H_ + +#define AO_EXTI_MODE_RISING SAMD21_EIC_CONFIG_SENSE_RISE +#define AO_EXTI_MODE_FALLING SAMD21_EIC_CONFIG_SENSE_FALL +#define AO_EXTI_MODE_PULL_NONE 0 +#define AO_EXTI_MODE_PULL_UP 4 +#define AO_EXTI_MODE_PULL_DOWN 8 +#define AO_EXTI_PRIORITY_LOW 16 +#define AO_EXTI_PRIORITY_MED 0 +#define AO_EXTI_PRIORITY_HIGH 32 +#define AO_EXTI_PIN_NOCONFIGURE 64 + +void +ao_exti_setup(struct samd21_port *gpio, uint8_t pin, uint8_t mode, void (*callback)(void)); + +void +ao_exti_set_mode(struct samd21_port *gpio, uint8_t pin, uint8_t mode); + +void +ao_exti_set_callback(struct samd21_port *gpio, uint8_t pin, void (*callback)(void)); + +void +ao_exti_enable(struct samd21_port *gpio, uint8_t pin); + +void +ao_exti_disable(struct samd21_port *gpio, uint8_t pin); + +void +ao_exti_init(void); + +#endif /* _AO_EXTI_H_ */ diff --git a/src/samd21/ao_exti_samd21.c b/src/samd21/ao_exti_samd21.c new file mode 100644 index 00000000..688f6433 --- /dev/null +++ b/src/samd21/ao_exti_samd21.c @@ -0,0 +1,181 @@ +/* + * Copyright © 2022 Keith Packard + * + * 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 +#include + +struct ao_samd21_exti { + void (*callback)(void); + uint8_t pmux; + uint8_t pincfg; +}; + +static struct ao_samd21_exti ao_samd21_exti[SAMD21_NUM_EIC]; + +static uint8_t +pin_id(struct samd21_port *port, uint8_t pin) +{ + /* Why, atmel, why? */ + if (port == &samd21_port_a) { + switch (pin) { + case 24: + case 25: + case 27: + return pin - 12; + case 28: + case 30: + case 31: + return pin - 20; + default: + break; + } + } + + /* most pins use exti mapped to their pin number directly (mod 16) */ + return pin & 0xf; +} + + +void +samd21_eic_isr(void) +{ + uint32_t intflag = samd21_eic.intflag; + uint8_t id; + + for (id = 0; id < SAMD21_NUM_EIC; id++) { + uint32_t mask = (1 << id); + + if (intflag & mask) { + samd21_eic.intflag = mask; + if (ao_samd21_exti[id].callback) + (*ao_samd21_exti[id].callback)(); + } + } +} + +static void +_ao_exti_set_sense(uint8_t id, uint8_t mode) +{ + uint8_t sense = mode & (SAMD21_EIC_CONFIG_SENSE_RISE | SAMD21_EIC_CONFIG_SENSE_FALL); + uint8_t n = SAMD21_EIC_CONFIG_N(id); + uint32_t config; + + config = samd21_eic.config[n]; + config &= ~(SAMD21_EIC_CONFIG_SENSE_MASK << SAMD21_EIC_CONFIG_SENSE(id)); + config |= (sense << SAMD21_EIC_CONFIG_SENSE(id)); + samd21_eic.config[n] = config; +} + +void +ao_exti_setup (struct samd21_port *port, uint8_t pin, uint8_t mode, void (*callback)(void)) +{ + uint8_t id = pin_id(port,pin); + struct ao_samd21_exti *exti = &ao_samd21_exti[id]; + + if (exti->callback) + ao_panic(AO_PANIC_EXTI); + + if (mode & AO_EXTI_PIN_NOCONFIGURE) { + ao_enable_port(port); + samd21_port_dir_set(port, pin, SAMD21_PORT_DIR_IN); + samd21_port_pincfg_set(port, pin, + (1 << SAMD21_PORT_PINCFG_INEN), + (1 << SAMD21_PORT_PINCFG_INEN)); + } else { + ao_enable_input(port, pin, mode); + } + + ao_arch_block_interrupts(); + + exti->callback = callback; + + /* Set edge triggered */ + _ao_exti_set_sense(id, mode); + + ao_arch_release_interrupts(); +} + +void +ao_exti_set_mode(struct samd21_port *port, uint8_t pin, uint8_t mode) +{ + uint8_t id = pin_id(port,pin); + + ao_arch_block_interrupts(); + _ao_exti_set_sense(id, mode); + ao_arch_release_interrupts(); +} + +void +ao_exti_set_callback(struct samd21_port *port, uint8_t pin, void (*callback)(void)) +{ + uint8_t id = pin_id(port,pin); + + ao_arch_block_interrupts(); + ao_samd21_exti[id].callback = callback; + ao_arch_release_interrupts(); +} + +void +ao_exti_enable(struct samd21_port *port, uint8_t pin) +{ + uint8_t id = pin_id(port,pin); + + ao_arch_block_interrupts(); + /* configure gpio to interrupt routing */ + if ((samd21_eic.intenset & (1 << id)) == 0) { + ao_samd21_exti[id].pmux = samd21_port_pmux_get(port, pin); + ao_samd21_exti[id].pincfg = samd21_port_pincfg_get(port, pin); + } + samd21_port_pmux_set(port, pin, SAMD21_PORT_PMUX_FUNC_A); + samd21_eic.intenset = 1 << id; + ao_arch_release_interrupts(); +} + +void +ao_exti_disable(struct samd21_port *port, uint8_t pin) +{ + uint8_t id = pin_id(port,pin); + + ao_arch_block_interrupts(); + samd21_eic.intenclr = 1 << id; + /* restore gpio config */ + if (ao_samd21_exti[id].pincfg & (1 << SAMD21_PORT_PINCFG_PMUXEN)) + samd21_port_pmux_set(port, pin, ao_samd21_exti[id].pmux); + else + samd21_port_pmux_clr(port, pin); + ao_arch_release_interrupts(); +} + +void +ao_exti_init(void) +{ + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_EIC); + + /* Reset */ + samd21_eic.ctrl = (1 << SAMD21_EIC_CTRL_SWRST); + + while (samd21_eic.status & (1 << SAMD21_EIC_STATUS_SYNCBUSY)) + ; + + /* Wire up interrupts */ + samd21_nvic_set_enable(SAMD21_NVIC_ISR_EIC_POS); + samd21_nvic_set_priority(SAMD21_NVIC_ISR_EIC_POS, 3); + + /* Enable */ + samd21_eic.ctrl = (1 << SAMD21_EIC_CTRL_ENABLE); +} diff --git a/src/samd21/ao_flash.h b/src/samd21/ao_flash.h new file mode 100644 index 00000000..5922625e --- /dev/null +++ b/src/samd21/ao_flash.h @@ -0,0 +1,28 @@ +/* + * Copyright © 2019 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_FLASH_H_ +#define _AO_FLASH_H_ + +void +ao_flash_erase_page(uint32_t *page); + +void +ao_flash_page(uint32_t *page, uint32_t *src); + +#endif /* _AO_FLASH_H_ */ diff --git a/src/samd21/ao_flash_loader_samd21.c b/src/samd21/ao_flash_loader_samd21.c new file mode 100644 index 00000000..8b08ab2a --- /dev/null +++ b/src/samd21/ao_flash_loader_samd21.c @@ -0,0 +1,40 @@ +/* + * Copyright © 2022 Keith Packard + * + * 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 +#include +#include + +int +main(void) +{ + ao_clock_init(); + + ao_usb_init(); + +#if HAS_TICK + ao_timer_init(); +#endif + +#ifdef AO_FLASH_LOADER_INIT + AO_FLASH_LOADER_INIT; +#endif + ao_flash_task(); + return 0; +} diff --git a/src/samd21/ao_flash_samd21.c b/src/samd21/ao_flash_samd21.c new file mode 100644 index 00000000..502ff35e --- /dev/null +++ b/src/samd21/ao_flash_samd21.c @@ -0,0 +1,150 @@ +/* + * Copyright © 2019 Keith Packard + * + * 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 +#include +#include + +/* Erase rows are four pages */ +static uint32_t +samd21_nvmctrl_row_size(void) +{ + return samd21_nvmctrl_page_size() * 4; +} + +/* size of a lock region. That's just total flash size / 16 */ +static uint32_t +samd21_nvmctrl_lock_region(void) +{ + return samd21_flash_size() >> 4; +} + +/* Find the bit index of an address within the lock word */ +static uint8_t +ao_flash_lock_region_bit(void *addr) +{ + uint32_t lock_region = samd21_nvmctrl_lock_region(); + uintptr_t a = (uintptr_t) addr; + + while (lock_region) { + a >>= 1; + lock_region >>= 1; + } + + return (uint8_t) a; +} + +static uint8_t +ao_flash_is_locked(void *addr) +{ + return (samd21_nvmctrl.lock >> ao_flash_lock_region_bit(addr)) & 1; +} + +/* Execute a single flash operation, waiting for it to complete. This + * bit of code must be in ram + */ +static void __attribute__ ((section(".sdata2.flash"), noinline)) +_ao_flash_execute(uint16_t cmd) +{ + while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0) + ; + samd21_nvmctrl.ctrla = ((cmd << SAMD21_NVMCTRL_CTRLA_CMD) | + (SAMD21_NVMCTRL_CTRLA_CMDEX_KEY << SAMD21_NVMCTRL_CTRLA_CMDEX)); + while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0) + ; + samd21_nvmctrl.intflag = ((1 << SAMD21_NVMCTRL_INTFLAG_READY) | + (1 << SAMD21_NVMCTRL_INTFLAG_ERROR)); +} + +/* Set the address of the next flash operation */ +static void +_ao_flash_set_addr(void *addr) +{ + while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0) + ; + samd21_nvmctrl.addr = ((uint32_t) addr) >> 1; + while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0) + ; +} + +/* Unlock a region of flash */ +static void +_ao_flash_unlock(void *addr) +{ + if (!ao_flash_is_locked(addr)) + return; + + _ao_flash_set_addr(addr); + _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_UR); +} + +/* Erase a row of flash */ +static void +_ao_flash_erase_row(void *row) +{ + _ao_flash_unlock(row); + _ao_flash_set_addr(row); + _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_ER); +} + +void +ao_flash_erase_page(uint32_t *page) +{ + uint8_t *row = (uint8_t *) page; + uint32_t row_size = samd21_nvmctrl_row_size(); + uint32_t rows = (row_size + 255) / 256; + + if ((uintptr_t) page & (row_size - 1)) + return; + + ao_arch_block_interrupts(); + + if (((uintptr_t) row & (row_size - 1)) == 0) { + while (rows--) { + _ao_flash_erase_row(row); + row += row_size; + } + } + + ao_arch_release_interrupts(); +} + +void +ao_flash_page(uint32_t *page, uint32_t *src) +{ + uint32_t page_shift = samd21_nvmctrl_page_shift(); + uint32_t pages = 256 >> page_shift; + uint32_t i; + uint32_t per_page = 1 << (page_shift - 2); + + ao_flash_erase_page(page); + + ao_arch_block_interrupts(); + + while(pages--) { + /* Clear write buffer */ + _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_PBC); + _ao_flash_set_addr(page); + for (i = 0; i < per_page; i++) + *page++ = *src++; + _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_WP); + } + + ao_arch_release_interrupts(); +} + diff --git a/src/samd21/ao_flash_samd21_pins.h b/src/samd21/ao_flash_samd21_pins.h new file mode 100644 index 00000000..22939684 --- /dev/null +++ b/src/samd21/ao_flash_samd21_pins.h @@ -0,0 +1,29 @@ +/* + * Copyright © 2022 Keith Packard + * + * 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 + +#define AO_AHB_PRESCALER 1 +#define AO_APBA_PRESCALER 1 + +#define HAS_USB 1 + +#endif /* _AO_FLASH_SAMD21_PINS_H_ */ diff --git a/src/samd21/ao_interrupt.c b/src/samd21/ao_interrupt.c new file mode 100644 index 00000000..e425520b --- /dev/null +++ b/src/samd21/ao_interrupt.c @@ -0,0 +1,169 @@ +/* + * Copyright © 2019 Keith Packard + * + * 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 +#include + +/* Interrupt functions */ + +void samd21_halt_isr(void) +{ + ao_panic(AO_PANIC_CRASH); +} + +void samd21_ignore_isr(void) +{ +} + +uint32_t +samd21_flash_size(void) +{ + uint32_t nvmp = (samd21_nvmctrl.param >> SAMD21_NVMCTRL_PARAM_NVMP) & SAMD21_NVMCTRL_PARAM_NVMP_MASK; + uint32_t psz = (samd21_nvmctrl.param >> SAMD21_NVMCTRL_PARAM_PSZ) & SAMD21_NVMCTRL_PARAM_PSZ_MASK; + + /* page size is 2**(3 + psz) */ + return nvmp << (3 + psz); +} + +#define STRINGIFY(x) #x + +#define isr(name) \ + void __attribute__ ((weak)) samd21_ ## name ## _isr(void); \ + _Pragma(STRINGIFY(weak samd21_ ## name ## _isr = samd21_ignore_isr)) + +#define isr_halt(name) \ + void __attribute__ ((weak)) samd21_ ## name ## _isr(void); \ + _Pragma(STRINGIFY(weak samd21_ ## name ## _isr = samd21_halt_isr)) + +isr(nmi); +isr_halt(hardfault); +isr_halt(memmanage); +isr_halt(busfault); +isr_halt(usagefault); +isr(svc); +isr(debugmon); +isr(pendsv); +isr(systick); +isr(pm); /* IRQ0 */ +isr(sysctrl); +isr(wdt); +isr(rtc); +isr(eic); +isr(nvmctrl); +isr(dmac); +isr(usb); +isr(evsys); +isr(sercom0); +isr(sercom1); +isr(sercom2); +isr(sercom3); +isr(sercom4); +isr(sercom5); +isr(tcc0); +isr(tcc1); +isr(tcc2); +isr(tc3); +isr(tc4); +isr(tc5); +isr(tc6); +isr(tc7); +isr(adc); +isr(ac); +isr(dac); +isr(ptc); +isr(i2s); +isr(ac1); +isr(tcc3); + +#undef isr +#undef isr_halt + +#define i(addr,name) [(addr)/4] = samd21_ ## name ## _isr + +extern char __stack[]; +void _start(void) __attribute__((__noreturn__)); +void main(void) __attribute__((__noreturn__)); + +__attribute__ ((section(".init"))) +void (*const __interrupt_vector[])(void) __attribute((aligned(128))) = { + [0] = (void *) __stack, + [1] = _start, + i(0x08, nmi), + i(0x0c, hardfault), + i(0x2c, svc), + i(0x30, debugmon), + i(0x38, pendsv), + i(0x3c, systick), + + i(0x40, pm), /* IRQ0 */ + i(0x44, sysctrl), + i(0x48, wdt), + i(0x4c, rtc), + i(0x50, eic), + i(0x54, nvmctrl), + i(0x58, dmac), + i(0x5c, usb), + i(0x60, evsys), + i(0x64, sercom0), + i(0x68, sercom1), + i(0x6c, sercom2), + i(0x70, sercom3), + i(0x74, sercom4), + i(0x78, sercom5), + i(0x7c, tcc0), + i(0x80, tcc1), + i(0x84, tcc2), + i(0x88, tc3), + i(0x8c, tc4), + i(0x90, tc5), + i(0x94, tc6), + i(0x98, tc7), + i(0x9c, adc), + i(0xa0, ac), + i(0xa4, dac), + i(0xa8, ptc), + i(0xac, i2s), + i(0xb0, ac1), + i(0xb4, tcc3), +}; + +extern char __data_source[]; +extern char __data_start[]; +extern char __data_size[]; +extern char __bss_start[]; +extern char __bss_size[]; + +void _start(void) +{ + memcpy(__data_start, __data_source, (uintptr_t) __data_size); + memset(__bss_start, '\0', (uintptr_t) __bss_size); + +#if AO_BOOT_CHAIN + if (ao_boot_check_chain()) { +#if AO_BOOT_PIN + if (ao_boot_check_pin()) +#endif + { + ao_boot_chain(AO_BOOT_APPLICATION_BASE); + } + } +#endif + + /* Turn on sysctrl */ + samd21_pm.apbamask |= (1 << SAMD21_PM_APBAMASK_SYSCTRL); + /* Set interrupt vector */ + samd21_scb.vtor = (uint32_t) &__interrupt_vector; + + main(); +} diff --git a/src/samd21/ao_neopixel.c b/src/samd21/ao_neopixel.c new file mode 100644 index 00000000..818910ca --- /dev/null +++ b/src/samd21/ao_neopixel.c @@ -0,0 +1,100 @@ +/* + * Copyright © 2019 Keith Packard + * + * 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 +#include + +void +ao_snek_neopixel_write(void *gpio, uint8_t pin, int npixel, const struct snek_neopixel *pixels) +{ + volatile uint32_t *outtgl = &(((struct samd21_port *) gpio)->outtgl); + uint32_t value = ((uint32_t) 1 << pin); + + while (npixel--) { + int32_t p = pixels->p; + uint8_t bit; + pixels++; + + ao_arch_block_interrupts(); + for (bit = 0; bit < 24; bit++) { + *outtgl = value; + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + if (p < 0) { + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + + *outtgl = value; + } else { + *outtgl = value; + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + + } + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + + ao_arch_nop(); + + p <<= 1; + } + ao_arch_release_interrupts(); + } +} diff --git a/src/samd21/ao_serial.h b/src/samd21/ao_serial.h new file mode 100644 index 00000000..1120f65f --- /dev/null +++ b/src/samd21/ao_serial.h @@ -0,0 +1,163 @@ +/* + * Copyright © 2012 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_SERIAL_H_ +#define _AO_SERIAL_H_ + +#define AO_SERIAL_SPEED_4800 0 +#define AO_SERIAL_SPEED_9600 1 +#define AO_SERIAL_SPEED_19200 2 +#define AO_SERIAL_SPEED_57600 3 +#define AO_SERIAL_SPEED_115200 4 + +#if HAS_SERIAL_0 +extern struct ao_samd21_usart ao_samd21_usart0; + +char +ao_serial0_getchar(void); + +int +_ao_serial0_pollchar(void); + +uint8_t +_ao_serial0_sleep_for(uint16_t timeout); + +void +ao_serial0_putchar(char c); + +void +ao_serial0_drain(void); + +void +ao_serial0_set_speed(uint8_t speed); +#endif + +#if HAS_SERIAL_1 +extern struct ao_samd21_usart ao_samd21_usart1; + +char +ao_serial1_getchar(void); + +int +_ao_serial1_pollchar(void); + +uint8_t +_ao_serial1_sleep_for(uint16_t timeout); + +void +ao_serial1_putchar(char c); + +void +ao_serial1_drain(void); + +void +ao_serial1_set_speed(uint8_t speed); +#endif + +#if HAS_SERIAL_2 +extern struct ao_samd21_usart ao_samd21_usart2; + +char +ao_serial2_getchar(void); + +int +_ao_serial2_pollchar(void); + +uint8_t +_ao_serial2_sleep_for(uint16_t timeout); + +void +ao_serial2_putchar(char c); + +void +ao_serial2_drain(void); + +void +ao_serial2_set_speed(uint8_t speed); +#endif + +#if HAS_SERIAL_3 +extern struct ao_samd21_usart ao_samd21_usart3; + +char +ao_serial3_getchar(void); + +int +_ao_serial3_pollchar(void); + +uint8_t +_ao_serial3_sleep_for(uint16_t timeout); + +void +ao_serial3_putchar(char c); + +void +ao_serial3_drain(void); + +void +ao_serial3_set_speed(uint8_t speed); +#endif + +#if HAS_SERIAL_4 +extern struct ao_samd21_usart ao_samd21_usart4; + +char +ao_serial4_getchar(void); + +int +_ao_serial4_pollchar(void); + +uint8_t +_ao_serial4_sleep_for(uint16_t timeout); + +void +ao_serial4_putchar(char c); + +void +ao_serial4_drain(void); + +void +ao_serial4_set_speed(uint8_t speed); +#endif + +#if HAS_SERIAL_5 +extern struct ao_samd21_usart ao_samd21_usart5; + +char +ao_serial5_getchar(void); + +int +_ao_serial5_pollchar(void); + +uint8_t +_ao_serial5_sleep_for(uint16_t timeout); + +void +ao_serial5_putchar(char c); + +void +ao_serial5_drain(void); + +void +ao_serial5_set_speed(uint8_t speed); +#endif + +void +ao_serial_init(void); + +#endif /* _AO_SERIAL_H_ */ diff --git a/src/samd21/ao_serial_samd21.c b/src/samd21/ao_serial_samd21.c new file mode 100644 index 00000000..97d83790 --- /dev/null +++ b/src/samd21/ao_serial_samd21.c @@ -0,0 +1,353 @@ +/* + * Copyright © 2019 Keith Packard + * + * 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 + +static int +_ao_usart_tx_start(struct ao_samd21_usart *usart) +{ + if (!ao_fifo_empty(usart->tx_fifo)) { +#if HAS_SERIAL_SW_FLOW + if (usart->gpio_cts && ao_gpio_get(usart->gpio_cts, usart->pin_cts, foo) == 1) { + ao_exti_enable(usart->gpio_cts, usart->pin_cts); + return 0; + } +#endif + if (usart->reg->intflag & (1 << SAMD21_SERCOM_INTFLAG_DRE)) + { + usart->tx_running = 1; + usart->reg->intenset = (1 << SAMD21_SERCOM_INTFLAG_DRE) | (1 << SAMD21_SERCOM_INTFLAG_TXC); + ao_fifo_remove(usart->tx_fifo, usart->reg->data); + ao_wakeup(&usart->tx_fifo); + return 1; + } + } + return 0; +} + +static void +_ao_usart_rx(struct ao_samd21_usart *usart, int is_stdin) +{ + if (usart->reg->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) { + uint8_t data = (uint8_t) usart->reg->data; + if (!ao_fifo_full(usart->rx_fifo)) { + ao_fifo_insert(usart->rx_fifo, data); + ao_wakeup(&usart->rx_fifo); + if (is_stdin) + ao_wakeup(&ao_stdin_ready); +#if HAS_SERIAL_SW_FLOW + /* If the fifo is nearly full, turn off RTS and wait + * for it to drain a bunch + */ + if (usart->gpio_rts && ao_fifo_mostly(usart->rx_fifo)) { + ao_gpio_set(usart->gpio_rts, usart->pin_rts, usart->pin_rts, 1); + usart->rts = 0; + } +#endif + } + } +} + +static void +ao_usart_isr(struct ao_samd21_usart *usart, int is_stdin) +{ + _ao_usart_rx(usart, is_stdin); + + if (!_ao_usart_tx_start(usart)) + usart->reg->intenclr = (1 << SAMD21_SERCOM_INTFLAG_DRE); + + if (usart->reg->intflag & (1 << SAMD21_SERCOM_INTFLAG_TXC)) { + usart->tx_running = 0; + usart->reg->intenclr = (1 << SAMD21_SERCOM_INTFLAG_TXC); + if (usart->draining) { + usart->draining = 0; + ao_wakeup(&usart->tx_fifo); + } + } +} + +static const uint32_t ao_usart_speeds[] = { + [AO_SERIAL_SPEED_4800] = 4800, + [AO_SERIAL_SPEED_9600] = 9600, + [AO_SERIAL_SPEED_19200] = 19200, + [AO_SERIAL_SPEED_57600] = 57600, + [AO_SERIAL_SPEED_115200] = 115200, +}; + +static void +ao_usart_set_speed(struct ao_samd21_usart *usart, uint8_t speed) +{ + struct samd21_sercom *reg = usart->reg; + uint64_t top = (uint64_t) ao_usart_speeds[speed] << (4 + 16); + uint16_t baud = (uint16_t) (65536 - (top + AO_SYSCLK/2) / AO_SYSCLK); + uint32_t ctrla = reg->ctrla; + + if (ctrla & (1UL << SAMD21_SERCOM_CTRLA_ENABLE)) { + usart->reg->ctrla = ctrla & ~(1UL << SAMD21_SERCOM_CTRLA_ENABLE); + while (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE)) + ; + } + usart->reg->baud = baud; + if (ctrla & (1UL << SAMD21_SERCOM_CTRLA_ENABLE)) { + usart->reg->ctrla = ctrla; + while (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE)) + ; + } +} + +static void +ao_usart_init(struct ao_samd21_usart *usart, bool hw_flow, uint8_t id, uint8_t txpo, uint8_t rxpo) +{ + struct samd21_sercom *reg = usart->reg; + + (void) hw_flow; + + /* Send a clock along */ + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_SERCOM0_CORE + id); + + samd21_nvic_set_enable(SAMD21_NVIC_ISR_SERCOM0_POS + id); + samd21_nvic_set_priority(SAMD21_NVIC_ISR_SERCOM0_POS + id, 4); + + /* enable */ + samd21_pm.apbcmask |= (1 << (SAMD21_PM_APBCMASK_SERCOM0 + id)); + + /* Reset */ + reg->ctrla = (1 << SAMD21_SERCOM_CTRLA_SWRST); + + while ((reg->ctrla & (1 << SAMD21_SERCOM_CTRLA_SWRST)) || + (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_SWRST))) + ; + + reg->ctrlb = ((0 << SAMD21_SERCOM_CTRLB_CHSIZE) | + (0 << SAMD21_SERCOM_CTRLB_SBMODE) | + (0 << SAMD21_SERCOM_CTRLB_COLDEN) | + (0 << SAMD21_SERCOM_CTRLB_SFDE) | + (0 << SAMD21_SERCOM_CTRLB_ENC) | + (0 << SAMD21_SERCOM_CTRLB_PMODE) | + (1 << SAMD21_SERCOM_CTRLB_TXEN) | + (1 << SAMD21_SERCOM_CTRLB_RXEN) | + (3 << SAMD21_SERCOM_CTRLB_FIFOCLR)); + + ao_usart_set_speed(usart, AO_SERIAL_SPEED_9600); + + /* finish setup and enable the hardware */ + reg->ctrla = ((0 << SAMD21_SERCOM_CTRLA_SWRST) | + (1 << SAMD21_SERCOM_CTRLA_ENABLE) | + (1 << SAMD21_SERCOM_CTRLA_MODE) | + (1 << SAMD21_SERCOM_CTRLA_RUNSTDBY) | + (0 << SAMD21_SERCOM_CTRLA_IBON) | + (0 << SAMD21_SERCOM_CTRLA_SAMPR) | + (txpo << SAMD21_SERCOM_CTRLA_TXPO) | /* pad[2] */ + (rxpo << SAMD21_SERCOM_CTRLA_RXPO) | /* pad[3] */ + (0 << SAMD21_SERCOM_CTRLA_SAMPA) | + (0 << SAMD21_SERCOM_CTRLA_FORM) | /* no parity */ + (0 << SAMD21_SERCOM_CTRLA_CMODE) | /* async */ + (0 << SAMD21_SERCOM_CTRLA_CPOL) | + (1 << SAMD21_SERCOM_CTRLA_DORD)); /* LSB first */ + + /* Enable receive interrupt */ + reg->intenset = (1 << SAMD21_SERCOM_INTFLAG_RXC); + + while (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE)) + ; + +} + +static int +_ao_usart_pollchar(struct ao_samd21_usart *usart) +{ + int c; + + if (ao_fifo_empty(usart->rx_fifo)) + c = AO_READ_AGAIN; + else { + uint8_t u; + ao_fifo_remove(usart->rx_fifo, u); +#if HAS_SERIAL_SW_FLOW + /* If we've cleared RTS, check if there's space now and turn it back on */ + if (usart->gpio_rts && usart->rts == 0 && ao_fifo_barely(usart->rx_fifo)) { + ao_gpio_set(usart->gpio_rts, usart->pin_rts, foo, 0); + usart->rts = 1; + } +#endif + c = u; + } + return c; +} + +static char +ao_usart_getchar(struct ao_samd21_usart *usart) +{ + int c; + ao_arch_block_interrupts(); + while ((c = _ao_usart_pollchar(usart)) == AO_READ_AGAIN) + ao_sleep(&usart->rx_fifo); + ao_arch_release_interrupts(); + return (char) c; +} + +static void +ao_usart_putchar(struct ao_samd21_usart *usart, char c) +{ + ao_arch_block_interrupts(); + while (ao_fifo_full(usart->tx_fifo)) + ao_sleep(&usart->tx_fifo); + ao_fifo_insert(usart->tx_fifo, c); + _ao_usart_tx_start(usart); + ao_arch_release_interrupts(); +} + +static void +ao_usart_drain(struct ao_samd21_usart *usart) +{ + ao_arch_block_interrupts(); + while (!ao_fifo_empty(usart->tx_fifo) || usart->tx_running) { + usart->draining = 1; + ao_sleep(&usart->tx_fifo); + } + ao_arch_release_interrupts(); +} + +#if HAS_SERIAL_0 + +struct ao_samd21_usart ao_samd21_usart0; + +void samd21_sercom0_isr(void) { ao_usart_isr(&ao_samd21_usart0, USE_SERIAL_0_STDIN); } + +char +ao_serial0_getchar(void) +{ + return ao_usart_getchar(&ao_samd21_usart0); +} + +void +ao_serial0_putchar(char c) +{ + ao_usart_putchar(&ao_samd21_usart0, c); +} + +int +_ao_serial0_pollchar(void) +{ + return _ao_usart_pollchar(&ao_samd21_usart0); +} + +void +ao_serial0_drain(void) +{ + ao_usart_drain(&ao_samd21_usart0); +} + +void +ao_serial0_set_speed(uint8_t speed) +{ + ao_usart_drain(&ao_samd21_usart0); + ao_usart_set_speed(&ao_samd21_usart0, speed); +} +#endif /* HAS_SERIAL_0 */ + +#if HAS_SERIAL_1 + +struct ao_samd21_usart ao_samd21_usart1; + +void samd21_sercom1_isr(void) { ao_usart_isr(&ao_samd21_usart1, USE_SERIAL_1_STDIN); } + +char +ao_serial1_getchar(void) +{ + return ao_usart_getchar(&ao_samd21_usart1); +} + +void +ao_serial1_putchar(char c) +{ + ao_usart_putchar(&ao_samd21_usart1, c); +} + +int +_ao_serial1_pollchar(void) +{ + return _ao_usart_pollchar(&ao_samd21_usart1); +} + +void +ao_serial1_drain(void) +{ + ao_usart_drain(&ao_samd21_usart1); +} + +void +ao_serial1_set_speed(uint8_t speed) +{ + ao_usart_drain(&ao_samd21_usart1); + ao_usart_set_speed(&ao_samd21_usart1, speed); +} +#endif /* HAS_SERIAL_1 */ + +void +ao_serial_init(void) +{ + uint8_t txpo, rxpo; +#if HAS_SERIAL_0 + +#if SERIAL_0_PA10_PA11 + /* Pin settings */ + ao_enable_port(&samd21_port_a); + samd21_port_pmux_set(&samd21_port_a, 10, SAMD21_PORT_PMUX_FUNC_C); + samd21_port_pmux_set(&samd21_port_a, 11, SAMD21_PORT_PMUX_FUNC_C); + txpo = SAMD21_SERCOM_CTRLA_TXPO_TX_2; /* pad 2 */ + rxpo = SAMD21_SERCOM_CTRLA_RXPO_RX_3; /* pad 3 */ +#elif SERIAL_0_PA08_PA09 + /* Pin settings */ + ao_enable_port(&samd21_port_a); + samd21_port_pmux_set(&samd21_port_a, 8, SAMD21_PORT_PMUX_FUNC_C); + samd21_port_pmux_set(&samd21_port_a, 9, SAMD21_PORT_PMUX_FUNC_C); + txpo = SAMD21_SERCOM_CTRLA_TXPO_TX_0; /* pad 0 */ + rxpo = SAMD21_SERCOM_CTRLA_RXPO_RX_1; /* pad 1 */ +#else +#error "No SERIAL_0 port configuration specified" +#endif + + ao_samd21_usart0.reg = &samd21_sercom0; + ao_usart_init(&ao_samd21_usart0, 0, 0, txpo, rxpo); + +#if USE_SERIAL_0_STDIN + ao_add_stdio(_ao_serial0_pollchar, + ao_serial0_putchar, + NULL); +#endif +#endif +#if HAS_SERIAL_1 + +#if SERIAL_1_PA00_PA01 + /* Pin settings */ + ao_enable_port(&samd21_port_a); + samd21_port_pmux_set(&samd21_port_a, 0, SAMD21_PORT_PMUX_FUNC_D); + samd21_port_pmux_set(&samd21_port_a, 1, SAMD21_PORT_PMUX_FUNC_D); + txpo = SAMD21_SERCOM_CTRLA_TXPO_TX_0; + rxpo = SAMD21_SERCOM_CTRLA_RXPO_RX_1; +#else +#error "No SERIAL_1 port configuration specified" +#endif + + ao_samd21_usart1.reg = &samd21_sercom1; + ao_usart_init(&ao_samd21_usart1, 0, 1, txpo, rxpo); + +#if USE_SERIAL_1_STDIN + ao_add_stdio(_ao_serial1_pollchar, + ao_serial1_putchar, + NULL); +#endif +#endif +} diff --git a/src/samd21/ao_spi_samd21.c b/src/samd21/ao_spi_samd21.c new file mode 100644 index 00000000..aee8cb1a --- /dev/null +++ b/src/samd21/ao_spi_samd21.c @@ -0,0 +1,486 @@ +/* + * Copyright © 2022 Keith Packard + * + * 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 +#include + +static uint8_t ao_spi_mutex[SAMD21_NUM_SERCOM]; +static uint16_t ao_spi_pin_config[SAMD21_NUM_SERCOM]; + +#define SPI_DEBUG 0 +#define SPI_USE_DMA 1 + +struct ao_spi_samd21_info { + struct samd21_sercom *sercom; +}; + +static const struct ao_spi_samd21_info ao_spi_samd21_info[SAMD21_NUM_SERCOM] = { + { + .sercom = &samd21_sercom0, + }, + { + .sercom = &samd21_sercom1, + }, + { + .sercom = &samd21_sercom2, + }, + { + .sercom = &samd21_sercom3, + }, + { + .sercom = &samd21_sercom4, + }, + { + .sercom = &samd21_sercom5, + }, +}; + +static uint8_t spi_dev_null; + +#if SPI_USE_DMA + +static uint8_t ao_spi_done[SAMD21_NUM_SERCOM]; + +static void +_ao_spi_recv_dma_done(uint8_t dma_id, void *closure) +{ + uint8_t id = (uint8_t) (uintptr_t) closure; + + (void) dma_id; + ao_spi_done[id] = 1; + ao_wakeup(&ao_spi_done[id]); +} + +static inline uint32_t +dma_chctrlb(uint8_t id, bool tx) +{ + uint32_t chctrlb = 0; + + /* No complicated actions needed */ + chctrlb |= SAMD21_DMAC_CHCTRLB_CMD_NOACT << SAMD21_DMAC_CHCTRLB_CMD; + + /* Trigger after each byte transferred */ + chctrlb |= SAMD21_DMAC_CHCTRLB_TRIGACT_BEAT << SAMD21_DMAC_CHCTRLB_TRIGACT; + + /* Set the trigger source */ + if (tx) + chctrlb |= SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_TX(id) << SAMD21_DMAC_CHCTRLB_TRIGSRC; + else + chctrlb |= SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_RX(id) << SAMD21_DMAC_CHCTRLB_TRIGSRC; + + /* RX has priority over TX so that we don't drop incoming bytes */ + if (tx) + chctrlb |= SAMD21_DMAC_CHCTRLB_LVL_LVL0 << SAMD21_DMAC_CHCTRLB_LVL; + else + chctrlb |= SAMD21_DMAC_CHCTRLB_LVL_LVL3 << SAMD21_DMAC_CHCTRLB_LVL; + + /* No events needed */ + chctrlb |= 0UL << SAMD21_DMAC_CHCTRLB_EVOE; + chctrlb |= 0UL << SAMD21_DMAC_CHCTRLB_EVIE; + + /* And no actions either */ + chctrlb |= SAMD21_DMAC_CHCTRLB_EVACT_NOACT << SAMD21_DMAC_CHCTRLB_EVACT; + + return chctrlb; +} + +static inline uint16_t +dma_btctrl(bool step, bool tx) +{ + uint16_t btctrl = 0; + + /* Always step by 1 */ + btctrl |= SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X1 << SAMD21_DMAC_DESC_BTCTRL_STEPSIZE; + + /* Step the source if transmit, otherwise step the dest */ + if (tx) + btctrl |= SAMD21_DMAC_DESC_BTCTRL_STEPSEL_SRC << SAMD21_DMAC_DESC_BTCTRL_STEPSEL; + else + btctrl |= SAMD21_DMAC_DESC_BTCTRL_STEPSEL_DST << SAMD21_DMAC_DESC_BTCTRL_STEPSEL; + + /* Set the increment if stepping */ + if (tx) { + if (step) + btctrl |= 1UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC; + else + btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC; + btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC; + } else { + btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC; + if (step) + btctrl |= 1UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC; + else + btctrl |= 0UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC; + } + + /* byte at a time please */ + btctrl |= SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_BYTE << SAMD21_DMAC_DESC_BTCTRL_BEATSIZE; + + /* + * Watch for interrupts on RX -- we need to wait for the last byte to get received + * to know the SPI bus is idle + */ + if (tx) + btctrl |= SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_NOACT << SAMD21_DMAC_DESC_BTCTRL_BLOCKACT; + else + btctrl |= SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_INT << SAMD21_DMAC_DESC_BTCTRL_BLOCKACT; + + /* don't need any events */ + btctrl |= SAMD21_DMAC_DESC_BTCTRL_EVOSEL_DISABLE << SAMD21_DMAC_DESC_BTCTRL_EVOSEL; + + /* And make the descriptor valid */ + btctrl |= 1UL << SAMD21_DMAC_DESC_BTCTRL_VALID; + + return btctrl; +} + +static void +spi_run(const void *out, void *in, uint16_t len, uint16_t spi_index, bool step_out, bool step_in) +{ + const uint8_t *o = out; + uint8_t *i = in; + uint8_t id = AO_SPI_INDEX(spi_index); + struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom; + + ao_arch_block_interrupts(); + ao_spi_done[id] = 0; + + /* + * Stepped addresses to the DMA engine point past the end of + * the block + */ + if (step_out) + o += len; + if (step_in) + i += len; + + /* read any stuck data */ + (void) sercom->data; + + _ao_dma_start_transfer(AO_SERCOM_INPUT_DMA_ID(id), + (void *) &sercom->data, + i, + len, + dma_chctrlb(id, false), + dma_btctrl(step_in, false), + + _ao_spi_recv_dma_done, + (void *) (uintptr_t) id + ); + + _ao_dma_start_transfer(AO_SERCOM_OUTPUT_DMA_ID(id), + o, + (void *) &sercom->data, + len, + dma_chctrlb(id, true), + dma_btctrl(step_out, true), + NULL, + NULL + ); + + while (ao_spi_done[id] == 0) + ao_sleep(&ao_spi_done[id]); + + _ao_dma_done_transfer(AO_SERCOM_OUTPUT_DMA_ID(id)); + _ao_dma_done_transfer(AO_SERCOM_INPUT_DMA_ID(id)); + ao_arch_release_interrupts(); +} + +#else + +static void +spi_run(const void *out, void *in, uint16_t len, uint16_t spi_index, bool step_out, bool step_in) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom; + const uint8_t *o = out; + uint8_t *i = in; + + while (len--) { +#if SPI_DEBUG + printf("%02x", *o); +#endif + sercom->data = *o; + while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0) + ; + *i = (uint8_t) sercom->data; +#if SPI_DEBUG + printf("\t%02x\n", *i); +#endif + if (step_out) + o++; + if (step_in) + i++; + } +} + +#endif + +void +ao_spi_send(const void *block, uint16_t len, uint16_t spi_index) +{ + spi_run(block, &spi_dev_null, len, spi_index, true, false); +} + +void +ao_spi_send_fixed(uint8_t data, uint16_t len, uint16_t spi_index) +{ + spi_run(&data, &spi_dev_null, len, spi_index, false, false); +} + +void +ao_spi_recv(void *block, uint16_t len, uint16_t spi_index) +{ + spi_dev_null = 0xff; + spi_run(&spi_dev_null, block, len, spi_index, false, true); +} + + +void +ao_spi_duplex(const void *out, void *in, uint16_t len, uint16_t spi_index) +{ + spi_run(out, in, len, spi_index, true, true); +} + +static void +ao_spi_disable_pin_config(uint16_t spi_pin_config) +{ + switch (spi_pin_config) { +#if HAS_SPI_0 + case AO_SPI_PIN_CONFIG(AO_SPI_0_PA08_PA09_PA10): + samd21_port_pmux_clr(&samd21_port_a, 8); /* MOSI */ + samd21_port_pmux_clr(&samd21_port_a, 9); /* SCLK */ + samd21_port_pmux_clr(&samd21_port_a, 10); /* MISO */ + break; + case AO_SPI_PIN_CONFIG(AO_SPI_0_PA04_PA05_PA06): + samd21_port_pmux_clr(&samd21_port_a, 4); /* MOSI */ + samd21_port_pmux_clr(&samd21_port_a, 5); /* SCLK */ + samd21_port_pmux_clr(&samd21_port_a, 6); /* MISO */ + break; +#endif +#if HAS_SPI_3 + case AO_SPI_PIN_CONFIG(AO_SPI_3_PA22_PA23_PA20): + samd21_port_pmux_clr(&samd21_port_a, 22); /* MOSI */ + samd21_port_pmux_clr(&samd21_port_a, 23); /* SCLK */ + samd21_port_pmux_clr(&samd21_port_a, 20); /* MISO */ + break; +#endif +#if HAS_SPI_4 + case AO_SPI_PIN_CONFIG(AO_SPI_4_PB10_PB11_PA12): + samd21_port_pmux_clr(&samd21_port_b, 10); /* MOSI */ + samd21_port_pmux_clr(&samd21_port_b, 11); /* SCLK */ + samd21_port_pmux_clr(&samd21_port_a, 12); /* MISO */ + break; +#endif +#if HAS_SPI_5 + case AO_SPI_PIN_CONFIG(AO_SPI_5_PB22_PB23_PB03): + samd21_port_pmux_clr(&samd21_port_b, 22); /* MOSI */ + samd21_port_pmux_clr(&samd21_port_b, 23); /* SCLK */ + samd21_port_pmux_clr(&samd21_port_b, 3); /* MISO */ + break; +#endif + case 0xffff: + break; + } +} + +static void +ao_spi_enable_pin_config(uint16_t spi_pin_config) +{ + switch (spi_pin_config) { +#if HAS_SPI_0 + case AO_SPI_PIN_CONFIG(AO_SPI_0_PA08_PA09_PA10): + ao_enable_output(&samd21_port_a, 8, 1); + ao_enable_output(&samd21_port_a, 9, 1); + ao_enable_input(&samd21_port_a, 10, AO_MODE_PULL_NONE); + + samd21_port_pmux_set(&samd21_port_a, 8, SAMD21_PORT_PMUX_FUNC_C); /* MOSI */ + samd21_port_pmux_set(&samd21_port_a, 9, SAMD21_PORT_PMUX_FUNC_C); /* SCLK */ + samd21_port_pmux_set(&samd21_port_a, 10, SAMD21_PORT_PMUX_FUNC_C); /* MISO */ + break; + case AO_SPI_PIN_CONFIG(AO_SPI_0_PA04_PA05_PA06): + ao_enable_output(&samd21_port_a, 4, 1); + ao_enable_output(&samd21_port_a, 5, 1); + ao_enable_input(&samd21_port_a, 6, AO_MODE_PULL_NONE); + + samd21_port_pmux_set(&samd21_port_a, 4, SAMD21_PORT_PMUX_FUNC_D); /* MOSI */ + samd21_port_pmux_set(&samd21_port_a, 5, SAMD21_PORT_PMUX_FUNC_D); /* SCLK */ + samd21_port_pmux_set(&samd21_port_a, 6, SAMD21_PORT_PMUX_FUNC_D); /* MISO */ + break; +#endif +#if HAS_SPI_3 + case AO_SPI_PIN_CONFIG(AO_SPI_3_PA22_PA23_PA20): + ao_enable_output(&samd21_port_a, 22, 1); + ao_enable_output(&samd21_port_a, 23, 1); + ao_enable_input(&samd21_port_a, 20, AO_MODE_PULL_NONE); + + samd21_port_pmux_set(&samd21_port_a, 22, SAMD21_PORT_PMUX_FUNC_C); /* MOSI */ + samd21_port_pmux_set(&samd21_port_a, 23, SAMD21_PORT_PMUX_FUNC_C); /* SCLK */ + samd21_port_pmux_set(&samd21_port_a, 20, SAMD21_PORT_PMUX_FUNC_D); /* MISO */ + break; +#endif +#if HAS_SPI_4 + case AO_SPI_PIN_CONFIG(AO_SPI_4_PB10_PB11_PA12): + ao_enable_output(&samd21_port_b, 10, 1); + ao_enable_output(&samd21_port_b, 11, 1); + ao_enable_input(&samd21_port_a, 12, AO_MODE_PULL_NONE); + + samd21_port_pmux_set(&samd21_port_b, 10, SAMD21_PORT_PMUX_FUNC_D); /* MOSI */ + samd21_port_pmux_set(&samd21_port_b, 11, SAMD21_PORT_PMUX_FUNC_D); /* SCLK */ + samd21_port_pmux_set(&samd21_port_a, 12, SAMD21_PORT_PMUX_FUNC_D); /* MISO */ + break; +#endif +#if HAS_SPI_5 + case AO_SPI_PIN_CONFIG(AO_SPI_5_PB22_PB23_PB03): + ao_enable_output(&samd21_port_b, 22, 1); + ao_enable_output(&samd21_port_b, 23, 1); + ao_enable_input(&samd21_port_b, 3, AO_MODE_PULL_NONE); + + samd21_port_pmux_set(&samd21_port_b, 22, SAMD21_PORT_PMUX_FUNC_D); /* 5.2 MOSI */ + samd21_port_pmux_set(&samd21_port_b, 23, SAMD21_PORT_PMUX_FUNC_D); /* 5.3 SCLK */ + samd21_port_pmux_set(&samd21_port_b, 3, SAMD21_PORT_PMUX_FUNC_D); /* 5.1 MISO */ + break; +#endif + default: + ao_panic(AO_PANIC_SPI); + break; + } +} + +static void +ao_spi_config(uint16_t spi_index, uint32_t baud) +{ + uint16_t spi_pin_config = AO_SPI_PIN_CONFIG(spi_index); + uint8_t id = AO_SPI_INDEX(spi_index); + struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom; + + if (spi_pin_config != ao_spi_pin_config[id]) { + ao_spi_disable_pin_config(ao_spi_pin_config[id]); + ao_spi_enable_pin_config(spi_pin_config); + ao_spi_pin_config[id] = spi_pin_config; + } + + sercom->baud = (uint16_t) baud; + + /* Set spi mode */ + uint32_t ctrla = sercom->ctrla; + ctrla &= ~((1UL << SAMD21_SERCOM_CTRLA_CPOL) | + (1UL << SAMD21_SERCOM_CTRLA_CPHA) | + (SAMD21_SERCOM_CTRLA_DOPO_MASK << SAMD21_SERCOM_CTRLA_DOPO) | + (SAMD21_SERCOM_CTRLA_DIPO_MASK << SAMD21_SERCOM_CTRLA_DIPO)); + ctrla |= ((AO_SPI_CPOL(spi_index) << SAMD21_SERCOM_CTRLA_CPOL) | + (AO_SPI_CPHA(spi_index) << SAMD21_SERCOM_CTRLA_CPHA) | + (AO_SPI_DOPO(spi_index) << SAMD21_SERCOM_CTRLA_DOPO) | + (AO_SPI_DIPO(spi_index) << SAMD21_SERCOM_CTRLA_DIPO)); + + /* finish setup and enable the hardware */ + ctrla |= (1 << SAMD21_SERCOM_CTRLA_ENABLE); + +#if SPI_DEBUG + printf("ctrla %08lx\n", ctrla); +#endif + + sercom->ctrla = ctrla; + + while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE)) + ; +} + +void +ao_spi_get(uint16_t spi_index, uint32_t speed) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + + ao_mutex_get(&ao_spi_mutex[id]); + ao_spi_config(spi_index, speed); +} + +void +ao_spi_put(uint16_t spi_index) +{ + uint8_t id = AO_SPI_INDEX(spi_index); + struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom; + + sercom->ctrla &= ~(1UL << SAMD21_SERCOM_CTRLA_ENABLE); + while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE)) + ; + ao_mutex_put(&ao_spi_mutex[id]); +} + +static void +ao_spi_init_sercom(uint8_t id) +{ + struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom; + + /* Send a clock along */ + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_SERCOM0_CORE + id); + + samd21_nvic_set_enable(SAMD21_NVIC_ISR_SERCOM0_POS + id); + samd21_nvic_set_priority(SAMD21_NVIC_ISR_SERCOM0_POS + id, 4); + + /* Enable */ + samd21_pm.apbcmask |= (1 << (SAMD21_PM_APBCMASK_SERCOM0 + id)); + + /* Reset */ + sercom->ctrla = (1 << SAMD21_SERCOM_CTRLA_SWRST); + + while ((sercom->ctrla & (1 << SAMD21_SERCOM_CTRLA_SWRST)) || + (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_SWRST))) + ; + + /* set SPI mode */ + sercom->ctrla = ((SAMD21_SERCOM_CTRLA_DORD_MSB << SAMD21_SERCOM_CTRLA_DORD) | + (0 << SAMD21_SERCOM_CTRLA_CPOL) | + (0 << SAMD21_SERCOM_CTRLA_CPHA) | + (0 << SAMD21_SERCOM_CTRLA_FORM) | + (2 << SAMD21_SERCOM_CTRLA_DIPO) | + (0 << SAMD21_SERCOM_CTRLA_DOPO) | + (0 << SAMD21_SERCOM_CTRLA_IBON) | + (0 << SAMD21_SERCOM_CTRLA_RUNSTDBY) | + (SAMD21_SERCOM_CTRLA_MODE_SPI_HOST << SAMD21_SERCOM_CTRLA_MODE) | + (0 << SAMD21_SERCOM_CTRLA_ENABLE) | + (0 << SAMD21_SERCOM_CTRLA_SWRST)); + + sercom->ctrlb = ((1 << SAMD21_SERCOM_CTRLB_RXEN) | + (0 << SAMD21_SERCOM_CTRLB_AMODE) | + (0 << SAMD21_SERCOM_CTRLB_MSSEN) | + (0 << SAMD21_SERCOM_CTRLB_SSDE) | + (0 << SAMD21_SERCOM_CTRLB_PLOADEN) | + (SAMD21_SERCOM_CTRLB_CHSIZE_8 << SAMD21_SERCOM_CTRLB_CHSIZE)); + + ao_spi_pin_config[id] = 0xffff; +} + +void +ao_spi_init(void) +{ +#if HAS_SPI_0 + ao_spi_init_sercom(0); +#endif +#if HAS_SPI_1 + ao_spi_init_sercom(1); +#endif +#if HAS_SPI_2 + ao_spi_init_sercom(2); +#endif +#if HAS_SPI_3 + ao_spi_init_sercom(3); +#endif +#if HAS_SPI_4 + ao_spi_init_sercom(4); +#endif +#if HAS_SPI_5 + ao_spi_init_sercom(5); +#endif +} diff --git a/src/samd21/ao_tc_samd21.c b/src/samd21/ao_tc_samd21.c new file mode 100644 index 00000000..f08c7383 --- /dev/null +++ b/src/samd21/ao_tc_samd21.c @@ -0,0 +1,54 @@ +/* + * Copyright © 2019 Keith Packard + * + * 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 +#include + +void +ao_tc_set(struct samd21_tc *tc, uint8_t channel, uint32_t value) +{ + tc->mode_16.cc[channel] = value; +} + +static void +ao_tc_init(struct samd21_tc *tc, uint32_t apbcmask) +{ + samd21_pm.apbcmask |= apbcmask; + + /* Reset the device */ + tc->ctrla = (1 << SAMD21_TC_CTRLA_SWRST); + + while ((tc->ctrla & (1 << SAMD21_TC_CTRLA_SWRST)) != 0 || + (tc->status & (1 << SAMD21_TC_STATUS_SYNCBUSY)) != 0) + ; + + tc->ctrla = ((SAMD21_TC_CTRLA_PRESCSYNC_GCLK << SAMD21_TC_CTRLA_PRESCSYNC) | + (SAMD21_TC_CTRLA_PRESCALER_DIV1 << SAMD21_TC_CTRLA_PRESCALER) | + (SAMD21_TC_CTRLA_WAVEGEN_NPWM << SAMD21_TC_CTRLA_WAVEGEN) | + (SAMD21_TC_CTRLA_MODE_COUNT16) | + (1 << SAMD21_TC_CTRLA_ENABLE)); + tc->dbgctrl = (1 << SAMD21_TC_DBGCTRL_DBGRUN); +} + +void +ao_tc_samd21_init(void) +{ + /* SAMD21G18 has only TC3-TC5 */ + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC2_TC3); + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TC4_TC5); + + ao_tc_init(&samd21_tc3, 1 << SAMD21_PM_APBCMASK_TC3); + ao_tc_init(&samd21_tc4, 1 << SAMD21_PM_APBCMASK_TC4); + ao_tc_init(&samd21_tc5, 1 << SAMD21_PM_APBCMASK_TC5); +} diff --git a/src/samd21/ao_tc_samd21.h b/src/samd21/ao_tc_samd21.h new file mode 100644 index 00000000..1d6cf416 --- /dev/null +++ b/src/samd21/ao_tc_samd21.h @@ -0,0 +1,24 @@ +/* + * Copyright © 2019 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _AO_TC_SAMD21_H_ +#define _AO_TC_SAMD21_H_ + +void +ao_tc_set(struct samd21_tc *tc, uint8_t channel, uint32_t value); + +void +ao_tc_samd21_init(void); + +#endif /* _AO_TC_SAMD21_H_ */ diff --git a/src/samd21/ao_tcc_samd21.c b/src/samd21/ao_tcc_samd21.c new file mode 100644 index 00000000..9cb45c06 --- /dev/null +++ b/src/samd21/ao_tcc_samd21.c @@ -0,0 +1,74 @@ +/* + * Copyright © 2019 Keith Packard + * + * 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 +#include + +void +ao_tcc_set(struct samd21_tcc *tcc, uint8_t channel, uint32_t value) +{ + tcc->cc[channel] = value; +} + +static void +ao_tcc_init(struct samd21_tcc *tcc, uint32_t apbcmask) +{ + samd21_pm.apbcmask |= apbcmask; + + /* Reset the device */ + tcc->ctrla = (1 << SAMD21_TCC_CTRLA_SWRST); + + while ((tcc->ctrla & (1 << SAMD21_TCC_CTRLA_SWRST)) != 0 || + (tcc->syncbusy & (1 << SAMD21_TCC_SYNCBUSY_SWRST)) != 0) + ; + + tcc->per = AO_TCC_PERIOD - 1; + +#if 0 + tcc->evctrl = ((1 << SAMD21_TCC_EVCTRL_MCEO(0)) | + (1 << SAMD21_TCC_EVCTRL_MCEO(1)) | + (1 << SAMD21_TCC_EVCTRL_MCEO(2)) | + (1 << SAMD21_TCC_EVCTRL_MCEO(3))); +#endif + + tcc->wave = ((SAMD21_TCC_WAVE_WAVEGEN_NPWM << SAMD21_TCC_WAVE_WAVEGEN) | + (0 << SAMD21_TCC_WAVE_RAMP) | + (0 << SAMD21_TCC_WAVE_CIPEREN) | + (0 << SAMD21_TCC_WAVE_CCCEN(0)) | + (0 << SAMD21_TCC_WAVE_CCCEN(1)) | + (0 << SAMD21_TCC_WAVE_CCCEN(2)) | + (0 << SAMD21_TCC_WAVE_CCCEN(3)) | + (0 << SAMD21_TCC_WAVE_POL(0)) | + (0 << SAMD21_TCC_WAVE_POL(1)) | + (0 << SAMD21_TCC_WAVE_POL(1)) | + (0 << SAMD21_TCC_WAVE_POL(3)) | + (0 << SAMD21_TCC_WAVE_SWAP(0)) | + (0 << SAMD21_TCC_WAVE_SWAP(1)) | + (0 << SAMD21_TCC_WAVE_SWAP(1)) | + (0 << SAMD21_TCC_WAVE_SWAP(3))); + + tcc->ctrla = (1 << SAMD21_TCC_CTRLA_ENABLE); + tcc->dbgctrl = (1 << SAMD21_TCC_DBGCTRL_DBGRUN); +} + +void +ao_tcc_samd21_init(void) +{ + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC0_TCC1); + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_TCC2_TC3); + + ao_tcc_init(&samd21_tcc0, 1 << SAMD21_PM_APBCMASK_TCC0); + ao_tcc_init(&samd21_tcc1, 1 << SAMD21_PM_APBCMASK_TCC1); + ao_tcc_init(&samd21_tcc2, 1 << SAMD21_PM_APBCMASK_TCC2); +} diff --git a/src/samd21/ao_tcc_samd21.h b/src/samd21/ao_tcc_samd21.h new file mode 100644 index 00000000..a8f41a81 --- /dev/null +++ b/src/samd21/ao_tcc_samd21.h @@ -0,0 +1,24 @@ +/* + * Copyright © 2019 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _AO_TCC_SAMD21_H_ +#define _AO_TCC_SAMD21_H_ + +void +ao_tcc_set(struct samd21_tcc *tcc, uint8_t channel, uint32_t value); + +void +ao_tcc_samd21_init(void); + +#endif /* _AO_TCC_SAMD21_H_ */ diff --git a/src/samd21/ao_timer.c b/src/samd21/ao_timer.c new file mode 100644 index 00000000..2b431d38 --- /dev/null +++ b/src/samd21/ao_timer.c @@ -0,0 +1,292 @@ +/* + * Copyright © 2019 Keith Packard + * + * 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 + +#ifndef HAS_TICK +#define HAS_TICK 1 +#endif + +#if HAS_TICK +volatile AO_TICK_TYPE ao_tick_count; + +AO_TICK_TYPE +ao_time(void) +{ + return ao_tick_count; +} + +uint64_t +ao_time_ns(void) +{ + AO_TICK_TYPE before, after; + uint32_t cvr; + + do { + before = ao_tick_count; + cvr = samd21_systick.cvr; + after = ao_tick_count; + } while (before != after); + + return (uint64_t) after * (1000000000ULL / AO_HERTZ) + + (uint64_t) cvr * (1000000000ULL / AO_SYSTICK); +} + +#if AO_DATA_ALL +volatile uint8_t ao_data_interval = 1; +volatile uint8_t ao_data_count; +#endif + +void samd21_systick_isr(void) +{ + ao_arch_release_interrupts(); + if (samd21_systick.csr & (1 << SAMD21_SYSTICK_CSR_COUNTFLAG)) { + ++ao_tick_count; + ao_task_check_alarm(); +#if AO_DATA_ALL + if (++ao_data_count == ao_data_interval && ao_data_interval) { + ao_data_count = 0; +#if HAS_FAKE_FLIGHT + if (ao_fake_flight_active) + ao_fake_flight_poll(); + else +#endif + ao_adc_poll(); +#if (AO_DATA_ALL & ~(AO_DATA_ADC)) + ao_wakeup((void *) &ao_data_count); +#endif + } +#endif +#ifdef AO_TIMER_HOOK + AO_TIMER_HOOK; +#endif + } +} + +#if HAS_ADC +void +ao_timer_set_adc_interval(uint8_t interval) +{ + ao_arch_critical( + ao_data_interval = interval; + ao_data_count = 0; + ); +} +#endif + +#define SYSTICK_RELOAD (AO_SYSTICK / AO_HERTZ - 1) + +void +ao_timer_init(void) +{ + samd21_systick.csr = 0; + samd21_systick.rvr = SYSTICK_RELOAD; + samd21_systick.cvr = 0; + samd21_systick.csr = ((1 << SAMD21_SYSTICK_CSR_ENABLE) | + (1 << SAMD21_SYSTICK_CSR_TICKINT) | + (SAMD21_SYSTICK_CSR_CLKSOURCE_HCLK_8 << SAMD21_SYSTICK_CSR_CLKSOURCE)); + /* Set clock to lowest priority */ + samd21_scb.shpr3 |= 3UL << 30; +} + +#endif + + +void +ao_clock_init(void) +{ + /* Set flash wait state to tolerate 48MHz */ + samd21_nvmctrl.ctrlb |= (1 << SAMD21_NVMCTRL_CTRLB_RWS); + + samd21_pm.apbamask |= ((1 << SAMD21_PM_APBAMASK_GCLK) | + (1 << SAMD21_PM_APBAMASK_SYSCTRL)); + + /* Reset gclk */ + samd21_gclk.ctrl = (1 << SAMD21_GCLK_CTRL_SWRST) + ; + + /* Wait for reset to complete */ + while ((samd21_gclk.ctrl & (1 << SAMD21_GCLK_CTRL_SWRST)) && + (samd21_gclk.status & (1 << SAMD21_GCLK_STATUS_SYNCBUSY))) + ; + +#ifdef AO_XOSC + /* Enable xosc (external xtal oscillator) */ + samd21_sysctrl.xosc = ((SAMD21_SYSCTRL_XOSC_STARTUP_8192 << SAMD21_SYSCTRL_XOSC_STARTUP) | + (0 << SAMD21_SYSCTRL_XOSC_AMPGC) | + (SAMD21_SYSCTRL_XOSC_GAIN_16MHz << SAMD21_SYSCTRL_XOSC_GAIN) | + (0 << SAMD21_SYSCTRL_XOSC_ONDEMAND) | + (1 << SAMD21_SYSCTRL_XOSC_RUNSTDBY) | + (1 << SAMD21_SYSCTRL_XOSC_XTALEN)); + samd21_sysctrl.xosc |= ((1 << SAMD21_SYSCTRL_XOSC_ENABLE)); + + /* Wait for xosc */ + while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_XOSCRDY)) == 0) + ; + + /* program DPLL */ + + /* Divide down */ + samd21_sysctrl.dpllctrlb = (((AO_XOSC_DIV/2 - 1) << SAMD21_SYSCTRL_DPLLCTRLB_DIV) | + (0 << SAMD21_SYSCTRL_DPLLCTRLB_LBYPASS) | + (SAMD21_SYSCTRL_DPLLCTRLB_LTIME_DEFAULT << SAMD21_SYSCTRL_DPLLCTRLB_LTIME) | + (SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_XOSC << SAMD21_SYSCTRL_DPLLCTRLB_REFCLK) | + (0 << SAMD21_SYSCTRL_DPLLCTRLB_WUF) | + (1 << SAMD21_SYSCTRL_DPLLCTRLB_LPEN) | + (SAMD21_SYSCTRL_DPLLCTRLB_FILTER_DEFAULT << SAMD21_SYSCTRL_DPLLCTRLB_FILTER)); + + /* Multiply up */ + samd21_sysctrl.dpllratio = ((AO_XOSC_MUL - 1) << SAMD21_SYSCTRL_DPLLRATIO_LDR); + + /* Always on in run mode, off in standby mode */ + samd21_sysctrl.dpllctrla = ((0 << SAMD21_SYSCTRL_DPLLCTRLA_ONDEMAND) | + (0 << SAMD21_SYSCTRL_DPLLCTRLA_RUNSTDBY)); + + /* Enable DPLL */ + samd21_sysctrl.dpllctrla |= (1 << SAMD21_SYSCTRL_DPLLCTRLA_ENABLE); + + /* Wait for the DPLL to be enabled */ + while ((samd21_sysctrl.dpllstatus & (1 << SAMD21_SYSCTRL_DPLLSTATUS_ENABLE)) == 0) + ; + + /* Wait for the DPLL to be ready */ + while ((samd21_sysctrl.dpllstatus & (1 << SAMD21_SYSCTRL_DPLLSTATUS_CLKRDY)) == 0) + ; + + samd21_gclk_wait_sync(); + + /* + * Switch generator 0 (CPU clock) to DPLL + */ + + /* divide by 1 */ + samd21_gclk_gendiv(AO_GCLK_SYSCLK, AO_XOSC_GCLK_DIV); + + /* select DPLL as source */ + samd21_gclk_genctrl(SAMD21_GCLK_GENCTRL_SRC_FDPLL96M, AO_GCLK_FDPLL96M); +#endif + +#ifdef AO_DFLL48M + + /* + * Enable DFLL48M clock + */ + + samd21_sysctrl.dfllctrl = (1 << SAMD21_SYSCTRL_DFLLCTRL_ENABLE); + samd21_dfll_wait_sync(); + +#ifdef AO_XOSC32K +#define AO_GCLK_XOSC32K 1 + + /* Enable xosc32k (external 32.768kHz oscillator) */ + samd21_sysctrl.xosc32k = ((6 << SAMD21_SYSCTRL_XOSC32K_STARTUP) | + (1 << SAMD21_SYSCTRL_XOSC32K_XTALEN) | + (1 << SAMD21_SYSCTRL_XOSC32K_EN32K)); + + /* requires separate store */ + samd21_sysctrl.xosc32k |= (1 << SAMD21_SYSCTRL_XOSC32K_ENABLE); + + /* Wait for osc */ + while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_XOSC32KRDY)) == 0) + ; + + /* + * Use xosc32k as source of gclk generator AO_GCLK_XOSC32K + */ + + samd21_gclk_gendiv(AO_GCLK_XOSC32K, 1); + samd21_gclk_genctrl(SAMD21_GCLK_GENCTRL_SRC_XOSC32K, AO_GCLK_XOSC32K); + + /* + * Use generator as source for dfm48m reference + */ + + samd21_gclk_clkctrl(AO_GCLK_XOSC32K, SAMD21_GCLK_CLKCTRL_ID_DFLL48M_REF); + + /* Set multiplier to get as close to 48MHz as we can without going over */ + samd21_sysctrl.dfllmul = (((31/4) << SAMD21_SYSCTRL_DFLLMUL_CSTEP) | + ((255/4) << SAMD21_SYSCTRL_DFLLMUL_FSTEP) | + ((AO_DFLL48M / AO_XOSC32K) << SAMD21_SYSCTRL_DFLLMUL_MUL)); + + /* pull out coarse calibration value from rom */ + uint32_t coarse = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL) & + SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL_MASK); + + samd21_sysctrl.dfllval = ((coarse << SAMD21_SYSCTRL_DFLLVAL_COARSE) | + (512 << SAMD21_SYSCTRL_DFLLVAL_FINE)); + + samd21_sysctrl.dfllctrl = 0; + samd21_dfll_wait_sync(); + + samd21_sysctrl.dfllctrl = ((1 << SAMD21_SYSCTRL_DFLLCTRL_MODE) | + (1 << SAMD21_SYSCTRL_DFLLCTRL_ENABLE)); + + samd21_dfll_wait_sync(); + samd21_gclk_wait_sync(); + + /* wait for fine lock */ + while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_DFLLLCKC)) == 0 || + (samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_DFLLLCKF)) == 0) + ; +#else + samd21_sysctrl.dfllmul = (((31/4) << SAMD21_SYSCTRL_DFLLMUL_CSTEP) | + ((255/4) << SAMD21_SYSCTRL_DFLLMUL_FSTEP) | + ((AO_DFLL48M / 1000) << SAMD21_SYSCTRL_DFLLMUL_MUL)); + + /* pull out coarse calibration value from rom */ + uint32_t coarse = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL) & + SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL_MASK); + + samd21_sysctrl.dfllval = ((coarse << SAMD21_SYSCTRL_DFLLVAL_COARSE) | + (512 << SAMD21_SYSCTRL_DFLLVAL_FINE)); + + samd21_sysctrl.dfllctrl = 0; + samd21_dfll_wait_sync(); + + samd21_sysctrl.dfllctrl = ((1 << SAMD21_SYSCTRL_DFLLCTRL_MODE) | + (1 << SAMD21_SYSCTRL_DFLLCTRL_BPLCKC) | + (1 << SAMD21_SYSCTRL_DFLLCTRL_CCDIS) | + (1 << SAMD21_SYSCTRL_DFLLCTRL_USBCRM) | + (1 << SAMD21_SYSCTRL_DFLLCTRL_ENABLE)); + + samd21_dfll_wait_sync(); + samd21_gclk_wait_sync(); +#endif + + /* + * Switch generator to DFLL48M + */ + + /* divide by 1 */ + samd21_gclk_gendiv(AO_GCLK_SYSCLK, 1); + + /* select DFLL48M as source */ + samd21_gclk_genctrl(SAMD21_GCLK_GENCTRL_SRC_DFLL48M, AO_GCLK_DFLL48M); +#endif + + /* Set up all of the clocks to be /1 */ + + samd21_pm.cpusel = ((0 << SAMD21_PM_CPUSEL_CPUDIV)); + samd21_pm.apbasel = ((0 << SAMD21_PM_APBASEL_APBADIV)); + samd21_pm.apbbsel = ((0 << SAMD21_PM_APBBSEL_APBBDIV)); + samd21_pm.apbcsel = ((0 << SAMD21_PM_APBCSEL_APBCDIV)); + + /* Disable OSC8M */ + samd21_sysctrl.osc8m &= ~(1UL << SAMD21_SYSCTRL_OSC8M_ENABLE); + + /* Additional misc configuration stuff */ + + /* Disable automatic NVM write operations */ + samd21_nvmctrl.ctrlb |= (1UL << SAMD21_NVMCTRL_CTRLB_MANW); +} diff --git a/src/samd21/ao_usb_samd21.c b/src/samd21/ao_usb_samd21.c new file mode 100644 index 00000000..bed4f54a --- /dev/null +++ b/src/samd21/ao_usb_samd21.c @@ -0,0 +1,875 @@ +/* + * Copyright © 2012 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" +#include "ao_usb.h" +#include "ao_product.h" + +#ifndef AO_POWER_MANAGEMENT +#define AO_POWER_MANAGEMENT 0 +#endif + +#ifndef AO_USB_DEVICE_ID_SERIAL +#define AO_USB_DEVICE_ID_SERIAL 0 +#endif + +#if USE_USB_FIFO +static struct ao_fifo ao_usb_rx_fifo; +#endif + +#if USE_USB_STDIO +#define AO_USB_OUT_SLEEP_ADDR (&ao_stdin_ready) +#else +#define AO_USB_OUT_SLEEP_ADDR (&ao_usb_out_avail) +#endif + +#define SAMD21_USB_ALIGN __attribute__ ((aligned(4))) + +struct ao_usb_setup { + uint8_t dir_type_recip; + uint8_t request; + uint16_t value; + uint16_t index; + uint16_t length; +} ao_usb_setup; + +static uint8_t ao_usb_ep0_state; + +struct samd21_usb_desc samd21_usb_desc[8] SAMD21_USB_ALIGN; + +/* Pending EP0 IN data */ +static uint8_t ao_usb_ep0_in_tmp[2]; /* small buffer */ +static const uint8_t *ao_usb_ep0_in_data; /* Remaining data */ +static uint8_t ao_usb_ep0_in_len; /* Remaining amount */ + +/* Pending EP0 OUT data */ +static uint8_t *ao_usb_ep0_out_data; +static uint8_t ao_usb_ep0_out_len; + +/* Endpoint 0 buffers */ +static uint8_t ao_usb_ep0_in_buf[AO_USB_CONTROL_SIZE] SAMD21_USB_ALIGN; +static uint8_t ao_usb_ep0_out_buf[AO_USB_CONTROL_SIZE] SAMD21_USB_ALIGN; + +#if AO_USB_HAS_INT +/* Pointer to interrupt buffer in USB memory */ +static uint8_t ao_usb_int_buf[AO_USB_INT_SIZE] SAMD21_USB_ALIGN; +#endif + +/* Buffers in DRAM */ +#if AO_USB_HAS_IN +static uint8_t ao_usb_in_tx_which; +static uint8_t ao_usb_tx_count; +static uint8_t ao_usb_in_buf[2][AO_USB_IN_SIZE] SAMD21_USB_ALIGN; + +#endif +#if AO_USB_HAS_OUT +static uint8_t ao_usb_out_rx_which; +#if !USE_USB_FIFO +static uint8_t ao_usb_rx_count, ao_usb_rx_pos; +#endif +static uint8_t ao_usb_out_buf[2][AO_USB_OUT_SIZE] SAMD21_USB_ALIGN; + +#endif + +/* Marks when we don't need to send an IN packet. + * This happens only when the last IN packet is not full, + * otherwise the host will expect to keep seeing packets. + * Send a zero-length packet as required + */ +static uint8_t ao_usb_in_flushed; + +/* Marks when we have delivered an IN packet to the hardware + * and it has not been received yet. ao_sleep on this address + * to wait for it to be delivered. + */ +static uint8_t ao_usb_in_pending; + +/* Marks when an OUT packet has been received by the hardware + * but not pulled to the shadow buffer. + */ +static uint8_t ao_usb_out_avail; +uint8_t ao_usb_running; +static uint8_t ao_usb_configuration; + +#define AO_USB_EP0_GOT_SETUP 1 +#define AO_USB_EP0_GOT_RX_DATA 2 +#define AO_USB_EP0_GOT_TX_ACK 4 + +static uint8_t ao_usb_ep0_receive; +static uint8_t ao_usb_address; +static uint8_t ao_usb_address_pending; + +/* + * Set current device address and mark the + * interface as active + */ +static void +ao_usb_set_address(uint8_t address) +{ + samd21_usb.dadd = (1 << SAMD21_USB_DADD_ADDEN) | (address << SAMD21_USB_DADD_DADD); + ao_usb_address_pending = 0; +} + +/* + * Initialize an entpoint + */ + +static void +ao_usb_init_bank(struct samd21_usb_desc_bank *bank, + uint8_t *buf, + uint16_t size) +{ + bank->addr = (uint32_t) buf; + + uint32_t size_bits = 0; + switch (size) { + case 8: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_8; break; + case 16: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_16; break; + case 32: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_32; break; + case 64: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_64; break; + case 128: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_128; break; + case 256: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_256; break; + case 512: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_512; break; + case 1023: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_1023; break; + } + bank->pcksize = ((0 << SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT) | + (0 << SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE) | + (size_bits << SAMD21_USB_DESC_PCKSIZE_SIZE) | + (0 << SAMD21_USB_DESC_PCKSIZE_AUTO_ZLP)); +} + +static void +ao_usb_init_ep(uint8_t ep, + uint8_t type_out, void *out_buf, uint16_t out_size, + uint8_t type_in, void *in_buf, uint16_t in_size) +{ + /* set up descriptors */ + ao_usb_init_bank(&samd21_usb_desc[ep].bank[0], + out_buf, out_size); + + ao_usb_init_bank(&samd21_usb_desc[ep].bank[1], + in_buf, in_size); + + samd21_usb.ep[ep].epcfg = (uint8_t) ((type_out << SAMD21_USB_EP_EPCFG_EP_TYPE_OUT) | + (type_in << SAMD21_USB_EP_EPCFG_EP_TYPE_IN)); + + /* Clear all status bits */ + samd21_usb.ep[ep].epstatusclr = 0xff; + + /* Select interrupts */ + uint8_t epinten = 0; + if (out_buf) + epinten |= (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT0); + if (in_buf) + epinten |= (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT1); + if (ep == 0) + epinten |= (1 << SAMD21_USB_EP_EPINTFLAG_RXSTP); + samd21_usb.ep[ep].epintenset = epinten; +} + +static void +ao_usb_set_ep0(void) +{ + ao_usb_init_ep(AO_USB_CONTROL_EP, + SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_CONTROL, + ao_usb_ep0_out_buf, AO_USB_CONTROL_SIZE, + SAMD21_USB_EP_EPCFG_EP_TYPE_IN_CONTROL, + ao_usb_ep0_in_buf, AO_USB_CONTROL_SIZE); + + ao_usb_running = 0; + + /* Reset our internal state + */ + + ao_usb_ep0_state = AO_USB_EP0_IDLE; + + ao_usb_ep0_in_data = NULL; + ao_usb_ep0_in_len = 0; + + ao_usb_ep0_out_data = NULL; + ao_usb_ep0_out_len = 0; +} + +static void +ao_usb_set_configuration(void) +{ +#if AO_USB_HAS_INT + ao_usb_init_ep(AO_USB_INT_EP, + SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DISABLED, + NULL, 0, + SAMD21_USB_EP_EPCFG_EP_TYPE_IN_INTERRUPT, + ao_usb_int_buf, AO_USB_INT_SIZE); +#endif + +#if AO_USB_HAS_OUT + ao_usb_init_ep(AO_USB_OUT_EP, + SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_BULK, + &ao_usb_out_buf[0][0], AO_USB_OUT_SIZE, + SAMD21_USB_EP_EPCFG_EP_TYPE_IN_DUAL_BANK, + &ao_usb_out_buf[1][0], AO_USB_OUT_SIZE); + + /* At first receive, we'll flip this back to 0 */ + ao_usb_out_rx_which = 1; +#endif + +#if AO_USB_HAS_IN + /* Set up the IN end point */ + ao_usb_init_ep(AO_USB_IN_EP, + SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DUAL_BANK, + &ao_usb_in_buf[0][0], AO_USB_IN_SIZE, + SAMD21_USB_EP_EPCFG_EP_TYPE_IN_BULK, + &ao_usb_in_buf[1][0], AO_USB_IN_SIZE); + + /* First transmit data goes to buffer 0 */ + ao_usb_in_tx_which = 0; +#endif + + ao_usb_in_flushed = 1; + ao_usb_in_pending = 0; + ao_wakeup(&ao_usb_in_pending); + + ao_usb_out_avail = 0; + ao_usb_configuration = 0; + + ao_wakeup(AO_USB_OUT_SLEEP_ADDR); +} + +/* Send an IN data packet */ +static void +ao_usb_ep0_flush(void) +{ + uint8_t this_len; + + /* Check to see if the endpoint is still busy */ + if ((samd21_usb.ep[0].epstatus & (1 << (SAMD21_USB_EP_EPSTATUS_BK1RDY))) != 0) + return; + + this_len = ao_usb_ep0_in_len; + if (this_len > AO_USB_CONTROL_SIZE) + this_len = AO_USB_CONTROL_SIZE; + + if (this_len < AO_USB_CONTROL_SIZE) + ao_usb_ep0_state = AO_USB_EP0_IDLE; + + ao_usb_ep0_in_len -= this_len; + + memcpy(ao_usb_ep0_in_buf, ao_usb_ep0_in_data, this_len); + ao_usb_ep0_in_data += this_len; + + /* Mark the endpoint as TX valid to send the packet */ + samd21_usb_desc_set_byte_count(AO_USB_CONTROL_EP, 1, this_len); + samd21_usb_ep_set_ready(AO_USB_CONTROL_EP, 1); +} + +/* Read data from the ep0 OUT fifo */ +static void +ao_usb_ep0_fill(void) +{ + uint16_t len = samd21_usb_desc_get_byte_count(AO_USB_CONTROL_EP, 0); + + if (len > ao_usb_ep0_out_len) + len = ao_usb_ep0_out_len; + ao_usb_ep0_out_len -= (uint8_t) len; + + /* Pull all of the data out of the packet */ + memcpy(ao_usb_ep0_out_data, ao_usb_ep0_out_buf, len); + ao_usb_ep0_out_data += len; + + /* ACK the packet */ + samd21_usb_ep_clr_ready(AO_USB_CONTROL_EP, 0); +} + +static void +ao_usb_ep0_in_reset(void) +{ + ao_usb_ep0_in_data = ao_usb_ep0_in_tmp; + ao_usb_ep0_in_len = 0; +} + +static void +ao_usb_ep0_in_queue_byte(uint8_t a) +{ + if (ao_usb_ep0_in_len < sizeof (ao_usb_ep0_in_tmp)) + ao_usb_ep0_in_tmp[ao_usb_ep0_in_len++] = a; +} + +static void +ao_usb_ep0_in_set(const uint8_t *data, uint8_t len) +{ + ao_usb_ep0_in_data = data; + ao_usb_ep0_in_len = len; +} + +static void +ao_usb_ep0_out_set(uint8_t *data, uint8_t len) +{ + ao_usb_ep0_out_data = data; + ao_usb_ep0_out_len = len; +} + +static void +ao_usb_ep0_in_start(uint16_t max) +{ + /* Don't send more than asked for */ + if (ao_usb_ep0_in_len > max) + ao_usb_ep0_in_len = (uint8_t) max; + ao_usb_ep0_flush(); +} + +struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8}; + +#if AO_USB_DEVICE_ID_SERIAL +static uint8_t ao_usb_serial[2 + 64]; + +/* Convert a 32-bit value to 8 hexidecimal UCS2 characters */ +static void +hex_to_ucs2(uint32_t in, uint8_t *out) +{ + int i; + + for (i = 28; i >= 0; i -= 4) { + uint8_t bits = (in >> i) & 0xf; + *out++ = ((bits < 10) ? '0' : ('a' - 10)) + bits; + *out++ = 0; + } +} + +/* Encode the device ID (128 bits) in hexidecimal to use as a device + * serial number + */ +static void +ao_usb_serial_init(void) +{ + ao_usb_serial[0] = 66; /* length */ + ao_usb_serial[1] = AO_USB_DESC_STRING; + hex_to_ucs2(samd21_serial.word0, ao_usb_serial + 2 + 0); + hex_to_ucs2(samd21_serial.word1, ao_usb_serial + 2 + 16); + hex_to_ucs2(samd21_serial.word2, ao_usb_serial + 2 + 32); + hex_to_ucs2(samd21_serial.word3, ao_usb_serial + 2 + 48); +} +#endif + +/* Walk through the list of descriptors and find a match + */ +static void +ao_usb_get_descriptor(uint16_t value, uint16_t length) +{ + const uint8_t *descriptor; + uint8_t type = (uint8_t) (value >> 8); + uint8_t index = (uint8_t) value; + + descriptor = ao_usb_descriptors; + while (descriptor[0] != 0) { + if (descriptor[1] == type && index-- == 0) { + uint8_t len; + if (type == AO_USB_DESC_CONFIGURATION) + len = descriptor[2]; + else + len = descriptor[0]; +#if AO_USB_DEVICE_ID_SERIAL + /* Slightly hacky - the serial number is string 3 */ + if (type == AO_USB_DESC_STRING && (value & 0xff) == 3) { + descriptor = ao_usb_serial; + len = sizeof (ao_usb_serial); + } +#endif + if (len > length) + len = (uint8_t) length; + ao_usb_ep0_in_set(descriptor, len); + break; + } + descriptor += descriptor[0]; + } +} + +static void +ao_usb_ep0_setup(void) +{ + /* Pull the setup packet out of the fifo */ + ao_usb_ep0_out_set((uint8_t *) &ao_usb_setup, 8); + ao_usb_ep0_fill(); + if (ao_usb_ep0_out_len != 0) + return; + + if ((ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) || ao_usb_setup.length == 0) + ao_usb_ep0_state = AO_USB_EP0_DATA_IN; + else + ao_usb_ep0_state = AO_USB_EP0_DATA_OUT; + + ao_usb_ep0_in_reset(); + + switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) { + case AO_USB_TYPE_STANDARD: + switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) { + case AO_USB_RECIP_DEVICE: + switch(ao_usb_setup.request) { + case AO_USB_REQ_GET_STATUS: + ao_usb_ep0_in_queue_byte(0); + ao_usb_ep0_in_queue_byte(0); + break; + case AO_USB_REQ_SET_ADDRESS: + ao_usb_address = (uint8_t) ao_usb_setup.value; + ao_usb_address_pending = 1; + break; + case AO_USB_REQ_GET_DESCRIPTOR: + ao_usb_get_descriptor(ao_usb_setup.value, ao_usb_setup.length); + break; + case AO_USB_REQ_GET_CONFIGURATION: + ao_usb_ep0_in_queue_byte(ao_usb_configuration); + break; + case AO_USB_REQ_SET_CONFIGURATION: + ao_usb_configuration = (uint8_t) ao_usb_setup.value; + ao_usb_set_configuration(); + break; + } + break; + case AO_USB_RECIP_INTERFACE: + switch(ao_usb_setup.request) { + case AO_USB_REQ_GET_STATUS: + ao_usb_ep0_in_queue_byte(0); + ao_usb_ep0_in_queue_byte(0); + break; + case AO_USB_REQ_GET_INTERFACE: + ao_usb_ep0_in_queue_byte(0); + break; + case AO_USB_REQ_SET_INTERFACE: + break; + } + break; + case AO_USB_RECIP_ENDPOINT: + switch(ao_usb_setup.request) { + case AO_USB_REQ_GET_STATUS: + ao_usb_ep0_in_queue_byte(0); + ao_usb_ep0_in_queue_byte(0); + break; + } + break; + } + break; + case AO_USB_TYPE_CLASS: + switch (ao_usb_setup.request) { + case AO_USB_SET_LINE_CODING: + ao_usb_ep0_out_set((uint8_t *) &ao_usb_line_coding, 7); + break; + case AO_USB_GET_LINE_CODING: + ao_usb_ep0_in_set((const uint8_t *) &ao_usb_line_coding, 7); + break; + case AO_USB_SET_CONTROL_LINE_STATE: + break; + } + break; + } + + /* If we're not waiting to receive data from the host, + * queue an IN response + */ + if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN) + ao_usb_ep0_in_start(ao_usb_setup.length); +} + +static void +ao_usb_ep0_handle(uint8_t receive) +{ + ao_usb_ep0_receive = 0; + if (receive & AO_USB_EP0_GOT_SETUP) { + ao_usb_ep0_setup(); + } + if (receive & AO_USB_EP0_GOT_RX_DATA) { + if (ao_usb_ep0_state == AO_USB_EP0_DATA_OUT) { + ao_usb_ep0_fill(); + if (ao_usb_ep0_out_len == 0) { + ao_usb_ep0_state = AO_USB_EP0_DATA_IN; + ao_usb_ep0_in_start(0); + } + } + } + if (receive & AO_USB_EP0_GOT_TX_ACK) { + +#if HAS_FLIGHT && AO_USB_FORCE_IDLE + ao_flight_force_idle = 1; +#endif + if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN) + ao_usb_ep0_flush(); + /* Wait until the IN packet is received from addr 0 + * before assigning our local address + */ + if (ao_usb_address_pending) + ao_usb_set_address(ao_usb_address); + } +} + +#if AO_POWER_MANAGEMENT +static void +ao_usb_suspend(void) +{ + stm_usb.cntr |= (1 << STM_USB_CNTR_FSUSP); + ao_power_suspend(); + stm_usb.cntr |= (1 << STM_USB_CNTR_LP_MODE); + ao_clock_suspend(); +} + +static void +ao_usb_wakeup(void) +{ + ao_clock_resume(); + stm_usb.cntr &= ~(1 << STM_USB_CNTR_FSUSP); + ao_power_resume(); +} +#endif + +#if USE_USB_FIFO +static void +ao_usb_fifo_check(void) +{ + uint8_t next_which = 1 - ao_usb_out_rx_which; + if (ao_usb_out_avail & (1 << next_which)) { + uint8_t *buf = ao_usb_out_buf[next_which]; + uint16_t len = samd21_usb_desc_get_byte_count(AO_USB_OUT_EP, next_which); + + if (ao_fifo_has_space(&ao_usb_rx_fifo, len)) { + uint16_t i; + + for (i = 0; i < len; i++) + ao_fifo_insert(&ao_usb_rx_fifo, buf[i]); + samd21_usb_ep_clr_ready(AO_USB_OUT_EP, next_which); + ao_usb_out_avail &= ~(1 << next_which); + ao_usb_out_rx_which = next_which; + ao_wakeup(AO_USB_OUT_SLEEP_ADDR); + } +#if AO_USB_OUT_HOOK + ao_usb_out_hook(buf, len); +#endif + } +} +#endif + +void +samd21_usb_isr(void) +{ + uint16_t intflag = samd21_usb.intflag; + uint16_t epintsmry = samd21_usb.epintsmry; + + samd21_usb.intflag = intflag; + + if (epintsmry & (1 << 0)) { + uint8_t epintflag = samd21_usb.ep[0].epintflag; + samd21_usb.ep[0].epintflag = epintflag; + + if (epintflag & (1 << SAMD21_USB_EP_EPINTFLAG_RXSTP)) + ao_usb_ep0_receive |= AO_USB_EP0_GOT_SETUP; + else if (epintflag & (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT0)) + ao_usb_ep0_receive |= AO_USB_EP0_GOT_RX_DATA; + if (epintflag & (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT1)) + ao_usb_ep0_receive |= AO_USB_EP0_GOT_TX_ACK; + ao_usb_ep0_handle(ao_usb_ep0_receive); + } +#if AO_USB_HAS_OUT + if (epintsmry & (1 << AO_USB_OUT_EP)) { + uint8_t epintflag = samd21_usb.ep[AO_USB_OUT_EP].epintflag; + samd21_usb.ep[AO_USB_OUT_EP].epintflag = epintflag; + uint8_t avail = (epintflag >> SAMD21_USB_EP_EPINTFLAG_TRCPT0) & 3; + if (avail) { + ao_usb_out_avail |= avail; + ao_usb_running = 1; +#if USE_USB_FIFO + ao_usb_fifo_check(); +#else + ao_wakeup(AO_USB_OUT_SLEEP_ADDR); +#endif + } + } +#endif +#if AO_USB_HAS_IN + if (epintsmry & (1 << AO_USB_IN_EP)) { + uint8_t epintflag = samd21_usb.ep[AO_USB_IN_EP].epintflag; + samd21_usb.ep[AO_USB_IN_EP].epintflag = epintflag; + uint8_t done = (epintflag >> SAMD21_USB_EP_EPINTFLAG_TRCPT0) & 3; + if (done) { + ao_usb_in_pending &= (uint8_t) ~done; + ao_wakeup(&ao_usb_in_pending); + } + } +#endif +#if AO_USB_HAS_INT + if (epintsmry & (1 << AO_USB_INT_EP)) { + } +#endif + if (intflag & (1 << SAMD21_USB_INTFLAG_EORST)) { + ao_usb_set_ep0(); + } +#if AO_POWER_MANAGEMENT + if (istr & (1 << STM_USB_ISTR_SUSP)) + ao_usb_suspend(); + + if (istr & (1 << STM_USB_ISTR_WKUP)) + ao_usb_wakeup(); +#endif +} + +#if AO_USB_HAS_IN +/* Queue the current IN buffer for transmission */ +static void +_ao_usb_in_send(void) +{ + ao_usb_in_pending |= (uint8_t) (1 << ao_usb_in_tx_which); + if (ao_usb_tx_count != AO_USB_IN_SIZE) + ao_usb_in_flushed = 1; + + samd21_usb_desc_set_byte_count(AO_USB_IN_EP, ao_usb_in_tx_which, ao_usb_tx_count); + samd21_usb_ep_set_ready(AO_USB_IN_EP, ao_usb_in_tx_which); + + /* Toggle our usage */ + ao_usb_in_tx_which = 1 - ao_usb_in_tx_which; + ao_usb_tx_count = 0; +} + +/* Wait for a free IN buffer. Interrupts are blocked */ +static void +_ao_usb_in_wait(void) +{ + /* Wait for an IN buffer to be ready */ + while ((ao_usb_in_pending & (1 << ao_usb_in_tx_which)) != 0) + ao_sleep(&ao_usb_in_pending); +} + +void +ao_usb_flush(void) +{ + if (!ao_usb_running) + return; + + /* Anytime we've sent a character since + * the last time we flushed, we'll need + * to send a packet -- the only other time + * we would send a packet is when that + * packet was full, in which case we now + * want to send an empty packet + */ + ao_arch_block_interrupts(); + if (!ao_usb_in_flushed) { + _ao_usb_in_wait(); + if (!ao_usb_in_flushed) + _ao_usb_in_send(); + } + ao_arch_release_interrupts(); +} + +void +ao_usb_putchar(char c) +{ + if (!ao_usb_running) + return; + + ao_arch_block_interrupts(); + _ao_usb_in_wait(); + + ao_usb_in_flushed = 0; + ao_usb_in_buf[ao_usb_in_tx_which][ao_usb_tx_count++] = c; + + /* Send the packet when full */ + if (ao_usb_tx_count == AO_USB_IN_SIZE) + _ao_usb_in_send(); + + ao_arch_release_interrupts(); + if (c == '\n') + ao_usb_flush(); +} +#endif + +#if AO_USB_HAS_OUT +#if !USE_USB_FIFO +static bool +_ao_usb_out_recv(void) +{ + uint8_t next_which = 1 - ao_usb_out_rx_which; + + if ((ao_usb_out_avail & (1 << next_which)) != 0) { + /* switch current buffer */ + ao_usb_out_rx_which = next_which; + ao_usb_out_avail &= (uint8_t) ~(1 << ao_usb_out_rx_which); + + ao_usb_rx_count = (uint8_t) samd21_usb_desc_get_byte_count(AO_USB_OUT_EP, ao_usb_out_rx_which); + ao_usb_rx_pos = 0; + return true; + } + return false; +} +#endif + +static int +_ao_usb_pollchar(void) +{ + uint8_t c; + + if (!ao_usb_running) + return AO_READ_AGAIN; + +#if USE_USB_FIFO + if (ao_fifo_empty(&ao_usb_rx_fifo)) + return AO_READ_AGAIN; + c = ao_fifo_remove(&ao_usb_rx_fifo); + ao_usb_fifo_check(); +#else + for (;;) { + if (ao_usb_rx_pos != ao_usb_rx_count) + break; + + /* Check for packet */ + if (!_ao_usb_out_recv()) + return AO_READ_AGAIN; + } + + /* Pull a character out of the fifo */ + c = ao_usb_out_buf[ao_usb_out_rx_which][ao_usb_rx_pos++]; + + if (ao_usb_rx_pos == ao_usb_rx_count) + samd21_usb_ep_clr_ready(AO_USB_OUT_EP, ao_usb_out_rx_which); +#endif + + return c; +} + +char +ao_usb_getchar(void) +{ + int c; + + ao_arch_block_interrupts(); + while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN) + ao_sleep(AO_USB_OUT_SLEEP_ADDR); + ao_arch_release_interrupts(); + return (char) c; +} + +#endif + +void +ao_usb_disable(void) +{ + ao_arch_block_interrupts(); + samd21_nvic_clear_enable(SAMD21_NVIC_ISR_USB_POS); + + /* Disable USB pull-up */ + samd21_usb.ctrlb |= (1 << SAMD21_USB_CTRLB_DETACH); + + /* Switch off the device */ + samd21_usb.ctrla &= (uint8_t) ~(1 << SAMD21_USB_CTRLA_ENABLE); + + /* Disable the interface */ + samd21_pm.apbbmask &= ~(1UL << SAMD21_PM_APBBMASK_USB); + ao_arch_release_interrupts(); +} + +void +ao_usb_enable(void) +{ + int t; + + ao_enable_port(&samd21_port_a); + + /* Set up USB DM/DP pins */ + samd21_port_pmux_set(&samd21_port_a, 24, SAMD21_PORT_PMUX_FUNC_G); + samd21_port_pmux_set(&samd21_port_a, 25, SAMD21_PORT_PMUX_FUNC_G); + + /* Assign gclk 0 to USB reference */ + samd21_gclk_clkctrl(AO_GCLK_USB, SAMD21_GCLK_CLKCTRL_ID_USB); + + /* Enable USB clock */ + samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_USB); + + /* Reset USB Device */ + + samd21_usb.ctrla |= (1 << SAMD21_USB_CTRLA_SWRST); + + while ((samd21_usb.ctrla & (1 << SAMD21_USB_CTRLA_SWRST)) != 0) + ; + + while ((samd21_usb.syncbusy & (1 << SAMD21_USB_SYNCBUSY_SWRST)) != 0) + ; + + memset(&samd21_usb_desc, 0, sizeof (samd21_usb_desc)); + + /* Detach */ + samd21_usb.ctrlb |= (1 << SAMD21_USB_CTRLB_DETACH); + + samd21_usb.descadd = (uint32_t) &samd21_usb_desc; + + /* Load calibration values */ + uint32_t transn = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_USB_TRANSN) & + SAMD21_AUX1_CALIBRATION_USB_TRANSN_MASK); + if (transn == 0x1f) + transn = 5; + uint32_t transp = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_USB_TRANSP) & + SAMD21_AUX1_CALIBRATION_USB_TRANSP_MASK); + if (transp == 0x1f) + transp = 29; + uint32_t trim = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_USB_TRIM) & + SAMD21_AUX1_CALIBRATION_USB_TRIM_MASK); + if (trim == 7) + trim = 3; + + samd21_usb.padcal = (uint16_t) ((transn << SAMD21_USB_PADCAL_TRANSN) | + (transp << SAMD21_USB_PADCAL_TRANSP) | + (trim << SAMD21_USB_PADCAL_TRIM)); + + samd21_usb.qosctrl = ((3 << SAMD21_USB_QOSCTRL_CQOS) | + (3 << SAMD21_USB_QOSCTRL_DQOS)); + + ao_arch_block_interrupts(); + + /* set full speed */ + samd21_usb.ctrlb = (SAMD21_USB_CTRLB_SPDCONF_FS << SAMD21_USB_CTRLB_SPDCONF); + + /* Set device mode */ + samd21_usb.ctrla = ((0 << SAMD21_USB_CTRLA_MODE) | + (1 << SAMD21_USB_CTRLA_RUNSTDBY) | + (1 << SAMD21_USB_CTRLA_ENABLE)); + + while ((samd21_usb.syncbusy & (1 << SAMD21_USB_SYNCBUSY_ENABLE)) != 0) + ; + + /* configure interrupts */ + samd21_nvic_set_enable(SAMD21_NVIC_ISR_USB_POS); + samd21_nvic_set_priority(SAMD21_NVIC_ISR_USB_POS, 2); + + samd21_usb.intenset = ((1 << SAMD21_USB_INTFLAG_EORST)); + + ao_arch_release_interrupts(); + + for (t = 0; t < 50000; t++) + ao_arch_nop(); + + /* Attach */ + samd21_usb.ctrlb &= (uint16_t) ~(1 << SAMD21_USB_CTRLB_DETACH); +} + +void +ao_usb_init(void) +{ + ao_usb_enable(); + +#if AO_USB_DEVICE_ID_SERIAL + ao_usb_serial_init(); +#endif + + ao_usb_ep0_state = AO_USB_EP0_IDLE; + +#if USE_USB_STDIO + ao_add_stdio(_ao_usb_pollchar, ao_usb_putchar, ao_usb_flush); +#endif +} diff --git a/src/samd21/registers.ld b/src/samd21/registers.ld new file mode 100644 index 00000000..4ff885b5 --- /dev/null +++ b/src/samd21/registers.ld @@ -0,0 +1,53 @@ +samd21_scs = 0xe000e000; +samd21_systick = 0xe000e010; +samd21_nvic = 0xe000e100; +samd21_scb = 0xe000ed00; + +samd21_ahb_apb_a = 0x40000000; +samd21_pac0 = 0x40000000; +samd21_pm = 0x40000400; +samd21_sysctrl = 0x40000800; +samd21_gclk = 0x40000c00; +samd21_wdt = 0x40001000; +samd21_rtc = 0x40001400; +samd21_rtc = 0x40001400; +samd21_eic = 0x40001800; + +samd21_ahb_apb_b = 0x41000000; +samd21_pac1 = 0x41000000; +samd21_dsu = 0x41002000; +samd21_nvmctrl = 0x41004000; +samd21_port_a = 0x41004400; +samd21_port_b = 0x41004480; +samd21_dmac = 0x41004800; +samd21_usb = 0x41005000; +samd21_mtb = 0x41006000; + +samd21_ahb_apb_c = 0x42000000; +samd21_pac2 = 0x42000000; +samd21_evsys = 0x42000400; +samd21_sercom0 = 0x42000800; +samd21_sercom1 = 0x42000c00; +samd21_sercom2 = 0x42001000; +samd21_sercom3 = 0x42001400; +samd21_sercom4 = 0x42001800; +samd21_sercom5 = 0x42001c00; +samd21_tcc0 = 0x42002000; +samd21_tcc1 = 0x42002400; +samd21_tcc2 = 0x42002800; +samd21_tc3 = 0x42002c00; +samd21_tc4 = 0x42003000; +samd21_tc5 = 0x42003400; +samd21_tc6 = 0x42003800; +samd21_tc7 = 0x42003c00; +samd21_adc = 0x42004000; +samd21_ac = 0x42004400; +samd21_dac = 0x42004800; +samd21_ptc = 0x42004c00; +samd21_i2s = 0x42005000; +samd21_ac1 = 0x42005400; +samd21_tcc3 = 0x42006000; + +samd21_aux0 = 0x00804000; +samd21_aux1 = 0x00806000; +samd21_serial = 0x0080a000; diff --git a/src/samd21/samd21.h b/src/samd21/samd21.h new file mode 100644 index 00000000..b101c6b9 --- /dev/null +++ b/src/samd21/samd21.h @@ -0,0 +1,2102 @@ +/* + * Copyright © 2019 Keith Packard + * + * 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 + +typedef volatile uint64_t vuint64_t; +typedef volatile uint32_t vuint32_t; +typedef volatile void * vvoid_t; +typedef volatile uint16_t vuint16_t; +typedef volatile uint8_t vuint8_t; + +struct samd21_pac { + vuint32_t wpclr; + vuint32_t wpset; +}; + +extern struct samd21_pac samd21_pac0; +extern struct samd21_pac samd21_pac1; +extern struct samd21_pac samd21_pac2; + +#define samd21_pac0 (*(struct samd21_pac *) 0x40000000) +#define samd21_pac1 (*(struct samd21_pac *) 0x41000000) +#define samd21_pac2 (*(struct samd21_pac *) 0x42000000) + +struct samd21_gclk { + vuint8_t ctrl; + vuint8_t status; + vuint16_t clkctrl; + vuint32_t genctrl; + vuint32_t gendiv; +}; + +extern struct samd21_gclk samd21_gclk; + +#define samd21_gclk (*(struct samd21_gclk *) 0x40000c00) + +#define SAMD21_GCLK_CTRL_SWRST 0 + +#define SAMD21_GCLK_STATUS_SYNCBUSY 7 + +#define SAMD21_GCLK_CLKCTRL_ID 0 +#define SAMD21_GCLK_CLKCTRL_ID_DFLL48M_REF 0 +#define SAMD21_GCLK_CLKCTRL_ID_DPLL 1 +#define SAMD21_GCLK_CLKCTRL_ID_DPLL_32K 2 +#define SAMD21_GCLK_CLKCTRL_ID_WDT 3 +#define SAMD21_GCLK_CLKCTRL_ID_RTC 4 +#define SAMD21_GCLK_CLKCTRL_ID_EIC 5 +#define SAMD21_GCLK_CLKCTRL_ID_USB 6 +#define SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_0 0x07 +#define SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_1 0x08 +#define SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_2 0x09 +#define SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_3 0x0a +#define SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_4 0x0b +#define SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_5 0x0c +#define SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_6 0x0d +#define SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_7 0x0e +#define SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_8 0e0f +#define SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_9 0x10 +#define SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_10 0x11 +#define SAMD21_GCLK_CLKCTRL_ID_EVSYS_CHANNEL_11 0x12 +#define SAMD21_GCLK_CLKCTRL_ID_SERCOMx_SLOW 0x13 +#define SAMD21_GCLK_CLKCTRL_ID_SERCOM0_CORE 0x14 +#define SAMD21_GCLK_CLKCTRL_ID_SERCOM1_CORE 0x15 +#define SAMD21_GCLK_CLKCTRL_ID_SERCOM2_CORE 0x16 +#define SAMD21_GCLK_CLKCTRL_ID_SERCOM3_CORE 0x17 +#define SAMD21_GCLK_CLKCTRL_ID_SERCOM4_CORE 0x18 +#define SAMD21_GCLK_CLKCTRL_ID_SERCOM5_CORE 0x19 +#define SAMD21_GCLK_CLKCTRL_ID_TCC0_TCC1 0x1a +#define SAMD21_GCLK_CLKCTRL_ID_TCC2_TC3 0x1b +#define SAMD21_GCLK_CLKCTRL_ID_TC4_TC5 0x1c +#define SAMD21_GCLK_CLKCTRL_ID_TC6_TC7 0x1d +#define SAMD21_GCLK_CLKCTRL_ID_ADC 0x1e +#define SAMD21_GCLK_CLKCTRL_ID_AC_DIG 0x1f +#define SAMD21_GCLK_CLKCTRL_ID_AC_ANA 0x20 +#define SAMD21_GCLK_CLKCTRL_ID_DAC 0x21 +#define SAMD21_GCLK_CLKCTRL_ID_PTC 0x22 +#define SAMD21_GCLK_CLKCTRL_ID_I2S_0 0x23 +#define SAMD21_GCLK_CLKCTRL_ID_I2S_1 0x24 +#define SAMD21_GCLK_CLKCTRL_ID_TCC3 0x25 + +#define SAMD21_GCLK_CLKCTRL_GEN 8 +#define SAMD21_GCLK_CLKCTRL_CLKEN 14 +#define SAMD21_GCLK_CLKCTRL_WRTLOCK 15 + +#define SAMD21_GCLK_GENCTRL_ID 0 +#define SAMD21_GCLK_GENCTRL_SRC 8 +#define SAMD21_GCLK_GENCTRL_SRC_XOSC 0 +#define SAMD21_GCLK_GENCTRL_SRC_GCLKIN 1 +#define SAMD21_GCLK_GENCTRL_SRC_GCLKGEN1 2 +#define SAMD21_GCLK_GENCTRL_SRC_OSCULP32K 3 +#define SAMD21_GCLK_GENCTRL_SRC_OSC32K 4 +#define SAMD21_GCLK_GENCTRL_SRC_XOSC32K 5 +#define SAMD21_GCLK_GENCTRL_SRC_OSC8M 6 +#define SAMD21_GCLK_GENCTRL_SRC_DFLL48M 7 +#define SAMD21_GCLK_GENCTRL_SRC_FDPLL96M 8 + +#define SAMD21_GCLK_GENCTRL_GENEN 16 +#define SAMD21_GCLK_GENCTRL_IDC 17 +#define SAMD21_GCLK_GENCTRL_OOV 18 +#define SAMD21_GCLK_GENCTRL_OE 19 +#define SAMD21_GCLK_GENCTRL_DIVSEL 20 +#define SAMD21_GCLK_GENCTRL_RUNSTDBY 21 + +#define SAMD21_GCLK_GENDIV_ID 0 +#define SAMD21_GCLK_GENDIV_DIV 8 + +struct samd21_pm { + vuint8_t ctrl; + vuint8_t sleep; + vuint8_t reserved_02; + vuint8_t reserved_03; + vuint32_t reserved_04; + vuint8_t cpusel; + vuint8_t apbasel; + vuint8_t apbbsel; + vuint8_t apbcsel; + vuint32_t reserved_0c; + + vuint32_t reserved_10; + vuint32_t ahbmask; + vuint32_t apbamask; + vuint32_t apbbmask; + + vuint32_t apbcmask; + vuint32_t reserved_24; + vuint32_t reserved_28; + vuint32_t reserved_2c; + + vuint32_t reserved_30; + vuint8_t intenclr; + vuint8_t intelset; + vuint8_t intflag; + vuint8_t reserved_37; + vuint8_t rcause; +}; + +extern struct samd21_pm samd21_pm; + +#define samd21_pm (*(struct samd21_pm *) 0x40000400) + +#define SAMD21_PM_CPUSEL_CPUDIV 0 +#define SAMD21_PM_APBASEL_APBADIV 0 +#define SAMD21_PM_APBBSEL_APBBDIV 0 +#define SAMD21_PM_APBCSEL_APBCDIV 0 + +#define SAMD21_PM_APBAMASK_PAC0 0 +#define SAMD21_PM_APBAMASK_PM 1 +#define SAMD21_PM_APBAMASK_SYSCTRL 2 +#define SAMD21_PM_APBAMASK_GCLK 3 +#define SAMD21_PM_APBAMASK_WDT 4 +#define SAMD21_PM_APBAMASK_RTC 5 +#define SAMD21_PM_APBAMASK_EIC 6 + +#define SAMD21_PM_AHBMASK_HPB0 0 +#define SAMD21_PM_AHBMASK_HPB1 1 +#define SAMD21_PM_AHBMASK_HPB2 2 +#define SAMD21_PM_AHBMASK_DSU 3 +#define SAMD21_PM_AHBMASK_NVMCTRL 4 +#define SAMD21_PM_AHBMASK_DMAC 5 +#define SAMD21_PM_AHBMASK_USB 6 + +#define SAMD21_PM_APBBMASK_PAC1 0 +#define SAMD21_PM_APBBMASK_DSU 1 +#define SAMD21_PM_APBBMASK_NVMCTRL 2 +#define SAMD21_PM_APBBMASK_PORT 3 +#define SAMD21_PM_APBBMASK_DMAC 4 +#define SAMD21_PM_APBBMASK_USB 5 + +#define SAMD21_PM_APBCMASK_PAC2 0 +#define SAMD21_PM_APBCMASK_EVSYS 1 +#define SAMD21_PM_APBCMASK_SERCOM0 2 +#define SAMD21_PM_APBCMASK_SERCOM1 3 +#define SAMD21_PM_APBCMASK_SERCOM2 4 +#define SAMD21_PM_APBCMASK_SERCOM3 5 +#define SAMD21_PM_APBCMASK_SERCOM4 6 +#define SAMD21_PM_APBCMASK_SERCOM5 7 +#define SAMD21_PM_APBCMASK_TCC0 8 +#define SAMD21_PM_APBCMASK_TCC1 9 +#define SAMD21_PM_APBCMASK_TCC2 10 +#define SAMD21_PM_APBCMASK_TC3 11 +#define SAMD21_PM_APBCMASK_TC4 12 +#define SAMD21_PM_APBCMASK_TC5 13 +#define SAMD21_PM_APBCMASK_TC6 14 +#define SAMD21_PM_APBCMASK_TC7 15 +#define SAMD21_PM_APBCMASK_ADC 16 +#define SAMD21_PM_APBCMASK_AC 17 +#define SAMD21_PM_APBCMASK_DAC 18 +#define SAMD21_PM_APBCMASK_PTC 19 +#define SAMD21_PM_APBCMASK_I2S 20 +#define SAMD21_PM_APBCMASK_AC1 21 +#define SAMD21_PM_APBCMASK_TCC3 24 + +struct samd21_sysctrl { + vuint32_t intenclr; + vuint32_t intenset; + vuint32_t intflag; + vuint32_t pclksr; + + vuint32_t xosc; + vuint32_t xosc32k; + vuint32_t osc32k; + vuint32_t osculp32k; + + vuint32_t osc8m; + vuint32_t dfllctrl; + vuint32_t dfllval; + vuint32_t dfllmul; + + vuint32_t dfllsync; + vuint32_t bod33; + vuint32_t reserved_38; + vuint32_t vreg; + + vuint32_t vref; + vuint32_t dpllctrla; + vuint32_t dpllratio; + vuint32_t dpllctrlb; + + vuint32_t dpllstatus; +}; + +extern struct samd21_sysctrl samd21_sysctrl; + +#define samd21_sysctrl (*(struct samd21_sysctrl *) 0x40000800) + +#define SAMD21_SYSCTRL_PCLKSR_XOSCRDY 0 +#define SAMD21_SYSCTRL_PCLKSR_XOSC32KRDY 1 +#define SAMD21_SYSCTRL_PCLKSR_OSC32KRDY 2 +#define SAMD21_SYSCTRL_PCLKSR_OSC8MRDY 3 +#define SAMD21_SYSCTRL_PCLKSR_DFLLRDY 4 +#define SAMD21_SYSCTRL_PCLKSR_DFLLOOB 5 +#define SAMD21_SYSCTRL_PCLKSR_DFLLLCKF 6 +#define SAMD21_SYSCTRL_PCLKSR_DFLLLCKC 7 +#define SAMD21_SYSCTRL_PCLKSR_DFLLRCS 8 +#define SAMD21_SYSCTRL_PCLKSR_BOD33RDY 9 +#define SAMD21_SYSCTRL_PCLKSR_BOD33DET 10 +#define SAMD21_SYSCTRL_PCLKSR_B33SRDY 11 +#define SAMD21_SYSCTRL_PCLKSR_DBPLLLCKR 15 +#define SAMD21_SYSCTRL_PCLKSR_DPLLLCKF 16 +#define SAMD21_SYSCTRL_PCLKSR_DPLLTO 17 + +#define SAMD21_SYSCTRL_XOSC_ENABLE 1 +#define SAMD21_SYSCTRL_XOSC_XTALEN 2 +#define SAMD21_SYSCTRL_XOSC_RUNSTDBY 6 +#define SAMD21_SYSCTRL_XOSC_ONDEMAND 7 +#define SAMD21_SYSCTRL_XOSC_GAIN 8 +#define SAMD21_SYSCTRL_XOSC_GAIN_2MHz 0 +#define SAMD21_SYSCTRL_XOSC_GAIN_4MHz 1 +#define SAMD21_SYSCTRL_XOSC_GAIN_8MHz 2 +#define SAMD21_SYSCTRL_XOSC_GAIN_16MHz 3 +#define SAMD21_SYSCTRL_XOSC_GAIN_30MHz 4 +#define SAMD21_SYSCTRL_XOSC_AMPGC 11 +#define SAMD21_SYSCTRL_XOSC_STARTUP 12 +#define SAMD21_SYSCTRL_XOSC_STARTUP_1 0 +#define SAMD21_SYSCTRL_XOSC_STARTUP_2 1 +#define SAMD21_SYSCTRL_XOSC_STARTUP_4 2 +#define SAMD21_SYSCTRL_XOSC_STARTUP_8 3 +#define SAMD21_SYSCTRL_XOSC_STARTUP_16 4 +#define SAMD21_SYSCTRL_XOSC_STARTUP_32 5 +#define SAMD21_SYSCTRL_XOSC_STARTUP_64 6 +#define SAMD21_SYSCTRL_XOSC_STARTUP_128 7 +#define SAMD21_SYSCTRL_XOSC_STARTUP_256 8 +#define SAMD21_SYSCTRL_XOSC_STARTUP_512 9 +#define SAMD21_SYSCTRL_XOSC_STARTUP_1024 10 +#define SAMD21_SYSCTRL_XOSC_STARTUP_2048 11 +#define SAMD21_SYSCTRL_XOSC_STARTUP_4096 12 +#define SAMD21_SYSCTRL_XOSC_STARTUP_8192 13 +#define SAMD21_SYSCTRL_XOSC_STARTUP_16384 14 +#define SAMD21_SYSCTRL_XOSC_STARTUP_32768 15 + +#define SAMD21_SYSCTRL_XOSC32K_ENABLE 1 +#define SAMD21_SYSCTRL_XOSC32K_XTALEN 2 +#define SAMD21_SYSCTRL_XOSC32K_EN32K 3 +#define SAMD21_SYSCTRL_XOSC32K_AAMPEN 5 +#define SAMD21_SYSCTRL_XOSC32K_RUNSTDBY 6 +#define SAMD21_SYSCTRL_XOSC32K_ONDEMAND 7 +#define SAMD21_SYSCTRL_XOSC32K_STARTUP 8 +#define SAMD21_SYSCTRL_XOSC32K_WRTLOCK 12 + +#define SAMD21_SYSCTRL_OSC8M_ENABLE 1 +#define SAMD21_SYSCTRL_OSC8M_RUNSTDBY 6 +#define SAMD21_SYSCTRL_OSC8M_ONDEMAND 7 +#define SAMD21_SYSCTRL_OSC8M_PRESC 8 +#define SAMD21_SYSCTRL_OSC8M_PRESC_1 0 +#define SAMD21_SYSCTRL_OSC8M_PRESC_2 1 +#define SAMD21_SYSCTRL_OSC8M_PRESC_4 2 +#define SAMD21_SYSCTRL_OSC8M_PRESC_8 3 +#define SAMD21_SYSCTRL_OSC8M_PRESC_MASK 3 +#define SAMD21_SYSCTRL_OSC8M_CALIB 16 +#define SAMD21_SYSCTRL_OSC8M_FRANGE 30 +#define SAMD21_SYSCTRL_OSC8M_FRANGE_4_6 0 +#define SAMD21_SYSCTRL_OSC8M_FRANGE_6_8 1 +#define SAMD21_SYSCTRL_OSC8M_FRANGE_8_11 2 +#define SAMD21_SYSCTRL_OSC8M_FRANGE_11_15 3 + +#define SAMD21_SYSCTRL_DFLLCTRL_ENABLE 1 +#define SAMD21_SYSCTRL_DFLLCTRL_MODE 2 +#define SAMD21_SYSCTRL_DFLLCTRL_STABLE 3 +#define SAMD21_SYSCTRL_DFLLCTRL_LLAW 4 +#define SAMD21_SYSCTRL_DFLLCTRL_USBCRM 5 +#define SAMD21_SYSCTRL_DFLLCTRL_RUNSTDBY 6 +#define SAMD21_SYSCTRL_DFLLCTRL_ONDEMAND 7 +#define SAMD21_SYSCTRL_DFLLCTRL_CCDIS 8 +#define SAMD21_SYSCTRL_DFLLCTRL_QLDIS 9 +#define SAMD21_SYSCTRL_DFLLCTRL_BPLCKC 10 +#define SAMD21_SYSCTRL_DFLLCTRL_WAITLOCK 11 + +#define SAMD21_SYSCTRL_DFLLVAL_FINE 0 +#define SAMD21_SYSCTRL_DFLLVAL_COARSE 10 +#define SAMD21_SYSCTRL_DFLLVAL_DIFF 16 + +#define SAMD21_SYSCTRL_DFLLMUL_MUL 0 +#define SAMD21_SYSCTRL_DFLLMUL_FSTEP 16 +#define SAMD21_SYSCTRL_DFLLMUL_CSTEP 26 + +#define SAMD21_SYSCTRL_DFLLSYNC_READREQ 7 + +#define SAMD21_SYSCTRL_DPLLCTRLA_ENABLE 1 +#define SAMD21_SYSCTRL_DPLLCTRLA_RUNSTDBY 6 +#define SAMD21_SYSCTRL_DPLLCTRLA_ONDEMAND 7 + +#define SAMD21_SYSCTRL_DPLLRATIO_LDR 0 +#define SAMD21_SYSCTRL_DPLLRATIO_LDRFRAC 0 + +#define SAMD21_SYSCTRL_DPLLCTRLB_FILTER 0 +#define SAMD21_SYSCTRL_DPLLCTRLB_FILTER_DEFAULT 0 +#define SAMD21_SYSCTRL_DPLLCTRLB_FILTER_LBFILT 1 +#define SAMD21_SYSCTRL_DPLLCTRLB_FILTER_HBFILT 2 +#define SAMD21_SYSCTRL_DPLLCTRLB_FILTER_HDFILT 3 +#define SAMD21_SYSCTRL_DPLLCTRLB_LPEN 2 +#define SAMD21_SYSCTRL_DPLLCTRLB_WUF 3 +#define SAMD21_SYSCTRL_DPLLCTRLB_REFCLK 4 +#define SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_XOSC32 0 +#define SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_XOSC 1 +#define SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_GCLK_DPLL 2 +#define SAMD21_SYSCTRL_DPLLCTRLB_LTIME 8 +#define SAMD21_SYSCTRL_DPLLCTRLB_LTIME_DEFAULT 0 +#define SAMD21_SYSCTRL_DPLLCTRLB_LTIME_8MS 4 +#define SAMD21_SYSCTRL_DPLLCTRLB_LTIME_9MS 5 +#define SAMD21_SYSCTRL_DPLLCTRLB_LTIME_10MS 6 +#define SAMD21_SYSCTRL_DPLLCTRLB_LTIME_11MS 7 +#define SAMD21_SYSCTRL_DPLLCTRLB_LBYPASS 12 +#define SAMD21_SYSCTRL_DPLLCTRLB_DIV 16 + +#define SAMD21_SYSCTRL_DPLLSTATUS_LOCK 0 +#define SAMD21_SYSCTRL_DPLLSTATUS_CLKRDY 1 +#define SAMD21_SYSCTRL_DPLLSTATUS_ENABLE 2 +#define SAMD21_SYSCTRL_DPLLSTATUS_DIV 3 + +struct samd21_dmac { + vuint16_t ctrl; + vuint16_t crcctrl; + vuint32_t crcdatain; + vuint32_t crcchksum; + vuint8_t crcstatus; + vuint8_t dbgctrl; + vuint8_t qosctrl; + uint8_t reserved_0f; + + vuint32_t swtrigctrl; + vuint32_t prictrl0; + uint32_t reserved_18; + uint32_t reserved_1c; + + vuint16_t intpend; + uint16_t reserved_22; + vuint32_t intstatus; + vuint32_t busych; + vuint32_t pendch; + + vuint32_t active; + vuint32_t baseaddr; + vuint32_t wrbaddr; + uint16_t reserved_3c; + uint8_t reserved_3e; + vuint8_t chid; + + vuint8_t chctrla; + uint8_t reserved_41; + uint16_t reserved_42; + vuint32_t chctrlb; + uint32_t reserved_48; + vuint8_t chintenclr; + vuint8_t chintenset; + vuint8_t chintflag; + vuint8_t chstatus; +}; + +extern struct samd21_dmac samd21_dmac; + +#define samd21_dmac (*(struct samd21_dmac *) 0x41004800) + +struct samd21_dmac_desc { + vuint16_t btctrl; + vuint16_t btcnt; + vuint32_t srcaddr; + vuint32_t dstaddr; + vuint32_t descaddr; +} __attribute__((aligned(8))); + +#define SAMD21_DMAC_NCHAN 12 + +#define SAMD21_DMAC_CTRL_SWRST 0 +#define SAMD21_DMAC_CTRL_DMAENABLE 1 +#define SAMD21_DMAC_CTRL_CRCENABLE 2 +#define SAMD21_DMAC_CTRL_LVLEN(x) (8 + (x)) + +#define SAMD21_DMAC_QOSCTRL_WRBQOS 0 +#define SAMD21_DMAC_QOSCTRL_FQOS 2 +#define SAMD21_DMAC_QOSCTRL_DQOS 4 + +#define SAMD21_DMAC_QOSCTRL_DISABLE 0 +#define SAMD21_DMAC_QOSCTRL_LOW 1 +#define SAMD21_DMAC_QOSCTRL_MEDIUM 2 +#define SAMD21_DMAC_QOSCTRL_HIGH 3 + +#define SAMD21_DMAC_SWTRIGCTRL_SWTRIG(n) (0 + (n)) + +#define SAMD21_DMAC_PRICTRL0_LVLPRI0 0 +#define SAMD21_DMAC_PRICTRL0_RRLVLEN0 7 +#define SAMD21_DMAC_PRICTRL0_LVLPRI1 8 +#define SAMD21_DMAC_PRICTRL0_RRLVLEN1 15 +#define SAMD21_DMAC_PRICTRL0_LVLPRI2 16 +#define SAMD21_DMAC_PRICTRL0_RRLVLEN2 23 +#define SAMD21_DMAC_PRICTRL0_LVLPRI3 24 +#define SAMD21_DMAC_PRICTRL0_RRLVLEN3 31 + +#define SAMD21_DMAC_INTPEND_ID 0 +#define SAMD21_DMAC_INTPEND_ID_MASK 0xf +#define SAMD21_DMAC_INTPEND_TERR 8 +#define SAMD21_DMAC_INTPEND_TCMPL 9 +#define SAMD21_DMAC_INTPEND_SUSP 10 +#define SAMD21_DMAC_INTPEND_FERR 13 +#define SAMD21_DMAC_INTPEND_BUSY 14 +#define SAMD21_DMAC_INTPEND_PEND 15 + +#define SAMD21_DMAC_INTSTATUS_CHINT(n) (0 + (n)) + +#define SAMD21_DMAC_BUSYCH_BUSYCH(n) (0 + (n)) + +#define SAMD21_DMAC_PENDCH_PENDCH(n) (0 + (n)) + +#define SAMD21_DMAC_ACTIVE_LVLEX(x) (0 + (x)) +#define SAMD21_DMAC_ACTIVE_ID 8 +#define SAMD21_DMAC_ACTIVE_ABUSY 15 +#define SAMD21_DMAC_ACTIVE_BTCNT 16 + +#define SAMD21_DMAC_CHCTRLA_SWRST 0 +#define SAMD21_DMAC_CHCTRLA_ENABLE 1 + +#define SAMD21_DMAC_CHCTRLB_EVACT 0 +#define SAMD21_DMAC_CHCTRLB_EVACT_NOACT 0 +#define SAMD21_DMAC_CHCTRLB_EVACT_TRIG 1 +#define SAMD21_DMAC_CHCTRLB_EVACT_CTRIG 2 +#define SAMD21_DMAC_CHCTRLB_EVACT_CBLOCK 3 +#define SAMD21_DMAC_CHCTRLB_EVACT_SUSPEND 4 +#define SAMD21_DMAC_CHCTRLB_EVACT_RESUME 5 +#define SAMD21_DMAC_CHCTRLB_EVACT_SSKIP 6 + +#define SAMD21_DMAC_CHCTRLB_EVIE 3 +#define SAMD21_DMAC_CHCTRLB_EVOE 4 +#define SAMD21_DMAC_CHCTRLB_LVL 5 +#define SAMD21_DMAC_CHCTRLB_LVL_LVL0 0UL +#define SAMD21_DMAC_CHCTRLB_LVL_LVL1 1UL +#define SAMD21_DMAC_CHCTRLB_LVL_LVL2 2UL +#define SAMD21_DMAC_CHCTRLB_LVL_LVL3 3UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC 8 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_DISABLE 0x00UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_RX(n) (0x01UL + (n) * 2UL) +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_TX(n) (0x02UL + (n) * 2UL) +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_OVF 0x0dUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC0 0x0eUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC1 0x0fUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC2 0x10UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC3 0x11UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_OVF 0x12UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_MC0 0x13UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_MC1 0x14UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_OVF 0x15UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_MC0 0x16UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_MC1 0x17UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_OVF 0x18UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_MC0 0x19UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_MC1 0x1aUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_OVF 0x1bUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_MC0 0x1cUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_MC1 0x1dUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_OVF 0x1eUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_MC0 0x1fUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_MC1 0x20UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_OVF 0x21UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_MC0 0x22UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_MC1 0x23UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_OVF 0x24UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_MC0 0x25UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_MC1 0x26UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_ADC_RESRDY 0x27UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_DAC_EMPTY 0x28UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_RX_0 0x29UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_RX_1 0x2aUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_TX_0 0x2bUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_TX_1 0x2cUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_OVF 0x2dUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC0 0x2eUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC1 0x2fUL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC2 0x30UL +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC3 0x31UL + +#define SAMD21_DMAC_CHCTRLB_TRIGACT 22 +#define SAMD21_DMAC_CHCTRLB_TRIGACT_BLOCK 0UL +#define SAMD21_DMAC_CHCTRLB_TRIGACT_BEAT 2UL +#define SAMD21_DMAC_CHCTRLB_TRIGACT_TRANSACTION 3UL + +#define SAMD21_DMAC_CHCTRLB_CMD 24 +#define SAMD21_DMAC_CHCTRLB_CMD_NOACT 0UL +#define SAMD21_DMAC_CHCTRLB_CMD_SUSPEND 1UL +#define SAMD21_DMAC_CHCTRLB_CMD_RESUME 2UL + +#define SAMD21_DMAC_CHINTFLAG_TERR 0 +#define SAMD21_DMAC_CHINTFLAG_TCMPL 1 +#define SAMD21_DMAC_CHINTFLAG_SUSP 2 + +#define SAMD21_DMAC_CHSTATUS_PEND 0 +#define SAMD21_DMAC_CHSTATUS_BUSY 1 +#define SAMD21_DMAC_CHSTATUS_FERR 2 + +#define SAMD21_DMAC_DESC_BTCTRL_VALID 0 +#define SAMD21_DMAC_DESC_BTCTRL_EVOSEL 1 +#define SAMD21_DMAC_DESC_BTCTRL_EVOSEL_DISABLE 0UL +#define SAMD21_DMAC_DESC_BTCTRL_EVOSEL_BLOCK 1UL +#define SAMD21_DMAC_DESC_BTCTRL_EVOSEL_BEAT 3UL +#define SAMD21_DMAC_DESC_BTCTRL_BLOCKACT 3 +#define SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_NOACT 0UL +#define SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_INT 1UL +#define SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_SUSPEND 2UL +#define SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_BOTH 3UL +#define SAMD21_DMAC_DESC_BTCTRL_BEATSIZE 8 +#define SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_BYTE 0UL +#define SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_HWORD 1UL +#define SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_WORD 2UL +#define SAMD21_DMAC_DESC_BTCTRL_SRCINC 10 +#define SAMD21_DMAC_DESC_BTCTRL_DSTINC 11 +#define SAMD21_DMAC_DESC_BTCTRL_STEPSEL 12 +#define SAMD21_DMAC_DESC_BTCTRL_STEPSEL_DST 0UL +#define SAMD21_DMAC_DESC_BTCTRL_STEPSEL_SRC 1UL +#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE 13 +#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X1 0UL +#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X2 1UL +#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X4 2UL +#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X8 3UL +#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X16 4UL +#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X32 5UL +#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X64 6UL +#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X128 7UL + +struct samd21_eic { + vuint8_t ctrl; + vuint8_t status; + vuint8_t nmictrl; + vuint8_t nmiflag; + vuint32_t evctrl; + vuint32_t intenclr; + vuint32_t intenset; + + vuint32_t intflag; + vuint32_t wakeup; + vuint32_t config[2]; +}; + +extern struct samd21_eic samd21_eic; + +#define samd21_eic (*(struct samd21_eic *) 0x40001800) + +#define SAMD21_NUM_EIC 16 + +#define SAMD21_EIC_CTRL_ENABLE 1 +#define SAMD21_EIC_CTRL_SWRST 0 + +#define SAMD21_EIC_STATUS_SYNCBUSY 7 + +#define SAMD21_EIC_NMICTRL_NMIFILTEN 3 +#define SAMD21_EIC_NMICTRL_NMISENSE 0 + +#define SAMD21_EIC_NMIFLAG_NMI 0 + +#define SAMD21_EIC_EVCTRL_EXTINTEO(n) (n) + +#define SAMD21_EIC_INTENCLR_EXTINT(n) (n) + +#define SAMD21_EIC_INTENSET_EXTINT(n) (n) + +#define SAMD21_EIC_INTFLAG_EXTINT(n) (n) +#define SAMD21_EIC_WAKEUP_WAKEUPEN(n) (n) +#define SAMD21_EIC_CONFIG_N(n) ((n) >> 3) +#define SAMD21_EIC_CONFIG_SENSE(n) (((n) & 7) << 2) +#define SAMD21_EIC_CONFIG_FILTEN(n) (SAMD21_EIC_CONFIG_SENSE(n) + 3) +#define SAMD21_EIC_CONFIG_SENSE_NONE 0 +#define SAMD21_EIC_CONFIG_SENSE_RISE 1 +#define SAMD21_EIC_CONFIG_SENSE_FALL 2 +#define SAMD21_EIC_CONFIG_SENSE_BOTH 3 +#define SAMD21_EIC_CONFIG_SENSE_HIGH 4 +#define SAMD21_EIC_CONFIG_SENSE_LOW 5 +#define SAMD21_EIC_CONFIG_SENSE_MASK 7UL + +struct samd21_nvmctrl { + vuint32_t ctrla; + vuint32_t ctrlb; + vuint32_t param; + vuint32_t intenclr; + + vuint32_t intenset; + vuint32_t intflag; + vuint32_t status; + vuint32_t addr; + + vuint32_t lock; +}; + +extern struct samd21_nvmctrl samd21_nvmctrl; + +#define samd21_nvmctrl (*(struct samd21_nvmctrl *) 0x41004000) + +#define SAMD21_NVMCTRL_CTRLA_CMD 0 +#define SAMD21_NVMCTRL_CTRLA_CMD_ER 0x02 +#define SAMD21_NVMCTRL_CTRLA_CMD_WP 0x04 +#define SAMD21_NVMCTRL_CTRLA_CMD_EAR 0x05 +#define SAMD21_NVMCTRL_CTRLA_CMD_WAP 0x06 +#define SAMD21_NVMCTRL_CTRLA_CMD_RWWEEER 0x1a +#define SAMD21_NVMCTRL_CTRLA_CMD_RWEEEWP 0x1c +#define SAMD21_NVMCTRL_CTRLA_CMD_LR 0x40 +#define SAMD21_NVMCTRL_CTRLA_CMD_UR 0x41 +#define SAMD21_NVMCTRL_CTRLA_CMD_SPRM 0x42 +#define SAMD21_NVMCTRL_CTRLA_CMD_CPRM 0x43 +#define SAMD21_NVMCTRL_CTRLA_CMD_PBC 0x44 +#define SAMD21_NVMCTRL_CTRLA_CMD_SSB 0x45 +#define SAMD21_NVMCTRL_CTRLA_CMD_INVALL 0x46 +#define SAMD21_NVMCTRL_CTRLA_CMD_LDR 0x47 +#define SAMD21_NVMCTRL_CTRLA_CMD_UDR 0x48 +#define SAMD21_NVMCTRL_CTRLA_CMDEX 8 +#define SAMD21_NVMCTRL_CTRLA_CMDEX_KEY 0xa5 + +#define SAMD21_NVMCTRL_CTRLB_RWS 1 +#define SAMD21_NVMCTRL_CTRLB_MANW 7 +#define SAMD21_NVMCTRL_CTRLB_SLEEPRM 8 +#define SAMD21_NVMCTRL_CTRLB_READMODE 16 +#define SAMD21_NVMCTRL_CTRLB_CACHEDIS 18 + +#define SAMD21_NVMCTRL_INTENCLR_READY 0 +#define SAMD21_NVMCTRL_INTENCLR_ERROR 1 + +#define SAMD21_NVMCTRL_INTENSET_READY 0 +#define SAMD21_NVMCTRL_INTENSET_ERROR 1 + +#define SAMD21_NVMCTRL_INTFLAG_READY 0 +#define SAMD21_NVMCTRL_INTFLAG_ERROR 1 + +#define SAMD21_NVMCTRL_STATUS_PRM 0 +#define SAMD21_NVMCTRL_STATUS_LOAD 1 +#define SAMD21_NVMCTRL_STATUS_PROGE 2 +#define SAMD21_NVMCTRL_STATUS_LOCKE 3 +#define SAMD21_NVMCTRL_STATUS_NVME 4 +#define SAMD21_NVMCTRL_STATUS_SB 8 + +#define SAMD21_NVMCTRL_PARAM_NVMP 0 +#define SAMD21_NVMCTRL_PARAM_NVMP_MASK 0xffff +#define SAMD21_NVMCTRL_PARAM_PSZ 16 +#define SAMD21_NVMCTRL_PARAM_PSZ_MASK 0x7 +#define SAMD21_NVMCTRL_PARAM_RWWEEP 20 +#define SAMD21_NVMCTRL_PARAM_RWWEEP_MASK 0xfff + +static inline uint32_t +samd21_nvmctrl_page_shift(void) +{ + return(3 + ((samd21_nvmctrl.param >> SAMD21_NVMCTRL_PARAM_PSZ) & + SAMD21_NVMCTRL_PARAM_PSZ_MASK)); +} + +static inline uint32_t +samd21_nvmctrl_page_size(void) +{ + return 1 << samd21_nvmctrl_page_shift(); +} + +uint32_t +samd21_flash_size(void); + +struct samd21_port { + vuint32_t dir; + vuint32_t dirclr; + vuint32_t dirset; + vuint32_t dirtgl; + + vuint32_t out; + vuint32_t outclr; + vuint32_t outset; + vuint32_t outtgl; + + vuint32_t in; + vuint32_t ctrl; + vuint32_t wrconfig; + vuint32_t reserved_2c; + + vuint8_t pmux[16]; + + vuint8_t pincfg[32]; +}; + +extern struct samd21_port samd21_port_a; +extern struct samd21_port samd21_port_b; + +#define samd21_port_a (*(struct samd21_port *) 0x41004400) +#define samd21_port_b (*(struct samd21_port *) 0x41004480) + +#define SAMD21_PORT_PINCFG_PMUXEN 0 +#define SAMD21_PORT_PINCFG_INEN 1 +#define SAMD21_PORT_PINCFG_PULLEN 2 +#define SAMD21_PORT_PINCFG_DRVSTR 6 + +#define SAMD21_PORT_PMUX_FUNC_A 0 +#define SAMD21_PORT_PMUX_FUNC_B 1 +#define SAMD21_PORT_PMUX_FUNC_C 2 +#define SAMD21_PORT_PMUX_FUNC_D 3 +#define SAMD21_PORT_PMUX_FUNC_E 4 +#define SAMD21_PORT_PMUX_FUNC_F 5 +#define SAMD21_PORT_PMUX_FUNC_G 6 +#define SAMD21_PORT_PMUX_FUNC_H 7 +#define SAMD21_PORT_PMUX_FUNC_I 8 + +#define SAMD21_PORT_DIR_OUT 1 +#define SAMD21_PORT_DIR_IN 0 + +static inline void +samd21_port_dir_set(struct samd21_port *port, uint8_t pin, uint8_t dir) +{ + if (dir) + port->dirset = (1 << pin); + else + port->dirclr = (1 << pin); +} + +static inline void +samd21_port_pincfg_set(struct samd21_port *port, uint8_t pin, uint8_t pincfg_mask, uint8_t pincfg) +{ + port->pincfg[pin] = (uint8_t) ((port->pincfg[pin] & ~pincfg_mask) | pincfg); +} + +static inline uint8_t +samd21_port_pincfg_get(struct samd21_port *port, uint8_t pin) +{ + return port->pincfg[pin]; +} + +static inline void +samd21_port_pmux_set(struct samd21_port *port, uint8_t pin, uint8_t func) +{ + uint8_t byte = pin >> 1; + uint8_t bit = (pin & 1) << 2; + uint8_t mask = 0xf << bit; + uint8_t value = (uint8_t) ((port->pmux[byte] & ~mask) | (func << bit)); + port->pmux[byte] = value; + samd21_port_pincfg_set(port, pin, + (1 << SAMD21_PORT_PINCFG_PMUXEN), + (1 << SAMD21_PORT_PINCFG_PMUXEN)); +} + +static inline uint8_t +samd21_port_pmux_get(struct samd21_port *port, uint8_t pin) +{ + uint8_t byte = pin >> 1; + uint8_t bit = (pin & 1) << 2; + uint8_t mask = 0xf << bit; + uint8_t value = (uint8_t) ((port->pmux[byte] & mask) >> bit); + return value; +} + +static inline void +samd21_port_pmux_clr(struct samd21_port *port, uint8_t pin) +{ + samd21_port_pincfg_set(port, pin, + (0 << SAMD21_PORT_PINCFG_PMUXEN), + (1 << SAMD21_PORT_PINCFG_PMUXEN)); +} + +struct samd21_adc { + vuint8_t ctrla; + vuint8_t refctrl; + vuint8_t avgctrl; + vuint8_t sampctrl; + vuint16_t ctrlb; + vuint16_t reserved_06; + vuint8_t winctrl; + vuint8_t reserved_09; + vuint16_t reserved_0a; + vuint8_t swtrig; + vuint8_t reserved_0d; + vuint16_t reserved_0e; + + vuint32_t inputctrl; + vuint8_t evctrl; + vuint8_t reserved_15; + vuint8_t intenclr; + vuint8_t intenset; + vuint8_t intflag; + vuint8_t status; + vuint16_t result; + vuint16_t winlt; + vuint16_t reserved_1e; + + vuint16_t winut; + vuint16_t reserved_22; + vuint16_t gaincorr; + vuint16_t offsetcorr; + vuint16_t calib; + vuint8_t dbgctrl; + vuint8_t reserved_2b; + vuint32_t reserved_2c; +}; + +#define SAMD21_ADC_CTRLA_SWRST 0 +#define SAMD21_ADC_CTRLA_ENABLE 1 +#define SAMD21_ADC_CTRLA_RUNSTDBY 2 + +#define SAMD21_ADC_REFCTRL_REFSEL 0 +#define SAMD21_ADC_REFCTRL_REFSEL_INT1V 0 +#define SAMD21_ADC_REFCTRL_REFSEL_INTVCC0 1 +#define SAMD21_ADC_REFCTRL_REFSEL_INTVCC1 2 +#define SAMD21_ADC_REFCTRL_REFSEL_VREFA 3 +#define SAMD21_ADC_REFCTRL_REFSEL_VREFB 4 +#define SAMD21_ADC_REFCTRL_REFCOMP 7 + +#define SAMD21_ADC_AVGCTRL_SAMPLENUM 0 +#define SAMD21_ADC_AVGCTRL_ADJRES 4 + +#define SAMD21_ADC_SAMPCTRL_SAMPLEN 0 + +#define SAMD21_ADC_CTRLB_DIFFMODE 0 +#define SAMD21_ADC_CTRLB_LEFTADJ 1 +#define SAMD21_ADC_CTRLB_FREERUN 2 +#define SAMD21_ADC_CTRLB_CORREN 3 +#define SAMD21_ADC_CTRLB_RESSEL 4 +#define SAMD21_ADC_CTRLB_RESSEL_12BIT 0 +#define SAMD21_ADC_CTRLB_RESSEL_16BIT 1 +#define SAMD21_ADC_CTRLB_RESSEL_10BIT 2 +#define SAMD21_ADC_CTRLB_RESSEL_8BIT 3 +#define SAMD21_ADC_CTRLB_PRESCALER 8 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV4 0 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV8 1 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV16 2 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV32 3 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV64 4 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV128 5 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV256 6 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV512 7 + +#define SAMD21_ADC_SWTRIG_FLUSH 0 +#define SAMD21_ADC_SWTRIG_START 1 + +#define SAMD21_ADC_INPUTCTRL_MUXPOS 0 +# define SAMD21_ADC_INPUTCTRL_MUXPOS_TEMP 0x18 +# define SAMD21_ADC_INPUTCTRL_MUXPOS_BANDGAP 0x19 +# define SAMD21_ADC_INPUTCTRL_MUXPOS_SCALEDCOREVCC 0x1a +# define SAMD21_ADC_INPUTCTRL_MUXPOS_SCALEDIOVCC 0x1b +# define SAMD21_ADC_INPUTCTRL_MUXPOS_DAC 0x1c +#define SAMD21_ADC_INPUTCTRL_MUXNEG 8 +# define SAMD21_ADC_INPUTCTRL_MUXNEG_GND 0x18 +# define SAMD21_ADC_INPUTCTRL_MUXNEG_IOGND 0x19 +#define SAMD21_ADC_INPUTCTRL_INPUTSCAN 16 +#define SAMD21_ADC_INPUTCTRL_INPUTOFFSET 20 +#define SAMD21_ADC_INPUTCTRL_GAIN 24 +#define SAMD21_ADC_INPUTCTRL_GAIN_1X 0 +#define SAMD21_ADC_INPUTCTRL_GAIN_DIV2 0xf + +#define SAMD21_ADC_INTFLAG_RESRDY 0 +#define SAMD21_ADC_INTFLAG_OVERRUN 1 +#define SAMD21_ADC_INTFLAG_WINMON 2 +#define SAMD21_ADC_INTFLAG_SYNCRDY 3 + +#define SAMD21_ADC_STATUS_SYNCBUSY 7 + +#define SAMD21_ADC_CALIB_LINEARITY_CAL 0 +#define SAMD21_ADC_CALIB_BIAS_CAL 16 + +extern struct samd21_adc samd21_adc; + +#define samd21_adc (*(struct samd21_adc *) 0x42004000) + +struct samd21_dac { + vuint8_t ctrla; + vuint8_t ctrlb; + vuint8_t evctrl; + uint8_t reserved_03; + + vuint8_t intenclr; + vuint8_t intenset; + vuint8_t intflag; + vuint8_t status; + + vuint16_t data; + uint16_t reserved_0a; + + vuint16_t databuf; +}; + +#define SAMD21_DAC_CTRLA_SWRST 0 +#define SAMD21_DAC_CTRLA_ENABLE 1 +#define SAMD21_DAC_CTRLA_RUNSTDBY 2 + +#define SAMD21_DAC_CTRLB_EOEN 0 +#define SAMD21_DAC_CTRLB_IOEN 1 +#define SAMD21_DAC_CTRLB_LEFTADJ 2 +#define SAMD21_DAC_CTRLB_VPD 3 +#define SAMD21_DAC_CTRLB_BDWP 4 +#define SAMD21_DAC_CTRLB_REFSEL 6 +#define SAMD21_DAC_CTRLB_REFSEL_INTREF 0 +#define SAMD21_DAC_CTRLB_REFSEL_VDDANA 1 +#define SAMD21_DAC_CTRLB_REFSEL_VREFA 2 +#define SAMD21_DAC_CTRLB_REFSEL_MASK 3 + +#define SAMD21_DAC_EVCTRL_STARTEI 0 +#define SAMD21_DAC_EVCTRL_EMPTYEO 1 + +#define SAMD21_DAC_INTENCLR_UNDERRUN 0 +#define SAMD21_DAC_INTENCLR_EMPTY 1 +#define SAMD21_DAC_INTENCLR_SYNCRDY 2 + +#define SAMD21_DAC_INTENSET_UNDERRUN 0 +#define SAMD21_DAC_INTENSET_EMPTY 1 +#define SAMD21_DAC_INTENSET_SYNCRDY 2 + +#define SAMD21_DAC_INTFLAG_UNDERRUN 0 +#define SAMD21_DAC_INTFLAG_EMPTY 1 +#define SAMD21_DAC_INTFLAG_SYNCRDY 2 + +#define SAMD21_DAC_STATUS_SYNCBUSY 7 + +extern struct samd21_dac samd21_dac; +#define samd21_dac (*(struct samd21_dac *) 0x42004800) + +/* TC */ +struct samd21_tc { + vuint16_t ctrla; + vuint16_t readreq; + vuint8_t ctrlbclr; + vuint8_t ctrlbset; + vuint8_t ctrlc; + vuint8_t reserved_07; + vuint8_t dbgctrl; + vuint8_t reserved_09; + vuint16_t evctrl; + vuint8_t intenclr; + vuint8_t intenset; + vuint8_t intflag; + vuint8_t status; + + union { + struct { + vuint8_t count; + vuint8_t reserved_11; + vuint16_t reserved_12; + vuint8_t per; + vuint8_t reserved_15; + vuint16_t reserved_16; + vuint8_t cc[2]; + } mode_8; + struct { + vuint16_t count; + vuint16_t reserved_12; + vuint32_t reserved_14; + vuint16_t cc[2]; + } mode_16; + struct { + vuint32_t count; + vuint32_t reserved_14; + vuint32_t cc[2]; + } mode_32; + }; +}; + +extern struct samd21_tc samd21_tc3; +#define samd21_tc3 (*(struct samd21_tc *) 0x42002c00) + +extern struct samd21_tc samd21_tc4; +#define samd21_tc4 (*(struct samd21_tc *) 0x42003000) + +extern struct samd21_tc samd21_tc5; +#define samd21_tc5 (*(struct samd21_tc *) 0x42003400) + +#ifdef ATSAMD21J +/* Present on all of the samd21j parts and the samd21g16l */ +extern struct samd21_tc samd21_tc6; +#define samd21_tc6 (*(struct samd21_tc *) 0x42003800) + +extern struct samd21_tc samd21_tc7; +#define samd21_tc7 (*(struct samd21_tc *) 0x42003c00) +#endif + +#define SAMD21_TC_CTRLA_SWRST 0 +#define SAMD21_TC_CTRLA_ENABLE 1 +#define SAMD21_TC_CTRLA_MODE 2 +#define SAMD21_TC_CTRLA_MODE_COUNT16 0 +#define SAMD21_TC_CTRLA_MODE_COUNT8 1 +#define SAMD21_TC_CTRLA_MODE_COUNT32 2 +#define SAMD21_TC_CTRLA_WAVEGEN 5 +#define SAMD21_TC_CTRLA_WAVEGEN_NFRQ 0 +#define SAMD21_TC_CTRLA_WAVEGEN_MFRQ 1 +#define SAMD21_TC_CTRLA_WAVEGEN_NPWM 2 +#define SAMD21_TC_CTRLA_WAVEGEN_MPWM 3 +#define SAMD21_TC_CTRLA_PRESCALER 8 +#define SAMD21_TC_CTRLA_PRESCALER_DIV1 0 +#define SAMD21_TC_CTRLA_PRESCALER_DIV2 1 +#define SAMD21_TC_CTRLA_PRESCALER_DIV4 2 +#define SAMD21_TC_CTRLA_PRESCALER_DIV8 3 +#define SAMD21_TC_CTRLA_PRESCALER_DIV16 4 +#define SAMD21_TC_CTRLA_PRESCALER_DIV64 5 +#define SAMD21_TC_CTRLA_PRESCALER_DIV256 6 +#define SAMD21_TC_CTRLA_PRESCALER_DIV1024 7 +#define SAMD21_TC_CTRLA_RUNSTDBY 11 +#define SAMD21_TC_CTRLA_PRESCSYNC 12 +#define SAMD21_TC_CTRLA_PRESCSYNC_GCLK 0 +#define SAMD21_TC_CTRLA_PRESCSYNC_PRSEC 1 +#define SAMD21_TC_CTRLA_PRESCSYNC_RESYNC 2 + +#define SAMD21_TC_READREQ_ADDR 0 +#define SAMD21_TC_READREQ_RCONT 14 +#define SAMD21_TC_READREQ_RREQ 15 +#define SAMD21_TC_CTRLB_DIR 0 +#define SAMD21_TC_CTRLB_ONESHOT 2 +#define SAMD21_TC_CTRLB_CMD 6 +#define SAMD21_TC_CTRLC_INVEN(x) (0 + (x)) +#define SAMD21_TC_CTRLC_CPTEN(x) (4 + (x)) +#define SAMD21_TC_DBGCTRL_DBGRUN 0 +#define SAMD21_TC_EVCTRL_EVACT 0 +#define SAMD21_TC_EVCTRL_TCINV 4 +#define SAMD21_TC_EVCTRL_TCEI 5 +#define SAMD21_TC_EVCTRL_OVFEO 8 +#define SAMD21_TC_EVCTRL_MCEO(x) (12 + (x)) + +#define SAMD21_TC_INTFLAG_MC(x) (4 + (x)) +#define SAMD21_TC_INTFLAG_SYNCRDY 3 +#define SAMD21_TC_INTFLAG_ERR 1 +#define SAMD21_TC_INTFLAG_OVF 0 + +#define SAMD21_TC_STATUS_STOP 3 +#define SAMD21_TC_STATUS_FOLLOWER 4 +#define SAMD21_TC_STATUS_SYNCBUSY 7 + +/* TCC */ + +struct samd21_tcc { + vuint32_t ctrla; + vuint8_t ctrlbclr; + vuint8_t ctrlbset; + vuint16_t reserved_06; + vuint32_t syncbusy; + vuint32_t fctrla; + + vuint32_t fctlrb; + vuint32_t wexctrl; + vuint32_t drvctrl; + vuint16_t reserved_1c; + vuint8_t dbgctrl; + vuint8_t reserved_1f; + + vuint32_t evctrl; + vuint32_t intenclr; + vuint32_t intenset; + vuint32_t intflag; + + vuint32_t status; + vuint32_t count; + vuint16_t patt; + vuint16_t reserved_3a; + vuint32_t wave; + + vuint32_t per; + vuint32_t cc[4]; + vuint32_t reserved_54; + vuint32_t reserved_58; + vuint32_t reserved_5c; + + vuint32_t reserved_60; + vuint16_t pattb; + vuint16_t reserved_66; + vuint32_t waveb; + vuint32_t perb; + + vuint32_t ccb[4]; +}; + +extern struct samd21_tcc samd21_tcc0; +#define samd21_tcc0 (*(struct samd21_tcc *) 0x42002000) + +extern struct samd21_tcc samd21_tcc1; +#define samd21_tcc1 (*(struct samd21_tcc *) 0x42002400) + +extern struct samd21_tcc samd21_tcc2; +#define samd21_tcc2 (*(struct samd21_tcc *) 0x42002800) + +#ifdef SAMD21E17D +/* only on the samd21e17d */ +extern struct samd21_tcc samd21_tcc3; +#define samd21_tcc3 (*(struct samd21_tcc *) 0x42006000) +#endif + +#define SAMD21_TCC_CTRLA_SWRST 0 +#define SAMD21_TCC_CTRLA_ENABLE 1 +#define SAMD21_TCC_CTRLA_RESOLUTION 5 +#define SAMD21_TCC_CTRLA_RESOLUTION_NONE 0 +#define SAMD21_TCC_CTRLA_RESOLUTION_DITH4 1 +#define SAMD21_TCC_CTRLA_RESOLUTION_DITH5 2 +#define SAMD21_TCC_CTRLA_RESOLUTION_DITH6 3 +#define SAMD21_TCC_CTRLA_PRESCALER 8 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV1 0 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV2 1 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV4 2 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV8 3 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV16 4 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV64 5 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV256 6 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV1024 7 +#define SAMD21_TCC_CTRLA_RUNSTDBY 11 +#define SAMD21_TCC_CTRLA_PRESYNC 12 +#define SAMD21_TCC_CTRLA_PRESYNC_GCLK 0 +#define SAMD21_TCC_CTRLA_PRESYNC_PRESC 1 +#define SAMD21_TCC_CTRLA_PRESYNC_RESYNC 2 +#define SAMD21_TCC_CTRLA_ALOCK 14 +#define SAMD21_TCC_CTRLA_CPTEN(n) (24 + (n)) + +#define SAMD21_TCC_CTRLB_DIR 0 +#define SAMD21_TCC_CTRLB_LUPD 1 +#define SAMD21_TCC_CTRLB_ONESHOT 2 +#define SAMD21_TCC_CTRLB_IDXCMD 3 +#define SAMD21_TCC_CTRLB_IDXCMD_DISABLE 0 +#define SAMD21_TCC_CTRLB_IDXCMD_SET 1 +#define SAMD21_TCC_CTRLB_IDXCMD_CLEAR 2 +#define SAMD21_TCC_CTRLB_IDXCMD_HOLD 3 +#define SAMD21_TCC_CTRLB_CMD 5 +#define SAMD21_TCC_CTRLB_CMD_NONE 0 +#define SAMD21_TCC_CTRLB_CMD_RETRIGGER 1 +#define SAMD21_TCC_CTRLB_CMD_STOP 2 +#define SAMD21_TCC_CTRLB_CMD_UPDATE 3 +#define SAMD21_TCC_CTRLB_CMD_READSYNC 4 +#define SAMD21_TCC_CTRLB_CMD_DMAOS 5 + +#define SAMD21_TCC_SYNCBUSY_SWRST 0 +#define SAMD21_TCC_SYNCBUSY_ENABLE 1 +#define SAMD21_TCC_SYNCBUSY_CTRLB 2 +#define SAMD21_TCC_SYNCBUSY_STATUS 3 +#define SAMD21_TCC_SYNCBUSY_COUNT 4 +#define SAMD21_TCC_SYNCBUSY_PATT 5 +#define SAMD21_TCC_SYNCBUSY_WAVE 6 +#define SAMD21_TCC_SYNCBUSY_PER 7 +#define SAMD21_TCC_SYNCBUSY_CC(x) (8 + (x)) +#define SAMD21_TCC_SYNCBUSY_PATTB 16 +#define SAMD21_TCC_SYNCBUSY_WAVEB 17 +#define SAMD21_TCC_SYNCBUSY_PERB 18 +#define SAMD21_TCC_SYNCBUSY_CCB(x) ((19 + (x)) + +#define SAMD21_TCC_DBGCTRL_FDDBD 2 +#define SAMD21_TCC_DBGCTRL_DBGRUN 0 + +#define SAMD21_TCC_EVCTRL_EVACTO 0 +#define SAMD21_TCC_EVCTRL_EVACT1 3 +#define SAMD21_TCC_EVCTRL_CNTSEL 6 +#define SAMD21_TCC_EVCTRL_OVFEO 8 +#define SAMD21_TCC_EVCTRL_TRGEO 9 +#define SAMD21_TCC_EVCTRL_CNTEO 10 +#define SAMD21_TCC_EVCTRL_TCINV(x) (12 + (x)) +#define SAMD21_TCC_EVCTRL_MCEI(x) (16 + (x)) +#define SAMD21_TCC_EVCTRL_MCEO(x) (24 + (x)) + +#define SAMD21_TCC_INTFLAG_OVF 0 +#define SAMD21_TCC_INTFLAG_TRG 1 +#define SAMD21_TCC_INTFLAG_CNT 2 +#define SAMD21_TCC_INTFLAG_ERR 3 +#define SAMD21_TCC_INTFLAG_UFS 10 +#define SAMD21_TCC_INTFLAG_DFS 11 +#define SAMD21_TCC_INTFLAG_FAULTA 12 +#define SAMD21_TCC_INTFLAG_FAULTB 13 +#define SAMD21_TCC_INTFLAG_FAULT0 14 +#define SAMD21_TCC_INTFLAG_FAULT1 15 +#define SAMD21_TCC_INTFLAG_MC(x) (16 + (x)) + +#define SAMD21_TCC_WAVE_WAVEGEN 0 +#define SAMD21_TCC_WAVE_WAVEGEN_NFRQ 0 +#define SAMD21_TCC_WAVE_WAVEGEN_MFRQ 1 +#define SAMD21_TCC_WAVE_WAVEGEN_NPWM 2 +#define SAMD21_TCC_WAVE_WAVEGEN_DSCRITICAL 4 +#define SAMD21_TCC_WAVE_WAVEGEN_DSBOTTOM 5 +#define SAMD21_TCC_WAVE_WAVEGEN_DSBOTH 6 +#define SAMD21_TCC_WAVE_WAVEGEN_DSTOP 7 +#define SAMD21_TCC_WAVE_RAMP 4 +#define SAMD21_TCC_WAVE_CIPEREN 7 +#define SAMD21_TCC_WAVE_CCCEN(x) (8 + (x)) +#define SAMD21_TCC_WAVE_POL(x) (16 + (x)) +#define SAMD21_TCC_WAVE_SWAP(x) (24 + (x)) + +/* USB */ + +struct samd21_usb { + vuint8_t ctrla; + vuint8_t reserved_01; + vuint8_t syncbusy; + vuint8_t qosctrl; + + vuint32_t reserved_04; + vuint16_t ctrlb; + vuint8_t dadd; + vuint8_t reserved_0b; + vuint8_t status; + vuint8_t fsmstatus; + vuint16_t reserved_0e; + + vuint16_t fnum; + vuint16_t reserved_12; + vuint16_t intenclr; + vuint16_t reserved_16; + vuint16_t intenset; + vuint16_t reserved_1a; + vuint16_t intflag; + vuint16_t reserved_1e; + + vuint16_t epintsmry; + vuint16_t reserved_22; + + vuint32_t descadd; + vuint16_t padcal; + uint8_t reserved_2a[0x100 - 0x2a]; + + struct { + vuint8_t epcfg; + vuint8_t reserved_01; + vuint8_t reserved_02; + vuint8_t binterval; + vuint8_t epstatusclr; + vuint8_t epstatusset; + vuint8_t epstatus; + vuint8_t epintflag; + vuint8_t epintenclr; + vuint8_t epintenset; + vuint8_t reserved_0a[0x20 - 0x0a]; + } ep[8]; +}; + +extern struct samd21_usb samd21_usb; + +#define samd21_usb (*(struct samd21_usb *) 0x41005000) + +#define SAMD21_USB_CTRLA_SWRST 0 +#define SAMD21_USB_CTRLA_ENABLE 1 +#define SAMD21_USB_CTRLA_RUNSTDBY 2 +#define SAMD21_USB_CTRLA_MODE 7 + +#define SAMD21_USB_SYNCBUSY_SWRST 0 +#define SAMD21_USB_SYNCBUSY_ENABLE 1 + +#define SAMD21_USB_QOSCTRL_CQOS 0 +#define SAMD21_USB_QOSCTRL_DQOS 2 + +#define SAMD21_USB_CTRLB_DETACH 0 +#define SAMD21_USB_CTRLB_UPRSM 1 +#define SAMD21_USB_CTRLB_SPDCONF 2 +#define SAMD21_USB_CTRLB_SPDCONF_FS 0 +#define SAMD21_USB_CTRLB_SPDCONF_LS 1 +#define SAMD21_USB_CTRLB_SPDCONF_MASK 0x3 +#define SAMD21_USB_CTRLB_NREPLY 4 +#define SAMD21_USB_CTRLB_GNAK 9 +#define SAMD21_USB_CTRLB_LPMHDSK 10 +#define SAMD21_USB_CTRLB_LPMHDSK_NONE 0 +#define SAMD21_USB_CTRLB_LPMHDSK_ACK 1 +#define SAMD21_USB_CTRLB_LPMHDSK_NYET 2 +#define SAMD21_USB_CTRLB_LPMHDSK_MASK 3 + +#define SAMD21_USB_DADD_DADD 0 +#define SAMD21_USB_DADD_ADDEN 7 + +#define SAMD21_USB_STATUS_SPEED 2 +#define SAMD21_USB_STATUS_LINESTATE 6 +#define SAMD21_USB_FNUM_MFNUM 0 +#define SAMD21_USB_FNUM_FNUM 3 +#define SAMD21_USB_FNUM_FNCERR 15 +#define SAMD21_USB_INTFLAG_SUSPEND 0 +#define SAMD21_USB_INTFLAG_SOF 2 +#define SAMD21_USB_INTFLAG_EORST 3 +#define SAMD21_USB_INTFLAG_WAKEUP 4 +#define SAMD21_USB_INTFLAG_EORSM 5 +#define SAMD21_USB_INTFLAG_UPRSM 6 +#define SAMD21_USB_INTFLAG_RAMACER 7 +#define SAMD21_USB_INTFLAG_LPMNYET 8 +#define SAMD21_USB_INTFLAG_LPMSUSP 9 + +#define SAMD21_USB_PADCAL_TRANSP 0 +#define SAMD21_USB_PADCAL_TRANSN 6 +#define SAMD21_USB_PADCAL_TRIM 12 + +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT 0 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DISABLED 0 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_CONTROL 1 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_ISOCHRONOUS 2 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_BULK 3 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_INTERRUPT 4 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DUAL_BANK 5 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN 4 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN_DISABLED 0 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN_CONTROL 1 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN_ISOCHRONOUS 2 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN_BULK 3 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN_INTERRUPT 4 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN_DUAL_BANK 5 + +#define SAMD21_USB_EP_EPSTATUS_DTGLOUT 0 +#define SAMD21_USB_EP_EPSTATUS_DTGLIN 1 +#define SAMD21_USB_EP_EPSTATUS_CURBK 2 +#define SAMD21_USB_EP_EPSTATUS_STALLRQ0 4 +#define SAMD21_USB_EP_EPSTATUS_STALLRQ1 5 +#define SAMD21_USB_EP_EPSTATUS_BK0RDY 6 +#define SAMD21_USB_EP_EPSTATUS_BK1RDY 7 + +#define SAMD21_USB_EP_EPINTFLAG_TRCPT0 0 +#define SAMD21_USB_EP_EPINTFLAG_TRCPT1 1 +#define SAMD21_USB_EP_EPINTFLAG_TRFAIL0 2 +#define SAMD21_USB_EP_EPINTFLAG_TRFAIL1 3 +#define SAMD21_USB_EP_EPINTFLAG_RXSTP 4 +#define SAMD21_USB_EP_EPINTFLAG_STALL 5 + +struct samd21_usb_desc_bank { + vuint32_t addr; + vuint32_t pcksize; + vuint16_t extreg; + vuint8_t status_bk; + vuint8_t reserved_0b; + vuint32_t reserved_0c; +}; + +struct samd21_usb_desc { + struct samd21_usb_desc_bank bank[2]; +}; + +extern struct samd21_usb_desc samd21_usb_desc[8]; + +#define SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT 0 +#define SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT_MASK 0x3fffU +#define SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE 14 +#define SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE_MASK 0x3fffU +#define SAMD21_USB_DESC_PCKSIZE_SIZE 28 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_8 0 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_16 1 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_32 2 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_64 3 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_128 4 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_256 5 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_512 6 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_1023 7 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_MASK 7U +#define SAMD21_USB_DESC_PCKSIZE_AUTO_ZLP 31 + +static inline uint16_t +samd21_usb_desc_get_byte_count(uint8_t ep, uint8_t bank) +{ + return ((samd21_usb_desc[ep].bank[bank].pcksize >> SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT) & + SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT_MASK); +} + +static inline void +samd21_usb_desc_set_byte_count(uint8_t ep, uint8_t bank, uint32_t count) +{ + uint32_t pcksize = samd21_usb_desc[ep].bank[bank].pcksize; + + pcksize &= ~(SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT_MASK << SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT); + pcksize &= ~(SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE_MASK << SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE); + pcksize |= (count << SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT); + samd21_usb_desc[ep].bank[bank].pcksize = pcksize; +} + +static inline void +samd21_usb_desc_set_size(uint8_t ep, uint8_t bank, uint32_t size) +{ + uint32_t pcksize = samd21_usb_desc[ep].bank[bank].pcksize; + + pcksize &= ~(SAMD21_USB_DESC_PCKSIZE_SIZE_MASK << SAMD21_USB_DESC_PCKSIZE_SIZE); + + uint32_t size_bits = 0; + switch (size) { + case 8: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_8; break; + case 16: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_16; break; + case 32: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_32; break; + case 64: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_64; break; + case 128: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_128; break; + case 256: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_256; break; + case 512: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_512; break; + case 1023: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_1023; break; + } + pcksize |= (size_bits << SAMD21_USB_DESC_PCKSIZE_SIZE); + samd21_usb_desc[ep].bank[bank].pcksize = pcksize; +} + +static inline void +samd21_usb_ep_set_ready(uint8_t ep, uint8_t bank) +{ + samd21_usb.ep[ep].epstatusset = (1 << (SAMD21_USB_EP_EPSTATUS_BK0RDY + bank)); + samd21_usb.ep[ep].epintflag = (1 << (SAMD21_USB_EP_EPINTFLAG_TRFAIL0 + bank)); +} + +static inline void +samd21_usb_ep_clr_ready(uint8_t ep, uint8_t bank) +{ + samd21_usb.ep[ep].epstatusclr = (1 << (SAMD21_USB_EP_EPSTATUS_BK0RDY + bank)); +} + +static inline uint8_t +samd21_usb_ep_ready(uint8_t ep) +{ + return (samd21_usb.ep[ep].epstatus >> SAMD21_USB_EP_EPSTATUS_BK0RDY) & 3; +} + +static inline uint8_t +samd21_usb_ep_curbk(uint8_t ep) +{ + return (samd21_usb.ep[ep].epstatus >> SAMD21_USB_EP_EPSTATUS_CURBK) & 1; +} + +/* evsys */ + +struct samd21_evsys { + vuint8_t ctrl; + vuint8_t reserved_01; + vuint16_t reserved_02; + vuint32_t channel; + vuint16_t user; + vuint16_t reserved_0a; + vuint32_t chstatus; + + vuint32_t intenclr; + vuint32_t intenset; + vuint32_t intflag; +}; + +extern struct samd21_evsys samd21_evsys; + +#define SAMD21_NUM_EVSYS 16 + +#define samd21_evsys (*(struct samd21_evsys *) 0x42000400) + +#define SAMD21_EVSYS_CONTROL_SWRST 0 +#define SAMD21_EVSYS_CONTROL_GCLKREQ 4 + +#define SAMD21_EVSYS_CHANNEL_CHANNEL 0 + +#define SAMD21_EVSYS_CHANNEL_SWEVT 8 + +#define SAMD21_EVSYS_CHANNEL_EVGEN 16 +#define SAMD21_EVSYS_CHANNEL_EVGEN_NONE 0x00 +#define SAMD21_EVSYS_CHANNEL_EVGEN_RTC_CMP(i) (0x01 + (i)) +#define SAMD21_EVSYS_CHANNEL_EVGEN_OVF 0x03 +#define SAMD21_EVSYS_CHANNEL_EVGEN_PER(i) (0x04 + (i)) +#define SAMD21_EVSYS_CHANNEL_EVGEN_EXTINT(i) (0x0c + (i)) +#define SAMD21_EVSYS_CHANNEL_EVGEN_DMAC_CH(i) (0x1e + (i)) +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC0_OVF 0x22 +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC0_TRG 0x23 +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC0_CNT 0x29 +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC0_MCX(i) (0x25 + (i)) +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC1_OVF 0x29 +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC1_TRG 0x2a +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC1_CNT 0x2b +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC1_MCX(i) (0x2c + (i)) +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC2_OVF 0x2e +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC2_TRG 0x2f +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC2_CNT 0x30 +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC2_MCX(i) (0x31 + (i)) +#define SAMD21_EVSYS_CHANNEL_EVGEN_TC3_OVF 0x33 +#define SAMD21_EVSYS_CHANNEL_EVGEN_TC3_MC(i) (0x34 + (i)) +#define SAMD21_EVSYS_CHANNEL_EVGEN_TC4_OVF 0x36 +#define SAMD21_EVSYS_CHANNEL_EVGEN_TC4_MC(i) (0x37 + (i)) +#define SAMD21_EVSYS_CHANNEL_EVGEN_TC5_OVF 0x39 +#define SAMD21_EVSYS_CHANNEL_EVGEN_TC5_MC(i) (0x3a + (i)) +#define SAMD21_EVSYS_CHANNEL_EVGEN_TC6_OVF 0x3c +#define SAMD21_EVSYS_CHANNEL_EVGEN_TC6_MC(i) (0x3d + (i)) +#define SAMD21_EVSYS_CHANNEL_EVGEN_TC7_OVF 0x3f +#define SAMD21_EVSYS_CHANNEL_EVGEN_TC7_MC(i) (0x40 + (i)) +#define SAMD21_EVSYS_CHANNEL_EVGEN_ADC_RESRDY 0x42 +#define SAMD21_EVSYS_CHANNEL_EVGEN_ADC_WINMON 0x43 +#define SAMD21_EVSYS_CHANNEL_EVGEN_AC_COMP0 0x44 +#define SAMD21_EVSYS_CHANNEL_EVGEN_AC_COMP1 0x45 +#define SAMD21_EVSYS_CHANNEL_EVGEN_AC_WIN0 0x46 +#define SAMD21_EVSYS_CHANNEL_EVGEN_DAC_EMPTY 0x47 +#define SAMD21_EVSYS_CHANNEL_EVGEN_PTC_EOC 0x48 +#define SAMD21_EVSYS_CHANNEL_EVGEN_PTC_WCOMP 0x49 +#define SAMD21_EVSYS_CHANNEL_EVGEN_AC_COMP2 0x4a +#define SAMD21_EVSYS_CHANNEL_EVGEN_AC_COMP3 0x4b +#define SAMD21_EVSYS_CHANNEL_EVGEN_AC_WIN1 0x4c +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC3_OVF 0x4d +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC3_TRG 0x4e +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC3_CNT 0x4f +#define SAMD21_EVSYS_CHANNEL_EVGEN_TCC3_MCX(i) (0x50 + (i)) + +#define SAMD21_EVSYS_CHANNEL_PATH 24 +#define SAMD21_EVSYS_CHANNEL_PATH_SYNCHRONOUS 0 +#define SAMD21_EVSYS_CHANNEL_PATH_RESYNCHRONIZED 1 +#define SAMD21_EVSYS_CHANNEL_PATH_ASYNCHRONOUS 2 + +#define SAMD21_EVSYS_CHANNEL_EDGESEL 26 +#define SAMD21_EVSYS_CHANNEL_EDGESEL_NO_EVT_OUTPUT 0 +#define SAMD21_EVSYS_CHANNEL_EDGESEL_RISING_EDGE 1 +#define SAMD21_EVSYS_CHANNEL_EDGESEL_FALLING_EDGE 2 +#define SAMD21_EVSYS_CHANNEL_EDGESEL_BOTH_EDGES 3 + +#define SAMD21_EVSYS_USER_USER 0 +#define SAMD21_EVSYS_USER_USER_DMAC_CH(n) (0x00 + (n)) +#define SAMD21_EVSYS_USER_USER_TCC0_EV(n) (0x04 + (n)) +#define SAMD21_EVSYS_USER_USER_TCC0_MC(n) (0x06 + (n)) +#define SAMD21_EVSYS_USER_USER_TCC1_EV(n) (0x0a + (n)) +#define SAMD21_EVSYS_USER_USER_TCC1_MC(n) (0x0c + (n)) +#define SAMD21_EVSYS_USER_USER_TCC2_EV(n) (0x0e + (n)) +#define SAMD21_EVSYS_USER_USER_TCC2_MC(n) (0x10 + (n)) +#define SAMD21_EVSYS_USER_USER_TC(n) (0x12 + (n)) +#define SAMD21_EVSYS_USER_USER_ADC_START (0x17) +#define SAMD21_EVSYS_USER_USER_ADC_SYNC 0x18 +#define SAMD21_EVSYS_USER_USER_AC_COMP0 0x19 +#define SAMD21_EVSYS_USER_USER_AC_COMP1 0x1a +#define SAMD21_EVSYS_USER_USER_DAC_START 0x1b +#define SAMD21_EVSYS_USER_USER_PTC_STCONV 0x1c +#define SAMD21_EVSYS_USER_USER_AC_COMP2 0x1d +#define SAMD21_EVSYS_USER_USER_AC_COMP3 0x1e +#define SAMD21_EVSYS_USER_USER_TCC3_EV(n) (0x1f + (n)) +#define SAMD21_EVSYS_USER_USER_TCC3_MC(n) (0x21 + (n)) + +#define SAMD21_EVSYS_USER_CHANNEL 8 +#define SAMD21_EVSYS_USER_CHANNEL_NONE 0 +#define SAMD21_EVSYS_USER_CHANNEL_NUM(n) ((n) + 1) + + +#define SAMD21_EVSYS_CHSTATUS_USRRDY(n) (((n) & 7) | (((n) & 8) << 1)) +#define SAMD21_EVSYS_CHSTATUS_CHBUSY(n) (((n) & 7) | (((n) & 8) << 1) | 8) + +/* sercom */ + +struct samd21_sercom { + vuint32_t ctrla; + vuint32_t ctrlb; + vuint32_t reserved_08; + vuint16_t baud; + vuint8_t rxpl; + vuint8_t reserved_0f; + + vuint32_t reserved_10; + vuint8_t intenclr; + vuint8_t reserved_15; + vuint8_t intenset; + vuint8_t reserved_17; + vuint8_t intflag; + vuint8_t reserved_19; + vuint16_t status; + vuint32_t syncbusy; + + vuint32_t reserved_20; + vuint32_t addr; + vuint16_t data; + vuint16_t reserved_2a; + vuint32_t reserved_2c; + + vuint8_t dbgctrl; + vuint8_t reserved_31; + vuint16_t reserved_32; + vuint16_t fifospace; + vuint16_t fifoptr; +}; + +extern struct samd21_sercom samd21_sercom0; +extern struct samd21_sercom samd21_sercom1; +extern struct samd21_sercom samd21_sercom2; +extern struct samd21_sercom samd21_sercom3; +extern struct samd21_sercom samd21_sercom4; +extern struct samd21_sercom samd21_sercom5; + +#define SAMD21_NUM_SERCOM 6 + +#define samd21_sercom0 (*(struct samd21_sercom *) 0x42000800) +#define samd21_sercom1 (*(struct samd21_sercom *) 0x42000c00) +#define samd21_sercom2 (*(struct samd21_sercom *) 0x42001000) +#define samd21_sercom3 (*(struct samd21_sercom *) 0x42001400) +#define samd21_sercom4 (*(struct samd21_sercom *) 0x42001800) +#define samd21_sercom5 (*(struct samd21_sercom *) 0x42001c00) + +#define SAMD21_SERCOM_CTRLA_SWRST 0 +#define SAMD21_SERCOM_CTRLA_ENABLE 1 +#define SAMD21_SERCOM_CTRLA_MODE 2 +# define SAMD21_SERCOM_CTRLA_MODE_USART 1 +# define SAMD21_SERCOM_CTRLA_MODE_SPI_CLIENT 2 +# define SAMD21_SERCOM_CTRLA_MODE_SPI_HOST 3 +# define SAMD21_SERCOM_CTRLA_MODE_I2C_CLIENT 4 +# define SAMD21_SERCOM_CTRLA_MODE_I2C_HOST 5 + +#define SAMD21_SERCOM_CTRLA_RUNSTDBY 7 + +/* USART mode */ +#define SAMD21_SERCOM_CTRLA_IBON 8 +#define SAMD21_SERCOM_CTRLA_SAMPR 13 +#define SAMD21_SERCOM_CTRLA_TXPO 16 +#define SAMD21_SERCOM_CTRLA_TXPO_TX_0 0 +#define SAMD21_SERCOM_CTRLA_TXPO_TX_2 1 +#define SAMD21_SERCOM_CTRLA_TXPO_TX_0_RTS_2_CTS_3 2 +#define SAMD21_SERCOM_CTRLA_RXPO 20 +#define SAMD21_SERCOM_CTRLA_RXPO_RX_0 0 +#define SAMD21_SERCOM_CTRLA_RXPO_RX_1 1 +#define SAMD21_SERCOM_CTRLA_RXPO_RX_2 2 +#define SAMD21_SERCOM_CTRLA_RXPO_RX_3 3 +#define SAMD21_SERCOM_CTRLA_SAMPA 22 +#define SAMD21_SERCOM_CTRLA_FORM 24 +#define SAMD21_SERCOM_CTRLA_CMODE 28 +#define SAMD21_SERCOM_CTRLA_CPOL 29 +#define SAMD21_SERCOM_CTRLA_DORD 30 + +/* I2C controller mode */ +#define SAMD21_SERCOM_CTRLA_PINOUT 16 +#define SAMD21_SERCOM_CTRLA_SDAHOLD 20 +#define SAMD21_SERCOM_CTRLA_SDAHOLD_DIS 0 +#define SAMD21_SERCOM_CTRLA_SDAHOLD_75NS 1 +#define SAMD21_SERCOM_CTRLA_SDAHOLD_450NS 2 +#define SAMD21_SERCOM_CTRLA_SDAHOLD_600NS 3 +#define SAMD21_SERCOM_CTRLA_MEXTTOEN 22 +#define SAMD21_SERCOM_CTRLA_SEXTTOEN 23 +#define SAMD21_SERCOM_CTRLA_SPEED 24 +#define SAMD21_SERCOM_CTRLA_SPEED_STANDARD 0 +#define SAMD21_SERCOM_CTRLA_SPEED_FAST 1 +#define SAMD21_SERCOM_CTRLA_SPEED_HIGH 2 +#define SAMD21_SERCOM_CTRLA_SCLSM 27 +#define SAMD21_SERCOM_CTRLA_INACTOUT 28 +#define SAMD21_SERCOM_CTRLA_INACTOUT_DIS 0 +#define SAMD21_SERCOM_CTRLA_INACTOUT_55US 1 +#define SAMD21_SERCOM_CTRLA_INACTOUT_105US 2 +#define SAMD21_SERCOM_CTRLA_INACTOUT_205US 3 +#define SAMD21_SERCOM_CTRLA_LOWTOUT 30 + +/* SPI controller mode */ +#define SAMD21_SERCOM_CTRLA_DOPO 16 +#define SAMD21_SERCOM_CTRLA_DOPO_MOSI_0_SCLK_1 0UL +#define SAMD21_SERCOM_CTRLA_DOPO_MOSI_2_SCLK_3 1UL +#define SAMD21_SERCOM_CTRLA_DOPO_MOSI_3_SCLK_1 2UL +#define SAMD21_SERCOM_CTRLA_DOPO_MOSI_0_SCLK_3 3UL +#define SAMD21_SERCOM_CTRLA_DOPO_MASK 3UL + +#define SAMD21_SERCOM_CTRLA_DIPO 20 +#define SAMD21_SERCOM_CTRLA_DIPO_MISO_0 0UL +#define SAMD21_SERCOM_CTRLA_DIPO_MISO_1 1UL +#define SAMD21_SERCOM_CTRLA_DIPO_MISO_2 2UL +#define SAMD21_SERCOM_CTRLA_DIPO_MISO_3 3UL +#define SAMD21_SERCOM_CTRLA_DIPO_MASK 3UL + +#define SAMD21_SERCOM_CTRLA_FORM 24 +#define SAMD21_SERCOM_CTRLA_CPHA 28 +#define SAMD21_SERCOM_CTRLA_CPOL 29 +#define SAMD21_SERCOM_CTRLA_DORD 30 +#define SAMD21_SERCOM_CTRLA_DORD_LSB 1 +#define SAMD21_SERCOM_CTRLA_DORD_MSB 0 + +/* USART mode */ +#define SAMD21_SERCOM_CTRLB_CHSIZE 0 +#define SAMD21_SERCOM_CTRLB_SBMODE 6 +#define SAMD21_SERCOM_CTRLB_COLDEN 8 +#define SAMD21_SERCOM_CTRLB_SFDE 9 +#define SAMD21_SERCOM_CTRLB_ENC 10 +#define SAMD21_SERCOM_CTRLB_PMODE 13 +#define SAMD21_SERCOM_CTRLB_TXEN 16 +#define SAMD21_SERCOM_CTRLB_RXEN 17 +#define SAMD21_SERCOM_CTRLB_FIFOCLR 22 + +/* I2C mode */ +#define SAMD21_SERCOM_CTRLB_SMEN 8 +#define SAMD21_SERCOM_CTRLB_QCEN 9 +#define SAMD21_SERCOM_CTRLB_CMD 16 +#define SAMD21_SERCOM_CTRLB_CMD_NOP 0 +#define SAMD21_SERCOM_CTRLB_CMD_START 1 +#define SAMD21_SERCOM_CTRLB_CMD_READ 2 +#define SAMD21_SERCOM_CTRLB_CMD_STOP 3 +#define SAMD21_SERCOM_CTRLB_ACKACT 18 +#define SAMD21_SERCOM_CTRLB_ACKACT_ACK 0 +#define SAMD21_SERCOM_CTRLB_ACKACT_NACK 1 +#define SAMD21_SERCOM_CTRLB_FIFOCLR 22 + +/* SPI mode */ +#define SAMD21_SERCOM_CTRLB_CHSIZE 0 +# define SAMD21_SERCOM_CTRLB_CHSIZE_8 0 +#define SAMD21_SERCOM_CTRLB_PLOADEN 6 +#define SAMD21_SERCOM_CTRLB_SSDE 9 +#define SAMD21_SERCOM_CTRLB_MSSEN 13 +#define SAMD21_SERCOM_CTRLB_AMODE 14 +#define SAMD21_SERCOM_CTRLB_RXEN 17 + +/* USART mode */ +#define SAMD21_SERCOM_INTFLAG_DRE 0 +#define SAMD21_SERCOM_INTFLAG_TXC 1 +#define SAMD21_SERCOM_INTFLAG_RXC 2 +#define SAMD21_SERCOM_INTFLAG_RXS 3 +#define SAMD21_SERCOM_INTFLAG_CTSIC 4 +#define SAMD21_SERCOM_INTFLAG_RXBRK 5 +#define SAMD21_SERCOM_INTFLAG_ERROR 7 + +/* I2C mode */ +#define SAMD21_SERCOM_INTFLAG_ERROR 7 +#define SAMD21_SERCOM_INTFLAG_RXFF 4 +#define SAMD21_SERCOM_INTFLAG_TXFE 3 +#define SAMD21_SERCOM_INTFLAG_SB 1 +#define SAMD21_SERCOM_INTFLAG_MB 0 + +/* SPI mode */ +#define SAMD21_SERCOM_INTFLAG_SSL 3 + +#define SAMD21_SERCOM_INTENCLR_DRE 0 +#define SAMD21_SERCOM_INTENCLR_TXC 1 +#define SAMD21_SERCOM_INTENCLR_RXC 2 +#define SAMD21_SERCOM_INTENCLR_RXS 3 +#define SAMD21_SERCOM_INTENCLR_CTSIC 4 +#define SAMD21_SERCOM_INTENCLR_RXBRK 5 +#define SAMD21_SERCOM_INTENCLR_ERROR 7 + +#define SAMD21_SERCOM_STATUS_PERR 0 +#define SAMD21_SERCOM_STATUS_FERR 1 +#define SAMD21_SERCOM_STATUS_BUFOVF 2 +#define SAMD21_SERCOM_STATUS_CTS 3 +#define SAMD21_SERCOM_STATUS_ISF 4 +#define SAMD21_SERCOM_STATUS_COLL 5 +#define SAMD21_SERCOM_STATUS_TXE 6 + +#define SAMD21_SERCOM_SYNCBUSY_SWRST 0 +#define SAMD21_SERCOM_SYNCBUSY_ENABLE 1 +#define SAMD21_SERCOM_SYNCBUSY_CTRLB 2 +#define SAMD21_SERCOM_SYNCBUSY_SYSOP 2 + +#define SAMD21_SERCOM_ADDR_ADDR 0 +#define SAMD21_SERCOM_ADDR_LENEN 13 +#define SAMD21_SERCOM_ADDR_HS 14 +#define SAMD21_SERCOM_ADDR_TENBITEN 15 +#define SAMD21_SERCOM_ADDR_LEN 16 + +#define SAMD21_SERCOM_DBGCTRL_DBGSTOP 0 + +#define SAMD21_SERCOM_FIFOSPACE_TXSPACE 0 +#define SAMD21_SERCOM_FIFOSPACE_TXSPACE_MASK 0x1f +#define SAMD21_SERCOM_FIFOSPACE_RXSPACE 8 +#define SAMD21_SERCOM_FIFOSPACE_RXSPACE_MASK 0x1f + +#define SAMD21_SERCOM_FIFOPTR_CPUWRPTR 0 +#define SAMD21_SERCOM_FIFOPTR_CPUWRPTR_MASK 0xf +#define SAMD21_SERCOM_FIFOPTR_CPURDPTR 8 +#define SAMD21_SERCOM_FIFOPTR_CPURDPTR_MASK 0xf + +/* The SYSTICK starts at 0xe000e010 */ +struct samd21_systick { + vuint32_t csr; + vuint32_t rvr; + vuint32_t cvr; + vuint32_t calib; +}; + +extern struct samd21_systick samd21_systick; + +#define samd21_systick (*(struct samd21_systick *) 0xe000e010) + +#define SAMD21_SYSTICK_CSR_ENABLE 0 +#define SAMD21_SYSTICK_CSR_TICKINT 1 +#define SAMD21_SYSTICK_CSR_CLKSOURCE 2 +#define SAMD21_SYSTICK_CSR_CLKSOURCE_EXTERNAL 0 +#define SAMD21_SYSTICK_CSR_CLKSOURCE_HCLK_8 1 +#define SAMD21_SYSTICK_CSR_COUNTFLAG 16 + +#define SAMD21_SYSTICK_PRI 15 + +/* The NVIC starts at 0xe000e100, so add that to the offsets to find the absolute address */ + +struct samd21_nvic { + vuint32_t iser; /* 0x000 0xe000e100 Set Enable Register */ + + uint8_t _unused020[0x080 - 0x004]; + + vuint32_t icer; /* 0x080 0xe000e180 Clear Enable Register */ + + uint8_t _unused0a0[0x100 - 0x084]; + + vuint32_t ispr; /* 0x100 0xe000e200 Set Pending Register */ + + uint8_t _unused120[0x180 - 0x104]; + + vuint32_t icpr; /* 0x180 0xe000e280 Clear Pending Register */ + + uint8_t _unused1a0[0x300 - 0x184]; + + vuint32_t ipr[8]; /* 0x300 0xe000e400 Priority Register */ +}; + +extern struct samd21_nvic samd21_nvic; + +#define samd21_nvic (*(struct samd21_nvic *) 0xe000e100) + +#define SAMD21_NVIC_ISR_PM_POS 0 +#define SAMD21_NVIC_ISR_SYSCTRL_POS 1 +#define SAMD21_NVIC_ISR_WDT_POS 2 +#define SAMD21_NVIC_ISR_RTC_POS 3 +#define SAMD21_NVIC_ISR_EIC_POS 4 +#define SAMD21_NVIC_ISR_NVMCTRL_POS 5 +#define SAMD21_NVIC_ISR_DMAC_POS 6 +#define SAMD21_NVIC_ISR_USB_POS 7 +#define SAMD21_NVIC_ISR_EVSYS_POS 8 +#define SAMD21_NVIC_ISR_SERCOM0_POS 9 +#define SAMD21_NVIC_ISR_SERCOM1_POS 10 +#define SAMD21_NVIC_ISR_SERCOM2_POS 11 +#define SAMD21_NVIC_ISR_SERCOM3_POS 12 +#define SAMD21_NVIC_ISR_SERCOM4_POS 13 +#define SAMD21_NVIC_ISR_SERCOM5_POS 14 +#define SAMD21_NVIC_ISR_TCC0_POS 15 +#define SAMD21_NVIC_ISR_TCC1_POS 16 +#define SAMD21_NVIC_ISR_TCC2_POS 17 +#define SAMD21_NVIC_ISR_TC3_POS 18 +#define SAMD21_NVIC_ISR_TC4_POS 19 +#define SAMD21_NVIC_ISR_TC5_POS 20 +#define SAMD21_NVIC_ISR_TC6_POS 21 +#define SAMD21_NVIC_ISR_TC7_POS 22 +#define SAMD21_NVIC_ISR_ADC_POS 23 +#define SAMD21_NVIC_ISR_AC_POS 24 +#define SAMD21_NVIC_ISR_DAC_POS 25 +#define SAMD21_NVIC_ISR_PTC_POS 26 +#define SAMD21_NVIC_ISR_I2S_POS 27 +#define SAMD21_NVIC_ISR_AC1_POS 28 +#define SAMD21_NVIC_ISR_TCC3_POS 29 + +#define IRQ_MASK(irq) (1 << (irq)) +#define IRQ_BOOL(v,irq) (((v) >> (irq)) & 1) + +static inline void +samd21_nvic_set_enable(int irq) { + samd21_nvic.iser = IRQ_MASK(irq); +} + +static inline void +samd21_nvic_clear_enable(int irq) { + samd21_nvic.icer = IRQ_MASK(irq); +} + +static inline int +samd21_nvic_enabled(int irq) { + return IRQ_BOOL(samd21_nvic.iser, irq); +} + +static inline void +samd21_nvic_set_pending(int irq) { + samd21_nvic.ispr = IRQ_MASK(irq); +} + +static inline void +samd21_nvic_clear_pending(int irq) { + samd21_nvic.icpr = IRQ_MASK(irq); +} + +static inline int +samd21_nvic_pending(int irq) { + return IRQ_BOOL(samd21_nvic.ispr, irq); +} + +#define IRQ_PRIO_REG(irq) ((irq) >> 2) +#define IRQ_PRIO_BIT(irq) (((irq) & 3) << 3) +#define IRQ_PRIO_MASK(irq) (0xffU << IRQ_PRIO_BIT(irq)) + +static inline void +samd21_nvic_set_priority(int irq, uint8_t prio) { + int n = IRQ_PRIO_REG(irq); + uint32_t v; + + v = samd21_nvic.ipr[n]; + v &= ~IRQ_PRIO_MASK(irq); + v |= (prio) << IRQ_PRIO_BIT(irq); + samd21_nvic.ipr[n] = v; +} + +static inline uint8_t +samd21_nvic_get_priority(int irq) { + return (samd21_nvic.ipr[IRQ_PRIO_REG(irq)] >> IRQ_PRIO_BIT(irq)) & IRQ_PRIO_MASK(0); +} + + + +/* Cortex M0+ SCB */ + +struct samd21_scb { + vuint32_t cpuid; + vuint32_t icsr; + vuint32_t vtor; + vuint32_t aircr; + + vuint32_t scr; + vuint32_t ccr; + vuint32_t shpr1; + vuint32_t shpr2; + + vuint32_t shpr3; + vuint32_t shcrs; + vuint32_t cfsr; + vuint32_t hfsr; + + uint32_t unused_30; + vuint32_t mmfar; + vuint32_t bfar; +}; + +extern struct samd21_scb samd21_scb; + +#define samd21_scb (*(struct samd21_scb *) 0xe000ed00) + +#define SAMD21_SCB_AIRCR_VECTKEY 16 +#define SAMD21_SCB_AIRCR_VECTKEY_KEY 0x05fa +#define SAMD21_SCB_AIRCR_PRIGROUP 8 +#define SAMD21_SCB_AIRCR_SYSRESETREQ 2 +#define SAMD21_SCB_AIRCR_VECTCLRACTIVE 1 +#define SAMD21_SCB_AIRCR_VECTRESET 0 + +/* The NVM Calibration and auxiliary space starts at 0x00800000 */ + +struct samd21_aux0 { + vuint64_t userrow; +}; + +extern struct samd21_aux0 samd21_aux0; + +#define samd21_aux0 (*(struct samd21_aux0 *) 0x00804000) + +#define SAMD21_AUX0_USERROW_BOOTPROT 0 +#define SAMD21_AUX0_USERROW_EEPROM 4 +#define SAMD21_AUX0_USERROW_BOD33_LEVEL 8 +#define SAMD21_AUX0_USERROW_BOD33_ENABLE 14 +#define SAMD21_AUX0_USERROW_BOD33_ACTION 15 +#define SAMD21_AUX0_USERROW_WDT_ENABLE 25 +#define SAMD21_AUX0_USERROW_WDT_ALWAYS_ON 26 +#define SAMD21_AUX0_USERROW_WDT_PERIOD 27 +#define SAMD21_AUX0_USERROW_WDT_WINDOW 31 +#define SAMD21_AUX0_USERROW_WDT_EWOFFSET 35 +#define SAMD21_AUX0_USERROW_WDT_WEN 39 +#define SAMD21_AUX0_USERROW_BOD33_HYST 40 +#define SAMD21_AUX0_USERROW_LOCK 48 + +struct samd21_aux1 { + vuint64_t reserved_00; + vuint64_t device_config; + + vuint64_t reserved_10; + vuint64_t reserved_18; + + vuint64_t calibration; + vuint64_t reserved_28; +}; + +extern struct samd21_aux1 samd21_aux1; + +#define samd21_aux1 (*(struct samd21_aux1 *) 0x00806000) + +#define SAMD21_AUX1_CALIBRATION_ADC_LINEARITY 27 +#define SAMD21_AUX1_CALIBRATION_ADC_LINEARITY_MASK 0xff +#define SAMD21_AUX1_CALIBRATION_ADC_BIASCAL 35 +#define SAMD21_AUX1_CALIBRATION_ADC_BIASCAL_MASK 0x7 +#define SAMD21_AUX1_CALIBRATION_OSC32K_CAL 38 +#define SAMD21_AUX1_CALIBRATION_USB_TRANSN 45 +#define SAMD21_AUX1_CALIBRATION_USB_TRANSN_MASK 0x1f +#define SAMD21_AUX1_CALIBRATION_USB_TRANSP 50 +#define SAMD21_AUX1_CALIBRATION_USB_TRANSP_MASK 0x1f +#define SAMD21_AUX1_CALIBRATION_USB_TRIM 55 +#define SAMD21_AUX1_CALIBRATION_USB_TRIM_MASK 0x07 +#define SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL 58 +#define SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL_MASK 0x3f + +struct samd21_serial { + vuint32_t reserved_00; + vuint32_t reserved_04; + vuint32_t reserved_08; + vuint32_t word0; + + vuint32_t reserved_10; + vuint32_t reserved_14; + vuint32_t reserved_18; + vuint32_t reserved_1c; + + vuint32_t reserved_20; + vuint32_t reserved_24; + vuint32_t reserved_28; + vuint32_t reserved_2c; + + vuint32_t reserved_30; + vuint32_t reserved_34; + vuint32_t reserved_38; + vuint32_t reserved_3c; + + vuint32_t word1; + vuint32_t word2; + vuint32_t word3; + vuint32_t reserved_4c; +}; + +extern struct samd21_serial samd21_serial; + +#define samd21_serial (*(struct samd21_serial *) 0x0080a000) + +static inline void +samd21_gclk_wait_sync(void) +{ + while (samd21_gclk.status & (1 << SAMD21_GCLK_STATUS_SYNCBUSY)) + ; +} + +static inline void +samd21_dfll_wait_sync(void) +{ + while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_DFLLRDY)) == 0) + ; +} + +static inline void +samd21_gclk_gendiv(uint32_t id, uint32_t div) +{ + if (div == 1) + div = 0; + samd21_gclk.gendiv = ((id << SAMD21_GCLK_GENDIV_ID) | + (div << SAMD21_GCLK_GENDIV_DIV)); + samd21_gclk_wait_sync(); +} + +static inline void +samd21_gclk_genctrl(uint32_t src, uint32_t id) +{ + samd21_gclk.genctrl = ((id << SAMD21_GCLK_GENCTRL_ID) | + (src << SAMD21_GCLK_GENCTRL_SRC) | + (0 << SAMD21_GCLK_GENCTRL_OE) | + (1 << SAMD21_GCLK_GENCTRL_GENEN)); + samd21_gclk_wait_sync(); +} + +static inline void +samd21_gclk_clkctrl(uint32_t gen, uint32_t id) +{ + samd21_gclk.clkctrl = (uint16_t) ((gen << SAMD21_GCLK_CLKCTRL_GEN) | + (id << SAMD21_GCLK_CLKCTRL_ID) | + (1U << SAMD21_GCLK_CLKCTRL_CLKEN)); + samd21_gclk_wait_sync(); +} + +#define isr_decl(name) \ + void samd21_ ## name ## _isr(void) + +isr_decl(halt); +isr_decl(ignore); +isr_decl(nmi); +isr_decl(hardfault); +isr_decl(memmanage); +isr_decl(busfault); +isr_decl(usagefault); +isr_decl(svc); +isr_decl(debugmon); +isr_decl(pendsv); +isr_decl(systick); +isr_decl(pm); /* IRQ0 */ +isr_decl(sysctrl); +isr_decl(wdt); +isr_decl(rtc); +isr_decl(eic); +isr_decl(nvmctrl); +isr_decl(dmac); +isr_decl(usb); +isr_decl(evsys); +isr_decl(sercom0); +isr_decl(sercom1); +isr_decl(sercom2); +isr_decl(sercom3); +isr_decl(sercom4); +isr_decl(sercom5); +isr_decl(tcc0); +isr_decl(tcc1); +isr_decl(tcc2); +isr_decl(tc3); +isr_decl(tc4); +isr_decl(tc5); +isr_decl(tc6); +isr_decl(tc7); +isr_decl(adc); +isr_decl(ac); +isr_decl(dac); +isr_decl(ptc); +isr_decl(i2s); +isr_decl(ac1); +isr_decl(tcc3); + +#undef isr_decl + +#endif /* _SAMD21_H_ */ diff --git a/src/snekboard/Makefile b/src/snekboard/Makefile new file mode 100644 index 00000000..5d4f8630 --- /dev/null +++ b/src/snekboard/Makefile @@ -0,0 +1,71 @@ +# +# AltOS build +# +# + +include ../samd21/Makefile.defs + +INC = \ + ao.h \ + ao_arch.h \ + ao_arch_funcs.h \ + ao_boot.h \ + ao_pins.h \ + ao_product.h \ + ao_task.h \ + samd21.h \ + Makefile + +ALTOS_SRC = \ + ao_boot_chain.c \ + ao_romconfig.c \ + ao_interrupt.c \ + ao_product.c \ + ao_cmd.c \ + ao_task.c \ + ao_led.c \ + ao_stdio.c \ + ao_panic.c \ + ao_timer.c \ + ao_mutex.c \ + ao_dma_samd21.c \ + ao_usb_samd21.c \ + ao_spi_samd21.c + +# SAMD21G18A + +SAMD21_ROM=256 +SAMD21_RAM=32 + +PRODUCT=SnekBoard +PRODUCT_DEF=-DSNEKBOARD +IDPRODUCT=0x000a + +CFLAGS = $(PRODUCT_DEF) $(SAMD21_CFLAGS) + +PROGNAME=snekboard +PROG=$(PROGNAME)-$(VERSION).elf +HEX=$(PROGNAME)-$(VERSION).ihx + +SRC=$(ALTOS_SRC) snekboard.c +OBJ=$(SRC:.c=.o) + +all: $(PROG) $(HEX) + +$(PROG): Makefile $(OBJ) + $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS) + +$(OBJ): $(INC) + +load: $(PROG) + stm-load $(PROG) + +distclean: clean + +clean: + rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map + rm -f ao_product.h + +install: + +uninstall: diff --git a/src/snekboard/ao_pins.h b/src/snekboard/ao_pins.h new file mode 100644 index 00000000..80b2cb77 --- /dev/null +++ b/src/snekboard/ao_pins.h @@ -0,0 +1,47 @@ +/* + * Copyright © 2022 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_PINS_H_ +#define _AO_PINS_H_ + +#define LED_0_PORT (&samd21_port_a) +#define LED_0_PIN 2 + +#define LED_BLUE (1 << 0) + +#define AO_LED_PANIC LED_BLUE + +#define HAS_BEEP 0 + +#define HAS_USB 1 +#define USE_USB_STDIO 1 + +#define HAS_LED 1 + +#define AO_XOSC 1 +#define AO_XOSC_FREQ 16000000 +#define AO_XOSC_DIV 256 +#define AO_XOSC_MUL 768 + +#define AO_AHB_PRESCALER 1 +#define AO_APBA_PRESCALER 1 + +#define HAS_SPI_0 1 +#define SPI_0_PA08_PA09_PA10 1 + +#endif /* _AO_PINS_H_ */ diff --git a/src/snekboard/flash-loader/Makefile b/src/snekboard/flash-loader/Makefile new file mode 100644 index 00000000..12330d35 --- /dev/null +++ b/src/snekboard/flash-loader/Makefile @@ -0,0 +1,8 @@ +# +# AltOS flash loader build +# +# + +TOPDIR=../.. +HARDWARE=snekboard +include $(TOPDIR)/samd21/Makefile-flash.defs diff --git a/src/snekboard/flash-loader/ao_pins.h b/src/snekboard/flash-loader/ao_pins.h new file mode 100644 index 00000000..fb399818 --- /dev/null +++ b/src/snekboard/flash-loader/ao_pins.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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 + +/* ANALOG1 to gnd for boot loader mode */ + +#define AO_BOOT_PIN 1 +#define AO_BOOT_APPLICATION_GPIO (samd21_port_b) +#define AO_BOOT_APPLICATION_PIN 8 +#define AO_BOOT_APPLICATION_VALUE 1 +#define AO_BOOT_APPLICATION_MODE AO_MODE_PULL_UP + +/* USB */ +#define HAS_USB 1 + +#define AO_XOSC 1 +#define AO_XOSC_FREQ 16000000 +#define AO_XOSC_DIV 256 +#define AO_XOSC_MUL 768 + +#endif /* _AO_PINS_H_ */ diff --git a/src/snekboard/snekboard.c b/src/snekboard/snekboard.c new file mode 100644 index 00000000..719ea01d --- /dev/null +++ b/src/snekboard/snekboard.c @@ -0,0 +1,55 @@ +/* + * Copyright © 2016 Keith Packard + * + * 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 +#include +#include + +#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; +} diff --git a/src/stm32l0/ao_arch_funcs.h b/src/stm32l0/ao_arch_funcs.h index f03d9695..385fe0e3 100644 --- a/src/stm32l0/ao_arch_funcs.h +++ b/src/stm32l0/ao_arch_funcs.h @@ -139,8 +139,7 @@ ao_spi_recv_byte(uint8_t spi_index) void ao_spi_recv(void *block, uint16_t len, uint8_t spi_index); -void -ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index); +#define AO_SPI_DUPLEX 0 void ao_spi_init(void); diff --git a/src/telebt-v4.0/ao_pins.h b/src/telebt-v4.0/ao_pins.h index ce2d949f..6efa407d 100644 --- a/src/telebt-v4.0/ao_pins.h +++ b/src/telebt-v4.0/ao_pins.h @@ -178,50 +178,10 @@ struct ao_adc { #define AO_RN_SW_BTN_PORT (&stm_gpioc) #define AO_RN_SW_BTN_PIN 14 -/* Pin 9. BM70 P2_3 */ -#define AO_RN_WAKEUP_PORT (&stm_gpiob) -#define AO_RN_WAKEUP_PIN 9 - -/* Pin 11. BM70 P2_7/tx_ind. Status indication along with P1_5 */ -#define AO_RN_P0_4_PORT (&stm_gpioc) -#define AO_RN_P0_4_PIN 13 - -/* Pin 12. BM70 P1_1. Status indication along with P0_4 */ +/* Pin 12. BM70 P1_5. Status indication along with P0_4 */ #define AO_RN_P1_5_PORT (&stm_gpiob) #define AO_RN_P1_5_PIN 6 -/* Pin 13. BM70 P1_2. Also I2C SCL */ -#define AO_RN_P1_2_PORT (&stm_gpiob) -#define AO_RN_P1_2_PIN 7 - -/* Pin 14. BM70 P1_3. Also I2C SDA */ -#define AO_RN_P1_3_PORT (&stm_gpiob) -#define AO_RN_P1_3_PIN 8 - -/* Pin 15. BM70 P0_0/cts. */ -#define AO_RN_CTS_PORT (&stm_gpioa) -#define AO_RN_CTS_PIN 1 - -/* Pin 16. BM70 P1_0. */ -#define AO_RN_P0_5_PORT (&stm_gpiob) -#define AO_RN_P0_5_PIN 5 - -/* Pin 17. BM70 P3_6. */ -#define AO_RN_RTS_PORT (&stm_gpioa) -#define AO_RN_RTS_PIN 0 - -/* Pin 18. BM70 P2_0. */ -#define AO_RN_P2_0_PORT (&stm_gpiob) -#define AO_RN_P2_0_PIN 3 - -/* Pin 19. BM70 P2_4. */ -#define AO_RN_P2_4_PORT (&stm_gpioa) -#define AO_RN_P2_4_PIN 10 - -/* Pin 20. BM70 NC. */ -#define AO_RN_EAN_PORT -#define AO_RN_EAN_PIN - /* Pin 21. BM70 RST_N. */ #define AO_RN_RST_N_PORT (&stm_gpioa) #define AO_RN_RST_N_PIN 15 @@ -238,22 +198,6 @@ struct ao_adc { #define AO_RN_P3_1_PORT (&stm_gpiob) #define AO_RN_P3_1_PIN 2 -/* Pin 25. BM70 P3_2/LINK_DROP. */ -#define AO_RN_P3_2_PORT (&stm_gpioa) -#define AO_RN_P3_2_PIN 8 - -/* Pin 26. BM70 P3_3/UART_RX_IND. */ -#define AO_RN_P3_3_PORT (&stm_gpiob) -#define AO_RN_P3_3_PIN 15 - -/* Pin 27. BM70 P3_4/PAIRING_KEY. */ -#define AO_RN_P3_4_PORT (&stm_gpiob) -#define AO_RN_P3_4_PIN 14 - -/* Pin 28. BM70 P3_5. */ -#define AO_RN_P3_6_PORT (&stm_gpiob) -#define AO_RN_P3_6_PIN 13 - /* Pin 29. BM70 P0_7. */ #define AO_RN_P3_7_PORT (&stm_gpiob) #define AO_RN_P3_7_PIN 12 diff --git a/src/telemetrum-v4.0/Makefile b/src/telemetrum-v4.0/Makefile new file mode 100644 index 00000000..51916407 --- /dev/null +++ b/src/telemetrum-v4.0/Makefile @@ -0,0 +1,122 @@ +# +# AltOS build +# +# + +include ../samd21/Makefile.defs + +INC = \ + ao.h \ + ao_arch.h \ + ao_arch_funcs.h \ + ao_boot.h \ + ao_companion.h \ + ao_data.h \ + ao_sample.h \ + ao_pins.h \ + altitude-pa.h \ + ao_kalman.h \ + ao_product.h \ + ao_ms5607.h \ + ao_adxl375.h \ + ao_cc1200_CC1200.h \ + ao_task.h \ + ao_whiten.h \ + samd21.h \ + Makefile + +# SAMD21G17D + +SAMD21_ROM=128 +SAMD21_RAM=16 + +#PROFILE=ao_profile.c +#PROFILE_DEF=-DAO_PROFILE=1 + +#SAMPLE_PROFILE=ao_sample_profile.c \ +# ao_sample_profile_timer.c +#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1 + +#STACK_GUARD=ao_mpu_stm.c +#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1 + +ALTOS_SRC = \ + ao_boot_chain.c \ + ao_interrupt.c \ + ao_product.c \ + ao_romconfig.c \ + ao_cmd.c \ + ao_config.c \ + ao_task.c \ + ao_led.c \ + ao_stdio.c \ + ao_panic.c \ + ao_timer.c \ + ao_mutex.c \ + ao_serial_samd21.c \ + ao_gps_ublox.c \ + ao_gps_show.c \ + ao_gps_report_metrum.c \ + ao_ignite.c \ + ao_freq.c \ + ao_dma_samd21.c \ + ao_spi_samd21.c \ + ao_cc1200.c \ + ao_data.c \ + ao_ms5607.c \ + ao_adxl375.c \ + ao_adc_samd21.c \ + ao_beep_samd21.c \ + ao_storage.c \ + ao_m25.c \ + ao_usb_samd21.c \ + ao_exti_samd21.c \ + ao_report.c \ + ao_convert_pa.c \ + ao_convert_volt.c \ + ao_log.c \ + ao_log_metrum.c \ + ao_sample.c \ + ao_kalman.c \ + ao_flight.c \ + ao_telemetry.c \ + ao_packet_slave.c \ + ao_packet.c \ + ao_companion.c \ + ao_aprs.c \ + $(PROFILE) \ + $(SAMPLE_PROFILE) \ + $(STACK_GUARD) + +PRODUCT=TeleMetrum-v4.0 +PRODUCT_DEF=-DTELEMETRUM_V_4_0 +IDPRODUCT=0x000b + +CFLAGS = $(PRODUCT_DEF) $(SAMD21_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) + +PROGNAME=telemetrum-v4.0 +PROG=$(PROGNAME)-$(VERSION).elf +HEX=$(PROGNAME)-$(VERSION).ihx + +SRC=$(ALTOS_SRC) ao_telemetrum.c +OBJ=$(SRC:.c=.o) + +all: $(PROG) $(HEX) + +$(PROG): Makefile $(OBJ) + $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS) + +$(OBJ): $(INC) + +load: $(PROG) + stm-load $(PROG) + +distclean: clean + +clean: + rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map + rm -f ao_product.h + +install: + +uninstall: diff --git a/src/telemetrum-v4.0/ao_pins.h b/src/telemetrum-v4.0/ao_pins.h new file mode 100644 index 00000000..bc057611 --- /dev/null +++ b/src/telemetrum-v4.0/ao_pins.h @@ -0,0 +1,280 @@ +/* + * Copyright © 2022 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_PINS_H_ +#define _AO_PINS_H_ + +#define AO_XOSC 1 +#define AO_XOSC_FREQ 16000000 +#define AO_XOSC_DIV 256 +#define AO_XOSC_MUL 768 + +#define AO_AHB_PRESCALER 1 +#define AO_APBA_PRESCALER 1 + +#define HAS_SERIAL_1 1 +#define USE_SERIAL_1_STDIN 0 + +#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX (512 * 1024) +#define AO_CONFIG_MAX_SIZE 1024 +#define LOG_ERASE_MARK 0x55 +#define LOG_MAX_ERASE 128 +#define AO_LOG_FORMAT AO_LOG_FORMAT_TELEMETRUM + +#define HAS_EEPROM 1 +#define USE_INTERNAL_FLASH 0 +#define USE_EEPROM_CONFIG 0 +#define USE_STORAGE_CONFIG 1 +#define HAS_USB 1 +#define USE_USB_STDIO 1 +#define HAS_BATTERY_REPORT 1 +#define BEEPER_CHANNEL 4 +#define BEEPER_TIMER 3 +#define BEEPER_PORT (&samd21_port_a) +#define BEEPER_PIN 16 +#define HAS_RADIO 1 +#define HAS_RADIO_10MW 1 +#define HAS_TELEMETRY 1 +#define HAS_APRS 1 +#define HAS_COMPANION 1 + +#define HAS_SPI_0 1 +#define HAS_SPI_3 1 +#define HAS_SPI_5 1 + +#define PACKET_HAS_SLAVE 1 +#define PACKET_HAS_MASTER 0 + +#define LOW_LEVEL_DEBUG 0 + +#define HAS_LED 1 +#define LED_0_PORT (&samd21_port_b) +#define LED_0_PIN 10 +#define LED_1_PORT (&samd21_port_b) +#define LED_1_PIN 11 +#define AO_LED_RED (1 << 0) +#define AO_LED_GREEN (1 << 1) + +#define HAS_GPS 1 +#define HAS_FLIGHT 1 +#define HAS_ADC 1 +#define HAS_ADC_TEMP 1 +#define HAS_LOG 1 + +/* + * Beeper + */ + +#define HAS_BEEP 1 +/* Beep on PA16 function E TCC2.0 */ + +#define AO_BEEP_TCC (&samd21_tcc2) +#define AO_BEEP_TCC_APBC_MASK SAMD21_PM_APBCMASK_TCC2 +#define AO_BEEP_PORT (&samd21_port_a) +#define AO_BEEP_PIN (16) +#define AO_BEEP_FUNC SAMD21_PORT_PMUX_FUNC_E + +/* + * Igniter + */ + +#define HAS_IGNITE 1 +#define HAS_IGNITE_REPORT 1 + +#define AO_SENSE_DROGUE(p) ((p)->adc.sense_a) +#define AO_SENSE_MAIN(p) ((p)->adc.sense_m) +#define AO_IGNITER_CLOSED 400 +#define AO_IGNITER_OPEN 60 + +/* Drogue */ +#define AO_IGNITER_DROGUE_PORT (&samd21_port_a) +#define AO_IGNITER_DROGUE_PIN 19 + +/* Main */ +#define AO_IGNITER_MAIN_PORT (&samd21_port_a) +#define AO_IGNITER_MAIN_PIN 18 + +/* + * ADC + */ +#define AO_DATA_RING 32 +#define AO_ADC_NUM_SENSE 2 + +struct ao_adc { + int16_t sense_a; + int16_t sense_m; + int16_t v_batt; + int16_t temp; +}; + +#define AO_ADC_DUMP(p) \ + printf("tick: %5lu drogue: %5d main: %5d batt: %5d\n", \ + (p)->tick, \ + (p)->adc.sense_a, (p)->adc.sense_m, \ + (p)->adc.v_batt); + +#define AO_ADC_SENSE_DROGUE 18 +#define AO_ADC_SENSE_DROGUE_PORT (&samd21_port_a) +#define AO_ADC_SENSE_DROGUE_PIN 10 + +#define AO_ADC_SENSE_MAIN 19 +#define AO_ADC_SENSE_MAIN_PORT (&samd21_port_a) +#define AO_ADC_SENSE_MAIN_PIN 11 + +#define AO_ADC_V_BATT 17 +#define AO_ADC_V_BATT_PORT (&samd21_port_a) +#define AO_ADC_V_BATT_PIN 9 + +#define AO_ADC_TEMP SAMD21_ADC_INPUTCTRL_MUXPOS_TEMP + +#define AO_NUM_ADC_PIN 3 + +#define AO_ADC_PIN0_PORT AO_ADC_SENSE_DROGUE_PORT +#define AO_ADC_PIN0_PIN AO_ADC_SENSE_DROGUE_PIN +#define AO_ADC_PIN1_PORT AO_ADC_SENSE_MAIN_PORT +#define AO_ADC_PIN1_PIN AO_ADC_SENSE_MAIN_PIN +#define AO_ADC_PIN2_PORT AO_ADC_V_BATT_PORT +#define AO_ADC_PIN2_PIN AO_ADC_V_BATT_PIN + +#define AO_NUM_ADC (AO_NUM_ADC_PIN + 1) + +#define AO_ADC_SQ0 AO_ADC_SENSE_DROGUE +#define AO_ADC_SQ1 AO_ADC_SENSE_MAIN +#define AO_ADC_SQ2 AO_ADC_V_BATT +#define AO_ADC_SQ3 AO_ADC_TEMP + +/* + * Voltage divider on ADC battery sampler + */ +#define AO_BATTERY_DIV_PLUS 56 /* 5.6k */ +#define AO_BATTERY_DIV_MINUS 100 /* 10k */ + +/* + * Voltage divider on ADC igniter samplers + */ +#define AO_IGNITE_DIV_PLUS 100 /* 100k */ +#define AO_IGNITE_DIV_MINUS 27 /* 27k */ + +/* + * ADC reference in decivolts + */ +#define AO_ADC_REFERENCE_DV 33 + +/* + * GPS + */ + +#define AO_SERIAL_SPEED_UBLOX AO_SERIAL_SPEED_9600 + +#define HAS_SERIAL_1 1 +#define USE_SERIAL_1_STDIN 0 +#define SERIAL_1_PA00_PA01 1 + +#define ao_gps_getchar ao_serial1_getchar +#define ao_gps_putchar ao_serial1_putchar +#define ao_gps_set_speed ao_serial1_set_speed +#define ao_gps_fifo (ao_samd21_usart1.rx_fifo) + +/* + * Pressure sensor settings + */ +#define HAS_MS5607 1 +#define HAS_MS5611 0 +#define AO_MS5607_PRIVATE_PINS 0 +#define AO_MS5607_CS_PORT (&samd21_port_a) +#define AO_MS5607_CS_PIN 21 +#define AO_MS5607_MISO_PORT (&samd21_port_a) +#define AO_MS5607_MISO_PIN 20 +#define AO_MS5607_SPI_INDEX AO_SPI_3_PA22_PA23_PA20 + +/* + * SPI Flash memory + */ + +#define M25_MAX_CHIPS 1 +#define AO_M25_SPI_CS_PORT (&samd21_port_a) +#define AO_M25_SPI_CS_MASK (1 << 27) +#define AO_M25_SPI_BUS AO_SPI_5_PB22_PB23_PB03 + + +/* + * Radio (cc1200) + */ + +/* gets pretty close to 434.550 */ + +#define AO_RADIO_CAL_DEFAULT 5695733 + +#define AO_CC1200_SPI_CS_PORT (&samd21_port_a) +#define AO_CC1200_SPI_CS_PIN 7 +#define AO_CC1200_SPI_BUS AO_SPI_5_PB22_PB23_PB03 + +#define AO_CC1200_INT_PORT (&samd21_port_b) +#define AO_CC1200_INT_PIN (8) +#define AO_CC1200_MCU_WAKEUP_PORT (&samd21_port_b) +#define AO_CC1200_MCU_WAKEUP_PIN (9) + +#define AO_CC1200_INT_GPIO 2 +#define AO_CC1200_INT_GPIO_IOCFG CC1200_IOCFG2 + +#define AO_CC1200_MARC_GPIO 3 +#define AO_CC1200_MARC_GPIO_IOCFG CC1200_IOCFG3 + +#define HAS_BOOT_RADIO 0 + +#define HAS_HIGHG_ACCEL 1 + +/* ADXL375 */ + +#define HAS_ADXL375 1 +#define AO_ADXL375_CS_PORT (&samd21_port_a) +#define AO_ADXL375_CS_PIN 8 +#define AO_ADXL375_SPI_INDEX (AO_SPI_0_PA04_PA05_PA06 | AO_SPI_MODE_3) + +#define AO_ADXL375_AXIS x +#define AO_ADXL375_INVERT 1 + +#define NUM_CMDS 16 + +/* + * Companion + */ + +#define AO_COMPANION_CS_PORT (&samd21_port_a) +#define AO_COMPANION_CS_PIN (13) +#define AO_COMPANION_SPI_BUS AO_SPI_5_PB22_PB23_PB03 + +/* + * Monitor + */ + +#define HAS_MONITOR 0 +#define LEGACY_MONITOR 0 +#define HAS_MONITOR_PUT 1 +#define AO_MONITOR_LED 0 +#define HAS_RSSI 0 + +/* + * Profiling Viterbi decoding + */ + +#ifndef AO_PROFILE +#define AO_PROFILE 0 +#endif + +#endif /* _AO_PINS_H_ */ diff --git a/src/telemetrum-v4.0/ao_telemetrum.c b/src/telemetrum-v4.0/ao_telemetrum.c new file mode 100644 index 00000000..db0fc6a6 --- /dev/null +++ b/src/telemetrum-v4.0/ao_telemetrum.c @@ -0,0 +1,88 @@ +/* + * Copyright © 2016 Keith Packard + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#if HAS_SAMPLE_PROFILE +#include +#include +#endif +#if HAS_STACK_GUARD +#include +#endif +#include + +int +main(void) +{ + ao_clock_init(); + +#if HAS_STACK_GUARD + ao_mpu_init(); +#endif + + ao_task_init(); + ao_serial_init(); + ao_led_init(); + ao_led_on(LEDS_AVAILABLE); + ao_timer_init(); + + ao_spi_init(); + ao_dma_init(); + ao_exti_init(); + + ao_adc_init(); + ao_beep_init(); + ao_cmd_init(); + + ao_ms5607_init(); + ao_adxl375_init(); + + ao_storage_init(); + + ao_flight_init(); + ao_log_init(); + ao_report_init(); + + ao_usb_init(); + ao_gps_init(); + ao_gps_report_metrum_init(); + ao_telemetry_init(); + ao_radio_init(); + ao_packet_slave_init(false); + ao_igniter_init(); + ao_companion_init(); + + ao_config_init(); +#if AO_PROFILE + ao_profile_init(); +#endif +#if HAS_SAMPLE_PROFILE + ao_sample_profile_init(); +#endif + ao_led_off(LEDS_AVAILABLE); + + ao_start_scheduler(); + return 0; +} diff --git a/src/telemetrum-v4.0/flash-loader/Makefile b/src/telemetrum-v4.0/flash-loader/Makefile new file mode 100644 index 00000000..5fabbc08 --- /dev/null +++ b/src/telemetrum-v4.0/flash-loader/Makefile @@ -0,0 +1,8 @@ +# +# AltOS flash loader build +# +# + +TOPDIR=../.. +HARDWARE=telemetrum-v4.0 +include $(TOPDIR)/samd21/Makefile-flash.defs diff --git a/src/telemetrum-v4.0/flash-loader/ao_pins.h b/src/telemetrum-v4.0/flash-loader/ao_pins.h new file mode 100644 index 00000000..5653c24c --- /dev/null +++ b/src/telemetrum-v4.0/flash-loader/ao_pins.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2022 Bdale Garbee + * + * 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 + +/* 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_ */