atsamd: Initial support for SAMD51
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
f6ce875818
commit
8cd83b4c2d
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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")));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")));
|
||||
|
|
Loading…
Reference in New Issue