aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWulfsta <wulfstawulfsta@gmail.com>2023-01-16 01:32:31 -0500
committerKevinOConnor <kevin@koconnor.net>2023-05-01 13:56:32 -0400
commit3b0729c94932f4df0298a7c992022f54a5454428 (patch)
tree7de15ab057083440fb76ed990294f9c47f85b512
parent06e5c577bd1874d48e1aefd13d8639525e571670 (diff)
downloadkutter-3b0729c94932f4df0298a7c992022f54a5454428.tar.gz
kutter-3b0729c94932f4df0298a7c992022f54a5454428.tar.xz
kutter-3b0729c94932f4df0298a7c992022f54a5454428.zip
atsamd: Add support for SAMC21
Signed-off-by: Luke Vuksta <wulfstawulfsta@gmail.com>
-rw-r--r--src/Kconfig2
-rw-r--r--src/atsamd/Kconfig47
-rw-r--r--src/atsamd/Makefile3
-rw-r--r--src/atsamd/adc.c59
-rw-r--r--src/atsamd/chipid.c2
-rw-r--r--src/atsamd/fdcan.c22
-rw-r--r--src/atsamd/gpio.c2
-rw-r--r--src/atsamd/hard_pwm.c8
-rw-r--r--src/atsamd/internal.h4
-rw-r--r--src/atsamd/samc21_clock.c148
-rw-r--r--src/atsamd/samd51_watchdog.c4
-rw-r--r--src/atsamd/sercom.c4
-rw-r--r--src/atsamd/timer.c37
13 files changed, 311 insertions, 31 deletions
diff --git a/src/Kconfig b/src/Kconfig
index aad59e6d..8084335e 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -15,7 +15,7 @@ choice
config MACH_ATSAM
bool "SAM3/SAM4/SAM E70 (Due and Duet)"
config MACH_ATSAMD
- bool "SAMD21/SAMD51/SAME5x"
+ bool "SAMC21/SAMD21/SAMD51/SAME5x"
config MACH_LPC176X
bool "LPC176x (Smoothieboard)"
config MACH_STM32
diff --git a/src/atsamd/Kconfig b/src/atsamd/Kconfig
index 05e4c50e..e1062e53 100644
--- a/src/atsamd/Kconfig
+++ b/src/atsamd/Kconfig
@@ -9,7 +9,7 @@ config ATSAMD_SELECT
select HAVE_GPIO_ADC
select HAVE_GPIO_I2C
select HAVE_GPIO_SPI
- select HAVE_GPIO_HARD_PWM if MACH_SAMD21
+ select HAVE_GPIO_HARD_PWM if MACH_SAMX2
select HAVE_GPIO_BITBANGING
select HAVE_STRICT_TIMING
select HAVE_CHIPID
@@ -27,6 +27,9 @@ config BOARD_DIRECTORY
choice
prompt "Processor model"
+ config MACH_SAMC21G18
+ bool "SAMC21G18 (Duet 3 Toolboard 1LC)"
+ select MACH_SAMC21
config MACH_SAMD21G18
bool "SAMD21G18 (Arduino Zero)"
select MACH_SAMD21
@@ -56,8 +59,14 @@ choice
select MACH_SAME54
endchoice
+config MACH_SAMX2
+ bool
+config MACH_SAMC21
+ bool
+ select MACH_SAMX2
config MACH_SAMD21
bool
+ select MACH_SAMX2
config MACH_SAMX5
bool
config MACH_SAMD51
@@ -71,10 +80,15 @@ config MACH_SAME54
select MACH_SAMX5
config HAVE_SAMD_CANBUS
bool
- default y if MACH_SAME51 || MACH_SAME54
+ default y if MACH_SAMC21 || MACH_SAME51 || MACH_SAME54
+config HAVE_SAMD_USB
+ bool
+ default n if MACH_SAMC21G18
+ default y
config MCU
string
+ default "samc21g18a" if MACH_SAMC21G18
default "samd21g18a" if MACH_SAMD21G18
default "samd21e18a" if MACH_SAMD21E18
default "samd21e15a" if MACH_SAMD21E15
@@ -88,7 +102,7 @@ config MCU
config FLASH_SIZE
hex
default 0x8000 if MACH_SAMD21E15
- default 0x40000 if MACH_SAMD21G18 || MACH_SAMD21E18
+ default 0x40000 if MACH_SAMC21G18 || MACH_SAMD21G18 || MACH_SAMD21E18
default 0x80000 if MACH_SAMD51G19 || MACH_SAMD51J19 || MACH_SAMD51N19 || MACH_SAME51J19
default 0x100000 if MACH_SAMD51P20 || MACH_SAME54P20
@@ -103,7 +117,7 @@ config RAM_START
config RAM_SIZE
hex
default 0x1000 if MACH_SAMD21E15
- default 0x8000 if MACH_SAMD21G18 || MACH_SAMD21E18
+ default 0x8000 if MACH_SAMC21G18 || MACH_SAMD21G18 || MACH_SAMD21E18
default 0x30000 if MACH_SAMD51G19 || MACH_SAMD51J19 || MACH_SAMD51N19 || MACH_SAME51J19
default 0x40000 if MACH_SAMD51P20 || MACH_SAME54P20
@@ -122,7 +136,7 @@ choice
depends on MACH_SAMD21
bool "8KiB bootloader (Arduino Zero)"
config SAMD_FLASH_START_4000
- bool "16KiB bootloader (Arduino M0)"
+ bool "16KiB bootloader (Arduino M0, Duet 3 Bootloader)"
config SAMD_FLASH_START_0000
bool "No bootloader"
endchoice
@@ -140,11 +154,13 @@ config FLASH_APPLICATION_ADDRESS
choice
prompt "Clock Reference"
config CLOCK_REF_X32K
- bool "32.768Khz crystal"
+ bool "32.768Khz crystal" if !MACH_SAMC21
+ config CLOCK_REF_X12M
+ bool "12Mhz crystal" if MACH_SAMC21
config CLOCK_REF_X25M
- bool "25Mhz crystal" if MACH_SAMX5
+ bool "25Mhz crystal" if MACH_SAMC21 || MACH_SAMX5
config CLOCK_REF_INTERNAL
- bool "Internal clock"
+ bool "Internal clock" if !MACH_SAMC21
endchoice
choice
@@ -162,7 +178,7 @@ endchoice
config CLOCK_FREQ
int
- default 48000000 if MACH_SAMD21
+ default 48000000 if MACH_SAMX2
default 150000000 if SAMD51_FREQ_150
default 180000000 if SAMD51_FREQ_180
default 200000000 if SAMD51_FREQ_200
@@ -177,9 +193,11 @@ choice
prompt "Communication interface"
config ATSAMD_USB
bool "USB"
+ depends on HAVE_SAMD_USB
select USBSERIAL
config ATSAMD_SERIAL
bool "Serial"
+ depends on !MACH_SAMC21
select SERIAL
config ATSAMD_MMENU_CANBUS_PA23_PA22
bool "CAN bus (on PA23/PA22)"
@@ -189,9 +207,13 @@ choice
bool "CAN bus (on PA25/PA24)"
depends on HAVE_SAMD_CANBUS
select CANSERIAL
+ config ATSAMD_MMENU_CANBUS_PB11_PB10
+ bool "CAN bus (on PB11/PB10)"
+ depends on HAVE_SAMD_CANBUS && MACH_SAMC21
+ select CANSERIAL
config ATSAMD_MMENU_CANBUS_PB13_PB12
bool "CAN bus (on PB13/PB12)"
- depends on HAVE_SAMD_CANBUS
+ depends on HAVE_SAMD_CANBUS && !MACH_SAMC21
select CANSERIAL
config ATSAMD_MMENU_CANBUS_PB15_PB14
bool "CAN bus (on PB15/PB14)"
@@ -199,7 +221,7 @@ choice
select CANSERIAL
config ATSAMD_USBCANBUS
bool "USB to CAN bus bridge"
- depends on HAVE_SAMD_CANBUS
+ depends on HAVE_SAMD_CANBUS && HAVE_SAMD_USB
select USBCANBUS
endchoice
choice
@@ -218,6 +240,9 @@ config ATSAMD_CANBUS_PA23_PA22
config ATSAMD_CANBUS_PA25_PA24
bool
default y if ATSAMD_MMENU_CANBUS_PA25_PA24
+config ATSAMD_CANBUS_PB11_PB10
+ bool
+ default y if ATSAMD_MMENU_CANBUS_PB11_PB10
config ATSAMD_CANBUS_PB13_PB12
bool
default y if ATSAMD_MMENU_CANBUS_PB13_PB12 || ATSAMD_CMENU_CANBUS_PB13_PB12
diff --git a/src/atsamd/Makefile b/src/atsamd/Makefile
index 9c94b812..d241cd8c 100644
--- a/src/atsamd/Makefile
+++ b/src/atsamd/Makefile
@@ -7,6 +7,7 @@ dirs-y += src/atsamd src/generic lib/fast-hash
MCU := $(shell echo $(CONFIG_MCU) | tr a-z A-Z)
+CFLAGS-$(CONFIG_MACH_SAMC21) += -mcpu=cortex-m0plus -Ilib/samc21/samc21/include
CFLAGS-$(CONFIG_MACH_SAMD21) += -mcpu=cortex-m0plus -Ilib/samd21/samd21a/include
CFLAGS-$(CONFIG_MACH_SAMD51) += -Ilib/samd51/samd51a/include
CFLAGS-$(CONFIG_MACH_SAME51) += -Ilib/same51/include
@@ -31,6 +32,8 @@ src-$(CONFIG_HAVE_GPIO_I2C) += atsamd/i2c.c
src-$(CONFIG_HAVE_GPIO_SPI) += atsamd/spi.c
src-$(CONFIG_HAVE_SERCOM) += atsamd/sercom.c
src-$(CONFIG_HAVE_GPIO_HARD_PWM) += atsamd/hard_pwm.c
+src-$(CONFIG_MACH_SAMC21) += atsamd/samd51_watchdog.c
+src-$(CONFIG_MACH_SAMC21) += atsamd/samc21_clock.c atsamd/timer.c generic/timer_irq.c
src-$(CONFIG_MACH_SAMD21) += atsamd/watchdog.c
src-$(CONFIG_MACH_SAMD21) += atsamd/clock.c atsamd/timer.c generic/timer_irq.c
src-$(CONFIG_MACH_SAMX5) += atsamd/samd51_watchdog.c
diff --git a/src/atsamd/adc.c b/src/atsamd/adc.c
index a48a5471..a5436982 100644
--- a/src/atsamd/adc.c
+++ b/src/atsamd/adc.c
@@ -12,7 +12,26 @@
#define ADC_TEMPERATURE_PIN 0xfe
DECL_ENUMERATION("pin", "ADC_TEMPERATURE", ADC_TEMPERATURE_PIN);
-#if CONFIG_MACH_SAMD21
+#if CONFIG_MACH_SAMC21
+
+#define ADC_INPUTCTRL_MUXNEG_GND 0x18
+
+#define SAMD51_ADC_SYNC(ADC, BIT) \
+ while(ADC->SYNCBUSY.reg & ADC_SYNCBUSY_ ## BIT)
+static const uint8_t adc_pins[] = {
+ /* ADC0 */
+ GPIO('A', 2), GPIO('A', 3), GPIO('B', 8), GPIO('B', 9), GPIO('A', 4),
+ GPIO('A', 5), GPIO('A', 6), GPIO('A', 7), GPIO('A', 8), GPIO('A', 9),
+ GPIO('A', 10), GPIO('A', 11),
+ /* Padding to 16 */
+ 255, 255, 255, 255,
+ /* ADC1 */
+ GPIO('B', 0), GPIO('B', 1), GPIO('B', 2), GPIO('B', 3), GPIO('B', 8),
+ GPIO('B', 9), GPIO('B', 4), GPIO('B', 5), GPIO('B', 6), GPIO('B', 7),
+ GPIO('A', 8), GPIO('A', 9)
+};
+
+#elif CONFIG_MACH_SAMD21
#define SAMD51_ADC_SYNC(ADC, BIT)
static const uint8_t adc_pins[] = {
@@ -21,6 +40,7 @@ static const uint8_t adc_pins[] = {
GPIO('B', 2), GPIO('B', 3), GPIO('B', 4), GPIO('B', 5), GPIO('B', 6),
GPIO('B', 7), GPIO('A', 8), GPIO('A', 9), GPIO('A', 10), GPIO('A', 11)
};
+
#elif CONFIG_MACH_SAMX5
#define SAMD51_ADC_SYNC(ADC, BIT) \
@@ -53,7 +73,7 @@ static struct gpio_adc gpio_adc_pin_to_struct(uint8_t pin)
}
#if CONFIG_MACH_SAMD21
Adc* reg = ADC;
-#elif CONFIG_MACH_SAMX5
+#elif CONFIG_MACH_SAMC21 || CONFIG_MACH_SAMX5
Adc* reg = (chan < 16 ? ADC0 : ADC1);
chan %= 16;
#endif
@@ -69,7 +89,38 @@ adc_init(void)
return;
have_run_init = 1;
-#if CONFIG_MACH_SAMD21
+#if CONFIG_MACH_SAMC21
+ // Enable adc clock
+ enable_pclock(ADC0_GCLK_ID, ID_ADC0);
+ enable_pclock(ADC1_GCLK_ID, ID_ADC1);
+
+ // Load calibration info
+ // ADC0
+ uint32_t refbuf = GET_FUSE(ADC0_FUSES_BIASREFBUF);
+ uint32_t comp = GET_FUSE(ADC0_FUSES_BIASCOMP);
+ ADC0->CALIB.reg = (ADC0_FUSES_BIASREFBUF(refbuf)
+ | ADC0_FUSES_BIASCOMP(comp));
+
+ // ADC1
+ refbuf = GET_FUSE(ADC1_FUSES_BIASREFBUF);
+ comp = GET_FUSE(ADC1_FUSES_BIASCOMP);
+ ADC1->CALIB.reg = (ADC0_FUSES_BIASREFBUF(refbuf)
+ | ADC0_FUSES_BIASCOMP(comp));
+
+ // Setup and enable
+ // ADC0
+ ADC0->REFCTRL.reg = ADC_REFCTRL_REFSEL_INTVCC1;
+ ADC0->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV128;
+ ADC0->SAMPCTRL.reg = ADC_SAMPCTRL_SAMPLEN(63);
+ ADC0->CTRLA.reg = ADC_CTRLA_ENABLE;
+
+ // ADC1
+ ADC1->REFCTRL.reg = ADC_REFCTRL_REFSEL_INTVCC1;
+ ADC1->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV128;
+ ADC1->SAMPCTRL.reg = ADC_SAMPCTRL_SAMPLEN(63);
+ ADC1->CTRLA.reg = ADC_CTRLA_ENABLE;
+
+#elif CONFIG_MACH_SAMD21
// Enable adc clock
enable_pclock(ADC_GCLK_ID, ID_ADC);
// Load calibraiton info
@@ -135,7 +186,7 @@ gpio_adc_setup(uint8_t pin)
SYSCTRL->VREF.reg |= SYSCTRL_VREF_TSEN;
return (struct gpio_adc){ .regs=ADC,
.chan=ADC_INPUTCTRL_MUXPOS_TEMP_Val };
-#else
+#elif CONFIG_MACH_SAMX5
SUPC->VREF.reg |= SUPC_VREF_TSEN;
return (struct gpio_adc){ .regs=ADC0,
.chan=ADC_INPUTCTRL_MUXPOS_PTAT_Val };
diff --git a/src/atsamd/chipid.c b/src/atsamd/chipid.c
index 95aaf9be..ec2534f4 100644
--- a/src/atsamd/chipid.c
+++ b/src/atsamd/chipid.c
@@ -30,7 +30,7 @@ chipid_init(void)
return;
uint32_t id[4];
- if (CONFIG_MACH_SAMD21) {
+ if (CONFIG_MACH_SAMX2) {
id[0] = *(uint32_t*)0x0080A00C;
id[1] = *(uint32_t*)0x0080A040;
id[2] = *(uint32_t*)0x0080A044;
diff --git a/src/atsamd/fdcan.c b/src/atsamd/fdcan.c
index 758cb4f2..7c3e6b65 100644
--- a/src/atsamd/fdcan.c
+++ b/src/atsamd/fdcan.c
@@ -28,6 +28,11 @@
#define GPIO_Rx GPIO('A', 25)
#define GPIO_Tx GPIO('A', 24)
#define CANx_GCLK_ID CAN0_GCLK_ID
+#elif CONFIG_ATSAMD_CANBUS_PB11_PB10
+ DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PB11,PB10");
+ #define GPIO_Rx GPIO('B', 11)
+ #define GPIO_Tx GPIO('B', 10)
+ #define CANx_GCLK_ID CAN1_GCLK_ID
#elif CONFIG_ATSAMD_CANBUS_PB13_PB12
DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PB13,PB12");
#define GPIO_Rx GPIO('B', 13)
@@ -40,7 +45,17 @@
#define CANx_GCLK_ID CAN1_GCLK_ID
#endif
-#if CANx_GCLK_ID == CAN0_GCLK_ID
+#if CANx_GCLK_ID == CAN0_GCLK_ID && CONFIG_MACH_SAMC21
+ #define CAN_FUNCTION 'G'
+ #define CANx CAN0
+ #define CANx_IRQn CAN0_IRQn
+ #define MCLK_AHBMASK_CANx MCLK_AHBMASK_CAN0
+#elif CANx_GCLK_ID == CAN1_GCLK_ID && CONFIG_MACH_SAMC21
+ #define CAN_FUNCTION 'G'
+ #define CANx CAN1
+ #define CANx_IRQn CAN1_IRQn
+ #define MCLK_AHBMASK_CANx MCLK_AHBMASK_CAN1
+#elif CANx_GCLK_ID == CAN0_GCLK_ID
#define CAN_FUNCTION 'I'
#define CANx CAN0
#define CANx_IRQn CAN0_IRQn
@@ -234,13 +249,18 @@ compute_btr(uint32_t pclock, uint32_t bitrate)
void
can_init(void)
{
+#if CONFIG_HAVE_SAMD_USB
if (!CONFIG_USB) {
// The FDCAN peripheral only seems to run if at least one
// other peripheral is also enabled.
enable_pclock(USB_GCLK_ID, ID_USB);
USB->DEVICE.CTRLA.reg = USB_CTRLA_ENABLE;
}
+#endif
+#if CONFIG_MACH_SAMC21
+ MCLK->AHBMASK.reg |= MCLK_AHBMASK_CANx;
+#endif
enable_pclock(CANx_GCLK_ID, -1);
gpio_peripheral(GPIO_Rx, CAN_FUNCTION, 1);
diff --git a/src/atsamd/gpio.c b/src/atsamd/gpio.c
index ad5bfdb7..d819ceed 100644
--- a/src/atsamd/gpio.c
+++ b/src/atsamd/gpio.c
@@ -42,7 +42,7 @@ gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up)
* General Purpose Input Output (GPIO) pins
****************************************************************/
-#if CONFIG_MACH_SAMD21
+#if CONFIG_MACH_SAMX2
#define NUM_PORT 2
DECL_ENUMERATION_RANGE("pin", "PA0", GPIO('A', 0), 32);
DECL_ENUMERATION_RANGE("pin", "PB0", GPIO('B', 0), 32);
diff --git a/src/atsamd/hard_pwm.c b/src/atsamd/hard_pwm.c
index dfbd0bb3..f07328ff 100644
--- a/src/atsamd/hard_pwm.c
+++ b/src/atsamd/hard_pwm.c
@@ -92,7 +92,13 @@ gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val)
}
// Set initial value
- struct gpio_pwm g = (struct gpio_pwm) { (void*)&tcc->CCB[p->channel].reg };
+ struct gpio_pwm g = (struct gpio_pwm) {
+#if CONFIG_MACH_SAMD21
+ (void*)&tcc->CCB[p->channel].reg
+#elif CONFIG_MACH_SAMC21
+ (void*)&tcc->CCBUF[p->channel].reg
+#endif
+ };
gpio_pwm_write(g, val);
// Route output to pin
diff --git a/src/atsamd/internal.h b/src/atsamd/internal.h
index 967833c9..6232a8fc 100644
--- a/src/atsamd/internal.h
+++ b/src/atsamd/internal.h
@@ -5,7 +5,9 @@
#include <stdint.h> // uint32_t
#include "autoconf.h" // CONFIG_MACH_SAMD21A
-#if CONFIG_MACH_SAMD21
+#if CONFIG_MACH_SAMC21
+#include "samc21.h"
+#elif CONFIG_MACH_SAMD21
#include "samd21.h"
#elif CONFIG_MACH_SAMD51
#include "samd51.h"
diff --git a/src/atsamd/samc21_clock.c b/src/atsamd/samc21_clock.c
new file mode 100644
index 00000000..3d95cf0d
--- /dev/null
+++ b/src/atsamd/samc21_clock.c
@@ -0,0 +1,148 @@
+// Code to setup peripheral clocks on the SAMC21
+//
+// Copyright (C) 2018-2023 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2023 Luke Vuksta <wulfstawulfsta@gmail.com>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "command.h" // DECL_CONSTANT_STR
+#include "compiler.h" // DIV_ROUND_CLOSEST
+#include "internal.h" // enable_pclock
+
+// The "generic clock generators" that are configured
+#define CLKGEN_MAIN 0
+
+#define FREQ_MAIN CONFIG_CLOCK_FREQ
+#define FREQ_48M 48000000
+
+// 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(1 << 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;
+ // Don't do anything if already enabled (Timer Counter).
+ if (GCLK->PCHCTRL[pclk_id].reg != val) {
+ 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, int32_t pm_id)
+{
+ route_pclock(pclk_id, CLKGEN_MAIN);
+ if (pm_id >= 0) {
+ 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)
+{
+ return FREQ_MAIN;
+}
+
+// Configure a dpll to a given clock multiplier
+static void
+config_dpll(uint32_t mul, uint32_t ctrlb)
+{
+ OSCCTRL->DPLLCTRLA.reg = 0;
+ while (OSCCTRL->DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_ENABLE)
+ ;
+ OSCCTRL->DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDR(mul - 1);
+ while (OSCCTRL->DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_DPLLRATIO)
+ ;
+ OSCCTRL->DPLLCTRLB.reg = ctrlb | OSCCTRL_DPLLCTRLB_LBYPASS;
+ OSCCTRL->DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
+ uint32_t mask = OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK;
+ while ((OSCCTRL->DPLLSTATUS.reg & mask) != mask)
+ ;
+}
+
+#if CONFIG_CLOCK_REF_X12M
+DECL_CONSTANT_STR("RESERVE_PINS_crystal", "PA14,PA15");
+#endif
+
+// Initialize the clocks using an external 12M crystal
+static void
+clock_init_12m(void)
+{
+ // Enable XOSC1
+ uint32_t freq_xosc = 12000000;
+ uint32_t val = (OSCCTRL_XOSCCTRL_ENABLE | OSCCTRL_XOSCCTRL_XTALEN
+ | OSCCTRL_XOSCCTRL_GAIN(3) | OSCCTRL_XOSCCTRL_AMPGC);
+ OSCCTRL->XOSCCTRL.reg = val;
+ while (!(OSCCTRL->STATUS.reg & OSCCTRL_STATUS_XOSCRDY))
+ ;
+
+ // Generate 48Mhz clock on PLL (with XOSC as reference)
+ uint32_t p0div = 24, p0mul = DIV_ROUND_CLOSEST(FREQ_MAIN, freq_xosc/p0div);
+ uint32_t p0ctrlb = OSCCTRL_DPLLCTRLB_DIV(p0div / 2 - 1);
+ config_dpll(p0mul, p0ctrlb | OSCCTRL_DPLLCTRLB_REFCLK(1));
+
+ // Switch main clock to 48Mhz PLL
+ gen_clock(CLKGEN_MAIN, GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DPLL96M_Val));
+}
+
+
+#if CONFIG_CLOCK_REF_X25M
+DECL_CONSTANT_STR("RESERVE_PINS_crystal", "PA14,PA15");
+#endif
+
+// Initialize the clocks using an external 25M crystal
+static void
+clock_init_25m(void)
+{
+ // Enable XOSC1
+ uint32_t freq_xosc = 25000000;
+ uint32_t val = (OSCCTRL_XOSCCTRL_ENABLE | OSCCTRL_XOSCCTRL_XTALEN
+ | OSCCTRL_XOSCCTRL_GAIN(4) | OSCCTRL_XOSCCTRL_AMPGC);
+ OSCCTRL->XOSCCTRL.reg = val;
+ while (!(OSCCTRL->STATUS.reg & OSCCTRL_STATUS_XOSCRDY))
+ ;
+
+ // Generate 48Mhz clock on PLL (with XOSC as reference)
+ uint32_t p0div = 50, p0mul = DIV_ROUND_CLOSEST(FREQ_MAIN, freq_xosc/p0div);
+ uint32_t p0ctrlb = OSCCTRL_DPLLCTRLB_DIV(p0div / 2 - 1);
+ config_dpll(p0mul, p0ctrlb | OSCCTRL_DPLLCTRLB_REFCLK(1));
+
+ // Switch main clock to 48Mhz PLL
+ gen_clock(CLKGEN_MAIN, GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DPLL96M_Val));
+}
+
+void
+SystemInit(void)
+{
+ // Set NVM wait states for 48MHz. This might need to be set higher if
+ // running at a lower voltage.
+ NVMCTRL->CTRLB.reg |= (NVMCTRL_CTRLB_SLEEPPRM_WAKEUPINSTANT
+ | NVMCTRL_CTRLB_RWS(2));
+
+ OSCCTRL->OSC48MDIV.reg = OSCCTRL_OSC48MDIV_DIV(0);
+ while (OSCCTRL->OSC48MSYNCBUSY.reg & OSCCTRL_OSC48MSYNCBUSY_OSC48MDIV)
+ ;
+
+ // Reset GCLK
+ GCLK->CTRLA.reg = GCLK_CTRLA_SWRST;
+ while (GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_SWRST)
+ ;
+
+ // Init clocks
+ if (CONFIG_CLOCK_REF_X12M)
+ clock_init_12m();
+ else
+ clock_init_25m();
+}
diff --git a/src/atsamd/samd51_watchdog.c b/src/atsamd/samd51_watchdog.c
index 8c970dba..a0ec5c9e 100644
--- a/src/atsamd/samd51_watchdog.c
+++ b/src/atsamd/samd51_watchdog.c
@@ -18,7 +18,11 @@ DECL_TASK(watchdog_reset);
void
watchdog_init(void)
{
+#if CONFIG_MACH_SAMC21
+ WDT->CONFIG.reg = WDT_CONFIG_PER_CYC512; // 500ms timeout
+#elif CONFIG_MACH_SAMX5
WDT->CONFIG.reg = WDT_CONFIG_PER(6); // 500ms timeout
+#endif
WDT->CTRLA.reg = WDT_CTRLA_ENABLE;
}
DECL_INIT(watchdog_init);
diff --git a/src/atsamd/sercom.c b/src/atsamd/sercom.c
index cd1dfcd3..f8c70303 100644
--- a/src/atsamd/sercom.c
+++ b/src/atsamd/sercom.c
@@ -64,7 +64,7 @@ struct sercom_pad {
};
static const struct sercom_pad sercom_pads[] = {
-#if CONFIG_MACH_SAMD21
+#if CONFIG_MACH_SAMX2
{ 0, GPIO('A', 8), 0, 'C'},
{ 0, GPIO('A', 9), 1, 'C'},
{ 0, GPIO('A', 10), 2, 'C'},
@@ -326,7 +326,7 @@ struct sercom_spi_map {
static const struct sercom_spi_map sercom_spi[] = {
{ 0, 1, 0 },
{ 3, 1, 2 },
-#if CONFIG_MACH_SAMD21
+#if CONFIG_MACH_SAMX2
{ 2, 3, 1 },
{ 0, 3, 3 },
#endif
diff --git a/src/atsamd/timer.c b/src/atsamd/timer.c
index dc728055..87dc20fa 100644
--- a/src/atsamd/timer.c
+++ b/src/atsamd/timer.c
@@ -11,19 +11,40 @@
#include "internal.h" // enable_pclock
#include "sched.h" // DECL_INIT
+#if CONFIG_MACH_SAMC21
+ #define TCp TC0
+ #define TCp_IRQn TC0_IRQn
+ #define TCp_GCLK_ID TC0_GCLK_ID
+ #define ID_TCp ID_TC0
+ #define TCd_GCLK_ID TC1_GCLK_ID
+ #define ID_TCd ID_TC1
+#else
+ #define TCp TC4
+ #define TCp_IRQn TC4_IRQn
+ #define TCp_GCLK_ID TC4_GCLK_ID
+ #define ID_TCp ID_TC4
+ #define TCd_GCLK_ID TC3_GCLK_ID
+ #define ID_TCd ID_TC3
+#endif
+
// Set the next irq time
static void
timer_set(uint32_t value)
{
- TC4->COUNT32.CC[0].reg = value;
- TC4->COUNT32.INTFLAG.reg = TC_INTFLAG_MC0;
+ TCp->COUNT32.CC[0].reg = value;
+ TCp->COUNT32.INTFLAG.reg = TC_INTFLAG_MC0;
}
// Return the current time (in absolute clock ticks).
uint32_t
timer_read_time(void)
{
- return TC4->COUNT32.COUNT.reg;
+#if CONFIG_MACH_SAMC21
+ TCp->COUNT32.CTRLBSET.reg |= TC_CTRLBSET_CMD(TC_CTRLBCLR_CMD_READSYNC_Val);
+ while (TCp->COUNT32.SYNCBUSY.reg & TC_SYNCBUSY_COUNT)
+ ;
+#endif
+ return TCp->COUNT32.COUNT.reg;
}
// Activate timer dispatch as soon as possible
@@ -35,7 +56,7 @@ timer_kick(void)
// IRQ handler
void __aligned(16) // aligning helps stabilize perf benchmarks
-TC4_Handler(void)
+TCp_Handler(void)
{
irq_disable();
uint32_t next = timer_dispatch_many();
@@ -47,15 +68,15 @@ void
timer_init(void)
{
// Supply power and clock to the timer
- enable_pclock(TC3_GCLK_ID, ID_TC3);
- enable_pclock(TC4_GCLK_ID, ID_TC4);
+ enable_pclock(TCd_GCLK_ID, ID_TCd);
+ enable_pclock(TCp_GCLK_ID, ID_TCp);
// Configure the timer
- TcCount32 *tc = &TC4->COUNT32;
+ TcCount32 *tc = &TCp->COUNT32;
irqstatus_t flag = irq_save();
tc->CTRLA.reg = 0;
tc->CTRLA.reg = TC_CTRLA_MODE_COUNT32;
- armcm_enable_irq(TC4_Handler, TC4_IRQn, 2);
+ armcm_enable_irq(TCp_Handler, TCp_IRQn, 2);
tc->INTENSET.reg = TC_INTENSET_MC0;
tc->COUNT.reg = 0;
timer_kick();