atsamd: Initial support for SAMD51

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2019-01-14 18:21:50 -05:00
parent f6ce875818
commit 8cd83b4c2d
17 changed files with 434 additions and 61 deletions

View File

@ -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(),

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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[] = {

View File

@ -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

View File

@ -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)

View File

@ -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 {

View File

@ -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;

View File

@ -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)

View File

@ -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);

196
src/atsamd/samd51_clock.c Normal file
View File

@ -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;
}

70
src/atsamd/samd51_timer.c Normal file
View File

@ -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();
}

View File

@ -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")));

View File

@ -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)

View File

@ -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

View File

@ -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")));