From adeb19339ca592e979c93c58929fd5739a7a8316 Mon Sep 17 00:00:00 2001 From: Dylan Laduranty <dylan.laduranty@mesotic.com> Date: Mon, 21 Jan 2019 17:05:04 +0100 Subject: [PATCH] cpu/saml1x: add support for saml10/saml11 MCUs --- cpu/saml1x/Makefile | 10 ++ cpu/saml1x/Makefile.features | 1 + cpu/saml1x/Makefile.include | 4 + cpu/saml1x/cpu.c | 71 ++++++++++ cpu/saml1x/doc.txt | 9 ++ cpu/saml1x/include/periph_cpu.h | 42 ++++++ cpu/saml1x/periph/Makefile | 1 + cpu/saml1x/periph/pm.c | 51 +++++++ cpu/saml1x/periph/rtc.c | 236 ++++++++++++++++++++++++++++++++ cpu/saml1x/periph/rtt.c | 149 ++++++++++++++++++++ cpu/saml1x/periph/timer.c | 231 +++++++++++++++++++++++++++++++ cpu/saml1x/vectors.c | 125 +++++++++++++++++ 12 files changed, 930 insertions(+) create mode 100644 cpu/saml1x/Makefile create mode 100644 cpu/saml1x/Makefile.features create mode 100644 cpu/saml1x/Makefile.include create mode 100644 cpu/saml1x/cpu.c create mode 100644 cpu/saml1x/doc.txt create mode 100644 cpu/saml1x/include/periph_cpu.h create mode 100644 cpu/saml1x/periph/Makefile create mode 100644 cpu/saml1x/periph/pm.c create mode 100644 cpu/saml1x/periph/rtc.c create mode 100644 cpu/saml1x/periph/rtt.c create mode 100644 cpu/saml1x/periph/timer.c create mode 100644 cpu/saml1x/vectors.c diff --git a/cpu/saml1x/Makefile b/cpu/saml1x/Makefile new file mode 100644 index 0000000000..8794dfbc47 --- /dev/null +++ b/cpu/saml1x/Makefile @@ -0,0 +1,10 @@ +# define the module that is build +MODULE = cpu + +# add a list of subdirectories, that should also be build +DIRS = periph $(RIOTCPU)/cortexm_common $(RIOTCPU)/sam0_common + +# (file triggers compiler bug. see #5775) +SRC_NOLTO += vectors.c + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/saml1x/Makefile.features b/cpu/saml1x/Makefile.features new file mode 100644 index 0000000000..52f4483551 --- /dev/null +++ b/cpu/saml1x/Makefile.features @@ -0,0 +1 @@ +include $(RIOTCPU)/sam0_common/Makefile.features diff --git a/cpu/saml1x/Makefile.include b/cpu/saml1x/Makefile.include new file mode 100644 index 0000000000..9da8c0ba5d --- /dev/null +++ b/cpu/saml1x/Makefile.include @@ -0,0 +1,4 @@ +export CPU_ARCH = cortex-m23 + +include $(RIOTCPU)/sam0_common/Makefile.include +include $(RIOTMAKE)/arch/cortexm.inc.mk diff --git a/cpu/saml1x/cpu.c b/cpu/saml1x/cpu.c new file mode 100644 index 0000000000..f3ee82c2eb --- /dev/null +++ b/cpu/saml1x/cpu.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2018 Mesotic SAS + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_saml1x + * @{ + * + * @file cpu.c + * @brief Implementation of the CPU initialization for Microchip + * SAML10/SAML11 MCUs + * + * @author Dylan Laduranty <dylan.laduranty@mesotic.com> + * @} + */ + +#include "cpu.h" +#include "periph/init.h" +#include "board.h" + +static void _gclk_setup(int gclk, uint32_t reg) +{ + GCLK->GENCTRL[gclk].reg = reg; + while (GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL(gclk)) {} +} + +/** + * @brief Initialize the CPU, set IRQ priorities, clocks + */ +void cpu_init(void) +{ + /* initialize the Cortex-M core */ + cortexm_init(); + + /* turn on only needed APB peripherals */ + MCLK->APBAMASK.reg = MCLK_APBAMASK_MCLK + | MCLK_APBAMASK_OSCCTRL + | MCLK_APBAMASK_GCLK +#ifdef MODULE_PERIPH_GPIO_IRQ + | MCLK_APBAMASK_EIC +#endif +#ifdef MODULE_PERIPH_GPIO + | MCLK_APBAMASK_PORT +#endif + ; + + /* Software reset the GCLK module to ensure it is re-initialized correctly */ + GCLK->CTRLA.reg = GCLK_CTRLA_SWRST; + while (GCLK->CTRLA.reg & GCLK_CTRLA_SWRST) {} + while (GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_SWRST) {} + + /* set OSC16M to 16MHz */ + OSCCTRL->OSC16MCTRL.bit.FSEL = 3; + OSCCTRL->OSC16MCTRL.bit.ONDEMAND = 0; + OSCCTRL->OSC16MCTRL.bit.RUNSTDBY = 0; + + /* Setup GCLK generators */ + _gclk_setup(0, GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSC16M); + +#ifdef MODULE_PERIPH_PM + /* enable power managemet module */ + MCLK->APBAMASK.reg |= MCLK_APBAMASK_PM; +#endif + + /* trigger static peripheral initialization */ + periph_init(); +} diff --git a/cpu/saml1x/doc.txt b/cpu/saml1x/doc.txt new file mode 100644 index 0000000000..9abaf68470 --- /dev/null +++ b/cpu/saml1x/doc.txt @@ -0,0 +1,9 @@ +/** + * @defgroup cpu_saml1x Microchip SAML10/SAML11 + * @ingroup cpu + * @brief Microchip SAML1x Cortex-M23 MCU specific implementation. + * + * This module contains Microchip SAML10/SAML11 specific code and definition. + * + * @see cpu_saml1x + */ diff --git a/cpu/saml1x/include/periph_cpu.h b/cpu/saml1x/include/periph_cpu.h new file mode 100644 index 0000000000..1b3ab8337b --- /dev/null +++ b/cpu/saml1x/include/periph_cpu.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 Mesotic SAS + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_saml1x + * @brief CPU specific definitions for internal peripheral handling + * @{ + * + * @file + * @brief CPU specific definitions for internal peripheral handling + * + * @author Dylan Laduranty <dylan.laduranty@mesotic.com> + */ + +#ifndef PERIPH_CPU_H +#define PERIPH_CPU_H + +#include "periph_cpu_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Mapping of pins to EXTI lines, -1 means not EXTI possible + */ +static const int8_t exti_config[1][32] = { + { 0, 1, 2, 3, 4, 5, 6, 7, -1, 0, 1, 2, -1, -1, 3, 4, + 5, 6, 7, 0, -1, -1, 1, 2, 3, 4, -1, 5, -1, -1, 6, 7}, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* PERIPH_CPU_H */ +/** @} */ diff --git a/cpu/saml1x/periph/Makefile b/cpu/saml1x/periph/Makefile new file mode 100644 index 0000000000..a36df249ac --- /dev/null +++ b/cpu/saml1x/periph/Makefile @@ -0,0 +1 @@ +include $(RIOTMAKE)/periph.mk diff --git a/cpu/saml1x/periph/pm.c b/cpu/saml1x/periph/pm.c new file mode 100644 index 0000000000..e6525e888c --- /dev/null +++ b/cpu/saml1x/periph/pm.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_saml21 + * @ingroup drivers_periph_pm + * @{ + * + * @file + * @brief Implementation of the kernels power management interface + * + * @author Kaspar Schleiser <kaspar@schleiser.de> + * + * @} + */ + +#include "periph/pm.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +void pm_set(unsigned mode) +{ + if (mode < PM_NUM_MODES) { + uint32_t _mode; + + switch (mode) { + case 0: + DEBUG("pm_set(): setting STANDBY mode.\n"); + _mode = PM_SLEEPCFG_SLEEPMODE_STANDBY; + break; + default: /* Falls through */ + case 1: + DEBUG("pm_set(): setting IDLE mode.\n"); + _mode = PM_SLEEPCFG_SLEEPMODE_IDLE; + break; + } + + /* write sleep configuration */ + PM->SLEEPCFG.bit.SLEEPMODE = _mode; + /* make sure value has been set */ + while (PM->SLEEPCFG.bit.SLEEPMODE != _mode) {} + } + + cortexm_sleep(0); +} diff --git a/cpu/saml1x/periph/rtc.c b/cpu/saml1x/periph/rtc.c new file mode 100644 index 0000000000..56046dfb1f --- /dev/null +++ b/cpu/saml1x/periph/rtc.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2014 Baptiste CLENET + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_saml21 + * @ingroup drivers_periph_rtc + * @{ + * @file + * @brief Low-level RTC driver implementation + * @author Baptiste Clenet <bapclenet@gmail.com> + * @autor ported to SAML21 by FWX <FWX@dialine.fr> + * @} + */ + +#include <time.h> +#include "cpu.h" +#include "periph/rtc.h" +#include "periph_conf.h" + +/* SAML21 rev B needs an extra bit, which in rev A defaults to 1, but isn't + * visible. Thus define it here. */ +#ifndef RTC_MODE2_CTRLA_CLOCKSYNC +#define RTC_MODE2_CTRLA_CLOCKSYNC_Pos 15 +#define RTC_MODE2_CTRLA_CLOCKSYNC (0x1ul << RTC_MODE2_CTRLA_CLOCKSYNC_Pos) +#endif + +typedef struct { + rtc_alarm_cb_t cb; /**< callback called from RTC interrupt */ + void *arg; /**< argument passed to the callback */ +} rtc_state_t; + +static rtc_state_t rtc_callback; + +/* At 1Hz, RTC goes till 63 years (2^5, see 17.8.22 in datasheet) +* reference_year is set to 100 (offset) to be in our current time (2000) +* Thanks to this, the user will be able to set time in 2000's*/ +static uint16_t reference_year = 100; + +void rtc_init(void) +{ + /* Turn on power manager for RTC */ + MCLK->APBAMASK.reg |= MCLK_APBAMASK_RTC | MCLK_APBAMASK_OSC32KCTRL; + /* DISABLE RTC MASTER */ + rtc_poweroff(); + + +#if EXTERNAL_OSC32_SOURCE + + /* RTC uses External 32,768KHz Oscillator */ + OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_XTALEN + | OSC32KCTRL_XOSC32K_EN1K + | OSC32KCTRL_XOSC32K_RUNSTDBY + | OSC32KCTRL_XOSC32K_ENABLE; + + /* Wait XOSC32K Ready */ + while (OSC32KCTRL->STATUS.bit.XOSC32KRDY==0); + + /* RTC source clock is external oscillator at 1kHz */ + OSC32KCTRL->RTCCTRL.reg = OSC32KCTRL_RTCCTRL_RTCSEL_XOSC1K; + +#endif /* EXTERNAL_OSC32_SOURCE */ + +#if INTERNAL_OSC32_SOURCE + uint32_t * pCalibrationArea; + uint32_t osc32kcal; + + /* Read OSC32KCAL, calibration data for OSC32 !!! */ + pCalibrationArea = (uint32_t*) NVMCTRL_OTP5; + osc32kcal = ( (*pCalibrationArea) & 0x1FC0 ) >> 6; + + /* RTC use Low Power Internal Oscillator at 1kHz */ + OSC32KCTRL->OSC32K.reg = OSC32KCTRL_OSC32K_RUNSTDBY + | OSC32KCTRL_OSC32K_EN1K + | OSC32KCTRL_OSC32K_CALIB(osc32kcal) + | OSC32KCTRL_OSC32K_ENABLE; + + /* Wait OSC32K Ready */ + while (OSC32KCTRL->STATUS.bit.OSC32KRDY==0); + + /* RTC uses internal 32,768KHz Oscillator */ + OSC32KCTRL->RTCCTRL.reg = OSC32KCTRL_RTCCTRL_RTCSEL_OSC1K; + + +#endif /* INTERNAL_OSC32_SOURCE */ + +#if ULTRA_LOW_POWER_INTERNAL_OSC_SOURCE + + /* RTC uses Ultra Low Power internal 32,768KHz Oscillator */ + OSC32KCTRL->RTCCTRL.reg = OSC32KCTRL_RTCCTRL_RTCSEL_ULP1K; + +#endif /* ULTRA_LOW_POWER_INTERNAL_OSC_SOURCE */ + + /* Software Reset the RTC */ + RTC->MODE2.CTRLA.bit.SWRST = 1; + /* Wait end of reset */ + while (RTC->MODE2.CTRLA.bit.SWRST); + + /* RTC config with RTC_MODE2_CTRL_CLKREP = 0 (24h) */ + RTC->MODE2.CTRLA.reg = RTC_MODE2_CTRLA_PRESCALER_DIV1024 | /* CLK_RTC_CNT = 1KHz / 1024 -> 1Hz */ + RTC_MODE2_CTRLA_CLOCKSYNC | /* Clock Read Synchronization Enable */ + RTC_MODE2_CTRLA_MODE_CLOCK; /* Mode 2: Clock/Calendar */ + + /* Clear interrupt flags */ + RTC->MODE2.INTFLAG.reg |= RTC_MODE2_INTFLAG_OVF; + RTC->MODE2.INTFLAG.reg |= RTC_MODE2_INTFLAG_ALARM0; + + rtc_poweron(); +} + +int rtc_set_time(struct tm *time) +{ + if ((time->tm_year < reference_year) || (time->tm_year > reference_year + 63)) { + return -1; + } + else { + while (RTC->MODE2.SYNCBUSY.bit.CLOCK); + RTC->MODE2.CLOCK.reg = RTC_MODE2_CLOCK_YEAR(time->tm_year - reference_year) + | RTC_MODE2_CLOCK_MONTH(time->tm_mon + 1) + | RTC_MODE2_CLOCK_DAY(time->tm_mday) + | RTC_MODE2_CLOCK_HOUR(time->tm_hour) + | RTC_MODE2_CLOCK_MINUTE(time->tm_min) + | RTC_MODE2_CLOCK_SECOND(time->tm_sec); + while (RTC->MODE2.SYNCBUSY.bit.CLOCK); + } + return 0; +} + +int rtc_get_time(struct tm *time) +{ + RTC_MODE2_CLOCK_Type clock; + + /* Read register in one time */ + clock.reg = RTC->MODE2.CLOCK.reg; + + time->tm_year = clock.bit.YEAR + reference_year; + if ((time->tm_year < reference_year) || (time->tm_year > (reference_year + 63))) { + return -1; + } + time->tm_mon = clock.bit.MONTH - 1; + time->tm_mday = clock.bit.DAY; + time->tm_hour = clock.bit.HOUR; + time->tm_min = clock.bit.MINUTE; + time->tm_sec = clock.bit.SECOND; + return 0; +} + +int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg) +{ + rtc_clear_alarm(); + if ((time->tm_year < reference_year) || (time->tm_year > (reference_year + 63))) { + return -2; + } + else { + RTC->MODE2.Mode2Alarm[0].ALARM.reg = RTC_MODE2_ALARM_YEAR(time->tm_year - reference_year) + | RTC_MODE2_ALARM_MONTH(time->tm_mon + 1) + | RTC_MODE2_ALARM_DAY(time->tm_mday) + | RTC_MODE2_ALARM_HOUR(time->tm_hour) + | RTC_MODE2_ALARM_MINUTE(time->tm_min) + | RTC_MODE2_ALARM_SECOND(time->tm_sec); + RTC->MODE2.Mode2Alarm[0].MASK.reg = RTC_MODE2_MASK_SEL(6); + while (RTC->MODE2.SYNCBUSY.bit.ALARM0); + } + + /* Setup interrupt */ + NVIC_EnableIRQ(RTC_IRQn); + + /* Enable IRQ */ + rtc_callback.cb = cb; + rtc_callback.arg = arg; + RTC->MODE2.INTFLAG.reg |= RTC_MODE2_INTFLAG_ALARM0; + RTC->MODE2.INTENSET.bit.ALARM0 = 1; + + return 0; +} + +int rtc_get_alarm(struct tm *time) +{ + RTC_MODE2_ALARM_Type alarm; + + /* Read alarm register in one time */ + alarm.reg = RTC->MODE2.Mode2Alarm[0].ALARM.reg; + + time->tm_year = alarm.bit.YEAR + reference_year; + if ((time->tm_year < reference_year) || (time->tm_year > (reference_year + 63))) { + return -1; + } + time->tm_mon = alarm.bit.MONTH - 1; + time->tm_mday = alarm.bit.DAY; + time->tm_hour = alarm.bit.HOUR; + time->tm_min = alarm.bit.MINUTE; + time->tm_sec = alarm.bit.SECOND; + + return 0; +} + +void rtc_clear_alarm(void) +{ + /* disable interrupt */ + RTC->MODE2.INTENCLR.bit.ALARM0 = 1; + rtc_callback.cb = NULL; + rtc_callback.arg = NULL; +} + +void rtc_poweron(void) +{ + RTC->MODE2.CTRLA.bit.ENABLE = 1; + while (RTC->MODE2.SYNCBUSY.bit.ENABLE); +} + +void rtc_poweroff(void) +{ + RTC->MODE2.CTRLA.bit.ENABLE = 0; + while (RTC->MODE2.SYNCBUSY.bit.ENABLE); +} + +void isr_rtc(void) +{ + if (RTC->MODE2.INTFLAG.bit.ALARM0) { + rtc_callback.cb(rtc_callback.arg); + /* clear flag */ + RTC->MODE2.INTFLAG.reg |= RTC_MODE2_INTFLAG_ALARM0; + } + if (RTC->MODE2.INTFLAG.bit.OVF) { + /* clear flag */ + RTC->MODE2.INTFLAG.reg |= RTC_MODE2_INTFLAG_OVF; + /* At 1Hz, RTC goes till 63 years (2^5, see 17.8.22 in datasheet) + * Start RTC again with reference_year 64 years more (Be careful with alarm set) */ + reference_year += 64; + } + cortexm_isr_end(); +} diff --git a/cpu/saml1x/periph/rtt.c b/cpu/saml1x/periph/rtt.c new file mode 100644 index 0000000000..d2da91a6fc --- /dev/null +++ b/cpu/saml1x/periph/rtt.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de> + * 2015 FreshTemp, LLC. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_saml21 + * @ingroup drivers_periph_rtt + * @{ + * + * @file rtt.c + * @brief Low-level RTT driver implementation + * + * @author Kaspar Schleiser <kaspar@schleiser.de> + * + * @} + */ + +#include <stdint.h> +#include "periph/rtt.h" +#include "board.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static rtt_cb_t _overflow_cb; +static void* _overflow_arg; + +static rtt_cb_t _cmp0_cb; +static void* _cmp0_arg; + +void rtt_init(void) +{ + DEBUG("%s:%d\n", __func__, __LINE__); + /* Turn on power manager for RTC */ + MCLK->APBAMASK.reg |= MCLK_APBAMASK_RTC | MCLK_APBAMASK_OSC32KCTRL; + rtt_poweron(); + + /* reset */ + RTC->MODE0.CTRLA.bit.SWRST = 1; + while(RTC->MODE0.CTRLA.bit.SWRST) {} + + /* set 32bit counting mode */ + RTC->MODE0.CTRLA.bit.MODE = 0; + + /* set clock source */ + OSC32KCTRL->RTCCTRL.reg = OSC32KCTRL_RTCCTRL_RTCSEL_ULP32K; + + /* enable */ + RTC->MODE0.CTRLA.bit.ENABLE = 1; + while(RTC->MODE0.SYNCBUSY.bit.ENABLE) {} + + /* initially clear flag */ + RTC->MODE0.INTFLAG.reg |= RTC_MODE1_INTFLAG_CMP(1 << 0); + + /* enable RTT IRQ */ + NVIC_EnableIRQ(RTC_IRQn); + + DEBUG("%s:%d %u\n", __func__, __LINE__, (unsigned)rtt_get_counter()); +} + +void rtt_set_overflow_cb(rtt_cb_t cb, void *arg) +{ + DEBUG("%s:%d\n", __func__, __LINE__); + /* clear overflow cb to avoid race while assigning */ + rtt_clear_overflow_cb(); + + /* set callback variables */ + _overflow_cb = cb; + _overflow_arg = arg; + + /* enable overflow interrupt */ + RTC->MODE0.INTENSET.bit.OVF = 1; +} +void rtt_clear_overflow_cb(void) +{ + DEBUG("%s:%d\n", __func__, __LINE__); + /* disable overflow interrupt */ + RTC->MODE0.INTENCLR.bit.OVF = 1; +} + +uint32_t rtt_get_counter(void) +{ + DEBUG("%s:%d\n", __func__, __LINE__); + while (RTC->MODE0.SYNCBUSY.bit.COUNT) {} + return RTC->MODE0.COUNT.reg; +} + +void rtt_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg) +{ + DEBUG("%s:%d alarm=%u\n", __func__, __LINE__, (unsigned)alarm); + + /* disable interrupt to avoid race */ + rtt_clear_alarm(); + + /* set COM register */ + while (RTC->MODE0.SYNCBUSY.bit.COMP0) {} + RTC->MODE0.COMP[0].reg = alarm; + + /* setup callback */ + _cmp0_cb = cb; + _cmp0_arg = arg; + + /* enable compare interrupt */ + RTC->MODE0.INTENSET.bit.CMP0 = 1; +} + +void rtt_clear_alarm(void) +{ + DEBUG("%s:%d\n", __func__, __LINE__); + /* clear compare interrupt */ + RTC->MODE0.INTENCLR.bit.CMP0 = 1; +} + +void rtt_poweron(void) +{ + DEBUG("%s:%d\n", __func__, __LINE__); + MCLK->APBAMASK.reg |= MCLK_APBAMASK_RTC; +} + +void rtt_poweroff(void) +{ + DEBUG("%s:%d\n", __func__, __LINE__); + MCLK->APBAMASK.reg &= ~MCLK_APBAMASK_RTC; +} + +void isr_rtc(void) +{ + if (RTC->MODE0.INTFLAG.bit.OVF) { + RTC->MODE0.INTFLAG.reg |= RTC_MODE0_INTFLAG_OVF; + if (_overflow_cb) { + _overflow_cb(_overflow_arg); + } + } + if (RTC->MODE0.INTFLAG.bit.CMP0) { + /* clear flag */ + RTC->MODE0.INTFLAG.reg |= RTC_MODE1_INTFLAG_CMP(1 << 0); + /* disable interrupt */ + RTC->MODE0.INTENCLR.bit.CMP0 = 1; + if (_cmp0_cb) { + _cmp0_cb(_cmp0_arg); + } + } + cortexm_isr_end(); +} diff --git a/cpu/saml1x/periph/timer.c b/cpu/saml1x/periph/timer.c new file mode 100644 index 0000000000..bf0a13f4b1 --- /dev/null +++ b/cpu/saml1x/periph/timer.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2018 Mesotic SAS + * + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_saml1x + * @ingroup drivers_periph_timer + * @{ + * + * @file timer.c + * @brief Low-level timer driver implementation + * + * @author Dylan Laduranty <dylan.laduranty@mesotic.com> + * + * @} + */ + +#include <stdlib.h> +#include <stdio.h> + +#include "board.h" +#include "cpu.h" + +#include "periph/timer.h" +#include "periph_conf.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Timer state memory + */ +static timer_isr_ctx_t config[TIMER_NUMOF]; + +/* enable timer interrupts */ +static inline void _irq_enable(tim_t dev); + + +/** + * @brief Setup the given timer + */ +int timer_init(tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) +{ + /* at the moment, the timer can only run at 1MHz */ + if (freq != 1000000ul) { + return -1; + } + /* configure GCLK0 to feed TC0 & TC1*/ + GCLK->PCHCTRL[TC0_GCLK_ID].reg |= GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0; + while (!(GCLK->PCHCTRL[TC0_GCLK_ID].reg & GCLK_PCHCTRL_CHEN)) {} + + /* select the timer and enable the timer specific peripheral clocks */ + switch (dev) { +#if TIMER_0_EN + case TIMER_0: + if (TIMER_0_DEV.CTRLA.bit.ENABLE) { + return 0; + } + MCLK->APBCMASK.reg |= MCLK_APBCMASK_TC0; + /* reset timer */ + TIMER_0_DEV.CTRLA.bit.SWRST = 1; + while (TIMER_0_DEV.SYNCBUSY.bit.SWRST) {} + TIMER_0_DEV.CTRLA.reg |= TC_CTRLA_MODE_COUNT32 | /* choosing 32 bit mode */ + TC_CTRLA_PRESCALER(4) | /* sourced by 4MHz with Presc 4 results in 1MHz*/ + TC_CTRLA_PRESCSYNC_RESYNC; /* initial prescaler resync */ + break; +#endif + case TIMER_UNDEFINED: + default: + return -1; + } + + /* save callback */ + config[dev].cb = cb; + config[dev].arg = arg; + + /* enable interrupts for given timer */ + _irq_enable(dev); + + timer_start(dev); + + return 0; +} + +int timer_set_absolute(tim_t dev, int channel, unsigned int value) +{ + DEBUG("Setting timer %i channel %i to %i\n", dev, channel, value); + + /* get timer base register address */ + switch (dev) { +#if TIMER_0_EN + case TIMER_0: + /* set timeout value */ + switch (channel) { + case 0: + TIMER_0_DEV.INTFLAG.reg |= TC_INTFLAG_MC0; + TIMER_0_DEV.CC[0].reg = value; + TIMER_0_DEV.INTENSET.bit.MC0 = 1; + break; + case 1: + TIMER_0_DEV.INTFLAG.reg |= TC_INTFLAG_MC1; + TIMER_0_DEV.CC[1].reg = value; + TIMER_0_DEV.INTENSET.bit.MC1 = 1; + break; + default: + return -1; + } + break; +#endif + case TIMER_UNDEFINED: + default: + return -1; + } + + return 1; +} + +int timer_clear(tim_t dev, int channel) +{ + /* get timer base register address */ + switch (dev) { +#if TIMER_0_EN + case TIMER_0: + switch (channel) { + case 0: + TIMER_0_DEV.INTFLAG.reg |= TC_INTFLAG_MC0; + TIMER_0_DEV.INTENCLR.bit.MC0 = 1; + break; + case 1: + TIMER_0_DEV.INTFLAG.reg |= TC_INTFLAG_MC1; + TIMER_0_DEV.INTENCLR.bit.MC1 = 1; + break; + default: + return -1; + } + break; +#endif + case TIMER_UNDEFINED: + default: + return -1; + } + + return 1; +} + +unsigned int timer_read(tim_t dev) +{ + switch (dev) { +#if TIMER_0_EN + case TIMER_0: + /* request syncronisation */ + TIMER_0_DEV.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_READSYNC_Val; + while (TIMER_0_DEV.SYNCBUSY.bit.CTRLB) { + /* WORKAROUND to prevent being stuck there if timer not init */ + if(!TIMER_0_DEV.CTRLA.bit.ENABLE) { + return 0; + } + } + return TIMER_0_DEV.COUNT.reg; +#endif + default: + return 0; + } + + +} + +void timer_stop(tim_t dev) +{ + switch (dev) { +#if TIMER_0_EN + case TIMER_0: + TIMER_0_DEV.CTRLA.bit.ENABLE = 0; + break; +#endif + case TIMER_UNDEFINED: + break; + } +} + +void timer_start(tim_t dev) +{ + switch (dev) { +#if TIMER_0_EN + case TIMER_0: + TIMER_0_DEV.CTRLA.bit.ENABLE = 1; + break; +#endif + case TIMER_UNDEFINED: + break; + } +} + +static inline void _irq_enable(tim_t dev) +{ + switch (dev) { +#if TIMER_0_EN + case TIMER_0: + NVIC_EnableIRQ(TC0_IRQn); + break; +#endif + case TIMER_UNDEFINED: + break; + } +} + +#if TIMER_0_EN +void TIMER_0_ISR(void) +{ + if (TIMER_0_DEV.INTFLAG.bit.MC0 && TIMER_0_DEV.INTENSET.bit.MC0) { + if(config[TIMER_0].cb) { + TIMER_0_DEV.INTFLAG.reg |= TC_INTFLAG_MC0; + TIMER_0_DEV.INTENCLR.reg = TC_INTENCLR_MC0; + config[TIMER_0].cb(config[TIMER_0].arg, 0); + } + } + else if (TIMER_0_DEV.INTFLAG.bit.MC1 && TIMER_0_DEV.INTENSET.bit.MC1) { + if(config[TIMER_0].cb) { + TIMER_0_DEV.INTFLAG.reg |= TC_INTFLAG_MC1; + TIMER_0_DEV.INTENCLR.reg = TC_INTENCLR_MC1; + config[TIMER_0].cb(config[TIMER_0].arg, 1); + } + } + cortexm_isr_end(); +} +#endif /* TIMER_0_EN */ diff --git a/cpu/saml1x/vectors.c b/cpu/saml1x/vectors.c new file mode 100644 index 0000000000..7e4e93ee21 --- /dev/null +++ b/cpu/saml1x/vectors.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2018 Mesotic SAS + * + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_saml1x + * @{ + * + * @file vectors.c + * @brief Startup code and interrupt vector definition + * + * @author Dylan Laduranty <dylan.laduranty@mesotic.com> + * + * @} + */ + +#include <stdint.h> +#include "vectors_cortexm.h" + +/* define a local dummy handler as it needs to be in the same compilation unit + * as the alias definition */ +void dummy_handler(void) { + dummy_handler_default(); +} + +/* SAML1x specific interrupt vector */ +WEAK_DEFAULT void isr_system(void); +WEAK_DEFAULT void isr_wdt(void); +WEAK_DEFAULT void isr_rtc(void); +WEAK_DEFAULT void isr_eic0(void); +WEAK_DEFAULT void isr_eic1(void); +WEAK_DEFAULT void isr_eic2(void); +WEAK_DEFAULT void isr_eic3(void); +WEAK_DEFAULT void isr_eic_other(void); +WEAK_DEFAULT void isr_freqm(void); +WEAK_DEFAULT void isr_nvmctrl(void); +WEAK_DEFAULT void isr_port(void); +WEAK_DEFAULT void isr_dmac0(void); +WEAK_DEFAULT void isr_dmac1(void); +WEAK_DEFAULT void isr_dmac2(void); +WEAK_DEFAULT void isr_dmac3(void); +WEAK_DEFAULT void isr_dmac_other(void); +WEAK_DEFAULT void isr_evsys0(void); +WEAK_DEFAULT void isr_evsys1(void); +WEAK_DEFAULT void isr_evsys2(void); +WEAK_DEFAULT void isr_evsys3(void); +WEAK_DEFAULT void isr_evsys_nschk(void); +WEAK_DEFAULT void isr_pac(void); +WEAK_DEFAULT void isr_sercom0_0(void); +WEAK_DEFAULT void isr_sercom0_1(void); +WEAK_DEFAULT void isr_sercom0_2(void); +WEAK_DEFAULT void isr_sercom0_other(void); +WEAK_DEFAULT void isr_sercom1_0(void); +WEAK_DEFAULT void isr_sercom1_1(void); +WEAK_DEFAULT void isr_sercom1_2(void); +WEAK_DEFAULT void isr_sercom1_other(void); +WEAK_DEFAULT void isr_sercom2_0(void); +WEAK_DEFAULT void isr_sercom2_1(void); +WEAK_DEFAULT void isr_sercom2_2(void); +WEAK_DEFAULT void isr_sercom2_other(void); +WEAK_DEFAULT void isr_tc0(void); +WEAK_DEFAULT void isr_tc1(void); +WEAK_DEFAULT void isr_tc2(void); +WEAK_DEFAULT void isr_adc_other(void); +WEAK_DEFAULT void isr_adc_resrdy(void); +WEAK_DEFAULT void isr_ac(void); +WEAK_DEFAULT void isr_dac_underrun_a(void); +WEAK_DEFAULT void isr_dac_empty(void); +WEAK_DEFAULT void isr_ptc(void); +WEAK_DEFAULT void isr_trng(void); +WEAK_DEFAULT void isr_tram(void); + +/* CPU specific interrupt vector table */ +ISR_VECTOR(1) const isr_t vector_cpu[CPU_IRQ_NUMOF] = { + (void*) isr_system, /* 0 Main Clock */ + (void*) isr_wdt, /* 1 Watchdog Timer */ + (void*) isr_rtc, /* 2 Real-Time Counter */ + (void*) isr_eic0, /* 3 External Interrupt Controller */ + (void*) isr_eic1, /* 4 External Interrupt Controller */ + (void*) isr_eic2, /* 5 External Interrupt Controller */ + (void*) isr_eic3, /* 6 External Interrupt Controller */ + (void*) isr_eic_other, /* 7 External Interrupt Controller */ + (void*) isr_freqm, /* 8 Frequency Meter */ + (void*) isr_nvmctrl, /* 9 Non-Volatile Memory Controller */ + (void*) isr_port, /* 10 Port Module */ + (void*) isr_dmac0, /* 11 Direct Memory Access Controller */ + (void*) isr_dmac1, /* 12 Direct Memory Access Controller */ + (void*) isr_dmac2, /* 13 Direct Memory Access Controller */ + (void*) isr_dmac3, /* 14 Direct Memory Access Controller */ + (void*) isr_dmac_other, /* 15 Direct Memory Access Controller */ + (void*) isr_evsys0, /* 16 Event System Interface */ + (void*) isr_evsys1, /* 17 Event System Interface */ + (void*) isr_evsys2, /* 18 Event System Interface */ + (void*) isr_evsys3, /* 19 Event System Interface */ + (void*) isr_evsys_nschk, /* 20 Event System Interface */ + (void*) isr_pac, /* 21 Peripheral Access Controller */ + (void*) isr_sercom0_0, /* 22 Serial Communication Interface */ + (void*) isr_sercom0_1, /* 23 Serial Communication Interface */ + (void*) isr_sercom0_2, /* 24 Serial Communication Interface */ + (void*) isr_sercom0_other, /* 25 Serial Communication Interface */ + (void*) isr_sercom1_0, /* 26 Serial Communication Interface */ + (void*) isr_sercom1_1, /* 27 Serial Communication Interface */ + (void*) isr_sercom1_2, /* 28 Serial Communication Interface */ + (void*) isr_sercom1_other, /* 29 Serial Communication Interface */ + (void*) isr_sercom2_0, /* 30 Serial Communication Interface */ + (void*) isr_sercom2_1, /* 31 Serial Communication Interface */ + (void*) isr_sercom2_2, /* 32 Serial Communication Interface */ + (void*) isr_sercom2_other, /* 33 Serial Communication Interface */ + (void*) isr_tc0, /* 34 Basic Timer Counter */ + (void*) isr_tc1, /* 35 Basic Timer Counter */ + (void*) isr_tc2, /* 36 Basic Timer Counter */ + (void*) isr_adc_other, /* 37 Analog Digital Converter */ + (void*) isr_adc_resrdy, /* 38 Analog Digital Converter */ + (void*) isr_ac, /* 39 Analog Comparators */ + (void*) isr_dac_underrun_a, /* 40 Digital Analog Converter */ + (void*) isr_dac_empty, /* 41 Analog Digital Converter */ + (void*) isr_ptc, /* 42 Peripheral Touch Controller */ + (void*) isr_trng, /* 43 True Random Number Generator */ + (void*) isr_tram, /* 44 Trust RAM */ +}; -- GitLab