aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Kconfig2
-rw-r--r--src/atsamd/Kconfig50
-rw-r--r--src/atsamd/Makefile38
-rw-r--r--src/atsamd/adc.c1
-rw-r--r--src/atsamd/clock.c1
-rw-r--r--src/atsamd/gpio.c9
-rw-r--r--src/atsamd/hard_pwm.c1
-rw-r--r--src/atsamd/i2c.c7
-rw-r--r--src/atsamd/internal.h13
-rw-r--r--src/atsamd/main.c13
-rw-r--r--src/atsamd/samd51_clock.c196
-rw-r--r--src/atsamd/samd51_timer.c70
-rw-r--r--src/atsamd/serial.c41
-rw-r--r--src/atsamd/spi.c7
-rw-r--r--src/atsamd/timer.c1
-rw-r--r--src/atsamd/usbserial.c43
16 files changed, 432 insertions, 61 deletions
diff --git a/src/Kconfig b/src/Kconfig
index 6f98b8c8..d8410ef1 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -16,7 +16,7 @@ choice
config MACH_ATSAM
bool "SAM3/SAM4 (Due and Duet)"
config MACH_ATSAMD
- bool "SAMD21 (Arduino Zero)"
+ bool "SAMD21/SAMD51"
config MACH_LPC176X
bool "LPC176x (Smoothieboard)"
config MACH_STM32F1
diff --git a/src/atsamd/Kconfig b/src/atsamd/Kconfig
index c56473d5..4d39df60 100644
--- a/src/atsamd/Kconfig
+++ b/src/atsamd/Kconfig
@@ -6,10 +6,10 @@ config ATSAMD_SELECT
bool
default y
select HAVE_GPIO
- select HAVE_GPIO_ADC
+ select HAVE_GPIO_ADC if MACH_SAMD21
select HAVE_GPIO_I2C
- select HAVE_GPIO_SPI
- select HAVE_GPIO_HARD_PWM
+ select HAVE_GPIO_SPI if MACH_SAMD21
+ select HAVE_GPIO_HARD_PWM if MACH_SAMD21
select HAVE_GPIO_BITBANGING
config BOARD_DIRECTORY
@@ -18,13 +18,49 @@ config BOARD_DIRECTORY
choice
prompt "Processor model"
- config MACH_SAMD21A
- bool "SAMD21G18A (Arduino Zero)"
+ config MACH_SAMD21G18
+ bool "SAMD21G18 (Arduino Zero)"
+ select MACH_SAMD21
+ config MACH_SAMD51G19
+ bool "SAMD51G19 (Adafruit ItsyBitsy M4)"
+ select MACH_SAMD51
+ config MACH_SAMD51J19
+ bool "SAMD51J19 (Adafruit Metro M4)"
+ select MACH_SAMD51
+ config MACH_SAMD51N19
+ bool "SAMD51N19"
+ select MACH_SAMD51
+ config MACH_SAMD51P20
+ bool "SAMD51P20 (Adafruit Grand Central)"
+ select MACH_SAMD51
endchoice
+config MACH_SAMD21
+ bool
+config MACH_SAMD51
+ bool
+
+config MCU
+ string
+ default "samd21g" if MACH_SAMD21G18
+ default "samd51g19" if MACH_SAMD51G19
+ default "samd51j19" if MACH_SAMD51J19
+ default "samd51n19" if MACH_SAMD51N19
+ default "samd51p20" if MACH_SAMD51P20
+
config CLOCK_FREQ
int
- default 48000000
+ default 48000000 if MACH_SAMD21
+ default 25000000 if MACH_SAMD51 # 200000000/8
+
+choice
+ depends on MACH_SAMD51
+ prompt "Clock Reference"
+ config CLOCK_REF_X32K
+ bool "32.768Khz crystal"
+ config CLOCK_REF_INTERNAL
+ bool "Factory calibration"
+endchoice
choice
prompt "Bootloader offset"
@@ -43,7 +79,7 @@ config FLASH_START
default 0x0000
config USBSERIAL
- bool "Use USB for communication (instead of serial)"
+ bool "Use USB for communication (instead of serial)" if MACH_SAMD21
default y
config SERIAL
depends on !USBSERIAL
diff --git a/src/atsamd/Makefile b/src/atsamd/Makefile
index ca8b2e99..68a1b539 100644
--- a/src/atsamd/Makefile
+++ b/src/atsamd/Makefile
@@ -4,17 +4,23 @@
CROSS_PREFIX=arm-none-eabi-
dirs-y += src/atsamd src/generic
-dirs-$(CONFIG_MACH_SAMD21A) += lib/samd21/samd21a/gcc/gcc/
+dirs-$(CONFIG_MACH_SAMD21) += lib/samd21/samd21a/gcc/gcc/
+dirs-$(CONFIG_MACH_SAMD51) += lib/samd51/samd51a/gcc/gcc/
-CFLAGS-$(CONFIG_MACH_SAMD21A) += -mcpu=cortex-m0plus
-CFLAGS-$(CONFIG_MACH_SAMD21A) += -Ilib/samd21/samd21a/include -D__SAMD21G18A__
+CFLAGS-$(CONFIG_MACH_SAMD21) += -mcpu=cortex-m0plus -Ilib/samd21/samd21a/include
+CFLAGS-$(CONFIG_MACH_SAMD51) += -mcpu=cortex-m4 -Ilib/samd51/samd51a/include
+CFLAGS-$(CONFIG_MACH_SAMD51) += -mfpu=fpv4-sp-d16 -mfloat-abi=hard
+CFLAGS-$(CONFIG_MACH_SAMD21G18) += -D__SAMD21G18A__
+CFLAGS-$(CONFIG_MACH_SAMD51G19) += -D__SAMD51G19A__
+CFLAGS-$(CONFIG_MACH_SAMD51J19) += -D__SAMD51J19A__
+CFLAGS-$(CONFIG_MACH_SAMD51N19) += -D__SAMD51N19A__
+CFLAGS-$(CONFIG_MACH_SAMD51P20) += -D__SAMD51P20A__
CFLAGS += $(CFLAGS-y) -mthumb -Ilib/cmsis-core
-eflags-$(CONFIG_MACH_SAMD21A) += -T $(OUT)samd21a.ld
-CFLAGS_klipper.elf += $(eflags-y) --specs=nano.specs --specs=nosys.specs
+CFLAGS_klipper.elf += -T $(OUT)samd.ld --specs=nano.specs --specs=nosys.specs
# Add source files
-src-y += atsamd/main.c atsamd/timer.c atsamd/clock.c atsamd/gpio.c
+src-y += atsamd/main.c atsamd/gpio.c
src-y += generic/crc16_ccitt.c generic/alloc.c
src-y += generic/armcm_irq.c generic/timer_irq.c
src-$(CONFIG_USBSERIAL) += atsamd/usbserial.c generic/usb_cdc.c
@@ -23,12 +29,21 @@ src-$(CONFIG_HAVE_GPIO_ADC) += atsamd/adc.c
src-$(CONFIG_HAVE_GPIO_I2C) += atsamd/i2c.c
src-$(CONFIG_HAVE_GPIO_SPI) += atsamd/spi.c
src-$(CONFIG_HAVE_GPIO_HARD_PWM) += atsamd/hard_pwm.c
-src-$(CONFIG_MACH_SAMD21A) += ../lib/samd21/samd21a/gcc/gcc/startup_samd21.c
+src-$(CONFIG_MACH_SAMD21) += atsamd/clock.c atsamd/timer.c
+src-$(CONFIG_MACH_SAMD21) += ../lib/samd21/samd21a/gcc/gcc/startup_samd21.c
+src-$(CONFIG_MACH_SAMD51) += atsamd/samd51_clock.c atsamd/samd51_timer.c
+src-$(CONFIG_MACH_SAMD51) += ../lib/samd51/samd51a/gcc/gcc/startup_samd51.c
# Support bootloader offset address
-target-$(CONFIG_MACH_SAMD21A) := $(OUT)samd21a.ld $(target-y)
+target-y := $(OUT)samd.ld $(target-y)
-$(OUT)samd21a.ld: lib/samd21/samd21a/gcc/gcc/samd21g18a_flash.ld $(OUT)board-link
+ldfile-$(CONFIG_MACH_SAMD21G18) := lib/samd21/samd21a/gcc/gcc/samd21g18a_flash.ld
+ldfile-$(CONFIG_MACH_SAMD51G19) := lib/samd51/samd51a/gcc/gcc/samd51g19a_flash.ld
+ldfile-$(CONFIG_MACH_SAMD51J19) := lib/samd51/samd51a/gcc/gcc/samd51j19a_flash.ld
+ldfile-$(CONFIG_MACH_SAMD51N19) := lib/samd51/samd51a/gcc/gcc/samd51n19a_flash.ld
+ldfile-$(CONFIG_MACH_SAMD51P20) := lib/samd51/samd51a/gcc/gcc/samd51p20a_flash.ld
+
+$(OUT)samd.ld: $(ldfile-y) $(OUT)board-link
@echo " Preprocessing $@"
$(Q)$(CPP) -P -MD -MT $@ -DFLASH_START=$(CONFIG_FLASH_START) $< -o $@
@@ -48,7 +63,10 @@ lib/bossac/bin/bossac:
@echo " Building bossac"
$(Q)make -C lib/bossac bin/bossac
+BOSSAC_OFFSET-$(CONFIG_MACH_SAMD21) := 0x2000
+BOSSAC_OFFSET-$(CONFIG_MACH_SAMD51) := 0x4000
+
flash: $(OUT)klipper.bin lib/bossac/bin/bossac
@echo " Flashing $^ to $(FLASH_DEVICE) via bossac"
$(Q)if [ -z $(FLASH_DEVICE) ]; then echo "Please specify FLASH_DEVICE"; exit 1; fi
- $(Q)lib/bossac/bin/bossac -U -p "$(FLASH_DEVICE)" -a --offset=0x2000 -w $(OUT)klipper.bin -v -b -R
+ $(Q)lib/bossac/bin/bossac -U -p "$(FLASH_DEVICE)" -a --offset=$(BOSSAC_OFFSET-y) -w $(OUT)klipper.bin -v -b -R
diff --git a/src/atsamd/adc.c b/src/atsamd/adc.c
index b340c1ff..65ea2e73 100644
--- a/src/atsamd/adc.c
+++ b/src/atsamd/adc.c
@@ -7,7 +7,6 @@
#include "command.h" // shutdown
#include "gpio.h" // gpio_adc_read
#include "internal.h" // GPIO
-#include "samd21.h" // ADC
#include "sched.h" // sched_shutdown
static const uint8_t adc_pins[] = {
diff --git a/src/atsamd/clock.c b/src/atsamd/clock.c
index 07dca9d3..46319f30 100644
--- a/src/atsamd/clock.c
+++ b/src/atsamd/clock.c
@@ -6,7 +6,6 @@
#include "compiler.h" // DIV_ROUND_CLOSEST
#include "internal.h" // enable_pclock
-#include "samd21.h" // GCLK
// The "generic clock generators" that are configured
#define CLKGEN_MAIN 0
diff --git a/src/atsamd/gpio.c b/src/atsamd/gpio.c
index 045dc003..2e26fa66 100644
--- a/src/atsamd/gpio.c
+++ b/src/atsamd/gpio.c
@@ -1,6 +1,6 @@
-// samd21 gpio functions
+// samd gpio functions
//
-// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2018-2019 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
@@ -9,7 +9,6 @@
#include "command.h" // shutdown
#include "gpio.h" // gpio_out_setup
#include "internal.h" // gpio_peripheral
-#include "samd21.h" // PORT
#include "sched.h" // sched_shutdown
@@ -43,7 +42,11 @@ gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up)
* General Purpose Input Output (GPIO) pins
****************************************************************/
+#if CONFIG_MACH_SAMD21
#define NUM_PORT 2
+#elif CONFIG_MACH_SAMD51
+#define NUM_PORT 4
+#endif
struct gpio_out
gpio_out_setup(uint8_t pin, uint8_t val)
diff --git a/src/atsamd/hard_pwm.c b/src/atsamd/hard_pwm.c
index 0f96cb2f..3f510520 100644
--- a/src/atsamd/hard_pwm.c
+++ b/src/atsamd/hard_pwm.c
@@ -7,7 +7,6 @@
#include "command.h" // shutdown
#include "gpio.h" // gpio_pwm_write
#include "internal.h" // GPIO
-#include "samd21.h" // TCC0
#include "sched.h" // sched_shutdown
struct gpio_pwm_info {
diff --git a/src/atsamd/i2c.c b/src/atsamd/i2c.c
index 57374443..03a75f11 100644
--- a/src/atsamd/i2c.c
+++ b/src/atsamd/i2c.c
@@ -1,13 +1,12 @@
-// i2c support on samd21
+// i2c support on samd
//
-// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2018-2019 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "internal.h" // enable_pclock
#include "command.h" // shutdown
#include "gpio.h" // i2c_setup
-#include "samd21.h" // SERCOM3
#include "sched.h" // sched_shutdown
#define TIME_RISE 125ULL // 125 nanoseconds
@@ -35,7 +34,7 @@ i2c_init(void)
| SERCOM_I2CM_CTRLA_INACTOUT(3)
| SERCOM_I2CM_STATUS_SEXTTOUT
| SERCOM_I2CM_STATUS_MEXTTOUT
- | SERCOM_I2CM_CTRLA_MODE_I2C_MASTER);
+ | SERCOM_I2CM_CTRLA_MODE(5));
si->CTRLA.reg = areg;
uint32_t freq = get_pclock_frequency(SERCOM3_GCLK_ID_CORE);
uint32_t baud = (freq/I2C_FREQ - 10 - freq*TIME_RISE/1000000000) / 2;
diff --git a/src/atsamd/internal.h b/src/atsamd/internal.h
index 2674acc7..ca748492 100644
--- a/src/atsamd/internal.h
+++ b/src/atsamd/internal.h
@@ -1,8 +1,15 @@
-#ifndef __SAMD21_INTERNAL_H
-#define __SAMD21_INTERNAL_H
-// Local definitions for samd21 code
+#ifndef __ATSAMD_INTERNAL_H
+#define __ATSAMD_INTERNAL_H
+// Local definitions for atsamd code
#include <stdint.h> // uint32_t
+#include "autoconf.h" // CONFIG_MACH_SAMD21A
+
+#if CONFIG_MACH_SAMD21
+#include "samd21.h"
+#elif CONFIG_MACH_SAMD51
+#include "samd51.h"
+#endif
#define GPIO(PORT, NUM) (((PORT)-'A') * 32 + (NUM))
#define GPIO2PORT(PIN) ((PIN) / 32)
diff --git a/src/atsamd/main.c b/src/atsamd/main.c
index 4687fc34..5cd85d44 100644
--- a/src/atsamd/main.c
+++ b/src/atsamd/main.c
@@ -1,14 +1,14 @@
-// Main starting point for SAMD21 boards
+// Main starting point for SAMD boards
//
-// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2018-2019 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "command.h" // DECL_CONSTANT
-#include "samd21.h" // SystemInit
+#include "internal.h" // WDT
#include "sched.h" // sched_main
-DECL_CONSTANT(MCU, "samd21g");
+DECL_CONSTANT(MCU, CONFIG_MCU);
/****************************************************************
@@ -25,8 +25,13 @@ DECL_TASK(watchdog_reset);
void
watchdog_init(void)
{
+#if CONFIG_MACH_SAMD21
WDT->CONFIG.reg = WDT_CONFIG_PER_16K; // 500ms timeout
WDT->CTRL.reg = WDT_CTRL_ENABLE;
+#elif CONFIG_MACH_SAMD51
+ WDT->CONFIG.reg = WDT_CONFIG_PER(6); // 500ms timeout
+ WDT->CTRLA.reg = WDT_CTRLA_ENABLE;
+#endif
}
DECL_INIT(watchdog_init);
diff --git a/src/atsamd/samd51_clock.c b/src/atsamd/samd51_clock.c
new file mode 100644
index 00000000..c7ffe2aa
--- /dev/null
+++ b/src/atsamd/samd51_clock.c
@@ -0,0 +1,196 @@
+// Code to setup peripheral clocks on the SAMD51
+//
+// Copyright (C) 2018-2019 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "compiler.h" // DIV_ROUND_CLOSEST
+#include "internal.h" // enable_pclock
+
+// The "generic clock generators" that are configured
+#define CLKGEN_MAIN 0
+#define CLKGEN_200M 1
+#define CLKGEN_32K 2
+#define CLKGEN_48M 3
+#define CLKGEN_2M 4
+#define CLKGEN_100M 5
+
+#define FREQ_MAIN 120000000
+#define FREQ_200M 200000000
+#define FREQ_32K 32768
+#define FREQ_48M 48000000
+#define FREQ_2M 2000000
+#define FREQ_100M 100000000
+
+// Configure a clock generator using a given source as input
+static inline void
+gen_clock(uint32_t clkgen_id, uint32_t flags)
+{
+ GCLK->GENCTRL[clkgen_id].reg = flags | GCLK_GENCTRL_GENEN;
+ while (GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL(clkgen_id))
+ ;
+}
+
+// Route a peripheral clock to a given clkgen
+static inline void
+route_pclock(uint32_t pclk_id, uint32_t clkgen_id)
+{
+ uint32_t val = GCLK_PCHCTRL_GEN(clkgen_id) | GCLK_PCHCTRL_CHEN;
+ GCLK->PCHCTRL[pclk_id].reg = val;
+ while (GCLK->PCHCTRL[pclk_id].reg != val)
+ ;
+}
+
+// Enable a peripheral clock and power to that peripheral
+void
+enable_pclock(uint32_t pclk_id, uint32_t pm_id)
+{
+ uint32_t clkgen_id = CLKGEN_100M;
+ if (pclk_id == TC0_GCLK_ID || pclk_id == TC1_GCLK_ID)
+ clkgen_id = CLKGEN_200M;
+ else if (pclk_id == USB_GCLK_ID)
+ clkgen_id = CLKGEN_48M;
+ route_pclock(pclk_id, clkgen_id);
+ uint32_t pm_port = pm_id / 32, pm_bit = 1 << (pm_id % 32);
+ (&MCLK->APBAMASK.reg)[pm_port] |= pm_bit;
+}
+
+// Return the frequency of the given peripheral clock
+uint32_t
+get_pclock_frequency(uint32_t pclk_id)
+{
+ if (pclk_id == TC0_GCLK_ID || pclk_id == TC1_GCLK_ID)
+ return FREQ_200M;
+ else if (pclk_id == USB_GCLK_ID)
+ return FREQ_48M;
+ return FREQ_100M;
+}
+
+// Initialize the clocks using an external 32K crystal
+static void
+clock_init_32k(void)
+{
+ // Enable external 32Khz crystal and route to CLKGEN_32K
+ uint32_t val = (OSC32KCTRL_XOSC32K_ENABLE | OSC32KCTRL_XOSC32K_EN32K
+ | OSC32KCTRL_XOSC32K_CGM_XT | OSC32KCTRL_XOSC32K_XTALEN);
+ OSC32KCTRL->XOSC32K.reg = val;
+ while (!(OSC32KCTRL->STATUS.reg & OSC32KCTRL_STATUS_XOSC32KRDY))
+ ;
+ gen_clock(CLKGEN_32K, GCLK_GENCTRL_SRC_XOSC32K);
+
+ // Generate 120Mhz clock on PLL0 (with CLKGEN_32K as reference)
+ route_pclock(OSCCTRL_GCLK_ID_FDPLL0, CLKGEN_32K);
+ uint32_t mul = DIV_ROUND_CLOSEST(FREQ_MAIN, FREQ_32K);
+ OSCCTRL->Dpll[0].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDR(mul - 1);
+ while (OSCCTRL->Dpll[0].DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_DPLLRATIO)
+ ;
+ OSCCTRL->Dpll[0].DPLLCTRLB.reg = (OSCCTRL_DPLLCTRLB_REFCLK_GCLK
+ | OSCCTRL_DPLLCTRLB_LBYPASS);
+ OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
+ uint32_t mask = OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK;
+ while ((OSCCTRL->Dpll[0].DPLLSTATUS.reg & mask) != mask)
+ ;
+
+ // Switch main clock to 120Mhz PLL0
+ gen_clock(CLKGEN_MAIN, GCLK_GENCTRL_SRC_DPLL0);
+
+ // Generate 200Mhz clock on PLL1 (with CLKGEN_32K as reference)
+ route_pclock(OSCCTRL_GCLK_ID_FDPLL1, CLKGEN_32K);
+ mul = DIV_ROUND_CLOSEST(FREQ_200M, FREQ_32K);
+ OSCCTRL->Dpll[1].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDR(mul - 1);
+ while (OSCCTRL->Dpll[1].DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_DPLLRATIO)
+ ;
+ OSCCTRL->Dpll[1].DPLLCTRLB.reg = (OSCCTRL_DPLLCTRLB_REFCLK_GCLK
+ | OSCCTRL_DPLLCTRLB_LBYPASS);
+ OSCCTRL->Dpll[1].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
+ mask = OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK;
+ while ((OSCCTRL->Dpll[1].DPLLSTATUS.reg & mask) != mask)
+ ;
+
+ // Produce 100Mhz and 200Mhz clocks from PLL1
+ gen_clock(CLKGEN_200M, GCLK_GENCTRL_SRC_DPLL1);
+ uint32_t div = DIV_ROUND_CLOSEST(FREQ_200M, FREQ_100M);
+ gen_clock(CLKGEN_100M, GCLK_GENCTRL_SRC_DPLL1 | GCLK_GENCTRL_DIV(div));
+
+ // Configure DFLL48M clock (with CLKGEN_32K as reference)
+ OSCCTRL->DFLLCTRLA.reg = 0;
+ route_pclock(OSCCTRL_GCLK_ID_DFLL48, CLKGEN_32K);
+ mul = DIV_ROUND_CLOSEST(FREQ_48M, FREQ_32K);
+ OSCCTRL->DFLLMUL.reg = (OSCCTRL_DFLLMUL_CSTEP(31)
+ | OSCCTRL_DFLLMUL_FSTEP(511)
+ | OSCCTRL_DFLLMUL_MUL(mul));
+ while (OSCCTRL->DFLLSYNC.reg & OSCCTRL_DFLLSYNC_DFLLMUL)
+ ;
+ OSCCTRL->DFLLCTRLB.reg = (OSCCTRL_DFLLCTRLB_MODE | OSCCTRL_DFLLCTRLB_QLDIS
+ | OSCCTRL_DFLLCTRLB_WAITLOCK);
+ while (OSCCTRL->DFLLSYNC.reg & OSCCTRL_DFLLSYNC_DFLLCTRLB)
+ ;
+ OSCCTRL->DFLLCTRLA.reg = OSCCTRL_DFLLCTRLA_ENABLE;
+ while (!(OSCCTRL->STATUS.reg & OSCCTRL_STATUS_DFLLRDY))
+ ;
+ gen_clock(CLKGEN_48M, GCLK_GENCTRL_SRC_DFLL);
+}
+
+// Initialize clocks from factory calibrated internal clock
+static void
+clock_init_internal(void)
+{
+ // Route factory calibrated DFLL48M to CLKGEN_48M
+ gen_clock(CLKGEN_48M, GCLK_GENCTRL_SRC_DFLL);
+
+ // Generate CLKGEN_2M (with CLKGEN_48M as reference)
+ uint32_t div = DIV_ROUND_CLOSEST(FREQ_48M, FREQ_2M);
+ gen_clock(CLKGEN_2M, GCLK_GENCTRL_SRC_DFLL | GCLK_GENCTRL_DIV(div));
+
+ // Generate 120Mhz clock on PLL0 (with CLKGEN_2M as reference)
+ route_pclock(OSCCTRL_GCLK_ID_FDPLL0, CLKGEN_2M);
+ uint32_t mul = DIV_ROUND_CLOSEST(FREQ_MAIN, FREQ_2M);
+ OSCCTRL->Dpll[0].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDR(mul - 1);
+ while (OSCCTRL->Dpll[0].DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_DPLLRATIO)
+ ;
+ OSCCTRL->Dpll[0].DPLLCTRLB.reg = (OSCCTRL_DPLLCTRLB_REFCLK_GCLK
+ | OSCCTRL_DPLLCTRLB_LBYPASS);
+ OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
+ uint32_t mask = OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK;
+ while ((OSCCTRL->Dpll[0].DPLLSTATUS.reg & mask) != mask)
+ ;
+
+ // Switch main clock to 120Mhz PLL0
+ gen_clock(CLKGEN_MAIN, GCLK_GENCTRL_SRC_DPLL0);
+
+ // Generate 200Mhz clock on PLL1 (with CLKGEN_2M as reference)
+ route_pclock(OSCCTRL_GCLK_ID_FDPLL1, CLKGEN_2M);
+ mul = DIV_ROUND_CLOSEST(FREQ_200M, FREQ_2M);
+ OSCCTRL->Dpll[1].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDR(mul - 1);
+ while (OSCCTRL->Dpll[1].DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_DPLLRATIO)
+ ;
+ OSCCTRL->Dpll[1].DPLLCTRLB.reg = (OSCCTRL_DPLLCTRLB_REFCLK_GCLK
+ | OSCCTRL_DPLLCTRLB_LBYPASS);
+ OSCCTRL->Dpll[1].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
+ mask = OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK;
+ while ((OSCCTRL->Dpll[1].DPLLSTATUS.reg & mask) != mask)
+ ;
+
+ // Produce 100Mhz and 200Mhz clocks from PLL1
+ gen_clock(CLKGEN_200M, GCLK_GENCTRL_SRC_DPLL1);
+ div = DIV_ROUND_CLOSEST(FREQ_200M, FREQ_100M);
+ gen_clock(CLKGEN_100M, GCLK_GENCTRL_SRC_DPLL1 | GCLK_GENCTRL_DIV(div));
+}
+
+void
+SystemInit(void)
+{
+ // Reset GCLK
+ GCLK->CTRLA.reg = GCLK_CTRLA_SWRST;
+ while (GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_SWRST)
+ ;
+
+ // Init clocks
+ if (CONFIG_CLOCK_REF_X32K)
+ clock_init_32k();
+ else
+ clock_init_internal();
+
+ // Enable SAMD51 cache
+ CMCC->CTRL.reg = 1;
+}
diff --git a/src/atsamd/samd51_timer.c b/src/atsamd/samd51_timer.c
new file mode 100644
index 00000000..60baf4f4
--- /dev/null
+++ b/src/atsamd/samd51_timer.c
@@ -0,0 +1,70 @@
+// SAMD51 timer interrupt scheduling
+//
+// Copyright (C) 2018-2019 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "board/irq.h" // irq_disable
+#include "board/misc.h" // timer_read_time
+#include "board/timer_irq.h" // timer_dispatch_many
+#include "internal.h" // enable_pclock
+#include "sched.h" // DECL_INIT
+
+// Set the next irq time
+static void
+timer_set(uint32_t value)
+{
+ TC0->COUNT32.CC[0].reg = value;
+ TC0->COUNT32.INTFLAG.reg = TC_INTFLAG_MC0;
+}
+
+// Return the current time (in absolute clock ticks).
+uint32_t __always_inline
+timer_read_time(void)
+{
+ // Need to request a COUNT update and then delay for it to be ready
+ TC0->COUNT32.CTRLBSET.reg = TC_CTRLBSET_CMD_READSYNC;
+ TC0->COUNT32.COUNT.reg;
+ TC0->COUNT32.COUNT.reg;
+ return TC0->COUNT32.COUNT.reg;
+}
+
+// Activate timer dispatch as soon as possible
+void
+timer_kick(void)
+{
+ timer_set(timer_read_time() + 50);
+}
+
+void
+timer_init(void)
+{
+ // Supply power and clock to the timer
+ enable_pclock(TC0_GCLK_ID, ID_TC0);
+ enable_pclock(TC1_GCLK_ID, ID_TC1);
+
+ // Configure the timer
+ TcCount32 *tc = &TC0->COUNT32;
+ irqstatus_t flag = irq_save();
+ tc->CTRLA.reg = 0;
+ tc->CTRLA.reg = TC_CTRLA_MODE_COUNT32;
+ NVIC_SetPriority(TC0_IRQn, 2);
+ NVIC_EnableIRQ(TC0_IRQn);
+ tc->INTENSET.reg = TC_INTENSET_MC0;
+ tc->COUNT.reg = 0;
+ timer_kick();
+ tc->CTRLA.reg = (TC_CTRLA_MODE_COUNT32 | TC_CTRLA_PRESCALER_DIV8
+ | TC_CTRLA_ENABLE);
+ irq_restore(flag);
+}
+DECL_INIT(timer_init);
+
+// IRQ handler
+void __visible __aligned(16) // aligning helps stabilize perf benchmarks
+TC0_Handler(void)
+{
+ irq_disable();
+ uint32_t next = timer_dispatch_many();
+ timer_set(next);
+ irq_enable();
+}
diff --git a/src/atsamd/serial.c b/src/atsamd/serial.c
index 81eda46f..91e85796 100644
--- a/src/atsamd/serial.c
+++ b/src/atsamd/serial.c
@@ -1,12 +1,11 @@
// samd21 serial port
//
-// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2018-2019 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "board/serial_irq.h" // serial_rx_data
#include "internal.h" // enable_pclock
-#include "samd21.h" // SERCOM0
#include "sched.h" // DECL_INIT
void
@@ -20,7 +19,7 @@ serial_init(void)
// Configure serial
SercomUsart *su = &SERCOM0->USART;
su->CTRLA.reg = 0;
- uint32_t areg = (SERCOM_USART_CTRLA_MODE_USART_INT_CLK
+ uint32_t areg = (SERCOM_USART_CTRLA_MODE(1)
| SERCOM_USART_CTRLA_DORD
| SERCOM_USART_CTRLA_SAMPR(1)
| SERCOM_USART_CTRLA_TXPO(1)
@@ -31,13 +30,31 @@ serial_init(void)
uint32_t baud8 = freq / (2 * CONFIG_SERIAL_BAUD);
su->BAUD.reg = (SERCOM_USART_BAUD_FRAC_BAUD(baud8 / 8)
| SERCOM_USART_BAUD_FRAC_FP(baud8 % 8));
- NVIC_SetPriority(SERCOM0_IRQn, 0);
- NVIC_EnableIRQ(SERCOM0_IRQn);
+ // enable irqs
su->INTENSET.reg = SERCOM_USART_INTENSET_RXC;
su->CTRLA.reg = areg | SERCOM_USART_CTRLA_ENABLE;
+#if CONFIG_MACH_SAMD21
+ NVIC_SetPriority(SERCOM0_IRQn, 0);
+ NVIC_EnableIRQ(SERCOM0_IRQn);
+#elif CONFIG_MACH_SAMD51
+ NVIC_SetPriority(SERCOM0_0_IRQn, 0);
+ NVIC_SetPriority(SERCOM0_1_IRQn, 0);
+ NVIC_SetPriority(SERCOM0_2_IRQn, 0);
+ NVIC_SetPriority(SERCOM0_3_IRQn, 0);
+ NVIC_EnableIRQ(SERCOM0_0_IRQn);
+ NVIC_EnableIRQ(SERCOM0_1_IRQn);
+ NVIC_EnableIRQ(SERCOM0_2_IRQn);
+ NVIC_EnableIRQ(SERCOM0_3_IRQn);
+#endif
}
DECL_INIT(serial_init);
+void
+serial_enable_tx_irq(void)
+{
+ SERCOM0->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE;
+}
+
void __visible
SERCOM0_Handler(void)
{
@@ -54,8 +71,12 @@ SERCOM0_Handler(void)
}
}
-void
-serial_enable_tx_irq(void)
-{
- SERCOM0->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE;
-}
+// Aliases for irq handeler on SAMD51
+void SERCOM0_0_Handler(void)
+ __visible __attribute__((alias("SERCOM0_Handler")));
+void SERCOM0_1_Handler(void)
+ __visible __attribute__((alias("SERCOM0_Handler")));
+void SERCOM0_2_Handler(void)
+ __visible __attribute__((alias("SERCOM0_Handler")));
+void SERCOM0_3_Handler(void)
+ __visible __attribute__((alias("SERCOM0_Handler")));
diff --git a/src/atsamd/spi.c b/src/atsamd/spi.c
index e0860576..2037f312 100644
--- a/src/atsamd/spi.c
+++ b/src/atsamd/spi.c
@@ -1,13 +1,12 @@
-// spi support on samd21
+// spi support on samd
//
-// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2018-2019 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "internal.h" // enable_pclock
#include "command.h" // shutdown
#include "gpio.h" // spi_setup
-#include "samd21.h" // SERCOM4
#include "sched.h" // sched_shutdown
static void
@@ -41,7 +40,7 @@ spi_setup(uint32_t bus, uint8_t mode, uint32_t rate)
if (bus)
shutdown("Invalid spi bus");
- uint32_t ctrla = (SERCOM_SPI_CTRLA_MODE_SPI_MASTER
+ uint32_t ctrla = (SERCOM_SPI_CTRLA_MODE(3)
| (mode << SERCOM_SPI_CTRLA_CPHA_Pos)
| SERCOM_SPI_CTRLA_DIPO(0)
| SERCOM_SPI_CTRLA_DOPO(1)
diff --git a/src/atsamd/timer.c b/src/atsamd/timer.c
index 3a133d01..f6b00ab3 100644
--- a/src/atsamd/timer.c
+++ b/src/atsamd/timer.c
@@ -8,7 +8,6 @@
#include "board/misc.h" // timer_read_time
#include "board/timer_irq.h" // timer_dispatch_many
#include "internal.h" // enable_pclock
-#include "samd21.h" // TC4
#include "sched.h" // DECL_INIT
// Set the next irq time
diff --git a/src/atsamd/usbserial.c b/src/atsamd/usbserial.c
index ff9ec519..3ae73d9b 100644
--- a/src/atsamd/usbserial.c
+++ b/src/atsamd/usbserial.c
@@ -1,6 +1,6 @@
-// Hardware interface to USB on samd21
+// Hardware interface to USB on samd
//
-// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2018-2019 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
@@ -11,7 +11,6 @@
#include "board/usb_cdc.h" // usb_notify_ep0
#include "board/usb_cdc_ep.h" // USB_CDC_EP_BULK_IN
#include "internal.h" // enable_pclock
-#include "samd21.h" // USB
#include "sched.h" // DECL_INIT
@@ -173,12 +172,16 @@ usb_set_configure(void)
void
usb_request_bootloader(void)
{
- if (CONFIG_FLASH_START) {
- // Arduino Zero bootloader hack
- irq_disable();
- writel((void*)0x20007FFC, 0x07738135);
- NVIC_SystemReset();
- }
+ if (!CONFIG_FLASH_START)
+ return;
+ // Bootloader hack
+ irq_disable();
+#if CONFIG_MACH_SAMD21
+ writel((void*)0x20007FFC, 0x07738135);
+#elif CONFIG_MACH_SAMD51
+ writel((void*)(HSRAM_ADDR + HSRAM_SIZE - 4), 0xf01669ef);
+#endif
+ NVIC_SystemReset();
}
void
@@ -187,8 +190,9 @@ usbserial_init(void)
// configure usb clock
enable_pclock(USB_GCLK_ID, ID_USB);
// configure USBD+ and USBD- pins
- gpio_peripheral(GPIO('A', 24), 'G', 0);
- gpio_peripheral(GPIO('A', 25), 'G', 0);
+ uint32_t ptype = CONFIG_MACH_SAMD21 ? 'G' : 'H';
+ gpio_peripheral(GPIO('A', 24), ptype, 0);
+ gpio_peripheral(GPIO('A', 25), ptype, 0);
uint16_t trim = (readl((void*)USB_FUSES_TRIM_ADDR)
& USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos;
uint16_t transp = (readl((void*)USB_FUSES_TRANSP_ADDR)
@@ -205,8 +209,19 @@ usbserial_init(void)
USB->DEVICE.CTRLB.reg = 0;
// enable irqs
USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_EORST;
+#if CONFIG_MACH_SAMD21
NVIC_SetPriority(USB_IRQn, 1);
NVIC_EnableIRQ(USB_IRQn);
+#elif CONFIG_MACH_SAMD51
+ NVIC_SetPriority(USB_0_IRQn, 1);
+ NVIC_SetPriority(USB_1_IRQn, 1);
+ NVIC_SetPriority(USB_2_IRQn, 1);
+ NVIC_SetPriority(USB_3_IRQn, 1);
+ NVIC_EnableIRQ(USB_0_IRQn);
+ NVIC_EnableIRQ(USB_1_IRQn);
+ NVIC_EnableIRQ(USB_2_IRQn);
+ NVIC_EnableIRQ(USB_3_IRQn);
+#endif
}
DECL_INIT(usbserial_init);
@@ -244,3 +259,9 @@ USB_Handler(void)
usb_notify_bulk_in();
}
}
+
+// Aliases for irq handeler on SAMD51
+void USB_0_Handler(void) __visible __attribute__((alias("USB_Handler")));
+void USB_1_Handler(void) __visible __attribute__((alias("USB_Handler")));
+void USB_2_Handler(void) __visible __attribute__((alias("USB_Handler")));
+void USB_3_Handler(void) __visible __attribute__((alias("USB_Handler")));