From 8cd83b4c2dcaeac1fe68f18eadd7f25ff42476c2 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 14 Jan 2019 18:21:50 -0500 Subject: [PATCH] atsamd: Initial support for SAMD51 Signed-off-by: Kevin O'Connor --- klippy/pins.py | 2 + src/Kconfig | 2 +- src/atsamd/Kconfig | 50 ++++++++-- src/atsamd/Makefile | 38 ++++++-- src/atsamd/adc.c | 1 - src/atsamd/clock.c | 1 - src/atsamd/gpio.c | 9 +- src/atsamd/hard_pwm.c | 1 - src/atsamd/i2c.c | 7 +- src/atsamd/internal.h | 13 ++- src/atsamd/main.c | 13 ++- src/atsamd/samd51_clock.c | 196 ++++++++++++++++++++++++++++++++++++++ src/atsamd/samd51_timer.c | 70 ++++++++++++++ src/atsamd/serial.c | 41 ++++++-- src/atsamd/spi.c | 7 +- src/atsamd/timer.c | 1 - src/atsamd/usbserial.c | 43 ++++++--- 17 files changed, 434 insertions(+), 61 deletions(-) create mode 100644 src/atsamd/samd51_clock.c create mode 100644 src/atsamd/samd51_timer.c diff --git a/klippy/pins.py b/klippy/pins.py index abca279e..47bd87b5 100644 --- a/klippy/pins.py +++ b/klippy/pins.py @@ -47,6 +47,8 @@ MCU_PINS = { "sam3x8e": port_pins(4, 32), "sam3x8c": port_pins(2, 32), "sam4s8c": port_pins(3, 32), "sam4e8e" : port_pins(5, 32), "samd21g": port_pins(2, 32), + "samd51g19": port_pins(2, 32), "samd51j19": port_pins(3, 32), + "samd51n19": port_pins(3, 32), "samd51p20": port_pins(4, 32), "stm32f103": port_pins(5, 16), "lpc176x": lpc_pins(), "pru": beaglebone_pins(), 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 +// Copyright (C) 2018-2019 Kevin O'Connor // // 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 +// Copyright (C) 2018-2019 Kevin O'Connor // // 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 // 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 +// Copyright (C) 2018-2019 Kevin O'Connor // // 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 +// +// 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 +// +// 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 +// Copyright (C) 2018-2019 Kevin O'Connor // // 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 +// Copyright (C) 2018-2019 Kevin O'Connor // // 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 +// Copyright (C) 2018-2019 Kevin O'Connor // // 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")));