From: Keith Packard Date: Sun, 25 Sep 2022 20:55:43 +0000 (-0700) Subject: altos: Add SAMD21 bits X-Git-Tag: 1.9.13~1^2~26^2~54^2~6 X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=c59892cd337162c63e5d7ba8e8eec779d201022d;p=fw%2Faltos altos: Add SAMD21 bits Brought back from snek, these bits provide Altos support for the Atmel SAMD21 family of chips. Signed-off-by: Keith Packard --- 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..eb6a267d --- /dev/null +++ b/src/samd21/Makefile.defs @@ -0,0 +1,9 @@ +ifndef TOPDIR +TOPDIR=.. +endif + +include $(TOPDIR)/samd21/Makefile-samd21.defs + +LDFLAGS=-nostartfiles $(CFLAGS) -L$(TOPDIR)/samd21 -Taltos.ld -n + +.DEFAULT_GOAL=all diff --git a/src/samd21/altos-loader.ld b/src/samd21/altos-loader.ld new file mode 100644 index 00000000..6c12d324 --- /dev/null +++ b/src/samd21/altos-loader.ld @@ -0,0 +1,25 @@ +/* + * 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 = 32k; +__stack_size = 256; + +INCLUDE picolibc.ld diff --git a/src/samd21/altos.ld b/src/samd21/altos.ld new file mode 100644 index 00000000..b5cb413e --- /dev/null +++ b/src/samd21/altos.ld @@ -0,0 +1,25 @@ +/* + * 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 = 32k; +__stack_size = 256; + +INCLUDE picolibc.ld diff --git a/src/samd21/ao_adc_samd21.c b/src/samd21/ao_adc_samd21.c new file mode 100644 index 00000000..b2e6aa7f --- /dev/null +++ b/src/samd21/ao_adc_samd21.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. + */ + +#include +#include + +static void +ao_adc_sync(void) +{ + while (samd21_adc.status & (1 << SAMD21_ADC_STATUS_SYNCBUSY)) + ; +} + +static uint16_t +ao_adc_do_conversion(void) +{ + ao_adc_sync(); + samd21_adc.swtrig = (1 << SAMD21_ADC_SWTRIG_START); + ao_adc_sync(); + while ((samd21_adc.intflag & (1 << SAMD21_ADC_INTFLAG_RESRDY)) == 0) + ao_adc_sync(); + ao_adc_sync(); + return samd21_adc.result; +} + +uint16_t +ao_adc_read(uint8_t channel) +{ + ao_adc_sync(); + samd21_adc.inputctrl = ((channel << SAMD21_ADC_INPUTCTRL_MUXPOS) | + (SAMD21_ADC_INPUTCTRL_MUXNEG_GND << SAMD21_ADC_INPUTCTRL_MUXNEG) | + (0 << SAMD21_ADC_INPUTCTRL_INPUTSCAN) | + (0 << SAMD21_ADC_INPUTCTRL_INPUTOFFSET) | + (SAMD21_ADC_INPUTCTRL_GAIN_DIV2 << SAMD21_ADC_INPUTCTRL_GAIN)); + + /* Read twice and discard the first value as recommended by app note + * http://www.atmel.com/images/Atmel-42645-ADC-Configurations-with-Examples_ApplicationNote_AT11481.pdf + */ + (void) ao_adc_do_conversion(); + return ao_adc_do_conversion(); +} + +void +ao_adc_init(void) +{ + /* supply a clock */ + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_ADC); + + /* enable the device */ + samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_ADC); + + /* Reset */ + samd21_adc.ctrla = (1 << SAMD21_ADC_CTRLA_SWRST); + + ao_adc_sync(); + + while ((samd21_adc.ctrla & (1 << SAMD21_ADC_CTRLA_SWRST)) != 0 || + (samd21_adc.status & (1 << SAMD21_ADC_STATUS_SYNCBUSY)) != 0) + ao_adc_sync(); + + /* Load ADC calibration values */ + uint32_t b = (samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_ADC_BIASCAL) & SAMD21_AUX1_CALIBRATION_ADC_BIASCAL_MASK; + uint32_t l = (samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_ADC_LINEARITY) & SAMD21_AUX1_CALIBRATION_ADC_LINEARITY_MASK; + + samd21_adc.calib = ((b << SAMD21_ADC_CALIB_BIAS_CAL) | + (l << SAMD21_ADC_CALIB_LINEARITY_CAL)); + + + ao_adc_sync(); + + samd21_adc.ctrlb = ((0 << SAMD21_ADC_CTRLB_DIFFMODE) | + (0 << SAMD21_ADC_CTRLB_LEFTADJ) | + (0 << SAMD21_ADC_CTRLB_FREERUN) | + (0 << SAMD21_ADC_CTRLB_CORREN) | + (SAMD21_ADC_CTRLB_RESSEL_12BIT << SAMD21_ADC_CTRLB_RESSEL) | + (SAMD21_ADC_CTRLB_PRESCALER_DIV512 << SAMD21_ADC_CTRLB_PRESCALER)); + + ao_adc_sync(); + + samd21_adc.sampctrl = 0x1f; + + ao_adc_sync(); + + samd21_adc.refctrl = (SAMD21_ADC_REFCTRL_REFSEL_INTVCC1 << SAMD21_ADC_REFCTRL_REFSEL); + + ao_adc_sync(); + + samd21_adc.ctrla = (1 << SAMD21_ADC_CTRLA_ENABLE); +} diff --git a/src/samd21/ao_adc_samd21.h b/src/samd21/ao_adc_samd21.h new file mode 100644 index 00000000..dd562a05 --- /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_ + +uint16_t +ao_adc_read(uint8_t channel); + +void +ao_adc_init(void); + +#endif /* _AO_ADC_SAMD21_H_ */ diff --git a/src/samd21/ao_apa102.c b/src/samd21/ao_apa102.c new file mode 100644 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..af2ca3e5 --- /dev/null +++ b/src/samd21/ao_arch.h @@ -0,0 +1,209 @@ +/* + * 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 +#define AO_STACK_SIZE 2048 + +#ifndef HAS_BOOT_LOADER +#define HAS_BOOT_LOADER 1 +#endif + +#if HAS_BOOT_LOADER +#define AO_BOOT_APPLICATION_BASE ((uint32_t *) 0x00001000) +#ifndef AO_BOOT_APPLICATION_BOUND +#define AO_BOOT_APPLICATION_BOUND ((uint32_t *) (0x00000000 + samd21_flash_size())) +#endif +#define AO_BOOT_LOADER_BASE ((uint32_t *) 0x00000000) +#endif + +#endif /* _AO_ARCH_H_ */ diff --git a/src/samd21/ao_arch_funcs.h b/src/samd21/ao_arch_funcs.h new file mode 100644 index 00000000..8ef93aa4 --- /dev/null +++ b/src/samd21/ao_arch_funcs.h @@ -0,0 +1,302 @@ +/* + * 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); +} + +#define ARM_PUSH32(stack, val) (*(--(stack)) = (val)) + +typedef uint32_t ao_arch_irq_t; + +static inline uint32_t +ao_arch_irqsave(void) { + uint32_t primask; + asm("mrs %0,primask" : "=&r" (primask)); + ao_arch_block_interrupts(); + return primask; +} + +static inline void +ao_arch_irqrestore(uint32_t primask) { + asm("msr primask,%0" : : "r" (primask)); +} + +static inline void +ao_arch_memory_barrier(void) { + asm volatile("" ::: "memory"); +} + +#if HAS_TASK +static inline void +ao_arch_init_stack(struct ao_task *task, uint32_t *sp, void *start) +{ + uint32_t a = (uint32_t) start; + int i; + + /* Return address (goes into LR) */ + ARM_PUSH32(sp, a); + + /* Clear register values r0-r7 */ + i = 8; + while (i--) + ARM_PUSH32(sp, 0); + + /* APSR */ + ARM_PUSH32(sp, 0); + + /* PRIMASK with interrupts enabled */ + ARM_PUSH32(sp, 0); + + task->sp32 = sp; +} + +static inline void ao_arch_save_regs(void) { + /* Save general registers */ + asm("push {r0-r7,lr}\n"); + + /* Save APSR */ + asm("mrs r0,apsr"); + asm("push {r0}"); + + /* Save PRIMASK */ + asm("mrs r0,primask"); + asm("push {r0}"); +} + +static inline void ao_arch_save_stack(void) { + uint32_t *sp; + asm("mov %0,sp" : "=&r" (sp) ); + ao_cur_task->sp32 = (sp); + if (sp < &ao_cur_task->stack32[0]) + ao_panic (AO_PANIC_STACK); +} + +static inline void ao_arch_restore_stack(void) { + /* Switch stacks */ + asm("mov sp, %0" : : "r" (ao_cur_task->sp32) ); + + /* Restore PRIMASK */ + asm("pop {r0}"); + asm("msr primask,r0"); + + /* Restore APSR */ + asm("pop {r0}"); + asm("msr apsr_nczvq,r0"); + + /* Restore general registers */ + asm("pop {r0-r7,pc}\n"); +} + +#ifndef HAS_SAMPLE_PROFILE +#define HAS_SAMPLE_PROFILE 0 +#endif + +#if !HAS_SAMPLE_PROFILE +#define HAS_ARCH_START_SCHEDULER 1 + +static inline void ao_arch_start_scheduler(void) { + uint32_t sp; + uint32_t control; + + asm("mrs %0,msp" : "=&r" (sp)); + asm("msr psp,%0" : : "r" (sp)); + asm("mrs %0,control" : "=&r" (control)); + control |= (1 << 1); + asm("msr control,%0" : : "r" (control)); + asm("isb"); +} +#endif + +#define ao_arch_isr_stack() + +#endif + +#define ao_arch_wait_interrupt() do { \ + asm("\twfi\n"); \ + ao_arch_release_interrupts(); \ + asm(".global ao_idle_loc\nao_idle_loc:"); \ + ao_arch_block_interrupts(); \ + } while (0) + +#define ao_arch_critical(b) do { \ + uint32_t __mask = ao_arch_irqsave(); \ + do { b } while (0); \ + ao_arch_irqrestore(__mask); \ + } while (0) + +/* ao_serial_samd21.c */ + +#if USE_SERIAL_0_FLOW && USE_SERIAL_0_SW_FLOW || USE_SERIAL_1_FLOW && USE_SERIAL_1_SW_FLOW +#define HAS_SERIAL_SW_FLOW 1 +#else +#define HAS_SERIAL_SW_FLOW 0 +#endif + +#if USE_SERIAL_1_FLOW && !USE_SERIAL_1_SW_FLOW +#define USE_SERIAL_1_HW_FLOW 1 +#endif + +#if USE_SERIAL_0_FLOW && !USE_SERIAL_0_SW_FLOW +#define USE_SERIAL_0_HW_FLOW 1 +#endif + +#if USE_SERIAL_0_HW_FLOW || USE_SERIAL_1_HW_FLOW +#define HAS_SERIAL_HW_FLOW 1 +#else +#define HAS_SERIAL_HW_FLOW 0 +#endif + +struct ao_samd21_usart { + struct ao_fifo rx_fifo; + struct ao_fifo tx_fifo; + struct samd21_sercom *reg; + uint8_t tx_running; + uint8_t draining; +#if HAS_SERIAL_SW_FLOW + /* RTS - 0 if we have FIFO space, 1 if not + * CTS - 0 if we can send, 0 if not + */ + struct samd21_port *gpio_rts; + struct samd21_port *gpio_cts; + uint8_t pin_rts; + uint8_t pin_cts; + uint8_t rts; +#endif +}; + +#if HAS_USART_0 +extern struct ao_samd21_usart ao_samd21_usart0; +#endif + +void +ao_serial_init(void); + +/* ao_usb_samd21.c */ + +#if AO_USB_OUT_HOOK +void +ao_usb_out_hook(uint8_t *buffer, uint16_t count); +#endif + +void start(void); + +#endif /* _AO_ARCH_FUNCS_H_ */ diff --git a/src/samd21/ao_boot_chain.c b/src/samd21/ao_boot_chain.c new file mode 100644 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..60bf34f1 --- /dev/null +++ b/src/samd21/ao_dma_samd21.c @@ -0,0 +1,170 @@ +/* + * 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 volatile uint16_t saved_intpend; +static volatile int interrupts; + +static struct { + void (*callback)(uint8_t id, void *closure); + void *closure; +} dmac_callback[SAMD21_DMAC_NCHAN]; + +void +samd21_dmac_isr(void) +{ + uint16_t intpend = samd21_dmac.intpend; + + ++interrupts; + saved_intpend = intpend; + if (intpend & 0xff00) { + uint8_t id = (intpend >> SAMD21_DMAC_INTPEND_ID) & SAMD21_DMAC_INTPEND_ID_MASK; + samd21_dmac.intpend = intpend; + if (intpend & (1 << SAMD21_DMAC_INTPEND_TCMPL)) { + if (dmac_callback[id].callback) + (*dmac_callback[id].callback)(id, dmac_callback[id].closure); + } + } +} + +void +ao_dma_dump(char *where) +{ + printf("DMA %s ctrl %04x intpend %04x intstatus %04x\n", + where, + samd21_dmac.ctrl, + samd21_dmac.intpend, + samd21_dmac.intstatus); + fflush(stdout); + printf(" busych %04x pendch %04x active %08x chctrla %02x\n", + samd21_dmac.busych, + samd21_dmac.pendch, + samd21_dmac.active, + samd21_dmac.chctrla); + fflush(stdout); + printf(" chctrlb %08x chintflag %02x chstatus %02x\n", + samd21_dmac.chctrlb, + samd21_dmac.chintflag, + samd21_dmac.chstatus); + fflush(stdout); + printf(" btctrl %04x btcnt %04x srcaddr %08x dstaddr %08x descaddr %08x\n", + samd21_dmac_desc[0].btctrl, + samd21_dmac_desc[0].btcnt, + samd21_dmac_desc[0].srcaddr, + samd21_dmac_desc[0].dstaddr, + samd21_dmac_desc[0].descaddr); + fflush(stdout); + printf("intpend %04x interrupts %d\n", saved_intpend, interrupts); +} + +void +_ao_dma_start_transfer(uint8_t id, + void *src, + void *dst, + uint16_t count, + uint32_t chctrlb, + uint16_t btctrl, + void (*callback)(uint8_t id, void *closure), + void *closure) +{ + /* Set up the callback */ + dmac_callback[id].closure = closure; + dmac_callback[id].callback = callback; + + /* Set up the descriptor */ + samd21_dmac_desc[id].btctrl = btctrl; + samd21_dmac_desc[id].btcnt = count; + samd21_dmac_desc[id].srcaddr = (uint32_t) src; + samd21_dmac_desc[id].dstaddr = (uint32_t) dst; + samd21_dmac_desc[id].descaddr = 0; + + /* Configure the channel and enable it */ + samd21_dmac.chid = id; + samd21_dmac.chctrlb = chctrlb; + samd21_dmac.chctrla = (1 << SAMD21_DMAC_CHCTRLA_ENABLE); +} + +void +_ao_dma_done_transfer(uint8_t id) +{ + /* Disable channel */ + samd21_dmac.chid = id; + samd21_dmac.chctrla = 0; +} + +void +ao_dma_init(void) +{ + uint8_t ch; + + /* Enable DMAC clocks */ + samd21_pm.ahbmask |= (1 << SAMD21_PM_AHBMASK_DMAC); + samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_DMAC); + +#if 0 + /* Enable HPB clocks so we can talk to peripherals */ + samd21_pm.ahbmask |= ((1 << SAMD21_PM_AHBMASK_HPB0) | + (1 << SAMD21_PM_AHBMASK_HPB1) | + (1 << SAMD21_PM_AHBMASK_HPB2)); +#endif + + samd21_pm.apbamask |= (1 << SAMD21_PM_APBAMASK_PAC0); + samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_PAC1); + samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_PAC2); + + /* Reset DMAC device */ + samd21_dmac.ctrl = 0; + samd21_dmac.ctrl = (1 << SAMD21_DMAC_CTRL_SWRST); + while (samd21_dmac.ctrl & (1 << SAMD21_DMAC_CTRL_SWRST)) + ; + + samd21_dmac.baseaddr = (uint32_t) &samd21_dmac_desc[0]; + samd21_dmac.wrbaddr = (uint32_t) &samd21_dmac_wrb[0]; + + samd21_dmac.swtrigctrl = 0; + + /* Set QoS to highest value */ + samd21_dmac.qosctrl = ((SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_DQOS) | + (SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_FQOS) | + (SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_WRBQOS)); + + /* Enable DMAC controller with all priority levels */ + samd21_dmac.ctrl = ((1 << SAMD21_DMAC_CTRL_DMAENABLE) | + (1 << SAMD21_DMAC_CTRL_LVLEN(0)) | + (1 << SAMD21_DMAC_CTRL_LVLEN(1)) | + (1 << SAMD21_DMAC_CTRL_LVLEN(2)) | + (1 << SAMD21_DMAC_CTRL_LVLEN(3))); + + /* Reset all DMAC channels */ + for (ch = 0; ch < SAMD21_DMAC_NCHAN; ch++) { + samd21_dmac.chid = ch; + samd21_dmac.chctrla = (1 << SAMD21_DMAC_CHCTRLA_SWRST); + while (samd21_dmac.chctrla & (1 << SAMD21_DMAC_CHCTRLA_SWRST)) + ; + samd21_dmac.chintenset = (1 << SAMD21_DMAC_CHINTFLAG_TCMPL); + } + + /* configure interrupts */ + samd21_nvic_set_enable(SAMD21_NVIC_ISR_DMAC_POS); + samd21_nvic_set_priority(SAMD21_NVIC_ISR_DMAC_POS, 3); +} diff --git a/src/samd21/ao_dma_samd21.h b/src/samd21/ao_dma_samd21.h new file mode 100644 index 00000000..725e621a --- /dev/null +++ b/src/samd21/ao_dma_samd21.h @@ -0,0 +1,41 @@ +/* + * 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, + void *src, + void *dst, + uint16_t count, + uint32_t chctrlb, + uint16_t btctrl, + void (*callback)(uint8_t id, void *closure), + void *closure); + +void +_ao_dma_done_transfer(uint8_t id); + +void +ao_dma_dump(char *where); + +#endif /* _AO_DMA_SAM21_H_ */ diff --git a/src/samd21/ao_exti.h b/src/samd21/ao_exti.h new file mode 100644 index 00000000..43cfcb2d --- /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 1 +#define AO_EXTI_MODE_FALLING 2 +#define AO_EXTI_MODE_PULL_NONE 0 +#define AO_EXTI_MODE_PULL_UP 4 +#define AO_EXTI_MODE_PULL_DOWN 8 +#define AO_EXTI_PRIORITY_LOW 16 +#define AO_EXTI_PRIORITY_MED 0 +#define AO_EXTI_PRIORITY_HIGH 32 +#define AO_EXTI_PIN_NOCONFIGURE 64 + +void +ao_exti_setup(struct samd21_port *gpio, uint8_t pin, uint8_t mode, void (*callback)(void)); + +void +ao_exti_set_mode(struct samd21_port *gpio, uint8_t pin, uint8_t mode); + +void +ao_exti_set_callback(struct samd21_port *gpio, uint8_t pin, void (*callback)(void)); + +void +ao_exti_enable(struct samd21_port *gpio, uint8_t pin); + +void +ao_exti_disable(struct samd21_port *gpio, uint8_t pin); + +void +ao_exti_init(void); + +#endif /* _AO_EXTI_H_ */ diff --git a/src/samd21/ao_flash.h b/src/samd21/ao_flash.h new file mode 100644 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..c1cee3f8 --- /dev/null +++ b/src/samd21/ao_flash_samd21.c @@ -0,0 +1,137 @@ +/* + * 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) +{ + return (uint8_t) (((uintptr_t) addr) / samd21_nvmctrl_lock_region()); +} + +static uint8_t +ao_flash_is_locked(void *addr) +{ + return (samd21_nvmctrl.lock >> ao_flash_lock_region_bit(addr)) & 1; +} + +/* Execute a single flash operation, waiting for it to complete. This + * bit of code must be in ram + */ +static void __attribute__ ((section(".sdata2.flash"), noinline)) +_ao_flash_execute(uint16_t cmd) +{ + while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0) + ; + samd21_nvmctrl.ctrla = ((cmd << SAMD21_NVMCTRL_CTRLA_CMD) | + (SAMD21_NVMCTRL_CTRLA_CMDEX_KEY << SAMD21_NVMCTRL_CTRLA_CMDEX)); + while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0) + ; + samd21_nvmctrl.intflag = ((1 << SAMD21_NVMCTRL_INTFLAG_READY) | + (1 << SAMD21_NVMCTRL_INTFLAG_ERROR)); +} + +/* Set the address of the next flash operation */ +static void +_ao_flash_set_addr(void *addr) +{ + while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0) + ; + samd21_nvmctrl.addr = ((uint32_t) addr) >> 1; + while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0) + ; +} + +/* Unlock a region of flash */ +static void +_ao_flash_unlock(void *addr) +{ + if (!ao_flash_is_locked(addr)) + return; + + _ao_flash_set_addr(addr); + _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_UR); +} + +/* Erase a row of flash */ +static void +_ao_flash_erase_row(void *row) +{ + _ao_flash_unlock(row); + _ao_flash_set_addr(row); + _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_ER); +} + +void +ao_flash_erase_page(uint32_t *page) +{ + uint8_t *row = (uint8_t *) page; + uint32_t row_size = samd21_nvmctrl_row_size(); + uint32_t rows = (row_size + 255) / 256; + + ao_arch_block_interrupts(); + + if (((uintptr_t) row & (row_size - 1)) == 0) { + while (rows--) { + _ao_flash_erase_row(row); + row += row_size; + } + } + + ao_arch_release_interrupts(); +} + +void +ao_flash_page(uint32_t *page, uint32_t *src) +{ + uint32_t page_size = samd21_nvmctrl_page_size(); + uint32_t pages = 256 / page_size; + uint32_t i; + uint32_t per_page = page_size / sizeof(uint32_t); + + ao_arch_block_interrupts(); + + while(pages--) { + /* Clear write buffer */ + _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_PBC); + _ao_flash_set_addr(page); + for (i = 0; i < per_page; i++) + *page++ = *src++; + _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_WP); + } + + ao_arch_release_interrupts(); +} + diff --git a/src/samd21/ao_flash_samd21_pins.h b/src/samd21/ao_flash_samd21_pins.h new file mode 100644 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..440b562b --- /dev/null +++ b/src/samd21/ao_serial.h @@ -0,0 +1,123 @@ +/* + * 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 volatile struct ao_fifo ao_serial0_rx_fifo; +extern volatile struct ao_fifo ao_serial0_tx_fifo; + +char +ao_serial0_getchar(void); + +int +_ao_serial0_pollchar(void); + +uint8_t +_ao_serial0_sleep_for(uint16_t timeout); + +void +ao_serial0_putchar(char c); + +void +ao_serial0_drain(void); + +void +ao_serial0_set_speed(uint8_t speed); +#endif + +#if HAS_SERIAL_1 +extern volatile struct ao_fifo ao_serial1_rx_fifo; +extern volatile struct ao_fifo ao_serial1_tx_fifo; + +char +ao_serial1_getchar(void); + +int +_ao_serial1_pollchar(void); + +uint8_t +_ao_serial1_sleep_for(uint16_t timeout); + +void +ao_serial1_putchar(char c); + +void +ao_serial1_drain(void); + +void +ao_serial1_set_speed(uint8_t speed); +#endif + +#if HAS_SERIAL_2 +extern volatile struct ao_fifo ao_serial2_rx_fifo; +extern volatile struct ao_fifo ao_serial2_tx_fifo; + +char +ao_serial2_getchar(void); + +int +_ao_serial2_pollchar(void); + +uint8_t +_ao_serial2_sleep_for(uint16_t timeout); + +void +ao_serial2_putchar(char c); + +void +ao_serial2_drain(void); + +void +ao_serial2_set_speed(uint8_t speed); +#endif + +#if HAS_SERIAL_3 +extern volatile struct ao_fifo ao_serial3_rx_fifo; +extern volatile struct ao_fifo ao_serial3_tx_fifo; + +char +ao_serial3_getchar(void); + +int +_ao_serial3_pollchar(void); + +uint8_t +_ao_serial3_sleep_for(uint16_t timeout); + +void +ao_serial3_putchar(char c); + +void +ao_serial3_drain(void); + +void +ao_serial3_set_speed(uint8_t speed); +#endif + +void +ao_serial_init(void); + +#endif /* _AO_SERIAL_H_ */ diff --git a/src/samd21/ao_serial_samd21.c b/src/samd21/ao_serial_samd21.c new file mode 100644 index 00000000..a52966c1 --- /dev/null +++ b/src/samd21/ao_serial_samd21.c @@ -0,0 +1,272 @@ +/* + * 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); + usart->reg->data = ao_fifo_remove(&usart->tx_fifo); + ao_wakeup(&usart->tx_fifo); + return 1; + } + } + return 0; +} + +static void +_ao_usart_rx(struct ao_samd21_usart *usart, int is_stdin) +{ + if (usart->reg->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) { + if (!ao_fifo_full(&usart->rx_fifo)) { + ao_fifo_insert(&usart->rx_fifo, usart->reg->data); + ao_wakeup(&usart->rx_fifo); + if (is_stdin) + ao_wakeup(&ao_stdin_ready); +#if HAS_SERIAL_SW_FLOW + /* If the fifo is nearly full, turn off RTS and wait + * for it to drain a bunch + */ + if (usart->gpio_rts && ao_fifo_mostly(usart->rx_fifo)) { + ao_gpio_set(usart->gpio_rts, usart->pin_rts, usart->pin_rts, 1); + usart->rts = 0; + } +#endif + } else { + usart->reg->intenclr = (1 << SAMD21_SERCOM_INTFLAG_RXC); + } + } +} + +static void +ao_usart_isr(struct ao_samd21_usart *usart, int is_stdin) +{ + _ao_usart_rx(usart, is_stdin); + + if (!_ao_usart_tx_start(usart)) + usart->reg->intenclr = (1 << SAMD21_SERCOM_INTFLAG_DRE); + + if (usart->reg->intflag & (1 << SAMD21_SERCOM_INTFLAG_TXC)) { + usart->tx_running = 0; + usart->reg->intenclr = (1 << SAMD21_SERCOM_INTFLAG_TXC); + if (usart->draining) { + usart->draining = 0; + ao_wakeup(&usart->tx_fifo); + } + } +} + +static const uint32_t ao_usart_speeds[] = { + [AO_SERIAL_SPEED_4800] = 4800, + [AO_SERIAL_SPEED_9600] = 9600, + [AO_SERIAL_SPEED_19200] = 19200, + [AO_SERIAL_SPEED_57600] = 57600, + [AO_SERIAL_SPEED_115200] = 115200, +}; + +static void +ao_usart_set_speed(struct ao_samd21_usart *usart, uint8_t speed) +{ + uint64_t top = (uint64_t) ao_usart_speeds[speed] << (4 + 16); + uint16_t baud = 65536 - (top + AO_SYSCLK/2) / AO_SYSCLK; + + usart->reg->baud = baud; +} + +static void +ao_usart_init(struct ao_samd21_usart *usart, int hw_flow, int id) +{ + struct samd21_sercom *reg = usart->reg; + + (void) hw_flow; + + /* Send a clock along */ + samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_SERCOM0_CORE + id); + + samd21_nvic_set_enable(SAMD21_NVIC_ISR_SERCOM0_POS + id); + samd21_nvic_set_priority(SAMD21_NVIC_ISR_SERCOM0_POS + id, 4); + + /* enable */ + samd21_pm.apbcmask |= (1 << (SAMD21_PM_APBCMASK_SERCOM0 + id)); + + /* Reset */ + reg->ctrla = (1 << SAMD21_SERCOM_CTRLA_SWRST); + + while ((reg->ctrla & (1 << SAMD21_SERCOM_CTRLA_SWRST)) || + (reg->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_SWRST))) + ; + + reg->ctrlb = ((0 << SAMD21_SERCOM_CTRLB_CHSIZE) | + (0 << SAMD21_SERCOM_CTRLB_SBMODE) | + (0 << SAMD21_SERCOM_CTRLB_COLDEN) | + (0 << SAMD21_SERCOM_CTRLB_SFDE) | + (0 << SAMD21_SERCOM_CTRLB_ENC) | + (0 << SAMD21_SERCOM_CTRLB_PMODE) | + (1 << SAMD21_SERCOM_CTRLB_TXEN) | + (1 << SAMD21_SERCOM_CTRLB_RXEN) | + (3 << SAMD21_SERCOM_CTRLB_FIFOCLR)); + + ao_usart_set_speed(usart, AO_SERIAL_SPEED_9600); + + /* finish setup and enable the hardware */ + reg->ctrla = ((0 << SAMD21_SERCOM_CTRLA_SWRST) | + (1 << SAMD21_SERCOM_CTRLA_ENABLE) | + (1 << SAMD21_SERCOM_CTRLA_MODE) | + (1 << SAMD21_SERCOM_CTRLA_RUNSTDBY) | + (0 << SAMD21_SERCOM_CTRLA_IBON) | + (0 << SAMD21_SERCOM_CTRLA_SAMPR) | + (1 << SAMD21_SERCOM_CTRLA_TXPO) | /* pad[2] */ + (3 << SAMD21_SERCOM_CTRLA_RXPO) | /* pad[3] */ + (0 << SAMD21_SERCOM_CTRLA_SAMPA) | + (0 << SAMD21_SERCOM_CTRLA_FORM) | /* no parity */ + (0 << SAMD21_SERCOM_CTRLA_CMODE) | /* async */ + (0 << SAMD21_SERCOM_CTRLA_CPOL) | + (1 << SAMD21_SERCOM_CTRLA_DORD)); /* LSB first */ + + /* Enable receive interrupt */ + reg->intenset = (1 << SAMD21_SERCOM_INTFLAG_RXC); +} + +static int +_ao_usart_pollchar(struct ao_samd21_usart *usart) +{ + int c; + + if (ao_fifo_empty(&usart->rx_fifo)) + c = AO_READ_AGAIN; + else { + uint8_t u; + u = ao_fifo_remove(&usart->rx_fifo); + if ((usart->reg->intenset & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0) { + if (ao_fifo_barely(&usart->rx_fifo)) + usart->reg->intenset = (1 << SAMD21_SERCOM_INTFLAG_RXC); + } +#if HAS_SERIAL_SW_FLOW + /* If we've cleared RTS, check if there's space now and turn it back on */ + if (usart->gpio_rts && usart->rts == 0 && ao_fifo_barely(usart->rx_fifo)) { + ao_gpio_set(usart->gpio_rts, usart->pin_rts, foo, 0); + usart->rts = 1; + } +#endif + c = u; + } + return c; +} + +static char +ao_usart_getchar(struct ao_samd21_usart *usart) +{ + int c; + ao_arch_block_interrupts(); + while ((c = _ao_usart_pollchar(usart)) == AO_READ_AGAIN) + ao_sleep(&usart->rx_fifo); + ao_arch_release_interrupts(); + return (char) c; +} + +static void +ao_usart_putchar(struct ao_samd21_usart *usart, char c) +{ + ao_arch_block_interrupts(); + while (ao_fifo_full(&usart->tx_fifo)) + ao_sleep(&usart->tx_fifo); + ao_fifo_insert(&usart->tx_fifo, c); + _ao_usart_tx_start(usart); + ao_arch_release_interrupts(); +} + +static void +ao_usart_drain(struct ao_samd21_usart *usart) +{ + ao_arch_block_interrupts(); + while (!ao_fifo_empty(&usart->tx_fifo) || usart->tx_running) { + usart->draining = 1; + ao_sleep(&usart->tx_fifo); + } + ao_arch_release_interrupts(); +} + +#if HAS_SERIAL_0 + +struct ao_samd21_usart ao_samd21_usart0; + +void samd21_sercom0_isr(void) { ao_usart_isr(&ao_samd21_usart0, USE_SERIAL_0_STDIN); } + +char +ao_serial0_getchar(void) +{ + return ao_usart_getchar(&ao_samd21_usart0); +} + +void +ao_serial0_putchar(char c) +{ + ao_usart_putchar(&ao_samd21_usart0, c); +} + +int +_ao_serial0_pollchar(void) +{ + return _ao_usart_pollchar(&ao_samd21_usart0); +} + +void +ao_serial0_drain(void) +{ + ao_usart_drain(&ao_samd21_usart0); +} + +void +ao_serial0_set_speed(uint8_t speed) +{ + ao_usart_drain(&ao_samd21_usart0); + ao_usart_set_speed(&ao_samd21_usart0, speed); +} +#endif /* HAS_SERIAL_0 */ + +void +ao_serial_init(void) +{ +#if HAS_SERIAL_0 + +#if SERIAL_0_PA10_PA11 + /* Pin settings */ + ao_enable_port(&samd21_port_a); + samd21_port_pmux_set(&samd21_port_a, 10, SAMD21_PORT_PMUX_FUNC_C); + samd21_port_pmux_set(&samd21_port_a, 11, SAMD21_PORT_PMUX_FUNC_C); +#else +#error "No SERIAL_0 port configuration specified" +#endif + + ao_samd21_usart0.reg = &samd21_sercom0; + ao_usart_init(&ao_samd21_usart0, 0, 0); + +#if USE_SERIAL_0_STDIN + ao_add_stdio(_ao_serial0_pollchar, + ao_serial0_putchar, + NULL); +#endif +#endif +} diff --git a/src/samd21/ao_tc_samd21.c b/src/samd21/ao_tc_samd21.c new file mode 100644 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..0eb0dbea --- /dev/null +++ b/src/samd21/ao_timer.c @@ -0,0 +1,277 @@ +/* + * 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; +#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..7f6d2d3d --- /dev/null +++ b/src/samd21/ao_usb_samd21.c @@ -0,0 +1,867 @@ +/* + * 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 + +#define AO_USB_OUT_SLEEP_ADDR (&ao_usb_out_avail) + +#define SAMD21_USB_ALIGN __attribute__ ((aligned(4))) + +struct ao_usb_setup { + uint8_t dir_type_recip; + uint8_t request; + uint16_t value; + uint16_t index; + uint16_t length; +} ao_usb_setup; + +static uint8_t ao_usb_ep0_state; + +struct samd21_usb_desc samd21_usb_desc[8] SAMD21_USB_ALIGN; + +/* Pending EP0 IN data */ +static uint8_t ao_usb_ep0_in_tmp[2]; /* small buffer */ +static const uint8_t *ao_usb_ep0_in_data; /* Remaining data */ +static uint8_t ao_usb_ep0_in_len; /* Remaining amount */ + +/* Pending EP0 OUT data */ +static uint8_t *ao_usb_ep0_out_data; +static uint8_t ao_usb_ep0_out_len; + +/* Endpoint 0 buffers */ +static uint8_t ao_usb_ep0_in_buf[AO_USB_CONTROL_SIZE] SAMD21_USB_ALIGN; +static uint8_t ao_usb_ep0_out_buf[AO_USB_CONTROL_SIZE] SAMD21_USB_ALIGN; + +#if AO_USB_HAS_INT +/* Pointer to interrupt buffer in USB memory */ +static uint8_t ao_usb_int_buf[AO_USB_INT_SIZE] SAMD21_USB_ALIGN; +#endif + +/* Buffers in DRAM */ +#if AO_USB_HAS_IN +static uint8_t ao_usb_in_tx_which; +static uint8_t ao_usb_tx_count; +static uint8_t ao_usb_in_buf[2][AO_USB_IN_SIZE] SAMD21_USB_ALIGN; + +#endif +#if AO_USB_HAS_OUT +static uint8_t ao_usb_out_rx_which; +#if !USE_USB_FIFO +static uint8_t ao_usb_rx_count, ao_usb_rx_pos; +#endif +static uint8_t ao_usb_out_buf[2][AO_USB_OUT_SIZE] SAMD21_USB_ALIGN; + +#endif + +/* Marks when we don't need to send an IN packet. + * This happens only when the last IN packet is not full, + * otherwise the host will expect to keep seeing packets. + * Send a zero-length packet as required + */ +static uint8_t ao_usb_in_flushed; + +/* Marks when we have delivered an IN packet to the hardware + * and it has not been received yet. ao_sleep on this address + * to wait for it to be delivered. + */ +static uint8_t ao_usb_in_pending; + +/* Marks when an OUT packet has been received by the hardware + * but not pulled to the shadow buffer. + */ +static uint8_t ao_usb_out_avail; +uint8_t ao_usb_running; +static uint8_t ao_usb_configuration; + +#define AO_USB_EP0_GOT_SETUP 1 +#define AO_USB_EP0_GOT_RX_DATA 2 +#define AO_USB_EP0_GOT_TX_ACK 4 + +static uint8_t ao_usb_ep0_receive; +static uint8_t ao_usb_address; +static uint8_t ao_usb_address_pending; + +/* + * Set current device address and mark the + * interface as active + */ +static void +ao_usb_set_address(uint8_t address) +{ + samd21_usb.dadd = (1 << SAMD21_USB_DADD_ADDEN) | (address << SAMD21_USB_DADD_DADD); + ao_usb_address_pending = 0; +} + +/* + * Initialize an entpoint + */ + +static void +ao_usb_init_bank(struct samd21_usb_desc_bank *bank, + uint8_t *buf, + uint16_t size) +{ + bank->addr = (uint32_t) buf; + + uint32_t size_bits = 0; + switch (size) { + case 8: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_8; break; + case 16: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_16; break; + case 32: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_32; break; + case 64: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_64; break; + case 128: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_128; break; + case 256: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_256; break; + case 512: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_512; break; + case 1023: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_1023; break; + } + bank->pcksize = ((0 << SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT) | + (0 << SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE) | + (size_bits << SAMD21_USB_DESC_PCKSIZE_SIZE) | + (0 << SAMD21_USB_DESC_PCKSIZE_AUTO_ZLP)); +} + +static void +ao_usb_init_ep(uint8_t ep, + uint8_t type_out, void *out_buf, uint16_t out_size, + uint8_t type_in, void *in_buf, uint16_t in_size) +{ + /* set up descriptors */ + ao_usb_init_bank(&samd21_usb_desc[ep].bank[0], + out_buf, out_size); + + ao_usb_init_bank(&samd21_usb_desc[ep].bank[1], + in_buf, in_size); + + samd21_usb.ep[ep].epcfg = (uint8_t) ((type_out << SAMD21_USB_EP_EPCFG_EP_TYPE_OUT) | + (type_in << SAMD21_USB_EP_EPCFG_EP_TYPE_IN)); + + /* Clear all status bits */ + samd21_usb.ep[ep].epstatusclr = 0xff; + + /* Select interrupts */ + uint8_t epinten = 0; + if (out_buf) + epinten |= (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT0); + if (in_buf) + epinten |= (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT1); + if (ep == 0) + epinten |= (1 << SAMD21_USB_EP_EPINTFLAG_RXSTP); + samd21_usb.ep[ep].epintenset = epinten; +} + +static void +ao_usb_set_ep0(void) +{ + ao_usb_init_ep(AO_USB_CONTROL_EP, + SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_CONTROL, + ao_usb_ep0_out_buf, AO_USB_CONTROL_SIZE, + SAMD21_USB_EP_EPCFG_EP_TYPE_IN_CONTROL, + ao_usb_ep0_in_buf, AO_USB_CONTROL_SIZE); + + ao_usb_running = 0; + + /* Reset our internal state + */ + + ao_usb_ep0_state = AO_USB_EP0_IDLE; + + ao_usb_ep0_in_data = NULL; + ao_usb_ep0_in_len = 0; + + ao_usb_ep0_out_data = NULL; + ao_usb_ep0_out_len = 0; +} + +static void +ao_usb_set_configuration(void) +{ +#if AO_USB_HAS_INT + ao_usb_init_ep(AO_USB_INT_EP, + SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DISABLED, + NULL, 0, + SAMD21_USB_EP_EPCFG_EP_TYPE_IN_INTERRUPT, + ao_usb_int_buf, AO_USB_INT_SIZE); +#endif + +#if AO_USB_HAS_OUT + ao_usb_init_ep(AO_USB_OUT_EP, + SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_BULK, + &ao_usb_out_buf[0][0], AO_USB_OUT_SIZE, + SAMD21_USB_EP_EPCFG_EP_TYPE_IN_DUAL_BANK, + &ao_usb_out_buf[1][0], AO_USB_OUT_SIZE); + + /* At first receive, we'll flip this back to 0 */ + ao_usb_out_rx_which = 1; +#endif + +#if AO_USB_HAS_IN + /* Set up the IN end point */ + ao_usb_init_ep(AO_USB_IN_EP, + SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DUAL_BANK, + &ao_usb_in_buf[0][0], AO_USB_IN_SIZE, + SAMD21_USB_EP_EPCFG_EP_TYPE_IN_BULK, + &ao_usb_in_buf[1][0], AO_USB_IN_SIZE); + + /* First transmit data goes to buffer 0 */ + ao_usb_in_tx_which = 0; +#endif + + ao_usb_in_flushed = 1; + ao_usb_in_pending = 0; + ao_wakeup(&ao_usb_in_pending); + + ao_usb_out_avail = 0; + ao_usb_configuration = 0; + + ao_wakeup(AO_USB_OUT_SLEEP_ADDR); +} + +/* Send an IN data packet */ +static void +ao_usb_ep0_flush(void) +{ + uint8_t this_len; + + /* Check to see if the endpoint is still busy */ + if ((samd21_usb.ep[0].epstatus & (1 << (SAMD21_USB_EP_EPSTATUS_BK1RDY))) != 0) + return; + + this_len = ao_usb_ep0_in_len; + if (this_len > AO_USB_CONTROL_SIZE) + this_len = AO_USB_CONTROL_SIZE; + + if (this_len < AO_USB_CONTROL_SIZE) + ao_usb_ep0_state = AO_USB_EP0_IDLE; + + ao_usb_ep0_in_len -= this_len; + + memcpy(ao_usb_ep0_in_buf, ao_usb_ep0_in_data, this_len); + ao_usb_ep0_in_data += this_len; + + /* Mark the endpoint as TX valid to send the packet */ + samd21_usb_desc_set_byte_count(AO_USB_CONTROL_EP, 1, this_len); + samd21_usb_ep_set_ready(AO_USB_CONTROL_EP, 1); +} + +/* Read data from the ep0 OUT fifo */ +static void +ao_usb_ep0_fill(void) +{ + uint16_t len = samd21_usb_desc_get_byte_count(AO_USB_CONTROL_EP, 0); + + if (len > ao_usb_ep0_out_len) + len = ao_usb_ep0_out_len; + ao_usb_ep0_out_len -= (uint8_t) len; + + /* Pull all of the data out of the packet */ + memcpy(ao_usb_ep0_out_data, ao_usb_ep0_out_buf, len); + ao_usb_ep0_out_data += len; + + /* ACK the packet */ + samd21_usb_ep_clr_ready(AO_USB_CONTROL_EP, 0); +} + +static void +ao_usb_ep0_in_reset(void) +{ + ao_usb_ep0_in_data = ao_usb_ep0_in_tmp; + ao_usb_ep0_in_len = 0; +} + +static void +ao_usb_ep0_in_queue_byte(uint8_t a) +{ + if (ao_usb_ep0_in_len < sizeof (ao_usb_ep0_in_tmp)) + ao_usb_ep0_in_tmp[ao_usb_ep0_in_len++] = a; +} + +static void +ao_usb_ep0_in_set(const uint8_t *data, uint8_t len) +{ + ao_usb_ep0_in_data = data; + ao_usb_ep0_in_len = len; +} + +static void +ao_usb_ep0_out_set(uint8_t *data, uint8_t len) +{ + ao_usb_ep0_out_data = data; + ao_usb_ep0_out_len = len; +} + +static void +ao_usb_ep0_in_start(uint16_t max) +{ + /* Don't send more than asked for */ + if (ao_usb_ep0_in_len > max) + ao_usb_ep0_in_len = (uint8_t) max; + ao_usb_ep0_flush(); +} + +struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8}; + +#if AO_USB_DEVICE_ID_SERIAL +static uint8_t ao_usb_serial[2 + 64]; + +/* Convert a 32-bit value to 8 hexidecimal UCS2 characters */ +static void +hex_to_ucs2(uint32_t in, uint8_t *out) +{ + int i; + + for (i = 28; i >= 0; i -= 4) { + uint8_t bits = (in >> i) & 0xf; + *out++ = ((bits < 10) ? '0' : ('a' - 10)) + bits; + *out++ = 0; + } +} + +/* Encode the device ID (128 bits) in hexidecimal to use as a device + * serial number + */ +static void +ao_usb_serial_init(void) +{ + ao_usb_serial[0] = 66; /* length */ + ao_usb_serial[1] = AO_USB_DESC_STRING; + hex_to_ucs2(samd21_serial.word0, ao_usb_serial + 2 + 0); + hex_to_ucs2(samd21_serial.word1, ao_usb_serial + 2 + 16); + hex_to_ucs2(samd21_serial.word2, ao_usb_serial + 2 + 32); + hex_to_ucs2(samd21_serial.word3, ao_usb_serial + 2 + 48); +} +#endif + +/* Walk through the list of descriptors and find a match + */ +static void +ao_usb_get_descriptor(uint16_t value, uint16_t length) +{ + const uint8_t *descriptor; + uint8_t type = (uint8_t) (value >> 8); + uint8_t index = (uint8_t) value; + + descriptor = ao_usb_descriptors; + while (descriptor[0] != 0) { + if (descriptor[1] == type && index-- == 0) { + uint8_t len; + if (type == AO_USB_DESC_CONFIGURATION) + len = descriptor[2]; + else + len = descriptor[0]; +#if AO_USB_DEVICE_ID_SERIAL + /* Slightly hacky - the serial number is string 3 */ + if (type == AO_USB_DESC_STRING && (value & 0xff) == 3) { + descriptor = ao_usb_serial; + len = sizeof (ao_usb_serial); + } +#endif + if (len > length) + len = (uint8_t) length; + ao_usb_ep0_in_set(descriptor, len); + break; + } + descriptor += descriptor[0]; + } +} + +static void +ao_usb_ep0_setup(void) +{ + /* Pull the setup packet out of the fifo */ + ao_usb_ep0_out_set((uint8_t *) &ao_usb_setup, 8); + ao_usb_ep0_fill(); + if (ao_usb_ep0_out_len != 0) + return; + + if ((ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) || ao_usb_setup.length == 0) + ao_usb_ep0_state = AO_USB_EP0_DATA_IN; + else + ao_usb_ep0_state = AO_USB_EP0_DATA_OUT; + + ao_usb_ep0_in_reset(); + + switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) { + case AO_USB_TYPE_STANDARD: + switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) { + case AO_USB_RECIP_DEVICE: + switch(ao_usb_setup.request) { + case AO_USB_REQ_GET_STATUS: + ao_usb_ep0_in_queue_byte(0); + ao_usb_ep0_in_queue_byte(0); + break; + case AO_USB_REQ_SET_ADDRESS: + ao_usb_address = (uint8_t) ao_usb_setup.value; + ao_usb_address_pending = 1; + break; + case AO_USB_REQ_GET_DESCRIPTOR: + ao_usb_get_descriptor(ao_usb_setup.value, ao_usb_setup.length); + break; + case AO_USB_REQ_GET_CONFIGURATION: + ao_usb_ep0_in_queue_byte(ao_usb_configuration); + break; + case AO_USB_REQ_SET_CONFIGURATION: + ao_usb_configuration = (uint8_t) ao_usb_setup.value; + ao_usb_set_configuration(); + break; + } + break; + case AO_USB_RECIP_INTERFACE: + switch(ao_usb_setup.request) { + case AO_USB_REQ_GET_STATUS: + ao_usb_ep0_in_queue_byte(0); + ao_usb_ep0_in_queue_byte(0); + break; + case AO_USB_REQ_GET_INTERFACE: + ao_usb_ep0_in_queue_byte(0); + break; + case AO_USB_REQ_SET_INTERFACE: + break; + } + break; + case AO_USB_RECIP_ENDPOINT: + switch(ao_usb_setup.request) { + case AO_USB_REQ_GET_STATUS: + ao_usb_ep0_in_queue_byte(0); + ao_usb_ep0_in_queue_byte(0); + break; + } + break; + } + break; + case AO_USB_TYPE_CLASS: + switch (ao_usb_setup.request) { + case AO_USB_SET_LINE_CODING: + ao_usb_ep0_out_set((uint8_t *) &ao_usb_line_coding, 7); + break; + case AO_USB_GET_LINE_CODING: + ao_usb_ep0_in_set((const uint8_t *) &ao_usb_line_coding, 7); + break; + case AO_USB_SET_CONTROL_LINE_STATE: + break; + } + break; + } + + /* If we're not waiting to receive data from the host, + * queue an IN response + */ + if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN) + ao_usb_ep0_in_start(ao_usb_setup.length); +} + +static void +ao_usb_ep0_handle(uint8_t receive) +{ + ao_usb_ep0_receive = 0; + if (receive & AO_USB_EP0_GOT_SETUP) { + ao_usb_ep0_setup(); + } + if (receive & AO_USB_EP0_GOT_RX_DATA) { + if (ao_usb_ep0_state == AO_USB_EP0_DATA_OUT) { + ao_usb_ep0_fill(); + if (ao_usb_ep0_out_len == 0) { + ao_usb_ep0_state = AO_USB_EP0_DATA_IN; + ao_usb_ep0_in_start(0); + } + } + } + if (receive & AO_USB_EP0_GOT_TX_ACK) { + +#if HAS_FLIGHT && AO_USB_FORCE_IDLE + ao_flight_force_idle = 1; +#endif + if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN) + ao_usb_ep0_flush(); + /* Wait until the IN packet is received from addr 0 + * before assigning our local address + */ + if (ao_usb_address_pending) + ao_usb_set_address(ao_usb_address); + } +} + +#if AO_POWER_MANAGEMENT +static void +ao_usb_suspend(void) +{ + stm_usb.cntr |= (1 << STM_USB_CNTR_FSUSP); + ao_power_suspend(); + stm_usb.cntr |= (1 << STM_USB_CNTR_LP_MODE); + ao_clock_suspend(); +} + +static void +ao_usb_wakeup(void) +{ + ao_clock_resume(); + stm_usb.cntr &= ~(1 << STM_USB_CNTR_FSUSP); + ao_power_resume(); +} +#endif + +#if USE_USB_FIFO +static void +ao_usb_fifo_check(void) +{ + uint8_t next_which = 1 - ao_usb_out_rx_which; + if (ao_usb_out_avail & (1 << next_which)) { + uint8_t *buf = ao_usb_out_buf[next_which]; + uint16_t len = samd21_usb_desc_get_byte_count(AO_USB_OUT_EP, next_which); + + if (ao_fifo_has_space(&ao_usb_rx_fifo, len)) { + uint16_t i; + +#if AO_USB_OUT_HOOK + ao_usb_out_hook(buf, len); +#endif + for (i = 0; i < len; i++) + ao_fifo_insert(&ao_usb_rx_fifo, buf[i]); + samd21_usb_ep_clr_ready(AO_USB_OUT_EP, next_which); + ao_usb_out_avail &= ~(1 << next_which); + ao_usb_out_rx_which = next_which; + ao_wakeup(AO_USB_OUT_SLEEP_ADDR); + } + } +} +#endif + +void +samd21_usb_isr(void) +{ + uint16_t intflag = samd21_usb.intflag; + uint16_t epintsmry = samd21_usb.epintsmry; + + samd21_usb.intflag = intflag; + + if (epintsmry & (1 << 0)) { + uint8_t epintflag = samd21_usb.ep[0].epintflag; + samd21_usb.ep[0].epintflag = epintflag; + + if (epintflag & (1 << SAMD21_USB_EP_EPINTFLAG_RXSTP)) + ao_usb_ep0_receive |= AO_USB_EP0_GOT_SETUP; + else if (epintflag & (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT0)) + ao_usb_ep0_receive |= AO_USB_EP0_GOT_RX_DATA; + if (epintflag & (1 << SAMD21_USB_EP_EPINTFLAG_TRCPT1)) + ao_usb_ep0_receive |= AO_USB_EP0_GOT_TX_ACK; + ao_usb_ep0_handle(ao_usb_ep0_receive); + } +#if AO_USB_HAS_OUT + if (epintsmry & (1 << AO_USB_OUT_EP)) { + uint8_t epintflag = samd21_usb.ep[AO_USB_OUT_EP].epintflag; + samd21_usb.ep[AO_USB_OUT_EP].epintflag = epintflag; + uint8_t avail = (epintflag >> SAMD21_USB_EP_EPINTFLAG_TRCPT0) & 3; + if (avail) { + ao_usb_out_avail |= avail; + ao_usb_running = 1; +#if USE_USB_FIFO + ao_usb_fifo_check(); +#else + ao_wakeup(AO_USB_OUT_SLEEP_ADDR); +#endif + } + } +#endif +#if AO_USB_HAS_IN + if (epintsmry & (1 << AO_USB_IN_EP)) { + uint8_t epintflag = samd21_usb.ep[AO_USB_IN_EP].epintflag; + samd21_usb.ep[AO_USB_IN_EP].epintflag = epintflag; + uint8_t done = (epintflag >> SAMD21_USB_EP_EPINTFLAG_TRCPT0) & 3; + if (done) { + ao_usb_in_pending &= (uint8_t) ~done; + ao_wakeup(&ao_usb_in_pending); + } + } +#endif +#if AO_USB_HAS_INT + if (epintsmry & (1 << AO_USB_INT_EP)) { + } +#endif + if (intflag & (1 << SAMD21_USB_INTFLAG_EORST)) { + ao_usb_set_ep0(); + } +#if AO_POWER_MANAGEMENT + if (istr & (1 << STM_USB_ISTR_SUSP)) + ao_usb_suspend(); + + if (istr & (1 << STM_USB_ISTR_WKUP)) + ao_usb_wakeup(); +#endif +} + +#if AO_USB_HAS_IN +/* Queue the current IN buffer for transmission */ +static void +_ao_usb_in_send(void) +{ + ao_usb_in_pending |= (uint8_t) (1 << ao_usb_in_tx_which); + if (ao_usb_tx_count != AO_USB_IN_SIZE) + ao_usb_in_flushed = 1; + + samd21_usb_desc_set_byte_count(AO_USB_IN_EP, ao_usb_in_tx_which, ao_usb_tx_count); + samd21_usb_ep_set_ready(AO_USB_IN_EP, ao_usb_in_tx_which); + + /* Toggle our usage */ + ao_usb_in_tx_which = 1 - ao_usb_in_tx_which; + ao_usb_tx_count = 0; +} + +/* Wait for a free IN buffer. Interrupts are blocked */ +static void +_ao_usb_in_wait(void) +{ + /* Wait for an IN buffer to be ready */ + while ((ao_usb_in_pending & (1 << ao_usb_in_tx_which)) != 0) + ao_sleep(&ao_usb_in_pending); +} + +void +ao_usb_flush(void) +{ + if (!ao_usb_running) + return; + + /* Anytime we've sent a character since + * the last time we flushed, we'll need + * to send a packet -- the only other time + * we would send a packet is when that + * packet was full, in which case we now + * want to send an empty packet + */ + ao_arch_block_interrupts(); + if (!ao_usb_in_flushed) { + _ao_usb_in_wait(); + if (!ao_usb_in_flushed) + _ao_usb_in_send(); + } + ao_arch_release_interrupts(); +} + +void +ao_usb_putchar(char c) +{ + if (!ao_usb_running) + return; + + if (c == '\n') + ao_usb_putchar('\r'); + ao_arch_block_interrupts(); + _ao_usb_in_wait(); + + ao_usb_in_flushed = 0; + ao_usb_in_buf[ao_usb_in_tx_which][ao_usb_tx_count++] = c; + + /* Send the packet when full */ + if (ao_usb_tx_count == AO_USB_IN_SIZE) + _ao_usb_in_send(); + + ao_arch_release_interrupts(); + if (c == '\n') + ao_usb_flush(); +} +#endif + +#if AO_USB_HAS_OUT +#if !USE_USB_FIFO +static bool +_ao_usb_out_recv(void) +{ + uint8_t next_which = 1 - ao_usb_out_rx_which; + + if ((ao_usb_out_avail & (1 << next_which)) != 0) { + /* switch current buffer */ + ao_usb_out_rx_which = next_which; + ao_usb_out_avail &= (uint8_t) ~(1 << ao_usb_out_rx_which); + + ao_usb_rx_count = (uint8_t) samd21_usb_desc_get_byte_count(AO_USB_OUT_EP, ao_usb_out_rx_which); + ao_usb_rx_pos = 0; + return true; + } + return false; +} +#endif + +static int +_ao_usb_pollchar(void) +{ + uint8_t c; + + if (!ao_usb_running) + return AO_READ_AGAIN; + +#if USE_USB_FIFO + if (ao_fifo_empty(&ao_usb_rx_fifo)) + return AO_READ_AGAIN; + c = ao_fifo_remove(&ao_usb_rx_fifo); + ao_usb_fifo_check(); +#else + for (;;) { + if (ao_usb_rx_pos != ao_usb_rx_count) + break; + + /* Check for packet */ + if (!_ao_usb_out_recv()) + return AO_READ_AGAIN; + } + + /* Pull a character out of the fifo */ + c = ao_usb_out_buf[ao_usb_out_rx_which][ao_usb_rx_pos++]; + + if (ao_usb_rx_pos == ao_usb_rx_count) + samd21_usb_ep_clr_ready(AO_USB_OUT_EP, ao_usb_out_rx_which); +#endif + + return c; +} + +char +ao_usb_getchar(void) +{ + int c; + + ao_arch_block_interrupts(); + while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN) + ao_sleep(AO_USB_OUT_SLEEP_ADDR); + ao_arch_release_interrupts(); + return (char) c; +} + +#endif + +void +ao_usb_disable(void) +{ + ao_arch_block_interrupts(); + samd21_nvic_clear_enable(SAMD21_NVIC_ISR_USB_POS); + + /* Disable USB pull-up */ + samd21_usb.ctrlb |= (1 << SAMD21_USB_CTRLB_DETACH); + + /* Switch off the device */ + samd21_usb.ctrla &= (uint8_t) ~(1 << SAMD21_USB_CTRLA_ENABLE); + + /* Disable the interface */ + samd21_pm.apbbmask &= ~(1UL << SAMD21_PM_APBBMASK_USB); + ao_arch_release_interrupts(); +} + +void +ao_usb_enable(void) +{ + int t; + + /* Set up USB DM/DP pins */ + samd21_port_pmux_set(&samd21_port_a, 24, SAMD21_PORT_PMUX_FUNC_G); + samd21_port_pmux_set(&samd21_port_a, 25, SAMD21_PORT_PMUX_FUNC_G); + + /* Assign gclk 0 to USB reference */ + samd21_gclk_clkctrl(AO_GCLK_USB, SAMD21_GCLK_CLKCTRL_ID_USB); + + /* Enable USB clock */ + samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_USB); + + /* Reset USB Device */ + + samd21_usb.ctrla |= (1 << SAMD21_USB_CTRLA_SWRST); + + while ((samd21_usb.syncbusy & (1 << SAMD21_USB_SYNCBUSY_SWRST)) == 0) + ; + + while ((samd21_usb.syncbusy & (1 << SAMD21_USB_SYNCBUSY_SWRST)) != 0) + ; + + memset(&samd21_usb_desc, 0, sizeof (samd21_usb_desc)); + + /* Detach */ + samd21_usb.ctrlb |= (1 << SAMD21_USB_CTRLB_DETACH); + + samd21_usb.descadd = (uint32_t) &samd21_usb_desc; + + /* Load calibration values */ + uint32_t transn = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_USB_TRANSN) & + SAMD21_AUX1_CALIBRATION_USB_TRANSN_MASK); + if (transn == 0x1f) + transn = 5; + uint32_t transp = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_USB_TRANSP) & + SAMD21_AUX1_CALIBRATION_USB_TRANSP_MASK); + if (transp == 0x1f) + transp = 29; + uint32_t trim = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_USB_TRIM) & + SAMD21_AUX1_CALIBRATION_USB_TRIM_MASK); + if (trim == 7) + trim = 3; + + samd21_usb.padcal = (uint16_t) ((transn << SAMD21_USB_PADCAL_TRANSN) | + (transp << SAMD21_USB_PADCAL_TRANSP) | + (trim << SAMD21_USB_PADCAL_TRIM)); + + samd21_usb.qosctrl = ((3 << SAMD21_USB_QOSCTRL_CQOS) | + (3 << SAMD21_USB_QOSCTRL_DQOS)); + + ao_arch_block_interrupts(); + + /* set full speed */ + samd21_usb.ctrlb = (SAMD21_USB_CTRLB_SPDCONF_FS << SAMD21_USB_CTRLB_SPDCONF); + + /* Set device mode */ + samd21_usb.ctrla = ((0 << SAMD21_USB_CTRLA_MODE) | + (1 << SAMD21_USB_CTRLA_RUNSTDBY) | + (1 << SAMD21_USB_CTRLA_ENABLE)); + + while ((samd21_usb.syncbusy & (1 << SAMD21_USB_SYNCBUSY_ENABLE)) != 0) + ; + + /* configure interrupts */ + samd21_nvic_set_enable(SAMD21_NVIC_ISR_USB_POS); + samd21_nvic_set_priority(SAMD21_NVIC_ISR_USB_POS, 2); + + samd21_usb.intenset = ((1 << SAMD21_USB_INTFLAG_EORST)); + + ao_arch_release_interrupts(); + + for (t = 0; t < 50000; t++) + ao_arch_nop(); + + /* Attach */ + samd21_usb.ctrlb &= (uint16_t) ~(1 << SAMD21_USB_CTRLB_DETACH); +} + +void +ao_usb_init(void) +{ + ao_usb_enable(); + +#if AO_USB_DEVICE_ID_SERIAL + ao_usb_serial_init(); +#endif + + ao_usb_ep0_state = AO_USB_EP0_IDLE; +} diff --git a/src/samd21/samd21.h b/src/samd21/samd21.h new file mode 100644 index 00000000..73b3db89 --- /dev/null +++ b/src/samd21/samd21.h @@ -0,0 +1,1848 @@ +/* + * 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_TRIGSRC 8 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_DISABLE 0x00 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_RX(n) (0x01 + (n) * 2) +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_TX(n) (0x02 + (n) * 2) +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_OVF 0x0d +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC0 0x0e +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC1 0x0f +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC2 0x10 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC0_MC3 0x11 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_OVF 0x12 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_MC0 0x13 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC1_MC1 0x14 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_OVF 0x15 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_MC0 0x16 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC2_MC1 0x17 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_OVF 0x18 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_MC0 0x19 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC3_MC1 0x1a +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_OVF 0x1b +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_MC0 0x1c +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC4_MC1 0x1d +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_OVF 0x1e +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_MC0 0x1f +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC5_MC1 0x20 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_OVF 0x21 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_MC0 0x22 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC6_MC1 0x23 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_OVF 0x24 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_MC0 0x25 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TC7_MC1 0x26 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_ADC_RESRDY 0x27 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_DAC_EMPTY 0x28 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_RX_0 0x29 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_RX_1 0x2a +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_TX_0 0x2b +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_I2S_TX_1 0x2c +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_OVF 0x2d +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC0 0x2e +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC1 0x2f +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC2 0x30 +#define SAMD21_DMAC_CHCTRLB_TRIGSRC_TCC3_MC3 0x31 + +#define SAMD21_DMAC_CHCTRLB_TRIGACT 22 +#define SAMD21_DMAC_CHCTRLB_TRIGACT_BLOCK 0 +#define SAMD21_DMAC_CHCTRLB_TRIGACT_BEAT 2 +#define SAMD21_DMAC_CHCTRLB_TRIGACT_TRANSACTION 3 + +#define SAMD21_DMAC_CHCTRLB_CMD 24 +#define SAMD21_DMAC_CHCTRLB_CMD_NOACT 0 +#define SAMD21_DMAC_CHCTRLB_CMD_SUSPEND 1 +#define SAMD21_DMAC_CHCTRLB_CMD_RESUME 2 + +#define SAMD21_DMAC_CHINTFLAG_TERR 0 +#define SAMD21_DMAC_CHINTFLAG_TCMPL 1 +#define SAMD21_DMAC_CHINTFLAG_SUSP 2 + +#define SAMD21_DMAC_CHSTATUS_PEND 0 +#define SAMD21_DMAC_CHSTATUS_BUSY 1 +#define SAMD21_DMAC_CHSTATUS_FERR 2 + +#define SAMD21_DMAC_DESC_BTCTRL_VALID 0 +#define SAMD21_DMAC_DESC_BTCTRL_EVOSEL 1 +#define SAMD21_DMAC_DESC_BTCTRL_BLOCKACT 3 +#define SAMD21_DMAC_DESC_BTCTRL_BEATSIZE 8 +#define SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_BYTE 0 +#define SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_HWORD 1 +#define SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_WORD 2 +#define SAMD21_DMAC_DESC_BTCTRL_SRCINC 10 +#define SAMD21_DMAC_DESC_BTCTRL_DSTINC 11 +#define SAMD21_DMAC_DESC_BTCTRL_STEPSEL 12 +#define SAMD21_DMAC_DESC_BTCTRL_STEPSIZE 13 + +struct samd21_nvmctrl { + vuint32_t ctrla; + vuint32_t ctrlb; + vuint32_t param; + vuint32_t intenclr; + + vuint32_t intenset; + vuint32_t intflag; + vuint32_t status; + vuint32_t addr; + + vuint32_t lock; +}; + +extern struct samd21_nvmctrl samd21_nvmctrl; + +#define samd21_nvmctrl (*(struct samd21_nvmctrl *) 0x41004000) + +#define SAMD21_NVMCTRL_CTRLA_CMD 0 +#define SAMD21_NVMCTRL_CTRLA_CMD_ER 0x02 +#define SAMD21_NVMCTRL_CTRLA_CMD_WP 0x04 +#define SAMD21_NVMCTRL_CTRLA_CMD_EAR 0x05 +#define SAMD21_NVMCTRL_CTRLA_CMD_WAP 0x06 +#define SAMD21_NVMCTRL_CTRLA_CMD_RWWEEER 0x1a +#define SAMD21_NVMCTRL_CTRLA_CMD_RWEEEWP 0x1c +#define SAMD21_NVMCTRL_CTRLA_CMD_LR 0x40 +#define SAMD21_NVMCTRL_CTRLA_CMD_UR 0x41 +#define SAMD21_NVMCTRL_CTRLA_CMD_SPRM 0x42 +#define SAMD21_NVMCTRL_CTRLA_CMD_CPRM 0x43 +#define SAMD21_NVMCTRL_CTRLA_CMD_PBC 0x44 +#define SAMD21_NVMCTRL_CTRLA_CMD_SSB 0x45 +#define SAMD21_NVMCTRL_CTRLA_CMD_INVALL 0x46 +#define SAMD21_NVMCTRL_CTRLA_CMD_LDR 0x47 +#define SAMD21_NVMCTRL_CTRLA_CMD_UDR 0x48 +#define SAMD21_NVMCTRL_CTRLA_CMDEX 8 +#define SAMD21_NVMCTRL_CTRLA_CMDEX_KEY 0xa5 + +#define SAMD21_NVMCTRL_CTRLB_RWS 1 +#define SAMD21_NVMCTRL_CTRLB_MANW 7 +#define SAMD21_NVMCTRL_CTRLB_SLEEPRM 8 +#define SAMD21_NVMCTRL_CTRLB_READMODE 16 +#define SAMD21_NVMCTRL_CTRLB_CACHEDIS 18 + +#define SAMD21_NVMCTRL_INTENCLR_READY 0 +#define SAMD21_NVMCTRL_INTENCLR_ERROR 1 + +#define SAMD21_NVMCTRL_INTENSET_READY 0 +#define SAMD21_NVMCTRL_INTENSET_ERROR 1 + +#define SAMD21_NVMCTRL_INTFLAG_READY 0 +#define SAMD21_NVMCTRL_INTFLAG_ERROR 1 + +#define SAMD21_NVMCTRL_STATUS_PRM 0 +#define SAMD21_NVMCTRL_STATUS_LOAD 1 +#define SAMD21_NVMCTRL_STATUS_PROGE 2 +#define SAMD21_NVMCTRL_STATUS_LOCKE 3 +#define SAMD21_NVMCTRL_STATUS_NVME 4 +#define SAMD21_NVMCTRL_STATUS_SB 8 + +#define SAMD21_NVMCTRL_PARAM_NVMP 0 +#define SAMD21_NVMCTRL_PARAM_NVMP_MASK 0xffff +#define SAMD21_NVMCTRL_PARAM_PSZ 16 +#define SAMD21_NVMCTRL_PARAM_PSZ_MASK 0x7 +#define SAMD21_NVMCTRL_PARAM_RWWEEP 20 +#define SAMD21_NVMCTRL_PARAM_RWWEEP_MASK 0xfff + +static inline uint32_t +samd21_nvmctrl_page_size(void) +{ + return 1 << (3 + ((samd21_nvmctrl.param >> SAMD21_NVMCTRL_PARAM_PSZ) & + SAMD21_NVMCTRL_PARAM_PSZ_MASK)); +} + +uint32_t +samd21_flash_size(void); + +struct samd21_port { + vuint32_t dir; + vuint32_t dirclr; + vuint32_t dirset; + vuint32_t dirtgl; + + vuint32_t out; + vuint32_t outclr; + vuint32_t outset; + vuint32_t outtgl; + + vuint32_t in; + vuint32_t ctrl; + vuint32_t wrconfig; + vuint32_t reserved_2c; + + vuint8_t pmux[16]; + + vuint8_t pincfg[32]; +}; + +extern struct samd21_port samd21_port_a; +extern struct samd21_port samd21_port_b; + +#define samd21_port_a (*(struct samd21_port *) 0x41004400) +#define samd21_port_b (*(struct samd21_port *) 0x41004480) + +#define SAMD21_PORT_PINCFG_PMUXEN 0 +#define SAMD21_PORT_PINCFG_INEN 1 +#define SAMD21_PORT_PINCFG_PULLEN 2 +#define SAMD21_PORT_PINCFG_DRVSTR 6 + +#define SAMD21_PORT_PMUX_FUNC_A 0 +#define SAMD21_PORT_PMUX_FUNC_B 1 +#define SAMD21_PORT_PMUX_FUNC_C 2 +#define SAMD21_PORT_PMUX_FUNC_D 3 +#define SAMD21_PORT_PMUX_FUNC_E 4 +#define SAMD21_PORT_PMUX_FUNC_F 5 +#define SAMD21_PORT_PMUX_FUNC_G 6 +#define SAMD21_PORT_PMUX_FUNC_H 7 +#define SAMD21_PORT_PMUX_FUNC_I 8 + +#define SAMD21_PORT_DIR_OUT 1 +#define SAMD21_PORT_DIR_IN 0 + +static inline void +samd21_port_dir_set(struct samd21_port *port, uint8_t pin, uint8_t dir) +{ + if (dir) + port->dirset = (1 << pin); + else + port->dirclr = (1 << pin); +} + +static inline void +samd21_port_pincfg_set(struct samd21_port *port, uint8_t pin, uint8_t pincfg_mask, uint8_t pincfg) +{ + port->pincfg[pin] = (uint8_t) ((port->pincfg[pin] & ~pincfg_mask) | pincfg); +} + +static inline uint8_t +samd21_port_pincfg_get(struct samd21_port *port, uint8_t pin) +{ + return port->pincfg[pin]; +} + +static inline void +samd21_port_pmux_set(struct samd21_port *port, uint8_t pin, uint8_t func) +{ + uint8_t byte = pin >> 1; + uint8_t bit = (pin & 1) << 2; + uint8_t mask = 0xf << bit; + uint8_t value = (uint8_t) ((port->pmux[byte] & ~mask) | (func << bit)); + port->pmux[byte] = value; + samd21_port_pincfg_set(port, pin, + (1 << SAMD21_PORT_PINCFG_PMUXEN), + (1 << SAMD21_PORT_PINCFG_PMUXEN)); +} + +struct samd21_adc { + vuint8_t ctrla; + vuint8_t refctrl; + vuint8_t avgctrl; + vuint8_t sampctrl; + vuint16_t ctrlb; + vuint16_t reserved_06; + vuint8_t winctrl; + vuint8_t reserved_09; + vuint16_t reserved_0a; + vuint8_t swtrig; + vuint8_t reserved_0d; + vuint16_t reserved_0e; + + vuint32_t inputctrl; + vuint8_t evctrl; + vuint8_t reserved_15; + vuint8_t intenclr; + vuint8_t intenset; + vuint8_t intflag; + vuint8_t status; + vuint16_t result; + vuint16_t winlt; + vuint16_t reserved_1e; + + vuint16_t winut; + vuint16_t reserved_22; + vuint16_t gaincorr; + vuint16_t offsetcorr; + vuint16_t calib; + vuint8_t dbgctrl; + vuint8_t reserved_2b; + vuint32_t reserved_2c; +}; + +#define SAMD21_ADC_CTRLA_SWRST 0 +#define SAMD21_ADC_CTRLA_ENABLE 1 +#define SAMD21_ADC_CTRLA_RUNSTDBY 2 + +#define SAMD21_ADC_REFCTRL_REFSEL 0 +#define SAMD21_ADC_REFCTRL_REFSEL_INT1V 0 +#define SAMD21_ADC_REFCTRL_REFSEL_INTVCC0 1 +#define SAMD21_ADC_REFCTRL_REFSEL_INTVCC1 2 +#define SAMD21_ADC_REFCTRL_REFSEL_VREFA 3 +#define SAMD21_ADC_REFCTRL_REFSEL_VREFB 4 +#define SAMD21_ADC_REFCTRL_REFCOMP 7 + +#define SAMD21_ADC_AVGCTRL_SAMPLENUM 0 +#define SAMD21_ADC_AVGCTRL_ADJRES 4 + +#define SAMD21_ADC_SAMPCTRL_SAMPLEN 0 + +#define SAMD21_ADC_CTRLB_DIFFMODE 0 +#define SAMD21_ADC_CTRLB_LEFTADJ 1 +#define SAMD21_ADC_CTRLB_FREERUN 2 +#define SAMD21_ADC_CTRLB_CORREN 3 +#define SAMD21_ADC_CTRLB_RESSEL 4 +#define SAMD21_ADC_CTRLB_RESSEL_12BIT 0 +#define SAMD21_ADC_CTRLB_RESSEL_16BIT 1 +#define SAMD21_ADC_CTRLB_RESSEL_10BIT 2 +#define SAMD21_ADC_CTRLB_RESSEL_8BIT 3 +#define SAMD21_ADC_CTRLB_PRESCALER 8 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV4 0 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV8 1 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV16 2 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV32 3 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV64 4 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV128 5 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV256 6 +#define SAMD21_ADC_CTRLB_PRESCALER_DIV512 7 + +#define SAMD21_ADC_SWTRIG_FLUSH 0 +#define SAMD21_ADC_SWTRIG_START 1 + +#define SAMD21_ADC_INPUTCTRL_MUXPOS 0 +# define SAMD21_ADC_INPUTCTRL_MUXPOS_BANDGAP 0x19 +# define SAMD21_ADC_INPUTCTRL_MUXPOS_SCALEDCOREVCC 0x1a +# define SAMD21_ADC_INPUTCTRL_MUXPOS_SCALEDIOVCC 0x1b +# define SAMD21_ADC_INPUTCTRL_MUXPOS_DAC 0x1c +#define SAMD21_ADC_INPUTCTRL_MUXNEG 8 +# define SAMD21_ADC_INPUTCTRL_MUXNEG_GND 0x18 +# define SAMD21_ADC_INPUTCTRL_MUXNEG_IOGND 0x19 +#define SAMD21_ADC_INPUTCTRL_INPUTSCAN 16 +#define SAMD21_ADC_INPUTCTRL_INPUTOFFSET 20 +#define SAMD21_ADC_INPUTCTRL_GAIN 24 +#define SAMD21_ADC_INPUTCTRL_GAIN_1X 0 +#define SAMD21_ADC_INPUTCTRL_GAIN_DIV2 0xf + +#define SAMD21_ADC_INTFLAG_RESRDY 0 +#define SAMD21_ADC_INTFLAG_OVERRUN 1 +#define SAMD21_ADC_INTFLAG_WINMON 2 +#define SAMD21_ADC_INTFLAG_SYNCRDY 3 + +#define SAMD21_ADC_STATUS_SYNCBUSY 7 + +#define SAMD21_ADC_CALIB_LINEARITY_CAL 0 +#define SAMD21_ADC_CALIB_BIAS_CAL 16 + +extern struct samd21_adc samd21_adc; + +#define samd21_adc (*(struct samd21_adc *) 0x42004000) + +struct samd21_dac { + vuint8_t ctrla; + vuint8_t ctrlb; + vuint8_t evctrl; + uint8_t reserved_03; + + vuint8_t intenclr; + vuint8_t intenset; + vuint8_t intflag; + vuint8_t status; + + vuint16_t data; + uint16_t reserved_0a; + + vuint16_t databuf; +}; + +#define SAMD21_DAC_CTRLA_SWRST 0 +#define SAMD21_DAC_CTRLA_ENABLE 1 +#define SAMD21_DAC_CTRLA_RUNSTDBY 2 + +#define SAMD21_DAC_CTRLB_EOEN 0 +#define SAMD21_DAC_CTRLB_IOEN 1 +#define SAMD21_DAC_CTRLB_LEFTADJ 2 +#define SAMD21_DAC_CTRLB_VPD 3 +#define SAMD21_DAC_CTRLB_BDWP 4 +#define SAMD21_DAC_CTRLB_REFSEL 6 +#define SAMD21_DAC_CTRLB_REFSEL_INTREF 0 +#define SAMD21_DAC_CTRLB_REFSEL_VDDANA 1 +#define SAMD21_DAC_CTRLB_REFSEL_VREFA 2 +#define SAMD21_DAC_CTRLB_REFSEL_MASK 3 + +#define SAMD21_DAC_EVCTRL_STARTEI 0 +#define SAMD21_DAC_EVCTRL_EMPTYEO 1 + +#define SAMD21_DAC_INTENCLR_UNDERRUN 0 +#define SAMD21_DAC_INTENCLR_EMPTY 1 +#define SAMD21_DAC_INTENCLR_SYNCRDY 2 + +#define SAMD21_DAC_INTENSET_UNDERRUN 0 +#define SAMD21_DAC_INTENSET_EMPTY 1 +#define SAMD21_DAC_INTENSET_SYNCRDY 2 + +#define SAMD21_DAC_INTFLAG_UNDERRUN 0 +#define SAMD21_DAC_INTFLAG_EMPTY 1 +#define SAMD21_DAC_INTFLAG_SYNCRDY 2 + +#define SAMD21_DAC_STATUS_SYNCBUSY 7 + +extern struct samd21_dac samd21_dac; +#define samd21_dac (*(struct samd21_dac *) 0x42004800) + +/* TC */ +struct samd21_tc { + vuint16_t ctrla; + vuint16_t readreq; + vuint8_t ctrlbclr; + vuint8_t ctrlbset; + vuint8_t ctrlc; + vuint8_t reserved_07; + vuint8_t dbgctrl; + vuint8_t reserved_09; + vuint16_t evctrl; + vuint8_t intenclr; + vuint8_t intenset; + vuint8_t intflag; + vuint8_t status; + + union { + struct { + vuint8_t count; + vuint8_t reserved_11; + vuint16_t reserved_12; + vuint8_t per; + vuint8_t reserved_15; + vuint16_t reserved_16; + vuint8_t cc[2]; + } mode_8; + struct { + vuint16_t count; + vuint16_t reserved_12; + vuint32_t reserved_14; + vuint16_t cc[2]; + } mode_16; + struct { + vuint32_t count; + vuint32_t reserved_14; + vuint32_t cc[2]; + } mode_32; + }; +}; + +extern struct samd21_tc samd21_tc3; +#define samd21_tc3 (*(struct samd21_tc *) 0x42002c00) + +extern struct samd21_tc samd21_tc4; +#define samd21_tc4 (*(struct samd21_tc *) 0x42003000) + +extern struct samd21_tc samd21_tc5; +#define samd21_tc5 (*(struct samd21_tc *) 0x42003400) + +#ifdef ATSAMD21J +/* Present on all of the samd21j parts and the samd21g16l */ +extern struct samd21_tc samd21_tc6; +#define samd21_tc6 (*(struct samd21_tc *) 0x42003800) + +extern struct samd21_tc samd21_tc7; +#define samd21_tc7 (*(struct samd21_tc *) 0x42003c00) +#endif + +#define SAMD21_TC_CTRLA_SWRST 0 +#define SAMD21_TC_CTRLA_ENABLE 1 +#define SAMD21_TC_CTRLA_MODE 2 +#define SAMD21_TC_CTRLA_MODE_COUNT16 0 +#define SAMD21_TC_CTRLA_MODE_COUNT8 1 +#define SAMD21_TC_CTRLA_MODE_COUNT32 2 +#define SAMD21_TC_CTRLA_WAVEGEN 5 +#define SAMD21_TC_CTRLA_WAVEGEN_NFRQ 0 +#define SAMD21_TC_CTRLA_WAVEGEN_MFRQ 1 +#define SAMD21_TC_CTRLA_WAVEGEN_NPWM 2 +#define SAMD21_TC_CTRLA_WAVEGEN_MPWM 3 +#define SAMD21_TC_CTRLA_PRESCALER 8 +#define SAMD21_TC_CTRLA_PRESCALER_DIV1 0 +#define SAMD21_TC_CTRLA_PRESCALER_DIV2 1 +#define SAMD21_TC_CTRLA_PRESCALER_DIV4 2 +#define SAMD21_TC_CTRLA_PRESCALER_DIV8 3 +#define SAMD21_TC_CTRLA_PRESCALER_DIV16 4 +#define SAMD21_TC_CTRLA_PRESCALER_DIV64 5 +#define SAMD21_TC_CTRLA_PRESCALER_DIV256 6 +#define SAMD21_TC_CTRLA_PRESCALER_DIV1024 7 +#define SAMD21_TC_CTRLA_RUNSTDBY 11 +#define SAMD21_TC_CTRLA_PRESCSYNC 12 +#define SAMD21_TC_CTRLA_PRESCSYNC_GCLK 0 +#define SAMD21_TC_CTRLA_PRESCSYNC_PRSEC 1 +#define SAMD21_TC_CTRLA_PRESCSYNC_RESYNC 2 + +#define SAMD21_TC_READREQ_ADDR 0 +#define SAMD21_TC_READREQ_RCONT 14 +#define SAMD21_TC_READREQ_RREQ 15 +#define SAMD21_TC_CTRLB_DIR 0 +#define SAMD21_TC_CTRLB_ONESHOT 2 +#define SAMD21_TC_CTRLB_CMD 6 +#define SAMD21_TC_CTRLC_INVEN(x) (0 + (x)) +#define SAMD21_TC_CTRLC_CPTEN(x) (4 + (x)) +#define SAMD21_TC_DBGCTRL_DBGRUN 0 +#define SAMD21_TC_EVCTRL_EVACT 0 +#define SAMD21_TC_EVCTRL_TCINV 4 +#define SAMD21_TC_EVCTRL_TCEI 5 +#define SAMD21_TC_EVCTRL_OVFEO 8 +#define SAMD21_TC_EVCTRL_MCEO(x) (12 + (x)) + +#define SAMD21_TC_INTFLAG_MC(x) (4 + (x)) +#define SAMD21_TC_INTFLAG_SYNCRDY 3 +#define SAMD21_TC_INTFLAG_ERR 1 +#define SAMD21_TC_INTFLAG_OVF 0 + +#define SAMD21_TC_STATUS_STOP 3 +#define SAMD21_TC_STATUS_FOLLOWER 4 +#define SAMD21_TC_STATUS_SYNCBUSY 7 + +/* TCC */ + +struct samd21_tcc { + vuint32_t ctrla; + vuint8_t ctrlbclr; + vuint8_t ctrlbset; + vuint16_t reserved_06; + vuint32_t syncbusy; + vuint32_t fctrla; + + vuint32_t fctlrb; + vuint32_t wexctrl; + vuint32_t drvctrl; + vuint16_t reserved_1c; + vuint8_t dbgctrl; + vuint8_t reserved_1f; + + vuint32_t evctrl; + vuint32_t intenclr; + vuint32_t intenset; + vuint32_t intflag; + + vuint32_t status; + vuint32_t count; + vuint16_t patt; + vuint16_t reserved_3a; + vuint32_t wave; + + vuint32_t per; + vuint32_t cc[4]; + vuint32_t reserved_54; + vuint32_t reserved_58; + vuint32_t reserved_5c; + + vuint32_t reserved_60; + vuint16_t pattb; + vuint16_t reserved_66; + vuint32_t waveb; + vuint32_t perb; + + vuint32_t ccb[4]; +}; + +extern struct samd21_tcc samd21_tcc0; +#define samd21_tcc0 (*(struct samd21_tcc *) 0x42002000) + +extern struct samd21_tcc samd21_tcc1; +#define samd21_tcc1 (*(struct samd21_tcc *) 0x42002400) + +extern struct samd21_tcc samd21_tcc2; +#define samd21_tcc2 (*(struct samd21_tcc *) 0x42002800) + +#ifdef SAMD21E17D +/* only on the samd21e17d */ +extern struct samd21_tcc samd21_tcc3; +#define samd21_tcc3 (*(struct samd21_tcc *) 0x42006000) +#endif + +#define SAMD21_TCC_CTRLA_SWRST 0 +#define SAMD21_TCC_CTRLA_ENABLE 1 +#define SAMD21_TCC_CTRLA_RESOLUTION 5 +#define SAMD21_TCC_CTRLA_RESOLUTION_NONE 0 +#define SAMD21_TCC_CTRLA_RESOLUTION_DITH4 1 +#define SAMD21_TCC_CTRLA_RESOLUTION_DITH5 2 +#define SAMD21_TCC_CTRLA_RESOLUTION_DITH6 3 +#define SAMD21_TCC_CTRLA_PRESCALER 8 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV1 0 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV2 1 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV4 2 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV8 3 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV16 4 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV64 5 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV256 6 +#define SAMD21_TCC_CTRLA_PRESCALER_DIV1024 7 +#define SAMD21_TCC_CTRLA_RUNSTDBY 11 +#define SAMD21_TCC_CTRLA_PRESYNC 12 +#define SAMD21_TCC_CTRLA_PRESYNC_GCLK 0 +#define SAMD21_TCC_CTRLA_PRESYNC_PRESC 1 +#define SAMD21_TCC_CTRLA_PRESYNC_RESYNC 2 +#define SAMD21_TCC_CTRLA_ALOCK 14 +#define SAMD21_TCC_CTRLA_CPTEN(n) (24 + (n)) + +#define SAMD21_TCC_CTRLB_DIR 0 +#define SAMD21_TCC_CTRLB_LUPD 1 +#define SAMD21_TCC_CTRLB_ONESHOT 2 +#define SAMD21_TCC_CTRLB_IDXCMD 3 +#define SAMD21_TCC_CTRLB_IDXCMD_DISABLE 0 +#define SAMD21_TCC_CTRLB_IDXCMD_SET 1 +#define SAMD21_TCC_CTRLB_IDXCMD_CLEAR 2 +#define SAMD21_TCC_CTRLB_IDXCMD_HOLD 3 +#define SAMD21_TCC_CTRLB_CMD 5 +#define SAMD21_TCC_CTRLB_CMD_NONE 0 +#define SAMD21_TCC_CTRLB_CMD_RETRIGGER 1 +#define SAMD21_TCC_CTRLB_CMD_STOP 2 +#define SAMD21_TCC_CTRLB_CMD_UPDATE 3 +#define SAMD21_TCC_CTRLB_CMD_READSYNC 4 +#define SAMD21_TCC_CTRLB_CMD_DMAOS 5 + +#define SAMD21_TCC_SYNCBUSY_SWRST 0 +#define SAMD21_TCC_SYNCBUSY_ENABLE 1 +#define SAMD21_TCC_SYNCBUSY_CTRLB 2 +#define SAMD21_TCC_SYNCBUSY_STATUS 3 +#define SAMD21_TCC_SYNCBUSY_COUNT 4 +#define SAMD21_TCC_SYNCBUSY_PATT 5 +#define SAMD21_TCC_SYNCBUSY_WAVE 6 +#define SAMD21_TCC_SYNCBUSY_PER 7 +#define SAMD21_TCC_SYNCBUSY_CC(x) (8 + (x)) +#define SAMD21_TCC_SYNCBUSY_PATTB 16 +#define SAMD21_TCC_SYNCBUSY_WAVEB 17 +#define SAMD21_TCC_SYNCBUSY_PERB 18 +#define SAMD21_TCC_SYNCBUSY_CCB(x) ((19 + (x)) + +#define SAMD21_TCC_DBGCTRL_FDDBD 2 +#define SAMD21_TCC_DBGCTRL_DBGRUN 0 + +#define SAMD21_TCC_EVCTRL_EVACTO 0 +#define SAMD21_TCC_EVCTRL_EVACT1 3 +#define SAMD21_TCC_EVCTRL_CNTSEL 6 +#define SAMD21_TCC_EVCTRL_OVFEO 8 +#define SAMD21_TCC_EVCTRL_TRGEO 9 +#define SAMD21_TCC_EVCTRL_CNTEO 10 +#define SAMD21_TCC_EVCTRL_TCINV(x) (12 + (x)) +#define SAMD21_TCC_EVCTRL_MCEI(x) (16 + (x)) +#define SAMD21_TCC_EVCTRL_MCEO(x) (24 + (x)) + +#define SAMD21_TCC_INTFLAG_OVF 0 +#define SAMD21_TCC_INTFLAG_TRG 1 +#define SAMD21_TCC_INTFLAG_CNT 2 +#define SAMD21_TCC_INTFLAG_ERR 3 +#define SAMD21_TCC_INTFLAG_UFS 10 +#define SAMD21_TCC_INTFLAG_DFS 11 +#define SAMD21_TCC_INTFLAG_FAULTA 12 +#define SAMD21_TCC_INTFLAG_FAULTB 13 +#define SAMD21_TCC_INTFLAG_FAULT0 14 +#define SAMD21_TCC_INTFLAG_FAULT1 15 +#define SAMD21_TCC_INTFLAG_MC(x) (16 + (x)) + +#define SAMD21_TCC_WAVE_WAVEGEN 0 +#define SAMD21_TCC_WAVE_WAVEGEN_NFRQ 0 +#define SAMD21_TCC_WAVE_WAVEGEN_MFRQ 1 +#define SAMD21_TCC_WAVE_WAVEGEN_NPWM 2 +#define SAMD21_TCC_WAVE_WAVEGEN_DSCRITICAL 4 +#define SAMD21_TCC_WAVE_WAVEGEN_DSBOTTOM 5 +#define SAMD21_TCC_WAVE_WAVEGEN_DSBOTH 6 +#define SAMD21_TCC_WAVE_WAVEGEN_DSTOP 7 +#define SAMD21_TCC_WAVE_RAMP 4 +#define SAMD21_TCC_WAVE_CIPEREN 7 +#define SAMD21_TCC_WAVE_CCCEN(x) (8 + (x)) +#define SAMD21_TCC_WAVE_POL(x) (16 + (x)) +#define SAMD21_TCC_WAVE_SWAP(x) (24 + (x)) + +/* USB */ + +struct samd21_usb { + vuint8_t ctrla; + vuint8_t reserved_01; + vuint8_t syncbusy; + vuint8_t qosctrl; + + vuint32_t reserved_04; + vuint16_t ctrlb; + vuint8_t dadd; + vuint8_t reserved_0b; + vuint8_t status; + vuint8_t fsmstatus; + vuint16_t reserved_0e; + + vuint16_t fnum; + vuint16_t reserved_12; + vuint16_t intenclr; + vuint16_t reserved_16; + vuint16_t intenset; + vuint16_t reserved_1a; + vuint16_t intflag; + vuint16_t reserved_1e; + + vuint16_t epintsmry; + vuint16_t reserved_22; + + vuint32_t descadd; + vuint16_t padcal; + uint8_t reserved_2a[0x100 - 0x2a]; + + struct { + vuint8_t epcfg; + vuint8_t reserved_01; + vuint8_t reserved_02; + vuint8_t binterval; + vuint8_t epstatusclr; + vuint8_t epstatusset; + vuint8_t epstatus; + vuint8_t epintflag; + vuint8_t epintenclr; + vuint8_t epintenset; + vuint8_t reserved_0a[0x20 - 0x0a]; + } ep[8]; +}; + +extern struct samd21_usb samd21_usb; + +#define samd21_usb (*(struct samd21_usb *) 0x41005000) + +#define SAMD21_USB_CTRLA_SWRST 0 +#define SAMD21_USB_CTRLA_ENABLE 1 +#define SAMD21_USB_CTRLA_RUNSTDBY 2 +#define SAMD21_USB_CTRLA_MODE 7 + +#define SAMD21_USB_SYNCBUSY_SWRST 0 +#define SAMD21_USB_SYNCBUSY_ENABLE 1 + +#define SAMD21_USB_QOSCTRL_CQOS 0 +#define SAMD21_USB_QOSCTRL_DQOS 2 + +#define SAMD21_USB_CTRLB_DETACH 0 +#define SAMD21_USB_CTRLB_UPRSM 1 +#define SAMD21_USB_CTRLB_SPDCONF 2 +#define SAMD21_USB_CTRLB_SPDCONF_FS 0 +#define SAMD21_USB_CTRLB_SPDCONF_LS 1 +#define SAMD21_USB_CTRLB_SPDCONF_MASK 0x3 +#define SAMD21_USB_CTRLB_NREPLY 4 +#define SAMD21_USB_CTRLB_GNAK 9 +#define SAMD21_USB_CTRLB_LPMHDSK 10 +#define SAMD21_USB_CTRLB_LPMHDSK_NONE 0 +#define SAMD21_USB_CTRLB_LPMHDSK_ACK 1 +#define SAMD21_USB_CTRLB_LPMHDSK_NYET 2 +#define SAMD21_USB_CTRLB_LPMHDSK_MASK 3 + +#define SAMD21_USB_DADD_DADD 0 +#define SAMD21_USB_DADD_ADDEN 7 + +#define SAMD21_USB_STATUS_SPEED 2 +#define SAMD21_USB_STATUS_LINESTATE 6 +#define SAMD21_USB_FNUM_MFNUM 0 +#define SAMD21_USB_FNUM_FNUM 3 +#define SAMD21_USB_FNUM_FNCERR 15 +#define SAMD21_USB_INTFLAG_SUSPEND 0 +#define SAMD21_USB_INTFLAG_SOF 2 +#define SAMD21_USB_INTFLAG_EORST 3 +#define SAMD21_USB_INTFLAG_WAKEUP 4 +#define SAMD21_USB_INTFLAG_EORSM 5 +#define SAMD21_USB_INTFLAG_UPRSM 6 +#define SAMD21_USB_INTFLAG_RAMACER 7 +#define SAMD21_USB_INTFLAG_LPMNYET 8 +#define SAMD21_USB_INTFLAG_LPMSUSP 9 + +#define SAMD21_USB_PADCAL_TRANSP 0 +#define SAMD21_USB_PADCAL_TRANSN 6 +#define SAMD21_USB_PADCAL_TRIM 12 + +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT 0 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DISABLED 0 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_CONTROL 1 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_ISOCHRONOUS 2 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_BULK 3 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_INTERRUPT 4 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_OUT_DUAL_BANK 5 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN 4 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN_DISABLED 0 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN_CONTROL 1 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN_ISOCHRONOUS 2 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN_BULK 3 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN_INTERRUPT 4 +#define SAMD21_USB_EP_EPCFG_EP_TYPE_IN_DUAL_BANK 5 + +#define SAMD21_USB_EP_EPSTATUS_DTGLOUT 0 +#define SAMD21_USB_EP_EPSTATUS_DTGLIN 1 +#define SAMD21_USB_EP_EPSTATUS_CURBK 2 +#define SAMD21_USB_EP_EPSTATUS_STALLRQ0 4 +#define SAMD21_USB_EP_EPSTATUS_STALLRQ1 5 +#define SAMD21_USB_EP_EPSTATUS_BK0RDY 6 +#define SAMD21_USB_EP_EPSTATUS_BK1RDY 7 + +#define SAMD21_USB_EP_EPINTFLAG_TRCPT0 0 +#define SAMD21_USB_EP_EPINTFLAG_TRCPT1 1 +#define SAMD21_USB_EP_EPINTFLAG_TRFAIL0 2 +#define SAMD21_USB_EP_EPINTFLAG_TRFAIL1 3 +#define SAMD21_USB_EP_EPINTFLAG_RXSTP 4 +#define SAMD21_USB_EP_EPINTFLAG_STALL 5 + +struct samd21_usb_desc_bank { + vuint32_t addr; + vuint32_t pcksize; + vuint16_t extreg; + vuint8_t status_bk; + vuint8_t reserved_0b; + vuint32_t reserved_0c; +}; + +struct samd21_usb_desc { + struct samd21_usb_desc_bank bank[2]; +}; + +extern struct samd21_usb_desc samd21_usb_desc[8]; + +#define SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT 0 +#define SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT_MASK 0x3fffU +#define SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE 14 +#define SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE_MASK 0x3fffU +#define SAMD21_USB_DESC_PCKSIZE_SIZE 28 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_8 0 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_16 1 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_32 2 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_64 3 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_128 4 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_256 5 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_512 6 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_1023 7 +#define SAMD21_USB_DESC_PCKSIZE_SIZE_MASK 7U +#define SAMD21_USB_DESC_PCKSIZE_AUTO_ZLP 31 + +static inline uint16_t +samd21_usb_desc_get_byte_count(uint8_t ep, uint8_t bank) +{ + return ((samd21_usb_desc[ep].bank[bank].pcksize >> SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT) & + SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT_MASK); +} + +static inline void +samd21_usb_desc_set_byte_count(uint8_t ep, uint8_t bank, uint32_t count) +{ + uint32_t pcksize = samd21_usb_desc[ep].bank[bank].pcksize; + + pcksize &= ~(SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT_MASK << SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT); + pcksize &= ~(SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE_MASK << SAMD21_USB_DESC_PCKSIZE_MULTI_PACKET_SIZE); + pcksize |= (count << SAMD21_USB_DESC_PCKSIZE_BYTE_COUNT); + samd21_usb_desc[ep].bank[bank].pcksize = pcksize; +} + +static inline void +samd21_usb_desc_set_size(uint8_t ep, uint8_t bank, uint32_t size) +{ + uint32_t pcksize = samd21_usb_desc[ep].bank[bank].pcksize; + + pcksize &= ~(SAMD21_USB_DESC_PCKSIZE_SIZE_MASK << SAMD21_USB_DESC_PCKSIZE_SIZE); + + uint32_t size_bits = 0; + switch (size) { + case 8: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_8; break; + case 16: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_16; break; + case 32: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_32; break; + case 64: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_64; break; + case 128: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_128; break; + case 256: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_256; break; + case 512: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_512; break; + case 1023: size_bits = SAMD21_USB_DESC_PCKSIZE_SIZE_1023; break; + } + pcksize |= (size_bits << SAMD21_USB_DESC_PCKSIZE_SIZE); + samd21_usb_desc[ep].bank[bank].pcksize = pcksize; +} + +static inline void +samd21_usb_ep_set_ready(uint8_t ep, uint8_t bank) +{ + samd21_usb.ep[ep].epstatusset = (1 << (SAMD21_USB_EP_EPSTATUS_BK0RDY + bank)); + samd21_usb.ep[ep].epintflag = (1 << (SAMD21_USB_EP_EPINTFLAG_TRFAIL0 + bank)); +} + +static inline void +samd21_usb_ep_clr_ready(uint8_t ep, uint8_t bank) +{ + samd21_usb.ep[ep].epstatusclr = (1 << (SAMD21_USB_EP_EPSTATUS_BK0RDY + bank)); +} + +static inline uint8_t +samd21_usb_ep_ready(uint8_t ep) +{ + return (samd21_usb.ep[ep].epstatus >> SAMD21_USB_EP_EPSTATUS_BK0RDY) & 3; +} + +static inline uint8_t +samd21_usb_ep_curbk(uint8_t ep) +{ + return (samd21_usb.ep[ep].epstatus >> SAMD21_USB_EP_EPSTATUS_CURBK) & 1; +} + +/* sercom */ + +struct samd21_sercom { + vuint32_t ctrla; + vuint32_t ctrlb; + vuint32_t reserved_08; + vuint16_t baud; + vuint8_t rxpl; + vuint8_t reserved_0f; + + vuint32_t reserved_10; + vuint8_t intenclr; + vuint8_t reserved_15; + vuint8_t intenset; + vuint8_t reserved_17; + vuint8_t intflag; + vuint8_t reserved_19; + vuint16_t status; + vuint32_t syncbusy; + + vuint32_t reserved_20; + vuint32_t addr; + vuint16_t data; + vuint16_t reserved_2a; + vuint32_t reserved_2c; + + vuint8_t dbgctrl; + vuint8_t reserved_31; + vuint16_t reserved_32; + vuint16_t fifospace; + vuint16_t fifoptr; +}; + +extern struct samd21_sercom samd21_sercom0; +extern struct samd21_sercom samd21_sercom1; +extern struct samd21_sercom samd21_sercom2; +extern struct samd21_sercom samd21_sercom3; +extern struct samd21_sercom samd21_sercom4; +extern struct samd21_sercom samd21_sercom5; + +#define samd21_sercom0 (*(struct samd21_sercom *) 0x42000800) +#define samd21_sercom1 (*(struct samd21_sercom *) 0x42000c00) +#define samd21_sercom2 (*(struct samd21_sercom *) 0x42001000) +#define samd21_sercom3 (*(struct samd21_sercom *) 0x42001400) +#define samd21_sercom4 (*(struct samd21_sercom *) 0x42001800) +#define samd21_sercom5 (*(struct samd21_sercom *) 0x42001c00) + +#define SAMD21_SERCOM_CTRLA_SWRST 0 +#define SAMD21_SERCOM_CTRLA_ENABLE 1 +#define SAMD21_SERCOM_CTRLA_MODE 2 +# define SAMD21_SERCOM_CTRLA_MODE_USART 1 +# define SAMD21_SERCOM_CTRLA_MODE_I2C_LEADER 5 + +#define SAMD21_SERCOM_CTRLA_RUNSTDBY 7 + +/* USART mode */ +#define SAMD21_SERCOM_CTRLA_IBON 8 +#define SAMD21_SERCOM_CTRLA_SAMPR 13 +#define SAMD21_SERCOM_CTRLA_TXPO 16 +#define SAMD21_SERCOM_CTRLA_RXPO 20 +#define SAMD21_SERCOM_CTRLA_SAMPA 22 +#define SAMD21_SERCOM_CTRLA_FORM 24 +#define SAMD21_SERCOM_CTRLA_CMODE 28 +#define SAMD21_SERCOM_CTRLA_CPOL 29 +#define SAMD21_SERCOM_CTRLA_DORD 30 + +/* I2C controller mode */ +#define SAMD21_SERCOM_CTRLA_PINOUT 16 +#define SAMD21_SERCOM_CTRLA_SDAHOLD 20 +#define SAMD21_SERCOM_CTRLA_SDAHOLD_DIS 0 +#define SAMD21_SERCOM_CTRLA_SDAHOLD_75NS 1 +#define SAMD21_SERCOM_CTRLA_SDAHOLD_450NS 2 +#define SAMD21_SERCOM_CTRLA_SDAHOLD_600NS 3 +#define SAMD21_SERCOM_CTRLA_MEXTTOEN 22 +#define SAMD21_SERCOM_CTRLA_SEXTTOEN 23 +#define SAMD21_SERCOM_CTRLA_SPEED 24 +#define SAMD21_SERCOM_CTRLA_SPEED_STANDARD 0 +#define SAMD21_SERCOM_CTRLA_SPEED_FAST 1 +#define SAMD21_SERCOM_CTRLA_SPEED_HIGH 2 +#define SAMD21_SERCOM_CTRLA_SCLSM 27 +#define SAMD21_SERCOM_CTRLA_INACTOUT 28 +#define SAMD21_SERCOM_CTRLA_INACTOUT_DIS 0 +#define SAMD21_SERCOM_CTRLA_INACTOUT_55US 1 +#define SAMD21_SERCOM_CTRLA_INACTOUT_105US 2 +#define SAMD21_SERCOM_CTRLA_INACTOUT_205US 3 +#define SAMD21_SERCOM_CTRLA_LOWTOUT 30 + +/* USART mode */ +#define SAMD21_SERCOM_CTRLB_CHSIZE 0 +#define SAMD21_SERCOM_CTRLB_SBMODE 6 +#define SAMD21_SERCOM_CTRLB_COLDEN 8 +#define SAMD21_SERCOM_CTRLB_SFDE 9 +#define SAMD21_SERCOM_CTRLB_ENC 10 +#define SAMD21_SERCOM_CTRLB_PMODE 13 +#define SAMD21_SERCOM_CTRLB_TXEN 16 +#define SAMD21_SERCOM_CTRLB_RXEN 17 +#define SAMD21_SERCOM_CTRLB_FIFOCLR 22 + +/* I2C mode */ +#define SAMD21_SERCOM_CTRLB_SMEN 8 +#define SAMD21_SERCOM_CTRLB_QCEN 9 +#define SAMD21_SERCOM_CTRLB_CMD 16 +#define SAMD21_SERCOM_CTRLB_CMD_NOP 0 +#define SAMD21_SERCOM_CTRLB_CMD_START 1 +#define SAMD21_SERCOM_CTRLB_CMD_READ 2 +#define SAMD21_SERCOM_CTRLB_CMD_STOP 3 +#define SAMD21_SERCOM_CTRLB_ACKACT 18 +#define SAMD21_SERCOM_CTRLB_ACKACT_ACK 0 +#define SAMD21_SERCOM_CTRLB_ACKACT_NACK 1 +#define SAMD21_SERCOM_CTRLB_FIFOCLR 22 + +/* USART mode */ +#define SAMD21_SERCOM_INTFLAG_DRE 0 +#define SAMD21_SERCOM_INTFLAG_TXC 1 +#define SAMD21_SERCOM_INTFLAG_RXC 2 +#define SAMD21_SERCOM_INTFLAG_RXS 3 +#define SAMD21_SERCOM_INTFLAG_CTSIC 4 +#define SAMD21_SERCOM_INTFLAG_RXBRK 5 +#define SAMD21_SERCOM_INTFLAG_ERROR 7 + +/* I2C mode */ +#define SAMD21_SERCOM_INTFLAG_ERROR 7 +#define SAMD21_SERCOM_INTFLAG_RXFF 4 +#define SAMD21_SERCOM_INTFLAG_TXFE 3 +#define SAMD21_SERCOM_INTFLAG_SB 1 +#define SAMD21_SERCOM_INTFLAG_MB 0 + +#define SAMD21_SERCOM_INTENCLR_DRE 0 +#define SAMD21_SERCOM_INTENCLR_TXC 1 +#define SAMD21_SERCOM_INTENCLR_RXC 2 +#define SAMD21_SERCOM_INTENCLR_RXS 3 +#define SAMD21_SERCOM_INTENCLR_CTSIC 4 +#define SAMD21_SERCOM_INTENCLR_RXBRK 5 +#define SAMD21_SERCOM_INTENCLR_ERROR 7 + +#define SAMD21_SERCOM_STATUS_PERR 0 +#define SAMD21_SERCOM_STATUS_FERR 1 +#define SAMD21_SERCOM_STATUS_BUFOVF 2 +#define SAMD21_SERCOM_STATUS_CTS 3 +#define SAMD21_SERCOM_STATUS_ISF 4 +#define SAMD21_SERCOM_STATUS_COLL 5 +#define SAMD21_SERCOM_STATUS_TXE 6 + +#define SAMD21_SERCOM_SYNCBUSY_SWRST 0 +#define SAMD21_SERCOM_SYNCBUSY_ENABLE 1 +#define SAMD21_SERCOM_SYNCBUSY_CTRLB 2 +#define SAMD21_SERCOM_SYNCBUSY_SYSOP 2 + +#define SAMD21_SERCOM_ADDR_ADDR 0 +#define SAMD21_SERCOM_ADDR_LENEN 13 +#define SAMD21_SERCOM_ADDR_HS 14 +#define SAMD21_SERCOM_ADDR_TENBITEN 15 +#define SAMD21_SERCOM_ADDR_LEN 16 + +#define SAMD21_SERCOM_DBGCTRL_DBGSTOP 0 + +#define SAMD21_SERCOM_FIFOSPACE_TXSPACE 0 +#define SAMD21_SERCOM_FIFOSPACE_TXSPACE_MASK 0x1f +#define SAMD21_SERCOM_FIFOSPACE_RXSPACE 8 +#define SAMD21_SERCOM_FIFOSPACE_RXSPACE_MASK 0x1f + +#define SAMD21_SERCOM_FIFOPTR_CPUWRPTR 0 +#define SAMD21_SERCOM_FIFOPTR_CPUWRPTR_MASK 0xf +#define SAMD21_SERCOM_FIFOPTR_CPURDPTR 8 +#define SAMD21_SERCOM_FIFOPTR_CPURDPTR_MASK 0xf + +/* The SYSTICK starts at 0xe000e010 */ +struct samd21_systick { + vuint32_t csr; + vuint32_t rvr; + vuint32_t cvr; + vuint32_t calib; +}; + +extern struct samd21_systick samd21_systick; + +#define samd21_systick (*(struct samd21_systick *) 0xe000e010) + +#define SAMD21_SYSTICK_CSR_ENABLE 0 +#define SAMD21_SYSTICK_CSR_TICKINT 1 +#define SAMD21_SYSTICK_CSR_CLKSOURCE 2 +#define SAMD21_SYSTICK_CSR_CLKSOURCE_EXTERNAL 0 +#define SAMD21_SYSTICK_CSR_CLKSOURCE_HCLK_8 1 +#define SAMD21_SYSTICK_CSR_COUNTFLAG 16 + +#define SAMD21_SYSTICK_PRI 15 + +/* The NVIC starts at 0xe000e100, so add that to the offsets to find the absolute address */ + +struct samd21_nvic { + vuint32_t iser; /* 0x000 0xe000e100 Set Enable Register */ + + uint8_t _unused020[0x080 - 0x004]; + + vuint32_t icer; /* 0x080 0xe000e180 Clear Enable Register */ + + uint8_t _unused0a0[0x100 - 0x084]; + + vuint32_t ispr; /* 0x100 0xe000e200 Set Pending Register */ + + uint8_t _unused120[0x180 - 0x104]; + + vuint32_t icpr; /* 0x180 0xe000e280 Clear Pending Register */ + + uint8_t _unused1a0[0x300 - 0x184]; + + vuint32_t ipr[8]; /* 0x300 0xe000e400 Priority Register */ +}; + +extern struct samd21_nvic samd21_nvic; + +#define samd21_nvic (*(struct samd21_nvic *) 0xe000e100) + +#define SAMD21_NVIC_ISR_PM_POS 0 +#define SAMD21_NVIC_ISR_SYSCTRL_POS 1 +#define SAMD21_NVIC_ISR_WDT_POS 2 +#define SAMD21_NVIC_ISR_RTC_POS 3 +#define SAMD21_NVIC_ISR_EIC_POS 4 +#define SAMD21_NVIC_ISR_NVMCTRL_POS 5 +#define SAMD21_NVIC_ISR_DMAC_POS 6 +#define SAMD21_NVIC_ISR_USB_POS 7 +#define SAMD21_NVIC_ISR_EVSYS_POS 8 +#define SAMD21_NVIC_ISR_SERCOM0_POS 9 +#define SAMD21_NVIC_ISR_SERCOM1_POS 10 +#define SAMD21_NVIC_ISR_SERCOM2_POS 11 +#define SAMD21_NVIC_ISR_SERCOM3_POS 12 +#define SAMD21_NVIC_ISR_SERCOM4_POS 13 +#define SAMD21_NVIC_ISR_SERCOM5_POS 14 +#define SAMD21_NVIC_ISR_TCC0_POS 15 +#define SAMD21_NVIC_ISR_TCC1_POS 16 +#define SAMD21_NVIC_ISR_TCC2_POS 17 +#define SAMD21_NVIC_ISR_TC3_POS 18 +#define SAMD21_NVIC_ISR_TC4_POS 19 +#define SAMD21_NVIC_ISR_TC5_POS 20 +#define SAMD21_NVIC_ISR_TC6_POS 21 +#define SAMD21_NVIC_ISR_TC7_POS 22 +#define SAMD21_NVIC_ISR_ADC_POS 23 +#define SAMD21_NVIC_ISR_AC_POS 24 +#define SAMD21_NVIC_ISR_DAC_POS 25 +#define SAMD21_NVIC_ISR_PTC_POS 26 +#define SAMD21_NVIC_ISR_I2S_POS 27 +#define SAMD21_NVIC_ISR_AC1_POS 28 +#define SAMD21_NVIC_ISR_TCC3_POS 29 + +#define IRQ_MASK(irq) (1 << (irq)) +#define IRQ_BOOL(v,irq) (((v) >> (irq)) & 1) + +static inline void +samd21_nvic_set_enable(int irq) { + samd21_nvic.iser = IRQ_MASK(irq); +} + +static inline void +samd21_nvic_clear_enable(int irq) { + samd21_nvic.icer = IRQ_MASK(irq); +} + +static inline int +samd21_nvic_enabled(int irq) { + return IRQ_BOOL(samd21_nvic.iser, irq); +} + +static inline void +samd21_nvic_set_pending(int irq) { + samd21_nvic.ispr = IRQ_MASK(irq); +} + +static inline void +samd21_nvic_clear_pending(int irq) { + samd21_nvic.icpr = IRQ_MASK(irq); +} + +static inline int +samd21_nvic_pending(int irq) { + return IRQ_BOOL(samd21_nvic.ispr, irq); +} + +#define IRQ_PRIO_REG(irq) ((irq) >> 2) +#define IRQ_PRIO_BIT(irq) (((irq) & 3) << 3) +#define IRQ_PRIO_MASK(irq) (0xffU << IRQ_PRIO_BIT(irq)) + +static inline void +samd21_nvic_set_priority(int irq, uint8_t prio) { + int n = IRQ_PRIO_REG(irq); + uint32_t v; + + v = samd21_nvic.ipr[n]; + v &= ~IRQ_PRIO_MASK(irq); + v |= (prio) << IRQ_PRIO_BIT(irq); + samd21_nvic.ipr[n] = v; +} + +static inline uint8_t +samd21_nvic_get_priority(int irq) { + return (samd21_nvic.ipr[IRQ_PRIO_REG(irq)] >> IRQ_PRIO_BIT(irq)) & IRQ_PRIO_MASK(0); +} + + + +/* Cortex M0+ SCB */ + +struct samd21_scb { + vuint32_t cpuid; + vuint32_t icsr; + vuint32_t vtor; + vuint32_t aircr; + + vuint32_t scr; + vuint32_t ccr; + vuint32_t shpr1; + vuint32_t shpr2; + + vuint32_t shpr3; + vuint32_t shcrs; + vuint32_t cfsr; + vuint32_t hfsr; + + uint32_t unused_30; + vuint32_t mmfar; + vuint32_t bfar; +}; + +extern struct samd21_scb samd21_scb; + +#define samd21_scb (*(struct samd21_scb *) 0xe000ed00) + +#define SAMD21_SCB_AIRCR_VECTKEY 16 +#define SAMD21_SCB_AIRCR_VECTKEY_KEY 0x05fa +#define SAMD21_SCB_AIRCR_PRIGROUP 8 +#define SAMD21_SCB_AIRCR_SYSRESETREQ 2 +#define SAMD21_SCB_AIRCR_VECTCLRACTIVE 1 +#define SAMD21_SCB_AIRCR_VECTRESET 0 + +/* The NVM Calibration and auxiliary space starts at 0x00800000 */ + +struct samd21_aux0 { + vuint64_t userrow; +}; + +extern struct samd21_aux0 samd21_aux0; + +#define samd21_aux0 (*(struct samd21_aux0 *) 0x00804000) + +#define SAMD21_AUX0_USERROW_BOOTPROT 0 +#define SAMD21_AUX0_USERROW_EEPROM 4 +#define SAMD21_AUX0_USERROW_BOD33_LEVEL 8 +#define SAMD21_AUX0_USERROW_BOD33_ENABLE 14 +#define SAMD21_AUX0_USERROW_BOD33_ACTION 15 +#define SAMD21_AUX0_USERROW_WDT_ENABLE 25 +#define SAMD21_AUX0_USERROW_WDT_ALWAYS_ON 26 +#define SAMD21_AUX0_USERROW_WDT_PERIOD 27 +#define SAMD21_AUX0_USERROW_WDT_WINDOW 31 +#define SAMD21_AUX0_USERROW_WDT_EWOFFSET 35 +#define SAMD21_AUX0_USERROW_WDT_WEN 39 +#define SAMD21_AUX0_USERROW_BOD33_HYST 40 +#define SAMD21_AUX0_USERROW_LOCK 48 + +struct samd21_aux1 { + vuint64_t reserved_00; + vuint64_t device_config; + + vuint64_t reserved_10; + vuint64_t reserved_18; + + vuint64_t calibration; + vuint64_t reserved_28; +}; + +extern struct samd21_aux1 samd21_aux1; + +#define samd21_aux1 (*(struct samd21_aux1 *) 0x00806000) + +#define SAMD21_AUX1_CALIBRATION_ADC_LINEARITY 27 +#define SAMD21_AUX1_CALIBRATION_ADC_LINEARITY_MASK 0xff +#define SAMD21_AUX1_CALIBRATION_ADC_BIASCAL 35 +#define SAMD21_AUX1_CALIBRATION_ADC_BIASCAL_MASK 0x7 +#define SAMD21_AUX1_CALIBRATION_OSC32K_CAL 38 +#define SAMD21_AUX1_CALIBRATION_USB_TRANSN 45 +#define SAMD21_AUX1_CALIBRATION_USB_TRANSN_MASK 0x1f +#define SAMD21_AUX1_CALIBRATION_USB_TRANSP 50 +#define SAMD21_AUX1_CALIBRATION_USB_TRANSP_MASK 0x1f +#define SAMD21_AUX1_CALIBRATION_USB_TRIM 55 +#define SAMD21_AUX1_CALIBRATION_USB_TRIM_MASK 0x07 +#define SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL 58 +#define SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL_MASK 0x3f + +struct samd21_serial { + vuint32_t reserved_00; + vuint32_t reserved_04; + vuint32_t reserved_08; + vuint32_t word0; + + vuint32_t reserved_10; + vuint32_t reserved_14; + vuint32_t reserved_18; + vuint32_t reserved_1c; + + vuint32_t reserved_20; + vuint32_t reserved_24; + vuint32_t reserved_28; + vuint32_t reserved_2c; + + vuint32_t reserved_30; + vuint32_t reserved_34; + vuint32_t reserved_38; + vuint32_t reserved_3c; + + vuint32_t word1; + vuint32_t word2; + vuint32_t word3; + vuint32_t reserved_4c; +}; + +extern struct samd21_serial samd21_serial; + +#define samd21_serial (*(struct samd21_serial *) 0x0080a000) + +static inline void +samd21_gclk_wait_sync(void) +{ + while (samd21_gclk.status & (1 << SAMD21_GCLK_STATUS_SYNCBUSY)) + ; +} + +static inline void +samd21_dfll_wait_sync(void) +{ + while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_DFLLRDY)) == 0) + ; +} + +static inline void +samd21_gclk_gendiv(uint32_t id, uint32_t div) +{ + if (div == 1) + div = 0; + samd21_gclk.gendiv = ((id << SAMD21_GCLK_GENDIV_ID) | + (div << SAMD21_GCLK_GENDIV_DIV)); + samd21_gclk_wait_sync(); +} + +static inline void +samd21_gclk_genctrl(uint32_t src, uint32_t id) +{ + samd21_gclk.genctrl = ((id << SAMD21_GCLK_GENCTRL_ID) | + (src << SAMD21_GCLK_GENCTRL_SRC) | + (0 << SAMD21_GCLK_GENCTRL_OE) | + (1 << SAMD21_GCLK_GENCTRL_GENEN)); + samd21_gclk_wait_sync(); +} + +static inline void +samd21_gclk_clkctrl(uint32_t gen, uint32_t id) +{ + samd21_gclk.clkctrl = (uint16_t) ((gen << SAMD21_GCLK_CLKCTRL_GEN) | + (id << SAMD21_GCLK_CLKCTRL_ID) | + (1U << SAMD21_GCLK_CLKCTRL_CLKEN)); + samd21_gclk_wait_sync(); +} + +#define isr_decl(name) \ + void samd21_ ## name ## _isr(void) + +isr_decl(halt); +isr_decl(ignore); +isr_decl(nmi); +isr_decl(hardfault); +isr_decl(memmanage); +isr_decl(busfault); +isr_decl(usagefault); +isr_decl(svc); +isr_decl(debugmon); +isr_decl(pendsv); +isr_decl(systick); +isr_decl(pm); /* IRQ0 */ +isr_decl(sysctrl); +isr_decl(wdt); +isr_decl(rtc); +isr_decl(eic); +isr_decl(nvmctrl); +isr_decl(dmac); +isr_decl(usb); +isr_decl(evsys); +isr_decl(sercom0); +isr_decl(sercom1); +isr_decl(sercom2); +isr_decl(sercom3); +isr_decl(sercom4); +isr_decl(sercom5); +isr_decl(tcc0); +isr_decl(tcc1); +isr_decl(tcc2); +isr_decl(tc3); +isr_decl(tc4); +isr_decl(tc5); +isr_decl(tc6); +isr_decl(tc7); +isr_decl(adc); +isr_decl(ac); +isr_decl(dac); +isr_decl(ptc); +isr_decl(i2s); +isr_decl(ac1); +isr_decl(tcc3); + +#undef isr_decl + +#endif /* _SAMD21_H_ */